又是==和equals(100分)
不行了,搞不懂,请教
据查Object类的equals()定义如下:
public boolean equals(Object x)
{
return this==x;// 其最终还是用“==”比较,因此两个对象比较真值要重载重写 equals,但是下面的测试结果就很耐人寻味
}
a和z1有什么区别,一个是分配了堆空间,一个是栈?或者a是一个指向堆空间的指针而z1难道就不是指针了吗?它们之间的分别难道源自“=”的运算符重载调用了不同的成员函数?
注意:下面的结果都经过测试,equals没有进行重写!
Try x1=new Try( "aafasf ");
Try x2=new Try( "aafasf ");
String a=new String( "asdsfa ");
String b=new String( "asdsfa ");
String c=new String( "asdsfl ");
String z1= "asdsfa ";
String z2= "asdsfa ";
String z3= "asdsfl ";
-----------------------
if(x1==x2) //no,这个不用说
System.out.print( "t String ok\n ");
else
System.out.print( "t String no\n ");
-----------------------
if(a==b) //no,
System.out.print( "a,b Stringnew ok\n ");
else
System.out.print( "a,b Stringnew no\n ");
a和b是个指向空间的指针(句柄)那么就没问题了,但是a.equals(b)怎么就为真呢?
因为实质上也是“==”
-----------------------
if(a==c) //no
System.out.print( "a,c Stringnew ok\n ");
else
System.out.print( "a,c Stringnew no\n ");
是地址比较还是真值比较呢?应该是地址吧
-----------------------
if(z1==z2) //ok
System.out.print( "a,b String ok\n ");
else
System.out.print( "a,b String no\n ");
z1和z2假如也是指向存储空间的,这个应该不相等吧,可结果~~
到底String类型的new分配和“=”赋值有何具体不同呢?
-----------------------
if(z1==z3) //no
System.out.print( "a,b String ok\n ");
else
System.out.print( "a,b String no\n ");
看样子z1和z3是比较真值
-----------------------
if(a.equals(b)) //ok
System.out.print( "a,equals,b Stringnew ok\n ");
else
System.out.print( "a,equals,b Stringnew no\n ");
怎么为真呢,因为实质上也是“==”比较(a==b为假)
-----------------------
if(x1.equals(x2)) //no
System.out.print( "x1,equals,x2 trynew ok\n ");
else
System.out.print( "x1,equals,x2 Stringnew no\n ");
这个不用说,比较的不是String,也说明equals并不是说的比较真值那么简单,他还有隐藏更深的秘密
-----------------------
if(z1.equals(z2)) //ok
System.out.print( "a,equals,b String ok\n)
else
System.out.print( "a,equals,b String no\n ");
这个也奇迹般的为真
-----------------------
if(z1.equals(z3)) //no
System.out.print( "a,equals,b String ok\n ");
else
System.out.print( "a,equals,b String no\n);
-----------------------
if(x1.equals(x2)) //no
System.out.print( "String1 ok\n ");
else
System.out.print( "String1 no\n ");
-----------------------
if(x1.u.equals(x2)) //no
System.out.print( "String2 ok\n ");
else
System.out.print( "String2 no\n ");
-----------------------
if(x1.u==x2.u) //ok
System.out.print( "String3 ok\n ");
else
System.out.print( "String3 no\n ");
由这两个可以看出“==”也确实是值比较,关键看这个值是地址还是真值
-----------------------
if(x1.u.equals(x2.u)) //ok
System.out.print( "String4 ok\n ");
else
System.out.print( "String4 no\n ");
[解决办法]
我因为还是有地址的,变量引用的就是地址,这个地址就是指向这个对像的起始地址。
所说要比较两个变量的值是否相同,就是表示它们所引用的地址是否相同,如果相同,那么肯定是相等的了。
A a=new A();
A b=new A();创建了两个对像,所以a!=b;
A a = new A();
A b = a;只创建一个对像,都指向同一个起始地址a==b;
equals(Object x)
首先,this就是指向此对像的地址。
然后就是判断这个x指向的地址是不是等于this,也就是说是不是指向对一个对像。如果相等,那么它们必然是相等的。
一般情况这个equals不会这样,所以说一般来都要覆盖这个方法。
[解决办法]
你可能是没有搞明白String 类与其他的类的区别 ,由于String 类是一个特殊的类,所以一些问题在他上面会有不同;
其实我们都知道 String 类对象的创建方法有两种:
先讲第一种
1.String str1=new String( "ABC ");
String str2=new String( "ABC ");
str1,str2 这两个引用指向的是堆空间不同的地址, 这两个地址就是 刚才new 所产生的两个Sting 类的对象 , 如果你比较 str1==str2 则比较的是他们的地址; 所以结果是false;
关于equals()方法;
equals()方法是从Object继承过来的,而所有的类又都是Object的间接或直接字类
先看Object 类的equals()方法
public boolean equals(Object obj) {
return (this == obj);
}
他就是简单比较一下传入的 Object obj 引用和 this (既本象的引用)是不是指向的同一块地址,还是比较地址;而String 类继承了 覆盖了equals()方法
String 类的equals()方法:
public boolean equals(Object anObject) {
if (this == anObject) {
return true;
}
if (anObject instanceof String) {
String anotherString = (String)anObject;
int n = count;
if (n == anotherString.count) {
char v1[] = value;
char v2[] = anotherString.value;
int i = offset;
int j = anotherString.offset;
while (n-- != 0) {
if (v1[i++] != v2[j++])
return false;
}
return true;
}
}
return false;
}
它做的则是 把里面的字符都比较一遍 所以 就会有
str1==str2 是false;
str1.equals(str2)是 true 了; 如果你的类没有覆盖从Object类继承来的equals()方法则是比较地址
第二种
2.String str3= "ABC ";
String str4= "ABC ";
我们都知道String 是一个类,所以按照常理来说他的对象是不应该用这种方式创建的,而应new 一个对象,但由于他的常用性所以java提供了这种方式,而这种的方式创建的对象也是特殊的,
使用这种方式创建 字符串,JVM会采用 字符串常量池 的方式来存储和维护 字符串,
在创建 str3时 JVM首先会到常量池里面(而不是保存普通对象堆区里)去查看有没有一块地址里放的是 "ABC " 如果没有则在常量池里面创建一块地址 把 "ABC "放到里面.
当再次用这种方式又创建一个了str4 时,会同样先到 字符常量池里面 查看有没有一块地址里放的是 "ABC " 这时由于已经存在了 所以 就把 str4直接指向他(不去再创建一块新的地址了);
所以会有 str3==str4 true;
str3.equals(str4) true;
------------------------------------------------
在给个思考题看看
String str5= "A "+ "BC ";
String str6= "A ";
String str7=str6+ "BC ";
System.out.println(str3==str5);
System.out.println(str3==str7);
会是什么结果哪?
[解决办法]
你这样理解就不对了,他们原理其实是一样的~
public class A {
int a;
public int getA() {
return a;
}
public void setA(int a) {
this.a = a;
}
/**
* @param args
*/
public static void main(String[] args) {
A a1 = new A();
A a2 = new A();
A a3 = a1;
a1.setA(10);
a2.setA(10);
System.out.println(a1.equals(a2));
System.out.println(a1 == a2);
System.out.println(a1.equals(a3));
System.out.println(a1 == a3);
System.out.println(new A() == new A());
System.out.println(new A().equals(new A()));
}
}
看看这个简单的class,输出的6个结果依次是:false,false,true,true,false,false!
why?
其实这就牵涉到java数据的存储方式了:
简单一点说,基本类型及引用类型存在stack(堆栈),具体对象存放在heap(堆)中。
解析A a2 = new A();
A a2定义一个引用,存放在stack;
new A()在heap中为A申请一块空间,也就是定义一个A对象;
a2=new A();把heap中的那块空间的首地址存放到a2里面.
==属于数学运算符,只能对数据比较,
为什么会出现a1 == a2?其实这是在比较a1,a2中存放的数据,也就是比较的两块地址,
如果这两块地址相等(那就是同一块空间了!),那么a1==a2就会返回true,
但能出现这种情况的只能是两个对象其实是一个,就像a1,a3,他们指向同一块地址!
对于equal而言,定义
public boolean equals(Object x){
return this==x
}
显然比较的是两个地址,所以System.out.println(a1.equals(a2));返回false。
但由于equals是一个函数,因此我们可以重载这个函数,来定义我们自己的比较规则,
这就是为什么 "aaa ".equal(new String( "aaa ")),因为String重载了equal,定义他们自己的比较规则,与此同时,这里又牵涉到java的另一个关键,子类重载父类方法后,由子类调用该方法只能是子类的方法,而不能使父类的方法!
附:String的重载equal:
public boolean equals(Object anObject) {
if (this == anObject) {
return true;
}
if (anObject instanceof String) {
String anotherString = (String)anObject;
int n = count;
if (n == anotherString.count) {
char v1[] = value;
char v2[] = anotherString.value;
int i = offset;
int j = anotherString.offset;
while (n-- != 0) {
if (v1[i++] != v2[j++])
return false;
}
return true;
}
}
return false;
}
看到了吧^_^
所以 ,
==是比较地址,若两个对象用==连接返回true,那他们肯定是同一个对象;
equal比较对象时需要你去给他们定义比较规则,否则默认用==比较,也就是地址比较!!
还有什么疑问吗~
[解决办法]
if(a.equals(b)) //ok
System.out.print( "a,equals,b Stringnew ok\n ");
else
System.out.print( "a,equals,b Stringnew no\n ");
怎么为真呢,因为实质上也是“==”比较(a==b为假)
if(z1.equals(z2)) //ok
System.out.print( "a,equals,b String ok\n)
else
System.out.print( "a,equals,b String no\n ");
这个也奇迹般的为真
这时的a,b,z1,z2都是String 类型,会调用STRING类里重载的equals方法
方法如下
public boolean equals(Object anObject) {
if (this == anObject) {
return true;
}
if (anObject instanceof String) {
String anotherString = (String)anObject;
int n = count;
if (n == anotherString.count) {
char v1[] = value;
char v2[] = anotherString.value;
int i = offset;
int j = anotherString.offset;
while (n-- != 0) {
if (v1[i++] != v2[j++])
return false;
}
return true;
}
}
return false;
}
我们分析可以看到,首先是地址比较,如果是同一个对象,返回TRUE
如果地址不同,比较内容,如果内容相同的话也会返回TRUE
这个方法还是很简单的,看一看吧
[解决办法]
String str1 = "A " + "B " + "C ";
String str2 = "AB " + "C ";
为什么System.out.println(str1 == str2);
会返回true?!!
这又牵涉到另一个规则,java用到临时变量时,不会直接去创建,而是先查找heap中是否有满足条件的数据,有的话就去取,没有的话才新创建!!
String str1 = "A " + "B " + "C ";执行完后,“ABC”就会存在内存中,并且str1指向它;
当运行 "AB " + "C ";时,发现“ABC”已经存在了,那么str2就直接指向也取指向它,也就是str1与str2其实指的是同一个地址,因此str1==str2!
[解决办法]
没必要把问题复杂化把.
Try x1=new Try( "aafasf ");
Try x2=new Try( "aafasf ");
x1!=x2 因为x1,x2是不同的对象引用,它们比较的是引用指向的地址
x1.equals(x2)==false; Try类没有覆盖Object的equals方法,x1.equals(x2)用的是Object中的继承的equals方法,即相当于x1==x2,道理同上.
String a=new String( "asdsfa ");
String b=new String( "asdsfa ");
String c=new String( "asdsfl ");
a!=b / a!=b / a!=c 因为a,b,c也都是对象引用, 他们分别指向堆中3个String对象,它们比较的是引用指向的地址
a.equals(b) == true, a.equals(c) == false, String类覆盖了Object的equals方法,其比较的是a,b,c的值
String z1= "asdsfa ";
在给z1赋值前, "asdsfa "在池中不存在,运行完后,池中创建 "asdsfa ",并把 "asdsfa "的地址赋给z1
String z2= "asdsfa ";
JVM先在池中寻找是否已经存在 "asdsfa "字符串,存在则直接把它的地址赋给z2
String z3= "asdsfl ";
JVM在池中没找到 "asdsfl ",所以创建一个 "asdsfl "放如池,并把其地址赋给z3.
所以,在上面3条语句完成后,池中有2个串 "asdsfa ", "asdsfl ".
z1==z2 它们指向的都是池中同一地址(比较的是地址)
z1!=z3 它们指向的是不同的串的地址(同上)
又因为z1,z2,z3都是String类型的,所以z1.equals(z2)==true,z1.equals(z3)==false,它们此时比较的z1,z2,z3的值,(因为String类覆盖了Object的equals方法)
差不多就这些了,都是些概念,总结一下就3点
==比较的两个对象的地址
未覆盖Object的equals方法比较的还是两个对象的地址,同==
覆盖Object的equals方法,一般需要你自己根据具体要求来实现什么情况下2个对象相等.
以下是一个一般的实现,Try类,一个String x; 一个int y;
public boolean equals(Object o) {
if (o == null) return false; //如果传过来的是null,也没什么好比的了
if (this == o) return true; // 如果传过来的对象引用与当前对象是指向同一个地址,它们一定相等
if (!(o instanceof Try)) return false; //如果传过来的不是Try类型的对象,也没有比的比较性了
Try try = (Try)o; //到这里就一定能确定传过来的对象是Try类型的(或其子类型)
//比较他们的实例字段(因为x是String类型的,所以比较它们的值要用String的equals方法,不能用==,而y是基本类型,所以可以==比较),当它们的所以实例字段都相等了,也就可以认为这2个对象相等.否则不相等(这里相等的逻辑要依据实际的业务需要,你也可以认为这2个对象其中一个实例字段相等,这2个对象就相等)
if ( x.equals(try.x) && y==try.y) return true
else return false;
}
如果传近来的Try是Base类的子类
class Try extends Base
而Base实现了以上的equals方法则Try类中的equals应改成以下模式
public boolean equals(Object o) {
if(super.equals(o) && 子类的字段比较) return true;
else return false;
}
写的比较快,有错误枉指正.
[解决办法]
/*
首先明确第一点:“==”运算符是一个二元运算符,
如果左右两边的操作数的类型都是Object或Object的子类时,
实际参与比较的操作数是指向Object或Object的子类的实例的引用,
引用可以简单理解为一个存放Object或Object的子类的实例的“内存地址”,
JVM内部是用“if_acmpeq”指令来对应源代码级别“==”运算符的引用比较,
引用(“内存地址”)相等,“==”运算符的结果就为true。
第二点:对于每一个不同的字面字符串(literal string),JVM会根据程序运行
的需要生成相应的java.lang.String类的实例,String类的实例及其引用会存放
在一个特殊的列表中,JVM每次遇到一个字面字符串就会先去列表中查找,如果该
字面字符串在前面已出现过,不会另外生成一个java.lang.String类的实例,而是
直接返回已找到的String类的实例引用。
例如:
String a= "123 ";//此时字面字符串 "123 "是第一次出现,生成一个新的String类的实例
String b= "123 ";//字面字符串 "123 "是第二次出现,b与a指向同一个String类的实例
第三点:
java.lang.Object类equals方法的源代码:
------------------------------------
public boolean equals(Object obj) {
return (this == obj); //“==”运算符比较方法同第一点
}
------------------------------------
java.lang.String类equals方法的源代码:
------------------------------------
public boolean equals(Object anObject) {
if (this == anObject) { //“==”运算符比较方法同第一点
return true;
}
//java.lang.String类的实例在内存中就相当于是一个char数组,
//只有char数组的每个char都相等时才能认为两个String类实例相等
if (anObject instanceof String) {
String anotherString = (String)anObject;
int n = count;
if (n == anotherString.count) {
char v1[] = value;
char v2[] = anotherString.value;
int i = offset;
int j = anotherString.offset;
while (n-- != 0) {
if (v1[i++] != v2[j++])
return false;
}
return true;
}
}
return false;
}
------------------------------------
例子分析:
*/
class Try{
public String u;
Try(String u) {
this.u=u;
}
}
public class Test {
public static void main(String args[]) {
Try x1=new Try( "123 ");
Try x2=new Try( "123 ");
//会生成两个String实例,虽然内容是一样的,
String a=new String( "456 ");
String b=new String( "456 ");
String c=new String( "789 ");
String z1= "456 ";
String z2= "456 ";
String z3= "789 ";
/*运行到这里已有的java.lang.String类的实例有:
StringInstance( "123 ");
StringInstance( "456 ");
StringInstance( "789 ");
StringInstance(a);
StringInstance(b);
StringInstance(c);
*/
//x1与x2的类型都是Try,而Try是java.lang.Object类的子类,
//虽然两次构造Try类实例的参数都是一样的,但存放这两个Try类实例
//的“内存地址”不一样,也就是两个引用x1与x2不相同,根据第一点
//结果一定是no
if(x1==x2) //no,这个不用说
System.out.print( "t String ok\n ");
else
System.out.print( "t String no\n ");
//a和b指向两个不同的String类实例,结果一定是no
if(a==b) //no,
System.out.print( "a,b Stringnew ok\n ");
else
System.out.print( "a,b Stringnew no\n ");
//a和c指向两个不同的String类实例,结果一定是no
if(a==c) //no
System.out.print( "a,c Stringnew ok\n ");
else
System.out.print( "a,c Stringnew no\n ");
//z1和z2指向相同的String类实例StringInstance( "456 "),结果一定是ok
if(z1==z2) //ok
System.out.print( "a,b String ok\n ");
else
System.out.print( "a,b String no\n ");
//z1指向String类实例StringInstance( "456 ")
//z3指向String类实例StringInstance( "789 ");,结果一定是no
if(z1==z3) //no
System.out.print( "a,b String ok\n ");
else
System.out.print( "a,b String no\n ");
//a,b的类型都是String,
//调用Stringe类的quals方法:首先引用不相等,接着判断字符串内容相等
//所以结果一定是ok
if(a.equals(b)) //ok
System.out.print( "a,equals,b Stringnew ok\n ");
else
System.out.print( "a,equals,b Stringnew no\n ");
//x1,x2的类型都是Try,而Try是java.lang.Object类的子类,
//但Try类没有覆盖java.lang.Object类的equals方法
//所以调用java.lang.Object类的equals方法
//在Object类的equals方法直接判断x1,x2这两个引用,
//相档于if(x1==x2),所以结果一定是no
if(x1.equals(x2)) //no
System.out.print( "x1,equals,x2 trynew ok\n ");
else
System.out.print( "x1,equals,x2 Stringnew no\n ");
//z1,z2的类型都是String,
//调用Stringe类的quals方法:
//因为z1,z2都指向相同的String类实例StringInstance( "456 "),
//所以结果一定是ok
if(z1.equals(z2)) //ok
System.out.print( "a,equals,b String ok\n ");
else
System.out.print( "a,equals,b String no\n ");
//z1,z3的类型都是String,
//调用Stringe类的quals方法:
//首先引用不相等,接着判断字符串内容也不相等
//所以结果一定是no
if(z1.equals(z3)) //no
System.out.print( "a,equals,b String ok\n ");
else
System.out.print( "a,equals,b String no\n ");
//x1,x2的类型都是Try,而Try是java.lang.Object类的子类,
//但Try类没有覆盖java.lang.Object类的equals方法
//所以调用java.lang.Object类的equals方法
//在Object类的equals方法直接判断x1,x2这两个引用,
//相档于if(x1==x2),所以结果一定是no
if(x1.equals(x2)) //no
System.out.print( "String1 ok\n ");
else
System.out.print( "String1 no\n ");
//x1.u的类型是String,
//x2的类型是Try,
//调用Stringe类的equals方法:
//首先引用不相等,接着判断x2不是Stringe类的实例引用
//所以结果一定是no
if(x1.u.equals(x2)) //no
System.out.print( "String2 ok\n ");
else
System.out.print( "String2 no\n ");
//x1.u, x2.u的类型都是String,
//因为x1.u, x2.u都指向相同的String类实例StringInstance( "123 "),
//所以结果一定是ok
if(x1.u==x2.u) //ok
System.out.print( "String3 ok\n ");
else
System.out.print( "String3 no\n ");
//x1.u, x2.u的类型都是String,
//调用Stringe类的equals方法:
//因为x1.u, x2.u都指向相同的String类实例StringInstance( "123 "),
//所以结果一定是ok
if(x1.u.equals(x2.u)) //ok
System.out.print( "String4 ok\n ");
else
System.out.print( "String4 no\n ");
}
}