zoukankan      html  css  js  c++  java
  • 24 Java学习之对象序列化和反序列化

    一. 序列化和反序列化的概念

    对象序列化:把对象转换为字节序列的过程

    对象反序列化:把字节序列恢复为对象的过程

    1. 为何要进行序列化

            我们知道当虚拟机停止运行之后,内存中的对象就会消失。在很多应用中,需要对某些对象进行序列化,让他们离开内存空间,进入物理硬盘,便于长期保存。例如,最常见的是WEB服务器中的Session对象,当有10万用户并发访问,就有可能出现10万个Session对象,内存可能吃不消,于是Web容器就会把一些Session先序列化到硬盘中,等要用了,再把保存在硬盘中的对象还原到内存中。

            当两个进程在进行远程通信时,彼此可以发送各种类型的数据。无论是何种类型的数据,都会以二进制序列的形式在网络上传送。发送方需要把这个Java对象转换为字节序列,才能在网络上传送;接收方则需要把字节序列再恢复为Java对象。

    即对象序列化主要有两种用途:

    (1)把对象的字节序列永久地保存到硬盘上,通常存放在一个文件中;

    (2)在网络上传送对象的字节序列。

     二. 如何序列化

    1. 前提条件

    如果要让每个对象支持序列化机制,比如让它的类是可序列化的,则该类必须实现如下两个接口之一:

    • Serializable
    • Extmalizable:该接口继承自Serializable接口,实现Externalizable接口的类完全由自身来控制序列化的行为,而仅仅实现Serializable接口的类可以采用默认的序列化方式

    2. 相关API

    有两个类常常用于序列化和反序列化:java.io.ObjectOutputStream和java.io.ObjectInputStream

    (1)java.io.ObjectOutputStream

             java.io.ObjectOutputStream代表对象输出流,它的writeObject(Object obj)方法可以对参数指定的obj对象进行序列化,把得到的字节序列写到一个目标输出流中。

    (2)java.io.ObjectInputStream

             java.io.ObjectInputStream代表对象输入流,它的readObject()方法从一个源输入流中读取字节序列,再把他们反序列化为一个对象,并将其返回。

    3. 对象序列化步骤

     (1)创建一个对象输出流,它可以包装一个其它类型的目标输出流,如文件输出流;

     (2)通过对象输出流的writeObject()方法写对象

    4. 对象反序列化的步骤

    (1)创建一个对象输入流,它可以包装一个其它类型的源输入流,如文件输入流;

    (2)通过对象输入流的readObject()方法读取对象。

    5. 重要原则

    • Serializable是一个标示性接口,接口中没有定义任何的方法或字段,仅用于标示可序列化的语义。要序列化必须实现,否则异常
    • 静态变量和成员方法不可序列化。
    • 一个类要能被序列化,该类中的所有引用对象也必须是可以被序列化的。否则整个序列化操作将会失败,并且会抛出一个NotSerializableException,除非我们将不可序列化的引用标记为transient。
    • 声明成transient的变量不被序列化工具存储,同样,static变量也不被存储。

    三. 使用举例

     1. 将一个对象序列化之后存储到文件中

     1 package com.test.a;
     2 
     3 import java.io.Serializable;
     4 
     5 public class Person implements Serializable{
     6     private static final long serialVersionUID = 1L;
     7     public String name;
     8     public int age;
     9 
    10     public Person(String name, int age) {
    11         this.name = name;
    12         this.age = age;
    13     }
    14 
    15     public String getName() {
    16         return name;
    17     }
    18 
    19     public void setName(String name) {
    20         this.name = name;
    21     }
    22 
    23     public int getAge() {
    24         return age;
    25     }
    26 
    27     public void setAge(int age) {
    28         this.age = age;
    29     }
    30 
    31 }
    View Code
     1 package com.test.a;
     2 
     3 import java.io.FileNotFoundException;
     4 import java.io.FileOutputStream;
     5 import java.io.IOException;
     6 import java.io.ObjectOutputStream;
     7 
     8 
     9 public class Test {
    10     public static void main(String args[]) throws FileNotFoundException, IOException{
    11         Person person=new Person("zhangsan", 23);
    12         String path="C:\Users\hermioner\Desktop\test.txt";
    13         ObjectOutputStream objectOutputStream=new ObjectOutputStream(new FileOutputStream(path));
    14         objectOutputStream.writeObject(person);
    15         objectOutputStream.close();
    16     }
    17 }
    View Code

    说明:上面是执行结果。对象序列化之后,写入的是一个二进制文件,所以打开乱码是正常现象,不过透过乱码我们还是能够知道就是我们序列化的哪个对象。Person对象实现了Serializable接口,这个接口没有任何方法需要被实现,只是一个标记接口,表示这个类的对象可以被序列化,如果没有明确写了实现这个接口,就会抛出异常。

    2. 从文件中反序列化对象

     1 package com.test.a;
     2 
     3 import java.io.FileInputStream;
     4 import java.io.FileNotFoundException;
     5 import java.io.FileOutputStream;
     6 import java.io.IOException;
     7 import java.io.ObjectInputStream;
     8 import java.io.ObjectOutputStream;
     9 
    10 
    11 public class Test {
    12     public static void main(String args[]) throws FileNotFoundException, IOException, ClassNotFoundException{
    13         Person person=new Person("zhangsan", 23);
    14         String path="C:\Users\hermioner\Desktop\test.txt";
    15         ObjectOutputStream objectOutputStream=new ObjectOutputStream(new FileOutputStream(path));
    16         objectOutputStream.writeObject(person);
    17         objectOutputStream.close();
    18         
    19         ObjectInputStream objectInputStream=new ObjectInputStream(new FileInputStream(path));
    20         Person person2=(Person) objectInputStream.readObject();
    21         System.out.println(person2.getName());
    22         System.out.println(person2.getAge());
    23         objectInputStream.close();
    24     }
    25 }
    26 
    27 zhangsan
    28 23
    View Code

    说明:在调用readObject()方法的时候,有一个强转的动作。所以在反序列化时,要提供java对象所属类的class文件。

    3. 多个对象的反序列化

     1 package com.test.a;
     2 
     3 import java.io.FileInputStream;
     4 import java.io.FileNotFoundException;
     5 import java.io.FileOutputStream;
     6 import java.io.IOException;
     7 import java.io.ObjectInputStream;
     8 import java.io.ObjectOutputStream;
     9 
    10 
    11 public class Test {
    12     public static void main(String args[]) throws FileNotFoundException, IOException, ClassNotFoundException{
    13         Person person=new Person("zhangsan", 23);
    14         Person person2=new Person("lisi", 13);
    15         String path="C:\Users\hermioner\Desktop\test.txt";
    16         ObjectOutputStream objectOutputStream=new ObjectOutputStream(new FileOutputStream(path));
    17         objectOutputStream.writeObject(person);
    18         objectOutputStream.writeObject(person2);
    19         objectOutputStream.close();
    20         
    21         ObjectInputStream objectInputStream=new ObjectInputStream(new FileInputStream(path));
    22         Person person4=(Person) objectInputStream.readObject();
    23         System.out.println(person4.getName());
    24         System.out.println(person4.getAge());
    25         Person person5=(Person) objectInputStream.readObject();
    26         System.out.println(person5.getName());
    27         System.out.println(person5.getAge());
    28         objectInputStream.close();
    29     }
    30 }
    31 
    32 
    33 zhangsan
    34 23
    35 lisi
    36 13
    View Code

    说明:如果使用序列化机制向文件中写入了多个对象,在反序列化时,需要按实际写入的顺序读取。

    4. 对象引用的序列化

     1 package com.test.a;
     2 
     3 import java.io.Serializable;
     4 
     5 public class Person implements Serializable {
     6     private static final long serialVersionUID = 1L;
     7     public String name;
     8     public int age;
     9     public Man man;
    10 
    11     public Person(String name, int age, Man man) {
    12         this.name = name;
    13         this.age = age;
    14         this.man = man;
    15     }
    16 
    17     public String getName() {
    18         return name;
    19     }
    20 
    21     public void setName(String name) {
    22         this.name = name;
    23     }
    24 
    25     public int getAge() {
    26         return age;
    27     }
    28 
    29     public void setAge(int age) {
    30         this.age = age;
    31     }
    32 
    33     public Man getMan() {
    34         return man;
    35     }
    36 
    37     public void setMan(Man man) {
    38         this.man = man;
    39     }
    40 
    41 }
    View Code
     1 package com.test.a;
     2 
     3 import java.io.Serializable;
     4 
     5 public abstract class Man implements Serializable{  //必须实现Serializable
     6     public abstract void getInfo();
     7 }
     8 
     9 
    10 package com.test.a;
    11 
    12 public class Femal extends Man{
    13 
    14     @Override
    15     public void getInfo() {
    16         System.out.println("Femal");
    17         
    18     }
    19 
    20 }
    21 
    22 package com.test.a;
    23 
    24 public class Male extends Man{
    25 
    26     @Override
    27     public void getInfo() {
    28         System.out.println("male");
    29         
    30     }
    31 
    32 }
    View Code
     1 package com.test.a;
     2 
     3 import java.io.FileInputStream;
     4 import java.io.FileNotFoundException;
     5 import java.io.FileOutputStream;
     6 import java.io.IOException;
     7 import java.io.ObjectInputStream;
     8 import java.io.ObjectOutputStream;
     9 
    10 
    11 public class Test {
    12     public static void main(String args[]) throws FileNotFoundException, IOException, ClassNotFoundException{
    13         Person person=new Person("zhangsan",24, new Femal());
    14         String path="C:\Users\hermioner\Desktop\test.txt";
    15         ObjectOutputStream objectOutputStream=new ObjectOutputStream(new FileOutputStream(path));
    16         objectOutputStream.writeObject(person);
    17         objectOutputStream.close();
    18         
    19         ObjectInputStream objectInputStream=new ObjectInputStream(new FileInputStream(path));
    20         Person person4=(Person) objectInputStream.readObject();
    21         System.out.println(person4.getName());
    22         System.out.println(person4.getAge());
    23         Femal man=(Femal) person4.getMan();
    24         man.getInfo();
    25         objectInputStream.close();
    26     }
    27 }
    View Code
    1 zhangsan
    2 24
    3 Femal
    View Code

    说明:上面介绍对象的成员变量都是基本数据类型,如果对象的成员变量是引用类型,这个引用类型的成员变量必须也是可序列化的,否则拥有该类型成员变量的类的对象不可序列化。

    四. serialVersionUID

    serialVersionUID:字面意思是序列化的版本号,凡是实现了Seriallizable接口的类都有一个表示序列化版本标识符的静态变量:

    private static final long serialVersionUID = 1L;

    实现Serializable接口的类如果类中没有添加serialVersionUID,那么就会出现如下的警告提示

     看上面的提示信息,可以知道有两种方式来生成serial version ID

    (1)采用默认方式

    private static final long serialVersionUID = 1L;

    这种方式生成的serialVersionUID是1L.

    (2)采用第二种

    private static final long serialVersionUID = -6587084022709540081L;

      这种方式生成的serialVersionUID是根据类名,接口名,方法和属性等来生成的。

    1. serialVersionUID的作用

    (1)假设如下Person对象进行序列化和反序列化----没有加入serialVersionUID

     1 package com.test.a;
     2 
     3 import java.io.Serializable;
     4 
     5 public class Person implements Serializable {
     6 
     7     public String name;
     8     public int age;
     9     
    10     
    11     public Person(String name,int age) {
    12         this.name=name;
    13         this.age=age;
    14     }
    15 
    16     public String getName() {
    17         return name;
    18     }
    19 
    20     public void setName(String name) {
    21         this.name = name;
    22     }
    23 
    24     public int getAge() {
    25         return age;
    26     }
    27 
    28     public void setAge(int age) {
    29         this.age = age;
    30     }
    31 }
    View Code
     1 package com.test.a;
     2 
     3 import java.io.FileInputStream;
     4 import java.io.FileNotFoundException;
     5 import java.io.FileOutputStream;
     6 import java.io.IOException;
     7 import java.io.ObjectInputStream;
     8 import java.io.ObjectOutputStream;
     9 
    10 
    11 public class Test {
    12     public static void main(String args[]) throws FileNotFoundException, IOException, ClassNotFoundException{
    13         Person person=new Person("zhangsan",24);
    14         String path="C:\Users\hermioner\Desktop\test.txt";
    15         ObjectOutputStream objectOutputStream=new ObjectOutputStream(new FileOutputStream(path));
    16         objectOutputStream.writeObject(person);
    17         objectOutputStream.close();
    18         
    19         ObjectInputStream objectInputStream=new ObjectInputStream(new FileInputStream(path));
    20         Person person4=(Person) objectInputStream.readObject();
    21         System.out.println(person4);
    22         System.out.println(person4.getName());
    23         System.out.println(person4.getAge());
    24 
    25         objectInputStream.close();
    26     }
    27 }
    28 
    29 com.test.a.Person@448139f0
    30 zhangsan
    31 24
    View Code
     

    com.test.a.Person@448139f0
    zhangsan
    24

    (2)修改Person,添加一个新的属性

     1 package com.test.a;
     2 
     3 import java.io.Serializable;
     4 
     5 public class Person implements Serializable {
     6 
     7     public String name;
     8     public int age;
     9     public String sex;/////new added
    10     
    11     
    12     public Person(String name,int age) {
    13         this.name=name;
    14         this.age=age;
    15     }
    16     
    17     public Person(String name,int age,String sex)/////new added
    18     {
    19         this.name=name;
    20         this.age=age;
    21         this.sex=sex;
    22     }
    23 
    24     public String getName() {
    25         return name;
    26     }
    27 
    28     public void setName(String name) {
    29         this.name = name;
    30     }
    31 
    32     public int getAge() {
    33         return age;
    34     }
    35 
    36     public void setAge(int age) {
    37         this.age = age;
    38     }
    39 }
    View Code

    此时只执行反序列化,(因为生成的对象还是调用的之前两个参数的哪个对象)

     1 package com.test.a;
     2 
     3 import java.io.FileInputStream;
     4 import java.io.FileNotFoundException;
     5 import java.io.FileOutputStream;
     6 import java.io.IOException;
     7 import java.io.ObjectInputStream;
     8 import java.io.ObjectOutputStream;
     9 
    10 
    11 public class Test {
    12     public static void main(String args[]) throws FileNotFoundException, IOException, ClassNotFoundException{
    13 //        Person person=new Person("zhangsan",24);
    14         String path="C:\Users\hermioner\Desktop\test.txt";
    15 //        ObjectOutputStream objectOutputStream=new ObjectOutputStream(new FileOutputStream(path));
    16 //        objectOutputStream.writeObject(person);
    17 //        objectOutputStream.close();
    18         
    19         ObjectInputStream objectInputStream=new ObjectInputStream(new FileInputStream(path));
    20         Person person4=(Person) objectInputStream.readObject();
    21         System.out.println(person4);
    22         System.out.println(person4.getName());
    23         System.out.println(person4.getAge());
    24 
    25         objectInputStream.close();
    26     }
    27 }
    View Code
    1 Exception in thread "main" java.io.InvalidClassException: com.test.a.Person; local class incompatible: stream classdesc serialVersionUID = 4647091331428092166, local class serialVersionUID = -7232940355925514760
    2     at java.io.ObjectStreamClass.initNonProxy(ObjectStreamClass.java:687)
    3     at java.io.ObjectInputStream.readNonProxyDesc(ObjectInputStream.java:1883)
    4     at java.io.ObjectInputStream.readClassDesc(ObjectInputStream.java:1749)
    5     at java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:2040)
    6     at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1571)
    7     at java.io.ObjectInputStream.readObject(ObjectInputStream.java:431)
    8     at com.test.a.Test.main(Test.java:20)
    View Code

    说明:此时用的还是之前序列化的结果进行反序列化,只是此次反序列化的之前,还修改了Person。因此会造成不兼容的现象。根据上面错误提示我们知道:文件流中的class和classpath中的class,也就是修改过后的class,不兼容了,处于安全机制考虑,程序抛出了错误,并且拒绝载入。那么如果我们真的有需求要在序列化后添加一个字段或者方法呢?应该怎么办?那就是自己去指定serialVersionUID。在Test例子中,没有指定Person类的serialVersionUID的,那么java编译器会自动给这个class进行一个摘要算法,类似于指纹算法,只要这个文件 多一个空格,得到的UID就会截然不同的,可以保证在这么多类中,这个编号是唯一的。所以,添加了一个字段后,由于没有显指定 serialVersionUID,编译器又为我们生成了一个UID,当然和前面保存在文件中的那个不会一样了,于是就出现了2个序列化版本号不一致的错误。因此,只要我们自己指定了serialVersionUID,就可以在序列化后,去添加一个字段,或者方法,而不会影响到后期的还原,还原后的对象照样可以使用,而且还多了方法或者属性可以用。

    因此总结Class不兼容解决办法:

    • 添加serialVersionUID,重复(1)和(2)的操做的化就可以避免Class不兼容现象了。
    • 还有中办法:可以在类修改以后,再重新序列化,这样反序列化就可以拿到最新的序列化的文件拉。

    显式地定义serialVersionUID有两种用途:

    •     在某些场合,希望类的不同版本对序列化兼容,因此需要确保类的不同版本具有相同的serialVersionUID;
    •     在某些场合,不希望类的不同版本对序列化兼容,因此需要确保类的不同版本具有不同的serialVersionUID。

    SerialVersionUID其实是JAVA的序列化机制采取的一种特殊的算法:

    • 所有保存到磁盘中的对象都有一个序列化编号。
    • 当程序试图序列化一个对象时,会先检查该对象是否已经被序列化过,只有该对象从未(在本次虚拟机中)被序列化,系统才会将该对象转换成字节序列并输出。
    • 如果对象已经被序列化,程序将直接输出一个序列化编号,而不是重新序列化。

    五. 自定义序列化

     1. transient关键字

      用transient关键字来修饰实例变量,该变量就会被完全隔离在序列化机制之外。

     1 package com.test.a;
     2 
     3 import java.io.Serializable;
     4 
     5 public class Person implements Serializable {
     6     private static final long serialVersionUID = -7232940355925514760L;
     7     public String name;
     8     public transient int age;
     9     
    10     public Person(String name,int age) {
    11         this.name=name;
    12         this.age=age;
    13     }
    14     
    15     public String getName() {
    16         return name;
    17     }
    18 
    19     public void setName(String name) {
    20         this.name = name;
    21     }
    22 
    23     public int getAge() {
    24         return age;
    25     }
    26 
    27     public void setAge(int age) {
    28         this.age = age;
    29     }
    30 }
    View Code
     1 package com.test.a;
     2 
     3 import java.io.FileInputStream;
     4 import java.io.FileNotFoundException;
     5 import java.io.FileOutputStream;
     6 import java.io.IOException;
     7 import java.io.ObjectInputStream;
     8 import java.io.ObjectOutputStream;
     9 
    10 
    11 public class Test {
    12     public static void main(String args[]) throws FileNotFoundException, IOException, ClassNotFoundException{
    13         Person person=new Person("zhangsan",24);
    14         String path="C:\Users\hermioner\Desktop\test.txt";
    15         ObjectOutputStream objectOutputStream=new ObjectOutputStream(new FileOutputStream(path));
    16         objectOutputStream.writeObject(person);
    17         objectOutputStream.close();
    18         
    19         ObjectInputStream objectInputStream=new ObjectInputStream(new FileInputStream(path));
    20         Person person4=(Person) objectInputStream.readObject();
    21         System.out.println(person4);
    22         System.out.println(person4.getName());
    23         System.out.println(person4.getAge());
    24 
    25         objectInputStream.close();
    26     }
    27 }
    View Code
    1 com.test.a.Person@448139f0
    2 zhangsan
    3 0
    View Code

    说明:age的打印值为0,是默认初始化值。如果是字符串,就为null。这说明使用tranisent修饰的变量,在经过序列化和反序列化之后,JAVA对象会丢失该实例变量的值。因此,Java提供了一种自定义序列化机制,这样程序就可以自己来控制如何序列化各实例变量,甚至不序列化实例变量。

    2. 自定义序列化

    在序列化和反序列化过程中需要特殊处理的类应该提供如下的方法,这些方法用于实现自定义的序列化。

    writeObject()

    readObject()

    这两个方法并不属于任何的类和接口,只要在要序列化的类中提供这两个方法,就会在序列化机制中自动被调用。

    其中writeObject方法用于写入特定类的实例状态,以便相应的readObject方法可以恢复它。通过重写该方法,程序员可以获取对序列化的控制,可以自主决定可以哪些实例变量需要序列化,怎样序列化。该方法调用out.defaultWriteObject来保存JAVA对象的实例变量,从而可以实现序列化java对象状态的目的。

     1 package com.test.a;
     2 import java.io.IOException;
     3 import java.io.ObjectInputStream;
     4 import java.io.ObjectOutputStream;
     5 import java.io.Serializable;
     6 
     7 public class Person implements Serializable
     8 {
     9     private static final long serialVersionUID = 1L;
    10     int age;
    11     String address;
    12     double height;
    13     public Person(int age, String address, double height)
    14     {
    15         this.age = age;
    16         this.address = address;
    17         this.height = height;
    18     }
    19     
    20     //JAVA BEAN自定义的writeObject方法
    21     private void writeObject(ObjectOutputStream out) throws IOException
    22     {
    23         System.out.println("writeObejct ------");
    24         out.writeInt(age);
    25         out.writeObject(new StringBuffer(address).reverse());
    26         out.writeDouble(height);
    27     }
    28     
    29     private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException
    30     {
    31         System.out.println("readObject ------");
    32         this.age = in.readInt();
    33         this.address = ((StringBuffer)in.readObject()).reverse().toString();
    34         this.height = in.readDouble();
    35     }
    36 }
    View Code
     1 package com.test.a;
     2 import java.io.FileInputStream;
     3 import java.io.FileOutputStream;
     4 import java.io.IOException;
     5 import java.io.ObjectInputStream;
     6 import java.io.ObjectOutputStream;
     7 
     8 public class Test
     9 {
    10     public static void main(String[] args) throws IOException, IOException,
    11             ClassNotFoundException
    12     {
    13         ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(
    14                 "C:\Users\hermioner\Desktop\test.txt"));
    15         Person p = new Person(25, "China", 180);
    16         oos.writeObject(p);
    17         oos.close();
    18 
    19         ObjectInputStream ois = new ObjectInputStream(new FileInputStream(
    20                 "C:\Users\hermioner\Desktop\test.txt"));
    21         Person p1 = (Person) ois.readObject();
    22         System.out.println("age=" + p1.age  + ";address=" + p1.address
    23                 + ";height=" + p1.height);
    24         ois.close();
    25     }
    26 }
    View Code
    1 writeObejct ------
    2 readObject ------
    3 age=25;address=China;height=180.0
    View Code

    1、这个地方跟前面的区别就是在Person类中提供了writeObject方法和readObject方法,并且提供了具体的实现。

    2、在ObjectOutputStream调用writeObject方法执行过程,肯定调用了Person类的writeObject方法

    3、Person中自定义的writeObject和readObject只能是private才可以被调用,如果是public则不会被调用。???WHY

    4. 自定义序列API

       比如ArrayList和HashMap都自定义了序列化和反序列化中的核心方法

    5. readObject和writeObject的实现机制

    自定义序列化是由ObjectInput/OutputStream在序列化/反序列化时候通过反射检查该类是否存在以下方法(0个或多个):执行顺序从上往下,序列化调用1和2,反序列调用3和4;transient关键字当某个字段被声明为transient后,默认序列化机制就会忽略该字段。
    
    1 Object writeReplace() throws ObjectStreamException;可以通过此方法修改序列化的对象
    
    2 void writeObject(java.io.ObjectOutputStream out) throws IOException; 方法中调用defaultWriteObject() 使用writeObject的默认的序列化方式,除此之外可以加上一些其他的操作,如添加额外的序列化对象到输出:out.writeObject("XX")
    
    3 void readObject(java.io.ObjectInputStream in) throws Exception; 方法中调用defaultReadObject()使用readObject默认的反序列化方式,除此之外可以加上一些其他的操作,如读入额外的序列化对象到输入:in.readObject()
    
    4 Object readResolve() throws ObjectStreamException;可以通过此方法修改返回的对象
    方法writeObject处理对象的序列化。如果声明该方法,它将会被ObjectOutputStream调用而不是默认的序列化进程。如果你是第一次看见它,你会很惊奇尽管它们被外部类调用但事实上这是两个private的方法。并且它们既不存在于java.lang.Object,也没有在Serializable中声明。那么ObjectOutputStream如何使用它们的呢?这个吗,ObjectOutputStream使用了反射来寻找是否声明了这两个方法。因为ObjectOutputStream使用getPrivateMethod,所以这些方法不得不被声明为priate以至于供ObjectOutputStream来使用
  • 相关阅读:
    Hibernate HQL查询:
    Struts2里如何取得request,session,application
    Hibernate 报错org.hibernate.PropertyAccessException: IllegalArgumentException(已解决)
    PHP 读取文件的几种方法
    PHP 关于文件操作的简单介绍
    VB 中ListView 某一列的颜色添加不上去的解决方法
    SQL Server 版本号汇总
    C# 单元测试
    Web Api单元测试写法
    git bash 出现vim的时候怎么退出
  • 原文地址:https://www.cnblogs.com/Hermioner/p/9776434.html
Copyright © 2011-2022 走看看