﻿<?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-NewMoring-文章分类-java语言的相关问题</title><link>http://www.blogjava.net/NewMoring/category/39349.html</link><description>与Java同路，赢在未来。</description><language>zh-cn</language><lastBuildDate>Wed, 03 Jun 2009 07:56:05 GMT</lastBuildDate><pubDate>Wed, 03 Jun 2009 07:56:05 GMT</pubDate><ttl>60</ttl><item><title>中文乱码大总结《转帖》</title><link>http://www.blogjava.net/NewMoring/articles/278963.html</link><dc:creator>清晨</dc:creator><author>清晨</author><pubDate>Fri, 29 May 2009 15:38:00 GMT</pubDate><guid>http://www.blogjava.net/NewMoring/articles/278963.html</guid><wfw:comment>http://www.blogjava.net/NewMoring/comments/278963.html</wfw:comment><comments>http://www.blogjava.net/NewMoring/articles/278963.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/NewMoring/comments/commentRss/278963.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/NewMoring/services/trackbacks/278963.html</trackback:ping><description><![CDATA[1.字节和unicode <br />
&nbsp; &nbsp; java内核是unicode的，就连class文件也是，但是很多媒体，包括文件/流的保存方式是使用字节流的。因此java要对这些字节流经行转化。char是unicode的，而byte是字节。java中byte/char互转的函数在sun.io的包中间有。其中ByteToCharConverter类是中调度，可以用来告诉你，你用的convertor。其中两个很常用的静态函数是 <br />
public static ByteToCharConverter getDefault(); <br />
public static ByteToCharConverter getConverter(String encoding); <br />
如果你不指定converter，则系统会自动使用当前的encoding,gb平台上用gbk,en平台上用8859_1。 <br />
byte ??〉char： <br />
"你"的gb码是：0xc4e3 ,unicode是0x4f60 <br />
&nbsp; String encoding = "gb2312"; <br />
&nbsp; byte b[] = {(byte)'u00c4',(byte)'u00e3'}; <br />
&nbsp; ByteToCharConverter converter = ByteToCharConverter.getConverter(encoding); <br />
&nbsp; char c[] = converter.convertAll(b); <br />
&nbsp; for (int i = 0; i &lt; c.length; i++) { <br />
&nbsp; &nbsp; &nbsp; System.out.println(Integer.toHexString(c[i])); <br />
&nbsp; } <br />
结果是什么？0x4f60 <br />
如果encoding ="8859_1"，结果又是什么？0x00c4,0x00e3 <br />
如果代码改为 <br />
byte b[] = {(byte)'u00c4',(byte)'u00e3'}; <br />
ByteToCharConverter converter = ByteToCharConverter. getDefault(); <br />
char c[] = converter.convertAll(b); <br />
for (int i = 0; i &lt; c.length; i++) { <br />
&nbsp; &nbsp; System.out.println(Integer.toHexString(c[i])); <br />
} <br />
结果将又是什么？根据平台的编码而定。 <br />
<br />
char ??〉byte： <br />
&nbsp; &nbsp;
<dl class="code">
<dt>Java code
<dd>
<pre>
<div><!-- Code highlighting produced by Actipro CodeHighlighter (freeware)
http://www.CodeHighlighter.com/

--><span style="color: #000000">String encoding </span><span style="color: #000000">=</span><span style="color: #000000"> </span><span style="color: #000000">"</span><span style="color: #000000">gb2312</span><span style="color: #000000">"</span><span style="color: #000000">;
</span><span style="color: #0000ff">char</span><span style="color: #000000"> c[] </span><span style="color: #000000">=</span><span style="color: #000000"> {</span><span style="color: #000000">'</span><span style="color: #000000">u4f60</span><span style="color: #000000">'</span><span style="color: #000000">};
CharToByteConverter converter </span><span style="color: #000000">=</span><span style="color: #000000"> CharToByteConverter.getConverter(encoding);
</span><span style="color: #0000ff">byte</span><span style="color: #000000"> b[] </span><span style="color: #000000">=</span><span style="color: #000000"> converter.convertAll(c);
</span><span style="color: #0000ff">for</span><span style="color: #000000"> (</span><span style="color: #0000ff">int</span><span style="color: #000000"> i </span><span style="color: #000000">=</span><span style="color: #000000"> </span><span style="color: #000000">0</span><span style="color: #000000">; i </span><span style="color: #000000">&lt;</span><span style="color: #000000"> b.length; i</span><span style="color: #000000">++</span><span style="color: #000000">) {
System.out.println(Integer.toHexString(b[i]));
}</span></div>
</pre>
</dd></dl>结果是什么？0x00c4,0x00e3 <br />
如果encoding ="8859_1"，结果又是什么？0x3f <br />
如果代码改为 <br />
<dl class="code">
<dt>Java code
<dd>
<pre>
<div><!-- Code highlighting produced by Actipro CodeHighlighter (freeware)
http://www.CodeHighlighter.com/

--><span style="color: #000000">String encoding </span><span style="color: #000000">=</span><span style="color: #000000"> </span><span style="color: #000000">"</span><span style="color: #000000">gb2312</span><span style="color: #000000">"</span><span style="color: #000000">;
</span><span style="color: #0000ff">char</span><span style="color: #000000"> c[] </span><span style="color: #000000">=</span><span style="color: #000000"> {</span><span style="color: #000000">'</span><span style="color: #000000">u4f60</span><span style="color: #000000">'</span><span style="color: #000000">};
CharToByteConverter converter </span><span style="color: #000000">=</span><span style="color: #000000"> CharToByteConverter.getDefault();
</span><span style="color: #0000ff">byte</span><span style="color: #000000"> b[] </span><span style="color: #000000">=</span><span style="color: #000000"> converter.convertAll(c);
</span><span style="color: #0000ff">for</span><span style="color: #000000"> (</span><span style="color: #0000ff">int</span><span style="color: #000000"> i </span><span style="color: #000000">=</span><span style="color: #000000"> </span><span style="color: #000000">0</span><span style="color: #000000">; i </span><span style="color: #000000">&lt;</span><span style="color: #000000"> b.length; i</span><span style="color: #000000">++</span><span style="color: #000000">) {
System.out.println(Integer.toHexString(b[i]));
}</span></div>
</pre>
</dd></dl><br />
结果将又是什么？根据平台的编码而定。 <br />
很多中文问题就是从这两个最简单的类派生出来的。而却有很多类不直接支持把encoding输入，这给我们带来诸多不便。很多程序难得用encoding了，直接用default的encoding，这就给我们移植带来了很多困难。 <br />
<br />
2.utf-8 <br />
utf-8是和unicode一一对应的，其实现很简单 <br />
7位的unicode: 0 _ _ _ _ _ _ _ <br />
11位的unicode: 1 1 0 _ _ _ _ _ 1 0 _ _ _ _ _ _ <br />
16位的unicode: 1 1 1 0 _ _ _ _ 1 0 _ _ _ _ _ _ 1 0 _ _ _ _ _ _ <br />
21位的unicode: 1 1 1 1 0 _ _ _ 1 0 _ _ _ _ _ _ 1 0 _ _ _ _ _ _ 1 0 _ _ _ _ _ _ <br />
大多数情况是只使用到16位以下的unicode: <br />
"你"的gb码是：0xc4e3 ,unicode是0x4f60 <br />
&nbsp; &nbsp; 0xc4e3的二进制： <br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; 1100 ，0100 ，1110 ，0011 <br />
由于只有两位我们按照两位的编码来排，但是我们发现这行不通，因为第７位不是0因此，返回"?" <br />
&nbsp; &nbsp; 0x4f60的二进制： <br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; 0100 ，1111 ，0110 ，0000 <br />
&nbsp; &nbsp; &nbsp; &nbsp; 我们用utf-8补齐，变成： <br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; 1110 ，0100 ，1011 ，1101 ，1010 ，0000 <br />
&nbsp; &nbsp; &nbsp; &nbsp; e4--bd-- a0 <br />
&nbsp; &nbsp; &nbsp; &nbsp; 于是返回：0xe4,0xbd,0xa0。 <br />
<br />
3.string和byte[] <br />
string其实核心是char[],然而要把byte转化成string，必须经过编码。string.length()其实就是char数组的长度，如果使用不同的编码，很可能会错分，造成散字和乱码。 <br />
例如： <br />
String encoding = &#8220;&#8221;; <br />
byte [] b={(byte)'u00c4',(byte)'u00e3'}; <br />
String str=new String(b,encoding);　　 <br />
如果encoding=8859_1，会有两个字，但是encoding=gb2312只有一个字这个问题在处理分页是经常发生 。 <br />
<br />
4.Reader,Writer / InputStream,OutputStream <br />
Reader和Writer核心是char，InputStream和OutputStream核心是byte。但是Reader和Writer的主要目的是要把char读/写InputStream/OutputStream。 <br />
例如： <br />
文件test.txt只有一个"你"字，0xc4,0xe3 <br />
<dl class="code">
<dt>Java code
<dd>
<pre>
<div><!-- Code highlighting produced by Actipro CodeHighlighter (freeware)
http://www.CodeHighlighter.com/

