Java8 Object源码阅读

概述

Object类是类层次结构的根类,是每个Class的超类,Java中的所有类都默认继承自Object类,可以直接调用Object的成员方法。

方法摘要:

  • clone():创建并返回此对象的一个副本
  • equals(Object obj) :判断两个对象是否相等
  • hashCode():返回该对象的哈希码值
  • getClass():返回该对象的运行时类
  • toString():返回该对象的字符串表示
  • notify():唤醒在此对象monitor上等待的单个线程
  • notifyAll():唤醒在此对象monitor上等待的所有线程
  • wai():在其他线程调用此对象的notify()方法或notifyAll()方法前,导致当前线程等待,这个方法有三个重载
  • finalize():当垃圾回收器确定不存在对该对象的更多引用时,由对象的垃圾回收器调用此方法

Object源码阅读笔记

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
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
package java.lang;

/**
* Class {@code Object} is the root of the class hierarchy.
* Every class has {@code Object} as a superclass. All objects,
* including arrays, implement the methods of this class.
*
* Object类是类层次结构的根类,是每个Class的超类;
* Java中的类默认继承Object类,都可以直接调用Object的成员方法
*/

public class Object {
// registerNatives()为本地方法,由C/C++在dll中实现的,再通过JNI调用
private static native void registerNatives();
// 定义了一个静态初始化块,当创建Object对象时,会自动调用registerNatives()方法
static {
registerNatives();
}

// 返回对象的运行时类,不可重写
public final native Class<?> getClass();

/**
* 返回对象的HashCode
*
* 对同一对象多次调用hashCode()方法时,必须返回相同的int值
* 若根据equals()方法判断两个对象是相等的,那么它们调用hashCode()方法都必须返回相同的int值;但是若判断两个对象不相等,则不要求一定要生成不同的int值
*/
public native int hashCode();

/**
* 判断两个对象是否相等
*
* 重写equals()方法需要满足以下等价关系(equivalence relation):
* 1. 自反性:对于任何非空引用值 x,x.equals(x) 都应返回 true
* 2. 对称性:对于任何非空引用值 x 和 y,当且仅当 y.equals(x) 返回 true 时,x.equals(y) 才应返回 true
* 3. 传递性:对于任何非空引用值 x、y 和 z,如果 x.equals(y) 返回 true,并且 y.equals(z) 返回 true,那么 x.equals(z) 应返回 true
* 4. 一致性:对于任何非空引用值 x 和 y,多次调用 x.equals(y) 始终返回 true 或始终返回 false,前提是对象上 equals 比较中所用的信息没有被修改
* 5. 非空性:对于任何非空引用值x,x.equals(null)都应返回 false
*
* ==对于8大基本类型,比较的是【值】
* 浮点型:float(4 byte), double(8 byte)
* 整型:byte(1 byte), short(2 byte), int(4 byte) , long(8 byte)
* 字符型: char(2 byte)
* ==对于引用类型比较的则是【内存地址】
* 因此equals()也默认比较的是内存地址,但一些类如String、Integer会重写该方法,比较的是内容(or值)
*
* 若重写equals(),同时必须要重写hashCode()方法,不这么做的话会违反Object.hashCode的通用规范,导致该类不能与基于Hash的集合类(如HashMap,HashSet,HashTable)一起正常运作
*/
public boolean equals(Object obj) {
return (this == obj);
}

/**
* 返回对象的一个浅拷贝(只对引用进行拷贝,实际仍指向同一内存地址),具体的含义是:
* 对于x.clone() != x,返回true
* 对于x.clone().getClass() == x.getClass(),返回true
* 对于x.clone().equals(x),返回true
* 但这些要求都不一定要满足
*
* clone()方法是由protected修饰的,无法在类外调用
* 如果对象没有实现Cloneable接口,将会被抛出CloneNotSupportedException异常
*/
protected native Object clone() throws CloneNotSupportedException;

/**
* 返回该对象的字符串表示
*
* 原注释中建议所有子类都重写该方法
*/
public String toString() {
return getClass().getName() + "@" + Integer.toHexString(hashCode());
}

/**
* 唤醒在此对象monitor(监视器)上等待的单个线程
*
* 如果其他线程在此对象上等待,则会选择唤醒其中一个线程,选择策略可以是任意性的,取决于具体实现
* 线程通过调用对象的wait()方法,会成为对象monitor上的等待线程
* 被唤醒的线程并不能立即执行操作,除非当前线程释放了该对象的锁,被唤醒的线程将会和其它线程一起竞争这个对象,它们的优先级是一样的
*
* notify()方法只能被此对象monitor的所属线程来调用,一个线程成为某个对象的monitor所有者的方式有以下3种:
* 1. 通过执行该对象的synchronized实例方法
* 2. 通过执行synchronized代码块
* 3. 对于Class类型对象,通过执行该类的synchronized static方法
*/
public final native void notify();

/**
* 唤醒在此对象monitor上等待的所有线程
* 一个线程可以通过调用wait()方法,在对象monitor上等待
* 被唤醒的线程并不能立即执行操作,除非当前线程释放了该对象的锁,被唤醒的线程将会和其它线程一起竞争这个对象,它们的优先级是一样的
* notifyAll()方法也只能被此对象monitor的所属线程来调用,关于一个线程如何成为某个对象的monitor所有者的方式参考notify()注释
*/
public final native void notifyAll();

/**
* 调用wait()方法会使得当前线程处于等待状态,直到其它线程调用了对应对象的notify()方法或notifyAll()方法,或超过了等待时间
*
* 当前线程必须拥有此对象的monitor
*
* 调用wait()方法的线程(这里称为线程T)会将自己放在当前对象的wait set中,并放弃所有对于该对象的同步声明;基于线程调度的目的,线程T将被禁用并保持睡眠,直到触发以下4个条件之一:
* 1. 其他某个线程调用此对象的notify()方法,并且线程T恰好是被那个选中唤醒的线程
* 2. 其它某个线程调用了此对象的notifyAll()方法
* 3. 其他某个线程中断线程T
* 4. 到达了线程T的等待时间,如果timeout在一开始被设为0,则不考虑实际的时间,该线程将一直等待直到被其他线程唤醒
*
* 当线程T被唤醒后,会从wait set中删除并重新参与线程调度,然后线程T以常规方式(相同优先级)与其他线程竞争对当前对象的同步操作。如果线程T获得了当前对象的控制权,那么线程T的同步声明都会被恢复到它之前调用wait()方法时候的状态,然后从wait()方法返回。
* 所以,从wait()方法返回时,该对象和线程T的同步状态与调用wait方法时时的状态完全相同。
*
* 在没有被唤醒、中断或等待时间超时的情况下,线程也能自己醒来,这种情况被称为虚假唤醒 (spurious wakeup),尽管这在实践中几乎不会出现,但是应用程序还是应该防范这一情况的出现。应当测试线程被唤醒的条件,如果不满足该条件,则继续等待,这样可以达到防御目的。换句话说,wait()方法必须放在循环里面,如下面例子所示:
* synchronized (obj) {
* while (condition does not hold)
* obj.wait(timeout);
* }
*
* (For more information on this topic, see Section 3.2.3 in Doug Lea's
* "Concurrent Programming in Java (Second Edition)" (Addison-Wesley,
* 2000), or Item 50 in Joshua Bloch's "Effective Java Programming
* Language Guide" (Addison-Wesley, 2001).
* 注:"Effective Java"现在应该是第69条了。
*
* 如果当前线程在wait()方法调用前或者正在wait()时被其他线程中断,则会抛出异常InterruptedException,这个异常只会当前对象的锁状态被重置为上述情况下才会抛出
* 注意:由于wait()方法将当前进程放到当前对象的wait set中,所以当wait方法结束时,解锁的也只是当前这个对象,该进程在其他对象上的锁保持不变。
*
* wait(long timeout)方法只能被此对象monitor的所属线程来调用,关于一个线程如何成为某个对象的monitor所有者的方式参考notify()注释
*/
public final native void wait(long timeout) throws InterruptedException;

/**
* 调用wait()方法会使得当前线程处于等待状态,直到其它线程调用了对应对象的notify()方法或notifyAll()方法,或超过了等待时间
*
* 这个方法类似于只有一个timeout参数的wait()方法,但该方法可以更好地控制在放弃之前等待通知的时间。按纳秒计算的实际时间可以通过以下公式计算出来:
* 1000000*timeout+nanos
* 通过格式可以看出参数timeout的单位是毫秒,参数nanos的单位是纳秒
* 在其他方面,这个方法执行的操作和wait(long timeout)方法相同,需要特别指出的是wait(0)和wait(0,0),这两个方法是等效的
*
* 当前线程必须拥有对象的monitor。当前线程释放monitor的所有权并进行等待,直到下面2种情况其中之一发生为止:
* 1. 其他某个线程唤醒了对象monitor上等待的线程,这一唤醒可以通过调用notify()方法或者notifyAll()方法来完成
* 2. 1000000*timeout+nanos 的等待时间到达
*
* 当前线程会一直等待,直到它重新获得对象monitor的所有权,然后恢复运行
*
* 和wait(long timeout)方法中一样,在这个方法中,中断和虚假唤醒也是可能会发生的,这个方法也应该放在循环里面,如下所示:
* synchronized (obj) {
* while (<condition does not hold>)
* obj.wait(timeout, nanos);
* ... // Perform action appropriate to condition
* }
*
* wait(long timeout, int nanos)方法也只能被此对象monitor的所属线程来调用,关于一个线程如何成为某个对象的monitor所有者的方式参考notify()注释
*
*/
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 >= 500000 || (nanos != 0 && timeout == 0)) {
timeout++;
}

