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. 将一个对象序列化之后存储到文件中

8f900a89c6347c561fdf2122f13be562.png

961ddebeb323a10fe0623af514929fc1.png

1 packagecom.test.a;2

3 importjava.io.Serializable;4

5 public class Person implementsSerializable{6 private static final long serialVersionUID = 1L;7 publicString name;8 public intage;9

10 public Person(String name, intage) {11 this.name =name;12 this.age =age;13 }14

15 publicString getName() {16 returnname;17 }18

19 public voidsetName(String name) {20 this.name =name;21 }22

23 public intgetAge() {24 returnage;25 }26

27 public void setAge(intage) {28 this.age =age;29 }30

31 }

View Code

8f900a89c6347c561fdf2122f13be562.png

961ddebeb323a10fe0623af514929fc1.png

1 packagecom.test.a;2

3 importjava.io.FileNotFoundException;4 importjava.io.FileOutputStream;5 importjava.io.IOException;6 importjava.io.ObjectOutputStream;7

8

9 public classTest {10 public static void main(String args[]) throwsFileNotFoundException, IOException{11 Person person=new Person("zhangsan", 23);12 String path="C:\\Users\\hermioner\\Desktop\\test.txt";13 ObjectOutputStream objectOutputStream=new ObjectOutputStream(newFileOutputStream(path));14 objectOutputStream.writeObject(person);15 objectOutputStream.close();16 }17 }

View Code

c9e408697bcb638f5c769e2da2e28301.png

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

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

8f900a89c6347c561fdf2122f13be562.png

961ddebeb323a10fe0623af514929fc1.png

1 packagecom.test.a;2

3 importjava.io.FileInputStream;4 importjava.io.FileNotFoundException;5 importjava.io.FileOutputStream;6 importjava.io.IOException;7 importjava.io.ObjectInputStream;8 importjava.io.ObjectOutputStream;9

10

