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
去重的时候,会执行hashcode
和equals
方法。
当添加到Set
的对象 HashCode
码不相同时(根据hashcode不等,两个对象一定不等
)不会调用equals
方法,对象直接存到Set
集合中,HashCode
相同时才会调用equals
方法查看是否是同一个对象(是否重复),是,则无法存入。
按照上面的案例,如果不重写hashcode
,hashcode
码返回不相等,直接添加到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结果。