A股上市公司传智教育(股票代码 003032)旗下技术交流社区北京昌平校区

 找回密码
 加入黑马

QQ登录

只需一步,快速开始

© 宋国涛 黑马帝   /  2011-8-31 20:27  /  2538 人查看  /  12 人回复  /   0 人收藏 转载请遵从CC协议 禁止商业使用本文

public class TestString
{
        public static void main(String [] args)
        {
                String s1 = "hello";
                String s2 = " world";
                String s3 = "hello";

                System.out.println(s1 ==s3);

                s1 = new String ("hello");
                s2 = new String("hello");
                System.out.println(s1 ==s2);
                System.out.println(s1.equals(s2));

                char c [] = {'s','u','n',' ', 'j','a','v','a'};
                String s4 = new String(c);
                String s5 = new String(c,4,4);

                System.out.println(s4);
                System.out.println(s5);
        }
}

写了一个Test小程序,主要目的是了解“==”和equals() 方法的区别,熟悉一下String类的常用构造方法,对“==”和 equals()方法还不是很明白,大家能给讲讲么。。。

评分

参与人数 1技术分 +1 收起 理由
wangfayin + 1

查看全部评分

12 个回复

倒序浏览
黑马网友  发表于 2011-8-31 21:22:38
沙发

回复 楼主 的帖子

==比较的是两个对象的地址,equals()比较的是两个对象的内容。
同一个地址的也就是说是同一个对象,那么他们的内容当然也一样。反过来内容一样,却不一定是同一个对象哦:
举个例子:
   String s1 = "abc";
      String s2 = "abc";
     main()
     {
        if (s1 == s2)
       system.out.println("s1==s2");
     }
这个结果就是输出:s1==s2 证明他们两个引用了同一个对象。
再来:
  String s1 = "abc";
    String s2 = new String("abc");
    main()
   {
    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);
   }
输出:s1!=s2  s1e quals s2  因为他们现在不是属于同一个对象,但是内容一致!说普通一点 == 更严格。

评分

参与人数 1技术分 +2 收起 理由
wangfayin + 2 回答的不错!

查看全部评分

回复 使用道具 举报
==用于比较两个引用变量是否相等,如你的程序中s1和s3字符串他们是相等,对于String s1 = "hello这种形式的字符串初始化,在java中,会把hello字符串放在一个叫做字符串常量池(专门存放字符串的地方)的区域中,当程序的其他地方又有一个定义如String s3 = "hello",这时程序不会开辟一块新的内存来存放s3字符串,而是直接从字符串池中取出hello这个字符串,s1和s3这2个引用指向的是同一个对象,即他们的引用是相等的,即s1 == s3返回的是true
       对于String s1 = new String("hello");  String s2 = new String("hello");这种new 式的初始化,jvm会在堆区划出2块内存来存放s1和s2字符串,这时s1和s2的引用是不同的,即s1==s2返回的是false
       此外,==运算符还可以用于比较基本数据类型是否相等
       对于equals方法,比较的是2个对象的内容是否是相等,如上面的s1.equals(s2)返回的是true,这里要注意的是,因为String类中覆盖了Object的equals方法,该方法中首先会判断要比较的2个对象的引用是否相等,如果相等,那返回的是true,如果2个对象的引用不相等,则比较2个对象的内容是否相等,如果相等则返回true,不相等则返回false,下面附上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;
    }

        当我们要比较自定义的对象时,通常要覆盖equals方法,在equals方法中定义自己的比较规则

        总结:
         1.==运算符用于比较2个对象的引用是否相等,还可以用于比较基本数据类型是否相等
         2.equals用于比较2个对象的内容是否相等
        


    希望可以帮到楼主:)
[ 本帖最后由 黄敏文 于 2011-08-31  22:05 编辑 ]

评分

参与人数 1技术分 +2 收起 理由
wangfayin + 2 回答的很好!

查看全部评分

