Чтение онлайн

на главную - закладки

Жанры

Философия Java3

Эккель Брюс

Шрифт:

print ("Amphibian (Г);

}

protected void disposeO {

print ("disposeO в Amphibian "); t.disposeO; p.disposeO; super.disposeO,

зависят друг от друга. Для полей это означает порядок, обратный последовательности объявления полей в классе (инициализация соответствует порядку объявления). В базовых классах сначала следует выполнять финализацию для производного класса, а затем — для базового класса. Это объясняется тем, что завершающий метод производного класса может вызывать некоторые методы базового класса, для которых необходимы действительные компоненты базового класса. Из результатов работы программы видно, что все части объекта Frog будут финализованы в порядке, противоположном очередности их создания.

Также обратите внимание на то, что в описанном примере объект Frog является «владельцем» встроенных объектов. Он создает их, определяет продолжительность их существования (до тех пор, пока существует Frog) и знает, когда вызывать dispose для встроенных объектов. Но если встроенный объект используется совместно с другими объектами, ситуация усложняется и вы уже не можете просто вызвать dispose. В таких случаях для отслеживания количества объектов, работающих со встроенным объектом, приходится использовать подсчет ссылок. Вот как это выглядит:

// polymorphism/ReferenceCounting.java

11 Уничтожение совместно используемых встроенных объектов

import static net mindview.util.Print.*;

class Shared {

private int refcount = 0; private static long counter = 0, private final long id = counter++, public SharedO {

print("Создаем " + this);

}

public void addRefO { refcount++; } protected void disposeO { if(--refcount == 0)

printODisposing " + this),

}

public String toStringO { return "Shared " + id; }

}

class Composing {

private Shared shared; private static long counter = 0. private final long id = counter++, public Composing(Shared shared) { print("Создаем " + this); this.shared = shared, this shared addRefO.

}

protected void disposeO {

printC'disposing " + this), shared disposeO,

}

public String toStringO { return "Composing " + id; }

}

public class ReferenceCounting {

public static void main(String[] args) {

Shared shared = new SharedO;

Composing[] composing = { new Composing(shared).

new Composing(shared), new Composing(shared), new Composing(shared), new Composing(shared) }; for(Composing с • composing) с disposeO.

}

} /* Output: Создаем Shared 0 Создаем Composing 0 Создаем Composing 1 Создаем Composing 2 Создаем Composing 3 Создаем Composing 4 уничтожаем Composing 0 уничтожаем Composing 1 уничтожаем Composing 2 уничтожаем Composing 3 уничтожаем Composing 4 уничтожаем Shared 0 *///:-

В переменной static long counter хранится количество созданных экземпляров Shared. Для счетчика выбран тип long вместо int для того, чтобы предотвратить переполнение (это всего лишь хороший стиль программирования; в рассматриваемых примерах переполнение вряд ли возможно). Поле id объявлено со спецификатором final, поскольку его значение остается постоянным на протяжении жизненного цикла объекта

Присоединяя к классу общий объект, необходимо вызвать addRef, но метод dispose будет следить за состоянием счетчика ссылок и сам решит, когда нужно выполнить завершающие действия. Подсчет ссылок требует дополнительных усилий со стороны программиста, но при совместном использовании объектов, требующих завершения, у вас нет особого выбора.

Поведение полиморфных методов при вызове из конструкторов

В иерархиях конструкторов возникает интересный вопрос. Что происходит, если вызвать в конструкторе динамически связываемый метод конструируемого объекта?

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

Но ничего подобного. При вызове динамически связываемого метода в конструкторе используется переопределенное описание этого метода. Однако последствия такого вызова могут быть весьма неожиданными, и здесь могут крыться некоторые коварные ошибки.

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

// polymorphism/PolyConstructors java // Конструкторы и полиморфизм дают не тот // результат, который можно было бы ожидать import static net mindview util Print *.

class Glyph {

void drawO { print("Glyph drawO"), } GlyphO {

printCGlyphO перед вызовом drawO");

drawO.

print ("GlyphO после вызова drawO").

class RoundGlyph extends Glyph { private int radius = 1; RoundGlyph(int r) { radius = r.

print("RoundGlyph RoundGlyph. radius = " + radius);

}

void drawO {

print ("RoundGlyph. drawO, radius = " + radius);

public class PolyConstructors {

public static void main(String[] args) { new RoundGlyph(5);

}

} /* Output-

GlyphO перед вызовом drawO RoundGlyph drawO, radius = 0 GlyphO после вызова drawO RoundGlyph RoundGlyphO, radius = 5 *///:-

Метод Glyph.draw изначально предназначен для переопределения в производных классах, что и происходит в RoundGlyph. Но конструктор Glyph вызывает этот метод, и в результате это приводит к вызову метода RoundGlyph.draw, что вроде бы и предполагалось. Однако из результатов работы программы видно — когда конструктор класса Glyph вызывает метод draw, переменной radius еще не присвоено даже значение по умолчанию 1. Переменная равна 0. В итоге класс может не выполнить свою задачу, а вам придется долго всматриваться в код программы, чтобы определить причину неверного результата.

Поделиться:
Популярные книги

Чужое наследие

Кораблев Родион
3. Другая сторона
Фантастика:
боевая фантастика
8.47
рейтинг книги
Чужое наследие

Наследник, скрывающий свой Род

Тарс Элиан
2. Десять Принцев Российской Империи
Фантастика:
попаданцы
альтернативная история
аниме
5.00
рейтинг книги
Наследник, скрывающий свой Род

Я снова князь. Книга XXIII

Дрейк Сириус
23. Дорогой барон!
Фантастика:
юмористическое фэнтези
аниме
попаданцы
5.00
рейтинг книги
Я снова князь. Книга XXIII

Локки 5. Потомок бога

Решетов Евгений Валерьевич
5. Локки
Фантастика:
юмористическое фэнтези
аниме
фэнтези
5.00
рейтинг книги
Локки 5. Потомок бога

Глубокий космос

Вайс Александр
9. Фронтир
Фантастика:
боевая фантастика
космическая фантастика
космоопера
5.00
рейтинг книги
Глубокий космос

Надуй щеки!

Вишневский Сергей Викторович
1. Чеболь за партой
Фантастика:
попаданцы
дорама
5.00
рейтинг книги
Надуй щеки!

Московское золото и нежная попа комсомолки. Часть Пятая

Хренов Алексей
5. Летчик Леха
Фантастика:
попаданцы
5.00
рейтинг книги
Московское золото и нежная попа комсомолки. Часть Пятая

Дитя прибоя

Трофимов Ерофей
Дитя прибоя
Фантастика:
боевая фантастика
попаданцы
фэнтези
5.00
рейтинг книги
Дитя прибоя

Княжна попаданка. Последняя из рода

Семина Дия
1. Княжна попаданка. Магическая управа
Фантастика:
попаданцы
альтернативная история
историческое фэнтези
аниме
сказочная фантастика
фэнтези
5.00
рейтинг книги
Княжна попаданка. Последняя из рода

Печать мастера

Лисина Александра
6. Гибрид
Фантастика:
попаданцы
технофэнтези
аниме
фэнтези
6.00
рейтинг книги
Печать мастера

Офицер Красной Армии

Поселягин Владимир Геннадьевич
2. Командир Красной Армии
Фантастика:
попаданцы
8.51
рейтинг книги
Офицер Красной Армии

Локки 6. Потомок бога

Решетов Евгений Валерьевич
6. Локки
Фантастика:
аниме
фэнтези
5.00
рейтинг книги
Локки 6. Потомок бога

На границе империй. Том 10. Часть 4

INDIGO
Вселенная EVE Online
Фантастика:
боевая фантастика
космическая фантастика
попаданцы
5.00
рейтинг книги
На границе империй. Том 10. Часть 4

Барон запрещает правила

Ренгач Евгений
9. Закон сильного
Фантастика:
аниме
фэнтези
попаданцы
5.00
рейтинг книги
Барон запрещает правила