﻿<?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-呓语的博客-随笔分类-学习心得</title><link>http://www.blogjava.net/xieyunlong/category/5650.html</link><description>不管前面的路有多艰辛多长，只要怀着一颗执着的心。成功就离你不远了!</description><language>zh-cn</language><lastBuildDate>Wed, 28 Feb 2007 03:24:14 GMT</lastBuildDate><pubDate>Wed, 28 Feb 2007 03:24:14 GMT</pubDate><ttl>60</ttl><item><title>final关键字的使用(Thinking In Java 读书笔记)</title><link>http://www.blogjava.net/xieyunlong/archive/2006/01/06/26953.html</link><dc:creator>呓语的博客</dc:creator><author>呓语的博客</author><pubDate>Fri, 06 Jan 2006 14:27:00 GMT</pubDate><guid>http://www.blogjava.net/xieyunlong/archive/2006/01/06/26953.html</guid><wfw:comment>http://www.blogjava.net/xieyunlong/comments/26953.html</wfw:comment><comments>http://www.blogjava.net/xieyunlong/archive/2006/01/06/26953.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/xieyunlong/comments/commentRss/26953.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/xieyunlong/services/trackbacks/26953.html</trackback:ping><description><![CDATA[<P><FONT face=Arial>final关键字最基本的含义就是表明“这个东西不能改变”。之所以这样，可能是考虑到两方面的因素：设计或效率。</FONT></P>
<P><FONT face=Arial>final关键字可应用在三种场合： 数据、方法以及类</FONT></P>
<P><STRONG><FONT face=Arial color=#0000ff>1. final数据</FONT></STRONG></P>
<P><FONT face=Arial>表明某个数据是“常数”，永远不会改变。使用final定义，编译器可以直接将常数值封装到需要的计算过程里。也就是说，计算可以在编译期前执行，从而节省运行时的开销。在JAVA中，这些常数必须属于基本数据类型。并且在对这样的一个常数进行定义的时候，必须给出一个值。</FONT></P>
<P><FONT face=Arial>而当对象句柄使用final时，必须将句柄初始化到一个具体的对象，而且永远不能将句柄指向另一个对象。然而，对象本身可以修改。</FONT></P>
<P><STRONG><FONT face=Arial>空白final</FONT></STRONG></P>
<P><FONT face=Arial>Java允许创建“空白final”。即不赋初始值，但空白final必须在实际使用前得到正确的初始化。如果将赋初始值的语句放在不同的构造器中，则final字段可以随着调用不同的构造器而获得不同的初始值。但一旦确定，值将无法改变。</FONT></P>
<P><FONT face=Arial>final数据无法更改：</FONT></P>
<P><FONT face=Arial>{<BR>&nbsp; final int i=5;<BR>&nbsp;&nbsp;i=6;<BR>}</FONT></P>
<P><FONT face=Arial>以上语句会在编译期间出错：<BR>:cannot assign a value to final variable i=6;</FONT></P>
<P><STRONG><FONT face=Arial>final自变量</FONT></STRONG></P>
<P><FONT face=Arial>JAVA允许将自变量设成final属性。即在方法的变量列表中进行声明。这样意味着在一个方法内部，不能改变自变量句柄所指向的东西。</FONT></P>
<P><STRONG><FONT face=Arial color=#0000ff>2. final 方法</FONT></STRONG></P>
<P><FONT face=Arial>将一个方法标识为final，则可防止任何继承类改变这个方法的本来含义。即不可被覆盖或改写。</FONT></P>
<P><FONT face=Arial>同时将一个方法设置成final后，编译器可以把对那个方法的所有调用都嵌入到调用里，即采用常规的代码插入方法。因此可以提高程序效率。当然，如果方法体积太大，那么程序就会变的臃肿。</FONT></P>
<P><FONT face=Arial>因此，通常只有在方法的代码量非常少，或者想明确禁止方法被覆盖的时候，才应考虑将一个方法设置为final 。</FONT></P>
<P><FONT face=Arial>类中所有的private方法都自动成为final。因为我们不能从类外访问final方法，所以它绝对不会被其他方法覆盖。</FONT></P>
<P><STRONG><FONT face=Arial color=#0000ff>3. final类</FONT></STRONG></P>
<P><FONT face=Arial color=#000000>若在整个类定义前冠以final关键字，表明不希望从这个类继承。而final类的数据成员既可以是final的，也可以不是。因为final类禁止了继承，所以final类中的所有方法都默认为final。因为此时不可能覆盖它们。（当然可以再为final类中的方法添加final指示符号，但这样做没有任何意义）</FONT></P><img src ="http://www.blogjava.net/xieyunlong/aggbug/26953.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/xieyunlong/" target="_blank">呓语的博客</a> 2006-01-06 22:27 <a href="http://www.blogjava.net/xieyunlong/archive/2006/01/06/26953.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>java对象序列化学习笔记</title><link>http://www.blogjava.net/xieyunlong/archive/2005/12/06/22711.html</link><dc:creator>呓语的博客</dc:creator><author>呓语的博客</author><pubDate>Tue, 06 Dec 2005 06:24:00 GMT</pubDate><guid>http://www.blogjava.net/xieyunlong/archive/2005/12/06/22711.html</guid><wfw:comment>http://www.blogjava.net/xieyunlong/comments/22711.html</wfw:comment><comments>http://www.blogjava.net/xieyunlong/archive/2005/12/06/22711.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/xieyunlong/comments/commentRss/22711.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/xieyunlong/services/trackbacks/22711.html</trackback:ping><description><![CDATA[<P>目前网络上关于对象序列化的文章不少，但是我发现详细叙述用法和原理的文章太少。本人把自己经过经验总结和实际运用中的体会写成的学习笔记贡献给大家。希望能为整个java社区的繁荣做一点事情。<BR>序列化的过程就是对象写入字节流和从字节流中读取对象。将对象状态转换成字节流之后，可以用java.io包中的各种字节流类将其保存到文件中，管道到另一线程中或通过网络连接将对象数据发送到另一主机。对象序列化功能非常简单、强大，在RMI、Socket、JMS、EJB都有应用。对象序列化问题在网络编程中并不是最激动人心的课题，但却相当重要，具有许多实用意义。<BR>一：对象序列化可以实现分布式对象。主要应用例如：RMI要利用对象序列化运行远程主机上的服务，就像在本地机上运行对象时一样。<BR>二：java对象序列化不仅保留一个对象的数据，而且递归保存对象引用的每个对象的数据。可以将整个对象层次写入字节流中，可以保存在文件中或在网络连接上传递。利用对象序列化可以进行对象的“深复制”，即复制对象本身及引用的对象本身。序列化一个对象可能得到整个对象序列。<BR>从上面的叙述中，我们知道了对象序列化是java编程中的必备武器，那么让我们从基础开始，好好学习一下它的机制和用法。<BR><BR>java序列化比较简单，通常不需要编写保存和恢复对象状态的定制代码。实现java.io.Serializable接口的类对象可以转换成字节流或从字节流恢复，不需要在类中增加任何代码。只有极少数情况下才需要定制代码保存或恢复对象状态。这里要注意：不是每个类都可序列化，有些类是不能序列化的，例如涉及线程的类与特定JVM有非常复杂的关系。<BR></P>
<H3>序列化机制：</H3>
<P><BR>序列化分为两大部分：<B>序列化</B>和<B>反序列化</B>。序列化是这个过程的第一部分，将数据分解成字节流，以便存储在文件中或在网络上传输。反序列化就是打开字节流并重构对象。对象序列化不仅要将基本数据类型转换成字节表示，有时还要恢复数据。恢复数据要求有恢复数据的对象实例。ObjectOutputStream中的序列化过程与字节流连接，包括对象类型和版本信息。反序列化时，JVM用头信息生成对象实例，然后将对象字节流中的数据复制到对象数据成员中。下面我们分两大部分来阐述：<BR><BR></P>
<H3>处理对象流：</H3>
<P>（序列化过程和反序列化过程）<BR><BR>java.io包有两个序列化对象的类。ObjectOutputStream负责将对象写入字节流，ObjectInputStream从字节流重构对象。<BR>我们先了解ObjectOutputStream类吧。ObjectOutputStream类扩展DataOutput接口。<BR>writeObject()方法是最重要的方法，用于对象序列化。如果对象包含其他对象的引用，则writeObject()方法递归序列化这些对象。每个ObjectOutputStream维护序列化的对象引用表，防止发送同一对象的多个拷贝。（这点很重要）由于writeObject()可以序列化整组交叉引用的对象，因此同一ObjectOutputStream实例可能不小心被请求序列化同一对象。这时，进行反引用序列化，而不是再次写入对象字节流。<BR>下面，让我们从例子中来了解ObjectOutputStream这个类吧。<BR></P><PRE><FONT color=#006600><I>// 序列化 today's date 到一个文件中.</FONT></I><BR>    FileOutputStream f = <B>new</B> FileOutputStream(<FONT color=#000099>"tmp"</FONT>);<BR>    ObjectOutputStream s = <B>new</B> ObjectOutputStream(f);<BR>    s.writeObject(<FONT color=#000099>"Today"</FONT>);<BR>    s.writeObject(<B>new</B> Date());<BR>    s.flush();<BR></PRE>
<P><BR>现在，让我们来了解ObjectInputStream这个类。它与ObjectOutputStream相似。它扩展DataInput接口。ObjectInputStream中的方法镜像DataInputStream中读取Java基本数据类型的公开方法。readObject()方法从字节流中反序列化对象。每次调用readObject()方法都返回流中下一个Object。对象字节流并不传输类的字节码，而是包括类名及其签名。readObject()收到对象时，JVM装入头中指定的类。如果找不到这个类，则readObject()抛出ClassNotFoundException,如果需要传输对象数据和字节码，则可以用RMI框架。ObjectInputStream的其余方法用于定制反序列化过程。<BR>例子如下：<BR></P><PRE><FONT color=#006600><I>//从文件中反序列化 string 对象和 date 对象</FONT></I><BR>    FileInputStream in = <B>new</B> FileInputStream(<FONT color=#000099>"tmp"</FONT>);<BR>    ObjectInputStream s = <B>new</B> ObjectInputStream(in);<BR>    String today = (String)s.readObject();<BR>    Date date = (Date)s.readObject();<BR></PRE>
<P><BR>&nbsp;</P>
<H3>定制序列化过程:</H3>
<P><BR><BR>序列化通常可以自动完成，但有时可能要对这个过程进行控制。java可以将类声明为serializable，但仍可手工控制声明为static或transient的数据成员。<BR>例子：一个非常简单的序列化类。<BR></P><PRE><B>public</B> <B>class</B> simpleSerializableClass <B>implements</B> Serializable{<BR>	String sToday=<FONT color=#000099>"Today:"</FONT>;<BR>	<B>transient</B> Date dtToday=<B>new</B> Date();<BR>}<BR></PRE>
<P><BR>序列化时，类的所有数据成员应可序列化除了声明为transient或static的成员。将变量声明为transient告诉JVM我们会负责将变元序列化。将数据成员声明为transient后，序列化过程就无法将其加进对象字节流中，没有从transient数据成员发送的数据。后面数据反序列化时，要重建数据成员（因为它是类定义的一部分），但不包含任何数据，因为这个数据成员不向流中写入任何数据。记住，对象流不序列化static或transient。我们的类要用writeObject()与readObject()方法以处理这些数据成员。使用writeObject()与readObject()方法时，还要注意按写入的顺序读取这些数据成员。<BR>关于如何使用定制序列化的部分代码如下：<BR></P><PRE><FONT color=#006600><I>//重写writeObject()方法以便处理transient的成员。</FONT></I><BR><B>public</B> <B>void</B> writeObject(ObjectOutputStream outputStream) <B>throws</B> IOException{<BR>	outputStream.defaultWriteObject();<FONT color=#006600><I>//使定制的writeObject()方法可以</FONT></I><BR>						利用自动序列化中内置的逻辑。<BR>	outputStream.writeObject(oSocket.getInetAddress());<BR>	outputStream.writeInt(oSocket.getPort());<BR>}<BR><FONT color=#006600><I>//重写readObject()方法以便接收transient的成员。</FONT></I><BR><B>private</B> <B>void</B> readObject(ObjectInputStream inputStream) <B>throws</B> IOException,ClassNotFoundException{<BR>	inputStream.defaultReadObject();<FONT color=#006600><I>//defaultReadObject()补充自动序列化</FONT></I><BR>	InetAddress oAddress=(InetAddress)inputStream.readObject();<BR>	<B>int</B> iPort =inputStream.readInt();<BR>	oSocket = <B>new</B> Socket(oAddress,iPort);<BR>	iID=getID();<BR>	dtToday =<B>new</B> Date();<BR>}<BR></PRE>
<P><BR>&nbsp;</P>
<H3>完全定制序列化过程:</H3>
<P><BR>如果一个类要完全负责自己的序列化，则实现Externalizable接口而不是Serializable接口。Externalizable接口定义包括两个方法writeExternal()与readExternal()。利用这些方法可以控制对象数据成员如何写入字节流.类实现Externalizable时，头写入对象流中，然后类完全负责序列化和恢复数据成员，除了头以外，根本没有自动序列化。这里要注意了。声明类实现Externalizable接口会有重大的安全风险。writeExternal()与readExternal()方法声明为public，恶意类可以用这些方法读取和写入对象数据。如果对象包含敏感信息，则要格外小心。这包括使用安全套接或加密整个字节流。到此为至，我们学习了序列化的基础部分知识。关于序<BR>列化的高级教程，以后再述。<BR><BR>参考资料：http://java.sun.com/j2se/1.3/docs/guide/serialization/spec/serialTOC.doc.html </P><img src ="http://www.blogjava.net/xieyunlong/aggbug/22711.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/xieyunlong/" target="_blank">呓语的博客</a> 2005-12-06 14:24 <a href="http://www.blogjava.net/xieyunlong/archive/2005/12/06/22711.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>