一、序列化和反序列化的理解
序列化代码演示如下:(内存----> 硬盘文件)
student类:
package com.bjpowernode.java.io;
import java.io.Serializable;
// 序列化对象必须实现Serializable接口 起到标志性作用
public class Student implements Serializable {
// 属性
private String name;
private int age;
// 构造方法
public Student() {
}
public Student(String name, int age) {
this.name = name;
this.age = age;
}
// getter and setter方法
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;
}
// toString方法
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
代码测试如下:
package com.bjpowernode.java.io;
/*
1、java.io.NotSerializableException异常
2、参加序列化和反序列化的对象,必须实现Serializable接口
此接口起到标志性作用,是给java虚拟机看的,java虚拟机看到这个接口后,会为该类自动生成一个序列化版本号
*/
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;
public class ObjectOutputStreamTest01 {
public static void main(String[] args) throws IOException {
// 创建java对象
Student student =new Student("junker",5);
// 创建序列化对象
ObjectOutputStream objectOutputStream =new ObjectOutputStream(new FileOutputStream("students"));
// 序列化对象
objectOutputStream.writeObject(student);
// 刷新
objectOutputStream.flush();
// 关流
objectOutputStream.close();
}
}
结果:
反序列化代码演示如下:
package com.bjpowernode.java.io;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
// 反序列化: 文件----> 内存
public class ObjectInputStreamTest01 {
public static void main(String[] args) throws IOException, ClassNotFoundException {
// 创反序列化对象
ObjectInputStream objectInputStream =new ObjectInputStream(new FileInputStream("students"));
// 开始反序列化 读
Object o =objectInputStream.readObject();
System.out.println(o);
// 关流
objectInputStream.close();
}
}
输出结果:
二、序列化多个对象
代码演示如下:
package com.bjpowernode.java.io;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;
import java.util.ArrayList;
public class ObjectOutputStreamTest02 {
public static void main(String[] args) throws IOException {
// 创建一个集合
ArrayList<User> arrayList =new ArrayList<>();
// 向集合中添加元素
arrayList.add(new User("kitty",5));
arrayList.add(new User("junker",9));
// 创建序列化对象
ObjectOutputStream oos =new ObjectOutputStream(new FileOutputStream("users"));
// 序列化
oos.writeObject(arrayList);
// 刷新
oos.flush();
// 关流
oos.close();
}
}
结果:
反序列化多个对象
代码演示如下:
package com.bjpowernode.java.io;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.util.List;
// 反序列化 文件》内存
public class ObjectInputStreamTest02 {
public static void main(String[] args) throws IOException, ClassNotFoundException {
// 创建反序列化对象
ObjectInputStream ois =new ObjectInputStream(new FileInputStream("users"));
// 反序列化
// Object o =ois.readObject(); // 读到的是集合 因为存的时候就是集合
// System.out.println(o instanceof List); // true
List<User> list =(List<User>)ois.readObject();
for (User data :list){
System.out.println(data);
}
}
}
输出结果:
三、transient关键字
package com.bjpowernode.java.io;
import java.io.Serializable;
public class User implements Serializable {
// 属性
transient String name; // transient 不参于序列化 结果 null
int age;
public User(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public String toString() {
return "User{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
反序列化后的输出结果:
四、关于序列化版本号
假设这个User类是我们很久以前写的业务:
package com.bjpowernode.java.io;
import java.io.Serializable;
public class User implements Serializable {
/*
java虚拟机看到Serializable接口之后,会自动生成一个序列化版本号
// 这里没有手动写出来,java虚拟机会默认提供这个序列化版本号
*/
// 属性
String name;
int age;
public User(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public String toString() {
return "User{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
我们通过序列化把它存入到了文件当中
package com.bjpowernode.java.io;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;
import java.util.ArrayList;
// 序列化 内存----> 文件
public class ObjectOutputStreamTest02 {
public static void main(String[] args) throws IOException {
// 创建一个集合
ArrayList<User> arrayList =new ArrayList<>();
// 向集合中添加元素
arrayList.add(new User("kitty",5));
arrayList.add(new User("junker",9));
// 创建序列化对象
ObjectOutputStream oos =new ObjectOutputStream(new FileOutputStream("users"));
// 序列化
oos.writeObject(arrayList);
// 刷新
oos.flush();
// 关流
oos.close();
}
}
现在我们假定对User这个类当中做了优化/改进 那么我们通过反序列化这个users文件会是什么情况?
报错: java.io.InvalidClassException: stream classdesc serialVersionUID = 7674971421060302263, local class serialVersionUID = -7731114658773239639
通过报错可以看出第一个序列化版本号和我们修改完User类再次自动生成的序列化版本号不同了
解决方案: 我们可以手动把序列化版本号定义成第一个JVM机看到Serialzable后自动生成的序列化版本号 然后不管我们以后怎么优化修改User类 序列化版本号都是这一个 就可以进行反序列化了
package com.bjpowernode.java.io;
import java.io.Serializable;
public class User implements Serializable {
// 属性
String name;
int age;
private String addr; // 假定现在优化了User这个类
public User(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public String toString() {
return "User{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
反序列化该文件:
package com.bjpowernode.java.io;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.util.List;
// 反序列化 文件----> 内存
public class ObjectInputStreamTest02 {
public static void main(String[] args) throws IOException, ClassNotFoundException {
// 创建反序列化对象
ObjectInputStream ois =new ObjectInputStream(new FileInputStream("users"));
// 反序列化
// Object o =ois.readObject();
// System.out.println(o instanceof List); // true
List<User> list =(List<User>)ois.readObject();
for (User data :list){
System.out.println(data);
}
}
}
原因:
package com.bjpowernode.java.io;
import java.io.Serializable;
public class User implements Serializable {
/*
原因: java虚拟机看到Serializable接口之后,会自动生成一个序列化版本号
// 这里没有手动写出来,java虚拟机会默认提供这个序列化版本号
*/
// 手动定义成和JVM自动生成的第一个序列化版本号序列化版本号
private static final long serialVersionUID = 7674971421060302263L;
// 属性
String name;
int age;
private String addr; // 假定现在优化了User这个类
// 过了很久,User这个类源代码改动了
// 源代码改动后需要重新编译,编译之后生成了全新的字节码文件
// 并且class文件再次运行的时候,java虚拟机生成的序列化版本号也会发生相应的改变
// (相当于第一个User implements Serializable 这个类没修改前是两个属性 name 、age java虚拟机看到Serializable接口之后自动生成
// 一个序列化版本号 当我们修改这个User implements Serializable这个类之后 不再和第一个类一样,java虚拟机看到Serializable接口
// 后又会自动生成一个序列化版本号 修改前后的序列化版本号不同 所以当我们再次反序列化此文件的时候就会出现报错异常)
// 解决方案: 我们可以手动定义一个序列化版本号,让User这个类修改前后的序列化版本号固定一样,那么我们就可以反序列化了
public User(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public String toString() {
return "User{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
手动定义序列化版本号后反序列化结果:
package com.bjpowernode.java.io;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.util.List;
// 反序列化 文件----> 内存
public class ObjectInputStreamTest02 {
public static void main(String[] args) throws IOException, ClassNotFoundException {
// 创建反序列化对象
ObjectInputStream ois =new ObjectInputStream(new FileInputStream("users"));
// 反序列化
// Object o =ois.readObject();
// System.out.println(o instanceof List); // true
List<User> list =(List<User>)ois.readObject();
for (User data :list){
System.out.println(data);
}
}
}