java源码品读(1)— Object

java是一门面向对象的语言,那么就从一切对象的根本Object读起吧。

位置:java.lang.Object

 * Class {@code Object} is the root of the class hierarchy.
 * Every class has {@code Object} as a superclass. All objects,
 * including arrays, implement the methods of this class.

类的注释:Object类是所有类的父类,所有的对象包括数组,都实现了Object类中的方法。

这里写图片描述
Object类中的参数及方法,下面按源码的顺序一个一个来看:

  • private static native void registerNatives();
    private static native void registerNatives();
    static {
        registerNatives();
    }

一个本地方法,在类初始的时候便已运行,但是方法体中并没有任何的东西,出于疑惑各种google,大部分的说法是,方法的实现不是java实现的,运行时需要调用本地的方法中的C函数。(一说是在java.dll中实现,不是很懂,暂且放下)

  • public final native Class
Returns the runtime class of this. The returned object is the object that is locked by
methods of the represented class.
The actual result type is where is the erasure of the static type 
of the expression on which is called.

方法头上的解释告诉我们:这个方法返回给我们的是对象的运行时类型,这个运行时类型实际上是这个对象在编译时被擦拭掉的类型。这里涉及到编译时类型、运行时类型以及java类型擦拭的相关知识,有兴趣的各位可以仔细了解一下。

  • public native int hashCode();
a hash code value for this object.

方法返回这个对象int类型的hash码值,相同的对象肯定会返回相同的hash码值,因此这个方法也可以用来初步判断两个对象是否是同一个对象。(两个对象是否是相同依赖于equals()方法,而equals()方法往往实现比较复杂,所以一般使用hashCode()方法进行初步判断以提高效率)

  • public boolean equals(Object obj)
    判断两个对象是否相等的方法,java的源码中也给我们了这样的提示
Note that it is generally necessary to override the
method whenever this method is overridden, so as to maintain the
general contract for the method, which states
that equal objects must have equal hash codes.

这是一个经常需要重写的方法,这是因为在Object类中的equals()方法仅仅是根据两个对象的内存地址来确定两个对象是否是同一个对象。

 public boolean equals(Object obj) {
        return (this == obj);
    }

显然这在很多情况下是不能满足我们的需求的,当然你再重写equals方法的同时必须同时重写hashCode方法,上面的提示中也说到相同的对象必须拥有相同的hash码。

  • protected native Object clone() throws CloneNotSupportedException;
    也是native修饰的一个方法,java本身不实现。
    方法会返回这个对象的一个一模一样的克隆体,当然这个一样只是指对象内的东西一致,即拥有原对象的所有属性,但是两个对象在内存中的存放位置已经不同。
    源码给我们的一点小提示:
if the object's class does not support the Cloneable interface. Subclasses
that override the clone method can also throw this exception to indicate 
that an instance cannot be cloned.

如果一个类没有实现Cloneable接口的话,那么直接使用clone方法就会抛出异常CloneNotSupportedException

  • public String toString()
    这也是一个经常会被重写的方法,作用是为了方便打印输出。
  • public final native void notifyAll()
  • public final native void notify();
    这两个方法放在一起说,notify和notifyAll的作用都是唤醒线程,前者是单个,后者是全部,在方法的注释中,笔者为我们提提供了一个线程被唤醒的三种方式:
A thread becomes the owner of the
object's monitor in one of three ways:
*By executing a synchronized instance method of that object.
*By executing the body of a {@code synchronized} statement
that synchronizes on the object.
*For objects of type {@code Class,} by executing a
synchronized static method of that class.

1、通过执行一个同步对象的实例方法。
2、通过执行一个同步对象中的同步代码块。
3、通过执行对象的同步静态方法。
由此我们可以得出,notify和notifyAll方法都是必须出现在同步代码块当中的。
还有两个小点也值得注意:
1、同一时间只能有一个线程拥有一个对象的控制器
2、两个方法都是只能被拥有对象控制器的线程调用如果当前线程没有拥有对象的控制器时,
会抛出IllegalMonitorStateException异常。

  • public final void wait() throws InterruptedException
  • public final native void wait(long timeout) throws InterruptedException;
  • public final void wait(long timeout, int nanos) throws InterruptedException
    wait0方法同样需要被拥有对象控制器的线程调用,方法可不接受参数,代表无限期等待,与wait(0)作用一致,直到有其他线程唤醒它;也可接受一个long类型的参数,参数为线程将要等待的毫秒数;或者接受一个毫秒数和一个纳秒,方便进一步做更精细的时间控制,具体的控制如下:
    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 >= 500000 || (nanos != 0 && timeout == 0)) {
            timeout++;
        }
        wait(timeout);
    }

下面是一点作者给的提示:

A thread can also wake up without being notified, interrupted, or
timing out, a so-called spurious wakeup.  While this will rarely
occur in practice, applications must guard against it by testing for
the condition that should have caused the thread to be awakened, and
continuing to wait if the condition is not satisfied.

应用中最好循环判断线程是否应该被唤醒或是继续等待,否则就会出现虚假唤醒的情况,虽然这个情况在实践中出现比较少。也就是说我们的wait语句的执行条件必须在循环中进行判断,而不应该在if语句中进行判断。

几个注意点:
1、方法中的参数必须是正数(纳秒不能大于999999),否则会抛出IllegalArgumentException异常。
2、执行wait方法的线程必须拥有对象的控制器,否则会抛出IllegalMonitorStateException异常。
3、线程在等在唤醒或者被唤醒之前不能被打断,否则会抛出InterruptedException异常,抛出异常的同时,线程被打断的状态会被清除。

作者给出的一个模板写法:

          synchronized (obj) {
              while (&lt;condition does not hold&gt;)
                  obj.wait(timeout);
              ... // Perform action appropriate to condition
          }
  • protected void finalize() throws Throwable { }
    1、一个对象在确定没有其他对象引用,即将被垃圾回收器回收时调用。子类可以覆盖这个方法释放资源并执行其他清理操作。
    2、finalize()函数是在垃圾回收器准备释放对象占用的存储空间的时候被调用的,绝对不能直接调用finalize()。
    3、finalize代码中若出现异常,异常会被忽略

猜你喜欢

转载自blog.csdn.net/m0_37664906/article/details/79901598
今日推荐