﻿<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/"><channel><title>BlogJava-Dreava-文章分类-Java基础</title><link>http://www.blogjava.net/benniaolk/category/44433.html</link><description>梦幻瓜哇国</description><language>zh-cn</language><lastBuildDate>Sun, 28 Mar 2010 08:46:55 GMT</lastBuildDate><pubDate>Sun, 28 Mar 2010 08:46:55 GMT</pubDate><ttl>60</ttl><item><title>Java序列化及反序列化</title><link>http://www.blogjava.net/benniaolk/articles/316753.html</link><dc:creator>Dreava</dc:creator><author>Dreava</author><pubDate>Sun, 28 Mar 2010 08:33:00 GMT</pubDate><guid>http://www.blogjava.net/benniaolk/articles/316753.html</guid><wfw:comment>http://www.blogjava.net/benniaolk/comments/316753.html</wfw:comment><comments>http://www.blogjava.net/benniaolk/articles/316753.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/benniaolk/comments/commentRss/316753.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/benniaolk/services/trackbacks/316753.html</trackback:ping><description><![CDATA[<h1><span style="font-size: medium">1.概述</span></h1>
<p>&nbsp;&nbsp;&nbsp;&nbsp; 要实现序列化，则必须实现serializable或Externalizable接口。后者继承自前者，两者的区别：实现前者的类可以采用默认的序列化方式。而实现后者的类则完全由自身来控制序列化的行为。<br />
&nbsp;&nbsp;&nbsp;&nbsp; 在序列化和反序列化过程中需要特殊处理的类必须使用下列准确签名来实现特殊方法：</p>
<div style="border-right: #cccccc 1px solid; padding-right: 5px; border-top: #cccccc 1px solid; padding-left: 4px; font-size: 13px; padding-bottom: 4px; border-left: #cccccc 1px solid; width: 98%; word-break: break-all; padding-top: 4px; border-bottom: #cccccc 1px solid; background-color: #eeeeee"><span style="color: #0000ff">private</span><span style="color: #000000">&nbsp;</span><span style="color: #0000ff">void</span><span style="color: #000000">&nbsp;writeObject&nbsp;java.io.ObjectOutputStream&nbsp;out)&nbsp;</span><span style="color: #0000ff">throws</span><span style="color: #000000">&nbsp;IOException&nbsp;&nbsp;&nbsp;<br />
</span><span style="color: #0000ff">private</span><span style="color: #000000">&nbsp;</span><span style="color: #0000ff">void</span><span style="color: #000000">&nbsp;readObject(java.io.ObjectInputStream&nbsp;in)&nbsp;</span><span style="color: #0000ff">throws</span><span style="color: #000000">&nbsp;IOException,&nbsp;ClassNotFoundException;&nbsp;&nbsp;</span></div>
<br />
&nbsp;&nbsp;&nbsp;&nbsp; writeObject 方法负责写入特定类的对象的状态，以便相应的 readObject 方法可以还原它。通过调用 out.defaultWriteObject 可以调用保存 Object 的字段的默认机制。该方法本身不需要涉及属于其超类或子类的状态。状态是通过使用 writeObject 方法或使用 DataOutput 支持的用于基本数据类型的方法将各个字段写入 ObjectOutputStream 来保存的。 <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; readObject 方法负责从流中读取并还原类字段。它可以调用 in.defaultReadObject 来调用默认机制，以还原对象的非静态和非瞬态字段。defaultReadObject 方法使用流中的信息来分配流中通过当前对象中相应命名字段保存的对象的字段。这用于处理类发展后需要添加新字段的情形。该方法本身不需要涉及属于其超类或子类的状态。状态是通过使用 writeObject 方法或使用 DataOutput 支持的用于基本数据类型的方法将各个字段写入 ObjectOutputStream 来保存的。&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;假定有一个名为Customer的类，它的对象需要序列化，如果Customer类仅仅实现了Serializable接口的类，那么将按照以下方式序列化及反序列化Customer对象：<br />
<ul>
    <li>ObjectOutputStream采用默认的序列化方式，对Customer对象的非transient的实例变量进行序列化。
    <li>ObjectInputStream采用默认的反序列化方式，对Customer对象的非transient的实例变量进行反序列化。 </li>
