Java - Obejct

关于Object类(Java 10)中的方法,根据其所涉及的知识点,分为以下4个部分:html

  • 基础
  • 反射
  • 线程
    • wait
      • public final void wait() throws InterruptedException
      • public final void wait​(long timeout) throws InterruptedException
      • public final void wait​(long timeout,int nanos) throws InterruptedException
    • notify : public final void notify()
    • notifyAll : public final void notifyAll()
  • JVM GC
    • finalize : protected void finalize() throws Throwable

 2018-09-15java

今天写:equals、hashCode、toString、clone、getClass,后面的方法等学到相关知识再做补充。sql

 一. equals

1 public boolean equals(Object obj) {
2     return (this == obj);
3 }
View Code

equals用来判断两个对象是否”相等“,而对“相等”这个词的定义不一样,得出的结果也不一样:编程

  • 1. Object类中的equals使用 this == obj 来定义相等,即:两个引用是否指向同一个对象;
  • 2. 有些时候,咱们并不须要定义如此严格的相等,好比:定义一个Point类,只要两个点的横纵坐标分别相等,咱们就认为它们相等;

要实现上面的需求,咱们就须要重写equals方法,以下:api

 1 public class Point {
 2     int x;
 3     int y;
 4     
 5     Point(int x, int y) {
 6         this.x = x;
 7         this.y = y;
 8     }
 9     
10     @Override
11     public boolean equals(Object obj) {
12         if (this == obj) {
13             return true;
14         }
15         if (obj instanceof Point) {
16             Point p = (Point)obj;
17             return (this.x == p.x && this.y == p.y);
18         }
19         return false;
20     }
21 }
View Code

测试:安全

 1 public static void main(String[] args) {
 2     Point p1 = new Point(1, 1);
 3     Point p2 = new Point(1, 1);
 4     System.out.println("p1.equals(p2) : " + p1.equals(p2));
 5     p2.x = 2;
 6     System.out.println("p1.equals(p2) : " + p1.equals(p2));
 7 }
 8 /* 输出
 9 p1.equals(p2) : true
10 p1.equals(p2) : false
11 */
View Code

另记:String类也重写了equals方法,实现了:只要两个字符串长度相等及字符串中对应位置的字符相等,则两字符串相等。能够参考String类中的equals方法。数据结构

注意:在重写equals方法时,方法定义要写为:public boolean equals(Object obj) {....} ,参数类型是:Object obj
并发

回到顶部oracle

 二. hashCode

1 public native int hashCode();
View Code

hashCode,官方解释:返回对象的一个哈希码,为基于哈希表的数据结构提供便利,如:HashMap等。ide

Object类中的hashCode方法为本地方法,在重写hashCode方法时,需遵循如下规则:

  • 1. 在对象equals比较中使用的信息未被修改的前提下,在Java程序运行期间,同一个对象屡次调用(不一样时间)应该始终返回同一个整数。在程序不一样的运行期间,返回的整数不须要保持一致;
  • 2. 若是两个对象调用 equals(Object obj) 时相等,那么这两个对象在调用 hashCode 时应该返回同一个整数值;
  • 3. 并不要求为调用 equals(Object obj) 时不相等的两个对象,返回两个不一样的哈希码。只不过为不相等的对象返回不一样的哈希码能够提升哈希表的性能;

关于第2条规则,咱们继续Point类这个例子。首先,在未重写hashCode方法的状况下,咱们测试两个对象的hashCode()输出值:

 1 public static void main(String[] args) {
 2     Point p1 = new Point(9483, 89382);
 3     Point p2 = new Point(9483, 89382);
 4     System.out.println("p1.hashCode() : " + p1.hashCode());
 5     System.out.println("p2.hashCode() : " + p2.hashCode());
 6 }
 7 /* 输出:
 8 p1.hashCode() : 166239592
 9 p2.hashCode() : 991505714
10 */
View Code

能够看到,在咱们定义的equals方法下相等的两个对象,获得的hashCode是不一样的,如此不一致会形成什么后果呢?咱们知道 HashMap 在存储<Key, Value>时,若是Key1等于Key2,那么存储的键值对为:<Key1, Value2>,即:只会存储一个Key,使用的是最新的Value。而 HashMap 中在判断 Key1是否等于Key2时,就使用到了它们的hashCode。在未重写hashCode方法的状况下,看以下测试:

 1 public static void main(String[] args) {
 2     Point p1 = new Point(9483, 89382);
 3     Point p2 = new Point(9483, 89382);
 4     
 5     HashMap<Point, Integer> map = new HashMap<Point, Integer>();
 6     map.put(p1, p1.hashCode());
 7     map.put(p2, p2.hashCode());
 8     for (Map.Entry<Point, Integer> m : map.entrySet()) {
 9         System.out.println(m);
10     }
11 }
12 /* 输出
13 Point@9e89d68=166239592
14 Point@3b192d32=991505714
15 */
View Code

