Object类实例方法
Object 类是所有类的超类,可见这个类是有多么基础,Object类内部可以考察的知识点也是可以非常深入的。我们先从Object实例方法入手,所有的方法都很基础,需要熟记于心。
public class Object {
//将Java层的方法名和本地函数对应起来
private static native void registerNatives();
static {
registerNatives();
}
//得到当前Object的类类型
public final native Class<?> getClass();
public native int hashCode();
//直接比较地址值
public boolean equals(Object obj) {
return (this == obj);
}
//克隆当前实例
protected native Object clone() throws CloneNotSupportedException;
//类名字@类实例内存地址
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 {}
}
registerNatives方法
private static native void registerNatives();
static {
registerNatives();
}
从上面的代码中看到Object类定义了一个静态初始化块,我们知道当创建Java对象时,系统总是先调用静态初始化块。而静态初始化块调用的是registerNatives方法,它的修饰的关键字使用了native。Java中,用native关键字修饰的函数表明该方法的实现并不是在Java中去完成,而是由C/C++去完成,并被编译成了.dll文件,由Java去调用。方法的具体实现体在dll文件中,对于不同平台,其具体实现可能有所不同。而registerNatives()方法本身,其主要作用是将C/C++中的方法映射到Java中的native方法,实现方法命名的解耦。registerNatives()对应的本地方法为Java_java_lang_Object_registerNatives
JNIEXPORT void JNICALL
Java_java_lang_Object_registerNatives(JNIEnv *env, jclass cls){
(*env)->RegisterNatives(env, cls, methods, sizeof(methods)/sizeof(methods[0]));
}
可以看到它间接调用了JNINativeInterface_结构体的方法,简单可以看成是这样:它干的事大概就是将Java层的方法名和本地函数对应起来,方便执行引擎在执行字节码时根据这些对应关系表来调用C/C++函数,如下面,将这些方法进行注册,执行引擎执行到hashCode方法时就可以通过关系表来查找到JVM的JVM_IHashCode函数,其中()I还可以得知Java层上的类型应该转为int类型。这个映射其实就可以看成将字符串映射到函数指针。
static JNINativeMethod methods[] = {
{"hashCode", "()I", (void *)&JVM_IHashCode},
{"wait", "(J)V", (void *)&JVM_MonitorWait},
{"notify", "()V", (void *)&JVM_MonitorNotify},
{"notifyAll", "()V", (void *)&JVM_MonitorNotifyAll},
{"clone", "()Ljava/lang/Object;", (void *)&JVM_Clone},
};
getClass方法
getClass方法也是个本地方法,对应的本地方法为Java_java_lang_Object_getClass
JNIEXPORT jclass JNICALL
Java_java_lang_Object_getClass(JNIEnv *env, jobject this){
if (this == NULL) {
JNU_ThrowNullPointerException(env, NULL);
return 0;
} else {
return (*env)->GetObjectClass(env, this);
}
}
所以这里主要就是看GetObjectClass函数了,Java层的Class在C++层与之对应的则是klassOop,所以关于类的元数据和方法信息可以通过它获得。
JNI_ENTRY(jclass, jni_GetObjectClass(JNIEnv *env, jobject obj))
JNIWrapper("GetObjectClass");
DTRACE_PROBE2(hotspot_jni, GetObjectClass__entry, env, obj);
klassOop k = JNIHandles::resolve_non_null(obj)->klass();
jclass ret =
(jclass) JNIHandles::make_local(env, Klass::cast(k)->java_mirror());
DTRACE_PROBE1(hotspot_jni, GetObjectClass__return, ret);
return ret;
JNI_END
hashCode方法
由前面registerNatives方法将几个本地方法注册可知,hashCode方法对应的函数为JVM_IHashCode,即
JVM_ENTRY(jint, JVM_IHashCode(JNIEnv* env, jobject handle))
JVMWrapper("JVM_IHashCode");
// as implemented in the classic virtual machine; return 0 if object is NULL
return handle == NULL ? 0 : ObjectSynchronizer::FastHashCode (THREAD, JNIHandles::resolve_non_null(handle)) ;
JVM_END
对于hashcode生成的逻辑由synchronizer.cpp的get_next_hash函数决定,实现比较复杂,根据hashcode的不同值有不同的生成策略,最后使用一个hash掩码处理。
static inline intptr_t get_next_hash(Thread * Self, oop obj) {
intptr_t value = 0 ;
if (hashCode == 0) {
value = os::random() ;
} else
if (hashCode == 1) {
intptr_t addrBits = intptr_t(obj) >> 3 ;
value = addrBits ^ (addrBits >> 5) ^ GVars.stwRandom ;
} else
if (hashCode == 2) {
value = 1 ; // for sensitivity testing
} else
if (hashCode == 3) {
value = ++GVars.hcSequence ;
} else
if (hashCode == 4) {
value = intptr_t(obj) ;
} else {
unsigned t = Self->_hashStateX ;
t ^= (t << 11) ;
Self->_hashStateX = Self->_hashStateY ;
Self->_hashStateY = Self->_hashStateZ ;
Self->_hashStateZ = Self->_hashStateW ;
unsigned v = Self->_hashStateW ;
v = (v ^ (v >> 19)) ^ (t ^ (t >> 8)) ;
Self->_hashStateW = v ;
value = v ;
}
value &= markOopDesc::hash_mask;
if (value == 0) value = 0xBAD ;
assert (value != markOopDesc::no_hash, "invariant") ;
TEVENT (hashCode: GENERATE) ;
return value;
}
equals方法
== 符号是来判断左右值的直接引用地址是否相同。而equals则表达逻辑上左右值是否相等。hashCode方法与equals的方法是需要配合的。规则如下:
- 如果 A.equals(B),则 A.hashCode == B.hashCode
- 如果 A == B,则 A.equals(B) => true
- 如果 A.hashCode == B.hashCode,不能推出 A == B
综上,equals ⽅法被覆盖过,则 hashCode ⽅法也必须被覆盖。hashCode()的默认⾏为是对堆上的对象产⽣独特值。如果没有重写 hashCode(),则该 class 的两个对象⽆论如何都不会相等。
clone方法
后面我们详细描述。
toString方法
逻辑是获取class名称加上@再加上十六进制的hashCode。
notify()、notifyAll()、wait()
我们先来看看这几个方法是怎么用的:
public class NotifyTest {
@Test
public void test1() {
Object lock = new Object();
new Thread(new Runnable() {
@Override
public void run() {
System.out.println("线程A等待获取lock锁");
synchronized (lock) {
try {
System.out.println("线程A获取了lock锁");
Thread.sleep(1000);
System.out.println("线程A将要运行lock.wait()方法进行等待");
lock.wait();
System.out.println("线程A等待结束");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}).start();
new Thread(new Runnable() {
@Override
public void run() {
System.out.println("线程B等待获取lock锁");
synchronized (lock) {
System.out.println("线程B获取了lock锁");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("线程B将要运行lock.notify()方法进行通知");
lock.notify();
while (true) {
}
}
}
}).start();
try {
Thread.sleep(1000000L);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
执行之后获得的输出是:
线程A等待获取lock锁
线程A获取了lock锁
线程B等待获取lock锁
线程A将要运行lock.wait()方法进行等待
线程B获取了lock锁
线程B将要运行lock.notify()方法进行通知
notify等方法需要和synchronized关键字配合使用。notify方法用来唤醒线程,final修饰说明不可重写。与之对应的本地方法为JVM_MonitorNotify,ObjectSynchronizer::notify最终会调用ObjectMonitor::notify(TRAPS),这个过程是ObjectSynchronizer会尝试当前线程获取free ObjectMonitor对象,不成功则尝试从全局中获取。
JVM_ENTRY(void, JVM_MonitorNotify(JNIEnv* env, jobject handle))
JVMWrapper("JVM_MonitorNotify");
Handle obj(THREAD, JNIHandles::resolve_non_null(handle));
assert(obj->is_instance() || obj->is_array(), "JVM_MonitorNotify must apply to an object");
ObjectSynchronizer::notify(obj, CHECK);
JVM_END
ObjectMonitor对象包含一个WaitSet队列对象,此对象保存着所有处于wait状态的线程,用ObjectWaiter对象表示。notify要做的事是先获取WaitSet队列锁,再取出WaitSet队列中第一个ObjectWaiter对象,再根据不同策略处理该对象,比如把它加入到EntryList队列中。然后再释放WaitSet队列锁。它并没有释放synchronized对应的锁,所以锁只能等到synchronized同步块结束时才释放。
void ObjectMonitor::notify(TRAPS) {
CHECK_OWNER();
if (_WaitSet == NULL) {
TEVENT (Empty-Notify) ;
return ;
}
DTRACE_MONITOR_PROBE(notify, this, object(), THREAD);
int Policy = Knob_MoveNotifyee ;
Thread::SpinAcquire (&_WaitSetLock, "WaitSet - notify") ;
ObjectWaiter * iterator = DequeueWaiter() ;
if (iterator != NULL) {
TEVENT (Notify1 - Transfer) ;
guarantee (iterator->TState == ObjectWaiter::TS_WAIT, "invariant") ;
guarantee (iterator->_notified == 0, "invariant") ;
if (Policy != 4) {
iterator->TState = ObjectWaiter::TS_ENTER ;
}
iterator->_notified = 1 ;
ObjectWaiter * List = _EntryList ;
if (List != NULL) {
assert (List->_prev == NULL, "invariant") ;
assert (List->TState == ObjectWaiter::TS_ENTER, "invariant") ;
assert (List != iterator, "invariant") ;
}
if (Policy == 0) { // prepend to EntryList
if (List == NULL) {
iterator->_next = iterator->_prev = NULL ;
_EntryList = iterator ;
} else {
List->_prev = iterator ;
iterator->_next = List ;
iterator->_prev = NULL ;
_EntryList = iterator ;
}
} else
if (Policy == 1) { // append to EntryList
if (List == NULL) {
iterator->_next = iterator->_prev = NULL ;
_EntryList = iterator ;
} else {
// CONSIDER: finding the tail currently requires a linear-time walk of
// the EntryList. We can make tail access constant-time by converting to
// a CDLL instead of using our current DLL.
ObjectWaiter * Tail ;
for (Tail = List ; Tail->_next != NULL ; Tail = Tail->_next) ;
assert (Tail != NULL && Tail->_next == NULL, "invariant") ;
Tail->_next = iterator ;
iterator->_prev = Tail ;
iterator->_next = NULL ;
}
} else
if (Policy == 2) { // prepend to cxq
// prepend to cxq
if (List == NULL) {
iterator->_next = iterator->_prev = NULL ;
_EntryList = iterator ;
} else {
iterator->TState = ObjectWaiter::TS_CXQ ;
for (;;) {
ObjectWaiter * Front = _cxq ;
iterator->_next = Front ;
if (Atomic::cmpxchg_ptr (iterator, &_cxq, Front) == Front) {
break ;
}
}
}
} else
if (Policy == 3) { // append to cxq
iterator->TState = ObjectWaiter::TS_CXQ ;
for (;;) {
ObjectWaiter * Tail ;
Tail = _cxq ;
if (Tail == NULL) {
iterator->_next = NULL ;
if (Atomic::cmpxchg_ptr (iterator, &_cxq, NULL) == NULL) {
break ;
}
} else {
while (Tail->_next != NULL) Tail = Tail->_next ;
Tail->_next = iterator ;
iterator->_prev = Tail ;
iterator->_next = NULL ;
break ;
}
}
} else {
ParkEvent * ev = iterator->_event ;
iterator->TState = ObjectWaiter::TS_RUN ;
OrderAccess::fence() ;
ev->unpark() ;
}
if (Policy < 4) {
iterator->wait_reenter_begin(this);
}
// _WaitSetLock protects the wait queue, not the EntryList. We could
// move the add-to-EntryList operation, above, outside the critical section
// protected by _WaitSetLock. In practice that's not useful. With the
// exception of wait() timeouts and interrupts the monitor owner
// is the only thread that grabs _WaitSetLock. There's almost no contention
// on _WaitSetLock so it's not profitable to reduce the length of the
// critical section.
}
Thread::SpinRelease (&_WaitSetLock) ;
if (iterator != NULL && ObjectMonitor::_sync_Notifications != NULL) {
ObjectMonitor::_sync_Notifications->inc() ;
}
}
notifyAll方法 与notify方法类似,只是在取_WaitSet队列时不是取第一个而是取所有。
wait方法是让线程等待,它对应的本地方法是JVM_MonitorWait,间接调用了ObjectSynchronizer::wait,与notify对应,它也是对应调用ObjectMonitor对象的wait方法。这个方法大概就是创建一个ObjectWaiter对象,接着获取_WaitSet队列锁将ObjectWaiter对象添加到该队列中,再释放队列锁。另外,它还会释放synchronized对应的锁,所以锁没有等到synchronized同步块结束时才释放。
JVM_ENTRY(void, JVM_MonitorWait(JNIEnv* env, jobject handle, jlong ms))
JVMWrapper("JVM_MonitorWait");
Handle obj(THREAD, JNIHandles::resolve_non_null(handle));
assert(obj->is_instance() || obj->is_array(), "JVM_MonitorWait must apply to an object");
JavaThreadInObjectWaitState jtiows(thread, ms != 0);
if (JvmtiExport::should_post_monitor_wait()) {
JvmtiExport::post_monitor_wait((JavaThread *)THREAD, (oop)obj(), ms);
}
ObjectSynchronizer::wait(obj, ms, CHECK);
JVM_END
finalize方法
这个方法用于当对象被回收时调用,这个由JVM支持,Object的finalize方法默认是什么都没有做,如果子类需要在对象被回收时执行一些逻辑处理,则可以重写finalize方法。一般不需要关心这个方法。
Object对象的创建策略
创建一个对象并不是只能通过new出来。明面上,创建object的方式有很多很多,至少可以列出以下6点
- 通过构造方法实例化一个类;
- 通过Class实例化一个类;
- 通过反射实例化一个类;
- 通过克隆实例化一个类;
- 通过反序列化实例化一个类;
- 通过Unsafe实例化一个类;
public class InstantialTest {
private static Unsafe unsafe;
static {
try {
Field f = Unsafe.class.getDeclaredField("theUnsafe");
f.setAccessible(true);
unsafe = (Unsafe) f.get(null);
} catch (NoSuchFieldException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
public static void main(String[] args) throws Exception {
// 1. 构造方法
User user1 = new User();
// 2. Class,里面实际也是反射
User user2 = User.class.newInstance();
// 3. 通过构造函数反射
User user3 = User.class.getConstructor().newInstance();
// 4. 克隆
User user4 = (User) user1.clone();
// 5. 反序列化
User user5 = unserialize(user1);
// 6. Unsafe
User user6 = (User) unsafe.allocateInstance(User.class);
System.out.println(user1.age);
System.out.println(user2.age);
System.out.println(user3.age);
System.out.println(user4.age);
System.out.println(user5.age);
//输出为0
//Unsafe的allocateInstance()方法只会给对象分配内存,并不会初始化对象中的属性,所以int类型的默认值就是0
System.out.println(user6.age);
}
private static User unserialize(User user1) throws Exception {
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("D://object.txt"));
oos.writeObject(user1);
oos.close();
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("D://object.txt"));
// 反序列化
User user5 = (User) ois.readObject();
ois.close();
return user5;
}
static class User implements Cloneable, Serializable {
private int age = 20;
public User() {
this.age = 10;
}
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
}
Object对象初始化顺序
一般来说,对象初始化都是使用new 关键字进行的。而一个类的成分很多,并且还涉及到父类。我们走一个测试用例就可以将执行顺序表达出来。
class Field {
static{
System.out.println("Field-initial-static");
}
{
System.out.println("Field-not-initial-static");
}
public Field(String str){
System.out.println(str+"-Field-construct");
}
}
class Father{
private Field field = new Field("father");
static{
System.out.println("father-initial-static");
}
{
System.out.println("father-not-initial-static");
}
public Father(){
System.out.println("father-construct");
}
}
public class Son extends Father{
private Field field = new Field("son");
static{
System.out.println("Son-initial-static");
}
{
System.out.println("Son-not-initial-static");
}
public Son(){
System.out.println("Son-construct");
}
public static void main(String[] args) {
Son son = new Son();
}
}
获得到输出是:
father-initial-static
Son-initial-static
Field-initial-static
Field-not-initial-static
father-Field-construct
father-not-initial-static
father-construct
Field-not-initial-static
son-Field-construct
Son-not-initial-static
Son-construct
从输出可以看出满足以下规律:
- 碰到new关键字,先进行类加载,类加载会优先递归父类进行类加载,类加载会导致static块儿执行
- 进一步开始递归父类进行字段初始化,字段初始化同样会导致类加载
- 在执行构造函数之前,将判定是否有父类,存在则优先执行父类构造函数,不存在则执行非static块
Object对象克隆机制
我们知道所有的类都是Object的子类,而Object的类内天生就会有一个clone方法:
protected native Object clone() throws CloneNotSupportedException;
clone方法,顾名思义,就是将实例在内存内拷贝一份。这个Clone方法是native的,因此拷贝的能力是jvm提供的。但是JVM并不是针对所有的类都赋予可以拷贝的能力,如何区分呢?这就需要涉及到Cloneable接口的作用。Cloneable接口是一个空接口,起的是一个标识作用,而这个标识是给JVM看的,表达当前类是可以被拷贝的。如果一个类没有实现这个接口,那么当你在操作clone方法的时候,虚拟机会抛出CloneNotSupportedException异常。为了更加深刻理解,我们写一下测试用例:
package com.zifang.ex.bust.ex;
public class CloneableTest{
public String name;
public static void main(String[] args) throws CloneNotSupportedException {
CloneableTest cloneableTest = new CloneableTest();
cloneableTest.name = "吃饭";
CloneableTest cloneableTest1 = (CloneableTest)cloneableTest.clone();
System.out.println(cloneableTest1.name);
}
}
这样执行的结果是抛出异常:
Exception in thread "main" java.lang.CloneNotSupportedException: com.zifang.ex.bust.ex.CloneableTest
at java.lang.Object.clone(Native Method)
at com.zifang.ex.bust.ex.CloneableTest.main(CloneableTest.java:10)
Process finished with exit code 1
那么我改成这样:
package com.zifang.ex.bust.ex;
public class CloneableTest implements Cloneable{
public String name;
public static void main(String[] args) throws CloneNotSupportedException {
CloneableTest cloneableTest = new CloneableTest();
cloneableTest.name = "吃饭";
CloneableTest cloneableTest1 = (CloneableTest)cloneableTest.clone();
System.out.println(cloneableTest1.name);
}
}
这样一来,可以正常执行。这里需要注意,clone方法是protected标记的,因此在其他类内调用是调用不到的,因此在这个类内需要进行覆盖,将clone方法暴露为public方法,这样才能在其他类内被访问到。
package com.zifang.ex.bust.ex;
public class CloneableTest implements Cloneable{
public String name;
@Override
public Object clone() throws CloneNotSupportedException {
return super.clone();
}
public static void main(String[] args) throws CloneNotSupportedException {
CloneableTest cloneableTest = new CloneableTest();
cloneableTest.name = "吃饭";
CloneableTest cloneableTest1 = (CloneableTest)cloneableTest.clone();
System.out.println(cloneableTest1.name);
}
}
在覆盖了的类内调用父类的clone方法,委托jvm进行数据拷贝,然后clone方法暴露为public类型,在其他类内可以访问到。
我们把虚拟机的C++代码扒出来,clone方法对应的本地函数为JVM_Clone,其中if (!klass->is_cloneable())
这里会校验是否有实现Cloneable接口,如果不是则抛出异常。
JVM_ENTRY(jobject, JVM_Clone(JNIEnv* env, jobject handle))
JVMWrapper("JVM_Clone");
Handle obj(THREAD, JNIHandles::resolve_non_null(handle));
const KlassHandle klass (THREAD, obj->klass());
JvmtiVMObjectAllocEventCollector oam;
if (!klass->is_cloneable()) {
ResourceMark rm(THREAD);
THROW_MSG_0(vmSymbols::java_lang_CloneNotSupportedException(), klass->external_name());
}
const int size = obj->size();
oop new_obj = NULL;
// 判断是否是数组分两种情况分配内存空间
if (obj->is_javaArray()) {
const int length = ((arrayOop)obj())->length();
// 新对象为new_obj
new_obj = CollectedHeap::array_allocate(klass, size, length, CHECK_NULL);
} else {
new_obj = CollectedHeap::obj_allocate(klass, size, CHECK_NULL);
}
// 对new_obj进行copy
Copy::conjoint_jlongs_atomic((jlong*)obj(), (jlong*)new_obj,
(size_t)align_object_size(size) / HeapWordsPerLong);
// 进行数据结构的设置
new_obj->init_mark();
BarrierSet* bs = Universe::heap()->barrier_set();
assert(bs->has_write_region_opt(), "Barrier set does not have write_region");
bs->write_region(MemRegion((HeapWord*)new_obj, size));
if (klass->has_finalizer()) {
assert(obj->is_instance(), "should be instanceOop");
new_obj = instanceKlass::register_finalizer(instanceOop(new_obj), CHECK_NULL);
}
// 转成jobject类型方便转成Java层的Object类型
return JNIHandles::make_local(env, oop(new_obj));
JVM_END
Object#clone的行为是,将当前的Object数据在内存里整个重新复制一遍,生成另外一个相同的Obejct,但是这两个Object的内存地址是一样的,如果在拷贝出来的对象上进行数据变更,是有可能影响到拷贝之前的数据的。这个我们称为浅拷贝与深拷贝。
class CloneField implements Cloneable {
private Long l = System.currentTimeMillis();
}
public class CloneableTest implements Cloneable {
public String name;
public CloneField field = new CloneField();
@Override
public Object clone() throws CloneNotSupportedException {
return super.clone();
}
public static void main(String[] args) throws CloneNotSupportedException {
CloneableTest cloneableTest = new CloneableTest();
cloneableTest.name = "吃饭";
CloneableTest cloneableTest1 = (CloneableTest) cloneableTest.clone();
System.out.println(cloneableTest1.name);
System.out.println(cloneableTest.field == cloneableTest1.field);
}
}
如果希望执行clone是深拷贝的结果,可以有两种思路:
- 使用序列化,将Object存为字节数组然后读入jvm生成一个新的Obejct;
- 在Object子类内覆盖Clone方式,手动实现深拷贝