ObjectOutputStream:序列化

  • ObjectOutputStream:序列化,将对象作为流输出到文件中存储起来
  • 报异常:java.io.NotSerializableException:
    Student对象不支持序列化,没有实现Serializable接口
  • 参与序列化和反序列化的对象,必须实现Serializable接口
  • 注意:通过源代码发现:Serializable接口只是一个标志接口:
    public interface Serializable{}
    这个接口中什么代码都没有
    那么它起到一个什么作用呢?
    起到标识的作用,标志的作用,java虚拟机看到这个类实现了这个接口,
    会为该类自动生成一个序列化版本号
  • 创建一个Student类用于序列化对象:
public class Student implements Serializable {
	//java虚拟机看到Serializable接口后,会自动生成一个序列化版本号
    //这里没有手动写出来,java虚拟机会默认提供这个序列化版本号
    String name;
    int age;

    public Student() {
    }

    public Student(String name, int age) {
        this.name = name;
        this.age = age;
    }

    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}
  • ObjectOutputStream对象输出流,进行序列化
public class ObjectOutputStreamTest01 {
    public static void main(String[] args) throws Exception {
        //序列化
        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("src\\io\\objectoutputstream\\students"));

        //序列化对象
        oos.writeObject(new Student("zhangsan",18));

        //flush  close
        oos.flush();
        oos.close();
    }
}

ObjectInputStream:反序列化

public class ObjectInputStreamTest01 {
    public static void main(String[] args) throws Exception{
        //反序列化
        ObjectInputStream ois = new ObjectInputStream(new FileInputStream("src\\io\\objectoutputstream\\students"));

        //反序列化对象,返回一个学生对象,所以会调用学生对象的toString方法
        System.out.println(ois.readObject());

        //close
        ois.close();
    }
}
//运行结果:
Student{name='zhangsan', age=18}

同时对多个对象进行序列化和反序列化

  • transient关键字:游离的,被该关键字修饰的变量不参与序列化
  • 经transient修饰的变量在序列化时保存的是类型默认值
  • 同时对多个对象进行序列化需要将对象存进集合中,对集合进行序列化
  • 创建新的User类集合:
/*
建议将序列化版本号手动写出来,不建议自动生成
 */
public class User implements Serializable {
    private static final long serialVersionUID = -1592057586725198947L;
    //transient:游离的,不参与序列化
    String name;
    transient int age; //age不参与序列化
    int id;

    public User(String name, int age) {
        this.name = name;
        this.age = age;
    }

    @Override
    public String toString() {
        return "User{" +
                "name='" + name + '\'' +
                ", age='" + age + '\'' +
                '}';
    }
}
  • 对多个对象进行序列化
public class ObjectOutputStreamTest02 {
    public static void main(String[] args) throws Exception{
        //序列化
        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("src\\io\\objectoutputstream\\Users"));

        //创建集合
        List<User> userList = new ArrayList<>();

        //向集合中添加对象
        userList.add(new User("张三",18));
        userList.add(new User("李四",20));

        //序列化集合对象
        oos.writeObject(userList);

        //flush close
        oos.flush();
        oos.close();
    }
}
  • 对多个对象进行反序列化
public class ObjectInputStreamTest02 {
    public static void main(String[] args) throws Exception{
        //反序列化
        ObjectInputStream ois = new ObjectInputStream(new FileInputStream("src\\io\\objectoutputstream\\Users"));

        //创建集合
        List<User> list;

        //反序列化对象
        list = (List<User>) ois.readObject();

        //遍历
        for (User user : list){
            System.out.println(user);
        }
        //close
        ois.close();
    }
}
//运行结果:
User{name='张三', age='0'}
User{name='李四', age='0'}

结论:

  • 修改对象类文件后,如果不重新序列化,会出InvalidClassException异常
•  Exception in thread “main” java.io.InvalidClassException:
 io.objectoutputstream.User; local class incompatible:
 stream classdesc serialVersionUID = -3965806672486621565, //修改后
 local class serialVersionUID = -1592057586725198947 //修改前
  • 为了避免上述异常的发生,建议手动添加序列化版本号
  • java语言中是采用什么机制来区分类的
    第一:首先通过类名来进行对比,如果类名不一样,肯定不是同一个类
    第二:如果类名一样,再怎么进行类的区别?靠序列化版本号进行区分
    不同的人编写了同一个类,但“这两个类确实不是同一个类”,这时序列化版本就起上作用了
    对于java虚拟机来说,java虚拟机是可以区分开这两个类的,因为这两个类都实现了Serializable接口
    都有默认的序列化版本号,他们的序列化版本号不一样。所以区分开了
    这种自动生成序列化版本号有什么缺陷:
    一旦代码确定之后,不能进行后续的修改,因为只要修改,必然会重新编译,
    此时会生成全新的序列化版本号,这个时候java虚拟机会认为这是一个全新的类
  • 最终结论:
    凡是一个类实现了Serializable接口,建议给该类提供一个固定不变的序列化版本号
    这样,以后这个类即使代码修改了,但是版本号不变,java虚拟机会认为是同一个类
  • 使用IDEA工具添加序列化版本号的方法:
  • java string不序列化 java 不可序列化_java

java string不序列化 java 不可序列化_序列化_02