序列化

101 阅读5分钟

携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第29天,点击查看活动详情

哈喽,大家好!我是Why,一名在读学生,目前刚刚开始进入自己的编程学习生涯。虽然学习起步较晚,但我坚信做了才有0或1的可能。学了一段时间以后也是选择在掘金上分享自己的日常笔记,也希望能够在众多道友的大家庭中打成一片。 本文主要讲解序列化,如果大家读后觉得有用的话,还请大家多多支持博主:欢迎 ❤️点赞👍、收藏⭐、留言💬 ✨✨✨个人主页:JinHuan

序列化

 1、java.io.NotSerializableException:
 ​
 2、参与序列化和反序列化的对象,必须实现Serializable接口。
 ​
 3、注意:通过源代码发现,Serializable接口只是一个标志接口:
     public interface Serializable {
     }
     这个接口当中什么代码都没有。
     那么它起到一个什么作用呢?
         起到标识的作用,标志的作用,java虚拟机看到这个类实现了这个接口,可能会对这个类进行特殊待遇。
         Serializable这个标志接口是给java虚拟机参考的,java虚拟机看到这个接口之后,会为该类自动生成
         一个序列化版本号。
 ​
 4、序列化版本号有什么用呢?
     java.io.InvalidClassException:
         com.bjpowernode.java.bean.Student;
         local class incompatible:
             stream classdesc serialVersionUID = -684255398724514298(十年后),
             local class serialVersionUID = -3463447116624555755(十年前)
 ​
     'java语言中是采用什么机制来区分类的?
         第一:首先通过类名进行比对,如果类名不一样,肯定不是同一个类。
         第二:如果类名一样,再怎么进行类的区别?靠序列化版本号进行区分。
 ​
          A编写了一个类:com.java.bean.Student implements Serializable
          B编写了一个类:com.java.bean.Student implements Serializable
     不同的人编写了同一个类,但“这两个类确实不是同一个类”。这个时候序列化版本就起上作用了。
     对于java虚拟机来说,java虚拟机是可以区分开这两个类的,因为这两个类都实现了Serializable接口,
     都有默认的序列化版本号,他们的序列化版本号不一样。所以区分开了。(这是自动生成序列化版本号的好处)
 ​
     请思考?
         这种自动生成序列化版本号有什么缺陷?
             这种自动生成的序列化版本号缺点是:一旦代码确定之后,不能进行后续的修改,
             因为只要修改,必然会重新编译,此时会生成全新的序列化版本号,这个时候java
             虚拟机会认为这是一个全新的类。(这样就不好了!)
 ​
     '最终结论:
         凡是一个类实现了Serializable接口,建议给该类提供一个固定不变的序列化版本号。
         这样,以后这个类即使代码修改了,但是版本号不变,java虚拟机会认为是同一个类。
 ​

实例

 public class ObjectOutputStreamTest01 {
     public static void main(String[] args) throws Exception{
         // 创建java对象
         Student s = new Student(1111, "zhangsan");
         // 序列化
         ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("students"));
 ​
         // 序列化对象
         oos.writeObject(s);
 ​
         // 刷新
         oos.flush();
         // 关闭
         oos.close();
     }
 }
 ​

一次实例化多个对象

 /*
 一次序列化多个对象呢?
     可以,可以将对象放到集合当中,序列化集合。
 提示:
     参与序列化的ArrayList集合以及集合中的元素User都需要实现 java.io.Serializable接口。
  */
 public class ObjectOutputStreamTest02 {
     public static void main(String[] args) throws Exception{
         List<User> userList = new ArrayList<>();
         userList.add(new User(1,"zhangsan"));
         userList.add(new User(2, "lisi"));
         userList.add(new User(3, "wangwu"));
         ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("users"));
 ​
         // 序列化一个集合,这个集合对象中放了很多其他对象。
         oos.writeObject(userList);
 ​
         oos.flush();
         oos.close();
     }
 }
 ​

java.io.PrintStream

 public class PrintStreamTest {
     public static void main(String[] args) throws Exception{
 ​
         // 联合起来写
         System.out.println("hello world!");
 ​
         // 分开写
         PrintStream ps = System.out;
         ps.println("hello zhangsan");
         ps.println("hello lisi");
         ps.println("hello wangwu");
 ​
         // 标准输出流不需要手动close()关闭。
         // 可以改变标准输出流的输出方向吗? 可以
         /*
         // 这些是之前System类使用过的方法和属性。
         System.gc();
         System.currentTimeMillis();
         PrintStream ps2 = System.out;
         System.exit(0);
         System.arraycopy(....);
          */
 ​
         // 标准输出流不再指向控制台,指向“log”文件。
         PrintStream printStream = new PrintStream(new FileOutputStream("log"));
         // 修改输出方向,将输出方向修改到"log"文件。
         System.setOut(printStream);
         // 再输出
         System.out.println("hello world");
         System.out.println("hello kitty");
         System.out.println("hello zhangsan");
 ​
     }
 }
 ​

Idea一键生成序列号

 import java.io.Serializable;
 ​
 public class Student implements Serializable {
 ​
     // IDEA工具自动生成序列化版本号。
     //private static final long serialVersionUID = -7998917368642754840L;
 ​
     // Java虚拟机看到Serializable接口之后,会自动生成一个序列化版本号。
     // 这里没有手动写出来,java虚拟机会默认提供这个序列化版本号。
     // 建议将序列化版本号手动的写出来。不建议自动生成
     private static final long serialVersionUID = 1L; // java虚拟机识别一个类的时候先通过类名,如果类名一致,再通过序列化版本号。
 ​
     private int no;
     //private String name;
 ​
     // 过了很久,Student这个类源代码改动了。
     // 源代码改动之后,需要重新编译,编译之后生成了全新的字节码文件。
     // 并且class文件再次运行的时候,java虚拟机生成的序列化版本号也会发生相应的改变。
     private int age;
     private String email;
     private String address;
 ​
     public Student() {
     }
 ​
     public Student(int no, String name) {
         this.no = no;
         //this.name = name;
     }
 ​
     public int getNo() {
         return no;
     }
 ​
     public void setNo(int no) {
         this.no = no;
     }
 ​
     /*public String getName() {
         return name;
     }*/
 ​
     /*public void setName(String name) {
         this.name = name;
     }*/
 ​
     /*@Override
     public String toString() {
         return "Student{" +
                 "no=" + no +
                 ", name='" + name + ''' +
                 '}';
     }*/
 ​
     @Override
     public String toString() {
         return "Student{" +
                 "no=" + no +
                 ", age=" + age +
                 ", email='" + email + ''' +
                 ", address='" + address + ''' +
                 '}';
     }
 }
 ​

transient关键字

 import java.io.Serializable;
 ​
 public class User implements Serializable {
     private int no;
     // transient关键字表示游离的,不参与序列化。
     private transient String name; // name不参与序列化操作!
 ​
     public User() {
     }
 ​
     public User(int no, String name) {
         this.no = no;
         this.name = name;
     }
 ​
     @Override
     public String toString() {
         return "User{" +
                 "no=" + no +
                 ", name='" + name + ''' +
                 '}';
     }
 ​
     public int getNo() {
         return no;
     }
 ​
     public void setNo(int no) {
         this.no = no;
     }
 ​
     public String getName() {
         return name;
     }
 ​
     public void setName(String name) {
         this.name = name;
     }
 }
 ​