- 浏览: 97997 次
- 性别:
- 来自: 广州
文章分类
最新评论
-
鸟气凌人:
最经正在看基础
Oracle PL/SQL中游标和游标变量的使用 -
matraxa:
讲得很清楚,顶一个!
JVM垃圾回收机制总结(1) —基本概念 -
ZavaKid:
顶楼主,通俗易懂。
byw,上面的图用什么软件画的?
JVM垃圾回收机制总结(1) —基本概念 -
47478220:
对这里还不太了解,学习了。
Java 虚拟机体系结构
我们都知道,Java序列化可以让我们记录下运行时的对象状态(对象实例域的值),也就是我们经常说的对象持久化 。这个过程其实是非常复杂的,这里我们就好好理解一下Java的对象序列化。
1、首先我们要搞清楚,Java对象序列化是将 对象的实例域数据( 包括private私有域) 进行持久化存储。而并非是将整个对象所属的类信息进行存储。 其实了解JVM的话,我们就能明白这一点了。实际上堆中所存储的对象包含了实例域数据值以及指向类信息的地址,而对象所属的类信息却存放在方法区中。当我们要对持久层数据反序列化成对象的时候,也就只需要将实例域数据值存放在新创建的对象中即可。
2、我们都知道凡要序列化的类都必须实现Serializable接口。 但是不是所有类都可以序列化呢?当然不是这样,想想看序列化可以让我们轻而易举的接触到对象的私有数据域,这是多么危险的漏洞呀!总结一下,JDK中有四种类型的类对象是绝对不能序列化的 。
(1) 太依赖于底层实现的类(too closely tied to native code)。比如java.util.zip.Deflater。
(2) 对象的状态依赖于虚拟机内部和不停变化的运行时环境。比如java.lang.Thread, java.io.InputStream
(3) 涉及到潜在的安全性问题。比如:java.lang.SecurityManager, java.security.MessageDigest
(4) 全是静态域的类,没有对象实例数据。要知道静态域本身也是存储在方法区中的。
3、自定义的类只要实现了Serializable接口,是不是都可以序列化呢? 当然也不是这样,看看下面的例子:
class Employee implements Serializable{ private ZipFile zf=null; Employee(ZipFile zf){ this.zf=zf; } } ObjectOutputStream oout= new ObjectOutputStream(new FileInputStream(new File("aaa.txt"))); oout.writeObject(new Employee(new ZipFile("c://.."));
我们会发现运行之后抛出java.io.NotSerializableException : java.util.zip.ZipFile 。很明显,如果要对Employee对象序列化,就必须对其数据域ZipFile对象也进行序列化,而这个类在JDK中是不可序列化的。因此,包含了不可序列化的对象域的对象也是不能序列化的。 实际上,这也并非不可能,我们在下面第6点会谈到。
4、可序列化的类成功序列化之后,是不是一定可以反序列化呢? (这里默认在同一环境下,而且类定义永远不会改变,即满足兼容性。在下面我们会讨论序列化的不兼容性)。答案是不一定哦!我们还是看一个列子:
//父类对象不能序列化 class Employee{ private String name; Employee(String n){ this.name=n; } public String getName(){ return this.name; } } //子类对象可以序列化 class Manager extends Employee implements Serializable{ private int id; Manager(String name, int id){ super(name); this.id=id; } } //序列化与反序列化测试 public static void main(String[] args) throws IOException, ClassNotFoundException{ File file=new File("E:/aaa.txt"); ObjectOutputStream oout=new ObjectOutputStream(new FileOutputStream(file)); oout.writeObject(new Manager("amao",123)); oout.close(); System.out.println("序列化成功"); ObjectInputStream oin=new ObjectInputStream(new FileInputStream(file)); Object o=oin.readObject(); oin.close(); System.out.println("反序列化成功:"+((Manager) o).getName()); }
程序的运行结果是:打印出“序列化成功”之后抛出java.io.InvalidClassException: Manager; Manager; no valid constructor。 为什么会出现这种情况呢?很显然,序列化的时候只是将Manager类对象的数据域id写入了文件,但在反序列化的过程中,需要在堆中建立一个Manager新对象。我们都知道任何一个类对象的建立都首先需要调用父类的构造器对父类进行初始化,很可惜序列化文件中并没有父类Employee的name数据,那么此时调用Employee(String)构造器会因为没有数据而出现异常。既然没有数据,那么可不可以调用无参构造器呢? 事实却是如此,如果有Employee()无参构造器的存在,将不会抛出异常,只是在执行打印的时候出现--- “反序列化成功:null”。
总结一下:如果当前类的所有超类中有一个类即不能序列化,也没有无参构造器。那么当前类将不能反序列化。如果有无参构造器,那么此超类反序列化的数据域将会是null或者0,false等等。
5、序列化的兼容性问题!
类定义很有可能在不停的人为更新(比如JDK1.1到JDK1.2中HashTable的改变)。那么以前序列化的旧类对象很可能不能再反序列化成为新类对象。这就是序列化的兼容性问题,严格意义上来说改变类中除static 和transient以外的所有部分都会造成兼容性问题。而JDK采用了一种stream unique identifier (SUID) 来识别兼容性。SUID是通过复杂的函数来计算的类名,接口名,方法和数据域的 一个64位 hash值。而这个值存储在类中的静态域内:
private static final long serialVersionUID = 3487495895819393L
只要稍微改动类的定义,这个类的SUID就会发生变化,我们通过下面的程序来看看:
//修改前的Employee class Employee implements Serializable{ private String name; Employee(String n){ this.name=n; } public String getName(){ return this.name; } } //测试,打印SUID=5135178525467874279L long serialVersionUID=ObjectStreamClass.lookup(Class.forName("Employee")).getSerialVersionUID(); System.out.println(serialVersionUID); //修改后的Employee class Employee implements Serializable{ private String name1; //注意,这里略微改动一下数据域的名字 Employee(String n){ this.name1=n; } public String getName(){ return this.name1; } } //测试,打印SUID=-2226350316230217613L long serialVersionUID=ObjectStreamClass.lookup(Class.forName("Employee")).getSerialVersionUID(); System.out.println(serialVersionUID);
两次测试的SUID都不一样,不过你可以试试如果name域是static或transient声明的,那么改变这个域名是不会影响SUID的。
很显然,JVM正是通过检测新旧类SUID的不同,来检测出序列化对象与反序列化对象的不兼容。抛出 java.io.InvalidClassException: Employee; local class incompatible:
很多时候,类定义的改变势在必行,但又不希望出现序列化的不兼容性。我们就可以通过在类中显示的定义serialVersionUID,并赋予一个明确的long值即可。这样会逃过JVM的默认兼容性检查。但是如果数据域名的改变会导致反序列化后,改变的数据域只能得到默认的null或者0或者false值。
6、在上面第3点中谈到了一个不能成功序列化的Employee的列子,原因就是包含了一个不能序列化的ZipFile对象引用的数据域。但有时我们非常想将ZipFile所对应的本地文件路径进行序列化,是不是真的没有办法了呢? 这里我们就将一个非常有用的应用。
当我们需要用writeObject(Object)方法对某个类对象序列化的时候,会首先对这个类对象的所有超类按照继承层次从高到低来写出每个超类的数据域。谁能保证每个超类都实现了Serializable接口呢? 其实,对于这些不能序列化的类,JVM会检查这些类是否有这样一个方法:
private void writeObject(ObjectOutputStream out)throws IOException
如果有,JVM会调用这个方法仍然对该类的数据域进行序列化。我们来看看JDK的ObjectOutputStream类中对这一部分的实现(我这里只列出了源码中的执行过程):
//下面的方法从上到下进行调用 writeObject(Object); //ObjectOutputStream的writeObject方法 public final void writeObject(Object obj) throws IOException { writeObject0(obj, false); } //ObjectOutputStream, 底层写入Object的实现 private void writeObject0(Object obj, boolean unshared) { if (obj instanceof Serializable) { writeOrdinaryObject(obj, desc, unshared); } //ObjectOutputStream private void writeOrdinaryObject(Object obj, ObjectStreamClass desc, boolean unshared) { writeSerialData(obj, desc); } //ObjectOutputStream, 对超类到子类的每个可序列化的类,写出数据域 private void writeSerialData(Object obj, ObjectStreamClass desc) throws IOException{ //如果类中有writeObject(ObjectOutputStream)方法,则通过底层进行调用 if (slotDesc.hasWriteObjectMethod()) { slotDesc.invokeWriteObject(obj, this); }//如果没有此方法,则采用默认的写类数据域的方法。 else {//这个方法会对可序列化的对象中的数据域进行写出,但是如果这个数据域是不可序列化而且没有writeObject(ObjectOutputStream)方法的类对象,那么将抛出异常。 defaultWriteFields(obj, slotDesc); } }
ObjectOutputStream中的writeSerialData()方法说明了JVM检查writeObject(ObjectOutputStream out) 这个私有方法的潜在执行机制。这就是说,我们可以通过构造这个方法,使得原本不能序列化的类的部分数据域可以序列化。下面我们就开始对ZipFile进行可序列化的改造吧!
//自定义的一个可序列化的ZipFile,当然这个类不能继承JDK中的ZipFile,否则序列化将不可能完成。 class SerializableZipFile implements Serializable{ public ZipFile zf; //包含一个ZipFile对象 SerializableZipFile(String filename) throws IOException{ zf=new ZipFile(filename); } //对ZipFile中的文件名进行序列化,因为它是String类型的 private void writeObject(ObjectOutputStream out)throws IOException{ out.writeObject(zf.getName()); } //对应的,反序列化过程中JVM也会检查类似的一个私有方法。 private void readObject(ObjectInputStream in)throws IOException,ClassNotFoundException{ String filename=(String)in.readObject(); zf=new ZipFile(filename); } } //测试 public static void main(String[] args) throws IOException, ClassNotFoundException{ //序列化 File file=new File("E:/aaa.txt"); ObjectOutputStream oout=new ObjectOutputStream(new FileOutputStream(file)); oout.writeObject(new SerializableZipFile("e:/aaa.zip")); oout.close(); System.out.println("序列化成功"); //反序列化 ObjectInputStream oin=new ObjectInputStream(new FileInputStream(file)); Object o=oin.readObject(); oin.close(); System.out.println("反序列化成功:"+((SerializableZipFile) o).zf.getName()); } //序列化成功 //反序列化成功:e:\aaa.zip
太棒了,我们构造了一个可序列化的ZipFile类。这真是一件伟大的事情。
发表评论
-
Java异常错误收集
2011-06-27 14:30 1207算术异常类:ArithmeticExe ... -
Java方法参数是引用调用还是值调用?
2011-01-31 15:06 843方法调用(call by) ... -
类与对象的初始化问题
2011-01-31 15:05 950★ 类常量 Java类常量(final修饰) ... -
Java动态绑定机制的内幕
2011-01-31 15:04 685在Java方法调用的过程中,JVM是如何知道调用的是 ... -
Java代理机制
2011-01-31 15:03 717代理(Proxy)实际上是一种设计模式。代理模式的作用是: ... -
杂谈Java异常处理
2011-01-31 14:58 746软件开发中一个古老的说法是:80%的工作使用2 ... -
Java类型间的转型
2011-01-31 14:55 883★ 基本数据类型间的 ... -
String in Java
2011-01-31 14:53 638作者:每次上网冲杯Java时,都能看到关于String无休无止 ... -
Java集合类详解(一):集合框架概述
2010-12-01 14:24 13951、集合框架概述1.1、容器简介 到目前为止,我们已经 ... -
java集合类详解(二):Collection
2010-12-01 14:22 9741、Collection1.1、常用方 ... -
java集合类详解(三):List
2010-12-01 14:21 11801、List1.1、概述 前面我们讲述的Collect ... -
java集合类详解(四):Map
2010-12-01 14:19 20291、Map 1.1、概述 ... -
java集合类详解(五):Set
2010-12-01 14:09 9161.1、概述 Java 中的Set和正好和数学上直观 ... -
详解 HashCode
2010-11-12 14:03 877hashcode的作用就是为了快速查找集合中是否存在重复元素。 ... -
Java堆.栈和常量池学习
2010-11-12 14:01 10181.寄存器:最快的存储区, 由编译器根据需求进行分配,我们在程 ... -
集合 中Iterator 、Vector、ArrayList、List 使用深入剖析
2010-11-12 10:03 1122线性表,链表,哈希表是常用的数据结构,在进行Java ... -
MyEclipse使用小技巧
2010-11-11 20:31 704第一步: 取消自动validation validat ...
相关推荐
java 对象的序列化与反序列化 java 对象的序列化与反序列化
java反序列化工具,覆盖jboss、weblogic、websphere。
java序列化和反序列化java序列化和反序列化java序列化和反序列化
Xson是一个Java对象序列化和反序列化程序。支持Java对象到字节数组的序列化,和从字节数组到Java对象的反序列化。 Maven: <groupId>com.github.xsonorg</groupId> <artifactId>xson-core <version>1.0.1 ...
学习Java序列化,里面包含一份PPT以及相应的源码演示
大家请看Java的序列化结构有不足的地方请指教也希望指出不足的地方。
java 序列化详细解释 很详细 适用于高级软件开发者
序列化是干什么的? 简单说就是为了保存在内存中的各种对象的状态(也就是实例...虽然你可以用你自己的各种各样的方法来保 存object states,但是Java给你提供一种应该比你自己好的保存对象状态的机制,那就是序列化。
在应用java进行c-s开发的时候,尤其涉及到图片和视频之间的传输时,需要用序列化和反序列化技术,希望对您有帮助
java序列化代码示例,详细讲解序列化作用于使用注意规则项!!!
java序列化(Serializable)的作用和反序列化.doc 有详细的讲解哦。 在什么地方用的到都有说明的.
java对象 java序列化 java反序列化
java 常用序列化和反序列化框架使用demo ,java 常用序列化和反序列化框架使用demo
Java序列化与反序列化 Java序列化与反序列化 Java序列化与反序列化 Java序列化与反序列化 Java序列化与反序列化
将java数据 序列化成PHP的格式 a:4:{s:6:"title2";s:13:"这是标题2";s:6:"title3";s:13:"这是标题3";s:5:"title";s:13:"这是标题1";s:6:"title4";s:13:"这是标题4";} 或者a:1:{i:0;a:1:{s:4:"name";s:10:"这是1321";...
java 的序列化与反序列化举例测试
极好的序列化与发序列化代码。可以处理array集合,数组或者单个对象等的序列化与反序列化。
java 序列化,java 序列化,java 序列化,java 序列化,java 序列化,java 序列化
这是Java序列化标准规范,对于研究序列化源码能够提供指导作用,为深入研究Java序列技术奠定基础。
Java Json序列化与反序列化