== 和 equals 的区别

来源:互联网 发布:淘宝要怎么做推广 编辑:程序博客网 时间:2024/06/06 02:36

在讲 == equals 的区别前我们需要确认一下 Java 中的数据类型。在Java中有基本数据类型和引用数据类型两种。
1
8种基本数据类型:
四种整数类型(byteshortintlong)
两种浮点数类型(float
double)
一种字符类型
(char)
一种布尔类型
(boolean)
2
、引用数据类型:

除了上面我们说的 8 种基本数据类型外,其他在Java中出现的类型都是引用数据类型。如我们自己写了一个Dog类,而在上面说的8种基本数据类型中不包括Dog类型,所以Dog类型是隶属于引用数据类型的。

==的理解:

要理解 == 我们学要从两个方面来理解:

1 == 两边为 8 种基本数据类型时:
   
==
两边为8种基本数据类型时, == 判断的是两边变量中存放的内容是否相等。例:
public class Test {

 public static void main(String[] args) {
  int a = 10;
  int b = 10;
  if (a == b) {
   System.out.println("
变量a和变量b中的值相等");
  }
 }

}

上面的例子的运行结果是在控制台中输出"变量a和变量b中的值相等我们可以看出当 == 两边为8种基本数据类型时比较的时内容换句话说就是比较变量的值。

2 == 两边为引用数据类型时:
    
== 两边为
引用数据类型时,== 判断的是引用数据类型的对象的内存地址是否一样,如果 == 比较的内存地址一样表示 == 两边是同一个对象,否则 == 两边不是同一个对象。例:
public class Test {

