1.IO流
JAVA的IO流是JAVA对数据流的操作实现数据的复制和转换等的API,可以将我们平时见到的音频视频,文档图片进行复制等。按照方式分为输入流和输出流,按照类型分为字节流和字符流。
字节输入流:InputStream,字节输出流:OutputStream,这是JAVA.IO类库的两个接口,我们平时比较常用的有文件流FileInputStream,FileOutputStream,缓冲流BufferedInputStream,BufferedOutputStream,以及字符字节转换流ReaderInputStream,WriterOutputStream(可以双向转换,前面转后面,传入相应的流对象), 举例说明使用FileInputStream,FileOutputStream。

byte count = 0;
byte num[]  = new byte[1024];
InpitStream in = FileInputStream("文件地址");
OutputStream out = new FileOutputStream("文件地址");
while(count = in.read(num)!=-1){
    out.write(0,num,count);
 }

注意在使用完之后进行关流,如果使用的是缓冲流可以用flush()刷新,使用缓冲流主要是因为可以提高转换效率。
字符输入流:Reader,字符输出流:Writer,大部分是和字节流类似的实现类,包括FileReader,FileWriter,BufferedReader,BufferedWriter等,使用方法和字节流类似不再多述。

2.集合
分为单列集合和双列集合。
单列集合大体分为两个接口,List和Set,两个接口都是Collection的子接口,其中List接口的实现类,例如ArrayList,LinkedList,ventor,这类集合内的元素是按照存入顺序排列并且元素可以重复的,而Set接口下的实现类例如HashSet,LinkedHashSet,TreeSet内的元素的存储顺序不是按照存入顺序排列的,并且元素的值不可重复。造成这种情况的原因主要是这几种集合的底层存储算法不同,分开来说:
(1)ArrayList在底层的存储方式是数组,这就导致了在内存中ArrayList中的元素存在索引值,在查询集合中的元素时可以按照索引值进行查询,相应的,在向ArrayList集合中添加元素时要重新生成元素的索引值。
(2)LinkedList在底层的存储方式是链表,内存中的相邻元素通过节点进行连接,在向LinkedList增加元素时只需要断开相邻元素的节点即可,如果是向链表两侧增加元素则更快,相应的在查询链表元素时需要向前或向后遍历。
总结:对ArryList和LinkedList来讲,ArryList查询元素速度快于LinkedList,因为ArrayList可以根据索引值进行定位,而LinkedList需要向前或向后遍历链表;相应的LinkedList增删元素的速度要快于ArrayList,因为LinkedList增删元素只需要操作相邻元素的节点,而ArrayList需要在增删后重新对元素索引值进行更新。
(3)HashSet底层是Hash表,也就是说JAVA对存入HashSet的元素依据hashcode()对照hash表生成相应的hash值,所以会导致Set接口的集合存入顺序不等于最终元素的排序,而且JAVA会根据equal()判断存入的元素是否重复。
但也有例外,LinkedSet则是有序可重复的,因为在底层用链表对hash值进行了封装。
双列集合也就是映射存储,key-value形式的存储,我们常用的HashMap就是多对一或是一对一的映射,HashMap的底层是数组+链表,元素之间是按照数组进行存储的,而每个元素中的key和value是链表形式存储。HashMap依旧是无序唯一的集合。
集合容量
拿HashMap举例,默认容量是16,就是说你new HashMap()一下,这个map可以存储16个元素,但是实际情况却不是,因为我们有扩容机制,当这个默认容量为16的map存储到12时,系统会自动为他扩容,扩到多少呢,2倍,32个。
这里的12我们叫临界容量,0.75叫默认负载因子,这里的负载因子和默认容量在初始化创建集合时都可以通过构造进行设置,公式:临界容量 = 集合容量*负载因子。默认12 = 16*0.75,出发扩容条件容量翻倍。这里注意不是所有的集合都是扩容翻倍,大多数是。
遗留容器
ArrayList和ventor都是底层是数组的集合,只不过ventor是遗留容器,其内部的方法都是同步的,会影响效率。
同理HashMap和HashTable一样,都是一对一或多对一的映射,只不过HahsTable的内部方法都是同步的,会影响效率。如果要实现同步可以使用Collections里的SyList()方法。

