Java 之 equals、==、hashcode、String详解

equals 和 == 的区别

==

比较 new 出来的对象肯定是false,因为两个对象的内存地址肯定是不一样的。
比较 基本数据类型,比较的是值。

equals

Object 的方法,所以只能比较引用类型,不能比较基本类型。

如果没有被覆写,相当于 == ,比较的是内存地址值。

hashcode

hashcode方法是根据对象的内存地址经哈希算法得来的。

hashCode() 只需要生成一个hash值就可以比较,效率高。
equals() 比较全面复杂,所以效率低。

hashcode规定:
hashcode不等,两个对象一定不等。
hashcode相等,两个对象不一定相等。

为什么重写equals后要一定要重写hashCode

两个Student对象:

Student s1=new Student("James",18);
Student s2=new Student("James",18);

如果不重写equals,此时s1.equals(s2)一定返回false

然而重写了equals,且s1.equals(s2)返回true;由于默认的hashcode方法是根据对象的内存地址经哈希算法得来的,此时s1、s2两者的hashcode不一定相等。根据hashcode的规则,两个对象相等其哈希值一定相等,所以就产生矛盾了。

那为什么重写hashcode后,返回值就一样了呢?
重写hashcode:

@Override
public int hashCode() {
    return Objects.hash(name, age);
}

发现重写的hash值与属性有关。

重写equals后不重写hashcode会有什么后果:(导致集合添加重复元素)
在用Set去重的时候,会执行hashcodeequals方法。
当添加到Set的对象 HashCode码不相同时(根据hashcode不等,两个对象一定不等)不会调用equals方法,对象直接存到Set集合中,HashCode相同时才会调用equals方法查看是否是同一个对象(是否重复),是,则无法存入。

按照上面的案例,如果不重写hashcodehashcode码返回不相等,直接添加到Set集合中,其实对象的内容是一样的,所以会重复添加元素,无法去重。

String 类详解

String 类对象是不可变的,字符串一旦创建,内容不能再变。
String 每次+操作,就会隐式的在堆上new了一个跟原字符串相同的StringBuilder对象,再调用append方法拼接+后面的字符。
String s1 = "a"+"b"; => String s1 = new StringBuffer(a).append(b).toString();
所以在进行频繁的字符串操作时,建议使用StringBuffer和StringBuilder来进行操作。 因为他们的底层都是可变的字符数组,另外 StringBuffer 对方法加了同步锁或者对调用的方法加了同步锁,所以是线程安全的。StringBuilder 并没有对方法进行加同步锁,所以是非线程安全的。

String 类到底是什么

String 类就是一个字符数组,并且是 final 类型的。所以自然是不可变的。

public final class String
      implements java.io.Serializable, Comparable<String>, CharSequence {
      /** The value is used for character storage. */
      private final char value[];
      
      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);
          str.getChars(buf, len);
          return new String(buf, true);
      }
  }

为什么我们可以执行下面的操作呢?

String str = "hello";
  str += " world";
  System.out.println(str);
  // hello world

表面上看 str 确实变了啊

但实际上呢?str += " world"; 到底发生了什么?

实际上是执行了 concat() 方法, str += " world"; => str.concat(" world");

String str = "hello";
  str.concat(" world");
  System.out.println(str);
  // hello

为什么没有变呢

我们看上面 String 类中的 concat() 方法,仔细观察发现,他的返回值才是我们要的新字符串。

String str = "hello";
str = str.concat(" world");
System.out.println(str);
// hello world

由此看来 String 类的对象确实没有变,在执行 str += " world"; 实际上是执行了 str = str.concat(" world"); 新的字符串地址被赋给了 str。

Java 中为了效率考虑:
"" 包括的字符串,只要内容相同、顺序、大小写相同。
无论在程序代码中出现几次,JVM 都只会建立一个实例放在字符串池(String Pool)中维护。

使用不同方式创建字符串

String str1 = "hello world";
String str2 = "hello" + " world";
String str3 = new String("hello world");
String str4 = "hello" + new String(" world");
String str5 = new String("hello") + " world";
String str6 = new String("hello") + new String(" world");

// System.identityHashCode(Object) 方法可以返回对象的内存地址,不管该对象的类是否重写了hashCode()方法。
System.out.println(System.identityHashCode(str1)); // 455896770
System.out.println(System.identityHashCode(str2)); // 455896770
System.out.println(System.identityHashCode(str3)); // 1323165413
System.out.println(System.identityHashCode(str4)); // 1880587981
System.out.println(System.identityHashCode(str5)); // 511754216
System.out.println(System.identityHashCode(str6)); // 1721931908

new创建与直接赋值内存比较:
通过new创建,也就是使用构造方法创建的String对象,存放在Java虚拟机的堆内存,堆内存里存放的是字符串常量的地址,字符串常量存放在方法区的常量池中;
通过直接赋值所创建的String对象直接是方法区中的常量池中的字符串常量。
而语句(s1==s2)是比较s1和s2中存放的地址,显然不相同,但是s2和s3的地址相同,则会得到true结果。
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值