Wil*_*aan 38
虽然大多数用户已经给出了答案,但我想为那些需要它的人添加一个例子来解释这个想法:
假设您有一个类似以下人员的人:
public class Person implements java.io.Serializable {
/**
*
*/
private static final long serialVersionUID = 1L;
public String firstName;
public String lastName;
public int age;
public String address;
public void play() {
System.out.println(String.format(
"If I win, send me the trophy to this address: %s", address));
}
@Override
public String toString() {
return String.format(".....Person......\nFirst Name = %s\nLast Name = %s", firstName, lastName);
}
}
Run Code Online (Sandbox Code Playgroud)
然后你创建一个这样的对象:
Person william = new Person();
william.firstName = "William";
william.lastName = "Kinaan";
william.age = 26;
william.address = "Lisbon, Portugal";
Run Code Online (Sandbox Code Playgroud)
您可以将该对象序列化为多个流.我会这样做到两个流:
序列化为标准输出:
public static void serializeToStandardOutput(Person person)
throws IOException {
OutputStream outStream = System.out;
ObjectOutputStream stdObjectOut = new ObjectOutputStream(outStream);
stdObjectOut.writeObject(person);
stdObjectOut.close();
outStream.close();
}
Run Code Online (Sandbox Code Playgroud)
序列化到文件:
public static void serializeToFile(Person person) throws IOException {
OutputStream outStream = new FileOutputStream("person.ser");
ObjectOutputStream fileObjectOut = new ObjectOutputStream(outStream);
fileObjectOut.writeObject(person);
fileObjectOut.close();
outStream.close();
}
Run Code Online (Sandbox Code Playgroud)
然后:
从文件反序列化:
public static void deserializeFromFile() throws IOException,
ClassNotFoundException {
InputStream inStream = new FileInputStream("person.ser");
ObjectInputStream fileObjectIn = new ObjectInputStream(inStream);
Person person = (Person) fileObjectIn.readObject();
System.out.println(person);
fileObjectIn.close();
inStream.close();
}
Run Code Online (Sandbox Code Playgroud)
Dav*_*vid 35
这意味着类的实例可以转换为字节流(例如,保存到文件中),然后再次转换回类.这种重新加载可能发生在程序的不同实例中,甚至可能发生在不同的机器上.序列化(使用任何语言)涉及各种问题,尤其是当您在可序列化对象中引用其他对象时.
Pri*_*jee 12
以下是序列化的详细说明 :(我自己的博客)
连载:
序列化是序列化对象状态的过程,以字节序列的形式表示和存储.这可以存储在文件中.从文件中读取对象状态并恢复它的过程称为反序列化.
序列化需要什么?
在现代架构中,总是需要存储对象状态然后检索它.例如在Hibernate中,要存储对象,我们应该使类Serializable.它的作用是,一旦对象状态以字节的形式保存,它就可以转移到另一个系统,然后该系统可以从状态读取并检索该类.对象状态可以来自数据库或不同的jvm,也可以来自单独的组件.在Serialization的帮助下,我们可以检索Object状态.
代码示例和说明:
首先让我们看一下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;
}
}
Run Code Online (Sandbox Code Playgroud)
在上面的代码中可以看出Item类实现了Serializable.
这是使类可序列化的接口.
现在我们可以看到一个名为serialVersionUID的变量被初始化为Long变量.此编号由编译器根据类的状态和类属性计算.这是一个有助于jvm在从文件中读取对象状态时识别对象状态的数字.
为此,我们可以查看官方Oracle文档:
序列化运行时将每个可序列化类与版本号相关联,称为serialVersionUID,在反序列化期间使用该版本号来验证序列化对象的发送方和接收方是否已加载与该序列化兼容的该对象的类.如果接收者为具有与相应发送者类的serialVersionUID不同的对象加载了一个类,则反序列化将导致InvalidClassException.可序列化的类可以通过声明一个名为"serialVersionUID"的字段来显式声明它自己的serialVersionUID,该字段必须是static,final和long类型:ANY-ACCESS-MODIFIER static final long serialVersionUID = 42L; 如果可序列化类未显式声明serialVersionUID,则序列化运行时将基于类的各个方面计算该类的默认serialVersionUID值,如Java(TM)对象序列化规范中所述.但是,强烈建议所有可序列化类显式声明serialVersionUID值,因为默认的serialVersionUID计算对类细节高度敏感,这些细节可能因编译器实现而异,因此在反序列化期间可能导致意外的InvalidClassExceptions.因此,为了保证跨不同java编译器实现的一致的serialVersionUID值,可序列化类必须声明显式的serialVersionUID值.强烈建议显式serialVersionUID声明尽可能使用private修饰符,因为此类声明仅适用于立即声明的类 - serialVersionUID字段作为继承成员无用.
如果你注意到我们使用了另一个关键词,那就是短暂的.
如果字段不可序列化,则必须将其标记为瞬态.这里我们将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();
}
}
}
Run Code Online (Sandbox Code Playgroud)
在上面我们可以看到一个对象的序列化和反序列化的例子.
为此,我们使用了两个类.为了序列化对象,我们使用了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]
Run Code Online (Sandbox Code Playgroud)
请注意,反序列化对象中的itemCostPrice为null,因为它未写入.
Serializable像接口一样被调用,但它更像是编译器的标志.它说这个对象可以保存.除了没有可序列化对象和标记volatile之外的所有Objects实例变量都将被保存.
想象一下,您的应用程序可以更改颜色作为选项,而不必将该设置保持在外部,您每次运行时都需要更改颜色.