序列化

作者: 安安静静写代码 | 来源:发表于2017-08-17 09:24 被阅读9次

    Java 序列化
    序列化作用
    注意事项
    Java 序列化的缺点
    举例说明
    项目中曾遇到的一个小问题
    参考

    序列化定义:将一个对象编码成一个字节流,称作将该对象序列化,反之,将字节流重新构建成对象,则称作反序列化。

    序列化作用
    序列化将对象编码成字节流,主要用于对象的持久化,远程通信,跨进程访问等地方。
    比如开发中常用到的 ORM
    框架 Mybatis
    ,或者 JPA
    都是需要将实体类实现序列化接口才能使用,再者如缓存,缓存的对象如果没有实现 Serializable
    接口,那么会抛出异常。
    注意事项
    父类实现了序列化,则子类自动实现了序列化,即子类不需要显式实现 Serializable
    接口。
    当父类没有实现序列化,而子类需要实现时,子类需要显式实现 Serializable
    接口,并且父类中需要有无参的构造函数。
    序列化只对对象的属性进行保存,而不会保存其方法。
    当类中的实例变量引用了其他对象,那么在对该类进行序列化时,引用的对象也会被序列化。

    Java 序列化的缺点
    序列化会让类变得不灵活。
    实现序列化之后,会有一个序列化 ID ,我们可以使用默认的 ID ,也可以重写这 ID,如果没有显式指定该序列 ID ,系统会经过一系列复杂的计算算出该 ID,那当我们改变类中的方法时,这个 ID 就会变化,这时候往往就会报 InvalidClassException
    异常。

    序列化可以重构,存在安全隐患。

    无法跨语言,Java 进行序列化,别的语言无法进行反序列化。

    序列化后的码流太大。

    Java 序列化性能较低。

    举例说明
    父类实现了序列化,则子类自动实现了序列化,即子类不需要显式实现 Serializable
    接口

    Parent.java
    
    public class Parent implements Serializable { 
    int age;
     public Parent(int age)
     { 
    this.age = age; 
    }
     public int getAge()
     {
     return age; 
    }
     public void setAge(int age)
     { 
    this.age = age;
     } 
    @Override public String toString()
     { 
    return "Parent{" + "age=" + age + '}'; }
    }
    
    Children.java
    
    public class Children extends Parent { 
    public Children(int age) 
    { 
    super(age);
     } 
    public void say() 
    { 
    System.out.println("Hello World " + age);
     } 
    @Override public String toString() 
    {
     return "Children{" + "} " + super.toString(); }
    }
    
    Test.java
    
    public class Test {
     public static void main(String[] args) throws IOException, ClassNotFoundException { 
    // 持久化到文件中 Children children = new Children(12);
    FileOutputStream outputStream = new FileOutputStream("test.txt"); ObjectOutputStream objectOutputStream = new
    ObjectOutputStream(outputStream); objectOutputStream.writeObject(children); 
    outputStream.close();
    objectOutputStream.close();
    // 从文件中读取 FileInputStream inputStream = new FileInputStream("test.txt");
    ObjectInputStream objectInputStream = new ObjectInputStream(inputStream); 
    Children o = (Children) objectInputStream.readObject(); 
    o.say();
    inputStream.close();
    objectInputStream.close();
     System.out.println(o);
    // new File("test.txt").delete(); }
    }
    

    控制台打印出

    children
    在这个例子中,父类 Parent实现了 Serializable接口,子类序列化时并不需要显式实现 Serializable。当父类没有实现序列化,而子类需要实现时,子类需要显式实现 Serializable接口,并且父类中需要有无参的构造函数。

    如果将 Parent改写,不实现序列化,让子类自己来实现会怎样呢?

    Parent.java

    public class Parent { int age; public Parent(int age) { this.age = age; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } @Override public String toString() { return "Parent{" + "age=" + age + '}'; }}

    Children.java

    public class Children extends Parent implements Serializable{ public Children(int age) { super(age); } public void say() { System.out.println("Hello World " + age); } @Override public String toString() { return "Children{" + "} " + super.toString(); }}

    然后运行 Test.java

    error 控制台会打印出没有合适的构造方法。
    序列化会让类变得不灵活。

    继续改写上述代码(注意:我们上述代码没有重写 serialVersionUID),我们先将该 Children保存到文件中,然后再对 Children进行修改,修改之后,再从文件中把刚才保存的字节码反序列化,看看会出现什么问题。步骤:
    运行 Test.java

    改写 Children.java,新增字段 name,并改写 say()方法注释掉 Test.java
    中的序列化方法,执行其中的反序列化

    改写后的 Children.java

    public class Children extends Parent {
     private String name; public String getName()
     { 
    return name; 
    } 
    public void setName(String name) {
     this.name = name;
     } 
    public Children(int age) { 
    super(age);
     }
     public void say() { 
    System.out.println("Hello World " + age + ",name: " + name); 
    } 
    @Override public String toString() {
     return "Children{" + "name='" + name + '\'' + "} " + super.toString(); }
    }
    

    控制台打印出:

    serial_version_error
    为了解决这个问题,我们需要在 Children.java中重写序列化 ID在 Children.java
    加入 private static final long serialVersionUID = -1;
    即可。
    序列化只对对象的属性进行保存,而不会保存其方法。****序列化可以重构,存在安全隐患。

    然后重新运行按照上诉步骤重新执行一遍,会发现不会报异常了,并且会发现当我们在运行时期改变 Children
    中的 say()方法时,打印出的 say() 方法变了,而 Children中的属性 age
    不会改变,且会发现 name默认为 null,这里在运行时我们重构了 Children
    类,改变了一些属性及方法,这也就存在了安全隐患。当类中的实例变量引用了其他对象,那么在对该类进行序列化时,引用的对象也会被序列化。如果该引用的对象没有实例化,则不需要序列化。

    创建一个 XiaoMing类, 改写 Children ,让 Children包含 XiaoMing的引用,并且在 Children的构造函数中初始化 XiaoMing,然后运行 Test.java。
    控制台打印出:

    xiaoming_error 表示 XiaoMing
    没有实现序列化接口,这说明在序列化对象的时候,对象的引用对象也会被序列化。

    相关文章

      网友评论

          本文标题:序列化

          本文链接:https://www.haomeiwen.com/subject/adewrxtx.html