当我们在聊Object

本文出自:http://blog.csdn.net/dt235201314/article/details/78318399

一丶概述

JAVA中所有的类都继承自Object类,就从Object作为源码解析的开始。

二丶常见方法

image.png

注: 以上绿色方法为 非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");
 
         }
 
     }
 
 }

image.png

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;
     }
}

image.png

"深复制"时,需要将共同关联的引用也复制完全看示例

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 相关面试题

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,736评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,167评论 1 291
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,442评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,902评论 0 204
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,302评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,573评论 1 216
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,847评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,562评论 0 197
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,260评论 1 241
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,531评论 2 245
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,021评论 1 258
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,367评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,016评论 3 235
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,068评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,827评论 0 194
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,610评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,514评论 2 269

推荐阅读更多精彩内容