java_src_object

java的Object类源码阅读:
Object类是Java所有类的基类,如果某个类不继承任何类,则编译器会在编译的时候自动的让这个类extends Object,所以Object是任何类的基类。这个类包含了Java类所通用的所有方法和属性。

其中比较重要的几个方法是getClass()、hashCode()、equals()、clone()等。
getClass()获取这个类的运行时类,这个运行时类是Class<?>的一个对象,这个方法是一个native方法,它的具体实现是由c实现的。获取的这个运行时类对象是被锁在这个类的一个静态同步的类方法里,然而这个方法在Java层面是不可见的,不过这个运行时对象肯定是静态的,是属于该类这个层面的,该类的任何对象都可以获取这个静态的运行时类对象。拿到的这个Class<?> 对象为什么需要是Synchronized,这个问题要等学到了Class 类以及反射这一块才能够解答。


hashCode()是Object类的一个本地方法,其实现方法不得而知。hashCode的典型实现方法是将对象的内部地址转换为Int,但是这不是Java必须要求的方法。我们自定义类的时候可以覆盖这个方法,hashCode方法返回一个Int类型的数,它在Hashtable、HashSet、HashMap等具有hash映射性质的集合中。添加对象到hash类型的集合中,需要要判断对象是否重复,首先由hashCode做初步判断。如果hashCode映射到的位置没有元素,则这个集合中一定不存在这个对象,可以放心加入,由于hash的判断时间复杂度为O(1)。所有可以较快的提高判断重复的效率。如果hashCode相同,而且key的equels方法也相同,则断定改对象已存在,不能加入。下面贴上Hashtable类的put方法,来理解上面的说明:
 public Object put(Object key, Object value) {
        // Make sure the value is not null
        if (value == null) throw new NullPointerException();
        // Makes sure the key is not already in the hashtable.
        HashtableEntry e;
        HashtableEntry tab[] = table;
        int hash = key.hashCode();
        int index = (hash & 0x7FFFFFFF) % tab.length;
        for (e = tab[index] ; e != null ; e = e.next) {
            if ((e.hash == hash) && e.key.equals(key)) {
                Object old = e.value;
                e.value = value;
                return old;
            }
        }
        // Rehash the table if the threshold is exceeded
        if (count >= threshold) {
            rehash();
            return put(key, value);
        }
        // Creates the new entry.
        e = new HashtableEntry();
        e.hash = hash;
        e.key = key;
        e.value = value;
        e.next = tab[index];
        tab[index] = e;
        count++;
        return null;
    }
代码1


equels()方法在Object类中的实现是==,即判断两个引用是否指向同一个对象。在自定义类中,通常会覆盖这个方法,通常的逻辑是两个对象的内容相同,则认为equels方法为true。


小结: == VS equels VS hashCode
a.==  代表引用的是否值同一个对象,即两个对象引用是不是对应于内存中的同一个地址
b.equels  通常代表两个对象的内容是否相同,如果两个对象的equels相等,那么要求他们的hashCode也相同。如果equels为false,不要求hashCode一定不同,即两个对象的hashCode一样,他们equels方法可以为false。然而,对于不同对象采用不同的hashCode有助于提高hash类集合的效率,可以参照代码1理解。
c. hashCode 大多用于hash类集合中,用于提高对象比较的效率。不用于一般的对象的地址或者内容的比较。
d.改写equals时基本都要改写hashCode.以满足上面的约束。


clone方法:通常来讲,clone方法应该满足以下几个条件:
a.   x.clone() != x
b. x.clone().getClass() == x.getClass()
c.   x.clone().equals(x)
但是这个也不是必须的。


通常,拷贝后的对象b=a.clone(),b应该不被a影响,即相互独立。若a中的属性都是基本类型变量或者是不可变对象的变量,那么自然是相互独立的。但是如果a中包含可变对象的引用变量a_var,当var引用的对象的状态发生变化时,b中引用变量b_var=a_var,二者引用同一个变量,故b中的b_var也会随着改变,这样就不符合相互独立的原则。为了符合这个原则,通常在调用super.clone()之后,可变对象的引用变量需要被修改,应该指向一个新的相同对象,即应该为“深拷贝”。
对于数组的拷贝而言,看以下代码实例:
a. 数组里存储的是基础类型:
int t[] =new int[10];
for(int i=0;i<10;i++)
{
t[i]=i;
}
int copy[]=t.clone();
t[2]=100;
if(copy[2]==100)
System.out.println("这是浅拷贝");
else
System.out.println("这是深拷贝");
     控制台打印为:这是深拷贝