3.反射
反射顾名思义,就是用它生成的文件去获取它原本的东西,放在JAVA中就是用.class文件去获取原本的.java文件的内容,从某些角度说这里反对了JAVA的封装思想,java文件要经过jvm的编译才能生成它认识的文件,也就是说JVM把我们写的.java文件编译成.class的字节码文件,再经过JVM加载器加载,把.class文件加载进内存,经过连接(符号引用变为直接引用),初始化(执行初始化语句),这就是基本的程序运行的过程。
了解了上述过程后就简单了,反射就是拿字节码文件对象(也就是类对象:静态变量和方法所属的对象)去获取.java文件的变量和函数的。有三种方式可以获取到字节码文件对象,Class.forName(“包路径下的地址”);类.Class;类对象.getClass()。常用第一种。
进行函数演示:
Class clazz = String.class;
Method[] declaredMethods = clazz.getDeclaredMethods();
for (Method entity:declaredMethods
) {
System.out.println(“方法传参:”+entity.getParameterTypes()+”方法名:”+entity.getName()+”方法返回值:”+entity.getReturnType());
}
这里declaredMethods会获取到String类的所有方法(包括私有方法)。按照方法名和参数列表可以获取具体的方法。
Method toLowerCase = clazz.getMethod(“toLowerCase”);
Object o = toLowerCase.invoke( “dasdad”);
getMethod(方法名,传参的类对象(没有则不写))得到Method对象,这里注意传参的顺序因为可能会有方法重载。
再调用invoke(反射的类的实例对象,具体参数)可以得到方法的返回值,
反射的类的实例对象可以通过clazz.getConstructor().newInstance()调用构造器获取。
如果方法是静态的则不用传实例对象,如我的例子所示。
Field字段获取的方式类似,调用getFields()或者getDeclaredFields()获取字段集合进行遍历获取字段名和类型后再调用getField()或者getDeclaredField()获取具体的Field对象,
再用set()设置值就完事了。
此外clazz还有一些判断方法,判断一些名称是不是类中的方法函数注解等。

4.线程
线程区别于进程,进程是操作系统对资源进行分配和操作的单元,线程是进程的基本单元,是cpu对资源进行分配和操作的单元,所以多线程可以提高程序的执行效率,但是相应的资源的消耗会增加,属于用空间换时间。
我们平常用的创建线程的方式是实现Runable接口或者Callable接口,当然也可以继承Thread类,但由于JAVA是单继承多实现的,所以能实现接口就不要用继承,重写其中的run()方法及可。
开启线程要用Thread的start()方法,可以用有参构造把实现Runable的类实例对象传入,再调用start()方法。并不是说调用了此方法线程就会立即执行。
线程状态:新建,就绪,运行,阻塞,死亡。线程调用start()方法后不会立即调用,而是进入就绪状态,当cpu调用时进入运行状态,因为cpu运行时分时分区的,线程状态就会在就绪和运行来回切换。
sleep()和wait()的区别:sleep()来自Thread类,和wait()来自Object类。调用sleep()方法的过程中,线程不会释放对象锁。而 调用 wait 方法线程会释放对象锁;
sleep()睡眠后不出让系统资源,wait让其他线程可以占用CPU;
sleep(milliseconds)需要指定一个睡眠时间,时间一到会自动唤醒.而wait()需要配合notify()或者notifyAll()使用;
线程在唤醒后会线程会重新进入等待池,当轮到它时会进入等锁池。

5.事务
Java事务的类型有三种:JDBC事务、JTA(Java Transaction API)事务、容器事务
事务的基本ACID属性:
(1)原子性:一组事务操作要不全部成功,要不全部失败;
(2)一致性:保持事务的系统一致性,事务开始时是对5000块钱处理,结束时也是要对这5000的处理;
(3)隔离性:不同事务之间的操作互不影响,否则会引起虚读;
(4)持久性:一组事务完成后不管是什么结果都会持久化。

脏读:读到其他事务未提交或回滚的数据
线程A 线程B
读取数据5000块 读取数据5000块
花1000块
剩余4000块
回滚
读取数据4000块
花500块
数据剩3500块

不可重复读:读到其他事务已提交的数据
线程A 线程B
读取数据5000块 读取数据5000块
花1000块
剩余4000块
提交
读取数据4000块

虚读:读到其他事务的操作,好像产生幻觉一样
线程A 线程B
读取数据5000块
对10000块进行-1000操作
读取数据4000块

事务的安全级别:
未提交读(READ UNCOMMITTED ):最低隔离级别,一个事务能读取到别的事务未提交的更新数据,很不安全,可能出现丢失更新、脏读、不可重复读、幻读;
提交读(READ COMMITTED):一个事务能读取到别的事务提交的更新数据,不能看到未提交的更新数据,不会出现丢失更新、脏读,但可能出现不可重复读、幻读;
可重复读(REPEATABLE READ):保证同一事务中先后执行的多次查询将返回同一结果,不受其他事务影响,不可能出现丢失更新、脏读、不可重复读,但可能出现幻读;
序列化(SERIALIZABLE):最高隔离级别,不允许事务并发执行,而必须串行化执行,最安全,不可能出现更新、脏读、不可重复读、幻读,但是效率最低。
隔离级别越高,数据库事务并发执行性能越差,能处理的操作越少。所以一般地,推荐使用REPEATABLE READ级别保证数据的读一致性。对于幻读的问题,可以通过加锁来防止。
MySQL支持这四种事务等级,默认事务隔离级别是REPEATABLE READ。Oracle数据库支持READ COMMITTED 和 SERIALIZABLE这两种事务隔离级别,所以Oracle数据库不支持脏读。Oracle数据库默认的事务隔离级别是READ COMMITTED。