BIO、NIO、AIO的区别
BIO(同步阻塞式IO) 就是传统的 java.io 包(上面内容),它是基于流模型实现的,交互的方式是同步、阻塞方式,也就是说在读入输入流或者输出流时,在读写动作完成之前,线程会一直阻塞在那里,它们之间的调用时可靠的线性顺序。它的有点就是代码比较简单、直观;缺点就是 IO 的效率和扩展性很低,容易成为应用性能瓶颈。
(一个应用独享一个socket连接)
NIO(同步非阻塞式IO) 是 Java 1.4 引入的 java.nio 包,提供了 Channel、Selector、Buffer 等新的抽象,可以构建多路复用的、同步非阻塞 IO 程序,同时提供了更接近操作系统底层高性能的数据操作方式。
(多个应用共享一个socket连接, 一个socket连接中有多个channel, 每个应用一个channel, 又seletor轮询, 哪个channel的数据ready,就调用哪个继续后续处理)
AIO(异步非阻塞式IO)是 Java 1.7 之后引入的包,是 NIO 的升级版本,提供了异步非堵塞的 IO 操作方式,所以人们叫它 AIO(Asynchronous IO),异步 IO 是基于事件和回调机制实现的,也就是应用操作之后会直接返回,不会堵塞在那里,当后台处理完成,操作系统会通知相应的线程进行后续的操作。
(应用程序向操作系统注册IO监听,然后继续做自己的事情。操作系统发生IO事件,并且准备好数据后,在主动通知应用程序,触发相应的函数(这就是一种以订阅者模式进行的改造)。由于应用程序不是“轮询”方式而是订阅-通知方式,所以不再需要selector轮询,由channel通道直接到操作系统注册监听。)
同步和异步的区别: 同步需要保持连接
阻塞和非阻塞的区别: 阻塞式等待时不能做其他事, 非阻塞等待时可以坐其他事
JAVA序列化
序列化机制允许将实现序列化的Java对象转换位字节序列,这些字节序列可以保存在磁盘上,或通过网络传输,以达到以后恢复成原来的对象。
这个类应该实现Serializable接口或者Externalizable接口之一。
1. Serializable
注意点:
- 反序列化顺序和序列化顺序一致
- 如果序列化的类中的属性是引用类型, 引用的类需要也实现Serializable接口
- 对同一个对象的多次序列化,只会序列化第一次, 后续只会记录序列化编码号, 哪怕第一次序列化后,改变了实例属性, 第二次序列化也不会记录改动
- static属性不会被序列化(反序列化后会到内存中查询static属性的最新值), transient关键字修饰的属性不会被序列化(被transient修饰的属性,反序列化后是默认值。对于引用类型,值是null;基本类型,值是0;boolean类型,值是false),方法/构造器不会被序列化. 只有普通属性会被序列化
- 通过重写writeObject与readObject方法,可以自己选择哪些属性需要序列化, 哪些属性不需要。如果writeObject使用某种规则序列化,则相应的readObject需要相反的规则反序列化,以便能正确反序列化出对象。这里展示对名字进行反转加密。
package javatest;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import javax.annotation.Resource;
class Clock implements Serializable {
/**
*
*/
private static final long serialVersionUID = -7139810225881431803L;
private int size;
private String brand;
private double price;
//transient关键字修饰的属性,序列化时不被序列化,反序列化时,自动设置为默认值(对于引用类型,值是null;基本类型,值是0;boolean类型,值是false。)
private transient boolean sold;
public Clock(int size, String brand){
this.size = size;
this.brand = brand;
}
public int getSize() {
return size;
}
public void setSize(int size) {
this.size = size;
}
public String getBrand() {
return brand;
}
public void setBrand(String brand) {
this.brand = brand;
}
@Override
public String toString() {
return "Clock{" + "size='" + size + '\'' + ", brand=" + brand + " price=" + price + " sold=" + sold +'}';
}
public double getPrice() {
return price;
}
public void setPrice(double price) {
this.price = price;
}
public boolean isSold() {
return sold;
}
public void setSold(boolean sold) {
this.sold = sold;
}
}
public class Xuliehua {
public static void main(String[] args) throws FileNotFoundException, IOException, ClassNotFoundException {
// TODO Auto-generated method stub
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("object.txt"));
Clock c1 = new Clock(10,"tianqing");
c1.setPrice(528.0);
c1.setSold(true);
System.out.println(c1);
Clock c2 = new Clock(20,"IWC");
oos.writeObject(c1);
oos.writeObject(c2);
//修改c1属性后,再次序列化
c1.setBrand("tissot");
oos.writeObject(c1);
oos.close();
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("object.txt"));
Clock c3 = (Clock)ois.readObject();
Clock c4 = (Clock)ois.readObject();
Clock c5 = (Clock)ois.readObject();
//多次序列化不同对象, 依次读出就行了(反序列化的顺序与序列化时的顺序一致)
System.out.println(c3);
System.out.println(c4);
//如果对象已经被序列化,后续对同一个对象的序列化,不会真正重新序列化,只会直接储存第一个序列化时的序列化编码
//就算改变了对象的属性,序列化编码也不变,所以取出来的对象值还是没变(c5=c3)
System.out.println(c5);
ois.close();
}
}
2. Externalizable
通过实现Externalizable接口,必须重写writeExternal、readExternal方法。(自动以序列化方法)
3. 序列化版本号serialVersionUID
private static final long serialVersionUID = 1L;
反序列化必须拥有class文件,但随着项目的升级,class文件也会升级,序列化怎么保证升级前后的兼容性呢?
java序列化提供了一个private static final long serialVersionUID 的序列化版本号,只有版本号相同,即使更改了序列化属性,对象也可以正确被反序列化回来。
如果反序列化使用的class的版本号与序列化时使用的不一致,反序列化会报InvalidClassException异常。
序列化版本号可自由指定,如果不指定,JVM会根据类信息自己计算一个版本号,这样随着class的升级,就无法正确反序列化;不指定版本号另一个明显隐患是,不利于jvm间的移植,可能class文件没有更改,但不同jvm可能计算的规则不一样,这样也会导致无法反序列化。
什么情况下需要修改serialVersionUID呢? (手动设定UID的情况下)
无需修改版本号:
如果只是修改了方法,反序列化不受影响
如果只是修改了静态变量,瞬态变量(transient修饰的变量)
可以修改也可以不修改:
如果只是新增了实例变量,则反序列化回来新增的是默认值;
如果减少了实例变量,反序列化时会忽略掉减少的实例变量。
如果修改了非瞬态变量名字,会被反序列化认为: 原始变量被删除, 新增了一个变量, 结果和上面2条一致
必须修改(手动在类中改变序列化版本号,以通知使用反序列化一方, 类有改动):
如果修改了非瞬态变量类型,则会反序列化失败
P.S.
手动不设定UID, 系统序列化时会根据类的属性自动hash出一个UID, 如果在反序列化前, 类有任何变动, 都会报错