根据咱们对Point类相等的定义,p1与p2相等,而在 HashMap 中却存入了两个键值对,显然不符合咱们的意图。(equals与hashCode的不一致,会形成使用时产生歧义,从而致使意想不到的错误。因此,咱们在重写equals方法后,也要重写hashCode方法,使其意义一致)如今咱们来重写hashCode方法,再进行如上测试:

1 @Override
2 public int hashCode() {
3     return (x & y) | (x ^ y);
4 }
5 /* 输出
6 Point@17d2f=97583
7 */
View Code

根据咱们对hashCode方法的定义,对象的hashCode只与(x, y)相关,因此 p1.hashCode() == p2.hashCode() 为 true。这样一来,HashMap 中只会存入一个键值对,符合咱们的预期。

回到顶部

 三. toString

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

源码中直接返回:对象类型名@对象hashCode的十六进制,举个例子:

1 public static void main(String[] args) {
2     Point p1 = new Point(9483, 89382);
3     System.out.println(p1.toString());
4 }
5 /* 输出
6 Point@17d2f
7 */
View Code

不少状况下,咱们都要重写toString()方法,就好比Point类,咱们想知道的是点的横纵坐标(x, y),而不是 Point@17d2f 这串不知所云的字符。

1 @Override
2 public String toString() {
3     return "(" + x + ", " + y + ")";
4 }
5 /* 输出
6 (9483, 89382)
7 */
View Code

回到顶部

四.Clone

1 protected native Object clone() throws CloneNotSupportedException;
View Code

从方法定义入手:

  • 1. protected,代表只有本类及其子类可使用该方法来克隆对象,子类能够重写该方法;
  • 2. native,代表Object类中clone是本地方法;
  • 3. CloneNotSupportedException,若是调用clone方法的对象的类没有 implememnts Cloneable,就会抛出这个异常;

如今,咱们对以前的Point类进行部分修改,为了节省空间,我只贴出修改部分的代码:

首先,定义Data类,用来记录一个点所包含的相关信息;

1 public class Data {
2     int weight;
3     String name;
4     
5     Data(int weight, String name) {
6         this.weight = weight;
7         this.name = name;
8     }
9 }
View Code

而后,Point类实现Cloneable接口,而且Point类中包含一个Data类型字段,以下:

 1 public class Point implements Cloneable {
 2     int x;
 3     int y;
 4     Data data;
 5 
 6     Point(int x, int y, Data data) {
 7         this.x = x;
 8         this.y = y;
 9         this.data = data;
10     }
11     ...
12 }
View Code

测试:

 1 public static void main(String[] args) throws Exception {
 2     Data data = new Data(20, "A");
 3     Point p1 = new Point(1, 2, data);
 4     Point p2 = (Point)p1.clone();
 5     
 6     System.out.println("p1 == p2 : " + (p1 == p2));
 7     System.out.println("p1.(x, y) = " + p1.toString() + ", p2.(x, y) = " + p2.toString());
 8     System.out.println("p1.data == p2.data : " + (p1.data == p2.data));
 9 }
10 /* 输出
11 p1 == p2 : false
12 p1.(x, y) = (1, 2), p2.(x, y) = (1, 2)
13 p1.data == p2.data : true
14 */
View Code

对于测试的输出,咱们能够发现:

  • 1. p1 == p2 为 false,说明 p1.clone() 从新生成了一个对象;
  • 2. p1.(x, y) 等于 p2.(x, y),说明 p1.clone() 会把原对象的基础数据类型字段的值拷贝给生成的新对象的对应字段;
  • 3. p1.data == p2.data 为 true,说明引用类型字段,新对象的字段与原对象的字段引用同一个对象;

对于第3条,即Object类的clone方法是浅拷贝,理解如图:

 

在一些并发编程情景下,咱们经常须要操做 不可变对象 来保证并发安全性。不可变对象,顾名思义就是你建立的对象不会改变,你能够理解为:

  • 1. 对象自己就是不可变的,如:字段都是final修饰等;
  • 2. 对象自己可变,可是咱们确保在使用的时候不会去改变它,即人为的不可变;

