对象专属流
文章目录
基本概念
序列化和反序列化的定义:
- Java序列化:指把Java对象转换为字节序列的过程
- java反序列化:指把字节序列化恢复为Java对象的过程
序列化和反序列的最重要的作用:
- 序列化:
- 在传递和保护对象时,保证对象的完整性和可传递性。对象转换有序字节流,以便在网络上传输或者保护在本地文件种
- 反序列化:
- 根据字节流中保存的对象状态及描述信息,通过反序列化重建对象
总结:核心作用就是对象状态的保存和重建(整个过程核心点就是字节流中所保存的对象那个状态及描述信息)
序列化优点:
- 将对象转为字节流存储到硬盘上,当JVM停机的时,字节流还会再硬盘上默默等待,等待下一处JVM的启动,把序列化的对象,通过反序列化为原来的对象,并且序列化的二进制序列能够减少存储空间(永久性保存对象)
- 序列化成字节流的对象可以进行网络传输(二进制形式),方便网络传输
- 通过序列化可以在进程间传递对象
代码示例
Student类:
public class Student implements Serializable {
//Java虚拟机看到Serializable接口之后,会自动生成一个序列化版本号
//这里没有手动写出来,Java虚拟机会默认提供这个序列化版本号
private String name ;
private int age ;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public Student(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public String toString() {
return "姓名:"+name+"\n"+"年龄:"+age;
}
}
【序列化】:
public class ObjectOutputStreamTest01 {
public static void main(String[] args) {
//创建java对象
Student student = new Student("佘楽",21);
ObjectOutputStream objectOutputStream = null ;
try {
//序列化
objectOutputStream = new ObjectOutputStream(new FileOutputStream("E:\\Typora笔记\\JavaSE\\IO流\\students"));
//序列化对象
objectOutputStream.writeObject(student);
//刷新
objectOutputStream.flush();
} catch (IOException e) {
e.printStackTrace();
}finally {
if (objectOutputStream!=null) {
try {
//关闭
objectOutputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
1 、报错NotSerializableException:Student对象不支持序列化
2、参与序列化和反序列化的对象,必须实现Serializable接口
3、注意通过源代码发现,Serializable接口只是一个标志接口:
public interface Serializable{ }
这个接口当中什么代码都没有
4、那么它起到什么作用呢?
起到标识的作用,标志的作用,Java虚拟机看到这个类实现了这个接口,可能会对这个类进行特殊待遇,Serializable这个标志接口是Java虚拟机参考的,Java虚拟机看到这个接口之后,会为该类自动生成一个序列化版本号
【反序列化】:
public class ObjectInputStreamTest01 {
public static void main(String[] args) {
ObjectInputStream objectInputStream = null ;
try {
objectInputStream = new ObjectInputStream(new FileInputStream("E:\\Typora笔记\\JavaSE\\IO流\\students"));
//反序列化,读
Object obj = objectInputStream.readObject();
//反序列化回来是一个学生对象,所以会调用学生对象的toString()方法
System.out.println(obj);
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
} finally {
if (objectInputStream!=null) {
try {
objectInputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
一次序列化多个对象呢?
可以,可以将对象做到集合当中,序列化集合
提示:
参与序列化的ArrayList集合以及集合中的元素Student都需要实现java.io.Serializable接口
【一次序列化多个对象】:
public class ObjectOutputStreamTest02 {
public static void main(String[] args) {
List<Student> list = new ArrayList<>();
list.add(new Student("唐蛇皮",21));
list.add(new Student("葛蔓蔓",22));
list.add(new Student("佘楽樂",21));
ObjectOutputStream out = null ;
try {
out = new ObjectOutputStream(new FileOutputStream("E:\\Typora笔记\\JavaSE\\IO流\\users"));
//序列化一个集合,这个集合对象中放了很多其他对象
out.writeObject(list);
out.flush();
} catch (IOException e) {
e.printStackTrace();
}finally {
if (out!=null) {
try {
out.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
【反序列化多个对象】:
public class ObjectInputStreamTest02 {
public static void main(String[] args) {
ObjectInputStream objectInputStream = null ;
try {
objectInputStream = new ObjectInputStream(new FileInputStream("E:\\Typora笔记\\JavaSE\\IO流\\users"));
// Object obj = objectInputStream.readObject();
// System.out.println(obj);
//判断obj是不是一个List集合
// System.out.println(obj instanceof List);
List<Student> studentlist = (List<Student>)objectInputStream.readObject();
for (Student student : studentlist) {
System.out.println(student);
}
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
} finally {
if (objectInputStream!=null) {
try {
objectInputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
运行结果:
姓名:唐蛇皮年龄:21
姓名:葛蔓蔓年龄:22
姓名:佘楽樂年龄:21
如果不用集合,直接存储多个对象,反序列化有什么不同?
存储第二个对象的时候会报错,所以需要List集合
补充:
当希望Student对象中的某个对象不参与序列化时怎么办
使用transient关键字 : 表示游离,不参与序列化
以上代码Student为例
//表示name不参与序列化操作
private transient String name ;
Student中name添加transient关键字之后运行结果:
姓名:null年龄:21
姓名:null年龄:22
姓名:null年龄:21
添加关键字transient关键字之后,要把序列化程序在执行一边,不然会报错
Student类中添加版本号
//建议将序列化版本号手动的写出来,不建议自动生成【自动生成快捷键+alt+回车】
private static final long serialVersionUID = 1L;//JAVA虚拟机识别一个类
补充:
当过了很久,Student这个类源代码改动了
源代码改动之后,需要重新编译,编译之后会生成全新的字节码文件
并且class类再次运行的时候,Java虚拟机生成的序列化版本号也会发生相应的改变
在Student类中和添加新的属性之后,在执行序列化会报错,如下:
java.io.InvalidClassException:com.IO.lesson07.Student;
local class incompatible:
stream classdesc serialVersionUID = 3456139176802951041,
local class serialVersionUID = 1
版本号具有唯一性
序列化版本号有什么用?
Java语言中是采用什么机制来区分类的?
第一:首先通过类名进行比对,如果类名不一样,肯定不是同一类
第二: 如果类名一样,就会根据版本号进行区分
例如:
当不同的人编写同一个类,但“这两个类确定不是同一个类”。这时候序列化版本号就起上作用了
对于Java 虚拟机来说,java虚拟机是可以区分开这两个类的,因为这两个类都实现了Serializable接口
都有默认的序列化版本号,他们的序列化版本号不一样,就区分开了(这是自动生成版本号的好处)
请思考?
这种自动生成序列化版本号有什么缺陷?
这种自动生成的序列化版本号缺点就是:一旦代码确定后,不能进行后续的修改
因为只要修改,必然会重新编译,此时会生成全新的序列化版本号,这个时候java虚拟机会认为这是一个全新的类
最终结论:
凡是一个实现Serializable接口,建议给该类提供一个固定不变的序列化版本号,这样,以后这个类即使代码修改了,但是版本号不变,Java虚拟机会认为是同一个类
【建议将序列化版本号手动写出来,不建议自动生成】