Object类解析

Java中的Object类是所有类的父类,它提供了以下11个方法:

public final native Class<?> getClass()

public native int hashCode()

public boolean equals(Object obj)

protected native Object clone() throws CloneNotSupportedException

public String toString()

public final native void notify()

public final native void notifyAll()

public final native void wait(long timeout) throws InterruptedException

public final void wait(long timeout, int nanos) throws InterruptedException

public final void wait() throws InterruptedException

protected void finalize() throws Throwable

getClass方法

getClass方法是一个final方法,不允许子类重写,并且也是一个native方法。

返回当前运行时对象的Class对象,注意这里是运行时,比如以下代码中n是一个Number类型的实例,但是java中数值默认是Integer类型,所以getClass方法返回的是java.lang.Integer:

Number n = 0;
Class<? extends Number> c = n.getClass(); // class java.lang.Integer

hashCode方法

hashCode方法也是一个native方法。

该方法返回对象的哈希码,主要使用在哈希表中,比如JDK中的HashMap。

哈希码的通用约定如下:

在java程序执行过程中,在一个对象没有被改变的前提下,无论这个对象被调用多少次,hashCode方法都会返回相同的整数值。对象的哈希码没有必要在不同的程序中保持相同的值。

如果2个对象使用equals方法进行比较并且相同的话,那么这2个对象的hashCode方法的值也必须相等。 如果根据equals方法,得到两个对象不相等,那么这2个对象的hashCode值不需要必须不相同。但是,不相等的对象的hashCode值不同的话可以提高哈希表的性能。

通常情况下,不同的对象产生的哈希码是不同的。默认情况下,对象的哈希码是通过将该对象的内部地址转换成一个整数来实现的。

如果2个对象的equals方法相等,那么他们的hashCode值也必须相等,反之,如果2个对象hashCode值相等,但是equals不相等,这样会影响性能,所以还是建议2个方法都一起重写。

equals方法

比较两个对象是否相等。Object类的默认实现,即比较2个对象的内存地址是否相等:

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

clone方法

创建并返回当前对象的一份拷贝。一般情况下,对于任何对象 x,表达式 x.clone() != x 为true,x.clone().getClass() == x.getClass() 也为true。

Object类的clone方法是一个protected的native方法。

由于Object本身没有实现Cloneable接口,所以不重写clone方法并且进行调用的话会发生CloneNotSupportedException异常。

toString方法

Object对象的默认实现,即输出类的名字@实例的哈希码的16进制:

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

toString方法的结果应该是一个简明但易于读懂的字符串。建议Object所有的子类都重写这个方法。

notify方法

**notify方法是一个native方法,并且也是final的,不允许子类重写。 **

notify方法只能被作为此对象监视器的所有者的线程来调用。一个线程要想成为对象监视器的所有者,可以使用以下3种方法:

执行对象的同步实例方法 使用synchronized内置锁 对于Class类型的对象,执行同步静态方法 一次只能有一个线程拥有对象的监视器。

如果当前线程不是此对象监视器的所有者的话会抛出IllegalMonitorStateException异常

注意点:

因为notify只能在拥有对象监视器的所有者线程中调用,否则会抛出IllegalMonitorStateException异常

notifyAll方法

跟notify一样,唯一的区别就是会唤醒在此对象监视器上等待的所有线程,而不是一个线程。

同样,如果当前线程不是对象监视器的所有者,那么调用notifyAll同样会发生IllegalMonitorStateException异常。

wait(long timeout) throws InterruptedException方法

wait(long timeout)方法同样是一个native方法,并且也是final的,不允许子类重写。

wait方法会让当前线程等待直到另外一个线程调用对象的notify或notifyAll方法,或者超过参数设置的timeout超时时间。

跟notify和notifyAll方法一样,当前线程必须是此对象的监视器所有者,否则还是会发生IllegalMonitorStateException异常。

wait方法会让当前线程(我们先叫做线程T)将其自身放置在对象的等待集中,并且放弃该对象上的所有同步要求。出于线程调度目的,线程T是不可用并处于休眠状态,直到发生以下四件事中的任意一件:

其他某个线程调用此对象的notify方法,并且线程T碰巧被任选为被唤醒的线程

其他某个线程调用此对象的notifyAll方法

其他某个线程调用Thread.interrupt方法中断线程T

时间到了参数设置的超时时间。如果timeout参数为0,则不会超时,会一直进行等待

所以可以理解wait方法相当于放弃了当前线程对对象监视器的所有者(也就是说释放了对象的锁)

之后,线程T会被等待集中被移除,并且重新进行线程调度。然后,该线程以常规方式与其他线程竞争,以获得在该对象上同步的权利;一旦获得对该对象的控制权,该对象上的所有其同步声明都将被恢复到以前的状态,这就是调用wait方法时的情况。然后,线程T从wait方法的调用中返回。所以,从wait方法返回时,该对象和线程T的同步状态与调用wait方法时的情况完全相同。

