Java从JDK源码角度对Object进行实例分析

Object是所有类的父类,也就是说java中所有的类都是直接或者间接继承自Object类。比如你随便创建一个classA,虽然没有明说,但默认是extendsObject的。

后面的三个点"..."表示可以接受若干不确定数量的参数。老的写法是Objectargs[]这样,但新版本的java中推荐使用...来表示。例如

publicvoidgetSomething(String...strings)(){}

object是java中所有类的父类,也就是说所有的类,不管是自己创建的类还是系统中的类都继承自object类,也就是说所有的类在任何场合都可以代替object类,根据里氏替换原则,子类在任何场合都可以代替其父类,而父类却不一定能代替其子类,java中常说的万物皆对象说的其实就是这个道理!object类体现了oop思想中的多态,继承,封装,抽象四大特性!

object类是所有类的基类,不是数据类型。这个你可以查询jdk文档了解,所有类都继承自Object。

Object...objects这种参数定义是在不确定方法参数的情况下的一种多态表现形式。即这个方法可以传递多个参数,这个参数的个数是不确定的。这样你在方法体中需要相应的做些处理。因为Object是基类,所以使用Object...objects这样的参数形式,允许一切继承自Object的对象作为参数。这种方法在实际中应该还是比较少用的。

Object[]obj这样的形式,就是一个Object数组构成的参数形式。说明这个方法的参数是固定的,是一个Object数组,至于这个数组中存储的元素,可以是继承自Object的所有类的对象。

这些基础东西建议你多看几遍"Thinkinjava"

Java的Object是所有其他类的父类,从继承的层次来看它就是最顶层根,所以它也是唯一一个没有父类的类。它包含了对象常用的一些方法,比如getClass、hashCode、equals、clone、toString、notify、wait等常用方法。所以其他类继承了Object后就可以不用重复实现这些方法。这些方法大多数是native方法,下面具体分析。

主要的代码如下:

public class Object {
	private static native void registerNatives();
	static {
		registerNatives();
	}
	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方法

由于registerNatives方法被static块修饰,所以在加载Object类时就会执行该方法,对应的本地方法为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方法

这是一个非本地方法,判断逻辑也十分简单,直接==比较。

clone方法

由本地方法表知道clone方法对应的本地函数为JVM_Clone,clone方法主要实现对象的克隆功能,根据该对象生成一个相同的新对象(我们常见的类的对象的属性如果是原始类型则会克隆值,但如果是对象则会克隆对象的地址)。Java的类要实现克隆则需要实现Cloneable接口,if (!klass->is_cloneable())这里会校验是否有实现该接口。然后判断是否是数组分两种情况分配内存空间,新对象为new_obj,接着对new_obj进行copy及C++层数据结构的设置。最后再转成jobject类型方便转成Java层的Object类型。

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 = CollectedHeap::array_allocate(klass, size, length, CHECK_NULL);
} else {
	new_obj = CollectedHeap::obj_allocate(klass, size, CHECK_NULL);
}
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);
}
return JNIHandles::make_local(env, oop(new_obj));
JVM_END

toString方法

逻辑是获取class名称加上@再加上十六进制的hashCode。

notify方法

此方法用来唤醒线程,final修饰说明不可重写。与之对应的本地方法为JVM_MonitorNotify,ObjectSynchronizer::notify最终会调用ObjectMonitor::notify(TRAPS),这个过程是ObjectSynchronizer会尝试当前线程获取freeObjectMonitor对象,不成功则尝试从全局中获取。

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方法

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方法

总结

以上就是本文关于Java从JDK源码角度对Object进行实例分析的全部内容,希望对大家有所帮助。感兴趣的朋友可以继续参阅本站其他相关专题,如有不足之处,欢迎留言指出。感谢朋友们对本站的支持!

时间: 2017-12-12

java中用ObjectMapper类实现Json与bean的转换示例

