1.==: ==比较的是的地址
如果进行比较的两个操作数都是数值类型,即使数据类型不一样,只要他们的值相同,都能返回true;如97=='a',或者t==5.0;
如果两个操作数是引用类型,那么只有两个引用变量的类型具有父子关系才可以比较,当指向同一个地址,即同一个对象的时候,才会返回true;
需要注意的地方1:
对于String类,当java程序直接使用如"hello"这种字符串直接量(编译时期就计算出来的字符串值)时,JVM会使用常量池来管理这些字符串;当使用new String("hello")的时候,JVM会先使用常量池管理"hello"直接量,再用String类的构造函数,new一个新的String对象;所以这里会产生两个对象,一个是编译时存放在常量池的"hello"字符串对象,一个是运行时new的String对象;
第一步,因为“hello”直接使用了双引号声明,故JVM会在运行时常量池中首先查找有没有该字符串,有则进入第二步;没有则直接在常量池中创建该字符串,然后进入第二步。第二步:在常量池中创建了一个String对象之后,由于使用了new,JVM会在Heap(堆)中创建一个内容相同的String对象,然后返回堆中String对象的引用。该行代码分别在常量池和堆中生成了两个内容相同的String对象。
需要注意的地方2:
public class Test1 {
public static void main(String[] args) {
// TODO Auto-generated method stub
String s1 = "sayhello";//这里在编译时期就在常量池产生了一个字符串常量;
String s2 = "say";//这里在编译时期就在常量池产生了一个字符串常量;
String s3 = "hello";//这里在编译时期就在常量池产生了一个字符串常量;
String s4 = "say" + "hello";//编译期间优化成一个字符串,直接引用指向常量池里已经产生的"sayhello"
String s5 = s2 + s3;//s2,s3都是变量,编译期不能确定s5是啥;用的是运行时产生在堆内存的s2,s3
//注意的第一点已经说过了;这里因为常量池已经有了"sayhello",所以只产生了一个对象;
String s6 = new String("sayhello");
System.out.println(s1==s4);//true,s1和s4都指向常量池的"sayhello",所以是一个对象,一个地址
System.out.println(s1==s5);//false,s1指向的是常量池里面的"sayhello",s5指向的是运行时堆内存中的"sayhello";
System.out.println(s1==s6);//false,同上
}
}
需要注意的地方3:
public class Test1 {
public static void main(String[] args) {
// TODO Auto-generated method stub
String s1 = "sayhello";//这里在编译时期就在常量池产生了一个字符串常量;
final String s2 = "say";//这里在编译时期就在常量池产生了一个字符串常量;
String s3 = "hello";//这里在编译时期就在常量池产生了一个字符串常量;
String s4 = s2 + "hello";//因为s2加了final 实际也是常量,
System.out.println(s1==s4);//在编译期能够确定;所以s4还是引用的常量池的字符串常量
}
}
所以记住:当用双引号声明的String对象会在编译期间储存在常量池!
需要注意的地方4:
对于Integer:
public class Test2 {
public static void main(String[] args) {
// TODO Auto-generated method stub
//首先要知道这里是自动装箱,在编译期间,这里被翻译成Integer a1 = Integer.valueOf(10);
//然而在Integer类中,系统会把一个-128--127之间的整数自动装箱成Integer实例,存放在一个cache数组,
//如果以后吧一个-128--127的整数自动装箱成一个Integer实例,会直接指向这个数组对于的元素;如果再这个
//范围之外系统会重新创建一个Integer实例,所以a3和a4是两个不同的对象,而a1和a1都指向同一个数组元素;
Integer a1 = 10;
Integer a2 = 10;
System.out.println(a1==a2);//true
Integer a3 = 128;
Integer a4 = 128;
System.out.println(a3==a4);//false
}
}
需要注意的地方5:
String str1 = new String("S") + new String("C");
System.out.println(str1.intern() == str1);
System.out.println(str1 == "SC");
true, false
String str2 = "SEUCalvin";//新加的一行代码,其余不变
String str1 = new String("S")+ new String("C");
System.out.println(str1.intern() == str1);
System.out.println(str1 == "SC");
false,false;
str1.intern是去查找常量池是否含有SC字符串,如果有就返回常量池的引用,如果没有就返回堆上的引用;所以第一段的第一个是true,第二个SC是放在常量池的,所以str1是堆上的,所以返回false;
第二段的话因为常量池上已经有了,所以str1.intern返回常量池上的,和堆上的不一样;
2.equals:比较的是值,不过比较的规则是需要定义的;
首先,equals方法是object类的,object的equals规则是,只要两个比较的对象地址一样就算一样,和==的比较规则是一样的;
然而Integer,String等包装类是重写了equals方法的,他们的规则定义为只要两个对象的值一样,就算一样;
所以,如果是自己定义的类,就需要重写equals方法, 自己定义规则,否则就会继承Object的equals方法的规则;
例题:
Consider the following code:
Integer s=new Integer(9);
Integer t=new Integer(9);
Long u=new Long(9);
Which test would return true?
1.(s==u) ,因为, s 是 Integer 类型, u 是 Long 类型,两个不同类型的引用不能进行 == 比较。
2.(s==t) , s 是指向一个 9 的引用,而 t 也是一个指向 9 的引用,虽然都是指向 9 ,但却是指向不同的 9 ,即是两个不同的引用。因此 == 比较返回的是假。
3.(s.equals(t)) , Integer 的 equals 方法如下:
public boolean equals(Object obj) {
if (obj instanceof Integer) {
return value == ((Integer)obj).intValue();
}
return false ;
}
是 Integer 的实例且 value 值也相等的情况下返回真,其他返回假。
在这里, s 和 t 都是 Integer 类型且值都为 9 ,因此结果为真。
所以如果s.equals(u),类型不一样也会返回false;
4.(s.equals(9)) , 在进行 equals 比较之前,会对 9 调用 Integer.valueOf 方法,进行自动装箱 , 由于 IntegerCache 中已经存在 9 ,所以,直接返回其引用,引用相同, equals 就自然相同了。所以结果为真。
5.(s.equals( new Integer(9)) ,直接创建了一个新的 Integer 实例,但且值也为 9 ,所以,满足条件,返回真。