在没有被通知、中断或超时的情况下,线程还可以唤醒一个所谓的虚假唤醒 (spurious wakeup)。虽然这种情况在实践中很少发生,但是应用程序必须通过以下方式防止其发生,即对应该导致该线程被提醒的条件进行测试,如果不满足该条件,则继续等待。换句话说,等待应总是发生在循环中,如下面的示例:

synchronized (obj) {
    while (<condition does not hold>)
        obj.wait(timeout);
        ... // Perform action appropriate to condition
}```

如果当前线程在等待之前或在等待时被任何线程中断,则会抛出InterruptedException异常。在按上述形式恢复此对象的锁定状态时才会抛出此异常。

### wait(long timeout, int nanos) throws InterruptedException方法

跟wait(long timeout)方法类似,多了一个nanos参数,这个参数表示额外时间(以毫微秒为单位,范围是 0-999999)。 所以超时的时间还需要加上nanos毫秒。

需要注意的是 wait(0, 0)和wait(0)效果是一样的,即一直等待。

### wait() throws InterruptedException方法

跟之前的2个wait方法一样,只不过该方法一直等待,没有超时时间这个概念。

以下这段代码直接调用wait方法会发生IllegalMonitorStateException异常,这是因为调用wait方法需要当前线程是对象监视器的所有者:

Factory factory = new Factory(); factory.wait();


一般情况下,wait方法和notify方法会一起使用的,wait方法阻塞当前线程,notify方法唤醒当前线程,一个使用wait和notify方法的生产者消费者例子代码如下:

public class WaitNotifyTest {

public static void main(String[] args) {
    Factory factory = new Factory();
    new Thread(new Producer(factory, 5)).start();
    new Thread(new Producer(factory, 5)).start();
    new Thread(new Producer(factory, 20)).start();
    new Thread(new Producer(factory, 30)).start();
    new Thread(new Consumer(factory, 10)).start();
    new Thread(new Consumer(factory, 20)).start();
    new Thread(new Consumer(factory, 5)).start();
    new Thread(new Consumer(factory, 5)).start();
    new Thread(new Consumer(factory, 20)).start();
}

}

class Factory {

public static final Integer MAX_NUM = 50;

private int currentNum = 0;

public void consume(int num) throws InterruptedException {
    synchronized (this) {
        while(currentNum - num < 0) {
            this.wait();
        }
        currentNum -= num;
        System.out.println("consume " + num + ", left: " + currentNum);
        this.notifyAll();
    }
}

public void produce(int num) throws InterruptedException {
    synchronized (this) {
        while(currentNum + num > MAX_NUM) {
            this.wait();
        }
        currentNum += num;
        System.out.println("produce " + num + ", left: " + currentNum);
        this.notifyAll();
    }
}

}

class Producer implements Runnable { private Factory factory; private int num; public Producer(Factory factory, int num) { this.factory = factory; this.num = num; } @Override public void run() { try { factory.produce(num); } catch (InterruptedException e) { e.printStackTrace(); } } }

class Consumer implements Runnable { private Factory factory; private int num; public Consumer(Factory factory, int num) { this.factory = factory; this.num = num; } @Override public void run() { try { factory.consume(num); } catch (InterruptedException e) { e.printStackTrace(); } } }

注意的是Factory类的produce和consume方法都将Factory实例锁住了,锁住之后线程就成为了对象监视器的所有者,然后才能调用wait和notify方法。

输出:

produce 5, left: 5 produce 20, left: 25 produce 5, left: 30 consume 10, left: 20 produce 30, left: 50 consume 20, left: 30 consume 5, left: 25 consume 5, left: 20 consume 20, left: 0


### finalize方法

finalize方法是一个protected方法,Object类的默认实现是不进行任何操作。

该方法的作用是实例被垃圾回收器回收的时候触发的操作,就好比 “死前的最后一波挣扎”。

直接写个弱引用例子:

Car car = new Car(9999, "black"); WeakReference<Car> carWeakReference = new WeakReference<Car>(car);

int i = 0; while(true) { if(carWeakReference.get() != null) { i++; System.out.println("Object is alive for "+i+" loops - "+carWeakReference); } else { System.out.println("Object has been collected."); break; } }

class Car { private double price; private String colour;

public Car(double price, String colour){
    this.price = price;
    this.colour = colour;
}

// get set method

@Override
protected void finalize() throws Throwable {
    System.out.println("i will be destroyed");
}

}


输出:

.... Object is alive for 26417 loops - java.lang.ref.WeakReference@7c2f1622 Object is alive for 26418 loops - java.lang.ref.WeakReference@7c2f1622 Object is alive for 26419 loops - java.lang.ref.WeakReference@7c2f1622 Object is alive for 26420 loops - java.lang.ref.WeakReference@7c2f1622 Object is alive for 26421 loops - java.lang.ref.WeakReference@7c2f1622 Object is alive for 26422 loops - java.lang.ref.WeakReference@7c2f1622 Object has been collected. i will be destroyed

转载于:https://my.oschina.net/u/3672057/blog/1541294

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值