JAVA中的equals和==的区别
==比较的是2个对象的地址,而equals比较的是2个对象的内容。显然,当equals为true时,==不一定为true。
折腾了一阵子又查了查书,终于对 String 这个特殊的对象有了点感悟
public class TestString {
public static void main(String[] args) {
String s1 = "Monday";
String s2 = "Monday";
}
}
有什么问题呢?
1. 来自 String 的忧虑
上面这段程序中,到底有几个对象呢?
可能很多人脱口而出:两个,s1 和 s2
为什么?
String 是 final 类,它的值不可变。
看起来似乎很有道理,那么来检测一下吧,稍微改动一下程序
就可以看到结果了:
public class TestString {
public static void main(String[] args) {
String s1 = "Monday";
String s2 = "Monday";
if (s1 == s2)
System.out.println("s1 == s2");
else
System.out.println("s1 != s2");
}
}
编译并运行程序,输出:s1 == s2
为什么 s1 == s2 ?
== 分明是在说:s1 与 s2 引用同一个 String 对象 -- "Monday"!
2. 千变万化的 String
再稍微改动一下程序,会有更奇怪的发现:
public class TestString {
public static void main(String[] args) {
String s1 = "Monday";
String s2 = new String("Monday");
if (s1 == s2)
System.out.println("s1 == s2");
else
System.out.println("s1 != s2");
if (s1.equals(s2))
System.out.println("s1 equals s2");
else
System.out.println("s1 not equals s2");
}
}
我们将 s2 用 new 操作符创建
程序输出:
s1 != s2
s1 equals s2
嗯,很明显嘛
s1 s2分别引用了两个"Monday"String对象
可是为什么两段程序不一样呢?
3. 在 String 的游泳池中游泳
哈哈,翻了翻书终于找到了答案:
原来,程序在运行的时候会创建一个字符串缓冲池
当使用 s2 = "Monday" 这样的表达式创建字符串的时候,JAVA首先会
在这个String缓冲池中寻找相同值的对象,在第一个程序中,s1先被
放到了池中,所以在s2被创建的时候,程序找到了具有相同值的 s1
将 s2 引用 s1 所引用的对象"Monday"
第二段程序中,使用了 new 操作符,他明白的告诉程序:
“我要一个新的!不要旧的!”与是一个新的"Monday"Sting对象被创
建在内存中。他们的值相同,但是位置不同,一个在池中游泳
一个在岸边休息。哎呀,真是资源浪费,明明是一样的非要分开做什么呢?
4. 继续潜水
再次更改程序:
public class TestString {
public static void main(String[] args) {
String s1 = "Monday";
String s2 = new String("Monday");
s2 = s2.intern();
if (s1 == s2)
System.out.println("s1 == s2");
else
System.out.println("s1 != s2");
if (s1.equals(s2))
System.out.println("s1 equals s2");
else
System.out.println("s1 not equals s2");
}
}
这次加入:s2 = s2.intern();
哇!程序输出:
s1 == s2
s1 equals s2
原来,程序新建了 s2 之后,又用intern()把他打翻在了池里
哈哈,这次 s2 和 s1 有引用了同样的对象了
我们成功的减少了内存的占用
5. == 与 equals() 的争斗
String 是个对象,要对比两个不同的String对象的值是否相同
明显的要用到 equals() 这个方法
可是如果程序里面有那么多的String对象,有那么多次的要用到 equals ,
更好的办法:
把所有的String都intern()到缓冲池去吧
最好在用到new的时候就进行这个操作
String s2 = new String("Monday").intern();
现在我可以无所顾忌的用 == 来比较 String 对象的值了
真是爽啊,又快又方便!
测试代码:
public class StringAddress {
public static void main(String[] args) {
String a = "hehe";
String b = "hehe";
String c = new String("hehe");
String d = a;
String e = c.intern();
if (a == b)
System.out.println("a==b---使用 a='hehe'创建字符串的时候,JAVA首先在String缓冲池中寻找相同值,若找到则不新建对象");
if (a == c)
System.out.println("a==c---使用new操作符,重新申请内存");
if (a.equals(c))
System.out.println("a.equals(c)---值相同,内存地址不同");
if (a == d)
System.out.println("a==d---a==d ==> a.equals(d)");
if (a.equals(d))
System.out.println("a.equals(d)");
if (a == e)
System.out.println("a==e---intern()后都到String池中");
System.out.println(System.getProperty("user.dir"));
}
}
--------------------------------------------------------------------------------------------
String和StringBuffer的区别
而在某些特别情况下, String 对象的字符串拼接其实是被 JVM 解释成了 StringBuffer 对象的拼接,所以这些时候 String 对象的速度并不会比 StringBuffer 对象慢,而特别是以下的字符串对象生成中, String 效率是远要比 StringBuffer 快的: String S1 = “This is only a” + “ simple” + “ test”; StringBuffer Sb = new StringBuilder(“This is only a”).append(“ simple”).append(“ test”); 你会很惊讶的发现,生成 String S1 对象的速度简直太快了,而这个时候 StringBuffer 居然速度上根本一点都不占优势。其实这是 JVM 的一个把戏,在 JVM 眼里,这个 String S1 = “This is only a” + “ simple” + “test”; 其实就是: String S1 = “This is only a simple test”; 所以当然不需要太多的时间了。但大家这里要注意的是,如果你的字符串是来自另外的 String 对象的话,速度就没那么快了,譬如: String S2 = “This is only a”; String S3 = “ simple”; String S4 = “ test”; String S1 = S2 +S3 + S4; 这时候 JVM 会规规矩矩的按照原来的方式去做
// String+String
String tempstr = "abcdefghijklmnopqrstuvwxyz";
int times = 3500;
long lstart1 = System.currentTimeMillis();
String str = "";
for (int i = 0; i < times; i++) {
str += tempstr;
}
long lend1 = System.currentTimeMillis();
long time1 = (lend1 - lstart1) / 1000;
System.out.println("String+String时间为: " + time1 + "毫秒");
// "abcdefghijklmnopqrstuvwxyz"+"abcdefghijklmnopqrstuvwxyz"
long lstart2 = System.currentTimeMillis();
for (int i = 0; i < times; i++) {
str = "abcdefghijklmnopqrstuvwxyz"+"abcdefghijklmnopqrstuvwxyz";
}
long lend2 = System.currentTimeMillis();
long time2 = (lend2 - lstart2) / 1000;
System.out.println("abcdefghijklmnopqrstuvwxyz+abcdefghijklmnopqrstuvwxyz时间为: " + time2 + "毫秒");
// StringBuffer.append()
long lstart3 = System.currentTimeMillis();
StringBuffer sb = new StringBuffer();
for (int i = 0; i < times; i++) {
sb.append(tempstr);
}
long lend3 = System.currentTimeMillis();
long time3 = (lend3 - lstart3) / 1000;
System.out.println("StringBuffer.append()时间为: " + time3 + "毫秒");
---------------------------------------------------------------------------------------------
创建了几个对象
String s = new String( "xyz ");
这个语句创建了几个对象?
可能是一个,因为常量池中可能已经存在“xyz”这个对象了,所以这个语句不会再在常量池中创建了,只在堆里创建一个对象。如果常量池中不存在“xyz”,则会创建两个。
String s = "a" + "b";创建了几个对象
下面简单证明我的推断,首先编译这个类:
public class Test { private String s = "ab"; } |
复制class文件备用,然后修改为
public class Test { private String s = "a" + "b"; } |
再次编译,用ue之类的文本编辑器打开,察看二进制内容,可以发现,两个class文件完全一致,连一个字节都不差.
ok,真相大白了.根本不存在运行期的处理String b = "a" + "b";这样的代码的问题,编译时就直接优化掉了。
只有使用""和+创建的String对象才会加入String池中,其他用new或者String变量+""的形势均在用于存储对象的堆中。
栈(Stack):用于保存基本类型(byte,int,char)和对象引用,速度仅次于寄存器
堆(heap):用于存储复杂对象。String中有一个Value属性,由char[]型数组存储