回复 使用道具 举报
这个问题,看似很简单,但是如果想要彻底理解的话,其实涉及到的知识点非常的多呢……
那我就从基础开始,一个一个地慢慢讲吧,希望你能够有耐心把它看完~嘿嘿
如果你真正理解了这些的话,对你以后理解JAVA中的任何概念都是非常有好处的

=======================分割线=======================

第一个知识点是:引用和对象的关系是什么
为了方便讲解,我先讲一个比喻吧:
首先,你可以把内存想象成一个游泳场,这个游泳场分为两个区域:岸上和游泳池里
每当你创建一个对象的时候,你就相当于往游泳池里扔了一个东西
但是,当你把这个东西扔进游泳池以后,你以后需要使用它的时候,该如何去拿到它呢?
这就需要你在把东西扔进游泳池之前,首先往这个东西上绑一根绳子,然后在绳子的另一端绑一个纸条
在纸条上,你需要写明:你在这个绳子上绑了什么东西,以及这张纸条的编号是多少(以便和其他的纸条区分开)
这样,你只要把这个纸条放在岸上,你就可以放心地把这个东西扔进游泳池里了
以后你当你需要从游泳池里把这个东西取出来的时候,你就只需要在岸上找到相应编号的这张纸条就可以了

下面,让我们用术语来翻译一下上面那个例子中的名词吧~
“游泳场”用术语表示,就是“内存”
游泳场中分为两块区域,其中:
“岸上”是内存中的一块特殊的区域,我们称之为:“栈空间(Stack)”
而“游泳池里”是内存中的另一块特殊的区域,我们称之为:“堆空间(Heap)”
你向游泳池里扔的“东西”,我们称之为:“对象”
而你在“岸上”放的“纸条”,我们称之为:“引用”
而纸条“连着一个”对象,我们称之为:引用“指向一个”对象

下面,让我们来看看这个比喻和你的程序的关系吧
在你的程序中,可以看到有这样一条语句:
String s1 = "hello";
在这个语句中,s1就是一个引用,而"hello"就是一个对象

=======================分割线=======================

第二个知识点:"=="的含义
当我们理解了引用和对象的关系以后,我们就可以讲解“==”的含义了
首先看一下你程序中的这条语句:[code=java]System.out.println(s1==s3);[/code]这句话中的s1==s3是什么含义呢?它的含义就是:
当且仅当s1这个引用指向的对象,和s3这个引用指向的对象,是同一个对象的话,==才会返回true,否则返回false
套用一下比喻,这句话应该很好理解吧,那就是
只有当写着s1的纸条和写着s3的纸条,都连着游泳池里同一个东西的时候,==才会返回true,否则返回false

=======================分割线=======================

第三个知识点:String这个类中equals()的含义
如果你查询String类型的API的话,你可以看到下面这句话:
将此字符串与指定的对象比较。当且仅当该参数不为 null,并且是与此对象表示相同字符序列的 String 对象时,结果才为 true。
这句话的意思如果翻译过来的话,就是:
只要两个String对象中保存的字符串内容相同的话,equals()就会返回true,否则返回false
就拿你程序中的代码举个例子吧:[code=java]s1 = new String("hello");   //s1指向的String对象中,保存的字符串为"hello"
s2 = new String("hello");   //s2指向的String对象中,保存的字符串也是"hello"
System.out.println(s1.equals(s2));  //所以这里返回的就是true[/code]

=======================分割线=======================

第四个知识点:s1 = "hello"; 和 s1 = new String("hello");的区别
如果你运行过你写的这个程序,你就会发现这样一个问题:[code=java]//代码1
String s1 = "hello";
String s3 = "hello";
System.out.println(s1 == s3);   //输出true

//代码2
s1 = new String("hello");
s2 = new String("hello");
System.out.println(s1 ==s2);   //输出false[/code]由于刚才我们讲过,当你调用a==b的时候,只有当a和b指向的是同一个对象的时候,==才会返回true
所以,我们就可以看出:
在代码1中,s1和s3指向的是同一个对象
而在代码2中,s1和s2指向的是不同的对象
这又是为什么呢?