--><span style="color: #000000">String encoding </span><span style="color: #000000">=</span><span style="color: #000000"> </span><span style="color: #000000">"</span><span style="color: #000000">gb2312</span><span style="color: #000000">"</span><span style="color: #000000">;
InputStreamReader reader </span><span style="color: #000000">=</span><span style="color: #000000"> </span><span style="color: #0000ff">new</span><span style="color: #000000"> InputStreamReader(</span><span style="color: #0000ff">new</span><span style="color: #000000"> FileInputStream(
</span><span style="color: #000000">"</span><span style="color: #000000">text.txt</span><span style="color: #000000">"</span><span style="color: #000000">), encoding);
</span><span style="color: #0000ff">char</span><span style="color: #000000"> c[] </span><span style="color: #000000">=</span><span style="color: #000000"> </span><span style="color: #0000ff">new</span><span style="color: #000000"> </span><span style="color: #0000ff">char</span><span style="color: #000000">[</span><span style="color: #000000">10</span><span style="color: #000000">];
</span><span style="color: #0000ff">int</span><span style="color: #000000"> length </span><span style="color: #000000">=</span><span style="color: #000000"> reader.read(c);
</span><span style="color: #0000ff">for</span><span style="color: #000000"> (</span><span style="color: #0000ff">int</span><span style="color: #000000"> i </span><span style="color: #000000">=</span><span style="color: #000000"> </span><span style="color: #000000">0</span><span style="color: #000000">; i </span><span style="color: #000000">&lt;</span><span style="color: #000000"> length; i</span><span style="color: #000000">++</span><span style="color: #000000">) {
System.out.println(c[i]);
}</span></div>
</pre>
</dd></dl>结果是什么？你 <br />
如果encoding ="8859_1"，结果是什么？??两个字符，表示不认识。 <br />
反过来的例子自己做。 <br />
<br />
5.我们要对java的编译器有所了解： <br />
javac ?encoding <br />
我们常常没有用到encoding这个参数。其实encoding这个参数对于跨平台的操作是很重要的。如果没有指定encoding，则按照系统的默认encoding,gb平台上是gb2312，英文平台上是iso8859_1。 <br />
java的编译器实际上是调用sun.tools.javac.main的类，对文件进行编译，这个类有compile函数中间有一个encoding的变量,-encoding的参数其实直接传给encoding变量。编译器就是根据这个变量来读取java文件的，然后把用utf-8形式编译成class文件。 <br />
例子代码： <br />
String str = "你"; <br />
&nbsp; &nbsp; FileWriter writer = new FileWriter("text.txt"); <br />
&nbsp; &nbsp; write.write(str); <br />
&nbsp; &nbsp; writer.close(); <br />
<br />
如果用gb2312编译，你会找到e4 bd a0的字段 ； <br />
如果用8859_1编译， 00c4 00e3的二进制： <br />
0000，0000 ，1100，0100 ，0000，0000 ，1110，0011 <br />
因为每个字符都大于7位，因此用11位编码： <br />
1100，0001，1000，0100，1100，0011，1010，0011 <br />
c1-- 84--　c3--　 a3 <br />
你会找到c1 84 c3 a3 。 <br />
<br />
但是我们往往忽略掉这个参数，因此这样往往会有跨平台的问题： <br />
样例代码在中文平台上编译，生成zhclass <br />
样例代码在英文平台上编译，输出enclass <br />
&nbsp; (1).　 zhclass在中文平台上执行ok,但是在英文平台上不行 <br />
&nbsp; (2).&nbsp; &nbsp; enclass在英文平台上执行ok,但是在中文平台上不行 <br />
原因： <br />
&nbsp; (1).&nbsp; &nbsp; 在中文平台上编译后，其实str在运行态的char[]是0x4f60,　在中文平台上运行，filewriter的缺省编码是gb2312,因此 chartobyteconverter会自动用调用gb2312的converter,把str转化成byte输入到fileoutputstream中，于是0xc4,0xe3放进了文件。 <br />
但是如果是在英文平台下，chartobyteconverter的缺省值是8859_1, filewriter会自动调用8859_1去转化str,但是他无法解释，因此他会输出"?" <br />
&nbsp; (2).&nbsp; &nbsp; 在英文平台上编译后，其实str在运行态的char[]是0x00c4 0x00e3, 在中文平台上运行，中文无法识别，因此会出现??； <br />
在英文平台上，0x00c4--&gt;0xc4,0x00e3-&gt;0xe3，因此0xc4,0xe3被放进了文件。 <br />
<br />
6.&nbsp; &nbsp; 其它原因： &lt;%@ page contentType="text/html; charset=GBK" %&gt; <br />
设置浏览器的显示编码，如果response的数据是utf8编码，显示将是乱码，但是乱码和上述原因还不一样。 <br />
<br />
7.&nbsp; &nbsp; 发生编码的地方： <br />
&nbsp; &nbsp; 从数据库到java程序 byte??〉char <br />
&nbsp; &nbsp; 从java程序到数据库 char??〉byte <br />
&nbsp; &nbsp; 从文件到java程序 byte??〉char <br />
&nbsp; &nbsp; 从java程序到文件 char??〉byte <br />
&nbsp; &nbsp; 从java程序到页面显示 char??〉byte <br />
&nbsp; &nbsp; 从页面form提交数据到java程序byte??〉char <br />
&nbsp; &nbsp; 从流到java程序byte??〉char <br />
&nbsp; &nbsp; 从java程序到流char??〉byte <br />
<br />
谢志钢的解决方法： <br />
我是使用配置过滤器的方法解决中文乱码的： <br />
<br />
<dl class="code">
<dt>XML code
<dd>
<pre>
<div><!-- Code highlighting produced by Actipro CodeHighlighter (freeware)
http://www.CodeHighlighter.com/

--><span style="color: #0000ff">&lt;</span><span style="color: #800000">web-app</span><span style="color: #0000ff">&gt;</span><span style="color: #000000">
</span><span style="color: #0000ff">&lt;</span><span style="color: #800000">filter</span><span style="color: #0000ff">&gt;</span><span style="color: #000000">
</span><span style="color: #0000ff">&lt;</span><span style="color: #800000">filter-name</span><span style="color: #0000ff">&gt;</span><span style="color: #000000">RequestFilter</span><span style="color: #0000ff">&lt;/</span><span style="color: #800000">filter-name</span><span style="color: #0000ff">&gt;</span><span style="color: #000000">
</span><span style="color: #0000ff">&lt;</span><span style="color: #800000">filter-class</span><span style="color: #0000ff">&gt;</span><span style="color: #000000">net.golden.uirs.util.RequestFilter</span><span style="color: #0000ff">&lt;/</span><span style="color: #800000">filter-class</span><span style="color: #0000ff">&gt;</span><span style="color: #000000">
</span><span style="color: #0000ff">&lt;</span><span style="color: #800000">init-param</span><span style="color: #0000ff">&gt;</span><span style="color: #000000">
</span><span style="color: #0000ff">&lt;</span><span style="color: #800000">param-name</span><span style="color: #0000ff">&gt;</span><span style="color: #000000">charset</span><span style="color: #0000ff">&lt;/</span><span style="color: #800000">param-name</span><span style="color: #0000ff">&gt;</span><span style="color: #000000">
</span><span style="color: #0000ff">&lt;</span><span style="color: #800000">param-value</span><span style="color: #0000ff">&gt;</span><span style="color: #000000">gb2312</span><span style="color: #0000ff">&lt;/</span><span style="color: #800000">param-value</span><span style="color: #0000ff">&gt;</span><span style="color: #000000">
</span><span style="color: #0000ff">&lt;/</span><span style="color: #800000">init-param</span><span style="color: #0000ff">&gt;</span><span style="color: #000000">
</span><span style="color: #0000ff">&lt;/</span><span style="color: #800000">filter</span><span style="color: #0000ff">&gt;</span><span style="color: #000000">
</span><span style="color: #0000ff">&lt;</span><span style="color: #800000">filter-mapping</span><span style="color: #0000ff">&gt;</span><span style="color: #000000">
</span><span style="color: #0000ff">&lt;</span><span style="color: #800000">filter-name</span><span style="color: #0000ff">&gt;</span><span style="color: #000000">RequestFilter</span><span style="color: #0000ff">&lt;/</span><span style="color: #800000">filter-name</span><span style="color: #0000ff">&gt;</span><span style="color: #000000">
</span><span style="color: #0000ff">&lt;</span><span style="color: #800000">url-pattern</span><span style="color: #0000ff">&gt;</span><span style="color: #000000">*.jsp</span><span style="color: #0000ff">&lt;/</span><span style="color: #800000">url-pattern</span><span style="color: #0000ff">&gt;</span><span style="color: #000000">
</span><span style="color: #0000ff">&lt;/</span><span style="color: #800000">filter-mapping</span><span style="color: #0000ff">&gt;</span><span style="color: #000000">
</span><span style="color: #0000ff">&lt;/</span><span style="color: #800000">web-app</span><span style="color: #0000ff">&gt;</span></div>
</pre>
</dd></dl><br />
<br />
<br />
&nbsp;
<dl class="code">
<dt>Java code
<dd>
<pre>
<div><!-- Code highlighting produced by Actipro CodeHighlighter (freeware)
http://www.CodeHighlighter.com/

--><span style="color: #0000ff">public</span><span style="color: #000000"> </span><span style="color: #0000ff">void</span><span style="color: #000000"> doFilter(ServletRequest req, ServletResponse res,
FilterChain fChain) </span><span style="color: #0000ff">throws</span><span style="color: #000000"> IOException, ServletException {
HttpServletRequest request </span><span style="color: #000000">=</span><span style="color: #000000"> (HttpServletRequest) req;
HttpServletResponse response </span><span style="color: #000000">=</span><span style="color: #000000"> (HttpServletResponse) res;
HttpSession session </span><span style="color: #000000">=</span><span style="color: #000000"> request.getSession();
String userId </span><span style="color: #000000">=</span><span style="color: #000000"> (String) session.getAttribute(</span><span style="color: #000000">"</span><span style="color: #000000">userid</span><span style="color: #000000">"</span><span style="color: #000000">);
req.setCharacterEncoding(</span><span style="color: #0000ff">this</span><span style="color: #000000">.filterConfig.getInitParameter(</span><span style="color: #000000">"</span><span style="color: #000000">charset</span><span style="color: #000000">"</span><span style="color: #000000">)); </span><span style="color: #008000">//</span><span style="color: #008000"> 设置字符集？实际上是设置了byte ??〉char的encoding</span><span style="color: #008000">
</span><span style="color: #000000">    </span><span style="color: #0000ff">try</span><span style="color: #000000"> {
</span><span style="color: #0000ff">if</span><span style="color: #000000"> (userId </span><span style="color: #000000">==</span><span style="color: #000000"> </span><span style="color: #0000ff">null</span><span style="color: #000000"> </span><span style="color: #000000">||</span><span style="color: #000000"> userId.equals(</span><span style="color: #000000">""</span><span style="color: #000000">)) {
</span><span style="color: #0000ff">if</span><span style="color: #000000"> (</span><span style="color: #000000">!</span><span style="color: #000000">request.getRequestURL().toString().matches(
</span><span style="color: #000000">"</span><span style="color: #000000">.*/uirs/logon/logon(Controller){0,1}x2Ejsp$</span><span style="color: #000000">"</span><span style="color: #000000">)) {
session.invalidate();
response.sendRedirect(request.getContextPath() </span><span style="color: #000000">+</span><span style="color: #000000">
</span><span style="color: #000000">"</span><span style="color: #000000">/uirs/logon/logon.jsp</span><span style="color: #000000">"</span><span style="color: #000000">);
}
}
</span><span style="color: #0000ff">else</span><span style="color: #000000"> { </span><span style="color: #008000">//</span><span style="color: #008000"> 看看是否具有信息上报系统的权限</span><span style="color: #008000">
</span><span style="color: #000000">        </span><span style="color: #0000ff">if</span><span style="color: #000000"> (</span><span style="color: #000000">!</span><span style="color: #000000">net.golden.uirs.util.UirsChecker.check(userId, </span><span style="color: #000000">"</span><span style="color: #000000">信息上报系统</span><span style="color: #000000">"</span><span style="color: #000000">,
net.golden.uirs.util.UirsChecker.ACTION_DO)) {
</span><span style="color: #0000ff">if</span><span style="color: #000000"> (</span><span style="color: #000000">!</span><span style="color: #000000">request.getRequestURL().toString().matches(
</span><span style="color: #000000">"</span><span style="color: #000000">.*/uirs/logon/logon(Controller){0,1}x2Ejsp$</span><span style="color: #000000">"</span><span style="color: #000000">)) {
response.sendRedirect(request.getContextPath() </span><span style="color: #000000">+</span><span style="color: #000000">
</span><span style="color: #000000">"</span><span style="color: #000000">/uirs/logon/logonController.jsp</span><span style="color: #000000">"</span><span style="color: #000000">);
}
}
}
}
</span><span style="color: #0000ff">catch</span><span style="color: #000000"> (Exception ex) {
response.sendRedirect(request.getContextPath() </span><span style="color: #000000">+</span><span style="color: #000000">
</span><span style="color: #000000">"</span><span style="color: #000000">/uirs/logon/logon.jsp</span><span style="color: #000000">"</span><span style="color: #000000">);
}
fChain.doFilter(req, res);
}</span></div>
</pre>
</dd></dl>
<img src ="http://www.blogjava.net/NewMoring/aggbug/278963.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/NewMoring/" target="_blank">清晨</a> 2009-05-29 23:38 <a href="http://www.blogjava.net/NewMoring/articles/278963.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Java 序列化 2 《转帖》</title><link>http://www.blogjava.net/NewMoring/articles/278196.html</link><dc:creator>清晨</dc:creator><author>清晨</author><pubDate>Wed, 27 May 2009 06:18:00 GMT</pubDate><guid>http://www.blogjava.net/NewMoring/articles/278196.html</guid><wfw:comment>http://www.blogjava.net/NewMoring/comments/278196.html</wfw:comment><comments>http://www.blogjava.net/NewMoring/articles/278196.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/NewMoring/comments/commentRss/278196.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/NewMoring/services/trackbacks/278196.html</trackback:ping><description><![CDATA[<table border="0" cellspacing="0" cellpadding="0" width="99%">
    <tbody>
        <tr>
            <td>
            <p>目前网络上关于对象序列化的文章不少，但是我发现详细叙述用法和原理的文章太少。本人把自己经过经验总结和实际运用中的体会写成的学习笔记贡献给大家。希望能为整个java社区的繁荣做一点事情。<br />
            <br />
            &nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 序列化的过程就是对象写入字节流和从字节流中读取对象。将对象状态转换成字节流之后，可以用java.io包中的各种字节流类将其保存到文件中，管道到另 一线程中或通过网络连接将对象数据发送到另一主机。对象序列化功能非常简单、强大，在RMI、Socket、JMS、EJB都有应用。对象序列化问题在网 络编程中并不是最激动人心的课题，但却相当重要，具有许多实用意义。<br />
            <br />
            <font color="#0000ff">一：对象序列化可以实现分布式对象。<br />
            </font>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 主要应用例如：RMI要利用对象序列化运行远程主机上的服务，就像在本地机上运行对象时一样。</p>
            </td>
        </tr>
        <tr>
            <td colspan="2">
            <p>二： <font color="#0000ff">java对象序列化不仅保留一个对象的数据，</font>而且递归保存对象引用的每个对象的数据。可以将整个对象层次写入字节流中，可以保存在文件中或在网络连接上传 递。利用对象序列化可以进行对象的&#8220;深复制&#8221;，即复制对象本身及引用的对象本身。序列化一个对象可能得到整个对象序列。<br />
            <br />
            &nbsp;&nbsp; 从上面的叙述中，我们知道了对象序列化是java编程中的必备武器，那么让我们从基础开始，好好学习一下它的机制和用法。 <br />
            <br />
            &nbsp;&nbsp;&nbsp;&nbsp; java 序列化比较简单，通常不需要编写保存和恢复对象状态的定制代码。实现java.io.Serializable接口的类对象可以转换成字节流或从字节流恢 复，不需要在类中增加任何代码。只有极少数情况下才需要定制代码保存或恢复对象状态。这里要注意：不是每个类都可序列化，有些类是不能序列化的，例如涉及 线程的类与特定JVM有非常复杂的关系。<br />
            序列化机制：</p>
            <p>&nbsp;&nbsp;&nbsp;&nbsp; 序列化分为两大部分：序列化和反序列化。 序列化是这个过程的第一部分，将数据分解成字节流，以便存储在文件中或在网络上传输。反序列化就是打开字节流并重构对象。对象序列化不仅要将基本数据类型 转换成字节表示，有时还要恢复数据。恢复数据要求有恢复数据的对象实例。ObjectOutputStream中的序列化过程与字节流连接，包括对象类型 和版本信息。反序列化时，JVM用头信息生成对象实例，然后将对象字节流中的数据复制到对象数据成员中。下面我们分两大部分来阐述：<br />
            <br />
            <font color="#0000ff">处理对象流：</font><br />
            （序列化过程和反序列化过程）</p>
            <p>&nbsp;&nbsp; java.io包有两个序列化对象的类。ObjectOutputStream负责将对象写入字节流，ObjectInputStream从字节流重构对象。<br />
            &nbsp;&nbsp;&nbsp;&nbsp; 我们先了解ObjectOutputStream类吧。ObjectOutputStream类扩展DataOutput接口。<br />
            writeObject ()方法是最重要的方法，用于对象序列化。如果对象包含其他对象的引用，则writeObject()方法递归序列化这些对象。每个 ObjectOutputStream维护序列化的对象引用表，防止发送同一对象的多个拷贝。（这点很重要）由于writeObject()可以序列化整 组交叉引用的对象，因此同一ObjectOutputStream实例可能不小心被请求序列化同一对象。这时，进行反引用序列化，而不是再次写入对象字节 流。<br />
            <br />
            下面，让我们从例子中来了解ObjectOutputStream这个类吧。<br />
            1. // 序列化 today's date 到一个文件中. <br />
            2.&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; FileOutputStream f = new FileOutputStream("tmp"); <br />
            3.&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ObjectOutputStream s = new ObjectOutputStream(f); <br />
            4.&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; s.writeObject("Today"); <br />
            5.&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; s.writeObject(new Date()); <br />
            6.&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; s.flush(); </p>
            <p>&nbsp;&nbsp;&nbsp; 现在，让我们来了解ObjectInputStream这个类。它与 ObjectOutputStream相似。它扩展DataInput接口。ObjectInputStream中的方法镜像 DataInputStream中读取Java基本数据类型的公开方法。readObject()方法从字节流中反序列化对象。每次调用 readObject()方法都返回流中下一个Object。对象字节流并不传输类的字节码，而是包括类名及其签名。readObject()收到对象 时，JVM装入头中指定的类。如果找不到这个类，则readObject()抛出ClassNotFoundException,如果需要传输对象数据和 字节码，则可以用RMI框架。ObjectInputStream的其余方法用于定制反序列化过程。<br />
            <br />
            例子如下：<br />
            1. //从文件中反序列化 string 对象和 date 对象 <br />
            2.&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; FileInputStream in = new FileInputStream("tmp"); <br />
            3.&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ObjectInputStream s = new ObjectInputStream(in); <br />
            4.&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; String today = (String)s.readObject(); <br />
            5.&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Date date = (Date)s.readObject(); </p>
            <p><font color="#0000ff">定制序列化过程:</font></p>
            <p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 序列化通常可以自动完成，但有时可能要对这个过程进行控制。java可以将类声明为serializable，但仍可手工控制声明为static或transient的数据成员。<br />
            例子：一个非常简单的序列化类。<br />
            1. public class simpleSerializableClass implements Serializable{ <br />
            2.&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; String sToday="Today:"; <br />
            3.&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; transient Date dtToday=new Date(); <br />
            4. } </p>
            <p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 序列化时，类的所有数据成员应可序列化除了声明为transient或static的成员。将变量声明为 transient告诉JVM我们会负责将变元序列化。将数据成员声明为transient后，序列化过程就无法将其加进对象字节流中，没有从 transient数据成员发送的数据。后面数据反序列化时，要重建数据成员（因为它是类定义的一部分），但不包含任何数据，因为这个数据成员不向流中写 入任何数据。记住，对象流不序列化static或transient。我们的类要用writeObject()与readObject()方法以处理这些 数据成员。使用writeObject()与readObject()方法时，还要注意按写入的顺序读取这些数据成员。<br />
            <br />
            关于如何使用定制序列化的部分代码如下：<br />
            <br />
            1. //重写writeObject()方法以便处理transient的成员。 <br />
            2. public void writeObject(ObjectOutputStream outputStream) throws IOException{ <br />
            3.&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; outputStream.defaultWriteObject();//使定制的writeObject()方法可以 <br />
            4.&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 利用自动序列化中内置的逻辑。 <br />
            5.&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; outputStream.writeObject(oSocket.getInetAddress()); <br />
            6.&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; outputStream.writeInt(oSocket.getPort()); <br />
            7. } <br />
            8. //重写readObject()方法以便接收transient的成员。 <br />
            9. private void readObject(ObjectInputStream inputStream) throws IOException,ClassNotFoundException{ <br />
            10.&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; inputStream.defaultReadObject();//defaultReadObject()补充自动序列化 <br />
            11.&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; InetAddress oAddress=(InetAddress)inputStream.readObject(); <br />
            12.&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; int iPort =inputStream.readInt(); <br />
            13.&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; oSocket = new Socket(oAddress,iPort); <br />
            14.&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; iID=getID(); <br />
            15.&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; dtToday =new Date(); <br />
            16. } </p>
            <p><font color="#0000ff">完全定制序列化过程:</font></p>
            <p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 如果一个类要完全负责自己的序列化，则实现 Externalizable接口而不是Serializable接口。Externalizable接口定义包括两个方法writeExternal ()与readExternal()。利用这些方法可以控制对象数据成员如何写入字节流.类实现Externalizable时，头写入对象流中，然后类 完全负责序列化和恢复数据成员，除了头以外，根本没有自动序列化。这里要注意了。声明类实现Externalizable接口会有重大的安全风险。 writeExternal()与readExternal()方法声明为public，恶意类可以用这些方法读取和写入对象数据。如果对象包含敏感信 息，则要格外小心。这包括使用安全套接或加密整个字节流。到此为至，我们学习了序列化的基础部分知识.</p>
            </td>
        </tr>
    </tbody>
</table>
<img src ="http://www.blogjava.net/NewMoring/aggbug/278196.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/NewMoring/" target="_blank">清晨</a> 2009-05-27 14:18 <a href="http://www.blogjava.net/NewMoring/articles/278196.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>java Serializable 类 序列化1《转帖》</title><link>http://www.blogjava.net/NewMoring/articles/278195.html</link><dc:creator>清晨</dc:creator><author>清晨</author><pubDate>Wed, 27 May 2009 06:16:00 GMT</pubDate><guid>http://www.blogjava.net/NewMoring/articles/278195.html</guid><wfw:comment>http://www.blogjava.net/NewMoring/comments/278195.html</wfw:comment><comments>http://www.blogjava.net/NewMoring/articles/278195.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/NewMoring/comments/commentRss/278195.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/NewMoring/services/trackbacks/278195.html</trackback:ping><description><![CDATA[<p>1.5 定义类的可序列化域<br />
可用两种不同的方式来定义类的可序列化域。类的可序列化域被缺省定义为非瞬态和非静态域。 通过在 Serializable 类中声明一个指定的域 serialPersistentFields，即可覆盖该缺省值。 该域的初始化必须用 ObjectStreamField 对象的数组来完成，该对象列出了可序列化域的名称和类型。该域的修饰符应为 private、static 和 final。<br />
例如，以下声明将复制缺省行为。<br />
class List implements Serializable {<br />
&nbsp;&nbsp;&nbsp; List next;<br />
&nbsp;&nbsp;&nbsp; private static final ObjectStreamField[] serialPersistentFields<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; = {new ObjectStreamField("next", List.class)};</p>
<p>}<br />
用 serialPersistentFields 为某个类定义可序列化域后，即无需再将可序列化域限制在当前 Serializable 类的定义之内。利用第 1.7 节，"访问类的可序列化域" 中描述的接口，Serializable 类的 writeObject 和 readObject 方法可将该类的当前实现映射到其可序列化域。因此，后续版本中可以改变 Serializable 类的域，但前提是保持与版本兼容的 Serializable 域的映射。</p>
<p>1.6 对类的可序列化域和数据存档<br />
对类的可序列化状态进行存档以使其与 Serializable 类的其它实现间可互操作，或者对类演变进行存档，这些都非常必要。对可序列化域进行存档可帮助用户最终确定该域是否应序列化。 序列化 javadoc 标记 @serial、@serialField 和 @serialData 可提供对源代码中 Serializable 类的序列化形式进行存档的方法。</p>
<p>对于缺省的可序列化域，@serial 标记应放在 javadoc 注释中。语法如下：@serial field-description 其中可选项 field-description 描述域及其容许值的含义。 field-description 可跨多行。当初始版本中增加一个域时，@since 标记指出所添域的版本。@serial 的 field-description 提供针对序列化的文档。在序列化形式文档内，field-description 将被添加到 javadoc 注释中。 <br />
@serialField 标记用于对 serialPersistentFields 数组的 ObjectStreamField 组件进行存档。每个 ObjectStreamField 组件都应使用其中的一个标记。语法如下：@serialField field-name field-type field-description <br />
@serialData 标记描述写入或读取数据的次序和类型。该标记描述 writeObject 所写入的可选数据或 Externalizable.writeExternal 方法所写入的所有数据的次序或类型。语法如下：@serialData data-description <br />
javadoc 应用程序识别序列化 javadoc 标记并为每个 Serializable 和 Externalizable 类生成说明文字。有关这些标记用法的示例，参见第 C.1 节 "java.io.File 可替代实现的示例。"</p>
<p>当某个类被声明为 Serializable 时，即通过可序列化域及可选数据（按名称和类型）定义了该对象的可序列化状态。可选数据只能用 Serializable 类中的 writeObject 方法显式写入，而其读取则可通过 Serializable 类和 readObject 方法进行。否则，序列化将跳过无法读取的可选数据。 </p>
<p>当一个类被声明为 Externalizable 时，通过类自身写入流的数据即定义了该类的序列化状态。 该类必须指定顺序、类型及写入流的每个数据的含义。同时还必须处理其自身的演变，从而可读取以前版本所写入的数据，或写入以前版本所能读取的数据。在存储和恢复数据时，该类必须与父类相一致。必须指定流中父类数据的位置。</p>
<p>类的设计者必须确保为类存储的信息对于持久性是合适的，而且遵循为实现序列化的互操作性和演变而指定的序列化规则。第 5 章&#8220;可序列化对象的版本演变&#8221;对类的演变进行了更详细的说明。 </p>
<p>1.10 Serializable 接口<br />
Serialization 对象生成流，其中提供有关所存对象的 JavaTM 类的信息。对于可序列化对象，即使存在该类的不同（但兼容）版本的实现，也要保持足够的信息以恢复这些对象。定义 Serializable 接口可以标识实现可序列化协议的类：</p>
<p><br />
package java.io;</p>
<p>public interface Serializable {};</p>
<p>Serializable 类必须具备如下功能：</p>
<p>实现 java.io.Serializable 接口 <br />
标识应序列化的域<br />
(使用 serialPersistentFields 成员以显式声明其可序列化，或者使用瞬态关键字来指示非序列域。) <br />
可访问其第一个非序列化父类的无参数构造函数。 <br />
该类可根据需要定义如下方法：</p>
<p>writeObject 方法，用于控制要保存哪些信息或将附加信息添加到流中 <br />
readObject 方法，用于读取由相应 writeObject 方法写入的信息，或在对象恢复后更新其状态 <br />
writeReplace 方法，用于允许类指定写入流的替换对象<br />
readResolve 方法，用于允许类为刚从流中读取的对象指定替换对象<br />
ObjectOutputStream 和 ObjectInputStream 支持所操作的可序列化类的演变（允许类发生改变以兼容早期版本的类）。有关实现兼容变化的机制的信息，参见第 5.5 节&#8220;兼容的 JavaTM 类型演变&#8221;。<br />
--------------------------------------------------------------------------<br />
class ObjectYouWannaSave implements Serializable{<br />
&nbsp;&nbsp; int x; int y; <br />
&nbsp;&nbsp; SubObject so = new SubObject();<br />
&nbsp;&nbsp; ..........<br />
}</p>
<p>class SubObject implements Serializable{<br />
&nbsp;&nbsp; String s = "abc"; //String is OK, because String is java.io.Serializable<br />
}</p>
<p>public class Save{ // in Save.java<br />
&nbsp;&nbsp; public void main(String[] args) {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ObjectYouWannaSave original = new ObjectYouWannaSave();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; original.x = -1; original.y = 100; .....<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ObjectOutputStream out = null;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; try{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; out = new ObjectOutputStream(new FileOutputStream(new File("c:/save.dat")));<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; out.writeObject(original);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }catch(Exceptione){<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }finally{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; try{out.close();}catch(Exception e){}<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />
&nbsp;&nbsp; }<br />
}</p>
<p>public class Read{ // in Read.java<br />
&nbsp;&nbsp; public void main(String[] args) {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ObjectInputStream in = null;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; try{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; out = new ObjectInStream(new FileInStream(new File("c:/save.dat")));<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ObjectYouWannaSave o = (ObjectYouWannaSave)in.readObject();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; System.out.println("x="+o.x); <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; System.out.println("subobject.string=" + o.so.s);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ........<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }catch(Exceptione){<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }finally{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; try{in.close();}catch(Exception e){}<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />
&nbsp;&nbsp; }<br />
}</p>
<p>这是个最简单的例子，先运行Save,以后什么时候运行Read都可以（只要c:\save.dat文件还存在就可以了）<br />
--------------------------------------------------<br />
对象的寿命通常随着生成该对象的程序终止而终止，有时候，可能需要将对象的状态保存下来，在需要的时候<br />
再将对象恢复，我们把对象的这种记录自己状态的以便将来再生的能力，叫做对象的持续性（PERSISTENCE），<br />
对象通过写出描述自己状态的数值来记录自己，这个过程叫对象的串行化Serializable。串行化的主要任务是写<br />
出对象实例变量的值。如果变量是另一个对象的引用，则引用的对象也要串行化，这是个递归过程。<br />
--------------------------------------------------------------------<br />
java.io.Serializable interface是一个较为特殊的接口：<br />
The serialization interface has no methods or fields and serves <br />
only to identify the semantics of being serializable<br />
即：它主要是一个用于通知JVM的标志</p>
<p>class X implements java.io.Serializable{...}</p>
<p>如果你要定制你自己的serializable行为，你应该在X中实现以下2个方法：<br />
&nbsp;private void writeObject(java.io.ObjectOutputStream out)<br />
&nbsp;&nbsp;&nbsp;&nbsp; throws IOException<br />
&nbsp;private void readObject(java.io.ObjectInputStream in)<br />
&nbsp;&nbsp;&nbsp;&nbsp; throws IOException, ClassNotFoundException;</p>
<p>否则将按缺省的策略进行。</p>
<p>由于上述2个方法是private的，所以在某些情况下，可以使用<br />
java.io.Serializable的子接口java.io.Externalizable，<br />
在这个interface中有2个public方法：</p>
<p>public void writeExternal(ObjectOutput out)<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; throws IOException</p>
<p>public void readExternal(ObjectInput in)<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; throws IOException,<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ClassNotFoundException</p>
<p><br />
----------------------------------------------------------------------------<br />
Serializable是一个串行化接口，它没有任何的属性和方法，一个类实现了串行化接口，就表明<br />
该类具有持久性，通过WriteObjcet可将其持久保存在磁盘，在需要时，可用ReadObject从磁盘中读出，次时，对象的状态和存入似的相同。上面给出的列子比较简单，可通过串行化保存一个<br />
链表对象，它的每个节点数据也会被保存。 当然，串行化接口还有其他用途，例如HTTP隧道等<br />
。<br />
-----------------------------------------------------------------------<br />
对象序列化的持续性要和永久对象分别开。序列化只能达到有线持续性<br />
在同一个jvm上可以重构对象的内容，达到持续性<br />
---------------------------------------------------------------------<br />
java序列化主要是用于两个方面：<br />
一个是java的RMI（远程方法调用－Remote method invocation），你可以使用别人机器上的对象就像在你自己的机器上使用它一样（通过序列化），另外，在使用javabean时也需要使用序列化，序列化主要就是使用在这两方面。<br />
-------------------------------------------------------------<br />
所谓序列化就是把把对象以一个代码串的形式表示出来，<br />
这样可以保存到磁盘或则通过网络传输，接受方可以在把代码串恢复成对象<br />
具体的串形化方法可以自己写，接收放就可以根据您串形的规则把对象恢复出来<br />
--------------------------------------------------------------------<br />
序列化是把一个对象的状态写入一个字节流的过程，它执行RMI，RMI允许一台机器上的JAVA对象调用不同机器上的JAVA对象方法，对象可以作为参数提供给那个远程方法，发送机序列化该对象并传送它，接收机执行反序列化。 <br />
序列化和反序列化的关系图表可形成包含循环引用的顺序图表。这是整个序列化的总体思想。 <br />
而Serializable接口属于支持序列化的一个接口，只有一个实现它的对象可以被序列化工具存储和回复，Serializable接口没有定义任何成员，只用来表示一个累可以被序列化，若该类可以序列化，那么它的所有子类都可以。 <br />
下面是关于序列化的一个实例： <br />
[b:a55efb5f91]程序名称：SerializationDemo.java <br />
程序主题：实现对象的序列化和反序列化 <br />
程序说明：该程序由实例化一个MyClass类的对象开始，该对象有三个实例变量，类型分别为String、int、double，是希望存储和恢复的信息。[/b:a55efb5f91] </p>
<p>[code:1:a55efb5f91] </p>
<p>import java.io.*; </p>
<p>public class SerializationDemo{ <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; public static void main(String args[]){ </p>
<p>//Object serialization <br />
try{ <br />
MyClass object1=new MyClass("Hello",-7,2.7e10); <br />
System.out.println("object1:"+object1); <br />
FileOutputStream fos=new FileOutputStream("serial"); <br />
ObjectOutputStream oos=new ObjectOutputStream(fos); <br />
oos.writeObject(object1); <br />
oos.flush(); <br />
oos.close(); <br />
} <br />
catch(Exception e){ <br />
System.out.println("Exception during serialization:"+e); <br />
System.exit(0); <br />
} </p>
<p>//Object deserialization <br />
try{ <br />
MyClass object2; <br />
FileInputStream fis=new FileInputStream("serial"); <br />
ObjectInputStream ois=new ObjectInputStream(fis); <br />
object2=(MyClass)ois.readObject(); <br />
ois.close(); <br />
System.out.println("object2:"+object2); <br />
} <br />
catch(Exception e){ <br />
System.out.println("Exception during deserialization:"+e); <br />
System.exit(0); <br />
} <br />
} <br />
} </p>
<p>class MyClass implements Serializable{ <br />
String s; <br />
int i; <br />
double d; <br />
public MyClass(String s,int i,double d){ <br />
this.s=s; <br />
this.i=i; <br />
this.d=d; <br />
} <br />
public String toString(){ <br />
return "s="+s+";i="+i+";d="+d; <br />
} <br />
} <br />
[/code:1:a55efb5f91] </p>
<p>程序运行结果：object1和object2的实例变量是一样的，输出如下：[code:1:a55efb5f91]object1:s=Hello;i=-7;d=2.7E10 <br />
object2:s=Hello;i=-7;d=2.7E10[/code:1:a55efb5f91]<br />
--------------------------------------------------------------------------------<br />
Object serialization的定义： <br />
Object serialization 允许你将实现了Serializable接口的对象转换为字节序列，这些字节序列可以被完全存储以备以后重新生成原来的对象。&nbsp; </p>
<p>serialization不但可以在本机做，而且可以经由网络操作（就是猫小说的RMI）。这个好处是很大的----因为它自动屏蔽了操作系统的差异，字节顺序（用Unix下的c开发过网络编程的人应该知道这个概念，我就容易在这上面犯错）等。比如，在Window平台生成一个对象并序列化之，然后通过网络传到一台Unix机器上，然后可以在这台Unix机器上正确地重构这个对象。 </p>
<p><br />
Object serialization主要用来支持2种主要的特性： <br />
1。Java的RMI(remote method invocation).RMI允许象在本机上一样操作远程机器上的对象。当发送消息给远程对象时，就需要用到serializaiton机制来发送参数和接收返回直。 </p>
<p>2。Java的JavaBeans.&nbsp;&nbsp; Bean的状态信息通常是在设计时配置的。Bean的状态信息必须被存起来，以便当程序运行时能恢复这些状态信息。这也需要serializaiton机制。 </p>
<p>&nbsp;</p>
<p>二。持久化 <br />
持久化应该是英文里的persistence.但是Java语言里现在只支持lightweight persistence,就是轻量级持久化，这是通过serialization机制来实现的。 </p>
<p>persistence是指一个对象的生命周期不由程序是否执行来决定，即使是在程序终止时这个对象也存在。它把一个serializable的对象写到磁盘（本机或其他机器上的非RAM存储器），并在程序重新调用时再读取对象到通常的RAM存储器。 </p>
<p>为什么说Java的serialization机制实现的是lightweight persistence?因为你必须显式的序列化和反序列化程序里的对象；而不是直接由一个关键词来定义一个对象是序列化的然后由系统做相应的处理。&nbsp; 如果以后的Java版本出现一个新的关键字来实现这种机制，比如就是persistence,如果我用 </p>
<p>persistence&nbsp; (String s="chinaunix") </p>
<p>然后系统自动做上面程序里的那些处理，那么Java就实现了persistence. </p>
 <img src ="http://www.blogjava.net/NewMoring/aggbug/278195.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/NewMoring/" target="_blank">清晨</a> 2009-05-27 14:16 <a href="http://www.blogjava.net/NewMoring/articles/278195.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>构造方法(2)</title><link>http://www.blogjava.net/NewMoring/articles/268600.html</link><dc:creator>清晨</dc:creator><author>清晨</author><pubDate>Sat, 02 May 2009 04:38:00 GMT</pubDate><guid>http://www.blogjava.net/NewMoring/articles/268600.html</guid><wfw:comment>http://www.blogjava.net/NewMoring/comments/268600.html</wfw:comment><comments>http://www.blogjava.net/NewMoring/articles/268600.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/NewMoring/comments/commentRss/268600.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/NewMoring/services/trackbacks/268600.html</trackback:ping><description><![CDATA[<p style="text-indent: 2em">一个新对象的初始化的最终步骤是去调用对象的构造方法。 </p>
<p style="text-indent: 2em">构造方法必须满足以下条件： </p>
<p style="text-indent: 2em">l. 方法名必须与类名称完全相匹配； </p>
<p style="text-indent: 2em">2. 不要声明返回类型； </p>
<p style="text-indent: 2em">3. 不能被static、final、synchronized、abstract、native修饰。 </p>
<p style="text-indent: 2em">下列代码中的构造方法都是合法的。 </p>
<p style="text-indent: 2em">public class Xyz { </p>
<p style="text-indent: 2em">public Xyz() { // No-arg constructor </p>
<p style="text-indent: 2em">// set up the object. </p>
<p style="text-indent: 2em">} </p>
<p style="text-indent: 2em">public Xyz(int x) { //int-arg constructor </p>
<p style="text-indent: 2em">// set up the object using the parameter x. </p>
<p style="text-indent: 2em">} </p>
<p style="text-indent: 2em">} </p>
<p style="text-indent: 2em">5.2.2 重载构造方法 </p>
<p style="text-indent: 2em">如果有一个类带有几个构造方法，那么也许会想复制其中一个构造方法的某些操作到另一个构造方法中。可以通过使用关键字this作为一个方法调用来达到这个目的。不能通过方法名直接调用构造方法。 </p>
<p style="text-indent: 2em">public class Employee { </p>
<p style="text-indent: 2em">private String name; </p>
<p style="text-indent: 2em">private int salary; </p>
<p style="text-indent: 2em">public Employee(String n, int s) { </p>
<p style="text-indent: 2em">name = n; </p>
<p style="text-indent: 2em">salary = s; </p>
<p style="text-indent: 2em">} </p>
<p style="text-indent: 2em">public Employee(String n) { </p>
<p style="text-indent: 2em">this(n, 0); </p>
<p style="text-indent: 2em">} </p>
<p style="text-indent: 2em">public Employee() { </p>
<p style="text-indent: 2em">this( " Unknown " ); </p>
<p style="text-indent: 2em">} </p>
<p style="text-indent: 2em">} </p>
<p style="text-indent: 2em">在第二个构造方法中，有一个字符串参数，调用this(n,0)将控制权传递到构造方法的另一个版本，即采用了一个String参数和一个int参数的构造方法。 </p>
<p style="text-indent: 2em">在第三个构造方法中，它没有参数，调用this("Unknownn")将控制权传递到构造方法的另一个版本，即采用了一个String参数的构造方法。 </p>
<p style="text-indent: 2em">对于this的任何调用，如果出现在任何构造方法中，必须作为第一个语句。 </p>
<p style="text-indent: 2em">5.2.3 缺省构造方法 </p>
<p style="text-indent: 2em">缺省构造方法是没有参数的构造方法，你可以显式定义类的缺省构造方法。 </p>
<p style="text-indent: 2em">为了保证每个类至少有一个构造方法，如果定义的类中一个构造方法也没有写，Java将自动提供一个缺省构造方法。该构造方法没有参数，用public 修饰，而且方法体为空。格式如下： </p>
<p style="text-indent: 2em">public ClassName(){} </p>
<p style="text-indent: 2em">只要类中显式定义了一个或多个构造方法，而且所有显式定义的构造方法都带参数，那么将失去缺省构造方法。 </p>
<p style="text-indent: 2em">举例如下： </p>
<p style="text-indent: 2em">public class Sample1{} </p>
<p style="text-indent: 2em">public class Sample2 </p>
<p style="text-indent: 2em">{ </p>
<p style="text-indent: 2em">public Sample2(int a){System.out.println("My Constructor");} </p>
<p style="text-indent: 2em">} </p>
<p style="text-indent: 2em">public class Sample3 </p>
<p style="text-indent: 2em">{ </p>
<p style="text-indent: 2em">public Sample3(){System.out.println("My Default Constructor");} </p>
<p style="text-indent: 2em">} </p>
<p style="text-indent: 2em">可以调用Sample1的缺省构造方法来创建Sample1对象。 </p>
<p style="text-indent: 2em">Sample1 s=new Sample1(); </p>
<p style="text-indent: 2em">Sample2类的缺省构造方法失效，因此以下的创建Sample2对象的方法编译会出错。 </p>
<p style="text-indent: 2em">Sample2 s=new Sample2(); </p>
<p style="text-indent: 2em">正确的创建方法是： </p>
<p style="text-indent: 2em">Sample2 s=new Sample2(0); </p>
<p style="text-indent: 2em">Sample3类定义了自己的缺省构造方法，因此以下语句是合法的。 </p>
<p style="text-indent: 2em">Sample3 s=new Sample3(); </p>
<p style="text-indent: 2em">5.2.4 子类调用父类的构造方法 </p>
<p style="text-indent: 2em">父类的构造方法不能被子类继承。子类不能直接通过方法名调用父类的一个构造方法，而是要通过super关键字调用父类的一个构造方法，super语句必须位于子类构造方法的第一行。 </p>
<p style="text-indent: 2em">例如： </p>
<p style="text-indent: 2em">/** </p>
<p style="text-indent: 2em">* Son.java </p>
<p style="text-indent: 2em">*/ </p>
<p style="text-indent: 2em">class GrandPa </p>
<p style="text-indent: 2em">{ </p>
<p style="text-indent: 2em">protected GrandPa() </p>
<p style="text-indent: 2em">{ </p>
<p style="text-indent: 2em">System.out.println("default GrandPa"); </p>
<p style="text-indent: 2em">} </p>
<p style="text-indent: 2em">public GrandPa(String name) </p>
<p style="text-indent: 2em">{ </p>
<p style="text-indent: 2em">System.out.println(name); </p>
<p style="text-indent: 2em">} </p>
<p style="text-indent: 2em">} </p>
<p style="text-indent: 2em">class Father extends GrandPa </p>
<p style="text-indent: 2em">{ </p>
<p style="text-indent: 2em">protected Father() </p>
<p style="text-indent: 2em">{ </p>
<p style="text-indent: 2em">System.out.println("default Father"); </p>
<p style="text-indent: 2em">} </p>
<p style="text-indent: 2em">public Father(String grandPaName,String fatherName) </p>
<p style="text-indent: 2em">{ </p>
<p style="text-indent: 2em">super(grandPaName); </p>
<p style="text-indent: 2em">System.out.println(fatherName); </p>
<p style="text-indent: 2em">} </p>
<p style="text-indent: 2em">} </p>
<p style="text-indent: 2em">public class Son extends Father </p>
<p style="text-indent: 2em">{ </p>
<p style="text-indent: 2em">public Son() </p>
<p style="text-indent: 2em">{ </p>
<p style="text-indent: 2em">System.out.println("default Son"); </p>
<p style="text-indent: 2em">} </p>
<p style="text-indent: 2em">public Son(String grandPaName,String fatherName,String sonName) </p>
<p style="text-indent: 2em">{ </p>
<p style="text-indent: 2em">super(grandPaName,fatherName); </p>
<p style="text-indent: 2em">System.out.println(sonName); </p>
<p style="text-indent: 2em">} </p>
<p style="text-indent: 2em">public static void main(String args[]) </p>
<p style="text-indent: 2em">{ </p>
<p style="text-indent: 2em">Son s1=new Son(); </p>
<p style="text-indent: 2em">Son s2= new Son("My GrandPa", "My Father", "My Son"); </p>
<p style="text-indent: 2em">} </p>
<p style="text-indent: 2em">} </p>
<p style="text-indent: 2em">执行语句： </p>
<p style="text-indent: 2em">Son s2=new Son("My GrandPa", "My Father", "My Son"); </p>
<p style="text-indent: 2em">将会输出如下结果： </p>
<p style="text-indent: 2em">My GrandPa </p>
<p style="text-indent: 2em">My Father </p>
<p style="text-indent: 2em">My Son </p>
<p style="text-indent: 2em">当子类的某个构造方法没有调用父类的一个构造方法，通过这个构造方法创建子类对象时，会自动先调用父类的缺省构造方法。对于多级继承，执行缺省构造方法的顺序是从最上层的类开始。 </p>
<p style="text-indent: 2em">对于以上的代码执行语句Son s1=new Son(); </p>
<p style="text-indent: 2em">将会输出如下结果： </p>
<p style="text-indent: 2em">default GrandPa </p>
<p style="text-indent: 2em">default Father </p>
<p style="text-indent: 2em">default Son </p>
<p style="text-indent: 2em">当子类的某个构造方法没有调用父类的一个构造方法，而父类又没有提供缺省构造方法时，将会出现编译错误。 </p>
<p style="text-indent: 2em">例如：修改一下Son.java文件。去掉GrandPa类中显式定义的缺省构造方法： </p>
<p style="text-indent: 2em">protected GrandPa() </p>
<p style="text-indent: 2em">{ </p>
<p style="text-indent: 2em">System.out.println("default GrandPa"); </p>
<p style="text-indent: 2em">} </p>
<p style="text-indent: 2em">这样，GrandPa类中就失去了缺省构造方法，这时，在编译Father类的缺省构造方法时，因为找不到GrandPa类的缺省构造方法而出错。 </p>
<p style="text-indent: 2em">子类的某个构造方法没有通过super语句调用父类的构造方法，而是通过this语句调用了另一个构造方法，而在另一个构造方法中通过super语句调用了父类的构造方法，这种情况下，父类的缺省构造方法不会被调用。 </p>
<p style="text-indent: 2em">例如： </p>
<p style="text-indent: 2em">class A </p>
<p style="text-indent: 2em">{ </p>
<p style="text-indent: 2em">A(int i){} </p>
<p style="text-indent: 2em">} </p>
<p style="text-indent: 2em">class B extends A </p>
<p style="text-indent: 2em">{ </p>
<p style="text-indent: 2em">B(){this(0);} </p>
<p style="text-indent: 2em">B(int i){super(i);} </p>
<p style="text-indent: 2em">} </p>
<p style="text-indent: 2em">对于new B()语句将依次执行如下方法： </p>
<p style="text-indent: 2em">1：A(int) 2：B(int) 3：B() </p>
<p style="text-indent: 2em">5.2.5 构造方法的作用域 </p>
<p style="text-indent: 2em">构造方法只能被所在类的其他构造方法或子类的构造方法调用，或在用new语句创建类的实例时被调用。引用类型的变量不能访问对象的构造方法。 </p>
<p style="text-indent: 2em">对于以下代码，请读者自己分析某些语句编译出错的原因。 </p>
<p style="text-indent: 2em">class Base </p>
<p style="text-indent: 2em">{ </p>
<p style="text-indent: 2em">public Base(int i,int j){} </p>
<p style="text-indent: 2em">public Base(int i){ </p>
<p style="text-indent: 2em">this(i,0); //CORRECT </p>
<p style="text-indent: 2em">Base(i,0); //ERROR </p>
<p style="text-indent: 2em">} </p>
<p style="text-indent: 2em">} </p>
<p style="text-indent: 2em">class Sub extends Base </p>
<p style="text-indent: 2em">{ </p>
<p style="text-indent: 2em">public Sub(int i,int j) </p>
<p style="text-indent: 2em">{ </p>
<p style="text-indent: 2em">super(i,0); //CORRECT </p>
<p style="text-indent: 2em">} </p>
<p style="text-indent: 2em">void method1(int i,int j) </p>
<p style="text-indent: 2em">{ </p>
<p style="text-indent: 2em">this(i,j); //ERROR </p>
<p style="text-indent: 2em">Sub(i,j); //ERROR </p>
<p style="text-indent: 2em">} </p>
<p style="text-indent: 2em">void method2(int i,int j) </p>
<p style="text-indent: 2em">{ </p>
<p style="text-indent: 2em">super(i,j); //ERROR </p>
<p style="text-indent: 2em">} </p>
<p style="text-indent: 2em">void method3(int i,int j) </p>
<p style="text-indent: 2em">{ </p>
<p style="text-indent: 2em">Base s＝new Base(0,0); //CORRECT </p>
<p style="text-indent: 2em">s.Base(0,0); //ERROR </p>
<p style="text-indent: 2em">} </p>
<p style="text-indent: 2em">} </p>
<p style="text-indent: 2em">5.2.6 构造方法不能被继承 </p>
<p style="text-indent: 2em">构造方法可以被子类的构造方法调用，但不能被子类继承。 </p>
<p style="text-indent: 2em">例如： </p>
<p style="text-indent: 2em">class A </p>
<p style="text-indent: 2em">{ </p>
<p style="text-indent: 2em">A(int i){}; </p>
<p style="text-indent: 2em">A(){} </p>
<p style="text-indent: 2em">} </p>
<p style="text-indent: 2em">class B extends A{} </p>
<p style="text-indent: 2em">class C extends B </p>
<p style="text-indent: 2em">{ </p>
<p style="text-indent: 2em">C(){ </p>
<p style="text-indent: 2em">super.super(0); //COMPILE ERROR </p>
<p style="text-indent: 2em">A(0); //COMPILE ERROR </p>
<p style="text-indent: 2em">} </p>
<p style="text-indent: 2em">} </p>
<p style="text-indent: 2em">由于类B并没有继承类A的构造方法A(int i)，所以在构造方法C()中调用类A的构造方法是非法的。 </p>
<p style="text-indent: 2em">5.2.7 考察要点 </p>
<p style="text-indent: 2em">l 识别合法的构造方法； </p>
<p style="text-indent: 2em">2 构造方法可以被重载，一个构造方法可以通过this关键字调用另一个构造方法，this语句必须位于构造方法的第一行； </p>
<p style="text-indent: 2em">3 当一个类中没有定义任何构造方法，Java将自动提供一个缺省构造方法； </p>
<p style="text-indent: 2em">4 子类通过super关键字调用父类的一个构造方法； </p>
<p style="text-indent: 2em">5 当子类的某个构造方法没有通过super关键字调用父类的构造方法，通过这个构造方法创建子类对象时，会自动先调用父类的缺省构造方法 </p>
<p style="text-indent: 2em">6 构造方法不能被static、final、synchronized、abstract、native修饰，但可以被public、private、protected修饰； </p>
<p style="text-indent: 2em">7 构造方法不是类的成员方法； </p>
<p style="text-indent: 2em">8 构造方法不能被继承。 </p>
<p style="text-indent: 2em">5.2.8 试题解析 </p>
<p style="text-indent: 2em">1．Which line contains a constructor in this class definition? </p>
<p style="text-indent: 2em">public class Counter { // (a) </p>
<p style="text-indent: 2em">int current, step; </p>
<p style="text-indent: 2em">public Counter(int startValue, int stepValue) { // (b) </p>
<p style="text-indent: 2em">set(startValue); </p>
<p style="text-indent: 2em">setStepValue(stepValue); </p>
<p style="text-indent: 2em">} </p>
<p style="text-indent: 2em">public int get() { return current; } // (c) </p>
<p style="text-indent: 2em">public void set(int value) { current = value; } // (d) </p>
<p style="text-indent: 2em">public void setStepValue(int stepValue) { step = stepValue; } // (e) </p>
<p style="text-indent: 2em">} </p>
<p style="text-indent: 2em">a) Code marked with (a) is a constructor </p>
<p style="text-indent: 2em">b) Code marked with (b) is a constructor </p>
<p style="text-indent: 2em">c) Code marked with (c) is a constructor </p>
<p style="text-indent: 2em">d) Code marked with (d) is a constructor </p>
<p style="text-indent: 2em">e) Code marked with (e) is a Constructor </p>
<p style="text-indent: 2em">答案：b </p>
<p style="text-indent: 2em">解析：类的构造方法必须遵循以下规则：方法名和类同名，无返回值。一个方法同时满足这两个条件，Java编译器就会将其当作类的构造方法。一个方法如果只满足其中的一个条件，将会编译出错，被认为是不合法的方法声明。 </p>
<p style="text-indent: 2em">2．Which of the following can be applied to constructors: </p>
<p style="text-indent: 2em">a) final </p>
<p style="text-indent: 2em">b) static </p>
<p style="text-indent: 2em">c) synchronized </p>
<p style="text-indent: 2em">d) native </p>
<p style="text-indent: 2em">e) None of these. </p>
<p style="text-indent: 2em">答案：e </p>
<p style="text-indent: 2em">解析：构造方法不能被子类继承，所以用final修饰没有意义。构造方法用于创建一个新的对象，不能作为类的静态方法，所以用static修饰没有意义。此外，Java语言不支持native或synchronized的构造方法。 </p>
<p style="text-indent: 2em">3．What will happen when you attempt to compile and run the following code . </p>
<p style="text-indent: 2em">public class Hope{ </p>
<p style="text-indent: 2em">public static void main(String argv[]){ </p>
<p style="text-indent: 2em">Hope h = new Hope(); </p>
<p style="text-indent: 2em">} </p>
<p style="text-indent: 2em">protected Hope(){ </p>
<p style="text-indent: 2em">for(int i =0; i &lt;10; i ++){ </p>
<p style="text-indent: 2em">System.out.println(i); </p>
<p style="text-indent: 2em">} </p>
<p style="text-indent: 2em">} </p>
<p style="text-indent: 2em">} </p>
<p style="text-indent: 2em">a) Compilation error: Constructors cannot be declared protected </p>
<p style="text-indent: 2em">b) Run time error: Constructors cannot be declared protected </p>
<p style="text-indent: 2em">c) Compilation and running with output 0 to 10 </p>
<p style="text-indent: 2em">d) Compilation and running with output 0 to 9 </p>
<p style="text-indent: 2em">答案：d </p>
<p style="text-indent: 2em">解析：构造方法可以被public、protected、private修饰。 </p>
<p style="text-indent: 2em">4．What will happen if you try to compile and execute B&#180;s main() method? </p>
<p style="text-indent: 2em">class A { </p>
<p style="text-indent: 2em">int i; </p>
<p style="text-indent: 2em">A(int i) { </p>
<p style="text-indent: 2em">this.i = i * 2; </p>
<p style="text-indent: 2em">} </p>
<p style="text-indent: 2em">} </p>
<p style="text-indent: 2em">class B extends A { </p>
<p style="text-indent: 2em">public static void main(String[] args) { </p>
<p style="text-indent: 2em">B b = new B(2); </p>
<p style="text-indent: 2em">} </p>
<p style="text-indent: 2em">B(int i) { </p>
<p style="text-indent: 2em">System.out.println(i); </p>
<p style="text-indent: 2em">} </p>
<p style="text-indent: 2em">} </p>
<p style="text-indent: 2em">Select the one right answer. </p>
<p style="text-indent: 2em">a) The instance variable i is set to 4 </p>
<p style="text-indent: 2em">b) The instance variable i is set to 2 </p>
<p style="text-indent: 2em">c) The instance variable i is set to 0 </p>
<p style="text-indent: 2em">d) This code will not compile </p>
<p style="text-indent: 2em">答案：d </p>
<p style="text-indent: 2em">解析：由于类B的构造方法B(int i)中没有调用父类的构造方法，而父类中又没有缺省构造方法，导致编译错误。 </p>
<p style="text-indent: 2em">5．What happens when you try to compile and run the following program? </p>
<p style="text-indent: 2em">class Mystery { </p>
<p style="text-indent: 2em">String s; </p>
<p style="text-indent: 2em">public static void main(String[] args) { </p>
<p style="text-indent: 2em">Mystery m = new Mystery(); </p>
<p style="text-indent: 2em">m.go(); </p>
<p style="text-indent: 2em">} </p>
<p style="text-indent: 2em">void Mystery() { </p>
<p style="text-indent: 2em">s = "constructor"; </p>
<p style="text-indent: 2em">} </p>
<p style="text-indent: 2em">void go() { </p>
<p style="text-indent: 2em">System.out.println(s); </p>
<p style="text-indent: 2em">} </p>
<p style="text-indent: 2em">} </p>
<p style="text-indent: 2em">Select the one right answer. </p>
<p style="text-indent: 2em">a) this code will not compile </p>
<p style="text-indent: 2em">b) this code compiles but throws an exception at runtime </p>
<p style="text-indent: 2em">c) this code runs but nothing appears in the standard output </p>
<p style="text-indent: 2em">d) this code runs and "constructor" in the standard output </p>
<p style="text-indent: 2em">e) this code runs and writes "null" in the standard output </p>
<p style="text-indent: 2em">答案：e </p>
<p style="text-indent: 2em">解析：这道题中故意设置了一个陷阱，定义了一个貌似构造方法的Mystery()方法。由于它被声明了返回类型，所以其实不是构造方法。new Mystery()语句其实调用的是类Mystery的缺省构造方法。 </p>
<p style="text-indent: 2em">6．Yes or No? </p>
<p style="text-indent: 2em">Statement1) If you create a non-default derived constructor and don&#180;t call the base class constructor ，the compiler will call the default base class constructor automatically. (Assume that the default constructor is defined for the base class). </p>
<p style="text-indent: 2em">Statement2) What about if it is not defined? </p>
<p style="text-indent: 2em">Statement3) What about the case of a default derived constructor, does the compiler </p>
<p style="text-indent: 2em">call the default base class constructor </p>
<p style="text-indent: 2em">a) Yes ,No,Yes </p>
<p style="text-indent: 2em">b) Yes,No,No </p>
<p style="text-indent: 2em">答案：a </p>
<p style="text-indent: 2em">解析：子类的构造方法中如果没有调用父类的构造方法，程序运行时会自动先调用父类的缺省构造方法（包括自动提供的缺省构造方法或显式定义的缺省构造方法）。如果父类没有缺省构造方法，将会编译出错。 </p>
<p style="text-indent: 2em">7．Given the following code how could you invoke the Base constructor that will print out the string "base constructor"; </p>
<p style="text-indent: 2em">class Base{ </p>
<p style="text-indent: 2em">Base(int i){ </p>
<p style="text-indent: 2em">System.out.println("base constructor"); </p>
<p style="text-indent: 2em">} </p>
<p style="text-indent: 2em">Base(){ </p>
<p style="text-indent: 2em">} </p>
<p style="text-indent: 2em">} </p>
<p style="text-indent: 2em">public class Sup extends Base{ </p>
<p style="text-indent: 2em">public static void main(String argv[]){ </p>
<p style="text-indent: 2em">Sup s= new Sup(); </p>
<p style="text-indent: 2em">//One </p>
<p style="text-indent: 2em">} </p>
<p style="text-indent: 2em">Sup() </p>
<p style="text-indent: 2em">{ </p>
<p style="text-indent: 2em">//Two </p>
<p style="text-indent: 2em">} </p>
<p style="text-indent: 2em">public void derived() </p>
<p style="text-indent: 2em">{ </p>
<p style="text-indent: 2em">//Three </p>
<p style="text-indent: 2em">} </p>
<p style="text-indent: 2em">} </p>
<p style="text-indent: 2em">a) On the line After //One put Base(10); </p>
<p style="text-indent: 2em">b) On the line After //One put super(10); </p>
<p style="text-indent: 2em">c) On the line After //Two put super(10); </p>
<p style="text-indent: 2em">d) On the line After //Three put super(10); </p>
<p style="text-indent: 2em">答案：c </p>
<p style="text-indent: 2em">解析：构造方法只能被所在类的其他构造方法或子类的构造方法调用。所以在//one、//three处调用构造方法都是非法的。 </p>
<p style="text-indent: 2em">8．True or False. </p>
<p style="text-indent: 2em">The Class class has no public constructor. </p>
<p style="text-indent: 2em">a) True </p>
<p style="text-indent: 2em">b) False </p>
<p style="text-indent: 2em">答案：a </p>
<p style="text-indent: 2em">解析：Class类没有公共构造方法，所以Class类的对象不能在程序中通过构造方法创建。当Java虚拟机装载某个类（假如类名是ClassSample）的时候，会自动调用JVM的类装载器的defineClass()方法，这个方法会创建和某个类ClassSample相关的Class类的对象。Object类有个方法getClass()，它返回一个Class类的对象。所以，所有的Java对象都可以通过方法getClass()获得它的Class类的对象。例如： </p>
<p style="text-indent: 2em">ClassSample s＝new ClassSample(); </p>
<p style="text-indent: 2em">System.out.println(s.getClass().getName()); </p>
<p style="text-indent: 2em">将会打印ClassSample。 </p>
<p style="text-indent: 2em">9．Which statements concerning the following code are true? </p>
<p style="text-indent: 2em">class A { </p>
<p style="text-indent: 2em">public A() {} </p>
<p style="text-indent: 2em">public A(int i) { this(); } </p>
<p style="text-indent: 2em">} </p>
<p style="text-indent: 2em">class B extends A { </p>
<p style="text-indent: 2em">public boolean B(String msg) { return false; } </p>
<p style="text-indent: 2em">} </p>
<p style="text-indent: 2em">class C extends B { </p>
<p style="text-indent: 2em">private C() { super(); } </p>
<p style="text-indent: 2em">public C(String msg) { this(); } </p>
<p style="text-indent: 2em">public C(int i) {} </p>
<p style="text-indent: 2em">} </p>
<p style="text-indent: 2em">a) The code will fail to compile. </p>
<p style="text-indent: 2em">b) The constructor in A that takes an int as an argument will never be called as a result of constructing an object of class B or C. </p>
<p style="text-indent: 2em">c) Class C has three constructors. </p>
<p style="text-indent: 2em">d) Objects of class B cannot be constructed. </p>
<p style="text-indent: 2em">e) At most one of the constructors of each class is called as a result of constructing an object of class C. </p>
<p style="text-indent: 2em">答案：b，c </p>
<p style="text-indent: 2em">解析：在类B中定义的方法B()有返回值，所以它不是构造方法。类B只有一个缺省构造方法，所以创建类B或类C的对象时，会先调用类B的缺省构造方法，而类B的缺省构造方法会先调用类C的缺省构造方法，类A的构造方法A(int i)永远不会被调用。选项e是不正确的，new C("hello")语句将调用类C的两个构造方法C()和C(String msg)。 </p>
<p style="text-indent: 2em">10．Which of the following code fragments are legal? </p>
<p style="text-indent: 2em">a) class MusicWork { </p>
<p style="text-indent: 2em">MusicWork(String s) { </p>
<p style="text-indent: 2em">System.out.println("The name of this work is" + s); </p>
<p style="text-indent: 2em">} </p>
<p style="text-indent: 2em">} </p>
<p style="text-indent: 2em">class ClassicalWork extends MusicWork { </p>
<p style="text-indent: 2em">ClassicalWork(String s, String composer) { </p>
<p style="text-indent: 2em">System.out.println("The composer is " + composer); </p>
<p style="text-indent: 2em">} </p>
<p style="text-indent: 2em">} </p>
<p style="text-indent: 2em">b) class MusicWork { </p>
<p style="text-indent: 2em">MusicWork(String s) { </p>
<p style="text-indent: 2em">System.out.println("The name of this work is" + s); </p>
<p style="text-indent: 2em">} </p>
<p style="text-indent: 2em">} </p>
<p style="text-indent: 2em">class ClassicalWork extends MusicWork { </p>
<p style="text-indent: 2em">ClassicalWork(String s, String composer) { </p>
<p style="text-indent: 2em">super(s); </p>
<p style="text-indent: 2em">System.out.println("The composer is " + composer); </p>
<p style="text-indent: 2em">} </p>
<p style="text-indent: 2em">} </p>
<p style="text-indent: 2em">c) class MusicWork { </p>
<p style="text-indent: 2em">MusicWork(String s) { </p>
<p style="text-indent: 2em">System.out.println("The name of this work is" + s); </p>
<p style="text-indent: 2em">} </p>
<p style="text-indent: 2em">} </p>
<p style="text-indent: 2em">class ClassicalWork extends MusicWork { </p>
<p style="text-indent: 2em">ClassicalWork(String s, String composer) { </p>
<p style="text-indent: 2em">System.out.println("This is a work of classical music"); </p>
<p style="text-indent: 2em">System.out.println("The composer is " + composer); </p>
<p style="text-indent: 2em">super(s); </p>
<p style="text-indent: 2em">} </p>
<p style="text-indent: 2em">} </p>
<p style="text-indent: 2em">d) class MusicWork { </p>
<p style="text-indent: 2em">MusicWork() { </p>
<p style="text-indent: 2em">System.out.println("This is a work of music"); </p>
<p style="text-indent: 2em">} </p>
<p style="text-indent: 2em">MusicWork(String name) { </p>
<p style="text-indent: 2em">this(); </p>
<p style="text-indent: 2em">System.out.println("The name of this work is" + name); </p>
<p style="text-indent: 2em">} </p>
<p style="text-indent: 2em">} </p>
<p style="text-indent: 2em">e) class MusicWork { </p>
<p style="text-indent: 2em">MusicWork() { </p>
<p style="text-indent: 2em">System.out.println("This is a work of music"); </p>
<p style="text-indent: 2em">} </p>
<p style="text-indent: 2em">MusicWork(String name) { </p>
<p style="text-indent: 2em">this(); </p>
<p style="text-indent: 2em">System.out.println("The name of this work is" + name); </p>
<p style="text-indent: 2em">} </p>
<p style="text-indent: 2em">MusicWork(String composer) { </p>
<p style="text-indent: 2em">this(); </p>
<p style="text-indent: 2em">System.out.println("The composer of this work is" + composer); </p>
<p style="text-indent: 2em">} </p>
<p style="text-indent: 2em">} </p>
<p style="text-indent: 2em">f) class MusicWork { </p>
<p style="text-indent: 2em">MusicWork() { </p>
<p style="text-indent: 2em">System.out.println("This is a work of music"); </p>
<p style="text-indent: 2em">} </p>
<p style="text-indent: 2em">MusicWork(String name) { </p>
<p style="text-indent: 2em">System.out.println("The name of this work is" + name); </p>
<p style="text-indent: 2em">this(); </p>
<p style="text-indent: 2em">} </p>
<p style="text-indent: 2em">} </p>
<p style="text-indent: 2em">答案：b，d </p>
<p style="text-indent: 2em">解析：选项a中必须为父类提供缺省构造方法。选项c中没有把super语句放在构造方法的第一行。选项e中有两个Music(String)构造方法，不合法。选项f中没有把this语句放在构造方法的第一行。 </p>
<p style="text-indent: 2em">11．Given the following class definition which of the following can be legally placed after the comment line//Here ? </p>
<p style="text-indent: 2em">class Base{ </p>
<p style="text-indent: 2em">public Base(int i){} </p>
<p style="text-indent: 2em">} </p>
<p style="text-indent: 2em">public class MyOver extends Base{ </p>
<p style="text-indent: 2em">public static void main(String arg[]){ </p>
<p style="text-indent: 2em">MyOver m = new MyOver(10); </p>
<p style="text-indent: 2em">} </p>
<p style="text-indent: 2em">MyOver(int i){ </p>
<p style="text-indent: 2em">super(i); </p>
<p style="text-indent: 2em">} </p>
<p style="text-indent: 2em">MyOver(String s, int i){ </p>
<p style="text-indent: 2em">this(i); </p>
<p style="text-indent: 2em">//Here </p>
<p style="text-indent: 2em">} </p>
<p style="text-indent: 2em">} </p>
<p style="text-indent: 2em">a) MyOver m = new MyOver(); </p>
<p style="text-indent: 2em">b) super(); </p>
<p style="text-indent: 2em">c) this("Hello",10); </p>
<p style="text-indent: 2em">d) Base b = new Base(10); </p>
<p style="text-indent: 2em">答案：d </p>
<p style="text-indent: 2em">解析：类MyOver没有缺省构造方法，所以选项a不正确。</p>
<img src ="http://www.blogjava.net/NewMoring/aggbug/268600.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/NewMoring/" target="_blank">清晨</a> 2009-05-02 12:38 <a href="http://www.blogjava.net/NewMoring/articles/268600.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Java的子类调用父类的构造方法，构造方法的相关问题</title><link>http://www.blogjava.net/NewMoring/articles/268599.html</link><dc:creator>清晨</dc:creator><author>清晨</author><pubDate>Sat, 02 May 2009 04:34:00 GMT</pubDate><guid>http://www.blogjava.net/NewMoring/articles/268599.html</guid><wfw:comment>http://www.blogjava.net/NewMoring/comments/268599.html</wfw:comment><comments>http://www.blogjava.net/NewMoring/articles/268599.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/NewMoring/comments/commentRss/268599.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/NewMoring/services/trackbacks/268599.html</trackback:ping><description><![CDATA[<p>11.2.1 重载构造方法 <br />
11.2.2 默认构造方法 <br />
11.2.3 子类调用父类的构造方法 <br />
11.2.4 构造方法的作用域 <br />
11.2.5 构造方法的访问级别 <br />
</p>
<p>从上一节可以看出，在多数情况下，初始化一个对象的最终步骤是去调用这个对象的构造方法。构造方法负责对象的初始化工作，为实例变量赋予合适的初始值。构造方法必须满足以下语法规则：<br />
(1) 方法名必须与类名相同。<br />
(2)不要声明返回类型。<br />
(3)不能被static、final、synchronized、abstract和native修饰。构造方法不能被子类继承，所以用final和</p>
<p>abstract修饰没有意义。构造方法用于初始化一个新建的对象，所以用static修饰没有意义。多个线程不会同时创建内存地址相同的同一个对象，因此用synchronized修饰没有必要。此外，Java语言不支持native类型的构造方法。<br />
在以下Sample类中，具有int返回类型的Sample(int x)方法只是个普通的实例方法，不能作为构造方法：<br />
<font color="#0000ff">public class Sample {<br />
private int x;<br />
public Sample() { // 不带参数的构造方法<br />
this(1); <br />
}<br />
public Sample(int x) { //带参数的构造方法<br />
this.x=x;<br />
}<br />
public int Sample(int x) { //不是构造方法<br />
return x++;<br />
}<br />
}<br />
</font></p>
<p>以上例子尽管能编译通过，但是把实例方法和构造方法同名，不是好的编程习惯，容易引起混淆。例如以下Mystery类的Mystery()方法有void返回类型，因此是普通的实例方法：<br />
</p>
<p><font color="#0000ff">public class Mystery {<br />
private String s;<br />
public void Mystery() { //不是构造方法<br />
s = "constructor";<br />
}<br />
void go() {<br />
System.out.println(s);<br />
}<br />
public static void main(String[] args) {<br />
Mystery m = new Mystery();<br />
m.go();<br />
}<br />
}</font></p>
<p><font color="#0000ff"><br />
</font>以上程序的打印结果为null。因为用new语句创建Mystery实例时，调用的是Mystery类的默认构造方法，而不是以上有void返回类型的Mystery()方法。关于默认构造方法的概念，参见本章第11.2.2节（默认构造方法）。<br />
</p>
<p><strong>11.2.1 重载构造方法</strong></p>
<p>当通过new语句创建一个对象时，在不同的条件下，对象可能会有不同的初始化行为。例如对于公司新进来的一个雇员，在一开始的时候，有可能他的姓名和年龄是未知的，也有可能仅仅他的姓名是已知的，也有可能姓名和年龄都是已知的。如果姓名是未知的，就暂且把姓名设为"无名氏"，如果年龄是未知的，就暂且把年龄设为-1。<br />
可通过重载构造方法来表达对象的多种初始化行为。以下例程11-2的Employee类的构造方法有三种重载形式。在一个类的多个构造方法中，可能会出现一些重复操作。为了提高代码的可重用性，Java语言允许在一个构造方法中，用this语句来调用另一个构造方法。<br />
</p>
<p><font color="#0000ff">例程11-2 Employee.java<br />
public class Employee {<br />
private String name;<br />
private int age; </font></p>
<p><font color="#0000ff">/** 当雇员的姓名和年龄都已知，就调用此构造方法 */<br />
public Employee(String name, int age) {<br />
this.name = name;<br />
this.age=age;<br />
}</font></p>
<p><font color="#0000ff">/** 当雇员的姓名已知而年龄未知，就调用此构造方法 */<br />
public Employee(String name) {<br />
this(name, -1);<br />
}</font></p>
<p><font color="#0000ff">/** 当雇员的姓名和年龄都未知，就调用此构造方法 */<br />
public Employee() {<br />
this( "无名氏" );<br />
}<br />
public void setName(String name){this.name=name; }<br />
public String getName(){return name; }<br />
public void setAge(int age){this.age=age;}<br />
public int getAge(){return age;}<br />
}</font></p>
<p><br />
以下程序分别通过三个构造方法创建了三个Employee对象：<br />
</p>
<p><font color="#0000ff">Employee zhangsan=new Employee("张三",25);<br />
Employee lisi=new Employee("李四");<br />
Employee someone=new Employee();</font></p>
<p><font color="#0000ff"><br />
</font>在Employee(String name)构造方法中，this(name,-1)语句用于调用Employee(String name,int age)构造方法。在Employee()构造方法中，this("无名氏")语句用于调用Employee(String name)构造方法。<br />
用this语句来调用其他构造方法时，必须遵守以下语法规则：<br />
</p>
<p>（1）假如在一个构造方法中使用了this语句，那么它必须作为构造方法的第一条语句（不考虑注释语句）。以下构造方法是非法的： <br />
<font color="#0000ff">public Employee(){<br />
String name="无名氏";<br />
this(name); //编译错误，this语句必须作为第一条语句<br />
}<br />
</font>（2）只能在一个构造方法中用this语句来调用类的其他构造方法，而不能在实例方法中用this语句来调用类的其他构造方法。<br />
（3）只能用this语句来调用其他构造方法，而不能通过方法名来直接调用构造方法。以下对构造方法的调用方式是非法的：<br />
<font color="#0000ff">public Employee() {<br />
String name= "无名氏";<br />
Employee(name); //编译错误，不能通过方法名来直接调用构造方法<br />
}</font></p>
<p><br />
<strong>11.2.2 默认构造方法</strong></p>
<p><br />
默认构造方法是没有参数的构造方法，可分为两种：（1）隐含的默认构造方法（2）程序显式定义的默认构造方法。<br />
在Java语言中，每个类至少有一个构造方法。为了保证这一点，如果用户定义的类中没有提供任何构造方法，那么Java语言将自动提供一个隐含的默认构造方法。该构造方法没有参数，用public 修饰，而且方法体为空，格式如下：<br />
</p>
<p>public ClassName(){} //隐含的默认构造方法<br />
</p>
<p>在程序中也可以显式的定义默认构造方法，它可以是任意的访问级别。例如：<br />
</p>
<p><font color="#0000ff">protected Employee() { //程序显式定义的默认构造方法<br />
this("无名氏"); <br />
}<br />
</font></p>
<p>如果类中显式定义了一个或多个构造方法，并且所有的构造方法都带参数，那么这个类就失去了默认构造方法。在以下程序中，Sample1类有一个隐含的默认构造方法，Sample2类没有默认构造方法，Sample3类有一个显式定义的默认构造方法：<br />
</p>
<p><font color="#0000ff">public class Sample1{}<br />
public class Sample2{<br />
public Sample2(int a){System.out.println("My Constructor");}<br />
}</font></p>
<p><font color="#0000ff">public class Sample3{<br />
public Sample3(){System.out.println("My Default Constructor");}<br />
}</font></p>
<p><font color="#0000ff"><br />
</font>可以调用Sample1类的默认构造方法来创建Sample1对象：<br />
</p>
<p><font color="#0000ff">Sample1 s=new Sample1(); //合法<br />
Sample2类没有默认构造方法，因此以下语句会导致编译错误：<br />
Sample2 s=new Sample2(); //编译出错<br />
Sample3类显式定义了默认构造方法，因此以下语句是合法的。<br />
Sample3 s=new Sample3();</font></p>
<p><font color="#0000ff"><br />
</font><strong>11.2.3 子类调用父类的构造方法</strong></p>
<p><br />
父类的构造方法不能被子类继承。以下MyException类继承了java.lang.Exception类：<br />
</p>
<p>public class MyException extends Exception{} // MyException类只有一个隐含的默认构造方法<br />
</p>
<p>尽管在Exception类中定义了如下形式的构造方法：<br />
</p>
<p>public Exception(String msg)<br />
</p>
<p>但MyException类不会继承以上Exception类的构造方法，因此以下代码是不合法的：<br />
</p>
<p><font color="#0000ff">//编译出错，MyException类不存在这样的构造方法<br />
Exception e=new MyException("Something is error"); <br />
</font></p>
<p>在子类的构造方法中，可以通过super语句调用父类的构造方法。例如：<br />
<font color="#0000ff">public class MyException extends Exception{<br />
public MyException(){<br />
//调用Exception父类的Exception(String msg)构造方法<br />
super("Something is error");<br />
}</font></p>
<p><font color="#0000ff">public MyException(String msg){<br />
//调用Exception父类的Exception(String msg)构造方法<br />
super(msg);<br />
} <br />
}</font><br />
用super语句来调用父类的构造方法时，必须遵守以下语法规则。<br />
（1）在子类的构造方法中，不能直接通过父类方法名调用父类的构造方法，而是要使用super语句，以下代码是非法的：<br />
<font color="#0000ff">public MyException(String msg){<br />
Exception(msg); //编译错误<br />
}</font><br />
（2）假如在子类的构造方法中有super语句，它必须作为构造方法的第一条语句，以下代码是非法的：</p>
<p><br />
<font color="#0000ff">public MyException(){<br />
String msg= "Something wrong";<br />
super(msg); //编译错误，super语句必须作为构造方法的第一条语句<br />
}</font><br />
</p>
<p>在创建子类的对象时，Java虚拟机首先执行父类的构造方法，然后再执行子类的构造方法。在多级继承的情况下，将从继承树的最上层的父类开始，依次执行各个类的构造方法，这可以保证子类对象从所有直接或间接父类中继承的实例变量都被正确的初始化。例如以下Base父类和Sub子类分别有一个实例变量 a和b，当构造Sub实例时，这两个实例变量都会被初始化。<br />
</p>
<p><font color="#0000ff">public class Base{<br />
private int a;<br />
public Base(int a){ this.a=a;}<br />
public int getA(){return a;}<br />
}<br />
public class Sub extends Base{<br />
private int b;<br />
public Base(int a,int b){super(a); this.b=b;}<br />
public int getB(){return b;}<br />
<br />
public static void main(String args[]){<br />
Sub sub=new Sub(1,2);<br />
System.out.println("a="+sub.getA()+" b="+sub.getB()); //打印a=1 b=2<br />
}<br />
}<br />
</font>在以下例程11-3（Son.java）中，Son类继承Father类，Father类继承Grandpa类。这三个类都显式定义了默认的构造方法，此外还定义了一个带参数的构造方法。</p>
<p><br />
<font color="#0000ff">例程11-3 Son.java<br />
class Grandpa{<br />
protected Grandpa(){<br />
System.out.println("default Grandpa");<br />
}<br />
public Grandpa(String name){<br />
System.out.println(name);<br />
}<br />
}</font></p>
<p><font color="#0000ff">class Father extends Grandpa{<br />
protected Father(){<br />
System.out.println("default Father");<br />
}</font></p>
<p><font color="#0000ff">public Father(String grandpaName,String fatherName){<br />
super(grandpaName);<br />
System.out.println(fatherName);<br />
}<br />
}</font></p>
<p><font color="#0000ff">public class Son extends Father{<br />
public Son(){<br />
System.out.println("default Son");<br />
}</font></p>
<p><font color="#0000ff">public Son(String grandpaName,String fatherName,String sonName){<br />
super(grandpaName,fatherName);<br />
System.out.println(sonName);<br />
}</font></p>
<p><font color="#0000ff">public static void main(String args[]){<br />
Son s1= new Son("My Grandpa", "My Father", "My Son"); //①<br />
Son s2=new Son(); //②<br />
}<br />
}<br />
</font>执行以上main()方法的第①条语句，打印结果如下：<br />
My Grandpa<br />
My Father<br />
My Son<br />
</p>
<p>此时构造方法的执行顺序如图11-1所示。</p>
<p><img src="http://www.javathinker.org/java%5Cconstruct_1.gif" width="354" height="193"  alt="" /></p>
<p>图11-1 调用Son类的带参数的构造方法时所有构造方法的执行顺序<br />
</p>
<p>当子类的构造方法没有用super语句显式调用父类的构造方法，那么通过这个构造方法创建子类对象时，Java虚拟机会自动先调用父类的默认构造方法。执行以上Son类的main()方法的第②条语句，打印结果如下：<br />
default Grandpa<br />
default Father<br />
default Son<br />
</p>
<p>此时构造方法的执行顺序如图11-2所示。</p>
<p><img src="http://www.javathinker.org/java%5Cconstruct_2.gif" width="302" height="206"  alt="" /></p>
<p>图11-2 调用Son类的默认构造方法时所有构造方法的执行顺序<br />
</p>
<p>当子类的构造方法没有用super语句显式调用父类的构造方法，而父类又没有提供默认构造方法时，将会出现编译错误。例如把例程11-3做适当修改，删除Grandpa类中显式定义的默认构造方法：<br />
</p>
<p>// protected Grandpa(){<br />
// System.out.println("default GrandPa");<br />
// }<br />
</p>
<p>这样，Grandpa类就失去了默认构造方法，这时，在编译Father类的默认构造方法时，因为找不到Grandpa类的默认构造方法而编译出错。如果把Grandpa类的默认构造方法的protected访问级别改为private访问级别，也会导致编译错误，因为Father类的默认构造方法无法访问Grandpa类的私有默认构造方法。<br />
在以下例子中，子类Sub的默认构造方法没有通过super语句调用父类的构造方法，而是通过this语句调用了自身的另一个构造方法Sub(int i)，而在Sub(int i)中通过super语句调用了父类Base的Base(int i)构造方法。这样，无论通过Sub类的哪个构造方法来创建Sub实例，都会先调用父类Base的Base(int i)构造方法。<br />
</p>
<p><font color="#0000ff">class Base{<br />
Base(int i){System.out.println("call Base(int i)");}<br />
}<br />
public class Sub extends Base{<br />
Sub(){this(0); System.out.println("call Sub()");}</font></p>
<p><font color="#0000ff">Sub(int i){super(i); System.out.println("call Sub(int i)");}<br />
public static void main(String args[]){<br />
Sub sub=new Sub();<br />
}<br />
}<br />
</font>执行以上Sub类的main()方法的new Sub()语句，打印结果如下：<br />
call Base(int i)<br />
call Sub(int i)<br />
call Sub()<br />
此时构造方法的执行顺序如图11-3所示。<br />
</p>
<p><br />
图11-3 调用Sub类的默认构造方法时所有构造方法的执行顺序<br />
在下面的例子中，Base类中没有定义任何构造方法，它实际上有一个隐含的默认构造方法：<br />
Base(){}<br />
Sub类的Sub(int i)构造方法没有用super语句显式调用父类的构造方法，因此当创建Sub实例时，会先调用Base父类的隐含默认构造方法。 <br />
<font color="#0000ff">class Base{} //具有隐含默认构造方法<br />
public class Sub extends Base{<br />
Sub(int i){System.out.println(i);}<br />
public static void main(String args[]){<br />
System.out.println(new Sub(1)); //打印1<br />
}<br />
}</font><br />
<strong>11.2.4 构造方法的作用域</strong><br />
构造方法只能通过以下方式被调用：<br />
(1) 当前类的其他构造方法通过this语句调用它。<br />
(2) 当前类的子类的构造方法通过super语句调用它。<br />
(3)在程序中通过new语句调用它。<br />
对于例程11-4（Sub.java）的代码，请读者自己分析某些语句编译出错的原因。<br />
</p>
<p><font color="#0000ff">例程11-4 Sub.java<br />
class Base{<br />
public Base(int i,int j){}<br />
public Base(int i){<br />
this(i,0); //合法<br />
Base(i,0); //编译出错<br />
}<br />
}</font></p>
<p><font color="#0000ff">class Sub extends Base{<br />
public Sub(int i,int j){<br />
super(i,0); //合法<br />
}</font></p>
<p><font color="#0000ff">void method1(int i,int j){<br />
this(i,j); //编译出错<br />
Sub(i,j); //编译出错<br />
}<br />
void method2(int i,int j){<br />
super(i,j); //编译出错<br />
}<br />
void method3(int i,int j){<br />
Base s=new Base(0,0); //合法<br />
s.Base(0,0); //编译出错<br />
}<br />
}<br />
</font></p>
<p><strong>11.2.5 构造方法的访问级别</strong><br />
构造方法可以处于public、protected、默认和private这四种访问级别之一。本节着重介绍构造方法处于private级别的意义。<br />
当构造方法为private级别，意味着只能在当前类中访问它：在当前类的其他构造方法中可以通过this语句调用它，此外还可以在当前类的成员方法中通过new语句调用它。<br />
在以下场合之一，可以把类的所有构造方法都声明为private类型。<br />
（1）在这个类中仅仅包含了一些供其他程序调用的静态方法，没有任何实例方法。其他程序无需创建该类的实例，就能访问类的静态方法。例如 java.lang.Math类就符合这种情况，在Math类中提供了一系列用于数学运算的公共静态方法，为了禁止外部程序创建Math类的实例， Math类的惟一的构造方法是private类型的：<br />
</p>
<p>private Math(){}<br />
</p>
<p>在第7章的7.2节（abstract修饰符）提到过，abstract类型的类也不允许实例化。也许你会问，把Math类定义为如下abstract类型，不是也能禁止Math类被实例化吗？<br />
</p>
<p>public abstract class Math{&#8230;}<br />
</p>
<p>如果一个类是抽象类，意味着它是专门用于被继承的类，可以拥有子类，而且可以创建具体子类的实例。而JDK并不希望用户创建Math类的子类，在这种情况下，把类的构造方法定义为private类型更合适。<br />
</p>
<p>（2）禁止这个类被继承。当一个类的所有构造方法都是private类型，假如定义了它的子类，那么子类的构造方法无法调用父类的任何构造方法，因此会导致编译错误。在第7章的7.3.1节（final类）提到过，把一个类声明为final类型，也能禁止这个类被继承。这两者的区别是：<br />
1)如果一个类允许其他程序用new语句构造它的实例，但不允许拥有子类，那就把类声明为final类型。<br />
2)如果一个类既不允许其他程序用new语句构造它的实例，又不允许拥有子类，那就把类的所有构造方法声明为private类型。</p>
<p><br />
由于大多数类都允许其他程序用new语句构造它的实例，因此用final修饰符来禁止类被继承的做法更常见。<br />
</p>
<p>（3）这个类需要把构造自身实例的细节封装起来，不允许其他程序通过new语句创建这个类的实例，这个类向其他程序提供了获得自身实例的静态方法，这种方法称为静态工厂方法，本章第11.3节（静态工厂方法）对此作了进一步的介绍。</p>
 <img src ="http://www.blogjava.net/NewMoring/aggbug/268599.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/NewMoring/" target="_blank">清晨</a> 2009-05-02 12:34 <a href="http://www.blogjava.net/NewMoring/articles/268599.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>