Java’s serialization process is as follows:
Java’s deserialization process is as follows:
Note: Not all classes need to be serialized, there are two main reasons
1) Security Question. Some classes in Java are sensitive classes, and the object data of this type is inconvenient to be disclosed to the outside world. The serialized object data is easy to crack, and the security of the data cannot be guaranteed. Therefore, generally this type of object will not be serialized.
2) Resource issues. Objects can be created using serialized byte streams, and this creation is not restricted. Sometimes creating too many objects can cause big resource problems, so such objects are not suitable for serialization.
Serializable
Serializable is a serialization interface provided by Java. It is an empty interface that provides standard serialization and deserialization operations for objects.
Serialization process:
Person p = new Person("name","id"); File file = new File("cache.txt"); FileOutputStream output = new FileOutputStream(file); ObjectOutputStream objectOutputStream = new ObjectOutputStream(output); objectOutputStream.writeObject(p); output.close(); objectOutputStream.close();
Deserialization process:
File file = new File("cache.txt"); FileInputStream input= new FileInputStream(file); ObjectInputStream objectInputStream = new ObjectInputStream(input); Person p = (Person)objectInputStream.readObject(); System.out.println(p.getName()+"---"+p.getId()); input.close(); objectInputStream.close();
Class members that need to be serialized
When an object is serialized, not all members need to be converted into binary byte sequences, because in order to save storage or Transmission space and improve serialization efficiency, some unnecessary members do not need to be serialized. These include:
Static variables. Because static variables belong to the attributes of the class and do not belong to a specific instance, there is no need to serialize during serialization. During deserialization, you can directly obtain the static member reference of the class.
Method. A method is just a set of operations. The method does not depend on the object, and the operations will not be different depending on the object. During deserialization, the method information can also be obtained directly from the class.
Serialization of inheritance relationship
When the parent class implements Serializable, the child class is serialized and the parent class will also be serialized.
When the parent class does not implement Serializable, the subclass is serialized and the parent class will not be serialized.
Serialization of reference relationships
If a serialization operation is performed on a class that implements Serializable, it will be serialized at the same time. The reference class performs serialization operations. If the reference class does not implement the Serializable interface, the JVM will throw java.io.NotSerializableExeception.
class Person implements Serializable{ private String name; private Tool tool = new Tool(); } class Tool implements Serializable{ }
At this time, if the Person class is serialized, the Tool class will be serialized at the same time. If the Tool class does not implement the Serializable interface, an exception will be thrown.
Protect sensitive data:
After a class is added with a serialization identifier, all attribute information of the object of this class will be serialized and then stored locally or transmitted over the network. Then sometimes some fields in the object are sensitive information and should not be exposed. If it is also serialized, it can be easily cracked, thus causing security risks, such as common password fields.
Java provides a keyword transient, which is the transient keyword. This keyword turns off the serialization of the field so that protected information is not exposed through serialization.
Serialization Identification ID
Imagine this scenario: Both ends are transmitting serialized objects over the network. For some reason, the versions of the classes used by both ends are different. Assume that the receiver's classes have been deleted. field. When the sender sends the serialized byte stream of the object to the receiver, it cannot be parsed because the receiver's class is missing several fields.
Java requires that classes that implement the serialization interface must declare a serialVersionUID static attribute. If there is no such attribute, the JVM will automatically declare the attribute and assign a value to the attribute (different values will be assigned when the class changes). The value of this attribute is unique and is used to identify different serialization classes. Only if the serialization identifier of the class is exactly the same, Java will perform deserialization work. This is the role of the serialization identifier.
For the aforementioned scenario, assuming that the serialVersionUID is not declared manually, the JVM assigns different values to the serialVersionUID in the classes used by the sender and the receiver, and the deserialization fails. When serialVersionUID is manually assigned, deserialization can be successful even if the fields of the class change.
Custom serialization strategy
Custom serialization strategy
Java provides a set of effective mechanisms that allow customized methods to be used for corresponding processing during serialization and deserialization. After the transmission parties agree on the serialization strategy, they only need to add a set of methods to the serialization class that needs to be transmitted to implement this set of strategies. During serialization, these specified methods will be automatically called for serialization and deserialization. The method is as follows:
1) private void writeObject(ObjectOutputSteam out) throws IOException
在方法的内部有重要的代码:out.defaultWriteObject() //将对象数据以默认方式写入到输出流中
2)private void readObject(ObjectInputStream in) throws IOException,ClassNotFoundException
同样的,此方法内部也有相似代码:in.defaultReadObject(); //以默认方式从输入流中恢复对象
这两个方法的作用分别是将特定的对象写入到输出流中以及从输入流中恢复特定的对象,通过这两个方法,用户即可实现自定义的序列化。当在实现Serializable接口的类中写了上面两个方法之后,序列化或反序列化该类时则会通过反射来调用这两个方法,从而实现自定义序列化。
限制序列化对象的数量
我们看下面的单例模式:
public class Singleton implements Serializable { private volatile static Singleton mInstance; private Singleton() { } public static Singleton getInstance() { if (mInstance == null) { synchronized (Singleton.class) { if (mInstance == null) { mInstance = new Singleton(); } } } return mInstance; } }
此时通过反序列化获取实例,则单例模式会失效。那该如何解决这个问题呢?
Java有一种机制,可以让我们在序列化和反序列化时,可以根据自己的需要,写入或读取指定的实例。使用这种机制,需要在实现Serializable接口的类中添加两个方法:
private Object readResolve() //如果用户在序列化类中添加了该方法,则在进行反序列化时,使用该方法返回的对象,作为反序列化对象。
private Object writeReplace() //如果用户在序列化类中添加了该方法,则在进行序列化时,序列化该类返回的对象。
再看使用了该机制的单例模式:
public class Singleton implements Serializable { private volatile static Singleton mInstance; private Singleton() { } public static Singleton getInstance() { if (mInstance == null) { synchronized (Singleton.class) { if (mInstance == null) { mInstance = new Singleton(); } } } return mInstance; } private Object readResolve() { return getInstance(); } private Object writeReplace() { return getInstance(); } }
此时的通过反序列化得到的对象也是同一个,即单例模式依然有效!
相关文章:
Java序列化Serializable和Externalizable区别的示例代码