Смекни!
smekni.com

Распределенные системы обработки информации (стр. 2 из 7)

public enum MyEnum {

FIRST(“first”), SECOND(“second”), THIRD(“third”);

MyEnum(String arg) {

text = arg;

}

String getText() { return text; }

private String text;

}

Можно перебрать все значения enum в цикле for:

for (MyEnum m : MyEnum.values())

doSomething(m);

1.6 Наследование и о классе Class

Наследование:

class DerivedClass extends BaseClass {…}

Можно наследовать только один класс (нет множественного наследования), зато можно реализовывать несколько интерфейсов. Для обращения к полям и методам суперкласса в методе дочернего класс используется super. Также можно вызывать конструктор базового класса в конструкторе дочернего:

public DerivedClass() {

super(param1, param2);

}

Для переопределения метода достаточно еще раз описать его в дочернем классе, все методы подвержены полиморфизму (нет необходимости объявлять их виртуальными). Чтобы метод нельзя было переопределить, используется модификатор final. Также можно объявить final класс, тогда от него нельзя будет унаследовать другие классы.

Если объявить класс abstract, то нельзя создать его экземпляр, только унаследовать. Метод тоже может быть abstract – в этом случае его тело определяется не в этом классе, а в дочерних.

Дочерним классам доступны поля и методы уровня public и protected.

a instanceof B – возвращает true, если a – экземпляр класса B, в противном случае false.

Если наследуемый класс не указывается явно, то неявным базовым классом становится Object. Таким образом, каждый класс унаследован (возможно, косвенно) от Object. Переменную типа Object можно использовать как ссылку на любой объект. Простые типы не наследуются от Object. Object содержит ряд важных методов:

equals() – проверяет эквивалентность двух объектов. Изначально проверяет только равенство ссылок, поэтому должен переопределяться в дочерних классах.

getClass() – возвращает объект типа Class, описывающий тип данного объекта.

hashCode() – генерирует целое число на основе состояния конкретного объекта. Метод используется при включении объекта в хэш-таблицу. При переопределении equals() надо переопределять и hashCode() – у эквивалентных объектов хэш-коды должны совпадать.

toString() – возвращает строку, отражающую состояние объекта.

clone() – создает копию объекта. Без переопределения недоступен.

Class:

Обьект метаданных о каком либо классе. Позволяет узнать методы, поля, создать экземпляр другого класса. Обьект Class создается либо как жертва.getClass() или как Class.forName(“полное квалификационное имя”). Данный класс – основа отражения (reflection).


1.7 Интерфейсы и обратные вызовы в Java

Интерфейс – спецификация требований к реализуемым классом методам:

public interface MyInterface {

int myMethod();

}

Все методы, объявленные в интерфейсе, считаются обьявлеными как public.

Класс может реализовать один или несколько интерфейсов:

class MyClass implements MyInterface { … }

class MyClass2 implements MyInterface, MyInterface2 { … }

Интерфейс – не класс, нельзя создать его экземпляр. Но интерфейсная переменная может ссылаться на объект класса, который реализует этот интерфейс. С помощью instanceof можно проверить, реализует ли объект заданный интерфейс.

В Java интерфейсы часто применяются вместо указателей на функции для организации механизма обратного вызова. Объекту, который будет выполнять обратный вызов, передается интерфейсная переменная, метод которой будет вызван. После этого первый объект сохраняет эту переменную и при необходимости вызывает один из ее методов. Например, таймер javax.swing.Timer требует интерфейс java.awt.event.ActionListener для периодического выполнения обратного вызова.

public class MoneyCounter implements ActionListener {

public void actionPerformed( ActionEvent e ) {

money += money * percent;

System.out.println( "Денег сейчас: " + money );

}

}


1.8 Вложенные классы и интерфейсы

Вложенный (inner) класс или интерфейс – класс или интерфейс, определенный внутри другого класса. Объект вложенного класса имеет доступ ко всем полям и методам содержащего его класса (включая закрытые). Его можно скрыть от других классов пакета. Можно создавать анонимные вложенные классы.

class MyClass {

...

private class MyInnerClass { … }

}

Обращение из вложенного класса к методам и полям внешнего: можно просто someMethod(), а можно так: MyClass.this.SomeMethod().

Создание экземпляра вложенного класса:

MyInnerClass obj = new MyInnerClass();

MyInnerClass obj = this.new MyInnerClass();

А если вне класса:

MyClass myobj = new MyClass();

MyClass.MyInnerClass obj = myobj.new MyInnerClass();

Вложенный класс можно определить даже внутри метода (локальный вложенный класс). Они объявляются без спецификаторов видимости.

Анонимные внутренние классы:

Чаще всего используется в обработчиках GUI:

MyInterface obj = new MyInterface() { … };

myButton.addActionListener( new ActionListener() {

public void actionPerformed( ActionEvent e ) {

doSmth( );

}

} );

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

Вложенные классы, определенные в интерфейсах, считаются по умолчанию статическими и публичными.

1.9 Цикл for-each, аргументы переменной длины, настраиваемые типы в Java

Цикл for each позволяет последовательно перебрать все элементы набора данных.

for (ElementType element : collection) { … }