(更详细的内容,能够参考《Java并发编程实战》)

如今,假如我要在并发环境下使用p1.clone()出来的对象p2,并要求p2是不可变的。而事实上,其余线程能够经过 p1.data 来改变 p2.data 的状态,以破坏p2的不可变性。

要想使p2不可变,咱们就须要对Point类进行深拷贝,即:对Piont类中的Data类型字段也建立一个新的对象,使得 p1.data != p2.data,以下:

 1 public class Data {
 2     ...
 3     // 自定义的clone(),并不是重写Object类中的clone()
 4     public Data clone() {
 5         return new Data(weight, name);
 6     }
 7 }
 8 public class Point implements Cloneable {
 9     ...
10     @Override
11     protected Object clone() throws CloneNotSupportedException {
12         Point p = (Point)super.clone();
13         p.data = data.clone(); // 这里的data.clone()与Object类中的clone()无关
14         return p;
15     }
16     ...
17 }
18 /* 重复上面的测试,输出:
19 p1 == p2 : false
20 p1.(x, y) = (1, 2), p2.(x, y) = (1, 2)
21 p1.data == p2.data : false
22 */
View Code

思考:若是一个类中一直嵌套着包含引用类型字段,那么咱们该怎么才能作到深拷贝呢?很明显,对于类中每个引用类型对象都作深拷贝。(递归处理)

回到顶部

 五. getClass

1 public final native Class<?> getClass();
View Code

getClass方法,返回对象的类对象,在反射中常用,例如:

Data类中有个私有方法 printInfo(),该方法在Point类中没法正常调用,可是咱们能够经过反射机制来调用该方法。

 1 public class Data {
 2     ...
 3     private void printInfo() {
 4         System.out.println("weight = " + weight);
 5         System.out.println("name : " + name);
 6     }
 7 }
 8 // 在Point类中
 9 public static void main(String[] args) throws Exception {
10     Data data = new Data(20, "A");
11     Class<?> clz = data.getClass();
12     Method m = clz.getDeclaredMethod("printInfo");
13     m.setAccessible(true); // 抑制Java的访问控制检查
14     m.invoke(data);
15 }
16 /* 输出
17 weight = 20
18 name : A
19 */
View Code

这里只是简单提一下,更多关于反射的知识,会在后期总结。

回到顶部


2018-10-06

今天更新:wait系列、notify系列、finalize。

(我尽可能以简单清晰的方式来展示个人内容,对于涉及到的知识点,这里只是抛砖引玉,若想深刻研究,你们能够进一步去查阅资料)

六. wait、notify

来看字面意思的解释:

  • wait
    • wait():使当前线程等待,直到被唤醒(notified)或被中断(interrupted),等价于wait(0L, 0) ;
    • wait(long timeout):使当前线程等待,直到被唤醒或被中断,或者一段肯定的时间(timeout)过去了;
    • wait(long timeout, int nanos):使当前线程等待,直到被唤醒或被中断,或者一段肯定的时间(1000000*timeout+nanos)过去了;
  • notify
    • notify():唤醒一个正在等待该对象锁的线程,若是有多个线程在等待资源,那么以某种规则(不肯定的)选取其中一个线程唤醒;
    • notifyAll():唤醒全部正在等待该对象锁的线程;

(注:线程被唤醒后,须要等待当前线程释放锁资源,而后与其余请求该锁的线程竞争,获取锁后才能获得执行)

字面解释就这些,下面要写的是我在阅读API(JavaSE 10 & JDK10)中该部份内容时的困惑及解答。先来看看这些方法的源码吧:

 1 /************************** wait **************************/
 2 public final void wait() throws InterruptedException {
 3     wait(0L); // 调用 wait(long timeout)
 4 }
 5 
 6 public final native void wait(long timeout) throws InterruptedException;
 7 
 8 public final void wait(long timeout, int nanos) throws InterruptedException {
 9     if (timeout < 0) {
10         throw new IllegalArgumentException("timeout value is negative");
11     }
12     if (nanos < 0 || nanos > 999999) {
13         throw new IllegalArgumentException(
14                             "nanosecond timeout value out of range");
15     }
16     /* 
17     timeout 是ms(毫秒)计时
18     nanos   是ns(纳秒)计时
19     本来我觉得某些领域须要更精确的时间控制,因此才提供wait(long timeout, int nanos)这个方法
20     而当我看到源码的时候,这不就多了个timeout++吗?这个判断和加法在外部也能够作啊。
21     因此,为何要有这个方法呢? - -?(这个问题这里不深刻讨论)
22     */
23     if (nanos > 0) {
24         timeout++;
25     }
26     wait(timeout); // 调用 wait(long timeout)
27 }
28 
29 /************************** notify ************************/
30 @HotSpotIntrinsicCandidate
31 public final native void notify();
32 
33 @HotSpotIntrinsicCandidate
34 public final native void notifyAll();
View Code

