18.7.19
序列化serializable
一、概述
需要被序列化的类需要实现Serializable接口
一个对象序列化的接口,一个类只有实现了Serializable接口,它的对象才是可序列化的。而实际上,Serializable是一个空接口,没有什么具体内容,它的目的只是简单的标识一个类的对象可以被序列化。实现serializable接口的作用是就是可以把对象存到字节Byte流,然后可以恢复。
二、用途:
a)当你想把的内存中的对象写入到硬盘的时候;
b)当你想用套接字在网络上传送对象的时候;
c)当你想通过RMI传输对象的时候;
三、注意:
1、在反序列化过程中,将使用该类的公用或受保护的无参数构造方法初始化不可序列化类的字段。可序列化的子类必须能够访问无参数的构造方法。可序列化子类的字段将从该流中还原。
2、当遍历一个图形时,可能会遇到不支持可序列化接口的对象。在此情况下,将抛出 NotSerializableException,并将标识不可序列化对象的类。
3、对象序列化不仅保存了对象的“全景图”,而且能追踪对象内包含的所有句柄并保存那些对象;接着又能对每个对象内包含的句柄进行追踪
4、使用transient关键字修饰的的变量,在序列化对象的过程中,该属性不会被序列化。
5、当一个父类实现序列化,子类自动实现序列化,不需要显式实现Serializable接口。
6、一个子类实现了 Serializable 接口,它的父类都没有实现 Serializable 接口,要想将父类对象也序列化,就需要让父类也实现Serializable 接口。
如果父类不实现 Serializable接口的话,就需要有默认的无参的构造函数。在反序列化时,为了构造父对象,只能调用父类的无参构造函数作为默认的父对象。因此当我们取父对象的变量值时,它的值是调用父类无参构造函数后的值。在这种情况下,在序列化时根据需要在父类无参构造函数中对变量进行初始化,否则的话,父类变量值都是默认声明的值,如 int 型的默认是 0,string 型的默认是 null。
ps:亲测有效,父类默认无参构造函数中给属性赋值,子类序列化再反序列化会有该值。
四、序列化步骤:(例子就不举了,网上太多)
首先要创建某些OutputStream对象:OutputStream outputStream = new FileOutputStream("output.txt")
将其封装到ObjectOutputStream对象内:ObjectOutputStream objectOutputStream = new ObjectOutputStream(outputStream);
此后只需调用writeObject()即可完成对象的序列化,并将其发送给OutputStream:objectOutputStream.writeObject(Object);
最后不要忘记关闭资源:objectOutputStream.close(), outputStream .close();
五、反序列化步骤:
首先要创建某些OutputStream对象:InputStream inputStream= new FileInputStream("output.txt")
将其封装到ObjectInputStream对象内:ObjectInputStream objectInputStream= new ObjectInputStream(inputStream);
此后只需调用writeObject()即可完成对象的反序列化:objectInputStream.readObject();
最后不要忘记关闭资源:objectInputStream.close(),inputStream.close();
六、serialVersionUID
序列化 ID 在 Eclipse 下提供了两种生成策略
一个是固定的 1L
一个是随机生成一个不重复的 long 类型数据(实际上是使用 JDK 工具,根据类名、接口名、成员方法及属性等来生成)
如果是通过网络传输的话,如果Person类的serialVersionUID不一致,那么反序列化就不能正常进行。例如在客户端A中Person类的serialVersionUID=1L,而在客户端B中Person类的serialVersionUID=2L 那么就不能重构这个Person对象。
如果没有特殊需求的话,使用用默认的 1L 就可以,这样可以确保代码一致时反序列化成功。那么随机生成的序列化 ID 有什么作用呢,有些时候,通过改变序列化 ID 可以用来限制某些用户的使用。
七、静态变量序列化
串行化只能保存对象的非静态成员交量,不能保存任何的成员方法和静态的成员变量,而且串行化保存的只是变量的值,对于变量的任何修饰符都不能保存。
ps:也就是说对一个类的静态成员变量序列化,不会被序列化到本地。但是反序列化后的对象中的静态成员变量会有值,原因是反序列化的时候会初始化。如果反序列化的时候原类中的静态变量改变了,则反序列化后的对象的静态变量是改变后的值。
八、Externalizable序列化
Externalizable继承了Serializable接口
public interface Externalizable extends java.io.Serializable
实现了Externalizable的User类:需要实现里面两个方法readExternal和writeExternal
这个Externalizable接口有两个方法,分别表示在序列化的时候需要序列化哪些字段和反序列化的时候能够反序列化哪些字段:
@Override
public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
System.out.println("read external.");
user = (String) in.readObject();
age = in.readInt();
}
@Override
public void writeExternal(ObjectOutput out) throws IOException {
System.out.println("write external.");
out.writeObject(user);//实现具体想要序列化的属性,与@Transient无关
out.writeInt(age);
}
需要注意的是:对于恢复Serializable对象,对象完全以它存储的二进制为基础来构造,而不调用构造器。而对于一个Externalizable对象,public的无参构造器将会被调用(因此你可以看到上面的测试结果中有LoginInfoConstructor这一行),之后再调用readExternal()方法。
如果没有发现public的无参构造器,那么将会报错。
参考:https://blog.csdn.net/u012940983/article/details/24793415
https://blog.csdn.net/bzduizhang/article/details/53412445
https://blog.csdn.net/kingo0/article/details/52300850
https://www.cnblogs.com/huhx/p/serializable.html
http://blademastercoder.github.io/2015/01/29/java-Serializable.html
https://blog.csdn.net/u011568312/article/details/57611440
https://my.oschina.net/itblog/blog/525883
——————————
18.7.18
transient关键字
我们都知道一个对象只要实现了Serilizable接口,这个对象就可以被序列化,java的这种序列化模式为开发者提供了很多便利,我们可以不必关系具体序列化的过程,只要这个类实现了Serilizable接口,这个类的所有属性和方法都会自动序列化。
然而在实际开发过程中,这个类的有些属性需要序列化,而其他属性不需要被序列化。
java 的transient关键字为我们提供了便利。将不需要序列化的属性前添加关键字transient,序列化对象的时候,这个属性就不会序列化到指定的目的地中。
小结:
1)一旦变量被transient修饰,变量将不再是对象持久化的一部分,该变量内容在序列化后无法获得访问。
2)transient关键字只能修饰变量,而不能修饰方法和类。注意,本地变量是不能被transient关键字修饰的。变量如果是用户自定义类变量,则该类需要实现Serializable接口。
3)被transient关键字修饰的变量不再能被序列化,一个静态变量不管是否被transient修饰,均不能被序列化。
第三点反序列化后还是能访问静态变量,并且有值,原因是反序列化时被jvm自动加回来了。
4)注:对于某些类型的属性,其状态是瞬时的,这样的属性是无法保存其状态的。例如一个线程属性或需要访问IO、本地资源、网络资源等的属性,对于这些字段,我们必须用transient关键字标明,否则编译器将报措。
例外:实现Externalizable接口的类中的被transient修饰的变量就可以被序列化。需要在writeExternal方法中进行手工指定所要序列化的变量,这与是否被transient修饰无关。
但是实现了Serializable接口的被transient修饰的变量则一定不会被序列化。