Java中equals()与==的区别

来源:互联网 发布:明山控股集团知乎 编辑:程序博客网 时间:2024/05/17 02:48


java中的数据类型,可分为两类:
1.基本数据类型,也称原始数据类型。byte,short,char,int,long,float,double,boolean
  他们之间的比较,应用双等号(==),比较的是他们的值。
2.复合数据类型(类)
  当他们用(==)进行比较的时候,比较的是他们在内存中的存放地址,所以,除非是同一个new出来的对象,他们的比较后的结果为true,否则比较后结果为false。JAVA当中所有的类都是继承于Object这个基类的,在Object中的基类中定义了一个equals的方法,这个方法的初始行为是比较对象的内存地址,但在一些类库当中这个方法被覆盖掉了,如String,Integer,Date在这些类当中equals有其自身的实现,而不再是比较类在堆内存中的存放地址了。

3。为什么equals重写后hashCode必须要重写?
    equals重写后可能是按值比较,那么为了能够将对象正常装入散列表不被覆盖所以要重写hashCode方法。比如String和基本数据类型的包装类就是重写了hashCode方法,其hashCode方法是按值给出的散列吗,有可能会导致覆盖问题。所以当用到集合框架中的Set时一定要检查hashCode,因为Set集合必须保证值唯一。


  对于复合数据类型之间进行equals比较,在没有覆写equals方法的情况下,他们之间的比较还是基于他们在内存中的存放位置的地址值的,因为Object的equals方法也是用双等号(==)进行比较的,所以比较后的结果跟双等号(==)的结果相同。

public class TestString  
  1. public static void main(String[] args)  
  2. String s1 "Monday";  
  3. String s2 "Monday";  
  4. if (s1 == s2)  
  5.  
  6. System.out.println("s1 == s2");}  
  7. else{  
  8. System.out.println("s1 != s2");}  
  9.   
  10.   
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 引用同一个 String 对象 -- "Monday"!
2.再稍微改动一下程序,会有更奇怪的发现:
public class TestString  
  1. public static void main(String[] args)  
  2. String s1 "Monday";  
  3. String s2 new String("Monday");  
  4. if (s1 == s2)  
  5. {System.out.println("s1 == s2");}  
  6. else  
  7. {System.out.println("s1 != s2");}  
  8. if (s1.equals(s2)) {System.out.println("s1 equals s2");}  
  9. else{  
  10. System.out.println("s1 not equals s2");}  
  11.  
  12.  
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. 字符串缓冲池
原来,程序在运行的时候会创建一个字符串缓冲池当使用 s2 = "Monday" 这样的表达是创建字符串的时候,程序首先会在这个String缓冲池中寻找相同值的对象,在第一个程序中,s1先被放到了池中,所以在s2被创建的时候,程序找到了具有相同值的 s1
将s2引用s1所引用的对象"Monday"
第二段程序中,使用了 new 操作符,他明白的告诉程序:"我要一个新的!不要旧的!"于是一个新的"Monday"Sting对象被创建在内存中。他们的值相同,但是位置不同,一个在池中游泳一个在岸边休息。哎呀,真是资源浪费,明明是一样的非要分开做什么呢?