wait(timeout);
}

/**
* 调用这个wait()方法会使得当前线程处于等待状态,直到其它线程调用了对应对象的notify()方法或notifyAll()方法,或超过了等待时间
* 换句话说,这个方法和wait(0)方法的功能基本一样
*
* 当前线程必须有这个对象的monitor,当前线程调用了wait方法后,会释放对象的监听器,然后进入等待状态,直到其他线程通过调用notify()方法或notifyAll()方法通知在此对象的monitor上等待的线程醒来,然后该线程会等到重新获得此对象monitor所有权后才能继续执行
*
* 中断和虚假唤醒也是有可能发生的,参照上面注释
*/
public final void wait() throws InterruptedException {
wait(0);
}

/**
* 当垃圾回收器确定不存在对该对象的引用时,由该对象的垃圾回收器调用此方法
* 子类重写finalize方法,是为了释放系统资源,或执行其他的清理工作
*
* finalize 的通用规范是:
* 当JVM已确定当前存活的所有线程都不会再次通过任何方法访问这个对象时,就会调用该对象的finalize()方法,除非其他即将被finalized的object或class已经将该对象finalize了。
* finalize()方法可以采取任何行为,包括使得某个Object成为某个线程的可利用资源(此时,这个Object不会被jvm回收,而是被重新使用);不过finalize()方法的通常功能是在一个对象被不可撤销地丢弃前,做一些相关的清除工作。例如,一个用于I/O连接的对象在被永久丢弃前,它的finalize()方法可能会执行一些I/O事务以断开连接
*
* Object类的finalize()方法并没有什么执行什么特别的行为,它仅执行一些常规返回,Object的子类可以重写此方法
*
* JAVA语言不会保证由哪个线程调用某个Object的finalize()方法,但是可以保证当一个线程在调用finalize()方法时,这个线程将不会持有对任何用户可见的同步锁;如果finalize()方法抛出了一个未被捕获的异常,那么这个异常会被忽略,并且finalize()的执行就此结束
*
* 当某个Object执行了finalize()方法后,就不会再执行其他操作,直到JVM再次确认这个Object不会再被其他存活的线程所访问(JVM会对这个Object执行GC),这些操作包括:其他即将被finalized的object或class的某些操作可能会导致该对象被丢弃
*
* 对于任意一个Object,JVM最多只调用一次finalize()方法
*
* finalize()方法执行过程中抛出的异常会使得对这个Object的finalize()操作终止,但这一情况是被忽略的
*/
protected void finalize() throws Throwable { }
}
  • 本文作者: Marticles
  • 版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 3.0 许可协议。转载请注明出处!