Chinaunix

标题: java的判断对象相等的两个问题 [打印本页]

作者: 大马虎    时间: 2006-04-05 08:53
标题: java的判断对象相等的两个问题

String a="Hello World!";
String b="Hello World!";
a=b?  a和b是否相等 ? 为什么?

String a=new String("Hello World!";
String b="Hello World!";
a=b?  a和b是否相等 ? 为什么?
作者: carset    时间: 2006-04-05 09:46
看你用哪个比较符

是用 == 还是用 equals()

equals 仅仅比较值是否相等。。

== 还比较是否指向同一个对象。
作者: 大马虎    时间: 2006-04-05 09:50
使用==进行判断
作者: kakasi    时间: 2006-04-05 09:59
equals是object的方法,String类将其实现为字符串内容的对比,也就是两个串是否“内容一致”

==这个运算符则比较两个变量是否相等,而java中所有obj都是引用(姑且可以看成是指针),故而比较的是指针的值,换句话说就是看两个引用是否指向同一个内存地址
作者: xxjoyjn    时间: 2006-04-05 11:58
原帖由 kakasi 于 2006-4-5 09:59 发表
equals是object的方法,String类将其实现为字符串内容的对比,也就是两个串是否“内容一致”

==这个运算符则比较两个变量是否相等,而java中所有obj都是引用(姑且可以看成是指针),故而比较的是指针的值,换 ...



kakasi怎么把个JAVA学的那么深邃呀!教教我你是怎么学的?
作者: tong0245    时间: 2006-04-05 14:57
楼上的 要是这个问题都不知道 还是算了吧 是本书都要说这个问题的
作者: xxjoyjn    时间: 2006-04-05 15:09
楼上的兄弟想多了,对于这个问题我当然知道,我赞赏kakasi不仅仅源于他对这个问题
圆满解答,而是因为看到他多次对一些问题所做的简练、精辟、深刻阐述,还有他的耐心,对于初学者来说,
这个很有用,CU是大家相互学习和讨论的地方,不是看戏或者说风凉话的地方!
作者: 艾斯尼勒    时间: 2006-04-05 15:18
结合四楼的看

如果是在程序开始的时候,

a==b是返回true的,以后的不保证

两种情况都是返回true

因为String是一个不可变的对象,而String的直接量赋值会自动寻找以前生成了的内容相同的实例赋给引用,若以前没有内容相同的实例存在,则创建新实例

也就是说LZ的两种情况,在String b="HelloWorld"时候,都会作b=a的操作
所以b==a的值都是true

但若程序在之前还有一些别的操作的话,尤其是第二种情况

例如 String c="HelloWorld!";
        String a=new String("HelloWorld");
        String b ="HelloWorld!";
这种情况a!=b了

[ 本帖最后由 艾斯尼勒 于 2006-4-5 15:20 编辑 ]
作者: xxjoyjn    时间: 2006-04-05 15:26
按照LZ的字面意思,我认为他说的两种情况下,a都不等于b,
因为在两种情况的本质都是一样的,a,b 分别是两个独立对象的引用,或者说
是两个独立对象在内存中的地址,既然是两个独立的地址,它们的值当然不一样!
作者: 艾斯尼勒    时间: 2006-04-05 15:40
原帖由 xxjoyjn 于 2006-4-5 15:26 发表
按照LZ的字面意思,我认为他说的两种情况下,a都不等于b,
因为在两种情况的本质都是一样的,a,b 分别是两个独立对象的引用,或者说
是两个独立对象在内存中的地址,既然是两个独立的地址,它们的值当然不一样!



情看搂上我的解释,都应该是返回true的,您可以写个程序实验一下
作者: tong0245    时间: 2006-04-05 15:44
原帖由 艾斯尼勒 于 2006-4-5 15:40 发表



情看搂上我的解释,都应该是返回true的,您可以写个程序实验一下

还是作个试验好 不过我感觉第一种情况是true 第二种是false
作者: carset    时间: 2006-04-05 16:03
。。你们回帖子。都不先自己试一下的么?

总之每次我都自己先试一下。确保没有错误才回复。。。


  1. package source;

  2. public class TestString {

  3.         /**
  4.          * @param args
  5.          */
  6.         public static void main(String[] args) {
  7.                 // TODO 自动生成方法存根
  8.                
  9.                 String a = new String("Hello World");
  10.                 String b = "Hello World";
  11.                 String c ="Hello World";
  12.                
  13.                 //*
  14.                 if(a.equals(b)){
  15.                         System.out.println("a equals b");
  16.                 }else{
  17.                         System.out.println("a no equals b");
  18.                 }
  19.                 if(a==b){
  20.                         System.out.println("a == b");
  21.                 }else{
  22.                         System.out.println("a != b");
  23.                 }
  24.                 if(c.equals(b)){
  25.                         System.out.println("c equals b");
  26.                 }else{
  27.                         System.out.println("c no equals b");
  28.                 }
  29.                 if(c==b){
  30.                         System.out.println("c == b");
  31.                 }else{
  32.                         System.out.println("c != b");
  33.                 }
  34.         }
复制代码


输出

a equals b
a != b
c equals b
c == b
作者: tong0245    时间: 2006-04-05 16:04
楼上的好同志
作者: 艾斯尼勒    时间: 2006-04-05 16:08
原帖由 tong0245 于 2006-4-5 15:44 发表

还是作个试验好 不过我感觉第一种情况是true 第二种是false


嗯,第二种的确是false,不过hashCode是一样的。奇怪阿奇怪
作者: 大马虎    时间: 2006-04-05 16:12
我也是通过程序验证才知道结果的,不过不太清楚原因,我想通过String a="Hello world !"; 方式声明的变量在栈上分配的,String a="Hello World !" ; String b ="Hello World !"; 其实都是指向了栈里相同的一个地址即在栈里的"Hello World !" ,而String c =new String("Hello World !");是在堆里分配的所以c !=b 不知道是不是这个原因。
作者: carset    时间: 2006-04-05 16:14


不讨论了。这个属于JVM的范畴。有兴趣可以找点资料看看。忘记那有中文的了。讲的还不错。

[ 本帖最后由 carset 于 2006-4-5 16:17 编辑 ]
作者: 艾斯尼勒    时间: 2006-04-05 16:16
原帖由 carset 于 2006-4-5 16:03 发表
。。你们回帖子。都不先自己试一下的么?

总之每次我都自己先试一下。确保没有错误才回复。。。



呵呵。我以前试过的,不过还是把结果记错了。


  1. public class Equal
  2. {
  3.         public static void main(String[] args)
  4.         {
  5.                 String a = new String("ABC");
  6.                 String b = "ABC";
  7.                 System.out.println(a.hashCode());
  8.                 System.out.println(b.hashCode());
  9.                 System.out.println(a==b);
  10.         }
  11. }
复制代码

返回结果是false,但hashcode是相同的,应该是同一个地址吧。String重写了hashCode()?

[ 本帖最后由 艾斯尼勒 于 2006-4-5 16:19 编辑 ]
作者: carset    时间: 2006-04-05 16:18
String.hashcode();


  1.   public int hashCode() {
  2.         int h = hash;
  3.         if (h == 0) {
  4.             int off = offset;
  5.             char val[] = value;
  6.             int len = count;

  7.             for (int i = 0; i < len; i++) {
  8.                 h = 31*h + val[off++];
  9.             }
  10.             hash = h;
  11.         }
  12.         return h;
  13.     }

复制代码

作者: picktracy    时间: 2006-04-05 16:24
因为Java的hashCode()有要求,当equals()调用返回true时,两者
的hashCode()必须相同^_^
作者: 艾斯尼勒    时间: 2006-04-05 16:26
原帖由 carset 于 2006-4-5 16:18 发表
String.hashcode();



嗯,的确是重写了,为内容相同的对象返回相同的值,为了equals方法能返回想要的结果
作者: xxjoyjn    时间: 2006-04-05 16:47
不过,我确实没有做过试验,听了大家的意见,我确信了,长了见识
作者: kakasi    时间: 2006-04-05 19:13
学习热情真高,就快撞到牛角尖了~~
作者: kinns    时间: 2006-04-05 23:05
1. 首先String不属于8种基本数据类型,String是一个对象。  

  因为对象的默认值是null,所以String的默认值也是null;但它又是一种特殊的对象
,有其它对象没有的一些特性。  

  2. new String()和new String(“”)都是申明一个新的空字符串,是空串不是null;
  

  3. String str=”kvill”;  
String str=new String (“kvill”);的区别:  

  在这里,我们不谈堆,也不谈栈,只先简单引入常量池这个简单的概念。  

  常量池(constant pool)指的是在编译期被确定,并被保存在已编译的.class文件中的
一些数据。它包括了关于类、方法、接口等中的常量,也包括字符串常量。  

  看例1:  

String s0=”kvill”;  
String s1=”kvill”;  
String s2=”kv” + “ill”;  
System.out.println( s0==s1 );  
System.out.println( s0==s2 );  
  结果为:  

true  
true  
  首先,我们要知道Java会确保一个字符串常量只有一个拷贝。  

  因为例子中的s0和s1中的”kvill”都是字符串常量,它们在编译期就被确定了,所以
s0==s1为true;而”kv”和”ill”也都是字符串常量,当一个字符串由多个字符串常量连
接而成时,它自己肯定也是字符串常量,所以s2也同样在编译期就被解析为一个字符串常
量,所以s2也是常量池中”kvill”的一个引用。  

  所以我们得出s0==s1==s2;  

  用new String() 创建的字符串不是常量,不能在编译期就确定,所以new String()
创建的字符串不放入常量池中,它们有自己的地址空间。  

  看例2:  

String s0=”kvill”;  
String s1=new String(”kvill”);  
String s2=”kv” + new String(“ill”);  
System.out.println( s0==s1 );  
System.out.println( s0==s2 );  
System.out.println( s1==s2 );  
  结果为:  

false  
false  
false  
  例2中s0还是常量池中”kvill”的应用,s1因为无法在编译期确定,所以是运行时创
建的新对象”kvill”的引用,s2因为有后半部分new String(“ill”)所以也无法在编译
期确定,所以也是一个新创建对象”kvill”的应用;明白了这些也就知道为何得出此结果
了。  

  4. String.intern():  

  再补充介绍一点:存在于.class文件中的常量池,在运行期被JVM装载,并且可以扩充
。String的intern()方法就是扩充常量池的一个方法;当一个String实例str调用intern(
)方法时,Java查找常量池中是否有相同Unicode的字符串常量,如果有,则返回其的引用
,如果没有,则在常量池中增加一个Unicode等于str的字符串并返回它的引用;看例3就清
楚了  

  例3:  

String s0= “kvill”;  
String s1=new String(”kvill”);  
String s2=new String(“kvill”);  
System.out.println( s0==s1 );  
System.out.println( “**********” );  
s1.intern();  
s2=s2.intern(); //把常量池中“kvill”的引用赋给s2  
System.out.println( s0==s1);  
System.out.println( s0==s1.intern() );  
System.out.println( s0==s2 );  
  结果为:  

false  
**********  
false //虽然执行了s1.intern(),但它的返回值没有赋给s1  
true //说明s1.intern()返回的是常量池中”kvill”的引用  
true  
  最后我再破除一个错误的理解:  

  有人说,“使用String.intern()方法则可以将一个String类的保存到一个全局Strin
g表中,如果具有相同值的Unicode字符串已经在这个表中,那么该方法返回表中已有字符
串的地址,如果在表中没有相同值的字符串,则将自己的地址注册到表中“如果我把他说
的这个全局的String表理解为常量池的话,他的最后一句话,“如果在表中没有相同值的
字符串,则将自己的地址注册到表中”是错的:  

  看例4:  
String s1=new String("kvill");  
String s2=s1.intern();  
System.out.println( s1==s1.intern() );  
System.out.println( s1+" "+s2 );  
System.out.println( s2==s1.intern() );  
  结果:  

false  
kvill kvill  
true  
  在这个类中我们没有声名一个”kvill”常量,所以A砍刂幸豢际敲挥小眐vill”的
,当我们调用s1.intern()后就在常量池中新添加了一个”kvill”常量,原来的不在常量
池中的”kvill”仍然存在,也就不是“将自己的地址注册到常量池中”了。  

  s1==s1.intern()为false说明原来的“kvill”仍然存在;  

  s2现在为常量池中“kvill”的地址,所以有s2==s1.intern()为true。  

  5. 关于equals()和==:  

  这个对于String简单来说就是比较两字符串的Unicode序列是否相当,如果相等返回t
rue;而==是比较两字符串的地址是否相同,也就是是否是同一个字符串的引用。  

  6. 关于String是不可变的  

  这一说又要说很多,大家只要知道String的实例一旦生成就不会再改变了,比如说:
String str=”kv”+”ill”+” “+”ans”;  
就是有4个字符串常量,首先”kv”和”ill”生成了”kvill”存在内存中,然后”kvill
”又和” “ 生成 ”kvill “存在内存中,最后又和生成了”kvill ans”;并把这个字符
串的地址赋给了str,就是因为String的“不可变”产生了很多临时变量,这也就是为什么
建议用StringBuffer的原因了,因为StringBuffer是可改变的
作者: kakasi    时间: 2006-04-05 23:39
原帖由 kinns 于 2006-4-6 00:05 发表
1. 首先String不属于8种基本数据类型,String是一个对象。  

  因为对象的默认值是null,所以String的默认值也是null;但它又是一种特殊的对象
,有其它对象没有的一些特性。  

  2. new String()和 ...


非常的深刻,有见地!~:em11:
作者: yxgk    时间: 2006-04-06 09:37
原帖由 kinns 于 2006-4-5 23:05 发表
1. 首先String不属于8种基本数据类型,String是一个对象。  

  因为对象的默认值是null,所以String的默认值也是null;但它又是一种特殊的对象
,有其它对象没有的一些特性。  

  2. new String()和 ...



佩服的五体投地

作者: 艾斯尼勒    时间: 2006-04-06 13:50
原帖由 kinns 于 2006-4-5 23:05 发表
1. 首先String不属于8种基本数据类型,String是一个对象。  

  因为对象的默认值是null,所以String的默认值也是null;但它又是一种特殊的对象
,有其它对象没有的一些特性。  

  2. new String()和 ...



好文阿
作者: blackcar    时间: 2006-04-06 22:50
原帖由 kinns 于 2006-4-5 23:05 发表
1. 首先String不属于8种基本数据类型,String是一个对象。  

  因为对象的默认值是null,所以String的默认值也是null;但它又是一种特殊的对象
,有其它对象没有的一些特性。  

  2. new String()和 ...


呵呵,大涨见识!原来==和equal还蕴藏着这么深的道理!
作者: xiaoshinn    时间: 2006-04-07 14:20
原帖由 kinns 于 2006-4-5 23:05 发表
1. 首先String不属于8种基本数据类型,String是一个对象。  

  因为对象的默认值是null,所以String的默认值也是null;但它又是一种特殊的对象
,有其它对象没有的一些特性。  

  2. new String()和 ...


说明的很清楚,好文。
作者: 大马虎    时间: 2006-04-08 12:13
几天没有来,想不到能够学到这么多,谢谢!!
作者: snowman_zhu    时间: 2006-04-08 12:53
标题: 回复 12楼 carset 的帖子
实践者代表,支持!
作者: xxjoyjn    时间: 2006-04-08 13:24
从本质上阐述清楚了,我看没有人再去争论了
作者: renxiao2003    时间: 2006-04-10 16:59
每次申请一个String都是指向了不同的地址。
作者: yoshubom    时间: 2006-04-17 01:24
建议楼主买一本书叫作 java puzzel 的
作者: terry6394    时间: 2006-04-28 14:27
原帖由 大马虎 于 2006-4-5 08:53 发表

String a="Hello World!";
String b="Hello World!";
a=b?  a和b是否相等 ? 为什么?

String a=new String("Hello World!";
String b="Hello World!&q ...


如果== 操作符的两边是对象,其比较的是两个对象的引用。换句话说,就是测试这两个变量是不是指向同一个地址。
要比较两个对象是否相等,要使用equals()方法(如果这个对象是你自己写的,你应该覆盖这个方法)。

但是,java里对String又有特殊的出处理。
如果你用
String a="Hello World!";
String b="Hello World!";
这样的方法声明的String系统会自动吧a 和 b 指向同一个位置。
用String a=new String("Hello World!"; 声明的String则按照一般的对象处理。
作者: 小猴儿    时间: 2006-05-01 17:22
..............
作者: timego    时间: 2006-05-02 10:57
当然不一样!你这是在比较两个对象,而不是两个对象的值
作者: seraphin    时间: 2006-05-04 02:11
呵呵。基础的java问题,看看String的特殊点介绍部分。就都回自己明白了。
作者: jacksun1105    时间: 2006-05-04 17:45
// String share = "Hello"; // sharing way;
// String selfish = new String ("Hello" ); // selfish way;



public class tryString {

        /**
         * @param args
         */
        public static void main(String[] args) {
                // TODO Auto-generated method stub
                String a = "Hello";
                String b = "Hello";
                String c = new String ("Hello" );// this guy do not share "Hello" with
                                                                // friends born earlier or later than himself
                String d = new String ("Hello!" );
                String e = "Hello";
                String f = "Hello!";  // the JV CAN rEcOgnIsE that d is born in that selfish
                                                        // a way that d will not share "Hello!" with f, so
                                                        // f have to create a new "Hello!" himself.
                System.out.println(b==a);
                System.out.println(c==a);
                System.out.println(e==a);
                System.out.println(e==c);
               
                System.out.println(f==d);
        }

}


OUTPUT:

true
false
true
false
false
作者: hh123456    时间: 2006-09-27 18:01
这样写更清楚一些:


  1.   public static void testEquals() throws Exception {
  2.     真命题( "abc" == "abc" );
  3.     注意了( "abc" == "a" + "bc" );

  4.     String a = "abc";
  5.     String b = "abc";
  6.     注意了( a == b );
  7.     b = "a" + "bc";
  8.     注意了( a == b );

  9.     a = new String("abc");
  10.     b = new String("abc");
  11.     注意了( a != b );
  12.     真命题( a.equals(b) );
  13.     真命题( a.intern() == b.intern() );
  14.   }

  15.   public static final void fail(String message)
  16.    {throw new RuntimeException(message);}
  17.   public static final void 真命题(boolean b)
  18.    {if (!b) fail("真命题为假");}
  19.   public static final void 注意了(boolean b)
  20.    {if (!b) fail("注意:真命题为假");}
复制代码

作者: fantix    时间: 2006-09-27 20:53
public static final void 来来,我是一个方法,法法法法法法,法法法法法法法法法() {
    System.out.println("毛主席教导我们:把问题研究透彻");
}




欢迎光临 Chinaunix (http://bbs.chinaunix.net/) Powered by Discuz! X3.2