Объектная модель в Java

advertisement
Объектная модель в Java
Часть 2. Интерфейсы и
внутренние классы, параметры
метода
Кафедра ЮНЕСКО по НИТ
Параметры метода




Вызов по значению (call by value)– метод
получает значение, переданное ему вызывающим
модулем.
Вызов по ссылке (call by reference) – метод
получает от вызывающего модуля адрес
переменной.
Метод может модифицировать значение,
хранящееся в переменной, переданной по ссылке,
но не может изменять значение переменной,
переданной по значению.
В Java всегда используется только вызов по
значению. Это значит, что метод получает копии
значений всех параметров.
Кафедра ЮНЕСКО по НИТ
Пример метода для основного
типа данных
// метод утраивает значение аргумента
public static void tripleValue (double x){
x = 3*x;
}
// вызовем этот метод
double percent = 10;
tripleValue(percent);
1.
2.
3.
Переменная x инициализируется копией
значения параметра percent=10.
Значение переменной x утраивается – теперь
оно равно 30. Значение переменной percent
остается равным 10.
Метод завершает свою работу, и параметр x
больше не используется.
Кафедра ЮНЕСКО по НИТ
Пример метода для объектного
типа данных
// метод утраивает значение поля у объекта
public static void tripleSalary (Employee x){
x.raiseSalary(200) ;
}
// вызовем этот метод
harry = new Employee(…);
tripleSalary(harry);
1.
2.
3.
Переменная x инициализируется копией значения
переменной harry, т.е. ссылкой на объект.
Метод raiseSalary применяется к этой ссылке на объект.
Объект класса Employee, к которому относятся ссылки x и
harry, увеличивает зарплату сотрудников на 200%.
Метод завершает свою работу, и параметр x больше не
используется. Объектная переменная harry продолжает
ссылаться на объект, в котором зарплата увеличена втрое.
Кафедра ЮНЕСКО по НИТ
Метод, выполняющий обмен 2
объектов
public static void swap (Employee x, Employee y){
Employee temp=x;
x=y;
y=temp;}
// вызовем этот метод
Employee a = new Employee(“Алиса”…);
Employee b = new Employee(“Боб”…);
swap(a,b);
Относится ли ссылка
a к Бобу, а ссылка b –
к Алисе?
Изначально, ссылка x
относится к Алисе,
y – к Бобу. После
окончания работы
метода ссылка x –
относится к Бобу.
Все усилия по созданию метода были напрасны!



Метод не может изменять параметры основных типов.
Метод может изменять состояние объекта, передаваемого
как параметр.
Метод не может изменять ссылки и переназначать их на
новые объекты.
Кафедра ЮНЕСКО по НИТ
Интерфейсы
Интерфейс – множество требований,
предъявляемых к классу.
 Объявление интерфейсов похоже на
упрощенное объявление классов.
 От интерфейсов нельзя порождать
объектов (из-за абстрактности
методов), но можно их
реализовывать.

Кафедра ЮНЕСКО по НИТ
Объявление интерфейсов
Объявление интерфейса может начинается с модификатора public (по
умолч. default ). Ключевое слово abstract не требуется, т.к. все
интерфейсы абстрактные. Затем слово interface и имя интерфейса,
после которого может стоять extends и список интерфейсов
наследования.
public interface Drawble extends Colorable, Resizable {тело интерфейса}
Все поля интерфейса должны быть public final static, все методы
интерфейса public abstract. Данные модификаторы рекомендуется не
писать в программе.
public interface Moveable{
int right=1;
int left=2;
void moveLeft();
void moveRight();
}
Кафедра ЮНЕСКО по НИТ
Интерфейсы. Пример

Метод sort класса Array упорядочивает массив объектов при
одном условии: объекты должны принадлежать классам,
реализующим интерфейс Comparable:
public interface Comparable {
int compareTo(Object other);}


Это значит, что любой класс, реализующий интерфейс
Comparable, должен иметь метод compareTo, получающий
объект Object и возвращающий целое число.
Существует и неявное требование: при вызове
x.compareTo(y) метод compareTo должен уметь сравнивать
два объекта и возвращать признак того, что один из них
больше другого. Отрицательное значение – x меньше y,
положительное – x больше y, равное 0 – объекты равны.
Кафедра ЮНЕСКО по НИТ
Интерфейсы. Пример


Для сортировки объектов класса Employee нужно
реализовать метод sort класса Array. В этом случае класс
Employee должен реализовать интерфейс Comparable.
Для того чтобы класс реализовывал интерфейс, нужно
выполнить 2 действия:


объявить, что класс предназначен для реализации интерфейса:
class Employee implements Comparable {…}
поместить в класс определение всех методов, указанных в
интерфейсе:
public int compareTo (Object otherObject){
Employee other = (Employee) otherObject;
if(salary < other.salary) return -1;
if(salary > other.salary) return 1;
return 0;}
Кафедра ЮНЕСКО по НИТ
Свойства интерфейсов
1.
2.
Интерфейсы – это не классы! С помощью
операции new() невозможно создать экземпляр
интерфейса. x = new Comparable(…); //не правильно
Но несмотря на это, можно объявлять
переменные, ссылающиеся на интерфейсы.
Comparable x; x=new Employee(…);
3.
4.
С помощью операции instanceof можно
проверять, реализует ли данный объект
интерфейс.
Аналогично классам, интерфейсы могут
реализовывать иерархию наследования от
интерфейсов.
Кафедра ЮНЕСКО по НИТ
Выводы
1.
2.
3.
Каждый класс может реализовывать
любые доступные интерфейсы, при этом
в классе должны быть реализованы все
абстрактные методы
Если из разных источников наследуются
методы с одинаковыми сигнатурами, то
достаточно один раз описать метод
Интерфейс – полностью абстрактный
класс. Но с помощью его механизмов
можно реализовать пример
множественного наследования.
Кафедра ЮНЕСКО по НИТ
Некоторые «полезные»
примеры применения
интерфейсов
Обратные вызовы, клонирование
объектов, многопоточные
приложения
Кафедра ЮНЕСКО по НИТ
Интерфейсы и обратные вызовы



При обратном вызове программист задает действия, которые
должны выполняться всякий раз, когда происходит некоторое
событие. Например, действие, которое должно быть
выполнено при нажатии кнопки мыши.
Класс javax.swing.Timer можно использовать для отсчета
времени. Устанавливая таймер, необходимо указать, что
должно произойти по истечении интервала времени.
Программист должен передать таймеру объект некоторого
класса, метод которого будет вызван. Класс объекта должен
реализовывать интерфейс ActionListener из java.awt.event:
public interface ActionListener{
void actionPerformed(ActionEvent event);
}
Кафедра ЮНЕСКО по НИТ
Интерфейсы и обратные вызовы
import java.awt.*; import java.awt.event.*; import javax.swing.Timer;
public class TimerTest{
public static void main(String[] args){
ActionListener listener=new TimePrinter();
Timer t=new Timer(10000, listener);
t.start();
JOptionPane.showMessageDialog(null, “Выход?”);
System.exit(0); }
}
class TimePrinter implements ActionListener{
public void actionPerformed(ActionEvent event){
Date now=new Date();
System.out.println(“Текущее время”+now);
Toolkit.getDefaultToolkit().beep();
}
}
Кафедра ЮНЕСКО по НИТ
Клонирование объектов


После создания копии переменной и оригинал и копия
представляют собой один и тот же объект.
Изменение одной переменной повлечет изменение другой.
Employee original = new Employee(“John”, 50000);
Employee copy = original;
copy.raiseSalary(10); // Оригинал тоже изменился
Копирование
оригинал
копия
Employee
name = “John”
salary = 50000
Кафедра ЮНЕСКО по НИТ
Клонирование объектов

Если нужно, чтобы переменная copy представляла собой
новый объект, в первый момент своего существования
идентичный объекту original, но совершенно не зависимый
от него, используется метод clone().
Employee original = new Employee (“John”, 50000);
Employee copy = (Employee) original.clone();
copy.raiseSalary(10); // Оригинал не изменился
Клонирование
оригинал
Employee
name = “John”
salary = 50000
копия
Employee
name = “John”
salary = 50000
Кафедра ЮНЕСКО по НИТ
Клонирование объектов


Метод clone() является защищенным методом класса Object. Т.е. только
класс Employee может клонировать объекты своего класса.
Если все поля класса имеют основной тип, то в “двойнике” они просто
копируются. Если объект содержит ссылку на подобъект, то и оригинал, и
клонированные объекты будут совместно использовать один и тот же объект
– поверхностное клонирование.
Клонирование
оригинал
Employee
name = “John”
salary = 50000
payDay =
GregorianCalendar
копия
Employee
name = “John”
salary = 50000
payDay =
Кафедра ЮНЕСКО по НИТ
Клонирование объектов



Поверхностное копирование используется в случае, когда
подобъект является неизменяемым (например, String).
Если подобъект является изменяемым, то необходимо
переопределять метод clone(), чтобы выполнить глубокое
копирование, которое позволяет клонировать подобъекты
наряду с содержащими их объектами.
Для этого необходимо:



реализовать интерфейс Cloneable.
переопределить метод clone() с модификатором доступа public.
При реализации интерфейса Cloneable необходимо
перехватывать исключительную ситуацию
CloneNotSupportedException.
Кафедра ЮНЕСКО по НИТ
Клонирование объектов
class Employee implements Cloneable {
…
public Object clone(){ // повышает уровень видимости
try{
// вызов метода Object.clone()
Employee cloned = (Employee) super.clone();
// клонирование изменяемых полей
cloned.payDay = (GregorianCalendar)payDay.clone();
return cloned;
}
catch(CloneNotSupportedException e){ return null;}
}
}
Чтобы реализовать глубокое копирование, нужно приложить
дополнительные усилия и организовать клонирование изменяемых
полей экземпляра!
Кафедра ЮНЕСКО по НИТ
Многопоточность и интерфейс
Runnable
Применение процедуры квантования
времени (time slicing) на однопроцессорных
компьютерах позволило запускать
многопоточные приложения.
 Применение многопоточности:



в задачах, где необходимо одновременно
выполнять несколько действий;
активные игры или подобные приложения.
Кафедра ЮНЕСКО по НИТ
Многопоточность и интерфейс
Runnable


Поток выполнения в Java представляется
экземпляром класса Thread. Необходимо от него
наследоваться и перекрыть метод run().
Метод run() содержит действия, которые должны
исполняться в новом потоке исполнения.
public class mythread extends Thread{
… //создание конструкторов и методов
public void run(){
//некоторое долгое действие, вычисление
}
}
public static void main(String[] args){
mythread mt = new mythread();
mt.start();// унаследованный метод, вызывает метод run()
}
Кафедра ЮНЕСКО по НИТ
Многопоточность и интерфейс
Runnable

Описанный выше подход обладает одним
недостатком – отсутствием в Java множественного
наследования. На помощь приходят интерфейсы:
public class myrun implements Runnable{
… //создание конструкторов и методов
public void run(){
//некоторое долгое действие, вычисление
}
}
public static void main(String[] args){
myrun mrun = new myrun();
Thread t = new Thread(mrun);
t.start();// вызывает метод run()
}
Кафедра ЮНЕСКО по НИТ
Внутренние классы
Внутренний (вложенный) класс – класс,
определенный внутри другого класса.
 Зачем он нужен:





Объект внутреннего класса имеет доступ к
реализации объекта, который его создал,
включая закрытые данные.
Внутренний класс можно скрыть от других
классов того же пакета.
Безымянный (анонимный) внутренний класс
удобен, если нужно «на лету» уточнить
обратные вызовы.
Внутренние классы очень удобны при создании
событийно-управляемых программ.
Кафедра ЮНЕСКО по НИТ
Пример



Создать программу, в которой банковский счет управляется
по таймеру: каждую секунду на счет добавляются проценты.
Не будем использовать открытые методы, поскольку любой
сможет вызвать их и изменить состояние счета.
Применим внутренние классы, методы которых могут
непосредственно манипулировать балансом.
class BankAccount{
public BankAccount (double initialBalance){…}
public void start (double rate){…}
private double balance;
private class InterestAdder implements ActionListener{
//внутренний класс
…}
}
Кафедра ЮНЕСКО по НИТ
Продолжение примера


Класс InterestAdder расположен внутри BankAccount. НО из
этого не следует, что каждый объект BankAccount содержит
поле типа InterestAdder!
Класс InterestAdder является закрытым внутренним классом,
что гарантирует безопасность.
private class InterestAdder implements ActionListener{
private double rate;
public InterestAdder (double aRate){
rate=aRate;}
public void actionPerformed (ActionEvent event){
double interest=balance*rate/100;
balance += interest;
System.out.println(“Баланс=”+balance);
}
}
Кафедра ЮНЕСКО по НИТ
Продолжение примера
import java.awt.event.*; import javax.swing.*;
public class InnerClassTest{
public static void main(String[] args){
BankAccount account=new BankAccount(1000);
account.start(10);
JOptionPane.showMessageDialog(null,”Выход?”);
System.exit(0);
}
}
class BankAccount{
private double balance;
public BankAccount (double initialBalance){
balance = initialBalance;
}
public void start (double rate){
ActionListener adder = new InterestAdder(rate);
Timer t = new Timer(1000, adder);
t.start();
}
private class InterestAdder implements …
}
Кафедра ЮНЕСКО по НИТ
Локальные внутренние классы
public void start (double rate){
class InterestAdder implements ActionListener{
private double rate;
public InterestAdder (double aRate){
rate=aRate;}
public void actionPerformed (ActionEvent event)
{ double interest=balance*rate/100;
balance += interest;
System.out.println(“Баланс=”+balance);
}
}
ActionListener adder = new InterestAdder(rate);
Timer t = new Timer(1000, adder);
t.start();
}
Кафедра ЮНЕСКО по НИТ





Класс определяется
локально в отдельном
методе.
Локальные классы
никогда не объявляются
с помощью
модификаторов
доступа.
Область видимости
класса – блок.
Классы полностью
скрыты от внешнего
мира.
Имеют доступ не только
к полям своего
внешнего класса, но и к
локальным переменным
метода, в котором класс
объявлен.
Анонимные вложенные классы
public void start (int interval, final boolean beep){
ActionListener listener = new ActionListener(){
public void actionPerformed(ActionEvent event){
Date now = new Date();
System.out.println(“Звонок, время: ” + now);
if(beep) Toolkit.getDefaultToolkit().beep();
}
};
Timer t = new Timer(1000, listener);
t.start();
}

Создается новый объект класса, реализующего
интерфейс ActionListener, в котором в {} определен
требуемый метод actionPerformed().
Кафедра ЮНЕСКО по НИТ
Download