概述
- Object类是Java类结构的“根”。
- Java中所有类都继承于Object类,即Object是其他类的父类。
- 所有的对象,包括泛型,都需要继承Object类中的方法。
核心方法
native是与C++ 联合开发的时候用的。使用native关键字说明这个方法是原生函数,被编译成了DLL,由java去调用。
equals
功能
比较是否对象是否相等
特性
- 自反性:对于任何非null的引用值x,x.equals(x)必须返回true。
- 对称性:对于任何非null的引用值x和y,当且仅当y.equals(x)返回true时,x.equals(y)必须返回true。
- 传递性:对于任何非null的引用值x、y、z,如果x.equals(y)返回true,并且y.equals(z)返回true,那么x.equals(z)也必须返回ture。
- 一致性:对于任何非null的引用值x和y,只要equals的比较操作在对象中所用的信息没有被修改,多次调用x.equals(y)就会一致的返回ture,或者一致的返回false。
- 非空性:对于任何非null的引用值x,x.equals(null)必须返回false。
示例
1
2
3
4
5public class User{
private int age;
private String name;
//省略构造函数和set、get
}
重写equals方法,先判断对象是否为null,再判断对象是否指定类型,最后判断对象的属性是否相等。
1 |
|
注意:在应用场景中,假设变量X要与常量C进行比较,一般用C.equals(X)而不是X.equals(C),由于equals的调用者是非空的,而我们已知常量C为非空,这样避免抛出异常。
当覆盖equals方法时,必须同时覆盖hashCode方法。这是为了维护使用hashCode来判断是否为同一对象的方法,如HashMap。
hashCode
功能
返回对象的哈希值,主要是对哈希表提供支持,如HashMap。
示例
1 | a.hashCode() |
在Object中hashCode方法是native,那如果我们重写了equals方法的话,该如何重写hashCode方法呢?
如何重写hashCode方法
定义一个整型变量result,并赋值一个非零数,如17。
1
int result = 17;
根据对象的域(类型)进行计算。
- boolean:计算(f?1:0)。
- byte、char、short、int:计算(int)f。
- long:计算(int)(f^(f>>>32))。
- float:计算Float.floatToIntBits(f)。
- double:计算Double.doubleToLongBits(f),然后再计算long。
- 如果该域是一个对象引用,并且该类的equals方法通过递归的调用equals的方式来比较这个域,则同样为这个域递归的调用hashCode。如果这个域的值为null,则返回0。
- 如果该域是一个数组,则要把每一个元素当作单独的域来处理,也就是说,递归地应用上述规则,对每个重要元素计算一个散列码,然后把这些散列值组合起来。也可以使用Arrays.hashCode方法。
1
2
3
4//String name
result = result * 31 + name.hashCode();
//int age
result = result * 31 + age;
- 返回结果。
1
return result;
clone
功能
复制对象,可以将其视为除了new以外,另一种新建对象的方式。
特性
x.clone().getClass() == x.getClass()、x.clone() != x两个表达式为true- 如果需要复制的对象中包含引用类型,则该引用类型在复制后,仍然指向同一个地址。
示例
1
2
3
4User u = new User(15,"test");
u.equals(u.clone()); //false
u.getClass().equals(u.clone().getClass()); //true
u.getName().equals(u.clone().getName()); //true
《Effective Java》的作者Joshua Bloch建议我们不应该实现Cloneable接口,而应该使用拷贝构造器或者拷贝工厂。
toString
功能
将对象以字符串的形式表示。
特性
- 默认表达式:
getClass().getName() + '@' + Integer.toHexString(hashCode()),即类的名称的字符串+@+对象的哈希码的无符号的十六进制
其他方法
registerNatives
- 声明为public final native
- 该方法主要是为了服务于JNI(Java Native Interface)的,它主要是提供了java类中的方法与对应C++ 代码中的方法的映射,方便jvm查找调用C++ 中的方法。
getClass
- 声明为public final native
- 获取Class类
wait、notify、notifyAll
- 声明为public final native
- 多线程基础
- 掘金:Wait/Notify通知机制解析
- 简书:notify()和wait()例子
- wait和notify基本原理示例
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31public class ThreadA {
private static int total;
public static void main(String[] args) {
ThreadB b = new ThreadB();
b.start();
synchronized (b){
System.out.println("Waiting for b to complete...");
try {
b.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Total is:"+total);
}
}
static class ThreadB extends Thread{
public void run() {
synchronized (this){
System.out.println("b is counting...");
for(int i = 0;i < 100;i++){
total += i;
if(total >100){
notify();
}
}
}
}
}
}
上述执行结果:
1 | Waiting for b to complete... |
从上述结果可以看出,线程A并不是在线程B调用notify时被立即唤醒,而是等待线程B的同步代码块执行完后,再从wait方法后继续执行。synchronized同步方法(块)同一时刻只允许一个线程在里面,线程B不退出,线程A就阻塞。
原理:
- 当线程A调用wait方法后,线程A让出锁,同时加入锁对象等待队列。
- 线程B获取锁后,调用notify方法通知锁对象等待队列,使得线程A从等待队列进入阻塞队列。
- 线程A进入阻塞队列后,直至线程B释放锁后,线程A竞争得到锁继续从wait方法后执行。
finalize
- 当垃圾收集确定不再有对该对象的引用时,垃圾收集器在对象上调用该方法。
- 现已不建议使用。
参考
- Oracle Java8 api
- Java8 中文文档
- jdk源码分析(一)——Object类
- 详解Java中的clone方法 – 原型模式