﻿<?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-crycz-文章分类-Java lang</title><link>http://www.blogjava.net/crycz/category/19519.html</link><description /><language>zh-cn</language><lastBuildDate>Tue, 09 Mar 2010 20:52:44 GMT</lastBuildDate><pubDate>Tue, 09 Mar 2010 20:52:44 GMT</pubDate><ttl>60</ttl><item><title>(转)--String和StringBuffer的对比及使用比较</title><link>http://www.blogjava.net/crycz/articles/299293.html</link><dc:creator>blues</dc:creator><author>blues</author><pubDate>Thu, 22 Oct 2009 02:10:00 GMT</pubDate><guid>http://www.blogjava.net/crycz/articles/299293.html</guid><wfw:comment>http://www.blogjava.net/crycz/comments/299293.html</wfw:comment><comments>http://www.blogjava.net/crycz/articles/299293.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/crycz/comments/commentRss/299293.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/crycz/services/trackbacks/299293.html</trackback:ping><description><![CDATA[<h3 class="text-center">String和StringBuffer的对比及使用技巧</h3>
<div class="from_info"> www.diybl.com 时间：2007-09-27 作者:佚名 编辑:本站
点击： <span id="articleTimes">211</span>
<a href="http://www.diybl.com/course/3_program/java/javajs/2007927/74795.html#comment">[评论]</a></div>
<font size="2">转自：<a href="http://ww1.blog.enorth.com.cn/article/30760.shtml">http://ww1.blog.enorth.com.cn/article/30760.shtml</a></font><br />
<br />
<font size="2">  String和StringBuffer之概览<br />
创建字符串的较佳途径<br />
滞留字符串带来的优化<br />
连接字符串时的优化技巧<br />
借助StringBuffer的初始化过程的优化技巧<br />
关键点</font><br />
<font size="2">&nbsp;</font><br />
<font size="2"><strong>String和StringBuffer之概览</strong><br />
非可变对象一旦创建之后就不能再被改变，可变对象则可以在创建之后被改变。String对象是非可变对象，StringBuffer对象则是可变对象。
为获得更佳的性能你需要根据实际情况小心谨慎地选择到底使用这两者中的某一个。下面的话题会作详细的阐述。（注意：这个章节假设读者已经具备Java的
String和StringBuffer的相关基础知识。）</font><br />
<font size="2">&nbsp;</font><br />
<strong><font size="2">创建字符串的较佳途径</font></strong><br />
<font size="2">你可以按照以下方式创建字符串对象：<br />
1. String s1 = "hello";&nbsp;<br />
&nbsp;&nbsp;&nbsp; String s2 = "hello";&nbsp;<br />
2. String s3 = new String("hello");<br />
&nbsp;&nbsp;&nbsp; String s4 = new String("hello");</font><br />
<font size="2">&nbsp;</font><br />
<font size="2">上面哪种方式会带来更好的性能呢？下面的代码片断用来测量二者之间的区别。</font><br />
<br />
<font size="2">StringTest1.java</font><br />
<font style="background-color: #c0c0c0;" size="2">package com.performance.string;</font><br />
<font style="background-color: #c0c0c0;" size="2">/** This class shows the time taken for creation of <br />
&nbsp;*&nbsp; String literals and String objects.<br />
&nbsp;*/</font><br />
<font style="background-color: #c0c0c0;" size="2">public class StringTest1 {</font><br />
<font style="background-color: #c0c0c0;" size="2">public static void main(String[] args){</font><br />
<font style="background-color: #c0c0c0;" size="2">&nbsp;&nbsp;&nbsp; // create String literals<br />
&nbsp;&nbsp;&nbsp; long startTime = System.currentTimeMillis();<br />
&nbsp;&nbsp;&nbsp; for(int i=0;i&lt;50000;i++){</font><br />
<font style="background-color: #c0c0c0;" size="2">&nbsp;&nbsp;&nbsp; String s1 = "hello";<br />
&nbsp;&nbsp;&nbsp; String s2 = "hello"; <br />
&nbsp;&nbsp;&nbsp; }</font><br />
<font style="background-color: #c0c0c0;" size="2">&nbsp;&nbsp;&nbsp; long endTime = System.currentTimeMillis();<br />
&nbsp;&nbsp;&nbsp; System.out.println("Time taken for creation of String literals : "<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; + (endTime - startTime) + " milli seconds" );</font><br />
<font style="background-color: #c0c0c0;" size="2">&nbsp;&nbsp;&nbsp; // create String objects using 'new' keyword&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br />
&nbsp;&nbsp;&nbsp; long startTime1 = System.currentTimeMillis();<br />
&nbsp;&nbsp;&nbsp; for(int i=0;i&lt;50000;i++){</font><br />
<font style="background-color: #c0c0c0;" size="2">&nbsp;&nbsp;&nbsp; String s3 = new String("hello");<br />
&nbsp;&nbsp;&nbsp; String s4 = new String("hello");<br />
&nbsp;&nbsp;&nbsp; }</font><br />
<font size="2"><font style="background-color: #c0c0c0;">&nbsp;&nbsp;&nbsp; long endTime1 = System.currentTimeMillis();<br />
&nbsp;&nbsp;&nbsp; System.out.println("Time taken for creation of String objects : " <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; + (endTime1 - startTime1)+" milli seconds");<br />
&nbsp;&nbsp;&nbsp; }<br />
}</font><br />
这段代码的输出：<br />
</font><font style="background-color: #c0c0c0;" size="2">Time taken for creation of String literals : 0 milli seconds<br />
Time taken for creation of String objects : 170 milli seconds</font><br />
<font size="2">&nbsp;</font><br />
<font size="2"><strong>JVM是怎样处理字符串的呢？</strong><br />
Java虚拟机会维护一个内部的滞留字符串对象的列表（唯一字符串的池）来避免在堆内存中产生重复的String对象。当JVM从class文件里加载
字符串字面量并执行的时候，它会先检查一下当前的字符串是否已经存在于滞留字符串列表，如果已经存在，那就不会再创建一个新的String对象而是将引用
指向已经存在的String对象，JVM会在内部为字符串字面量作这种检查，但并不会为通过new关键字创建的String对象作这种检查。当然你可以明
确地使用String.intern()方法强制JVM为通过new关键字创建的String对象作这样的检查。这样可以强制JVM检查内部列表而使用已
有的String对象。</font><br />
<font size="2">  所以结论是，JVM会内在地为字符串字面量维护一些唯一的String
对象，程序员不需要为字符串字面量而发愁，但是可能会被一些通过new关键字创建的String对象而困扰，不过他们可以使用intern()方法来避免
在堆内存上创建重复的String对象来改善Java的运行性能。下一小节会向大家展示更多的信息。</font><br />
<font size="2">&nbsp;</font><br />
<font size="2">下图展示了未使用intern()方法来创建字符串的情况。<br />
&nbsp;</font><font size="2"><img src="http://www.webjx.com/upfiles/20060117/20060117201239_1.gif" alt="" /> </font><br />
<font size="2">
你可以自己使用==操作符和String.equals()方法来编码测试上面提到的区别。==操作符会返回true如果一些引用指向一个相同的对象但
不会判断String对象的内容是否相同；String.equals()方法会返回true如果被操作的String对象的内容相同。对于上面的代码会
有s1==s2，因为s1和s2两个引用指向同一个对象，对于上面的代码，s3.equals(s4)会返回true因为两个对象的内容都一样为
&#8221;hello&#8221;。你可以从上图看出这种机制。在这里有三个独立的包含了相同的内容（&#8221;hello&#8221;）的对象，实际上我们不需要这么三个独立的对象――因为
要运行它们的话既浪费时间又浪费内存。</font><br />
<font size="2">&nbsp;</font><br />
<font size="2">  那么怎样才能确保String对象不会重复呢？下一个话题会涵盖对于内建String机制的兴趣。</font><br />
<font size="2">&nbsp;</font><br />
<font size="2"><strong>滞留字符串的优化作用<br />
</strong>
同一个字符串对象被重复地创建是不必要的，String.intern()方法可以避免这种情况。下图说明了String.intern()方法是如何
工作的，String.intern()方法检查字符串对象的存在性，如果需要的字符串对象已经存在，那么它会将引用指向已经存在的字符串对象而不是重新
创建一个。下图描绘了使用了intern()方法的字符串字面量和字符串对象的创建情况。<br />
&nbsp;<br />
<img src="http://www.webjx.com/upfiles/20060117/20060117201219_2.gif" alt="" /></font><br />
<font size="2">下面的例程帮助大家了解String.intern()方法的重要性。<br />
StringTest2.java</font><br />
<font size="2">&nbsp;</font><br />
<font style="background-color: #c0c0c0;" size="2">package com.performance.string;</font><br />
<font style="background-color: #c0c0c0;" size="2">// This class shows the use of intern() method to improve performance<br />
public class StringTest2 {</font><br />
<font style="background-color: #c0c0c0;" size="2">public static void main(String[] args){</font><br />
<font style="background-color: #c0c0c0;" size="2">&nbsp;&nbsp;&nbsp; // create String references like s1,s2,s3...so on..<br />
&nbsp;&nbsp;&nbsp; String variables[] = new String[50000];<br />
&nbsp;&nbsp;&nbsp; for( int i=0;i&lt;variables.length;i++){<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; variables[i] = "s"+i;<br />
&nbsp;&nbsp;&nbsp; }</font><br />
<font style="background-color: #c0c0c0;" size="2">&nbsp;&nbsp;&nbsp; // create String literals<br />
&nbsp;&nbsp;&nbsp; long startTime0 = System.currentTimeMillis();<br />
&nbsp;&nbsp;&nbsp; for(int i=0;i&lt;variables.length;i++){<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; variables[i] = "hello";<br />
&nbsp;&nbsp;&nbsp; }</font><br />
<font style="background-color: #c0c0c0;" size="2">&nbsp;&nbsp;&nbsp; long endTime0 = System.currentTimeMillis();<br />
&nbsp;&nbsp;&nbsp; System.out.println("Time taken for creation of String literals : "<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; + (endTime0 - startTime0) + " milli seconds" );</font><br />
<font style="background-color: #c0c0c0;" size="2">&nbsp;&nbsp;&nbsp; // create String objects using 'new' keyword&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br />
&nbsp;&nbsp;&nbsp; long startTime1 = System.currentTimeMillis();<br />
&nbsp;&nbsp;&nbsp; for(int i=0;i&lt;variables.length;i++){<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; variables[i] = new String("hello");<br />
&nbsp;&nbsp;&nbsp; }</font><br />
<font style="background-color: #c0c0c0;" size="2">&nbsp;&nbsp;&nbsp; long endTime1 = System.currentTimeMillis();<br />
&nbsp;&nbsp;&nbsp; System.out.println("Time taken for creation of String objects with 'new' key word : " <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; + (endTime1 - startTime1)+" milli seconds");</font><br />
<font style="background-color: #c0c0c0;" size="2">&nbsp;&nbsp;&nbsp; // intern String objects with intern() method&nbsp;&nbsp;&nbsp; <br />
&nbsp;&nbsp;&nbsp; long startTime2 = System.currentTimeMillis();<br />
&nbsp;&nbsp;&nbsp; for(int i=0;i&lt;variables.length;i++){<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; variables[i] = new String("hello");<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; variables[i] = variables[i].intern();<br />
&nbsp;&nbsp;&nbsp; }</font><br />
<font style="background-color: #c0c0c0;" size="2">&nbsp;&nbsp;&nbsp; long endTime2 = System.currentTimeMillis();<br />
&nbsp;&nbsp;&nbsp; System.out.println("Time taken for creation of String objects with intern(): " <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; + (endTime2 - startTime2)+" milli seconds");<br />
&nbsp;&nbsp;&nbsp; }<br />
}</font><br />
<font size="2">这是上面那段代码的输出结果：<br />
</font><font style="background-color: #c0c0c0;" size="2">Time taken for creation of String literals : 0 milli seconds<br />
Time taken for creation of String objects with 'new' key word : 160 milli seconds<br />
Time taken for creation of String objects with intern(): 60 milli seconds</font><br />
<font size="2">&nbsp;</font><br />
<font size="2"><strong>连接字符串时候的优化技巧<br />
</strong>  你可以使用+操作符或者String.concat()或者StringBuffer.append()等办法来连接多个字符串，那一种办法具有最佳的性能呢？</font><br />
<font size="2">
如何作出选择取决于两种情景，第一种情景是需要连接的字符串是在编译期决定的还是在运行期决定的，第二种情景是你使用的是StringBuffer还是
String。通常程序员会认为StringBuffer.append()方法会优于+操作符或String.concat()方法，但是在一些特定的
情况下这个假想是不成立的。</font><br />
<font size="2">&nbsp;</font><br />
<font size="2">1)&nbsp;第一种情景：编译期决定相对于运行期决定<br />
请看下面的StringTest3.java代码和输出结果。</font><br />
<br />
<font style="background-color: #c0c0c0;" size="2">package com.performance.string;</font><br />
<font style="background-color: #c0c0c0;" size="2">/** This class shows the time taken by string concatenation at compile time and run time.*/<br />
public class StringTest3 {</font><br />
<font style="background-color: #c0c0c0;" size="2">&nbsp; public static void main(String[] args){<br />
&nbsp;&nbsp;&nbsp; //Test the String Conca</font><font>tination<br />
&nbsp;&nbsp;&nbsp; long startTime = System.currentTimeMillis();<br />
&nbsp;&nbsp;&nbsp; for(int i=0;i&lt;5000;i++){<br />
&nbsp;&nbsp;&nbsp; String result = "This is"+ "testing the"+ "difference"+ "between"+ <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; "String"+ "and"+ "StringBuffer";<br />
&nbsp;&nbsp;&nbsp; }</font><br />
<font style="background-color: #c0c0c0;" size="2">&nbsp;&nbsp;&nbsp; long endTime = System.currentTimeMillis();<br />
&nbsp;&nbsp;&nbsp; System.out.println("Time taken for string concatenation using + operator : " <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; + (endTime - startTime)+ " milli seconds");</font><br />
<font style="background-color: #c0c0c0;" size="2">&nbsp;&nbsp;&nbsp; //Test the StringBuffer Concatination<br />
&nbsp;&nbsp;&nbsp; long startTime1 = System.currentTimeMillis();<br />
&nbsp;&nbsp;&nbsp; for(int i=0;i&lt;5000;i++){<br />
&nbsp;&nbsp;&nbsp; StringBuffer result = new StringBuffer();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; result.append("This is");<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; result.append("testing the");<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; result.append("difference");<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; result.append("between");<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; result.append("String");<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; result.append("and");<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; result.append("StringBuffer");<br />
&nbsp;&nbsp;&nbsp;&nbsp; }</font><br />
<font size="2"><font style="background-color: #c0c0c0;">&nbsp;&nbsp;&nbsp; long endTime1 = System.currentTimeMillis();<br />
&nbsp;&nbsp;&nbsp; System.out.println("Time taken for String concatenation using StringBuffer : "<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; + (endTime1 - startTime1)+ " milli seconds");<br />
&nbsp; }<br />
}<br />
</font>这是上面的代码的输出结果：<br />
</font><font size="2"><font style="background-color: #c0c0c0;">Time taken for String concatenation using + operator : 0 milli seconds<br />
Time taken for String concatenation using StringBuffer : 50 milli seconds</font><br />
很有趣地，+操作符居然比StringBuffer.append()方法要快，为什么呢？</font><br />
<font size="2">&nbsp;</font><br />
<font size="2">  这里编译器的优化起了关键作用，编译器像下面举例的那样简单地在编译期连接多个字符串。它使用编译期决定取代运行期决定，在你使用new关键字来创建String对象的时候也是如此。</font><br />
<font size="2">&nbsp;</font><br />
<font size="2">编译前：<br />
String result = "This is"+"testing the"+"difference"+"between"+"String"+"and"+"StringBuffer";<br />
编译后：<br />
String result = "This is testing the difference between String and StringBuffer";</font><br />
<br />
<font size="2">这里String对象在编译期就决定了而StringBuffer对象是在运行期决定的。运行期决定需要额外的开销当字符串的值无法预先知道的时候，编译期决定作用于字符串的值可以预先知道的时候，下面是一个例子。</font><br />
<font size="2">&nbsp;</font><br />
<font size="2">编译前：<br />
public String getString(String str1,String str2) {<br />
&nbsp;&nbsp;&nbsp; return str1+str2;<br />
}<br />
编译后：<br />
return new StringBuffer().append(str1).append(str2).toString();<br />
运行期决定需要更多的时间来运行。</font><br />
<font size="2">&nbsp;</font><br />
<font size="2">2)&nbsp;第二种情景：使用StringBuffer取代String<br />
看看下面的代码你会发现与情景一相反的结果――连接多个字符串的时候StringBuffer要比String快。<br />
StringTest4.java</font><br />
<font size="2">&nbsp;</font><br />
<font style="background-color: #c0c0c0;" size="2">package com.performance.string;</font><br />
<font style="background-color: #c0c0c0;" size="2">/** This class shows the time taken by string concatenation <br />
using + operator and StringBuffer&nbsp; */<br />
public class StringTest4 {</font><br />
<font style="background-color: #c0c0c0;" size="2">&nbsp;public static void main(String[] args){<br />
&nbsp;&nbsp;&nbsp; //Test the String Concatenation using + operator<br />
&nbsp;&nbsp;&nbsp; long startTime = System.currentTimeMillis();<br />
&nbsp;&nbsp;&nbsp; String result = "hello";<br />
&nbsp;&nbsp;&nbsp; for(int i=0;i&lt;1500;i++){<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; result += "hello";<br />
&nbsp;&nbsp;&nbsp; }</font><br />
<font style="background-color: #c0c0c0;" size="2">&nbsp;&nbsp;&nbsp; long endTime = System.currentTimeMillis();<br />
&nbsp;&nbsp;&nbsp; System.out.println("Time taken for string concatenation using + operator : "<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; + (endTime - startTime)+ " milli seconds");</font><br />
<font style="background-color: #c0c0c0;" size="2">&nbsp;&nbsp;&nbsp; //Test the String Concatenation using StringBuffer<br />
&nbsp;&nbsp;&nbsp; long startTime1 = System.currentTimeMillis();<br />
&nbsp;&nbsp;&nbsp; StringBuffer result1 = new StringBuffer("hello");<br />
&nbsp;&nbsp;&nbsp; for(int i=0;i&lt;1500;i++){<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; result1.append("hello");<br />
&nbsp;&nbsp;&nbsp; }</font><br />
<font size="2"><font style="background-color: #c0c0c0;">&nbsp;&nbsp;&nbsp; long endTime1 = System.currentTimeMillis();<br />
&nbsp;&nbsp;&nbsp; System.out.println("Time taken for string concatenation using StringBuffer :&nbsp; " <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; + (endTime1 - startTime1)+ " milli seconds");<br />
&nbsp;&nbsp;&nbsp; }<br />
}<br />
</font>这是上面的代码的输出结果：<br />
</font><font style="background-color: #c0c0c0;" size="2">Time taken for string concatenation using + operator : 280 milli seconds<br />
Time taken for String concatenation using StringBuffer : 0 milli seconds</font><br />
<font size="2">看得出StringBuffer.append()方法要比+操作符要快得多，为什么呢？</font><br />
<br />
<font size="2">  原因是两者都是在运行期决定字符串对象，但是+操作符使用不同于StringBuffer.append()的规则通过String和StringBuffer来完成字符串连接操作。（译注：什么样的规则呢？）</font><br />
<font size="2">&nbsp;</font><br />
<font size="2"><strong>借助StringBuffer的初始化过程的优化技巧</strong><br />
你可以通过StringBuffer的构造函数来设定它的初始化容量，这样可以明显地提升性能。这里提到的构造函数是StringBuffer(int
length)，length参数表示当前的StringBuffer能保持的字符数量。你也可以使用ensureCapacity(int
minimumcapacity)方法在StringBuffer对象创建之后设置它的容量。首先我们看看StringBuffer的缺省行为，然后再找
出一条更好的提升性能的途径。</font><br />
<font size="2">&nbsp;</font><br />
<font size="2"><strong>StringBuffer的缺省行为：</strong><br />
StringBuffer在内部维护一个字符数组，当你使用缺省的构造函数来创建StringBuffer对象的时候，因为没有设置初始化字符长
度，StringBuffer的容量被初始化为16个字符，也就是说缺省容量就是16个字符。当StringBuffer达到最大容量的时候，它会将自身
容量增加到当前的2倍再加2，也就是（2*旧值+2）。</font><br />
<font size="2">  如果你使用缺省值，初始化之后接着往里
面追加字符，在你追加到第16个字符的时候它会将容量增加到34（2*16+2），当追加到34个字符的时候就会将容量增加到70（2*34+2）。无论
何事只要StringBuffer到达它的最大容量它就不得不创建一个新的字符数组然后重新将旧字符和新字符都拷贝一遍――这也太昂贵了点。所以总是给
StringBuffer设置一个合理的初始化容量值是错不了的，这样会带来立竿见影的性能增益。</font><br />
<font size="2">
我利用两个StringBuffer重新测试了上面的StringTest4.java代码，一个未使用初始化容量值而另一个使用了。这次我追加了
50000个&#8217;hello&#8217;对象没有使用+操作符。区别是我使用StringBuffer(250000)的构造函数来初始化第二个
StringBuffer了。</font><br />
<font size="2">&nbsp;</font><br />
<font size="2">输出结果如下：<br />
</font><font style="background-color: #c0c0c0;" size="2">Time taken for String concatenation using StringBuffer with out setting size: 280 milli seconds<br />
Time taken for String concatenation using StringBuffer with setting size: 0 milli seconds</font><br />
<font size="2">StringBuffer初始化过程的调整的作用由此可见一斑。所以，使用一个合适的容量值来初始化StringBuffer永远都是一个最佳的建议。</font><br />
<font size="2">&nbsp;</font><br />
<font size="2"><strong>关键点</strong><br />
1.&nbsp;无论何时只要可能的话使用字符串字面量来常见字符串而不是使用new关键字来创建字符串。<br />
2.&nbsp;无论何时当你要使用new关键字来创建很多内容重复的字符串的话，请使用String.intern()方法。<br />
3.&nbsp;+操作符会为字符串连接提供最佳的性能――当字符串是在编译期决定的时候。<br />
4.&nbsp;如果字符串在运行期决定，使用一个合适的初期容量值初始化的StringBuffer会为字符串连接提供最佳的性能。</font><font size="2">&nbsp;</font>
<img src ="http://www.blogjava.net/crycz/aggbug/299293.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/crycz/" target="_blank">blues</a> 2009-10-22 10:10 <a href="http://www.blogjava.net/crycz/articles/299293.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>(转)--java内存分配</title><link>http://www.blogjava.net/crycz/articles/204127.html</link><dc:creator>blues</dc:creator><author>blues</author><pubDate>Fri, 30 May 2008 08:39:00 GMT</pubDate><guid>http://www.blogjava.net/crycz/articles/204127.html</guid><wfw:comment>http://www.blogjava.net/crycz/comments/204127.html</wfw:comment><comments>http://www.blogjava.net/crycz/articles/204127.html#Feedback</comments><slash:comments>1</slash:comments><wfw:commentRss>http://www.blogjava.net/crycz/comments/commentRss/204127.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/crycz/services/trackbacks/204127.html</trackback:ping><description><![CDATA[<p>转自&nbsp; posted on 2008-05-21 21:14 <a href="http://www.blogjava.net/Jack2007/">Jack.Wang</a> 阅读(1790) <a href="http://www.blogjava.net/Jack2007/archive/2008/05/21/202018.html#Post">评论(3)</a> &nbsp;<a href="http://www.blogjava.net/Jack2007/admin/EditPosts.aspx?postid=202018">编辑</a> &nbsp;<a href="http://www.blogjava.net/Jack2007/AddToFavorite.aspx?id=202018">收藏</a> 所属分类: <a href="http://www.blogjava.net/Jack2007/category/23333.html">开发技术</a><br />
<br />
</p>
<p>今天看了一下java的内存分配，分享一下：<br />
基础数据类型直接在栈空间分配， 方法的形式参数，直接在栈空间分配，当方法调用完成后从栈空间回收。&nbsp;&nbsp;引用数据类型，需要用new来创建，既在栈空间分配一个地址空间，又在堆空间分配对象的类变量 。 方法的引用参数，在栈空间分配一个地址空间，并指向堆空间的对象区，当方法调用完成后从栈空间回收。局部变量 new 出来时，在栈空间和堆空间中分配空间，当局部变量生命周期结束后，栈空间立刻被回收，堆空间区域等待GC回收。 方法调用时传入的 literal 参数，先在栈空间分配，在方法调用完成后从栈空间分配。字符串常量在 DATA 区域分配 ，this 在堆空间分配 。数组既在栈空间分配数组名称， 又在堆空间分配数组实际的大小！<br />
哦 对了，补充一下static在DATA区域分配。<br />
其实是有规律的，只要你理解了这些个基本的原理：<br />
堆空间的话： 操作系统有一个记录空闲内存地址的链表，当系统收到程序的申请时，会遍历该链表，寻找第一个空间大于所申请空间的堆结点，然后将该结点从空闲结点链表中删除，并将该结点的空间分配给程序，另外，对于大多数系统，会在这块内存空间中的首地址处记录本次分配的大小，这样代码中的delete语句才能正确的释放本内存空间。另外由于找到的堆结点的大小不一定正好等于申请的大小，系统会自动的将多余的那部分重新放入空闲链表中。是由new分配的内存，一般速度比较慢，而且容易产生内存碎片，不过用起来最方便，另外，在WINDOWS下，最好的方式是用VirtualAlloc分配内存，他不是在堆，也不是在栈是直接在进程的地址空间中保留一快内存，虽然用起来最不方便。但是速度快，也最灵活。是向高地址扩展的数据结构，是不连续的内存区域。这是由于系统是用链表来存储的空闲内存地址的，自然是不连续的，而链表的遍历方向是由低地址向高地址。堆的大小受限于计算机系统中有效的虚拟内存。由此可见，堆获得的空间比较灵活，也比较大。</p>
<p>栈空间的话：在Windows下, 栈是向低地址扩展的数据结构，是一块连续的内存的区域。这句话的意思是栈顶的地址和栈的最大容量是系统预先规定好的，在WINDOWS下，栈的大小是固定的（是一个编译时就确定的常数），如果申请的空间超过栈的剩余空间时，将提示overflow。因此，能从栈获得的空间较小。只要栈的剩余空间大于所申请空间，系统将为程序提供内存，否则将报异常提示栈溢出。 由系统自动分配，速度较快。但程序员是无法控制的。</p>
<p>ok,头会不会有点小晕，不会的话继续吧：<br />
JVM中的堆和栈<br />
JVM是基于堆栈的虚拟机.JVM为每个新创建的线程都分配一个堆栈.也就是说,对于一个Java程序来说，它的运行就是通过对堆栈的操作来完成的。堆栈以帧为单位保存线程的状态。JVM对堆栈只进行两种操作:以帧为单位的压栈和出栈操作。<br />
我们知道,某个线程正在执行的方法称为此线程的当前方法.我们可能不知道,当前方法使用的帧称为当前帧。当线程激活一个Java方法,JVM就会在线程的Java堆栈里新压入一个帧。这个帧自然成为了当前帧.在此方法执行期间,这个帧将用来保存参数,局部变量,中间计算过程和其他数据.这个帧在这里和编译原理中的活动纪录的概念是差不多的.<br />
从Java的这种分配机制来看,堆栈又可以这样理解:堆栈(Stack)是操作系统在建立某个进程时或者线程(在支持多线程的操作系统中是线程)为这个线程建立的存储区域，该区域具有先进后出的特性。<br />
每一个Java应用都唯一对应一个JVM实例，每一个实例唯一对应一个堆。应用程序在运行中所创建的所有类实例或数组都放在这个堆中,并由应用所有的线程共享.跟C/C++不同，Java中分配堆内存是自动初始化的。Java中所有对象的存储空间都是在堆中分配的，但是这个对象的引用却是在堆栈中分配,也就是说在建立一个对象时从两个地方都分配内存，在堆中分配的内存实际建立这个对象，而在堆栈中分配的内存只是一个指向这个堆对象的指针(引用)而已。</p>
<p><br />
&nbsp;</p>
<img src ="http://www.blogjava.net/crycz/aggbug/204127.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/crycz/" target="_blank">blues</a> 2008-05-30 16:39 <a href="http://www.blogjava.net/crycz/articles/204127.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>(转载)Java的垃圾回收之算法</title><link>http://www.blogjava.net/crycz/articles/200337.html</link><dc:creator>blues</dc:creator><author>blues</author><pubDate>Wed, 14 May 2008 01:06:00 GMT</pubDate><guid>http://www.blogjava.net/crycz/articles/200337.html</guid><wfw:comment>http://www.blogjava.net/crycz/comments/200337.html</wfw:comment><comments>http://www.blogjava.net/crycz/articles/200337.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/crycz/comments/commentRss/200337.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/crycz/services/trackbacks/200337.html</trackback:ping><description><![CDATA[引言 <br />
<br />
原文出自<a href="http://www.blogjava.net/Jack2007/">Jack.Wang</a> <br />
<br />
　　Java的堆是一个运行时数据区，类的实例(对象)从中分配空间。Java虚拟机(JVM)的堆中储存着正在运行的应用程序所建立的所有对象，这些对象通过new、newarray、anewarray和multianewarray等指令建立，但是它们不需要程序代码来显式地释放。一般来说，堆的是由垃圾回收 来负责的，尽管JVM规范并不要求特殊的垃圾回收技术，甚至根本就不需要垃圾回收，但是由于内存的有限性，JVM在实现的时候都有一个由垃圾回收所管理的堆。垃圾回收是一种动态存储管理技术，它自动地释放不再被程序引用的对象，按照特定的垃圾收集算法来实现资源自动回收的功能。 <br />
<br />
<strong>　　垃圾收集的意义</strong> <br />
<br />
　　在C++中，对象所占的内存在程序结束运行之前一直被占用，在明确释放之前不能分配给其它对象；而在Java中，当没有对象引用指向原先分配给某个对象的内存时，该内存便成为垃圾。JVM的一个系统级线程会自动释放该内存块。垃圾收集意味着程序不再需要的对象是"无用信息"，这些信息将被丢弃。当一个对象不再被引用的时候，内存回收它占领的空间，以便空间被后来的新对象使用。事实上，除了释放没用的对象，垃圾收集也可以清除内存记录碎片。由于创建对象和垃圾收集器释放丢弃对象所占的内存空间，内存会出现碎片。碎片是分配给对象的内存块之间的空闲内存洞。碎片整理将所占用的堆内存移到堆的一端，JVM将整理出的内存分配给新的对象。 <br />
<br />
　　垃圾收集能自动释放内存空间，减轻编程的负担。这使Java 虚拟机具有一些优点。首先，它能使编程效率提高。在没有垃圾收集机制的时候，可能要花许多时间来解决一个难懂的存储器问题。在用Java语言编程的时候，靠垃圾收集机制可大大缩短时间。其次是它保护程序的完整性， 垃圾收集是Java语言安全性策略的一个重要部份。 <br />
<br />
　　垃圾收集的一个潜在的缺点是它的开销影响程序性能。Java虚拟机必须追踪运行程序中有用的对象， 而且最终释放没用的对象。这一个过程需要花费处理器的时间。其次垃圾收集算法的不完备性，早先采用的某些垃圾收集算法就不能保证100%收集到所有的废弃内存。当然随着垃圾收集算法的不断改进以及软硬件运行效率的不断提升，这些问题都可以迎刃而解。 <br />
<br />
　　<strong>垃圾收集的算法分析</strong> <br />
<br />
　　Java语言规范没有明确地说明JVM使用哪种垃圾回收算法，但是任何一种垃圾收集算法一般要做2件基本的事情：（1）发现无用信息对象；（2）回收被无用对象占用的内存空间，使该空间可被程序再次使用。 <br />
<br />
　　大多数垃圾回收算法使用了根集(root set)这个概念；所谓根集就量正在执行的Java程序可以访问的引用变量的集合(包括局部变量、参数、类变量)，程序可以使用引用变量访问对象的属性和调用对象的方法。垃圾收集首选需要确定从根开始哪些是可达的和哪些是不可达的，从根集可达的对象都是活动对象，它们不能作为垃圾被回收，这也包括从根集间接可达的对象。而根集通过任意路径不可达的对象符合垃圾收集的条件，应该被回收。下面介绍几个常用的算法。 <br />
<br />
　<strong>　1、 引用计数法(Reference Counting Collector) <br />
</strong><br />
　　引用计数法是唯一没有使用根集的垃圾回收的法，该算法使用引用计数器来区分存活对象和不再使用的对象。一般来说，堆中的每个对象对应一个引用计数器。当每一次创建一个对象并赋给一个变量时，引用计数器置为1。当对象被赋给任意变量时，引用计数器每次加1当对象出了作用域后(该对象丢弃不再使用)，引用计数器减1，一旦引用计数器为0，对象就满足了垃圾收集的条件。 <br />
<br />
　　基于引用计数器的垃圾收集器运行较快，不会长时间中断程序执行，适宜地必须 实时运行的程序。但引用计数器增加了程序执行的开销，因为每次对象赋给新的变量，计数器加1，而每次现有对象出了作用域生，计数器减1。 <br />
<br />
<strong>　　2、tracing算法(Tracing Collector) <br />
</strong><br />
　　tracing算法是为了解决引用计数法的问题而提出，它使用了根集的概念。基于tracing算法的垃圾收集器从根集开始扫描，识别出哪些对象可达，哪些对象不可达，并用某种方式标记可达对象，例如对每个可达对象设置一个或多个位。在扫描识别过程中，基于tracing算法的垃圾收集也称为标记和清除(mark-and-sweep)垃圾收集器. <br />
<br />
　　<strong>3、compacting算法(Compacting Collector) <br />
</strong><br />
　　为了解决堆碎片问题，基于tracing的垃圾回收吸收了Compacting算法的思想，在清除的过程中，算法将所有的对象移到堆的一端，堆的另一端就变成了一个相邻的空闲内存区，收集器会对它移动的所有对象的所有引用进行更新，使得这些引用在新的位置能识别原来 的对象。在基于Compacting算法的收集器的实现中，一般增加句柄和句柄表。<br />
<strong>4、copying算法(Coping Collector) <br />
</strong><br />
　　 该算法的提出是为了克服句柄的开销和解决堆碎片的垃圾回收。它开始时把堆分成 一个对象 面和多个空闲面， 程序从对象面为对象分配空间，当对象满了，基于coping算法的垃圾 收集就从根集中扫描活动对象，并将每个 活动对象复制到空闲面(使得活动对象所占的内存之间没有空闲洞)，这样空闲面变成了对象面，原来的对象面变成了空闲面，程序会在新的对象面中分配内存。 <br />
<br />
　　 一种典型的基于coping算法的垃圾回收是stop-and-copy算法，它将堆分成对象面和空闲区域面，在对象面与空闲区域面的切换过程中，程序暂停执行。 <br />
<br />
　　 <strong>5、generation算法(Generational Collector) <br />
</strong><br />
　　 stop-and-copy垃圾收集器的一个缺陷是收集器必须复制所有的活动对象，这增加了程序等待时间，这是coping算法低效的原因。在程序设计中有这样的规律：多数对象存在的时间比较短，少数的存在时间比较长。因此，generation算法将堆分成两个或多个，每个子堆作为对象的一代(generation)。由于多数对象存在的时间比较短，随着程序丢弃不使用的对象，垃圾收集器将从最年轻的子堆中收集这些对象。在分代式的垃圾收集器运行后，上次运行存活下来的对象移到下一最高代的子堆中，由于老一代的子堆不会经常被回收，因而节省了时间。 <br />
<br />
　　<strong> 6、adaptive算法(Adaptive Collector)</strong> <br />
<br />
　　 在特定的情况下，一些垃圾收集算法会优于其它算法。基于Adaptive算法的垃圾收集器就是监控当前堆的使用情况，并将选择适当算法的垃圾收集器。<br />
透视Java垃圾回收<br />
<br />
　　1、命令行参数透视垃圾收集器的运行<br />
<br />
　　2、使用System.gc()可以不管JVM使用的是哪一种垃圾回收的算法，都可以请求Java的垃圾回收。在命令行中有一个参数-verbosegc可以查看Java使用的堆内存的情况，它的格式如下：<br />
<br />
java -verbosegc classfile<br />
<br />
　　可以看个例子：<br />
<br />
<code>class TestGC <br />
{<br />
　public static void main(String[] args) <br />
　{<br />
　　new TestGC();<br />
　　System.gc();<br />
　　System.runFinalization();<br />
　}<br />
} <br />
</code><br />
　　在这个例子中，一个新的对象被创建，由于它没有使用，所以该对象迅速地变为可达，程序编译后，执行命令： java -verbosegc TestGC 后结果为：<br />
<br />
[Full GC 168K-&gt;97K(1984K)， 0.0253873 secs]<br />
<br />
　　机器的环境为，Windows 2000 + JDK1.3.1，箭头前后的数据168K和97K分别表示垃圾收集GC前后所有存活对象使用的内存容量，说明有168K-97K=71K的对象容量被回收，括号内的数据1984K为堆内存的总容量，收集所需要的时间是0.0253873秒（这个时间在每次执行的时候会有所不同）。<br />
<br />
　　2、finalize方法透视垃圾收集器的运行<br />
<br />
　　在JVM垃圾收集器收集一个对象之前 ，一般要求程序调用适当的方法释放资源，但在没有明确释放资源的情况下，Java提供了缺省机制来终止化该对象心释放资源，这个方法就是finalize（）。它的原型为：<br />
<br />
<code>protected void finalize() throws Throwable <br />
</code><br />
　　在finalize()方法返回之后，对象消失，垃圾收集开始执行。原型中的throws Throwable表示它可以抛出任何类型的异常。<br />
<br />
　　之所以要使用finalize()，是由于有时需要采取与Java的普通方法不同的一种方法，通过分配内存来做一些具有C风格的事情。这主要可以通过"固有方法"来进行，它是从Java里调用非Java方法的一种方式。C和C++是目前唯一获得固有方法支持的语言。但由于它们能调用通过其他语言编写的子程序，所以能够有效地调用任何东西。在非Java代码内部，也许能调用C的malloc()系列函数，用它分配存储空间。而且除非调用了free()，否则存储空间不会得到释放，从而造成内存"漏洞"的出现。当然，free()是一个C和C++函数，所以我们需要在finalize()内部的一个固有方法中调用它。也就是说我们不能过多地使用finalize()，它并不是进行普通清除工作的理想场所。<br />
<br />
　　在普通的清除工作中，为清除一个对象，那个对象的用户必须在希望进行清除的地点调用一个清除方法。这与C++"破坏器"的概念稍有抵触。在C++中，所有对象都会破坏（清除）。或者换句话说，所有对象都"应该"破坏。若将C++对象创建成一个本地对象，比如在堆栈中创建（在Java中是不可能的），那么清除或破坏工作就会在"结束花括号"所代表的、创建这个对象的作用域的末尾进行。若对象是用new创建的（类似于Java），那么当程序员调用C++的delete命令时（Java没有这个命令），就会调用相应的破坏器。若程序员忘记了，那么永远不会调用破坏器，我们最终得到的将是一个内存"漏洞"，另外还包括对象的其他部分永远不会得到清除。<br />
<br />
　　相反，Java不允许我们创建本地（局部）对象--无论如何都要使用new。但在Java中，没有"delete"命令来释放对象，因为垃圾收集器会帮助我们自动释放存储空间。所以如果站在比较简化的立场，我们可以说正是由于存在垃圾收集机制，所以Java没有破坏器。然而，随着以后学习的深入，就会知道垃圾收集器的存在并不能完全消除对破坏器的需要，或者说不能消除对破坏器代表的那种机制的需要（而且绝对不能直接调用finalize()，所以应尽量避免用它）。若希望执行除释放存储空间之外的其他某种形式的清除工作，仍然必须调用Java中的一个方法。它等价于C++的破坏器，只是没后者方便。<br />
<br />
　　下面这个例子向大家展示了垃圾收集所经历的过程，并对前面的陈述进行了总结。<br />
<br />
<code>class Chair {<br />
　static boolean gcrun = false;<br />
　static boolean f = false;<br />
　static int created = 0;<br />
　static int finalized = 0;<br />
　int i;<br />
　Chair() {<br />
　　i = ++created;<br />
　　if(created == 47) <br />
　　　System.out.println("Created 47");<br />
　}<br />
　protected void finalize() {<br />
　　if(!gcrun) {<br />
　　　gcrun = true;<br />
　　　System.out.println("Beginning to finalize after " + created + " Chairs have been created");<br />
　　}<br />
　　if(i == 47) {<br />
　　　System.out.println("Finalizing Chair #47， " +"Setting flag to stop Chair creation");<br />
　　　f = true;<br />
　　}<br />
　　finalized++;<br />
　　if(finalized &gt;= created)<br />
　　　System.out.println("All " + finalized + " finalized");<br />
　}<br />
}<br />
<br />
public class Garbage {<br />
　public static void main(String[] args) {<br />
　　if(args.length == 0) {<br />
　　　System.err.println("Usage: \n" + "java Garbage before\n or:\n" + "java Garbage after");<br />
　　　return;<br />
　　}<br />
　　while(!Chair.f) {<br />
　　　new Chair();<br />
　　　new String("To take up space");<br />
　　}<br />
　　System.out.println("After all Chairs have been created:\n" + "total created = " + Chair.created +<br />
"， total finalized = " + Chair.finalized);<br />
　　if(args[0].equals("before")) {<br />
　　　　System.out.println("gc():");<br />
　　　　System.gc();<br />
　　　　System.out.println("runFinalization():");<br />
　　　　System.runFinalization();<br />
　　}<br />
　　System.out.println("bye!");<br />
　　if(args[0].equals("after"))<br />
　　　System.runFinalizersOnExit(true);<br />
　}<br />
} <br />
</code><br />
　　上面这个程序创建了许多Chair对象，而且在垃圾收集器开始运行后的某些时候，程序会停止创建Chair。由于垃圾收集器可能在任何时间运行，所以我们不能准确知道它在何时启动。因此，程序用一个名为gcrun的标记来指出垃圾收集器是否已经开始运行。利用第二个标记f，Chair可告诉main()它应停止对象的生成。这两个标记都是在finalize()内部设置的，它调用于垃圾收集期间。另两个static变量--created以及finalized--分别用于跟踪已创建的对象数量以及垃圾收集器已进行完收尾工作的对象数量。最后，每个Chair都有它自己的（非static）int i，所以能跟踪了解它具体的编号是多少。编号为47的Chair进行完收尾工作后，标记会设为true，最终结束Chair对象的创建过程。<br />
<br />
关于垃圾收集的几点补充 <br />
<br />
　　经过上述的说明，可以发现垃圾回收有以下的几个特点： <br />
<br />
　　（1）垃圾收集发生的不可预知性：由于实现了不同的垃圾收集算法和采用了不同的收集机制，所以它有可能是定时发生，有可能是当出现系统空闲CPU资源时发生，也有可能是和原始的垃圾收集一样，等到内存消耗出现极限时发生，这与垃圾收集器的选择和具体的设置都有关系。 <br />
<br />
　　（2）垃圾收集的精确性：主要包括2 个方面：（a）垃圾收集器能够精确标记活着的对象；（b）垃圾收集器能够精确地定位对象之间的引用关系。前者是完全地回收所有废弃对象的前提，否则就可能造成内存泄漏。而后者则是实现归并和复制等算法的必要条件。所有不可达对象都能够可靠地得到回收，所有对象都能够重新分配，允许对象的复制和对象内存的缩并，这样就有效地防止内存的支离破碎。 <br />
<br />
　　（3）现在有许多种不同的垃圾收集器，每种有其算法且其表现各异，既有当垃圾收集开始时就停止应用程序的运行，又有当垃圾收集开始时也允许应用程序的线程运行，还有在同一时间垃圾收集多线程运行。 <br />
<br />
　　（4）垃圾收集的实现和具体的JVM 以及JVM的内存模型有非常紧密的关系。不同的JVM 可能采用不同的垃圾收集，而JVM 的内存模型决定着该JVM可以采用哪些类型垃圾收集。现在，HotSpot 系列JVM中的内存系统都采用先进的面向对象的框架设计，这使得该系列JVM都可以采用最先进的垃圾收集。 <br />
<br />
　　（5）随着技术的发展，现代垃圾收集技术提供许多可选的垃圾收集器，而且在配置每种收集器的时候又可以设置不同的参数，这就使得根据不同的应用环境获得最优的应用性能成为可能。 <br />
<br />
　<strong>　针对以上特点，我们在使用的时候要注意：</strong> <br />
<br />
　　（1）不要试图去假定垃圾收集发生的时间，这一切都是未知的。比如，方法中的一个临时对象在方法调用完毕后就变成了无用对象，这个时候它的内存就可以被释放。 <br />
<br />
　　（2）Java中提供了一些和垃圾收集打交道的类，而且提供了一种强行执行垃圾收集的方法--调用System.gc()，但这同样是个不确定的方法。Java 中并不保证每次调用该方法就一定能够启动垃圾收集，它只不过会向JVM发出这样一个申请，到底是否真正执行垃圾收集，一切都是个未知数。 <br />
<br />
　　（3）挑选适合自己的垃圾收集器。一般来说，如果系统没有特殊和苛刻的性能要求，可以采用JVM的缺省选项。否则可以考虑使用有针对性的垃圾收集器，比如增量收集器就比较适合实时性要求较高的系统之中。系统具有较高的配置，有比较多的闲置资源，可以考虑使用并行标记/清除收集器。 <br />
<br />
　　（4）关键的也是难把握的问题是内存泄漏。良好的编程习惯和严谨的编程态度永远是最重要的，不要让自己的一个小错误导致内存出现大漏洞。 <br />
<br />
　　（5）尽早释放无用对象的引用。大多数程序员在使用临时变量的时候，都是让引用变量在退出活动域(scope)后，自动设置为null，暗示垃圾收集器来收集该对象，还必须注意该引用的对象是否被监听，如果有，则要去掉监听器，然后再赋空值。 <br />
<br />
<strong>　　结束语</strong> <br />
<br />
　　一般来说，Java开发人员可以不重视JVM中堆内存的分配和垃圾处理收集，但是，充分理解Java的这一特性可以让我们更有效地利用资源。同时要注意finalize()方法是Java的缺省机制，有时为确保对象资源的明确释放，可以编写自己的finalize方法。 <br />
<img src ="http://www.blogjava.net/crycz/aggbug/200337.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/crycz/" target="_blank">blues</a> 2008-05-14 09:06 <a href="http://www.blogjava.net/crycz/articles/200337.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Java路径问题最终解决方案</title><link>http://www.blogjava.net/crycz/articles/95545.html</link><dc:creator>blues</dc:creator><author>blues</author><pubDate>Tue, 23 Jan 2007 07:02:00 GMT</pubDate><guid>http://www.blogjava.net/crycz/articles/95545.html</guid><wfw:comment>http://www.blogjava.net/crycz/comments/95545.html</wfw:comment><comments>http://www.blogjava.net/crycz/articles/95545.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/crycz/comments/commentRss/95545.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/crycz/services/trackbacks/95545.html</trackback:ping><description><![CDATA[		<p>Java路径问题最终解决方案<br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;可定位所有资源的相对路径寻址<br />&#160;<br />&#160;<br />&#160;<br />前言<br />Java的路径问题，非常难搞。最近的工作涉及到创建和读取文件的工作，这里我就给大家彻底得解决Java路径问题。<br />我编写了一个方法，比ClassLoader.getResource(String 相对路径)方法的能力更强。它可以接受&#8220;../&#8221;这样的参数，允许我们用</p>
		<p>相对路径来定位classpath外面的资源。这样，我们就可以使用相对于 classpath的路径，定位所有位置的资源！<br />&#160;<br />Java路径<br />Java中使用的路径，分为两种：绝对路径和相对路径。具体而言，又分为四种：<br />一、URI形式的绝对资源路径<br />如：file:/D:/java/eclipse32/workspace/jbpmtest3/bin/aaa.b<br />URL是URI的特例。URL的前缀/协议，必须是Java认识的。URL可以打开资源，而URI则不行。<br />URL和URI对象可以互相转换，使用各自的toURI(),toURL()方法即可！<br />&#160;<br />二、本地系统的绝对路径<br />D:/java/eclipse32/workspace/jbpmtest3/bin/aaa.b<br />Java.io包中的类，需要使用这种形式的参数。<br />但是，它们一般也提供了URI类型的参数，而URI类型的参数，接受的是URI样式的String。因此，通过URI转换，还是可以把URI样式</p>
		<p>的绝对路径用在java.io包中的类中。<br />&#160;<br />三、相对于classpath的相对路径<br />如：相对于<br />file:/D:/java/eclipse32/workspace/jbpmtest3/bin/这个路径的相对路径。其中，bin是本项目的classpath。所有的Java源文件编</p>
		<p>译后的.class文件复制到这个目录中。<br />&#160;<br />&#160;<br />四、相对于当前用户目录的相对路径<br />就是相对于System.getProperty("user.dir")返回的路径。<br />对于一般项目，这是项目的根路径。对于JavaEE服务器，这可能是服务器的某个路径。这个并没有统一的规范！<br />所以，绝对不要使用&#8220;相对于当前用户目录的相对路径&#8221;。然而：<br />默认情况下，java.io 包中的类总是根据当前用户目录来分析相对路径名。此目录由系统属性 user.dir 指定，通常是 Java 虚拟机</p>
		<p>的调用目录。<br />这就是说，在使用java.io包中的类时，最好不要使用相对路径。否则，虽然在J2SE应用程序中可能还算正常，但是到了J2EE程序中</p>
		<p>，一定会出问题！而且这个路径，在不同的服务器中都是不同的！<br />&#160;<br />相对路径最佳实践<br />推荐使用相对于当前classpath的相对路径<br />因此，我们在使用相对路径时，应当使用相对于当前classpath的相对路径。<br />ClassLoader类的getResource(String name),getResourceAsStream(String name)等方法，使用相对于当前项目的classpath的相对</p>
		<p>路径来查找资源。<br />读取属性文件常用到的ResourceBundle类的getBundle(String path)也是如此。<br />通过查看ClassLoader类及其相关类的源代码，我发现，它实际上还是使用了 URI形式的绝对路径。通过得到当前classpath的URI形</p>
		<p>式的绝对路径，构建了相对路径的URI形式的绝对路径。（这个实际上是猜想，因为JDK 内部调用了SUN的源代码，而这些代码不属于</p>
		<p>JDK，不是开源的。）<br />&#160;<br />相对路径本质上还是绝对路径<br />因此，归根结底，Java本质上只能使用绝对路径来寻找资源。所有的相对路径寻找资源的方法，都不过是一些便利方法。不过是API</p>
		<p>在底层帮助我们构建了绝对路径，从而找到资源的！<br />&#160;<br />得到classpath和当前类的绝对路径的一些方法<br />&#160;&#160;&#160; 下面是一些得到classpath和当前类的绝对路径的一些方法。你可能需要使用其中的一些方法来得到你需要的资源的绝对路径。<br />&#160;<br />&#160;//1.得到的是当前类ClassLoaderUtil.class文件的URI目录。不包括自己！<br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; //如：file:/D:/java/eclipse32/workspace/jbpmtest3/bin/com/test/<br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; System.out.print("1:");<br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; System.out.println(ClassLoaderUtil.class.getResource(""));<br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; //2.得到的是当前的classpath的绝对URI路径。<br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; //如：file:/D:/java/eclipse32/workspace/jbpmtest3/bin/<br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; System.out.print("2:");<br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; System.out.println(ClassLoaderUtil.class.getResource("/"));<br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; //3.得到的也是当前ClassPath的绝对URI路径。<br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; //如：file:/D:/java/eclipse32/workspace/jbpmtest3/bin/<br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; System.out.print("3:");<br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; System.out.println(Thread.currentThread().getContextClassLoader().getResource(""));<br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; //4.得到的也是当前ClassPath的绝对URI路径。<br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; //如：file:/D:/java/eclipse32/workspace/jbpmtest3/bin/<br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; System.out.print("4:");<br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; System.out.println(ClassLoaderUtil.class.getClassLoader().getResource(""));<br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; //5.得到的也是当前ClassPath的绝对URI路径。<br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; System.out.print("5:");<br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; System.out.println(ClassLoader.getSystemResource(""));</p>
		<p>&#160;//6.得到的也是当前ClassPath的绝对URI路径。<br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; System.out.print("6:");<br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; System.out.println(Thread.currentThread().getContextClassLoader().getResource(""));<br />&#160;&#160; <br />我推荐使用Thread.currentThread().getContextClassLoader().getResource("")来得到当前的classpath的绝对路径的URI表示法。<br />&#160;<br />Web应用程序中资源的寻址<br />&#160;&#160;&#160; 上文中说过，当前用户目录，即相对于System.getProperty("user.dir")返回的路径。<br />对于JavaEE服务器，这可能是服务器的某个路径，这个并没有统一的规范！<br />而不是我们发布的Web应用程序的根目录！<br />这样，在Web应用程序中，我们绝对不能使用相对于当前用户目录的相对路径。<br />在Web应用程序中，我们一般通过ServletContext.getRealPath("/")方法得到Web应用程序的根目录的绝对路径。<br />这样，我们只需要提供相对于Web应用程序根目录的路径，就可以构建出定位资源的绝对路径。<br />这是我们开发Web应用程序时一般所采取的策略。<br />&#160;<br />通用的相对路径解决办法<br />Java中各种相对路径非常多，不容易使用，非常容易出错。因此，我编写了一个便利方法，帮助更容易的解决相对路径问题。<br />&#160;<br />Web应用程序中使用JavaSE运行的资源寻址问题<br />在JavaSE程序中，我们一般使用classpath来作为存放资源的目的地。但是，在Web应用程序中，我们一般使用classpath外面的WEB-</p>
		<p>INF及其子目录作为资源文件的存放地。<br />在Web应用程序中，我们一般通过ServletContext.getRealPath("/")方法得到Web应用程序的根目录的绝对路径。这样，我们只需要</p>
		<p>提供相对于Web应用程序根目录的路径，就可以构建出定位资源的绝对路径。<br />Web应用程序，可以作为Web应用程序进行发布和运行。但是，我们也常常会以JavaSE的方式来运行Web应用程序的某个类的main方法</p>
		<p>。或者，使用JUnit测试。这都需要使用JavaSE的方式来运行。<br />这样，我们就无法使用ServletContext.getRealPath("/")方法得到Web应用程序的根目录的绝对路径。<br />而JDK提供的ClassLoader类，<br />它的getResource(String name),getResourceAsStream(String name)等方法，使用相对于当前项目的classpath的相对路径来查找资</p>
		<p>源。<br />读取属性文件常用到的ResourceBundle类的getBundle(String path)也是如此。<br />它们都只能使用相对路径来读取classpath下的资源，无法定位到classpath外面的资源。<br />Classpath外配置文件读取问题<br />如，我们使用测试驱动开发的方法，开发Spring、Hibernate、iBatis等使用配置文件的Web应用程序，就会遇到问题。<br />尽管Spring自己提供了FileSystem（也就是相对于user,dir目录）来读取Web配置文件的方法，但是终究不是很方便。而且与Web程序</p>
		<p>中的代码使用方式不一致！<br />至于Hibernate，iBatis就更麻烦了！只有把配置文件移到classpath下，否则根本不可能使用测试驱动开发！<br />&#160;<br />&#160;&#160;&#160; 这怎么办？<br />&#160;<br />通用的相对路径解决办法<br />面对这个问题，我决定编写一个助手类ClassLoaderUtil，提供一个便利方法[public static URL getExtendResource(String </p>
		<p>relativePath)]。在Web应用程序等一切Java程序中，需要定位classpath外的资源时，都使用这个助手类的便利方法，而不使用 Web</p>
		<p>应用程序特有的ServletContext.getRealPath("/")方法来定位资源。<br />&#160;<br />利用classpath的绝对路径，定位所有资源<br />这个便利方法的实现原理，就是&#8220;利用classpath的绝对路径，定位所有资源&#8221;。<br />ClassLoader类的getResource("")方法能够得到当前classpath的绝对路径，这是所有Java程序都拥有的能力，具有最大的适应性！<br />而目前的JDK提供的ClassLoader类的getResource(String 相对路径)方法，只能接受一般的相对路径。这样，使用ClassLoader类的</p>
		<p>getResource(String 相对路径)方法就只能定位到classpath下的资源。<br />如果，它能够接受&#8220;../&#8221;这样的参数，允许我们用相对路径来定位classpath外面的资源，那么我们就可以定位位置的资源！<br />当然，我无法修改ClassLoader类的这个方法，于是，我编写了一个助手类ClassLoaderUtil类，提供了[public static URL </p>
		<p>getExtendResource(String relativePath)]这个方法。它能够接受带有&#8220;../&#8221;符号的相对路径，实现了自由寻找资源的功能。<br />&#160;<br />通过相对classpath路径实现自由寻找资源的助手类的源代码：(转载,有改动)</p>
		<p>package test.io;</p>
		<p>import java.io.IOException;<br />import java.io.InputStream;<br />import java.net.MalformedURLException;<br />import java.net.URL;<br />import java.util.Properties;</p>
		<p>import org.apache.commons.logging.Log;<br />import org.apache.commons.logging.LogFactory;</p>
		<p>/**<br />&#160;* @author沈东良<a href="mailto:shendl_s@hotmail.com">shendl_s@hotmail.com</a> Nov29,2006 10:34:34AM&lt;br/&gt;<br />&#160;* 用来加载类，classpath下的资源文件，属性文件等。&lt;br/&gt;<br />&#160;* getExtendResource(StringrelativePath)方法，&lt;br/&gt;<br />&#160;* 可以使用../符号来加载classpath外部的资源。<br />&#160;*/<br />public class ClassLoaderUtil<br />{<br />&#160;&#160;&#160; private static Log log = LogFactory.getLog(ClassLoaderUtil.class);</p>
		<p>&#160;&#160;&#160; /**<br />&#160;&#160;&#160;&#160; * Thread.currentThread().getContextClassLoader().getResource("")<br />&#160;&#160;&#160;&#160; */</p>
		<p>&#160;&#160;&#160; /**<br />&#160;&#160;&#160;&#160; * 加载Java类。 使用全限定类名<br />&#160;&#160;&#160;&#160; * <br />&#160;&#160;&#160;&#160; * @param className<br />&#160;&#160;&#160;&#160; * @return<br />&#160;&#160;&#160;&#160; */<br />&#160;&#160;&#160; public static Class loadClass(String className)<br />&#160;&#160;&#160; {<br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; try<br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; {<br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; return getClassLoader().loadClass(className);<br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; }<br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; catch (ClassNotFoundException e)<br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; {<br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; throw new RuntimeException("class not found '" + className + "'", e);<br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; }<br />&#160;&#160;&#160; }</p>
		<p>&#160;&#160;&#160; /**<br />&#160;&#160;&#160;&#160; * 得到类加载器<br />&#160;&#160;&#160;&#160; * <br />&#160;&#160;&#160;&#160; * @return<br />&#160;&#160;&#160;&#160; */<br />&#160;&#160;&#160; public static ClassLoader getClassLoader()<br />&#160;&#160;&#160; {</p>
		<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160; return ClassLoaderUtil.class.getClassLoader();<br />&#160;&#160;&#160; }</p>
		<p>&#160;&#160;&#160; /**<br />&#160;&#160;&#160;&#160; * 提供相对于classpath的资源路径，返回文件的输入流<br />&#160;&#160;&#160;&#160; * <br />&#160;&#160;&#160;&#160; * @param<br />&#160;&#160;&#160;&#160; * relativePath必须传递资源的相对路径。是相对于classpath的路径。如果需要查找classpath外部的资源，需要使用../来查</p>
		<p>找<br />&#160;&#160;&#160;&#160; * @return 文件输入流<br />&#160;&#160;&#160;&#160; * @throws IOException<br />&#160;&#160;&#160;&#160; * @throws MalformedURLException<br />&#160;&#160;&#160;&#160; */<br />&#160;&#160;&#160; public static InputStream getStream(String relativePath)<br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; throws MalformedURLException, IOException<br />&#160;&#160;&#160; {<br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; if(!relativePath.contains("../"))<br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; {<br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; return getClassLoader().getResourceAsStream(relativePath);</p>
		<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160; }<br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; else<br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; {<br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; return ClassLoaderUtil.getStreamByExtendResource(relativePath);<br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; }</p>
		<p>&#160;&#160;&#160; }</p>
		<p>&#160;&#160;&#160; /**<br />&#160;&#160;&#160;&#160; * <br />&#160;&#160;&#160;&#160; * @param url<br />&#160;&#160;&#160;&#160; * @return<br />&#160;&#160;&#160;&#160; * @throws IOException<br />&#160;&#160;&#160;&#160; */<br />&#160;&#160;&#160; public static InputStream getStream(URL url) throws IOException<br />&#160;&#160;&#160; {<br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; if(url != null)<br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; {</p>
		<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; return url.openStream();</p>
		<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160; }<br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; else<br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; {<br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; return null;<br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; }<br />&#160;&#160;&#160; }</p>
		<p>&#160;&#160;&#160; /**<br />&#160;&#160;&#160;&#160; * <br />&#160;&#160;&#160;&#160; * @param relativePath必须传递资源的相对路径。是相对于classpath的路径。如果需要查找classpath外部的资源，需要使用</p>
		<p>../来查找<br />&#160;&#160;&#160;&#160; * @return<br />&#160;&#160;&#160;&#160; * @throws MalformedURLException<br />&#160;&#160;&#160;&#160; * @throws IOException<br />&#160;&#160;&#160;&#160; */<br />&#160;&#160;&#160; public static InputStream getStreamByExtendResource(String relativePath)<br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; throws MalformedURLException, IOException<br />&#160;&#160;&#160; {<br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; return ClassLoaderUtil.getStream(ClassLoaderUtil<br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; .getExtendResource(relativePath));</p>
		<p>&#160;&#160;&#160; }</p>
		<p>&#160;&#160;&#160; /**<br />&#160;&#160;&#160;&#160; * 提供相对于classpath的资源路径，返回属性对象，它是一个散列表<br />&#160;&#160;&#160;&#160; * <br />&#160;&#160;&#160;&#160; * @param resource<br />&#160;&#160;&#160;&#160; * @return<br />&#160;&#160;&#160;&#160; */<br />&#160;&#160;&#160; public static Properties getProperties(String resource)<br />&#160;&#160;&#160; {<br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; Properties properties = new Properties();<br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; try<br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; {<br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; properties.load(getStream(resource));<br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; }<br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; catch (IOException e)<br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; {<br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; throw new RuntimeException("couldn't load properties file '"<br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; + resource + "'", e);<br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; }<br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; return properties;<br />&#160;&#160;&#160; }</p>
		<p>&#160;&#160;&#160; /**<br />&#160;&#160;&#160;&#160; * 得到本Class所在的ClassLoader的Classpat的绝对路径。 URL形式的<br />&#160;&#160;&#160;&#160; * <br />&#160;&#160;&#160;&#160; * @return<br />&#160;&#160;&#160;&#160; */<br />&#160;&#160;&#160; public static String getAbsolutePathOfClassLoaderClassPath()<br />&#160;&#160;&#160; {</p>
		<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160; ClassLoaderUtil.log.info(ClassLoaderUtil.getClassLoader().getResource(<br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; "").toString());<br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; return ClassLoaderUtil.getClassLoader().getResource("").toString();</p>
		<p>&#160;&#160;&#160; }</p>
		<p>&#160;&#160;&#160; /**<br />&#160;&#160;&#160;&#160; * <br />&#160;&#160;&#160;&#160; * @param relativePath 必须传递资源的相对路径。是相对于classpath的路径。如果需要查找classpath外部的资源，需要使</p>
		<p>用../来查找<br />&#160;&#160;&#160;&#160; * @return资源的绝对URL<br />&#160;&#160;&#160;&#160; * @throws MalformedURLException<br />&#160;&#160;&#160;&#160; */<br />&#160;&#160;&#160; public static URL getExtendResource(String relativePath)<br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; throws MalformedURLException<br />&#160;&#160;&#160; {</p>
		<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160; ClassLoaderUtil.log.info("传入的相对路径：" + relativePath);<br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; // ClassLoaderUtil.log.info(Integer.valueOf(relativePath.indexOf("../")))<br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; // ;<br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; if(!relativePath.contains("../"))<br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; {<br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; return ClassLoaderUtil.getResource(relativePath);</p>
		<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160; }<br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; String classPathAbsolutePath = ClassLoaderUtil<br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; .getAbsolutePathOfClassLoaderClassPath();<br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; if(relativePath.substring(0, 1).equals("/"))<br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; {<br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; relativePath = relativePath.substring(1);<br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; }<br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; ClassLoaderUtil.log.info(Integer.valueOf(relativePath<br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; .lastIndexOf("../")));</p>
		<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160; String wildcardString = relativePath.substring(0, relativePath<br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; .lastIndexOf("../") + 3);<br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; relativePath = relativePath<br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; .substring(relativePath.lastIndexOf("../") + 3);<br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; int containSum = ClassLoaderUtil.containSum(wildcardString, "../");<br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; classPathAbsolutePath = ClassLoaderUtil.cutLastString(<br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; classPathAbsolutePath, "/", containSum);<br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; String resourceAbsolutePath = classPathAbsolutePath + relativePath;<br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; ClassLoaderUtil.log.info("绝对路径：" + resourceAbsolutePath);<br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; URL resourceAbsoluteURL = new URL(resourceAbsolutePath);<br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; return resourceAbsoluteURL;<br />&#160;&#160;&#160; }</p>
		<p>&#160;&#160;&#160; /**<br />&#160;&#160;&#160;&#160; * <br />&#160;&#160;&#160;&#160; * @param source<br />&#160;&#160;&#160;&#160; * @param dest<br />&#160;&#160;&#160;&#160; * @return<br />&#160;&#160;&#160;&#160; */<br />&#160;&#160;&#160; private static int containSum(String source, String dest)<br />&#160;&#160;&#160; {<br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; int containSum = 0;<br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; int destLength = dest.length();<br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; while (source.contains(dest))<br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; {<br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; containSum = containSum + 1;<br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; source = source.substring(destLength);</p>
		<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160; }</p>
		<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160; return containSum;<br />&#160;&#160;&#160; }</p>
		<p>&#160;&#160;&#160; /**<br />&#160;&#160;&#160;&#160; * <br />&#160;&#160;&#160;&#160; * @param source<br />&#160;&#160;&#160;&#160; * @param dest<br />&#160;&#160;&#160;&#160; * @param num<br />&#160;&#160;&#160;&#160; * @return<br />&#160;&#160;&#160;&#160; */<br />&#160;&#160;&#160; private static String cutLastString(String source, String dest, int num)<br />&#160;&#160;&#160; {<br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; // String cutSource=null;<br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; for(int i = 0; i &lt; num; i++)<br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; {<br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; source = source.substring(0, source.lastIndexOf(dest, source<br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; .length() - 2) + 1);</p>
		<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160; }</p>
		<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160; return source;<br />&#160;&#160;&#160; }</p>
		<p>&#160;&#160;&#160; /**<br />&#160;&#160;&#160;&#160; * <br />&#160;&#160;&#160;&#160; * @param resource<br />&#160;&#160;&#160;&#160; * @return<br />&#160;&#160;&#160;&#160; */<br />&#160;&#160;&#160; public static URL getResource(String resource)<br />&#160;&#160;&#160; {<br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; ClassLoaderUtil.log.info("传入的相对于classpath的路径：" + resource);<br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; return ClassLoaderUtil.getClassLoader().getResource(resource);<br />&#160;&#160;&#160; }</p>
		<p>&#160;&#160;&#160; /**<br />&#160;&#160;&#160;&#160; * @param args<br />&#160;&#160;&#160;&#160; * @throws MalformedURLException<br />&#160;&#160;&#160;&#160; */<br />&#160;&#160;&#160; public static void main(String[] args) throws MalformedURLException<br />&#160;&#160;&#160; {</p>
		<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160; // ClassLoaderUtil.getExtendResource("../spring/dao.xml");<br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; // ClassLoaderUtil.getExtendResource("../../../src/log4j.properties");<br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; //ClassLoaderUtil.getExtendResource("log4j.properties");</p>
		<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160; System.out.println(ClassLoaderUtil.getClassLoader().getResource(<br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; "resource/font/SIMYOU.TTF").toString());<br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; //<br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; //1.得到的是当前类ClassLoaderUtil.class文件的URI目录。不包括自己！<br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; //如：file:/D:/java/eclipse32/workspace/jbpmtest3/bin/com/test/<br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; System.out.print("1:");<br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; System.out.println(ClassLoaderUtil.class.getResource(""));<br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; //2.得到的是当前的classpath的绝对URI路径。<br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; //如：file:/D:/java/eclipse32/workspace/jbpmtest3/bin/<br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; System.out.print("2:");<br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; System.out.println(ClassLoaderUtil.class.getResource("/"));<br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; //3.得到的也是当前ClassPath的绝对URI路径。<br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; //如：file:/D:/java/eclipse32/workspace/jbpmtest3/bin/<br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; System.out.print("3:");<br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; System.out.println(Thread.currentThread().getContextClassLoader().getResource(""));<br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; //4.得到的也是当前ClassPath的绝对URI路径。<br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; //如：file:/D:/java/eclipse32/workspace/jbpmtest3/bin/<br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; System.out.print("4:");<br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; System.out.println(ClassLoaderUtil.class.getClassLoader().getResource(""));<br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; //5.得到的也是当前ClassPath的绝对URI路径。<br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; System.out.print("5:");<br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; System.out.println(ClassLoader.getSystemResource(""));</p>
		<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160; //6.得到的也是当前ClassPath的绝对URI路径。<br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; System.out.print("6:");<br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; System.out.println(Thread.currentThread().getContextClassLoader().getResource(""));<br />&#160;&#160;&#160; }</p>
		<p>}<br />&#160;<br />后记<br />ClassLoaderUtil类的public static URL getExtendResource(String relativePath)，虽然很简单，但是确实可以解决大问题。<br />不过这个方法还是比较简陋的。我还想在未来有空时，进一步增强它的能力。比如，增加Ant风格的匹配符。用**代表多个目录，*代</p>
		<p>表多个字符，？代表一个字符。达到Spring那样的能力，一次返回多个资源的URL，进一步方便大家开发。<br />&#160;<br />总结：<br />1，尽量不要使用相对于System.getProperty("user.dir")当前用户目录的相对路径。这是一颗定时炸弹，随时可能要你的命。<br />2，尽量使用URI形式的绝对路径资源。它可以很容易的转变为URI,URL，File对象。<br />3，尽量使用相对classpath的相对路径。不要使用绝对路径。使用上面ClassLoaderUtil类的public static URL </p>
		<p>getExtendResource(String relativePath)方法已经能够使用相对于classpath的相对路径定位所有位置的资源。<br />4，绝对不要使用硬编码的绝对路径。因为，我们完全可以使用ClassLoader类的getResource("")方法得到当前classpath的绝对路径</p>
		<p>。<br />使用硬编码的绝对路径是完全没有必要的！它一定会让你死的很难看！程序将无法移植！<br />如果你一定要指定一个绝对路径，那么使用配置文件，也比硬编码要好得多！<br />当然，我还是推荐你使用程序得到classpath的绝对路径来拼资源的绝对路径！<br />&#160;</p>
 <img src ="http://www.blogjava.net/crycz/aggbug/95545.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/crycz/" target="_blank">blues</a> 2007-01-23 15:02 <a href="http://www.blogjava.net/crycz/articles/95545.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>