JAVA源码剖析之---Object类(三)---toString,wait,notify,notifyAll,finalize方法的使用

来源:互联网 发布:学术猫数据库 编辑:程序博客网 时间:2024/05/16 09:53

今天剖析的方法有:toString()方法,notify()方法,notifyAll()方法,wait()方法,wait(args)方法,wait(args,args)方法,finalize()方法。

第6个方法:

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

该方法为被public修饰,所有对象都可见。

该方法返回对象的字符串表示形式,开发人员能够清晰的看到一个对象的各种属性。在官方建议上,建议所有的方法都重写这个方法。Eclipse有代码生成器,可以方便的重写该方

法。

看方法体的内容,可以知道,最终返回getClass().getName() + ' @' + Integer.toHexString(hashCode())。下面用一个例子看一下:

举例:

package edu.java.test;public class TestToString {public static void main(String[] args) {TestClone tc = new TestClone();tc.setName("my name is java");tc.setAge("70 yeas");System.out.println("未重写之前的toString方法:"+tc.getClass().getName()+"@"+Integer.toHexString(tc.hashCode()));System.out.println("重写之后的toString方法:"+tc.toString());}}

上面示例了两个打印语句,一个是原生态的toString方法,一个是重写后的toString方法。下面是结果:

未重写之前的toString方法:edu.java.test.TestClone@1e57e8f重写之后的toString方法:TestClone [name=my name is java, age=70 yeas]

可以看出,原生态的toString方法,不能很好的显示出对象的属性,所以在toString源码里,java开发人员建议每个类都应该重写toString方法的原因。下面是重写后的toString方法。

@Overridepublic String toString() {return "TestClone [name=" + name + ", age=" + age + "]";}

第7个方法:wait()

 public final void wait() throws InterruptedException {        wait(0);    }

该方法会导致当前线程等待,直到另一个线程调用notify()或者notifyAll()方法或者是设定的等待时间过去。

当前线程必须拥有对象监视器如果线程等待,有以下四种方式可以解除等待。

第一种方式:其他线程调用notify()方法,然后该线程刚好被唤醒。

第二种方式:其他线程中断。

第三种方式:其他线程调用notifyAll()方法。

第四种方式:超时时间为0。

一旦发生上面四件事情,就会把该线程从等待设置中删除。


第8个方法:notify()

public final native void notify();

notify本意在中文中是通知的意思。该方法由final和native修饰,表明该方法是依赖于本地,不被java实现,而且不能被子类重写。

这个方法主要是唤醒一个等待中的线程。而且,这个唤醒后的线程和其他正常活跃的线程一样。

在这里要提一下wait方法,wait方法是使当前线程等待,而notify则是唤醒,而且当前运行的线程必须具有该对象的对象监视器,监视器的获得有三种方式:

第一种方式:通过执行该对象的同步的方法。在此大致介绍一下同步,同步就相当于给一个对象上了一把锁,在一段时间内,这个对象只能被一个线程访问,其他任何线程都不能进入。比如说,我现在进入了我家,然后把门锁了,这个时候其他任何没有钥匙的人都进不来。屋里面的东西只能自己动。

synchronized(object)。

第二种方式:在同步代码块中执行。

synchronized void method(){}。

第三种方式:执行一个Class类型的同步静态方法。

而这三种方式都必须要求当前线程拥有对象的监视权。


第9个方法:notifyAll();

唤醒所有线程,可能是按照线程等待时间倒叙唤醒。


以上3个方法举例:

