JAVA源码解析之Object
简介
众所周知, JAVA是作为一门面向对象的语言去设计的。所有的JAVA对象中都会有一些方法,比如hashCode(),wait()等等,这些方法都是因为JAVA中的对象有一个统一的祖先类:Object,而今天我们就要解读一下这个祖先类。
常用方法
getClass()
简介
首先看下这个方法的源码:
1 |
|
这个方法主要用来获取运行时对象的类信息,返回值为一个Class类型的对象,通过这个对象我们可以获取到与该类相关的各种元数据,比如类名、报名、方法以及属性等等,这使得它在反射编程中非常有用。
用例
1 | public static void main(String[] args) { |
以下为出参,可以看到animal1和animal3虽然都是Animal类声明,但是运行时却准确的指向了各自实际的类
1 | class lang.object.Monkey |
hashCode()
简介
返回一个Object对象的hash码,这是一个int类型的数值,主要用来支持一些基于hash表构建的类,比如经典的HashMap等等。在对象信息不变的情况下,无论什么时候它的Hash值都是不变的。如果两个不同对象产生了相同的hash值,我们一般称这种现象为hash冲突。hashCode()方法要遵守一下约定
- 对同一个对象,无论什么时候调用hashCode(),返回值都是不会变的;
- 如果两个对象的eques()方法为true,那么他们各自的hashCode()方法返回的hash值必定相等;
- 如果两个对象的eques()方法为false,那么他们各自的hashCode()方法返回的hash值也不相等;
1 |
|
equals(Object obj)
简介
用来声明两个对象的引用地址相等。对于一些类会重写equals()方法,使得它不仅能比较引用地址,也能比较对象内容是否相等,典型的比如String类。
针对任意两个非NULL的对象,一般有以下特性:
- 自反性
- 对于非空对象x,那么x.equals(x)必定为true
1
2
3
4public static void main(String[] args) {
Monkey animal = new Monkey();
System.out.println(animal.equals(animal));
}
- 对于非空对象x,那么x.equals(x)必定为true
- 对称性
- 对于任意非空x和y,如果x.equals(y)为true,那么y.equals(x)也为true。注意这里的String重写了equals()方法,使得它能比较对象引用地址以及对象内容是否相等。这里以此证明它的对成习惯特性。
1
2
3
4
5
6public static void main(String[] args) {
String x = new String("Hello");
String y = new String("Hello");
System.out.println(x.equals(y)); // 输出 true
System.out.println(y.equals(x)); // 输出 true
}
- 对于任意非空x和y,如果x.equals(y)为true,那么y.equals(x)也为true。注意这里的String重写了equals()方法,使得它能比较对象引用地址以及对象内容是否相等。这里以此证明它的对成习惯特性。
- 传递性
- 对于任意非空对象x,y,z, 如果x.equals(y)为true,y.equals(z)为true,那么x.equals(z)必定也为true
1
2
3
4
5
6
7
8public static void main(String[] args) {
String x = new String("Hello");
String y = new String("Hello");
String z = new String("Hello");
System.out.println(x.equals(y)); // 输出 true
System.out.println(y.equals(z)); // 输出 true
System.out.println(x.equals(z)); // 输出 true
}
- 对于任意非空对象x,y,z, 如果x.equals(y)为true,y.equals(z)为true,那么x.equals(z)必定也为true
- 一致性
- 对于任意非空对象x,y,如果x.equals(y)为true且不调整对象x和y的属性,那么不论多用多少此x.equals(y),结果都为true
clone()
简介
先看下源码:
1 |
|
可以看到它是一个protected修饰的,意味着我们不能直接调用这个方法。它的作用就是copy一个对象的所有属性,并返回一个新的对象。需要注意两点,第一是克隆的对象必须要实现 Cloneable 接口并重写 clone 方法,否则会报 CloneNotSupportedException 异常;第二是这里的copy是浅拷贝,不是深拷贝,这意味我们只能复制基础属性,对于引用则是复制引用,而不是完整的复制一个对象的所有属性。
toString()
简介
1 | public String toString() { |
以字符串的形式表现一个对象。
- getClass().getName(),返回对象的类路径+类名,比如 lang.object.Student
- Integer.toHexString(hashCode()),将哈希值转成16进制数格式的字符串。
wait()
简介
使当前线程处于等待状态,直到它被唤醒。wait()方法主要有三种实现,这里统一来讲一下。
1 | // 1. 无限等待下去,直到被唤醒 |
处于等待状态的线程会释放掉自己的锁, 在被唤醒后会重新去竞争锁.被唤醒的场景主要有以下几种:
1、其它线程调用notify()或者notifyAll()方法,导致当前线程被唤醒
2、其他线程中断了这个线程,即调用了该线程的interrupt()方法,此时被唤醒的线程不会再继续执行,而是抛出InterruptedException异常
3、过了指定的超时时间而被唤醒,主要是调用了wait(long timeoutMillis)或者wait(long timeoutMillis, int nanos)方法
1 | final void checkForComodification() { |
以下为被中断时抛出异常的例子,执行完毕后会打印”线程被中断,抛出InterruptedException异常”:
1 | public class OwnerThread extends Thread{ |
notify()与notifyAll()
简介
都用于唤醒处于waiting状态的线程,区别是notify()方法会随机唤醒一个线程,而notifyAll()则是将所有处于等待队列的线程都唤醒,一同去竞争锁。只有拥有对象锁的线程才能调用者两个方法,线程通过以下三种方式之一成为对象监视器的所有者:
1、通过执行该对象的同步实例方法。
2、通过执行在对象上同步的语句的 synchronized 主体
3、对于类型的 Class, 对象,通过执行该类的同步静态方法