Serializable
和Externalizable
Java有什么区别?
要添加其他答案,通过实现java.io.Serializable
,您可以获得类的对象的"自动"序列化功能.不需要实现任何其他逻辑,它只会工作.Java运行时将使用反射来确定如何编组和解组对象.
在早期版本的Java中,反射非常慢,因此序列化大对象图(例如在客户端 - 服务器RMI应用程序中)是一个性能问题.为了处理这种情况,java.io.Externalizable
提供了接口,它就像java.io.Serializable
使用自定义编写的机制来执行编组和解组功能(您需要在类上实现readExternal
和writeExternal
方法).这为您提供了解决反射性能瓶颈的方法.
在Java的最新版本(当然是1.3版本)中,反射的性能比以前要好得多,因此这不是一个问题.我怀疑你很难从Externalizable
现代JVM中获得有意义的好处.
此外,内置的Java序列化机制并不是唯一的,您可以获得第三方替换,例如JBoss Serialization,它更快,并且是默认的替代品.
一个很大的缺点Externalizable
是你必须自己维护这个逻辑 - 如果你在类中添加,删除或更改一个字段,你必须改变writeExternal
/ readExternal
方法来解释它.
总之,Externalizable
是Java 1.1天的遗物.实在没有必要了.
序列化提供了存储和稍后重新创建对象的默认功能.它使用详细格式来定义要存储的对象的整个图形,例如假设您有一个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()和readExternal().这些在序列化或反序列化时自动调用.在使用Externalizable时,我们应该记住默认构造函数应该是public,否则代码将抛出异常.请按照以下代码:
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)方法中对其进行序列化,也没有在readExternal(ObjectInput oi)中设置它的值.这是Externalizable提供的灵活性.
上述代码的输出如下:
userName: nikki passWord: student001 roll: 20 *********************************************************************** userName: nikki passWord: null roll: 20
我们可以观察到因为我们没有设置passWord的值所以它是null.
通过将密码字段声明为瞬态也可以实现相同的目的.
private transient String passWord;
希望能帮助到你.如果我犯了任何错误,我道歉.谢谢.
为了完整起见,transient
关键字也缩小了两者之间的差距.
如果您只想序列化对象的一部分,只需将特定字段设置为transient
,将它们标记为不持久化并实现Serializable
.
Serializable
和之间的主要区别Externalizable
标记界面:Serializable
是没有任何方法的标记界面.Externalizable
接口包含两种方法:writeExternal()
和readExternal()
.
序列化过程:将为实现Serializable
接口的类启动默认序列化过程.程序员定义的序列化过程将为实现Externalizable
接口的类启动.
维护:不兼容的更改可能会破坏序列化.
向后兼容性和控制:如果您必须支持多个版本,则可以完全控制Externalizable
界面.您可以支持对象的不同版本.如果您实施Externalizable
,则您有责任序列化super
课程
public No-arg构造函数: Serializable
使用反射构造对象,不需要arg构造函数.但Externalizable
要求公开的非参数构造函数.
请参阅博客通过Hitesh Garg
更多的细节.
序列化使用某些默认行为来存储并稍后重新创建对象.您可以指定处理引用和复杂数据结构的顺序或方式,但最终归结为使用每个原始数据字段的默认行为.
外部化在极少数情况下用于您真正想要以完全不同的方式存储和重建对象,而不使用数据字段的默认序列化机制.例如,假设您拥有自己独特的编码和压缩方案.
对象序列化使用Serializable和Externalizable接口. Java对象只能序列化.如果一个类或它的任何超类实现java.io.Serializable接口或其子接口java.io.Externalizable.大多数java类都是可序列化的.
NotSerializableException
:packageName.ClassName
«要在序列化过程中参与类对象,该类必须实现Serializable或Externalizable接口.
可序列化接口
对象序列化生成一个流,其中包含有关正在保存的对象的Java类的信息.对于可序列化对象,即使存在类的实现的不同(但兼容)版本,也会保留足够的信息来恢复这些对象.Serializable接口定义为标识实现可序列化协议的类:
package java.io;
public interface Serializable {};
序列化接口没有方法或字段,仅用于标识可序列化的语义.对于序列化/反序列化类,要么我们可以使用默认的writeObject和readObject方法(或),我们可以从类中重写writeObject和readObject方法.
JVM将完全控制序列化对象.使用transient关键字来阻止数据成员被序列化.
这里可串行化的对象直接从流中重建而不执行
InvalidClassException
«在反序列化过程中,如果本地类serialVersionUID值与相应的发送者类不同.然后结果是冲突的
java.io.InvalidClassException: com.github.objects.User; local class incompatible: stream classdesc serialVersionUID = 5081877, local class serialVersionUID = 50818771
类的非瞬态和非静态字段的值被序列化.
可外部化的界面
对于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;
}
Externalizable接口有两个方法,一个可外化的对象必须实现一个writeExternal和readExternal方法来保存/恢复一个对象的状态.
程序员必须处理要序列化的对象.作为一个程序员来处理序列化所以,这里的transient关键字不会限制序列化过程中的任何对象.
重构Externalizable对象时,使用public no-arg构造函数创建实例,然后调用readExternal方法.通过从ObjectInputStream中读取可序列化对象来恢复它们.
OptionalDataException
« 我们写出来时,这些字段必须是相同的顺序和类型.如果流中存在任何类型不匹配,则抛出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();
}
写入(公开)以ObjectOutput
获取序列化的类的实例字段.
示例« implements 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();
}
}
}
@看到
什么是对象序列化
对象序列化:常见问题解答
https://docs.oracle.com/javase/8/docs/platform/serialization/spec/serialTOC.html
默认序列化有点冗长,并假定序列化对象的最广泛使用场景,因此默认格式(Serializable)使用有关序列化对象的类的信息来注释结果流.
外部化使对象流的生产者完全控制精确的类元数据(如果有的话),超出了类的最小必需标识(例如其名称).这在某些情况下显然是可取的,例如封闭环境,其中对象流的生产者及其消费者(从流中对对象进行统一)被匹配,并且关于该类的附加元数据没有用处并且降低性能.
另外(如Uri所指出的)外化还提供对与Java类型相对应的流中的数据的编码的完全控制.对于(一个人为的)示例,您可能希望将布尔值true记录为"Y",将false记录为"N".外化可以让你这样做.
实际上并未提供Externalizable接口来优化序列化过程的性能!但是提供实现自己的自定义处理的方法,并提供对对象及其超类型的流的格式和内容的完全控制!
这方面的示例是实现AMF(ActionScript消息格式)远程处理以通过网络传输本机动作脚本对象.