首先看一个实例:
package test4;
public class Test2 {
public static void main(String args[]) {
String s1 = "java";
String s2 = "java";
String s3 = new String("java");
System.out.println(s1 == s2 ? "s1 = s2" : "s1 != s2");
System.out.println(s1 == s3 ? "s1 = s3" : "s1 != s3");
System.out.println(s2 == s3 ? "s2 = s3" : "s2 != s3");
//equal fun
System.out.println(s1.equals(s2) ? "s1 equals to s2" : "s1 doesn't equals to s2");
System.out.println(s1.equals(s3) ? "s1 equals to s3" : "s1 doesn't equals to s3");
System.out.println(s2.equals(s3) ? "s2 equals to s3" : "s2 doesn't equals to s3");
}
}
程序很简单,分别用==和String中的方法equals对3个字符串作了比较。然而结果可能很多人不会想到(特别是学过C++的同学):
s1 = s2
s1 != s3
s2 != s3
s1 equals to s2
s1 equals to s3
s2 equals to s3
下面来解释解释个中缘由~~~
JVM在运行的时候,将内存分为两个部分,一部分是堆,另一部分是栈。堆中存放的是创建的对象,而栈中则是方法的调用过程中的局部变量或者引用。而设计Java字符串对象内存实现的时候,在堆中又开辟了一块很小的内存,称作字符串常量池。专门用来存放特定的字符串对象。
看注解:
package test4;
public class Test2 {
public static void main(String args[]) {
String s1 = "java"; //此时"java"被放入字符串常量池中,s1指向之
String s2 = "java"; //查看字符串常量池,存在"java",s2指向它,并未分配新的字符串
String s3 = new String("java");
System.out.println(s1 == s2 ? "s1 = s2" : "s1 != s2"); //作==比较时,实际比较的是两边的对象是否指向同一个引用,这里
System.out.println(s1 == s3 ? "s1 = s3" : "s1 != s3"); //s1,s2指向同一个常量的引用,而s3被分配在堆中非字符串内存池
System.out.println(s2 == s3 ? "s2 = s3" : "s2 != s3"); //的区块,从而不和s1/s2相等
//equal fun
System.out.println(s1.equals(s2) ? "s1 equals to s2" : "s1 doesn't equals to s2"); //equals函数则是比较对象中的具体
System.out.println(s1.equals(s3) ? "s1 equals to s3" : "s1 doesn't equals to s3"); //也就是具体的字符
System.out.println(s2.equals(s3) ? "s2 equals to s3" : "s2 doesn't equals to s3");
}
}
对于C++用户,要特别注意Java的String和C++的STL中封装的String类的差别,C++中支持操作符重载,从而==被重载后实现为比较字符串,而Java的String并没有operator==成员函数,在使用==比较时,并不是调用String中的函数,而是对引用对象的比较。结合上面的注释,应该很容易理解了吧?
Java中这种设计,有两个很可观的优点:其一,实际使用中,字符串比较比其它(比如int,double)等可能会多很多,并且String对象本身可能是十分庞大的,如果能直接用引用对象是否相同来达到比较效果,是可以节约很多资源的。其二,极大地节约了内存的利用率,减少了内存中的冗余。但是,在实际开发中,这种节约大多时候给我们带来的可能是灾难性的结果,甚至影响你本身的编程逻辑。所以,在设计字符串的比较时务必要用equals方法(函数)替换之。^_^