简介

众所周知, JAVA是作为一门面向对象的语言去设计的。所有的JAVA对象中都会有一些方法,比如hashCode(),wait()等等,这些方法都是因为JAVA中的对象有一个统一的祖先类:Object,而今天我们就要解读一下这个祖先类。

常用方法

getClass()

简介

首先看下这个方法的源码:

1
2
@IntrinsicCandidate
public final native Class<?> getClass();

这个方法主要用来获取运行时对象的类信息,返回值为一个Class类型的对象,通过这个对象我们可以获取到与该类相关的各种元数据,比如类名、报名、方法以及属性等等,这使得它在反射编程中非常有用。

用例

1
2
3
4
5
6
7
8
9
public static void main(String[] args) {
Animal animal1 = new Monkey();
Monkey animal2 = new Monkey();
Animal animal3 = new Pig();
System.out.println(animal1.getClass());
System.out.println(animal2.getClass());
System.out.println(animal3.getClass());
}

以下为出参,可以看到animal1和animal3虽然都是Animal类声明,但是运行时却准确的指向了各自实际的类

1
2
3
class lang.object.Monkey
class lang.object.Monkey
class lang.object.Pig

hashCode()

简介

返回一个Object对象的hash码,这是一个int类型的数值,主要用来支持一些基于hash表构建的类,比如经典的HashMap等等。在对象信息不变的情况下,无论什么时候它的Hash值都是不变的。如果两个不同对象产生了相同的hash值,我们一般称这种现象为hash冲突。hashCode()方法要遵守一下约定

  1. 对同一个对象,无论什么时候调用hashCode(),返回值都是不会变的;
  2. 如果两个对象的eques()方法为true,那么他们各自的hashCode()方法返回的hash值必定相等;
  3. 如果两个对象的eques()方法为false,那么他们各自的hashCode()方法返回的hash值也不相等;
1
2
3
@IntrinsicCandidate
public native int hashCode();

equals(Object obj)

简介

用来声明两个对象的引用地址相等。对于一些类会重写equals()方法,使得它不仅能比较引用地址,也能比较对象内容是否相等,典型的比如String类。
针对任意两个非NULL的对象,一般有以下特性:

  1. 自反性
    • 对于非空对象x,那么x.equals(x)必定为true
      1
      2
      3
      4
      public static void main(String[] args) {
      Monkey animal = new Monkey();
      System.out.println(animal.equals(animal));
      }
  2. 对称性
    • 对于任意非空x和y,如果x.equals(y)为true,那么y.equals(x)也为true。注意这里的String重写了equals()方法,使得它能比较对象引用地址以及对象内容是否相等。这里以此证明它的对成习惯特性。
      1
      2
      3
      4
      5
      6
      public static void main(String[] args) {
      String x = new String("Hello");
      String y = new String("Hello");
      System.out.println(x.equals(y)); // 输出 true
      System.out.println(y.equals(x)); // 输出 true
      }
  3. 传递性
    • 对于任意非空对象x,y,z, 如果x.equals(y)为true,y.equals(z)为true,那么x.equals(z)必定也为true
      1
      2
      3
      4
      5
      6
      7
      8
      public static void main(String[] args) {
      String x = new String("Hello");
      String y = new String("Hello");
      String z = new String("Hello");
      System.out.println(x.equals(y)); // 输出 true
      System.out.println(y.equals(z)); // 输出 true
      System.out.println(x.equals(z)); // 输出 true
      }
  4. 一致性
    • 对于任意非空对象x,y,如果x.equals(y)为true且不调整对象x和y的属性,那么不论多用多少此x.equals(y),结果都为true

clone()

简介

先看下源码:

1
2
@IntrinsicCandidate
protected native Object clone() throws CloneNotSupportedException;

可以看到它是一个protected修饰的,意味着我们不能直接调用这个方法。它的作用就是copy一个对象的所有属性,并返回一个新的对象。需要注意两点,第一是克隆的对象必须要实现 Cloneable 接口并重写 clone 方法,否则会报 CloneNotSupportedException 异常;第二是这里的copy是浅拷贝,不是深拷贝,这意味我们只能复制基础属性,对于引用则是复制引用,而不是完整的复制一个对象的所有属性。

toString()

简介

1
2
3
public String toString() {
return getClass().getName() + "@" + Integer.toHexString(hashCode());
}

以字符串的形式表现一个对象。

  • getClass().getName(),返回对象的类路径+类名,比如 lang.object.Student
  • Integer.toHexString(hashCode()),将哈希值转成16进制数格式的字符串。

wait()

简介

使当前线程处于等待状态,直到它被唤醒。wait()方法主要有三种实现,这里统一来讲一下。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// 1. 无限等待下去,直到被唤醒
public final void wait() throws InterruptedException {
wait(0L);
}
// 2. 等待指定时间,直到被唤醒或者超时
public final native void wait(long timeoutMillis) throws InterruptedException;

// 3. 等待指定时间,直到被唤醒或者超时, 这里的nanos是纳秒, 只要是大于0,timeoutMillis都会+1, 所以1或者其它数字的效果是一样的
public final void wait(long timeoutMillis, int nanos) throws InterruptedException {
if (timeoutMillis < 0) {
throw new IllegalArgumentException("timeoutMillis value is negative");
}

if (nanos < 0 || nanos > 999999) {
throw new IllegalArgumentException(
"nanosecond timeout value out of range");
}

if (nanos > 0 && timeoutMillis < Long.MAX_VALUE) {
timeoutMillis++;
}

wait(timeoutMillis);
}

处于等待状态的线程会释放掉自己的锁, 在被唤醒后会重新去竞争锁.被唤醒的场景主要有以下几种:
1、其它线程调用notify()或者notifyAll()方法,导致当前线程被唤醒
2、其他线程中断了这个线程,即调用了该线程的interrupt()方法,此时被唤醒的线程不会再继续执行,而是抛出InterruptedException异常
3、过了指定的超时时间而被唤醒,主要是调用了wait(long timeoutMillis)或者wait(long timeoutMillis, int nanos)方法

1
2
3
4
final void checkForComodification() {    
if (modCount != expectedModCount)        
throw new ConcurrentModificationException();
}

以下为被中断时抛出异常的例子,执行完毕后会打印”线程被中断,抛出InterruptedException异常”:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public class OwnerThread extends Thread{
public void run() {
synchronized (this){
try {
wait();
} catch (InterruptedException e) {
System.out.println("线程被中断,抛出InterruptedException异常");
}
}
}
}

public static void main(String[] args) {
OwnerThread ownerThread = new OwnerThread();
ownerThread.start();
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// nothing to do
}
ownerThread.interrupt();
}

notify()与notifyAll()

简介

都用于唤醒处于waiting状态的线程,区别是notify()方法会随机唤醒一个线程,而notifyAll()则是将所有处于等待队列的线程都唤醒,一同去竞争锁。只有拥有对象锁的线程才能调用者两个方法,线程通过以下三种方式之一成为对象监视器的所有者:

1、通过执行该对象的同步实例方法。

2、通过执行在对象上同步的语句的 synchronized 主体

3、对于类型的 Class, 对象,通过执行该类的同步静态方法