友情提示Q希望在阅读之前Q你已经对String对象有了基础的了解。ƈ且已l熟?cd"?引用"?对象"?实例??堆栈"和关于Object对象的equals()Ҏ(gu)的用机Ӟq且已经熟知String对象对equals()Ҏ(gu)重写后的使用机制Q?/p>
首先大家知道QString既可以作Z个对象来使用Q又可以作ؓ一个基本类型来使用。这里指的作Z个基本类型来使用只是指用方法上的,比如String s = "Hello"Q它的用方法如同基本类型int一P比如int i = 1;Q而作Z个对象来使用Q则是指通过new关键字来创徏一个新对象Q比如String s = new String("Hello")。但是它的内部动作其实还是创Z一个对象,q点E后会说到?/p>
其次Q对String对象的比较方法需要了解。Java里对象之间的比较有两U概念,q里拿String对象来说Q一U是?=="来比较,q种比较是针对两个Stringcd的变量的引用Q也是说如果两个Stringcd的变量,它们所引用同一个String对象(x向同一块内存堆)Q则"=="比较的结果是true。另一U是用Object对象的equals()Ҏ(gu)来比较,String对象l承自ObjectQƈ且对equals()Ҏ(gu)q行了重写。两个String对象通过equals()Ҏ(gu)来进行比较时Q其实就是对String对象所装的字W串内容q行比较Q也是说如果两个String对象所装的字W串内容相同(包括大小写相?Q则equals()Ҏ(gu)返回true?/p>
现在开始将对String对象的创建做具体的分析?/p>
首先看以下代码段Q?/p>
String s1 = new String("Hello");
String s2 = new String("Hello");
System.out.println(s1 == s2);
System.out.println(s1.equals(s2));
以上代码D늚打印l果是:
false
true
q个l果怿大家很好理解Q两个Stringcd的变量s1和s2都通过new关键字分别创Z一个新的String对象Q这个new关键字ؓ创徏的每个对象分配一块新的、独立的内存堆。因此当通过"=="来比较它们所引用的是否是同一个对象时Q将q回false。而通过equals()Ҏ(gu)来比较时Q则q回trueQ因两个对象所装的字W串内容是完全相同的?/p>
好,现在把上面的代码D修改如下:
String s1 = new String("Hello");
String s2 = s1;
System.out.println(s1 == s2);
System.out.println(s1.equals(s2));
以上代码D늚打印l果是:
true
true
q个l果应该更好理解Q变量s1q是通过new关键字来创徏了一个新的String对象Q但q里s2q没有通过new关键字来创徏一个新的String对象Q而是直接把s1赋值给了s2Q即把s1的引用赋值给了s2Q所以s2所引用的对象其实就是s1所引用的对象。所以通过"=="来比较时Q返回true。既然它们引用的都是同一个对象,那么通过equals()Ҏ(gu)来比较时Q肯定也q回trueQ这里equals()Ҏ(gu)其实在对同一个对象进行比较,自己肯定{于自己咯?/p>
说到此,其实应该没什么大问题Q因些都是标准的创徏对象的动作,但是String对象q有另一U用方法,也就是开头所提到的可以作Z个基本类型来使用Q请看以下代码段Q?/p>
String s = "Hello";
看到q里Q相信一些初学的朋友或者对String对象q没搞清楚的朋友开始疑惑了Q你肯定会问Q这个用方法在其内部到底发生了什么?其实q就是String对象Ҏ(gu)h的关键!
其实在启动程序时Q虚拟机会创Z块String对象的String~冲池。当String对象作ؓ一个基本类型来使用Ӟ比如QString s = "Hello";Q虚拟机会先在这个String~冲池内L是否有相同值的String对象存在Q如果存在,则把q个String对象的引用赋值给s。如果不存在Q虚拟机会先在这个String~冲池内创徏此String对象Q然后把引用赋值给s?/p>
说到q里Q相信大家已l开始明白了。那么请看下面的代码D:
String s1 = "Hello";
String s2 = "Hello";
System.out.println(s1 == s2);
System.out.println(s1.equals(s2));
以上代码D늚打印l果是:
true
true
Z么这个结果?那么来分析一下。首先这两个String对象都是作ؓ一个基本类型来使用的,而不是通过new关键字来创徏的,因此虚拟Z会ؓq两个String对象分配新的内存堆,而是到String~冲池中来寻找?/p>
首先为s1LString~冲池内是否有与"Hello"相同值的String对象存在Q此时String~冲池内是空的,没有相同值的String对象存在Q所以虚拟机会在String~冲池内创徏此String对象Q其动作是new String("Hello");。然后把此String对象的引用赋值给s1?/p>
接着为s2LString~冲池内是否有与"Hello"相同值的String对象存在Q此时虚拟机扑ֈ了一个与其相同值的String对象Q这个String对象其实是为s1所创徏的String对象。既然找C一个相同值的对象Q那么虚拟机׃在ؓ此创Z个新的String对象Q而是直接把存在的String对象的引用赋值给s2?/p>
q里既然s1和s2所引用的是同一个String对象Q即自己{于自己Q所以以上两U比较方法都q回ture?/p>
到这里,对String对象的基本概念应该都已经理解了。现在我来小l一下:
针对String作ؓ一个基本类型来使用Q?/p>
1。如果String作ؓ一个基本类型来使用Q那么我们视此String对象是String~冲池所拥有的?br />2。如果String作ؓ一个基本类型来使用Qƈ且此时String~冲池内不存在与其指定值相同的String对象Q那么此时虚拟机ؓ此创建新的String对象Qƈ存放在String~冲池内?br />3。如果String作ؓ一个基本类型来使用Qƈ且此时String~冲池内存在与其指定值相同的String对象Q那么此时虚拟机不为此创徏新的String对象Q而直接返回已存在的String对象的引用?/p>
针对String作ؓ一个对象来使用Q?/p>
1。如果String作ؓ一个对象来使用Q那么虚拟机ؓ此创Z个新的String对象Q即为此对象分配一块新的内存堆Qƈ且它q不是String~冲池所拥有的,卛_是独立的?/p>
理解了以上内容后Q请看以下代码段Q?/p>
String s1 = "Hello";
String s2 = new String("Hello");
System.out.println(s1 == s2);
System.out.println(s1.equals(s2));
以上代码D늚打印l果是:
false
true
Ҏ(gu)上面的小l来q行分析。第一行是把String作ؓ一个基本类型来使用的,因此s1所引用的对象是属于String~冲池内的。ƈ且此时String~冲池内q没有与其值相同的String对象存在Q因此虚拟机会ؓ此创Z个新的String对象Q即new String("Hello");。第二行是把String作ؓ一个对象来使用的,因此s2所引用的对象不属于String~冲池内的,卛_是独立的。通过new关键字,虚拟Z为此创徏一个新的String对象Q即为它分配了一块新的内存堆。因?=="比较后的l果是falseQ因为s1和s2所引用的ƈ不是同一个对象,它们是独立存在的。而equals()Ҏ(gu)所q回的是trueQ因两个对象所装的字W串内容是完全相同的?/p>
现在Q相信大家已l完全搞清楚String对象是怎么一回事了:Q但是到此ƈ没有l束Q因为String对象q有更深层次的应用?/p>
q里我将分析一下String对象的intern()Ҏ(gu)的应用?br />intern()Ҏ(gu)返回一个字W串对象的规范表C法Q即一个同该字W串内容相同的字W串Q但是来自于唯一字符串的String~冲池。这听v来有Ҏ(gu)口,其实它的机制有如以下代码D:
String s = new String("Hello");
s = s.intern();
以上代码D늚功能实现可以单的看成如下代码D:
String s = "Hello";
你一定又开始疑惑了Q那么你可以先看W二个代码段。第二个代码D늚意思就是从String~冲池内取出一个与其值相同的String对象的引用赋值给s。如果String~冲池内没有与其相同值的String对象存在Q则在其内ؓ此创Z个新的String对象。那么第一D代码的意思又是什么呢Q我们知道通过new关键字所创徏出的对象Q虚拟机会ؓ它分配一块新的内存堆。如果^凡地创徏相同内容的对象,虚拟机同样会为此分配许多新的内存堆,虽然它们的内Ҏ(gu)完全相同的。拿String对象来说Q如果连l创?0个相同内容的String对象(new String("Hello"))Q那么虚拟机ؓ此分?0块独立的内存堆。假设所创徏的String对象的字W串内容十分大,假设一个Stirng对象装?M大小的字W串内容Q那么如果我们创?0个此相同String对象的话Q我们将会毫无意义的费9M的内存空间。我们知道String是finalc,它所装的是字符串常量,因此String对象在创建后其内?字符?g能改变,也因此String对象可以被共享。所以对于刚才提到的假设Q我们所创徏?0个相同内容的String对象Q其实我们只需为此创徏一个String对象Q然后被其它String变量所׃n。要实现q种机制Q唯一的、简单的Ҏ(gu)是使用String~冲池,因ؓString~冲池内不会存在相同内容的String对象。而intern()Ҏ(gu)是使用q种机制的途径。在一个已实例化的String对象上调用intern()Ҏ(gu)后,虚拟Z在String~冲池内L与此Stirng对象所装的字W串内容相同值的String对象Q然后把引用赋值给引用原来的那个String对象的Stringcd变量。如果String~冲池内没有与此String对象所装的字W串内容相同值的String对象存在Q那么虚拟机会ؓ此创Z个新的String对象Qƈ把其引用赋值给引用原来的那个String对象的Stringcd变量。这样就辑ֈ了共享同一个String对象的目的,而原先那个通过new关键字所创徏出的String对象被抛弃q被垃圾回收器回收掉。这样不但降低了内存的用消耗,提高了性能Q而且在String对象的比较上也同h方便了,因ؓ相同的String对象被׃nQ所以要判断两个String对象是否相同Q则只需要?=="来比较,而无需再用equals()Ҏ(gu)来比较,q样不但使用h更方便,而且也提高了性能Q因为String对象的equals()Ҏ(gu)会对字W串内容拆解Q然后逐个q行比较Q如果字W串内容十分大的话,那么q个比较动作则大大降低了性能?/p>
说到此,大家可能对具体应用还有点模糊Q那么我来D个简单的CZQ以侉KqC上概念:
假设有一个类Q它有一个接收消息的Ҏ(gu)Q这个方法记录用户传来的消息(假设消息内容可能较大Qƈ且重复率较高)Qƈ且把消息按接攉序记录在一个列表中。我x些朋友会q样设计Q?/p>
import java.util.*;
public class Messages {
ArrayList messages = new ArrayList();
public void record(String msg) {
messages.add(msg);
}
public List getMessages() {
return messages;
}
}
q种设计Ҏ(gu)好吗Q假设我们重复的发送给record()Ҏ(gu)同一个消?消息来自不同的用P所以可以视每个消息Z个new String("..."))Qƈ且消息内容较大,那么q个设计会大大费内存I间Q因为消息列表中记录的都是新创徏的、独立的String对象Q虽然它们的内容都相同。那么怎么样可以对其进行优化呢Q其实很单,L(fng)如下优化后的CZQ?/p>
import java.util.*;
public class Messages {
ArrayList messages = new ArrayList();
public void record(String msg) {
messages.add(msg.intern());
}
public List getMessages() {
return messages;
}
}
正如你所看到的,原先record()Ҏ(gu)中的messages.add(msg);代码D变成了messages.add(msg.intern());Q仅仅对msg参数调用了intern()Ҏ(gu)Q这样将寚w复的消息q行׃n机制Q从而降低了内存消耗,提高了性能?/p>
q个例子的确有点牵强Q但是这里只是ؓ了阐qC上概念!
xQString对象的迷N被消除了Q大家只要牢记这些概念,以后再复杂的String应用都可以徏立在此基上来q行分析?