前言 ObjectMapper是jackson中的方法,本文主要给大家介绍了关于java中用ObjectMapper类实现Json与bean转换的相关内容,分享出来供大家参考学习,下面话不多说了,来一起看看详细的介绍吧. 一.将json字符串转为bean public class JsonToJavaBean { public static void main(String[] args) { String str="{\"student\":[{\"name\&q

Java利用反射获取object的属性和值代码示例

在看反射顺便做个笔记,目前知道的反射的Object都是要有对象的也就是实体Bean. referance:Java反射简易教程 import java.lang.reflect.Field; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; /** * 反射处理Bean,得到里面的属性值 * * @author liulinsen * */ publ

Java中JSONObject与JSONArray的使用区别详解

最近公司开发的几个项目中,后台Action向前端传递数据都是Json格式,于是对JSONObject.JSONArray简单的研究了一下,废话不多说,想使用JSONObject.JSONArray,策则在项目中必须要有commons-lang.jar commons-beanutils.jar commons-collections.jar commons-logging.jar  ezmorph.jar json-lib-2.2.2-jdk15.jar 这些Jar包. 1.JSONObject

java 正则,object中两个方法的使用(详解)

正则: "."和"\" "."点儿,在正则表达式中表示任意一个字符. "\"在正则表达式中是转意字符,当我们需要描述一个已经被正则表达式使用的特殊字符时,我们就可以通过使用"\"将其转变为原本的意思. "\"在正则表达式中也有一些预定义的特殊内容: \d:表示任意一个数字 \w:表示任意一个单词字符(只能是 数字,字母,下划线) \s:表示任意一个空白字符(\t \r \n \f \x0

java序列化与ObjectOutputStream和ObjectInputStream的实例详解

java序列化与ObjectOutputStream和ObjectInputStream的实例详解 一个测试的实体类: public class Param implements Serializable { private static final long serialVersionUID = 5187074869820982336L; private Integer param1; private String param2; public Integer getParam1() { re

java实现Object和Map之间的转换3种方式

利用commons.BeanUtils实现Obj和Map之间转换,这种是最简单,也是最经常用的 public static Object mapToObject(Map<String, Object> map, Class<?> beanClass) throws Exception { if (map == null) return null; Object obj = beanClass.newInstance(); org.apache.commons.beanutils.B

java反射获取一个object属性值代码解析

有些时候你明明知道这个object里面是什么,但是因为种种原因,你不能将它转化成一个对象,只是想单纯地提取出这个object里的一些东西,这个时候就需要用反射了. 假如你这个类是这样的: private class User { String userName; String userPassword; public String getUserName() { return userName; } public void setUserName(String userName) { this.

Java源码解析之object类

在源码的阅读过程中,可以了解别人实现某个功能的涉及思路,看看他们是怎么想,怎么做的.接下来,我们看看这篇Java源码解析之object的详细内容. Java基类Object java.lang.Object,Java所有类的父类,在你编写一个类的时候,若无指定父类(没有显式extends一个父类)编译器(一般编译器完成该步骤)会默认的添加Object为该类的父类(可以将该类反编译看其字节码,不过貌似Java7自带的反编译javap现在看不到了). 再说的详细点:假如类A,没有显式继承其他类,编译

java源码解析之String类的compareTo(String otherString)方法

一. 前言 最近我发现了一个事情,那就是在面试笔试中,好多公司都喜欢在String字符串上出问题,涉及到方方面面的知识,包括其中的一些常用方法. String 类代表字符串.Java 程序中的所有字符串字面值(如 "abc" )都作为此类的实例实现. 字符串是常量:它们的值在创建之后不能更改.字符串缓冲区支持可变的字符串.因为 String 对象是不可变的,所以可以共享. 近日研究了一下String类的一些方法, 通过查看源码, 对一些常用的方法也有了更透彻的认识, 也让我更加理解了设

Java源码解析之TypeVariable详解

TypeVariable,类型变量,描述类型,表示泛指任意或相关一类类型,也可以说狭义上的泛型(泛指某一类类型),一般用大写字母作为变量,比如K.V.E等. 源码 public interface TypeVariable<D extends GenericDeclaration> extends Type { //获得泛型的上限,若未明确声明上边界则默认为Object Type[] getBounds(); //获取声明该类型变量实体(即获得类.方法或构造器名) D getGenericDe

Java源码解析之GenericDeclaration详解

学习别人实现某个功能的设计思路,来提高自己的编程水平.话不多说,下面进入正题. GenericDeclaration 可以声明类型变量的实体的公共接口,也就是说,只有实现了该接口才能在对应的实体上声明(定义)类型变量,这些实体目前只有三个:Class(类).Construstor(构造器).Method(方法)(详见:Java源码解析之TypeVariable详解 源码 public interface GenericDeclaration { //获得声明列表上的类型变量数组 public T

Java源码解析ThreadLocal及使用场景

ThreadLocal是在多线程环境下经常使用的一个类. 这个类并不是为了解决多线程间共享变量的问题.举个例子,在一个电商系统中,用一个Long型变量表示某个商品的库存量,多个线程需要访问库存量进行销售,并减去销售数量,以更新库存量.在这个场景中,是不能使用ThreadLocal类的. ThreadLocal适用的场景是,多个线程都需要使用一个变量,但这个变量的值不需要在各个线程间共享,各个线程都只使用自己的这个变量的值.这样的场景下,可以使用ThreadLocal.此外,我们使用ThreadL

Java源码解析HashMap简介

本文基于jdk1.8进行分析 HashMap是java开发中可以说必然会用到的一个集合.本文就HashMap的源码实现进行分析. 首先看一下源码中类的javadoc注释对HashMap的解释.如下图.HashMap是对Map接口的基于hash表的实现.这个实现提供了map的所有可选操作,并且允许null值(可以多个)和一个null的key(仅限一个).HashMap和HashTable十分相似,除了HashMap是非同步的且允许null元素.这个类不保证map里的顺序,更进一步,随着时间的推移,

Java源码解析之可重入锁ReentrantLock

本文基于jdk1.8进行分析. ReentrantLock是一个可重入锁,在ConcurrentHashMap中使用了ReentrantLock. 首先看一下源码中对ReentrantLock的介绍.如下图.ReentrantLock是一个可重入的排他锁,它和synchronized的方法和代码有着相同的行为和语义,但有更多的功能.ReentrantLock是被最后一个成功lock锁并且还没有unlock的线程拥有着.如果锁没有被别的线程拥有,那么一个线程调用lock方法,就会成功获取锁并返回.

Java源码解析阻塞队列ArrayBlockingQueue功能简介

本文基于jdk1.8进行分析. 阻塞队列是java开发时常用的一个数据结构.首先看一下阻塞队列的作用是什么.阻塞队列的作用,从源码中类的注释中来了解,是最清晰准确的. ArrayBlockingQueue是一个用数组实现的有界阻塞队列.提供FIFO的功能.队列头上的元素是在队列中呆了最长时间的元素,队列尾上的元素是在队列中呆了时间最短的元素.新元素会插入在队列尾部,从队列获取元素时会从队列头上获取. 这是一个传统的有界队列,在这个有界队列里,一个固定大小的数组用来保存生产者产生的元素和消费者获取

Java源码解析HashMap的keySet()方法

HashMap的keySet()方法比较简单,作用是获取HashMap中的key的集合.虽然这个方法十分简单,似乎没有什么可供分析的,但真正看了源码,发现自己还是有很多不懂的地方.下面是keySet的代码. public Set<K> keySet() { Set<K> ks = keySet; if (ks == null) { ks = new KeySet(); keySet = ks; } return ks; } 从代码中了解到,第一次调用keySet方法时,keySet

Java源码解析ArrayList及ConcurrentModificationException

本文基于jdk1.8来分析ArrayList的源码 首先是主要的成员变量. /** * Default initial capacity. **/ private static final int DEFAULT_CAPACITY = 10; /** * Shared empty array instance used for empty instances. **/ private static final Object[] EMPTY_ELEMENTDATA = {}; /** * Shar