目录

背景Java版:终结器防卫者C#版:“终结器防卫者”备注

背景返回目录

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

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

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

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

Java版:终结器防卫者返回目录

测试代码

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

Java:终结器-LMLPHP
 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 }
Java:终结器-LMLPHP

输出结果

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

说明

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

C#版:“终结器防卫者”返回目录

测试代码

Java:终结器-LMLPHP
 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 }
Java:终结器-LMLPHP

输出结果

Java:终结器-LMLPHP

说明

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

Java:终结器-LMLPHP

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

备注返回目录

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

Java:终结器防卫者

05-07 15:15