Объект collection должен поддерживать интерфейс Iterable или быть массивом:

public interface Iterable<E> {

Iterator<E> iterator();

}

Метод с переменным числом параметров:

int sum(int… args);

void format(String fmt, Object… objs);

Многоточие здесь эквивалентно [], то есть args и objs – массивы аргументов указанного типа.

Настраиваемые типы (generics) – способ параметризации класса другим классом. Обычно применяется в коллекциях объектов.

Использование:

LinkedList<Integer> li = new LinkedList<Integer>();

li.add(new Integer(0));

Объявление:

class LinkedList<T> {

void add(T element) { … }

}

Ограничение:

class MyClass<T extends Exception> { … }

Здесь T – подтип класса Exception.

Можно использовать несколько параметров:

class MyClass<T, E, M> {…}

Можно параметризировать метод:

<T> void method(T[] a, Colletion<T> c) { … }

Параметр ? обозначает любой тип:

ArrayList<?> a = new ArrayList<Number>();

Можно также указать ограничения:

ArrayList<? extends Shape> a = new ArrayList<Triangle>(); /* Triangle – подкласс Shape */

ArrayList<? super Triangle> a = new ArrayList<Shape>(); /* Shape – базовый для Triangle */

Ограничения generics: нельзя использовать простые типы в качестве параметров, нельзя определять параметризированные массивы, нельзя использовать generic-типы в исключениях, нельзя использовать instanceof, нельзя задавать значения по умолчанию для параметров типов, нельзя использовать тип-параметр для создания объекта, нельзя клонировать объект, имеющий тип параметра. Механизм generics реализуется компилятором, на уровне виртуальной машины про это ничего не известно. При параметризации не создаются новые классы (ключевое отличие от templates в C++).

1.10 Многопоточность в Java: создание потоков

Класс, код которого будет выполняться в другом потоке, должен реализовывать интерфейс Runnable:

public interface Runnable {

void run();

}

Затем действуем так:

MyRunnable r = new MyRunnable();

Thread t = new Thread(r);

t.start();

Можно также унаследовать свой класс от Thread (он реализует Runnable), так еще проще.

Поток завершает работу, когда метод run() возвращает управление. Можно также запросить прерывание потока, вызвав t.interrupt(). После этого поток может проверить статус прерывания:

if( Thread.currentThread( ).isInterrupted( ) ) { … }

Если interrupt() вызван для заблокированного потока, он завершается и выбрасывает исключение InterruptedException.

Поток блокируется, если для него вызван sleep(), если он выполняет блокирующий ввод-вывод, если он пытается заблокировать заблокированный объект, если он ожидает выполнения условия, если для него вызван метод suspend() (устаревший).

1.11 Многопоточность в Java: синхронизация

Синхронизация в многопоточном приложении требуется в ситуации гонки (race condition). В этом случае требуется ограничить выполнение фрагмента кода одним потоком.

Раньше использовалось ключевое слово synchronized, которым помечается метод класса. Этот метод будет выполняться только одним потоком одновременно. Достигается за счет неявного создания объекта блокировки.

Более гибкий способ – использование объектов блокировок: java.util.concurrent.locks.ReentrantLock.

myLock.lock(); // объект ReentrantLock

try { … }

finally { myLock.unlock(); }

Объект ReentrantLock может блокироваться одним потоком несколько раз, но потом должен быть столько же раз разблокирован.

С объектом блокировки можно связать объект условия (типа Condition):

myCondition = myLock.newCondition();

Для условия определены методы:

myCondition.await() // уступить объект блокировки.

myCondition.signalAll() /* все ожидающие потоки возобновят выполнение */

Обычно делают так:

while (!(можно продолжать))

myCondition.await();

Каждый объект может быть объектом блокировки. Для этого в классе Object есть методы notifyAll(), notify(), wait().

Синхронизированные блоки:

synchronized(obj) { … }

Здесь obj – любой объект, который выступает в качестве объекта блокировки.

Можно сделать проще – объявить переменную volatile, тогда синхронизация доступа к этой переменной будет происходить автоматически.

Блокирующие очереди – используются для ситуации читателей и писателей. Классы: LinkedBlockingQueue, ArrayBlockingQueue, PriorityBlockingQueue, DelayQueue. Такие очереди обеспечивают синхронизированный доступ к своим элементам и имеют методы: add (добавить элемент), remove (удалить из головы), element (возвратить из головы без извлечения), offer (добавляет элемент с проверкой переполнения), poll (удаляет из головы с проверкой пустоты), peek (возвращает из головы без извлечения с проверкой пустоты), put (блокирующее добавление элемента), take (блокирующее удаление из головы).

1.12 Ввод-вывод в Java: общие сведения

В Java ввод-вывод основывается на потоках (streams) и файлах (files).

Поток ввода – объект, из которого можно считывать последовательность байтов или символов, определяется абстрактным классом InputStream (определяет метод int read(), читающий один байт). Поток вывода – объект, в который можно записывать последовательность байтов или символов, определяется абстрактным классом OutputString (определяет метод void write(int b), записывающий один байт). Методы read и write могут блокировать поток. Есть и другие методы: int read(byte[] b), void write(byte[] b) и т.д.