</ul>
&nbsp;&nbsp; 如果Customer类仅仅实现了Serializable接口的类，并且还定义了readObject(ObjectInputStream in)和writeObject(ObjectOutputStream out)方法，那么将按照以下方式序列化及反序化Customer对象：
<ul>
    <li>ObjectOutputStream会调用Customer类的writeObject(ObjectOutputStream out)方法来进行序列化。
    <li>ObjectInputStream会调用Customer类的readObject(ObjectInputStream in)方法来进行反序列化。 </li>
</ul>
<p>&nbsp;&nbsp;&nbsp;&nbsp; 如果Customer类实现了Externalizable接口，那么Customer类必须实现readExternal<br />
&nbsp;&nbsp;&nbsp;&nbsp; (ObjectInput in)和writeExternal(ObjectOutput out)方法。在这种情况下，将按照以下方式序列化及反序列化Customer对象：</p>
<ul>
    <li>ObjectOutputStream会调用Customer类的writeExternal(ObjectOutput out)方法来进行序列化。
    <li>&nbsp;ObjectInputStream先通过Customer类的不带参数的构造方法创建一个Customer对象，然后调用它的readExternal(ObjectInput int)方法来进行反序列化。 </li>
</ul>
<h1><span style="font-size: medium">2.实现Serializable接口</span></h1>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ObjectOutputStream只能对实现了Serializable接口的类的对象进行序列化。默认情况下，ObjectOutputStream按照默认方式序列化，这种序列化方式仅仅对对象的非transient的实例变量进行序列化，而不会序列化对象的transient的实例变量，也不会序列化静态变量。</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 而当ObjectInputStream按照默认方式反序列化时，有以下特点：</p>
<ul>
    <li>如果在内在中对象所属的类还没有被加载，那么会先加载并初始化这个类。如果在classpath中不存在相应的类文件，那么会抛出ClassNotFoundException。
    <li>在反序列化时不会调用类的任何构造方法。 </li>
