美文网首页Java 杂谈
Java中的序列化

Java中的序列化

作者: zhang5788 | 来源:发表于2018-08-12 18:46 被阅读0次

    序列化

    什么是序列化?

    当你创建对象时,只有你需要,它就会一直存在,但是在程序终止时,无论如何他都不会在内存中存活。尽管这是有意义的,但是如果能将一个对象剥离到硬盘中,进行持久化存储,这也是很有意义的。Java提供了这么一个功能,就是序列化。

    如何序列化?

    在Java中,提供了两个接口来进行序列化操作。

    1. Serializable 接口

    这个接口是java中的一个标记接口,实现这个接口的类会被java识别为可以被序列化的类。

    public class A implements Serializable {
        private String a;
        private String b;
        private int c;
    
        A(String a, String b, int c) {
            this.a = a;
            this.b = b;
            this.c = c;
        }
        @Override
        public String toString()  {
            return a + ", "+ b + ", " + c;
        }
    }
    
    public static void main(String[] args) throws Exception {
            A a = new A("1", "2", 3);
            ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("/Users/zhangt/Desktop/A.out"));
            out.writeObject(a);
            out.close();
    
            ObjectInputStream in = new ObjectInputStream(new FileInputStream("/Users/zhangt/Desktop/A.out"));
            A a1 = (A)in.readObject();
            System.out.println(a1);
    }
    

    输出结果为:
    1, 2, 3
    这代表我们从文件中读取的类就是我们实例化的那个类。

    2. Externalizable 接口

    Externalizable 接口是Seriazable 接口的一个子接口。这个接口中定义了两个方法:

    public interface Externalizable extends java.io.Serializable {
       void writeExternal(ObjectOutput out) throws IOException;
       void readExternal(ObjectInput in) throws IOException, ClassNotFoundException;
    }
    

    这个接口虽然是Serializable的子接口,但是并不是一个标记性质的接口。实现该接口的类必须强制实现这两个方法:

    • wirteExternal(ObjectOutput out)
      该方法中,用来控制哪些字段可以被序列化。
    • readExternal(ObjectInput in)
      该方法中,用来读取哪些字段可以被反序列化。
      我们改造一下上面的类A
    public class A implements Externalizable {
        private String a;
        private String b;
        private int c;
    
        public A(String a, String b, int c) {
            this.a = a;
            this.b = b;
            this.c = c;
        }
    
        @Override
        public String toString()  {
            return a + ", "+ b + ", " + c;
        }
    
    
        @Override
        public void writeExternal(ObjectOutput out) throws IOException {
            out.writeObject(a);
            out.writeObject(b);
        }
    
        @Override
        public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
            this.a = (String)in.readObject();
            this.b = (String)in.readObject();
        }
    }
    
    public static void main(String[] args) throws Exception {
            A a = new A("1", "2", 3);
            ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("/Users/zhangt/Desktop/A.out"));
            out.writeObject(a);
            out.close();
    
            ObjectInputStream in = new ObjectInputStream(new FileInputStream("/Users/zhangt/Desktop/A.out"));
            A a1 = (A)in.readObject();
            System.out.println(a1);
    }
    

    输出结果为:

    Exception in thread "main" java.io.InvalidClassException: ioPackage.A; no valid constructor
        at java.io.ObjectStreamClass$ExceptionInfo.newInvalidClassException(ObjectStreamClass.java:157)
        at java.io.ObjectStreamClass.checkDeserialize(ObjectStreamClass.java:862)
        at java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:2041)
        at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1571)
        at java.io.ObjectInputStream.readObject(ObjectInputStream.java:431)
        at ioPackage.Client.main(Client.java:14)
    

    在相关资料中,查到:

    Externalizable 接口标识的类,在进行反序列化时,会调用类的默认构造器。

    由于我们自定义了A的构造器,导致默认的构造器并没有被生成,于是我们在A中添加一个默认的构造器。运行, 结果为:
    1, 2, 0
    因为,我们并没有对int c 进行序列化,反序列化的操作,导致c被初始化为默认值0。

    是不是想要对序列化的类进行字段限制,就只能实现Externalizable 接口,实现方法呢? 并不是的,其实Serializable 接口也可以控制字段的序列化。

    3.Serializable的序列化控制

    transient 关键字就是用来控制Serializable的字段的.
    我们修改类A:

     private String a;
        transient private String b;
        private int c;
    
    
        public A(String a, String b, int c) {
            this.a = a;
            this.b = b;
            this.c = c;
        }
    
        @Override
        public String toString()  {
            return a + ", "+ b + ", " + c;
        }
    

    运行结果为:
    1, null, 3
    字段String b 就被默认为null

    这里需要提及的一点是:

    String 类型的默认值是null , 而不是字符串"null", 但是,在反序列化的过程中,被限制的String 类型的字段值是字符串"null".

    序列化可能会遇到的问题

    1. 如果我反序列化相同的类多次,它们的在java中会是同一个对象么?

    由于反序列的过程就是将一个类读入内存的过程,如果多次反序列化,肯定是开辟多个内存地址.这就表示在Java中,它们是不同的两个对象.
    但是,也存在例外.如果你的对象是由同一个流读进来的对象,那么它们就是相同的.

    ObjectInputStream in = new ObjectInputStream(new FileInputStream("/Users/zhangt/Desktop/A.out"));
            A a1 = (A)in.readObject();
            in.close();
            ObjectInputStream in2 = new ObjectInputStream(new FileInputStream("/Users/zhangt/Desktop/A.out"));
            A a2 = (A)in2.readObject();
            in2.close();
    

    a1a2的地址是不同的.

    A a = new A("1", "2", 3);
            ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("/Users/zhangt/Desktop/A.out"));
            out.writeObject(a);
            out.writeObject(a); // 输出两次
            out.close();
    
            ObjectInputStream in = new ObjectInputStream(new FileInputStream("/Users/zhangt/Desktop/A.out"));
            A a1 = (A)in.readObject();
            A a2 = (A)in.readObject();
            System.out.println(a1);
            System.out.println(a2);
    

    a1a2 的地址是相同的.

    2. 如果序列化的类中包含其他类的对象,多次反序列化后,第三方对象会在内存中存在一份还是多份?

    如果在序列化前,多个序列化类指向的对象是同一个对象,那么反序列化后指向的还是同一个对象.反序列化的结果的对象网和序列化之前是相同的.

    相关文章

      网友评论

        本文标题:Java中的序列化

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