4.再次更改程序:
public class TestString  
  1. public static void main(String[] args)  
  2. String s1 "Monday";  
  3. String s2 new String("Monday");  
  4. s2 s2.intern();  
  5. if (s1 == s2)  
  6. {System.out.println("s1 == s2");}  
  7. else  
  8. {System.out.println("s1 != s2");}  
  9. if (s1.equals(s2)) {System.out.println("s1 equals s2");}  
  10. else{  
  11. System.out.println("s1 not equals s2");}  
  12.  
  13.  
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
原来,(java.lang.String的intern()方法"abc".intern()方法的返回值还是字符串"abc",表面上看起来好像这个方法没什么用处。但实际上,它做了个小动作:检查字符串池里是否存在"abc"这么一个字符串,如果存在,就返回池里的字符串;如果不存在,该方法会把"abc"添加到字符串池中,然后再返回它的引用。
  《equals()与==比较》
 Java中的equals是十分重要的,和= =要区别开来,最近在看孙卫琴的JAVA面向对象编程一书,觉得对其阐述写的不错,所以现在小结其
主要内容,而且要将 = =和 equals列为重要的对比概念来学习

1、声明格式
    public  boolean equals(Object obj)
其比较规则为:当参数obj引用的对象与当前对象为同一个对象时,就返回true,否则返回false.
比如以下两个对象animal1和animal2,引用不同的对象,因此用==或equals()方法比较的结果为false;而animal1和animal3变量引用同一个DOg对象,因此用= =或者equals()方法比较的结果为true.

   Animal  animal1=new Dog();
   Animal  animal2=new  Cat();
   Animal animal3=animal1;

则animal1==animal2   (FALSE)
   animal1.equals(animal2)  (false)

   animal1==animal3   (true)
   animal1.equals(animal3)   (true)


而JDK类中有一些类覆盖了oject类的equals()方法,比较规则为:如果两个对象的类型一致,并且内容一致,则返回true,这些类有:
java.io.file,java.util.Date,java.lang.string,包装类(Integer,Double等)

比如
Integer  int1=new Integer(1);
Integer int2=new Integer(1);


String str1=new String("hello");
String str2=new String("hello");

int1==int2   输出:false,因为不同对象
int1.equals(int2)   输出:TRUE

str1==str2   (false)
str1.equals(str2)   (true)
  当然,可以自定义覆盖object类的equals()方法,重新定义比较规则。比如,下面Person类的equals()比较规则为:只要两个对象都是Person类,并且他们的属性name都相同,则比较结果为true,否则返回false

public class Person{
   private String name;
   public Person(String name)
  {
     this.name=name;
   }
public boolean equals(Object o)
{
  if (this==0) return true;
if (!o instanceof Person) return false;
final Person other=(Person)o;
 if (this.name().equals(other.name()))
    return true;
else
  return false;
}

}

注意,在重写equals方法时,要注意满足离散数学上的特性

1、 自反性 :对任意引用值X,x.equals(x)的返回值一定为true.
   对称性:对于任何引用值x,y,当且仅当y.equals(x)返回值为true时,x.equals(y)的返回值一定为true;
   传递性:如果x.equals(y)=true, y.equals(z)=true,则x.equals(z)=true
  一致性:如果参与比较的对象没任何改变,则对象比较的结果也不应该有任何改变
  非空性:任何非空的引用值X,x.equals(null)的返回值一定为false
堆与栈的例子

先看看代码呗

public static void main(String args[])     
  1.         
  2.              
  3.             Integer i1=new Integer(13);     
  4.         Integer i2=new Integer(13);     
  5.         System.out.println(i1==i2);//false   
  6.           
  7.           
  8.         int i3 1150;  
  9.         int i4 1150;  
  10.           
  11.         System.out.println(i3==i4);//true   
  12.           
  13.           
  14.         int i5=13;     
  15.         System.out.println(i2==i5);//true    
  16.         System.out.println(i5==i1);//true   
  17.           
  18.           
  19.         Integer i6 12;  
  20.         Integer i7 12;  
  21.         System.out.println(i6==i7);//true   
  22.         i6 200;   
  23.         i7 200;  
  24.         System.out.println(i6==i7);//false   
  25.                           
  26.         
public static void main(String args[]) { Integer i1=new Integer(13); Integer i2=new Integer(13); System.out.println(i1==i2);//false int i3 = 1150; int i4 = 1150; System.out.println(i3==i4);//true int i5=13; System.out.println(i2==i5);//true System.out.println(i5==i1);//true Integer i6 = 12; Integer i7 = 12; System.out.println(i6==i7);//true i6 = 200; i7 = 200; System.out.println(i6==i7);//false }


第一个问题应该很好理解,i1和i2是包装类型的引用,存放在栈区,new Integer(13)生成了两份,存在于堆区。i1和i2指向了堆内存中不同的地址,所以

System.out.println(i1==i2);//false  
 System.out.println(i1==i2);//false
第二个问题,哇哦,这种基本的都拿出来,其实第二个问题主要是为了和第一个问题做比较。“==”在包装类型之间是地址比较, “==”在基本类型之间是值比较

第三个问题,哎呀,这问题曾经着实让我纠结了很久。以前是这样子想的,因为 i5 是基本类型int的变量,所以i5是存放在栈内存中的,13这个数值也是存放在栈内存中的,i5 指向 13。可以这么理解吧,而引用变量i2指向的是堆内存中13这个integer对象,既然在i2和i3指向的内存地址不同,为什么“System.out.println(i2==i5)” 的结果为 true?

当时是不是脑子不转了,一句话 拆箱之后,值比较。其他就不要想了。

第四个问题看着挺有意思的,都是Integer类型,为什么这里存在一个范围呢?挺诡异的吧。其实看看源码就知道了。这里java在对装箱过程处理的时候,对-128到127之间的数值进行了一个缓存,我猜测,应该是这些数值比较常用,而且在处理的时候效率应该更高一些。

  
  •   
  • // value of java.lang.Integer.IntegerCache.high property (obtained during VM init)   
  • private static String integerCacheHighPropValue;  
  •   
  • static void getAndRemoveCacheProperties()  
  •     if (!sun.misc.VM.isBooted())  
  •         Properties props System.getProperties();  
  •         integerCacheHighPropValu 
  •             (String)props.remove("java.lang.Integer.IntegerCache.high");  
  •         if (integerCacheHighPropValu!= null)  
  •             System.setProperties(props);  // remove from system props   
  •      
  •  
  •   
  • private static class IntegerCache  
  •     static final int high;  
  •     static final Integer cache[];  
  •   
  •     static  
  •         final int low -128;  
  •   
  •         // high value may be configured by property   
  •         int 127;  
  •         if (integerCacheHighPropValu!= null)  
  •             // Use Long.decode here to avoid invoking methods that   
  •             // require Integer's autoboxing cache to be initialized   
  •             int Long.decode(integerCacheHighPropValue).intValue();  
  •             Math.max(i, 127);  
  •             // Maximum array size is Integer.MAX_VALUE   
  •             Math.min(i, Integer.MAX_VALUE -low);  
  •          
  •         high h;  
  •   
  •         cache new Integer[(high low) 1];  
  •         int low;  
  •         for(int 0; cache.length; k++)  
  •             cache[k] new Integer(j++);  
  •      
  •   
  •     private IntegerCache() {}  
  •  
  •   
  •   
  • public static Integer valueOf(int i)  
  •     if(i >= -128 && <= IntegerCache.high)  
  •         return IntegerCache.cache[i 128];  
  •     else  
  •         return new Integer(i);  
  •  
0 0