java中equal()与==的区别(顺便谈谈String,stringBuffer,StringBuilder)

首先==,当用于基本数据类型是比较大小,比较的是他们的值。

当用于复合数据类型(类对象)时,比较的是两个对象存放的的地址,除非是同一new出来的对象,否则比较的结果是false。


至于equal(),上帝类Object中有equal()方法,因此所有类都有equal()的方法,在Object里面equals方法同上面复合类型里面使用==一样也是比较两个对象的地址,但是并非所有的类的equals方法里面比较的都是对象的的地址 ,譬如String,Integer,Data等,复写了equals方法,并不是比较对象的地址。


就拿比较特殊的String为例,(

谈到String,顺便多谈一些:谈谈String,StringBuffer,StringBuilder之间的区别。

String类是final的,即不可继承,同时也是线程安全的,String对象的值(成员对象value)也是final的,不可改变,既然值不可改变,那String又是怎么完成字符拼接的呢,其实字符拼接是又开辟了一部分空间,将原来的内容以及拼接的内容放进去,返回时,返回新建的一个String的对象,jdk源代码如下:

 public String concat(String str) {
        int otherLen = str.length();
        if (otherLen == 0) {
            return this;
        }
        int len = value.length;
        char buf[] = Arrays.copyOf(value, len + otherLen);//这里的value字符数组就是String类的final的成员变量,这里的buffer就是新开辟的字符数组区域
        str.getChars(buf, len);
        return new String(buf, true);//由于value字符数组是final的,因此不能将value直接指向buffer,所以需要new新的String
    }


因此当频繁拼接字符串时,因为会间接地引发很多new操作,耗内存,时间。因此尽量避免使用String,取而代之的是用StringBuffer或者StringBuilder,

StringBuffer类也是final的,但StrngBuffer的成员对象value不是final的,但是它仍然是线程安全的,因为里面的方法都是加上了synchornized同步锁。由于成员对象value不是final的,因此拼接字符时,可以直接将value的引用指向拼接后的内容,不需要再new新的String。StringBuffer拼接代码的方法是append,

jdk源代码如下:

   public synchronized StringBuffer append(String str) {
        toStringCache = null;
        super.append(str);//StringBuilder的父类是AbstractStringBuilder,下面跟踪到父类方法看看
        return this;//这里的
    }
   public AbstractStringBuilder append(String str) {
        if (str == null)
            return appendNull();
        int len = str.length();
        ensureCapacityInternal(count + len);
        str.getChars(0, len, value, count);
        count += len;
        return this;//由于value不是final的,因此可以value指向新的字符数组,然后返回this
    }

 public final class StringBuffer
    extends AbstractStringBuilder
    implements java.io.Serializable, CharSequence

  char[] value;//AbstractStrngBuilder里定义的成员变量value字符数组不是final


StringBuilder类也是final的,与StringBuffer类似,StringBuilder的成员对象value不是final的,唯一的区别是StringBuilder不是线程安全的,它的方法里面没有加上同步锁,因此在单线程里。效率比StringBuffer更高。jdk源代码

public final class StringBuilder    extends AbstractStringBuilder    implements java.io.Serializable, CharSequence

由于StringBuilder也是继承于AbstractStringBuilder,这里就不赘述了。

括号里这些内容与本文equals与==区别无关,只是提一下而已

public class TestString {
   public static void main(String[] arg) {
  String s1 = "dfy";
  String s2 = "dfy";
  if (s1 == s2)
  {
 System.out.println("s1 == s2");}
 else{
 System.out.println("s1 != s2");}
 }
 }
执行结果是s1==s2,由此看出s1与s2引用的是同一对象,这个特点看似奇葩,其实是与字符串常量池有关,

其实当String s1="dfy"时,会在字符串常量池里建一个dfy的区域储存起来。当String s2="dfy",时,String并不会再在常量池里创建一个“dfy”的区域(因为s2对象引用并不是new出来的,而是直接指向了dfy字符串),而是在常量池里拿到原来创建的“dfy”,也就是说此时s1与s2引用的是同一对象“dfy”.

再看下面的变动,结果又出人意表!

public class TestString {
public static void main(String[] args) {
String s1 = "dfy";
String s2 = new String("dfy");
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  

    s1 equals s2

这里我们对结果或许有些迷惑,其实这里就说明两点:

其一:这里的String s2是new出来的,因此不管dfy字符串存在不存在,都会在字符串常量池里再创建一个新的“dfy”对象,而s2就指向这个新的对象(注意此时s1与s2的地址就不同了因此s1!=s2)。

其二:至于s1 equals s2,我们前面也提到,String复写了equals方法,这里的equals不再是比较地址的值,而是地址里的字符串是否相等。

再次更改程序,如下:

public class TestString {
public static void main(String[] args) {
String s1 = "Monday";
String s2 = new String("Monday");
s2 = s2.intern();
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  

    s1 equals s2

从结果上来看此时s1与s2指向了同一个对象。这好像与我们上面讲的不符呀,细心的话,会发现上面的程序加了一句代码,

s2 = s2.intern();
之所以会出现意外,都是String的intern方法捣的鬼,也就是说,虽然这里s2是new出来的一个新的对象,但是s2又调用了intern方法,这个方法会做一个小动作,就是在字符串常量池里找是否有与s2内容一样的对象,如果找到就返回常量池里的对象,否则就把s2里面的字符串放进常量池,返回自己的引用。

由于创建s2之前常量池就已经有dfy字符串,因此,s2.intern()返回的与s1是同一对象。










  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Java,`equals()`和`==`都是用来比较两个对象的方法,但是它们的作用不同。 `equals()`方法用于比较对象的内容是否相等,即比较对象的属性值是否相等。默认情况下,`equals()`方法比较的是两个对象的地址是否相等,如果想要比较对象的属性值是否相等,就需要在对应类重写`equals()`方法。一般而言,如果一个类重写了`equals()`方法,通常也需要重写`hashCode()`方法。 `==`运算符用于比较两个对象的地址是否相等,即判断两个对象是否是同一个对象。如果两个对象的地址相等,则它们一定是同一个对象;如果两个对象的地址不相等,则它们不一定是不同的对象,可能是同一类的不同对象。 举个例子,假设有一个Person类,包含两个属性name和age。当我们使用`equals()`方法比较两个Person对象时,比较的是它们的name和age属性值是否相等;而当我们使用`==`运算符比较两个Person对象时,比较的是它们的地址是否相等。 ``` Person p1 = new Person("张三", 20); Person p2 = new Person("张三", 20); Person p3 = p1; System.out.println(p1.equals(p2)); // true System.out.println(p1 == p2); // false System.out.println(p1 == p3); // true ``` 在上面的例子,p1和p2虽然属性值相等,但是它们是两个不同的对象,因此使用`==`运算符比较结果为false;而p1和p3是同一个对象,因此使用`==`运算符比较结果为true。`equals()`方法比较的是p1和p2的属性值是否相等,因此结果为true。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值