Философия Java

         

Наследование и finalize( )


Когда Вы используете композицию для создания нового класса, Вы никогда не беспокоитесь о завершении объекта этого класса. Каждый участник - независимый объект и поэтому сборщик мусора не обращают внимание на то, что это член вашего класса. С наследованием Вы должны переопределять finalize( ) в дочерних классах, если у вас есть какие либо специальные очистки, которые должны произойти как часть сбора мусора. Когда Вы переопределяете finalize( ) в наследуемом классе, то важно не забыть вызвать finalize( ) базового класса, поскольку в противном случае финализация базового класса не произойдет. Следующий пример доказывает это утверждение:

//: c07:Frog.java

// Проверка завершения с наследованием.

class DoBaseFinalization { public static boolean flag = false; }

class Characteristic { String s; Characteristic(String c) { s = c; System.out.println( "Creating Characteristic " + s); } protected void finalize() { System.out.println( "finalizing Characteristic " + s); } }

class LivingCreature { Characteristic p = new Characteristic("is alive"); LivingCreature() { System.out.println("LivingCreature()"); } protected void finalize() throws Throwable { System.out.println( "LivingCreature finalize"); // Вызов версии базового класса!

if(DoBaseFinalization.flag) super.finalize(); } }

class Animal extends LivingCreature { Characteristic p = new Characteristic("has heart"); Animal() { System.out.println("Animal()"); } protected void finalize() throws Throwable { System.out.println("Animal finalize"); if(DoBaseFinalization.flag) super.finalize(); } }

class Amphibian extends Animal { Characteristic p = new Characteristic("can live in water"); Amphibian() { System.out.println("Amphibian()"); } protected void finalize() throws Throwable { System.out.println("Amphibian finalize"); if(DoBaseFinalization.flag) super.finalize(); } }

public class Frog extends Amphibian { Frog() { System.out.println("Frog()"); } protected void finalize() throws Throwable { System.out.println("Frog finalize"); if(DoBaseFinalization.flag) super.finalize(); } public static void main(String[] args) { if(args.length != 0 && args[0].equals("finalize")) DoBaseFinalization.flag = true; else


System.out.println("Not finalizing bases"); new Frog(); // Тотчас становится мусором

System.out.println("Bye!"); // Принудительный вызов завершения и очистки:

System.gc(); } } ///:~

Класс DoBaseFinalization

просто содержит флаг, который показывает для каждого класса в иерархии вызывать ли super.finalize( ). Этот флаг устанавливается как аргумент командной строки, так что Вы можете посмотреть поведение с и без вызовов завершения базового класса.

Каждый класс в иерархии так же содержит объект класса Characteristic. Вы увидите, что не обращая внимание на вызов завершителя базового класса объект Characteristic всегда завершается.

Каждое переопределение finalize( ) должно иметь доступ к protected членам класса, поскольку метод finalize( ) в классе Object является protected и компилятор не позволит вам уменьшить права доступа во время наследования. ("Friendly" менее "достижимы" чем protected.)

В Frog.main( ), флаг DoBaseFinalization настраивается и создается единственный объект Frog. Помните, что сборщик мусора и индивидуальное завершение, могут не произойти для отдельного объекта, поэтому, что бы вызвать их насильно вызывается System.gc( ) и оттуда уже завершение. Без завершения базовых классов вывод такой:

Not finalizing bases Creating Characteristic is alive LivingCreature() Creating Characteristic has heart Animal() Creating Characteristic can live in water Amphibian() Frog() Bye! Frog finalize finalizing Characteristic is alive finalizing Characteristic has heart finalizing Characteristic can live in water

Вы можете видеть, что не были вызваны завершители для базовых классов Frog (объекты класса были завершены, как Вы и ожидали). Но если Вы добавите аргумент "finalize" в командную строку, Вы получите:

Creating Characteristic is alive LivingCreature() Creating Characteristic has heart Animal() Creating Characteristic can live in water Amphibian() Frog() bye! Frog finalize Amphibian finalize Animal finalize LivingCreature finalize finalizing Characteristic is alive finalizing Characteristic has heart finalizing Characteristic can live in water

Поскольку элементы объектов завершены в том же порядке, в каком они были созданы, то технически порядок финализации объектов не определен. С базовыми же классами, Вы можете осуществлять контроль над порядком завершения. Следуя форме, которая используется в C++ для деструкторов, Вы должны осуществить завершение дочернего класса раньше, чем завершение базового класса. Потому что финализация дочернего класса может вызвать некоторые методы в базовом классе, которые требуют, что бы компоненты этого базового классы были все еще активны, поэтому Вы не должны уничтожать их принудительно.


Содержание раздела