JAVA IO流及序列化

在这里插入图片描述
在这里插入图片描述
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

注意点:

  1. 反序列化顺序和序列化顺序一致
  2. 如果序列化的类中的属性是引用类型, 引用的类需要也实现Serializable接口
  3. 对同一个对象的多次序列化,只会序列化第一次, 后续只会记录序列化编码号, 哪怕第一次序列化后,改变了实例属性, 第二次序列化也不会记录改动
  4. static属性不会被序列化(反序列化后会到内存中查询static属性的最新值), transient关键字修饰的属性不会被序列化(被transient修饰的属性,反序列化后是默认值。对于引用类型,值是null;基本类型,值是0;boolean类型,值是false),方法/构造器不会被序列化. 只有普通属性会被序列化
  5. 通过重写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, 如果在反序列化前, 类有任何变动, 都会报错

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值