Java中可序列化和可外部化的区别是什么?
当前回答
序列化提供了存储对象和稍后重新创建对象的默认功能。它使用详细格式来定义要存储的对象的整个图,例如,假设你有一个linkedList,你像下面这样编码,那么默认的序列化将发现所有被链接的对象并将序列化。在默认的序列化中,对象完全由其存储的位构造,没有构造函数调用。
ObjectOutputStream oos = new ObjectOutputStream(
new FileOutputStream("/Users/Desktop/files/temp.txt"));
oos.writeObject(linkedListHead); //writing head of linked list
oos.close();
但是如果你想要限制序列化或者不希望对象的某些部分被序列化,那么就使用Externalizable。Externalizable接口扩展了Serializable接口,并添加了两个方法writeExternal()和readeexternal()。在序列化或反序列化时自动调用这些函数。在使用Externalizable时,我们应该记住默认构造函数应该是公共的,否则代码将抛出异常。请遵循以下代码:
public class MyExternalizable implements Externalizable
{
private String userName;
private String passWord;
private Integer roll;
public MyExternalizable()
{
}
public MyExternalizable(String userName, String passWord, Integer roll)
{
this.userName = userName;
this.passWord = passWord;
this.roll = roll;
}
@Override
public void writeExternal(ObjectOutput oo) throws IOException
{
oo.writeObject(userName);
oo.writeObject(roll);
}
@Override
public void readExternal(ObjectInput oi) throws IOException, ClassNotFoundException
{
userName = (String)oi.readObject();
roll = (Integer)oi.readObject();
}
public String toString()
{
StringBuilder b = new StringBuilder();
b.append("userName: ");
b.append(userName);
b.append(" passWord: ");
b.append(passWord);
b.append(" roll: ");
b.append(roll);
return b.toString();
}
public static void main(String[] args)
{
try
{
MyExternalizable m = new MyExternalizable("nikki", "student001", 20);
System.out.println(m.toString());
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("/Users/Desktop/files/temp1.txt"));
oos.writeObject(m);
oos.close();
System.out.println("***********************************************************************");
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("/Users/Desktop/files/temp1.txt"));
MyExternalizable mm = (MyExternalizable)ois.readObject();
mm.toString();
System.out.println(mm.toString());
}
catch (ClassNotFoundException ex)
{
Logger.getLogger(MyExternalizable.class.getName()).log(Level.SEVERE, null, ex);
}
catch(IOException ex)
{
Logger.getLogger(MyExternalizable.class.getName()).log(Level.SEVERE, null, ex);
}
}
}
在这里,如果你注释了默认构造函数,那么代码将抛出以下异常:
java.io.InvalidClassException: javaserialization.MyExternalizable;
javaserialization.MyExternalizable; no valid constructor.
我们可以观察到,由于密码是敏感信息,所以我没有在writeExternal(ObjectOutput oo)方法中序列化它,也没有在readexterexternal (ObjectInput oi)中设置相同的值。这就是Externalizable提供的灵活性。
上述代码的输出如下所示:
userName: nikki passWord: student001 roll: 20
***********************************************************************
userName: nikki passWord: null roll: 20
我们可以观察到,因为我们没有设置passWord的值,所以它是空的。
通过将密码字段声明为瞬态,也可以实现同样的效果。
private transient String passWord;
希望能有所帮助。如果我犯了错误,我向你道歉。谢谢。
其他回答
对象序列化使用Serializable和Externalizable接口。 Java对象只能序列化。如果一个类或它的任何超类实现了java.io.Serializable接口或它的子接口java.io.Externalizable。大多数java类都是可序列化的。
NotSerializableException: packageName。要使类对象参与序列化过程,类必须实现Serializable或Externalizable接口。
序列化接口
对象序列化生成一个流,其中包含保存的对象的Java类信息。对于可序列化的对象,即使存在不同的(但兼容的)类实现版本,也会保留足够的信息来恢复这些对象。Serializable接口被定义为识别实现了Serializable协议的类:
package java.io;
public interface Serializable {};
The serialization interface has no methods or fields and serves only to identify the semantics of being serializable. For serializing/deserializing a class, either we can use default writeObject and readObject methods (or) we can overriding writeObject and readObject methods from a class. JVM will have complete control in serializing the object. use transient keyword to prevent the data member from being serialized. Here serializable objects is reconstructed directly from the stream without executing InvalidClassException « In deserialization process, if local class serialVersionUID value is different from the corresponding sender's class. then result's in conflict as java.io.InvalidClassException: com.github.objects.User; local class incompatible: stream classdesc serialVersionUID = 5081877, local class serialVersionUID = 50818771 The values of the non-transient and non-static fields of the class get serialized.
外部化接口
对于Externalizable对象,容器只保存对象类的标识;类必须保存并恢复内容。Externalizable接口定义如下:
package java.io;
public interface Externalizable extends Serializable
{
public void writeExternal(ObjectOutput out)
throws IOException;
public void readExternal(ObjectInput in)
throws IOException, java.lang.ClassNotFoundException;
}
The Externalizable interface has two methods, an externalizable object must implement a writeExternal and readExternal methods to save/restore the state of an object. Programmer has to take care of which objects to be serialized. As a programmer take care of Serialization So, here transient keyword will not restrict any object in Serialization process. When an Externalizable object is reconstructed, an instance is created using the public no-arg constructor, then the readExternal method called. Serializable objects are restored by reading them from an ObjectInputStream. OptionalDataException « The fields MUST BE IN THE SAME ORDER AND TYPE as we wrote them out. If there is any mismatch of type from the stream it throws OptionalDataException. @Override public void writeExternal(ObjectOutput out) throws IOException { out.writeInt( id ); out.writeUTF( role ); out.writeObject(address); } @Override public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException { this.id = in.readInt(); this.address = (Address) in.readObject(); this.role = in.readUTF(); } The instance fields of the class which written (exposed) to ObjectOutput get serialized.
示例«实现Serializable
class Role {
String role;
}
class User extends Role implements Serializable {
private static final long serialVersionUID = 5081877L;
Integer id;
Address address;
public User() {
System.out.println("Default Constructor get executed.");
}
public User( String role ) {
this.role = role;
System.out.println("Parametarised Constructor.");
}
}
class Address implements Serializable {
private static final long serialVersionUID = 5081877L;
String country;
}
示例«实现了Externalizable
class User extends Role implements Externalizable {
Integer id;
Address address;
// mandatory public no-arg constructor
public User() {
System.out.println("Default Constructor get executed.");
}
public User( String role ) {
this.role = role;
System.out.println("Parametarised Constructor.");
}
@Override
public void writeExternal(ObjectOutput out) throws IOException {
out.writeInt( id );
out.writeUTF( role );
out.writeObject(address);
}
@Override
public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
this.id = in.readInt();
this.address = (Address) in.readObject();
this.role = in.readUTF();
}
}
例子
public class CustomClass_Serialization {
static String serFilename = "D:/serializable_CustomClass.ser";
public static void main(String[] args) throws IOException {
Address add = new Address();
add.country = "IND";
User obj = new User("SE");
obj.id = 7;
obj.address = add;
// Serialization
objects_serialize(obj, serFilename);
objects_deserialize(obj, serFilename);
// Externalization
objects_WriteRead_External(obj, serFilename);
}
public static void objects_serialize( User obj, String serFilename ) throws IOException{
FileOutputStream fos = new FileOutputStream( new File( serFilename ) );
ObjectOutputStream objectOut = new ObjectOutputStream( fos );
// java.io.NotSerializableException: com.github.objects.Address
objectOut.writeObject( obj );
objectOut.flush();
objectOut.close();
fos.close();
System.out.println("Data Stored in to a file");
}
public static void objects_deserialize( User obj, String serFilename ) throws IOException{
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
User user = (User) readObject;
System.out.format("Obj[Id:%d, Role:%s] \n", user.id, user.role);
Address add = (Address) user.address;
System.out.println("Inner Obj : "+ add.country );
ois.close();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
public static void objects_WriteRead_External( User 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.
User user = new User();
FileInputStream fis = new FileInputStream(new File( serFilename ));
ObjectInputStream ois = new ObjectInputStream( fis );
user.readExternal(ois);
System.out.format("Obj[Id:%d, Role:%s] \n", user.id, user.role);
Address add = (Address) user.address;
System.out.println("Inner Obj : "+ add.country );
ois.close();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}
@see
什么是对象序列化 对象序列化:常见问题
一些差异:
For Serialization there is no need of default constructor of that class because Object because JVM construct the same with help of Reflection API. In case of Externalization contructor with no arg is required, because the control is in hand of programmar and later on assign the deserialized data to object via setters. In serialization if user want to skip certain properties to be serialized then has to mark that properties as transient, vice versa is not required for Externalization. When backward compatiblity support is expected for any class then it is recommended to go with Externalizable. Serialization supports defaultObject persisting and if object structure is broken then it will cause issue while deserializing.
Serializable和Externalizable之间的主要区别
Marker interface: Serializable is marker interface without any methods. Externalizable interface contains two methods: writeExternal() and readExternal(). Serialization process: Default Serialization process will be kicked-in for classes implementing Serializable interface. Programmer defined Serialization process will be kicked-in for classes implementing Externalizable interface. Maintenance: Incompatible changes may break serialisation. Backward Compatibility and Control: If you have to support multiple versions, you can have full control with Externalizable interface. You can support different versions of your object. If you implement Externalizable, it's your responsibility to serialize super class public No-arg constructor: Serializable uses reflection to construct object and does not require no arg constructor. But Externalizable demands public no-arg constructor.
更多细节请参考Hitesh Garg的博客。
基本上,Serializable是一个标记接口,它暗示一个类对于序列化是安全的,并且JVM决定它如何序列化。Externalizable包含两个方法,readExternal和writeExternal。Externalizable允许实现者决定如何序列化一个对象,而Serializable序列化对象是默认的方式。
实际上并没有提供Externalizable接口来优化序列化进程的性能!而是提供实现您自己的自定义处理的方法,并为对象及其超类型提供对流的格式和内容的完全控制!
这方面的例子是AMF (ActionScript Message Format)远程处理的实现,通过网络传输本机操作脚本对象。
推荐文章
- javax.transaction.Transactional vs . org.springframework.transaction.annotation.Transactional
- Java 8接口方法中不允许“同步”的原因是什么?
- 如何找到Java堆大小和内存使用(Linux)?
- 使用Enum实现单例(Java)
- RabbitMQ与通道和连接之间的关系
- buildSessionFactory()配置方法在Hibernate中已弃用?
- Spring MVC -如何获得所有的请求参数在一个地图在Spring控制器?
- 如何在Java中按两个字段排序?
- 文件之间的差异。路径中的分隔符和斜杠
- 在方法参数中使用NotNull注释
- Spring MVC中处理可选参数的@RequestParam
- Tomcat:如何查找正在运行的Tomcat版本?
- “java”、“javaw”和“javaws”之间有什么区别?
- 将Date对象转换为日历对象
- 在Java中保存最后N个元素的大小有限的队列