Часто требуется использовать две технологии — наследование и композицию — вместе. В следующем примере показано, как одновременно применять наследование и композицию для создания более сложного класса и выполнения необходимой инициализации конструкторами:
//: PlaceSetting.java
// Объединение композиции и наследования
class Plate {
Plate(int i) {
System.out.println("Конструктор Plate");
}
}
class DinnerPlate extends Plate {
DinnerPlate(int i) {
super(i);
System.out.println(
"Конструктор DinnerPlate");
}
}
class Utensil {
Utensil(int i) {
System.out.println("Конструктор Utensil");
}
}
class Spoon extends Utensil {
Spoon(int i) {
super(i);
System.out.println("Конструктор Spoon");
}
}
class Fork extends Utensil {
Fork(int i) {
super(i);
System.out.println("Конструктор Fork");
}
}
class Knife extends Utensil {
Knife(int i) {
super(i);
System.out.println("Конструктор Knife");
}
}
// Культурный способ выполнения чего-либо:
class Custom {
Custom(int i) {
System.out.println("Конструктор Custom");
}
}
public class PlaceSetting extends Custom {
Spoon sp;
Fork frk;
Knife kn;
DinnerPlate pl;
PlaceSetting(int i) {
super(i + 1);
sp = new Spoon(i + 2);
frk = new Fork(i + 3);
kn = new Knife(i + 4);
pl = new DinnerPlate(i + 5);
System.out.println(
"Конструктор PlaceSetting");
}
public static void main(String[] args) {
PlaceSetting x = new PlaceSetting(9);
}
} ///:~
Хотя компилятор заставляет нас инициализировать базовый класс и требует делать это в самом начале конструктора, он не контролирует корректность инициализации членов объекта. Поэтому следует особенно внимательно относиться к этому вопросу.## 6.3.1 Уверенность в правильной очистке
Java не имеет такого понятия, как "деструктор" в C++. В C++ при удалении (очистке) объекта автоматически вызывается метод деструктора. Его отсутствие может быть связано с тем, что в Java достаточно просто забыть об объекте, не выполняя явной очистки. Garbage collector автоматически освобождает память, когда это необходимо. Сборщик мусора обычно хорошо работает в большинстве случаев, но в некоторых ситуациях наши классы могут выполнять какие-то действия в течение своего жизненного цикла, требующие явной очистки. Как было указано в главе 4, мы не знаем, когда сборщик мусора будет активирован или вызван. Поэтому, если требуется очистка каких-либо данных для класса, следует написать специальный метод, который будет специально предназначен для этой задачи. В то же время важно сообщить программистам-клиентам о необходимости использования этого метода. За всем этим, как подробно объясняется в главе 9 (контроль ошибок), следует поместить такой код очистки в блок finally
, чтобы защититься от любых возможных исключений.```markdown
Сборщик мусора обычно хорошо работает в большинстве случаев, но в некоторых ситуациях наши классы могут выполнять какие-то действия в течение своего жизненного цикла, требующие явной очистки. Как было указано в главе 4, мы не знаем, когда сборщик мусора будет активирован или вызван. Поэтому если требуется очистка каких-либо данных для класса, следует написать специальный метод, который будет специально предназначен для этой задачи. В то же время важно сообщить программистам-клиентам о необходимости использования этого метода. За всем этим, как подробно объясняется в главе 9 (контроль ошибок), следует поместить такой код очистки в блок finally
, чтобы защититься от любых возможных исключений.
Рассмотрим пример системы компьютерной графики, которая может отображать фигуры на экране:
```java
//: CADSystem.java
// Обеспечение правильной очистки
import java.util.*;
class Shape {
Shape(int i) {
System.out.println("Конструктор Shape");
}
void cleanup() {
System.out.println("Очистка Shape");
}
}
class Circle extends Shape {
Circle(int i) {
super(i);
System.out.println("Отрисовка круга");
}
void cleanup() {
System.out.println("Удаление круга");
super.cleanup();
}
}
class Triangle extends Shape {
Triangle(int i) {
super(i);
System.out.println("Отрисовка треугольника");
}
void cleanup() {
System.out.println("Удаление треугольника");
super.cleanup();
}
}
class Line extends Shape {
private int start, end;
Line(int start, int end) {
super(start);
this.start = start;
this.end = end;
System.out.println("Отрисовка линии: "
+ start + ", " + end);
}
void cleanup() {
System.out.println("Удаление линии: "
+ start + ", " + end);
super.cleanup();
}
}
public class CADSystem extends Shape {
private Circle c;
private Triangle t;
private Line[] lines = new Line[10];
CADSystem(int i) {
super(i + 1);
for(int j = 0; j < 10; j++)
lines[j] = new Line(j, j * j);
c = new Circle(1);
t = new Triangle(1);
System.out.println("Объединённый конструктор");
}
void cleanup() {
System.out.println("CADSystem.cleanup()");
t.cleanup();
c.cleanup();
for(int i = 0; i < lines.length; i++)
lines[i].cleanup();
super.cleanup();
}
public static void main(String[] args) {
CADSystem x = new CADSystem(47);
try {
// Код и обработка исключений...
} finally {
x.cleanup();
}
}
} ///:~
```В этой системе все объекты являются некоторым типом `Shape` (геометрической формы). `Shape` сам по себе является `Object` (объектом), так как он явно наследуется от базового класса. Каждый класс переопределяет метод `cleanup()` класса `Shape`, а также вызывает его версию в базовом классе с помощью ключевого слова `super`. Хотя все методы могут выполнять задачи очистки во время существования объекта, каждый конкретный тип `Shape` — `Circle` (круг), `Triangle` (треугольник) и `Line` (линия) имеют свои конструкторы для выполнения задачи отрисовки (`draw`). У каждого класса есть свой метод `cleanup()`, который используется для восстановления состояния до создания объекта, если это возможно. В методе `main()`, можно заметить два новых ключевых слова: `try` и `finally`. Полное объяснение этих ключевых слов будет дано в главе 9. Ключевое слово `try` указывает, что следующий за ним блок (ограниченный фигурными скобками) является "опасной зоной". Это значит, что он получает особое обращение. Одним из таких обращений является то, что код, расположенный после ключевого слова `finally`, обязательно выполнится — вне зависимости от того, произошло ли что-либо в блоке `try` (через механизм управления исключениями, блок `try` может иметь множество необычных применений). В данном случае, ключевое слово `finally` означает "всегда вызывать `cleanup()` для `x`, независимо от того, что происходит". Эти ключевые слова будут подробно объяснены в главе 9.При реализации своего метода очистки, важно обратить внимание на порядок вызова методов очистки базового класса и членских объектов — если один подкласс зависит от другого. Обычно следует придерживаться формы, аналогичной тому, как компилятор C++ обращается со своими "деструкторами": сначала выполняются все специальные операции, связанные с классом (что может потребовать видимости элементов базового класса), а затем вызывается метод очистки базового класса, как показано здесь.
Многие ситуации могут не требовать явной очистки; достаточно просто позволить сборщику мусора выполнять свои обязанности. Однако когда требуется явная очистка, это должно делаться с особым вниманием и всесторонним рассмотрением.
### 6.3.2 Прятание имен
Только программисты C++ могут удивиться прятанию имен, так как принцип работы отличается от C++. Если базовый класс Java имеет метод, имя которого используется несколько раз ("перегружено"), переопределение этого имени в производном классе не скроет ни одной версии базового класса. Таким образом, перегрузка будет работать независимо от того, где метод был определён:```java
//: Hide.java
// Переопределение имени метода базового класса
// в производном классе не скрывает версий
// базового класса
Как будет объяснено в следующей главе, редко когда переопределяемые методы имеют одинаковые сигнатуры и типы возвращаемых значений с базовым классом, так как это может вызвать путаницу (это одна из причин, почему C++ запрещает такое поведение, что позволяет избежать некоторых ненужных ошибок).```markdown class Homer { char doh(char c) { System.out.println("doh(char)"); return 'd'; } float doh(float f) { System.out.println("doh(float)"); return 1.0f; } }
class Milhouse {}
class Bart extends Homer { void doh(Milhouse m) {} }
class Hide { public static void main(String[] args) { Bart b = new Bart(); b.doh(1); // doh(float) используется b.doh('x'); b.doh(1.0f); b.doh(new Milhouse()); } } ///:~
Вы можете оставить комментарий после Вход в систему
Неприемлемый контент может быть отображен здесь и не будет показан на странице. Вы можете проверить и изменить его с помощью соответствующей функции редактирования.
Если вы подтверждаете, что содержание не содержит непристойной лексики/перенаправления на рекламу/насилия/вульгарной порнографии/нарушений/пиратства/ложного/незначительного или незаконного контента, связанного с национальными законами и предписаниями, вы можете нажать «Отправить» для подачи апелляции, и мы обработаем ее как можно скорее.
Опубликовать ( 0 )