</ul>
<p>&nbsp;&nbsp;&nbsp;&nbsp; 被transient修饰符来修饰的实例变量是不会序列化的，一般用transient来修饰以下类型的的变量：</p>
<p>&nbsp;&nbsp;&nbsp; １）&nbsp;实例变量不代表对象的固有的内部数据，仅仅代表具有一定逻辑含义的临时数据。<br />
&nbsp;&nbsp;&nbsp; ２）&nbsp;实例变量表示一些比较敏感的信息，如密码，出于安全方面的原因，不希望对其序列化。<br />
&nbsp;&nbsp;&nbsp; ３）&nbsp;实例变量需要按照用户自定义的方式序列化，如经过加密后再序列化。这这种情况下可以将其用transient修饰，然后在writeObject()方法中对其序列化。&nbsp;&nbsp;&nbsp; </p>
<h2>２.１序列化对象图</h2>
<p>&nbsp;&nbsp;&nbsp; 参见SerializableDemo中的Customer2及Order2类之间的关系。<br />
&nbsp;&nbsp;&nbsp; 当通过ObjectOutputStream对象的writeObject(customer)方法序列化Customer2对象时，也会序列化与它关联的Order2对象。而当通过ObjectInputStream对象的readObject()方法反序列化Customer2对象时，实际上会对整个对象图反序列化。<br />
&nbsp;&nbsp;&nbsp; 如果对象A持有对象B的引用（注意是A持有B的引用），以及间接持有其他对象的引用，则按照默认方式序列化对象A时，会将A以及A持有的以及间接持有的所有对象都序列化。反序列化也是如此。</p>
<p>&nbsp;&nbsp;&nbsp; </p>
<h2>２.２控制序列化的行为</h2>
<p>&nbsp;&nbsp;&nbsp;&nbsp;如果用户希望控制类的序列化方式，可以在可序列化类中提供以下形式的writeObject()方法和readObject()方法：</p>
<div style="border-right: #cccccc 1px solid; padding-right: 5px; border-top: #cccccc 1px solid; padding-left: 4px; font-size: 13px; padding-bottom: 4px; border-left: #cccccc 1px solid; width: 98%; word-break: break-all; padding-top: 4px; border-bottom: #cccccc 1px solid; background-color: #eeeeee"><img src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align="top"  alt="" /><span style="color: #0000ff">private</span><span style="color: #000000">&nbsp;</span><span style="color: #0000ff">void</span><span style="color: #000000">&nbsp;writeObject&nbsp;java.io.ObjectOutputStream&nbsp;out)</span><span style="color: #0000ff">throws</span><span style="color: #000000">&nbsp;IOException&nbsp;&nbsp;&nbsp;<br />
<img src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align="top"  alt="" /></span><span style="color: #0000ff">private</span><span style="color: #000000">&nbsp;</span><span style="color: #0000ff">void</span><span style="color: #000000">&nbsp;readObject&nbsp;java.io.ObjectInputStream&nbsp;in)</span><span style="color: #0000ff">throws</span><span style="color: #000000">&nbsp;IOException,ClassNotFoundException;&nbsp;&nbsp;<br />
<img src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align="top"  alt="" /></span></div>
<p>&nbsp;&nbsp;&nbsp;&nbsp;一般的做法是，在writeObject()方法中，选留骼ObjectOutputStream的defaultWriteObject()方法，使得对象输出流先执行默认的序列化操作。<br />
&nbsp;&nbsp;&nbsp; 反序列化时，在readObject()方法中，先调用ObjectInputStream的defaultReadObject()方法。</p>
<p>&nbsp;&nbsp;&nbsp; </p>
<h2>２.３readResolve()方法在童便类中的运用</h2>
<p><br />
&nbsp;&nbsp;&nbsp; 如果一个类提供了readResolve()方法，那么在执行反序列化操作时，先按照默认方式或者用户自定义的方式进行反序列化，最后再调用readResolve()方法，该方法返回的对象为反序列化的最终结果。<br />
&nbsp;&nbsp;&nbsp; readResolve()方法应该能够被类本身、同一个包中的类，或者访问，因此readResolve()方法的访问权限可以是pirvate、默认或protected级别。<br />
&nbsp;&nbsp;&nbsp; readResolve()方法用来重新指定反序列化得到的对象，与此对应，Java序列化规范还允许在可序列化类中定义一个writeReplace()方法，用来重新指定被序列化的对象。writeReplace()方法返回一个Object类型的对象，这个返回对象才是真正要被序列化的对象。权限也可以为以上三种之一。&nbsp;&nbsp; </p>
<h1>３.实现Externalizalbe接口</h1>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Externallizable接口继承自Serializable接口，如果一个类实现了Externalizable接口，那么将完全由这个类控制自身的序列化行为。Externalizable接口中声明了两个方法：<br />
</p>
<div style="border-right: #cccccc 1px solid; padding-right: 5px; border-top: #cccccc 1px solid; padding-left: 4px; font-size: 13px; padding-bottom: 4px; border-left: #cccccc 1px solid; width: 98%; word-break: break-all; padding-top: 4px; border-bottom: #cccccc 1px solid; background-color: #eeeeee"><img src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align="top"  alt="" /><span style="color: #0000ff">public</span><span style="color: #000000">&nbsp;</span><span style="color: #0000ff">void</span><span style="color: #000000">&nbsp;writeExternal(ObjectOutput&nbsp;out)</span><span style="color: #0000ff">throws</span><span style="color: #000000">&nbsp;IOException&nbsp;&nbsp;&nbsp;<br />
<img src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align="top"  alt="" /></span><span style="color: #0000ff">public</span><span style="color: #000000">&nbsp;</span><span style="color: #0000ff">void</span><span style="color: #000000">&nbsp;readExternal(ObjectInput&nbsp;in)&nbsp;</span><span style="color: #0000ff">throws</span><span style="color: #000000">&nbsp;IOException,ClassNotFoundException&nbsp;&nbsp;</span></div>
<p>&nbsp;&nbsp;&nbsp;&nbsp;writeExternal负责序列化操作，readExternal负责反序列化操作。在对实现了Externallizable接口的类的对象进行反序列化时，会先调用类的不带参数的构造方法，这是有别默认反序列化方式的（见２）。所以实现Externalizable接口的类必须要有不含参数的构造方法。</p>
<p>&nbsp;</p>
<h1>４.可序列化类的不同版本的序列化兼容性</h1>
<p>&nbsp;&nbsp;&nbsp; 实例见SerializableDemo里的Customer5的两个版本。将Customer１.０和SimpleServer放在server端，将Customer2.0和SimpleClient放在Client端，直接运行，将发现抛出错误，显示不兼容，解决办法是手动将两个Customer的serialVersionUID设为同一个值，这样就能兼容。但是这种办法的能力很有限，当一个类的不同版本的serialVersionUID相同，仍然有可能出现序列化不兼容的情况。因为序列化兼容性不仅取决于serialVersionUID，还取决于类的不同版本的实现细节和序列化细节。</p>
<img src ="http://www.blogjava.net/benniaolk/aggbug/316753.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/benniaolk/" target="_blank">Dreava</a> 2010-03-28 16:33 <a href="http://www.blogjava.net/benniaolk/articles/316753.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>详解finalize()</title><link>http://www.blogjava.net/benniaolk/articles/316752.html</link><dc:creator>Dreava</dc:creator><author>Dreava</author><pubDate>Sun, 28 Mar 2010 08:18:00 GMT</pubDate><guid>http://www.blogjava.net/benniaolk/articles/316752.html</guid><wfw:comment>http://www.blogjava.net/benniaolk/comments/316752.html</wfw:comment><comments>http://www.blogjava.net/benniaolk/articles/316752.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/benniaolk/comments/commentRss/316752.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/benniaolk/services/trackbacks/316752.html</trackback:ping><description><![CDATA[&nbsp;
<p style="text-indent: 21pt"><span style="font-family: 宋体">网上很多关于面试笔试的一些题目，基本上都问到了同一个问题，</span>finalize()<span style="font-family: 宋体">的作用是什么？网上给的答案说的可能比较笼统，今天又重温了一遍</span>Thinking in Java<span style="font-family: 宋体">，又回顾了一下</span>finalize()<span style="font-family: 宋体">的意义和作用，特意写出来，姑且作为备忘之用。</span></p>
<p style="text-indent: 21pt"><span style="font-family: 宋体">估计大家对</span>finalize()<span style="font-family: 宋体">都有个基本的认识，那就是作清理资源之用。比如在类的某个方法中打开了一个文件，那么你可能需要通过</span>finalize()<span style="font-family: 宋体">方法来释放该资源。但是话说回来，</span>java<span style="font-family: 宋体">世界里，一切皆对象，而任何一个学</span>java<span style="font-family: 宋体">的人都知道，</span>java<span style="font-family: 宋体">对象是不需要手动去清理的，因为</span>java<span style="font-family: 宋体">有强大的垃圾回收器，有人可能会有疑问，那既然如此，为什么还需要在</span>finalize()<span style="font-family: 宋体">中手动释放资源呢？当然，面对这一问题，有很好的理由去解释，因为资源是有限的，而我们又知道垃圾回收器的线程优先级非常低，在逼不得以的情况下，它才会工作，也就是说，只要内存足够，失效的对象就不会被清理，它所持有的资源也就得不到释放，而资源又是有限的，比如数据库连接，所以需要我们去手动释放。不过这一点，在</span>JDK7<span style="font-family: 宋体">里面似乎是有所发展，听说是会自动实现这一点，也就是说，</span>sun<span style="font-family: 宋体">可能为我们做了这个工作，以后的程序中可能不需要手动释放类似数据库资源的代码了。</span></p>
<p style="text-indent: 21pt"><span style="font-family: 宋体">如果的确如此，那是不是意味着</span>finalize()<span style="font-family: 宋体">就没用了呢？错。不过</span>finalize()<span style="font-family: 宋体">确实用到的时候不会很多，它主要用于一些本地方法调用过程中产生的资源清理。比如你通过</span>Native method<span style="font-family: 宋体">调用</span>c<span style="font-family: 宋体">语言的</span>melloc<span style="font-family: 宋体">方法分配了一些内存空间，而这部分内存是需要</span>c<span style="font-family: 宋体">语言的</span>free()<span style="font-family: 宋体">来进行释放的，如果不这样，就会产生内存泄漏，所以你需要在</span>finalize()<span style="font-family: 宋体">中用</span>Native method<span style="font-family: 宋体">方法调用</span>free()<span style="font-family: 宋体">将其释放。</span></p>
<p style="text-indent: 21pt"><span style="font-family: 宋体">不过话说回来，</span>finalize()<span style="font-family: 宋体">方法并不十分保险，因为</span>finalize()<span style="font-family: 宋体">只有在垃圾回收器工作的时候才会被调用，也就是说，通过它进行资源释放并不能确保马上被释放，甚至可能根本不会被释放（因为垃圾回收器可能不会工作），因此，资源释放尽量另想办法，别太相信</span>finalize()<span style="font-family: 宋体">和垃圾回收器了。</span></p>
 <img src ="http://www.blogjava.net/benniaolk/aggbug/316752.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/benniaolk/" target="_blank">Dreava</a> 2010-03-28 16:18 <a href="http://www.blogjava.net/benniaolk/articles/316752.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>