“对象序列化”是什么意思?你能举例解释一下吗?
当前回答
序列化是通过将对象转换为字节码来保存对象的特定状态的过程。转换后的字节码用于在2个JVM之间传输对象状态,接收JVM在其中反序列化字节码以检索共享对象的状态。 序列化和反序列化是使用serialVersionUID作为相关jvm中的引用来完成的。
在Java中,使用Serialization和Externalisation接口也可以实现同样的功能
其他回答
您可以将序列化看作是将对象实例转换为字节序列的过程(根据实现的不同,字节序列可能是二进制的,也可能不是)。
当您希望通过网络传输一个对象数据时(例如从一个JVM传输到另一个JVM),它非常有用。
在Java中,序列化机制内置于平台中,但您需要实现Serializable接口以使对象可序列化。
还可以通过将属性标记为transient来防止对象中的某些数据被序列化。
最后,您可以覆盖默认机制,并提供您自己的;这可能适用于某些特殊情况。为此,您需要使用java中的一个隐藏特性。
重要的是要注意,被序列化的是对象的“值”或内容,而不是类定义。因此方法不是序列化的。
下面是一个非常基本的示例,带有注释,以方便阅读:
import java.io.*;
import java.util.*;
// This class implements "Serializable" to let the system know
// it's ok to do it. You as programmer are aware of that.
public class SerializationSample implements Serializable {
// These attributes conform the "value" of the object.
// These two will be serialized;
private String aString = "The value of that string";
private int someInteger = 0;
// But this won't since it is marked as transient.
private transient List<File> unInterestingLongLongList;
// Main method to test.
public static void main( String [] args ) throws IOException {
// Create a sample object, that contains the default values.
SerializationSample instance = new SerializationSample();
// The "ObjectOutputStream" class has the default
// definition to serialize an object.
ObjectOutputStream oos = new ObjectOutputStream(
// By using "FileOutputStream" we will
// Write it to a File in the file system
// It could have been a Socket to another
// machine, a database, an in memory array, etc.
new FileOutputStream(new File("o.ser")));
// do the magic
oos.writeObject( instance );
// close the writing.
oos.close();
}
}
当我们运行这个程序时,文件“o.ser”被创建,我们可以看到后面发生了什么。
如果我们将:someInteger的值更改为,例如Integer。MAX_VALUE,我们可以比较输出,看看有什么不同。
下面的截图正好显示了这种差异:
你能看出区别吗?;)
在Java序列化中还有一个额外的相关字段:serialversionUID,但我想这已经太长了,无法涵盖它。
将文件返回为Object: http://www.tutorialspoint.com/java/java_serialization.htm
import java.io.*;
public class SerializeDemo
{
public static void main(String [] args)
{
Employee e = new Employee();
e.name = "Reyan Ali";
e.address = "Phokka Kuan, Ambehta Peer";
e.SSN = 11122333;
e.number = 101;
try
{
FileOutputStream fileOut =
new FileOutputStream("/tmp/employee.ser");
ObjectOutputStream out = new ObjectOutputStream(fileOut);
out.writeObject(e);
out.close();
fileOut.close();
System.out.printf("Serialized data is saved in /tmp/employee.ser");
}catch(IOException i)
{
i.printStackTrace();
}
}
}
import java.io.*;
public class DeserializeDemo
{
public static void main(String [] args)
{
Employee e = null;
try
{
FileInputStream fileIn = new FileInputStream("/tmp/employee.ser");
ObjectInputStream in = new ObjectInputStream(fileIn);
e = (Employee) in.readObject();
in.close();
fileIn.close();
}catch(IOException i)
{
i.printStackTrace();
return;
}catch(ClassNotFoundException c)
{
System.out.println("Employee class not found");
c.printStackTrace();
return;
}
System.out.println("Deserialized Employee...");
System.out.println("Name: " + e.name);
System.out.println("Address: " + e.address);
System.out.println("SSN: " + e.SSN);
System.out.println("Number: " + e.number);
}
}
序列化是在内存中获取一个“活动”对象,并将其转换为可以存储在某处的格式(例如。在内存中,在磁盘上),然后“反序列化”回一个活动对象。
我在自己的博客上说:
下面是关于序列化的详细解释:(我自己的博客)
序列化:
序列化是持久化对象状态的过程。它以字节序列的形式表示和存储。这可以存储在一个文件中。从文件中读取对象状态并恢复它的过程称为反序列化。
序列化的需求是什么?
In modern day architecture, there is always a need to store object state and then retrieve it. For example in Hibernate, to store a object we should make the class Serializable. What it does, is that once the object state is saved in the form of bytes it can be transferred to another system which can then read from the state and retrieve the class. The object state can come from a database or a different jvm or from a separate component. With the help of Serialization we can retrieve the Object state.
代码示例及说明:
首先让我们来看看Item Class:
public class Item implements Serializable{
/**
* This is the Serializable class
*/
private static final long serialVersionUID = 475918891428093041L;
private Long itemId;
private String itemName;
private transient Double itemCostPrice;
public Item(Long itemId, String itemName, Double itemCostPrice) {
super();
this.itemId = itemId;
this.itemName = itemName;
this.itemCostPrice = itemCostPrice;
}
public Long getItemId() {
return itemId;
}
@Override
public String toString() {
return "Item [itemId=" + itemId + ", itemName=" + itemName + ", itemCostPrice=" + itemCostPrice + "]";
}
public void setItemId(Long itemId) {
this.itemId = itemId;
}
public String getItemName() {
return itemName;
}
public void setItemName(String itemName) {
this.itemName = itemName;
}
public Double getItemCostPrice() {
return itemCostPrice;
}
public void setItemCostPrice(Double itemCostPrice) {
this.itemCostPrice = itemCostPrice;
}
}
在上面的代码中,可以看到Item类实现了Serializable。
这是使类可序列化的接口。
现在我们可以看到一个名为serialVersionUID的变量被初始化为Long变量。这个数字是由编译器根据类的状态和类属性计算出来的。这个数字将帮助jvm在从文件中读取对象的状态时识别对象的状态。
为此,我们可以看看官方的Oracle文档:
The serialization runtime associates with each serializable class a version number, called a serialVersionUID, which is used during deserialization to verify that the sender and receiver of a serialized object have loaded classes for that object that are compatible with respect to serialization. If the receiver has loaded a class for the object that has a different serialVersionUID than that of the corresponding sender's class, then deserialization will result in an InvalidClassException. A serializable class can declare its own serialVersionUID explicitly by declaring a field named "serialVersionUID" that must be static, final, and of type long: ANY-ACCESS-MODIFIER static final long serialVersionUID = 42L; If a serializable class does not explicitly declare a serialVersionUID, then the serialization runtime will calculate a default serialVersionUID value for that class based on various aspects of the class, as described in the Java(TM) Object Serialization Specification. However, it is strongly recommended that all serializable classes explicitly declare serialVersionUID values, since the default serialVersionUID computation is highly sensitive to class details that may vary depending on compiler implementations, and can thus result in unexpected InvalidClassExceptions during deserialization. Therefore, to guarantee a consistent serialVersionUID value across different java compiler implementations, a serializable class must declare an explicit serialVersionUID value. It is also strongly advised that explicit serialVersionUID declarations use the private modifier where possible, since such declarations apply only to the immediately declaring class--serialVersionUID fields are not useful as inherited members.
如果你注意到我们使用的另一个关键字是瞬态的。
如果字段不可序列化,则必须将其标记为transient。这里我们将itemCostPrice标记为transient,不希望将其写入文件中
现在让我们看看如何在文件中写入对象的状态,然后从那里读取它。
public class SerializationExample {
public static void main(String[] args){
serialize();
deserialize();
}
public static void serialize(){
Item item = new Item(1L,"Pen", 12.55);
System.out.println("Before Serialization" + item);
FileOutputStream fileOut;
try {
fileOut = new FileOutputStream("/tmp/item.ser");
ObjectOutputStream out = new ObjectOutputStream(fileOut);
out.writeObject(item);
out.close();
fileOut.close();
System.out.println("Serialized data is saved in /tmp/item.ser");
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
public static void deserialize(){
Item item;
try {
FileInputStream fileIn = new FileInputStream("/tmp/item.ser");
ObjectInputStream in = new ObjectInputStream(fileIn);
item = (Item) in.readObject();
System.out.println("Serialized data is read from /tmp/item.ser");
System.out.println("After Deserialization" + item);
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}
在上面的例子中,我们可以看到一个对象的序列化和反序列化。
为此我们使用了两个类。为了序列化对象,我们使用了ObjectOutputStream。我们已经使用writeObject方法将对象写入文件。
对于反序列化,我们使用ObjectInputStream从文件中读取对象。它使用readObject从文件中读取对象数据。
上述代码的输出如下所示:
Before SerializationItem [itemId=1, itemName=Pen, itemCostPrice=12.55]
Serialized data is saved in /tmp/item.ser
After DeserializationItem [itemId=1, itemName=Pen, itemCostPrice=null]
注意,来自反序列化对象的itemCostPrice是空的,因为它没有被写入。
在本文的第一部分中,我们已经讨论了Java序列化的基础知识。
现在让我们深入讨论它以及它是如何工作的。
首先让我们从serialversionuid开始。
serialVersionUID用作Serializable类中的版本控制。
如果您没有显式地声明serialVersionUID, JVM将根据Serializable类的各种属性自动为您声明。
Java的计算serialversionuid的算法(在这里阅读更多细节)
The class name. The class modifiers written as a 32-bit integer. The name of each interface sorted by name. For each field of the class sorted by field name (except private static and private transient fields: The name of the field. The modifiers of the field written as a 32-bit integer. The descriptor of the field. If a class initializer exists, write out the following: The name of the method, . The modifier of the method, java.lang.reflect.Modifier.STATIC, written as a 32-bit integer. The descriptor of the method, ()V. For each non-private constructor sorted by method name and signature: The name of the method, . The modifiers of the method written as a 32-bit integer. The descriptor of the method. For each non-private method sorted by method name and signature: The name of the method. The modifiers of the method written as a 32-bit integer. The descriptor of the method. The SHA-1 algorithm is executed on the stream of bytes produced by DataOutputStream and produces five 32-bit values sha[0..4]. The hash value is assembled from the first and second 32-bit values of the SHA-1 message digest. If the result of the message digest, the five 32-bit words H0 H1 H2 H3 H4, is in an array of five int values named sha, the hash value would be computed as follows:
long hash = ((sha[0] >>> 24) & 0xFF) |
> ((sha[0] >>> 16) & 0xFF) << 8 |
> ((sha[0] >>> 8) & 0xFF) << 16 |
> ((sha[0] >>> 0) & 0xFF) << 24 |
> ((sha[1] >>> 24) & 0xFF) << 32 |
> ((sha[1] >>> 16) & 0xFF) << 40 |
> ((sha[1] >>> 8) & 0xFF) << 48 |
> ((sha[1] >>> 0) & 0xFF) << 56;
Java的序列化算法
The algorithm to serialize an object is described as below: 1. It writes out the metadata of the class associated with an instance. 2. It recursively writes out the description of the superclass until it finds java.lang.object. 3. Once it finishes writing the metadata information, it then starts with the actual data associated with the instance. But this time, it starts from the topmost superclass. 4. It recursively writes the data associated with the instance, starting from the least superclass to the most-derived class.
要记住的事情:
Static fields in a class cannot be serialized. public class A implements Serializable{ String s; static String staticString = "I won't be serializable"; } If the serialversionuid is different in the read class it will throw a InvalidClassException exception. If a class implements serializable then all its sub classes will also be serializable. public class A implements Serializable {....}; public class B extends A{...} //also Serializable If a class has a reference of another class, all the references must be Serializable otherwise serialization process will not be performed. In such case, NotSerializableException is thrown at runtime.
Eg:
public class B{
String s,
A a; // class A needs to be serializable i.e. it must implement Serializable
}
Java对象序列化
序列化是一种将Java对象图转换为用于存储(到磁盘文件)或传输(通过网络)的字节数组的机制,然后通过使用反序列化,我们可以恢复对象图。 使用引用共享机制正确地恢复对象的图。但是在存储之前,请检查input-file/network中的serialVersionUID和.class文件中的serialVersionUID是否相同。如果不是,则抛出java.io.InvalidClassException。
每个版本化的类必须确定它能够为其写入流和从中读取流的原始类版本。例如,一个有版本控制的类必须声明: serialVersionUID的语法 // ANY-ACCESS-MODIFIER static final long serialVersionUID = (64-bit has)L; private static final long serialVersionUID = 3487495895819393L;
serialVersionUID是序列化过程所必需的。但是开发人员可以选择将其添加到java源文件中。如果没有包含serialVersionUID,序列化运行时将生成serialVersionUID并将其与类关联。序列化对象将包含这个serialVersionUID以及其他数据。
注意——强烈建议所有可序列化类显式声明serialVersionUID,因为默认的serialVersionUID计算对类细节非常敏感,这些细节可能会根据编译器实现的不同而不同,因此可能会在反序列化期间导致意外的serialVersionUID冲突,导致反序列化失败。
检查可序列化类
Java对象只能序列化。如果一个类或它的任何超类实现了java.io.Serializable接口 或其子接口java.io.Externalizable。
A class must implement java.io.Serializable interface in order to serialize its object successfully. Serializable is a marker interface and used to inform the compiler that the class implementing it has to be added serializable behavior. Here Java Virtual Machine (JVM) is responsible for its automatic serialization. transient Keyword: java.io.Serializable interface While serializing an object, if we don't want certain data members of the object to be serialized we can use the transient modifier. The transient keyword will prevent that data member from being serialized. Fields declared as transient or static are ignored by the serialization process. TRANSIENT & VOLATILE +--------------+--------+-------------------------------------+ | Flag Name | Value | Interpretation | +--------------+--------+-------------------------------------+ | ACC_VOLATILE | 0x0040 | Declared volatile; cannot be cached.| +--------------+--------+-------------------------------------+ |ACC_TRANSIENT | 0x0080 | Declared transient; not written or | | | | read by a persistent object manager.| +--------------+--------+-------------------------------------+ class Employee implements Serializable { private static final long serialVersionUID = 2L; static int id; int eno; String name; transient String password; // Using transient keyword means its not going to be Serialized. } Implementing the Externalizable interface allows the object to assume complete control over the contents and format of the object's serialized form. The methods of the Externalizable interface, writeExternal and readExternal, are called to save and restore the objects state. When implemented by a class they can write and read their own state using all of the methods of ObjectOutput and ObjectInput. It is the responsibility of the objects to handle any versioning that occurs. class Emp implements Externalizable { int eno; String name; transient String password; // No use of transient, we need to take care of write and read. @Override public void writeExternal(ObjectOutput out) throws IOException { out.writeInt(eno); out.writeUTF(name); //out.writeUTF(password); } @Override public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException { this.eno = in.readInt(); this.name = in.readUTF(); //this.password = in.readUTF(); // java.io.EOFException } } Only objects that support the java.io.Serializable or java.io.Externalizable interface can be written to/read from streams. The class of each serializable object is encoded including the class name and signature of the class, the values of the object's fields and arrays, and the closure of any other objects referenced from the initial objects.
文件的序列化示例
public class SerializationDemo {
static String fileName = "D:/serializable_file.ser";
public static void main(String[] args) throws IOException, ClassNotFoundException, InstantiationException, IllegalAccessException {
Employee emp = new Employee( );
Employee.id = 1; // Can not Serialize Class data.
emp.eno = 77;
emp.name = "Yash";
emp.password = "confidential";
objects_WriteRead(emp, fileName);
Emp e = new Emp( );
e.eno = 77;
e.name = "Yash";
e.password = "confidential";
objects_WriteRead_External(e, fileName);
/*String stubHost = "127.0.0.1";
Integer anyFreePort = 7777;
socketRead(anyFreePort); //Thread1
socketWrite(emp, stubHost, anyFreePort); //Thread2*/
}
public static void objects_WriteRead( Employee obj, String serFilename ) throws IOException{
FileOutputStream fos = new FileOutputStream( new File( serFilename ) );
ObjectOutputStream objectOut = new ObjectOutputStream( fos );
objectOut.writeObject( obj );
objectOut.close();
fos.close();
System.out.println("Data Stored in to a file");
try {
FileInputStream fis = new FileInputStream( new File( serFilename ) );
ObjectInputStream ois = new ObjectInputStream( fis );
Object readObject;
readObject = ois.readObject();
String calssName = readObject.getClass().getName();
System.out.println("Restoring Class Name : "+ calssName); // InvalidClassException
Employee emp = (Employee) readObject;
System.out.format("Obj[No:%s, Name:%s, Pass:%s]", emp.eno, emp.name, emp.password);
ois.close();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
public static void objects_WriteRead_External( Emp obj, String serFilename ) throws IOException {
FileOutputStream fos = new FileOutputStream(new File( serFilename ));
ObjectOutputStream objectOut = new ObjectOutputStream( fos );
obj.writeExternal( objectOut );
objectOut.flush();
fos.close();
System.out.println("Data Stored in to a file");
try {
// create a new instance and read the assign the contents from stream.
Emp emp = new Emp();
FileInputStream fis = new FileInputStream(new File( serFilename ));
ObjectInputStream ois = new ObjectInputStream( fis );
emp.readExternal(ois);
System.out.format("Obj[No:%s, Name:%s, Pass:%s]", emp.eno, emp.name, emp.password);
ois.close();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}
可通过网络序列化的例子
将对象的状态分布在不同的地址空间中,或者在同一台计算机上的不同进程中,或者甚至在通过网络连接的多台计算机中,但它们通过共享数据和调用方法一起工作。
数据编组 残根和骷髅
/**
* Creates a stream socket and connects it to the specified port number on the named host.
*/
public static void socketWrite(Employee objectToSend, String stubHost, Integer anyFreePort) {
try { // CLIENT - Stub[marshalling]
Socket client = new Socket(stubHost, anyFreePort);
ObjectOutputStream out = new ObjectOutputStream(client.getOutputStream());
out.writeObject(objectToSend);
out.flush();
client.close();
} catch (IOException e) {
e.printStackTrace();
}
}
// Creates a server socket, bound to the specified port.
public static void socketRead( Integer anyFreePort ) {
try { // SERVER - Stub[unmarshalling ]
ServerSocket serverSocket = new ServerSocket( anyFreePort );
System.out.println("Server serves on port and waiting for a client to communicate");
/*System.in.read();
System.in.read();*/
Socket socket = serverSocket.accept();
System.out.println("Client request to communicate on port server accepts it.");
ObjectInputStream in = new ObjectInputStream(socket.getInputStream());
Employee objectReceived = (Employee) in.readObject();
System.out.println("Server Obj : "+ objectReceived.name );
socket.close();
serverSocket.close();
} catch (IOException | ClassNotFoundException e) {
e.printStackTrace();
}
}
@see
实现Serializable和Externalizable
推荐文章
- 指定的子节点已经有一个父节点。你必须先在子对象的父对象上调用removeView() (Android)
- 将流转换为字符串并返回
- 对于一个布尔字段,它的getter/setter的命名约定是什么?
- 如何获得当前屏幕方向?
- 如何在Android中渲染PDF文件
- 如何计算一个元素在列表中出现的次数
- c++中类似于java的instanceof
- 我如何解决错误“minCompileSdk(31)指定在一个依赖的AAR元数据”在本机Java或Kotlin?
- 如何POST表单数据与Spring RestTemplate?
- Mockito中检测到未完成的存根
- 我应该如何复制字符串在Java?
- “while(true)”循环有那么糟糕吗?
- 这个方法签名中的省略号(…)是干什么用的?
- Java:如何测试调用System.exit()的方法?
- 带有返回类型的Java方法在没有返回语句的情况下编译