﻿<?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-笔记-随笔分类-jdk</title><link>http://www.blogjava.net/yuxh/category/42717.html</link><description>way</description><language>zh-cn</language><lastBuildDate>Tue, 16 Jun 2015 15:19:25 GMT</lastBuildDate><pubDate>Tue, 16 Jun 2015 15:19:25 GMT</pubDate><ttl>60</ttl><item><title>Eclipse中项目maven依赖库错误</title><link>http://www.blogjava.net/yuxh/archive/2015/06/02/425441.html</link><dc:creator>yuxh</dc:creator><author>yuxh</author><pubDate>Tue, 02 Jun 2015 02:27:00 GMT</pubDate><guid>http://www.blogjava.net/yuxh/archive/2015/06/02/425441.html</guid><wfw:comment>http://www.blogjava.net/yuxh/comments/425441.html</wfw:comment><comments>http://www.blogjava.net/yuxh/archive/2015/06/02/425441.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/yuxh/comments/commentRss/425441.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/yuxh/services/trackbacks/425441.html</trackback:ping><description><![CDATA[新电脑装上eclipse4.4.2，导入maven项目之后，依赖库总是有很多错误。最后搜索到可能是eclipse的bug（据说是<code>JAVA_HOME没有正确传递</code>），查看到eclipse默认的是安装的jre目录，修改到jdk目录下，依赖问题解决。<br />不过目前版本仍然没有解决pom文件的&#8220;Plugin execution not covered by lifecycle configuration&#8221;错误，暂时忽略不管吧。<img src ="http://www.blogjava.net/yuxh/aggbug/425441.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/yuxh/" target="_blank">yuxh</a> 2015-06-02 10:27 <a href="http://www.blogjava.net/yuxh/archive/2015/06/02/425441.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>JAVA调用重写的祖父方法</title><link>http://www.blogjava.net/yuxh/archive/2012/05/31/379647.html</link><dc:creator>yuxh</dc:creator><author>yuxh</author><pubDate>Thu, 31 May 2012 03:23:00 GMT</pubDate><guid>http://www.blogjava.net/yuxh/archive/2012/05/31/379647.html</guid><wfw:comment>http://www.blogjava.net/yuxh/comments/379647.html</wfw:comment><comments>http://www.blogjava.net/yuxh/archive/2012/05/31/379647.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/yuxh/comments/commentRss/379647.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/yuxh/services/trackbacks/379647.html</trackback:ping><description><![CDATA[本打算继承一个API中的Parent类（Parent继承自GrandParent类），重写其中的service方法，copy了Parent的service方法。不过发现Parent的service中也有super.service方法。当时考虑直接调用GrandParent的service方法。。。未遂（包括反射也不行）。正好看到老外写的一篇文章，翻译：<br />在Son类里面写一个test方法：<br /> <div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--><span style="color: #0000FF; ">public</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">void</span><span style="color: #000000; ">&nbsp;test()&nbsp;{<br />&nbsp;</span><span style="color: #0000FF; ">super</span><span style="color: #000000; ">.test();<br />&nbsp;</span><span style="color: #0000FF; ">this</span><span style="color: #000000; ">.test();<br />}<br />反编译之后：<br /></span><span style="color: #0000FF; ">public</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">void</span><span style="color: #000000; ">&nbsp;test()<br />&nbsp;&nbsp;&nbsp;&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000; ">//</span><span style="color: #008000; ">&nbsp;&nbsp;&nbsp;&nbsp;0&nbsp;&nbsp;&nbsp;&nbsp;0:aload_0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br />&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000; ">//</span><span style="color: #008000; ">&nbsp;&nbsp;&nbsp;&nbsp;1&nbsp;&nbsp;&nbsp;&nbsp;1:invokespecial&nbsp;&nbsp;&nbsp;#2&nbsp;&nbsp;&nbsp;&lt;Method&nbsp;void&nbsp;Parent.test()&gt;<br />&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000; ">//</span><span style="color: #008000; ">&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;&nbsp;4:aload_0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br />&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000; ">//</span><span style="color: #008000; ">&nbsp;&nbsp;&nbsp;&nbsp;3&nbsp;&nbsp;&nbsp;&nbsp;5:invokevirtual&nbsp;&nbsp;&nbsp;#3&nbsp;&nbsp;&nbsp;&lt;Method&nbsp;void&nbsp;test()&gt;<br />&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000; ">//</span><span style="color: #008000; ">&nbsp;&nbsp;&nbsp;&nbsp;4&nbsp;&nbsp;&nbsp;&nbsp;8:return&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000; "><br /></span><span style="color: #000000; ">&nbsp;&nbsp;&nbsp;&nbsp;}</span></div>使用ASM可以完成对GrandParent方法的调用<br /><div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--><span style="color: #0000FF; ">public</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">class</span><span style="color: #000000; ">&nbsp;GrandParent&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">public</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">void</span><span style="color: #000000; ">&nbsp;test()&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;System.out.println(</span><span style="color: #000000; ">"</span><span style="color: #000000; ">test&nbsp;of&nbsp;GrandParent</span><span style="color: #000000; ">"</span><span style="color: #000000; ">);<br />&nbsp;&nbsp;&nbsp;&nbsp;}<br />}<br /><br /></span><span style="color: #0000FF; ">public</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">class</span><span style="color: #000000; ">&nbsp;Parent&nbsp;</span><span style="color: #0000FF; ">extends</span><span style="color: #000000; ">&nbsp;GrandParent{<br />&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">public</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">void</span><span style="color: #000000; ">&nbsp;test()&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;System.out.println(</span><span style="color: #000000; ">"</span><span style="color: #000000; ">test&nbsp;of&nbsp;Parent</span><span style="color: #000000; ">"</span><span style="color: #000000; ">);<br />&nbsp;&nbsp;&nbsp;&nbsp;}<br />}<br /><br /></span><span style="color: #0000FF; ">public</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">class</span><span style="color: #000000; ">&nbsp;Son&nbsp;</span><span style="color: #0000FF; ">extends</span><span style="color: #000000; ">&nbsp;Parent{<br />&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">public</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">void</span><span style="color: #000000; ">&nbsp;test()&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;System.out.println(</span><span style="color: #000000; ">"</span><span style="color: #000000; ">test&nbsp;of&nbsp;Son</span><span style="color: #000000; ">"</span><span style="color: #000000; ">);<br />&nbsp;&nbsp;&nbsp;&nbsp;}<br />}</span></div>调用Son实例的test方法只会执行Son的test方法。而ASM可以修改class，先写一个Example类继承Son，重写test方法<br /><br /><div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--><span style="color: #008080; ">&nbsp;1</span>&nbsp;<span style="color: #0000FF; ">import</span><span style="color: #000000; ">&nbsp;java.io.FileOutputStream;<br /></span><span style="color: #008080; ">&nbsp;2</span>&nbsp;<span style="color: #000000; ">&nbsp;<br /></span><span style="color: #008080; ">&nbsp;3</span>&nbsp;<span style="color: #000000; "></span><span style="color: #0000FF; ">import</span><span style="color: #000000; ">&nbsp;org.objectweb.asm.ClassWriter;<br /></span><span style="color: #008080; ">&nbsp;4</span>&nbsp;<span style="color: #000000; "></span><span style="color: #0000FF; ">import</span><span style="color: #000000; ">&nbsp;org.objectweb.asm.MethodVisitor;<br /></span><span style="color: #008080; ">&nbsp;5</span>&nbsp;<span style="color: #000000; "></span><span style="color: #0000FF; ">import</span><span style="color: #000000; ">&nbsp;org.objectweb.asm.Opcodes;<br /></span><span style="color: #008080; ">&nbsp;6</span>&nbsp;<span style="color: #000000; ">&nbsp;<br /></span><span style="color: #008080; ">&nbsp;7</span>&nbsp;<span style="color: #000000; "></span><span style="color: #0000FF; ">public</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">class</span><span style="color: #000000; ">&nbsp;ASMByteCodeManipulation&nbsp;</span><span style="color: #0000FF; ">extends</span><span style="color: #000000; ">&nbsp;ClassLoader&nbsp;</span><span style="color: #0000FF; ">implements</span><span style="color: #000000; ">&nbsp;Opcodes&nbsp;{<br /></span><span style="color: #008080; ">&nbsp;8</span>&nbsp;<span style="color: #000000; ">&nbsp;<br /></span><span style="color: #008080; ">&nbsp;9</span>&nbsp;<span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">public</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">static</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">void</span><span style="color: #000000; ">&nbsp;main(String&nbsp;args[])&nbsp;</span><span style="color: #0000FF; ">throws</span><span style="color: #000000; ">&nbsp;Exception&nbsp;{<br /></span><span style="color: #008080; ">10</span>&nbsp;<span style="color: #000000; ">&nbsp;&nbsp;ClassWriter&nbsp;cw&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">new</span><span style="color: #000000; ">&nbsp;ClassWriter(</span><span style="color: #000000; ">0</span><span style="color: #000000; ">);<br /></span><span style="color: #008080; ">11</span>&nbsp;<span style="color: #000000; ">&nbsp;&nbsp;cw.visit(V1_1,&nbsp;ACC_PUBLIC,&nbsp;</span><span style="color: #000000; ">"</span><span style="color: #000000; ">Example</span><span style="color: #000000; ">"</span><span style="color: #000000; ">,&nbsp;</span><span style="color: #0000FF; ">null</span><span style="color: #000000; ">,&nbsp;</span><span style="color: #000000; ">"</span><span style="color: #000000; ">Son</span><span style="color: #000000; ">"</span><span style="color: #000000; ">,&nbsp;</span><span style="color: #0000FF; ">null</span><span style="color: #000000; ">);<br /></span><span style="color: #008080; ">12</span>&nbsp;<span style="color: #000000; ">&nbsp;<br /></span><span style="color: #008080; ">13</span>&nbsp;<span style="color: #000000; ">&nbsp;&nbsp;</span><span style="color: #008000; ">//</span><span style="color: #008000; ">&nbsp;creates&nbsp;a&nbsp;MethodWriter&nbsp;for&nbsp;the&nbsp;(implicit)&nbsp;constructor</span><span style="color: #008000; "><br /></span><span style="color: #008080; ">14</span>&nbsp;<span style="color: #008000; "></span><span style="color: #000000; ">&nbsp;&nbsp;MethodVisitor&nbsp;mw&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;cw.visitMethod(ACC_PUBLIC,&nbsp;</span><span style="color: #000000; ">"</span><span style="color: #000000; ">&lt;init&gt;</span><span style="color: #000000; ">"</span><span style="color: #000000; ">,&nbsp;</span><span style="color: #000000; ">"</span><span style="color: #000000; ">()V</span><span style="color: #000000; ">"</span><span style="color: #000000; ">,&nbsp;</span><span style="color: #0000FF; ">null</span><span style="color: #000000; ">,</span><span style="color: #0000FF; ">null</span><span style="color: #000000; ">);<br /></span><span style="color: #008080; ">15</span>&nbsp;<span style="color: #000000; ">&nbsp;&nbsp;mw.visitVarInsn(ALOAD,&nbsp;</span><span style="color: #000000; ">0</span><span style="color: #000000; ">);<br /></span><span style="color: #008080; ">16</span>&nbsp;<span style="color: #000000; ">&nbsp;&nbsp;mw.visitMethodInsn(INVOKESPECIAL,&nbsp;</span><span style="color: #000000; ">"</span><span style="color: #000000; ">Son</span><span style="color: #000000; ">"</span><span style="color: #000000; ">,&nbsp;</span><span style="color: #000000; ">"</span><span style="color: #000000; ">&lt;init&gt;</span><span style="color: #000000; ">"</span><span style="color: #000000; ">,&nbsp;</span><span style="color: #000000; ">"</span><span style="color: #000000; ">()V</span><span style="color: #000000; ">"</span><span style="color: #000000; ">);<br /></span><span style="color: #008080; ">17</span>&nbsp;<span style="color: #000000; ">&nbsp;&nbsp;mw.visitInsn(RETURN);<br /></span><span style="color: #008080; ">18</span>&nbsp;<span style="color: #000000; ">&nbsp;&nbsp;mw.visitMaxs(</span><span style="color: #000000; ">1</span><span style="color: #000000; ">,&nbsp;</span><span style="color: #000000; ">1</span><span style="color: #000000; ">);<br /></span><span style="color: #008080; ">19</span>&nbsp;<span style="color: #000000; ">&nbsp;&nbsp;mw.visitEnd();<br /></span><span style="color: #008080; ">20</span>&nbsp;<span style="color: #000000; ">&nbsp;<br /></span><span style="color: #008080; ">21</span>&nbsp;<span style="color: #000000; ">&nbsp;&nbsp;</span><span style="color: #008000; ">//</span><span style="color: #008000; ">&nbsp;creates&nbsp;a&nbsp;MethodWriter&nbsp;for&nbsp;the&nbsp;'test'&nbsp;method</span><span style="color: #008000; "><br /></span><span style="color: #008080; ">22</span>&nbsp;<span style="color: #008000; "></span><span style="color: #000000; ">&nbsp;&nbsp;mw&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;cw.visitMethod(ACC_PUBLIC,&nbsp;</span><span style="color: #000000; ">"</span><span style="color: #000000; ">test</span><span style="color: #000000; ">"</span><span style="color: #000000; ">,&nbsp;</span><span style="color: #000000; ">"</span><span style="color: #000000; ">()V</span><span style="color: #000000; ">"</span><span style="color: #000000; ">,&nbsp;</span><span style="color: #0000FF; ">null</span><span style="color: #000000; ">,&nbsp;</span><span style="color: #0000FF; ">null</span><span style="color: #000000; ">);<br /></span><span style="color: #008080; ">23</span>&nbsp;<span style="color: #000000; ">&nbsp;&nbsp;mw.visitFieldInsn(GETSTATIC,&nbsp;</span><span style="color: #000000; ">"</span><span style="color: #000000; ">java/lang/System</span><span style="color: #000000; ">"</span><span style="color: #000000; ">,&nbsp;</span><span style="color: #000000; ">"</span><span style="color: #000000; ">out</span><span style="color: #000000; ">"</span><span style="color: #000000; ">,</span><span style="color: #000000; ">"</span><span style="color: #000000; ">Ljava/io/PrintStream;</span><span style="color: #000000; ">"</span><span style="color: #000000; ">);<br /></span><span style="color: #008080; ">24</span>&nbsp;<span style="color: #000000; ">&nbsp;&nbsp;mw.visitLdcInsn(</span><span style="color: #000000; ">"</span><span style="color: #000000; ">test&nbsp;of&nbsp;AI3</span><span style="color: #000000; ">"</span><span style="color: #000000; ">);<br /></span><span style="color: #008080; ">25</span>&nbsp;<span style="color: #000000; ">&nbsp;&nbsp;mw.visitMethodInsn(INVOKEVIRTUAL,&nbsp;</span><span style="color: #000000; ">"</span><span style="color: #000000; ">java/io/PrintStream</span><span style="color: #000000; ">"</span><span style="color: #000000; ">,&nbsp;</span><span style="color: #000000; ">"</span><span style="color: #000000; ">println</span><span style="color: #000000; ">"</span><span style="color: #000000; ">,<br /></span><span style="color: #008080; ">26</span>&nbsp;<span style="color: #000000; ">&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #000000; ">"</span><span style="color: #000000; ">(Ljava/lang/String;)V</span><span style="color: #000000; ">"</span><span style="color: #000000; ">);<br /></span><span style="color: #008080; ">27</span>&nbsp;<span style="color: #000000; ">&nbsp;&nbsp;</span><span style="color: #008000; ">//</span><span style="color: #008000; ">Call&nbsp;test()&nbsp;of&nbsp;GrandParent</span><span style="color: #008000; "><br /></span><span style="color: #008080; ">28</span>&nbsp;<span style="color: #008000; "></span><span style="color: #000000; ">&nbsp;&nbsp;mw.visitVarInsn(ALOAD,&nbsp;</span><span style="color: #000000; ">0</span><span style="color: #000000; ">);<br /></span><span style="color: #008080; ">29</span>&nbsp;<span style="color: #000000; ">&nbsp;&nbsp;mw.visitMethodInsn(INVOKESPECIAL,&nbsp;</span><span style="color: #000000; ">"</span><span style="color: #000000; ">GrandParent</span><span style="color: #000000; ">"</span><span style="color: #000000; ">,&nbsp;</span><span style="color: #000000; ">"</span><span style="color: #000000; ">test</span><span style="color: #000000; ">"</span><span style="color: #000000; ">,&nbsp;</span><span style="color: #000000; ">"</span><span style="color: #000000; ">()V</span><span style="color: #000000; ">"</span><span style="color: #000000; ">);<br /></span><span style="color: #008080; ">30</span>&nbsp;<span style="color: #000000; ">&nbsp;&nbsp;</span><span style="color: #008000; ">//</span><span style="color: #008000; ">Call&nbsp;test()&nbsp;of&nbsp;GrandParent</span><span style="color: #008000; "><br /></span><span style="color: #008080; ">31</span>&nbsp;<span style="color: #008000; "></span><span style="color: #000000; ">&nbsp;&nbsp;mw.visitVarInsn(ALOAD,&nbsp;</span><span style="color: #000000; ">0</span><span style="color: #000000; ">);<br /></span><span style="color: #008080; ">32</span>&nbsp;<span style="color: #000000; ">&nbsp;&nbsp;mw.visitMethodInsn(INVOKESPECIAL,&nbsp;</span><span style="color: #000000; ">"</span><span style="color: #000000; ">Parent</span><span style="color: #000000; ">"</span><span style="color: #000000; ">,&nbsp;</span><span style="color: #000000; ">"</span><span style="color: #000000; ">test</span><span style="color: #000000; ">"</span><span style="color: #000000; ">,&nbsp;</span><span style="color: #000000; ">"</span><span style="color: #000000; ">()V</span><span style="color: #000000; ">"</span><span style="color: #000000; ">);<br /></span><span style="color: #008080; ">33</span>&nbsp;<span style="color: #000000; ">&nbsp;&nbsp;</span><span style="color: #008000; ">//</span><span style="color: #008000; ">Call&nbsp;test()&nbsp;of&nbsp;GrandParent</span><span style="color: #008000; "><br /></span><span style="color: #008080; ">34</span>&nbsp;<span style="color: #008000; "></span><span style="color: #000000; ">&nbsp;&nbsp;mw.visitVarInsn(ALOAD,&nbsp;</span><span style="color: #000000; ">0</span><span style="color: #000000; ">);<br /></span><span style="color: #008080; ">35</span>&nbsp;<span style="color: #000000; ">&nbsp;&nbsp;mw.visitMethodInsn(INVOKESPECIAL,&nbsp;</span><span style="color: #000000; ">"</span><span style="color: #000000; ">Son</span><span style="color: #000000; ">"</span><span style="color: #000000; ">,&nbsp;</span><span style="color: #000000; ">"</span><span style="color: #000000; ">test</span><span style="color: #000000; ">"</span><span style="color: #000000; ">,&nbsp;</span><span style="color: #000000; ">"</span><span style="color: #000000; ">()V</span><span style="color: #000000; ">"</span><span style="color: #000000; ">);<br /></span><span style="color: #008080; ">36</span>&nbsp;<span style="color: #000000; ">&nbsp;&nbsp;mw.visitInsn(RETURN);<br /></span><span style="color: #008080; ">37</span>&nbsp;<span style="color: #000000; ">&nbsp;&nbsp;mw.visitMaxs(</span><span style="color: #000000; ">2</span><span style="color: #000000; ">,&nbsp;</span><span style="color: #000000; ">1</span><span style="color: #000000; ">);<br /></span><span style="color: #008080; ">38</span>&nbsp;<span style="color: #000000; ">&nbsp;&nbsp;mw.visitEnd();<br /></span><span style="color: #008080; ">39</span>&nbsp;<span style="color: #000000; ">&nbsp;<br /></span><span style="color: #008080; ">40</span>&nbsp;<span style="color: #000000; ">&nbsp;&nbsp;</span><span style="color: #0000FF; ">byte</span><span style="color: #000000; ">[]&nbsp;code&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;cw.toByteArray();<br /></span><span style="color: #008080; ">41</span>&nbsp;<span style="color: #000000; ">&nbsp;&nbsp;FileOutputStream&nbsp;fos&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">new</span><span style="color: #000000; ">&nbsp;FileOutputStream(</span><span style="color: #000000; ">"</span><span style="color: #000000; ">Example.class</span><span style="color: #000000; ">"</span><span style="color: #000000; ">);<br /></span><span style="color: #008080; ">42</span>&nbsp;<span style="color: #000000; ">&nbsp;&nbsp;fos.write(code);<br /></span><span style="color: #008080; ">43</span>&nbsp;<span style="color: #000000; ">&nbsp;&nbsp;fos.close();<br /></span><span style="color: #008080; ">44</span>&nbsp;<span style="color: #000000; ">&nbsp;<br /></span><span style="color: #008080; ">45</span>&nbsp;<span style="color: #000000; ">&nbsp;&nbsp;ASMByteCodeManipulation&nbsp;loader&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">new</span><span style="color: #000000; ">&nbsp;ASMByteCodeManipulation();<br /></span><span style="color: #008080; ">46</span>&nbsp;<span style="color: #000000; ">&nbsp;&nbsp;Class</span><span style="color: #000000; ">&lt;?&gt;</span><span style="color: #000000; ">&nbsp;exampleClass&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;loader.defineClass(</span><span style="color: #000000; ">"</span><span style="color: #000000; ">Example</span><span style="color: #000000; ">"</span><span style="color: #000000; ">,&nbsp;code,&nbsp;</span><span style="color: #000000; ">0</span><span style="color: #000000; ">,<br /></span><span style="color: #008080; ">47</span>&nbsp;<span style="color: #000000; ">&nbsp;&nbsp;&nbsp;&nbsp;code.length);<br /></span><span style="color: #008080; ">48</span>&nbsp;<span style="color: #000000; ">&nbsp;&nbsp;Object&nbsp;obj&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;exampleClass.newInstance();<br /></span><span style="color: #008080; ">49</span>&nbsp;<span style="color: #000000; ">&nbsp;&nbsp;exampleClass.getMethod(</span><span style="color: #000000; ">"</span><span style="color: #000000; ">test</span><span style="color: #000000; ">"</span><span style="color: #000000; ">,&nbsp;</span><span style="color: #0000FF; ">null</span><span style="color: #000000; ">).invoke(obj,&nbsp;</span><span style="color: #0000FF; ">null</span><span style="color: #000000; ">);<br /></span><span style="color: #008080; ">50</span>&nbsp;<span style="color: #000000; ">&nbsp;<br /></span><span style="color: #008080; ">51</span>&nbsp;<span style="color: #000000; ">&nbsp;}<br /></span><span style="color: #008080; ">52</span>&nbsp;<span style="color: #000000;">}</span></div>输出：<br /><div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--><span style="color: #000000; ">test&nbsp;of&nbsp;AI3<br />test&nbsp;of&nbsp;GrandParent<br />test&nbsp;of&nbsp;Parent<br />test&nbsp;of&nbsp;Son</span></div>看看怎样实现的，11行定义一个新的类Example继承Son。22行，Example重写test方法，先打印&#8220;test of  AI3&#8221;，再分别在29、32、35行调用<span style="color: #000000; ">GrandParent、Parent、Son的test方法。</span><div></div><div>&nbsp;main方法中，45行创建Example的实例，再用反射调他的test方法。<br />使用invokespecial这种方式也有局限，只能从子类调用。否则报错：<div><strong>Exception in thread "main" java.lang.VerifyError: (class: Example,  method: test1 signature: (LAI2;)V) Illegal use of nonvirtual function  call</strong></div></div><img src ="http://www.blogjava.net/yuxh/aggbug/379647.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/yuxh/" target="_blank">yuxh</a> 2012-05-31 11:23 <a href="http://www.blogjava.net/yuxh/archive/2012/05/31/379647.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Java时区处理</title><link>http://www.blogjava.net/yuxh/archive/2012/03/15/371982.html</link><dc:creator>yuxh</dc:creator><author>yuxh</author><pubDate>Thu, 15 Mar 2012 15:08:00 GMT</pubDate><guid>http://www.blogjava.net/yuxh/archive/2012/03/15/371982.html</guid><wfw:comment>http://www.blogjava.net/yuxh/comments/371982.html</wfw:comment><comments>http://www.blogjava.net/yuxh/archive/2012/03/15/371982.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/yuxh/comments/commentRss/371982.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/yuxh/services/trackbacks/371982.html</trackback:ping><description><![CDATA[<div><div>通用协调时(UTC, Universal Time Coordinated)，格林尼治平均时(GMT, Greenwich Mean Time) 由于历史原因，这两个时间是一样的。<div>北京时区是东八区，领先UTC八个小时，在电子邮件信头的Date域记为+0800。<div>转换中，最重要的公式就是：<br />UTC ＋ 时区差 ＝ 本地时间</div></div></div></div><div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br />
<br />
Code highlighting produced by Actipro CodeHighlighter (freeware)<br />
http://www.CodeHighlighter.com/<br />
<br />
--><span style="color: #000000; ">&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">public</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">static</span><span style="color: #000000; ">&nbsp;Calendar&nbsp;convertToGmt(Calendar&nbsp;cal)&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Date&nbsp;date&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;cal.getTime();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;TimeZone&nbsp;tz&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;cal.getTimeZone();<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;System.out.println(</span><span style="color: #000000; ">"</span><span style="color: #000000; ">input&nbsp;calendar&nbsp;has&nbsp;date&nbsp;[</span><span style="color: #000000; ">"</span><span style="color: #000000; ">&nbsp;</span><span style="color: #000000; ">+</span><span style="color: #000000; ">&nbsp;date&nbsp;</span><span style="color: #000000; ">+</span><span style="color: #000000; ">&nbsp;</span><span style="color: #000000; ">"</span><span style="color: #000000; ">]</span><span style="color: #000000; ">"</span><span style="color: #000000; ">);<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000; ">//</span><span style="color: #008000; ">&nbsp;Returns&nbsp;the&nbsp;number&nbsp;of&nbsp;milliseconds&nbsp;since&nbsp;January&nbsp;1,&nbsp;1970,&nbsp;00:00:00 GMT<br />
</span><span style="color: #008000; "></span><span style="color: #008000; "></span><span style="color: #000000; ">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="color: #0000FF; ">long</span><span style="color: #000000; ">&nbsp;msFromEpochGmt&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;date.getTime();<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000; ">//</span><span style="color: #008000; ">&nbsp;gives&nbsp;you&nbsp;the&nbsp;current&nbsp;offset&nbsp;in&nbsp;ms&nbsp;from&nbsp;GMT&nbsp;at&nbsp;the&nbsp;current&nbsp;date</span><span style="color: #008000; "><br />
</span><span style="color: #000000; ">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">int</span><span style="color: #000000; ">&nbsp;offsetFromUTC&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;tz.getOffset(msFromEpochGmt);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;System.out.println(</span><span style="color: #000000; ">"</span><span style="color: #000000; ">offset&nbsp;is&nbsp;</span><span style="color: #000000; ">"</span><span style="color: #000000; ">&nbsp;</span><span style="color: #000000; ">+</span><span style="color: #000000; ">&nbsp;offsetFromUTC);<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000; ">//</span><span style="color: #008000; ">&nbsp;create&nbsp;a&nbsp;new&nbsp;calendar&nbsp;in&nbsp;GMT&nbsp;timezone,&nbsp;set&nbsp;to&nbsp;this&nbsp;date&nbsp;and&nbsp;add&nbsp;the offset&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="color: #008000; "></span><span style="color: #008000; "></span><span style="color: #008000; "><br />
</span><span style="color: #000000; ">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Calendar&nbsp;gmtCal&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;Calendar.getInstance(TimeZone.getTimeZone(</span><span style="color: #000000; ">"</span><span style="color: #000000; ">GMT</span><span style="color: #000000; ">"</span><span style="color: #000000; ">));<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Calendar&nbsp;utcCal&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;Calendar.getInstance(TimeZone.getTimeZone(</span><span style="color: #000000; ">"</span><span style="color: #000000; ">UTC</span><span style="color: #000000; ">"</span><span style="color: #000000; ">));<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;gmtCal.setTime(date);<br /><div>&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; <span style="color: #000000; ">//根据东西时区，选择offsetFromUTC为正或负数</span></div>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; gmtCal.add(Calendar.MILLISECOND,&nbsp;offsetFromUTC);<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;utcCal.setTime(date);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;utcCal.add(Calendar.MILLISECOND,&nbsp;offsetFromUTC);<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;System.out.println(</span><span style="color: #000000; ">"</span><span style="color: #000000; ">Created&nbsp;GMT&nbsp;cal&nbsp;with&nbsp;date&nbsp;[</span><span style="color: #000000; ">"</span><span style="color: #000000; ">&nbsp;</span><span style="color: #000000; ">+</span><span style="color: #000000; ">&nbsp;gmtCal.getTime()<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #000000; ">+</span><span style="color: #000000; ">&nbsp;</span><span style="color: #000000; ">"</span><span style="color: #000000; ">==</span><span style="color: #000000; ">"</span><span style="color: #000000; ">&nbsp;</span><span style="color: #000000; ">+</span><span style="color: #000000; ">&nbsp;utcCal.getTime()&nbsp;</span><span style="color: #000000; ">+</span><span style="color: #000000; ">&nbsp;</span><span style="color: #000000; ">"</span><span style="color: #000000; ">]</span><span style="color: #000000; ">"</span><span style="color: #000000; ">);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">return</span><span style="color: #000000;">&nbsp;gmtCal;<br />
&nbsp;&nbsp;&nbsp;&nbsp;}<br />
</span></div><img src ="http://www.blogjava.net/yuxh/aggbug/371982.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/yuxh/" target="_blank">yuxh</a> 2012-03-15 23:08 <a href="http://www.blogjava.net/yuxh/archive/2012/03/15/371982.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>区分getPath(), getAbsolutePath(), getCanonicalPath()</title><link>http://www.blogjava.net/yuxh/archive/2011/06/24/352951.html</link><dc:creator>yuxh</dc:creator><author>yuxh</author><pubDate>Fri, 24 Jun 2011 05:42:00 GMT</pubDate><guid>http://www.blogjava.net/yuxh/archive/2011/06/24/352951.html</guid><wfw:comment>http://www.blogjava.net/yuxh/comments/352951.html</wfw:comment><comments>http://www.blogjava.net/yuxh/archive/2011/06/24/352951.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/yuxh/comments/commentRss/352951.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/yuxh/services/trackbacks/352951.html</trackback:ping><description><![CDATA[<div><p>来自<div>http://stackoverflow.com/questions/1099300/whats-the-difference-between-getpath-getabsolutepath-and-getcanonicalpath</div><br /></p><p>C:\temp\file.txt" - this is a path, an absolute path, a canonical path</p>  <p>.\file.txt This is a path, It's not an absolute path nor canonical path.</p>  <p><code>C:\temp\myapp\bin\..\\..\file.txt</code> This is a path, and an absolute path, it's not a canonical path</p>  <p>Canonical path is always an absolute path.</p></div><div>Converting from a path to a canonical path makes it absolute (通常会处理改变当前目录，所以像. ./file.txt 变为c:/temp/file.txt). The  canonical path of a file just "purifies" the  path, 去除和解析类似&#8220; ..\&#8221; and resolving symlinks(on  unixes)</div><div><div><p>In short:</p>  <ul><li>getPath() gets the path string that the File object was constructed with, and it may be relative current directory.</li><li>getAbsolutePath() gets the path string after resolving it against  the current directory if it's relative, resulting in a fully qualified  path.</li><li>getCanonicalPath() gets the path string after resolving any relative  path against current directory, and removes any relative pathing (. and  ..), and any file system links to return a path which the file system  considers the canonical means to reference the file system object to  which it points.</li></ul>  <p>Also, each of this has a File equivalent which returns the corresponding File object.</p> </div></div><div><p>The best way I have found to get a feel for things like this is to try them out:</p>  <pre prettyprint=""><code>import java.io.File;<br />public class PathTesting {<br />&nbsp; &nbsp; &nbsp; &nbsp; public static void main(String [] args) {<br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; File f = new File("test/.././file.txt");<br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; System.out.println(f.getPath());<br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; System.out.println(f.getAbsolutePath());<br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; try {<br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; System.out.println(f.getCanonicalPath());<br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }<br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; catch(Exception e) {}<br />&nbsp; &nbsp; &nbsp; &nbsp; }<br />}<br /></code></pre>  <p>Your output will be something like:</p>  <pre prettyprint=""><code>test\..\.\file.txt<br />C:\projects\sandbox\trunk\test\..\.\file.txt<br />C:\projects\sandbox\trunk\file.txt<br /></code></pre>  <p>So, <code>getPath()</code> gives you the path based on the File object, which may or may not be relative; <code>getAbsolutePath()</code> gives you an absolute path to the file; and <code>getCanonicalPath()</code>  gives you the unique absolute path to the file. Notice that there are a  huge number of absolute paths that point to the same file, but only one  canonical path.</p>  When to use each? Depends on what you're trying to accomplish, but if you were trying to see if two <code>Files</code> are pointing at the same file on disk, you could compare their canonical paths.</div><img src ="http://www.blogjava.net/yuxh/aggbug/352951.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/yuxh/" target="_blank">yuxh</a> 2011-06-24 13:42 <a href="http://www.blogjava.net/yuxh/archive/2011/06/24/352951.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>路径斜杠处理</title><link>http://www.blogjava.net/yuxh/archive/2011/06/15/352358.html</link><dc:creator>yuxh</dc:creator><author>yuxh</author><pubDate>Wed, 15 Jun 2011 06:47:00 GMT</pubDate><guid>http://www.blogjava.net/yuxh/archive/2011/06/15/352358.html</guid><wfw:comment>http://www.blogjava.net/yuxh/comments/352358.html</wfw:comment><comments>http://www.blogjava.net/yuxh/archive/2011/06/15/352358.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/yuxh/comments/commentRss/352358.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/yuxh/services/trackbacks/352358.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: &nbsp;&nbsp;<a href='http://www.blogjava.net/yuxh/archive/2011/06/15/352358.html'>阅读全文</a><img src ="http://www.blogjava.net/yuxh/aggbug/352358.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/yuxh/" target="_blank">yuxh</a> 2011-06-15 14:47 <a href="http://www.blogjava.net/yuxh/archive/2011/06/15/352358.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Java Puzzlers（三-二 循环）</title><link>http://www.blogjava.net/yuxh/archive/2010/11/15/338121.html</link><dc:creator>yuxh</dc:creator><author>yuxh</author><pubDate>Mon, 15 Nov 2010 11:59:00 GMT</pubDate><guid>http://www.blogjava.net/yuxh/archive/2010/11/15/338121.html</guid><wfw:comment>http://www.blogjava.net/yuxh/comments/338121.html</wfw:comment><comments>http://www.blogjava.net/yuxh/archive/2010/11/15/338121.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/yuxh/comments/commentRss/338121.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/yuxh/services/trackbacks/338121.html</trackback:ping><description><![CDATA[<p>31 <br>while (i != 0)<br>&nbsp;&nbsp;&nbsp; i &gt;&gt;&gt;= 1; //无符号右移，不管正负左边都是补0<br>为了表达式合法，这里的i必须是整型(<tt>byte</tt>, <tt>char</tt>, <tt>short</tt>, <tt>int</tt>, or <tt>long</tt>)。谜题的关键在于<tt>&gt;&gt;&gt;=</tt> 是一个复合赋值操作符，不幸的是复合赋值操作符会默默的做<span class="docEmphasis">narrowing primitive conversions，即从一个数据类型转换为一个更小的数据类型。<span class="docEmphStrong">Narrowing primitive conversions can lose information about the magnitude or precision of numeric values</span>。为了使问题更具体，假设这样定义：<br>short i = -1; 因为初始值i (<tt>(short)0xffff</tt>) 非零，循环执行。第一步位移会把i提升为int。<tt>short</tt>, <tt>byte</tt>, or <tt>char</tt>类型的操作数都会做这样的操作。这是<span class="docEmphasis">widening</span> primitive conversion，没有信息丢失。这种提升有符号扩展，因此结果是int值<tt>0xffffffff</tt>。无符号右移一位产生int值<tt>0x7fffffff</tt>。为了把int值存回short变量，Java执行了可怕的narrowing primitive conversion,即简单去掉高十六位。这样又变回了<tt>(short)0xffff</tt>。如果定义类似<tt>short</tt> or <tt>byte</tt>型的负数，都会得到类似结果。你如果定义的是char话，则不会无限循环，因为char值非负，位移之前的宽扩展不会做符号扩展。<br><br>总结：<span style="color: red">不要在<tt>short</tt>, <tt>byte</tt>, or <tt>char</tt>变量上使用复合赋值操作符</span>。这种表达式进行混合类型计算，非常容易混淆。更糟糕的是隐含的窄映射会丢掉信息。<br><br>32 <br>while (i &lt;= j &amp;&amp; j &lt;= i &amp;&amp; i != j) {}<br><tt>i &lt;= j</tt> and <tt>j &lt;= i</tt>, surely <tt>i</tt> must equal <tt>j</tt>？对于实数来说是这样的。他非常重要，有个名称：The ≤ relation on the real numbers is said to be <span class="docEmphasis">antisymmetric。Java's <tt>&lt;=</tt> operator used to be antisymmetric before release 5.0, but no longer.Java 5之前数据比较符号(<tt>&lt;, &lt;=</tt>, <tt>&gt;</tt>, and <tt>&gt;=</tt>) 需要两边的操作数必须为基础类型(<tt>byte</tt>, <tt>char</tt>, <tt>short</tt>, <tt>int</tt>, <tt>long</tt>, <tt>float</tt>, or <tt>double).在Java 5变为两边操作数为凡是可转变为基础类型的类型。java 5引入autoboxing and auto-unboxing 。The boxed numeric types are <tt>Byte</tt>, <tt>Character</tt>, <tt>Short</tt>, <tt>Integer</tt>, <tt>Long</tt>, <tt>Float</tt>, and <tt>Double。具体点，让上面进入无限循环：<br></tt></tt></span>Integer i = new Integer(0);<br>Integer j = new Integer(0);<br> (<tt>i &lt;= j</tt> and <tt>j &lt;= i</tt>) perform <span class="docEmphasis">unboxing conversions</span> on <tt>i</tt> and <tt>j</tt> and compare the resulting <tt>int</tt> values numerically。i和j表示0，所以表达式为true。<tt>i != j</tt>比较的是对象引用，也为true。很奇怪规范没有把等号改为比较值。原因很简单：兼容性。当一种语言广泛应用的时候，不能破坏已存在的规范来改变程序的行为。System.out.println(new Integer(0) == new Integer(0));总是输出false，所以必须保留。当一个是boxed numeric 类型，另一个是基本类型的时候可以值比较。因为java 5之前这是非法的，具体点：<br>System.out.println(new Integer(0) == 0); //之前的版本非法，Java 5输出True<br>总结：<span style="color: red">当两边的操作数是boxed numeric类型的时候，数字比较符和等于符号是根本不同的：数字比较符是值比较，等号比较的是对象引用</span>。<br><br>33 <br>while (i != 0 &amp;&amp; i == -i) {}<br>有负号表示i一定是数字，NaN不行，因为他不等于任何数。事实上，没有实数可以出现这种情况。但Java的数字类型并没有完美表达实数。浮点数由一个符号位，一个有效数字（尾数），一个指数构成。浮点数只有0才会和自己的负数相等，所以i肯定是整数。有符号整数用的是二进制补码计算：取反加一。补码的一大优势是用一个唯一的数来表示0。然而有一个对应的缺点：本来可表达偶数个值，现在用一个表达了0，剩下奇数个来表示正负数，意味着正数和负数的数量不一样。比如int值，他的<tt>Integer.MIN_VALUE</tt>（<tt>-2</tt><sup>31</sup>）。十六进制表达<tt>0x8000000</tt>。通过补码计算可知他的负数仍然不变。对他取负数是溢出了的，不过Java在整数计算中忽略了溢出。<br>总结：Java使用二进制补码，是不对称的。有符号整数(<tt>int</tt>, <tt>long</tt>, <tt>byte</tt>, and <tt>short</tt>) 负数值比整数值多一个。<br><br>34<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; final int START = 2000000000;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; int count = 0;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; for (float f = START; f &lt; START + 50; f++)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; count++;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; System.out.println(count);<br>注意循环变量是float。回忆谜题28 ，明显f++没有任何作用。f的初始化值接近<tt>Integer.MAX_VALUE</tt>，因此需要31位来准确表达，但是float类型只提供了24位精度。增加这样大的一个float值不会改变值。看起来会死循环？运行程序会发现，输出0。循环中f和<tt>(float)(START + 50)做比较。但int和float比较的时候，自动把int先提示为float。不幸的是三个会引起精度丢失的宽基本类型转换的其中之一（另外两个是long到float，long到double）。f的初始值巨大，加50再转换为float和直接把f转换为float是一样的效果，即<tt>(float)2000000000 == 2000000050，所以<tt>f &lt; START + 50</tt> 失败。只要把float改为int即可修正。<br></tt></tt>&nbsp;&nbsp;&nbsp;&nbsp; 没有计算器，你怎么知道 2,000,000,050 和float表示2,000,000,000一样？……<br>The moral of this puzzle is simple: <span style="color: red" class="docEmphStrong">Do not use floating-point loop indices</span>, because it can lead to unpredictable behavior. If you need a floating-point value in the body of a loop, take the <tt>int</tt> or <tt>long</tt> loop index and convert it to a <tt>float</tt> or <tt>double</tt>. <span class="docEmphStrong">You may lose precision when converting an <tt>int</tt> or <tt>long</tt> to a <tt>float</tt> or a <tt>long</tt> to a <tt>double</tt></span>, but at least it will not affect the loop itself. <span class="docEmphStrong">When you use floating-point, use <tt>double</tt> rather than <tt>float</tt></span> unless you are certain that <tt>float</tt> provides enough precision <span class="docEmphasis">and</span> you have a compelling performance need to use <tt>float</tt>. The times when it's appropriate to use <tt>float</tt> rather than <tt>double</tt> are few and far between。<br></span></p> <p><span class="docEmphasis">35 <br></span><span class="docEmphasis">下面程序模拟一个简单的时钟<br>int minutes = 0; <br>for (int ms = 0; ms &lt; 60*60*1000; ms++) <br>&nbsp; if (ms %&nbsp; 60*1000 == 0) <br>&nbsp;&nbsp;&nbsp;&nbsp; minutes++; <br>&nbsp;</span><span class="docEmphasis">System.out.println(minutes); </span><span class="docEmphasis"><br>结果是<tt>60000，问题在于布尔表达式ms % 60*1000 == 0，最简单的修改方法是：if (ms % (60 * 1000) == 0) <br>更好的方法是<font color="#ff0000">用合适命名的常量代替魔力数字</font>：</tt></span></p> <p><span class="docEmphasis"><tt></tt></span>private static final int MS_PER_HOUR = 60 * 60 * 1000; <br>private static final int MS_PER_MINUTE = 60 * 1000; <br>public static void main(String[] args) {<br>&nbsp;&nbsp; int minutes = 0; <br>&nbsp;&nbsp; for (int ms = 0; ms &lt; MS_PER_HOUR; ms++) <br>&nbsp;&nbsp;&nbsp;&nbsp; if (ms % MS_PER_MINUTE == 0) <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; minutes++;<br>&nbsp;&nbsp;&nbsp; System.out.println(minutes); <br>} <span class="docEmphasis"><br><font color="#ff0000">绝不要用空格来分组；使用括号来决定优先级</font></p></span><img src ="http://www.blogjava.net/yuxh/aggbug/338121.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/yuxh/" target="_blank">yuxh</a> 2010-11-15 19:59 <a href="http://www.blogjava.net/yuxh/archive/2010/11/15/338121.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Java Puzzlers（三-一 循环）</title><link>http://www.blogjava.net/yuxh/archive/2010/11/15/336547.html</link><dc:creator>yuxh</dc:creator><author>yuxh</author><pubDate>Mon, 15 Nov 2010 08:39:00 GMT</pubDate><guid>http://www.blogjava.net/yuxh/archive/2010/11/15/336547.html</guid><wfw:comment>http://www.blogjava.net/yuxh/comments/336547.html</wfw:comment><comments>http://www.blogjava.net/yuxh/archive/2010/11/15/336547.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/yuxh/comments/commentRss/336547.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/yuxh/services/trackbacks/336547.html</trackback:ping><description><![CDATA[<span style="color: #000000">24&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br />
&nbsp; for (byte b = Byte.MIN_VALUE; b &lt; Byte.MAX_VALUE; b++) {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (b == 0x90)<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; System.out.print("Joy!");<br />
&nbsp;&nbsp;&nbsp; }<br />
<tt>Ox90</tt> 超过了byte的取值范围<font face="Courier New">-128到127。byte和int比较是一种混合类型比较。考虑个表达式<tt>((byte)0x90 == 0x90)得到的是false。byte和int做比较的时候，Java先对byte进行了widening primitive conversion 再比较两个int值。因为byte是有符号类型，转变做了符号扩展，把负的byte值转换为相应的int值。这个例子中(byte)0x90被转变为-112，当然不等于int值 <tt>0x90</tt>或者说+144。混合比较总让人迷惑，因为总是强迫系统去提升一个操作数来和另一种类型匹配。有几种方式可避免混合比较。可以把int映射为byte，之后比较两个byte值： <br />
if (b == (byte)0x90)<br />
&nbsp;&nbsp;&nbsp; System.out.println("Joy!");<br />
另外，可用mask抑制符号扩展，把byte转换为int，之后比较两个int值：<br />
if ((b &amp; 0xff) == 0x90)<br />
&nbsp;&nbsp;&nbsp; System.out.println("Joy!");<br />
但最好的方法是把常量值移出循环放到常量声明中。<br />
<span class="docEmphStrong">&nbsp;&nbsp;&nbsp; private static final byte TARGET = 0x90; // Broken!</span><br />
&nbsp;&nbsp;&nbsp; public static void main(String[] args) {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; for (byte b = Byte.MIN_VALUE; b &lt; Byte.MAX_VALUE; b++)<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (b == TARGET)<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; System.out.print("Joy!");<br />
&nbsp;&nbsp;&nbsp; }<br />
不幸的是，上面编译通不过：<tt>0x90</tt>对于byte类型来说不是一个有效值。这样修改即可：<br />
private static final byte TARGET = <span class="docEmphStrong">(byte)</span>0x90;<br />
<p class="docText">To summarize: <span class="docEmphStrong">Avoid mixed-type comparisons, because they are inherently confusing</span> (<a class="docLink" href="ch02lev1sec5.html#ch02lev1sec5">Puzzle 5</a>). To help achieve this goal, <span class="docEmphStrong">use declared constants in place of "magic numbers." </span>You already knew that this was a good idea; it documents the meanings of constants, centralizes their definitions, and eliminates duplicate definitions.现在你知道他还可以强制你为每一个常量定义适用的类型，避免一种混合类型比较的来源。</p>
<p class="docText">25 <br />
</span<br />
<font color="#000000">int j = 0;<br />
for (int i = 0; i &lt; 100; i++)<br />
&nbsp; j = j++;<br />
System.out.println(j); //</font><font color="#000000">打印出的是0<span></p>
</font></tt></font>
<p>问题出在 j = j++; 等同于下列操作：</p>
<p>int tmp = j; j = j + 1; j = tmp; <br />
这次的教训和难题7一样：<font color="#ff0000">在一个表达式中不要给同一个变量赋值超过一次。</font></p>
<p><font color="#000000">26</font></p>
<p><font color="#000000">public static final int END = Integer.MAX_VALUE;<br />
public static final int START = END - 100;<br />
public static void main(String[] args) {<br />
&nbsp;&nbsp;&nbsp; int count = 0;<br />
&nbsp;&nbsp;&nbsp; for (int i = START; i &lt;= END; i++)<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; count++;<br />
&nbsp;&nbsp;&nbsp;&nbsp; System.out.println(count);<br />
}<br />
看起来像100，再仔细看循环是小于等于，应该是101？结果是程序没有输出任何值，陷入一个死循环。问题出在<tt>Integer.MAX_VALUE，当继续增加的时候，他悄悄变为<tt>Integer.MIN_VALUE。如果你需要循环int值边界，最好用long变量做索引：<br />
for (long i = START; i &lt;= END; i++)&nbsp; //输出101<br />
<font color="#ff0000">教训是：<tt>int</tt>s are not integers。无论何时用基本类型，注意边界值。上溢或下溢会出现什么情况？一般来说最好用大一点的类型（基本类型是<tt>byte</tt>, <tt>char</tt>, <tt>short</tt>, <tt>int</tt>, and <tt>long</tt>）。</font>也可以不用long：</tt></tt></font></p>
<p><font color="#000000">int i = START; <br />
do { <br />
&nbsp; count++; <br />
} while (i++ != END); <br />
考虑到清晰和简单，总是用long索引，除了一种特殊情况：如果要遍历所有int值，这样用int索引的话会快两倍。<br />
一个循环四十亿int值，调用方法的常规用法：</font><font color="#ff0000"></p>
<pre><font color="#000000">// Apply the function f to all four billion int values
int i = Integer.MIN_VALUE;
do {
f(i);
} while (i++ != Integer.MAX_VALUE);
</font></pre>
<p><span style="color: #000000">27&nbsp; 位移<br />
<font color="#ff0000"><span style="color: #000000"><font style="color: #000000" color="#ff0000">记住java是使用二进制补码计算，在任何有符号基本类型中(<tt>byte</tt>, <tt>short</tt>, <tt>int</tt>, or <tt>long</tt>)都是用所有位置1来表示-1。 <br />
</font><font color="#ff0000"><span style="color: #000000"><font color="#ff0000">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span style="color: #000000"><font style="color: #000000" color="#ff0000">int i = 0;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; while (-1 &lt;&lt; i != 0)&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //左位移<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; i++;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; System.out.println(i);<br />
</font>int型的-1用<tt>0xffffffff</tt> 表示。不断左移，右边由0补位。移位32次，变为全0，跳出循环打印32？实际上程序会死循环。问题出在-1&lt;&lt;32不等于0而是等于-1，因为位<span style="color: #ff0000">移符号只用右边操作数的低五位作为移动距离</span>，如果左操作数是long的话用六位。三个位移操作符：&lt;&lt;,&gt;&gt;,&gt;&gt;&gt;都是这样。移动距离总是0到31，左边操作数是long的话0到63。位移距离用32取模，左边是long则用64取模。给int值位移32位或给long值位移64位只会返回本身。所以不可能用位移完全移除一个数据。幸运的是，有一个简单的办法解决这个问题。保存上一次的位移结果，每一次迭代多移动一位。<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; int distance = 0;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; for (int val = -1; val != 0; val &lt;&lt;= 1)<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; distance++;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; System.out.println(distance);<br />
修改后的程序说明了一个原则：<span style="color: #ff0000">位移距离如果可能的话，用常量</span>。<br />
另外一个问题，许多程序员认为右移一个负的移动距离，就和左移一样，反之亦然。事实上不是这样，左移是左移，右移就是右移。负数距离只留下低五位（long留六位），其余的置0就变为了正数距离。比如，左移一个int值-1的距离，实际上是左移31位。<br />
<br />
28 无穷的表示<br />
for (int i = start; i &lt;= start + 1; i++) {<br />
}<br />
看起来循环两次就会结束，如果这样定义呢：<br />
int start = Integer.MAX_VALUE - 1;//死循环<br />
while (i == i + 1) {<br />
}<br />
这个不可能死循环？如果i是无穷呢？Java采用IEEE 754浮点数算术，用double或float来表示无穷。所以可以用任何浮点数计算表达式得出无穷来初始化i。比如：double i = 1.0 / 0.0;<br />
更好的是可以利用标准库提供的常量:double i = Double.POSITIVE_INFINITY;<br />
事实上，根本用不着用无穷初始化i来引起死循环。只要足够大的浮点数就够了：double i = 1.0e40;<br />
这是因为浮点数越大，他的值和下一个数的值距离也就越大。distribution of floating-point values is a consequence of their representation with a fixed number of <u>significant</u> bits. 给足够大的浮点数加一不会改变值，因为他不能填充这个数和下一个数之间的距离。浮点数操作返回最接近准确数学结果的浮点值。一旦两个相邻浮点值之间的距离大于2，加1就不会有效果。float类型来说，超过2<sup>25</sup>（或33,554,432）再加1就无效；对double来说超过2<sup>54</sup>（接近1.8 x 10<sup>16</sup>）再加1就无效。<br />
&nbsp;&nbsp;&nbsp;&nbsp; 相邻浮点数之间的距离称为ulp（unit in the last place的缩写）。在Java 5里 <tt>Math.ulp方法被引入来计算float或double值的ulp。</tt></span></font></span></font></span></font></span></p>
<p><span style="color: #000000"><font color="#ff0000"><span style="color: #000000"><font color="#ff0000"><span style="color: #000000"><font color="#ff0000"><span style="color: #000000"><tt>总结：<font color="#ff0000">不可能用float或double来表示无穷。另外，在一个大的浮点数上加一个小的浮点数，值不会改变。有点不合常理，实数并不是这样。记住二进制浮点数计算只是近似于实数计算。</font></tt></span></font></span></font></span></font></span></p>
<p></font><font color="#ff0000"><span style="color: #000000"><span style="color: #000000"><span style="color: #000000"><span style="color: #000000"><tt>29 NaN</tt></span></span></span></span></font></span></span></p>
<p>while (i != i) { } <br />
IEEE 754 浮点数计算保留了一个特殊值来来表示不是数字的数量。NaN是浮点计算不能很好定义的数，比如<tt>0.0 / 0.0。规范定义<font color="#ff0000">NaN不等于任何数包括自己</font>。因此double i = 0.0 / 0.0; 可让开始的等式不成立。也有标准库定义的常量：</tt>double i = Double.NaN; 如果一个或多个操作数为NaN，那么浮点数计算就会等于NaN。</p>
<p>总结：float和doule存在特殊的值NaN，小心处理。</p>
<p>30<br />
while (i != i + 0) { } 这一次不能使用float或者double。<br />
<tt>+操作符除了数字，就只能处理String。+操作符会被重载：对于String类型，他做的是连接操作。如果操作数有非String类型，会先做转换变为String之后再做连接。i一般用作数字，要是对String型变量这么命名容易引起误解。</tt></p>
<p><tt>总结：<font color="#ff0000">操作符重载非常容易误导人。好的变量名，方法名，类名和好的注释对于程序的可读性一样重要。</font></tt></p>
<img src ="http://www.blogjava.net/yuxh/aggbug/336547.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/yuxh/" target="_blank">yuxh</a> 2010-11-15 16:39 <a href="http://www.blogjava.net/yuxh/archive/2010/11/15/336547.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Java Puzzlers（二-二 字符）</title><link>http://www.blogjava.net/yuxh/archive/2010/10/30/335872.html</link><dc:creator>yuxh</dc:creator><author>yuxh</author><pubDate>Sat, 30 Oct 2010 05:46:00 GMT</pubDate><guid>http://www.blogjava.net/yuxh/archive/2010/10/30/335872.html</guid><wfw:comment>http://www.blogjava.net/yuxh/comments/335872.html</wfw:comment><comments>http://www.blogjava.net/yuxh/archive/2010/10/30/335872.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/yuxh/comments/commentRss/335872.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/yuxh/services/trackbacks/335872.html</trackback:ping><description><![CDATA[19&nbsp; 单行注释<br />
&nbsp;&nbsp;&nbsp; public static void main(String[] args) {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; System.out.println(classify('n') + classify('+') + classify('2'));<br />
&nbsp;&nbsp;&nbsp; }<br />
&nbsp;&nbsp;&nbsp; static String classify(char ch) {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if ("0123456789".indexOf(ch) &gt;= 0)<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return "NUMERAL ";<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if ("abcdefghijklmnopqrstuvwxyz".indexOf(ch) &gt;= 0)<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return "LETTER ";<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /* (Operators not supported yet)<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if ("+-<span style="color: red">*/</span>&amp;|!=".indexOf(ch) &gt;= 0)<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return "OPERATOR ";<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; */<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return "UNKNOWN ";<br />
&nbsp;&nbsp;&nbsp; }<br />
编译出错，块注释不能嵌套，在注释内的文本都不会被特殊对待。<br />
<span class="docEmphStrong">// Code commented out with an if statement - doesn't always work!<br />
if (false) {</span><br />
&nbsp;&nbsp;&nbsp; /* Add the numbers from 1 to n */<br />
&nbsp;&nbsp;&nbsp; int sum = 0;<br />
&nbsp;&nbsp;&nbsp; for (int i = 1; i &lt;= n; i++)<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; sum += i;<br />
<span class="docEmphStrong">}</span><br />
这是语言规范推荐的一种条件编译的技术，但不是非常适合注释代码。除非包含的语句都是有效的表达式，否则这种条件编译不能用作注释。最好的注释代码方法是用单行注释。<br />
<br />
20&nbsp;&nbsp; 反斜杠<br />
&nbsp;&nbsp; <tt>Me.class.getName()</tt> 返回的是Me类的完整名，如<tt>"com.javapuzzlers.Me"</tt>。&nbsp;<br />
&nbsp;&nbsp; System.out.println(&nbsp;Me.class.getName().replaceAll(".", "/") + ".class"); <br />
&nbsp;&nbsp; 应该得到<tt>com/javapuzzlers/Me.class</tt>？不对。问题出在<span class="docEmphStrong"><tt>String.replaceAll</tt></span>把正则表达式作为第一个参数，而不是字符。正则表达是&#8220;.&#8221;表示配对任何单独的字符，所以类名的每一个字符都被斜线替代。为了只匹配句号，必须用反斜线（\）转义。因为反斜线在字符串中有特殊意义——它是escape sequence的开始——反斜线自身也必须用一个反斜线转义。<br />
正确：System.out.println(&nbsp;Me.class.getName().replaceAll(<span class="docEmphStrong">"\\."</span>, "/") + ".class");<br />
为了解决这类问题，java 5提供了一个新的静态方法<tt>java.util.regex.Pattern.quote</tt>。用一个字符串作为参数，增加任何需要的转义，返回一个和输入字符串完全匹配的正则表达式字符串：<br />
&nbsp; System.out.println(Me.class.getName().replaceAll(<span class="docEmphStrong">Pattern.quote(".")</span>, "/") + ".class");<br />
这个程序的另外一问题就是依赖于平台。不是所有的文件系统都是用斜线来组织文件。为了在你运行的平台取得正确的文件名，你必须使用正确的平台分隔符来替换斜线。<br />
<br />
21&nbsp;<br />
&nbsp;&nbsp;&nbsp; System.out.println(MeToo.class.getName().<br />
&nbsp;&nbsp;&nbsp;&nbsp;replaceAll("\\.", File.separator) + ".class");<br />
<tt>java.io.File.separator</tt> 是一个公共的<tt>String</tt> 属性，指定用来包含平台依赖的文件名分隔符。在UNIX上运行打印<tt>com/javapuzzlers/MeToo.class</tt>。然而，在Windows上程序抛出异常：<br />
&nbsp;&nbsp;&nbsp; StringIndexOutOfBoundsException: String index out of range: 1<br />
结果是<tt>String.replaceAll</tt> 的第二个参数不是普通字符串而是一个在<tt>java.util.regex</tt> 规范中定义的&nbsp;<span class="docEmphasis">replacement string，反斜线转义了后面的字符。当在Windows上运行的时候，替换字符是一个单独的反斜线，无效。JAVA 5提供了两个新方法来解决这个问题，一个是<tt>java.util.regex.Matcher.quoteReplacement，它替换字符串为相应的替换字符串：<br />
&nbsp;System.out.println(MeToo.class.getName().replaceAll(<br />
&nbsp;&nbsp; "\\.",&nbsp; <span class="docEmphStrong">Matcher.quoteReplacement(File.separator)</span>)+".class");<br />
第二个方法提供了更好的解决方法。<tt>String.replace(CharSequence, CharSequence)和<tt>String.replaceAll</tt>做同样的事情，但他把两个参数都作为字符串处理：System.out.println(MeToo.class.getName().<span class="docEmphStrong">replace(".", File.separator)</span> + ".class");&nbsp;&nbsp;&nbsp;<br />
如果用的是java早期版本就没有简单的方法产生替换字符串。完全不用正则表达式，使用<tt>String.replace(char, char)跟容易一些：<br />
System.out.println(MeToo.class.getName().<span class="docEmphStrong">replace('.', File.separatorChar) + ".class");</span><br />
<span style="color: red">教训：当用不熟悉的库方法的时候，小心点。有怀疑的话，查看Javadoc</span>。当然正则表达式也很棘手：他编译时可能没问题运行时却更容易出错。</tt></tt></tt></span> <br />
<br />
22&nbsp;&nbsp;<span class="docEmphasis">statement label</span><br />
&nbsp;&nbsp;&nbsp;&nbsp; <span style="color: red">认真写注释，及时更新。去掉无用代码</span>。如果有东西看起来奇怪不真实，很有可能是错误的。 <br />
23<br />
&nbsp;&nbsp;&nbsp;<font color="#3333cc"> private static Random rnd = new Random();<br />
&nbsp;&nbsp;&nbsp; public static void main(String[] args) {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; StringBuffer word = null;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; switch(rnd.nextInt(2)) {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; case 1:&nbsp; word = new StringBuffer('P');<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; case 2:&nbsp; word = new StringBuffer('G');<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; default: word = new StringBuffer('M');<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; word.append('a');<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; word.append('i');<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; word.append('n');<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; System.out.println(word);<br />
&nbsp;&nbsp; }<br />
<span style="color: rgb(0,0,0)">在一次又一次的运行中，以相等的概率打印出Pain，Gain或 Main？答案它总是在打印ain。一共有三个bug导致这种情况。<br />
</span>一是 <tt>Random.nextInt(int)</tt> ，看规范可知这里返回的是0到int值之间的前闭后开区间的随机数。因此程序中永远不会返回2。</font><span style="widows: 2; text-transform: none; text-indent: 0px; border-collapse: separate; white-space: normal; orphans: 2; letter-spacing: normal; color: rgb(0,0,0); font-weight: normal; word-spacing: 0px" class="Apple-style-span"><span style="text-align: left; line-height: 19px; font-family: 宋体,Arial; color: rgb(51,51,51); font-size: 14px" class="Apple-style-span">这是一个相当常见的问题源，被熟知为&#8220;栅栏柱错误（fencepost error）&#8221;。这个名字来源于对下面这个问题最常见的但却是错误的答案，如果你要建造一个100英尺长的栅栏，其栅栏柱间隔为10英尺，那么你需要多少根栅栏柱呢？11根或9根都是正确答案，这取决于是否要在栅栏的两端树立栅栏柱，但是10根却是错误的。要当心栅栏柱错误，每当你在处理长度、范围或模数的时候，都要仔细确定其端点是否应该被包括在内，并且要确保你的代码的行为要与其相对应。<br />
第二个bug是 case没有配套的</span></span><tt>break</tt>。<span style="widows: 2; text-transform: none; text-indent: 0px; border-collapse: separate; white-space: normal; orphans: 2; letter-spacing: normal; color: rgb(0,0,0); font-weight: normal; word-spacing: 0px" class="Apple-style-span"><span style="text-align: left; line-height: 19px; font-family: 宋体,Arial; color: rgb(51,51,51); font-size: 14px" class="Apple-style-span">从5.0版本起，javac提供了-Xlint:fallthrough标志，当你忘记在一个case与下一个case之间添加break语句是，它可以生成警告信息。不要从一个非空的case向下进入了另一个case。这是一种拙劣的风格，因为它并不常用，因此会误导读者。十次中有九次它都会包含错误。如果Java不是模仿C建模的，那么它倒是有可能不需要break。对语言设计者的教训是：应该考虑提供一个结构化的switch语句。<br />
</span></span><span style="widows: 2; text-transform: none; text-indent: 0px; border-collapse: separate; white-space: normal; orphans: 2; letter-spacing: normal; color: rgb(0,0,0); font-weight: normal; word-spacing: 0px" class="Apple-style-span"><span style="text-align: left; line-height: 19px; font-family: 宋体,Arial; color: rgb(51,51,51); font-size: 14px" class="Apple-style-span">最后一个，也是最微妙的一个bug是表达式new StringBuffer(&#8216;M')可能没有做哪些你希望它做的事情。S</span></span><span style="widows: 2; text-transform: none; text-indent: 0px; border-collapse: separate; white-space: normal; orphans: 2; letter-spacing: normal; color: rgb(0,0,0); font-weight: normal; word-spacing: 0px" class="Apple-style-span"><span style="text-align: left; line-height: 19px; font-family: 宋体,Arial; color: rgb(51,51,51); font-size: 14px" class="Apple-style-span">tringBuffer(char)构造器根本不存在。</span></span><span style="widows: 2; text-transform: none; text-indent: 0px; border-collapse: separate; white-space: normal; orphans: 2; letter-spacing: normal; color: rgb(0,0,0); font-weight: normal; word-spacing: 0px" class="Apple-style-span"><span style="text-align: left; line-height: 19px; font-family: 宋体,Arial; color: rgb(51,51,51); font-size: 14px" class="Apple-style-span">StringBuffer有一个无参数的构造器，一个接受一个String作为字符串缓冲区初始内容的构造器，以及一个接受一个int作为缓冲区初始容量的构造器。在本例中，编译器会选择接受int的构造器，通过拓宽原始类型转换把字符数值'M'转换为一个int数值77[JLS 5.1.2]。换句话说，new StringBuffer(&#8216;M')返回的是一个具有初始容量77的空的字符串缓冲区。该程序余下的部分将字符a、i和n添加到了这个空字符串缓冲区中，并打印出该字符串缓冲区那总是ain的内容<span style="color: red"><span style="widows: 2; text-transform: none; text-indent: 0px; border-collapse: separate; white-space: normal; orphans: 2; letter-spacing: normal; color: rgb(0,0,0); font-weight: normal; word-spacing: 0px" class="Apple-style-span"><span style="text-align: left; line-height: 19px; font-family: 宋体,Arial; color: rgb(51,51,51); font-size: 14px" class="Apple-style-span">。<span style="color: rgb(255,0,0)"><span style="widows: 2; text-transform: none; text-indent: 0px; border-collapse: separate; white-space: normal; orphans: 2; letter-spacing: normal; color: rgb(0,0,0); font-weight: normal; word-spacing: 0px" class="Apple-style-span"><span style="text-align: left; line-height: 19px; font-family: 宋体,Arial; color: rgb(51,51,51); font-size: 14px" class="Apple-style-span"><span style="color: red"><span style="widows: 2; text-transform: none; text-indent: 0px; border-collapse: separate; white-space: normal; orphans: 2; letter-spacing: normal; color: rgb(0,0,0); font-weight: normal; word-spacing: 0px" class="Apple-style-span"><span style="text-align: left; line-height: 19px; font-family: 宋体,Arial; color: rgb(51,51,51); font-size: 14px" class="Apple-style-span"><span style="color: red"><span style="color: red"><span style="widows: 2; text-transform: none; text-indent: 0px; border-collapse: separate; white-space: normal; orphans: 2; letter-spacing: normal; color: rgb(0,0,0); font-weight: normal; word-spacing: 0px" class="Apple-style-span"><span style="text-align: left; line-height: 19px; font-family: 宋体,Arial; color: rgb(51,51,51); font-size: 14px" class="Apple-style-span"><span style="color: red"> <span style="widows: 2; text-transform: none; text-indent: 0px; border-collapse: separate; white-space: normal; orphans: 2; letter-spacing: normal; word-spacing: 0px" class="Apple-style-span"><span style="text-align: left; line-height: 19px; font-family: 宋体,Arial; font-size: 14px" class="Apple-style-span"><span style="widows: 2; text-transform: none; text-indent: 0px; border-collapse: separate; white-space: normal; orphans: 2; letter-spacing: normal; word-spacing: 0px" class="Apple-style-span"><span style="text-align: left; line-height: 19px; font-family: 宋体,Arial; font-size: 14px" class="Apple-style-span"><span style="widows: 2; text-transform: none; text-indent: 0px; border-collapse: separate; white-space: normal; orphans: 2; letter-spacing: normal; word-spacing: 0px" class="Apple-style-span"><span style="text-align: left; line-height: 19px; font-family: 宋体,Arial; font-size: 14px" class="Apple-style-span">为了避免这类问题，不管在什么时候，都要尽可能使用熟悉的惯用法和API</span></span></span></span></span></span></span>。如果你必须使用不熟悉的API，那么请仔细阅读其文档</span></span></span></span></span></span></span></span></span><span style="widows: 2; text-transform: none; text-indent: 0px; border-collapse: separate; white-space: normal; orphans: 2; letter-spacing: normal; color: rgb(0,0,0); font-weight: normal; word-spacing: 0px" class="Apple-style-span"><span style="text-align: left; line-height: 19px; font-family: 宋体,Arial; color: rgb(51,51,51); font-size: 14px" class="Apple-style-span">。</span></span></span></span></span></span></span></span><span style="widows: 2; text-transform: none; text-indent: 0px; border-collapse: separate; white-space: normal; orphans: 2; letter-spacing: normal; color: rgb(0,0,0); font-weight: normal; word-spacing: 0px" class="Apple-style-span"><span style="text-align: left; line-height: 19px; font-family: 宋体,Arial; color: rgb(51,51,51); font-size: 14px" class="Apple-style-span">在本例中，程序应该使用常用的接受一个String的StringBuffer构造器。</span></span> 
<img src ="http://www.blogjava.net/yuxh/aggbug/335872.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/yuxh/" target="_blank">yuxh</a> 2010-10-30 13:46 <a href="http://www.blogjava.net/yuxh/archive/2010/10/30/335872.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Java Puzzlers(二-一 字符)</title><link>http://www.blogjava.net/yuxh/archive/2010/10/22/335023.html</link><dc:creator>yuxh</dc:creator><author>yuxh</author><pubDate>Fri, 22 Oct 2010 00:49:00 GMT</pubDate><guid>http://www.blogjava.net/yuxh/archive/2010/10/22/335023.html</guid><wfw:comment>http://www.blogjava.net/yuxh/comments/335023.html</wfw:comment><comments>http://www.blogjava.net/yuxh/archive/2010/10/22/335023.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/yuxh/comments/commentRss/335023.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/yuxh/services/trackbacks/335023.html</trackback:ping><description><![CDATA[11&nbsp;字符相加<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; System.out.print("H" + "a");System.out.print('H' + 'a'); //貌似输出<tt>HaHa？</tt><br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 最后输出的是<tt>Ha169</tt>。<tt>'H'</tt> 和<tt>'a'</tt>&nbsp;都是 <tt>char，不是<tt>String</tt>，+操作符做的是加法操作而不是拼接字符串。编译器提升两个char值到int值，从16位零扩展到32位的int。一个是72另一个是97。从语义上说，char值和字符串的相似是非常迷惑的。java语言仅仅把char看做无符号的16位基本整数。库不这样认为，他里面有许多方法把char参数当作Unicode字符在处理。怎样连接字符？可以使用库方法，如：<br />
</tt>&nbsp;&nbsp;&nbsp;&nbsp; StringBuffer sb = new StringBuffer();<br />
&nbsp;&nbsp;&nbsp;&nbsp; sb.append('H');sb.append('a');<br />
&nbsp;&nbsp;&nbsp;&nbsp; System.out.println(sb);&nbsp; //可行但丑陋<br />
&nbsp;&nbsp;&nbsp; 有很多方法避免这种繁琐，只要一个操作符是string就能强制+操作符做拼接操作。习惯用法是在前面加一个空字符串&#8220;&#8221;，如：<br />
&nbsp;&nbsp;&nbsp;&nbsp; System.out.print("" + 'H' + 'a');//虽然有用，但还是有点不雅而且容易导致一点困惑&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp; 试试：System.out.println("2 + 2 = " + 2+2);&nbsp; 如果用的是java 5还可以使用printf：System.out.printf("%c%c", 'H', 'a');<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 总结：小心应对字符串拼接操作符。<font face="Courier New"><span style="color: red"><font face="Courier New">&#8220;+&#8221;只有至少一个是String的时候才做字符串拼接</font></span>；否则做加法。如果没有String型，有几个选择：加空字符串；用<tt>String.valueOf</tt>把第一个值显示转换为String；用String buffer；java 5的话用printf</font> 。<br />
12&nbsp; 字符数组<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;String letters = "ABC";<br />
&nbsp;&nbsp;&nbsp;&nbsp; char[] numbers = { '1', '2', '3' };<br />
&nbsp;&nbsp;&nbsp;&nbsp; System.out.println(letters + " easy as " + numbers);&nbsp; //返回<tt>ABC easy as</tt> [C@16f0472<br />
&nbsp;<tt>char</tt> 虽是基本整数类型，但char值常常表示字符而不是整数，很多库对他特殊对待。比如，把char传给<tt>println</tt> 输出的是Unicode 字符而不是数字码。char数组获得同样对待：<tt>char[]</tt> 的重载是println输出数组中的所有字符，char[]对<tt>String.valueOf</tt> 和<tt>StringBuffer.append</tt> 的重载也类似。但是字符拼接操作符不是像这些方法，他是对两边做字符串转换然后再拼接。对于对象引用包括数组，字符串转换是这样定义的：如果应用是null，转换为字符串<tt>"null"</tt>，否则调用被引用对象的<tt>toString</tt> 无参数方法；如果<tt>toString</tt> 方法返回的是null，还是用&#8220;null&#8221;字符串。非null的char数组调用toString做什么操作？数组从<tt>Object</tt> 继承来的toString,定义，&#8220;返回字符串含有对象实例类名，字符<tt><a href="&#109;&#97;&#105;&#108;&#116;&#111;&#58;&#39;&#64;&#39;">'@'</a></tt>，对象的用无符号十六进制表示的hash码&#8221;。&nbsp;<tt>Class.getName</tt> 的说明表示对&nbsp;<tt>char[]</tt> 该类对象调用该方法返回&nbsp;<tt>"[C"</tt>。两个方法修正，拼接之前可显示转换数组为String：<br />
&nbsp;&nbsp;&nbsp;&nbsp; System.out.println(letters + " easy as " +&nbsp;String.valueOf(numbers));<br />
还可以把the <tt>System.out.println</tt> 分开成两个来利用&nbsp;<tt>char[]</tt> 对println的重载：<br />
&nbsp;&nbsp;&nbsp;&nbsp; System.out.print(letters + " easy as ");System.out.println(numbers);<br />
&nbsp;注意这些修正只是在你正确重载<tt>valueOf</tt> 和<tt>println方法才起效。换句话说，他们严重依赖编译时数组引用的类型。</tt> 下面例子看起来用来第二个修正方法，但还是输出丑陋字符串。因为他调用了Object对println的重载而不是char[]的重载。<br />
<span class="docEmphStrong">// Broken - invokes the wrong overloading of println!</span><br />
class Abc {<br />
&nbsp;&nbsp;&nbsp; public static void main(String[] args) {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; String letters = "ABC";<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span class="docEmphStrong">Object</span> numbers = new char[] { '1', '2', '3' };<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; System.out.print(letters + " easy as ");<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; System.out.println(numbers); <span class="docEmphStrong">// Invokes println(Object)</span><br />
&nbsp;&nbsp;&nbsp; }<br />
}<br />
总结：char数组不是字符串。<span style="color: red">把char数组转换为字符串，调用&nbsp;<tt>String.valueOf(char[])</tt></span><tt>。一些库方法给char数组提供了像字符串的支持，典型的是给Object提供一个重载再给char[]提供一个重载；后一个才是理想的操作。</tt><br />
<br />
13 Interning&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; final String pig = "length: 10";<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;final String dog = "length: " + pig.length();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;System.out.println("Animals are equal: "&nbsp; + pig == dog);<br />
compile-time constants of type <tt>String</tt> are <span class="docEmphasis">interned.换句话说，任何两个有相同字符的String类型的常量表达式是同一个对象引用表示的。所以如果用常量表达式初始化的话，pig和dog会指向同一个对象，但dog没用常量表达式。Java语言限制哪些操作可以出现在常量表达式中，方法调用是不允许的。因此，程序应该输出&nbsp;<tt>Animals are equal: false</tt>，对吧？事实上不是，运行发现只输出<tt>false</tt> 。操作符的优先级体现出来，事实上是<br />
&nbsp;&nbsp;&nbsp;&nbsp; System.out.println(("Animals are equal: " + pig) == dog);<br />
有一种方法能避免这种困难：但使用字符拼接操作符的时候，总是给重要操作数加上括号：System.out.println("Animals are equal: " + (pig == dog));<br />
<span class="docEmphStrong">辩证的说，这样还是有问题。Your code should rarely, if ever, depend on the interning of string constants</span>。Interning 只是用来减少虚拟机内存的，不是用来当作程序员工具的。由于字符串intern 失败带来的bug非常难以检测<span style="color: red">。对比两个对象引用的时候，应该用equals方法而不是==操作符除非你是想比较对象identity而不是值</span>。所以我们的例子应该这样：System.out.println("Animals are equal: " + pig.equals(dog));<br />
<br />
14 转义符</span> <br />
&nbsp;&nbsp;&nbsp; 下面程序使用 <span class="docEmphasis">Unicode escapes</span>：用他们的十六进制数字码表示Unicode 字符。<br />
&nbsp;&nbsp;&nbsp; // \u0022 is the Unicode escape for double quote (")<br />
&nbsp; &nbsp; System.out.println("a\u0022.length() + \u0022b".length());<br />
&nbsp; <span class="docEmphStrong">Java provides no special treatment for Unicode escapes within string literals</span>。编译器在把Unicode escapes程序解析为字符串之前，先变为了他们表示的字符。可以使用<span class="docEmphasis">escape sequences:即用\"表示双引号 ，例System.out.println("a\".length() + \"b".length());&nbsp;<br />
还有很多escape sequences ：&nbsp;single quote (<tt>\'</tt>), linefeed (<tt>\n</tt>), tab (<tt>\t</tt>), and backslash (<tt>\\</tt>).&nbsp; escape sequences 等程序先解析为符号之后才处理。ASCII是Unicode的子集。ASCII是最小的字符集，只有128个字符，Unicode有&nbsp;65,000的字符。Unicode escape 能被用来把Unicode 字符插入只能使用ASCII字符的程序中。一个&nbsp;Unicode escape意味着和他代表的字符完全相同的东西。但程序员用源文件的字符集不能插入一些字符的时候，可以使用&nbsp;Unicode escape，主要是把非ASCII字符变为标志符，字符串，注释等。<br />
&nbsp; 总结：在字符串和字符文字中，用escape sequences不用Unicode escapes&nbsp;。不要用Unicode escapes&nbsp; 表示ASCII字符。在字符串和字符文字中，用escape sequences；在外面的话直接把ASCII 字符插入源文件。<br />
<br />
15&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp; <span class="docEmphStrong">Unicode escapes must be well formed, even if they appear in comments. 下面这个例子编译出错<br />
/**<br />
* Generated by the IBM IDL-to-Java compiler, version 1.0<br />
* from F:\TestRoot\apps\a1<span style="color: red">\u</span>nits\include\PolicyHome.idl<br />
* Wednesday, June 17, 1998 6:44:40 o'clock AM GMT+00:00<br />
*/<br />
工具在把Windows 文件名放入注释之前，必须把\去掉。<br />
总之，<tt>\u</tt>不要出现在有效Unicode escape范围之外，即使注释也不行。特别是自动产生代码的时候。<br />
16&nbsp;<br />
&nbsp;&nbsp;&nbsp; <span class="docEmphasis">line separator</span> 用来表示分割文本行的字符，每个平台的<span class="docEmphasis">line separator</span> 不一样。Windows 上，CR character (carriage return) followed by the LF character (linefeed)。UNIX上只有LF字符（经常被较为newline character）。下面把这些字符传给<tt>println<br />
&nbsp;&nbsp; // Note: <span style="color: red">\u000A </span>is Unicode representation of linefeed (LF)<br />
&nbsp;&nbsp;&nbsp; char c = 0x000A;<br />
&nbsp;&nbsp;&nbsp; System.out.println(c);<br />
结果编译错误！仍然是由于注释中Unicode escape,编译器再抛弃注释内容和空格之前就把Unicode escapes 转换为字符。<span style="color: red">\u000A </span>代表linefeed character,因此程序最后转换为&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;// Note:<br />
<span class="docEmphStrong">&nbsp;&nbsp; is Unicode representation of linefeed (LF)</span><br />
&nbsp;&nbsp; char c = 0x000A;<br />
&nbsp;&nbsp;&nbsp;System.out.println(c);<br />
最简单的修改方法是去掉&nbsp;Unicode escape ，但更好的方法是用escape sequence 初始化c，而不是用十六进制整数，排除注释的需要<br />
&nbsp;&nbsp; char c = '\n';<br />
&nbsp;&nbsp; System.out.println(c);<br />
这样改后程序还是有问题，有平台依赖。在某些平台，如UNIX，他将输出两行完整的分割符；另外一些平台，如Windows，则不会。虽然肉眼看起来一样，但如果存在一个文件或管道里供其他程序处理话 是很容易出问题的。 如果打算输出两行空白，应该调用println两次。Java 5，你可以使用printf带上格式<tt>"%n%n"来</tt>代替println，每一个出现的&nbsp;<tt>%n</tt>是printf打印出平台相应的行分隔符。<br />
道理：非必需尽量不用Unicode escapes <br />
17&nbsp;<br />
&nbsp;&nbsp;&nbsp;<span style="color: red" class="docEmphStrong">Unicode escapes are essential when you need to insert characters that can't be represented in any other way into your program. Avoid them in all other cases</span>.Unicode escapes 减少程序的清晰性，增加bug的出现。对语言设计者来说，应该使Unicode escapes表示ASCII字符非法。<br />
18&nbsp;字符集<br />
&nbsp; byte bytes[] = new byte[256];<br />
&nbsp; for(int i = 0; i &lt; 256; i++)<br />
&nbsp;&nbsp;&nbsp;&nbsp;bytes[i] = (byte)i;<br />
&nbsp;&nbsp;String str = new String(bytes);<br />
&nbsp;&nbsp;for(int i = 0, n = str.length(); i &lt; n; i++)<br />
&nbsp;&nbsp; &nbsp;System.out.print((int)str.charAt(i) + " ");<br />
&nbsp; 在不同的机器上运行，结果完全不一样。原因是<tt>String(byte[])构造器。规范说&#8220;用平台默认的字符集给指定的byte数组解码创建一个新字符串。新字符串的长度是字符集的功能，因此可能和byte数组的长度不一样。当给定的字节在默认字符集中无效时，该构造器行为不确定&#8221;。什么是字符集？技术上说，他是&#8220;the combination of a coded character set and a character-encoding scheme&#8221;。换句话说，是一堆字符，表达字符的数字编码，以及一序列字符编码和字节互相转换的方法。转换方案在不同的字符集中差异巨大。一些字符和字节一一对应；大多数不这样。只有默认字符集是&nbsp;ISO-8859-1（更多被称为Latin-1 ）的时候上面的程序才会输出0到255的整数。J2SE运行环境的默认编码是由底层操作系统和区域决定的。在早期的JAVA版本读取系统属性&nbsp;<tt>"file.encoding"来得到JRE默认编码，JAVA 5后以后的版本，可使用&nbsp;<tt>java.nio.charset.Charset.defaultCharset()方法。幸运的是，你不是非得要面对默认字符集的变化多端。char<span style="color: red">序列和byte序列互相转换的时候，你可以并且大多数时候应当显式的指定字符集</span>。一个以字符集名字和byte数组为参数的String构造器可完成此任务。用下面方法，上面程序就不会受默认字符集的影响了：String str = new String(bytes, "ISO-8859-1"); 该构造器抛出<tt>UnsupportedEncodingException</tt>，你必须捕获，当更好的方法是声明一个main方法来抛出他否则不能编译通过。事实上，上面的程序不会抛出异常。因为<tt>Charset</tt> 的规范指明任何JAVA平台的实现必须支持某些字符集，ISO-8859-1是其中之一。<br />
</tt></tt></tt>&nbsp; 教训：<span style="color: red">每次从byte序列转换为String，不管显示或隐式都在使用一种字符集</span>。如果想程序不出意外，每次使用时显示指定一种字符集。<br />
</tt></span></span><img src ="http://www.blogjava.net/yuxh/aggbug/335023.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/yuxh/" target="_blank">yuxh</a> 2010-10-22 08:49 <a href="http://www.blogjava.net/yuxh/archive/2010/10/22/335023.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Java Puzzlers（一 表达式计算）</title><link>http://www.blogjava.net/yuxh/archive/2010/10/13/334085.html</link><dc:creator>yuxh</dc:creator><author>yuxh</author><pubDate>Wed, 13 Oct 2010 05:27:00 GMT</pubDate><guid>http://www.blogjava.net/yuxh/archive/2010/10/13/334085.html</guid><wfw:comment>http://www.blogjava.net/yuxh/comments/334085.html</wfw:comment><comments>http://www.blogjava.net/yuxh/archive/2010/10/13/334085.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/yuxh/comments/commentRss/334085.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/yuxh/services/trackbacks/334085.html</trackback:ping><description><![CDATA[1 奇数判断<br />
&nbsp; 误：public static boolean isOdd(int i) { return i % 2 == 1; }&nbsp;&nbsp; //没有考虑到负奇数的情况<br />
&nbsp; 正：return i % 2 != 0; 更好的性能：return (i &amp; 1) != 0;<br />
&nbsp;总结：求余操作需要考虑符号！<br />
2 浮点数计算<br />
&nbsp;&nbsp; public static void main(String args[]) { System.out.println(2.00 - 1.10); } //天真以为得到0.90<br />
&nbsp;&nbsp;&nbsp; 如果熟悉<tt>Double.toString</tt> 的文档，估计会觉得 <tt>double</tt> 会转为string，程序会打印出足够区分<tt>double</tt>值的小数部分，小数点前或后面至少一位。这样说来应该是0.9，可惜运行程序发现是 <tt>0.8999999999999999。问题是数字1.1不能被double准确表示！只能用最接近的double值表示。遗憾的是结果不是最接近0.9的double值</tt>。更普遍的看这问题是：<strong>不是所有的十进制数都能用二进制浮点数准确的表示</strong> 。如果用jdk5或以后版本，你可能会使用<tt>printf</tt>来准确设置：<br />
<span class="docEmphStrong">// Poor solution - still uses binary floating-point!</span> <br />
System.out.printf("%.2f%n", 2.00 - 1.10); <br />
&nbsp; 现在打印出来是正确的了，但治标不治本：它仍然使用的是<tt>double</tt>运算（二进制浮点），浮点计算在大范围内提供近似计算，但不总是产生准确的结果。二进制浮点数特别不适合金融计算，因为他不可能表示0.1&#8212;&#8212;或任何10的负幂&#8212;&#8212;exactly as a finite-length binary fraction。<br />
一种解决办法是使用基本类型，比如int long，然后扩大操作数倍数做计算。如果用这种流程，确保基本类型足够大来表示你所有你用到的数据，这个例子中，int足够了System.out.println((200 - 110) + " cents");<br />
&nbsp;&nbsp; 另一种办法使用<tt>BigDecimal，他进行准确的十进制计算，他还能通过JDBC和SQL的</tt><tt>DECIMAL类型合作。<strong>有一个箴言：总是使用</strong></tt><tt><strong>BigDecimal(String)构造器，绝不使用</strong></tt><span style="font-family: monospace"><span class="docEmphStrong"><tt><strong>BigDecimal(double)</strong></tt></span>.后面这个构造函数用参数的准确值创建一个实例：</span><tt>new BigDecimal(.1)返回一个</tt><tt>BigDecimal表示0.1000000000000000055511151231257827021181583404541015625。正确使用会得到预期结果</tt><tt>0.90： &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; &nbsp;&nbsp;</tt><span style="font-family: monospace">System.out.println(new BigDecimal("2.00"). subtract(new BigDecimal("1.10"))); 这个例子不是特别漂亮，java没有给</span><tt>BigDecimal提供语言学上的支持，</tt><tt>BigDecimal也可能比使用基本类型（对大量使用十进制计算的程序比较有用）更慢，大多数情况没有这个需要。</tt>&nbsp; <br />
&nbsp;&nbsp;总结：但需要准确答案的时候，避免使用<tt>float</tt> and <tt>double；金融计算，使用</tt><tt>int, long</tt>, or <tt>BigDecimal。对语言设计者来说，提供十进制计算的语言支持。一个方法是给操作符重载提供有限的支持，<span style="color: #ff0000"><strike>这样计算操作符就能和数字引用类型比如BigDecimal一起工作？</strike></span>。另一种方法就是像COBOL and PL/I一样，提供基本十进制类型。</tt>&nbsp;<br />
&nbsp;3 长整型除法<br />
&nbsp; 被除数表示一天的微秒数，除数表示一天的毫秒数：<br />
&nbsp;public static void main(String[] args) {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; final long MICROS_PER_DAY = 24 * 60 * 60 * 1000 * 1000;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; final long MILLIS_PER_DAY = 24 * 60 * 60 * 1000;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; System.out.println(MICROS_PER_DAY / MILLIS_PER_DAY);<br />
&nbsp;&nbsp;&nbsp; }<br />
你在想程序应该输出<tt>1000</tt>，很不幸输出的是5！问题出在计算<tt>MICROS_PER_DAY</tt>&nbsp;时溢出了，虽然结果是满足long的，但不满足int。这个计算过程全部是按int&nbsp;计算的，计算完之后才转为long。因此很明显计算过程中溢出。为什么会用int计算？因为因子都是int型的，Java没有&nbsp;<span class="docEmphasis">target typing特性（就是根据结果的类型来确定计算过程所用类型）。解决这个问题很简单，把第一个因子设置为long，这样会强制所有以后的计算都用long进行。虽然很多地方都不需要这么做，但这是一个好习惯。<br />
</span>&nbsp;&nbsp;&nbsp; final long MICROS_PER_DAY = 24<span class="docEmphStrong"><span style="color: red"><span class="docEmphStrong">L</span> </span></span>* 60 * 60 * 1000 * 1000;<br />
&nbsp;&nbsp;&nbsp;&nbsp;final long MILLIS_PER_DAY = 24<span style="color: red" class="docEmphStrong">L</span> * 60 * 60 * 1000;<br />
<span style="color: red">我们得到一个教训：和大数据打交道的时候，小心溢出！一个变量能装得下结果，并不代表计算过程中会确保得到正确类型<strong>。<br />
</strong><span style="color: #000000">4 小学生都知道的事情<br />
&nbsp;&nbsp;&nbsp; System.out.println(12345 + 5432l); // 毫无疑问的<tt>66666</tt>？ 看仔细了！输出<tt>17777</tt><br />
&nbsp;&nbsp;&nbsp; 教训：<span style="color: #ff0000">使用long的时候用大写的L，绝不用小写的l，类似的避免用l作为变量名。很难看出输出的是1还是l<br />
</span>// <span class="docEmphStrong">Bad code - uses el (l) as a variable name</span><br />
&nbsp;&nbsp; List&lt;String&gt; l = new ArrayList&lt;String&gt;();<br />
&nbsp;&nbsp; l.add("Foo");<br />
&nbsp;&nbsp; System.out.println(1);<br />
5&nbsp; 十六进制的快乐<br />
&nbsp;&nbsp;&nbsp; System.out.println(Long.toHexString(0x100000000L + 0xcafebabe)); //输出<tt>cafebabe</tt>，最左边的1丢了！<br />
&nbsp;&nbsp; 十进制有一个十六或八进制都没有的优点：数字都是正的，想表达负数需要一个负号。这样的话写十进制的int或long，不管正负都很方便。十六或八进制就不这样了，必须由高位来决定正负。这个例子中，<tt>0xcafebabe</tt> 是一个int常量，最高位是1，因此是负数=十进制&nbsp;<tt>-889275714</tt>。这里还有一个混合类型计算的额外操作：左边操作数是long，右边是int，计算时Java通过<span class="docEmphasis">widening primitive conversion</span> 把int变为long，再加这两个long。因为int是有符号整型，转变执行了一个符号扩展：把负的int值提升为数值相等的long值。右边的<tt>0xcafebabe</tt>被提升为long值&nbsp;<tt>0xffffffffcafebabeL，再加到左边<tt>0x100000000L上</tt>。当被看作int型的时候，<tt>0xcafebabe</tt>扩展出来的高32位是-1，而左边操作数高32位是1，相加之后为0，这解释了为什么最高位的1丢失。解决方法是把右边的操作数也写上long，这样就避免了符号扩展的破坏。<br />
System.out.println(Long.toHexString(0x100000000L + 0xcafebabe<span style="color: red" class="docEmphStrong">L</span>));<br />
教训：考虑十六或八进制自身带正负，混合类型计算让人迷惑。为避免出错，<span style="color: red">最好不要使用混合类型计算</span>。对语言设计者来说，考虑支持无符号整数类型来去掉符号扩展的可能。有人争论十六或八进制负数应该被禁止，但对于程序员来说非常不好，他们经常使用十六进制来表示符号没有意义的数值。<br />
</tt>6 多重映射<br />
&nbsp;&nbsp; System.out.println((int) (char) (byte) -1);<br />
&nbsp;&nbsp;&nbsp; 以<tt>int</tt> 类型的-1开始，映射到byte，到char，最后返回int。第一次32位变到8位，到16位，最后回到32位。最后发现值并没有回到原始！输出<tt>65535</tt><br />
问题来自映射时的符号扩展问题。int值-1的所有32位都是1，转为8位byte很直观，只留下低八位就行，仍然是-1.转char的时候，就要小心了，byte是有符号的，char无符号。通常有可能保留数值的同时把一个整型转到更&#8220;宽&#8221;的类型，但不可能用char来表示一个负的byte值。Therefore, the conversion from <tt>byte</tt> to <tt>char</tt> is not considered a <span class="docEmphasis">widening primitive conversion</span> [JLS 5.1.2], but a <span class="docEmphasis">widening and narrowing primitive conversion</span> [JLS 5.1.4]: The <tt>byte</tt> is converted to an <tt>int</tt> and the <tt>int</tt> to a <tt>char</tt>。看起来较复杂，但有一个简单规则描述窄变宽转换时的符号扩展：<span style="color: red">原始值有符号就做符号扩展；不管转换成什么类型，char只做零扩展。<span style="color: #000000">因为byte是有符号的，byte -1转成char会有符号扩展。结果是全1的16位，也就是&nbsp;2<sup>16</sup> &#8211; 1或65,535。char到int宽扩展，规则告诉我们做零扩展。int类型的结果是<tt>65535</tt>。虽然规则简单，但最好不要写依赖这规则的程序。如果你是宽转换到char，或从char转换（char总是无符号整数），最好显式说明。<br />
</span></span>&nbsp;&nbsp; 如果从char类型的c宽转换，并且不想符号扩展，虽然不需要，但为了清晰可以这样：<br />
&nbsp;&nbsp; int i = c &amp; 0xffff;&nbsp;&nbsp;&nbsp;&nbsp;<br />
还可以写注释：<br />
int i = c; // Sign extension is not performed<br />
&nbsp;如果从char类型的c宽转换，并且想符号扩展，强制char到short（宽度一样但有符号）<br />
int i = (short) c; // Cast causes sign extension<br />
byte到char，不要符号</span></span>，必须用位屏蔽抑制他，这是惯例不用注释(<span style="color: red;">0xff这种0x开头的默认是int类型的？)</span><br />
char c = (char) (b &amp; 0xff);<br />
<tt>byte</tt> to a <tt>char</tt> ，要符号，写注释<br />
char c = (char) b; // Sign extension is performed<br />
这一课很简单：<span style="color: #ff0000">如果你不能清晰看出程序在干什么，他可能就没有按你希望的在运行</span>。拼命寻求清晰，虽然整数转换的符号扩展规则简单，但大多程序员不知道。如果你的程序依赖于他，让你的意图明显。 <br />
7 交换美味<br />
&nbsp;&nbsp; <span style="color: red">在一个简单表达式中，不要对一个变量赋值超过一次。更普遍的说，不要用&#8220;聪明&#8221;的程序技巧。<br />
<span style="color: #000000;">8&nbsp; Dos Equis<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; char x = 'X';<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; int i = 0;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; System.out.print(true&nbsp; ? x : 0);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;System.out.print(false ? i : x);<br />
输出<tt>XX</tt>？可惜输出的是<tt>X88</tt>。注意第二三个操作数类型不一样，第5点说过，混合类型计算让人迷惑！条件表达式中是最明显的地方。虽然觉得两个表达式结果应该相同，毕竟他们类型相似，只是位置相反而已,但结果并不是这样。<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 决定条件表达式结果类型的规则很多，但有三个关键点：<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1 如果第二三个操作数类型一样，表达式也是这个类型，这样就避免了混合类型计算。<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp; 3 复杂略过 总之第一个表达式是调用了<tt>PrintStream.print(char)，第二个是<tt>PrintStream.print(int) 造成结果不同<br />
</tt></tt>&nbsp;&nbsp;&nbsp; 总结：<span style="color: red">最好在条件表达式中第二三个操作数用同一种类型</span><br />
9&nbsp;<br />
&nbsp;&nbsp;&nbsp; x += i;&nbsp; // 等同于x = x + i;？<br />
&nbsp;&nbsp; <span class="docEmphStrong">compound assignment expressions automatically cast the result of the computation they perform to the type of the variable on their left-hand side<span style="color: red">// 暗含映射</span></span><br />
&nbsp;&nbsp;&nbsp; 例如 short x = 0;int i = 123456;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="docEmphStrong">x += i; // &#8211;7,616，int值123456太大，short装不下，高位两个字节被去掉<br />
&nbsp;&nbsp;&nbsp;&nbsp; x = x + i; // 编译错误- "possible loss of precision"<br />
&nbsp;&nbsp;&nbsp; 为避免危险，<span style="color: red">不要在<tt>byte, short</tt>, or <tt>char</tt>上面用复合赋值符</span>。当在int上用时，确保右边不是<tt>long</tt>, <tt>float</tt>, or <tt>double</tt>类型。在<tt>float</tt>上用，确保右边不是double。<br />
10&nbsp; 复合赋值符需要两边操作数都为基本类型或boxed primitives，如int ，<tt>Integer。有一例外：<tt>+=</tt> 左边为String的话，允许右边为任意类型。这时做的是字符串拼接操作。<br />
&nbsp;&nbsp;&nbsp; Object x = "Buy ";<br />
&nbsp;&nbsp;&nbsp; String i = "Effective Java!";<br />
&nbsp;&nbsp;&nbsp; x = x + i; //x+i 为String，和Object兼容，因此表达式正确<br />
&nbsp;&nbsp;&nbsp; x += i; //非法左边不是String<br /><span style="color: red;">注意返回类型：</span><div>The 	  arithmetic, increment and decrement, <span style="color: red;">bitwise</span>, and shift 	  operators return a <tt>double</tt> if at least one 	  of the operands is a <tt>double</tt>. Otherwise, 	  they return a <tt>float</tt> if at least one of the 	  operands is a <tt>float</tt>. Otherwise, they return a 	  <tt>long</tt> if at least one of the operands is a 	  <tt>long</tt>. Otherwise, they return an 	  <tt>int</tt>, even if both operands are 	  <tt>byte</tt>, <tt>short</tt>, or 	  <tt>char</tt> types that are narrower than 	  <tt>int</tt>. </div><br />
</tt></span></span></span><img src ="http://www.blogjava.net/yuxh/aggbug/334085.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/yuxh/" target="_blank">yuxh</a> 2010-10-13 13:27 <a href="http://www.blogjava.net/yuxh/archive/2010/10/13/334085.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Beginning JO(interface/static/final)</title><link>http://www.blogjava.net/yuxh/archive/2009/12/16/305947.html</link><dc:creator>yuxh</dc:creator><author>yuxh</author><pubDate>Wed, 16 Dec 2009 03:16:00 GMT</pubDate><guid>http://www.blogjava.net/yuxh/archive/2009/12/16/305947.html</guid><wfw:comment>http://www.blogjava.net/yuxh/comments/305947.html</wfw:comment><comments>http://www.blogjava.net/yuxh/archive/2009/12/16/305947.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/yuxh/comments/commentRss/305947.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/yuxh/services/trackbacks/305947.html</trackback:ping><description><![CDATA[接口的方法默认为public，不能定义为private。<br />
If a class implements two or more interfaces that call for methods with identical signatures,we need only implement one such method in the implementing class—that method will do &#8220;double duty&#8221; in satisfying both interfaces&#8217; implementation requirements as far as the compiler is concerned.<br />
定义 属性，方法参数，返回类型的时候尽可能使用接口，客户端代码调用这样的类会更加灵活。<br />
List和Set都是Collection接口的子类，使用Collection可以更通用。<br />
&nbsp;&nbsp;&nbsp;&nbsp;如果一定要自己创建集合类而且不通过扩展存在的ArrayList等的话，至少实现Collection接口，这样才能在使用Collection的环境使用。<br />
&nbsp;&nbsp;&nbsp;&nbsp;静态方法不能为abstract，不能调用非静态的属性或方法。我们经常利用静态方法，属性实现一些&#8220;工具类&#8221;，比如java.lang中的Math.<br />
&nbsp;&nbsp;&nbsp;&nbsp;接口不允许定义变量，除了定义public static final 变量来作为全局常量。但是final类型的变量必须显示初始化，且初始化的方法必须是在申明时或者在构造方法中直接赋值，而不能通过调用函数赋值。<br />
&nbsp;&nbsp;&nbsp;&nbsp;j2se 5引入 ：import static Administrator.*; 这样在代码中可以直接使用Administrator类的静态变量。<br />
查询了下关于是否用final限定方法参数以及局部变量的问题，有争议（<a href="http://stackoverflow.com/questions/316352?sort=votes#sort-top">http://stackoverflow.com/questions/316352?sort=votes#sort-top</a>），类似习惯问题，不过对传入的参数重新赋值不是好习惯！否则在方法中使用该参数的时候你会考虑前面的代码是否对参数处理过，还有可能失误的进行了赋值。倾向于方法参数使用final，局部变量不使用。折中的办法是设置eclipse的重赋值警告。
 <img src ="http://www.blogjava.net/yuxh/aggbug/305947.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/yuxh/" target="_blank">yuxh</a> 2009-12-16 11:16 <a href="http://www.blogjava.net/yuxh/archive/2009/12/16/305947.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Beginning JO(2 Collection)</title><link>http://www.blogjava.net/yuxh/archive/2009/12/10/305397.html</link><dc:creator>yuxh</dc:creator><author>yuxh</author><pubDate>Thu, 10 Dec 2009 03:38:00 GMT</pubDate><guid>http://www.blogjava.net/yuxh/archive/2009/12/10/305397.html</guid><wfw:comment>http://www.blogjava.net/yuxh/comments/305397.html</wfw:comment><comments>http://www.blogjava.net/yuxh/archive/2009/12/10/305397.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/yuxh/comments/commentRss/305397.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/yuxh/services/trackbacks/305397.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp;数组可以装基本类型或者引用，collections只能装引用。<br />
&nbsp;&nbsp;&nbsp; 通常有两种方法可以扩展collection 来满足一些需要：继承某种集合类型和封装某种集合类型。第一种的优点是初始化的时候在内存中只产生一个对象，这是继承特性决定的。后者的优点是我们可以方便控制被封装集合的各种属性。<br />
Whenever possible, it&#8217;s desirable to bury implementation details inside of a class rather than exposing client code to such details。例：<br />
法1：<br />
public class Student {<br />
&nbsp;&nbsp;&nbsp;&nbsp;private String name;<br />
&nbsp;&nbsp;&nbsp;&nbsp;private String studentId;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;private ArrayList&lt;TranscriptEntry&gt; transcript; //成绩报告单<br />
&nbsp;&nbsp;&nbsp;&nbsp;public void addTranscriptEntry(TranscriptEntry te) {&nbsp;&nbsp;&nbsp;// 操作transcript达到记录成绩<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;// Store the TranscriptEntry in our ArrayList.<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;transcript.add(te);<br />
&nbsp;&nbsp;&nbsp;&nbsp;}<br />
}<br />
客户端调用代码：<br />
Student s = new Student("1234567", "James Huddleston");<br />
Course c = new Course("LANG 800", "Advanced Language Studies");<br />
TranscriptEntry te = new TranscriptEntry(c, "Fall 2006", "B+");<br />
s.addTranscriptEntry(te);<br />
法2：<br />
建立新对象，封装一个ArrayList：<br />
public class Transcript {<br />
&nbsp;&nbsp;&nbsp;&nbsp;private ArrayList&lt;TranscriptEntry&gt; transcriptEntries;<br />
&nbsp;&nbsp;&nbsp;&nbsp;public void courseCompleted(Course c, String semester, String grade) {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;// Instantiate and insert a brand-new TranscriptEntry object into the<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;// ArrayList - details hidden away!<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;transcriptEntries.add(new TranscriptEntry(c, semester, grade);<br />
&nbsp;&nbsp;&nbsp;&nbsp;}<br />
}<br />
public class Student {<br />
&nbsp;&nbsp;&nbsp;&nbsp;private String name;<br />
&nbsp;&nbsp;&nbsp;&nbsp;private String studentId;<br />
&nbsp;&nbsp;&nbsp;&nbsp;// This used to be declared as an ArrayList.<br />
&nbsp;&nbsp;&nbsp;&nbsp;private Transcript transcript;<br />
&nbsp;&nbsp;&nbsp;&nbsp;// etc.<br />
}<br />
客户端代码：<br />
s.courseCompleted(c, "Spring 2006", "A+");<br />
第二种方法使Student处理更少的细节，不用管transcripts怎么表达，看不到TranscriptEntry的存在。客户端代码更简单。
<img src ="http://www.blogjava.net/yuxh/aggbug/305397.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/yuxh/" target="_blank">yuxh</a> 2009-12-10 11:38 <a href="http://www.blogjava.net/yuxh/archive/2009/12/10/305397.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Beginning Java Objects</title><link>http://www.blogjava.net/yuxh/archive/2009/12/08/305181.html</link><dc:creator>yuxh</dc:creator><author>yuxh</author><pubDate>Tue, 08 Dec 2009 08:57:00 GMT</pubDate><guid>http://www.blogjava.net/yuxh/archive/2009/12/08/305181.html</guid><wfw:comment>http://www.blogjava.net/yuxh/comments/305181.html</wfw:comment><comments>http://www.blogjava.net/yuxh/archive/2009/12/08/305181.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/yuxh/comments/commentRss/305181.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/yuxh/services/trackbacks/305181.html</trackback:ping><description><![CDATA[<strong>Aggregation</strong> is a special form of association, alternatively referred to as the &#8220;consists of&#8221;, &#8220;is composed of&#8221;, or &#8220;has a&#8221; relationship.Like an association, an aggregation is used to represent a relationship between two classes, A and B. But, with an aggregation, we&#8217;re representing more than mere relationship: we&#8217;re stating that an object belonging to class A, known as an <strong>aggregate</strong>,is composed of, or contains, <strong>component objects</strong> belonging to class B.<br />
&nbsp; Note that these aggregation statements appear very similar to associations, where the name of the association just so happens to be is composed of or contains. That&#8217;s because an aggregation is an association in the broad sense of the term（aggregation 是association的一种特殊表现形式）!aggregation&nbsp;和associations UML表现不同但最终代码表现形式一样<br />
&nbsp; <strong>Composition</strong> is a strong form of aggregation, in which the &#8220;parts&#8221; cannot exist without the &#8220;whole.&#8221; As an example, given the relationship &#8220;a Book is composed of many Chapters&#8221;, we could argue that a chapter cannot exist if the book to which it belongs ceases to exist; whereas given the relationship &#8220;a Car is composed of many Wheels&#8221;, we know that a wheel can be removed from a car and still serve a useful purpose. Thus, we&#8217;d categorize the Book&#8211;Chapter relationship as composition and the Car&#8211;Wheel relationship as aggregation.<br />
继承没留意的好处：<br />
&#8226; <strong>Best of all, we can derive a new class from an existing class even if we don&#8217;t own the source code for the latter</strong>! As long as we have the compiled bytecode version of a class, the inheritance mechanism works just fine; we don&#8217;t need the original source code of a class in order to extend it. <strong>This is one of the most dramatic ways to achieve productivity with an objectoriented language</strong>: find a class (either one written by someone else or one that is built into the language) that does much of what you need, and create a subclass of that class,adding just those features that you need for your own purposes.<br />
&#8226; <strong>classification is the natural way that humans organize information</strong>; so, it only makes sense that we&#8217;d organize software along the same lines, making it much more intuitive and hence easier to develop, maintain,extend, and communicate with users about.<br />
继承与Association, aggregation异同（P186）：<br />
Association, aggregation, and inheritance are all said to be relationships between classes. Where inheritance differs from association and aggregation is at the <strong>object</strong> level.inheritance is indeed a relationship between <strong>classes</strong>, but <strong>not</strong> between distinct <strong>objects</strong>.<br />
<span style="color: red">注意</span>：避免连锁反应，<em>Whenever possible, avoid adding features to non-leaf classes once they have been established in code form in an application, to avoid ripple effects throughout an inheritance hierarchy</em>. 说比做容易，这就要求在编码之前尽可多的花时间在需求分析和对象建模阶段，虽然不能避免新需求出现，但至少避免忽视遗漏了当前的需求。 <br />
&nbsp;&nbsp;&nbsp;&nbsp; Overriding:子类继承父类，重写唯一能改变的是方法的访问控制，而且只能比父类更宽松，如父类用的是private，子类可以用public。参考了下thinking in java 4th P202 发现这种说法不对，而且是一个<span style="color: red">陷阱</span>：父类的该方法根本对子类不可见！子类的该方法实际上是一个全新的方法，连重载都算不上。所以只能重写non-private方法。遇到private方法你得小心，没有编译错误，但不会像你想象的工作，最好给方法重新取名，避免陷阱。<br />
<br />
不要做的事情：<br />
<strong>We shouldn&#8217;t change the semantics—that is, the intention, or meaning—of a feature</strong>.For example:<br />
&#8226; If the print method of a superclass such as Student is intended to display the values of all of an object&#8217;s attributes in the command window, then the print method of a subclass such as GraduateStudent shouldn&#8217;t, for example, be overridden so that it directs all of its output to a file instead.<br />
&#8226; If the name attribute of a superclass such as Person is intended to store a person&#8217;s name in &#8220;last name, first name&#8221; order, then the name attribute of a subclass such as Student should be used in the same fashion.<br />
&nbsp; We can&#8217;t physically eliminate features, nor should we effectively eliminate them by ignoring them. To attempt to do so would break the spirit of the &#8220;is a&#8221; hierarchy. By definition, inheritance requires that all features of all ancestors of a class A must also apply to class A itself in order for A to truly be a proper subclass. If a GraduateStudent could eliminate the degreeSought attribute&nbsp;that it inherits from Student, for example, is a GraduateStudent really a Student after all? Strictly speaking, the answer is no.<br />
&nbsp; 进一步从实用角度说，如果我们重写一个方法但不在这方法里做任何事情，其他继承我们类的人就会出问题：他们觉得我们的方法是有意义的（特别是他们不能看到我们源代码的时候）。而我们则打破了&#8220;is a&#8221; 原则，所以绝不要这样做！<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; protected关键字的运用，用于控制继承的访问控制。<br />
&nbsp;&nbsp;&nbsp;&nbsp; 运用super(arguments) 减少子类构造函数重复父类构造函数代码，和this类似必须在构造函数最开始调用。<br />
Student s = new Student("Fred", "123-45-6789"); 执行这段代码，Object的构造函数会自动执行，接着Student 的父类Person构造函数仔细，然后是我们调用的Student构造函数，如果调用的Student构造函数没有显示调用父类构造函数，则相当于默认调用super() 。<br />
java没有类的多继承，多继承很复杂的一点，如果两个父类都有相同的方法，子类怎么处理？ 
<img src ="http://www.blogjava.net/yuxh/aggbug/305181.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/yuxh/" target="_blank">yuxh</a> 2009-12-08 16:57 <a href="http://www.blogjava.net/yuxh/archive/2009/12/08/305181.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>构造函数的一些用法</title><link>http://www.blogjava.net/yuxh/archive/2009/12/06/304944.html</link><dc:creator>yuxh</dc:creator><author>yuxh</author><pubDate>Sun, 06 Dec 2009 14:19:00 GMT</pubDate><guid>http://www.blogjava.net/yuxh/archive/2009/12/06/304944.html</guid><wfw:comment>http://www.blogjava.net/yuxh/comments/304944.html</wfw:comment><comments>http://www.blogjava.net/yuxh/archive/2009/12/06/304944.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/yuxh/comments/commentRss/304944.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/yuxh/services/trackbacks/304944.html</trackback:ping><description><![CDATA[<p>We may wish to instantiate additional objects related to the Student object:<br />
初始化与对象相关的一些额外对象：<br />
public class Student() {<br />
&nbsp;&nbsp;&nbsp;&nbsp;// Every Student maintains a handle on his/her own individual Transcript object.<br />
&nbsp;&nbsp;&nbsp;&nbsp;private Transcript transcript;<br />
&nbsp;&nbsp;&nbsp;&nbsp;public Student() {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;// Create a new Transcript object for this new Student.<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;transcript = new Transcript();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;// etc.<br />
&nbsp;&nbsp;&nbsp;&nbsp;}<br />
}<br />
读取数据库来初始化对象属性：<br />
public class Student {<br />
&nbsp;&nbsp;&nbsp;&nbsp;// Attributes.<br />
&nbsp;&nbsp;&nbsp;&nbsp;String studentId;<br />
&nbsp;&nbsp;&nbsp;&nbsp;String name;<br />
&nbsp;&nbsp;&nbsp;&nbsp;double gpa;<br />
&nbsp;&nbsp;&nbsp;&nbsp;// etc.<br />
&nbsp;&nbsp;&nbsp;&nbsp;// Constructor.<br />
&nbsp;&nbsp;&nbsp;&nbsp;public Student(String id) {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;studentId = id;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;// Pseudocode.<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;use studentId as a primary key to retrieve data from the Student table of a<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;relational database;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if (studentId found in Student table) {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;retrieve all data in the Student record;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;name = name retrieved from database;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;gpa = value retrieved from database;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;// etc.<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;&nbsp;&nbsp;}<br />
// etc.<br />
}</p>
<p>和其他已存在的对象交流：<br />
public class Student {<br />
&nbsp;&nbsp;&nbsp;&nbsp;// Details omitted.<br />
&nbsp;&nbsp;&nbsp;&nbsp;// Constructor.<br />
&nbsp;&nbsp;&nbsp;&nbsp;public Student(String major) {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;// Alert the student's designated major department that a new student has<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;// joined the university.<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;// Pseudocode.<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;majorDept.notify(about this student ...);<br />
&nbsp;&nbsp;&nbsp;&nbsp;// etc.<br />
&nbsp;&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;&nbsp;&nbsp;// etc.<br />
}<br />
<br />
好习惯：如果需要有参数的构造函数，最好同时显示声明一个无参构造函数。<br />
<span style="color: red">容易出现的bug</span>：如果给构造函数加上void编译会通过！不过会被当作方法而不是构造函数！<br />
当有多个构造函数，而且都有共同的初始化内容时，就会出现很多重复的代码，比如构造一个新学生，我们会做：<br />
1 通知登记办公室学生的存在<br />
2 给学生创建学生成绩报告单<br />
重复引起以后修改必须修改多处，如果使用this 会得到改善</p>
<p>public class Student {<br />
&nbsp;// Attribute details omitted.<br />
&nbsp;// Constructor #1.<br />
&nbsp;public Student() {<br />
// Assign default values to selected attributes ... details omitted.<br />
// Do the things common to all three constructors in this first<br />
// constructor ...<br />
// Pseudocode.<br />
alert the registrar's office of this student's existence<br />
// Create a transcript for this student.<br />
transcript = new Transcript();<br />
}</p>
<p>&nbsp;// Constructor #2.<br />
&nbsp;public Student(String s) {<br />
&nbsp;&nbsp;// ... then, REUSE the code of the first constructor within the second!<br />
&nbsp;&nbsp;this();<br />
&nbsp;&nbsp;// Then, do whatever else extra is necessary for constructor #2.<br />
&nbsp;&nbsp;this.setSsn(s);<br />
&nbsp;}</p>
<p>&nbsp;// Constructor #3.<br />
&nbsp;public Student(String s, String n, int i) {<br />
&nbsp;&nbsp;// ... and REUSE the code of the first constructor within the third!<br />
&nbsp;&nbsp;this();<br />
&nbsp;&nbsp;// Then, do whatever else extra is necessary for constructor #3.<br />
&nbsp;&nbsp;this.setSsn(s);<br />
&nbsp;&nbsp;this.setName(n);<br />
&nbsp;&nbsp;this.setAge(i);<br />
&nbsp;}<br />
&nbsp;// etc.<br />
}</p>
<span style="color: red">注意：this必须在方法最前面调用</span>
<img src ="http://www.blogjava.net/yuxh/aggbug/304944.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/yuxh/" target="_blank">yuxh</a> 2009-12-06 22:19 <a href="http://www.blogjava.net/yuxh/archive/2009/12/06/304944.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>