package edu.java.test;import java.text.DateFormat;import java.text.SimpleDateFormat;import java.util.Calendar;public class TestNotify01 {private Object monitor = new Object();DateFormat format = new SimpleDateFormat("yyyy-MM-dd:hh:mm:ss");private String getTime(){return format.format(Calendar.getInstance().getTime());}/** * 0.首先获得对象的监视器 * 1.打印等待前后线程的名称和等待时间 * 2.线程开启 * @param thread * @param ms */public void waitOnce(String thread, final Long ms) {Thread waitThread = new Thread() {public void run() {// 对对象上锁,获得对象的监视器,用的第二种方式,对同步代码块进行上锁synchronized (monitor) {try {System.out.println("Thread等待之前 " + Thread.currentThread().getName() + " Wait at" + getTime());monitor.wait(ms);System.out.println("Thread等待之后 " + Thread.currentThread().getName() + " Wait at" + getTime());} catch (InterruptedException e) {e.printStackTrace();}}}};waitThread.setName(thread);waitThread.start();}/** * 0.获得对象的监视器 * 1.唤醒一个线程 * 2.睡眠两次 * 3.开启线程 * @param thread * @param ms */public void awake(String thread, final Long ms) {Thread notifyThread = new Thread() {public void run() {// 对对象上锁,获得对象的监视器,用的第一种方式synchronized (monitor) {monitor.notify();System.out.println("Thread" + Thread.currentThread().getName() + " 唤醒 at " + getTime());try {Thread.sleep(ms);} catch (InterruptedException e) {e.printStackTrace();}}try {Thread.sleep(ms);} catch (InterruptedException e) {e.printStackTrace();}};};notifyThread.setName(thread);notifyThread.start();}/** * 唤醒全部线程 * @param thread */public void awakeAll(String thread) {Thread notifyThread = new Thread() {public void run() {// 对对象上锁,获得对象的监视器,用的第一种方式synchronized (monitor) {monitor.notifyAll();System.out.println("Thread" + Thread.currentThread().getName() + " 唤醒 at " + getTime());}};};notifyThread.setName(thread);notifyThread.start();}public static void main(String[] args) {/** * 0.首先建立了一个对象 * 1.然后开启三个等待中的线程 * 2.睡眠两秒后唤醒一个线程,从线程的名称中可以看出,唤醒线程的顺序可能是按照线程开启时间来的. */TestNotify01 test = new TestNotify01();test.waitOnce("1",Long.MAX_VALUE);test.waitOnce("2",Long.MAX_VALUE);test.waitOnce("3",Long.MAX_VALUE);try {Thread.sleep(2000);} catch (InterruptedException e) {e.printStackTrace();}test.awake("100",2000L);//test.awakeAll("100");}}

这个测试主要是先等待,然后再唤醒,可以从结果直观的感受一下:

Thread等待之前 1 Wait at2016-07-15:12:28:19Thread等待之前 2 Wait at2016-07-15:12:28:19Thread等待之前 3 Wait at2016-07-15:12:28:19Thread100 唤醒 at 2016-07-15:12:28:21Thread等待之后 1 Wait at2016-07-15:12:28:23

先等待3个线程,然后2S后唤醒,按照顺序唤醒。接下来我们看一下如果是唤醒全部,会是什么结果:

Thread等待之前 1 Wait at2016-07-15:12:29:35Thread等待之前 2 Wait at2016-07-15:12:29:35Thread等待之前 3 Wait at2016-07-15:12:29:35Thread100 唤醒 at 2016-07-15:12:29:37Thread等待之后 3 Wait at2016-07-15:12:29:37Thread等待之后 2 Wait at2016-07-15:12:29:37Thread等待之后 1 Wait at2016-07-15:12:29:37

可以看出,唤醒是倒叙唤醒的。


第10个方法:

public final native void wait(long timeout) throws InterruptedException;

这个方法就是设置时间唤醒,一段时间过后唤醒,上面的wait()方法,其实就是这个方法里的参数设置为0,也就是理解唤醒。


第11个方法:

    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);    }

这个方法和wait()方法很类似,不过它能够更精确的控制时间,并且抛出对应的异常。

如果超时时间<0,就会抛出超时时间为负的异常,因为时间必须>=0。

如果时间最小单位超过999999,就会抛出纳秒时间超出范围异常。


第12个方法:

  protected void finalize() throws Throwable { }
Java 技术允许使用 finalize() 方法在垃圾收集器将对象从内存中清除出去之前做必要的清理工作。

这个方法是由垃圾收集器在确定这个对象没有被引用时对这个对象调用的。它是在 Object 类中定义的,因此所有的类都继承了它。子类覆盖 finalize() 方法以整理系统资源或者执行其他清理工作。finalize() 方法是在垃圾收集器删除对象之前被自动调用的。
垃圾收集器只知道释放那些由new分配的内存,所以不知道如何释放对象的“特殊”内存。为解决这个问题,Java提供了一个名为finalize()的方法,它的工作原理应该是这样的:一旦垃圾收集器准备好释放对象占用的存储空间,它首先调用finalize(),而且只有在下一次垃圾收集过程中,才会真正回收对象的内存。所以如果使用finalize(),就可以在垃圾收集期间进行一些重要的清除或清扫工作(如关闭流等操作)。但JVM(Java虚拟机)不保证此方法总被调用。

1 0
原创粉丝点击