Java:终结器防卫者,顺便看一下 C# 如何做的。

背景

多数情况我们不需要重写 finalize 方法,只有当我们需要持有未托管资源的时候才需要,而此时重写 finalize 方法,只是作为一个“安全网”,不能作为常规的资源释放模式,必须提供显式的释放方法,如:close。

如果某个类型重写了 finalize 方法,但是这个类型是可以继承的,这就要求所有的子类如果也重写了 finalize,就必须要调用父类的 finalize 方法,我们有三种策略:

  1. 按照约定。
  2. 终结器防卫者。
  3. 模板方法模式。

本文就介绍第 2 种模式,此模式是昨天看《Effective Java 第二版》时学习的,本文后面会介绍 C# 是如何做的。

Java版:终结器防卫者

测试代码

注意看注释,我就不多说了。

 1 public class Program {
 2 
 3     public static void main(String[] args) throws InterruptedException {
 4         {
 5             new CustomResourceOwner().doSomeThing();
 6         }
 7 
 8         System.gc();
 9 
10         System.out.println("程序结束!");
11     }
12 }
13 
14  class ResourceOwnerBase {
15     // 可以将父类中 finalize 的代码放到守卫者里,一定会被调用的。
16     @SuppressWarnings("unused")
17     private final Object finalizeGuarder = new Object() {
18         @Override
19         public void finalize() {
20             System.out.println("在资源守卫者中销毁父类!");
21         }
22     };
23 
24     // 子类可能故意不调用父类!
25     @Override
26     public void finalize() {
27         System.out.println("销毁父类!");
28     }
29 }
30 
31  final class CustomResourceOwner extends ResourceOwnerBase {
32     @Override
33     public void finalize() {
34         System.out.println("销毁子类!");
35         
36         // 故意不调用父类!
37         // super.finalize();
38     }
39 
40     public void doSomeThing() {
41         System.out.println("随便做点工作!");
42     }
43 }

输出结果

1 随便做点工作!
2 程序结束!
3 在资源守卫者中销毁父类!
4 销毁子类!

说明

因为终结器防卫者只被资源拥有者持有,当资源拥有者变为垃圾的时候,终结器防卫者也会变为垃圾。

C#版:“终结器防卫者”

测试代码

 1 using System;
 2 using System.Collections.Generic;
 3 using System.Linq;
 4 using System.Text;
 5 using System.Threading.Tasks;
 6 using System.IO;
 7 
 8 namespace DisposeStudy
 9 {
10     class Program
11     {
12         static void Main()
13         {
14             {
15                 var res = new CustomResourceOwner(IntPtr.Zero);
16                 res.DoSomeThing();
17             }
18         }
19     }
20 
21     class ResourceOwnerBase : IDisposable
22     {
23         private bool _disposed;
24         private readonly FileStream _fileStream;
25         private IntPtr _handle;
26 
27         protected ResourceOwnerBase(IntPtr handle)
28         {
29             _handle = handle;
30             _fileStream = File.OpenRead(@"E:\Coding\HappyStudy\DisposeStudy\DisposeStudy\Program.cs");
31         }
32 
33         protected bool Disposed
34         {
35             get { return _disposed; }
36         }
37 
38         public void Dispose()
39         {
40             Dispose(true);
41 
42             GC.SuppressFinalize(this);
43         }
44 
45         protected virtual void Dispose(bool disposing)
46         {
47             if (Disposed)
48             {
49                 if (disposing)
50                 {
51                     _fileStream.Dispose();
52                 }
53 
54                 CloseHandle(_handle);
55                 _handle = IntPtr.Zero;
56 
57                 _disposed = true;
58             }
59         }
60 
61         ~ResourceOwnerBase()
62         {
63             Console.WriteLine("父类析构方法!");
64             Dispose(false);
65         }
66 
67         [System.Runtime.InteropServices.DllImport("Kernel32")]
68         private extern static Boolean CloseHandle(IntPtr handle);
69     }
70 
71     sealed class CustomResourceOwner : ResourceOwnerBase
72     {
73         public CustomResourceOwner(IntPtr handle)
74             : base(handle)
75         {
76         }
77 
78         public void DoSomeThing()
79         {
80             if (Disposed)
81             {
82                 throw new ObjectDisposedException("资源已经消耗了,不能执行此操作!");
83             }
84 
85             Console.WriteLine("随便做点工作!");
86         }
87 
88         ~CustomResourceOwner()
89         {
90             Console.WriteLine("子类析构方法!");
91         }
92     }
93 }

输出结果

说明

让我们看看编译器帮我们做了什么工作:

看完大家就明白了,C#在编译器层面保证了子类的终结器一定会调用父类的终结器。

备注

同时学习 C# 和 Java 是一件挺快乐的事情。

 

posted on 2013-10-09 09:09  幸福框架  阅读(2051)  评论(1编辑  收藏  举报

导航

我要啦免费统计