Java中为啥要序列化?啥时候用到序列化?
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Java中为啥要序列化?啥时候用到序列化?相关的知识,希望对你有一定的参考价值。
序列化可以将内存中的类写入文件或数据库中。比如将某个类序列化后存为文件,下次读取时只需将文件中的数据反序列化就可以将原先的类还原到内存中。也可以将类序列化为流数据进行传输。总的来说就是将一个已经实例化的类转成文件存储,下次需要实例化的时候只要反序列化即可将类实例化到内存中并保留序列化时类中的所有变量和状态。例如:hibernate中实体类的钝化就是将类序列化后存入磁盘并释放内存空间。 参考技术A 序列化就是一种用来处理对象流的机制,所谓对象流也就是将对象的内容进行流化。可以对流化后的对象进行读写操作,也可将流化后的对象传输于网络之间。序列化是为了解决在对对象流进行读写操作时所引发的问题。
序列化的实现:将需要被序列化的类实现Serializable接口,该接口没有需要实现的方法,implements Serializable只是为了标注该对象是可被序列化的,然后使用一个输出流(如:FileOutputStream)来构造一个ObjectOutputStream(对象流)对象,接着,使用ObjectOutputStream对象的writeObject(Object obj)方法就可以将参数为obj的对象写出(即保存其状态),要恢复的话则用输入流。 参考技术B 对象、文件、数据,有许多不同的格式,很难统一传输和保存
序列化以后就都是字节流了,无论原来是什么东西,都能变成一样的东西,就可以进行通用的格式传输或保存,传输结束以后,要再次使用,就进行反序列化还原,这样对象还是对象,文件还是文件 参考技术C 我们的java程序必须要运行在java虚拟机中,那么在同一个java虚拟机进程中,换句话说是在同一快内存空间里面,什么地方需要用此对象,直接传递该对象的引用就可以了,我们想调某一个对象的方法,通过该对象的引用就可以了,传过去就可以了;
那么我问你,如果是不同的java虚拟机进程呢?不同的内存空间呢?甚至我北京的计算机的java虚拟机中的某一个类,需要使用广州的某台计算机上的某个java对象呢?怎么办?
这是不同的内存空间,你怎么调? 参考技术D 序列化就是一种用来处理对象流的机制,所谓对象流也就是将对象的内容进行流化。可以对流化后的对象进行读写操作,也可将流化后的对象传输于网络之间。序列化是为了解决在对对象流进行读写操作时所引发的问题。
序列化的实现:将需要被序列化的类实现Serializable接口,该接口没有需要实现的方法,implements
Serializable只是为了标注该对象是可被序列化的,然后使用一个输出流(如:FileOutputStream)来构造一个ObjectOutputStream(对象流)对象,接着,使用ObjectOutputStream对象的writeObject(Object
obj)方法就可以将参数为obj的对象写出(即保存其状态),要恢复的话则用输入流。
实体类为啥要序列化
我的理解:就像我们搬桌子,桌子太大了不能通过比较小的门,所以我们要把它拆了再运进去,这个拆桌子的过程就是序列化。
而反序列化就是等我们需要用桌子的时候再把它窦起来,这个过程就是反序列化。
我们知道,当两个进程进行远程通信时,可以相互发送各种类型的数据,包括文本、图片、音频、视频等, 而这些数据都会以二进制序列的形式在网络上传送。
那么当两个Java进程进行通信时,能否实现进程间的对象传送呢?答案是可以的。如何做到呢?这就需要Java序列化与反序列化了。
换句话说,一方面,发送方需要把这个Java对象转换为字节序列,然后在网络上传送;另一方面,接收方需要从字节序列中恢复出Java对象。
当我们明晰了为什么需要Java序列化和反序列化后,我们很自然地会想Java序列化的好处。
其好处一是实现了数据的持久化,通过序列化可以把数据永久地保存到硬盘上(通常存放在文件里),二是,利用序列化实现远程通信,即在网络上传送对象的字节序列。
-------------------------------------------------------------------------------------------------------------
序列化是一种用来处理对象流的机制 ,所谓对象流就是将对象的内容进行流化。
可以对流化后的对象进行读写操作,也可将流化后的对象传输于网络之间。
序列化是为了解决在对对象流进行读写操作时所引发的问题。
序列化的实现:
将需要被序列化的类实现Serializable接口,该接口没有需要实现的方法,implements Serializable只是为了标注该对象是可被序列化的,
然后使用一个输出流(如:FileOutputStream)来构造一个ObjectOutputStream(对象流)对象,接着,
使用ObjectOutputStream对象的writeObject(Object obj)方法就可以将参数为obj的对象写出(即保存其状态),要恢复的话则用输入流;
------------------------------------------------------------------------------------------------------------
什么时候使用序列化:
一:对象序列化可以实现分布式对象。主要应用例如:RMI要利用对象序列化运行远程主机上的服务,
就像在本地机上运行对象时一样。
二:java对象序列化不仅保留一个对象的数据,而且递归保存对象引用的每个对象的数据。
可以将整个对象层次写入字节流中,可以保存在文件中或在网络连接上传递。利用对象序列化可以进行对象的"深复制",
即复制对象本身及引用的对象本身。序列化一个对象可能得到整个对象序列。
引起这个疑问,还是从Hibernate使用查询缓存说起;对象实例除了存在于内存,二级缓存还会将对象写进硬盘在需要的时候再读取出来使用,此时就必须提到一个概念:序列化。
程序在运行时实例化出对象,这些对象存在于内存中,随着程序运行停止而消失,但如果我们想把某些对象(一般都是各不相同的属性)保存下来或者传输给其他进程,在程序终止运行后这些对象仍然存在,可以在程序再次运行时读取这些对象的信息,或者在其他程序中利用这些保存下来的对象信息恢复成实例对象。这种情况下就要用到对象的序列化和反序列化。
其实很早就知道的,在Java中常见的几个类,如:Interger/String等,都实现了java.io.Serializable接口。
这个序列化接口没有任何方法和域,仅用于标识序列化语意;实现 Serializable 接口的类是可序列化的,没有实现此接口的类将不能被序列化和反序列化。
序列化类的所有子类本身都是可序列化的,不再需要显式实现 Serializable 接口。只有经过序列化,才能兼容对象在磁盘文本以及在网络中的传输,以及恢复对象的时候反序列化等操作。
问题一:为何要实现序列化?
答:序列化就是对实例对象的状态(State 对象属性而不包括对象方法)进行通用编码(如格式化的字节码)并保存,以保证对象的完整性和可传递性。
简而言之:序列化,就是为了在不同时间或不同平台的JVM之间共享实例对象
// 经常使用如下: public static void main(String[] args) throws Exception { File file = new File("user.ser"); ObjectOutputStream oout = new ObjectOutputStream(new FileOutputStream(file)); User user = new User("zhang", 18, Gender.MALE); oout.writeObject(user); oout.close(); ObjectInputStream oin = new ObjectInputStream(new FileInputStream(file)); Object newUser = oin.readObject(); oin.close(); System.out.println(newUser); }
如没有 实现Serializable接口,在序列化时,使用ObjectOutputStream的write(object)方法将对象保存时将会出现异常。其实 java.io.Serializable 只是一个没有属性和方法的空接口,但是问题来了。。。
问题二:为何一定要实现 Serializable 才能进行序列化呢?
使用 ObjectOutputStream 来持久化对象, 对于此处抛出的异常,查看该类中实现如下:
private void writeObject0(Object obj, boolean unshared) throws IOException { // ... // remaining cases if (obj instanceof String) { writeString((String) obj, unshared); } else if (cl.isArray()) { writeArray(obj, desc, unshared); } else if (obj instanceof Enum) { writeEnum((Enum) obj, desc, unshared); } else if (obj instanceof Serializable) { writeOrdinaryObject(obj, desc, unshared); } else { if (extendedDebugInfo) { throw new NotSerializableException( cl.getName() + "\n" + debugInfoStack.toString()); } else { throw new NotSerializableException(cl.getName()); } } // ... }
从此可知,如果被写对象类型是String、数组、Enum、Serializable,就可以进行序列化,否则将抛出NotSerializableException。
最后提点注意:
1、在序列化对象时,不仅会序列化当前对象本身,还会对该对象引用的其它对象也进行序列化,如此引用传递序列化。如果一个对象包含的成员变量是容器类等并深层引用,那么序列化过程开销也较大。
2、当字段被声明为 transient 后,默认序列化机制就会忽略该字段。(还有方法就是自定义writeObject方法,见下代码示例)
3、在单例类中添加一个readResolve()方法(直接返回单例对象),以保证在序列化过程仍保持单例特性。
此外补充一下,
在路径下jdk中还有另外一种形式的对象持久化,即:外部化(Externalization)。
public interface Externalizable extends java.io.Serializable { void writeExternal(ObjectOutput out) throws IOException; void readExternal(ObjectInput in) throws IOException, ClassNotFoundException; }
外部化和序列化是实现同一目标的两种不同方法。
通过 Serializable 接口对对象序列化的支持是jdk内支持的 API ,但是java.io.Externalizable的所有实现者必须提供读入和写出的具体实现,怎么实现完全由你自定义。序列化(Serializable )会自动存储所有必要的信息(如属性以及属性类型等),用以反序列化成原来一样的实例,而外部化(Externalizable)则只保存被存储实例中你需要的信息。
示例代码如下:
public class User implements <strong>Externalizable</strong> { private String name; <strong>transient</strong> private Integer age; // 屏蔽字段 private Gender gender; <strong>public User</strong>() { System.out.println("none constructor"); } public User(String name, Integer age, Gender gender) { System.out.println("arg constructor"); this.name = name; this.age = age; this.gender = gender; } // 实现读写 private void <strong>writeObject</strong>(ObjectOutputStream out) throws IOException { out.defaultWriteObject(); out.writeInt(age); // 屏蔽gender } private void <strong>readObject</strong>(ObjectInputStream in) throws IOException, ClassNotFoundException { in.defaultReadObject(); age = in.readInt(); } // 具体重写 @Override public void <strong>writeExternal</strong>(ObjectOutput out) throws IOException { out.writeObject(name); out.writeInt(age); // 屏蔽gender } @Override public void <strong>readExternal</strong>(ObjectInput in) throws IOException, ClassNotFoundException { name = (String) in.readObject(); age = in.readInt(); } }
注意,用Externalizable进行序列化,当读取对象时,会调用被序列化类的无参构造器创建一个新的对象,然后再将被保存对象的字段的值分别填充到新对象中。实现Externalizable接口的类必须要提供一个无参的构造器,且访问权限为 public。
以上是关于Java中为啥要序列化?啥时候用到序列化?的主要内容,如果未能解决你的问题,请参考以下文章