首先我们先了解一下字符串,字符串在生成时就写入物理地址,定为不可更改,我们可以把它当作是final形的变量,既然是不可更改的那么我们对于字符串的任何修改将不是原对象,而是产生新的对象。Sun这样设计也许是为了在其性能有所提高。既然每次对字符串操作其结果将是新的字符串,那么频繁调用,既不是很浪费。Java设计者可能想到这一点,所以加了字符串池。也许有的人对于字符串池并不感到熟悉,那你就把它当成是一个ArrayList吧!它用来存储你使用到的所有字符串,追加字符串时,如果有则返加字串符池中的字符串引用,没有则创建一个。这样做是为了避免大量的字符串操作而产生浪费。
好了,我们来看一下下面几个程序:
String s = "abc";//这里用了几个字符串对象
当程序遇到"abc"时,会首先查找字符串池有没有"abc",没有则在字符串池中创建"abc",然后将引用赋给了s。
我们再来看这一句:
String s = new String("abc");//这里用了几个字符串对象
当程序遇到"abc"后,在字符串池创建并返回了引用。但是这里又用到了new关键字,new是在内存当中创建一个对象,并将内存地址赋给了s。也就是在这里一共创建了两个对象,一个是字符串池的引用,另一个是内存地址的引用。
那么这两句话有什么区别吗!当然有区别了,一个是字符串池引用,一个是内存地址。从速度上说当然是访问内存快了。但是它是在内存中创建了一个对象。于利于弊,个人看着使用。
关于字符串重载:
在Java中是不允许使用符号重载的,(在C++就可以)好像是Java的设计者说这是一个不好的设计。于是在Java中去了这个功能。但是在 String中却使用了+的重载符。因为字符串在生成时都是定为不可改变,所以在重载多个字符串时将会产生大量的字符,所以在后来的虚拟机中对于字符串重载使用了StringBuider进行优化。(至于什么版本的进行了优化,嘿嘿,偶不知道。怎么优化,下面有介绍。)
重载时还分有三种情况:
第一:变量重载
第二,字符串常量重载。变量是指字符串所赋值的对象。常量就是指以""的未分配的字符串。而常量重载时虚拟机会为你新建一个 StringBuilder作为临时的字符链接。把所有字符串链接完成,用toString()方法返回新的字符串。而常量字符串则是直接交由字符串池处理。一切操作只在字符串池中,也就是说新生成的字符串也是在字符串池中的引用。
第三种就是对象与常量一起用了。那虚拟会以第一种方式处理。
关于==与equals():
==是检测两个对象的地址是否相等。equals()则是检测两个对象值是否相等。
另有一点,""也是一个字符串,跟new String("")一样。可以用new String("").intern()==""来检测是否同一个引用。
我们再来做一下练习:
首先,定义一堆变量:
String _abc = "abc"; String _2_abc = "abc"; String _def = "def"; String _abcdef = "abcdef"; String _new_abc = new String("abc"); String _new_abcdef = new String("abcdef"); String _abc_def = _abc + _def; String _abc_add_def = "abc" + "def"; String _new_abc_def = _new_abc + _def; System.out.println("_abc == _2_abc -> " + (_abc == _2_abc)); // true System.out.println("_abc==_new_abc -> " + (_abc == _new_abc)); // false System.out.println("_abc==_new_abc.intern() -> " + (_abc == _new_abc.intern())); // true
第一个:第一个数在字符串池中创建了"abc",第二个数生成时,先在字符串池中查找有没有"abc",有则返回这个字符串的引用,所用两个都是同一个引用。
第二个:_new_abc在生成时先查找字符串池是否有"abc",之后又在内存中创建了"abc",并把内存地址给_new_abc,所以这两个对象当然不相同,因为一个是内存地址,一个是字符串池引用。
第三个:用到了intern()此方法返回的是一个新字符串,其对象是指向字符串池的引用。因为先前_abc在字符串池中也创建了"abc"字符串,所以返回的是与_abc相同的引用。
好了,小试了牛刀,再看下面的程序:
System.out.println("_abc_def==_new_abc_def -> " + (_abc_def == _new_abc_def)); // false System.out.println("_abcdef==abc add def -> " + (_abcdef == "abc" + "def")); // true System.out.println("_abcdef==_abc_def -> " + (_abcdef == _abc_def)); // false
第一个因为是变量与常量重载,所以虚拟为你开了一个StringBuilder作为临时链接字符串。其返回的是一个new出来的字符串,也就是其地址是内存地址。所以为false。
第二个因为是常量重载,所以生成的对象是字符串池中的引用。与原先的_abcdef是同一个地址。
第三个跟第一个同理。
再看以下这一句:
System.out.println("_abcdef==_abc_def.intern() -> " + (_abcdef == _abc_def.intern())); // true
_abcdef是字符池的引用,让_abc_def也指向字符池。所以两个对象都是同一个引用。
再看以下:
System.out.println("_abcdef==_abc_add_def -> " + (_abcdef == _abc_add_def)); // true System.out.println("_new_abcdef==_abc_add_def -> " + (_new_abcdef == _abc_add_def)); //false System.out.println("_new_abcdef==_new_abc_def -> " + (_new_abcdef == _new_abc_def)); //false
第一个:_abc_add_def是字符串池"abc"与"def"的常量重载。返回其结果也是字符串池的引用。
第二个:_new_abcdef是new出来的内存地址与_abc_add_def的字符串池引用当然是不同的对象。
第三个:_new_abcdef是new出来的对象,而_new_abc_def也是重载后new出来的对象。但它们之间却是不相同的。也就是说内存不会像字符串池那样给我们找字符串引用。(呵呵,弱智说明)
我们来做一下测试:
int time = 60; System.out.println("游戏时间" + time + "帧数" + 60);
这里用几个字符串4个5个6个7个,也许你会觉得是7个,但是这里共用了5个字符串,为什么,因为虚拟机给你做了优化,在字符串重载时使用了StringBuilder,也就是说上面的程序创建了四个字符串,并用append()方法将四个字符串链接,最后toString()返回一个新的字符串,共是五个字符串。
也许你会想,既然虚拟机会自动为我们优化,那不是怎么用都行。其实不然,虚拟机没有想像中的那么聪明,甚至说它很笨拙。看下面程序:
String s = ""; for (int i = 0; i < 10; i++) { s += i + " "; }
这里首先将i转化为String,再把s+i链接成一个新的字符串。也许你会说那虚拟机不就生成了StringBuilder将s与i链接起来再 toString()返回给s。说得没错。但有一点不对,那就是不止一个Stringbuilder,而是10个,在每一次的循环中都会创建一次 StringBuilder,处理完后又释放了。
既是这样。那怎样优化呢。可以看下面这个例子:
String s = ""; StringBuilder sb = new StringBuilder(); for (int i = 0; i < 10; i++) { sb.append(i); sb.append(" "); } s = sb.toString();
如果你想用sb.append(i+" ");那虚拟就会认为你需要StringBuilder链接,于是又为你创建了一个StringBuilder。
最后做一个小测试。
public class Test { public static void main(String[] args) { Hello(); } static class TestString { String string; public TestString(String s) { string = s; } public String toString() { return string; } } public static void Hello() { TestString ts = new TestString("123"); System.out.println("ts = " + ts); setString(ts); System.out.println("ts = " + ts); String string = "123"; System.out.println("string = " + string); setString(string); System.out.println("string = " + string); } private static void setString(TestString ts) { ts.string = "321"; } private static void setString(String s) { s = "321"; } }
输出结果是多少呢?就留给各位自己解决了。嘿嘿。。
如果有什么地方不对,欢迎拍砖。。。
转载自: http://www.j2megame.cn
作者: hubluesky