Группа 10 назначена на курс Алгебра 1 страница
Ссылка на абстрактный суперклассmng инициализируется объектом подкласса, в котором реализованы все абстрактные методы суперкласса. С помощью этой ссылки могут вызываться реализованные методы абстрактного класса, если они не переопределены в подклассе.
Класс Object
На вершине иерархии классов находится класс Object, который является суперклассом для всех классов. Ссылочная переменная типа Object может указывать на объект любого другого класса, на любой массив, так как массивы реализуются как классы. В классе Objectопределен набор методов, который наследуется всеми классами:
protected Object clone() – создает и возвращает копию вызывающего объекта;
boolean equals(Object ob)–предназначен для переопределения
в подклассах с выполнением общих соглашений о сравнении содержимого двух объектов;
Class<? extends Object> getClass()–возвращает объект типа Class;
protected void finalize() –вызывается перед уничтожением объекта автоматическим сборщиком мусора (garbage collection);
int hashCode() –возвращает хэш-код объекта;
String toString() –возвращает представление объекта в виде строки.
Методы notify(), notifyAll()и wait() будут рассмотрены в главе «Потоки выполнения».
Если при создании класса предполагается проверка логической эквивалентности объектов, которая не выполнена в суперклассе, следует переопределить два метода: equals(Object ob) и hashCode(). Кроме того, переопределение этих методов необходимо, если логика приложения предусматривает использование элементов в коллекциях. Метод equals() при сравнении двух объектов возвращает истину, если содержимое объектов эквивалентно, и ложь – в противном случае. При переопределении метода equals() должны выполняться соглашения, предусмотренные спецификацией языка Java, а именно:
· рефлексивность – объект равен самому себе;
· симметричность – если x.equals(y)возвращает значение true, то и y.equals(x)всегда возвращает значение true;
· транзитивность – если метод equals()возвращает значение true при сравнении объектов x и y, а также y и z, то и при сравнении x и z будет возвращено значение true;
· непротиворечивость – при многократном вызове метода для двух не подвергшихся изменению за это время объектов возвращаемое значение всегда должно быть одинаковым;
· ненулевая ссылка при сравнении с литералом null всегда возвращает значениеfalse.
При создании информационных классов также рекомендуется переопределять методы hashCode() и toString(), чтобы адаптировать их действия для создаваемого типа.
Метод hashCode()переопределен, как правило, в каждом классе и возвращает число, являющееся уникальным идентификатором объекта, зависящим в большинстве случаев только от значения объекта. Его следует переопределять всегда, когда переопределен метод equals(). Метод hashCode() возвращает хэш-код объекта, вычисление которого управляется следующими соглашениями:
· во время работы приложения значение хэш-кода объекта не изменяется, если объект не был изменен;
· все одинаковые по содержанию объекты одного типа должны иметь одинаковые хэш-коды;
· различные по содержанию объекты одного типа могут иметь различные хэш-коды.
Один из способов создания правильного метода hashCode(), гарантирующий выполнение соглашений, приведен ниже, в примере # 10.
Метод toString() следует переопределять таким образом, чтобы кроме стандартной информации о пакете (опционально), в котором находится класс, и самого имени класса (опционально), он возвращал значения полей объекта, вызвавшего этот метод (то есть всю полезную информацию объекта), вместо хэш-кода, как это делается в классе Object. Метод toString()класса Object возвращает строку с описанием объекта в виде:
getClass().getName() + '@' + Integer.toHexString(hashCode())Метод вызывается автоматически, когда объект выводится методами println(), print() и некоторыми другими.
/* пример # 10 : переопределение методов equals(), hashCode, toString():
Student.java */
package chapt04;
public class Student {
private int id;
private String name;
private int age;
public Student(int id, String name, int age){
this.id = id;
this.name = name;
this.age = age;
}
public int getId() {
return id;
}
public String getName() {
return name;
}
public int getAge() {
return age;
}
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (obj instanceof Student){ //warning
Student temp = (Student) obj;
return this.id == temp.id &&
name.equals(temp.name) &&
this.age == temp.age;
} else
return false;
}
public int hashCode() {
return (int)(31 * id + age
+ ((name == null) ? 0 : name.hashCode()));
}
public String toString() {
return getClass().getName()+"@name" +name
+ " id:" + id + " age:" + age;
}
}
Выражение 31 * id + age гарантирует различные результаты вычислений при перемене местами значений полей, а именно если id=1 и age=2, то в результате будет получено 33, если значения поменять местами, то 63. Такой подход применяется при наличии у классов полей базовых типов.
Метод equals() переопределяется для класса Student таким образом, чтобы убедиться в том, что полученный объект является объектом типа Student или одним из его наследников, а также сравнить содержимое полей id, name и ageсоответственно у вызывающего метод объекта и объекта, передаваемого в качестве параметра.
/*пример # 11 : класс студента факультета: SubStudent.java */
package chapt04;
public class SubStudent extends Student {
private int idFaculty;
public SubStudent (intid, String n,inta,intidf){
super(id, n, a);
this.idFaculty = idf;
}
}
/*пример # 12 : демонстрация работы метода equals() при наследовании:
StudentEq.java */
package chapt04;
public class StudentEq {
public static void main(String[] args) {
Student p1 = new Student(71, "Петров", 19);
Student p2 = new Student(71, "Петров", 19);
SubStudent p3 =
new SubStudent(71, "Петров", 19, 5);
System.out.println(p1.equals(p2));
System.out.println(p1.equals(p3));
System.out.println(p3.equals(p1));
}
}
В результате выполнения данного кода будет выведено следующее:
True
True
True
Переопределенный таким образом метод equals() позволяет сравнивать объекты суперкласса с объектами подклассов, но только по тем полям, которые являются общими. При наследовании с добавлением новых полей в подкласс использование метода сравнения из суперкласса приводит к некорректным результатам.
Эту проблему можно легко разрешить, если вместо строки с пометкой //warning в метод equals() класса Student подставить непосредственную проверку на соответствие типов сравниваемых объектов с использованием объекта класса Class в виде:
if (getClass() == obj.getClass())
то в результате будет выведено:
True
False
False
В то же время такая реализация метода equals() будет возвращать истину при сравнении объектов класса SubStudent с одинаковыми значениями полей, унаследованных от класса Student.
Клонирование объектов
Объекты в методы передаются по ссылке, в результате чего в метод передается ссылка на объект, находящийся вне метода. Поэтому если в методе изменить значение поля объекта, то это изменение коснется исходного объекта. Во избежание такой ситуации для защиты внешнего объекта следует создать клон (копию) объекта в методе. Класс Object содержит protected-метод clone(), осуществляющий побитовое копирование объекта производного класса. Однако сначала необходимо переопределить метод clone() как public для обеспечения возможности вызова из другого пакета. В переопределенном методе следует вызвать базовую версию метода super.clone(), которая и выполняет собственно клонирование. Чтобы окончательно сделать объект клонируемым, класс должен реализовать интерфейс Cloneable. ИнтерфейсCloneable не содержит методов относится к помеченным (tagged) интерфейсам, а его реализация гарантирует, что метод clone()класса Objectвозвратит точную копию вызвавшего его объекта с воспроизведением значений всех его полей. В противном случае метод генерирует исключение CloneNotSupportedException. Следует отметить, что при использовании этого механизма объект создается без вызова конструктора. В языке C++ аналогичный механизм реализован с помощью конструктора копирования.
/* пример # 13 : класс, поддерживающий клонирование: Student.java */
package chapt04;
public classStudentimplementsCloneable {/*включение
интерфейса */
private intid = 71;
public intgetId() {
returnid;
}
public voidsetId(intvalue) {
id = value;
}
public Object clone() {//переопределение метода
try {
return super.clone();//вызов базового метода
} catch (CloneNotSupportedException e) {
throw new AssertionError("невозможно!");
}
}
}
/* пример # 14 : безопасная передача по ссылке: DemoSimpleClone.java */
package chapt04;
public class DemoSimpleClone {
private static void changeId(Student p) {
p = (Student) p.clone();//клонирование
p.setId(1000);
System.out.println("->id = " + p.getId());
}
public static voidmain(String[] args) {
Student ob =newStudent();
System.out.println("id = " + ob.getId());
changeId(ob);
System.out.println("id = " + ob.getId());
}
}
В результате будет выведено:
id = 71
->id = 1000
id = 71
Если закомментировать вызов метода clone(), то выведено будет следующее:
id = 71
->id = 1000
id = 1000
Такое решение эффективно только в случае, если поля клонируемого объекта представляют собой значения базовых типов и их оболочек или неизменяемых (immutable) объектных типов. Если же поле клонируемого типа является изменяемым объектным типом, то для корректного клонирования требуется другой подход. Причина заключается в том, что при создании копии поля оригинал и копия представляют собой ссылку на один и тот же объект. В этой ситуации следует также клонировать и объект поля класса.
/* пример # 15 : глубокое клонирование: Student.java */
package chapt04;
importjava.util.ArrayList;
public classStudentimplementsCloneable {
private int id = 71;
private ArrayList<Mark> lm = new ArrayList<Mark>();
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public ArrayList<Mark> getMark() {
return lm;
}
public void setMark(ArrayList<Mark> lm) {
this.lm = lm;
}
public Object clone() {
try {
Student copy =(Student)super.clone();
copy.lm = (ArrayList<Mark>)lm.clone();
return copy;
} catch (CloneNotSupportedException e) {
throw new AssertionError(
"отсутствует Cloneable!");
}
}
}
Такое клонирование возможно только в случае, если тип атрибута класса также реализует интерфейс Cloneable и переопределяет метод clone(). В противном случае вызов метода невозможен, так как он просто недоступен. Следовательно, если класс имеет суперкласс, то для реализации механизма клонирования текущего класса необходимо наличие корректной реализации такого механизма в суперклассе. При этом следует отказаться от использования объявлений final для полей объектных типов по причине невозможности изменения их значений при реализации клонирования.
“Сборка мусора” и освобождение ресурсов
Так как объекты создаются динамически с помощью операции new, а уничтожаются автоматически, то желательно знать механизм ликвидации объектов и способ освобождения памяти. Автоматическое освобождение памяти, занимаемой объектом, выполняется с помощью механизма “сборки мусора”. Когда никаких ссылок на объект не существует, то есть все ссылки на него вышли из области видимости программы, предполагается, что объект больше не нужен, и память, занятая объектом, может быть освобождена. “Сборка мусора” происходит нерегулярно во время выполнения программы. Форсировать “сборку мусора” невозможно, можно лишь “рекомендовать” ее выполнить вызовом метода System.gc()или Runtime.getRuntime().gc(), но виртуальная машина выполнит очистку памяти тогда, когда сама посчитает это удобным. Вызов метода System.runFinalization() приведет к запуску метода finalize() для объектов утративших все ссылки.
Иногда объекту нужно выполнять некоторые действия перед освобождением памяти. Например, освободить внешние ресурсы. Для обработки таких ситуаций могут применяться два способа: конструкция try-finallyи механизм finalization. Конструкция try-finallyявляется предпочтительной, абсолютно надежной и будет рассмотрена в девятой главе. Запуск механизма finalization определяется алгоритмом сборки мусора и до его непосредственного исполнения может пройти сколь угодно много времени. Из-за всего этого поведение метода
finalize()может повлиять на корректную работу программы, особенно при смене JVM. Если существует возможность освободить ресурсы или выполнить другие подобные действия без привлечения этого механизма, то лучше без него обойтись. Виртуальная машина вызывает этот метод всегда, когда она собирается уничтожить объект данного класса. Внутри метода finalize(), вызываемого непосредственно перед освобождением памяти, следует определить действия, которые должны быть выполнены до уничтожения объекта.
Метод finalize() имеет следующую сигнатуру:
protected voidfinalize(){
// код завершения
}
Ключевое слово protected запрещает доступ к finalize() коду, определенному вне этого класса. Метод finalize() вызывается только перед самой “сборкой мусора”, а не тогда, когда объект выходит из области видимости, то есть заранее невозможно определить, когда finalize() будет выполнен, и недоступный объект может занимать память довольно долго. В принципе этот метод может быть вообще не выполнен! Недопустимо в приложении доверять такому методу критические по времени действия по освобождению ресурсов.
/* пример # 16 : класс Manager с поддержкой finalization : Manager.java */
package chapt04;
class Manager {
private int id;
public Manager(int value) {
id = value;
}
protected void finalize() throws Throwable {
try{
//освобождение ресурсов
System.out.println("объект будет удален, id=" + id);
} finally {
super.finalize();
}
}
}
package chapt04;
public class FinalizeDemo {
public static void main(String[] args) {
Manager d1 = new Manager(1);
d1 = null;
Manager d2 = new Manager(2);
Object d3 = d2; //1
//Object d3 = new Manager (3); //2
d2 = d1;
System.gc();// просьба выполнить "сборку мусора"
}
}
В результате выполнения этого кода перед вызовом метода System.gc() без ссылки останется только один объект.
объект будет удален, id=1
Если закомментировать строку 1 и снять комментарий со строки 2, то перед выполнением gc()ссылку потеряют уже два объекта.
объект будет удален, id=1
объект будет удален, id=2
Если не вызвать явно метод finalize() суперкласса, то он не будет вызван автоматически. Еще одна опасность заключается в том, что если при выполнении данного метода возникнет исключительная ситуация, то она будет проигнорирована и приложение будет продолжать выполняться, что также представляет опасность для его корректной работы.
Задания к главе 4
Вариант A
Создать приложение, удовлетворяющее требованиям, приведенным в задании. Аргументировать принадлежность классу каждого создаваемого метода и корректно переопределить для каждого класса методы equals(), hashCode(),toString().
1. Создать объект класса Текст, используя класс Абзац. Методы: дополнить текст, вывести на консоль текст, заголовок текста.
2. Создать объект класса Автомобиль, используя класс Колесо. Методы: ехать, заправляться, менять колесо, вывести на консоль марку автомобиля.
3. Создать объект класса Самолет, используя класс Крыло. Методы: летать, задавать маршрут, вывести на консоль маршрут.
4. Создать объект класса Беларусь, используя класс Область. Методы: вывести на консоль столицу, количество областей, площадь, областные центры.
5. Создать объект класса Планета, используя класс Материк. Методы: вывести на консоль название материка, планеты, количество материков.
6. Создать объект класса Звездная система, используя классы Планета, Звезда, Луна. Методы: вывести на консоль количество планет в звездной системе, название звезды, добавление планеты в систему.
7. Создать объект класса Компьютер, используя классы Винчестер, Дисковод, ОЗУ. Методы: включить, выключить, проверить на вирусы, вывести на консоль размер винчестера.
8. Создать объект класса Квадрат, используя классы Точка, Отрезок.Методы: задание размеров, растяжение, сжатие, поворот, изменение цвета.
9. Создать объект класса Круг, используя классы Точка, Окружность.Методы: задание размеров, изменение радиуса, определение принадлежности точки данному кругу.
10. Создать объект класса Котёнок, используя классы Животное, Кошка.Методы: вывести на консоль имя, подать голос, рожать потомство (создавать себе подобных).
11. Создать объект класса Наседка, используя классы Птица, Кукушка.Методы: летать, петь, нести яйца, высиживать птенцов.
12. Создать объект класса Текстовый файл, используя класс Файл.Методы: создать, переименовать, вывести на консоль содержимое, дополнить, удалить.
13. Создать объект класса Одномерный массив, используя класс Массив.Методы: создать, вывести на консоль, выполнить операции (сложить, вычесть, перемножить).
14. Создать объект класса Простая дробь, используя класс Число.Методы: вывод на экран, сложение, вычитание, умножение, деление.
15. Создать объект класса Дом, используя классы Окно, Дверь.Методы: закрыть на ключ, вывести на консоль количество окон, дверей.
16. Создать объект класса Роза, используя классыЛепесток, Бутон.Методы: расцвести, завять, вывести на консоль цвет бутона.
17. Создать объект класса Дерево, используя классы Лист.Методы: зацвести, опасть листьям, покрыться инеем, пожелтеть листьям.
18. Создать объект класса Пианино, используя классКлавиша.Методы: настроить, играть на пианино, нажимать клавишу.
19. Создать объект класса Фотоальбом, используя класс Фотография.Методы: задать название фотографии, дополнить фотоальбом фотографией, вывести на консоль количество фотографий.
20. Создать объект класса Год, используя классыМесяц, День.Методы: задать дату, вывести на консоль день недели по заданной дате, рассчитать количество дней, месяцев в заданном временном промежутке.
21. Создать объект класса Сутки, используя классы Час, Минута.Методы: вывести на консоль текущее время, рассчитать время суток (утро, день, вечер, ночь).
22. Создать объект класса Птица, используя класс Крылья.Методы: летать, питаться.
23. Создать объект класса Тигр, используя классКогти.Методы: рычать, бежать, добывать пищу.
24. Создать объект класса Гитара, используя класс Струна.Методы: играть, натягивать струну.
Вариант В
Построить модель программной системы.
1. СистемаФакультатив. Преподаватель объявляет запись на Курс. Студент записывается на Курс, обучается и по окончании Преподаватель выставляет Оценку, которая сохраняется в Архиве. Студентов, Преподавателей и Курсов при обучении может быть несколько.
2. Система Платежи. Клиент имеет Счет в банке и Кредитную Карту (КК). Клиент может оплатить Заказ, сделать платеж на другой Счет, заблокировать КК и аннулировать Счет. Администратор может заблокировать КК за превышение кредита.
3. Система Больница. Пациенту назначается лечащий Врач. Врач может сделать назначение Пациенту (процедуры, лекарства, операции). Медсестра или другой Врач выполняют назначение. Пациент может быть выписан из Больницы по окончании лечения, при нарушении режима или при иных обстоятельствах.
4. Система Вступительные экзамены. Абитуриент регистрируется на Факультет, сдает Экзамены. Преподаватель выставляет Оценку. Система подсчитывает средний балл и определяет Абитуриентов, зачисленных в учебное заведение.
5. Система Библиотека. Читатель оформляет Заказ на Книгу. Система осуществляет поиск в Каталоге. Библиотекарь выдает Читателю Книгу на абонемент или в читальный зал. При невозвращении Книги Читателем он может быть занесен Администратором в «черный список».
6. Система Конструкторское бюро. Заказчик представляет Техническое Задание (ТЗ) на проектирование многоэтажного Дома. Конструктор регистрирует ТЗ, определяет стоимость проектирования и строительства, выставляет Заказчику Счет за проектирование и создает Бригаду Конструкторов для выполнения Проекта.
7. Система Телефонная станция. Абонент оплачивает Счет за разговоры и Услуги, может попросить Администратора сменить номер и отказаться от услуг. Администратор изменяет номер, Услуги и временно отключает Абонента за неуплату.
8. Система Автобаза. Диспетчер распределяет заявки на Рейсымежду Водителями и назначает для этого Автомобиль. Водитель может сделать заявку на ремонт. Диспетчерможет отстранить Водителя от работы. Водитель делает отметку о выполнении Рейса и состоянии Автомобиля.
9. Система Интернет-магазин. Администратор добавляет информацию о Товаре. Клиент делает и оплачивает Заказ на Товары. Администратор регистрирует Продажу и может занести неплательщиков в «черный список».
10. Система Железнодорожная касса. Пассажир делает Заявку на станцию назначения, время и дату поездки. Система регистрирует Заявку и осуществляет поиск подходящего Поезда. Пассажир делает выбор Поезда и получает Счет на оплату. Администратор вводит номера Поездов, промежуточные и конечные станции, цены.
11. Система Городской транспорт. На Маршрут назначаются Автобус, Троллейбус или Трамвай. Транспортные средства должны двигаться с определенным для каждого Маршрута интервалом. При поломке на Маршрут должен выходить резервный транспорт или увеличиваться интервал движения.