Object
Java的Object是所有Java类的父类,所有的Java类直接或者间接的继承了Object类,Object类位于java.lang包中(编译时自动导入),主要提供了11种方法。
/**
* native 方法,用于返回当前运行时对象的 Class 对象,使用了 final 关键字修饰,故不允许子类重写。
*/
public final native Class<?> getClass()
/**
* native 方法,用于返回对象的哈希码,主要使用在哈希表中,比如 JDK 中的HashMap。
*/
public native int hashCode()
/**
* 用于比较 2 个对象的内存地址是否相等,String 类对该方法进行了重写以用于比较字符串的值是否相等。
*/
public boolean equals(Object obj)
/**
* native 方法,用于创建并返回当前对象的一份拷贝。
*/
protected native Object clone() throws CloneNotSupportedException
/**
* 返回类的名字实例的哈希码的 16 进制的字符串。
*/
public String toString()
/**
* native 方法,并且不能重写。唤醒一个在此对象监视器上等待的线程(监视器相当于就是锁的概念)。如果有多个线程在等待只会任意唤醒一个。
*/
public final native void notify()
/**
* native 方法,并且不能重写。跟 notify 一样,唯一的区别就是会唤醒在此对象监视器上等待的所有线程,而不是一个线程。
*/
public final native void notifyAll()
/**
* native方法,并且不能重写。暂停线程的执行。注意:sleep 方法没有释放锁,而 wait 方法释放了锁 ,timeout 是等待时间。
*/
public final native void wait(long timeout) throws InterruptedException
/**
* 多了 nanos 参数,这个参数表示额外时间(以纳秒为单位,范围是 0-999999)。 所以超时的时间还需要加上 nanos 纳秒。。
*/
public final void wait(long timeout, int nanos) throws InterruptedException
/**
* 跟之前的2个wait方法一样,只不过该方法一直等待,没有超时时间这个概念
*/
public final void wait() throws InterruptedException
/**
* 实例被垃圾回收器回收的时候触发的操作
*/
protected void finalize() throws Throwable { }
核心方法
equals
用于比较两个对象的是否一致,默认比较的是地址,为什么是地址呢?在equals的源码中,用的是“==”进行两个对象的比较,而“==”在比较对象(引用数据类型)的时候,就是比较对象的地址是否相同。例如
package new_2025.demo11;
public class Student {
public String name;
public int age;
public Student(String name,int age){
this.name = name;
this.age = age;
}
}
class Main {
public static void main(String[] args) {
Student stu1 = new Student("张三",18);
Student stu2 = new Student("张三",18);
System.out.println("stu1 = " + stu1);
System.out.println("stu2 = " + stu2);
System.out.println(stu1.equals(stu2));
System.out.println(stu1 == stu2);
}
}
由于没有重写equals方法,而且两者不是引用的同一对象,比较的是对象的引用,因此两者都是false.
当重写了equal方法之后,自定义根据对象中的值进行比较,代码和结果如下
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Student student = (Student) o;
return age == student.age &&
name.equals(student.name);
}
那么,equals和==有什么不同?
- “==”,对于基本的数据类型,直接按照值进行比较,对于引用数据类型,比较对象的内存地址,也就是比较是否是一个对象的实例。
- equals,默认继承Oject类的实现,和==行为相同,都是比较引用地址,但是许多类重写了equals,例如String,Integer,使其比较的是对象的值而不是地址。
- 重写equals方法时必须重写hashCode方法,否则就可能导致无法正确去重的现象。
import java.util.HashSet; import java.util.Set; // 定义一个 Person 类,重写了 equals 但未重写 hashCode class Person { private String name; private int age; public Person(String name, int age) { this.name = name; this.age = age; } // 重写 equals 方法:根据 name 和 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 && name.equals(person.name); } // 未重写 hashCode 方法!(使用 Object 的默认实现) public static void main(String[] args) { Set<Person> set = new HashSet<>(); Person p1 = new Person("Alice", 30); Person p2 = new Person("Alice", 30); // 字段相同,但对象不同 set.add(p1); set.add(p2); System.out.println("Expected size: 1"); System.out.println("Actual size: " + set.size()); // 输出 2!而非预期的 1 } }
HashSet先通过hashCode定位到存储位置,由于哈希码不同,认为是不同的元素,因此直接跳过equals比较,导致重复存储。
-
对于Integer来说,像Integer a == 100这样的,是通过集中装箱(Integer.valueOf(100))进行创建,在Integer中缓存默认是-128~127,在这个范围内,a和b指向的是同一对象,因此a==b是true,c和d不在这个范围内,那么就是两个不同的对象,因此c==d是false.
Integer a = 100;
Integer b = 100;
Integer c = 200;
Integer d = 200;
System.out.println("a == b :" + (a == b));
System.out.println("c == d :" + (c == d));
System.out.println("a.equals(b) :" + a.equals(b));
System.out.println("c.equals(d) :" + c.equals(d));
toString()
重写玩hashCode方法后,打印两个对象得到 ,指向的是两个对象的引用地址,这是println内部调用valueOf方法,valueOf方法内部调用了toString,toString内部比较的是引用对象的地址,如果是要比较两个对象的值,需要重写toString方法
最终得到