先看一个例子:
String str2 = new String("abc");
if(str1 == str2)
{
System.out.println("str1 == str2");
}
if(str1.equals(str2))
{
System.out.println("str1 equals str2");
}
String str3 = "abc";
String str4 = "abc";
if(str3 == str4)
{
System.out.println("str3 == str4");
}
if(str3.equals(str4))
{
System.out.println("str3 equals str4");
}
if(str1 == str4)
{
System.out.println("str1 equals str4");
}
if(str1.equals(str4))
{
System.out.println("str1 equals str4");
}
运行结果:
str1 equals str2
str3 == str4
str3 equals str4
str1 equals str4
为什么会出现这样的结果。。。这需要引入一些概念,其实很类似C++ 或C#之类,差别只是小小的。。。
首先了解一个对象在内存中创建的区域:
1.heap(即堆区):它是负责创建对象的,所有的创建出来的对象都是放在堆区的。所有new操作的创建的实例都存储在这里,包括用new String 创建的String。
2.stack(即栈区):它是负责存放局部变量,和成员变量的。所有的成员变量和局部变 量都放在这个区内,然后他通过一个引用指向栈区的对象或data segment(静态代码)区的静态数据。
3.data segment(静态代码区):在这个区主要存放的是静态常量,和字符串常量(字符串池)。在类一开始被加载的时候此常量就被初始化放在这个区内,而且被全局所共享,所有的访问直接指向他即可。
4.code segment(代码区):它是存放代码的区,所有的执行代码都放在此区内。通过对象的调用指向此区。
如果没有重载equals方法,则equals是比较地址是否相等, == 比较值是否相等。 来一段demo
{
int no = 0;
public CC(int _no)
{
no = _no;
}
}
CC c1 = new CC(1);
CC c2 = new CC(2);
CC c3 = c1;
if(c1 == c2)
{
System.out.println("c1 == c2");
}
if(c1.equals(c2))
{
System.out.println("c1 equals c2");
}
if(c1 == c3)
{
System.out.println("c1 == c3");
}
if(c1.equals(c3))
{
System.out.println("c1 equals c3");
}
运行结果:
c1 == c3
c1 equals c3
解释:equals判断两个内存地址是否相等,因为c1、c2都是用new实例化的,因此肯定是不同内存地址,因此equals肯定为false,因为c3是存的c1的引用,也就是c1在堆里的地址,所以为true
==判断两个内存中的内容是否相当,因为c1,c2都是new出来的2个不同对象,即使对象内的成员等一模一样,因此也为false,c3地址跟c1一样,因此内容也一样。
在java api中对equal的解释是:
equals 方法在非空对象引用上实现相等关系:
自反性:对于任何非空引用值 x,x.equals(x) 都应返回 true。
对称性:对于任何非空引用值 x 和 y,当且仅当 y.equals(x) 返回 true 时,x.equals(y) 才应返回 true。
传递性:对于任何非空引用值 x、y 和 z,如果 x.equals(y) 返回 true,并且 y.equals(z) 返回 true,那么 x.equals(z) 应返回 true。
一致性:对于任何非空引用值 x 和 y,多次调用 x.equals(y) 始终返回 true 或始终返回 false,前提是对象上 equals 比较中所用的信息没有被修改。
对于任何非空引用值 x,x.equals(null) 都应返回 false。
equals是可以被类重写的,因此判断的标准也跟具体类不一样了。
像String,date等系统类已经将该方法重写。在String类中equals就被重写为判断字符串内容是否相当,而不是字符串地址。
再看开篇的demo程序。
结果是
str1 equals str2
str3 == str4
str3 equals str4
str1 equals str4
解释一下,
在String中, String str1 = new String("abc")方式是在堆里分配一块内存;String str3 = "abc"方式,首先会到静态存储区的字符串池中搜索是否有该值,有的话,这直接给str3返回该指针,否则在字符串池中分配内存创建该字符串。
因此
String str1 = new String("abc");
String str2 = new String("abc");
是在堆分配了两块独立内存
而
String str3 = "abc";
String str4 = "abc";
执行第一句时,str3 = "abc"时候,先到静态存储区的字符串池中找,发现没有这个字符串,则创建之,第二句str4 = "abc" 逻辑跟第一句一样,这时字符串池中已有该串,则直接给str4返回了str3的地址。
因此有开篇的运行结果就容易理解了。
了解了这些特性,我们可以把一些常用字符串写到公共类里,设为static, 以后所有用到这些串的时候就不用再分配内存了