Serializable
和Externalizable
Java 之间有什么区别?
Serializable
和Externalizable
Java 之间有什么区别?
Answers:
要添加其他答案,请通过实现java.io.Serializable
,为类对象提供“自动”序列化功能。无需实现任何其他逻辑,它就可以工作。Java运行时将使用反射来弄清楚如何对您的对象进行封送。
在Java的早期版本中,反射非常慢,因此序列化大型对象图(例如,在客户端-服务器RMI应用程序中)存在一些性能问题。为了处理这种情况,该java.io.Externalizable
界面提供,这就好比java.io.Serializable
但进行打包和解包的功能自定义编写机制(需要实现readExternal
和writeExternal
在类上方法)。这为您提供了解决反射性能瓶颈的方法。
在Java的最新版本(当然,从1.3开始)中,反射的性能比以前好得多,因此,这不再是问题。我怀疑您很难从Externalizable
现代JVM中获得有意义的收益。
此外,内置的Java序列化机制并不是唯一的机制,您可以获取第三方替代品,例如JBoss序列化,它要快得多,并且是默认替代品。
一个很大的缺点Externalizable
是您必须自己维护此逻辑-如果您在类中添加,删除或更改了一个字段,则必须更改您的writeExternal
/ readExternal
方法以解决该问题。
总而言之,Externalizable
是Java 1.1的遗物。真的不再需要它了。
Externalizable
帮助。
Externalizable
适合我,因为我不想输出带有空空格或占位符对象的数组,以及带有可以处理继承的显式接口的输出,这意味着我的同步子对象-class可以轻松地在调用周围添加锁定writeExternal()
。是的,Externalizable仍然非常重要,当然对于大型或复杂对象。
序列化提供了默认功能来存储和以后重新创建对象。它使用冗长的格式来定义要存储的对象的整个图,例如,假设您有一个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 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;
希望能帮助到你。如果我有任何错误,我深表歉意。谢谢。
Serializable
和之间的主要区别Externalizable
Serializable
是没有任何方法的标记界面。Externalizable
接口包含两种方法:writeExternal()
和readExternal()
。Serializable
接口的类启动默认的序列化过程。程序员定义的序列化过程将为实现Externalizable
接口的类启动。Externalizable
界面。您可以支持对象的不同版本。如果实现Externalizable
,则序列化super
类是您的责任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 {};
InvalidClassException
«在反序列化过程中,如果本地类为serialVersionUID值与相应的发送者的类不同。然后结果是冲突
java.io.InvalidClassException: com.github.objects.User; local class incompatible: stream classdesc serialVersionUID = 5081877, local class serialVersionUID = 50818771
对于可外部化的对象,容器仅保存对象类的标识;该类必须保存并恢复其内容。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;
}
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
要序列化的类的实例字段。
示例«实现了可序列化
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;
}
示例«实现了可外部化
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();
}
}
}
@看到
实际上并未提供Externalizable接口来优化序列化过程的性能!但要提供实现自己的自定义处理的方法,并提供对对象及其超类型的流的格式和内容的完全控制!
这样的示例是实现AMF(ActionScript消息格式)远程处理以通过网络传输本机操作脚本对象的实现。
https://docs.oracle.com/javase/8/docs/platform/serialization/spec/serialTOC.html
默认序列化有些冗长,并且假定序列化对象的使用范围最广,因此,默认格式(可序列化)会使用有关序列化对象类的信息来注释结果流。
外部化使对象流的生产者可以完全控制精确的类元数据(如果有的话),而不必超出类的最小标识(例如,其名称)。在某些情况下,例如封闭环境中,对象流的生产者及其使用者(从流中对对象进行验证)是匹配的,并且有关该类的其他元数据毫无用处并降低了性能,这显然是合乎需要的。
另外(如Uri所指出的),外部化还提供对与Java类型相对应的流中数据编码的完全控制。对于(人为)示例,您可能希望将布尔值true记录为“ Y”,将false记录为“ N”。外部化允许您执行此操作。
Serializable和Externalizable之间存在太多差异,但是当我们比较自定义Serializable(overrided writeObject()&readObject())和Externalizable之间的差异时,我们发现自定义实现与ObjectOutputStream类紧密绑定,就像在Externalizable情况下,我们自己提供ObjectOutput的实现,该实现可以是ObjectOutputStream类,也可以是诸如org.apache.mina.filter.codec.serialization.ObjectSerializationOutputStream之类的其他对象
如果是可外部化的接口
@Override
public void writeExternal(ObjectOutput out) throws IOException {
out.writeUTF(key);
out.writeUTF(value);
out.writeObject(emp);
}
@Override
public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
this.key = in.readUTF();
this.value = in.readUTF();
this.emp = (Employee) in.readObject();
}
**In case of Serializable interface**
/*
We can comment below two method and use default serialization process as well
Sequence of class attributes in read and write methods MUST BE same.
// below will not work it will not work .
// Exception = java.io.StreamCorruptedException: invalid type code: 00\
private void writeObject(java.io.ObjectOutput stream)
*/
private void writeObject(java.io.ObjectOutputStream Outstream)
throws IOException {
System.out.println("from writeObject()");
/* We can define custom validation or business rules inside read/write methods.
This way our validation methods will be automatically
called by JVM, immediately after default serialization
and deserialization process
happens.
checkTestInfo();
*/
stream.writeUTF(name);
stream.writeInt(age);
stream.writeObject(salary);
stream.writeObject(address);
}
private void readObject(java.io.ObjectInputStream Instream)
throws IOException, ClassNotFoundException {
System.out.println("from readObject()");
name = (String) stream.readUTF();
age = stream.readInt();
salary = (BigDecimal) stream.readObject();
address = (Address) stream.readObject();
// validateTestInfo();
}
我添加了示例代码以更好地解释。请检查外部对象的进/出情况。这些不直接绑定到任何实现。
在哪里,出流/入流与类紧密绑定。我们可以扩展ObjectOutputStream / ObjectInputStream,但是使用起来有点困难。