这是因为:当你使用s = "hello"这种写法的时候
Java会首先去内存中里寻找有没有已经存在的内容为”hello“的对象
如果有,那么Java就不会为你重新创建一个对象,而是直接将这个对象返回给s
如果没有,Java才会为你创建一个对象扔到内存里,并将这个对象返回给s
所以,在代码1中,当你执行s1 = ”hello“的时候,Java会为你创建一个"hello"对象并返回给s1
而当你执行s3 = "hello"的时候,Java就会直接把之前创建出的这个"hello"对象返回给s3了
所以s1和s3指向的就是同一个对象

而如果你使用的是s1 = new String("hello")这种写法的话
那么Java就不会去查询,而是直接就创建一个新的内容为"hello"的String对象并返回
所以,在代码2中,当你执行s1=new String("hello");和s2=new String("hello")的时候
Java就给你直接创建了两个内容为”hello“的String对象,并分别返回给s1和s2
所以s1和s2指向的就是不同的对象

Tips:关于s1=="hello"的解释,我这里的解释是不够确切的
但是由于是初学,你暂时这样理解也没关系
如果你对真相感兴趣的话,你可以去查阅一下关于“字符串常量池”的知识~

[ 本帖最后由 李叶 于 2011-09-01  00:07 编辑 ]

评分

参与人数 1技术分 +3 收起 理由
wangfayin + 3 回答的非常好!

查看全部评分

回复 使用道具 举报
黑马网友  发表于 2011-8-31 23:13:08
报纸
李叶,太佩服你了
回复 使用道具 举报
黑马网友  发表于 2011-9-1 00:17:04
地板
呵呵,谢谢啦!
可能是因为在我以前学习的时候,特别希望能有一个人给我这样讲解吧
所以我就能够想到的知识点按照我的理解方式都写上来了呵呵~
主要还是,能够给大家带来收获就好~
回复 使用道具 举报
黑马网友  发表于 2011-9-1 08:29:33
7#
第一: String s1 = "hello";
              String s2 = " world";
              String s3 = "hello";
需理解内存对于String 常量的规则:当两次存入相同String常量时,内存仅开辟一个地址空间, 即s1,s3指向同一个地址。
所以s1==s3返回为真(注意:s1==s3是比较地址值的)
第二: s1 = new String ("hello");
              s2 = new String("hello");
这里不再是String常量,而是 “对象”:s1, s2代表的是两个不同的对象,内存会为其指定不同的地址。故:s1==s2返回假
回复 使用道具 举报
黑马网友  发表于 2011-9-1 13:06:37
8#
非常感谢大家的解答,让我又少了一些疑惑,看来学习不能够不求甚解,要知其然,还要知其所以然。所有的知识点要串联起来,就像老师说过的那样,要形成一张网,举一反三,触类旁通。
回复 使用道具 举报
黑马网友  发表于 2011-9-1 15:52:14
9#
。。。我简单说一下,简单又能说清楚才好使。
这个是编译器(JDK)优化,比如一个对象的方法在不同地方被多次使用,我们就可以说这个方法复用率高,同理,一个字符串在不同地方被多次使用,我们也可以说这个字符串复用率高,所以如果java的设计者愿意的话
String s1 = "hello";
String s3 = "hello";
System.out.println(s1 == s3);   
也能为true!!!
回复 使用道具 举报
黑马网友  发表于 2011-9-1 16:04:43
10#
每次我看到回答的同学,我就无地自容啊,反正我来回答肯定没那么详细和清除,看来我书面表达能力不行了……悲剧
回复 使用道具 举报
李叶 黑马帝 2011-9-1 19:14:34
11#
刚才在网上简单搜索了下,发现有一篇文章对理解s="hello"的含义很有启发
所以在这里我还是补充说明一下吧,正好做一个准确的解答,有兴趣的话可以看一看~