在API中,能够看到wait的3个方法中都抛出如下异常:

  • IllegalMonitorStateException - if the current thread is not the owner of the object's monitor
  • InterruptedException - if any thread interrupted the current thread before or while the current thread was waiting. The interrupted status of the current thread is cleared when this exception is thrown.

1. IllegalMonitorStateException

若是当前线程不是该对象锁的持有者时,抛出该异常。如何理解呢,看下面的代码:

 1 class T1 extends Thread {
 2     
 3     @Override
 4     public void run() {
 5         try {
 6             this.wait(1000);
 7             System.out.println("wait over");
 8         } catch (InterruptedException e) {}
 9     }
10 }
11 
12 public class IllegalMonitorStateTest {
13 
14     public static void main(String[] args) {
15         T1 t = new T1();
16         t.start();
17     }
18 }
19 
20 /* 异常
21 Exception in thread "Thread-0" java.lang.IllegalMonitorStateException
22     at java.base/java.lang.Object.wait(Native Method)
23     at T1.run(IllegalMonitorStateTest.java:6)
24 */
View Code

抛出异常的缘由:[线程 t]  执行 this.wait(),但其并未获取 this锁。wait操做是要释放当前锁资源的,都没有获取如何释放呢?

官方给出的说明:在如下3种状况下,线程为对象锁的持有者:

  • 执行该对象的同步实例方法,如:public synchronzied void foo(){}
  • 执行以该对象为锁的同步代码块,如:synchronized (this) {}
  • 对于类类型的对象,执行该类的静态同步方法,如:public static synchronized void foo(){}

 因此,针对上面的例子,作如下修改(只贴出修改的部分),确保其不会抛出IllegalMonitorStateException异常。

 1 @Override
 2 public void run() {
 3     synchronized (this) {
 4         try {
 5             this.wait(1000);
 6             System.out.println("wait over");
 7         } catch (InterruptedException e) {}
 8     }
 9 }
10 /* 再进行测试,输出
11 wait over
12 */
View Code

2. InterruptedException

 1 class T2 extends Thread {
 2     T2(String name) {
 3         super(name);
 4     }
 5     
 6     @Override
 7     public void run() {
 8         synchronized (this) {
 9             try {
10                 this.wait();
11                 System.out.println("wait over");
12             } catch (InterruptedException e) {
13                 e.printStackTrace();
14             }
15         }
16     }
17 }
18 
19 public class InterruptedExceptionTest {
20     public static void main(String[] args) throws Exception {
21         T2 t = new T2("[线程 t]");
22         t.start();
23         System.out.println(t.getName() + "中断状态:" + t.isInterrupted());
24         System.out.println("[线程 main]执行 t.interrupt();");
25         t.interrupt();
26         System.out.println(t.getName() + "中断状态:" + t.isInterrupted());
27         
28         System.out.println("------------------------------------------");
29         Thread mainThread = Thread.currentThread();
30         System.out.println("[线程 main]中断状态:" + mainThread.isInterrupted());
31         System.out.println("[线程 main]执行 mainThread.interrupt();");
32         mainThread.interrupt();
33         System.out.println("[线程 main]中断状态:" + mainThread.isInterrupted());
34         System.out.println("[线程 main]running......");
35     }
36 }
37 
38 /* 输出 & 异常,两个线程都有信息要输出到控制台,因此也可能异常信息先输出
39 [线程 t]中断状态:false
40 [线程 main]执行 t.interrupt();
41 [线程 t]中断状态:true
42 ------------------------------------------
43 [线程 main]中断状态:false
44 [线程 main]执行 mainThread.interrupt();
45 [线程 main]中断状态:true
46 [线程 main]running......
47 java.lang.InterruptedException
48     at java.base/java.lang.Object.wait(Native Method)
49     at java.base/java.lang.Object.wait(Unknown Source)
50     at T3.run(InterruptedExceptionTest.java:10)
51 */
View Code

在当前线程等待前或等待期间,若是有其余线程中断当前线程,则抛出该异常。看下面的例子:

在这个示例中,咱们须要注意两点:

  • 抛出了InterruptedException异常,由于 [线程 t] 在等待期间,[线程 main] 执行 t.interrupte()使其中断
  • interrupt()不能中断运行中的线程,它只能改变中断状态。在分割线后,能够看到正在运行的 [线程 main] 执行了 mainThread.interrupt(),中断状态也由 false -> true,可是后面的输出语句仍然被执行了,即 [线程 main] 并无被终止。那要如何才能终止呢?其实能够经过加个判断和return语句来完成终止,如:if (mainThread.isInterrupted()) return;

3. Note that only the locks on this object are relinquished; any other objects on which the current thread may be synchronized remain locked while the thread waits.

只有基于这个对象的锁被释放;在线程等待期间,当前线程可能被同步的任何其余对象都将保持锁定。(翻译看不懂?直接看代码吧)

 1 class T3 extends Thread {
 2     Object obj;
 3     
 4     T3(String name, Object obj) {
 5         super(name);
 6         this.obj = obj;
 7     }
 8     
 9     @Override
10     public void run() {
11         synchronized (this) {
12             System.out.println(Thread.currentThread().getName() + "获取this锁");
13             synchronized (obj) {
14                 System.out.println(Thread.currentThread().getName() + "获取obj锁");
15                 try {
16                     this.wait();
17                 } catch (InterruptedException e) {}
18             }
19         }
20     }
21 }
22 
23 public class OtherLockBeRetainedTest {
24     
25     public static void main(String[] args) throws Exception {
26         Object obj = "x";
27         T3 t1 = new T3("[线程 t1]", obj);
28         T3 t2 = new T3("[线程 t2]", obj);
29         t1.start();
30         Thread.sleep(2000);
31         t2.start();
32     }
33 }
34 
35 /* 输出 (程序死锁)
36 [线程 t1]获取this锁
37 [线程 t1]获取obj锁
38 [线程 t2]获取this锁
39 */
View Code

顺着代码理一下程序执行过程:

  • 首先,[线程 t1] 获得执行,而且相继拿到了 this锁 和 obj锁;
  • [线程 t1] 执行 this.wait()(等待被唤醒),把 this锁释放了,而 obj锁依旧保留在 [线程 t1]手中
  • [线程 t2] 获得执行,拿到了 this锁,进一步须要获取 obj锁,发现 obj锁依旧拽在 [线程 t1]手中,因而等待,产生了死锁;

4. 虚假唤醒(spurious wakeups)

(我这里只给出一种简单的虚假唤醒状况,更详细的内容,你们能够自行查阅资料,能够参看:《Java并发编程实战》14.2 “使用条件队列”,《Effective Java》第69条 “并发工具优先于wait和notify”)

在某些状况下,咱们须要某个条件成立,线程才能往下执行,而下面的示例中,未必按这个套路出牌:

 1 class T4 extends Thread {
 2    boolean condition;
 3    
 4    T4(boolean condition) {
 5        this.condition = condition;
 6    }
 7    
 8    @Override
 9    public void run() {
10        synchronized (this) {
11            try {
12                if (!condition) {
13                    this.wait();
14                }
15                // 当 condition == true 时,执行下面的操做
16                System.out.println("condition : " + condition);
17            } catch (InterruptedException e) {}
18        }
19    }
20 }
21 
22 public class SpuriousWakeupTest {
23     public static void main(String[] args) throws Exception {
24         T4 t = new T4(false);
25         t.start();
26         Thread.sleep(1000);
27         synchronized (t) {
28             t.notify();
29         }
30     }
31 }
32 
33 /* 输出
34 condition : false
35 */
View Code

上面的例子,咱们的原意是:当condition为true时,[线程 t] 继续执行下面的操做,不然继续等待直到条件成立。固然,[线程 t] 第一次判断condition时,符合咱们的意图,进行了等待;后来被主线程notify唤醒,condition依旧为false,而 [线程 t] 却执行了后续的操做,显然不符合咱们的意图。虽然 [线程 t] 真的被唤醒了,可是在咱们的业务逻辑定义下,它不该该被唤醒执行操做,应该继续等待。对于这样的唤醒,咱们称为虚假唤醒(状况不止于此)。如何解决这个问题呢?其实只须要作一点小修改便可,即:把 if语句换成 while语句,以下:

 1 @Override
 2 public void run() {
 3     synchronized (this) {
 4         try {
 5             while (!condition) {
 6                 this.wait();
 7             }
 8             // 当 condition == true 时,执行下面的操做
 9             System.out.println("condition : " + condition);
10         } catch (InterruptedException e) {}
11     }
12 }
View Code

