《TIJ4》多态 E13 继承与清理中的引用计数问题
引言
通常,我们无需考虑对象的清理问题,垃圾回收器会代劳。然而在特定情况下,需要手动控制对象的清理,这时,我们必须小心翼翼为类创建dispose()方法。由于继承的原因,如果子类中含有自己特殊的清理,那么子类就必须重写dispose()方法,并在执行完自己特殊的清理后调用父类的dispose()方法,否则,基类就不会清理。
一般的组合和继承中,对象会创建组合中的成员对象,并且在dispose()时释放这些对象。然而,如果这些成员对象中存在着被其他一个或多个对象所共享时,问题就复杂了,这时你不能简单得直接dispose(),你要考虑这些成员对象此时没有被其他对象共享,才能释放。下面是相关的代码:
package polymorphism;
class Shared {
private int refcount = 0;
private static long counter = 0;
private final long id = counter++;
public Shared() {
System.out.println("Creating " + this);
}
public void addRef() {
refcount++;
}
protected void dispose() {
if (--refcount == 0) {
System.out.println("Disposing " + this);
}
}
@Override
public String toString() {
return "Shared " + id;
}
}
class Composing {
private Shared shared;
private static long counter = 0;
private final long id = counter++;
public Composing(Shared shared) {
System.out.println("Creating " + this);
this.shared = shared;
this.shared.addRef(); //通过某个 Shared 对象 构造一个 Composing 对象时,那么该shared 引用数加1
}
protected void dispose() {
System.out.println("disposing " + this);
shared.dispose();
}
@Override
public String toString() {
return "Composing " + id;
}
}
public class ReferenceCounting {
//存在被共享的成员对象时,共享对象使用引用计数方式进行dispose
public static void main(String[] args) {
Shared shared = new Shared(); //一个Shared对象被5个Composing对象共享
Composing[] composings = {new Composing(shared), new Composing(shared), new Composing(shared), new Composing(shared), new Composing(shared)};
for (Composing c : composings) {
c.dispose();
}
}
}
/*输出:
Creating Shared 0
Creating Composing 0
Creating Composing 1
Creating Composing 2
Creating Composing 3
Creating Composing 4
disposing Composing 0
disposing Composing 1
disposing Composing 2
disposing Composing 3
disposing Composing 4
Disposing Shared 0
*/
注意,这里coutner是Shared的示例的数量,还可以为id提供数值,类型是long可以防止溢出,id是final的,因为我们不希望它的值在对象生命周期中被改变。
在将一个Shared对象附着在Composing上时,一定要调用addRef()来增加引用数量。
Shared对象在dispose()时将根据引用数量来决定是否清理。
问题
在Shared类中添加一个finalize()方法,用来校验终止条件。
添加的finalize()方法如下:
protected void finalize(){
if(refcount != 0){
System.out.println("Error: object is not properly cleaned-up!");
}
}
很明显,终止条件就是refcount为0时。
而在main()方法最后添加如下代码:
new Composing(new Shared());
System.gc();
System.runFinalization();
新添加的这个Composing()对象没有进行释放(dispose),因此这个Shared()对象在被清理时不满足终结条件“引用数为0”。
注:不能这样声明:
Composing c = new Composing(new Shared());
或者
Shared s = new Shared();
new Composing(s);
这两种方式Shared对象会有引用指向,不会被垃圾回收。