以下是文章的原文:
String literal:这里可以将它翻译为“字面常量”
它由双引号包围的0个或多个字符组成
比如"abc","Hello World"等等,都可以称之为字面常量
一个String类型的字面常量总是指向相同的String对象
比如"abc","abc"两个常量指向的就是同一个对象


通过上文的介绍,我们可以看到,当我们在程序中直接使用"hello"这样的字符串时
实际上这个"hello"的字符串也就好像一个引用一样,它指向堆空间的一个String类型的对象
只不过,这个字符串和普通的引用还是有区别的
首先,它并不叫“引用”,而是叫“字面常量”(虽然本质上就是个引用)
其次,它并不保存在栈空间中,而是保存在字符串常量池中的
而字符串常量池,你可以把它理解为和栈空间或者堆空间相似的,同样是内存中的一块特殊的区域

也许这样说明还是不太好理解,那就还是用游泳场的例子吧
你可以把字符串常量池想象成游泳场中的另一个区域,比如说服务台
然后,你可以把字面常量想象成可以从服务台领取到的东西,比如说一块号码牌
这个号码牌应当对应着游泳池里的一样东西,比如说上面写着“3号”的水球之类
这样,当你拿到这个号码牌之后,你就可以根据这块号码牌找到这个号码牌所对应的游泳池里的东西了

这样就再来确切的解答一下s="hello"这种写法的含义吧:
当你使用s = "hello"这种写法的时候
Java会首先去字符串常量池中获取"hello"这个字面常量
当获取到"hello"这个字面常量之后,Java再根据这个字面常量去堆空间中获取它所对应的String类型对象
如果有,那么Java就不会为你重新创建一个对象,而是直接将这个对象返回给s
如果没有,Java就会创建一个内容为"hello"的String对象扔到内存里,并将这个对象返回给s
并且,Java还会使字符串常量池中"hello"这个字面常量指向刚刚创建出的这个String对象

所以,在代码1中,当你执行s1="hello"的时候
Java会首先通过"hello"这个字面常量去堆空间中寻找与其对应的String类型对象,很显然这时还没有
所以Java就会为你创建一个内容为"hello"的String对象并返回给s1,并使"hello"这个字面常量指向这个对象
然后,当你执行s3="hello"的时候,Java还是会通过"hello"这个字面常量去堆空间中寻找String类型对象
很显然这次就可以找到了,所以Java就会直接把"hello"这个字面常量所指向的这个String对象返回给s3了
所以s1和s3最终指向的都是同一个String类型对象

解答完毕~如果还是对字面常量有什么不明白的地方,请参考下面这个帖子:
http://topic.csdn.net/u/20090519/18/7b8cf7ef-bc06-4d26-8a2c-692eb0562231.html
[ 本帖最后由 李叶 于 2011-09-01  19:18 编辑 ]
回复 使用道具 举报
黑马网友  发表于 2011-9-1 20:20:45
12#

回复 12 # 的帖子

我今天也在网上看到了一篇关于字符串常量池的文章,红色加粗的地方不解:
最后我再破除一个错误的理解:
  有人说,“使用String.intern()方法则可以将一个String类的保存到一个全局String表中,如果具有相同值的Unicode字符串已经在这个表中,那么该方法返回表中已有字符串的地址,如果在表中没有相同值的字符串,则将自己的地址注册到表中“如果我把他说的这个全局的String表理解为常量池的话,他的最后一句话,“如果在表中没有相同值的字符串,则将自己的地址注册到表中”是错的:[code]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() );   [/code]结果:
false
kvill kvill
true

在这个类中我们没有声名一个”kvill”常量,所以常量池中一开始是没有”kvill”的,当我们调用s1.intern()后就在常量池中新添加了一个”kvill”常量,[b][color=Red]原来的不在常量池中的”kvill”仍然存在,也就不是“将自己的地址注册到常量池中”了  
    s1==s1.intern()为false说明原来的“kvill”仍然存在;
  s2现在为常量池中“kvill”的地址,所以有s2==s1.intern()为true。
[/color][/b]
回复 使用道具 举报
您需要登录后才可以回帖 登录 | 加入黑马