 public static void main(String[] args) {
            //
8中数据类型中没有String类型所以String是属于引用数据类型的
  String str1 = new String("abc");
            //
在这里在创建一个str2对象让他的值和str1一样
  String str2 = new String("abc");
            //
我们在用 == 来进行判断会发现 str1 == str2条件不成立我们可以看出这里比较的不是str1str2的值
  if (str1 == str2) {
   System.out.println("
对象str1和对象str2是同一对象,他们的内存地址相同");
  } else {
   System.out.println("
对象str1和对象str2不是同一对象,他们的内存地址不相同
");
  }
 }
}

equals的理解:
equals
的理解就容易多了,equals他是一个方法,而
方法只有对象才能拥有,所以equals的左边一定是一个对象他的语法格式因该是:对象名.equals(比较的内容)
例:

public class Test {

 public static void main(String[] args) {
  String str1 = new String("abc");
        //equals
的括号中可以直接写入要比较的内容
  if (str1.equals("abc")) {
   System.out.println("
对象str1的内容和 abc 相同");
  }

String str2 = new String("abc");
        //equals
括号中也可以是一个要比较的对象,注意这里比较的是str1对象和str2对象的内容
  if (str1.equals(str2)) {
   System.out.println("
对象str1的内容和 str2 的内容相同");
  }

 }
}

 

 

Java中的equals是十分重要的,和= =要区别开来, = = equals
1
、声明格式
public boolean equals(Object obj)

其比较规则为:当参数obj引用的对象与当前对象为同一个对象时,就返回true,否则返回
false.


比如以下两个对象animal1animal2,引用不同的对象,因此用==equals()方法比较的结果为false;animal1animal3变量引用同一个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
、自反性 :对任意引用值Xx.
equals(x)的返回值一定为true.
2
对称性: 对于任何引用值x,y,当且仅当y.
equals(x)返回值为true时,x.equals(y)的返回值一定为true;
3
传递性:如果x.
equals(y)=true, y.equals(z)=true,x.equals(z)=true
4
一致性:如果参与比较的对象没任何改变,则对象比较的结果也不应该有任何改变
5
非空性:任何非空的引用值Xx.
equals(null)的返回值一定为false

equals方法的重要性毋须多言,只要你想比较的两个对象不愿是同一对象,你就应该实现equals方法,让对象用你认为相等的条件来进行比较。

  下面的内容只是API的规范,没有什么太高深的意义,但我之所以最先把它列在这儿,是因为这些规范在事实中并不是真正能保证得到实现。


对于任何引用类型, o.
equals(o) == true成立。
如果 o.
equals(o1) == true 成立,那么o1.equals(o)==true也一定要成立。
如果 o.
equals(o1) == true 成立且 o.equals(o2) == true 成立,那么o1.equals(o2) == true 也成立。
如果第一次调用o.equals(o1) == true成立再oo1没有改变的情况下以后的任何次调用都成立。

o.equals(null) == true
任何时间都不成立。

  以上几条规则并不是最完整的表述,详细的请参见API文档。


  对于Object,它提供了一个最最严密的实现,那就是只有是同一对象是,equals方法才返回true,也就是人们常说的引用比较而不是值比较。这个实现严密得已经没有什么实际的意义,所以在具体子类(相对于Object来说),如果我们要进行对象的值比较,就必须实现自己的equals方法。


  先来看一下以下这段程序
:

public boolean equals(Object obj)
{
if (obj == null) return false;
if (!(obj instanceof FieldPosition))
return false;
FieldPosition other = (FieldPosition) obj;
if (attribute == null) {
if (other.attribute != null) {
return false;
}
}
else if (!attribute.equals(other.attribute)) {
return false;
}
return (beginIndex == other.beginIndex
&& endIndex == other.endIndex
&& field == other.field);
}
  这是JDKjava.text.FieldPosition的标准实现,似乎没有什么可说的. 我相信大多数或绝大多数程序员认为,这是正确的合法的equals实现.毕竟它是JDKAPI实现啊. 还是让我们以事实来说话吧
:

package debug;
import java.text.*;
public class Test {
public static void main(String[] args) {
FieldPosition fp = new FieldPosition(10);
FieldPosition fp1 = new MyTest(10);
System.out.println(fp.equals(fp1));
System.out.println(fp1.
equals(fp));
}
}
class MyTest extends FieldPosition{
int x = 10;
public MyTest(int x){
super(x);
this.x = x;
}
public boolean equals(Object o){
if(o==null) return false;
if(!(o instanceof MyTest )) return false;
return ((MyTest)o).x == this.x;
}
}
   运行一下看看会打印出什么
:

System.out.println(fp.equals(fp1));
打印
true
System.out.println(fp1.equals(fp));
打印flase  两个对象,出现了不对称的equals算法.问题出在哪里(脑筋急转弯:当然出在JDK实现的
BUG)?

  我相信有太多的程序员(除了那些根本不知道实现equals方法的程序员外)在实现equals方法时都用过instanceof运行符来进行短路优化的,实事求是地说很长一段时间我也这么用过。太多的教程,文档都给了我们这样的误导。而有些稍有了解的程序员可能知道这样的优化可能有些不对但找不出问题的关键。另外一种极端是知道这个技术缺陷的骨灰级专家就提议不要这样应用。


  我们知道,"通常"要对两个对象进行比较,那么它们"应该"是同一类型。所以首先利用nstanceof运行符进行短路优化,如果被比较的对象不和当前对象是同一类型则不用比较返回false,但事实上,"子类是父类的一个实例",所以如果 子类 o instanceof 父类,始终返回true,这时肯定不会发生短路优化,下面的比较有可能出现多种情况,一种是不能造型成子类而抛出异常,另一种是父类的private 成员没有被子类继承而不能进行比较,还有就是形成上面这种不对称比较。可能会出现太多的情况。


  那么,是不是就不能用 instanceof运行符来进行优化?答案是否定的,JDK中仍然有很多实现是正确的,如果一个classfinal的,明知它不可能有子类,为什么不用 instanceof来优化呢?


  为了维护SUN的开发小组的声誉,我不说明哪个类中,但有一个小组成员在用这个方法优化时在后加加上了加上了这样的注释:


if (this == obj) // quick check
return true;
if (!(obj instanceof XXXXClass)) // (1) same object?
return false;
  可能是有些疑问,但不知道如何做(不知道为什么没有打电话给我......


  那么对于非final类,如何进行类型的quick check呢?


if(obj.getClass() != XXXClass.class) return false;
  用被比较对象的class对象和当前对象的class比较,看起来是没有问题,但是,如果这个类的子类没有重新实现equals方法,那么子类在比较的时候,obj.getClass() 肯定不等于XXXCalss.class,也就是子类的equals将无效,所以if(obj.getClass() != this.getClass()) return false;才是正确的比较。

另外一个quick checkif(this==obj) return true;

  是否equals方法一定比较的两个对象就一定是要同一类型?上面我用了"通常",这也是绝大多数程序员的愿望,但是有些特殊的情况,我们可以进行不同类型的比较,这并不违反规范。但这种特殊情况是非常罕见的,一个不恰当的例子是,Integer类的equals可以和Sort做比较,比较它们的value是不是同一数学值。(事实上JDKAPI中并没有这样做,所以我才说是不恰当的例子)。在完成quick check以后,我们就要真正实现你认为的相等。对于如果实现对象相等,没有太高的要求,比如你自己实现的类,你可以认为只要name相同即认为它们是相等的,其它的sex,ago都可以不考虑。这是不完全实现,但是如果是完全实现,即要求所有的属性都是相同的,那么如何实现equals方法?


class Human{
private String name;
private int ago;
private String sex;
....................
public boolean equals(Object obj){
quick check.......
Human other = (Human)ojb;
return this.name.equals(other.name)
&& this.ago == ohter.ago
&& this.sex.equals(other.sex);
}
}
   这是一个完全实现,但是,有时equals实现是在父类中实现,而要求被子类继承后equals能正确的工作,这时你并不事实知道子类到底扩展了哪些属性,所以用上面的方法无法使equals得到完全实现。


  一个好的方法是利用反射来对equals进行完全实现:


public boolean equals(Object obj){
quick check.......
Class c = this.getClass();
Filed[] fds = c.getDeclaredFields();
for(Filed f:fds){
if(!f.get(this).equals(f.get(obj)))
return false;
}
return true;
}
  为了说明的方便,上明的实现省略了异常,这样的实现放在父类中,可以保证你的子类的equals可以按你的愿望正确地工作。关于equals方法的最后一点是:如果你要是自己重写(正确说应该是履盖)了equals方法,那同时就一定要重写hashCode().为是规范,否则
.............

  我们还是看一下这个例子:


public final class PhoneNumber {
private final int areaCode;
private final int exchange;
private final int extension;
public PhoneNumber(int areaCode, int exchange, int extension) {
rangeCheck(areaCode, 999, "area code");
rangeCheck(exchange, 99999999, "exchange");
rangeCheck(extension, 9999, "extension");
this.areaCode = areaCode;
this.exchange = exchange;
this.extension = extension;
}
private static void rangeCheck(int arg, int max, String name) {
if(arg < 0 || arg > max)
throw new IllegalArgumentException(name + ": " + arg);
}
public boolean equals(Object o) {
if(o == this)
return true;
if(!(o instanceof PhoneNumber))
return false;
PhoneNumber pn = (PhoneNumber)o;
return pn.extension == extension && pn.exchange == exchange && pn.areaCode == areaCode;
}
}
  注意这个类是final的,所以这个equals实现没有什么问题。


  我们来测试一下
:

public static void main(String[] args) {
Map hm = new HashMap();
PhoneNumber pn = new PhoneNumber(123, 38942, 230);
hm.put(pn, "I love you");
PhoneNumber pn1 = new PhoneNumber(123, 38942, 230);
System.out.println(pn);
System.out.println("pn.equals(pn1) is " + pn.equals(pn1));
System.out.println(hm.get(pn1));
System.out.println(hm.get(pn));
}
  既然pn.equals(pn1),那么我put(pn,"I love you");后,get(pn1)这什么是null呢?


  答案是因为它们的hashCode不一样,而hashMap就是以hashCode为主键的。

所以规范要求,如果两个对象进行equals比较时如果返回true,那么它们的hashcode要求返回相等的值。

 

1.如果是基本变量,没有hashcodeequals方法,基本变量的比较方式就只有==

2.如果是变量,由于在java中所有变量定义都是一个指向实际存储的一个句柄(你可以理解为c++中的指针),在这里==是比较句柄的地址(你可以理解为指针的存储地址),而不是句柄指向的实际内存中的内容,如果要比较实际内存中的内容,那就要用equals方法,但是!!!

如果是你自己定义的一个类,比较自定义类用equals==是一样的,都是比较句柄地址,因为自定义的类是继承于object,而object中的equals就是用==来实现的,你可以看源码。

那为什么我们用的String等等类型equals是比较实际内容呢,是因为String等常用类已经重写了object中的equals方法,让equals来比较实际内容,你也可以看源码。

3. hashcode

在一般的应用中你不需要了解hashcode的用法,但当你用到hashmaphashset等集合类时要注意下hashcode

你想通过一个objectkey来拿hashmapvaluehashmap的工作方法是,通过你传入的objecthashcode在内存中找地址,当找到这个地址后再通过equals方法来比较这个地址中的内容是否和你原来放进去的一样,一样就取出value

所以这里要匹配2部分,hashcodeequals

但假如说你new一个object作为key去拿value是永远得不到结果的,因为每次new一个object,这个objecthashcode是永远不同的,所以我们要重写hashcode,你可以令你的hashcodeobject中的一个恒量,这样永远可以通过你的objecthashcode来找到key的地址,然后你要重写你的equals方法,使内存中的内容也相等。。。 

==是判断两个变量或实例是不是指向同一个内存空间
equals
是判断两个变量或实例所指向的内存空间的值是不是相同

 摘自百度,很实用。

原创粉丝点击