﻿<?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-金窝银窝不如自己的WEB窝!-文章分类-JAVA</title><link>http://www.blogjava.net/gkc/category/42149.html</link><description>gkc</description><language>zh-cn</language><lastBuildDate>Tue, 13 Oct 2009 12:22:32 GMT</lastBuildDate><pubDate>Tue, 13 Oct 2009 12:22:32 GMT</pubDate><ttl>60</ttl><item><title>彻底理解java String</title><link>http://www.blogjava.net/gkc/articles/298042.html</link><dc:creator>gkc</dc:creator><author>gkc</author><pubDate>Tue, 13 Oct 2009 03:56:00 GMT</pubDate><guid>http://www.blogjava.net/gkc/articles/298042.html</guid><wfw:comment>http://www.blogjava.net/gkc/comments/298042.html</wfw:comment><comments>http://www.blogjava.net/gkc/articles/298042.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/gkc/comments/commentRss/298042.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/gkc/services/trackbacks/298042.html</trackback:ping><description><![CDATA[String类在java中被大量运用，甚至在class文件中都有其身影，因此将其设计为简单轻便的非可变类是比较合适的。
<p>&nbsp;</p>
<p>一、创建。<br />
好了，知道String是非可变类以后，我们可以进一步了解String的构造方式了。创建一个Stirng对象，主要就有以下两种方式：<br />
java 代码</p>
<p>
<div style="border-bottom: #666666 1px solid; border-left: #666666 1px solid; border-top: #666666 1px solid; border-right: #666666 1px solid">1. String str1 = new String("abc"); <br />
2. Stirng str2 = "abc";</div>
<p>&nbsp;</p>
<p>虽然两个语句都是返回一个String对象的引用，但是jvm对两者的处理方式是不一样的。对于第一种，jvm会马上在heap中创建一个String对象，然后将该对象的引用返回给用户。对于第二种，jvm首先会在内部维护的strings pool中通过String的 equels 方法查找是对象池中是否存放有该String对象，如果有，则返回已有的String对象给用户，而不会在heap中重新创建一个新的String对象；如果对象池中没有该String对象，jvm则在heap中创建新的String对象，将其引用返回给用户，同时将该引用添加至strings pool中。注意：使用第一种方法创建对象时，jvm是不会主动把该对象放到strings pool里面的，除非程序调用 String的intern方法。看下面的例子：<br />
java 代码</p>
<p>
<div style="border-bottom: #666666 1px solid; border-left: #666666 1px solid; border-top: #666666 1px solid; border-right: #666666 1px solid">
<p>1. String str1 = new String("abc"); //jvm 在堆上创建一个String对象 <br />
2. <br />
3. //jvm 在strings pool中找不到值为&#8220;abc&#8221;的字符串，因此 <br />
4. //在堆上创建一个String对象，并将该对象的引用加入至strings pool中 <br />
5. //此时堆上有两个String对象 <br />
6. Stirng str2 = "abc"; <br />
7. <br />
8. if(str1 == str2){ <br />
9. System.out.println("str1 == str2"); <br />
10. }else{ <br />
11. System.out.println("str1 != str2"); <br />
12. } <br />
13. //打印结果是 str1 != str2,因为它们是堆上两个不同的对象 <br />
14. <br />
15. String str3 = "abc"; <br />
16. //此时，jvm发现strings pool中已有&#8220;abc&#8221;对象了，因为&#8220;abc&#8221;equels &#8220;abc&#8221; <br />
17. //因此直接返回str2指向的对象给str3，也就是说str2和str3是指向同一个对象的引用 <br />
18. if(str2 == str3){ <br />
19. System.out.println("str2 == str3"); <br />
20. }else{ <br />
21. System.out.println("str2 != str3"); <br />
22. } <br />
23. //打印结果为 str2 == str3</p>
</div>
再看下面的例子：<br />
java 代码</p>
<p>
<div style="border-bottom: #666666 1px solid; border-left: #666666 1px solid; border-top: #666666 1px solid; border-right: #666666 1px solid">
<p>1. String str1 = new String("abc"); //jvm 在堆上创建一个String对象 <br />
2. <br />
3. str1 = str1.intern(); <br />
4. //程序显式将str1放到strings pool中，intern运行过程是这样的：首先查看strings pool <br />
5. //有没&#8220;abc&#8221;对象的引用，没有，则在堆中新建一个对象，然后将新对象的引用加入至 <br />
6. //strings pool中。执行完该语句后，str1原来指向的String对象已经成为垃圾对象了，随时会 <br />
7. //被GC收集。 <br />
8. <br />
9. //此时，jvm发现strings pool中已有&#8220;abc&#8221;对象了，因为&#8220;abc&#8221;equels &#8220;abc&#8221; <br />
10. //因此直接返回str1指向的对象给str2，也就是说str2和str1引用着同一个对象， <br />
11. //此时，堆上的有效对象只有一个。 <br />
12. Stirng str2 = "abc"; <br />
13. <br />
14. if(str1 == str2){ <br />
15. System.out.println("str1 == str2"); <br />
16. }else{ <br />
17. System.out.println("str1 != str2"); <br />
18. } <br />
19. //打印结果是 str1 == str2 <br />
20.</p>
</div>
</p>
<p>为什么jvm可以这样处理String对象呢？就是因为String的非可变性。既然所引用的对象一旦创建就永不更改，那么多个引用共用一个对象时互不影响。</p>
<p><br />
二、串接（Concatenation）。<br />
java程序员应该都知道滥用String的串接操作符是会影响程序的性能的。性能问题从何而来呢？归根结底就是String类的非可变性。既然 String对象都是非可变的，也就是对象一旦创建了就不能够改变其内在状态了，但是串接操作明显是要增长字符串的，也就是要改变String的内部状态，两者出现了矛盾。怎么办呢？要维护String的非可变性，只好在串接完成后新建一个String 对象来表示新产生的字符串了。也就是说，每一次执行串接操作都会导致新对象的产生，如果串接操作执行很频繁，就会导致大量对象的创建，性能问题也就随之而来了。<br />
为了解决这个问题，jdk为String类提供了一个可变的配套类，StringBuffer。使用StringBuffer对象，由于该类是可变的，串接时仅仅时改变了内部数据结构，而不会创建新的对象，因此性能上有很大的提高。针对单线程，jdk 5.0还提供了StringBuilder类，在单线程环境下，由于不用考虑同步问题，使用该类使性能得到进一步的提高。</p>
<p>三、String的长度<br />
我们可以使用串接操作符得到一个长度更长的字符串，那么，String对象最多能容纳多少字符呢？查看String的源代码我们可以得知类String中是使用域 count 来记录对象字符的数量，而count 的类型为 int，因此，我们可以推测最长的长度为 2^32，也就是4G。<br />
不过，我们在编写源代码的时候，如果使用 Sting str = "aaaa";的形式定义一个字符串，那么双引号里面的ASCII字符最多只能有 65534 个。为什么呢？因为在class文件的规范中， CONSTANT_Utf8_info表中使用一个16位的无符号整数来记录字符串的长度的，最多能表示 65536个字节，而java class 文件是使用一种变体UTF-8格式来存放字符的，null值使用两个字节来表示，因此只剩下 65536－ 2 ＝ 65534个字节。也正是变体UTF-8的原因，如果字符串中含有中文等非ASCII字符，那么双引号中字符的数量会更少（一个中文字符占用三个字节）。如果超出这个数量，在编译的时候编译器会报错。</p>
<p>对String类的理解网上有很多的资料，个人觉得很多都是错误的，错误的解释危害性更大。所以为了解释清楚，就来看代码，代码运行出的结果才是最具有说服力的。说明：以下都是个人的理解，欢迎指正。<br />
Java代码 复制代码</p>
<p>
<div style="border-bottom: #666666 1px solid; border-left: #666666 1px solid; border-top: #666666 1px solid; border-right: #666666 1px solid">
<p>1. public class StringTest { <br />
2. public static void main(String[] args) { <br />
3. String str1 = new String("abc"); <br />
4. <br />
5. String str2 = "abc"; <br />
6. <br />
7. if (str1 == str2) { <br />
8. System.out.println("str1 == str2"); <br />
9. } else { <br />
10. System.out.println("str1 != str2"); <br />
11. } <br />
12. <br />
13. String str3 = "abc"; <br />
14. if (str2 == str3) { <br />
15. System.out.println("str2 == str3"); <br />
16. } else { <br />
17. System.out.println("str2 != str3"); <br />
18. } <br />
19. <br />
20. str1 = str1.intern(); <br />
21. <br />
22. if (str1 == str2) { <br />
23. System.out.println("str1 == str2"); <br />
24. } else { <br />
25. System.out.println("str1 != str2"); <br />
26. } <br />
27. <br />
28. String str4 = new String("abc"); <br />
29. str4 = str4.intern(); <br />
30. <br />
31. if (str1 == str4) { <br />
32. System.out.println("str1 == str4"); <br />
33. } else { <br />
34. System.out.println("str1 != str4"); <br />
35. } <br />
36. } <br />
37. <br />
38. }</p>
<p><br />
运行结果：</p>
<p>str1 != str2<br />
str2 == str3<br />
str1 == str2<br />
str1 == str4</p>
</div>
<br />
看看运行结果后，给出我自己的理解说明：</p>
<p>1、String有一个所谓的String pool，这是一个什么东西呢，我理解是它是堆（heap）上特殊的一个空间（我叫它特殊堆）。注意它也是在堆上。</p>
<p>2、产生String类型的对象有两种方法，那么这两种方法有什么区别呢？我的理解是String str = &#8220;abc&#8221;是先用equals方法（String类覆盖了equals方法）判断这个特殊堆（String pool）是否有abc，有则将原来在栈中指向abc的引用赋值给str，否则就在这个特殊堆（String pool）上创建一个abc对象。String str2 = new String（"abc"）则是在普通堆上创建abc对象。所以str和str2是指向不同的对象，它们是不同的。</p>
<p>3、String有个intern()方法，这个方法是个本地方法，当用String str2 = new String（"abc"）来创建对象时，它相当于告诉JVM，我这个abc对象是放在特殊堆（String pool）上的。所以第三个打印结果是相等的。</p>
<p>4、需要注意的一点：String是final类，它是恒定类，一旦创建就无法改变，所以用intern()方法是重新在String pool中创建了一个新的对象。</p>
<p>5、String类有一个伴随类StringBuffer，需要了解它们之间的区别请google之。</p>
 <img src ="http://www.blogjava.net/gkc/aggbug/298042.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/gkc/" target="_blank">gkc</a> 2009-10-13 11:56 <a href="http://www.blogjava.net/gkc/articles/298042.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>