﻿<?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-海阔天空-文章分类-java_base</title><link>http://www.blogjava.net/shiliqiang/category/40533.html</link><description>I'm on my way!</description><language>zh-cn</language><lastBuildDate>Tue, 25 Aug 2009 23:26:14 GMT</lastBuildDate><pubDate>Tue, 25 Aug 2009 23:26:14 GMT</pubDate><ttl>60</ttl><item><title>优化你的java代码性能</title><link>http://www.blogjava.net/shiliqiang/articles/292453.html</link><dc:creator>石头@</dc:creator><author>石头@</author><pubDate>Mon, 24 Aug 2009 13:40:00 GMT</pubDate><guid>http://www.blogjava.net/shiliqiang/articles/292453.html</guid><wfw:comment>http://www.blogjava.net/shiliqiang/comments/292453.html</wfw:comment><comments>http://www.blogjava.net/shiliqiang/articles/292453.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/shiliqiang/comments/commentRss/292453.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/shiliqiang/services/trackbacks/292453.html</trackback:ping><description><![CDATA[<div class="xspace-itemmessage" id="xspace-item97509">
<h3><font color="#990033">一、避免在循环条件中使用复杂表达式</font></h3>
<br />
<font color="#990033">在不做编译优化的情况下，在循环中，循环条件会被反复计算，如果不使用复杂表达式，而使循环条件值不变的话，程序将会运行的更快。<br />
<br />
例子：<br />
import java.util.Vector;<br />
class CEL {<br />
&nbsp;&nbsp;&nbsp;&nbsp;void method (Vector vector) {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;for (int i = 0; i &lt; vector.size (); i++)&nbsp;&nbsp;// Violation<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;; // ...<br />
&nbsp;&nbsp;&nbsp;&nbsp;}<br />
}<br />
<br />
更正：<br />
class CEL_fixed {<br />
&nbsp;&nbsp;&nbsp;&nbsp;void method (Vector vector) {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;int size = vector.size ()<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;for (int i = 0; i &lt; size; i++)<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;; // ...<br />
&nbsp;&nbsp;&nbsp;&nbsp;}<br />
}<br />
<br />
</font>
<h3><font color="#990033">二、为'Vectors' 和 'Hashtables'定义初始大小</font></h3>
<br />
<font color="#990033">JVM为Vector扩充大小的时候需要重新创建一个更大的数组，将原原先数组中的内容复制过来，最后，原先的数组再被回收。可见Vector容量的扩大是一个颇费时间的事。<br />
通常，默认的10个元素大小是不够的。你最好能准确的估计你所需要的最佳大小。<br />
<br />
例子：<br />
import java.util.Vector;<br />
public class DIC {<br />
&nbsp;&nbsp;&nbsp;&nbsp;public void addObjects (Object[] o) {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;// if length &gt; 10, Vector needs to expand <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;for (int i = 0; i&lt; o.length;i++) {&nbsp;&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;v.add(o);&nbsp;&nbsp;&nbsp;// capacity before it can add more elements.<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;&nbsp;&nbsp;public Vector v = new Vector();&nbsp;&nbsp;// no initialCapacity.<br />
}<br />
<br />
更正：<br />
自己设定初始大小。<br />
&nbsp;&nbsp;&nbsp;&nbsp;public Vector v = new Vector(20);&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;public Hashtable hash = new Hashtable(10); <br />
<br />
参考资料：<br />
Dov Bulka, "Java Performance and Scalability Volume 1: Server-Side Programming <br />
Techniques" Addison Wesley, ISBN: 0-201-70429-3 pp.55 &#8211; 57<br />
<br />
</font>
<h3><font color="#990033">三、在finally块中关闭Stream</font></h3>
<br />
<font color="#990033">程序中使用到的资源应当被释放，以避免资源泄漏。这最好在finally块中去做。不管程序执行的结果如何，finally块总是会执行的，以确保资源的正确关闭。<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br />
例子：<br />
import java.io.*;<br />
public class CS {<br />
&nbsp;&nbsp;&nbsp;&nbsp;public static void main (String args[]) {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;CS cs = new CS ();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;cs.method ();<br />
&nbsp;&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;&nbsp;&nbsp;public void method () {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;try {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;FileInputStream fis = new FileInputStream ("CS.java");<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;int count = 0;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;while (fis.read () != -1)<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;count++;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;System.out.println (count);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;fis.close ();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;} catch (FileNotFoundException e1) {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;} catch (IOException e2) {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;&nbsp;&nbsp;}<br />
}<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br />
更正：<br />
在最后一个catch后添加一个finally块<br />
<br />
参考资料：<br />
Peter Haggar: "Practical Java - Programming Language Guide".<br />
Addison Wesley, 2000, pp.77-79<br />
</font>
<h3><font color="#990033">四、使用'System.arraycopy ()'代替通过来循环复制数组</font></h3>
<br />
<font color="#990033">'System.arraycopy ()' 要比通过循环来复制数组快的多。<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br />
例子：<br />
public class IRB<br />
{<br />
&nbsp;&nbsp;&nbsp;&nbsp;void method () {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;int[] array1 = new int [100];<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;for (int i = 0; i &lt; array1.length; i++) {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;array1 [i] = i;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;int[] array2 = new int [100];<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;for (int i = 0; i &lt; array2.length; i++) {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;array2 [i] = array1 [i];&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;// Violation<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;&nbsp;&nbsp;}<br />
}<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br />
更正：<br />
public class IRB<br />
{<br />
&nbsp;&nbsp;&nbsp;&nbsp;void method () {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;int[] array1 = new int [100];<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;for (int i = 0; i &lt; array1.length; i++) {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;array1 [i] = i;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;int[] array2 = new int [100];<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;System.arraycopy(array1, 0, array2, 0, 100);<br />
&nbsp;&nbsp;&nbsp;&nbsp;}<br />
}<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br />
参考资料：<br />
http://www.cs.cmu.edu/~jch/java/speed.html<br />
<br />
</font>
<h3><font color="#990033">五、让访问实例内变量的getter/setter方法变成&#8221;final&#8221;</font></h3>
<br />
<font color="#990033">简单的getter/setter方法应该被置成final，这会告诉编译器，这个方法不会被重载，所以，可以变成&#8221;inlined&#8221;<br />
<br />
例子：<br />
class MAF {<br />
&nbsp;&nbsp;&nbsp;&nbsp;public void setSize (int size) {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;_size = size;<br />
&nbsp;&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;&nbsp;&nbsp;private int _size;<br />
}<br />
<br />
更正：<br />
class DAF_fixed {<br />
&nbsp;&nbsp;&nbsp;&nbsp;final public void setSize (int size) {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;_size = size;<br />
&nbsp;&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;&nbsp;&nbsp;private int _size;<br />
}<br />
<br />
参考资料：<br />
Warren N. and Bishop P. (1999), "Java in Practice", p. 4-5<br />
Addison-Wesley, ISBN 0-201-36065-9<br />
<br />
</font>
<h3><font color="#990033">六、避免不需要的instanceof操作</font></h3>
<br />
<font color="#990033">如果左边的对象的静态类型等于右边的，instanceof表达式返回永远为true。<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br />
例子：&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br />
public class UISO {<br />
&nbsp;&nbsp;&nbsp;&nbsp;public UISO () {}<br />
}<br />
class Dog extends UISO {<br />
&nbsp;&nbsp;&nbsp;&nbsp;void method (Dog dog, UISO u) {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Dog d = dog;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if (d instanceof UISO) // always true.<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;System.out.println("Dog is a UISO"); <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;UISO uiso = u;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if (uiso instanceof Object) // always true.<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;System.out.println("uiso is an Object"); <br />
&nbsp;&nbsp;&nbsp;&nbsp;}<br />
}<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br />
更正：&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br />
删掉不需要的instanceof操作。<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br />
class Dog extends UISO {<br />
&nbsp;&nbsp;&nbsp;&nbsp;void method () {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Dog d;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;System.out.println ("Dog is an UISO");<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;System.out.println ("UISO is an UISO");<br />
&nbsp;&nbsp;&nbsp;&nbsp;}<br />
}<br />
<br />
</font>
<h3><font color="#990033">七、避免不需要的造型操作</font></h3>
<br />
<font color="#990033">所有的类都是直接或者间接继承自Object。同样，所有的子类也都隐含的&#8220;等于&#8221;其父类。那么，由子类造型至父类的操作就是不必要的了。<br />
例子：<br />
class UNC {<br />
&nbsp;&nbsp;&nbsp;&nbsp;String _id = "UNC";<br />
}<br />
class Dog extends UNC {<br />
&nbsp;&nbsp;&nbsp;&nbsp;void method () {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Dog dog = new Dog ();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;UNC animal = (UNC)dog;&nbsp;&nbsp;// not necessary.<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Object o = (Object)dog;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;// not necessary.<br />
&nbsp;&nbsp;&nbsp;&nbsp;}<br />
}<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br />
更正：&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br />
class Dog extends UNC {<br />
&nbsp;&nbsp;&nbsp;&nbsp;void method () {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Dog dog = new Dog();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;UNC animal = dog;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Object o = dog;<br />
&nbsp;&nbsp;&nbsp;&nbsp;}<br />
}<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br />
参考资料：<br />
Nigel Warren, Philip Bishop: "Java in Practice - Design Styles and Idioms<br />
for Effective Java".&nbsp;&nbsp;Addison-Wesley, 1999. pp.22-23<br />
</font>
<h3><font color="#990033">八、如果只是查找单个字符的话，用charAt()代替startsWith()</font></h3>
<br />
<font color="#990033">用一个字符作为参数调用startsWith()也会工作的很好，但从性能角度上来看，调用用String API无疑是错误的!<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br />
例子：<br />
public class PCTS {<br />
&nbsp;&nbsp;&nbsp;&nbsp;private void method(String s) {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if (s.startsWith("a")) { // violation<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;// ...<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;&nbsp;&nbsp;}<br />
}<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br />
更正&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br />
将'startsWith()' 替换成'charAt()'.<br />
public class PCTS {<br />
&nbsp;&nbsp;&nbsp;&nbsp;private void method(String s) {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if ('a' == s.charAt(0)) {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;// ...<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;&nbsp;&nbsp;}<br />
}<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br />
参考资料：<br />
Dov Bulka, "Java Performance and Scalability Volume 1: Server-Side Programming <br />
Techniques"&nbsp;&nbsp;Addison Wesley, ISBN: 0-201-70429-3<br />
</font>
<h3><font color="#990033">九、使用移位操作来代替'a / b'操作 </font></h3>
<br />
<font color="#990033">"/"是一个很&#8220;昂贵&#8221;的操作，使用移位操作将会更快更有效。<br />
<br />
例子：<br />
public class SDIV {<br />
&nbsp;&nbsp;&nbsp;&nbsp;public static final int NUM = 16;<br />
&nbsp;&nbsp;&nbsp;&nbsp;public void calculate(int a) {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;int div = a / 4;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;// should be replaced with "a &gt;&gt; 2".<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;int div2 = a / 8;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;// should be replaced with "a &gt;&gt; 3".<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;int temp = a / 3;<br />
&nbsp;&nbsp;&nbsp;&nbsp;}<br />
}<br />
<br />
更正：<br />
public class SDIV {<br />
&nbsp;&nbsp;&nbsp;&nbsp;public static final int NUM = 16;<br />
&nbsp;&nbsp;&nbsp;&nbsp;public void calculate(int a) {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;int div = a &gt;&gt; 2;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;int div2 = a &gt;&gt; 3; <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;int temp = a / 3;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;// 不能转换成位移操作<br />
&nbsp;&nbsp;&nbsp;&nbsp;}<br />
}<br />
<br />
</font>
<h3><font color="#990033">十、使用移位操作代替'a * b' </font></h3>
<br />
<font color="#990033">同上。<br />
[i]但我个人认为，除非是在一个非常大的循环内，性能非常重要，而且你很清楚你自己在做什么，方可使用这种方法。否则提高性能所带来的程序晚读性的降低将是不合算的。<br />
<br />
例子：<br />
public class SMUL {<br />
&nbsp;&nbsp;&nbsp;&nbsp;public void calculate(int a) {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;int mul = a * 4;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;// should be replaced with "a &lt;&lt; 2".<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;int mul2 = 8 * a;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;// should be replaced with "a &lt;&lt; 3".<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;int temp = a * 3;<br />
&nbsp;&nbsp;&nbsp;&nbsp;}<br />
}<br />
<br />
更正：<br />
package OPT;<br />
public class SMUL {<br />
&nbsp;&nbsp;&nbsp;&nbsp;public void calculate(int a) {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;int mul = a &lt;&lt; 2;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;int mul2 = a &lt;&lt; 3; <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;int temp = a * 3;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;// 不能转换<br />
&nbsp;&nbsp;&nbsp;&nbsp;}<br />
}<br />
<br />
</font>
<h3><font color="#990033">十一、在字符串相加的时候，使用 ' ' 代替 " "，如果该字符串只有一个字符的话 </font></h3>
<br />
<br />
<font color="#990033">例子：<br />
public class STR {<br />
&nbsp;&nbsp;&nbsp;&nbsp;public void method(String s) {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;String string = s + "d"&nbsp;&nbsp;// violation.<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;string = "abc" + "d"&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;// violation.<br />
&nbsp;&nbsp;&nbsp;&nbsp;}<br />
}<br />
<br />
更正：<br />
将一个字符的字符串替换成' '<br />
public class STR {<br />
&nbsp;&nbsp;&nbsp;&nbsp;public void method(String s) {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;String string = s + 'd'<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;string = "abc" + 'd'&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;}<br />
}<br />
<br />
</font>
<h3><font color="#990033">十二、不要在循环中调用synchronized(同步)方法 </font></h3>
<br />
<font color="#990033">方法的同步需要消耗相当大的资料，在一个循环中调用它绝对不是一个好主意。<br />
<br />
例子：<br />
import java.util.Vector;<br />
public class SYN {<br />
&nbsp;&nbsp;&nbsp;&nbsp;public synchronized void method (Object o) {<br />
&nbsp;&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;&nbsp;&nbsp;private void test () {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;for (int i = 0; i &lt; vector.size(); i++) {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;method (vector.elementAt(i));&nbsp;&nbsp;&nbsp;&nbsp;// violation<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;&nbsp;&nbsp;private Vector vector = new Vector (5, 5);<br />
}<br />
<br />
更正：<br />
不要在循环体中调用同步方法，如果必须同步的话，推荐以下方式：<br />
import java.util.Vector;<br />
public class SYN {<br />
&nbsp;&nbsp;&nbsp;&nbsp;public void method (Object o) {<br />
&nbsp;&nbsp;&nbsp;&nbsp;}<br />
private void test () {<br />
&nbsp;&nbsp;&nbsp;&nbsp;synchronized{//在一个同步块中执行非同步方法<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;for (int i = 0; i &lt; vector.size(); i++) {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;method (vector.elementAt(i));&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;&nbsp;&nbsp;private Vector vector = new Vector (5, 5);<br />
}<br />
<br />
</font>
<h3><font color="#990033">十三、将try/catch块移出循环</font></h3>
<br />
<font color="#990033">把try/catch块放入循环体内，会极大的影响性能，如果编译JIT被关闭或者你所使用的是一个不带JIT的JVM，性能会将下降21%之多!<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br />
例子：&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br />
import java.io.FileInputStream;<br />
public class TRY {<br />
&nbsp;&nbsp;&nbsp;&nbsp;void method (FileInputStream fis) {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;for (int i = 0; i &lt; size; i++) {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;try {&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;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;// violation<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;_sum += fis.read();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;} catch (Exception e) {}<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;&nbsp;&nbsp;private int _sum;<br />
}<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br />
更正：&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br />
将try/catch块移出循环&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;void method (FileInputStream fis) {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;try {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;for (int i = 0; i &lt; size; i++) {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;_sum += fis.read();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&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 />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br />
参考资料：<br />
Peter Haggar: "Practical Java - Programming Language Guide".<br />
Addison Wesley, 2000, pp.81 &#8211; 83<br />
<br />
</font>
<h3><font color="#990033">十四、对于boolean值，避免不必要的等式判断</font></h3>
<br />
<font color="#990033">将一个boolean值与一个true比较是一个恒等操作(直接返回该boolean变量的值). 移走对于boolean的不必要操作至少会带来2个好处：<br />
1)代码执行的更快 (生成的字节码少了5个字节)；<br />
2)代码也会更加干净 。<br />
<br />
例子：<br />
public class UEQ<br />
{<br />
&nbsp;&nbsp;&nbsp;&nbsp;boolean method (String string) {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return string.endsWith ("a") == true;&nbsp;&nbsp;&nbsp;// Violation<br />
&nbsp;&nbsp;&nbsp;&nbsp;}<br />
}<br />
<br />
更正：<br />
class UEQ_fixed<br />
{<br />
&nbsp;&nbsp;&nbsp;&nbsp;boolean method (String string) {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return string.endsWith ("a");<br />
&nbsp;&nbsp;&nbsp;&nbsp;}<br />
}<br />
<br />
</font>
<h3><font color="#990033">十五、对于常量字符串，用'String' 代替 'StringBuffer' </font></h3>
<br />
<font color="#990033">常量字符串并不需要动态改变长度。<br />
例子：<br />
public class USC {<br />
&nbsp;&nbsp;&nbsp;&nbsp;String method () {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;StringBuffer s = new StringBuffer ("Hello");<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;String t = s + "World!";<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return t;<br />
&nbsp;&nbsp;&nbsp;&nbsp;}<br />
}<br />
<br />
更正：<br />
把StringBuffer换成String，如果确定这个String不会再变的话，这将会减少运行开销提高性能。<br />
<br />
</font>
<h3><font color="#990033">十六、用'StringTokenizer' 代替 'indexOf()' 和'substring()' </font></h3>
<br />
<font color="#990033">字符串的分析在很多应用中都是常见的。使用indexOf()和substring()来分析字符串容易导致StringIndexOutOfBoundsException。而使用StringTokenizer类来分析字符串则会容易一些，效率也会高一些。<br />
<br />
例子：<br />
public class UST {<br />
&nbsp;&nbsp;&nbsp;&nbsp;void parseString(String string) {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;int index = 0;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;while ((index = string.indexOf(".", index)) != -1) {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;System.out.println (string.substring(index, string.length()));<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;&nbsp;&nbsp;}<br />
}<br />
<br />
参考资料：<br />
Graig Larman, Rhett Guthrie: "Java 2 Performance and Idiom Guide"<br />
Prentice Hall PTR, ISBN: 0-13-014260-3 pp. 282 &#8211; 283<br />
<br />
</font>
<h3><font color="#990033">十七、使用条件操作符替代"if (cond) return; else return;" 结构</font></h3>
<br />
<font color="#990033">条件操作符更加的简捷<br />
例子：<br />
public class IF {<br />
&nbsp;&nbsp;&nbsp;&nbsp;public int method(boolean isDone) {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if (isDone) { <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return 0;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;} else {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return 10;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;&nbsp;&nbsp;}<br />
}<br />
<br />
更正：<br />
public class IF {<br />
&nbsp;&nbsp;&nbsp;&nbsp;public int method(boolean isDone) {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return (isDone ? 0 : 10);<br />
&nbsp;&nbsp;&nbsp;&nbsp;}<br />
}<br />
</font>
<h3><font color="#990033">十八、使用条件操作符代替"if (cond) a = b; else a = c;" 结构</font></h3>
<br />
<font color="#990033">例子：<br />
public class IFAS {<br />
&nbsp;&nbsp;&nbsp;&nbsp;void method(boolean isTrue) {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if (isTrue) { <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;_value = 0;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;} else {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;_value = 1;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;&nbsp;&nbsp;private int _value = 0;<br />
}<br />
<br />
更正：<br />
public class IFAS {<br />
&nbsp;&nbsp;&nbsp;&nbsp;void method(boolean isTrue) {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;_value = (isTrue ? 0 : 1);&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;// compact expression.<br />
&nbsp;&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;&nbsp;&nbsp;private int _value = 0;<br />
}<br />
<br />
</font>
<h3><font color="#990033">十九、不要在循环体中实例化变量</font></h3>
<br />
<font color="#990033">在循环体中实例化临时变量将会增加内存消耗<br />
<br />
例子：&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br />
import java.util.Vector;<br />
public class LOOP {<br />
&nbsp;&nbsp;&nbsp;&nbsp;void method (Vector v) {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;for (int i=0;i &lt; v.size();i++) {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Object o = new Object();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;o = v.elementAt(i);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;&nbsp;&nbsp;}<br />
}<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br />
更正：&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br />
在循环体外定义变量，并反复使用&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br />
import java.util.Vector;<br />
public class LOOP {<br />
&nbsp;&nbsp;&nbsp;&nbsp;void method (Vector v) {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Object o;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;for (int i=0;i&lt;v.size();i++) {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;o = v.elementAt(i);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;&nbsp;&nbsp;}<br />
}<br />
<br />
</font>
<h3><font color="#990033">二十、确定 StringBuffer的容量</font></h3>
<br />
<font color="#990033">StringBuffer
的构造器会创建一个默认大小(通常是16)的字符数组。在使用中，如果超出这个大小，就会重新分配内存，创建一个更大的数组，并将原先的数组复制过来，再
丢弃旧的数组。在大多数情况下，你可以在创建 StringBuffer的时候指定大小，这样就避免了在容量不够的时候自动增长，以提高性能。<br />
<br />
例子：&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br />
public class RSBC {<br />
&nbsp;&nbsp;&nbsp;&nbsp;void method () {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;StringBuffer buffer = new StringBuffer(); // violation<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;buffer.append ("hello");<br />
&nbsp;&nbsp;&nbsp;&nbsp;}<br />
}<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br />
更正：&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br />
为StringBuffer提供寝大小。&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br />
public class RSBC {<br />
&nbsp;&nbsp;&nbsp;&nbsp;void method () {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;StringBuffer buffer = new StringBuffer(MAX);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;buffer.append ("hello");<br />
&nbsp;&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;&nbsp;&nbsp;private final int MAX = 100;<br />
}<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br />
参考资料：<br />
Dov Bulka, "Java Performance and Scalability Volume 1: Server-Side Programming <br />
Techniques" Addison Wesley, ISBN: 0-201-70429-3 p.30 &#8211; 31<br />
<br />
</font>
<h3><font color="#990033">二十一、尽可能的使用栈变量</font></h3>
<br />
<font color="#990033">如果一个变量需要经常访问，那么你就需要考虑这个变量的作用域了。static? local?还是实例变量？访问静态变量和实例变量将会比访问局部变量多耗费2-3个时钟周期。<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br />
例子：<br />
public class USV {<br />
&nbsp;&nbsp;&nbsp;&nbsp;void getSum (int[] values) {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;for (int i=0; i &lt; value.length; i++) {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;_sum += value[i];&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;// violation.<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;&nbsp;&nbsp;void getSum2 (int[] values) {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;for (int i=0; i &lt; value.length; i++) {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;_staticSum += value[i];<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;&nbsp;&nbsp;private int _sum;<br />
&nbsp;&nbsp;&nbsp;&nbsp;private static int _staticSum;<br />
}&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br />
更正：&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br />
如果可能，请使用局部变量作为你经常访问的变量。<br />
你可以按下面的方法来修改getSum()方法：&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br />
void getSum (int[] values) {<br />
&nbsp;&nbsp;&nbsp;&nbsp;int sum = _sum;&nbsp;&nbsp;// temporary local variable.<br />
&nbsp;&nbsp;&nbsp;&nbsp;for (int i=0; i &lt; value.length; i++) {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;sum += value[i];<br />
&nbsp;&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;&nbsp;&nbsp;_sum = sum;<br />
}<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br />
参考资料：&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br />
Peter Haggar: "Practical Java - Programming Language Guide".<br />
Addison Wesley, 2000, pp.122 &#8211; 125<br />
<br />
</font>
<h3><font color="#990033">二十二、不要总是使用取反操作符(!)</font></h3>
<br />
<font color="#990033">取反操作符(!)降低程序的可读性，所以不要总是使用。<br />
<br />
例子：<br />
public class DUN {<br />
&nbsp;&nbsp;&nbsp;&nbsp;boolean method (boolean a, boolean b) {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if (!a)<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return !a;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;else<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return !b;<br />
&nbsp;&nbsp;&nbsp;&nbsp;}<br />
}<br />
<br />
更正：<br />
如果可能不要使用取反操作符(!)<br />
<br />
</font>
<h3><font color="#990033">二十三、与一个接口 进行instanceof操作</font></h3>
<br />
<font color="#990033">基于接口的设计通常是件好事，因为它允许有不同的实现，而又保持灵活。只要可能，对一个对象进行instanceof操作，以判断它是否某一接口要比是否某一个类要快。<br />
<br />
例子：<br />
public class INSOF {<br />
&nbsp;&nbsp;&nbsp;&nbsp;private void method (Object o) {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if (o instanceof InterfaceBase) { }&nbsp;&nbsp;// better<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if (o instanceof ClassBase) { }&nbsp;&nbsp;&nbsp;// worse.<br />
&nbsp;&nbsp;&nbsp;&nbsp;}<br />
}<br />
<br />
class ClassBase {}<br />
interface InterfaceBase {}<br />
<br />
摘自：http://www.51testing.com/?88979/spacelist-blog-page-2.html<br />
</font></div>
<img src ="http://www.blogjava.net/shiliqiang/aggbug/292453.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/shiliqiang/" target="_blank">石头@</a> 2009-08-24 21:40 <a href="http://www.blogjava.net/shiliqiang/articles/292453.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>java内存泄漏</title><link>http://www.blogjava.net/shiliqiang/articles/292452.html</link><dc:creator>石头@</dc:creator><author>石头@</author><pubDate>Mon, 24 Aug 2009 13:39:00 GMT</pubDate><guid>http://www.blogjava.net/shiliqiang/articles/292452.html</guid><wfw:comment>http://www.blogjava.net/shiliqiang/comments/292452.html</wfw:comment><comments>http://www.blogjava.net/shiliqiang/articles/292452.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/shiliqiang/comments/commentRss/292452.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/shiliqiang/services/trackbacks/292452.html</trackback:ping><description><![CDATA[<div class="xspace-itemmessage" id="xspace-item97510">
<p><br />
<font color="#990000">解决Java内存泄漏 <br />
</font></p>
<p><font color="#990000">Java内存泄漏是每个Java程序员都会遇到的问题,程序在本地运行一切正常,可是布署到远端就会出现内存无限制的增长,最后系统瘫痪,那么如何最快最好的检测程序的稳定性,防止系统崩盘,作者用自已的亲身经历与各位分享解决这些问题的办法. <br />
<br />
作
为Internet最流行的编程语言之一,Java现正非常流行.我们的网络应用程序就主要采用Java语言开发,大体上分为客户端、服务器和数据库三个
层次.在进入测试过程中,我们发现有一个程序模块系统内存和CPU资源消耗急剧增加,持续增长到出现
java.lang.OutOfMemoryError为止.经过分析Java内存泄漏是破坏系统的主要因素.这里与大家分享我们在开发过程中遇到的
Java内存泄漏的检测和处理解决过程. </font></p>
<p><font color="#990000">一. Java是如何管理内存 </font></p>
<p><font color="#990000">为了判断Java中是否有内存泄露,我们首先必须了解Java是如何管理内存的.Java的内存管理
就是对象的分配和释放问题.在Java中,内存的分配是由程序完成的,而内存的释放是由垃圾收集器(Garbage
Collection,GC)完成的,程序员不需要通过调用函数来释放内存,但它只能回收无用并且不再被其它对象引用的那些对象所占用的空间. </font></p>
<p><font color="#990000">Java的内存垃圾回收机制是从程序的主要运行对象开始检查引用链,当遍历一遍后发现没有被引用的孤
立对象就作为垃圾回收.GC为了能够正确释放对象,必须监控每一个对象的运行状态,包括对象的申请、引用、被引用、赋值等,GC都需要进行监控.监视对象
状态是为了更加准确地、及时地释放对象,而释放对象的根本原则就是该对象不再被引用. </font></p>
<p><font color="#990000">在Java中,这些无用的对象都由GC负责回收,因此程序员不需要考虑这部分的内存泄露.虽然,我们
有几个函数可以访问GC,例如运行GC的函数System.gc(),但是根据Java语言规范定义,该函数不保证JVM的垃圾收集器一定会执行.因为不
同的JVM实现者可能使用不同的算法管理GC.通常GC的线程的优先级别较低.JVM调用GC的策略也有很多种,有的是内存使用到达一定程度时,GC才开
始工作,也有定时执行的,有的是平缓执行GC,有的是中断式执行GC.但通常来说,我们不需要关心这些.</font></p>
<p><font color="#990000">二. 什么是Java中的内存泄露 </font></p>
<p><font color="#990000">导致内存泄漏主要的原因是,先前申请了内存空间而忘记了释放.如果程序中存在对无用对象的引用,那么
这些对象就会驻留内存,消耗内存,因为无法让垃圾回收器GC验证这些对象是否不再需要.如果存在对象的引用,这个对象就被定义为"有效的活动",同时不会
被释放.要确定对象所占内存将被回收,我们就要务必确认该对象不再会被使用.典型的做法就是把对象数据成员设为null或者从集合中移除该对象.但当局部
变量不需要时,不需明显的设为null,因为一个方法执行完毕时,这些引用会自动被清理. </font></p>
<p><font color="#990000">在Java中,内存泄漏就是存在一些被分配的对象,这些对象有下面两个特点,首先,这些对象是有被引
用的,即在有向树形图中,存在树枝通路可以与其相连；其次,这些对象是无用的,即程序以后不会再使用这些对象.如果对象满足这两个条件,这些对象就可以判
定为Java中的内存泄漏,这些对象不会被GC所回收,然而它却占用内存. </font></p>
<p><font color="#990000">这里引用一个常看到的例子,在下面的代码中,循环申请Object对象,并将所申请的对象放入一个
Vector中,如果仅仅释放对象本身,但因为Vector仍然引用该对象,所以这个对象对GC来说是不可回收的.因此,如果对象加入到Vector后,
还必须从Vector中删除,最简单的方法就是将Vector对象设置为null.实际上这些对象已经是无用的,但还被引用,GC就无能为力了(事实上
GC认为它还有用),这一点是导致内存泄漏最重要的原因.
再引用另一个例子来说明Java的内存泄漏.假设有一个日志类Logger,其提供一个静态的log(String
msg),任何其它类都可以调用Logger.Log(message)来将message的内容记录到系统的日志文件中.</font></p>
<p><font color="#990000">Logger类有一个类型为HashMap的静态变量temp,每次在执行log(message)
的时候,都首先将message的值写入temp中(以当前线程+当前时间为键),在退出之前再从temp中将以当前线程和当前时间为键的条目删除.注
意,这里当前时间是不断变化的,所以log在退出之前执行删除条目的操作并不能删除执行之初写入的条目.这样,任何一个作为参数传给log的字符串最终由
于被Logger的静态变量temp引用,而无法得到回收,这种对象保持就是我们所说的Java内存泄漏.
总的来说,内存管理中的内存泄漏产生的主要原因：保留下来却永远不再使用的对象引用.</font></p>
<p><font color="#990000">三. 几种典型的内存泄漏 </font></p>
<p><font color="#990000">我们知道了在Java中确实会存在内存泄漏,那么就让我们看一看几种典型的泄漏,并找出他们发生的原因和解决方法. </font></p>
<p><font color="#990000">3.1 全局集合</font></p>
<p><font color="#990000">在大型应用程序中存在各种各样的全局数据仓库是很普遍的,比如一个JNDI-tree或者一个session table.在这些情况下,必须注意管理储存库的大小.必须有某种机制从储存库中移除不再需要的数据.</font></p>
<p><font color="#990000">通常有很多不同的解决形式,其中最常用的是一种周期运行的清除作业.这个作业会验证仓库中的数据然后
清除一切不需要的数据.另一种管理储存库的方法是使用反向链接(referrer)计数.然后集合负责统计集合中每个入口的反向链接的数目.这要求反向链
接告诉集合何时会退出入口.当反向链接数目为零时,该元素就可以从集合中移除了.</font></p>
<p><font color="#990000">3.2 缓存 <br />
缓存一种用来快速查找已经执行过的操作结果的数据结构.因此,如果一个操作执
行需要比较多的资源并会多次被使用,通常做法是把常用的输入数据的操作结果进行缓存,以便在下次调用该操作时使用缓存的数据.缓存通常都是以动态方式实现
的,如果缓存设置不正确而大量使用缓存的话则会出现内存溢出的后果,因此需要将所使用的内存容量与检索数据的速度加以平衡.</font></p>
<p><font color="#990000">常用的解决途径是使用java.lang.ref.SoftReference类坚持将对象放入缓存.这个方法可以保证当虚拟机用完内存或者需要更多堆的时候,可以释放这些对象的引用.</font></p>
<p><font color="#990000">3.3 类装载器 <br />
Java类装载器的使用为内存泄漏提供了许多可乘之机.一般来说类装载器
都具有复杂结构,因为类装载器不仅仅是只与"常规"对象引用有关,同时也和对象内部的引用有关.比如数据变量,方法和各种类.这意味着只要存在对数据变
量,方法,各种类和对象的类装载器,那么类装载器将驻留在JVM中.既然类装载器可以同很多的类关联,同时也可以和静态数据变量关联,那么相当多的内存就
可能发生泄漏. </font></p>
<p><font color="#990000">四. 如何检测和处理内存泄漏</font></p>
<p><font color="#990000">如何查找引起内存泄漏的原因一般有两个步骤:第一是安排有经验的编程人员对代码进行走查和分析,找出内存泄漏发生的位置;第二是使用专门的内存泄漏测试工具进行测试. </font></p>
<p><font color="#990000">第一个步骤:在代码走查的工作中,可以安排对系统业务和开发语言工具比较熟悉的开发人员对应用的代码进行了交叉走查,尽量找出代码中存在的数据库连接声明和结果集未关闭、代码冗余等故障代码. </font></p>
<p><font color="#990000">第二个步骤:就是检测Java的内存泄漏.在这里我们通常使用一些工具来检查Java程序的内存泄漏
问题.市场上已有几种专业检查Java内存泄漏的工具,它们的基本工作原理大同小异,都是通过监测Java程序运行时,所有对象的申请、释放等动作,将内
存管理的所有信息进行统计、分析、可视化.开发人员将根据这些信息判断程序是否有内存泄漏问题.这些工具包括Optimizeit
Profiler,JProbe Profiler,JinSight , Rational 公司的Purify等. </font></p>
<p><font color="#990000">4.1检测内存泄漏的存在 <br />
这里我们将简单介绍我们在使用Optimizeit检查的过程.通常在知道发生内存泄漏之后,第一步是要弄清楚泄漏了什么数据和哪个类的对象引起了泄漏.</font></p>
<p><font color="#990000">一般说来,一个正常的系统在其运行稳定后其内存的占用量是基本稳定的,不应该是无限制的增长的.同
样,对任何一个类的对象的使用个数也有一个相对稳定的上限,不应该是持续增长的.根据这样的基本假设,我们持续地观察系统运行时使用的内存的大小和各实例
的个数,如果内存的大小持续地增长,则说明系统存在内存泄漏,如果特定类的实例对象个数随时间而增长(就是所谓的&#8220;增长率&#8221;),则说明这个类的实例可能存
在泄漏情况.</font></p>
<p><font color="#990000">另一方面通常发生内存泄漏的第一个迹象是：在应用程序中出现了OutOfMemoryError.在
这种情况下,需要使用一些开销较低的工具来监控和查找内存泄漏.虽然OutOfMemoryError也有可能应用程序确实正在使用这么多的内存；对于这
种情况则可以增加JVM可用的堆的数量,或者对应用程序进行某种更改,使它使用较少的内存. </font></p>
<p><font color="#990000">但是,在许多情况下,OutOfMemoryError都是内存泄漏的信号.一种查明方法是不间断地监控GC的活动,确定内存使用量是否随着时间增加.如果确实如此,就可能发生了内存泄漏.</font></p>
<p><font color="#990000">4.2处理内存泄漏的方法 <br />
一旦知道确实发生了内存泄漏,就需要更专业的工具来查明为什么会
发生泄漏.JVM自己是不会告诉您的.这些专业工具从JVM获得内存系统信息的方法基本上有两种：JVMTI和字节码技术(byte code
instrumentation).Java虚拟机工具接口(Java Virtual Machine Tools
Interface,JVMTI)及其前身Java虚拟机监视程序接口(Java Virtual Machine Profiling
Interface,JVMPI)是外部工具与JVM通信并从JVM收集信息的标准化接口.字节码技术是指使用探测器处理字节码以获得工具所需的信息的技
术. </font></p>
<p><font color="#990000">Optimizeit是Borland公司的产品,主要用于协助对软件系统进行代码优化和故障诊断,其中的Optimizeit Profiler主要用于内存泄漏的分析.Profiler的堆视图就是用来观察系统运行使用的内存大小和各个类的实例分配的个数的.</font></p>
<p><font color="#990000">首先,Profiler会进行趋势分析,找出是哪个类的对象在泄漏.系统运行长时间后可以得到四个内
存快照.对这四个内存快照进行综合分析,如果每一次快照的内存使用都比上一次有增长,可以认定系统存在内存泄漏,找出在四个快照中实例个数都保持增长的
类,这些类可以初步被认定为存在泄漏.通过数据收集和初步分析,可以得出初步结论:系统是否存在内存泄漏和哪些对象存在泄漏(被泄漏).</font></p>
<p><font color="#990000">接下来,看看有哪些其他的类与泄漏的类的对象相关联.前面已经谈到Java中的内存泄漏就是无用的对
象保持,简单地说就是因为编码的错误导致了一条本来不应该存在的引用链的存在(从而导致了被引用的对象无法释放),因此内存泄漏分析的任务就是找出这条多
余的引用链,并找到其形成的原因.查看对象分配到哪里是很有用的.同时只知道它们如何与其他对象相关联(即哪些对象引用了它们)是不够的,关于它们在何处
创建的信息也很有用.<br />
<br />
最后,进一步研究单个对象,看看它们是如何互相关联的.借助于Profiler工具,应用程序中的代码可以在分
配时进行动态添加,以创建堆栈跟踪.也有可以对系统中所有对象分配进行动态的堆栈跟踪.这些堆栈跟踪可以在工具中进行累积和分析.对每个被泄漏的实例对
象,必然存在一条从某个牵引对象出发到达该对象的引用链.处于堆栈空间的牵引对象在被从栈中弹出后就失去其牵引的能力,变为非牵引对象.因此,在长时间的
运行后,被泄露的对象基本上都是被作为类的静态变量的牵引对象牵引.</font></p>
<p><font color="#990000">总而言之, Java虽然有自动回收管理内存的功能,但内存泄漏也是不容忽视,它往往是破坏系统稳定性的重要因素.</font></p>
<p>&nbsp;</p>
<p><br />
</p>
<p>摘自：http://www.51testing.com/?88979/spacelist-blog-page-2.html<br />
</p>
</div>
<img src ="http://www.blogjava.net/shiliqiang/aggbug/292452.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/shiliqiang/" target="_blank">石头@</a> 2009-08-24 21:39 <a href="http://www.blogjava.net/shiliqiang/articles/292452.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>JVM的载入</title><link>http://www.blogjava.net/shiliqiang/articles/292200.html</link><dc:creator>石头@</dc:creator><author>石头@</author><pubDate>Sat, 22 Aug 2009 08:53:00 GMT</pubDate><guid>http://www.blogjava.net/shiliqiang/articles/292200.html</guid><wfw:comment>http://www.blogjava.net/shiliqiang/comments/292200.html</wfw:comment><comments>http://www.blogjava.net/shiliqiang/articles/292200.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/shiliqiang/comments/commentRss/292200.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/shiliqiang/services/trackbacks/292200.html</trackback:ping><description><![CDATA[　<br />
安装jdk将会生成如下3个项目： <br />
HKEY_LOCAL_MACHINE\SOFTWARE\JavaSoft\Java Development Kit <br />
HKEY_LOCAL_MACHINE\SOFTWARE\JavaSoft\Java Plug-in <br />
HKEY_LOCAL_MACHINE\SOFTWARE\JavaSoft\Java Runtime Environment <br />
同时，Java2 SDK安装程序将会把java.exe，javaw.exe，javareg.exe这3个<zmkey class="zoomino-searchword">可执行文件</zmkey>拷贝到winnt\system32目录下，由于winnt\system32被操作系统缺省的设置为最高优先权的PATH搜索路径，因此可保证用户在命令行任何目录下可运行java.exe来启动JVM。 <br />
那么java.exe在启动时如何确定其JRE所在的目录以及需要动态<zmkey class="zoomino-searchword">加载</zmkey>的链接库呢？java.exe是通过下面方式来确定的： <br />
假如存在../jre/bin/java.dll文件，则查找../jre/lib/
jvm.cfg文件，在该文件中，第1个被列出的jvm.dll类型作为缺省值（假如在java.exe命令行指定了jvm.dll的类型，则使用指定类
型）。jvm.dll类型分为hotspot，classic，server三种。假如不存在../jre/lib/jvm.cfg文件，则打印下面的错
误信息： <br />
Error: could not open 'c:\jdk1.3\jre\lib\jvm.cfg' <br />
如不存
在../jre/bin/java.dll（当运行的是winnt\system32\java.exe），则注册表将在此时发挥作
用，HKEY_LOCAL_MACHINE\SOFTWARE\JavaSoft\Java Runtime Environment\
CurrentVersion键值所记录的实际上是winnt\system32\java.exe的版本值，该版本值只保存主、次两个版本号，如
1.2，1.3等。 <br />
同时java.exe程序内部本身也有一个标识自身的版本值，如1.2、1.3等。java.exe根据自己内部的版
本值和CurrentVersion值相比较，假如发现两个值相等，则将在HKEY_LOCAL_MACHINE\SOFTWARE\JavaSoft
\Java Runtime Environment\MainVersion.MicroVersion项下获取JRE所在目录及<zmkey class="zoomino-searchword">动态链接库</zmkey>，这两个键的名称分别是JavaHome和RuntimeLib，MainVersion表示<zmkey class="zoomino-searchword">主版</zmkey>本号，MicroVersion表示次版本号。 <br />
假如java.exe内部版本值和CurrentVersion不一致，则报类似以下的错误： <br />
Registry key 'Software\JavaSoft\Java Runtime Environment\CurrentVersion' <br />
has value '1.2', but '1.3' is required. <br />
意思是说，注册表当前所记载的winnt\system32\java.exe版本为1.2，但是此时运行的java.exe版本为1.3。
java.exe抱怨除非注册表有1.3版的记载，否则自己无法正确定位JRE目录和jvm.dll，因此提示1.3是需要的。 <br />
这里，我
们不能简单的修改注册表的CurrentVersion值来达到这个目的。一般地，当在系统中装了两套版本的Java2
SDK（如先装1.2而后又装了1.3），后面安装的Java2
SDK会将自己带的java.exe和javaw.exe拷贝到winnt\system32目录下，从而覆盖先前版本的java.exe和
javaw.exe，并且在注册表中改写CurrentVersion为1.3。所以建议在安装Java2
SDK前，先卸载以前安装的版本。假如人为的修改CurrentVersion，会使得不同版本的java.exe加载与己版本不符的java.dll及
jvm.dll，将引起难以预料的后果！ <br />
非凡情况： <br />
JBuilder自己带一套JDK，在JBuilder安装完成后，JBuilder安装程序会修改CurrentVersion为自己所带JDK的版本，但不会覆盖winnt\system32下的java.exe和javaw.exe。 <br />
WebLogic自己带一套JDK，在WebLogic安装完成后，WebLogic安装程序不会修改注册表，也不会覆盖winnt\system32下的java.exe和javaw.exe。 <br />
Oracle自己带一套JDK（一般是比较低版本的，例如8.1.7仅仅带JDK
1.1.7），在Oracle安装完成后，Oracle安装程序不会修改注册表，也不会覆盖winnt\system32下的java.exe和
javaw.exe。但是，Oralce安装程序会修改系统PATH变量，将自带的JRE的bin路径加入其中，且置于最前面。随着Oracle安装版本
的不同，其自带JRE的JVM启动程序也不同。在笔者机器上安装的Oracle 8.1.7，其JRE就装在C:\Program
Files\Oracle下，并将C:\Program
Files\Oracle\jre\1.1.7\bin放在PATH变量最前，其JVM启动程序是jre.exe而非java.exe。 <br />
以上就是Java2 SDK在Windows下安装时所做的动作，这样会带来兼容性问题： <br />
问题背景：安装Java2 SDK后，安装了JBuilder6，未修改任何PATH变量 <br />
问题1 <br />
当在操作系统中安装了JDK 1.2，其后安装了JBuilder6（自带JDK 1.3.1），这时CurrentVersion为1.3，在命令行执行java -version时，提示： <br />
Registry key 'Software\JavaSoft\Java Runtime Environment\CurrentVersion' <br />
has value '1.3', but '1.2' is required. <br />
解决方法：将JDK 1.2中java.exe所在路径加入到操作系统PATH的首位，从而保证在命令行调用java时总是执行JDK 1.2中的java.exe，以使得java.exe可正确定位JRE和jvm.dll。 <br />
问题2 <br />
当在操作系统中安装了JDK 1.3.0，而后安装了JBuilder6（自带JDK
1.3.1），这时CurrentVersion为1.3，但是此1.3是指向的是JBuilder6自带的JDK
1.3.1的JRE，而非指向先前JDK 1.3.0的JRE，当在命令行执行java -version时，此时执行的是JDK
1.3.0拷贝到winnt\system32的一个java.exe副本，但打印的版本信息却是： <br />
java version "1.3.1" <br />
Java(TM) 2 Runtime Environment, Standard Edition (build 1.3.1-b24) <br />
Java HotSpot(TM) Client VM (build 1.3.1-b24, mixed mode) <br />
导致该问题的原因是java.exe只维护小数点后1位的版本号，而非2位。 <br />
解决方法：同问题1 <br />
问题3： <br />
假如在操作系统中先安装了JDK 1.3.0，而后安装了带有与安装JDK主次版本相同的JBuilder6（带JDK
1.3.1，前两位相同），则问题1实际上被隐蔽了，没有发生的机会；而问题2的隐蔽性也很强，不轻易发觉，因为人们往往会忽略JDK的第3个版本号。
<br />
如问题2所叙，在命令行执行java，虽然是使用JDK
1.3.0的一个java.exe副本（winnt\system32目录下），而实际上却是使用JBuilder6下JDK
1.3.1的JRE及其目录结构，其结果是当我们使用Java2的extension mechanism将jar文件放到JDK
1.3.0的jre\lib\ext目录下时，发现达不到希望的效果 &#8211; 在命令行用java启动程序时，不会自动去JDK
1.3.0的jre\lib\ext目录下去搜索jar文件，它只会去JBuilder6下JDK
1.3.1的jre\lib\ext去搜索jar文件，而JBuilder6下的JDK 1.3.1并不存在jre\lib\ext这么一个目录！ <br />
问题3极为隐蔽，除非完全对Java2 SDK的安装及class定位机制了解，一般的开发者是难以发现问题所在的。有关Java2中class定位机制，见《<zmkey class="zoomino-searchword">Java2中的class定位机制</zmkey>》一文。 <br />
事实上，即使仅仅在系统中存在一份JDK 1.3.0，假如在命令行运行java的话，使用的JRE目录是C:\Program
Files\JavaSoft\JRE\1.3，也就是说，即使我们在c:\jdk1.3\jre\lib\ext下放置我们的extension
jar，也得不到预期的结果。正确的做法是放在C:\Program Files\JavaSoft\JRE\1.3\lib\ext目录下。 <br />
解决方法：同问题1 <br />
综上所叙，强烈建议将%JDK_HOME%\bin目录放在Windows操作系统的PATH变量的首位，以避免潜在的问题。 <br />
而在UNIX下，则完全不存在类似Windows操作系统上的问题。 <br />
我们在命令下执行的java是/bin/java <br />
$which java <br />
$/bin/java <br />
而/bin是到/usr/bin的链接，也就是说/bin/java实际上是/usr/bin/java <br />
而/usr/bin/java实际上链接到/usr/java/bin/java，/usr/java是到/usr/java1.2的链接（Solaris 7或更高系统内置JDK 1.2），所以我们实际上执行的java是 <br />
/usr/java1.2/bin/java <br />
根据UNIX上的情况，java在运行时实际上总是可以用../jre/lib/sparc/libjava.so和../jre/lib/sparc
/libjvm.so来找到这2个文件，前者类似于Windows下的java.dll，而后者类似于Windows下的jvm.dll。所以java也
总是可以确定自己JRE的目录。 <br />
Windows和UNIX上用到的动态链接库，实际上在Sun的文档中称为optional
package's native code binaries，optional pakage实际上即为extension mechanism
classes，详见《Java2中的class定位机制》。 <br />
要更改UNIX上java的版本，更改/usr/java的链接是其中一个方法，具体可参见JDK在UNIX上的安装介绍。 <br />
补充：（2002-12-23） <br />
Windows如何定位Plug-in <br />
根据在PATH<zmkey class="zoomino-searchword">环境变量</zmkey>中
找到的java.exe的版本号，到HKEY_LOCAL_MACHINE\SOFTWARE\JavaSoft\Java
Plug-in下寻找对应版本的Java Plug-in，在HKEY_LOCAL_MACHINE\SOFTWARE\JavaSoft\Java
Plug-in下可以有多个版本的Plug-in存在。 <br />
不依靠HKEY_LOCAL_MACHINE\SOFTWARE
\JavaSoft\Java Development
Kit的CurrentVersion值和HKEY_LOCAL_MACHINE\SOFTWARE\JavaSoft\Java Runtime
Environment的CurrentVersion值来定位应该使用哪个版本的Java Plug-in。<br />
<br />
<img src ="http://www.blogjava.net/shiliqiang/aggbug/292200.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/shiliqiang/" target="_blank">石头@</a> 2009-08-22 16:53 <a href="http://www.blogjava.net/shiliqiang/articles/292200.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>java程序的运行机制</title><link>http://www.blogjava.net/shiliqiang/articles/292177.html</link><dc:creator>石头@</dc:creator><author>石头@</author><pubDate>Sat, 22 Aug 2009 01:52:00 GMT</pubDate><guid>http://www.blogjava.net/shiliqiang/articles/292177.html</guid><wfw:comment>http://www.blogjava.net/shiliqiang/comments/292177.html</wfw:comment><comments>http://www.blogjava.net/shiliqiang/articles/292177.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/shiliqiang/comments/commentRss/292177.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/shiliqiang/services/trackbacks/292177.html</trackback:ping><description><![CDATA[<p style="text-indent: 2em;">(一)&nbsp; Java应用程序的开发周期包括编译、下载、解释和执行几个部分。Java编译程序将
Java源程序翻译为JVM可执行代码--字节码。这一编译过程同C/C++的编译有些不同。当C编译器编译生成一个对象的代码时，该代码是为在某一特定
硬件平台运行而产生的。因此，在编译过程中，编译程序通过查表将所有对符号的引用转换为特定的内存偏移量，以保证程序运行。Java编译器却不将对变量和
方法的引用编译为数值引用，也不确定程序执行过程中的内存布局，而是将这些符号引用信息保留在字节码中，由解释器在运行过程中创立内存布局，然后再通过查
表来确定一个方法所在的地址。这样就有效的保证了Java的可移植性和安全性。</p>
<p style="text-indent: 2em;">运行JVM字节码的工作是由解释器( java命令
)来完成的。解释执行过程分三部进行：代码的装入、代码的校验和代码的执行。装入代码的工作由"类装载器"（class
loader）完成。类装载器负责装入运行一个程序需要的所有代码，这也包括程序代码中的类所继承的类和被其调用的类。当类装载器装入一个类时，该类被放
在自己的名字空间中。除了通过符号引用自己名字空间以外的类，类之间没有其他办法可以影响其他类。在本台计算机上的所有类都在同一地址空间内，而所有从外
部引进的类，都有一个自己独立的名字空间。这使得本地类通过共享相同的名字空间获得较高的运行效率，同时又保证它们与从外部引进的类不会相互影响。当装入
了运行程序需要的所有类后，解释器便可确定整个可执行程序的内存布局。解释器为符号引用同特定的地址空间建立对应关系及查询表。通过在这一阶段确定代码的
内存布局，Java很好地解决了由超类改变而使子类崩溃的问题，同时也防止了代码对地址的非法访问。</p>
<p style="text-indent: 2em;">&nbsp;<wbr></p>
<p style="text-indent: 2em;">随后，被装入的代码由字节码校验器进行检查。校验器可发现操作数栈溢出，非法数据类型转化等多种错误。通过校验后，代码便开始执行了。</p>
<p style="text-indent: 2em;">&nbsp;<wbr></p>
<p style="text-indent: 2em;">Java字节码的执行有两种方式：</p>
<p style="text-indent: 2em;">1.即时编译方式：解释器先将字节码编译成机器码，然后再执行该机器码。</p>
<p style="text-indent: 2em;">2.解释执行方式：解释器通过每次解释并执行一小段代码来完成Java字节码程序的所有操作。</p>
<p style="text-indent: 2em;">&nbsp;<wbr></p>
<p style="text-indent: 2em;">通常采用的是第二种方法。由于JVM规格描述具有足够的灵活性，这使得将字节码翻译为机器代码的工作</p>
<p style="text-indent: 2em;">具有较高的效率。对于那些对运行速度要求较高的应用程序，解释器可将Java字节码即时编译为机器码，从而很好地保证了Java代码的可移植性和高性能。</p>
<p style="text-indent: 2em;">（二）JVM规格描述<br />
JVM的设计目标是提供一个基于抽象规格描述的计算机模型，为解释程序开发人员提很好的灵活性，同时也确保Java代码可在符合该规范的任何系统上运行。JVM对其实现的某些方面给出了具体的定义，特别是对Java<zmkey class="zoomino-searchword">可执行代码</zmkey>，即字节码(Bytecode)的格式给出了明确的规格。这一规格包括<zmkey class="zoomino-searchword">操作码</zmkey>和操作数的语法和数值、标识符的数值表示方式、以及Java类文件中的Java对象、常量缓冲池在JVM的存储映象。这些定义为JVM解释器开发人员提供了所需的信息和开发环境。Java的设计者希望给开发人员以随心所欲使用Java的自由。<br />
JVM定义了控制Java代码解释执行和具体实现的五种规格，它们是：<br />
JVM指令系统<br />
JVM寄存器<br />
JVM栈结构<br />
JVM碎片回收堆<br />
JVM存储区<br />
2.1JVM指令系统<br />
JVM指令系统同其他计算机的指令系统极其相似。Java指令也是由
操作码和操作数两部分组成。操作码为8位二进制数，操作数进紧随在操作码的后面，其长度根据需要而不同。操作码用于指定一条指令操作的性质（在这里我们采
用汇编符号的形式进行说明），如iload表示从存储器中装入一个整数，anewarray表示为一个新数组分配空间，iand表示两个整数的"
与"，ret用于流程控制，表示从对某一方法的调用中返回。当长度大于8位时，操作数被分为两个以上字节存放。JVM采用了"big
endian"的编码方式来处理这种情况，即高位bits存放在低字节中。这同 Motorola及其他的RISC
CPU采用的编码方式是一致的，而与Intel采用的"little endian "的编码方式即低位bits存放在低位字节的方法不同。<br />
Java指令系统是以Java语言的实现为目的设计的，其中包含了用于调用方法和监视多先程系统的指令。Java的8位操作码的长度使得JVM最多有256种指令，目前已使用了160多种操作码。<br />
2.2JVM指令系统<br />
所有的CPU均包含用于保存系统状态和处理器所需信息的寄存器组。如果虚拟机定义较多的寄存器，便可以从中得到更多的信息而不必对栈或内存进行访问，这
有利于提高运行速度。然而，如果虚拟机中的寄存器比实际CPU的寄存器多，在实现虚拟机时就会占用处理器大量的时间来用常规存储器模拟寄存器，这反而会降
低虚拟机的效率。针对这种情况，JVM只设置了4个最为常用的寄存器。它们是：<br />
pc程序计数器<br />
optop操作数栈顶指针<br />
frame当前执行环境指针<br />
vars指向当前执行环境中第一个<zmkey class="zoomino-searchword">局部变量</zmkey>的指针<br />
所有寄存器均为32位。pc用于记录程序的执行。optop,frame和vars用于记录指向Java栈区的指针。<br />
2.3JVM栈结构<br />
作为基于栈结构的计算机，Java栈是JVM存储信息的主要方法。当JVM得到一个Java字节码应用程序后，便为该代码中一个类的每一个方法创建一个栈框架，以保存该方法的状态信息。每个栈框架包括以下三类信息：<br />
局部变量<br />
执行环境<br />
操作数栈<br />
局部变量用于存储一个类的方法中所用到的局部变量。vars寄存器指向该变量表中的第一个局部变量。<br />
执行环境用于保存解释器对Java字节码进行解释过程中所需的信息。它们是：上次调用的方法、局部变量指针和操作数栈的栈顶和栈底指针。执行环境是一个
执行一个方法的控制中心。例如：如果解释器要执行iadd(整数加法)，首先要从frame寄存器中找到当前执行环境，而后便从执行环境中找到操作数栈，
从栈顶弹出两个整数进行加法运算，最后将结果压入栈顶。<br />
操作数栈用于存储运算所需操作数及运算的结果。<br />
2.4JVM碎片回收堆<br />
Java类的实例所需的存储空间是在堆上分配的。解释器具体承担为类实例分配空间的工作。解释器在为一个实例分配完存储空间后，便开始记录对该实例所占用的内存区域的使用。一旦对象使用完毕，便将其回收到堆中。<br />
在Java语言中，除了new语句外没有其他方法为一对象申请和释放内存。对内存进行释放和回收的工作是由Java运行系统承担的。这允许Java运行
系统的设计者自己决定碎片回收的方法。在SUN公司开发的Java解释器和Hot
Java环境中，碎片回收用后台线程的方式来执行。这不但为运行系统提供了良好的性能，而且使程序设计人员摆脱了自己控制内存使用的风险。<br />
2.5JVM存储区<br />
JVM有两类存储区：常量缓冲池和方法区。常量缓冲池用于存储类名称、方法和字段名称以及串常量。方法区则用于存储Java方法的字节码。对于这两种存
储区域具体实现方式在JVM规格中没有明确规定。这使得Java应用程序的存储布局必须在运行过程中确定，依赖于具体平台的实现方式。<br />
JVM是为Java字节码定义的一种独立于具体平台的规格描述，是Java平台独立性的基础。目前的JVM还存在一些限制和不足，有待于进一步的完善，但无论如何，JVM的思想是成功的。<br />
对比分析：如果把Java原程序想象成我们的C++原程序，Java原<zmkey class="zoomino-searchword">程序编译</zmkey>后生成的字节码就相当于C++原程序编译后的80x86的机器码（二进制程序文件），JVM虚拟机相当于80x86计算机系统,Java解释器相当于80x86CPU。在80x86CPU上运行的是机器码，在Java解释器上运行的是Java字节码。<br />
Java解释器相当于运行Java字节码的&#8220;CPU&#8221;,但该&#8220;CPU&#8221;不是通过硬件实现的，而是用软件实现的。Java解释器实际上就是特定的平台下的
一个应用程序。只要实现了特定平台下的解释器程序，Java字节码就能通过解释器程序在该平台下运行，这是Java跨平台的根本。当前，并不是在所有的平
台下都有相应Java解释器程序，这也是Java并不能在所有的平台下都能运行的原因，它只能在已实现了Java解释器程序的平台下运行。</p>
<p style="text-indent: 2em;">&nbsp;<wbr></p>
<p style="text-indent: 2em;">Java宣称的一处编写随处运行就是由JVM来完成. 在sun的网站上你可以下载到基于各种cpu和各种操作系统的Jdk和jre的下载版本,只要寻找到合适你使用的版本,以前你所编写的class文件copy到其他的机器上可以直接运行,不需要再编译.</p>
<p style="text-indent: 2em;">&nbsp;<wbr></p>
<p style="text-indent: 2em;">其实j2se是一种规范,这种规范约定了其跨平台执行的所需要关注很多实现,基于该规范开发人员可以任意编写自己的Java代码而不需要关心这个程序可能在其他的机器和cpu上无法很好运行问题.</p>
<p style="text-indent: 2em;">&nbsp;<wbr></p>
<p style="text-indent: 2em;">其实你也可以看到Ibm和Weblogic都有基于j2se规范的自己实现的Java
虚拟机 . 而且Sun所宣称的不需要编译而可以直接用class文件在各个JVM上直接运行并不精确,博格曾经遇到过用sun
jre开发的class文件在ibm
jre上有一个自动转换的过程,然后这个类可以很好的工作了,幸好这种情况是自动完成,否则我们又要陷入类似于各种c c++的版本编译器兼容性问题中.</p>
<p style="text-indent: 2em;">&nbsp;<wbr></p>
<p style="text-indent: 2em;">以下下摘录了几个主要的概念:</p>
<p style="text-indent: 2em;">&nbsp;<wbr></p>
<p style="text-indent: 2em;">JVM Java Virtual
Machine（Java虚拟机），它是一个虚构出来的计算机,是通过在实际的计算机上仿真模拟各种计算机功能来实现的。
Java虚拟机有自己完善的硬件架构,如处理器、堆栈、寄存器等,还具有相应的指令系统。JVM屏蔽了与具体操作系统平台相关的信息,使得Java程序只
需生成在Java虚拟机上运行的目标代码(字节码),就可以在多种平台上不加修改地运行。Java虚拟机在执行字节码时,实际上最终还是把字节码解释成具
体平台上的机器指令执行。</p>
<p style="text-indent: 2em;">&nbsp;<wbr></p>
<p style="text-indent: 2em;">JRE Java Runtime Environment（Java运行环境），运行JAVA程序所必须的环境的集合，包含JVM标准实现及Java核心类库。</p>
<p style="text-indent: 2em;">&nbsp;<wbr></p>
<p style="text-indent: 2em;">JSDK Java Software Development Kit，和JDK以及J2SE等同。</p>
<p style="text-indent: 2em;">&nbsp;<wbr></p>
<p style="text-indent: 2em;">JDK Java Development Kit(Java开发工具包):包括运行环境、编译工具及其它工具、源代码等，基本上和J2SE等同。</p>
<p style="text-indent: 2em;">&nbsp;<wbr></p>
<p style="text-indent: 2em;">J2ME Java 2 Micro
Edition（JAVA2精简版）API规格基于J2SE ，但是被修改为可以适合某种产品的单一要求。J2ME使JAVA
程序可以很方便的应用于电话卡、寻呼机等小型设备，它包括两种类型的组件，即配置（configuration）和描述（profile）。</p>
<p style="text-indent: 2em;">&nbsp;<wbr></p>
<p style="text-indent: 2em;">J2EE Java 2 Enterprise
Edition（JAVA2企业版），使用Java进行企业开发的一套扩展标准，必须基于J2SE，提供一个基于组件设计、开发、集合、展开企业应用的途
径。J2EE 平台提供了多层、分布式的应用模型，重新利用组件的能力，统一安全的模式以及灵活的处理控制能力。 J2EE包括EJB, JTA,
JDBC, JCA, JMX, JNDI, JMS, JavaMail, Servlet, JSP等规范。</p>
<p style="text-indent: 2em;">&nbsp;<wbr></p>
<p style="text-indent: 2em;">J2SE Java 2 Standard Edition（JAVA2标准版），用来开发Java程序的基础，包括编译器、小工具、运行环境，SUN发布的标准版本中还包括核心类库的所有源代码。</p>
<p style="text-indent: 2em;"><br />
</p>
<p style="text-indent: 2em;">摘自：http://www.diybl.com/course/3_program/java/javajs/20090304/157613.html<br />
</p>
<img src ="http://www.blogjava.net/shiliqiang/aggbug/292177.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/shiliqiang/" target="_blank">石头@</a> 2009-08-22 09:52 <a href="http://www.blogjava.net/shiliqiang/articles/292177.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>如何“快速”掌握一门语言</title><link>http://www.blogjava.net/shiliqiang/articles/292163.html</link><dc:creator>石头@</dc:creator><author>石头@</author><pubDate>Fri, 21 Aug 2009 13:45:00 GMT</pubDate><guid>http://www.blogjava.net/shiliqiang/articles/292163.html</guid><wfw:comment>http://www.blogjava.net/shiliqiang/comments/292163.html</wfw:comment><comments>http://www.blogjava.net/shiliqiang/articles/292163.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/shiliqiang/comments/commentRss/292163.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/shiliqiang/services/trackbacks/292163.html</trackback:ping><description><![CDATA[<p>现在的开发工作要求我们能够快速掌握一门语言。一般来说应对这种挑战有两种态度：其一，粗粗看看语法，就撸起袖子开干，边查Google边学习；其
二是花很多时间完整地把整个语言学习一遍，做到胸有成竹，然后再开始做实际工作。然而这两种方法都有弊病。第二种方法的问题当然很明显，不仅浪费了时间，
偏离了目标，而且学习效率不高。因为没有实际问题驱动的语言学习通常是不牢固不深入的。有的人学着学着成了语言专家，反而忘了自己原本是要解决问题来的。
第一种路子也有问题，在对于这种语言的脾气秉性还没有了解的情况下大刀阔斧地拼凑代码，写出来的东西肯定不入流。说穿新鞋走老路，新瓶装旧酒，那都是小问
题，真正严重的是这样的程序员可以在短时间内堆积大量充满缺陷的垃圾代码。由于通常开发阶段的测试完备程度有限，这些垃圾代码往往能通过这个阶段，从而潜
伏下来，在后期成为整个项目的毒瘤，反反复复让后来的维护者陷入西西弗斯困境。</p>
<p>实际上语言学习有一定规律可循，对于已经掌握一门语言的
开发者来说，对于一般的语言，完全可以以最快的速度，在几天至一周之内掌握其最常用的50%，而且保证路子基本正宗，没有出偏的弊病。其实真正写程序不怕
完全不会，最怕一知半解的去攒解决方案。因为你完全不会，就自然会去认真查书学习，如果学习能力好的话，写出来的代码质量不会差。而一知半解，自己动手土
法炼钢，那搞出来的基本上都是废铜烂铁。比如错误处理和序列化，很多人不去了解&#8220;正路子&#8221;，而是凭借自己的一知半解去攒野路子，这是最危险的。因此，即使
时间再紧张，这些内容也是必须首先完整了解一遍的。掌握这些内容之后进入实际开发，即使有问题，也基本不会伤及项目大体。而开发者本人则可以安步当车，慢
慢在实践中提高自己。</p>
<p>以下列出一个学习提纲，主要针对的是有经验的人，初学者不合适。这个提纲只能用于一般的庸俗编程语言学习，目前在
流行编程语言排行榜上排前20的基本上都是庸俗语言。如果你要学的是LISP之类非庸俗语言，或是某个软件中的二次开发语言，这里的建议未必合适。还是那
句话，仅供参考。</p>
<p>&nbsp;</p>
<p>1. 首先<strong><font color="#0000ff">了解该语言的基本数据类型，基本语法和主要语言构造</font></strong>，主要数学运算符和print函数的使用，达到能够写谭浩强程序设计书课后数学习题的程度；</p>
<p>2. <strong><font color="#0000ff">其次掌握数组和其他集合类的使用</font></strong>，有基础的话可以理解一下泛型，如果理解不了也问题不大，后面可以补；</p>
<p>3. 简单字符串处理。所谓简单，就是Regex和Parser以下的内容，什么查找替换，截断去字串之类的。不过这个阶段有一个难点，就是字符编码问题。如果理解不了，可以先跳过，否则的话最好在这时候把这个问题搞定，免留后患；</p>
<p>4. <strong><font color="#0000ff">基本面向对象或者函数式编程的特征</font></strong>，无非是什么继承、多态、Lambda函数之类的，如果有经验的话很快就明白了；</p>
<p>5. <strong><font color="#0000ff">异常、错误处理、断言、日志和调试支持，对单元测试的支持</font></strong>。你不一定要用TDD，但是在这个时候应该掌握在这个语言里做TDD的基本技能；</p>
<p>6. <strong><font color="#0000ff">程序代码和可执行代码的组织机制，运行时模块加载、符号查找机制</font></strong>，这是初学时的一个难点，因为大部分书都不太注意介绍这个极为重要的内容；</p>
<p>7. <strong><font color="#0000ff">基本输入输出和文件处理，输入输出流类的组织</font></strong>，这通常是比较繁琐的一部分，可以提纲挈领学一下，搞清楚概念，用到的时候查就是了。到这个阶段可以写大部分控制台应用了；</p>
<p>8. <strong><font color="#0000ff">该语言如何进行callback方法调用，如何支持事件驱动编程模型。</font></strong>在
现代编程环境下，这个问题是涉及开发思想的一个核心问题，几乎每种语言在这里都会用足功夫，.NET的delegate，Java的anonymous
inner class，Java 7的closure，C++OX的
tr1::function/bind，五花八门。如果能彻底理解这个问题，不但程序就不至于写得太走样，而且对该语言的设计思路也能有比较好的认识；</p>
<p>9. 如果有必要，可在这时研究regex和XML处理问题，如无必要可跳过；</p>
<p>10. <strong><font color="#0000ff">序列化和反序列化</font></strong>，掌握一下缺省的机制就可以了；</p>
<p>11. 如果必要，可了解一下线程、并发和异步调用机制，主要是为了读懂别人的代码，如果自己要写这类代码，必须专门花时间严肃认真系统地学习，严禁半桶水上阵；</p>
<p>12. 动态编程，反射和元数据编程，数据和程序之间的相互转化机制，运行时编译和执行的机制，有抱负的开发者在这块可以多下些功夫，能够使你对语言的认识高出一个层面；</p>
<p>13. 如果有必要，可研究一下该语言对于泛型的支持，不必花太多时间，只要能使用现成的泛型集合和泛型函数就可以了，可在以后闲暇时抽时间系统学习。需要注意的是，泛型技术跟多线程技术一样，用不好就成为万恶之源，必须系统学习，谨慎使用，否则不如不学不用；</p>
<p>14.
如果还有时间，最好咨询一下有经验的人，看看这个语言较常用的特色features是什么，如果之前没学过，应当补一下。比如Ruby的block
interator, Java的dynamic proxy，C# 3的LINQ和extension
method。没时间的话，我认为也可以边做边学，没有大问题。</p>
<p>15. 有必要的话，在工作的闲暇时间，可以着重考察两个问题，第一，这个语言有哪些惯用法和模式，第二，这个语言的编译/解释执行机制。</p>
<p>至
此语言的基本部分就可以说掌握了，之后是做数据库、网络还是做图形，可以根据具体需求去搞，找相应的成熟框架或库，边做边学，加深理解。对于一个庸俗语
言，我自己把上面的内容走一遍大概要花2-3周时间，不能算很快，但也耽误不了太多事情，毕竟不是每个月都学新语言。掌握了以上的内容，就给练武术打好了
基本功，虽然不见得有多优秀，但是肯定是根正苗红，将来不必绕大弯子。就算是临时使用的语言，把上面这个提纲精简一下，只看蓝色重体字的部分，大致能在几
天到一周内搞定，不算是太耗时，而且写出来的代码不会太不靠谱。</p>
<p>以上提纲未设及内存模型。对于C/C++，这个问题很重要，要放在显著位置来考虑，但对于其他语言，这个问题被透明化了，除非你要做hardcore项目，否则不必太关注。</p>
<p><br />
</p>
<p><br />
</p>
<p>摘自：http://blog.csdn.net/myan/archive/2008/10/25/3144661.aspx<br />
</p>
<img src ="http://www.blogjava.net/shiliqiang/aggbug/292163.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/shiliqiang/" target="_blank">石头@</a> 2009-08-21 21:45 <a href="http://www.blogjava.net/shiliqiang/articles/292163.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>java中进行callback方法调用</title><link>http://www.blogjava.net/shiliqiang/articles/292162.html</link><dc:creator>石头@</dc:creator><author>石头@</author><pubDate>Fri, 21 Aug 2009 13:43:00 GMT</pubDate><guid>http://www.blogjava.net/shiliqiang/articles/292162.html</guid><wfw:comment>http://www.blogjava.net/shiliqiang/comments/292162.html</wfw:comment><comments>http://www.blogjava.net/shiliqiang/articles/292162.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/shiliqiang/comments/commentRss/292162.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/shiliqiang/services/trackbacks/292162.html</trackback:ping><description><![CDATA[<p>回调函数(callback function)这个概念,
所谓回调函数,就是指这个函数先在某处注册，而它将在稍后某个需要的时候被调用。比如在利用SDK
进行Windows编程的时候,我们需要注册一个WNDCLASS类,这个类中有这样一个参数 lpfnWndProc,
要进行消息处理,我们就要用处理消息的函数的指针给它赋值。消息处理函数什么时候被调用的？我们没有显式地在程序中看到啊。是OS调用的。&nbsp;
这是SDK的试验方式，当然用的是过程式的语言C，可以通过传递函数的指针实现。</p>
<p>C++中怎么来实现呢？当然，C++兼容C，用函数指针就可以。&nbsp; 同时C++又提供了面向对象的机制，可不可以有不同的实现机制呢？&nbsp; 当然！
STL 中的functor(Function object)就可以用到回调上。&nbsp;
比如对一个存放int数据的vector进行递减排序的话，我们可以这样进行。</p>
<p>&nbsp;&nbsp;&nbsp; sort(vec.begin(),vec.end(),greater());</p>
<p>&nbsp; greater()就是我们传递的一个匿名对象，它重载了函数调用运算符&#8220;()&#8221;。我们没有显式地调用这个对象里面提供的函数，sort函数对对象里面的函数进行call back。</p>
<p>&nbsp; Java中要实现类似functor的功能，应该怎么办呢？Command模式可以帮上忙。Command模式看起来很简单，只要把command封装到一个接口中就可以。Command模式是回调机制的一个面向对象的替代品。</p>
<p>&nbsp; 比如 java.io 中已经定义好的一个接口</p>
<p>&nbsp;&nbsp;&nbsp; public interface FilenameFilter {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; boolean accept(File dir, String name);<br />
&nbsp;&nbsp;&nbsp; }</p>
<p>&nbsp; 这个FilenameFilter就是Command，实现Command的类就是ConcreteCommand。这个接口所声明的操作
&#8220;accept&#8221;
就是看看目录dir中的文件name是否满足某种要求，如果满足就返回true，否则就返回false。这个要求是什么呢？你要对这个接口进行实现。比如
我想看看这个文件的名称包含不包含指定<br />
的字符串，那么就可以定义下面的类：</p>
<p>&nbsp;&nbsp;&nbsp; class DirFilter implements FilenameFilter {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; private String afn;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; public DirFilter(String afn){</p>
<p>&nbsp;&nbsp; this.afn = afn;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; public boolean accept(File dir, String name){<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; String f = new File(name).getName();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return f.indexOf(afn) != -1;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />
&nbsp;&nbsp;&nbsp; }</p>
<p>&nbsp;</p>
<p>&nbsp; 怎么样使用它呢？File类中有这样一个方法<br />
&nbsp;&nbsp;&nbsp; public String[] list(FilenameFilter filter)</p>
<p>&nbsp; 因此，我们就可以这样做了：<br />
&nbsp;&nbsp;&nbsp; File file = new File(&#8221;.&#8221;);<br />
&nbsp;&nbsp;&nbsp; String[] list = file.list(new DirFilter(&#8221;wf&#8221;));</p>
<p>&nbsp; 得到的list就是一个当然目录中所有包含字符串&#8221;wf&#8221;的文件名称的字符串数组。</p>
<p>&nbsp; 怎么样，看起来是不是和C++中的functor差不多呢？</p>
<p>&nbsp;anonymous inner class&nbsp;&nbsp;&nbsp; 和&nbsp; closure （7.0）都是体现回调的思想<br />
</p>
<img src ="http://www.blogjava.net/shiliqiang/aggbug/292162.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/shiliqiang/" target="_blank">石头@</a> 2009-08-21 21:43 <a href="http://www.blogjava.net/shiliqiang/articles/292162.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>java reference</title><link>http://www.blogjava.net/shiliqiang/articles/291775.html</link><dc:creator>石头@</dc:creator><author>石头@</author><pubDate>Wed, 19 Aug 2009 04:49:00 GMT</pubDate><guid>http://www.blogjava.net/shiliqiang/articles/291775.html</guid><wfw:comment>http://www.blogjava.net/shiliqiang/comments/291775.html</wfw:comment><comments>http://www.blogjava.net/shiliqiang/articles/291775.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/shiliqiang/comments/commentRss/291775.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/shiliqiang/services/trackbacks/291775.html</trackback:ping><description><![CDATA[<div class="blog_content">
<p><span style="font-size: medium;"><strong><span style="font-size: large;">Reference</span></strong><br />
</span>Java世界泰山北斗级大作《Thinking In Java》切入Java就提出&#8220;Everything is Object&#8221;。在Java这个充满Object的世界中，reference是一切谜题的根源，所有的故事都是从这里开始的。</p>
<p><strong><span>Reference是什么？</span><br />
</strong>如果你和我一样在进入
Java世界之前曾经浪迹于C/C++世界，就一定不会对指针陌生。谈到指针，往日种种不堪回首的经历一下子涌上心头，这里不是抱怨的地方，让我们暂时忘
记指针的痛苦，回忆一下最初接触指针的甜蜜吧！还记得你看过的教科书中，如何讲解指针吗？留在我印象中的一种说法是，指针就是地址，如同门牌号码一样，有
了地址，你可以轻而易举找到一个人家，而不必费尽心力的大海捞针。<br />
C++登上历史舞台，reference也随之而来，容我问个小问题，指针和reference区别何在？我的答案来自于在C++世界享誉盛名的《More Effective C++》。</p>
<ol>
    <li>没有null reference。 </li>
    <li>reference必须有初值。 </li>
    <li>使用reference要比使用指针效率高。因为reference不需要测试其有效性。 </li>
    <li>指针可以重新赋值，而reference总是指向它最初获得的对象</li>
</ol>
<p><strong>设计选择：</strong><br />
当你指向你需要指向的某个东西，而且绝不会改指向其它东西，或是当你实作一个运算符而其语法需要无法有指针达成，你就应该选择reference。其它任何时候，请采用指针。</p>
<p>这和Java有什么关系？<br />
初学Java，鉴于reference的名称，我毫不犹豫的将它和C++中的reference等同起来。不过，
我错了。在Java中，reference可以随心所欲的赋值置空，对比一下上面列出的差异，就不难发现，Java的reference如果要与
C/C++对应，它不过是一个穿着reference外衣的指针而已。<br />
于是，所有关于C中关于指针的理解方式，可以照搬到Java中，简而言之，reference就是一个地址。我们可以把它想象成一个把手，抓住它，就抓住了我们想要操纵的数据。如同掌握C的关键在于掌握指针，探索Java的钥匙就是reference。</p>
<p><span>一段小程序</span><br />
我知道，太多的文字总是令人犯困，那就来段代码吧！<br />
<span style="font-family: Arial;">public class ReferenceTricks {<br />
&nbsp;&nbsp;public static void main(String[] args) {<br />
&nbsp;&nbsp;&nbsp; ReferenceTricks r = new ReferenceTricks();<br />
&nbsp;&nbsp;&nbsp;&nbsp;// reset integer<br />
&nbsp; &nbsp;&nbsp;r.i = 0;<br />
&nbsp;&nbsp;&nbsp; System.out.println("Before changeInteger:" + r.i);<br />
&nbsp;&nbsp;&nbsp; changeInteger(r);<br />
&nbsp; &nbsp;&nbsp;System.out.println("After changeInteger:" + r.i);</span></p>
<p><span style="font-family: Arial;">&nbsp;&nbsp;&nbsp; // just for format<br />
&nbsp; &nbsp;&nbsp;System.out.println();<br />
&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp; // reset integer<br />
&nbsp; &nbsp;&nbsp;r.i = 0;<br />
&nbsp;&nbsp;&nbsp; System.out.println("Before changeReference:" + r.i);<br />
&nbsp; &nbsp;&nbsp;changeReference(r);<br />
&nbsp;&nbsp;&nbsp; System.out.println("After changeReference:" + r.i);<br />
&nbsp; }</span></p>
<p><span style="font-family: Arial;">&nbsp;&nbsp;private static void changeReference(ReferenceTricks r) {<br />
&nbsp; &nbsp;r = new ReferenceTricks();<br />
&nbsp;&nbsp; r.i = 5;<br />
&nbsp;&nbsp; System.out.println("In changeReference: " + r.i);<br />
&nbsp; }</span></p>
<p><span style="font-family: Arial;">&nbsp; private static void changeInteger(ReferenceTricks r) {<br />
&nbsp;&nbsp; r.i = 5;<br />
&nbsp;&nbsp; System.out.println("In changeInteger:" + r.i);<br />
&nbsp; }</span></p>
<p><span style="font-family: Arial;">&nbsp; public int i;<br />
}<br />
</span><br />
对不起，我知道，把一个字段设成public是一种不好的编码习惯，这里只是为了说明问题。<br />
如果你有兴趣自己运行一下这个程序，我等你！</p>
<p>OK，你已经运行过了吗？结果如何？是否如你预期？下面是我在自己的机器上运行的结果：<br />
<span style="font-family: Arial;">Before changeInteger:0<br />
In changeInteger:5<br />
After changeInteger:5</span></p>
<p><span style="font-family: Arial;">Before changeReference:0<br />
In changeReference: 5<br />
After changeReference:0</span><br />
这里，我们关注的是两个change——changeReference和changeInteger。从输出的内容中，我们可以看出，两个方法在调用前和调用中完全一样，差异出现在调用后的结果。</p>
<p><span>糊涂的讲解</span><br />
先让我们来分析一下changeInteger的行为。<br />
前面说过
了，Java中的reference就是一个地址，它指向了一个内存空间，这个空间存放着一个对象的相关信息。这里我们暂时不去关心这个内存具体如何排
布，只要知道，通过地址，我们可以找到r这个对象的i字段，然后我们给它赋成5。既然这个字段的内容得到了修改，从函数中返回之后，它自然就是改动后的结
果了，所以调用之后，r对象的i字段依然是5。下图展示了changeInteger调用前后内存变化。</p>
<p><span style="font-family: Times New Roman;"><span style="font-family: Courier New;">&nbsp;&nbsp;&nbsp;&nbsp; Reference +--------+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Reference +--------+<br />
&nbsp;&nbsp;&nbsp; ----------&gt;| i = 0&nbsp; |&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ----------&gt;| i = 5&nbsp; |<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; |--------|&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; |--------|<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; | Memory |&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; | Memory |<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; |&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; |&nbsp;&nbsp;&nbsp;&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;&nbsp;&nbsp; | <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; |&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; |&nbsp;&nbsp;&nbsp;&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;&nbsp;&nbsp; |<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; +--------+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; +--------+</span></span></p>
<p><span style="font-family: Times New Roman;"><span style="font-family: Courier New;">&nbsp;&nbsp;&nbsp;&nbsp;调用changeInteger之前&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;调用changeInteger之后</span></span></p>
<p>让我们把目光转向changeReference。<br />
从代码上，我们可以看出，同changeInteger之间的差别仅仅在于多了这么一句。<br />
r = new ReferenceTricks();<br />
这条语句的作用是分配一块新的内存，然后将r指向它。<br />
执行完这条语句，r就不再是原来的r，但它依然是一个ReferenceTricks的对象，所以我们依然可以对这个r的i字段赋值。到此为止，一切都是那么自然。</p>
<p><span style="font-family: Courier New;">&nbsp;&nbsp;&nbsp;&nbsp; Reference +--------+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; +--------+<br />
&nbsp;&nbsp;&nbsp; ----------&gt;| i = 0&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; | i = 0&nbsp; |<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; |--------|&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; |--------|<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; | Memory |&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; | Memory |<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; |&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; |&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Reference |--------| <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; |&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; |&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ----------&gt;| i = 5&nbsp; |<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; +--------+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; +--------+</span></p>
<p><span style="font-family: Courier New;">&nbsp;&nbsp;&nbsp;&nbsp;调用changeReference之前&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;调用changeReference之后</span></p>
<p>顺着这个思路继续下去的话，执行完changeReference，输出的r的i字段，那么应该是应该是新内存中的i，所以应该是5。至于那块被我们抛弃的内存，Java的GC功能自然会替我们善后的。<br />
事与愿违。<br />
实际的结果我们已经看到了，输出的是0。<br />
肯定哪个地方错了，究竟是哪个地方呢？</p>
<p><span>参数传递的秘密</span><br />
知道方法参数如何传递吗？<br />
记得刚开始学编程那会儿，老师教导，所谓参数，有形式参数和实际参数之分，参数列表中写的那些东西都叫形式参数，在实际调用的时候，它们会被实际参数所替代。<br />
编
译程序不可能知道每次调用的实际参数都是什么，于是写编译器的高手就出个办法，让实际参数按照一定顺序放到一个大家都可以找得到的地方，以此作为方法调用
的一种约定。所谓&#8220;没有规矩，不成方圆&#8221;，有了这个规矩，大家协作起来就容易多了。这个公共数据区，现在编译器的选择通常是&#8220;栈&#8221;，而所谓的顺序就是形式
参数声明的顺序。<br />
显然，程序运行的过程中，作为实际参数的变量可能遍布于内存的各个位置，而并不一定要老老实实的呆在栈里。为了守&#8220;规矩&#8221;，程序只好将变量复制一份到栈中，也就是通常所说的将参数压入栈中。<br />
打起精神，谜底就要揭晓了。<br />
我刚才说什么来着？将变量复制一份到栈中，没错，&#8220;复制&#8221;！<br />
这就是所谓的值传递。<br />
C语言的旷世经典《The C Programming Language》开篇的第一章中，谈到实际参数时说，&#8220;在C中，所有函数的实际参数都是传&#8216;值&#8217;的&#8221;。<br />
马上会有人站出来，&#8220;错了，还有传地址，比如以指针传递就是传地址&#8221;。<br />
不错，传指针就是传地址。在把指针视为地址的时候，是否考虑过这样一个问题，它也是一个变量。前面的讨论中说过了，参数传递必须要把参数压入栈中，作为地址的指针也不例外。所以，必须把这个指针也复制一份。函数中对于指针操作实际上是对于这个指针副本的操作。<br />
Java的reference等于C的指针。所以，在Java的方法调用中，reference也要复制一份压入堆栈。在方法中对reference的操作就是对这个reference副本的操作。<br />
谜底揭晓<br />
好，让我们回到最初的问题上。<br />
在changeReference中对于reference的赋值实际上是对这个reference的副本进行赋值，而对于reference的本尊没有产生丝毫的影响。<br />
回到调用点，本尊醒来，它并不知道自己睡去的这段时间内发生过什么，所以只好当作什么都没发生过一般。就这样，副本消失了，在方法中对它的修改也就烟消云散了。<br />
&nbsp;<br />
也许你会问出这样的问题，&#8220;听了你的解释，我反而对changeInteger感到迷惑了，既然是对于副本的操作，为什么changeInteger可以运作正常？&#8221;<br />
呵呵，很有趣的大脑短路现象。<br />
好，那我就用前面的说法解释一下changeInteger的运作。<br />
所谓复制，其结果必然是副本完全等同于本尊。reference复制的结果必然是两个reference指向同一块内存空间。<br />
虽然在方法中对于副本的操作并不会影响到本尊，但对内存空间的修改确实实实在在的。<br />
回到调用点，虽然本尊依然不知道曾经发生过的一切，但它按照原来的方式访问内存的时候，取到的确是经过方法修改之后的内容。<br />
于是方法可以把自己的影响扩展到方法之外。<br />
&nbsp;<br />
<span>多说几句</span><br />
这
个问题起源于我对C/C++中同样问题的思考。同C/C++相比，在changeReference中对reference赋值可能并不会造成什么很严重
的后果，而在C/C++中，这么做却会造成臭名昭著的&#8220;内存泄漏&#8221;，根本的原因在于Java拥有了可爱的GC功能。即便这样，我仍不推荐使用这种的手法，
毕竟GC已经很忙了，我们怎么好意思再麻烦人家。<br />
在C/C++中，这个问题还可以继续引申。既然在函数中对于指针直接赋值行不通，那么如何在函数中修改指针呢？答案很简单，指针的指针，也就是把原来的指针看作一个普通的数据，把一个指向它的指针传到函数中就可以了。<br />
同样的问题到了Java中就没有那么美妙的解决方案了，因为Java中可没有reference的reference这样的语法。可能的变通就是将reference进行封装成类。至于值不值，公道自在人心。</p>
<p><br />
</p>
<p><br />
</p>
<p>摘自：http://azi.javaeye.com/blog/182792<br />
</p>
</div>
<img src ="http://www.blogjava.net/shiliqiang/aggbug/291775.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/shiliqiang/" target="_blank">石头@</a> 2009-08-19 12:49 <a href="http://www.blogjava.net/shiliqiang/articles/291775.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>数据类型转换</title><link>http://www.blogjava.net/shiliqiang/articles/291774.html</link><dc:creator>石头@</dc:creator><author>石头@</author><pubDate>Wed, 19 Aug 2009 04:43:00 GMT</pubDate><guid>http://www.blogjava.net/shiliqiang/articles/291774.html</guid><wfw:comment>http://www.blogjava.net/shiliqiang/comments/291774.html</wfw:comment><comments>http://www.blogjava.net/shiliqiang/articles/291774.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/shiliqiang/comments/commentRss/291774.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/shiliqiang/services/trackbacks/291774.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: Code highlighting produced by Actipro CodeHighlighter (freeware)http://www.CodeHighlighter.com/-->public&nbsp;class&nbsp;BinConverter{&nbsp;&nbsp;&nbsp;&nbsp;/**&nbsp;&nbsp;&nbsp;&nbsp;&...&nbsp;&nbsp;<a href='http://www.blogjava.net/shiliqiang/articles/291774.html'>阅读全文</a><img src ="http://www.blogjava.net/shiliqiang/aggbug/291774.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/shiliqiang/" target="_blank">石头@</a> 2009-08-19 12:43 <a href="http://www.blogjava.net/shiliqiang/articles/291774.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>java从控制台读入数据的方法</title><link>http://www.blogjava.net/shiliqiang/articles/291187.html</link><dc:creator>石头@</dc:creator><author>石头@</author><pubDate>Fri, 14 Aug 2009 10:26:00 GMT</pubDate><guid>http://www.blogjava.net/shiliqiang/articles/291187.html</guid><wfw:comment>http://www.blogjava.net/shiliqiang/comments/291187.html</wfw:comment><comments>http://www.blogjava.net/shiliqiang/articles/291187.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/shiliqiang/comments/commentRss/291187.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/shiliqiang/services/trackbacks/291187.html</trackback:ping><description><![CDATA[0　引言
<br />
从控制台中读取数据是一个比较常用的功能，在 JDK 5.0 以前的版本中的实现是比较复杂的，需要手工处理系统的输入流。有意思的是，从
JDK 5.0
版本开始，能从控制台中输入数据的方法每增加一个版本号，就有一种新增的方法，这也增加了选择的种类，可以依据不同的要求来进行选择。下面来看一下，各个
版本中如何从控制台中读取数据以及各自的优缺点。
<br />
<br />
1　JDK 1.4 及以下版本读取的方法
<br />
JDK 1.4 及以下的版本中要想从控制台中输入数据只有一种办法，即使用System.in获得系统的输入流，再桥接至字符流从字符流中读入数据。示例代码如下：
<br />
<br />
import java.io.IOException;
<br />
import java.io.InputStreamReader;
<br />
<br />
public class Test1 {
<br />
<br />
&nbsp;&nbsp;&nbsp; public static void main(String[] args) {
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; String str = readString("请输入字符串：");
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; System.out.println("readString 方法的输入：" + str);
<br />
&nbsp;&nbsp;&nbsp; }
<br />
<br />
&nbsp;&nbsp;&nbsp; /**
<br />
&nbsp;&nbsp;&nbsp;&nbsp; * 使用系统的输入流，从控制台中读取数据&lt;br/&gt;
<br />
&nbsp;&nbsp;&nbsp;&nbsp; * 用于所用的JDK版本
<br />
&nbsp;&nbsp;&nbsp;&nbsp; * @param prompt 提示信息
<br />
&nbsp;&nbsp;&nbsp;&nbsp; * @return 输入的字符串
<br />
&nbsp;&nbsp;&nbsp;&nbsp; */
<br />
&nbsp;&nbsp;&nbsp; private static String readString(String prompt) {
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; String str = null;
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; try {
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; System.out.print(prompt);
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; str = br.readLine();
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; } catch (IOException e) {
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; e.printStackTrace();
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return str;
<br />
&nbsp;&nbsp;&nbsp; }
<br />
}
<br />
<br />
从上面的代码段来看，这种控制台输入的方法非常地麻烦，为了能读取整行的数据，采用了BufferedReader类来进行处理，而且在读取的过
程中还需要捕获IOException。不过这是 JDK 1.4
及以下版本中从控制台读取数据唯一的办法。还有一种非控制台读入数据的办法，就是采用 Swing
中的JOptionPane，会弹出一个非常漂亮的输入对话框让使用者输入数据，但这是一种比较另类的做法，不推荐使用。
<br />
<br />
import javax.swing.JOptionPane;
<br />
<br />
public class Test2 {
<br />
<br />
&nbsp;&nbsp;&nbsp; public static void main(String[] args) {
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; String str = readStringFromDialog("请输入字符串：");
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; System.out.println("readStringFromDialog 方法的输入：" + str);
<br />
&nbsp;&nbsp;&nbsp; }
<br />
<br />
&nbsp;&nbsp;&nbsp; /**
<br />
&nbsp;&nbsp;&nbsp;&nbsp; * 使用JOptionPane的输入对话框，输入字符串&lt;br/&gt;
<br />
&nbsp;&nbsp;&nbsp;&nbsp; * 用于所用的JDK版本
<br />
&nbsp;&nbsp;&nbsp;&nbsp; * @param prompt 提示信息
<br />
&nbsp;&nbsp;&nbsp;&nbsp; * @return 输入的字符串
<br />
&nbsp;&nbsp;&nbsp;&nbsp; */
<br />
&nbsp;&nbsp;&nbsp; private static String readStringFromDialog(String prompt) {
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return JOptionPane.showInputDialog(prompt);
<br />
&nbsp;&nbsp;&nbsp; }
<br />
}
<br />
上面的两种方法都有个共同的缺点——只能读取字符串，若需要读取其他类型的数据需要手工进行转换。
<br />
<br />
2　JDK 5.0 读取的方法
<br />
从 JDK 5.0 开始，基本类库中增加了java.util.Scanner类，根据它的 API
文档说明，这个类是采用正则表达式进行基本类型和字符串分析的文本扫描器。使用它的Scanner(InputStream
source)构造方法，可以传入系统的输入流System.in而从控制台中读取数据。示例代码如下：
<br />
<br />
import java.util.Scanner;
<br />
<br />
public class Test3 {
<br />
<br />
&nbsp;&nbsp;&nbsp; public static void main(String[] args) {
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; String str = readString5("请输入字符串：");
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; System.out.println("readString5 方法的输入：" + str);
<br />
&nbsp;&nbsp;&nbsp; }
<br />
<br />
&nbsp;&nbsp;&nbsp; /**
<br />
&nbsp;&nbsp;&nbsp;&nbsp; * 使用扫描器类（Scanner）从控制台中读取字符串&lt;br/&gt;
<br />
&nbsp;&nbsp;&nbsp;&nbsp; * 适用于JDK 5.0及以后的版本
<br />
&nbsp;&nbsp;&nbsp;&nbsp; * @param prompt 提示信息
<br />
&nbsp;&nbsp;&nbsp;&nbsp; * @return 输入的字符串
<br />
&nbsp;&nbsp;&nbsp;&nbsp; */
<br />
&nbsp;&nbsp;&nbsp; private static String readString5(String prompt) {
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Scanner scanner = new Scanner(System.in);
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; System.out.print(prompt);
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return scanner.nextLine();
<br />
&nbsp;&nbsp;&nbsp; }
<br />
}
<br />
从代码量上来看，Test3比Test1少了很多的代码，核心代码只有两行。其实并不是Scanner将控制台输入给简单化了，只是在其内部的实
现中已经将IOException处理了，而且采用InputStreamReader来一个字符一个字符进行扫描读取的（嘿嘿，它本身就是个扫描器），
只是Scanner做了更高层次的封装。
<br />
<br />
Scanner不仅可以从控制台中读取字符串，还可以读取除char之外的其他七种基本类型和两个大数字类型，并不需要显式地进行手工转换。
Scanner不单单只能扫描控制台中输入的字符，它还可以让读入的字符串匹配一定的正则表达式模式，如果不匹配时将抛出
InputMismatchException异常。
<br />
<br />
使用System.in作为它的构造参数时，它只扫描了系统输入流中的字符。它还有其他的构造，分别可以从文件或者是字符串中扫描分析字符串的，具体的使用方法可以参考 API 文档说明。
<br />
<br />
3　JDK 6.0 读取的方法
<br />
从 JDK 6.0 开始，基本类库中增加了java.io.Console类，用于获得与当前 Java 虚拟机关联的基于字符的控制台设备。在纯字符的控制台界面下，可以更加方便地读取数据。示例代码如下：
<br />
<br />
import java.io.Console;
<br />
import java.util.Scanner;
<br />
<br />
public class Test4 {
<br />
<br />
&nbsp;&nbsp;&nbsp; public static void main(String[] args) {&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; String str = readString6("请输入字符串：");
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; System.out.println("readString6 方法的输入：" + str);
<br />
&nbsp;&nbsp;&nbsp; }
<br />
&nbsp;&nbsp;&nbsp;
<br />
&nbsp;&nbsp;&nbsp; /**
<br />
&nbsp;&nbsp;&nbsp;&nbsp; * 使用控制台类（Console）从控制台中读取字符串&lt;br/&gt;
<br />
&nbsp;&nbsp;&nbsp;&nbsp; * 适用于JDK 1.6或以后的版本
<br />
&nbsp;&nbsp;&nbsp;&nbsp; * @param prompt 提示信息
<br />
&nbsp;&nbsp;&nbsp;&nbsp; * @return 输入的字符串
<br />
&nbsp;&nbsp;&nbsp;&nbsp; */
<br />
&nbsp;&nbsp;&nbsp; private static String readString6(String prompt) {
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Console console = System.console();
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (console == null) {
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; throw new IllegalStateException("不能使用控制台");
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return console.readLine(prompt);
<br />
&nbsp;&nbsp;&nbsp; }
<br />
}
<br />
在Test1和Test3中，输入数据前的提示信息需要使用System.out.print();来输出，但是使用基于Console的Test4类，可以在方法参数中直接放入提示信息。
<br />
<br />
如果需要在控制台中输入密码等敏感信息的话，像在浏览器或者是应用程序中那样显示替代字符，在 JDK 6.0
以前的做法是相当麻烦的（具体的做法可以参考《Java
编程语言中的口令屏蔽》一文），而使用Console类的readPassword()方法可以在控制台上不回显地输入密码，并将密码结果保存在char
数组中，根据 API 文档的建议，在使用后应立即将数组清空，以减少其在内存中占用的时间，以便增强安全性。
<br />
<br />
但是，Console也有一些缺点，根据ConsoleAPI 文档的说明：
<br />
<br />
虚拟机是否具有控制台取决于底层平台，还取决于调用虚拟机的方式。如果虚拟机从一个交互式命令行开始启动，且没有重定向标准输入和输出流，那么其
控制台将存在，并且通常连接到键盘并从虚拟机启动的地方显示。如果虚拟机是自动启动的（例如，由后台作业调度程序启动），那么它通常没有控制台。
<br />
<br />
通过上面的文档说明可以看出，在使用 IDE 的情况下，是无法获取到Console实例的，原因在于在 IDE
的环境下，重新定向了标准输入和输出流，也是就是将系统控制台上的输入输出重定向到了 IDE 的控制台中。因此，在 IDE
中不能使用这个程序，而Test1和Test3就没有这种限制。
<br />
<br />
4　总结
<br />
以上囊括了 Java 中各种版本从控制台中读入数据的方法，将对它们的优缺点进行了分析。下面给出了一些使用建议，可供参考：
<br />
<br />
JRE 1.4 或以下版本的情况下，没得选择只能采用Test1或者是非控制台读入的Test2的方法。
<br />
JRE 5.0 的情况下，建议使用基于Scanner的Test3的方法，更方便地进行数据读取。
<br />
JRE 6.0 的情况，并且只在字符界面的控制台下运行时，采用Test4的方法，如果需要读入像密码之类的敏感数据，为了安全性考虑也必须使用Test4或者是自行实现。如果需要读入除字符串类型之外的其他数据类型，建议使用基于Scanner的控制台输入。<br />
摘自：http://yutaozxy.javaeye.com/blog/372981<br />
<img src ="http://www.blogjava.net/shiliqiang/aggbug/291187.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/shiliqiang/" target="_blank">石头@</a> 2009-08-14 18:26 <a href="http://www.blogjava.net/shiliqiang/articles/291187.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>StringIndexOutOfBoundException 引出的对String类的看法</title><link>http://www.blogjava.net/shiliqiang/articles/290843.html</link><dc:creator>石头@</dc:creator><author>石头@</author><pubDate>Wed, 12 Aug 2009 08:15:00 GMT</pubDate><guid>http://www.blogjava.net/shiliqiang/articles/290843.html</guid><wfw:comment>http://www.blogjava.net/shiliqiang/comments/290843.html</wfw:comment><comments>http://www.blogjava.net/shiliqiang/articles/290843.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/shiliqiang/comments/commentRss/290843.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/shiliqiang/services/trackbacks/290843.html</trackback:ping><description><![CDATA[<table class="tabletop" style="border-collapse: collapse;" background="../../templates/default/images/bg_top.gif" bgcolor="#ffffff" border="0" cellpadding="0" cellspacing="0" height="130" width="760">
    <tbody>
        <tr>
            <td align="center" width="500"><br />
            </td>
            <td width="260"><br />
            </td>
        </tr>
    </tbody>
</table>
<table style="border-collapse: collapse;" background="../../templates/default/images/bg_menu.gif" border="0" cellpadding="0" cellspacing="0" height="27" width="760">
    <tbody>
        <tr>
            <td width="10"><br />
            </td>
        </tr>
    </tbody>
</table>
<table style="border-collapse: collapse; word-wrap: break-word;" bgcolor="#ffffff" border="0" cellpadding="0" cellspacing="0" width="760">
    <tbody>
        <tr>
            <td align="center" height="25"><font style="font-size: 14pt;" color="#02368d"><strong>邂逅StringIndexOutOfBoundsException</strong></font><br />
            </td>
        </tr>
        <tr>
            <td align="center" height="9"><img alt="" src="http://blog.chinaunix.net/templates/default/images/right_line.gif" border="0" height="9" width="502" /></td>
        </tr>
        <tr>
            <td align="center">
            <table style="border-collapse: collapse; word-wrap: break-word;" border="0" cellpadding="0" cellspacing="0" width="740">
                <tbody>
                    <tr>
                        <td width="740">
                        <div style="margin: 15px; line-height: 150%;" width="560" id="art">
                        <p>今天在WCS的测试中邂逅了这个从未接触的exception</p>
                        <p>迫使我对它做了一些分析</p>
                        <p>首先：<br />
                        &#8220;不断的将被选中的字符串加到某一字符串末尾，当长度超过一定量时提示：<br />
                        java.lang.StringIndexOutOfBoundsException: String index out of range: 10<br />
                        &#8221;并不能说明String有长度限制</p>
                        <p>Java API指出StringIndexOutOfBoundsException异常<br />
                        Thrown
                        by String methods to indicate that an index is either negative or
                        greater than the size of the string. For some methods such as the
                        charAt method。<br />
                        上面的错误是因为<br />
                        String.length()&lt;10;<br />
                        而你又要取index&gt;=10的字符从而抛出上面异常<br />
                        String其实是没有限制的，而是当String太大了，超过JVM的自身的内存后会抛出<br />
                        java.lang.OutOfMemoryError错误</p>
                        <p>String是没有长度限制的，而是有JVM的内存限制了String的长度</p>
                        <p>在dayworker的blog中还提到</p>
                        <p>[quote]</p>
                        <p>public class testString{<br />
                        public static void main(String args[])<br />
                        {<br />
                        String s="abbbbb";<br />
                        System.out.println("JVM MAX MEMORY: "+Runtime.getRuntime().maxMemory()/1024/1024+"M");<br />
                        System.out.println("JVM IS USING MEMORY:"+Runtime.getRuntime().totalMemory()/1024/1024+"M");<br />
                        Runtime.getRuntime().traceMethodCalls(true);<br />
                        while(true)<br />
                        {<br />
                        try{<br />
                        s=s+s;<br />
                        <br />
                        }catch(Exception e)<br />
                        {<br />
                        System.out.println(e);<br />
                        }<br />
                        catch(Error o)<br />
                        { String unit = null;<br />
                        int sizeb = s.length();<br />
                        int size = sizeb;<br />
                        int time = 0;<br />
                        while(size&gt;1024)<br />
                        {<br />
                        size = size/1024;<br />
                        time++;<br />
                        }<br />
                        switch(time)<br />
                        {<br />
                        case 0: unit = "byte";break;<br />
                        case 1: unit = "k"; break;<br />
                        case 2: unit = "M"; break;<br />
                        default : unit = "byte";<br />
                        }<br />
                        <br />
                        System.out.println("String has used memory:"+size+unit);<br />
                        System.out.println("JVM IS USING MEMORY:"+(float)Runtime.getRuntime().totalMemory()/1024/1024+"M");<br />
                        System.out.println("MemoryError:"+o);<br />
                        break;<br />
                        }<br />
                        <br />
                        }<br />
                        }<br />
                        }<br />
                        然后我们用JVM的默认参数执行（我的机器内存是128M）<br />
                        java testString<br />
                        结果：<br />
                        JVM MAX MEMORY: 128M<br />
                        JVM IS USING MEMORY:1M<br />
                        String has used memory:12M<br />
                        JVM IS USING MEMORY:63.5625M<br />
                        MemoryError:java.lang.OutOfMemoryError<br />
                        开始JVM使用的内存是1M，当String为12M，JVM使用了63M多时<br />
                        JVM溢出。<br />
                        <br />
                        然后，我们用限制JVM内存大小的参数来执行,限制最大内存5M<br />
                        java -mx5m testString<br />
                        结果：<br />
                        JVM MAX MEMORY: 70M<br />
                        JVM IS USING MEMORY:1M<br />
                        String has used memory:768.0k<br />
                        JVM IS USING MEMORY:5.9375M<br />
                        MemoryError:java.lang.OutOfMemoryError<br />
                        开始JVM使用的内存是1M，当String为768k，JVM使用了5M多时<br />
                        JVM溢出。<br />
                        <br />
                        大家还可以改变 -mx参数，来进一步做实验。<br />
                        以上两个实验证明,String是没有长度限制的，而是有JVM的内存限制了String的长度。同时说明，并不会抛出任何Exception而只会抛出Error.<br />
                        <br />
                        OutMemoryError表明程序的设计很差，或者遇到了超出编程人员所预想的大批量的数据。不管哪种情况，都只有下面这几种解决办法。它们是：<br />
                        <br />
                        设计人员重新设计程序，不致使程序一次载入所有的数据。<br />
                        <br />
                        数据可以分割成更小的块。<br />
                        <br />
                        可以为程序分配更多的内存。<br />
                        <br />
                        为Java虚拟机提供更多的内存。<br />
                        <br />
                        而上面的例子是为虚拟机提供更多的内存<br />
                        <br />
                        =======================================<br />
                        其实应该少用String这东西，特别是 String的 +=操作 <br />
                        不仅原来的String对象不能继续使用，主要是又要new出N多的新对象出来，再多的memory也要out~~<br />
                        String用char array实现，就肯定由长度限制的，不能用memory来衡量<br />
                        <br />
                        ==================================<br />
                        例如上面的程序改用StringBuffer实现，就可以得到极大的改善。<br />
                        下面是我改用StringBuffer做的测试：<br />
                        注意：程序循环了2097150次！<br />
                        是使用String的程序的99864倍！<br />
                        <br />
                        public class TestStringBuffer{<br />
                        public static void main(String args[])<br />
                        {<br />
                        String s="abbbbb";<br />
                        StringBuffer sb = new StringBuffer(s);<br />
                        System.out.println("JVM IS USING MEMORY:"+<br />
                        (Runtime.getRuntime().totalMemory()/1024/1024)+<br />
                        "M");<br />
                        Runtime.getRuntime().traceMethodCalls(true);<br />
                        <br />
                        int count = 0;<br />
                        while(true)<br />
                        {<br />
                        try{<br />
                        sb.append(s);<br />
                        count++;<br />
                        <br />
                        }catch(Exception e)<br />
                        {<br />
                        System.out.println(e);<br />
                        }<br />
                        catch(Error o)<br />
                        {<br />
                        String unit = null;<br />
                        int size = sb.length();<br />
                        size *= 2;<br />
                        <br />
                        int time = 0;<br />
                        while(size&gt;1024)<br />
                        {<br />
                        size = size/1024;<br />
                        time++;<br />
                        }<br />
                        switch(time)<br />
                        {<br />
                        case 0: unit = "byte";break;<br />
                        case 1: unit = "k"; break;<br />
                        case 2: unit = "M"; break;<br />
                        default : unit = "byte";<br />
                        }<br />
                        <br />
                        System.out.println("Loop times:"+count);<br />
                        System.out.println("String has used memory:"+size+unit);<br />
                        System.out.println("JVM IS USING MEMORY:"+<br />
                        (float)Runtime.getRuntime().totalMemory()/1024/1024+<br />
                        "M");<br />
                        System.out.println("MemoryError:"+o);<br />
                        break;<br />
                        }<br />
                        <br />
                        }<br />
                        }<br />
                        }<br />
                        <br />
                        输出结果：<br />
                        JVM IS USING MEMORY:1M<br />
                        Loop times:2097150<br />
                        String has used memory:23M<br />
                        JVM IS USING MEMORY:63.75M<br />
                        MemoryError:java.lang.OutOfMemoryError<br />
                        <br />
                        <br />
                        <br />
                        =====================<br />
                        从
                        另一方面说，如果你要处理的字符串达到百兆甚至上GB，使用String对象，根本没法工作，所以这个问题不需要太多讨论。看一下jdk的源文
                        件，String的长度是String对象的一个成员count，类型是int，不是long，也不是char。知道这些，我认为够了</p>
                        <p>摘自：http://blog.chinaunix.net/u/18/showart_18583.html<br />
                        </p>
                        </div>
                        </td>
                    </tr>
                </tbody>
            </table>
            </td>
        </tr>
    </tbody>
</table>
<img src ="http://www.blogjava.net/shiliqiang/aggbug/290843.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/shiliqiang/" target="_blank">石头@</a> 2009-08-12 16:15 <a href="http://www.blogjava.net/shiliqiang/articles/290843.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>有点变态的问题</title><link>http://www.blogjava.net/shiliqiang/articles/290449.html</link><dc:creator>石头@</dc:creator><author>石头@</author><pubDate>Sun, 09 Aug 2009 10:09:00 GMT</pubDate><guid>http://www.blogjava.net/shiliqiang/articles/290449.html</guid><wfw:comment>http://www.blogjava.net/shiliqiang/comments/290449.html</wfw:comment><comments>http://www.blogjava.net/shiliqiang/articles/290449.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/shiliqiang/comments/commentRss/290449.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/shiliqiang/services/trackbacks/290449.html</trackback:ping><description><![CDATA[<div class="t_msgfont">
<p>第一，谈谈final, finally, finalize的区别。 <br />
<br />
第二，Anonymous Inner Class (匿名内部类) 是否可以extends(继承)其它类，是否<br />
可以implements(实现)<a target="_blank" href="http://whatis.ctocio.com.cn/searchwhatis/103/6025603.shtml">interface</a>(接口)?<br />
<br />
第三，Static Nested Class 和 Inner Class的不同，说得越多越好(面试题有的很笼<br />
统)。<br />
<br />
第四，&amp;和&amp;&amp;的区别。<br />
<br />
第五，HashMap和Hashtable的区别。<br />
<br />
第六，Collection 和 Collections的区别。<br />
<br />
第七，什么时候用assert。<br />
<br />
第八，GC是什么? 为什么要有GC? <br />
<br />
第九，String s = new String("xyz");创建了几个String Object?<br />
<br />
第十，Math.round(11.5)等於多少? Math.round(-11.5)等於多少?<br />
<br />
第十一，short s1 = 1; s1 = s1 + 1;有什么错? short s1 = 1; s1 += 1;有什么错<br />
?<br />
<br />
第十二，sleep() 和 wait() 有什么区别?<br />
<br />
第十三，<a target="_blank" href="http://whatis.ctocio.com.cn/searchwhatis/403/5948403.shtml">Java</a>有没有goto?<br />
<br />
第十四，数组有没有length()这个方法? String有没有length()这个方法?<br />
<br />
第十五，Overload和Override的区别。Overloaded的方法是否可以改变返回值的类型<br />
?<br />
<br />
第十六，Set里的元素是不能重复的，那么用什么方法来区分重复与否呢? 是用==还是<br />
equals()? 它们有何区别?<br />
<br />
第十七，给我一个你最常见到的<a target="_blank" href="http://whatis.ctocio.com.cn/searchwhatis/266/6093766.shtml">runtime</a> exception。<br />
<br />
第十八，error和exception有什么区别?<br />
<br />
第十九，List, Set, Map是否继承自Collection接口?<br />
<br />
第二十，abstract <a target="_blank" href="http://whatis.ctocio.com.cn/searchwhatis/213/5947213.shtml">class</a>和interface有什么区别?<br />
<br />
第二十一，abstract的<a target="_blank" href="http://whatis.ctocio.com.cn/searchwhatis/229/5948729.shtml">method</a>是否可同时是static,是否可同时是<a target="_blank" href="http://whatis.ctocio.com.cn/searchwhatis/326/6093326.shtml">native</a>，是否可同时<br />
是synchronized?<br />
<br />
第二十二，接口是否可继承接口? 抽象类是否可实现(implements)接口? 抽象类是否<br />
可继承实体类(concrete class)?<br />
<br />
第二十三，启动一个<a target="_blank" href="http://whatis.ctocio.com.cn/searchwhatis/146/7352146.shtml">线程</a>是用run()还是start()?<br />
<br />
第二十四，构造器Constructor是否可被override?<br />
<br />
第二十五，是否可以继承String类?<br />
<br />
第二十六，当一个线程进入一个对象的一个synchronized方法后，其它线程是否可进<br />
入此对象的其它方法?<br />
<br />
第二十七，try {}里有一个return语句，那么紧跟在这个try后的finally {}里的cod<br />
e会不会被执行，什么时候被执行，在return前还是后?<br />
<br />
第二十八，编程题: 用最有效率的方法算出2乘以8等於几?<br />
<br />
第二十九，两个对象值相同(x.equals(y) == true)，但却可有不同的hash <a target="_blank" href="http://whatis.ctocio.com.cn/searchwhatis/275/5947275.shtml">code</a>，这<br />
句话对不对?<br />
<br />
第三十，当一个对象被当作参数传递到一个方法后，此方法可改变这个对象的属性，<br />
并可返回变化后的结果，那么这里到底是值传递还是引用传递?<br />
<br />
第三十一，swtich是否能作用在byte上，是否能作用在long上，是否能作用在String<br />
上?<br />
<br />
第三十二，编程题: 写一个Singleton出来。</p>
<p><br />
＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝</p>
<p>以下是答案：</p>
<p>＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝</p>
<p>第一，谈谈final, finally, finalize的区别。<br />
final—修饰符（关键字）如果一个类被声明为final，意味着它不能再派生出新的子<br />
类，不能作为父类被继承。因此一个类不能既被声明为 abstract的，又被声明为final的<br />
。将变量或方法声明为final，可以保证它们在使用中不被改变。被声明为final的变量必<br />
须在声明时给定初值，而在以后的引用中只能读取，不可修改。被声明为final的方法也同<br />
样只能使用，不能重载<br />
finally—再异常处理时提供 finally 块来执行任何清除操作。如果抛出一个异常，<br />
那么相匹配的 catch 子句就会执行，然后控制就会进入 finally 块（如果有的话）。</p>
<p>　　finalize—方法名。Java 技术允许使用 finalize() 方法在垃圾收集器将对象从<a target="_blank" href="http://product.yesky.com/memory/">内存</a><br />
中清除出去之前做必要的清理工作。这个方法是由垃圾收集器在确定这个对象没有被引用<br />
时对这个对象调用的。它是在 Object 类中定义的，因此所有的类都继承了它。子类覆盖<br />
finalize() 方法以整理<span class="t_tag" href="http://bbs.ctocio.com.cn/tag.%3Cspan%20href=">php</span>?name=%CF%B5%CD%B3" onclick="tagshow(event)" class="t_tag"&gt;系统资源或者执行其他清理工作。finalize() 方法是在垃圾收集<br />
器删除对象之前对这个对象调用的。<br />
<br />
第二，Anonymous Inner Class (匿名内部类) 是否可以extends(继承)其它类，是否<br />
可以implements(实现)interface(接口)?<br />
匿名的内部类是没有名字的内部类。不能extends(继承) 其它类，但一个内部类可以<br />
作为一个接口，由另一个内部类实现。<br />
<br />
第三，Static Nested Class 和 Inner Class的不同，说得越多越好(面试题有的很笼<br />
统)。<br />
Nested Class （一般是<a target="_blank" href="http://whatis.ctocio.com.cn/searchwhatis/441/5946941.shtml">C++</a>的说法），Inner Class (一般是JAVA的说法)。Java内部<br />
类与C++嵌套类最大的不同就在于是否有指向外部的引用上。具体可见http: //www.front<br />
free<a target="_blank" href="http://whatis.ctocio.com.cn/searchwhatis/261/5949261.shtml">.net</a>/articles/services/view.asp?id=704&amp;<a target="_blank" href="http://whatis.ctocio.com.cn/searchwhatis/395/6025895.shtml">page</a>=1<br />
注： 静态内部类（Inner Class）意味着1创建一个static内部类的对象，不需要一个<br />
外部类对象，2不能从一个static内部类的一个对象访问一个外部类对象<br />
Hashtable继承自Dictionary类，而HashMap是Java1.2引进的Map interface的一个实<br />
现</p>
<p>　　HashMap允许将null作为一个entry的<a target="_blank" href="http://whatis.ctocio.com.cn/searchwhatis/25/5948525.shtml">key</a>或者value，而Hashtable不允许</p>
<p>　　还有就是，HashMap把Hashtable的contains方法去掉了，改成containsvalue和conta<br />
insKey。因为contains方法容易让人引起误解。</p>
<p>　　最大的不同是，Hashtable的方法是Synchronize的，而HashMap不是，在 <br />
多个线程访问Hashtable时，不需要自己为它的方法实现同步，而HashMap <br />
就必须为之提供外同步。<br />
第四，&amp;和&amp;&amp;的区别。<br />
&amp;是位运算符。&amp;&amp;是布尔逻辑运算符。<br />
<br />
第五，HashMap和Hashtable的区别。<br />
都属于Map接口的类，实现了将惟一键映射到特定的值上。<br />
HashMap 类没有分类或者排序。它允许一个 null 键和多个 null 值。 <br />
Hashtable 类似于 HashMap，但是不允许 null 键和 null 值。它也比 HashMap 慢，<br />
因为它是同步的。<br />
Hashtable继承自Dictionary类，而HashMap是Java1.2引进的Map interface的一个实<br />
现</p>
<p>　　还有就是，HashMap把Hashtable的contains方法去掉了，改成containsvalue和conta<br />
insKey。因为contains方法容易让人引起误解。</p>
<p>　　最大的不同是，Hashtable的方法是Synchronize的，而HashMap不是，在 <br />
多个线程访问Hashtable时，不需要自己为它的方法实现同步，而HashMap <br />
就必须为之提供外同步。</p>
<p>　　第六，Collection 和 Collections的区别。<br />
Collections是个java.util下的类，它包含有各种有关集合操作的静态方法。<br />
Collection是个java.util下的接口，它是各种集合结构的父接口.<br />
第七，什么时候用assert。<br />
断言是一个包含布尔表达式的语句，在执行这个语句时假定该表达式为 true。如果表<br />
达式计算为 false，那么系统会报告一个 AssertionError。它用于调试目的： <br />
assert(a &gt; 0); // throws an AssertionError if a &lt;= 0 <br />
断言可以有两种形式： <br />
assert Expression1 ; <br />
assert Expression1 : Expression2 ; <br />
Expression1 应该总是产生一个布尔值。 <br />
Expression2 可以是得出一个值的任意表达式。这个值用于生成显示更多调试信息的<br />
String 消息。<br />
断言在默认情况下是禁用的。要在编译时启用断言，需要使用 source 1.4 标记：</p>
<p>　　javac -source 1.4 Test.java<br />
要在运行时启用断言，可使用 -enableassertions 或者 -ea 标记。 <br />
要在运行时选择禁用断言，可使用 -da 或者 -disableassertions 标记。 <br />
要系统类中启用断言，可使用 -esa 或者 -dsa 标记。还可以在包的基础上启用或者<br />
禁用断言。 <br />
可以在预计正常情况下不会到达的任何位置上放置断言。断言可以用于验证传递给私<br />
有方法的参数。不过，断言不应该用于验证传递给公有方法的参数，因为不管是否启用了<br />
断言，公有方法都必须检查其参数。不过，既可以在公有方法中，也可以在非公有方法中<br />
利用断言测试后置条件。另外，断言不应该以任何方式改变程序的状态。 <br />
<br />
<br />
第八，GC是什么? 为什么要有GC? (基础)。<br />
GC是垃圾收集器。Java 程序员不用担心内存<span class="t_tag" href="http://bbs.ctocio.com.cn/tag.php?name=%B9%DC%C0%ED">管理</span>，因为垃圾收集器会自动进行<span class="t_tag" href="http://bbs.ctocio.com.cn/tag.php?name=%B9%DC%C0%ED">管理</span>。<br />
要请求垃圾收集，可以调用下面的方法之一： <br />
System.gc()<br />
Runtime.getRuntime().gc()<br />
<br />
第九，String s = new String("xyz");创建了几个String Object?<br />
两个对象，一个是&#8220;xyx&#8221;,一个是指向&#8220;xyx&#8221;的引用对象s。<br />
<br />
第十，Math.round(11.5)等於多少? Math.round(-11.5)等於多少?<br />
Math.round(11.5)返回（long）12，Math.round(-11.5)返回（long）-11;<br />
<br />
第十一，short s1 = 1; s1 = s1 + 1;有什么错? short s1 = 1; s1 += 1;有什么错<br />
?<br />
short s1 = 1; s1 = s1 + 1;有错，s1是short型，s1+1是<a target="_blank" href="http://whatis.ctocio.com.cn/searchwhatis/69/6025569.shtml">int</a>型,不能显式转化为sho<br />
rt型。可修改为s1 =(short)(s1 + 1) 。short s1 = 1; s1 += 1正确。<br />
<br />
第十二，sleep() 和 wait() 有什么区别? 搞线程的最爱<br />
sleep()方法是使线程停止一段时间的方法。在sleep 时间间隔期满后，线程不一定立<br />
即恢复执行。这是因为在那个时刻，其它线程可能正在运行而且没有被调度为放弃执行，<br />
除非(a)&#8220;醒来&#8221;的线程具有更高的优先级<br />
(b)正在运行的线程因为其它原因而阻塞。<br />
wait()是线程交互时，如果线程对一个同步对象x 发出一个wait()调用，该线程会暂<br />
停执行，被调对象进入等待状态，直到被唤醒或等待时间到。<br />
第十三，Java有没有goto?<br />
Goto—java中的保留字，现在没有在java中使用。<br />
<br />
第十四，数组有没有length()这个方法? String有没有length()这个方法？<br />
数组没有length()这个方法，有length的属性。<br />
String有有length()这个方法。<br />
<br />
第十五，Overload和Override的区别。Overloaded的方法是否可以改变返回值的类型<br />
?<br />
方法的重写Overriding和重载Overloading是Java多态性的不同表现。重写Overridin<br />
g是父类与子类之间多态性的一种表现，重载Overloading是一个类中多态性的一种表现。<br />
如果在子类中定义某方法与其父类有相同的名称和参数，我们说该方法被重写 (Overridi<br />
ng)。子类的对象使用这个方法时，将调用子类中的定义，对它而言，父类中的定义如同被<br />
&#8220;屏蔽&#8221;了。如果在一个类中定义了多个同名的方法，它们或有不同的参数个数或有不同<br />
的参数类型，则称为方法的重载(Overloading)。Overloaded的方法是可以改变返回值的类<br />
型。<br />
<br />
第十六，Set里的元素是不能重复的，那么用什么方法来区分重复与否呢? 是用==还是<br />
equals()? 它们有何区别?<br />
Set里的元素是不能重复的，那么用iterator()方法来区分重复与否。equals()是判读<br />
两个Set是否相等。<br />
equals()和==方法决定引用值是否指向同一对象equals()在类中被覆盖，为的是当两<br />
个分离的对象的内容和类型相配的话，返回真值。<br />
<br />
第十七，给我一个你最常见到的runtime exception。<br />
ArithmeticException, ArrayStoreException, BufferOverflowException, BufferU<br />
nderflowException, CannotRedoException, CannotUndoException, ClassCastExceptio<br />
n, CMMException, ConcurrentModificationException, DOMException, EmptyStackExce<br />
ption, IllegalArgumentException, IllegalMonitorStateException, IllegalPathStat<br />
eException, IllegalStateException, <br />
ImagingOpException, IndexOutOfBoundsException, MissingResourceException, N<br />
egativeArraySizeException, NoSuchElementException, NullPointerException, Profi<br />
leDataException, ProviderException, RasterFormatException, SecurityException, <br />
SystemException, UndeclaredThrowableException, UnmodifiableSetException, Unsup<br />
portedOperationException<br />
<br />
第十八，error和exception有什么区别?<br />
error 表示恢复不是不可能但很困难的情况下的一种严重问题。比如说内存溢出。不<br />
可能指望程序能处理这样的情况。<br />
exception 表示一种设计或实现问题。也就是说，它表示如果程序运行正常，从不会<br />
发生的情况。<br />
第十九，List, Set, Map是否继承自Collection接口?<br />
List，Set是<br />
<br />
Map不是<br />
<br />
第二十，abstract class和interface有什么区别?<br />
声明方法的存在而不去实现它的类被叫做抽象类（abstract class），它用于要创建<br />
一个体现某些基本行为的类，并为该类声明方法，但不能在该类中实现该类的情况。不能<br />
创建abstract 类的实例。然而可以创建一个变量，其类型是一个抽象类，并让它指向具体<br />
子类的一个实例。不能有抽象构造函数或抽象静态方法。Abstract 类的子类为它们父类中<br />
的所有抽象方法提供实现，否则它们也是抽象类为。取而代之，在子类中实现该方法。知<br />
道其行为的其它类可以在类中实现这些方法。<br />
接口（interface）是抽象类的变体。在接口中，所有方法都是抽象的。多继承性可通<br />
过实现这样的接口而获得。接口中的所有方法都是抽象的，没有一个有程序体。接口只可<br />
以定义static final成员变量。接口的实现与子类相似，除了该实现类不能从接口定义中<br />
继承行为。当类实现特殊接口时，它定义（即将程序体给予）所有这种接口的方法。然后<br />
，它可以在实现了该接口的类的任何对象上调用接口的方法。由于有抽象类，它允许使用<br />
接口名作为引用变量的类型。通常的动态联编将生效。引用可以转换到接口类型或从接口<br />
类型转换，instanceof 运算符可以用来决定某对象的类是否实现了接口。<br />
<br />
第二十一，abstract的method是否可同时是static,是否可同时是native，是否可同时<br />
是synchronized?<br />
都不能<br />
<br />
第二十二，接口是否可继承接口? 抽象类是否可实现(implements)接口? 抽象类是否<br />
可继承实体类(concrete class)?<br />
接口可以继承接口。抽象类可以实现(implements)接口，抽象类是否可继承实体类，<br />
但前提是实体类必须有明确的构造函数。<br />
<br />
第二十三，启动一个线程是用run()还是start()?<br />
启动一个线程是调用start()方法，使线程所代表的虚拟处理机处于可运行状态，这意<br />
味着它可以由<a target="_blank" href="http://whatis.ctocio.com.cn/searchwhatis/0/5948500.shtml">JVM</a>调度并执行。这并不意味着线程就会立即运行。run()方法可以产生必须<br />
退出的标志来停止一个线程。<br />
第二十四，构造器Constructor是否可被override?<br />
构造器Constructor不能被继承，因此不能重写Overriding，但可以被重载Overloadi<br />
ng。<br />
<br />
第二十五，是否可以继承String类?<br />
String类是final类故不可以继承。<br />
<br />
第二十六，当一个线程进入一个对象的一个synchronized方法后，其它线程是否可进<br />
入此对象的其它方法?<br />
不能，一个对象的一个synchronized方法只能由一个线程访问。<br />
<br />
第二十七，try {}里有一个return语句，那么紧跟在这个try后的finally {}里的cod<br />
e会不会被执行，什么时候被执行，在return前还是后?<br />
会执行，在return前执行。<br />
<br />
<br />
第二十八，编程题: 用最有效率的方法算出2乘以8等於几?<br />
有C背景的程序员特别喜欢问这种问题。<br />
<br />
2 &lt;&lt; 3<br />
<br />
第二十九，两个对象值相同(x.equals(y) == true)，但却可有不同的hash code，这<br />
句话对不对?<br />
不对，有相同的hash code。<br />
<br />
第三十，当一个对象被当作参数传递到一个方法后，此方法可改变这个对象的属性，<br />
并可返回变化后的结果，那么这里到底是值传递还是引用传递? <br />
是值传递。Java 编程语言只由值传递参数。当一个对象实例作为一个参数被传递到方<br />
法中时，参数的值就是对该对象的引用。对象的内容可以在被调用的方法中改变，但对象<br />
的引用是永远不会改变的。<br />
<br />
<br />
第三十一，swtich是否能作用在byte上，是否能作用在long上，是否能作用在String<br />
上?<br />
<a target="_blank" href="http://whatis.ctocio.com.cn/searchwhatis/462/6026462.shtml">switch</a>（expr1）中，expr1是一个整数表达式。因此传递给 switch 和 case 语句的<br />
参数应该是 int、 short、 char 或者 byte。long,<a target="_blank" href="http://whatis.ctocio.com.cn/searchwhatis/400/6026400.shtml">string</a> 都不能作用于swtich。<br />
第三十二，编程题: 写一个Singleton出来。<br />
Singleton模式主要作用是保证在Java应用程序中，一个类Class只有一个实例存在。</p>
<p>　　一般Singleton模式通常有几种种形式:<br />
第一种形式: 定义一个类，它的构造函数为private的，它有一个static的private的<br />
该类变量，在类初始化时实例话，通过一个public的getInstance方法获取对它的引用,继<br />
而调用其中的方法。<br />
public class Singleton {<br />
private Singleton(){}<br />
//在自己内部定义自己一个实例，是不是很奇怪？<br />
//注意这是private 只供内部调用<br />
private static Singleton <a target="_blank" href="http://whatis.ctocio.com.cn/searchwhatis/60/6025560.shtml">instance</a> = new Singleton();<br />
//这里提供了一个供外部访问本class的静态方法，可以直接访问 <br />
public static Singleton getInstance() {<br />
return instance; <br />
} <br />
} <br />
第二种形式: <br />
public class Singleton { <br />
private static Singleton instance = null;<br />
public static synchronized Singleton getInstance() {<br />
//这个方法比上面有所改进，不用每次都进行生成对象，只是第一次 <br />
//使用时生成实例，提高了效率！<br />
if (instance==null)<br />
instance＝new Singleton();<br />
return instance; } <br />
} <br />
其他形式:<br />
定义一个类，它的构造函数为private的，所有方法为static的。<br />
一般认为第一种形式要更加<a target="_blank" href="http://security.ctocio.com.cn/">安全</a></p>
</div>
<img src ="http://www.blogjava.net/shiliqiang/aggbug/290449.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/shiliqiang/" target="_blank">石头@</a> 2009-08-09 18:09 <a href="http://www.blogjava.net/shiliqiang/articles/290449.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>java中容易出现的十大异常分析</title><link>http://www.blogjava.net/shiliqiang/articles/290313.html</link><dc:creator>石头@</dc:creator><author>石头@</author><pubDate>Sat, 08 Aug 2009 01:04:00 GMT</pubDate><guid>http://www.blogjava.net/shiliqiang/articles/290313.html</guid><wfw:comment>http://www.blogjava.net/shiliqiang/comments/290313.html</wfw:comment><comments>http://www.blogjava.net/shiliqiang/articles/290313.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/shiliqiang/comments/commentRss/290313.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/shiliqiang/services/trackbacks/290313.html</trackback:ping><description><![CDATA[<h3 class="type_reprint" title="转载"><a href="http://loveispopular.javaeye.com/blog/290394">【转】JAVA常见十大异常</a></h3>
<strong>关键字: java异常</strong>
<div class="blog_content">
java 10大常见异常，出现的原因和解决方法
<br />
作为JAVA程序员，写代码难免出现bug，我们来看看java 10大常见异常
<br />
<br />
NO.1　java.lang.NullPointerException
<br />
<br />
这个异常大家肯定都经常遇到，异常的解释是 "程序遇上了空指针
"，简单地说就是调用了未经初始化的对象或者是不存在的对象，这个错误经常出现在创建图片，调用数组这些操作中，比如图片未经初始化，或者图片创建时的路
径错误等等。对数组操作中出现空指针，很多情况下是一些刚开始学习编程的朋友常犯的错误，即把数组的初始化和数组元素的初始化混淆起来了。数组的初始化是
对数组分配需要的空间，而初始化后的数组，其中的元素并没有实例化，依然是空的，所以还需要对每个元素都进行初始化（如果要调用的话） <br />
<br />
NO.2　java.lang.ClassNotFoundException
<br />
<br />
这个异常是很多原本在JB等开发环境中开发的程序员，把JB下的程序包放在WTk下编译经常出现的问题，异常的解释是 "指定的类不存在
"，这里主要考虑一下类的名称和路径是否正确即可，如果是在JB下做的程序包，一般都是默认加上Package的，所以转到WTK下后要注意把
Package的路径加上。 <br />
&nbsp;
<br />
<br />
NO.3　java.lang.ArithmeticException
<br />
<br />
这个异常的解释是 "数学运算异常 "，比如程序中出现了除以零这样的运算就会出这样的异常，对这种异常，大家就要好好检查一下自己程序中涉及到数学运算的地方，公式是不是有不妥了。
<br />
<br />
NO.4　java.lang.ArrayIndexOutOfBoundsException
<br />
<br />
这个异常相信很多朋友也经常遇到过，异常的解释是 "数组下标越界
"，现在程序中大多都有对数组的操作，因此在调用数组的时候一定要认真检查，看自己调用的下标是不是超出了数组的范围，一般来说，显示（即直接用常数当下
标）调用不太容易出这样的错，但隐式（即用变量表示下标）调用就经常出错了，还有一种情况，是程序中定义的数组的长度是通过某些特定方法决定的，不是事先
声明的，这个时候，最好先查看一下数组的length，以免出现这个异常。 <br />
<br />
NO.5　java.lang.IllegalArgumentException
<br />
<br />
这个异常的解释是 "方法的参数错误
"，很多J2ME的类库中的方法在一些情况下都会引发这样的错误，比如音量调节方法中的音量参数如果写成负数就会出现这个异常，再比如
g.setColor(int red,int green,int
blue)这个方法中的三个值，如果有超过２５５的也会出现这个异常，因此一旦发现这个异常，我们要做的，就是赶紧去检查一下方法调用中的参数传递是不是
出现了错误。 <br />
<br />
NO.6　java.lang.IllegalAccessException
<br />
<br />
这个异常的解释是 "没有访问权限 "，当应用程序要调用一个类，但当前的方法即没有对该类的访问权限便会出现这个异常。对程序中用了Package的情况下要注意这个异常。
<br />
<br />
NO.7　java.lang.IncompatibleClassChangeError
<br />
<br />
不兼容的类变化错误。当正在执行的方法所依赖的类定义发生了不兼容的改变时，抛出该异常。一般在修改了应用中的某些类的声明定义而没有对整个应用重新编译而直接运行的情况下，容易引发该错误。
<br />
<br />
NO.8　java.lang.InstantiationError
<br />
<br />
实例化错误。当一个应用试图通过Java的new操作符构造一个抽象类或者接口时抛出该异常.
<br />
<br />
NO.9　java.lang.LinkageError
<br />
<br />
链接错误。该错误及其所有子类指示某个类依赖于另外一些类，在该类编译之后，被依赖的类改变了其类定义而没有重新编译所有的类，进而引发错误的情况。
<br />
<br />
NO.10　java.lang.StackOverflowError
<br />
<br />
堆栈溢出错误。当一个应用递归调用的层次太深而导致堆栈溢出时抛出该错误。
</div>
<img src ="http://www.blogjava.net/shiliqiang/aggbug/290313.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/shiliqiang/" target="_blank">石头@</a> 2009-08-08 09:04 <a href="http://www.blogjava.net/shiliqiang/articles/290313.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>很有深度的几个java题目！</title><link>http://www.blogjava.net/shiliqiang/articles/289907.html</link><dc:creator>石头@</dc:creator><author>石头@</author><pubDate>Wed, 05 Aug 2009 02:32:00 GMT</pubDate><guid>http://www.blogjava.net/shiliqiang/articles/289907.html</guid><wfw:comment>http://www.blogjava.net/shiliqiang/comments/289907.html</wfw:comment><comments>http://www.blogjava.net/shiliqiang/articles/289907.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/shiliqiang/comments/commentRss/289907.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/shiliqiang/services/trackbacks/289907.html</trackback:ping><description><![CDATA[&nbsp; 在2009年的JavaOne大会上，Joshua Bloch和Neal Gafter又为我们带来的7道谜题，挺有意思的。大家不妨看看。 <br />
摘自： <br />
Return of the Puzzlers: Schlock and Awe <br />
Joshua Bloch, Google, Inc.; Neal Gafter, Microsoft <br />
<a href="http://developers.sun.com/learning/javaoneonline/sessions/2009/pdf/TS-5186.pdf" target="_blank">http://developers.sun.com/learning/javaoneonline/sessions/2009/pdf/TS-5186.pdf</a> <br />
1.Life's Persistent Questions <br />
<dl class="code"><dt>Java code</dt><dd>
<pre>
<div><!-- Code highlighting produced by Actipro CodeHighlighter (freeware)
http://www.CodeHighlighter.com/
--><span style="color: #000000;"><br />
</span><span style="color: #0000ff;">public</span><span style="color: #000000;"> </span><span style="color: #0000ff;">class</span><span style="color: #000000;"> SimpleQuestion {<br />
<br />
</span><span style="color: #0000ff;">static</span><span style="color: #000000;"> </span><span style="color: #0000ff;">boolean</span><span style="color: #000000;"> yesOrNo(String s) {<br />
s </span><span style="color: #000000;">=</span><span style="color: #000000;"> s.toLowerCase();<br />
</span><span style="color: #0000ff;">if</span><span style="color: #000000;"> (s.equals(</span><span style="color: #000000;">"</span><span style="color: #000000;">yes</span><span style="color: #000000;">"</span><span style="color: #000000;">) </span><span style="color: #000000;">||</span><span style="color: #000000;"> s.equals(</span><span style="color: #000000;">"</span><span style="color: #000000;">y</span><span style="color: #000000;">"</span><span style="color: #000000;">) </span><span style="color: #000000;">||</span><span style="color: #000000;"> s.equals(</span><span style="color: #000000;">"</span><span style="color: #000000;">t</span><span style="color: #000000;">"</span><span style="color: #000000;">)) {<br />
s </span><span style="color: #000000;">=</span><span style="color: #000000;"> </span><span style="color: #000000;">"</span><span style="color: #000000;">true</span><span style="color: #000000;">"</span><span style="color: #000000;">;<br />
}<br />
</span><span style="color: #0000ff;">return</span><span style="color: #000000;"> Boolean.getBoolean(s);<br />
}<br />
<br />
</span><span style="color: #0000ff;">public</span><span style="color: #000000;"> </span><span style="color: #0000ff;">static</span><span style="color: #000000;"> </span><span style="color: #0000ff;">void</span><span style="color: #000000;"> main(String[] args) {<br />
System.out.println(yesOrNo(</span><span style="color: #000000;">"</span><span style="color: #000000;">true</span><span style="color: #000000;">"</span><span style="color: #000000;">) </span><span style="color: #000000;">+</span><span style="color: #000000;"> </span><span style="color: #000000;">"</span><span style="color: #000000;"> </span><span style="color: #000000;">"</span><span style="color: #000000;"> </span><span style="color: #000000;">+</span><span style="color: #000000;"> yesOrNo(</span><span style="color: #000000;">"</span><span style="color: #000000;">Yes</span><span style="color: #000000;">"</span><span style="color: #000000;">));<br />
}<br />
}<br />
</span></div>
<br />
</pre>
</dd></dl> <br />
问题：程序打印什么？ <br />
如果熟悉Boolean.getBoolean（）这个方法的话，应该不会出错。方法的功能参考文档。 <br />
<br />
2.Instruments of Tortue <br />
<dl class="code"><dt>Java code</dt><dd>
<pre>
<div><!-- Code highlighting produced by Actipro CodeHighlighter (freeware)
http://www.CodeHighlighter.com/
--><span style="color: #000000;"><br />
<br />
</span><span style="color: #0000ff;">import</span><span style="color: #000000;"> java.util.Arrays;<br />
</span><span style="color: #0000ff;">import</span><span style="color: #000000;"> java.util.Collection;<br />
</span><span style="color: #0000ff;">import</span><span style="color: #000000;"> java.util.HashSet;<br />
<br />
</span><span style="color: #0000ff;">public</span><span style="color: #000000;"> </span><span style="color: #0000ff;">class</span><span style="color: #000000;"> InstrumentedHashSet</span><span style="color: #000000;">&lt;</span><span style="color: #000000;">E</span><span style="color: #000000;">&gt;</span><span style="color: #000000;"> </span><span style="color: #0000ff;">extends</span><span style="color: #000000;"> HashSet</span><span style="color: #000000;">&lt;</span><span style="color: #000000;">E</span><span style="color: #000000;">&gt;</span><span style="color: #000000;"> {<br />
</span><span style="color: #0000ff;">private</span><span style="color: #000000;"> </span><span style="color: #0000ff;">int</span><span style="color: #000000;"> addCount </span><span style="color: #000000;">=</span><span style="color: #000000;"> </span><span style="color: #000000;">0</span><span style="color: #000000;">;<br />
@Override<br />
</span><span style="color: #0000ff;">public</span><span style="color: #000000;"> </span><span style="color: #0000ff;">boolean</span><span style="color: #000000;"> add(E e){<br />
addCount</span><span style="color: #000000;">++</span><span style="color: #000000;">;<br />
</span><span style="color: #0000ff;">return</span><span style="color: #000000;"> </span><span style="color: #0000ff;">super</span><span style="color: #000000;">.add(e);<br />
}<br />
<br />
@Override<br />
</span><span style="color: #0000ff;">public</span><span style="color: #000000;"> </span><span style="color: #0000ff;">boolean</span><span style="color: #000000;"> addAll(Collection</span><span style="color: #000000;">&lt;?</span><span style="color: #000000;"> </span><span style="color: #0000ff;">extends</span><span style="color: #000000;"> E</span><span style="color: #000000;">&gt;</span><span style="color: #000000;"> c){<br />
addCount </span><span style="color: #000000;">+=</span><span style="color: #000000;"> c.size();<br />
</span><span style="color: #0000ff;">return</span><span style="color: #000000;"> </span><span style="color: #0000ff;">super</span><span style="color: #000000;">.addAll(c);<br />
}<br />
<br />
</span><span style="color: #0000ff;">public</span><span style="color: #000000;"> </span><span style="color: #0000ff;">static</span><span style="color: #000000;"> </span><span style="color: #0000ff;">void</span><span style="color: #000000;"> main(String[] args) {<br />
InstrumentedHashSet</span><span style="color: #000000;">&lt;</span><span style="color: #000000;">String</span><span style="color: #000000;">&gt;</span><span style="color: #000000;"> s </span><span style="color: #000000;">=</span><span style="color: #000000;"> </span><span style="color: #0000ff;">new</span><span style="color: #000000;"> InstrumentedHashSet</span><span style="color: #000000;">&lt;</span><span style="color: #000000;">String</span><span style="color: #000000;">&gt;</span><span style="color: #000000;">();<br />
s.addAll(Arrays.asList(</span><span style="color: #000000;">"</span><span style="color: #000000;">Accordion</span><span style="color: #000000;">"</span><span style="color: #000000;">,</span><span style="color: #000000;">"</span><span style="color: #000000;">Banjo</span><span style="color: #000000;">"</span><span style="color: #000000;">,</span><span style="color: #000000;">"</span><span style="color: #000000;">Kazoo</span><span style="color: #000000;">"</span><span style="color: #000000;">));<br />
System.out.println(s.addCount);<br />
}<br />
}<br />
<br />
</span></div>
<br />
</pre>
</dd></dl> <br />
问题：打印结果是什么？ <br />
<br />
这个看第一遍可能会出错，不过也算容易理解。 <br />
<br />
3.Iterator Titillator <br />
<dl class="code"><dt>Java code</dt><dd>
<pre>
<div><!-- Code highlighting produced by Actipro CodeHighlighter (freeware)
http://www.CodeHighlighter.com/
--><span style="color: #000000;"><br />
</span><span style="color: #0000ff;">import</span><span style="color: #000000;"> java.util.Iterator;<br />
</span><span style="color: #0000ff;">import</span><span style="color: #000000;"> java.util.NoSuchElementException;<br />
<br />
</span><span style="color: #0000ff;">public</span><span style="color: #000000;"> </span><span style="color: #0000ff;">abstract</span><span style="color: #000000;"> </span><span style="color: #0000ff;">class</span><span style="color: #000000;"> AbstractIterator</span><span style="color: #000000;">&lt;</span><span style="color: #000000;">T</span><span style="color: #000000;">&gt;</span><span style="color: #000000;"> </span><span style="color: #0000ff;">implements</span><span style="color: #000000;"> Iterator</span><span style="color: #000000;">&lt;</span><span style="color: #000000;">T</span><span style="color: #000000;">&gt;</span><span style="color: #000000;"> {<br />
<br />
T next </span><span style="color: #000000;">=</span><span style="color: #000000;"> nextElement();<br />
<br />
</span><span style="color: #0000ff;">public</span><span style="color: #000000;"> </span><span style="color: #0000ff;">boolean</span><span style="color: #000000;"> hasNext() {<br />
</span><span style="color: #0000ff;">return</span><span style="color: #000000;"> next </span><span style="color: #000000;">!=</span><span style="color: #000000;"> </span><span style="color: #0000ff;">null</span><span style="color: #000000;">;<br />
}<br />
<br />
</span><span style="color: #0000ff;">public</span><span style="color: #000000;"> T next() {<br />
</span><span style="color: #0000ff;">if</span><span style="color: #000000;"> (next </span><span style="color: #000000;">==</span><span style="color: #000000;"> </span><span style="color: #0000ff;">null</span><span style="color: #000000;">) {<br />
</span><span style="color: #0000ff;">throw</span><span style="color: #000000;"> </span><span style="color: #0000ff;">new</span><span style="color: #000000;"> NoSuchElementException();<br />
}<br />
T result </span><span style="color: #000000;">=</span><span style="color: #000000;"> next;<br />
next </span><span style="color: #000000;">=</span><span style="color: #000000;"> nextElement();<br />
</span><span style="color: #0000ff;">return</span><span style="color: #000000;"> result;<br />
}<br />
<br />
</span><span style="color: #0000ff;">public</span><span style="color: #000000;"> </span><span style="color: #0000ff;">void</span><span style="color: #000000;"> remove() {<br />
</span><span style="color: #0000ff;">throw</span><span style="color: #000000;"> </span><span style="color: #0000ff;">new</span><span style="color: #000000;"> UnsupportedOperationException();<br />
}<br />
<br />
</span><span style="color: #0000ff;">protected</span><span style="color: #000000;"> </span><span style="color: #0000ff;">abstract</span><span style="color: #000000;"> T nextElement();<br />
<br />
</span><span style="color: #0000ff;">private</span><span style="color: #000000;"> </span><span style="color: #0000ff;">static</span><span style="color: #000000;"> Iterator</span><span style="color: #000000;">&lt;</span><span style="color: #000000;">Character</span><span style="color: #000000;">&gt;</span><span style="color: #000000;"> test(</span><span style="color: #0000ff;">final</span><span style="color: #000000;"> String s) {<br />
</span><span style="color: #0000ff;">return</span><span style="color: #000000;"> </span><span style="color: #0000ff;">new</span><span style="color: #000000;"> AbstractIterator</span><span style="color: #000000;">&lt;</span><span style="color: #000000;">Character</span><span style="color: #000000;">&gt;</span><span style="color: #000000;">() {<br />
<br />
</span><span style="color: #0000ff;">private</span><span style="color: #000000;"> </span><span style="color: #0000ff;">int</span><span style="color: #000000;"> cursor </span><span style="color: #000000;">=</span><span style="color: #000000;"> </span><span style="color: #000000;">0</span><span style="color: #000000;">;<br />
<br />
</span><span style="color: #0000ff;">protected</span><span style="color: #000000;"> Character nextElement() {<br />
</span><span style="color: #0000ff;">return</span><span style="color: #000000;"> cursor </span><span style="color: #000000;">==</span><span style="color: #000000;"> s.length() </span><span style="color: #000000;">?</span><span style="color: #000000;"> </span><span style="color: #0000ff;">null</span><span style="color: #000000;"> : s.charAt(cursor</span><span style="color: #000000;">++</span><span style="color: #000000;">);<br />
}<br />
};<br />
}<br />
<br />
</span><span style="color: #0000ff;">public</span><span style="color: #000000;"> </span><span style="color: #0000ff;">static</span><span style="color: #000000;"> </span><span style="color: #0000ff;">void</span><span style="color: #000000;"> main(String[] args) {<br />
</span><span style="color: #0000ff;">for</span><span style="color: #000000;"> (Iterator</span><span style="color: #000000;">&lt;</span><span style="color: #000000;">Character</span><span style="color: #000000;">&gt;</span><span style="color: #000000;"> i </span><span style="color: #000000;">=</span><span style="color: #000000;"> test(</span><span style="color: #000000;">"</span><span style="color: #000000;">OPS</span><span style="color: #000000;">"</span><span style="color: #000000;">); i.hasNext();) {<br />
System.out.print(i.next());<br />
}<br />
}<br />
}<br />
<br />
</span></div>
<br />
</pre>
</dd></dl> <br />
问题：输出结果是什么？ <br />
<br />
理解如何正确的设计Iterator。 <br />
<br />
4.Search for the One <br />
<dl class="code"><dt>Java code</dt><dd>
<pre>
<div><!-- Code highlighting produced by Actipro CodeHighlighter (freeware)
http://www.CodeHighlighter.com/
--><span style="color: #000000;"><br />
<br />
</span><span style="color: #0000ff;">import</span><span style="color: #000000;"> java.util.ArrayList;<br />
</span><span style="color: #0000ff;">import</span><span style="color: #000000;"> java.util.Collections;<br />
</span><span style="color: #0000ff;">import</span><span style="color: #000000;"> java.util.Comparator;<br />
</span><span style="color: #0000ff;">import</span><span style="color: #000000;"> java.util.List;<br />
<br />
<br />
</span><span style="color: #0000ff;">public</span><span style="color: #000000;"> </span><span style="color: #0000ff;">class</span><span style="color: #000000;"> Searching {<br />
</span><span style="color: #0000ff;">public</span><span style="color: #000000;"> </span><span style="color: #0000ff;">static</span><span style="color: #000000;"> </span><span style="color: #0000ff;">void</span><span style="color: #000000;"> main(String[] args) {<br />
String[] strings </span><span style="color: #000000;">=</span><span style="color: #000000;"> { </span><span style="color: #000000;">"</span><span style="color: #000000;">0</span><span style="color: #000000;">"</span><span style="color: #000000;">, </span><span style="color: #000000;">"</span><span style="color: #000000;">1</span><span style="color: #000000;">"</span><span style="color: #000000;">, </span><span style="color: #000000;">"</span><span style="color: #000000;">2</span><span style="color: #000000;">"</span><span style="color: #000000;">, </span><span style="color: #000000;">"</span><span style="color: #000000;">3</span><span style="color: #000000;">"</span><span style="color: #000000;">, </span><span style="color: #000000;">"</span><span style="color: #000000;">4</span><span style="color: #000000;">"</span><span style="color: #000000;">, </span><span style="color: #000000;">"</span><span style="color: #000000;">5</span><span style="color: #000000;">"</span><span style="color: #000000;">};<br />
<br />
List</span><span style="color: #000000;">&lt;</span><span style="color: #000000;">Integer</span><span style="color: #000000;">&gt;</span><span style="color: #000000;"> integers </span><span style="color: #000000;">=</span><span style="color: #000000;"> </span><span style="color: #0000ff;">new</span><span style="color: #000000;"> ArrayList</span><span style="color: #000000;">&lt;</span><span style="color: #000000;">Integer</span><span style="color: #000000;">&gt;</span><span style="color: #000000;">();<br />
</span><span style="color: #0000ff;">for</span><span style="color: #000000;">(String s : strings){<br />
integers.add(Integer.valueOf(s));<br />
}<br />
<br />
System.out.println(Collections.binarySearch(integers, </span><span style="color: #000000;">1</span><span style="color: #000000;">,cmp));<br />
}<br />
<br />
</span><span style="color: #0000ff;">static</span><span style="color: #000000;"> Comparator</span><span style="color: #000000;">&lt;</span><span style="color: #000000;">Integer</span><span style="color: #000000;">&gt;</span><span style="color: #000000;"> cmp </span><span style="color: #000000;">=</span><span style="color: #000000;"> </span><span style="color: #0000ff;">new</span><span style="color: #000000;"> Comparator</span><span style="color: #000000;">&lt;</span><span style="color: #000000;">Integer</span><span style="color: #000000;">&gt;</span><span style="color: #000000;">(){<br />
</span><span style="color: #0000ff;">public</span><span style="color: #000000;"> </span><span style="color: #0000ff;">int</span><span style="color: #000000;"> compare(Integer i,Integer j){<br />
</span><span style="color: #0000ff;">return</span><span style="color: #000000;"> i</span><span style="color: #000000;">&lt;</span><span style="color: #000000;">j</span><span style="color: #000000;">?-</span><span style="color: #000000;">1</span><span style="color: #000000;">:(i</span><span style="color: #000000;">==</span><span style="color: #000000;">j</span><span style="color: #000000;">?</span><span style="color: #000000;">0</span><span style="color: #000000;">:</span><span style="color: #000000;">1</span><span style="color: #000000;">);<br />
}<br />
};<br />
}<br />
<br />
</span></div>
<br />
</pre>
</dd></dl> <br />
问题：打印结果是什么？ <br />
<br />
如果看过《Java Puzzlers》这本书的话应该容易发现问题。 <br />
<br />
5.Cogito Ergo Sum <br />
<dl class="code"><dt>Java code</dt><dd>
<pre>
<div><!-- Code highlighting produced by Actipro CodeHighlighter (freeware)
http://www.CodeHighlighter.com/
--><span style="color: #000000;"><br />
<br />
</span><span style="color: #0000ff;">import</span><span style="color: #000000;"> java.util.LinkedHashMap;<br />
</span><span style="color: #0000ff;">import</span><span style="color: #000000;"> java.util.Map;<br />
<br />
</span><span style="color: #0000ff;">public</span><span style="color: #000000;"> </span><span style="color: #0000ff;">enum</span><span style="color: #000000;"> RomanNumeral {<br />
<br />
I(</span><span style="color: #000000;">1</span><span style="color: #000000;">), V(</span><span style="color: #000000;">5</span><span style="color: #000000;">), X(</span><span style="color: #000000;">10</span><span style="color: #000000;">), L(</span><span style="color: #000000;">50</span><span style="color: #000000;">), C(</span><span style="color: #000000;">100</span><span style="color: #000000;">), D(</span><span style="color: #000000;">500</span><span style="color: #000000;">), M(</span><span style="color: #000000;">1000</span><span style="color: #000000;">);<br />
</span><span style="color: #0000ff;">private</span><span style="color: #000000;"> </span><span style="color: #0000ff;">static</span><span style="color: #000000;"> Map</span><span style="color: #000000;">&lt;</span><span style="color: #000000;">Integer, RomanNumeral</span><span style="color: #000000;">&gt;</span><span style="color: #000000;"> map </span><span style="color: #000000;">=</span><span style="color: #000000;"> </span><span style="color: #0000ff;">new</span><span style="color: #000000;"> LinkedHashMap</span><span style="color: #000000;">&lt;</span><span style="color: #000000;">Integer, RomanNumeral</span><span style="color: #000000;">&gt;</span><span style="color: #000000;">();<br />
</span><span style="color: #0000ff;">public</span><span style="color: #000000;"> </span><span style="color: #0000ff;">final</span><span style="color: #000000;"> </span><span style="color: #0000ff;">int</span><span style="color: #000000;"> val;<br />
<br />
RomanNumeral(</span><span style="color: #0000ff;">int</span><span style="color: #000000;"> val) {<br />
</span><span style="color: #0000ff;">this</span><span style="color: #000000;">.val </span><span style="color: #000000;">=</span><span style="color: #000000;"> val;<br />
storeInMap();<br />
}<br />
<br />
</span><span style="color: #0000ff;">private</span><span style="color: #000000;"> </span><span style="color: #0000ff;">void</span><span style="color: #000000;"> storeInMap() {<br />
map.put(val, </span><span style="color: #0000ff;">this</span><span style="color: #000000;">);<br />
}<br />
<br />
</span><span style="color: #0000ff;">public</span><span style="color: #000000;"> </span><span style="color: #0000ff;">static</span><span style="color: #000000;"> RomanNumeral fromInt(</span><span style="color: #0000ff;">int</span><span style="color: #000000;"> val) {<br />
</span><span style="color: #0000ff;">return</span><span style="color: #000000;"> map.get(val);<br />
}<br />
<br />
</span><span style="color: #0000ff;">public</span><span style="color: #000000;"> </span><span style="color: #0000ff;">static</span><span style="color: #000000;"> </span><span style="color: #0000ff;">void</span><span style="color: #000000;"> main(String[] args) {<br />
</span><span style="color: #0000ff;">int</span><span style="color: #000000;"> sum </span><span style="color: #000000;">=</span><span style="color: #000000;"> </span><span style="color: #000000;">0</span><span style="color: #000000;">;<br />
</span><span style="color: #0000ff;">for</span><span style="color: #000000;"> (</span><span style="color: #0000ff;">int</span><span style="color: #000000;"> i </span><span style="color: #000000;">=</span><span style="color: #000000;"> </span><span style="color: #000000;">0</span><span style="color: #000000;">; i </span><span style="color: #000000;">&lt;</span><span style="color: #000000;"> </span><span style="color: #000000;">1000</span><span style="color: #000000;">; i</span><span style="color: #000000;">++</span><span style="color: #000000;">) {<br />
</span><span style="color: #0000ff;">if</span><span style="color: #000000;"> (fromInt(i) </span><span style="color: #000000;">!=</span><span style="color: #000000;"> </span><span style="color: #0000ff;">null</span><span style="color: #000000;">) {<br />
sum </span><span style="color: #000000;">+=</span><span style="color: #000000;"> i;<br />
}<br />
}<br />
System.out.println(sum);<br />
}<br />
}<br />
<br />
</span></div>
<br />
</pre>
</dd></dl> <br />
问题：打印结果是什么？ <br />
<br />
如果理解java加载类和创建对象的顺序的话这个问题容易理解。 <br />
<br />
6.Thread Friendly <br />
<dl class="code"><dt>Java code</dt><dd>
<pre>
<div><!-- Code highlighting produced by Actipro CodeHighlighter (freeware)
http://www.CodeHighlighter.com/
--><span style="color: #000000;"><br />
<br />
</span><span style="color: #0000ff;">public</span><span style="color: #000000;"> </span><span style="color: #0000ff;">class</span><span style="color: #000000;"> ThreadFriendly {<br />
ThreadLocal</span><span style="color: #000000;">&lt;</span><span style="color: #000000;">Value</span><span style="color: #000000;">&gt;</span><span style="color: #000000;"> threadLocalPart </span><span style="color: #000000;">=</span><span style="color: #000000;"> </span><span style="color: #0000ff;">new</span><span style="color: #000000;"> ThreadLocal</span><span style="color: #000000;">&lt;</span><span style="color: #000000;">Value</span><span style="color: #000000;">&gt;</span><span style="color: #000000;">();<br />
</span><span style="color: #0000ff;">class</span><span style="color: #000000;"> Value{<br />
</span><span style="color: #0000ff;">final</span><span style="color: #000000;"> </span><span style="color: #0000ff;">int</span><span style="color: #000000;"> i;<br />
Value(</span><span style="color: #0000ff;">int</span><span style="color: #000000;"> i){<br />
</span><span style="color: #0000ff;">this</span><span style="color: #000000;">.i </span><span style="color: #000000;">=</span><span style="color: #000000;"> i;<br />
}<br />
}<br />
<br />
ThreadFriendly setThreadVal(</span><span style="color: #0000ff;">int</span><span style="color: #000000;"> i){<br />
threadLocalPart.set(</span><span style="color: #0000ff;">new</span><span style="color: #000000;"> Value(i));<br />
</span><span style="color: #0000ff;">return</span><span style="color: #000000;"> </span><span style="color: #0000ff;">this</span><span style="color: #000000;">;<br />
}<br />
<br />
</span><span style="color: #0000ff;">int</span><span style="color: #000000;"> getThreadVal(){<br />
</span><span style="color: #0000ff;">return</span><span style="color: #000000;"> threadLocalPart.get().i;<br />
}<br />
<br />
</span><span style="color: #0000ff;">public</span><span style="color: #000000;"> </span><span style="color: #0000ff;">static</span><span style="color: #000000;"> </span><span style="color: #0000ff;">void</span><span style="color: #000000;"> main(String[] args) {<br />
</span><span style="color: #0000ff;">int</span><span style="color: #000000;"> sum </span><span style="color: #000000;">=</span><span style="color: #000000;"> </span><span style="color: #000000;">0</span><span style="color: #000000;">;<br />
</span><span style="color: #0000ff;">for</span><span style="color: #000000;">(</span><span style="color: #0000ff;">int</span><span style="color: #000000;"> i </span><span style="color: #000000;">=</span><span style="color: #000000;"> </span><span style="color: #000000;">-</span><span style="color: #000000;">500000</span><span style="color: #000000;">;i</span><span style="color: #000000;">&lt;=</span><span style="color: #000000;">500000</span><span style="color: #000000;">;i</span><span style="color: #000000;">++</span><span style="color: #000000;">){<br />
sum</span><span style="color: #000000;">+=</span><span style="color: #000000;"> </span><span style="color: #0000ff;">new</span><span style="color: #000000;"> ThreadFriendly().setThreadVal(i).getThreadVal();<br />
}<br />
System.out.println(sum);<br />
}<br />
}<br />
<br />
</span></div>
<br />
</pre>
</dd></dl> <br />
问题：打印结果是什么？ <br />
<br />
理解内部类和ThreadLocal。 <br />
<br />
7.When Words Collide <br />
<dl class="code"><dt>Java code</dt><dd>
<pre>
<div><!-- Code highlighting produced by Actipro CodeHighlighter (freeware)
http://www.CodeHighlighter.com/
--><span style="color: #000000;"><br />
</span><span style="color: #0000ff;">public</span><span style="color: #000000;"> </span><span style="color: #0000ff;">class</span><span style="color: #000000;"> PrintWords {<br />
</span><span style="color: #0000ff;">public</span><span style="color: #000000;"> </span><span style="color: #0000ff;">static</span><span style="color: #000000;"> </span><span style="color: #0000ff;">void</span><span style="color: #000000;"> main(String[] args) {<br />
System.out.println(<br />
Words.FIRST </span><span style="color: #000000;">+</span><span style="color: #000000;"> </span><span style="color: #000000;">"</span><span style="color: #000000;"> </span><span style="color: #000000;">"</span><span style="color: #000000;"> </span><span style="color: #000000;">+</span><span style="color: #000000;"> Words.SECOND </span><span style="color: #000000;">+</span><span style="color: #000000;"> </span><span style="color: #000000;">"</span><span style="color: #000000;"> </span><span style="color: #000000;">"</span><span style="color: #000000;"> </span><span style="color: #000000;">+</span><span style="color: #000000;"> Words.THIRD<br />
);<br />
}<br />
}<br />
<br />
</span><span style="color: #0000ff;">public</span><span style="color: #000000;"> </span><span style="color: #0000ff;">class</span><span style="color: #000000;"> Words{<br />
</span><span style="color: #0000ff;">public</span><span style="color: #000000;"> </span><span style="color: #0000ff;">static</span><span style="color: #000000;"> </span><span style="color: #0000ff;">final</span><span style="color: #000000;"> String FIRST </span><span style="color: #000000;">=</span><span style="color: #000000;"> </span><span style="color: #000000;">"</span><span style="color: #000000;">the</span><span style="color: #000000;">"</span><span style="color: #000000;">;<br />
</span><span style="color: #0000ff;">public</span><span style="color: #000000;"> </span><span style="color: #0000ff;">static</span><span style="color: #000000;"> </span><span style="color: #0000ff;">final</span><span style="color: #000000;"> String SECOND </span><span style="color: #000000;">=</span><span style="color: #000000;"> </span><span style="color: #0000ff;">null</span><span style="color: #000000;">;<br />
</span><span style="color: #0000ff;">public</span><span style="color: #000000;"> </span><span style="color: #0000ff;">static</span><span style="color: #000000;"> </span><span style="color: #0000ff;">final</span><span style="color: #000000;"> String THIRD </span><span style="color: #000000;">=</span><span style="color: #000000;"> </span><span style="color: #000000;">"</span><span style="color: #000000;">set</span><span style="color: #000000;">"</span><span style="color: #000000;">;<br />
}<br />
</span></div>
<br />
</pre>
</dd></dl> <br />
编译PrintWords.java文件。 <br />
修改Words.java文件为 <br />
<dl class="code"><dt>Java code</dt><dd>
<pre>
<div><!-- Code highlighting produced by Actipro CodeHighlighter (freeware)
http://www.CodeHighlighter.com/
--><span style="color: #000000;"><br />
</span><span style="color: #0000ff;">public</span><span style="color: #000000;"> </span><span style="color: #0000ff;">class</span><span style="color: #000000;"> Words{<br />
</span><span style="color: #0000ff;">public</span><span style="color: #000000;"> </span><span style="color: #0000ff;">static</span><span style="color: #000000;"> </span><span style="color: #0000ff;">final</span><span style="color: #000000;"> String FIRST </span><span style="color: #000000;">=</span><span style="color: #000000;"> </span><span style="color: #000000;">"</span><span style="color: #000000;">physics</span><span style="color: #000000;">"</span><span style="color: #000000;">;<br />
</span><span style="color: #0000ff;">public</span><span style="color: #000000;"> </span><span style="color: #0000ff;">static</span><span style="color: #000000;"> </span><span style="color: #0000ff;">final</span><span style="color: #000000;"> String SECOND </span><span style="color: #000000;">=</span><span style="color: #000000;"> </span><span style="color: #000000;">"</span><span style="color: #000000;">chemistry</span><span style="color: #000000;">"</span><span style="color: #000000;">;<br />
</span><span style="color: #0000ff;">public</span><span style="color: #000000;"> </span><span style="color: #0000ff;">static</span><span style="color: #000000;"> </span><span style="color: #0000ff;">final</span><span style="color: #000000;"> String THIRD </span><span style="color: #000000;">=</span><span style="color: #000000;"> </span><span style="color: #000000;">"</span><span style="color: #000000;">biology</span><span style="color: #000000;">"</span><span style="color: #000000;">;<br />
}<br />
</span></div>
<br />
</pre>
</dd></dl> <br />
问题：再次编译运行PrintWords.java，打印结果是什么？ <br />
<br />
需要了解常量折叠现象，理解什么是常量。
<img src ="http://www.blogjava.net/shiliqiang/aggbug/289907.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/shiliqiang/" target="_blank">石头@</a> 2009-08-05 10:32 <a href="http://www.blogjava.net/shiliqiang/articles/289907.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>java io系统概述</title><link>http://www.blogjava.net/shiliqiang/articles/289166.html</link><dc:creator>石头@</dc:creator><author>石头@</author><pubDate>Thu, 30 Jul 2009 11:05:00 GMT</pubDate><guid>http://www.blogjava.net/shiliqiang/articles/289166.html</guid><wfw:comment>http://www.blogjava.net/shiliqiang/comments/289166.html</wfw:comment><comments>http://www.blogjava.net/shiliqiang/articles/289166.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/shiliqiang/comments/commentRss/289166.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/shiliqiang/services/trackbacks/289166.html</trackback:ping><description><![CDATA[Java 流在处理上分为字符流和字节流。字符流处理的单元为 2 个字节的 Unicode 字符，分别操作字符、字符数组或字符串，而字节流处理单元为 1 个字节，操作字节和字节数组。
<p style="font-size: 10pt;">Java 内用 Unicode
编码存储字符，字符流处理类负责将外部的其他编码的字符流和 java 内 Unicode 字符流之间的转换。而类
InputStreamReader 和 OutputStreamWriter
处理字符流和字节流的转换。字符流（一次可以处理一个缓冲区）一次操作比字节流（一次一个字节）效率高。&nbsp;</p>
<p style="font-size: 10pt;">&nbsp;</p>
<p><span style="color: #ff6600;"><span style="font-size: medium;"><strong><span style="font-size: small;">( 一 )以字节为导向的 stream------InputStream/OutputStream </span></strong></span></span></p>
<p style="font-size: 10pt;">InputStream 和 OutputStream 是两个 abstact 类，对于字节为导向的 stream 都扩展这两个鸡肋（基类 ^_^ ） ; </p>
<p style="font-size: 10pt;"><span style="color: #0000ff;">1、 InputStream </span></p>
<p style="font-size: 10pt;">&nbsp;</p>
<p style="font-size: 10pt;"><img src="../../images/blogjava_net/sinojava/etc/42134.gif" alt="42134.gif" border="0" height="199" width="462" />&nbsp; </p>
<p style="font-size: 10pt;">1.1 </p>
<p style="font-size: 10pt;">ByteArrayInputStream -- 把内存中的一个缓冲区作为 InputStream 使用 . </p>
<p style="font-size: 10pt;">construct--- </p>
<p style="font-size: 10pt;">(A)ByteArrayInputStream(byte[]) 创建一个新字节数组输入流（ ByteArrayInputStream ），它从指定字节数组中读取数据（ 使用 byte 作为其缓冲区数组） </p>
<p style="font-size: 10pt;">(B)---ByteArrayInputStream(byte[], int, int) 创建一个新字节数组输入流，它从指定字节数组中读取数据。 </p>
<p style="font-size: 10pt;">---mark:: 该字节数组未被复制。 </p>
<p style="font-size: 10pt;">1.2 </p>
<p style="font-size: 10pt;">StringBufferInputStream -- 把一个 String 对象作为 InputStream . </p>
<p style="font-size: 10pt;">construct---&nbsp;&nbsp;</p>
<p style="font-size: 10pt;">StringBufferInputStream(String) 据指定串创建一个读取数据的输入流串。 </p>
<p style="font-size: 10pt;">&nbsp;</p>
<p style="font-size: 10pt;">注释：不推荐使用 StringBufferInputStream 方法。 此类不能将字符正确的转换为字节。 </p>
<p style="font-size: 10pt;">同 JDK 1.1 版中的类似，从一个串创建一个流的最佳方法是采用 StringReader 类。 </p>
<p style="font-size: 10pt;">1.3 </p>
<p style="font-size: 10pt;">FileInputStream -- 把一个文件作为 InputStream ，实现对文件的读取操作 </p>
<p style="font-size: 10pt;">construct--- </p>
<p style="font-size: 10pt;">(A)FileInputStream(File name) 创建一个输入文件流，从指定的 File 对象读取数据。 </p>
<p style="font-size: 10pt;">(B)FileInputStream(FileDescriptor) 创建一个输入文件流，从指定的文件描述器读取数据。 </p>
<p style="font-size: 10pt;">(C)-FileInputStream(String&nbsp; name) 创建一个输入文件流，从指定名称的文件读取数据。 </p>
<p style="font-size: 10pt;">method ---- read() 从当前输入流中读取一字节数据。 </p>
<p style="font-size: 10pt;">read(byte[]) 将当前输入流中 b.length 个字节数据读到一个字节数组中。 </p>
<p style="font-size: 10pt;">read(byte[], int, int) 将输入流中 len 个字节数据读入一个字节数组中。 </p>
<p style="font-size: 10pt;">1.4</p>
<p style="font-size: 10pt;">PipedInputStream ：实现了 pipe 的概念，主要在线程中使用 . 管道输入流是指一个通讯管道的接收端。 </p>
<p style="font-size: 10pt;">一个线程通过管道输出流发送数据，而另一个线程通过管道输入流读取数据，这样可实现两个线程间的通讯。 </p>
<p style="font-size: 10pt;">construct--- </p>
<p style="font-size: 10pt;">PipedInputStream() 创建一个管道输入流，它还未与一个管道输出流连接。 </p>
<p style="font-size: 10pt;">PipedInputStream(PipedOutputStream) 创建一个管道输入流 , 它已连接到一个管道输出流。 </p>
<p style="font-size: 10pt;">1.5 </p>
<p style="font-size: 10pt;">SequenceInputStream ：把多个 InputStream 合并为一个 InputStream . &#8220;序列输入流&#8221;类允许应用程序把几个输入流连续地合并起来， </p>
<p style="font-size: 10pt;">并且使它们像单个输入流一样出现。每个输入流依次被读取，直到到达该流的末尾。 </p>
<p style="font-size: 10pt;">然后&#8220;序列输入流&#8221;类关闭这个流并自动地切换到下一个输入流。 </p>
<p style="font-size: 10pt;">construct--- </p>
<p style="font-size: 10pt;">SequenceInputStream(Enumeration) 创建一个新的序列输入流，并用指定的输入流的枚举值初始化它。 </p>
<p style="font-size: 10pt;">SequenceInputStream(InputStream, InputStream) 创建一个新的序列输入流，初始化为首先 读输入流 s1, 然后读输入流 s2 。 </p>
<p style="font-size: 10pt;">&nbsp;</p>
<p style="font-size: 10pt;"><span style="color: #0000ff;">2、 OutputSteam </span></p>
<p style="font-size: 10pt;">&nbsp;</p>
<p style="font-size: 10pt;"><img src="../../images/blogjava_net/sinojava/etc/42135.gif" alt="42135.gif" border="0" height="142" width="458" /> <br />
2.1 </p>
<p style="font-size: 10pt;">ByteArrayOutputStream ： 把信息存入内存中的一个缓冲区中 . 该类实现一个以字节数组形式写入数据的输出流。 </p>
<p style="font-size: 10pt;">当数据写入缓冲区时，它自动扩大。用 toByteArray() 和 toString() 能检索数据。 </p>
<p style="font-size: 10pt;">constructor </p>
<p style="font-size: 10pt;">(A)--- ByteArrayOutputStream() 创建一个新的字节数组输出流。 </p>
<p style="font-size: 10pt;">(B)--- ByteArrayOutputStream() 创建一个新的字节数组输出流。 </p>
<p style="font-size: 10pt;">(C)--- ByteArrayOutputStream(int) 创建一个新的字节数组输出流，并带有指定大小字节的缓冲区容量。 </p>
<p style="font-size: 10pt;">toString(String) 根据指定字符编码将缓冲区内容转换为字符串，并将字节转换为字符。 </p>
<p style="font-size: 10pt;">write(byte[], int, int) 将指定字节数组中从偏移量 off 开始的 len 个字节写入该字节数组输出流。 </p>
<p style="font-size: 10pt;">write(int) 将指定字节写入该字节数组输出流。 </p>
<p style="font-size: 10pt;">writeTo(OutputStream) 用 out.write(buf, 0, count) 调用输出流的写方法将该字节数组输出流的全部内容写入指定的输出流参数。 </p>
<p style="font-size: 10pt;">2.2&nbsp;&nbsp;</p>
<p style="font-size: 10pt;">FileOutputStream: 文件输出流是向 File 或 FileDescriptor 输出数据的一个输出流。 </p>
<p style="font-size: 10pt;">constructor </p>
<p style="font-size: 10pt;">(A)FileOutputStream(File&nbsp; name) 创建一个文件输出流，向指定的 File 对象输出数据。 </p>
<p style="font-size: 10pt;">(B)FileOutputStream(FileDescriptor) 创建一个文件输出流，向指定的文件描述器输出数据。 </p>
<p style="font-size: 10pt;">(C)FileOutputStream(String&nbsp; name) 创建一个文件输出流，向指定名称的文件输出数据。 </p>
<p style="font-size: 10pt;">(D)FileOutputStream(String, boolean) 用指定系统的文件名，创建一个输出文件。 </p>
<p style="font-size: 10pt;">2.3 </p>
<p style="font-size: 10pt;">PipedOutputStream: 管道输出流是指一个通讯管道的发送端。 一个线程通过管道输出流发送数据， </p>
<p style="font-size: 10pt;">而另一个线程通过管道输入流读取数据，这样可实现两个线程间的通讯。 </p>
<p style="font-size: 10pt;">constructor </p>
<p style="font-size: 10pt;">(A)PipedOutputStream() 创建一个管道输出流，它还未与一个管道输入流连接。 </p>
<p style="font-size: 10pt;">(B)PipedOutputStream(PipedInputStream) 创建一个管道输出流，它已连接到一个管道输入流。 </p>
<p style="font-size: 10pt;">&nbsp;</p>
<p><span style="color: #ff6600;"><span style="font-size: medium;"><strong><span style="font-size: small;">( 二 )以字符为导向的 stream Reader/Writer </span></strong></span></span></p>
<p style="font-size: 10pt;">以 Unicode 字符为导向的 stream ，表示以 Unicode 字符为单位从 stream 中读取或往 stream 中写入信息。 </p>
<p style="font-size: 10pt;">Reader/Writer 为 abstact 类 </p>
<p style="font-size: 10pt;">以 Unicode 字符为导向的 stream 包括下面几种类型： </p>
<p style="font-size: 10pt;"><span style="color: #0000ff;">1. Reader </span></p>
<p style="font-size: 10pt;">&nbsp;</p>
<p style="font-size: 10pt;"><img src="../../images/blogjava_net/sinojava/etc/42136.gif" alt="42136.gif" border="0" height="169" width="462" />&nbsp; </p>
<p style="font-size: 10pt;">1.1 </p>
<p style="font-size: 10pt;">&nbsp; CharArrayReader ：与 ByteArrayInputStream 对应此类实现一个可用作字符输入流的字符缓冲区 </p>
<p style="font-size: 10pt;">constructor </p>
<p style="font-size: 10pt;">CharArrayReader(char[]) 用指定字符数组创建一个 CharArrayReader 。 </p>
<p style="font-size: 10pt;">CharArrayReader(char[], int, int) 用指定字符数组创建一个 CharArrayReader </p>
<p style="font-size: 10pt;">1.2 </p>
<p style="font-size: 10pt;">StringReader ： 与 StringBufferInputStream 对应其源为一个字符串的字符流。 </p>
<p style="font-size: 10pt;">StringReader(String) 创建一新的串读取者。</p>
<p style="font-size: 10pt;">1.3 </p>
<p style="font-size: 10pt;">FileReader ： 与 FileInputStream 对应 </p>
<p style="font-size: 10pt;">1.4 </p>
<p style="font-size: 10pt;">PipedReader ：与 PipedInputStream 对应 </p>
<p style="font-size: 10pt;">&nbsp;</p>
<p style="font-size: 10pt;"><span style="color: #0000ff;">2.&nbsp; Writer </span></p>
<p style="font-size: 10pt;">&nbsp;<img src="../../images/blogjava_net/sinojava/etc/42137.gif" alt="42137.gif" border="0" height="222" width="465" /> </p>
<p style="font-size: 10pt;">2.1&nbsp; &nbsp; CharArrayWrite ： 与 ByteArrayOutputStream 对应 </p>
<p style="font-size: 10pt;">2.2&nbsp;&nbsp; StringWrite ：无与之对应的以字节为导向的 stream </p>
<p style="font-size: 10pt;">2.3&nbsp; FileWrite ： 与 FileOutputStream 对应 </p>
<p style="font-size: 10pt;">2.4&nbsp; PipedWrite ：与 PipedOutputStream 对应 </p>
<p style="font-size: 10pt;">&nbsp;</p>
<p style="font-size: 10pt;"><span style="color: #0000ff;">3、两种不同导向的 stream 之间的转换&nbsp;&nbsp;</span></p>
<p style="font-size: 10pt;">3.1 </p>
<p style="font-size: 10pt;">InputStreamReader 和 OutputStreamReader ： </p>
<p style="font-size: 10pt;">把一个以字节为导向的 stream 转换成一个以字符为导向的 stream 。 </p>
<p style="font-size: 10pt;">InputStreamReader 类是从字节流到字符流的桥梁：它读入字节，并根据指定的编码方式，将之转换为字符流。 </p>
<p style="font-size: 10pt;">使用的编码方式可能由名称指定，或平台可接受的缺省编码方式。 </p>
<p style="font-size: 10pt;">InputStreamReader 的 read() 方法之一的每次调用，可能促使从基本字节输入流中读取一个或多个字节。 </p>
<p style="font-size: 10pt;">为了达到更高效率，考虑用 BufferedReader 封装 InputStreamReader ， </p>
<p style="font-size: 10pt;">BufferedReader in = new BufferedReader(new InputStreamReader(System.in)); </p>
<p style="font-size: 10pt;">例如： // 实现从键盘输入一个整数 </p>
<div class="dp-highlighter">
<div class="bar">
<div class="tools"><a onclick="function onclick()
{
dp.sh.Toolbar.Command('ViewSource',this);return false;
}" href="http://blog.csdn.net/ilibaba/archive/2009/03/04/3955799.aspx#"><span style="color: #006699;">view plain</span></a><a onclick="function onclick()
{
dp.sh.Toolbar.Command('CopyToClipboard',this);return false;
}" href="http://blog.csdn.net/ilibaba/archive/2009/03/04/3955799.aspx#"><span style="color: #006699;">copy to clipboard</span></a><a onclick="function onclick()
{
dp.sh.Toolbar.Command('PrintSource',this);return false;
}" href="http://blog.csdn.net/ilibaba/archive/2009/03/04/3955799.aspx#"><span style="color: #006699;">print</span></a><a onclick="function onclick()
{
dp.sh.Toolbar.Command('About',this);return false;
}" href="http://blog.csdn.net/ilibaba/archive/2009/03/04/3955799.aspx#"><span style="color: #006699;">?</span></a></div>
</div>
<ol class="dp-j">
    <li class="alt"><span><span>String&nbsp;s&nbsp;=&nbsp;</span><span class="keyword">null</span><span>;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;</span></span></li>
    <li><span>InputStreamReader&nbsp;re&nbsp;=&nbsp;</span><span class="keyword">new</span><span>&nbsp;InputStreamReader(System.in); &nbsp;&nbsp;</span></li>
    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;BufferedReader&nbsp;br&nbsp;=&nbsp;</span><span class="keyword">new</span><span>&nbsp;BufferedReader(re); &nbsp;&nbsp;</span></li>
    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword">try</span><span>&nbsp;{ &nbsp;&nbsp;</span></li>
    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;s&nbsp;=&nbsp;br.readLine(); &nbsp;&nbsp;</span></li>
    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;System.out.println(</span><span class="string">"s=&nbsp;"</span><span>&nbsp;+&nbsp;Integer.parseInt(s)); &nbsp;&nbsp;</span></li>
    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;br.close(); &nbsp;&nbsp;</span></li>
    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;} &nbsp;&nbsp;</span></li>
    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword">catch</span><span>&nbsp;(IOException&nbsp;e) &nbsp;&nbsp;</span></li>
    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{ &nbsp;&nbsp;</span></li>
    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;e.printStackTrace(); &nbsp;&nbsp;</span></li>
    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;} &nbsp;&nbsp;</span></li>
    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword">catch</span><span>&nbsp;(NumberFormatException&nbsp;e)</span><span class="comment">//&nbsp;当应用程序试图将字符串转换成一种数值类型，但该字符串不能转换为适当格式时，抛出该异常。 </span><span>&nbsp;&nbsp;</span></li>
    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{ &nbsp;&nbsp;</span></li>
    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;System.out.println(</span><span class="string">"&nbsp;输入的不是数字&nbsp;"</span><span>); &nbsp;&nbsp;</span></li>
    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;&nbsp;</span></li>
</ol>
</div>
<p><textarea cols="50" rows="15" name="code" class="java">String s = null;
InputStreamReader re = new InputStreamReader(System.in);
BufferedReader br = new BufferedReader(re);
try {
s = br.readLine();
System.out.println("s= " + Integer.parseInt(s));
br.close();
}
catch (IOException e)
{
e.printStackTrace();
}
catch (NumberFormatException e)// 当应用程序试图将字符串转换成一种数值类型，但该字符串不能转换为适当格式时，抛出该异常。
{
System.out.println(" 输入的不是数字 ");
}</textarea></p>
<p style="font-size: 10pt;">InputStreamReader(InputStream) 用缺省的字符编码方式，创建一个 InputStreamReader 。 </p>
<p style="font-size: 10pt;">InputStreamReader(InputStream, String) 用已命名的字符编码方式，创建一个 InputStreamReader 。 </p>
<p style="font-size: 10pt;">&nbsp;</p>
<p style="font-size: 10pt;">OutputStreamWriter 将多个字符写入到一个输出流，根据指定的字符编码将多个字符转换为字节。 </p>
<p style="font-size: 10pt;">每个 OutputStreamWriter 合并它自己的 CharToByteConverter, 因而是从字符流到字节流的桥梁。</p>
<p style="font-size: 10pt;">&nbsp;</p>
<p><span style="font-size: medium;"><strong><span style="color: #ff6600;"><span style="font-size: small;">（三）Java IO 的一般使用原则 ：&nbsp;&nbsp;</span></span></strong></span></p>
<p style="font-size: 10pt;">一、按数据来源（去向）分类： </p>
<p style="font-size: 10pt;">1 、是文件： FileInputStream, FileOutputStream, ( 字节流 )FileReader, FileWriter( 字符 ) </p>
<p style="font-size: 10pt;">2 、是 byte[] ： ByteArrayInputStream, ByteArrayOutputStream( 字节流 ) </p>
<p style="font-size: 10pt;">3 、是 Char[]: CharArrayReader, CharArrayWriter( 字符流 ) </p>
<p style="font-size: 10pt;">4 、是 String: StringBufferInputStream, StringBufferOuputStream ( 字节流 )StringReader, StringWriter( 字符流 ) </p>
<p style="font-size: 10pt;">5 、网络数据流： InputStream, OutputStream,( 字节流 ) Reader, Writer( 字符流 ) </p>
<p style="font-size: 10pt;">二、按是否格式化输出分： </p>
<p style="font-size: 10pt;">1 、要格式化输出： PrintStream, PrintWriter </p>
<p style="font-size: 10pt;">三、按是否要缓冲分： </p>
<p style="font-size: 10pt;">1 、要缓冲： BufferedInputStream, BufferedOutputStream,( 字节流 ) BufferedReader, BufferedWriter( 字符流 ) </p>
<p style="font-size: 10pt;">四、按数据格式分： </p>
<p style="font-size: 10pt;">1 、二进制格式（只要不能确定是纯文本的） : InputStream, OutputStream 及其所有带 Stream 结束的子类 </p>
<p style="font-size: 10pt;">2 、纯文本格式（含纯英文与汉字或其他编码方式）； Reader, Writer 及其所有带 Reader, Writer 的子类 </p>
<p style="font-size: 10pt;">五、按输入输出分： </p>
<p style="font-size: 10pt;">1 、输入： Reader, InputStream 类型的子类 </p>
<p style="font-size: 10pt;">2 、输出： Writer, OutputStream 类型的子类 </p>
<p style="font-size: 10pt;">六、特殊需要： </p>
<p style="font-size: 10pt;">1 、从 Stream 到 Reader,Writer 的转换类： InputStreamReader, OutputStreamWriter </p>
<p style="font-size: 10pt;">2 、对象输入输出： ObjectInputStream, ObjectOutputStream </p>
<p style="font-size: 10pt;">3 、进程间通信： PipeInputStream, PipeOutputStream, PipeReader, PipeWriter </p>
<p style="font-size: 10pt;">4 、合并输入： SequenceInputStream </p>
<p style="font-size: 10pt;">5 、更特殊的需要： PushbackInputStream, PushbackReader, LineNumberInputStream, LineNumberReader</p>
<p style="font-size: 10pt;">决定使用哪个类以及它的构造进程的一般准则如下（不考虑特殊需要）： </p>
<p style="font-size: 10pt;">首先，考虑最原始的数据格式是什么： 原则四 </p>
<p style="font-size: 10pt;">第二，是输入还是输出：原则五 </p>
<p style="font-size: 10pt;">第三，是否需要转换流：原则六第 1 点 </p>
<p style="font-size: 10pt;">第四，数据来源（去向）是什么：原则一 </p>
<p style="font-size: 10pt;">第五，是否要缓冲：原则三 （特别注明：一定要注意的是 readLine() 是否有定义，有什么比 read, write 更特殊的输入或输出方法） </p>
<p style="font-size: 10pt;">第六，是否要格式化输出：原则二 <br />
</p>
<p style="font-size: 10pt;"><br />
</p>
<p style="font-size: 10pt;">出处：http://blog.csdn.net/sqw131/archive/2009/03/25/4025047.aspx<br />
</p>
<img src ="http://www.blogjava.net/shiliqiang/aggbug/289166.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/shiliqiang/" target="_blank">石头@</a> 2009-07-30 19:05 <a href="http://www.blogjava.net/shiliqiang/articles/289166.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>java IO中的Decorator模式</title><link>http://www.blogjava.net/shiliqiang/articles/288779.html</link><dc:creator>石头@</dc:creator><author>石头@</author><pubDate>Tue, 28 Jul 2009 09:54:00 GMT</pubDate><guid>http://www.blogjava.net/shiliqiang/articles/288779.html</guid><wfw:comment>http://www.blogjava.net/shiliqiang/comments/288779.html</wfw:comment><comments>http://www.blogjava.net/shiliqiang/articles/288779.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/shiliqiang/comments/commentRss/288779.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/shiliqiang/services/trackbacks/288779.html</trackback:ping><description><![CDATA[<p>http://hi.baidu.com/xiaowei81/blog/item/28f9ff541ed7f51c3a2935d7.html</p>
<img src ="http://www.blogjava.net/shiliqiang/aggbug/288779.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/shiliqiang/" target="_blank">石头@</a> 2009-07-28 17:54 <a href="http://www.blogjava.net/shiliqiang/articles/288779.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>java中的io操作（三：流的使用）</title><link>http://www.blogjava.net/shiliqiang/articles/288380.html</link><dc:creator>石头@</dc:creator><author>石头@</author><pubDate>Sat, 25 Jul 2009 12:24:00 GMT</pubDate><guid>http://www.blogjava.net/shiliqiang/articles/288380.html</guid><wfw:comment>http://www.blogjava.net/shiliqiang/comments/288380.html</wfw:comment><comments>http://www.blogjava.net/shiliqiang/articles/288380.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/shiliqiang/comments/commentRss/288380.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/shiliqiang/services/trackbacks/288380.html</trackback:ping><description><![CDATA[&nbsp;<span style="color: rgb(255, 0, 0);"><span style="color: rgb(255, 32, 74);">1:</span></span>&nbsp; 分割符输出<br />
&nbsp;&nbsp;&nbsp;&nbsp; 可以确定自己的分割符，&#8220;|&#8221;，&#8220;：&#8221;，等等，<br />
&nbsp;&nbsp;&nbsp;&nbsp; 如果在要保留的字符串中存在一个分割符，<br />
<span style="color: rgb(255, 32, 74);"><span style="color: red;">&nbsp;2：</span></span>字符串记号处理器<br />
&nbsp;&nbsp;&nbsp; 当读取一行输入时，会得到很长的一个字符串，有时需要将它分割为独立的字符串，这时就需要找出其 中的分割符且将它分离成单独的片段， java.util中的StringToKenizer类就是为这个目的设计的，它提供了一个很简单的方法来分隔带分隔符文本的字符串，思路是：将字符串记号处理器对象附在字符串上，当构造字符串记号处理器对象时，要指定哪些字符是分割符， eg:<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; StringTokenizer tokenizer = new StringTokenizer(line,"|");<br />
也可在在字符串中指定多个分割符， eg:<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; StringTokenizer tokenizer = new StringTokenizer(line,"|,:");<br />
这就意味着字符串中的任意字符都能做为分割符出现。如果不指定分割符，则默认为"\t\n\r"<br />
<br />
一旦构造好了一个字符串记号处理器，就可以使用它的方法从字符串中快速找出记号，nextToken方法可以返回下一个未读记号，如果从在多个可用的记号，hasMoreTokens 方法会返回true， 可以用下面的循环处理所有的记号：<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; While(tokenizer.hasMoreTokens())<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; String token = tokenizer.nextToken();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //....process token<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />
<br />
<img src ="http://www.blogjava.net/shiliqiang/aggbug/288380.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/shiliqiang/" target="_blank">石头@</a> 2009-07-25 20:24 <a href="http://www.blogjava.net/shiliqiang/articles/288380.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>java中的io 操作(—：类的框架)</title><link>http://www.blogjava.net/shiliqiang/articles/288378.html</link><dc:creator>石头@</dc:creator><author>石头@</author><pubDate>Sat, 25 Jul 2009 12:04:00 GMT</pubDate><guid>http://www.blogjava.net/shiliqiang/articles/288378.html</guid><wfw:comment>http://www.blogjava.net/shiliqiang/comments/288378.html</wfw:comment><comments>http://www.blogjava.net/shiliqiang/articles/288378.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/shiliqiang/comments/commentRss/288378.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/shiliqiang/services/trackbacks/288378.html</trackback:ping><description><![CDATA[最经学习了java io包中的类，对io 操作熟悉了点，写点东西整理一下自己的思路。<br />
<br />
首先回顾一下这些类的框架；<br />
<br />
<img alt="" src="http://www.blogjava.net/images/blogjava_net/shiliqiang/kindsOfStream.jpg" height="800" width="600" /><br />
<img src ="http://www.blogjava.net/shiliqiang/aggbug/288378.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/shiliqiang/" target="_blank">石头@</a> 2009-07-25 20:04 <a href="http://www.blogjava.net/shiliqiang/articles/288378.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>java中的transient关键字</title><link>http://www.blogjava.net/shiliqiang/articles/288077.html</link><dc:creator>石头@</dc:creator><author>石头@</author><pubDate>Thu, 23 Jul 2009 11:25:00 GMT</pubDate><guid>http://www.blogjava.net/shiliqiang/articles/288077.html</guid><wfw:comment>http://www.blogjava.net/shiliqiang/comments/288077.html</wfw:comment><comments>http://www.blogjava.net/shiliqiang/articles/288077.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/shiliqiang/comments/commentRss/288077.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/shiliqiang/services/trackbacks/288077.html</trackback:ping><description><![CDATA[在对象传输的情况下,有些域是"瞬时的",也就是说只与当前进程环境相关,出了这个进程就没有意义了,这样的域是"本地化的",没有序列化的价值,标为瞬时态的.<br />
<br />
<br />
<div class="blog_content">
<p>Java
的serialization提供了一种持久化对象实例的机制。当持久化对象时，可能有一个特殊的对象数据成员，我们不想用serialization机
制来保存它。为了在一个特定对象的一个域上关闭serialization，可以在这个域前加上关键字transient。</p>
<div class="postText">
<p>transient是Java语言的关键字，用来表示一个域不是该对象串行化的一部分。当一个对象被串行化的时候，transient型变量的值不包括在串行化的表示中，然而非transient型的变量是被包括进去的。</p>
<p>首先，让我们看一些Java serialization的代码：<br />
public class LoggingInfo implements java.io.Serializable <br />
{ <br />
&nbsp;&nbsp;&nbsp;  private Date loggingDate = new Date(); <br />
&nbsp;&nbsp;&nbsp;  private String uid; <br />
&nbsp;&nbsp;&nbsp;  private transient String pwd; <br />
&nbsp;&nbsp;&nbsp;  <br />
&nbsp;&nbsp;&nbsp;  LoggingInfo(String user, String password) <br />
&nbsp;&nbsp;&nbsp;  { <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  uid = user; <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  pwd = password; <br />
&nbsp;&nbsp;&nbsp;  } <br />
&nbsp;&nbsp;&nbsp;  public String toString() <br />
&nbsp;&nbsp;&nbsp;  { <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  String password=null; <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  if(pwd == null) <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  { <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  password = "NOT SET"; <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  } <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  else <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  { <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  password = pwd; <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  } <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  return "logon info: \n&nbsp;&nbsp;  " + "user: " + uid + <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  "\n&nbsp;&nbsp;  logging date : " + loggingDate.toString() + <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  "\n&nbsp;&nbsp;  password: " + password; <br />
&nbsp;&nbsp;&nbsp;  } <br />
}</p>
<p>现在我们创建一个这个类的实例，并且串行化(serialize)它 ,然后将这个串行化对象写入磁盘。<br />
<br />
LoggingInfo logInfo = new LoggingInfo("MIKE", "MECHANICS"); <br />
System.out.println(logInfo.toString()); <br />
try <br />
{ <br />
&nbsp;&nbsp;&nbsp;  ObjectOutputStream o = new ObjectOutputStream(new FileOutputStream("logInfo.out"));<br />
&nbsp;&nbsp;&nbsp;  o.writeObject(logInfo); <br />
&nbsp;&nbsp;&nbsp;  o.close(); <br />
} <br />
catch(Exception e) {//deal with exception}</p>
<p>To read the object back, we can write <br />
<br />
try <br />
{ <br />
&nbsp;&nbsp;&nbsp;  ObjectInputStream in =new ObjectInputStream(new FileInputStream("logInfo.out")); <br />
&nbsp;&nbsp;&nbsp;  LoggingInfo logInfo = (LoggingInfo)in.readObject(); <br />
&nbsp;&nbsp;&nbsp;  System.out.println(logInfo.toString()); <br />
} <br />
catch(Exception e) {//deal with exception}</p>
<p>如果我们运行这段代码，我们会注意到从磁盘中读回(read——back (de-serializing))的对象打印password为"NOT SET"。这是当我们定义pwd域为transient时，所期望的正确结果。<br />
现在，让我们来看一下粗心对待transient域可能引起的潜在问题。假设我们修改了类定义，提供给transient域一个默认值，<br />
代码如下：<br />
<br />
public class GuestLoggingInfo implements java.io.Serializable <br />
{ <br />
&nbsp;&nbsp;&nbsp;  private Date loggingDate = new Date(); <br />
&nbsp;&nbsp;&nbsp;  private String uid; <br />
&nbsp;&nbsp;&nbsp;  private transient String pwd; <br />
&nbsp;&nbsp;&nbsp;  <br />
&nbsp;&nbsp;&nbsp;  GuestLoggingInfo() <br />
&nbsp;&nbsp;&nbsp;  { <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  uid = "guest"; <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  pwd = "guest"; <br />
&nbsp;&nbsp;&nbsp;  } <br />
&nbsp;&nbsp;&nbsp;  public String toString() <br />
&nbsp;&nbsp;&nbsp;  { <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  //same as above <br />
&nbsp;&nbsp;&nbsp;&nbsp;  } <br />
} <br />
现
在，如果我们串行化GuestLoggingInfo的一个实例，将它写入磁盘，并且再将它从磁盘中读出，我们仍然看到读回的对象打印password
为 "NOT
SET"。当从磁盘中读出某个类的实例时，实际上并不会执行这个类的构造函数，而是载入了一个该类对象的持久化状态，并将这个状态赋值给该类的另一个对
象。</p>
<p><br />
</p>
<p><br />
</p>
<p><br />
</p>
<p><br />
</p>
<p>摘自：<a href="http://www.devx.com/tips/Tip/13726">http://www.devx.com/tips/Tip/13726</a>
</p>
</div>
</div>
<br />
<img src ="http://www.blogjava.net/shiliqiang/aggbug/288077.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/shiliqiang/" target="_blank">石头@</a> 2009-07-23 19:25 <a href="http://www.blogjava.net/shiliqiang/articles/288077.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>java中的native关键字</title><link>http://www.blogjava.net/shiliqiang/articles/287920.html</link><dc:creator>石头@</dc:creator><author>石头@</author><pubDate>Wed, 22 Jul 2009 14:11:00 GMT</pubDate><guid>http://www.blogjava.net/shiliqiang/articles/287920.html</guid><wfw:comment>http://www.blogjava.net/shiliqiang/comments/287920.html</wfw:comment><comments>http://www.blogjava.net/shiliqiang/articles/287920.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/shiliqiang/comments/commentRss/287920.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/shiliqiang/services/trackbacks/287920.html</trackback:ping><description><![CDATA[<font face="Verdana">JNI是</font><a href="http://www.itisedu.com/phrase/200604261219045.html" target="_new"><font face="Verdana">J</font></a><font face="Verdana">ava
Native Interface的 缩写。从Java 1.1开始，Java Native Interface
(JNI)标准成为java平台的一部分，它允许Java代码和其他语言写的代码进行交互。JNI一开始是为了本地已编译语言，尤其是C和C++而设计
的，但是它并不妨碍你使用其他语言，只要调用约定受支持就可以了。 </font>
<p><font face="Verdana">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 使用java与本地已编译的代码交互，通常会丧失平台可移植性。但是，有些情况下这样做是可以接受的，甚至是必须的，比如，使用一些旧的库，与硬件、操作系统进行交互，或者为了提高<span style="text-decoration: underline;">程序</span>的性能。JNI标准至少保证本地代码能工作在任何Java 虚拟机实现下。</font></p>
<p><font face="Verdana"><strong>JNI（Java Native Interface）的书写步骤</strong></font> </p>
<p><font face="Verdana">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &#183;编写带有native声明的方法的java类<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &#183;使用javac命令编译所编写的java类<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &#183;使用javah ?jni java类名生成扩展名为h的头文件<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &#183;使用C/C++（或者其他编程想语言）实现本地方法<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &#183;将C/C++编写的文件生成动态连接库<br />
<br />
1) 编写java程序：<br />
这里以HelloWorld为例。<br />
代码1：<br />
class HelloWorld {<br />
public native void displayHelloWorld();</font></p>
<p><font face="Verdana">static {<br />
System.loadLibrary("hello");<br />
}</font></p>
<p><font face="Verdana">public static void main(String[] args) {<br />
new HelloWorld().displayHelloWorld();<br />
}<br />
}<br />
声明native方法：如果你想将一个方法做为一个本地方法的话，那么你就必须声明改方法为native的，并且不能实现。其中方法的参数和返回值在后面讲述。<br />
Load
动态库：System.loadLibrary("hello");加载动态库（我们可以这样理解：我们的方法displayHelloWorld()没
有实现，但是我们在下面就直接使用了，所以必须在使用之前对它进行初始化）这里一般是以static块进行加载的。同时需要注意的是
System.loadLibrary();的参数&#8220;hello&#8221;是动态库的名字。<br />
main()方法<br />
2) 编译没有什么好说的了<br />
javac HelloWorld.java<br />
3) 生成扩展名为h的头文件<br />
javah ?jni HelloWorld<br />
头文件的内容：<br />
/* DO NOT EDIT THIS FILE - it is machine generated */<br />
#include <br />
/* Header for class HelloWorld */</font></p>
<p><font face="Verdana">#ifndef _Included_HelloWorld<br />
#define _Included_HelloWorld<br />
#ifdef __cplusplus<br />
extern "C" {<br />
#endif<br />
/*<br />
* Class: HelloWorld<br />
* Method: displayHelloWorld<br />
* Signature: ()V<br />
*/<br />
JNIEXPORT void JNICALL Java_HelloWorld_displayHelloWorld<br />
(JNIEnv *, jobject);</font></p>
<p><font face="Verdana">#ifdef __cplusplus<br />
}<br />
#endif<br />
#endif<br />
（这
里我们可以这样理解：这个h文件相当于我们在java里面的接口，这里声明了一个Java_HelloWorld_displayHelloWorld
(JNIEnv *,
jobject);方法，然后在我们的本地方法里面实现这个方法，也就是说我们在编写C/C++程序的时候所使用的方法名必须和这里的一致）。<br />
4) 编写本地方法<br />
实现和由javah命令生成的头文件里面声明的方法名相同的方法。<br />
代码2：<br />
1 #include <br />
2 #include "HelloWorld.h"<br />
3 #include </font></p>
<p><font face="Verdana">4 JNIEXPORT void JNICALL Java_HelloWorld_displayHelloWorld(JNIEnv *env, jobject obj) <br />
{<br />
printf("Hello world!\n");<br />
return;<br />
}<br />
注
意代码2中的第1行，需要将jni.h（该文件可以在%JAVA_HOME%/include文件夹下面找到）文件引入，因为在程序中的JNIEnv、
jobject等类型都是在该头文件中定义的；另外在第2行需要将HelloWorld.h头文件引入（我是这么理解的：相当于我们在编写java程序的
时候，实现一个接口的话需要声明才可以，这里就是将HelloWorld.h头文件里面声明的方法加以实现。当然不一定是这样）。然后保存为
HelloWorldImpl.c就 ok了。<br />
5) 生成动态库<br />
这里以在Windows中为例，需要生成dll文件。在保存HelloWorldImpl.c文件夹下面，使用VC的编译器cl成。<br />
cl -I%java_home%\include -I%java_home%\include\win32 -LD HelloWorldImp.c -Fehello.dll<br />
注
意：生成的dll文件名在选项-Fe后面配置，这里是hello，因为在HelloWorld.java文件中我们loadLibary的时候使用的名字
是hello。当然这里修改之后那里也需要修改。另外需要将-I%java_home%\include
-I%java_home%\include\win32参数加上，因为在第四步里面编写本地方法的时候引入了jni.h文件。<br />
6) 运行程序<br />
java HelloWorld就ok。</font></p>
<p><font face="Verdana"><strong>JNI（Java Native Interface）调用中考虑的问题</strong></font> </p>
<p><font face="Verdana">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 在首次使用JNI的时候有些疑问，后来在使用中一一解决，下面就是这些问题的备忘：</font></p>
<p><font face="Verdana">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 1。 java和c是如何互通的？<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 其实不能互通的原因主要是数据类型的问题，jni解决了这个问题，例如那个c文件中的jstring数据类型就是java传入的String<a href="http://www.itisedu.com/phrase/200603090845215.html" target="_new">对象</a>，经过jni函数的转化就能成为c的char*。<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 对应数据类型关系如下表：<br />
&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; Java 类型 本地c类型 说明<br />
&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; boolean jboolean 无符号，8 位<br />
&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; byte jbyte 无符号，8 位<br />
&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; char jchar 无符号，16 位<br />
&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; short jshort 有符号，16 位<br />
&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; int jint 有符号，32 位<br />
&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; long jlong 有符号，64 位<br />
&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; float jfloat 32 位<br />
&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; double jdouble 64 位<br />
&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; void void N/A</font></p>
<p><font face="Verdana">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 2. 如何将java传入的String参数转换为c的char*，然后使用?<br />
java
传入的String参数，在c文件中被jni转换为jstring的数据类型，在c文件中声明char* test，然后test =
(char*)(*env)-&gt;GetStringUTFChars(env, jstring,
NULL);注意：test使用完后，通知虚拟机平台相关代码无需再访问：(*env)-&gt;ReleaseStringUTFChars(env,
jstring, test);</font></p>
<p><font face="Verdana">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 3. 将c中获取的一个char*的buffer传递给java？<br />
这个char*如果是一般的字符串的话，作为string传回去就可以了。如果是含有&#8217;\0&#8217;的buffer，最好作为bytearray传出，因为可以制定copy的length，如果copy到string，可能到&#8217;\0&#8217;就截断了。<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 有两种方式传递得到的数据：<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
一种是在jni中直接new一个byte数组，然后调用函数(*env)-&gt;SetByteArrayRegion(env,
bytearray, 0, len, buffer);将buffer的值copy到bytearray中，函数直接return
bytearray就可以了。<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 一种是return错误号，数据作为参数传出，但是java的基本数据类型是传值，对象是传递的引用，所以将这个需要传出的byte数组用某个类包一下，如下：<br />
class RetObj<br />
{<br />
public byte[] bytearray;<br />
}<br />
这个对象作为函数的参数retobj传出，通过如下函数将retobj中的byte数组赋值便于传出。代码如下：<br />
jclass cls;<br />
jfieldID fid;<br />
jbyteArray bytearray;<br />
bytearray = (*env)-&gt;NewByteArray(env,len);<br />
(*env)-&gt;SetByteArrayRegion(env, bytearray, 0, len, buffer);<br />
cls = (*env)-&gt;GetObjectClass(env, retobj);<br />
fid = (*env)-&gt;GetFieldID(env, cls, "retbytes", "[B"]);<br />
(*env)-&gt;SetObjectField(env, retobj, fid, bytearray);</font></p>
<p><font face="Verdana">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 4. 不知道占用多少空间的buffer，如何传递出去呢？<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 在jni的c文件中new出空间，传递出去。java的数据不初始化，指向传递出去的空间即可。</font></p>
<p><br />
</p>
<p><font face="Verdana">转自：http://blog.csdn.net/lion_6/archive/2008/03/17/2190442.aspx<br />
</font></p>
<img src ="http://www.blogjava.net/shiliqiang/aggbug/287920.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/shiliqiang/" target="_blank">石头@</a> 2009-07-22 22:11 <a href="http://www.blogjava.net/shiliqiang/articles/287920.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>浮点数比较</title><link>http://www.blogjava.net/shiliqiang/articles/285592.html</link><dc:creator>石头@</dc:creator><author>石头@</author><pubDate>Sun, 05 Jul 2009 13:52:00 GMT</pubDate><guid>http://www.blogjava.net/shiliqiang/articles/285592.html</guid><wfw:comment>http://www.blogjava.net/shiliqiang/comments/285592.html</wfw:comment><comments>http://www.blogjava.net/shiliqiang/articles/285592.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/shiliqiang/comments/commentRss/285592.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/shiliqiang/services/trackbacks/285592.html</trackback:ping><description><![CDATA[<div class="cnt" id="blog_text"><font size="3">在数学运算当中经常会涉及到判断两个数是否相等的情况<br />
对于整数很好处理 A==B这样的一个语句就可以解决全部的问题<br />
但是对于浮点数是不同的<br />
<br />
首先，浮点数在计算机当中的二进制表达方式就决定了大多数浮点数都是无法精确的表达的<br />
现在的计算机大部分都是数字计算机，不是模拟机，数字机的离散化的数据表示方法自然无法精确表达大部分的数据量的。<br />
<br />
其次计算机浮点数的精度在单精度float类型下，只有7位，在进行浮点运算的时候，这个精度往往会导致运算的结果和实际期望的结果之间有误差<br />
<br />
因为前两个原因，我们很难用 A==B来判定两个浮点数是否相同<br />
<br />
很自然，我们可以想到 fabs(A-B) &lt; epsilon 这样的一种判别方法<br />
但是这种判别方法稳妥吗？<br />
它也不稳妥。<br />
<br />
首先， epsilon是一个绝对的数据，也就是误差分析当中说说的绝对误差<br />
使用一个固定的数值，对于float类型可以表达的整个数域来说是不可以的<br />
比如epsilon取值为0.0001，而a和b的数值大小也是0.0001附近的，那么显然不合适<br />
另外对于a和b大小是10000这样的数据的时候，它也不合适，因为10000和10001也可以认为是相等的呢<br />
适合它的情况只是a或者b在1或者0附近的时候<br />
<br />
既然绝对误差不可以，那么自然的我们就会想到了相对误差<br />
bool IsEqual(float a, float b, float relError ) {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return ( fabs ( (a-b)/a ) &lt; relError ) ? true : false; <br />
}<br />
这样写还不完善，因为是拿固定的第一个参数做比较的，那么在调用<br />
IsEqual(a, b, relError ) 和 IsEqual(b, a, relError ) 的时候，可能得到不同的结果<br />
同时如果第一个参数是0的话，就有可能是除0溢出<br />
这个可以改造<br />
把除数选取为a和b当中绝对数值较大的即可 <br />
bool IsEqual(float a, float b, relError )<br />
{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (fabs(a)&lt;fabs(b)) return ( fabs((a-b)/a) &gt; relError ) ? true : false;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return (fabs( (a-b)/b) &gt; relError ) ? true : false;<br />
};<br />
<br />
使用相对误差就很完善吗？ <br />
也不是， 在某些特殊情况下， 相对误差也不能代表全部<br />
比如在判断空间三点是否共线的时候，使用判断点到另外两个点形成的线段的距离的方法的时候<br />
只用相对误差是不够的，应为线段距离可能很段，也可能很长，点到线段的距离，以及线段的长度做综合比较的时候，需要相对误差和绝对误差结合的方式才可以<br />
相对完整的比较算法应该如下：<br />
bool IsEqual(float a, float b, float absError, float relError )<br />
{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (a==b) return true;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (fabs(a-b)&lt;absError ) return true;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (fabs(a&gt;b) return (fabs((a-b)/a&gt;relError ) ? true : false;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return (fabs((a-b)/b&gt;relError ) ? true : false;<br />
}<br />
这样才相对完整<br />
<br />
这仅仅是浮点数之间最初级的比较方法<br />
高级的方法看 浮点数比较（二） 这个文章 —— 如何把两个浮点数之间的比较转化成为两个整数之间的比较。<br />
-------------------------------------<br />
</font>
<h1 class="title_txt"><font size="3">浮点数的内存结构</font></h1>
<font size="3"><br />
</font>
<h1 class="title_txt"></h1>
<div class="blogstory"><font size="3">根据IEEE的标准，浮点数的定义如下<br />
</font>
<table cellpadding="4" border="2">
    <tbody>
        <tr>
            <th><font size="3"><br />
            </font></th>
            <th><font size="3">符号位</font></th>
            <th><font size="3">指数位</font></th>
            <th><font size="3">小数部分</font></th>
            <th><font size="3">指数偏移量</font></th>
        </tr>
        <tr>
            <th><font size="3">单精度浮点数</font></th>
            <td align="center"><font size="3">1 位[31]</font></td>
            <td align="center"><font size="3">8位 [30-23]</font></td>
            <td align="center"><font size="3">23位 [22-00]</font></td>
            <td align="center"><font size="3">127</font></td>
        </tr>
        <tr>
            <th><font size="3">双精度浮点数</font></th>
            <td align="center"><font size="3">1 位[63]</font></td>
            <td align="center"><font size="3">11 位[62-52]</font></td>
            <td align="center"><font size="3">52 位[51-00]</font></td>
            <td align="center"><font size="3">1023</font></td>
        </tr>
    </tbody>
</table>
<p><font size="3">我们以单精度浮点数来说明：<br />
符号位，表述浮点数的正或者负<br />
指数实际也有正负的，但是没有单独的符号位，而是采用了一个偏移来表示<br />
在计算机的世界里，进位都是二进制的，指数表示的也是2的N次幂<br />
这个数据格式当中的，指数是8位，可表达的范围是0到255<br />
而对应的实际的指数是－127到＋128<br />
这里特殊说明，－127和＋128这两个数据在IEEE当中是保留的用作多种用途的<br />
－127表示的数字是0<br />
128和其他位数组合表示多种意义，最典型的就是NAN状态<br />
<br />
小数部分，并不是一个浮点数的实际的小数<br />
实际的小数在这个小数前面还保留了一个1<br />
拿浮点数1.0来说<br />
符号位是0， 实际指数是0，对应这里的指数就是127了，也就是0x7f<br />
而小数部分就是1.0了， 1是暗含的不存储，实际的小数部分就是0了<br />
因此组合起来的数据就是，0x3f80000<br />
<br />
可以用一个类来表示：<br />
class FloatType <br />
{<br />
public:<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; union {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; DWORD m_dwInt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; float&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; m_fFloat;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; struct {&nbsp;&nbsp;</font></p>
<p><font size="3">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; int&nbsp;&nbsp;&nbsp; m_nFra: 23;<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; int&nbsp;&nbsp;&nbsp; m_nExp : 8;</font></p>
<p><font size="3">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; bool m_bSign : 1;<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; };<br />
};</font></p>
<p class="right articalinfo"><font size="3">-----------------------------</font></p>
<h1 class="title_txt"></h1>
<div class="blogstory">参考IEEE的浮点数格式说明<br />
对于0到1范围内的浮点数是可以压缩的<br />
<br />
显然在0到1的范围内，一个单精度的浮点数，指数和符号位占据9个bit<br />
而这9个bit是可以不用的，把它去除，只保留小数部分的23bit就可以达到压缩的目的<br />
可以把一个浮点数从32bit，4字节压缩到23bit，3字节的范围内<br />
<br />
这也是在3dmax等一些工具软件当中对浮点数进行压缩存储的方法。<br />
比如，在单位化的法向量当中，每个浮点数都是0，1范围之间的数据<br />
正常情况下表示三维空间当中的单位化法向量就需要12个字节<br />
而经过这个压缩处理，只需要9个字节</div>
<p class="right articalinfo"><font size="3">-----------------------------<br />
</font></p>
</div>
<font size="3"><br />
</font>
<h1 class="title_txt"></h1>
<div class="blogstory"><font size="3">在写了上篇 浮点数的比较 以及 浮点数内存结构 两篇文章后<br />
对于浮点数的比较有新的想法<br />
<br />
我们先看正数的情况<br />
根据IEEE的内存结构， 指数在高位，尾数在低位<br />
浮点数大的对应的把其内存结构按照整数来理解进行比较的时候，情况也是成立的<br />
因此在这里如果把他们进行比较的话，作为整数运算效率会非常的高，比如<br />
float f1 = 1.23; <br />
float f2 = 1.24<br />
f1 &gt; f2 成立<br />
(int&amp;)f1 &gt; (int&amp;)f2 也是成立的<br />
<br />
而且，仔细研究IEEE的浮点结构，可以发现在《浮点数比较》当中提到的浮点数精度的问题——不是所有的浮点数都可以精确的表达<br />
可以精确表达的浮点数实际上是有限的，就是那些IEEE的各种情况的枚举了 2^32个。不能表达的占据了大多数<br />
<br />
IEEE在32位的情况下，尾数是23位的（暗含了第一个位数是1）<br />
对于可以精确表达的浮点数来说，如果我们把这23位当作整数来理解， 它加1，就意味着可以找到比当前对应浮点数大的最小的浮点数了<br />
反之，我们把两个浮点数，对应的整数做差值运算，得到的整数表明的是两个浮点数之间有多少个实际可以表达的浮点数（对应的指数相同的情况下很好理解；指数不同的时候，也是同样有效的）<br />
<br />
这样，对于两个正的浮点数，他们的大小比较就可以用 (int&amp;)f1 - (int&amp;)f2 来进行比较了<br />
差值的结果实际上就应该是相对误差了<br />
这个相对误差，不等同于普遍意义上的相对误差<br />
它所表达的是，两个浮点数之间可能还有多少个可以精确表达的浮点数<br />
这样通过指定这个阈值来控制两个浮点数的比较就更有效了<br />
对于两个正的浮点数<br />
bool IsEqual(float f1, float f2, int absDelta)<br />
{<br />
&nbsp;&nbsp;&nbsp;&nbsp; if ( abs ( (int&amp;)f1 - (int&amp;)f2 ) &lt; absDelta ) return true;<br />
}<br />
这里用abs而不是fabs这在asm上面的运算差距也是很大的了<br />
<br />
对于两个负数进行比较的情况也是相同的<br />
只不过负数内存对应的整数加1，相应的找到的是更小的负数而已<br />
<br />
但是负数和整数之间现在还不能进行直接的比较，因为根据IEEE的内存结构<br />
正数和负数是不同的，对应的整数不能连续<br />
正的最小的数就是0了，对应的整数也是0x00000000<br />
负的最小的数就是－0，对应的整数则是0x 80000000<br />
不用奇怪－0<br />
在IEEE的表达当中是有两个0的，一个是 +0 一个是-0<br />
<br />
有趣的是，按照 f1 == f2 的判断 +0和-0是相等的<br />
<br />
通过对比我们可以发现， <br />
+0 和正的浮点数可以按照转换成为整数的方式直接进行比较<br />
-0 和负的浮点数可以按照转换成为整数的方式直接进行比较<br />
<br />
如果我们能够把他们连接起来，整个整数方式的直接比较就完备了<br />
对比一下负数的结构， 可以找到一个简单的办法了：<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 把负数内存对应的整数减去 -0 ，他们就连续了<br />
而且更好的结果是，所有的负数经过这次减法后，对应的整数也都是负数了<br />
这样整个整数比较就变得连续了，而且在整个浮点数范围内都是有效的了<br />
<br />
最后的比较算法就是：<br />
// 函数:&nbsp;&nbsp; bool IsEqual(float f1, float f2, int absDelta)<br />
// 功能：把比较两个浮点数是否近似相同<br />
// 输入：f1, f2参与比较的两个浮点数<br />
//&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; absDelta 两个浮点数之间允许有多少个其他可以精确表达的浮点数存在，相当于相对误差<br />
// 输出:&nbsp;&nbsp; true，两个浮点数进行相等； false 两个浮点数不等<br />
// 注意：仅仅适合IEEE 32位浮点数结构<br />
bool IsEqual(float f1, float f2, int absDelta)<br />
{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; int i1, i2;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; i1 = ( f1&gt;0) ? ((int&amp;)f1) : ( (int&amp;) f1 - 0x80000000 );<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; i2 = (f2&gt;0) ? ((int&amp;)f2) : ( (int&amp;) f2 - 0x80000000 );<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return&nbsp;&nbsp; ((abs(i1-i2))&lt;absDelta) ? true : false;<br />
}</font></div>
</div>
<img src ="http://www.blogjava.net/shiliqiang/aggbug/285592.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/shiliqiang/" target="_blank">石头@</a> 2009-07-05 21:52 <a href="http://www.blogjava.net/shiliqiang/articles/285592.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>byte型数组转化成无符号整型 的函数</title><link>http://www.blogjava.net/shiliqiang/articles/284990.html</link><dc:creator>石头@</dc:creator><author>石头@</author><pubDate>Wed, 01 Jul 2009 09:43:00 GMT</pubDate><guid>http://www.blogjava.net/shiliqiang/articles/284990.html</guid><wfw:comment>http://www.blogjava.net/shiliqiang/comments/284990.html</wfw:comment><comments>http://www.blogjava.net/shiliqiang/articles/284990.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/shiliqiang/comments/commentRss/284990.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/shiliqiang/services/trackbacks/284990.html</trackback:ping><description><![CDATA[public static int byte2int(byte[] res)<br />
{<br />
&nbsp;&nbsp;&nbsp;&nbsp; int firstByte = (0x000000FF &amp; ((int)res[0]));<br />
&nbsp;&nbsp;&nbsp;&nbsp; int secondByte = (0x000000FF &amp; ((int)res[1]));<br />
&nbsp;&nbsp;&nbsp;&nbsp; int ToUnsignedInt = ((int)( firstByte &lt;&lt; 8 |secondByte)) &amp; 0xFFFFFFFF;<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp; return ToUnsignedInt;<br />
}<br />
<img src ="http://www.blogjava.net/shiliqiang/aggbug/284990.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/shiliqiang/" target="_blank">石头@</a> 2009-07-01 17:43 <a href="http://www.blogjava.net/shiliqiang/articles/284990.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>java中的对象名与c++中的指针和引用的区别！</title><link>http://www.blogjava.net/shiliqiang/articles/284655.html</link><dc:creator>石头@</dc:creator><author>石头@</author><pubDate>Mon, 29 Jun 2009 10:41:00 GMT</pubDate><guid>http://www.blogjava.net/shiliqiang/articles/284655.html</guid><wfw:comment>http://www.blogjava.net/shiliqiang/comments/284655.html</wfw:comment><comments>http://www.blogjava.net/shiliqiang/articles/284655.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/shiliqiang/comments/commentRss/284655.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/shiliqiang/services/trackbacks/284655.html</trackback:ping><description><![CDATA[<table width="72%" bgcolor="#ffffff" border="0">
    <tbody>
        <tr>
            <td width="97%" align="left">
            <table class="wr" border="0" cellpadding="0" cellspacing="0">
                <tbody>
                    <tr>
                        <td class="gray14"><font size="3">Java
                        语言的一个优点就是取消了指针的概念，但也导致了许多程序员在编程中常常忽略了对象与引用的区别，本文会试图澄清这一概念。并且由于Java不能通过简单
                        的赋值来解决对象复制的问题，在开发过程中，也常常要要应用clone（）方法来复制对象。本文会让你了解什么是影子clone与深度clone，认识它
                        们的区别、优点及缺点。 <br />
                        看到这个标题，是不是有点困惑：Java语言明确说明取消了指针，因为指针往往是在带来方便的同时也是导致代码不安全的根源，同时也会使程序的变得非常复
                        杂难以理解，滥用指针写成的代码不亚于使用早已臭名昭著的"GOTO"语句。Java放弃指针的概念绝对是极其明智的。但这只是在Java语言中没有明确
                        的指针定义，实质上每一个new语句返回的都是一个指针的引用，只不过在大多时候Java中不用关心如何操作这个"指针"，更不用象在操作C＋＋的指针那
                        样胆战心惊。唯一要多多关心的是在给函数传递对象的时候。如下例程： <br />
                        <br />
                        package reference; <br />
                        class Obj{ <br />
                        String str = "init value"; <br />
                        public String toString(){ <br />
                        return str; <br />
                        } <br />
                        } <br />
                        public class ObjRef{ <br />
                        Obj aObj = new Obj(); <br />
                        int aInt = 11; <br />
                        public void changeObj(Obj inObj){ <br />
                        inObj.str = "changed value"; <br />
                        } <br />
                        public void changePri(int inInt){ <br />
                        inInt = 22; <br />
                        } <br />
                        public static void main(String[] args) <br />
                        { <br />
                        ObjRef oRef = new ObjRef(); <br />
                        <br />
                        System.out.println("Before call changeObj() method: " + oRef.aObj); <br />
                        oRef.changeObj(oRef.aObj); <br />
                        System.out.println("After call changeObj() method: " + oRef.aObj); <br />
                        <br />
                        System.out.println("==================Print Primtive================="); <br />
                        System.out.println("Before call changePri() method: " + oRef.aInt); <br />
                        oRef.changePri(oRef.aInt); <br />
                        System.out.println("After call changePri() method: " + oRef.aInt); <br />
                        <br />
                        } <br />
                        } <br />
                        <br />
                        /* RUN RESULT <br />
                        Before call changeObj() method: init value <br />
                        After call changeObj() method: changed value <br />
                        ==================Print Primtive================= <br />
                        Before call changePri() method: 11 <br />
                        After call changePri() method: 11 <br />
                        <br />
                        * <br />
                        */ <br />
                        <br />
                        <br />
                        这段代码的主要部分调用了两个很相近的方法，changeObj()和changePri()。唯一不同的是它们一个把对象作为输入参数，另一个把
                        Java中的基本类型int作为输入参数。并且在这两个函数体内部都对输入的参数进行了改动。看似一样的方法，程序输出的结果却不太一样。
                        changeObj()方法真正的把输入的参数改变了，而changePri()方法对输入的参数没有任何的改变。 <br />
                        <br />
                        从这个例子知道Java对对象和基本的数据类型的处理是不一样的。和C语言一样，当把Java的基本数据类型（如int，char，double等）作为
                        入口参数传给函数体的时候，传入的参数在函数体内部变成了局部变量，这个局部变量是输入参数的一个拷贝，所有的函数体内部的操作都是针对这个拷贝的操作，
                        函数执行结束后，这个局部变量也就完成了它的使命，它影响不到作为输入参数的变量。这种方式的参数传递被称为"值传递"。而在Java中用对象的作为入口
                        参数的传递则缺省为"引用传递"，也就是说仅仅传递了对象的一个"引用"，这个"引用"的概念同C语言中的指针引用是一样的。当函数体内部对输入变量改变
                        时，实质上就是在对这个对象的直接操作。 <br />
                        <br />
                        除了在函数传值的时候是"引用传递"，在任何用"＝"向对象变量赋值的时候都是"引用传递"。如： <br />
                        <br />
                        package reference; <br />
                        class PassObj <br />
                        { <br />
                        String str = "init value"; <br />
                        } <br />
                        public class ObjPassvalue <br />
                        { <br />
                        <br />
                        public static void main(String[] args) <br />
                        { <br />
                        PassObj objA = new PassObj(); <br />
                        PassObj objB = objA; <br />
                        <br />
                        objA.str = "changed in objA"; <br />
                        System.out.println("Print objB.str value: " + objB.str); </font></td>
                    </tr>
                </tbody>
            </table>
            </td>
        </tr>
        <tr>
            <td height="17"> <br />
            </td>
            <td class="p14"> <br />
            </td>
        </tr>
        <tr>
            <td height="17"> <br />
            </td>
            <td class="au" align="left">
            <div class="uau"> </div>
            <br />
            </td>
        </tr>
        <tr>
            <td> <br />
            </td>
            <td> <br />
            </td>
        </tr>
    </tbody>
</table>
<font size="3"><a style="width: 20px; height: 20px; text-indent: 20px; background-repeat: no-repeat; background-image: url(/CuteSoft_Client/CuteEditor/Load.ashx?type=image&amp;file=anchor.gif);" name="#2"></a></font>
<table width="72%" bgcolor="#ffffff" border="0">
    <tbody>
        <tr>
            <td class="gray" width="3%" align="center" height="17"><font size="3">2</font></td>
            <td class="p14" align="left"><font size="3"><a style="width: 20px; height: 20px; text-indent: 20px; background-repeat: no-repeat; background-image: url(/CuteSoft_Client/CuteEditor/Load.ashx?type=image&amp;file=anchor.gif);" name="1028540400"></a><font color="#0000cc">JAVA中的指针,引用及对象的clone </font></font></td>
        </tr>
        <tr>
            <td> <br />
            </td>
            <td width="97%" align="left">
            <table class="wr" border="0" cellpadding="0" cellspacing="0">
                <tbody>
                    <tr>
                        <td class="gray14"><cc></cc><font size="3">} <br />
                        } <br />
                        /* RUN RESULT <br />
                        Print objB.str value: changed in objA <br />
                        */ <br />
                        <br />
                        第一句是在内存中生成一个新的PassObj对象，然后把这个PassObj的引用赋给变量objA，第二句是把PassObj对象的引用又赋给了变量
                        objB。此时objA和objB是两个完全一致的变量，以后任何对objA的改变都等同于对objB的改变。 <br />
                        <br />
                        即使明白了Java语言中的"指针"概念也许还会不经意间犯下面的错误。 <br />
                        <br />
                        Hashtable真的能存储对象吗？ <br />
                        <br />
                        看一看下面的很简单的代码，先是声明了一个Hashtable和StringBuffer对象，然后分四次把StriingBuffer对象放入到
                        Hashtable表中，在每次放入之前都对这个StringBuffer对象append()了一些新的字符串： <br />
                        <br />
                        package reference; <br />
                        import java.util.*; <br />
                        public class HashtableAdd{ <br />
                        public static void main(String[] args){ <br />
                        Hashtable ht = new Hashtable(); <br />
                        StringBuffer sb = new StringBuffer(); <br />
                        sb.append("abc,"); <br />
                        ht.put("1",sb); <br />
                        sb.append("def,"); <br />
                        ht.put("2",sb); <br />
                        sb.append("mno,"); <br />
                        ht.put("3",sb); <br />
                        sb.append("xyz."); <br />
                        ht.put("4",sb); <br />
                        <br />
                        int numObj=0; <br />
                        Enumeration it = ht.elements(); <br />
                        while(it.hasMoreElements()){ <br />
                        System.out.print("get StringBufffer "+(++numObj)+" from Hashtable: "); <br />
                        System.out.println(it.nextElement()); <br />
                        } <br />
                        } <br />
                        } <br />
                        <br />
                        如果你认为输出的结果是： <br />
                        get StringBufffer 1 from Hashtable: abc, <br />
                        get StringBufffer 2 from Hashtable: abc,def， <br />
                        get StringBufffer 3 from Hashtable: abc,def,mno, <br />
                        get StringBufffer 4 from Hashtable: abc,def,mno,xyz. <br />
                        <br />
                        那么你就要回过头再仔细看一看上一个问题了，把对象时作为入口参数传给函数，实质上是传递了对象的引用，向Hashtable传递
                        StringBuffer对象也是只传递了这个StringBuffer对象的引用！每一次向Hashtable表中put一次
                        StringBuffer，并没有生成新的StringBuffer对象，只是在Hashtable表中又放入了一个指向同一StringBuffer对
                        象的引用而已。 <br />
                        <br />
                        对Hashtable表存储的任何一个StringBuffer对象（更确切的说应该是对象的引用）的改动，实际上都是对同一
                        个"StringBuffer"的改动。所以Hashtable并不能真正存储能对象，而只能存储对象的引用。也应该知道这条原则对与Hashtable
                        相似的Vector, List, Map, Set等都是一样的。 <br />
                        <br />
                        上面的例程的实际输出的结果是： <br />
                        <br />
                        /* RUN RESULT <br />
                        get StringBufffer 1 from Hashtable: abc,def,mno,xyz. <br />
                        get StringBufffer 2 from Hashtable: abc,def,mno,xyz. <br />
                        get StringBufffer 3 from Hashtable: abc,def,mno,xyz. <br />
                        get StringBufffer 4 from Hashtable: abc,def,mno,xyz. <br />
                        */ <br />
                        <br />
                        类，对象与引用 <br />
                        <br />
                        Java最基本的概念就是类，类包括函数和变量。如果想要应用类，就要把类生成对象，这个过程被称作"类的实例化"。有几种方法把类实例化成对象，最常用
                        的就是用"new"操作符。类实例化成对象后，就意味着要在内存中占据一块空间存放实例。想要对这块空间操作就要应用到对象的引用。引用在Java语言中
                        的体现就是变量，而变量的类型就是这个引用的对象。虽然在语法上可以在生成一个对象后直接调用该对象的函数或变量，如： <br />
                        <br />
                        new String("Hello NDP")).substring(0,3)　　//RETURN RESULT: Hel <br />
                        <br />
                        但由于没有相应的引用，对这个对象的使用也只能局限这条语句中了。 <br />
                        <br />
                        产生：引用总是在把对象作参数"传递"的过程中自动发生，不需要人为的产生，也不能人为的控制引用的产生。这个传递包括把对象作为函数的入口参数的情况，也包括用"＝"进行对象赋值的时候。 <br />
                        范围：只有局部的引用，没有局部的对象。引用在Java语言的体现就是变量，而变量在Java语言中是有范围的，可以是局部的，也可以是全局的。 <br />
                        生存期：程序只能控制引用的生存周期。对象的生存期是由Java控制。用"new
                        Object()"语句生成一个新的对象，是在计算机的内存中声明一块区域存储对象，只有Java的垃圾收集器才能决定在适当的时候回收对象占用的内存。
                        </font></td>
                    </tr>
                </tbody>
            </table>
            </td>
        </tr>
        <tr>
            <td height="17"> <br />
            </td>
            <td class="p14"> <br />
            </td>
        </tr>
        <tr>
            <td height="17"> <br />
            </td>
            <td class="au" align="left">
            <div class="uau"> </div>
            <br />
            </td>
        </tr>
        <tr>
            <td> <br />
            </td>
            <td><font size="3"><hr class="fenge" size="1" width="87%" align="left" />
            </font></td>
        </tr>
    </tbody>
</table>
<font size="3"><a style="width: 20px; height: 20px; text-indent: 20px; background-repeat: no-repeat; background-image: url(/CuteSoft_Client/CuteEditor/Load.ashx?type=image&amp;file=anchor.gif);" name="#3"></a></font>
<table width="72%" bgcolor="#ffffff" border="0">
    <tbody>
        <tr>
            <td class="gray" width="3%" align="center" height="17"><font size="3">3</font></td>
            <td class="p14" align="left"><font size="3"><a style="width: 20px; height: 20px; text-indent: 20px; background-repeat: no-repeat; background-image: url(/CuteSoft_Client/CuteEditor/Load.ashx?type=image&amp;file=anchor.gif);" name="1028540401"></a><font color="#0000cc">JAVA中的指针,引用及对象的clone </font></font></td>
        </tr>
        <tr>
            <td> <br />
            </td>
            <td width="97%" align="left">
            <table class="wr" border="0" cellpadding="0" cellspacing="0">
                <tbody>
                    <tr>
                        <td class="gray14"><cc></cc><font size="3">没有办法阻止对引用的改动。 <br />
                        什么是"clone"？ <br />
                        <br />
                        在实际编程过程中，我们常常要遇到这种情况：有一个对象A，在某一时刻A中已经包含了一些有效值，此时可能会需要一个和A完全相同新对象B，并且此后对B
                        任何改动都不会影响到A中的值，也就是说，A与B是两个独立的对象，但B的初始值是由A对象确定的。在Java语言中，用简单的赋值语句是不能满足这种需
                        求的。要满足这种需求虽然有很多途径，但实现clone（）方法是其中最简单，也是最高效的手段。 <br />
                        <br />
                        Java的所有类都默认继承java.lang.Object类，在java.lang.Object类中有一个方法clone()。JDK
                        API的说明文档解释这个方法将返回Object对象的一个拷贝。要说明的有两点：一是拷贝对象返回的是一个新对象，而不是一个引用。二是拷贝对象与用
                        new操作符返回的新对象的区别就是这个拷贝已经包含了一些原来对象的信息，而不是对象的初始信息。 <br />
                        <br />
                        怎样应用clone()方法？ <br />
                        <br />
                        一个很典型的调用clone()代码如下： <br />
                        <br />
                        class CloneClass implements Cloneable{ <br />
                        public int aInt; <br />
                        public Object clone(){ <br />
                        CloneClass o = null; <br />
                        try{ <br />
                        o = (CloneClass)super.clone(); <br />
                        }catch(CloneNotSupportedException e){ <br />
                        e.printStackTrace(); <br />
                        } <br />
                        return o; <br />
                        } <br />
                        ｝ <br />
                        有三个值得注意的地方，一是希望能实现clone功能的CloneClass类实现了Cloneable接口，这个接口属于java.lang
                        包，java.lang包已经被缺省的导入类中，所以不需要写成java.lang.Cloneable。另一个值得请注意的是重载了clone()方
                        法。最后在clone()方法中调用了super.clone()，这也意味着无论clone类的继承结构是什么样的，super.clone()直接或
                        间接调用了java.lang.Object类的clone()方法。下面再详细的解释一下这几点。 <br />
                        <br />
                        应该说第三点是最重要的，仔细观察一下Object类的clone()一个native方法，native方法的效率一般来说都是远高于java中的非
                        native方法。这也解释了为什么要用Object中clone()方法而不是先new一个类，然后把原始对象中的信息赋到新对象中，虽然这也实现了
                        clone功能。对于第二点，也要观察Object类中的clone()还是一个protected属性的方法。这也意味着如果要应用clone()方
                        法，必须继承Object类，在Java中所有的类是缺省继承Object类的，也就不用关心这点了。然后重载clone()方法。还有一点要考虑的是为
                        了让其它类能调用这个clone类的clone()方法，重载之后要把clone()方法的属性设置为public。 <br />
                        <br />
                        那么clone类为什么还要实现Cloneable接口呢？稍微注意一下，Cloneable接口是不包含任何方法的！其实这个接口仅仅是一个标志，而且
                        这个标志也仅仅是针对Object类中clone()方法的，如果clone类没有实现Cloneable接口，并调用了Object的clone()方
                        法（也就是调用了super.Clone()方法），那么Object的clone()方法就会抛出
                        CloneNotSupportedException异常。 <br />
                        <br />
                        以上是clone的最基本的步骤，想要完成一个成功的clone，还要了解什么是"影子clone"和"深度clone"。 <br />
                        <br />
                        什么是影子clone？ <br />
                        <br />
                        下面的例子包含三个类UnCloneA，CloneB，CloneMain。CloneB类包含了一个UnCloneA的实例和一个int类型变量，并且
                        重载clone()方法。CloneMain类初始化UnCloneA类的一个实例b1，然后调用clone()方法生成了一个b1的拷贝b2。最后考察
                        一下b1和b2的输出： <br />
                        <br />
                        package clone; <br />
                        class UnCloneA { <br />
                        private int i; <br />
                        public UnCloneA(int ii) { i = ii; } <br />
                        public void doublevalue() { i *= 2; } <br />
                        public String toString() { <br />
                        return Integer.toString(i); <br />
                        } <br />
                        } <br />
                        class CloneB implements Cloneable{ <br />
                        public int aInt; <br />
                        public UnCloneA unCA = new UnCloneA(111); <br />
                        public Object clone(){ <br />
                        CloneB o = null; <br />
                        try{ <br />
                        o = (CloneB)super.clone(); <br />
                        }catch(CloneNotSupportedException e){ </font></td>
                    </tr>
                </tbody>
            </table>
            </td>
        </tr>
        <tr>
            <td height="17"> <br />
            </td>
            <td class="p14"> <br />
            </td>
        </tr>
        <tr>
            <td height="17"> <br />
            </td>
            <td class="au" align="left">
            <div class="uau"> </div>
            <br />
            </td>
        </tr>
        <tr>
            <td> <br />
            </td>
            <td><font size="3"><hr class="fenge" size="1" width="87%" align="left" />
            </font></td>
        </tr>
    </tbody>
</table>
<font size="3"><a style="width: 20px; height: 20px; text-indent: 20px; background-repeat: no-repeat; background-image: url(/CuteSoft_Client/CuteEditor/Load.ashx?type=image&amp;file=anchor.gif);" name="#4"></a></font>
<table width="72%" bgcolor="#ffffff" border="0">
    <tbody>
        <tr>
            <td class="gray" width="3%" align="center" height="17"><font size="3">4</font></td>
            <td class="p14" align="left"><font size="3"><a style="width: 20px; height: 20px; text-indent: 20px; background-repeat: no-repeat; background-image: url(/CuteSoft_Client/CuteEditor/Load.ashx?type=image&amp;file=anchor.gif);" name="1028540402"></a><font color="#0000cc">JAVA中的指针,引用及对象的clone </font></font></td>
        </tr>
        <tr>
            <td> <br />
            </td>
            <td width="97%" align="left">
            <table class="wr" border="0" cellpadding="0" cellspacing="0">
                <tbody>
                    <tr>
                        <td class="gray14"><cc></cc><font size="3">e.printStackTrace(); <br />
                        } <br />
                        return o; <br />
                        } <br />
                        } <br />
                        public class CloneMain { <br />
                        public static void main(String[] a){ <br />
                        CloneB b1 = new CloneB(); <br />
                        b1.aInt = 11; <br />
                        System.out.println("before clone,b1.aInt = "+ b1.aInt); <br />
                        System.out.println("before clone,b1.unCA = "+ b1.unCA); <br />
                        <br />
                        CloneB b2 = (CloneB)b1.clone(); <br />
                        b2.aInt = 22; <br />
                        b2.unCA.doublevalue(); <br />
                        System.out.println("================================="); <br />
                        System.out.println("after clone,b1.aInt = "+ b1.aInt); <br />
                        System.out.println("after clone,b1.unCA = "+ b1.unCA); <br />
                        System.out.println("================================="); <br />
                        System.out.println("after clone,b2.aInt = "+ b2.aInt); <br />
                        System.out.println("after clone,b2.unCA = "+ b2.unCA); <br />
                        } <br />
                        } <br />
                        <br />
                        <br />
                        /** RUN RESULT: <br />
                        before clone,b1.aInt = 11 <br />
                        before clone,b1.unCA = 111 <br />
                        ================================= <br />
                        after clone,b1.aInt = 11 <br />
                        after clone,b1.unCA = 222 <br />
                        ================================= <br />
                        after clone,b2.aInt = 22 <br />
                        after clone,b2.unCA = 222 <br />
                        */ <br />
                        <br />
                        输出的结果说明int类型的变量aInt和UnCloneA的实例对象unCA的clone结果不一致，int类型是真正的被clone了，因为改变了
                        b2中的aInt变量，对b1的aInt没有产生影响，也就是说，b2.aInt与b1.aInt已经占据了不同的内存空间，b2.aInt是
                        b1.aInt的一个真正拷贝。相反，对b2.unCA的改变同时改变了b1.unCA，很明显，b2.unCA和b1.unCA是仅仅指向同一个对象的
                        不同引用！从中可以看出，调用Object类中clone()方法产生的效果是：先在内存中开辟一块和原始对象一样的空间，然后原样拷贝原始对象中的内
                        容。对基本数据类型，这样的操作是没有问题的，但对非基本类型变量，我们知道它们保存的仅仅是对象的引用，这也导致clone后的非基本类型变量和原始对
                        象中相应的变量指向的是同一个对象。 <br />
                        <br />
                        大多时候，这种clone的结果往往不是我们所希望的结果，这种clone也被称为"影子clone"。要想让b2.unCA指向与b2.unCA不同的
                        对象，而且b2.unCA中还要包含b1.unCA中的信息作为初始信息，就要实现深度clone。 <br />
                        <br />
                        怎么进行深度clone？ <br />
                        <br />
                        把上面的例子改成深度clone很简单，需要两个改变：一是让UnCloneA类也实现和CloneB类一样的clone功能（实现Cloneable接
                        口，重载clone()方法）。二是在CloneB的clone()方法中加入一句o.unCA = (UnCloneA)unCA.clone();
                        <br />
                        <br />
                        程序如下： <br />
                        <br />
                        package clone.ext; <br />
                        class UnCloneA implements Cloneable{ <br />
                        private int i; <br />
                        public UnCloneA(int ii) { i = ii; } <br />
                        public void doublevalue() { i *= 2; } <br />
                        public String toString() { <br />
                        return Integer.toString(i); <br />
                        } <br />
                        public Object clone(){ <br />
                        UnCloneA o = null; <br />
                        try{ <br />
                        o = (UnCloneA)super.clone(); <br />
                        }catch(CloneNotSupportedException e){ <br />
                        e.printStackTrace(); <br />
                        } <br />
                        return o; <br />
                        } <br />
                        } <br />
                        class CloneB implements Cloneable{ <br />
                        public int aInt; <br />
                        public UnCloneA unCA = new UnCloneA(111); <br />
                        public Object clone(){ <br />
                        CloneB o = null; <br />
                        try{ <br />
                        o = (CloneB)super.clone(); <br />
                        }catch(CloneNotSupportedException e){ <br />
                        e.printStackTrace(); <br />
                        } <br />
                        o.unCA = (UnCloneA)unCA.clone(); <br />
                        return o; <br />
                        } <br />
                        } <br />
                        public class CloneMain { <br />
                        public static void main(String[] a){ <br />
                        CloneB b1 = new CloneB(); <br />
                        b1.aInt = 11; <br />
                        System.out.println("before clone,b1.aInt = "+ b1.aInt); <br />
                        System.out.println("before clone,b1.unCA = "+ b1.unCA); <br />
                        <br />
                        CloneB b2 = (CloneB)b1.clone(); <br />
                        b2.aInt = 22; <br />
                        b2.unCA.doublevalue(); <br />
                        System.out.println("================================="); </font></td>
                    </tr>
                </tbody>
            </table>
            </td>
        </tr>
        <tr>
            <td height="17"> <br />
            </td>
            <td class="p14"> <br />
            </td>
        </tr>
        <tr>
            <td height="17"> <br />
            </td>
            <td class="au" align="left">
            <div class="uau"> </div>
            <br />
            </td>
        </tr>
        <tr>
            <td> <br />
            </td>
            <td><font size="3"><hr class="fenge" size="1" width="87%" align="left" />
            </font></td>
        </tr>
    </tbody>
</table>
<font size="3"><a style="width: 20px; height: 20px; text-indent: 20px; background-repeat: no-repeat; background-image: url(/CuteSoft_Client/CuteEditor/Load.ashx?type=image&amp;file=anchor.gif);" name="#5"></a></font>
<table width="72%" bgcolor="#ffffff" border="0">
    <tbody>
        <tr>
            <td class="gray" width="3%" align="center" height="17"><font size="3">5</font></td>
            <td class="p14" align="left"><font size="3"><a style="width: 20px; height: 20px; text-indent: 20px; background-repeat: no-repeat; background-image: url(/CuteSoft_Client/CuteEditor/Load.ashx?type=image&amp;file=anchor.gif);" name="1028540403"></a><font color="#0000cc">JAVA中的指针,引用及对象的clone </font></font></td>
        </tr>
        <tr>
            <td> <br />
            </td>
            <td width="97%" align="left">
            <table class="wr" border="0" cellpadding="0" cellspacing="0">
                <tbody>
                    <tr>
                        <td class="gray14"><cc></cc><font size="3">System.out.println("after clone,b1.aInt = "+ b1.aInt); <br />
                        System.out.println("after clone,b1.unCA = "+ b1.unCA); <br />
                        System.out.println("================================="); <br />
                        System.out.println("after clone,b2.aInt = "+ b2.aInt); <br />
                        System.out.println("after clone,b2.unCA = "+ b2.unCA); <br />
                        } <br />
                        } <br />
                        <br />
                        /** RUN RESULT: <br />
                        before clone,b1.aInt = 11 <br />
                        before clone,b1.unCA = 111 <br />
                        ================================= <br />
                        after clone,b1.aInt = 11 <br />
                        after clone,b1.unCA = 111 <br />
                        ================================= <br />
                        after clone,b2.aInt = 22 <br />
                        after clone,b2.unCA = 222 <br />
                        */ <br />
                        <br />
                        可以看出，现在b2.unCA的改变对b1.unCA没有产生影响。此时b1.unCA与b2.unCA指向了两个不同的UnCloneA实例，而且在
                        CloneB b2 = (CloneB)b1.clone();调用的那一刻b1和b2拥有相同的值，在这里，b1.i = b2.i = 11。 <br />
                        <br />
                        要知道不是所有的类都能实现深度clone的。例如，如果把上面的CloneB类中的UnCloneA类型变量改成StringBuffer类型，看一下
                        JDK
                        API中关于StringBuffer的说明，StringBuffer没有重载clone()方法，更为严重的是StringBuffer还是一个
                        final类，这也是说我们也不能用继承的办法间接实现StringBuffer的clone。如果一个类中包含有StringBuffer类型对象或和
                        StringBuffer相似类的对象，我们有两种选择：要么只能实现影子clone，要么就在类的clone()方法中加一句（假设是
                        SringBuffer对象，而且变量名仍是unCA）： o.unCA = new StringBuffer(unCA.toString());
                        //原来的是：o.unCA = (UnCloneA)unCA.clone(); <br />
                        <br />
                        还要知道的是除了基本数据类型能自动实现深度clone以外，String对象是一个例外，它clone后的表现好象也实现了深度clone，虽然这只是一个假象，但却大大方便了我们的编程。 <br />
                        <br />
                        Clone中String和StringBuffer的区别 <br />
                        <br />
                        应该说明的是，这里不是着重说明String和StringBuffer的区别，但从这个例子里也能看出String类的一些与众不同的地方。 <br />
                        <br />
                        下面的例子中包括两个类，CloneC类包含一个String类型变量和一个StringBuffer类型变量，并且实现了clone()方法。在
                        StrClone类中声明了CloneC类型变量c1，然后调用c1的clone()方法生成c1的拷贝c2，在对c2中的String和
                        StringBuffer类型变量用相应的方法改动之后打印结果： <br />
                        <br />
                        package clone; <br />
                        class CloneC implements Cloneable{ <br />
                        public String str; <br />
                        public StringBuffer strBuff; <br />
                        public Object clone(){ <br />
                        CloneC o = null; <br />
                        try{ <br />
                        o = (CloneC)super.clone(); <br />
                        }catch(CloneNotSupportedException e){ <br />
                        e.printStackTrace(); <br />
                        } <br />
                        return o; <br />
                        } <br />
                        <br />
                        } <br />
                        public class StrClone { <br />
                        public static void main(String[] a){ <br />
                        CloneC c1 = new CloneC(); <br />
                        c1.str = new String("initializeStr"); <br />
                        c1.strBuff = new StringBuffer("initializeStrBuff"); <br />
                        System.out.println("before clone,c1.str = "+ c1.str); <br />
                        System.out.println("before clone,c1.strBuff = "+ c1.strBuff); <br />
                        <br />
                        CloneC c2 = (CloneC)c1.clone(); <br />
                        c2.str = c2.str.substring(0,5); <br />
                        c2.strBuff = c2.strBuff.append(" change strBuff clone"); <br />
                        System.out.println("================================="); <br />
                        System.out.println("after clone,c1.str = "+ c1.str); <br />
                        System.out.println("after clone,c1.strBuff = "+ c1.strBuff); <br />
                        System.out.println("================================="); <br />
                        System.out.println("after clone,c2.str = "+ c2.str); <br />
                        System.out.println("after clone,c2.strBuff = "+ c2.strBuff); <br />
                        } <br />
                        } <br />
                        /* RUN RESULT <br />
                        before clone,c1.str = initializeStr <br />
                        before clone,c1.strBuff = initializeStrBuff <br />
                        ================================= <br />
                        after clone,c1.str = initializeStr <br />
                        after clone,c1.strBuff = initializeStrBuff change strBuff clone <br />
                        ================================= <br />
                        after clone,c2.str = initi <br />
                        after clone,c2.strBuff = initializeStrBuff change strBuff clone <br />
                        * <br />
                        */ <br />
                        <br />
                        打印的结果可以看出，String类型的变量好象已经实现了深度clone，因为对c2.str的改动并没有影响到c1.str！难道Java把
                        Sring类看成了基本数据类型？其实不然，这里有一个小小的把戏，秘密就在于c2.str =
                        c2.str.substring(0,5)这一语句！实质上，在clone的时候c1.str与c2.str仍然是引用，而且都指向了同一个
                        String对象。但在执行c2.str =
                        c2.str.substring(0,5)的时候，它作用相当于生成了一个新的String类型，然后又赋回给c2.str。这是因为String被
                        Sun公司的工程师写成了一个不可更改的类（immutable
                        class），在所有String类中的函数都不能更改自身的值。下面给出很简单的一个例子： <br />
                        <br />
                        package clone; public class StrTest { public static void main(String[]
                        args) { String str1 = "This is a test for immutable"; String str2 =
                        str1.substring(0,8); System.out.println("print str1 : " + str1);
                        System.out.println("print str2 : " + str2); } } /* RUN RESULT print
                        str1 : This is a test for immutable print str2 : This is */ <br />
                        <br />
                        例子中，虽然str1调用了substring()方法，但str1的值并没有改变。类似的，String类中的其它方法也是如此。当然如果我们把最上面的例子中的这两条语句 <br />
                        <br />
                        c2.str = c2.str.substring(0,5); <br />
                        c2.strBuff = c2.strBuff.append(" change strBuff clone"); <br />
                        <br />
                        改成下面这样： <br />
                        <br />
                        c2.str.substring(0,5); <br />
                        c2.strBuff.append(" change strBuff clone"); <br />
                        <br />
                        去掉了重新赋值的过程，c2.str也就不能有变化了，我们的把戏也就露馅了。但在编程过程中只调用 <br />
                        <br />
                        c2.str.substring(0,5); <br />
                        <br />
                        语句是没有任何意义的。 <br />
                        <br />
                        应该知道的是在Java中所有的基本数据类型都有一个相对应的类，象Integer类对应int类型，Double类对应double类型等等，这些类也
                        与String类相同，都是不可以改变的类。也就是说，这些的类中的所有方法都是不能改变其自身的值的。这也让我们在编clone类的时候有了一个更多的
                        选择。同时我们也可以把自己的类编成不可更改的类。</font></td>
                    </tr>
                </tbody>
            </table>
            </td>
        </tr>
    </tbody>
</table>
 <img src ="http://www.blogjava.net/shiliqiang/aggbug/284655.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/shiliqiang/" target="_blank">石头@</a> 2009-06-29 18:41 <a href="http://www.blogjava.net/shiliqiang/articles/284655.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>