精华内容
下载资源
问答
  • 如果某个类的成员变量的类型不是基本类型或String类型,而是另一个引用类型,那么这个引用类必须序列化的,否则拥有该类型成员变量的类也不可序列化的如果多次请同一个对象序列化java只会储存一个对象,后续...

    如果某个类的成员变量的类型不是基本类型或String类型,而是另一个引用类型,那么这个引用类必须是可序列化的,否则拥有该类型成员变量的类也是不可序列化的

    如果多次请同一个对象序列化,java只会储存一个对象,后续的都只是存储一个编号,具体的序列化算法如下:

    所有保存到磁盘中的对象都有一个序列化编号

    当程序试图序列化一个对象时,程序将先检查该对象是否已经被序列化过,只有该对象从未(在本次虚拟机中)被序列化过,系统才会将该对象转换成字节序列并输出

    如果某个对象已经序列化过,程序将只是直接输出一个序列化编号,而不是再次重新序列化该对象

    Person 类

    @Data

    public class Person implements Serializable {

    private String name;

    private int age;

    public Person(String name, int age) {

    this.name = name;

    this.age = age;

    }

    }

    Teacher 类拥有 Person 类实例

    @Data

    public class Teacher implements Serializable {

    private String name;

    private Person student;

    public Teacher(String name, Person student) {

    this.name = name;

    this.student = student;

    }

    }

    将 Teacher 类多次序列化

    public class WriteTeacher {

    public static void main(String[] args) {

    try (

    ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("teacher.txt"));

    ) {

    Person per = new Person("孙悟空", 2500);

    Teacher t1 = new Teacher("唐僧", per);

    Teacher t2 = new Teacher("菩提祖师", per);

    // 将对象写入输出流

    oos.writeObject(t1);

    oos.writeObject(t2);

    oos.writeObject(per);

    oos.writeObject(t2);

    } catch (IOException e) {

    e.printStackTrace();

    }

    }

    }

    问题来了,序列化的四个对象中,Person 对象是不是同一个?

    public class ReadTeacher {

    public static void main(String[] args) {

    try (

    ObjectInputStream ois = new ObjectInputStream(new FileInputStream("teacher.txt"));

    ){

    Teacher t1 = (Teacher) ois.readObject();

    Teacher t2 = (Teacher) ois.readObject();

    Person p = (Person) ois.readObject();

    Teacher t3 = (Teacher) ois.readObject();

    System.out.println("t1 的 student 引用和 p 是否相同 " + (t1.getStudent() == p));

    System.out.println("t2 的 student 引用和 p 是否相同 " + (t2.getStudent() == p));

    System.out.println("t2 和 t3 是否是同一个对象:" + (t2 == t3));

    } catch (IOException | ClassNotFoundException e) {

    e.printStackTrace();

    }

    }

    }

    b128277fb0665cbc5dba8736178363ce.png

    如图,序列化一个对象多次,只会保存一次

    因为 Java 的序列化机制,我们要注意以下场景:如果多次序列化同一个Java对象时,只有第一次序列化时才会把该Java对象转换成字节序列并输出,这样可能引起一个潜在的问题——当程序序列化一个可变对象时,只有第一次使用writeObject()方法输出时才会将该对象转换成字节序列并输出,当程序再次调用writeObject()方法时,程序只是输出前面的序列化编号,即使后面该对象的实例变量值已被改变,改变的实例变量值也不会被输出

    展开全文
  • 如果两个对象是是另一个类对象C的成员序列化C,反序列化C之后,A和B的引用关系还存在。我就是做了这个两个实验。1 node2引用了node1,node2.parent=node1,序列化和反序列化之后,发现node2.par...

    今天写代码的时候用到序列化,不过突然想到这个问题。

    于是写了一些测试代码,得出两个结论。

    如果两个对象存在引用关系,比如A引用B。

    如果两个对象是各自序列化的,则引用关系不再存在。

    如果两个对象是是另一个类对象C的成员,序列化C,反序列化C之后,A和B的引用关系还存在。

    我就是做了这个两个实验。

    1 node2引用了node1,node2.parent=node1,

    序列化和反序列化之后,

    发现node2.parent !=node1。

    node2.parent 指向的内存跟node1不同,而是另外在内存生成了一个对象。

    2 Tree有两个成员,node1和node2,

    node2引用了node1,node2.parent=node1,

    序列化和反序列化之后,

    node2.parent =node1。

    数据结构定义:

    class node implements Serializable

    {

    /**

    *

    */

    private static final long serialVersionUID = -8475920991534073160L;

    node parent;

    int id;

    public node(int i)

    {

    id = i;

    parent= null;

    }

    }

    //nodeTree,node1和node2存在引用关系

    class nodeTree implements Serializable

    {

    node node1;

    node node2;

    public nodeTree()

    {

    node1 = new node(1);

    node2 = new node(2);

    node2.parent = node1;

    }

    public void test()

    {

    if(node1 ==node2.parent)

    System.out.println("node1 = node2.parent!");

    else System.out.println("node1 !=node2.parent!");

    if(node1.parent == null)

    System.out.println("node1's parent is null");

    else System.out.println("node1's parent is "+node1.parent.id);

    if(node2.parent == null)

    System.out.println("node2's parent is null");

    else System.out.println("node2's parent is "+node2.parent.id);

    }

    }

    测试代码:

    public class Serial2

    {

    //序列化 nodeTree,node1和node2的引用关系存在

    public void test() throws IOException, ClassNotFoundException

    {

    nodeTree tree = new nodeTree();

    tree.test();

    FileOutputStream fos = new FileOutputStream("temp1.out");

    ObjectOutputStream oos = new ObjectOutputStream(fos);

    oos.writeObject(tree);

    oos.flush();

    oos.close();

    FileInputStream fis = new FileInputStream("temp1.out");

    ObjectInputStream oin = new ObjectInputStream(fis);

    tree = (nodeTree) oin.readObject();

    System.out.println("序列化和反序列化之后");

    tree.test();

    }

    //各自序列化node1和node2,之间的引用关系不存在

    public void test2() throws IOException, ClassNotFoundException

    {

    node node1 = new node(1);

    node node2 = new node(2);

    node2.parent = node1;

    System.out.println(node1.id);

    System.out.println(node2.id);

    if(node1 ==node2.parent)

    System.out.println("node1 = node2.parent!");

    else System.out.println("node1 !=node2.parent!");

    if(node1.parent == null)

    System.out.println("node1's parent is null");

    else System.out.println("node1's parent is "+node1.parent.id);

    if(node2.parent == null)

    System.out.println("node2's parent is null");

    else System.out.println("node2's parent is "+node2.parent.id);

    //序列化

    FileOutputStream fos = new FileOutputStream("temp1.out");

    ObjectOutputStream oos = new ObjectOutputStream(fos);

    oos.writeObject(node1);

    oos.flush();

    oos.close();

    fos = new FileOutputStream("temp2.out");

    oos = new ObjectOutputStream(fos);

    oos.writeObject(node2);

    oos.flush();

    oos.close();

    //反序列化

    FileInputStream fis = new FileInputStream("temp1.out");

    ObjectInputStream oin = new ObjectInputStream(fis);

    node1 = (node) oin.readObject();

    fis = new FileInputStream("temp2.out");

    oin = new ObjectInputStream(fis);

    node2 = (node) oin.readObject();

    System.out.println("--------------------------------");

    System.out.println("序列化和反序列化之后");

    System.out.println(node1.id);

    System.out.println(node2.id);

    if(node1 ==node2.parent)

    System.out.println("node1 = node2.parent!");

    else System.out.println("node1 !=node2.parent!");

    if(node1.parent == null)

    System.out.println("node1's parent is null");

    else System.out.println("node1's parent is "+node1.parent.id);

    if(node2.parent == null)

    System.out.println("node2's parent is null");

    else System.out.println("node2's parent is "+node2.parent.id);

    }

    public static void main(String[] args) throws ClassNotFoundException, IOException

    {

    // TODO Auto-generated method stub

    Serial2 test = new Serial2();

    //test.test();

    test.test2();

    }

    }

    test()的输出结构:

    node1 = node2.parent!

    node1's parent is null

    node2's parent is 1

    序列化和反序列化之后

    node1 = node2.parent!

    node1's parent is null

    node2's parent is 1

    test2()的输出结果:

    1

    2

    node1 = node2.parent!

    node1's parent is null

    node2's parent is 1

    --------------------------------

    序列化和反序列化之后

    1

    2

    node1 !=node2.parent!

    node1's parent is null

    node2's parent is 1

    原文:http://blog.csdn.net/lingerlanlan/article/details/38087601

    展开全文
  • 什么是Java的序列化

    千次阅读 2020-06-27 21:14:51
    Java的序列化一、序列化的含义、意义及使用场景二、序列化实现的方式2.1 Serializable(1)普通序列化(2)成员是引用的序列化(3)同一对象序列化多次的机制(4)java序列化算法潜在的问题(5)可选的自定义序列化...

    一、序列化的含义、意义及使用场景

    序列化将对象写入到IO流中

    反序列化从IO流中恢复对象

    意义:序列化机制允许将实现序列化的Java对象转换位字节序列,这些字节序列可以保存在磁盘上,或通过网络传输,以达到以后恢复成原来的对象。序列化机制使得对象可以脱离程序的运行而独立存在

    使用场景:所有可在网络上传输的对象都必须是可序列化的,比如RMI(remote method invoke,即远程方法调用),传入的参数或返回的对象都是可序列化的,否则会出错;所有需要保存到磁盘的java对象都必须是可序列化的。通常建议:程序创建的每个JavaBean类都实现Serializeable接口。

    二、序列化实现的方式

    如果需要将某个对象保存到磁盘上或者通过网络传输,那么这个类应该实现Serializable接口或者Externalizable接口之一。

    2.1 Serializable

    (1)普通序列化

    Serializable接口是一个标记接口,不用实现任何方法。一旦实现了此接口,该类的对象就是可序列化的。

    1)序列化步骤:

    步骤一:创建一个ObjectOutputStream输出流;
    步骤二:调用ObjectOutputStream对象的writeObject输出可序列化对象。

    代码如下:

    public class Person implements Serializable {
      private String name;
      private int age;
      //我不提供无参构造器
      public Person(String name, int age) {
          this.name = name;
          this.age = age;
      }
    
      @Override
      public String toString() {
          return "Person{" +
                  "name='" + name + '\'' +
                  ", age=" + age +
                  '}';
      }
    }
    
    public class WriteObject {
      public static void main(String[] args) {
          try (//创建一个ObjectOutputStream输出流
               ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("object.txt"))) {
              //将对象序列化到文件s
              Person person = new Person("9龙", 23);
              oos.writeObject(person);
          } catch (Exception e) {
              e.printStackTrace();
          }
      }
    }

    2)反序列化步骤:

    步骤一:创建一个ObjectInputStream输入流;
    步骤二:调用ObjectInputStream对象的readObject()得到序列化的对象。

    我们将上面序列化到person.txt的person对象反序列化回来

    public class Person implements Serializable {
      private String name;
      private int age;
      //我不提供无参构造器
      public Person(String name, int age) {
          System.out.println("反序列化,你调用我了吗?");
          this.name = name;
          this.age = age;
      }
    
      @Override
      public String toString() {
          return "Person{" +
                  "name='" + name + '\'' +
                  ", age=" + age +
                  '}';
      }
    }
    
    public class ReadObject {
      public static void main(String[] args) {
          try (//创建一个ObjectInputStream输入流
               ObjectInputStream ois = new ObjectInputStream(new FileInputStream("person.txt"))) {
              Person brady = (Person) ois.readObject();
              System.out.println(brady);
          } catch (Exception e) {
              e.printStackTrace();
          }
      }
    }
    
    
    
    //运行结果
    //Person{name='9龙', age=23}

    输出告诉我们,反序列化并不会调用构造方法。反序列的对象是由JVM自己生成的对象,不通过构造方法生成。

    (2)成员是引用的序列化

    如果一个可序列化的类的成员不是基本类型,也不是String类型,那这个引用类型也必须是可序列化的;否则,会导致此类不能序列化。

    看例子,我们新增一个Teacher类。将Person去掉实现Serializable接口代码。

    public class Person{
        //省略相关属性与方法
    }
    public class Teacher implements Serializable {
    
        private String name;
        private Person person;
    
        public Teacher(String name, Person person) {
            this.name = name;
            this.person = person;
        }
    
         public static void main(String[] args) throws Exception {
            try (ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("teacher.txt"))) {
                Person person = new Person("路飞", 20);
                Teacher teacher = new Teacher("雷利", person);
                oos.writeObject(teacher);
            }
        }
    }

    在这里插入图片描述
    我们看到程序直接报错,因为Person类的对象是不可序列化的,这导致了Teacher的对象不可序列化。

    (3)同一对象序列化多次的机制

    同一对象序列化多次,会将这个对象序列化多次吗?答案是否定的。

    public class WriteTeacher {
        public static void main(String[] args) throws Exception {
            try (ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("teacher.txt"))) {
                Person person = new Person("路飞", 20);
                Teacher t1 = new Teacher("雷利", person);
                Teacher t2 = new Teacher("红发香克斯", person);
                //依次将4个对象写入输入流
                oos.writeObject(t1);
                oos.writeObject(t2);
                oos.writeObject(person);
                oos.writeObject(t2);
            }
        }
    }

    依次将t1、t2、person、t2对象序列化到文件teacher.txt文件中。

    注意:反序列化的顺序与序列化时的顺序一致。

    public class ReadTeacher {
        public static void main(String[] args) {
            try (ObjectInputStream ois = new ObjectInputStream(new FileInputStream("teacher.txt"))) {
                Teacher t1 = (Teacher) ois.readObject();
                Teacher t2 = (Teacher) ois.readObject();
                Person p = (Person) ois.readObject();
                Teacher t3 = (Teacher) ois.readObject();
                System.out.println(t1 == t2);
                System.out.println(t1.getPerson() == p);
                System.out.println(t2.getPerson() == p);
                System.out.println(t2 == t3);
                System.out.println(t1.getPerson() == t2.getPerson());
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
    //输出结果
    //false
    //true
    //true
    //true
    //true

    从输出结果可以看出,Java序列化同一对象,并不会将此对象序列化多次得到多个对象。

    Java序列化算法

    1)所有保存到磁盘的对象都有一个序列化编码号

    2)当程序试图序列化一个对象时,会先检查此对象是否已经序列化过,只有此对象从未(在此虚拟机)被序列化过,才会将此对象序列化为字节序列输出。
    3)如果此对象已经序列化过,则直接输出编号即可。

    上述序列化过程如下图:
    在这里插入图片描述

    (4)java序列化算法潜在的问题

    由于java序列化算法不会重复序列化同一个对象,只会记录已序列化对象的编号。如果序列化一个可变对象(对象内的内容可更改)后,更改了对象内容,再次序列化,并不会再次将此对象转换为字节序列,而只是保存序列化编号。

    public class WriteObject {
        public static void main(String[] args) {
            try (ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("person.txt"));
                 ObjectInputStream ios = new ObjectInputStream(new FileInputStream("person.txt"))) {
                //第一次序列化person
                Person person = new Person("9龙", 23);
                oos.writeObject(person);
                System.out.println(person);
    
                //修改name
                person.setName("海贼王");
                System.out.println(person);
                //第二次序列化person
                oos.writeObject(person);
    
                //依次反序列化出p1、p2
                Person p1 = (Person) ios.readObject();
                Person p2 = (Person) ios.readObject();
                System.out.println(p1 == p2);
                System.out.println(p1.getName().equals(p2.getName()));
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
    //输出结果
    //Person{name='9龙', age=23}
    //Person{name='海贼王', age=23}
    //true
    //true

    (5)可选的自定义序列化

    有些时候,我们有这样的需求,某些属性不需要序列化。

    1)使用transient关键字选择不需要序列化的字段。

    public class Person implements Serializable {
       //不需要序列化名字与年龄
       private transient String name;
       private transient int age;
       private int height;
       private transient boolean singlehood;
       public Person(String name, int age) {
           this.name = name;
           this.age = age;
       }
       //省略get,set方法
    }
    
    public class TransientTest {
       public static void main(String[] args) throws Exception {
           try (ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("person.txt"));
                ObjectInputStream ios = new ObjectInputStream(new FileInputStream("person.txt"))) {
               Person person = new Person("9龙", 23);
               person.setHeight(185);
               System.out.println(person);
               oos.writeObject(person);
               Person p1 = (Person)ios.readObject();
               System.out.println(p1);
           }
       }
    }
    //输出结果
    //Person{name='9龙', age=23', singlehood=true', height=185cm}
    //Person{name='null', age=0', singlehood=false', height=185cm}

    从输出我们看到,使用transient修饰的属性,java序列化时,会忽略掉此字段,所以反序列化出的对象,被transient修饰的属性是默认值。对于引用类型,值是null;基本类型,值是0;boolean类型,值是false。

    2)使用transient虽然简单,但将此属性完全隔离在了序列化之外。java提供了可选的自定义序列化。可以进行控制序列化的方式,或者对序列化数据进行编码加密等。

    private void writeObject(java.io.ObjectOutputStream out) throws IOException;
    private void readObject(java.io.ObjectIutputStream in) throws IOException,ClassNotFoundException;
    private void readObjectNoData() throws ObjectStreamException;

    通过重写writeObject与readObject方法,可以自己选择哪些属性需要序列化, 哪些属性不需要。如果writeObject使用某种规则序列化,则相应的readObject需要相反的规则反序列化,以便能正确反序列化出对象。这里展示对名字进行反转加密。

    public class Person implements Serializable {
       private String name;
       private int age;
       //省略构造方法,get及set方法
    
       private void writeObject(ObjectOutputStream out) throws IOException {
           //将名字反转写入二进制流
           out.writeObject(new StringBuffer(this.name).reverse());
           out.writeInt(age);
       }
    
       private void readObject(ObjectInputStream ins) throws IOException,ClassNotFoundException{
           //将读出的字符串反转恢复回来
           this.name = ((StringBuffer)ins.readObject()).reverse().toString();
           this.age = ins.readInt();
       }
    }

    当序列化流不完整时,readObjectNoData()方法可以用来正确地初始化反序列化的对象。例如,使用不同类接收反序列化对象,或者序列化流被篡改时,系统都会调用readObjectNoData()方法来初始化反序列化的对象。

    3)更彻底的自定义序列化

    ANY-ACCESS-MODIFIER Object writeReplace() throws ObjectStreamException;
    ANY-ACCESS-MODIFIER Object readResolve() throws ObjectStreamException;

    writeReplace:在序列化时,会先调用此方法,再调用writeObject方法。此方法可将任意对象代替目标序列化对象

    public class Person implements Serializable {
      private String name;
      private int age;
      //省略构造方法,get及set方法
    
      private Object writeReplace() throws ObjectStreamException {
          ArrayList<Object> list = new ArrayList<>(2);
          list.add(this.name);
          list.add(this.age);
          return list;
      }
    
       public static void main(String[] args) throws Exception {
          try (ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("person.txt"));
               ObjectInputStream ios = new ObjectInputStream(new FileInputStream("person.txt"))) {
              Person person = new Person("9龙", 23);
              oos.writeObject(person);
              ArrayList list = (ArrayList)ios.readObject();
              System.out.println(list);
          }
      }
    }
    //输出结果
    //[9龙, 23]

    readResolve:反序列化时替换反序列化出的对象,反序列化出来的对象被立即丢弃。此方法在readeObject后调用。

    public class Person implements Serializable {
        private String name;
        private int age;
        //省略构造方法,get及set方法
         private Object readResolve() throws ObjectStreamException{
            return new ("brady", 23);
        }
        public static void main(String[] args) throws Exception {
            try (ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("person.txt"));
                 ObjectInputStream ios = new ObjectInputStream(new FileInputStream("person.txt"))) {
                Person person = new Person("9龙", 23);
                oos.writeObject(person);
                HashMap map = (HashMap)ios.readObject();
                System.out.println(map);
            }
        }
    }
    //输出结果
    //{brady=23}

    readResolve常用来反序列单例类,保证单例类的唯一性。

    注意:readResolve与writeReplace的访问修饰符可以是private、protected、public,如果父类重写了这两个方法,子类都需要根据自身需求重写,这显然不是一个好的设计。通常建议对于final修饰的类重写readResolve方法没有问题;否则,重写readResolve使用private修饰。

    2.2 Externalizable:强制自定义序列化

    通过实现Externalizable接口,必须实现writeExternal、readExternal方法。

    public interface Externalizable extends java.io.Serializable {
         void writeExternal(ObjectOutput out) throws IOException;
         void readExternal(ObjectInput in) throws IOException, ClassNotFoundException;
    }
    public class ExPerson implements Externalizable {
    
        private String name;
        private int age;
        //注意,必须加上pulic 无参构造器
        public ExPerson() {
        }
    
        public ExPerson(String name, int age) {
            this.name = name;
            this.age = age;
        }
    
        @Override
        public void writeExternal(ObjectOutput out) throws IOException {
            //将name反转后写入二进制流
            StringBuffer reverse = new StringBuffer(name).reverse();
            System.out.println(reverse.toString());
            out.writeObject(reverse);
            out.writeInt(age);
        }
    
        @Override
        public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
            //将读取的字符串反转后赋值给name实例变量
            this.name = ((StringBuffer) in.readObject()).reverse().toString();
            System.out.println(name);
            this.age = in.readInt();
        }
    
        public static void main(String[] args) throws IOException, ClassNotFoundException {
            try (ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("ExPerson.txt"));
                 ObjectInputStream ois = new ObjectInputStream(new FileInputStream("ExPerson.txt"))) {
                oos.writeObject(new ExPerson("brady", 23));
                ExPerson ep = (ExPerson) ois.readObject();
                System.out.println(ep);
            }
        }
    }
    //输出结果
    //ydarb
    //brady
    //ExPerson{name='brady', age=23}

    注意:Externalizable接口不同于Serializable接口,实现此接口必须实现接口中的两个方法实现自定义序列化,这是强制性的;特别之处是必须提供pulic的无参构造器,因为在反序列化的时候需要反射创建对象。

    2.3 两种序列化机制对比

    实现Serializable接口 实现Externalizable接口
    系统自动存储必要的信息 程序员决定存储哪些信息
    Java内建支持,易于实现,只需要实现该接口即可,无需任何代码支持 必须实现接口内的两个方法
    性能略差 性能略好

    虽然Externalizable接口带来了一定的性能提升,但变成复杂度也提高了,所以一般通过实现Serializable接口进行序列化

    三、序列化版本号serialVersionUID

    我们知道,反序列化必须拥有class文件,但随着项目的升级,class文件也会升级,序列化怎么保证升级前后的兼容性呢?java序列化提供了一个private static final long serialVersionUID 的序列化版本号,只有版本号相同,即使更改了序列化属性,对象也可以正确被反序列化回来。

    public class Person implements Serializable {
        //序列化版本号
        private static final long serialVersionUID = 1111013L;
        private String name;
        private int age;
        //省略构造方法及get,set
    }

    如果反序列化使用的class的版本号与序列化时使用的不一致,反序列化会报InvalidClassException异常。
    在这里插入图片描述
    序列化版本号可自由指定,如果不指定,JVM会根据类信息自己计算一个版本号,这样随着class的升级,就无法正确反序列化;不指定版本号另一个明显隐患是,不利于jvm间的移植,可能class文件没有更改,但不同jvm可能计算的规则不一样,这样也会导致无法反序列化。

    什么情况下需要修改serialVersionUID呢?
    分以下三种情况:

    (1)如果只是修改了方法,反序列化不容影响,则无需修改版本号;
    (2)如果只是修改了静态变量,瞬态变量(transient修饰的变量),反序列化不受影响,无需修改版本号;
    (3)如果修改了非瞬态变量,则可能导致反序列化失败。如果新类中实例变量的类型与序列化时类的类型不一致,则会反序列化失败,这时候需要更改serialVersionUID。如果只是新增了实例变量,则反序列化回来新增的是默认值;如果减少了实例变量,反序列化时会忽略掉减少的实例变量。

    四、总结

    (1)所有需要网络传输的对象都需要实现序列化接口,通过建议所有的javaBean都实现Serializable接口。
    (2)对象的类名、实例变量(包括基本类型,数组,对其他对象的引用)都会被序列化;方法、类变量、transient实例变量都不会被序列化。
    (3)如果想让某个变量不被序列化,使用transient修饰。
    (4)序列化对象的引用类型成员变量,也必须是可序列化的,否则,会报错。
    (5)反序列化时必须有序列化对象的class文件。
    (6)当通过文件、网络来读取序列化后的对象时,必须按照实际写入的顺序读取。
    (7)单例类序列化,需要重写readResolve()方法;否则会破坏单例原则。
    (8)同一对象序列化多次,只有第一次序列化为二进制流,以后都只是保存序列化编号,不会重复序列化。
    (9)建议所有可序列化的类加上serialVersionUID 版本号,方便项目升级。

    展开全文
  • java中,为了避免对一个对象的重复序列化,,java序列化机制采用了一种特殊的序列化算法,即: 所有保存到磁盘中的(即序列化后保存)的对象都有一个序列化编号 在程序试图序列化一个对象时,系统将先检查该...
    1. 在java中,如果一个序列化的类的成员变量类型是其他类的引用,那么这个引用类也必须是可序列化的。
    2. 在java中,为了避免对一个对象的重复序列化,,java序列化机制采用了一种特殊的序列化算法,即是:
      1. 所有保存到磁盘中的(即序列化后保存)的对象都有一个序列化编号
      2. 在程序试图序列化一个对象时,系统将先检查该对象是否已经被序列化过,只有未被序列化过的对象,系统才会对该对象进行序列化
      3. 如果检查出来该对象是已经被序列化过的,那么程序就直接输出一个序列化编号,而不是再次序列化该对象
    3. 具体的讲解穿插在下面的代码中
      public class Teacher implements java.io.Serializable {
          private String name;
          private Person student;
      //    在这里,Person类必须是可序列化的,否则将导致Teacher类也变成不可序列化
      
          public Teacher(String name, Person student){
              this.name = name;
              this.student = student;
          }
      
          public String getName() {
              return name;
          }
      
          public void setName(String name) {
              this.name = name;
          }
      
          public Person getStudent() {
              return student;
          }
      
          public void setStudent(Person student) {
              this.student = student;
          }
      }
      
      import java.io.FileOutputStream;
      import java.io.IOException;
      import java.io.ObjectOutputStream;
      
      public class WriteTeacher {
          public static void main(String args[]){
              try{
                  ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("teacher.txt"));
      
                  Person person = new Person("孙悟空",500);
      //
                  Teacher teacher1 = new Teacher("唐僧", person);
                  Teacher teacher2 = new Teacher("菩提祖师", person);
      
      //           在这里连续用了四次调用writeObject,但是只有第一次是真正有把person这个对象序列化的,
      //            可以在readTeacher中看到证明
                  oos.writeObject(teacher1);
                  oos.writeObject(teacher2);
                  oos.writeObject(person);
                  oos.writeObject(teacher2);
      
      
              }catch (IOException e){
                  e.printStackTrace();
              }
      
          }
      }
      
      
      import java.io.FileInputStream;
      import java.io.ObjectInputStream;
      
      public class ReadTeacher {
          public static void main(String args[]){
              try{
                  ObjectInputStream ois = new ObjectInputStream(new FileInputStream("teacher.txt"));
      
      //            在这里,因为系统只对person对象序列化了一次,所以teacher1,teacher2中的person是一样的
      //            同理,系统也只是对teacher2序列化了一次,所以teacher3==teacher2是为true的
                  Teacher teacher1 = (Teacher)ois.readObject();
                  Teacher teacher2 = (Teacher)ois.readObject();
                  Person p = (Person)ois.readObject();
                  Teacher teacher3 = (Teacher)ois.readObject();
      
      
      
                  System.out.println("t1的student引用和p是否相同:" + (teacher1.getStudent() == p));
                  System.out.println("t2的student引用和p是否相同:" + (teacher2.getStudent() == p));
                  System.out.println("t2和t3是否是同一个对象"+(teacher2 == teacher3));
              }catch (Exception e){
                  e.printStackTrace();
              }
          }
      }
      //运行程序可以看到:
      //        t1的student引用和p是否相同:true
      //        t2的student引用和p是否相同:true
      //        t2和t3是否是同一个对象true
      

      这是我看李刚编著的《疯狂Java讲义》后总结出来的。

    展开全文
  • java序列化

    2021-04-16 16:01:29
    二、序列化实现的方式2.1 Serializable序列化2.1.1 普通序列化2.1.2 成员是引用的序列化2.1.3 同一个对象序列化多次的机制三、序列化算法四、潜在问题五、可选的自定义序列化5.1 使用transient5.2 重写writeObjec和...
  • Java序列化

    2021-04-05 17:09:51
    目录Java序列化普通序列化成员是引用的序列化同一对象序列化多次的机制 Java序列化 序列化的含义、意义以及使用场景 序列化:将对象写入到IO流中 反序列化:从IO流中恢复对象 意义:序列化机制允许将实现序列化的...
  • 反序列化步骤2.1.2 成员是引用的序列化2.1.3 同一对象序列化多次的机制2.1.4 java序列化算法潜在的问题2.1.5 可选的自定义序列化1.使用transient2.可选的自定义序列化3.更彻底的自定义序列化2.2 Externalizable:...
  • java序列化之后,对象的引用关系?

    千次阅读 2014-07-24 19:44:38
    今天写代码的时候用到序列化,不过突然想到这个问题。 于是写了一些测试代码,得出两个结论。 如果两个对象存在引用关系,...如果两个对象是是另一个类对象C的成员序列化C,反序列化C之后,A和B的引用关系还存在。
  • Java 序列化和反序列化 序列化和反序列化 1.序列化能保存的元素 (1)只能保存对象的非静态成员变量 (2)不能保存任何成员方法 (3)不能保存transient成员变量 (4)如果一个对象的成员变量一个对象,这个...
  • Java序列化详解

    2020-03-24 14:23:45
    文章目录一、 序列化的含义、意义... Serializable1.1 普通序列化和反序列化1.2 成员是引用的序列化1.3 同一对象多次序列化的机制1.4 Java序列化算法要点1.5 Java序列化算法存在的问题1.6 可选的自定义序列化1.6.1 ...
  • 在使用spring BeanUtils.copyProperties()进行对象赋值时,只能拷贝单一字段值,如果对象复合对象,比如成员变量中有对象,链表等时,BeanUtils.copyProperties()拷贝还是复杂成员变量的引用,如下代码: ...
  • Java序列化技术

    2019-09-30 14:06:25
    整个序列化的过程就是:将对象信息转化为二进制数据序列,里面保存了对象类型信息、引用类型信息、对象状态信息(我理解为成员变量值)。而反序列化,就是根据流(stream)中读取字节序列信息,重新包装一个对象,...
  • 原文链接:http://blog.csdn.net/tounaobun/article/details/8491392参考链接:https://www.cnblogs.com/mzzcy/p/7051044.html浅...一、浅拷贝、深拷贝 浅拷贝会对对象中的成员变量进行拷贝:如果基本类型,拷...
  • 一、序列化的含义、意义及使用场景二、序列化实现的方式1、Serializable1.1 普通序列化1.2 成员是引用的序列化1.3 同一对象序列化多次的机制1.4 java序列化算法潜在的问题1.5 可选的自定义序列化2、Externalizable:...
  • java序列化与深度克隆

    2014-03-31 15:18:25
    如果类的成员变量比较复杂,例如引用了多个可变引用类型,那么这时使用clone()方法来克隆非常麻烦的 可以考虑序列化的方法完成克隆 test代码 package com.mingrisoft; import java.io....
  • 一:引子开发中我们可能会遇到这样一种场景:有一个对象A,A中已经包含了一些...引用拷贝(对象的引用)2.浅拷贝(浅克隆)3.深拷贝(深克隆)那么究竟哪种方式可以满足我们以上所需场景呢?客官莫急,我们一...
  • 一、浅拷贝、深拷贝  浅拷贝会对对象中的成员变量进行拷贝:如果基本类型,...若要深层拷贝,一般可利用序列化和反序列化来实现,也可手动实现各级引用对象的深层拷贝;)   二、实现:  浅拷贝:实现Clone...
  • 4、如果一个对象的成员变量一个对象,那么这个成员变量对象也会被序列化吗?5、如果一个可序列化的对象包含对某个不可序列化的对象的引用,会发生哪些情况6、序列化是干什么的? 1、类通过什么实现序列化功能 类...
  • 一、浅拷贝、深拷贝浅拷贝会对对象中的成员变量进行拷贝:如果基本类型,拷贝的就是基本类型的值;如果属性内存地址(引用类型),拷贝的就是内存地址 ;深拷贝,除了基本类型外,引用类型所引用的对象也会进行...
  • 序列化和反序列化

    2020-11-29 10:29:11
    在拷贝引用类型成员变量时,为引用类型的数据成员开辟了一个独立的内存空间,实现真正内容上的拷贝,实现Serializable接口或者通过实现Cloneable接口实现(Serializable方式通过java对象的序列化和反序列化的操作实现...
  • previous: 也狗(本来我想要一个表情来表示这个字,没...浅拷贝会创建一个新对象,但是对象的引用类型成员还是会指向被复制对象的引用类型成员,与浅拷贝不同,深拷贝会为拷贝引用类型成员动态到动态分配内存中。而
  • 已知用一维数组存放一棵完全二叉树:ABCDEFGHIJKL,写出该二叉树先序、中序和后续遍历序列。以下关于字符串判定语句正确的是()某高速公路隧道最大埋深约为130m,设计净高5.0m,净宽14.0m,隧道长940m。隧道...
  • 序列化梳理

    千次阅读 多人点赞 2019-08-20 12:54:43
    二、反序列化时如何生成实例三、是不是所有类都需要序列化四、java序列化(Serializable)和外部化(Externalizable)主要区别五、哪些东西需要序列化1. 普通成员变量需要序列化2. 静态变量无需序列化3. 方法无需...
  • 对象copy和序列化

    2016-04-26 11:52:08
    深拷贝:当一个类的拷贝构造方法,不仅要复制对象的所有非引用成员变量值,还要为引用类型的成员变量创建新的实例,并且初始化为形式参数实例值。这个方式称为深拷贝JAVA Object自带的clone函数其实出现问题的关键就...
  • java创建对象方式

    2017-07-19 08:37:41
    java创建对象方式:new对象,序列化,反射,克隆 类:抽象,描述了其对象拥有数据和功能,类对象模版。 对象:具体生活中任何物体都某个类对象,对象体现,体系了所在类数据和功能。 ...

空空如也

空空如也

1 2 3 4 5 ... 8
收藏数 155
精华内容 62
关键字:

java的成员是引用的序列化

java 订阅