一、Object类
package java.lang;
public class Object {
private static native void registerNatives();
static {
registerNatives();
}
//获取class对象
public final native Class<?> getClass();
//hash码
public native int hashCode();
//判断对象是否相等
public boolean equals(Object obj) {
return (this == obj);
}
//克隆对象
protected native Object clone() throws CloneNotSupportedException;
//toString 方法
public String toString() {
return getClass().getName() + "@" + Integer.toHexString(hashCode());
}
//唤醒在对象上等待的线程
public final native void notify();
//唤醒所有在对象上等待的线程
public final native void notifyAll();
//线程等待
public final native void wait(long timeout) throws InterruptedException;
//线程等待多少时间
public final void wait(long timeout, int nanos) throws InterruptedException {
if (timeout < 0) {
throw new IllegalArgumentException("timeout value is negative");
}
if (nanos < 0 || nanos > 999999) {
throw new IllegalArgumentException(
"nanosecond timeout value out of range");
}
if (nanos > 0) {
timeout++;
}
wait(timeout);
}
//线程等待
public final void wait() throws InterruptedException {
wait(0);
}
//垃圾回收方法
protected void finalize() throws Throwable { }
}
1.1、为什么要有hashCode和equals方法
equals
方法用来判断两个对象是否相等
hashCode
方法返回该对象的哈希码值。它可以为像 HashMap 这样的哈希表有益,也有书上说是对象的内存地址
假如没有hashCode
,集合中的一个对象的判断只能通过遍历每一个集合中的元素然后再用equals
判断,这种方式效率不高,如果能为每一个对象都分配一个特殊的编码,根据这个编码可以计算出这个对象在集合中的位置,然后再用equals
判断是否存在该对象,使用这种方式效率比较高,其实HashMap
的put
和get
操作就是这么实现的。
1.2、为什么hashCode和equals方法要同时重写
由于不同对象的内容不一样,所以equals
方法必须重写。
那为什么要一同重写hashCode
,看下hashCode
注释:
/**
* Returns a hash code value for the object. This method is
* supported for the benefit of hash tables such as those provided by
* {@link java.util.HashMap}.
* <p>
* The general contract of {@code hashCode} is:
* <ul>
* <li>Whenever it is invoked on the same object more than once during
* an execution of a Java application, the {@code hashCode} method
* must consistently return the same integer, provided no information
* used in {@code equals} comparisons on the object is modified.
* This integer need not remain consistent from one execution of an
* application to another execution of the same application.
* <li>If two objects are equal according to the {@code equals(Object)}
* method, then calling the {@code hashCode} method on each of
* the two objects must produce the same integer result.
* <li>It is <em>not</em> required that if two objects are unequal
* according to the {@link java.lang.Object#equals(java.lang.Object)}
* method, then calling the {@code hashCode} method on each of the
* two objects must produce distinct integer results. However, the
* programmer should be aware that producing distinct integer results
* for unequal objects may improve the performance of hash tables.
* </ul>
* <p>
* As much as is reasonably practical, the hashCode method defined by
* class {@code Object} does return distinct integers for distinct
* objects. (This is typically implemented by converting the internal
* address of the object into an integer, but this implementation
* technique is not required by the
* Java™ programming language.)
*
* @return a hash code value for this object.
* @see java.lang.Object#equals(java.lang.Object)
* @see java.lang.System#identityHashCode
*/
翻译:
- 在应用程序的执行期间,只要对象的
equals
方法的比较操作所用到的信息没有被修改,那么对同一个对象的多次调用,hashCode
方法都必须始终返回同一个值。 - 如果两个对象根据
equals
方法比较是相等的,那么调用这两个对象中的hashCode
方法都必须产生同样的整数结果。 - 如果两个对象根据
equals
方法比较是不相等的,那么调用者两个对象中的hashCode
方法,则不一定要求hashCode
方法必须产生不同的结果。但是给不相等的对象产生不同的整数散列值,是有可能提高散列表(hash table
)的性能。
下面来做一个违背注释2
的验证:
我们定义一个Person
的类,只重写equals
方法,然后作为HashMap
的key
。
import java.util.Objects;
/**
* @author DUCHONG
* @since 2020-09-07 17:26:36
**/
public class Person {
public Person() {
}
public Person(String name, int age) {
this.name = name;
this.age = age;
}
private String name;
private int age;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Person person = (Person) o;
return age == person.age &&
Objects.equals(name, person.name);
}
}
main
方法里面new
了两个对象person1
和person2
,由于重写了equals
方法,name
和age
都相同,然后将person1
放在hashMap
中,利用person2
作为key
去get
import java.util.HashMap;
/**
* @author DUCHONG
* @since 2020-09-07 17:20
**/
public class ObjectDemo {
public static void main(String[] args) {
Person person1 = new Person("demo2",2);
Person person2 = new Person("demo2",2);
System.out.println("person1.equals person2---"+person1.equals(person2));
//只重写equals不重写hashCode,调用object的hashCode
System.out.println("person1 hashCode---"+person1.hashCode());
System.out.println("person1 hashCode---"+person2.hashCode());
HashMap<Person,String> hashMap=new HashMap<>();
hashMap.put(person1,"person1");
//这里用person1相同的person2去获取
System.out.println("name---"+hashMap.get(person2));
}
}
结果
看到了吧,获取到的值为null
,由于hashmap
在put
时是以key
的hashCode
来计算对象在集合中的位置的,由于person1
和person2
的hashCode
不同,自然分配到两个不同的位置,然后用equal
相同的key
是获取不到的。
但是name
为demo2
的那个person
确实在集合中啊,再者说你把Person
换替成String
这个类,一个String
类型的key
对应一个value
存在hashmap
中,然后用相同的key
却获取不到这个value
,这就乱了,集合没法用了。
接下来重写hashCode
方法,重新运行main
方法
import java.util.Objects;
/**
* @author DUCHONG
* @since 2020-09-07 17:26:36
**/
public class Person {
public Person() {
}
public Person(String name, int age) {
this.name = name;
this.age = age;
}
private String name;
private int age;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Person person = (Person) o;
return age == person.age &&
Objects.equals(name, person.name);
}
@Override
public int hashCode() {
return Objects.hash(name, age);
}
}
结果
同时重写equals()
和hashCode()
可以正常获取,再去看String
类的源码,也是同时重写了这两个方法的。
二、String类
既然上面提到了String类,这里就简单介绍下,jvm为了提供性能和减少内存开销,提供了一个常量池的,当一个String类被创建时,先会去查看常量池中有没有该对象,有,返回引用,没有则创建。
Java 6及以前,字符串常量池存放在永久代
Java 7中 oracle的工程师对字符串池的逻辑做了很大的改变,即将字符串常量池的位置调整到Java堆内
Java8元空间,字符串常量在堆
2.1、两种初始化方式
直接赋值:会自动进入常量池,也就是常量池中有一个对象的引用,当再创建同样的字符串时,发现常量池中有则直接使用。
使用new关键字:堆中有对象实例,还是先检查常量池中有没有,如果有,返回实例,如果没有则创建,都需要在堆中new一个出来,若常量池中无,则创建两个对象,有,则创建一个对象。
2.2、==和equals
==
:基本数据类型
,比较的是值
,引用数据类型
比较的是内存地址
equals
:属于Object
类的方法,没有重写过equals
方法,比较的是地址
,字符串里面的equals
被重写过了,比较的是值