Java源码之Object
本文出自:http://blog.csdn.net/dt235201314/article/details/78318399
一丶概述
JAVA中所有的类都继承自Object类,就从Object作为源码解析的开始。
二丶常见方法
注: 以上绿色方法为 非native方法 粉色方法为 native方法)
什么是native方法?
native关键字标识的java方法为本地方法,底层是有c/c++编写的程序编译后dll文件,java加载dll文件后,可用通过本地方法调用dll中函数,如有疑问可用参考JNI使用方式。
什么是JNI方法?
向东是底层,向西是应用,咱们先一路向西。向东参考:JNI方法使用
1.Object():默认构造函数(源码无系统默认提供)
2.void registerNatives()
- /* 一个本地方法,具体是用C(C++)在DLL中实现的,然后通过JNI调用。*/
- private static native void registerNatives();
- /* 对象初始化时自动调用此方法*/
- static {
- registerNatives();
- }
3.final getClass()
- /* 返回此 Object 的运行时类。*/
- public final native Class<?> getClass();
例:
- public class tests
- {
- public static void main(String[] args)
- {
- A te = new B();
- System.out.println(te.getClass());
- }
- }
- class A{
- }
- class B extends A
- {
- }
运行结果:class B
A类的引用,但是运行时te这个对象的实际类是B。
4.int hashCode()方法
- /*
- hashCode 的常规协定是:
- 1.在应用程序执行期间,如果一个对象用于equals()方法的属性没有被修改的话,
- 那么要保证对该对象多次返回的hashcode值要相等。
- 2.如果2个对象通过equals()方法判断的结果为true,那么要保证二者的hashcode值相等。
- 3.如果2个对象通过equals()方法判断的结果为false,那么对二者hashcode值是否相等并没有明确要求。
- 如果不相等,那么能够提升散列表的性能。
- */
- public native int hashCode();
实际计算方法:
- public int hashCode() {
- int h = hash;
- if (h == 0 && value.length > 0) {
- char val[] = value;
- for (int i = 0; i < value.length; i++) {
- h = 31 * h + val[i];
- }
- hash = h;
- }
- return h;
- }
5.boolean equals(Object obj)
- public boolean equals(Object obj) {
- return (this == obj);
- }
从这里我们可以看到,equals(obj)方法最根本的实现就是‘==’,因此对于一些自定义类,如果没有重写hashcode()方法和equals()方法的话,利用‘==’和equals()方法比较的结果是一样的。对于‘==’比较的是地址,equals()方法比较的是内容这种说法,是片面的。(虽然在最常用的String类中是这样的)。
equal方法常被重写
例:String(先比较String对象内存地址相同,若相同则返回true,否则判断String对象对应字符的内容是否相等,若相等则返回true)
- public boolean equals(Object anObject) {
- if (this == anObject) {
- return true;
- }
- if (anObject instanceof String) {
- String anotherString = (String)anObject;
- int n = value.length;
- if (n == anotherString.value.length) {
- char v1[] = value;
- char v2[] = anotherString.value;
- int i = 0;
- while (n-- != 0) {
- if (v1[i] != v2[i])
- return false;
- i++;
- }
- return true;
- }
- }
- return false;
- }
6.clone()
- /*本地CLONE方法,用于对象的复制。*/
- protected native Object clone() throws CloneNotSupportedException;
一起看下native方法 位于openjdk\hotspot\src\share\vm\prims\jvm.cpp中 JVM_Clone的实现 片段
- 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;
- #ifdef ASSERT
- // Just checking that the cloneable flag is set correct
- if (obj->is_javaArray()) {
- guarantee(klass->is_cloneable(), "all arrays are cloneable");
- } else {
- guarantee(obj->is_instance(), "should be instanceOop");
- bool cloneable = klass->is_subtype_of(SystemDictionary::Cloneable_klass());
- guarantee(cloneable == klass->is_cloneable(), "incorrect cloneable flag");
- }
- #endif
- // Check if class of obj supports the Cloneable interface.
- // All arrays are considered to be cloneable (See JLS 20.1.5)
- if (!klass->is_cloneable()) {
- ResourceMark rm(THREAD);
- THROW_MSG_0(vmSymbols::java_lang_CloneNotSupportedException(), klass->external_name());
- }
- // Make shallow object copy
- 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);
- }
- // 4839641 (4840070): We must do an oop-atomic copy, because if another thread
- // is modifying a reference field in the clonee, a non-oop-atomic copy might
- // be suspended in the middle of copying the pointer and end up with parts
- // of two different pointers in the field. Subsequent dereferences will crash.
- // 4846409: an oop-copy of objects with long or double fields or arrays of same
- // won't copy the longs/doubles atomically in 32-bit vm's, so we copy jlongs instead
- // of oops. We know objects are aligned on a minimum of an jlong boundary.
- // The same is true of StubRoutines::object_copy and the various oop_copy
- // variants, and of the code generated by the inline_native_clone intrinsic.
- assert(MinObjAlignmentInBytes >= BytesPerLong, "objects misaligned");
- Copy::conjoint_jlongs_atomic((jlong*)obj(), (jlong*)new_obj,
- (size_t)align_object_size(size) / HeapWordsPerLong);
- // Clear the header
- new_obj->init_mark();
- // Store check (mark entire object and let gc sort it out)
- 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));
- // Caution: this involves a java upcall, so the clone should be
- // "gc-robust" by this stage.
- 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
看不懂,这里参考大神讲解
隐含意思:数组类型默认可以直接克隆,而其他对象实现clone需要先实现Cloneable接口,否则抛出CloneNotSupportedException异常
问题1:对象的创建有多中方式,类似 new 、getInstance、clone等 clone有什么好处?
问题2:对象调用clone方法生成的对象 和 原对象是否还有什么关联关系?
问题3 : 对象clone存在 “浅复制”、“深复制”概念,怎么区分?
带着这3个问题,理解Object clone方法:
1、一般native方法比java中非native方法执行效率高 ,看示例
- public class ObjectCloneTest1 {
- static final int N = 100000;
- public static void main(String[] args) {
- final Date date = new Date();
- {
- final long startTime = System.currentTimeMillis();
- for (int i = 0; i < N; i++) {
- Date date2 = (Date) date.clone();
- }
- final long endTime = System.currentTimeMillis();
- System.out.println("clone:" + (endTime - startTime) + "ms");
- }
- {
- final long startTime = System.currentTimeMillis();
- for (int i = 0; i < N; i++) {
- final Calendar cal = Calendar.getInstance();
- cal.setTime(date);
- final Date date2 = cal.getTime();
- }
- final long endTime = System.currentTimeMillis();
- System.out.println("Calender.setTime:" + (endTime - startTime) + "ms");
- }
- }
- }
2、clone生成的新对象与原对象的关系,需要区别2个对象建是否存在相同的引用或对应的内存地址是否存在共用情况,若存在则 该次clone为 “浅复制”,否则为“深复制”, 而且Object的clone方法是属于 “浅复制”,看示例
- public class ObjectCloneTest2 {
- public static void main(String[] args) {
- Animal a1 = new Animal(1, "pig");
- Animal a2 = (Animal) a1.clone();
- System.out.println(a1.getName() == a2.getName() ? "浅复制" : "深复制");
- System.out.println(a1);
- a1.setAge(11);
- a1.setName("big pig");
- System.out.println(a1.age + ":" + a1.name);
- System.out.println(a2);
- System.out.println(a2.age + ":" + a2.name);
- }
- }
- class Animal implements Cloneable{
- int age;
- String name;
- Animal(int age, String name) {
- this.age = age;
- this.name = name;
- }
- public Animal clone() {
- Animal o = null;
- try {
- o = (Animal) super.clone();
- } catch (CloneNotSupportedException e) {
- e.printStackTrace();
- }
- return o;
- }
- public int getAge() {
- return age;
- }
- public void setAge(int age) {
- this.age = age;
- }
- public String getName() {
- return name;
- }
- public void setName(String name) {
- this.name = name;
- }
- }
"深复制"时,需要将共同关联的引用也复制完全看示例
- public class ObjectCloneTest3 {
- public static void main(String[] args) {
- Person p1 = new Person(10, "ll", new Race("yellow", "Asia"));
- Person p2 = (Person) p1.clone();
- System.out.println(p1.getRace() == p2.getRace());
- System.out.println(p1.getTestArray() == p2.getTestArray());
- }
- }
- class Person implements Cloneable {
- int age;
- String name;
- Race race;
- int[] testArray = { 1, 23, 5, 6, 0 };
- Person(int age, String name, Race race) {
- this.age = age;
- this.name = name;
- this.race = race;
- }
- public Person clone() {
- Person o = null;
- try {
- o = (Person) super.clone();
- o.setRace(this.race.clone());
- o.setTestArray(testArray.clone());
- } catch (CloneNotSupportedException e) {
- e.printStackTrace();
- }
- return o;
- }
- public int getAge() {
- return age;
- }
- public void setAge(int age) {
- this.age = age;
- }
- public String getName() {
- return name;
- }
- public void setName(String name) {
- this.name = name;
- }
- public Race getRace() {
- return race;
- }
- public void setRace(Race race) {
- this.race = race;
- }
- public void setTestArray(int[] testArray) {
- this.testArray = testArray;
- }
- public int[] getTestArray() {
- return testArray;
- }
- }
- class Race implements Cloneable {
- String color; // 颜色
- String distribution; // 分布
- public Race(String color, String distribution) {
- super();
- this.color = color;
- this.distribution = distribution;
- }
- public Race clone() throws CloneNotSupportedException {
- return (Race) super.clone();
- }
- }
false
false
7.toString()
- /*返回该对象的字符串表示。非常重要的方法*/
- public String toString() {
- return getClass().getName() + "@" + Integer.toHexString(hashCode());
- }
默认返回对象的名称及引用地址,但一般被子类重写用于说明子类相关属性值描述
8.final notify()
- /*不能被重写,唤醒在此对象监视器上等待的单个线程。*/
- public final native void notify();
9.final notifyAll()
- /*唤醒在此对象监视器上等待的所有线程。*/
- public final native void notifyAll();
10.final void wait()方法
- /*在其他线程调用此对象的 notify() 方法或 notifyAll() 方法前,导致当前线程等待。换句话说,此方法的行为就好像它仅执行 wait(0) 调用一样。
- 当前线程必须拥有此对象监视器。该线程发布对此监视器的所有权并等待,直到其他线程通过调用 notify 方法,或 notifyAll 方法通知在此对象的监视器上等待的线程醒来。然后该线程将等到重新获得对监视器的所有权后才能继续执行。*/
- public final void wait() throws InterruptedException {
- wait(0);
- }
11.final native void wait(long timeout)
- /*在其他线程调用此对象的 notify() 方法或 notifyAll() 方法,或者超过指定的时间量前,导致当前线程等待。*/
- public final native void wait(long timeout) throws InterruptedException;
该方法使当前线程等待,直到另外一个线程调用该对象的notify或notifyAll方法,或者等待时间已到,当前线程才会从等待池移到运行池。
如果在wait之前或者wait的时候,当前线程被中断了,那么直到该线程被恢复的时候才会抛出中断异常(InterruptedException)
12.final void wait(long timeout,int nanos)
- /* 在其他线程调用此对象的 notify() 方法或 notifyAll() 方法,或者其他某个线程中断当前线程,或者已超过某个实际时间量前,导致当前线程等待。*/
- 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);
- }
13.protected void finalize()
- /*当垃圾回收器确定不存在对该对象的更多引用时,由对象的垃圾回收器调用此方法。*/
- protected void finalize() throws Throwable { }
垃圾回收器在认为该对象是垃圾对象的时候会调用该方法。子类可以通过重写该方法来达到资源释放的目的。
在方法调用过程中出现的异常会被忽略且方法调用会被终止。
任何对象的该方法只会被调用一次。
三丶参看文章
Object类源码解析
【java基础之jdk源码】Object
四丶相关面试题
【码农每日一题】Java equals 与 hashCode 相关面试题
Java源码之Object的更多相关文章
- java源码阅读Object
1 类注释 Class {@code Object} is the root of the class hierarchy. Every class has {@code Object} as a s ...
- Java源码分析 | Object
本文基于 OracleJDK 11, HotSpot 虚拟机. Object 定义 Object 类是类层次结构的根.每个类都有 Object 类作为超类.所有对象,包括数组等,都实现了这个类的方法. ...
- Java源码分析 | CharSequence
本文基于 OracleJDK 11, HotSpot 虚拟机. CharSequence 定义 CharSequence 是 java.lang 包下的一个接口,是 char 值的可读序列, 即其本身 ...
- Programming a Spider in Java 源码帖
Programming a Spider in Java 源码帖 Listing 1: Finding the bad links (CheckLinks.java) import java.awt. ...
- 解密随机数生成器(二)——从java源码看线性同余算法
Random Java中的Random类生成的是伪随机数,使用的是48-bit的种子,然后调用一个linear congruential formula线性同余方程(Donald Knuth的编程艺术 ...
- Java源码解读(一)——HashMap
HashMap作为常用的一种数据结构,阅读源码去了解其底层的实现是十分有必要的.在这里也分享自己阅读源码遇到的困难以及自己的思考. HashMap的源码介绍已经有许许多多的博客,这里只记录了一些我看源 ...
- 【java集合框架源码剖析系列】java源码剖析之TreeSet
本博客将从源码的角度带领大家学习TreeSet相关的知识. 一TreeSet类的定义: public class TreeSet<E> extends AbstractSet<E&g ...
- 【java集合框架源码剖析系列】java源码剖析之HashSet
注:博主java集合框架源码剖析系列的源码全部基于JDK1.8.0版本.本博客将从源码角度带领大家学习关于HashSet的知识. 一HashSet的定义: public class HashSet&l ...
- 从Java源码到Java字节码
Java最主流的源码编译器,javac,基本上不对代码做优化,只会做少量由Java语言规范要求或推荐的优化:也不做任何混淆,包括名字混淆或控制流混淆这些都不做.这使得javac生成的代码能很好的维持与 ...
随机推荐
- 【MyBatis学习12】MyBatis中的一级缓存
缓存的作用是减轻数据库的压力,提高数据库的性能的.mybatis中提供了一级缓存和二级缓存,先来看一下两个缓存的示意图: 从图中可以看出: 一级缓存是SqlSession级别的缓存.在操作数据库时 ...
- go项目布局(摘录)
go的项目结构布局 或 包结构布局 这一块大家似乎还在摸索吧, 常用的应该还是类似于java的mvc布局, 但网上也有不同的布局方式,查阅github上的一些源码,也有大量的采用. 我把自己碰到的资料 ...
- 当你输入一个网址/点击一个链接,发生了什么?(以www.baidu.com为例)
>>>点击网址后,应用层的DNS协议会将网址解析为IP地址: DNS查找过程: 浏览器会检查缓存中有没有这个域名对应的解析过的IP地址,如果缓存中有,这个解析过程就将结束. 如果用户 ...
- 使用BMap.Label给百度地图的BMap.Marker上加上数字序号
marker = new BMap.Marker(pointList[i]) marker.setLabel(getNumberLabel(i)); function getNumberLabel(n ...
- jQuery读取json文件
转 http://www.jb51.net/article/36678.htm 1.userInfo.html <!DOCTYPE html PUBLIC "-//W3C//DTD X ...
- PILE读书笔记_基础知识
程序的构成 Linux下二进制可执行程序的格式一般为ELF格式. 我们可以用readelf命令来读取二进制的信息. ELF文件的主要内容就是由各个section及symbol表组成的. 下面来分别介绍 ...
- java 使用Date类、Calendar类,实现增加日期
Date date= new Date(); System.out.println("Date date= new Date()中的date: "+date); 输出对象date: ...
- DJI SDK iOS 开发之二:搭建主要的开发环境
本文想介绍搭建主要的DJI SDK iOS下的开发环境,只是DJI官方已经给出了非常具体的执行其demo的教程.网址例如以下: https://dev.dji.com/cn/guide 我这里总结一下 ...
- 谁是云的王者?OpenStack与VMware优劣对比
[编者按]在云计算生态系统中,有两种类型的用户需要使用云计算资源:传统型(Traditional IT applications)和在互联网大潮下逐渐崛起云计算应用型(Cloud-aware appl ...
- eclipse 打开的时候弹出 'Building workspace' has encountered a problem. Errors occurred during
Eclipse 里面project->Build Automatically上的对勾去掉