Java中的Serializable和Externalizable有什么区别?
Java中的Serializable
和Externalizable
什么区别?
要添加到其他的答案,通过实现java.io.Serializable
,你的类的对象的“自动”序列化能力。 没有必要执行任何其他的逻辑,它只会工作。 Java运行时将使用reflection来弄清楚如何编组和解组对象。
在Java的早期版本中,reflection非常缓慢,因此序列化大对象图(例如在客户端 – 服务器RMI应用程序中)是一个性能问题。 为了处理这种情况,提供了java.io.Externalizable
接口,它类似于java.io.Externalizable
接口,但是使用自定义编写的机制来执行编组和解组函数(您需要在类上实现readExternal
和writeExternal
方法)。 这给你一个解决reflection性能瓶颈的方法。
在最近的Java版本(当然是1.3)中,reflection的性能比以前要好得多,所以这个问题就less得多了。 我怀疑你会很难从现代JVM的Externalizable
获得有意义的好处。
另外,内置的Java序列化机制并不是唯一的,你可以获得第三方的替代品,比如JBoss Serialization,这个更快,而且是默认的替代品。
Externalizable
一大缺点是你必须自己维护这个逻辑 – 如果你在类中添加,删除或者改变一个字段,你必须改变你的writeExternal
/ readExternal
方法来解决这个问题。
总之, Externalizable
是Java 1.1天的遗迹。 真的没有必要了。
序列化提供了默认function来存储和稍后重新创build对象。 它使用复杂的algorithm来定义要存储的对象的整个graphics,例如,假设你有一个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的,否则代码将抛出exception。 请按照下面的代码:
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); } } }
在这里,如果你注释默认的构造函数,那么代码将抛出exception:
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
我们可以观察,因为我们没有设置密码的值,所以它是空的。
通过将密码字段声明为瞬态,也可以达到同样的效果。
private transient String passWord;
希望它有帮助。 如果我犯了错误,我很抱歉。 谢谢。
为了完整性, transient
关键字也缩小了两者之间的差距。
如果你只想序列化你的对象的一部分,只需将特定的字段设置为transient
,标记为不被持久化,并实现Serializable
。
序列化使用某些默认行为来存储并稍后重新创build对象。 您可以指定按什么顺序或如何处理引用和复杂的数据结构,但最终归结为使用每个基本数据字段的默认行为。
在极less数情况下,使用外部化是非常希望以完全不同的方式存储和重build对象,而不使用数据字段的默认序列化机制。 例如,假设你有自己独特的编码和压缩scheme。
Serializable
和Externalizable
之间的主要区别
- 标记接口 :可
Serializable
是没有任何方法的标记接口。Externalizable
接口包含两个方法:writeExternal()
和readExternal()
。 - 序列化过程 :对于实现
Serializable
接口的类,默认序列化过程将被踢入。 程序员定义的序列化过程将被踢入实现Externalizable
接口的类。 - 维护 : 不兼容的更改可能会破坏序列化。
- 向后兼容和控制 :如果你必须支持多个版本,你可以使用
Externalizable
接口完全控制。 你可以支持你的对象的不同版本。 如果你实现了Externalizable
,那么你有责任序列化super
类 - 公共无参数构造函数 :可
Serializable
使用reflection来构造对象,不需要参数构造函数。 但是,Externalizable
需要公有无参构造函数。
有关更多详细信息,请参阅Hitesh Garg
博客 。
Externalizable接口实际上没有提供用于优化序列化过程的性能! 而是提供实现自己的自定义处理的手段,并提供对对象及其超typesstream的格式和内容的完全控制!
这方面的例子是AMF(动作脚本消息格式)远程处理的实现,通过networking传输原生动作脚本对象。
https://docs.oracle.com/javase/8/docs/platform/serialization/spec/serialTOC.html
默认的序列化有些冗长,并假定序列化对象的使用场景尽可能广泛,相应的默认格式(Serializable)使用关于序列化对象的类的信息来注释结果stream。
外部化给对象stream的生产者完全控制超出类的最小需求标识(例如其名称)的精确类元数据(如果有的话)。 在某些情况下(比如封闭的环境),对象stream的生产者及其消费者(从stream中提取对象)是匹配的,并且关于该类的附加元数据不起任何作用并降低性能,这显然是令人满意的。
另外(如Uri指出的),外部化也提供了完全控制对应于Javatypes的stream中的数据的编码。 对于(一个人为的)例子,你可能希望将布尔值truelogging为“Y”,将falselogging为“N”。 外部化可以让你做到这一点。
在考虑改进性能的选项时,不要忘记自定义序列化。 你可以让Java做的很好,或者至less足够好, 免费的 ,并提供自定义的支持。 这通常比完全的Externalizable支持less得多的代码。
在Serializable和Externalizable之间存在着如此之多的差异,但是当我们比较自定义的Serializable(overrided writeObject()和readObject())和Externalizable之间的区别时,我们发现自定义实现与ObjectOutputStream类紧紧地绑定在Externalizable case中,提供ObjectOutput的实现,它可以是ObjectOutputStream类,也可以是其他类似org.apache.mina.filter.codec.serialization.ObjectSerializationOutputStream
在Externalizable接口的情况下
@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(); }
我已经添加了示例代码来更好地解释。 请检查Externalizable的对象情况。 这些不是直接绑定到任何实现。
Outstream / Instream与类紧密相连。 我们可以扩展ObjectOutputStream / ObjectInputStream,但会有点难以使用。