回到顶部

七. finalize

1 @Deprecated(since="9")
2 protected void finalize() throws Throwable {}
View Code

finalize,终结方法,当GC认为对象再也不被引用时,会先调用finalize(),再回收该对象。子类能够重写finalize方法,来处理一些系统资源或者完成其余清理工做。(自Java 9 开始被弃用)

关于finalize,有如下4个点须要注意(摘自《Effective Java》第2版,第7条:避免使用终结方法):

  • Java语言规范不只不保证终结方法会被及时地执行,并且根本就不保证它们会被执行;
  • System.gcSystem.runFinalization这两个方法确实增长了终结方法被执行的机会,但它们一样不保证终结方法必定会被执行;
  • 使用终结方法存在很是严重的性能损失,使用不当可能致使内存溢出(Java的Finalizer引起的内存溢出);
  • 终结方法链”并不会被自动执行。若是类(非Object)有终结方法,而且子类覆盖了终结方法,子类的终结方法就必须手工调用超类的终结方法;

在我看来,咱们能够把finalize()当作是对象被GC回收前的回调,来看个“对象复活”的例子。代码来自:http://www.javashuo.com/article/p-htdktnss-ht.html

 1 public class FinalizeTest {
 2 
 3     public static FinalizeTest SAVE_HOOK = null;
 4 
 5     public static void main(String[] args) throws Exception {
 6         SAVE_HOOK = new FinalizeTest();
 7         SAVE_HOOK = null;
 8         System.gc();
 9         Thread.sleep(500);
10         if (null != SAVE_HOOK) {
11             System.out.println("Yes, I am still alive");
12         } else {
13             System.out.println("No, I am dead");
14         }
15         SAVE_HOOK = null;
16         System.gc();
17         Thread.sleep(500);
18         if (null != SAVE_HOOK) {
19             System.out.println("Yes, I am still alive");
20         } else {
21             System.out.println("No, I am dead");
22         }
23     }
24   
25     @Override
26     protected void finalize() throws Throwable {
27         super.finalize();
28         System.out.println("execute method finalize()");
29         SAVE_HOOK = this;
30     }
31 }
32 
33 /* 输出
34 execute method finalize()
35 Yes, I am still alive
36 No, I am dead
37 */
View Code

分析(为了描述方便,把 new FinalizeTest() 生成的对象叫作 对象A):

  • 第1次调用System.gc(),此时SAVE_HOOK = null,没有引用指向对象A。GC认为对象A能够回收,而该类重写了finalize方法,因此会先调用对象的finalize(),所以输出“execute method finalize()”;
  • 调用finalize()后,SAVE_HOOK = this(对象A),因此接下来的 if 判断为true,输出“Yes, I am still alive”;(就这样,原本要被回收的对象复活了
  • 第2次调用System.gc(),此时SAVE_HOOK = null,GC认为对象A能够回收,可是不会再去调用该对象的finalize()(GC只会调用一次对象的finalize方法)。因此接下来的 if 判断为false,输出"No, I am dead";

总的来讲,终结方法(finalizer)一般是不可预测的,也是很危险的,通常状况下是没必要要的。使用终结方法会致使行为不稳定、下降性能,以及可移植性问题

那么,若是类的对象中封装了资源(如文件或线程)确实须要终止,怎么作才能不用编写终结方法呢?

其实,咱们只须要提供一个显示的终止方法便可,比较典型的例子:InputStream、OutputStream和java.sql.Connection上的close方法。一般,显示的终止方法会与try-finally结构结合使用,以确保及时终止。代码结构以下:

1 FOO foo = new Foo();
2 try {
3     // do something
4 } finally {
5     foo.terminate(); // 如flie.close();
6 }
View Code

终结方法有什么好处呢?或者说,咱们何时该使用它?

  • 当对象的全部者忘记调用显示的终止方法时,终结方法能够充当“安全网”。迟一点释放关键资源总比永远不释放要好,FileInputStream、FileOutputStream等都有终结方法;
  • 清理本地对等体(native peer)。本地对等体是一个本地对象(native object),普通对象经过本地方法(native method)委托给一个本地对象由于本地对等体不是一个普通对象,因此GC不会知道它,当它的Java对等体被回收的时候,它不会被回收

由于对JVM和GC方面不太了解,因此在深刻理解和实践时比较费劲,Emmmmm....再接再砺吧!

回到顶部

转载请说明出处,have a good time! :D

相关文章
相关标签/搜索