11 public classTest {12 public static void main(String args[]) throwsFileNotFoundException, IOException, ClassNotFoundException{13 Person person=new Person("zhangsan", 23);14 String path="C:\\Users\\hermioner\\Desktop\\test.txt";15 ObjectOutputStream objectOutputStream=new ObjectOutputStream(newFileOutputStream(path));16 objectOutputStream.writeObject(person);17 objectOutputStream.close();18

19 ObjectInputStream objectInputStream=new ObjectInputStream(newFileInputStream(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 zhangsan28 23

View Code

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

3. 多个对象的反序列化

8f900a89c6347c561fdf2122f13be562.png

961ddebeb323a10fe0623af514929fc1.png

1 packagecom.test.a;2

3 importjava.io.FileInputStream;4 importjava.io.FileNotFoundException;5 importjava.io.FileOutputStream;6 importjava.io.IOException;7 importjava.io.ObjectInputStream;8 importjava.io.ObjectOutputStream;9

10

11 public classTest {12 public static void main(String args[]) throwsFileNotFoundException, 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(newFileOutputStream(path));17 objectOutputStream.writeObject(person);18 objectOutputStream.writeObject(person2);19 objectOutputStream.close();20

21 ObjectInputStream objectInputStream=new ObjectInputStream(newFileInputStream(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 zhangsan34 23

35 lisi36 13

View Code

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

4. 对象引用的序列化

8f900a89c6347c561fdf2122f13be562.png

961ddebeb323a10fe0623af514929fc1.png

1 packagecom.test.a;2

3 importjava.io.Serializable;4

5 public class Person implementsSerializable {6 private static final long serialVersionUID = 1L;7 publicString name;8 public intage;9 publicMan man;10

11 public Person(String name, intage, Man man) {12 this.name =name;13 this.age =age;14 this.man =man;15 }16

17 publicString getName() {18 returnname;19 }20

21 public voidsetName(String name) {22 this.name =name;23 }24

25 public intgetAge() {26 returnage;27 }28

29 public void setAge(intage) {30 this.age =age;31 }32

33 publicMan getMan() {34 returnman;35 }36

37 public voidsetMan(Man man) {38 this.man =man;39 }40

41 }

View Code

8f900a89c6347c561fdf2122f13be562.png

961ddebeb323a10fe0623af514929fc1.png

1 packagecom.test.a;2

3 importjava.io.Serializable;4

5 public abstract class Man implements Serializable{ //必须实现Serializable

6 public abstract voidgetInfo();7 }8

9

10 packagecom.test.a;11

12 public class Femal extendsMan{13

14 @Override15 public voidgetInfo() {16 System.out.println("Femal");17

18 }19

20 }21

22 packagecom.test.a;23

24 public class Male extendsMan{25

26 @Override27 public voidgetInfo() {28 System.out.println("male");29

30 }31

32 }

View Code

8f900a89c6347c561fdf2122f13be562.png

961ddebeb323a10fe0623af514929fc1.png

1 packagecom.test.a;2

3 importjava.io.FileInputStream;4 importjava.io.FileNotFoundException;5 importjava.io.FileOutputStream;6 importjava.io.IOException;7 importjava.io.ObjectInputStream;8 importjava.io.ObjectOutputStream;9

10

11 public classTest {12 public static void main(String args[]) throwsFileNotFoundException, IOException, ClassNotFoundException{13 Person person=new Person("zhangsan",24, newFemal());14 String path="C:\\Users\\hermioner\\Desktop\\test.txt";15 ObjectOutputStream objectOutputStream=new ObjectOutputStream(newFileOutputStream(path));16 objectOutputStream.writeObject(person);17 objectOutputStream.close();18

19 ObjectInputStream objectInputStream=new ObjectInputStream(newFileInputStream(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

8f900a89c6347c561fdf2122f13be562.png

961ddebeb323a10fe0623af514929fc1.png

1 zhangsan2 24

3 Femal

View Code

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

四. serialVersionUID

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

private static final long serialVersionUID = 1L;

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

c28d7f9022c34fc1bb31f05afdde4657.png

看上面的提示信息,可以知道有两种方式来生成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

8f900a89c6347c561fdf2122f13be562.png

961ddebeb323a10fe0623af514929fc1.png

1 packagecom.test.a;2

3 importjava.io.Serializable;4

5 public class Person implementsSerializable {6

7 publicString name;8 public intage;9

10

11 public Person(String name,intage) {12 this.name=name;13 this.age=age;14 }15

16 publicString getName() {17 returnname;18 }19

20 public voidsetName(String name) {21 this.name =name;22 }23

24 public intgetAge() {25 returnage;26 }27

28 public void setAge(intage) {29 this.age =age;30 }31 }

View Code

8f900a89c6347c561fdf2122f13be562.png

961ddebeb323a10fe0623af514929fc1.png

1 packagecom.test.a;2

3 importjava.io.FileInputStream;4 importjava.io.FileNotFoundException;5 importjava.io.FileOutputStream;6 importjava.io.IOException;7 importjava.io.ObjectInputStream;8 importjava.io.ObjectOutputStream;9

10

11 public classTest {12 public static void main(String args[]) throwsFileNotFoundException, IOException, ClassNotFoundException{13 Person person=new Person("zhangsan",24);14 String path="C:\\Users\\hermioner\\Desktop\\test.txt";15 ObjectOutputStream objectOutputStream=new ObjectOutputStream(newFileOutputStream(path));16 objectOutputStream.writeObject(person);17 objectOutputStream.close();18

19 ObjectInputStream objectInputStream=new ObjectInputStream(newFileInputStream(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@448139f030 zhangsan31 24

View Code

com.test.a.Person@448139f0

zhangsan

24

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

8f900a89c6347c561fdf2122f13be562.png

961ddebeb323a10fe0623af514929fc1.png

1 packagecom.test.a;2

3 importjava.io.Serializable;4

5 public class Person implementsSerializable {6

7 publicString name;8 public intage;9 public String sex;/new added

10

11

12 public Person(String name,intage) {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 publicString getName() {25 returnname;26 }27

28 public voidsetName(String name) {29 this.name =name;30 }31

32 public intgetAge() {33 returnage;34 }35

36 public void setAge(intage) {37 this.age =age;38 }39 }

View Code

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

8f900a89c6347c561fdf2122f13be562.png

961ddebeb323a10fe0623af514929fc1.png

1 packagecom.test.a;2

3 importjava.io.FileInputStream;4 importjava.io.FileNotFoundException;5 importjava.io.FileOutputStream;6 importjava.io.IOException;7 importjava.io.ObjectInputStream;8 importjava.io.ObjectOutputStream;9

10

11 public classTest {12 public static void main(String args[]) throwsFileNotFoundException, 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(newFileInputStream(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

8f900a89c6347c561fdf2122f13be562.png

961ddebeb323a10fe0623af514929fc1.png

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关键字来修饰实例变量,该变量就会被完全隔离在序列化机制之外。

8f900a89c6347c561fdf2122f13be562.png

961ddebeb323a10fe0623af514929fc1.png

1 packagecom.test.a;2

3 importjava.io.Serializable;4

5 public class Person implementsSerializable {6 private static final long serialVersionUID = -7232940355925514760L;7 publicString name;8 public transient intage;9

10 public Person(String name,intage) {11 this.name=name;12 this.age=age;13 }14

15 publicString getName() {16 returnname;17 }18

19 public voidsetName(String name) {20 this.name =name;21 }22

23 public intgetAge() {24 returnage;25 }26

27 public void setAge(intage) {28 this.age =age;29 }30 }

View Code

8f900a89c6347c561fdf2122f13be562.png

961ddebeb323a10fe0623af514929fc1.png

1 packagecom.test.a;2

3 importjava.io.FileInputStream;4 importjava.io.FileNotFoundException;5 importjava.io.FileOutputStream;6 importjava.io.IOException;7 importjava.io.ObjectInputStream;8 importjava.io.ObjectOutputStream;9

10

11 public classTest {12 public static void main(String args[]) throwsFileNotFoundException, IOException, ClassNotFoundException{13 Person person=new Person("zhangsan",24);14 String path="C:\\Users\\hermioner\\Desktop\\test.txt";15 ObjectOutputStream objectOutputStream=new ObjectOutputStream(newFileOutputStream(path));16 objectOutputStream.writeObject(person);17 objectOutputStream.close();18

19 ObjectInputStream objectInputStream=new ObjectInputStream(newFileInputStream(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

8f900a89c6347c561fdf2122f13be562.png

961ddebeb323a10fe0623af514929fc1.png

1 com.test.a.Person@448139f02 zhangsan3 0

View Code

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

2. 自定义序列化

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

writeObject()

readObject()

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

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

8f900a89c6347c561fdf2122f13be562.png

961ddebeb323a10fe0623af514929fc1.png

1 packagecom.test.a;2 importjava.io.IOException;3 importjava.io.ObjectInputStream;4 importjava.io.ObjectOutputStream;5 importjava.io.Serializable;6

7 public class Person implementsSerializable8 {9 private static final long serialVersionUID = 1L;10 intage;11 String address;12 doubleheight;13 public Person(int age, String address, doubleheight)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) throwsIOException22 {23 System.out.println("writeObejct ------");24 out.writeInt(age);25 out.writeObject(newStringBuffer(address).reverse());26 out.writeDouble(height);27 }28

29 private void readObject(ObjectInputStream in) throwsIOException, ClassNotFoundException30 {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

8f900a89c6347c561fdf2122f13be562.png

961ddebeb323a10fe0623af514929fc1.png

1 packagecom.test.a;2 importjava.io.FileInputStream;3 importjava.io.FileOutputStream;4 importjava.io.IOException;5 importjava.io.ObjectInputStream;6 importjava.io.ObjectOutputStream;7

8 public classTest9 {10 public static void main(String[] args) throwsIOException, IOException,11 ClassNotFoundException12 {13 ObjectOutputStream oos = new ObjectOutputStream(newFileOutputStream(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(newFileInputStream(20 "C:\\Users\\hermioner\\Desktop\\test.txt"));21 Person p1 =(Person) ois.readObject();22 System.out.println("age=" + p1.age + ";address=" +p1.address23 + ";height=" +p1.height);24 ois.close();25 }26 }

View Code

8f900a89c6347c561fdf2122f13be562.png

961ddebeb323a10fe0623af514929fc1.png

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()throwsObjectStreamException;可以通过此方法修改序列化的对象

2void writeObject(java.io.ObjectOutputStream out) throws IOException; 方法中调用defaultWriteObject() 使用writeObject的默认的序列化方式,除此之外可以加上一些其他的操作,如添加额外的序列化对象到输出:out.writeObject("XX")

3void readObject(java.io.ObjectInputStream in) throwsException; 方法中调用defaultReadObject()使用readObject默认的反序列化方式,除此之外可以加上一些其他的操作,如读入额外的序列化对象到输入:in.readObject()

4 Object readResolve()throws ObjectStreamException;可以通过此方法修改返回的对象

方法writeObject处理对象的序列化。如果声明该方法,它将会被ObjectOutputStream调用而不是默认的序列化进程。如果你是第一次看见它,你会很惊奇尽管它们被外部类调用但事实上这是两个private的方法。并且它们既不存在于java.lang.Object,也没有在Serializable中声明。那么ObjectOutputStream如何使用它们的呢?这个吗,ObjectOutputStream使用了反射来寻找是否声明了这两个方法。因为ObjectOutputStream使用getPrivateMethod,所以这些方法不得不被声明为priate以至于供ObjectOutputStream来使用。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值