本文主要是介绍java “==”与equals()比较,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
首先对于"=="
1、对于基本数据类型,比较的是两个值是否相等,对于引用数据类型,比较的是引用对象的存储地址。
2、既然对于引用数据类型,比较的是对象的存储地址,那么可以用“==”比较是否是同一个对象。
3、对于 String a=“abc”,和String a=new String("abc")构造器,所引起的差别。
4、通常对于"=="比较的是栈中的存储数据,例如:基本数据的值,或者引用类型数据的存储地址,equals()比较的则是堆中的内容,当然如果是默认的继承object的equals方法的本质还是"=="。说道这里我会想起hashcode和equals 。详解: java中hashcode()和equals()的详解。
对于特殊的Intenger ,我们知道-128到127之间是他的缓存数据,
其实对于 Intenger a=100;等于Integer a=Intenger.valueOf(100);
Integer integer1 = 100;
Integer integer2 = 100;
上面的两个整形对象的值是相等的。
Integer integer3 = 200;
Integer integer4 = 200;
对于他们则== 比较的时候是false;
我们知道对于equals方法,默认的是equals(object o){this==o},他的本质也是用== 来比较,我们知道equals大部分的时候是比较的内容,例如:set中不能存放相同的对象,这个相同当然不是地址,而是对象的内容是否相同,所以我们需要重写equals方法。 java中hashcode()和equals()的详解。
下面看个例子:
Intenger对象 重写的equals方法
public boolean equals(Object obj) {if (obj instanceof Integer) {return value == ((Integer)obj).intValue();}return false;}
我们可以看到这个比较是两个对象的值,即内容是否一样。
看个例子:加深理解
Integer integer1 = 100;Integer integer2 = 100;System.out.println("integer1==integer2: " + (integer1 == integer2));// true 自动装箱的两个缓存中的 Integer对象的引用比较System.out.println("integer1.equals(integer2): " + (integer1.equals(integer2)));// trueSystem.out.println("integer1.compare(integer2): " + integer1.compareTo(integer2));// 0 Integer integer3 = 200;Integer integer4 = 200;System.out.println("integer3==integer4: " + (integer3 == integer4));// false 自动装箱的两个new Integer的引用比较System.out.println("integer3>integer4: " + (integer3 > integer4)); // false 将两个对象拆箱,再比较大小System.out.println("integer3.equals(integer4): " + (integer3.equals(integer4)));// trueSystem.out.println("integer3.compare(integer4): " + integer3.compareTo(integer4));// 0 Integer integer5 = new Integer(100);Integer integer6 = new Integer(100);System.out.println("integer5==integer6: " + (integer5 == integer6)); // false 两个不同的Integer对象引用的比较System.out.println("integer5.equals(integer6): " + (integer5.equals(integer6)));// trueSystem.out.println("integer5.compare(integer6): " + integer5.compareTo(integer6));// 0 int int1 = 100;System.out.println("integer1==int1: " + (integer1 == int1));// true Integer缓存对象拆箱后与int比较System.out.println("integer1.equals(int1): " + (integer1.equals(int1)));// trueSystem.out.println("integer1.compare(int1): " + integer1.compareTo(int1));// 0 int int2 = 200;System.out.println("integer3==int2: " + (integer3 == int2));// true Integer对象拆箱后与int比较System.out.println("integer3.equals(int2): " + (integer3.equals(int2)));// true System.out.println("integer3.compare(int2): " + integer3.compareTo(int2));// 0
Java 1.5中引入了自动装箱和拆箱机制:
(1)自动装箱:把基本类型用它们对应的引用类型包装起来,使它们具有对象的特质,可以调用toString()、hashCode()、getClass()、equals()等方法。
如下:
Integer a=3;//这是自动装箱
其实编译器调用的是static Integer valueOf(int i)这个方法,valueOf(int i)返回一个表示指定int值的Integer对象,那么就变成这样:
Integer a=3; => Integer a=Integer.valueOf(3);
(2)拆箱:跟自动装箱的方向相反,将Integer及Double这样的引用类型的对象重新简化为基本类型的数据。
如下:
int i = new Integer(2);//这是拆箱
编译器内部会调用int intValue()返回该Integer对象的int值
注意:自动装箱和拆箱是由编译器来完成的,编译器会在编译期根据语法决定是否进行装箱和拆箱动作。
Java使用自动装箱和拆箱机制,节省了常用数值的内存开销和创建对象的开销,提高了效率。通过上面的研究和测试,结论如下:
(1)Integer和 int之间可以进行各种比较;Integer对象将自动拆箱后与int值比较
(2)两个Integer对象之间也可以用>、<等符号比较大小;两个Integer对象都拆箱后,再比较大小
(3) 两个Integer对象最好不要用==比较。因为:-128~127范围(一般是这个范围)内是取缓存内对象用,所以相等,该范围外是两个不同对象引用比较,所以不等。
下面转载自大神的,自己懒得写了,更加想写的介绍:
概述:
详述:
String str1 = "abc";
String str2 = "abc";
String str3 = str1+str2;
String str4 = str1+"cd";
String str5 = "ab"+str2;
String str6 = "ab"+"cd"; //这种创建方式是放入字符串池的.这种情况实际上是创建了1个对象,abcd"1个对象
String str7 = "abcd";
System.out.println(str1==str2); //返回ture
System.out.println(str6==str7); //返回ture
我们首先来看一段 Java代码:
String str=new String("abc");
紧接着这段代码之后的往往是这个问题,那就是这行代码究竟创建了几个String对象呢?相信大家对这道题并不陌生,答案也是众所周知的,2个。接下来我们就从这道题展开,一起回顾一下与创建String对象相关的一些JAVA知识。
我们可以把上面这行代码分成String str、=、"abc"和new String()四部分来看待。String str只是定义了一个名为str的String类型的变量,因此它并没有创建对象;=是对变量str进行初始化,将某个对象的引用(或者叫句柄)赋值给它,显然也没有创建对象;现在只剩下new String("abc")了。那么,new String("abc")为什么又能被看成"abc"和new String()呢?我们来看一下被我们调用了的String的构造器:
Java代码
public String(String original) {
//other code ...
}
大家都知道,我们常用的创建一个类的实例(对象)的方法有以下两种:
我们正是使用new调用了String类的上面那个构造器方法创建了一个对象,并将它的引用赋值给了str变量。同时我们注意到,被调用的构造器方法接受的参数也是一个String对象,这个对象正是"abc"。
使用new创建对象是调用Class类的newInstance方法,利用反射机制创建对象。
String a=new String("foo");
String b=new String("foo");
两条new语句创建了两个对象,然后用a,b这两个变量分别指向了其中一个对象,这是两个不同的对象,他们的首地址是不同的,即a和b中存储的数值是不相同的,所以,表达式a==b即返回false,而这两个对象中内容是相同的,所以,表达式a.equals(b)将返回true。
boolean equals(Object o){
return this==o;
}
这说明,如果一个类没有自己定义equals方法,它默认的equals方法(从Object类继承的)就是使用==操作符,也是比较两个变量指向的对象是否是同一个对象,这时候使用equals和使用==会得到同样的结果,如果比较的是两个独立的对象则总返回false。如果你编写的类希望能够比较该类创建的两个实例对象的内容是否相同,那么你必须覆盖equals方法,由你自己写代码来决定在什么情况即可以认为两个对象的内容是相同的。
public class Test { public static void main(String[] args) { Integer p = 1; Integer q = 1;Integer i = new Integer(1); Integer j = new Integer(1);if(p == q){ System.out.println("integer:p == q"); //实际结果}else{ System.out.println("integer:p != q"); }if(p.equals(q)){System.out.println("integer:p.equals(q)"); //实际结果 }else{ System.out.println("integer:p.equals(q)");}if(i == j){ System.out.println("int:i == j"); }else{ System.out.println("int:i != j"); //实际结果} if(i.equals(j)){ System.out.println("integer:i.equals(j)");//实际结果}else{System.out.println("integer:!i.equals(j)");} String a = "abc"; String b = "abc"; String c = new String("abc"); String d = new String("abc"); if(a == b){ System.out.println("abc对象相等"); //实际结果}else{ System.out.println("abc对象不相等"); } if(a.equals(b)){ System.out.println("ab相等"); //实际结果}else{ System.out.println("ab不相等"); } if(c.equals(d)){ System.out.println("cd相等"); //实际结果}else{ System.out.println("cd不相等"); } if(c == d){ System.out.println("cd对象相等"); }else{ System.out.println("cd对象不相等"); //实际结果} } }
深入探讨equals:
===================
转自硅谷动力
equals方法的重要性毋须多言,只要你想比较两个对象是不是同一对象,你就应该实现equals方法,让对象用你认为相等的条件来进行比较.
下面的内容只是API的规范,没有什么太高深的意义,但我之所以最先把它列在这儿,是因为这些规范在事实中并不是真正能保证得到实现.
1.对于任何引用类型, o.equals(o) == true成立.
2.如果 o.equals(o1) == true 成立,那么o1.equals(o)==true也一定要成立.
3.如果 o.equals(o1) == true 成立且 o.equals(o2) == true 成立,那么
o1.equals(o2) == true 也成立.
4.如果第一次调用o.equals(o1) == true成立,在o和o1没有改变的情况下以后的任何次调用都成立.
5.o.equals(null) == true 任何时间都不成立.
以上几条规则并不是最完整的表述,详细的请参见API文档.对于Object类,它提供了一个最最严密的实现,那就是只有是同一对象时,equals方法才返回true,也就是人们常说的引用比较而不是值比较.这个实现严密得已经没有什么实际的意义, 所以在具体子类(相对于Object来说)中,如果我们要进行对象的值比较,就必须实现自己的equals方法.先来看一下以下这段程序:
{
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);
}
这是JDK中java.text.FieldPosition的标准实现,似乎没有什么可说的. 我信相大多数或绝大多数程序员认为,这是正确的合法的equals实现.毕竟它是JDK的API实现啊. 还是让我们以事实来说话吧:
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运行符来进行短路优化的,实事求是地说很长一段时间我也这么用过。
太多的教程,文档都给了我们这样的误导。而有些稍有了解的程序员可能知道这样的优化可能有些不对但找不出问题的关键。另外一种极端是知道这个技术缺陷的骨灰级专家就提议不要这样应用。我们知道,"通常"要对两个对象进行比较,那么它们"应该"是同一类型。所以首先利用instanceof运算符进行短路优化,如果被比较的对象不和当前对象是同一类型则不用比较返回false。
但事实上,"子类是父类的一个实例",所以如果子类 o instanceof 父类,始终返回true,这时肯定不会发生短路优化,下面的比较有可能出现多种情况,一种是不能造型成父类而抛出异常,另一种是父类的private 成员没有被子类继承而不能进行比较,还有就是形成上面这种不对称比较。可能会出现太多的情况。
那么,是不是就不能用instanceof运算符来进行优化?答案是否定的,JDK中仍然有很多实现是正确的,如果一个class是final的,明知它不可能有子类,为什么不用 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 check是if(this==obj) return true;
是否equals方法比较的两个对象一定是要同一类型?上面我用了"通常",这也是绝大多数程序员的愿望,但是有些特殊的情况,我们可以进行不同类型的比较,这并不违反规范。但这种特殊情况是非常罕见的,一个不恰当的例子是,Integer类的equals可以和Sort做比较,比较它们的value是不是同一数学值。(事实上JDK的API中并没有这样做,所以我才说是不恰当的例子)在完成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
}
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要求返回相等的值。
这篇关于java “==”与equals()比较的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!