“对象序列化”是什么意思?你能用一些例子解释一下吗?
15 回答
序列化是将对象转换为一系列字节,以便可以轻松地将对象保存到持久存储或通过通信链路流式传输。然后可以对字节流进行反序列化 - 转换为原始对象的副本。
您可以将序列化视为将对象实例转换为字节序列(取决于实现可能是二进制或非二进制)的过程。
当您想通过网络传输一个对象数据时,它非常有用,例如从一个 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,但我想这已经太长了,无法涵盖它。
敢于回答6年前的问题,为刚接触Java的人增加了一个非常高层次的理解
什么是序列化?
将对象转换为字节
什么是反序列化?
将字节转换回对象(反序列化)。
什么时候使用序列化?
当我们想要持久化对象时。当我们希望对象在 JVM 的生命周期之外存在时。
现实世界的例子:
ATM:当账户持有人尝试通过ATM从服务器取款时,账户持有人的取款明细等信息将被序列化并发送到服务器,在服务器上对明细进行反序列化并用于执行操作。
java中如何进行序列化。
实现
java.io.Serializable
接口(标记接口,所以没有方法可以实现)。持久化对象:使用
java.io.ObjectOutputStream
类,一个过滤器流,它是低级字节流的包装器(将对象写入文件系统或通过网络线路传输扁平对象并在另一端重建)。
writeObject(<<instance>>)
- 写一个对象readObject()
- 读取序列化对象
记住:
序列化对象时,只会保存对象的状态,不会保存对象的类文件或方法。
当您序列化一个 2 字节的对象时,您会看到 51 字节的序列化文件。
步骤如何序列化和反序列化对象。
回答:它是如何转换为 51 字节文件的?
- 首先写入序列化流魔术数据(STREAM_MAGIC= "AC ED" and STREAM_VERSION=JVM 的版本)。
- 然后它写出与实例关联的类的元数据(类的长度、类的名称、serialVersionUID)。
- 然后它递归地写出超类的元数据,直到找到
java.lang.Object
. - 然后从与实例关联的实际数据开始。
- 最后将与实例关联的对象的数据从元数据开始写入实际内容。
编辑:参考链接阅读。
这将回答几个常见问题:
如何不序列化类中的任何字段。
答:使用transient关键字当子类被序列化时,父类会被序列化吗?
回答:不,如果父级没有扩展 Serializable 接口的父级字段,则不会被序列化。当父类被序列化时,子类会被序列化吗?
Ans: 是的,默认情况下子类也被序列化。如何避免子类被序列化?
答:一个。覆盖 writeObject 和 readObject 方法并抛出NotSerializableException
。湾。您也可以在子类中标记所有字段瞬态。
一些系统级的类,例如 Thread、OutputStream 及其子类和 Socket 是不可序列化的。
序列化是在内存中获取“活动”对象并将其转换为可以存储在某处(例如,在内存中,磁盘上)的格式,然后“反序列化”回活动对象。
我喜欢@OscarRyz 呈现的方式。虽然在这里我继续讲述最初由@amitgupta 编写的序列化故事。
即使知道机器人类结构并拥有序列化数据,地球科学家也无法反序列化可以使机器人工作的数据。
Exception in thread "main" java.io.InvalidClassException:
SerializeMe; local class incompatible: stream classdesc
:
火星的科学家们正在等待全额付款。付款完成后,火星的科学家与地球的科学家分享了serialversionUID 。地球科学家将其设置为机器人类,一切都变得很好。
我自己博客中的两分钱:
下面是序列化的详细解释:(我自己的博客)
序列化:
序列化是保持对象状态的过程。它以字节序列的形式表示和存储。这可以存储在文件中。从文件中读取对象状态并恢复它的过程称为反序列化。
序列化需要什么?
在现代架构中,总是需要存储对象状态然后检索它。例如在 Hibernate 中,要存储一个对象,我们应该使类 Serializable。它的作用是,一旦对象状态以字节的形式保存,它就可以传输到另一个系统,然后该系统可以从状态中读取并检索类。对象状态可以来自数据库或不同的 jvm 或来自单独的组件。在序列化的帮助下,我们可以检索对象状态。
代码示例及说明:
首先让我们看一下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 文档:
序列化运行时将版本号与每个可序列化类相关联,称为 serialVersionUID,在反序列化期间使用该版本号来验证序列化对象的发送方和接收方是否已为该对象加载了与序列化兼容的类。如果接收者为对象加载了一个类,该对象的 serialVersionUID 与相应发送者的类不同,则反序列化将导致 InvalidClassException。可序列化的类可以通过声明一个名为“serialVersionUID”的字段来显式声明自己的serialVersionUID,该字段必须是静态的、最终的和long类型:ANY-ACCESS-MODIFIER static final long serialVersionUID = 42L; 如果一个可序列化的类没有显式声明一个 serialVersionUID,然后序列化运行时将根据类的各个方面计算该类的默认 serialVersionUID 值,如 Java(TM) 对象序列化规范中所述。但是,强烈建议所有可序列化的类都显式声明 serialVersionUID 值,因为默认的 serialVersionUID 计算对类细节高度敏感,这些细节可能因编译器实现而异,因此可能在反序列化期间导致意外的 InvalidClassExceptions。因此,为了保证在不同的 java 编译器实现中具有一致的 serialVersionUID 值,可序列化的类必须声明一个显式的 serialVersionUID 值。还强烈建议显式 serialVersionUID 声明尽可能使用 private 修饰符,
如果您注意到我们使用的另一个关键字是transient。
如果字段不可序列化,则必须将其标记为瞬态。在这里,我们将itemCostPrice标记为瞬态并且不希望将其写入文件
现在让我们看看如何在文件中写入对象的状态,然后从那里读取它。
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为null,因为它没有被写入。
我们已经在本文的第一部分讨论了 Java 序列化的基础知识。
现在让我们深入讨论它以及它是如何工作的。
首先让我们从serialversionuid 开始。
serialVersionUID用作 Serializable 类中的版本控制。
如果您没有显式声明 serialVersionUID,JVM 将根据 Serializable 类的各种属性自动为您完成。
Java 的计算 serialversionuid 的算法(在此处阅读更多详细信息)
- 类名。
- 写为 32 位整数的类修饰符。
- 每个接口的名称按名称排序。
- 对于按字段名称排序的类的每个字段(私有静态和私有瞬态字段除外:字段的名称。字段的修饰符写为 32 位整数。字段的描述符。
- 如果存在类初始值设定项,请写出以下内容:方法的名称,.
- 方法的修饰符 java.lang.reflect.Modifier.STATIC,写成 32 位整数。
- 方法的描述符,()V。
- 对于按方法名称和签名排序的每个非私有构造函数:方法的名称,. 方法的修饰符写为 32 位整数。方法的描述符。
- 对于按方法名称和签名排序的每个非私有方法:方法的名称。方法的修饰符写为 32 位整数。方法的描述符。
- SHA-1 算法在 DataOutputStream 产生的字节流上执行,并产生五个 32 位值 sha[0..4]。哈希值由 SHA-1 消息摘要的第一个和第二个 32 位值组合而成。如果消息摘要的结果,即五个 32 位字 H0 H1 H2 H3 H4,位于名为 sha 的五个 int 值的数组中,则哈希值将按如下方式计算:
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的序列化算法
序列化对象的算法描述如下:
1. 它写出与实例关联的类的元数据。
2. 递归地写出超类的描述,直到找到java.lang.object。
3. 一旦完成元数据信息的写入,它就会从与实例关联的实际数据开始。但这一次,它从最顶层的超类开始。
4. 它递归地写入与实例关联的数据,从最小的超类开始到最派生的类。
要记住的事情:
类中的静态字段不能被序列化。
public class A implements Serializable{ String s; static String staticString = "I won't be serializable"; }
如果 read 类中的 serialversionuid 不同,则会抛出
InvalidClassException
异常。如果一个类实现了可序列化,那么它的所有子类也将是可序列化的。
public class A implements Serializable {....}; public class B extends A{...} //also Serializable
如果一个类有另一个类的引用,则所有引用都必须是可序列化的,否则将不会执行序列化过程。在这种情况下,NotSerializableException在运行时被抛出。
例如:
public class B{
String s,
A a; // class A needs to be serializable i.e. it must implement Serializable
}
序列化意味着在java中持久化对象。如果你想保存对象的状态并且想稍后重建状态(可能在另一个JVM中)可以使用序列化。
请注意,对象的属性只会被保存。如果你想再次复活对象,你应该有类文件,因为只存储成员变量而不是成员函数。
例如:
ObjectInputStream oos = new ObjectInputStream(
new FileInputStream( new File("o.ser")) ) ;
SerializationSample SS = (SearializationSample) oos.readObject();
可序列化是一个标记接口,它标记你的类是可序列化的。标记接口意味着它只是一个空接口,使用该接口将通知 JVM 这个类可以被序列化。
序列化是将对象的状态转换为位以便将其存储在硬盘上的过程。当您反序列化同一个对象时,它将在以后保留其状态。它使您无需手动保存对象的属性即可重新创建对象。
Java对象序列化
Serialization
是一种将 Java 对象图转换为字节数组以用于存储(to disk file
)或传输(across a network
)的机制,然后通过使用反序列化,我们可以恢复对象图。使用参考共享机制正确恢复对象图。但在存储之前,请检查 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,这样会在反序列化since the default serialVersionUID computation is highly sensitive to class details that may vary depending on compiler implementations
期间导致意外的 serialVersionUID 冲突,从而导致反序列化失败。
Java 对象只能序列化。如果一个类或其任何超类实现了java.io.Serializable接口或其子接口java.io.Externalizable。
一个类必须实现java.io.Serializable 接口才能成功序列化其对象。Serializable 是一个标记接口,用于通知编译器实现它的类必须添加可序列化行为。这里Java虚拟机(JVM)负责它的自动序列化。
瞬态关键字:
java.io.Serializable interface
在序列化对象时,如果我们不希望对象的某些数据成员被序列化,我们可以使用瞬态修饰符。瞬态关键字将阻止该数据成员被序列化。
- 序列化过程会忽略声明为瞬态或静态的字段。
+--------------+--------+-------------------------------------+ | 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. }
实现 Externalizable 接口允许对象完全控制对象的序列化形式的内容和格式。调用 Externalizable 接口的方法 writeExternal 和 readExternal 来保存和恢复对象状态。当由一个类实现时,它们可以使用 ObjectOutput 和 ObjectInput 的所有方法写入和读取自己的状态。对象负责处理发生的任何版本控制。
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 } }
只有支持 java.io.Serializable 或 java.io.Externalizable 接口的对象才能成为
written to
/read from
流。每个可序列化对象的类都被编码,包括类的类名和签名、对象的字段和数组的值,以及从初始对象引用的任何其他对象的闭包。
文件的可序列化示例
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();
}
}
}
网络上的可序列化示例
将object的状态分布在不同的地址空间中,无论是在同一台计算机上的不同进程中,还是在通过网络连接的多台计算机中,但它们通过共享数据和调用方法一起工作。
/**
* 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();
}
}
@看
序列化是将对象保存在存储介质(如文件或内存缓冲区)中或以二进制形式通过网络连接传输的过程。序列化的对象是独立于 JVM 的,并且可以被任何 JVM 重新序列化。在这种情况下,“内存中”的 java 对象状态被转换为字节流。用户无法理解这种类型的文件。它是一种特殊类型的对象,即被 JVM(Java 虚拟机)重用。这种序列化对象的过程也称为对对象进行压缩或编组。
要序列化的对象必须实现java.io.Serializable
接口。对象的默认序列化机制写入对象的类、类签名以及所有非瞬态和非静态字段的值。
class ObjectOutputStream extends java.io.OutputStream implements ObjectOutput,
ObjectOutput
interface 扩展了DataOutput
接口并添加了用于序列化对象和将字节写入文件的方法。ObjectOutputStream
扩展java.io.OutputStream
和实现ObjectOutput
接口。它将对象、数组和其他值序列化为流。因此 的构造函数 ObjectOutputStream
写为:
ObjectOutput ObjOut = new ObjectOutputStream(new FileOutputStream(f));
上面的代码已用于使用构造函数创建ObjectOutput
类的实例,该ObjectOutputStream( )
构造函数将 的实例FileOuputStream
作为参数。
通过ObjectOutput
实现ObjectOutputStream
类来使用接口。被ObjectOutputStream
构造为序列化对象。
在java中反序列化一个对象
序列化的相反操作称为反序列化,即从一系列字节中提取数据称为反序列化,也称为膨胀或解组。
ObjectInputStream
扩展java.io.InputStream
和实现ObjectInput
接口。它反序列化来自输入流的对象、数组和其他值。因此 的构造函数 ObjectInputStream
写为:
ObjectInputStream obj = new ObjectInputStream(new FileInputStream(f));
上面的程序代码创建了类的实例ObjectInputStream
来反序列化已被ObjectInputStream
类序列化的文件。上面的代码使用类的实例创建实例,该实例FileInputStream
包含必须反序列化的指定文件对象,因为ObjectInputStream()
构造函数需要输入流。
序列化是将 Java 对象转换为字节数组,然后以保留状态再次转换回对象的过程。对于通过网络发送对象或将内容缓存到磁盘等各种事情很有用。
从这篇简短的文章中阅读更多内容,该文章很好地解释了该过程的编程部分,然后转到Serializable javadoc。您可能也有兴趣阅读此相关问题。
将文件作为对象返回: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);
}
}
|*| 序列化类:将对象转换为字节并将字节转换回对象(反序列化)。
class NamCls implements Serializable
{
int NumVar;
String NamVar;
}
|=> 对象序列化是将对象的状态转换为字节流的过程。
- |-> 当您希望对象在 JVM 的生命周期之外存在时实现。
- |-> 序列化对象可以存储在数据库中。
- |-> 可序列化对象无法被人类读取和理解,因此我们可以实现安全性。
|=> 对象反序列化是获取对象状态并将其存储到对象(java.lang.Object)中的过程。
- |-> 在存储其状态之前,它会检查来自输入文件/网络的serialVersionUID 和.class 文件serialVersionUID 是否相同。
如果不抛出 java.io.InvalidClassException。
|=> Java 对象只有在其类或其任何超类时才可序列化
- 实现 java.io.Serializable 接口或
- 它的子接口 java.io.Externalizable。
|=> 类中的静态字段不能被序列化。
class NamCls implements Serializable
{
int NumVar;
static String NamVar = "I won't be serializable";;
}
|=> 如果您不想序列化类的变量,请使用瞬态关键字
class NamCls implements Serializable
{
int NumVar;
transient String NamVar;
}
|=> 如果一个类实现了可序列化,那么它的所有子类也将是可序列化的。
|=> 如果一个类有另一个类的引用,所有的引用都必须是可序列化的,否则不会执行序列化过程。在这种情况下,
NotSerializableException 在运行时被抛出。
我将提供一个类比,以潜在地帮助巩固对象序列化/反序列化的概念目的/实用性。
我想象对象序列化/反序列化是在试图通过雨水渠移动对象的上下文中。对象本质上被“分解”或序列化成更模块化的版本——在这种情况下,是一系列字节——以便有效地被授予通过介质的通道。在计算意义上,我们可以将字节通过雨水渠的路径视为类似于通过网络的字节。我们正在改变我们的对象,以符合更理想的运输方式或格式。序列化的对象通常存储在二进制文件中,以后可以读取、写入或两者兼而有之。
也许一旦我们的对象能够以分解的一系列字节的形式从排水管中溜走,我们可能希望将该对象的表示作为二进制数据存储在数据库或硬盘驱动器中。不过,主要的收获是,通过序列化/反序列化,我们可以选择让我们的对象在序列化后保持二进制形式,或者通过执行反序列化“检索”对象的原始形式。
序列化是通过将对象转换为字节码来保存对象的特定状态的过程。此转换后的字节码用于在 2 个 JVM 之间传输对象状态,其中接收 JVM 反序列化字节码以检索共享对象的状态。序列化和反序列化是使用 serialVersionUID 作为所涉及的 JVMS 中的参考来完成的。
在 Java 中,使用序列化和外部化接口可以实现相同的目的