b. 数组离存储的是非基础类型,即引用类型。
A a[]=new A[10];
for(int i=0;i<10;i++)
{
a[i]=new A(i);
}
A copy[]=a.clone();
a[2].setT(100);
if(copy[2].getT()==100)
System.out.println("这是浅拷贝");
else
System.out.println("这是深拷贝");

控制台打印为:这是浅拷贝
由上可知,对于数组而言,若存储的是引用类型,clone方法是浅拷贝。如果想实现数组类型的深拷贝,应该如下实现:
A a[]=new A[10];
for(int i=0;i<10;i++)
{
a[i]=new A(i);
}
A copy[]=new A[a.length];
for(int i=0;i<10;i++)
{
copy[i]=(A)a[i].clone();
}
a[2].setT(100);
if(copy[2].getT()==100)
System.out.println("这是浅拷贝");
else
System.out.println("这是深拷贝");

       class A
       {
@Override
protected Object clone() throws CloneNotSupportedException {
// TODO Auto-generated method stub
Object copy=new A(t);
return copy;
}
}
控制台输出为:这是深拷贝
clone方法和Cloneable:
1、实现java.lang.Cloneable接口
要clone的类为什么还要实现Cloneable接口呢?Cloneable接口是一个标识接口,不包含任何方法的!这个标识仅仅是针对Object类中clone()方法的,
如果clone类没有实现Cloneable接口,并调用了Object的 clone()方法(也就是调用了super.Clone()方法),那么Object的clone()方法就会抛出 CloneNotSupportedException异常。
2.实现java.lang.Cloneable接口,不代表就实现了clone方法,因为这个接口不包含任何东西。要想实现自己的clone方法,还是要重载Object类的clone方法。

Object类还有几个比较方法,notify()、notifyAll()、wait()、wait(long timeout)、wait(long timeout,int nanos)

其中notify()是在拥有对象控制权的线程释放锁的时候被调用,用来通知等待锁释放的线程,让他们从阻塞态变为可运行态,而等待锁的线程可能有多个,具体通知哪一个是随机的,依赖于算法的实现。由于只通知一个,所有基本不会有线程去竞争这个锁。

notifyAll()  通知所有等待这个锁释放的线程,由于多个线程同时被唤醒,所有这些线程会经历一次竞争来获得对象控制权。最终只可能有一个线程获得控制权,其他的线程仍然需要等待。

wait()==wait(0),其意义不是等待0s就停止休眠,而是代表timeout==0时,这个无意义,将不会通过timeout的方式来停止休眠。只有通过notify和notifyAll。

wait(long timeout),等待timeout毫秒后停止休眠,也可以通过notify和notifyAll。

wait(long timeout,int nanos),作用等同于wait(long timeout),只是其实践是纳秒,timeout*1000000+nanos(0-999999),将时间精确到纳秒。


wait() 和sleep()  InterruptedException

wait和sleep在众多的博客里经常被拿来比较,通常的说法如下:

相同点:

a. 都可以是线程暂停运行,使线程进入阻塞状态。

b. 都可能抛出InterruptedException异常,都要捕获这个异常,这个异常指示在线程A阻塞期间,有其他线程B将调用线程A的interrupt方法,试图终止线程A,如果B有这个权限终止A,那么A将会收到一个InterruptedException,线程A会重新由阻塞态变为就绪态,会指向catch(Interrupted){}花括号里的方法,在这个方法里通常要调用A.interrupt()来终止线程。

不同点:

a. wait是对象调用的方法,是非静态的;sleep是Thread类的静态方法,通常由Thread.sleep调用。

b. wait必须在synchronized同步方法或者同步块里使用,而sleep可以在地方使用

c. wait 会释放锁,sleep不会释放锁。

其实我个人觉得wait和sleep完全是两个范畴的东西,wait是线程通信方面的、是对象放弃自己的对象锁;sleep是单个线程层面的,仅仅是当前线程自己阻塞,和锁以及其他线程没有任何关系。易混淆的地方就是sleep不会释放锁,所以人们通常喜欢把他们比较一下。


wait和notify相关的方法是线程相关的,以后研究Thread类的时候再详细理解一下这两个方法和线程的关系。


对于Object类的分析就到这里了,总结一下,Object类里是任何Java中任何一个类的父类,它里面包含了Java类最通用的基础方法,好几个都是实现好的native方法。这样我们在编写类的时候就不要再考虑wait、notify这些和对象锁相关的细节了,可以直接调用Object类的方法。对于hashCode、equals这些方法,我们通常要根据业务逻辑来重载。




  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值