概述

  • Object类是Java类结构的“根”。
  • Java中所有类都继承于Object类,即Object是其他类的父类。
  • 所有的对象,包括泛型,都需要继承Object类中的方法。

核心方法

native是与C++ 联合开发的时候用的。使用native关键字说明这个方法是原生函数,被编译成了DLL,由java去调用。

equals

功能

比较是否对象是否相等

特性

  • 自反性:对于任何非null的引用值x,x.equals(x)必须返回true。
  • 对称性:对于任何非null的引用值x和y,当且仅当y.equals(x)返回true时,x.equals(y)必须返回true。
  • 传递性:对于任何非null的引用值x、y、z,如果x.equals(y)返回true,并且y.equals(z)返回true,那么x.equals(z)也必须返回ture。
  • 一致性:对于任何非null的引用值x和y,只要equals的比较操作在对象中所用的信息没有被修改,多次调用x.equals(y)就会一致的返回ture,或者一致的返回false。
  • 非空性:对于任何非null的引用值x,x.equals(null)必须返回false。

    示例

    1
    2
    3
    4
    5
    public class User{
    private int age;
    private String name;
    //省略构造函数和set、get
    }

重写equals方法,先判断对象是否为null,再判断对象是否指定类型,最后判断对象的属性是否相等。

1
2
3
4
5
6
7
8
9
10
11
@Override
public boolean equals(Object obj) {
if(obj == null){
return false;
}
if(!(obj instanceof User)){
return false;
}
User user = (User)obj;
return user.age == age && user.name.equals(name);
}

注意:在应用场景中,假设变量X要与常量C进行比较,一般用C.equals(X)而不是X.equals(C),由于equals的调用者是非空的,而我们已知常量C为非空,这样避免抛出异常。

当覆盖equals方法时,必须同时覆盖hashCode方法。这是为了维护使用hashCode来判断是否为同一对象的方法,如HashMap。

hashCode

功能

返回对象的哈希值,主要是对哈希表提供支持,如HashMap。

示例

1
a.hashCode()

在Object中hashCode方法是native,那如果我们重写了equals方法的话,该如何重写hashCode方法呢?

如何重写hashCode方法

  1. 定义一个整型变量result,并赋值一个非零数,如17。

    1
    int result = 17;
  2. 根据对象的域(类型)进行计算。

  • boolean:计算(f?1:0)。
  • byte、char、short、int:计算(int)f。
  • long:计算(int)(f^(f>>>32))。
  • float:计算Float.floatToIntBits(f)。
  • double:计算Double.doubleToLongBits(f),然后再计算long。
  • 如果该域是一个对象引用,并且该类的equals方法通过递归的调用equals的方式来比较这个域,则同样为这个域递归的调用hashCode。如果这个域的值为null,则返回0。
  • 如果该域是一个数组,则要把每一个元素当作单独的域来处理,也就是说,递归地应用上述规则,对每个重要元素计算一个散列码,然后把这些散列值组合起来。也可以使用Arrays.hashCode方法。
    1
    2
    3
    4
    //String name
    result = result * 31 + name.hashCode();
    //int age
    result = result * 31 + age;
  1. 返回结果。
    1
    return result;

clone

功能

复制对象,可以将其视为除了new以外,另一种新建对象的方式。

特性

  • x.clone().getClass() == x.getClass()x.clone() != x 两个表达式为true
  • 如果需要复制的对象中包含引用类型,则该引用类型在复制后,仍然指向同一个地址。

    示例

    1
    2
    3
    4
    User u = new User(15,"test");
    u.equals(u.clone()); //false
    u.getClass().equals(u.clone().getClass()); //true
    u.getName().equals(u.clone().getName()); //true

《Effective Java》的作者Joshua Bloch建议我们不应该实现Cloneable接口,而应该使用拷贝构造器或者拷贝工厂。

toString

功能

将对象以字符串的形式表示。

特性

  • 默认表达式:getClass().getName() + '@' + Integer.toHexString(hashCode()),即类的名称的字符串+@+对象的哈希码的无符号的十六进制

其他方法

registerNatives

  • 声明为public final native
  • 该方法主要是为了服务于JNI(Java Native Interface)的,它主要是提供了java类中的方法与对应C++ 代码中的方法的映射,方便jvm查找调用C++ 中的方法。

    getClass

  • 声明为public final native
  • 获取Class类

    wait、notify、notifyAll

  • 声明为public final native
  • 多线程基础
  • 掘金:Wait/Notify通知机制解析
  • 简书:notify()和wait()例子
  • wait和notify基本原理示例
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    public class ThreadA {
    private static int total;
    public static void main(String[] args) {
    ThreadB b = new ThreadB();
    b.start();
    synchronized (b){
    System.out.println("Waiting for b to complete...");
    try {
    b.wait();
    } catch (InterruptedException e) {
    e.printStackTrace();
    }
    System.out.println("Total is:"+total);
    }

    }
    static class ThreadB extends Thread{
    @Override
    public void run() {
    synchronized (this){
    System.out.println("b is counting...");
    for(int i = 0;i < 100;i++){
    total += i;
    if(total >100){
    notify();
    }
    }
    }
    }
    }
    }

上述执行结果:

1
2
3
Waiting for b to complete...
b is counting...
Total is:499500

从上述结果可以看出,线程A并不是在线程B调用notify时被立即唤醒,而是等待线程B的同步代码块执行完后,再从wait方法后继续执行。synchronized同步方法(块)同一时刻只允许一个线程在里面,线程B不退出,线程A就阻塞。


原理

  1. 当线程A调用wait方法后,线程A让出锁,同时加入锁对象等待队列。
  2. 线程B获取锁后,调用notify方法通知锁对象等待队列,使得线程A从等待队列进入阻塞队列。
  3. 线程A进入阻塞队列后,直至线程B释放锁后,线程A竞争得到锁继续从wait方法后执行。

finalize