可序列化是啥意思?
Posted
技术标签:
【中文标题】可序列化是啥意思?【英文标题】:What does Serializable mean?可序列化是什么意思? 【发布时间】:2011-03-26 16:30:05 【问题描述】:在 Java 中,类为 Serializable
究竟意味着什么?或者一般来说,就此而言......
【问题讨论】:
@skaffman 这就是它对班级的说法Serializable
:Serializability of a class is enabled by the class implementing the java.io.Serializable interface. Classes that do not implement this interface will not have any of their state serialized or deserialized. All subtypes of a serializable class are themselves serializable. The serialization interface has no methods or fields and serves only to identify the semantics of being serializable.
如果您已经知道序列化和反序列化的含义,这是一个很好的解释。 (不是恭维。)这样的定义可以帮助您在技术上更好地理解问题一次,而且只有一次,您已经对此有所了解。
@RitwikBose 所以,java.io.Serializable 是一个标签接口
【参考方案1】:
Serialization 将一个对象从内存持久化到一个位序列中,例如保存到磁盘上。反序列化是相反的——从磁盘读取数据来合成/创建一个对象。
在你的问题的上下文中,它是一个接口,如果在一个类中实现,这个类可以被不同的序列化器自动序列化和反序列化。
【讨论】:
另请注意,所有未明确标记的字段也将被序列化。这意味着您只需序列化根对象即可轻松保存复杂的数据结构。 所以当我们谈论“对象”时,我们是指由类实例化的对象,还是只是任何“软件对象”,如程序集、文件等?如果是后者,它只是一种在程序和环境之间发送数据的标准化方式吗? @Sunburst275 - 在这种情况下,这是内存中类的内存表示 - 即类的实例(谈论程序集的序列化没有真正意义,因为它们通常作为文件在磁盘上,可以简单地按原样发送)。【参考方案2】:虽然大部分用户已经给出了答案,但我想为需要它的人添加一个示例以解释这个想法:
假设你有一个像下面这样的班级人:
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);
然后你创建一个像这样的对象:
Person william = new Person();
william.firstName = "William";
william.lastName = "Kinaan";
william.age = 26;
william.address = "Lisbon, Portugal";
您可以将该对象序列化为多个流。我将对两个流执行此操作:
序列化到标准输出:
public static void serializeToStandardOutput(Person person)
throws IOException
OutputStream outStream = System.out;
ObjectOutputStream stdObjectOut = new ObjectOutputStream(outStream);
stdObjectOut.writeObject(person);
stdObjectOut.close();
outStream.close();
序列化到文件:
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();
然后:
从文件反序列化:
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();
【讨论】:
谢谢。我想我现在明白了。【参考方案3】:这意味着可以将类的实例转换为字节流(例如,保存到文件中),然后再次转换回类。这种重新加载可能发生在程序的不同实例中,甚至可能发生在不同的机器上。不过,序列化(任何语言)都涉及各种问题,尤其是当您在可序列化对象中引用了其他对象时。
【讨论】:
【参考方案4】:Here is a detailed explanation of the Serialization:(我自己的博客)
序列化:
序列化是将对象的状态序列化的过程,以字节序列的形式表示和存储。这可以存储在文件中。从文件中读取对象状态并恢复它的过程称为反序列化。
序列化需要什么?
在现代架构中,总是需要存储对象状态然后检索它。例如在 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 与 对应的发件人的类,然后反序列化将导致 无效类异常。可序列化的类可以声明自己的 通过声明一个名为的字段显式 serialVersionUID "serialVersionUID" 必须是静态的、最终的并且是 long 类型: ANY-ACCESS-MODIFIER static final long serialVersionUID = 42L;如果一个 可序列化类没有显式声明一个serialVersionUID, 然后序列化运行时将计算一个默认值 该类的 serialVersionUID 值基于 类,如 Java(TM) 对象序列化中所述 规格。但是,强烈建议所有 可序列化的类显式声明 serialVersionUID 值,因为 默认的 serialVersionUID 计算对类高度敏感 细节可能因编译器实现而异,并且可以 因此导致意外的 InvalidClassExceptions 在 反序列化。因此,要保证一致的serialVersionUID 跨不同 java 编译器实现的值,一个可序列化的 类必须声明一个显式的 serialVersionUID 值。也是 强烈建议显式 serialVersionUID 声明使用 可能的情况下使用私有修饰符,因为此类声明仅适用于 立即声明类--serialVersionUID 字段不是 作为继承成员有用。
如果您注意到我们使用了另一个关键字 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,因为它没有被写入。
【讨论】:
这是一个很好的解释!谢谢!但老实说,这篇文章看起来比你博客上的要干净得多。无论如何,这有很大帮助!【参考方案5】:序列化涉及将对象的当前状态保存到流中,并从该流中恢复等效对象。流充当对象的容器
【讨论】:
这个定义似乎更准确。谢谢。【参考方案6】:Serializable 在运行时像接口一样被调用,但它更像是序列化子系统的标志。它说这个对象可以保存。除了不可序列化的对象和标记为 volatile 的对象之外的所有 Objects 实例变量都将被保存。
想象一下,您的应用程序可以选择更改颜色,而无需将该设置保持在外部,您每次运行时都需要更改颜色。
【讨论】:
它不是“编译器的标志”。它是运行时序列化子系统的标志。 @EJP - 谢谢,不知道 恕我直言,既然你不知道它是真的,为什么还要写它呢?您还遗漏了“瞬态”。总而言之,一个糟糕的答案,对不起。 如果我没有写它,我就不会被纠正,而且会更糟。所有其他答案也都离开了瞬态。你甚至没有写一个答案,你只是在拖钓别人。【参考方案7】:序列化是一种将对象和数据存储或写入文件的技术。通过使用 ObjectOutputStream
和 FileOutputStream
类。这些类有其特定的方法来持久化对象。喜欢writeObject();
为了清楚地解释数字。 See Here for more info
【讨论】:
【参考方案8】:从另一个角度呈现。序列化是一种称为“标记接口”的接口。标记接口是不包含方法声明的接口,但 仅仅指定(或“标记”)实现接口的类具有 一些财产。如果您了解多态性,这将非常有意义。在 Serializable 标记接口的情况下,如果 ObjectOutputStream.write(Object) 方法的参数未实现该接口,则该方法将失败。这是java中的一个潜在错误,可能是 ObjectOutputStream.write(Serializable)
强烈推荐:阅读 Joshua Bloch 的 Effective Java 中的第 37 条以了解更多信息。
【讨论】:
【参考方案9】:序列化:将对象的状态写入文件/网络或任何地方。 (意味着 Java 对象支持的形式到文件支持的形式或网络支持的形式)
反序列化:从文件/网络或任何地方读取对象的状态。 (平均文件/网络支持形式到 Java 对象支持形式)
【讨论】:
【参考方案10】:只是为了补充其他答案和一般性。序列化有时称为归档,例如在 Objective-C 中。
【讨论】:
【参考方案11】:序列化对象意味着将其状态转换为字节流,以便字节流可以恢复为对象的副本。如果 Java 对象的类或其任何超类实现了 java.io.Serializable 接口或其子接口 java.io.Externalizable,则它是可序列化的。反序列化是将对象的序列化形式转换回对象副本的过程
点击here查看更多。
【讨论】:
以上是关于可序列化是啥意思?的主要内容,如果未能解决你的问题,请参考以下文章