java的API
1.字符串—String
- charAt()—(char类型)–获取给定位置的对应字符
- indexOf()—(int类型)–获取给定字符串在当前的位置,若当前字符串没有给定字符串,返回-1
- lastIndexOf()----获取给定字符串在当前查找的字符串最后的位置
- length()—(int类型)–获取当前字符串的长度
- startsWith()—(boolean类型)–判断字符串是否是以,给定的字符串开始,
- endsWith()–(boolean类型)–判断字符串是否是以,给定的字符串结尾
- subString()—(String类型)–俩个参数截取指定范围内的字符串,一个参数是从指定位置截取到字符串末尾.
- 字符串提供了一组重载的String.valueOf()方法,可以将其他类型转化为字符串
- trim()—去除当前字符串前后的空白字符
- toUpperCase()—把字符串都变成大写,
- toLowerCase()—把当前字符串都变成小
- replaceAll()—替换字符串中字符
- split(String regex)–(String[]–数组类型)—依据给定的字符串,截取当前字符串,并放入数组中
2.StringBuilder builder = new StringBuilder(str);
StringBuilder是非线程安全, 并发处理,性能稍快
StringBuffer是线程安全,同步处理, 性能稍慢
- builder.append(),将指定的字符串,追加到当前字符串末尾
- builder.replace(),将指定范围内的字符串,替换为给定内容
- builder.delete(),删除指定范围内的字符串
- builder.insert(),将指定的内容,插入到指定的位置
- builder.reverse(),把指定的字符串反过来写
3.正则表达式
通用的邮箱的正则表达式:\aaa@qq.com[a-zA-Z0-9]+(.[a-zA-Z]+)+
Java中的邮箱的正则表达式:String regex = “\aaa@qq.com[a-zA-Z0-9]+(\.[a-zA-Z]+)+”;
boolean matches(String regex)
使用给定的正则表达式验证当前字符串是否符合格式的要求
无论给定的正则表达式是否指定了边界匹配(^…$)都是做完全匹配
String[] split(String regex)
将当前字符串中按照满足正则表达式的部分进行拆分,并将拆分出的每部分以一个字符串数组形成返回
如果连续匹配了俩次要拆分的内容时,中间会拆分出一个空字符串,
但是若在字符串末尾连续匹配,则所有拆分出的空字符串都会忽略.
String replaceAll(String regex,String str)
将当前字符串中满足正则表达式的部分,替换为给定字符串
4.包装类
int a = 128;
将 基本类型转化成包装类建议使用—valueOf()
Integer In1 = Integer.valueOf(a);
将包装类转化为基本类型—类型Value()
int in1 = In1.intValue();
数字类型的包装类型都支持俩个常量,MAX_VALUE,MIN_VALUE用于表示其对应的基本类型范围
包装类.parseXXX(String str)—可以将给定的字符串转化为对应的基本类型,前提是该字符串内容要正确的描述基本类型可以保存的值
equals与==的区别
用于比较变量的值,可以应用于任何类型,如果用于引用类型,比较的是俩个引用变量中存储的值(地址信息),判断俩个变量是否指向相同的对象;
equals是Object的方法,重写以后,可以用于比较俩个对象的内容是否”相等”;
需要注意的是,Object默认的equals方法的比较规则
5.File
用于表示文件系统中的一个文件或目录,使用File可以:
- 引用的包—java.io.File
- 访问其表示的文件或目录的属性信息(名字,大小等)
- 创建,删除文件或目录
- 访问目录子项
- 但是不能访问文件数据
File file = new File(“文件名”); - File.getName()—获取文件名
- file.exists()—判断file表示的文件或目录是否已经存在
- file.createNewFile()—创建文件
- file.mkdirs()—创建目录(可以多级一起创建)
- file.delete()—删除文件或目录
- file.isDirectory()—判断当前目录对象,表示的是否为一个目录(boolean型)
- file.isFile()—/判断当前目录对象,表示的是否为一个文件
- file.listFiles()—返回路径名数组,这些路径名表示文件和目录
- file.separator—是一个/的符号
FileFilter endsFilter = new FileFilter(){—编译过滤器
public boolean accept(File endsFile){
return endsFile.getName().endsWith(".txt");
}
};
File[] sub = file.listFiles(endsFilter);
File[] sub = file.listFiles(new FileFilter(){—写在对象中
public boolean accept(File endsFile) {
return endsFile.getName().endsWith(".txt");
}
});
6.RandomAccessFile
用来读写文件数据的类,其基于指针对文件数据进行读写操作
常见构造方法:
RandomAccessFile(String path, String mode)
RandomAccessFile(File file,String mode)
mode: 创建的模式(r,rw)
"rw"读写模式,读取文件数据,可以写入内容,"rw"模式创建时,若指定的文件件不存在时,会自动创建该文件.
"r"只读模式,对文件即可以读也能写,对于"r"模式创建时,若指定的文件不存在时,会报错误.
RandomAccessFile raf = new RandomAccessFile(“文件名”, “rw”);
- raf.read()—文件中读取1个字节(0-255), 从文件中读取1个字节,并以int返回.若返回-1,则表示读取到了文件末尾.
- raf.write()—向文件中写入1个字节,写入的是给定的int值对应的2进制的"低8位"
- raf.read(byte[] data)—(int类型)一次性从文件中读取给定的字节数组总长度的字节量,并将读取到的字节存入到该数组中。返回值为实际读取到的字节量,若返回值为-1,表示读取到了文件末尾,本次没有读到任何字节
- raf.write(byte[] data)—一次性将给定的字节数组中的所有字节写入文件
- raf.write(byte[] data,int start,int len)—将给定字节数组从下标start处开始的连续len个字节写入到文件中
- 传统的机械盘由于其物理特性决定这单字节读写效率差.但是块读写效率是可以的.所以通过提高每次的读取数据量,减少实际读写的次数,可以提高读写的效率.
随机读写:通常是单字节读写
块读写 :一次读写一组字节的模式 - raf.writeInt()— 一次性写入4字节,将给定的int值写入,对应的数据类型,都提供了写操作
- raf.getFilePointer()—(int类型)读取指针的位置
- raf.seek(long pos)—将指针移动到指定位置
byte[] data = new byte[(int)raf.length()]; - String(byte[] data,String csn)—按照指定的字符集将字符转换为对应的字符串,
byte[] data = new byte[文件大小];
raf.read(data);
String str = new String(data,“UTF-8”);
System.out.println(str); - byte[] getBytes(String csn)—把字符串转化成2进制,有重载方法getBytes(“字符编码格式”);
String str1 = “”;
byte[] data = str1.getBytes(“utf-8”);
raf.write(data); - csn:charset name 字符集名称,----常见字符集:
GNK:国际编码,其中英文1字节,中文2字节.
UTF_8:Unicode的字符集编码,其中英文1字节,中文3字节.UTF-8支持世界流行的所有文字,所以又称为万国码,互联网最常用字符集.
ISO8859-1:一种欧洲的编码字符集,不支持中文.
7.IO—Java标准的输入与输出
Java提供了统一的标准的输入与输出操作,用于与外界设备进行交互数据.其中:
输入流:用于从外界读取数据到程序中的流.()
输出流:用于将程序中的数据发送到外界的流.()
- java.io.InputStream:—所有字节输入流,定义了所有的输入流都具备的读取字节的方法.但本身是抽象的,不可以实例化.
- java.io.OutputStrem:—所有字节输出流的超类,定义了写出数据的方法
- 流读写数据采用的模式为:顺序读写,也就是说无论读还是写,都是一次性的,不可以退回
- Java将流划分为俩类:
节点流:又叫低级流,是实际连接程序与另一端的"通道",负者实际读写数据的流,读写一定时建立在节点流的基础上进行的.
处理流:又叫高级流,高级流不可以独立存在,必须连接在其他流上,这样当数据流经过当前流经高级流时会对数据进行加工处理,这样可以简化我们对数据加工的操作. - 用一组高级流进行串联操作,最终连接到某个低级流上,完成对读写数据的流水线加工.这样的操作称为:留链接—留链接是学习IO的精髓,要掌握.
- 文件流是一种低级流,用于读写文件数据的流
java.io.FileOutputStream
java.io.FileInputStream - 文流与RandomAccessFile对比
文件流是基于Java标准IO进行读写数据的,所以对文件数据是顺序读写形式.
RAF是基于指针的随机读写形式,可以操作指针对文件任意位置进行编辑(读写)
文件流可以基于流连接,串联若干高级流完成更复杂的读写数据操作,RAF很多操作都需要自行完成. - RandomAccessFile
a) RandomAccessFile raf = new RandomAccessFile(“user.dat”,“rw”);
b) raf.getFilePointer()----查看文件大小
c) 如果持续输入文件,需要指定指针位置到上一次输入的末尾raf.seek(raf.length());
byte[] data = username.getBytes(“UTF-8”);
data = Arrays.copyOf(data, 32);
raf.write(data);
write()—是1个字节
writeInt()—是4个字节 - 常见的构造方法:
- FileOutputStream(String path)
- FileOutputStream(File File)
- 以上俩种创建模式为覆盖模式,即:创建流的时候若该文件已经存在,流会将现有数据清除,然后才开始新的写操作
- FileOutputStream(String path, boolean append)-true时,追加
- FileOutputStream(File file, boolean append)-
- 以上俩种创建模式为追加写模式,即:文件数据都保留,会将通过该写入的数据继续追加到文件末尾.
9)缓冲流是一对高级流,作用是加快读写效率.这使得我们在读写数据时,无论有随机读写还是块读写都可以保证效率.实际上缓冲流会将我们的读写最终统一转化为块读写,提高的效率
java.io.BufferedInputStream
java.io.BufferedOutputStream
- 对象输入流的readObject()方法会将读取的字节.按照其结构还源会对应的对象.而这个过程称为:对象范序列化
- 对象输出流的writeObject()方法可以将给定的对象,按照其结构转换为一组字节后,通过其连接的流写出.但是这里有一个前提要求:
写出的对象所属的类必须实现可序列化接口,否则抛出常:NotSerializableException
当前案例中,我们在流连接中连接了俩个流:文件流,对象流
对象输出流负者将对象,按照其结构转换为一组字节二这个过程为:对象序列化
文件输出流负者将字节写入到文件中,二将数据写入的过程称为:数据持久化 - 所有希望被对象流读写的类都必须继承Serializable接口,该接口是一个"签名接口"实现该接口后再源代码中不需要重写任何方法.
实际上编译器在编译当前源代码为class文件时,发现当前类实现了可序列化接口,那么会为其自动添加一个方法,用于将当前类实例转换为一组字节.但是这个方法无需在源代码中被体现了. - 当一个属性被transfersient关键字修饰后,那么当前类实例在序列化时,该属性值会被忽略.忽略不必要的属性,可以减少序列化后的字节,做到对象瘦身的效果.减少资源开销.
- void flush()—缓存流的flush()方法用于将缓存区中已经缓存的数据,一次性写出.频繁调用flush()方法会降低些效率,但是可以保证写出的及时性,根据市局需求酌情调用.
- java将流,按照读写单位划分为俩类:
字节流:读写以字节为最小单位
字符流:读写以字符为(Unicode)最小单位,实际上地城本质还是读写字节,但是字节与字符的转换操作有字符流自行完成.字符流只适合读写文本数据.
java.io.Reader—是所有字符输入流的超类
java.io.Writer—是所有字符输出流的超类
16**)转换流:(是一种高级流)????*
- java.io.InputStreamReader
- java.io.OutputStreamWriter
他们在流连接中使用字符流完成字符读写操作时非常重要的一环,但是通常不直接操作者俩个流
17)java.io.InputStreamReader—转换输入流
int read()—字符流的read方法是一种读取一个字符的,所以虽然返回值是int型,但实际是一个char值,若返回的int值为-1时,表示文件末尾
- java.io.BufferedReader—缓冲字符输入流
快读操作,提高读取字符速度,并且提供了按行读取字符的操作
String readLine()—缓冲字符输入流提供的该方法可以连续读取若干字符,直到读取了换行符为止,然后将换行符之前的内容以字符串形式返回,返回不含有最后的换行符.若返回值为null,表示读取到文件末尾
19)java.io.PrintWriter—常用的缓冲字符输出流,可以按行写出字符串,并且具有自动行刷新功能.内部常连接java.io.BuffereedWriter作为缓冲使用
PrintWriter提供了直接对文件写出操作的构造方法:—都可以写编码格式
PrintWriter(String fileName,String csn)
printWriter(File file,String csn)
里面有println("");是行写—按行写
FileOutputStream fos = new FileOutputStream(“pw2.txt”);//负者将字节写入文件
OutputStreamWriter osw = new OutputStreamWriter(fos);//负者将字符转换为字节
BufferedWriter bw = new BufferedWriter(osw);//负者块写操作
PrintWriter pw = new PrintWriter(bw);//负者自动刷新
pw.println("");—按行写,自动换行,一句一行
8.excetion—异常问题( try{}catch{} )
java异常处理机制—try-catch-----当JVM执行过程出现某个异常时,会实例化对应的异常实例.并将程序执行过程设置进去.这时该异常实例可以完整说明该异常情况,实例化完毕后,JVM会将异常抛出.
finally是异常处理机制的最后一块,它可以直接在try语句块之后,活着最后的catch块之后,–finally能确保只要程序执行到try里面,无论try语句块中的代码是否抛出异常,finally块中的代码必定执行,–通常会将无关乎程序出错都要执行的代码块方法在这里,比如资源释放:IO流的关闭
在 JDK1.7 之后提供了一个接口:AutoCloseable—实现了该接口的类可以在此定义.最终会在finally中被关闭(编译器会在编译后改变代码)–try( IO操作 ){ }catch{ }
finally—必要执行,finally中的return会覆盖之前的return.-----常见面试题 请分别说明:final,finally,finalize-----常见面试题 1) final在类中不能被继承,在常量里面不能被改变,在方法里面不能被重写. 2) finally是try{}catch{}finally{}中一块,无论try是否出现异常,都会执行finally中的代码 3) finalize:方法是从Object中定义的方法,行当一个对象即将被GC释放时,GC会调用该方法,调用后即被GC释放,通常不会重写这个方法,若需要在一个对象被释放之前做某些操作,可以重写该方法.但是.该方法不应当包含耗时的操作,否则会印象GC会收操作
throw关键字用于将一个异常抛—通常遇到以下情况,主动在一个方法中抛出异常:
1.程序运行时,出现了异常,但是该异常不应该在当前方法中被解决时,可以抛出给调用者异常.
2.程序运行时出现了不符合业务逻辑的情况时,可以主动实例化一个异常抛出给调用者告知其这样的调用不合理.
当我们调用一个含有throws 声明异常的方法时,编译器要求我们必须处理该异常,否则编译不通过,—处理异常的手段:
1.使用try-catch捕获该方法throws声明抛出的异常
2.在当前方法上继续使用throws声明该异常的抛出
重写含有throws声明异常,抛出的方法时,对throws的重写准则:
- 可以仅抛出超类的部分异常
- 允许不在抛出任何异常
- 不允许抛出额外异常
- 不能抛出异常的父类异常
通常一个方法中通过throw抛出什么异常,就要在方法声明的同时使用throws声明该异常的抛出.当一个方法声明了抛出异常后,那么当调用该方法时编译器会要求调用的代码片段必须处理该异常. 注:只要方法中抛出RuntimeException及其子类型异常时,可以不在方法中声明使用throws声明该类异常的抛出.
自定义异常—通常自定义异常是,用来说明业务逻辑错误.—都要继承Exception异常—自定义异常都要(定义版本号,重写构造方法)
9.多线程—多线程允许我们"同时"执行多段代码.
- 实际上多线程是并发运行的,每段代码都是走走停停的.CPU会在这些线程减快速切换,保证每段代码都有进度,从而感官上是同时运行.
- 线程的创建3种模式:
第1种:定义一个线程类并继承线程的Thread,然后重写其run方法,run方法是用来定义线程要执行的代码.
Thread t1 = new MyThread2();—class MyThread1 extends Thread{-public void run(){}-}
t1.start();
第一种方法的不足之处
由于java是单线程,这导致若继承了Thread则无法再继承其他类,这会导致无法继承其他类来复用代码,实际开发不方便.
由于我们在线程内部直接重写run方法定义了线程要执行的任务,这导致该线程只能执行该任务,使得线程与任务存在一个必然的耦合关系,复用性差.
第2种:实现Runnable接口单独定义线程任务
Runnable r1 = new MyRunnable2();—class MyRunnable1 implements Runnable{-public void run(){}-}
Thread t1 = new Thread(r1);
t1.start();
第3种:使用ExecutorService/Callable/Future - 使用匿名内部类的方式完成线程的创建
//方式一:匿名内部类
Thread t1 = new Thread(){
public void run(){
for(int i = 1; i < 100; i++){
System.out.println(123);
}
}
};
t1.start();
//方式一:匿名内部类
Runnable r1 = new Runnable(){
public void run(){
for(int i = 1; i < 100; i++){
System.out.println(456);
}
}
};
Thread t3 = new Thread(r1);
t3.start(); - 多线程的四种状态:
产生(new):线程对象已经产生,但尚未被启动,所以无法执行.
就绪状态(Runnable):每个支持多线程
运行状态(Running)
阻塞状态(Blocked)
死亡状态(Dead) - 什么是线程,什么是进程
进程—
进程是操作系统中运行的一个任务(一个应用程序运行在一个进程中)
进程是一块包含了某些资源的内存区域,操作系统利用进程把它的工作划分为一些功能的单元.
进程中所包含的一个或多个执行单元称为线程(Thread),进程还拥有一个私有的虚拟地址空间,该空间仅被它所包含的线程访问.
线程只能归属于一个进程并且它只能访问该进程所拥有的资源,当操作系统创建一个进程后,该进程会自动申请一个名为主线程或首要线程的线程.
线程—是进程中的一个实体,是被系统独立调度和分派的基本单位,线程自己不拥有系统资源,只拥有一点在运行中必不可少的资源,但它可与同属一个进程的其它线程共享进程所拥有的全部资源。一个线程可以创建和撤消另一个线程,同一进程中的多个线程之间可以并发执行。线程有就绪、阻塞和运行三种基本状态。
一个线程是进程的一个顺序执行流.
同类的多个线程共享一块内存空间和一组系统资源,线程本身有一个程序执行的时间堆栈.线程在切换时负荷小,因此线程也被成为轻负荷进程.一个进程中可以包含多个线程.
线程是轻量级的进程
线程没有独立的地址空间(内存空间)
线程是由进程创建的(寄生在进程)
一个进程可以拥有多个线程–>这就是我们常说的多线程编程 - 线程与进程的区别
一个进程至少有一个普通的线程
线程不能独立运行
子进程和父进程有不同的代码和数据空间,而多个线程则共享数据空间
地址空间和其他资源:进程间相互独立,同一进程的个线程间共享,某进程内的线程在其他进程不可见
通信:进程间通信IPC,线程间可以直接读写进程数据段来进行通信—需要进程同步和互斥手段的辅助,以保证数据的一致性.
调度和切换:线程上下文切换比进程上下文切换要快的多
在多线程OS中,进程不是一个可执行的实体. - 线程提供了一个静态方法:static Thread currentThread()—该方法可以获取运行这个方法的线程
- setPriority(Thread.MAX_PRIORITY)—线程的优先级, 线程启动后便纳入线程调度中统一管理. 想成无法主动获取CPU时间片,何时获取完全听线程调度统一管理, 调节线程的优先级可以最大程度该善获取CPU时间片的几率.
- Thread.sleep(long ms)—该方法可以让运行这个方法的线程 就进入阻塞状态指定毫秒.超时候线程会自动回到RUNNABLE状态,等待再次获取时间片并发运行.
- Sleep()方法要求处理中断异常InterruptedException, 线程有一个方法:interrupt(),该方法时用来中断线程的,当一个线程调用sleep方法处于阻塞状态的过程中,其中断方法被调用,则这时sleep方法会立即抛出中断异常,提示我们该线程的阻塞状态被中断.
JDK1.8之前,有一个要求:当一个方法中局部内部类中若引用了这个方法的其他局部变量,那么该变量必须是final的. - setDaemon(true)—设置守护线程, 使用上与普通线程每什么区别,但是在结束时机上有一点不同:进程的退出,当一个进程退出时,所有的守护线程会被强制终止,进程的退出:当一个进程中所有普通线程结束时,进程退出,守护线程需要单独进行设置,因为默认创建出来的线程都是普通线程.
- 线程提供了一个join()方法,可以协调线程之间的同步运行.
同步运行:运行有先后顺序
异步运行:运行代码各干各的(多线程就是异步运行的) - 线程信息的相关方法
getId()–(long类型)–线程的唯一标识
getName()–(String类型)–线程的名称
getPriority()–(int类型)–线程的优先级
getState()–(类型)–线程的状态
isAlive()–(boolean类型)–线程是否活着
isDaemon()–(boolean类型)–线程是否为守护线程
isInterrupted()–(boolean类型)–线程是否被中断 - 当多线程访问统一临界资源时,由于线程切换的不确定性导致操作顺序出现了混乱,未按照程序预想的程序流程执行而导致一系列问题,严重时可能呆滞系统瘫痪.
当一个方法被synchronized修饰后,那么该方法称为"同步方法",即:多个线程不能同时进入方法内部执行,这样保证了多个线程执行该方法时右异步执行强制变为了同步执行(各干各的变为排队进行),从而解决了多线程"抢"的问题, 在方法上使用synchronized,那么同步监视器对象为当前方法所属对象:this - synchronized(同步监视器对象){ 需要同步运行的代码片段 }—同步块可以精准的控制需要同步运行的代码块,有效缩小同步范围,可以在保证并发安全的前提下提高并发的效率, 同步块需要指定同步监事对象,即:"(this)“中的内容该对象可以是java中任何类型的实例,但是必须保证多个线程看到的该对象是"同一个”,否则该同步块达不到同步效果.
- 静态方法上若使用synchronized修饰后,那么该方法一定具有同步效果, 静态方法的同步监视器对象为当前类的类对象(class类的实例)java中每个被JVM加载的类都有且只有唯一的一个class实例与之对应
- 互斥锁—当使用synchronized锁定多个代码片段,并且这些同步块使用的同步监视器对象(上锁的对象)是同一个时,那么这些代码片段之间就是互斥的,多个线程不能同时执行他们.
- 线程池—主要解决俩个问题
调用线程
控制线程数量
注意事项:
频繁创建销毁线程会给系统带来不必要的开销,所以线程尽量去重复使用
当系统中并发运行的线程数量过多时,会导致CPU过度切换,导致每个线程运行几率下降,导致整体并发性能下降,并且线程数量过多占用的资源也会更多, 因此我们要控制线程的数量
创建线程池—设置线程池里可以同时进行的线程数量
ExecutorService threadPool = Executors.newFixedThreadPool(2);
- threadPool.execute(runn); //指派任务给线程池
- threadPool.shutdownNow()—无论线程执行到哪,都关闭线程池
- threadPool.shutdown()—线程池里面的线程没有任务时,关闭线程池
10.集合框架—存放的是引用
java.util.Collection—是所有集合的*接口,里面规定了所有集合都必须具备的方法.
集合与数组一样都用来保存一组元素,但是集合提供了操作元素的相关方法,使用更方便.
Collection下面有2个常见的接口:
java.util.List—可重复集合
java.util.Set—不可重复集合
重复指的是元素是否可以重复,重复元素的判断依据,是靠元素自身的equals比较的结果
- Collection arr = new ArrayList();
- add(E e)–(boolean类型)–向当前集合中添加给定的元素,成功添加后返回true
- size()–(int类型)–返回当前集合元素的个数
- isEmpty()–(boolean类型)–判断集合是否为空集合(不含有任何元素)
- remove()–(boolean类型)–删除集合中某个元素
- clear()–(void类型)–清空集合元素
- contains(Object o)–(boolean类型)–判断当前集合是否包含给定元素,判断依据是依靠元素自身equals比较的结果
- addAll()----添加元素,把指定的集合赋值给当前集合
- containsAll()–(boolean类型)–全包含:判断当前集合是否包含给定集合的所有元素
- removeAll()–(boolean类型)–删除俩个集合的交集
- toArray()----可以将当前集合转换为数组,任何集合都可以转换为数组
Integer[] array = list.toArray( new Integer[list.size()] ); - iterator()–(Iterator类型)–遍历集合元素采用迭代器模式–该方法可以获取一个用来遍历当前集合元素的迭代器,java.util.Iterator接口–迭代器接口规定了所有迭代器遍历集合元素的相关操作,每个集合都提供了一个用于遍历自身元素的迭代器实现类,而我们无需记住名字,只当他们为Iterator去使用, 迭代器遍历集合遵循:问,取,删,的顺序遍历,其中删除不是必须的:
问: hasNext()–(boolean类型)–判断集合是否还有下一个元素
取: next()–(E类型,E代表的是集合的类型(总指:Object) )–获取集合下一个元素
迭代器在遍历时,不允许通过集合自身增删元素,否则会抛出错误
迭代器的remove删除的是通过next取出的元素
Iterator iterator = collection.iterator();
while(iterator.hasNext()){
String str = iterator.next();
if("#".equals(str)){
iterator.remove();
}
System.out.println(str);
} - JDK5 之后推出了一个特性:增强型for循环,也称新循环,用来遍历集合或数组
for(Object o:collection){ Object指定的是集合类型<>
String arr = (String)o;
System.out.println(arr);
}
新循环特性是java编译器认可,而非虚拟机认可,编译器在编译源代码时.若发现使用新循环遍历数组时,会将代码改为普通的for循环遍历,若发现使用新循环遍历集合,会改为迭代器遍历,因此注意:使用新循环遍历集合时,不要通过集合的方法增删元素. - 泛型:JDK5退出的一个特性<>,泛型也称为参数化类型,允许我们使用一个类时,指定其属性,方法参数,方法返回值的类型,使得该类的使用更符合需求,更灵活.
泛型是编译器认可,而非虚拟机认可,所有泛型定义的地方发最终会被编译器改为Object.
下面有泛型定义的地方会改为:
private Object X
public Type(Object x,Object y);
public setX(Object x)
public Object getX()
所有泛型的原型就是Object
使用泛型时,编译器会做俩件事:
1.在给泛型复制时,编译器会检查该值类型是否符合泛型要求.如:
t1.setX(2)
对于t1 而言,由于泛型指定为Integer,因此编译器会检查setX()传递的参数是否为整数,不是则编译不通过.
2.在获取泛型的值时,编译器会添加向下造型的代码将其转换为指定类型.
int x1 = t1.getX();
实际编译后为:
int x1 = (Integer)t1.getX()
还有自动拆装箱
int x1 = ( (Integer)t1.getX() ).intValue();
泛型在使用时可以不指定,若不指定则默认为原型Object,但是通常有泛型的地方发都应当指定泛型的实际类型
泛型在集合中使用广泛,而泛型在集合中用来约束集合的元素类型
10.1)集合List接口 - List接口是Collection的一个常用子接口,表示可重复集,并且该集合有序,特点是可以通过下标操作元素,java.util.List常见实现类: List list=new ArrayList();
- java.util.ArrayList----数组实现,查询性能好
- java.util.LinkedList----链表是实现,增删元素性能好,尤其首尾增删元素效率最佳.
- 对性能没有极端苛刻要求时,通常用ArrayList
- get(int index)–(E类型)–获取指定下标对应的元素
- set(int index,E e)–(E类型)–更换list中指定位置的元素,返回值为原位置对应的元素.
- Indexof(Object o)—(int类型)–获取下标,适用于集合
- add(int index,E e)----将给定的元素插入到指定位置
- remove(int index)–(E类型)–删除指定位置的元素,返回值是删除的元素
- subList(int start,int end)–(List类型)–List集合支持截取子集合(含头不含尾)
- ----修改子集合,父集合也相对修改
- toArray()----可以将当前集合转换为数组,任何集合都可以转换为数组
i. Integer[] array = list.toArray( new Integer[list.size()] ); - Arrays.asList(数组)–(返回一个集合)–可以将数组转换为一个List集合–对转换来的集合操作,就是对数组操作(从数组转换来的集合,不支持增删元素).
i. 数组转换集合—Arrays提供的方法
ii. 要想对这个集合做增删操作----可以自行创造一个集合来操作元素
iii. 所有集合都支持参数为集合的构造方法,作用是创造当前集合的同时包含给定集合的所有值
iv. List list2 = new ArrayList(list); - Collections.sort(list集合)----可以对List集合进行自然排序,从小到大,
i. ----集合的工具类java.util.Collections提供了一个静态方法 - Collections.sort(list)— 在排序集合时,要求集合元素必须实现Comparable接口并重写其中定义的比较方法,否则编译不通过,java API中常见数据类型都实现了该接口,如:包装类和字符串,— 但是我们自定义的元素通常不要去实现该接口,因为当我们使用某个方法时,该方法要求我们为其修改其他额外的代码是,这个方法就具有侵略性,这样的方法对程序结构不利,尽量避免.
- Collections.sort(List,Comparator)----重载的该方法要求我们传入要排序的集合外.在额外传入一个比较器, 该比较器是用来,为集合元素定义的一种比较大小的规则,这样sort()方法就会利用给定的比较器的比较规则对集合元素比较大小,后进行自然排序—就不再要求集合元素必须去实现接口Comparable,
i. —实现Comparator接口后,要求必须重写方法:compare()----该方法用来定义o1,o2的大小关系, 返回值为int型,该值不关心具体取值,值关注取值范围
ii. 当返回值>0 : 表示o1>o2;
iii. 当返回值<0 : 表示o1<o2;
iv. 当返回值=0 : 表示o1=o2;
v. o1各项平方相加,o2各项平方相加—做比较
- Collections.sort(list,new Comparator(){
a) public int compare(Point o1, Point o2) {
i. int olen1 = o1.getX()*o1.getX()+o1.getY()*o1.getY();
ii. int olen2 = o2.getX()*o2.getX()+o2.getY()*o2.getY();
iii. return olen1 - olen2;
b) } - });
- 排序字符串–String已经实现了Comparable接口,但有时该比较规则不符合我们的排序要求,这时也可以使用比较器来提供额外的比较规则并进行排序
10.2)队列java.util.Queue接口 - 该接口是队列接口,规定了队列具有的相关方法,Queue接口继承自Collection,所以同时具备集合的相关方法. 队列可以保存一组元素,但是存取元素必须遵循先进先出原则—常用实现类:java.util.LinkedList
Queue queue = new LinkedList(); - offer为入队操作,将元素添加到队列末尾
- poll()–(E类型)–出队操作,获取队首元素,获取后该元素即从队列中被删除
- peek()–(E类型)–引用队列首元素,获取后该元素依然在队列中
- poll()----使用该方法遍历队列,队列就没有元素了
- size()–(int类型)–查询队列的大小
- 使用迭代器遍历不影响队列元素
10.3)双端队列java.util.Deque接口 - 该接口继承自Queue双端队列是俩端都可以做出入队操作的队列—常用实现类:java.util.LinkedList
Deque deque = new LinkedList(); - offer为入队操作,将元素添加到队列末尾
- offerFirst()----位队列添加第一个位置上的元素
- offerLast()----位队列添加最后的位置上的元素
- pollFirst()----删除队列第一个元素
- pollLast()----删除队列最后一个元素
10.4)栈 - 双端队列若是只从同一端进出队操作,就形成了栈结构,栈结构存取元素遵循先进后出原则, 通常使用栈是完成"后退"的操作
- push()----为入栈操作,将元素依次向里添加
- pop()–(E类型)–为出栈操作,先进后出
10.5)线程安全 - java.util.Collections提供的一组静态方法,可以将给定集合转换为线程安全的
- 常用的集合实现类:ArrayList,LinkedList,HashSet都不是线程安全的,如果存在并发访问时,要将这些转换
- API手册有说明:一个线程安全的集合也不与迭代器遍历集合的操作互斥,所以若多线程"同时"操作集合存在遍历和增删元素时,需要自行维护他们之间的互斥关系,可参与聊天室Server端操作
- Collections.synchronizedList(arrayList)----将给定的List集合转换为线程安全
- Collections.synchronizedSet(hashSet)— 将给定的set集合转换为线程安全
- Collections.synchronizedList(linkedList)— 将给定的List集合转换为线程安全
- wait方法和sleep方法的区别:wait方法是不占用线程方法,需要其他线程去唤醒该线程,sleep方法时占用线程的,不需要外部线程去唤醒
10.6)集合Map<K,V>
- java,util.Map 查找表,map的结构是一个多行俩列的表格,其中左列成为key,右列成为value—map总是以key-value对的形式保存数据,并且总是以key来获取对应的value
Map<String,Integer> map = new HashMap<String,Integer>(); - java.util.HashMap 散列表,hashMap是Map最常见的实现类,演示当今最快的查询结构!!!
- Java.util.LinkedHashMap—是可以做到遍历顺序与普通时的顺序一样
- put(K k,V v)—将给定的键值对保存Map中
Map有一个要求,即Map中key不允许重复,重复的标准是依靠Key自身的equals比较结果.
所以put方法时右返回值的,若本次存放的key已经在Map中,则是替换value操作,那么返回值就是被替换的value否则为null,这时若拆箱会赵成空指针异常 - get(Object Key)----根据给定的Key获取对应的value,若给定的Key在Map中不存在,则返回值为null
- size()–获取Map的键值对数
- remove(K,k)–根据给定个Key值删除对应的这组键对,返回值为该组建值对的value,删除没有的,返回null
- containsKey(Object k)–(boolean)–判断当前Map是否包含给定的Key
containsValue(Object v)–(boolean)–判断当前Map是否包含给定的Value
包含的判断还是依靠元素自身的equals比较的结果 - keySet()–(返回Set集合)–将当前Map中所有的Key以一个Set集合形式返回,遍历该集合,就等同于遍历了所有的Key
Set keySet = map.keySet(); - values()–(返回Collection集合)–将当前Map中所有的Value以一个Collection集合形式返回,遍历该集合,就等同于遍历了所有的Value,由于map中不要求重复,所以不以Set形式存在----相对不常用
Collection values = map.values(); - entrySet()–(返回Set< Entry<Key,Value> >集合)–将当前Map中每组键值对(若干的Entry实例)以一个Set集合形式返回
java.util.Map.Entry----每个Entry实例表示Map中的一组键值对,常用方法:
getKey()----获取其表示的键值对中的Key
getValue()----获取其表示兼职对中的Value
Set<Entry<String,Integer>> entrySet = map.entrySet();
hashMap是当今查询速度最快的数据结构,但是作为Key元素的HashCode方法和equals方法的实现如果不妥当,就会降低散列表的查询性能.
在HashMap中出现链表就会影响其查询性能,而出现列表的一个主要原因为:
当俩个Key的HashCode(hashCode方法返回的数字)值相同,即(HashCode决定该元素在HashMap内部数组的下标位置),
但是他们equals比较部位true(equals方法决定HashMap是否认为这俩个Key为重复的)时,则会在HashMap内部形成链表
hashCode方法与equals方法时Object定义的方法,这俩个方法在API手册的Object类中有明确的说明:当我们需要重写一个类的equals或hashCode方法时要遵循下面几点要求:
1.成对重写,当我们重写一个类的equals方法时就应该连同重写hashCode,反过来也一样.
2.一致型,当俩个对象equals比较为true时,hashCode方法返回的数字必须相同,反过来则不是必须的,但是也尽量保证俩个对象hashcode相同时equals比较也为true
3.稳定性,当一个对象参与equals比较的属性值没有发生过关系的前提下,多次调用hashcode方法返回的数字应当不变
Map四种的遍历方式
// 第一种:
/*
- Set set = map.keySet(); //得到所有key的集合
- for (Integer in : set) { String str = map.get(in);
- System.out.println(in + " " + str);
- }
*/
System.out.println(“第一种:通过Map.keySet遍历key和value:”);
for (Integer in : map.keySet()) {
//map.keySet()返回的是所有key的值
String str = map.get(in);//得到每个key多对用value的值
System.out.println(in + " " + str);
}
// 第二种:
System.out.println(“第二种:通过Map.entrySet使用iterator遍历key和value:”);
Iterator<Map.Entry<Integer, String>> it = map.entrySet().iterator();
while (it.hasNext()) {
Map.Entry<Integer, String> entry = it.next();
System.out.println("key= " + entry.getKey() + " and value= " + entry.getValue()
);
// 第三种:推荐,尤其是容量大时
System.out.println(“第三种:通过Map.entrySet遍历key和value”);
for (Map.Entry<Integer, String> entry : map.entrySet()) {
//Map.entry<Integer,String> 映射项(键-值对) 有几个方法:用上面的名字entry
//entry.getKey() ;entry.getValue(); entry.setValue();
//map.entrySet() 返回此映射中包含的映射关系的 Set视图。
System.out.println("key= " + entry.getKey() + " and value= " + entry.getValue());
}
// 第四种:
System.out.println(“第四种:通过Map.values()遍历所有的value,但不能遍历key”);
for (String v : map.values()) {
System.out.println("value= " + v);
}
10.XML—使用dom4j解析XML文档
创建—
SAXReader sax = new SAXReader();
Document doc = sax.read(new File(“emplist.xml”));
Docuement提供了获取跟元素的方法:
getRootElement()–(Element)–的每一个实例用于表示XML文档中的一个元素,即一对标签
Element提供了很多获取其表示的元素相关信息的方法:
- getName()–(String类型)–获取当前元素的名字,即标签名
- getText()–(String类型)–获取当前元素中间的文本,如这里的文本
- element(String name)–(Element类型)–获取当前元素下指定名字的子元素
- elements()–(List类型)–获取当前元素下所有字元素
- elements(String name)–(List类型)–获取当前元素下所有同名字元素
- attribute(String name)–()–获取属性—getName(),getValue()
- attributeValue()–(String类型)—获取标签下的属性
- Document提供了一些方法的合并
11.反射:
反射是java中的动态机制,它允许我们实例化对象,调用方法或属性从原来的编码确定转为在程序运行期决定,这大大的增加了程序的灵活性.
反射提高了灵活度,但是会降低性能,因此适度使用
Class cls = Class.forName(name);
- cls.getName()----获取类名
- cls.getMethods()—获取所有方法(包括从超类继承的方法)
Method[] getMethods = cls.getMethods();
for(Method method : getMethods){
System.out.println(method.getName());
} - cls.getDeclaredMethods()—获取当前类自己定义的方法(不包含从超类继承的方法)
Method[] getDeclaredMethods = cls.getDeclaredMethods();
for(Method method : getDeclaredMethods){
System.out.println(method.getName());
} - cls.newInstance()—实例化(该方法要求类必须有无参构造方法)
bject obj = cls.newInstance(); - cls.getDeclaredMethod(name)—调用方法
Method method = cls.getDeclaredMethod(str);
Method method = cls.getDeclaredMethod(“say”,String.class,int.class); - invoke()—method:表示方法,obj:表示实例对象(相当于new出来的对象(new Person() ))
method.invoke(obj,“123”,23); - JDK5之后推出了一个特性:可变长参数
public static void dosome(String… a){—写什么类型,此类型可以写多个(也可以写不同类型的超类)
System.out.println(Arrays.toString(a));
} - Lkl
12.时间Date
java.util.Date----Date的每一个实例用于表示一个具体时间,内部维护一个Long值,表示自1970年1月1日 00:00:00到表示时间之间所经过的毫秒.
由于Date存在时间问题和千年虫问题,因此大部分操作时间的方法都被声明为过时的(不建议使用)
Date date = new Date();
- date.getTime()—把Date获取的时间转换为毫秒值
- date.setTime()—使用给定毫秒时间值设置现有Date对象
java.util.SimpleDateFormat—可以在String与Date之间相互转换
SimpleDateFormat sdf = new SimpleDateFormat(“yyyy-MM-dd a HH:mm:ss”); - format()–(String类型)–将给定的日期对象按照指定的日期格式转换为字符串
- parse()----将字符串按照指定的日期格式转换为Date
java.util.Calendar—日历类,本身是个抽象类,规定了日历类的功能定义,常规实现类:GregorianCalendar—日历
Calendar cal = Calendar.getInstance(); - getTime()----以一个Date实例形式返回当前Calendar表示的日期(把cal格式转换为date格式)
- setTime()----调整当前Calendar,表示规定Date所表示的日期(把date格式转换为cal格式)
- cal.get(int field)–(int类型)–int值表示时间分量, 获取指定时间分量
Calendar.YEAR—获取的年
Calendar.MONTH—获取的月(从0 开始计算,即0表示1月)
获取日—和天相关的常量:(field)
Calendar.DAY_OF_WEEK—周中的天(按照外国人计算,周日为一周的第一天)
Calendar.DAY_OF_MONTH—月中的天
Calendar.DAY_OF_YEAR—年中的天
Calendar.DATE—与DAY_OF_MONTH一致
获取小时—的常量: (field)
Calendar.HOUR_OF_DAY----24小时制
Calendar.HOUR----12小时制 - cal.getActualMaximum(int field)----获取指定时间分量所允许的最大值,该日期内最大值
- cal.set(int field, int value)----将给定的日历字段设置为给定值。
Set()方法有一个主意事项:
当set设置了俩次,而俩次影响的时间分量一致时,比如设置几号后,又设置了周几,这俩个看似不同的时间分量实际影响的都是月中的天.那么后设置的会覆盖前设置的操作,若想都启作用,可以在设置一个后调用getTime()方法,计算一下修改后的日期,然后在设置另一个 - add(int field,int amount)----对指定时间分量加上给定的值,若给定的值为负数则是减去
(周一到周日)—Monday, Tuesday, Wednesday, Thursday, Friday, Saturday, Sunday
(一月到十二月)—January, February, march, April, may, June, July, August, September, October, November December,
13.lambde表达式 JDK8 之后推出的特性
可以用更简洁的语法定义匿名内部类
使用lambde表达式创建匿名内部类时,要求实现的接口必须只能有一个方法,否则不可以使用
([实参列表])->{ 如果方法体只有一句代码,那么"{}"可以不写
方法体
}
无参数的效果如下:
Runnable r1 = new Runnable(){
public void run(){
System.out.println(“123456789”);
}
};
Runnable r2 = () ->{
System.out.println(“123456789”);
};
有参数的效果如下:
Comparator com1 = new Comparator() {
public int compare(String o1, String o2) {
return o1.length()-o2.length();
}
};
Comparator com2 = (o1, o2) -> {
return o1.length()-o2.length();
};
Comparator com3 = (o1, o2) -> o1.length()-o2.length();
14.进制问题
- 16进制 用于缩写2进制,2进制 数字从低位开始没4位2进制,可以缩写移位16进制
1,2,3,4,5,6,7,8,9,a,b,c,d,e,f - 补码
计算机中用于处理有符号数的一种编码规则,其核心思想是将固定为数的2进制数分一半作为负数,以4位补码为例研究补码的编码规则 - 2进制计算
与(&),或(|),反(~),移位(>>>,>>,<<)
与(&):
0 & 0 -> 0
0 & 1 -> 0
1 & 0 -> 0
1 & 1 -> 1
计算过程:将俩个2进制数对齐位数,上下对应的位置计算”与”
n = 01101001 11110101 01011111 00101001
m = 00000000 00000000 00000000 11111111
k=n&m 00000000 00000000 00000000 00101001
上一篇: SpringMVC 实现用户登录实例代码
下一篇: PHP文件操作详解