}

}

class Cat implements Voice {

public void voice() {

System. out. println("Miaou!");

}

}

class Cow implements Voice {

public void voice() {

System. out. println("Mu-u-u!");

}

}

public class Chorus {

public static void main(String[] args) {

Voice[] singer = new Voice[3];

singer[0] = new Dog();

singer[1] = new Cat();

singer[2] = new Cow();

for (int i = 0; i < singer. length; i++)

singer[i].voice();

}

}

Здесь используется интерфейс voice вместо абстрактного класса Pet , описанного в листинге 12.

Что же лучше использовать: абстрактный класс или интерфейс? На этот вопрос нет однозначного ответа.

Создавая абстрактный класс, вы волей-неволей погружаете его в иерархию классов, связанную условиями одиночного наследования и единым предком — классом object . Пользуясь интерфейсами, вы можете свободно проектировать систему, не задумываясь об этих ограничениях.

С другой стороны, в абстрактных классах можно сразу реализовать часть методов. Реализуя же интерфейсы, вы обречены на скучное переопределение всех методов.

Вы, наверное, заметили и еще одно ограничение: все реализации методов интерфейсов должны быть открытыми, public, поскольку при переопределении можно лишь расширять доступ, а методы интерфейсов всегда открыты.

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

Наконец, можно использовать интерфейсы просто для определения констант, как показано в листинге 14.

НЕ нашли? Не то? Что вы ищете?

Листинг 14. Система управления светофором

interface Lights {

int RED = 0;

int YELLOW = 1;

int GREEN = 2;

int ERROR = -1;

}

class Timer implements Lights {

private int delay;

private static int light = RED;

Timer(int sec) {

delay = 1000 * sec;

}

public int shift() {

int count = (light++) % 3;

try {

switch (count) {

case RED:

Thread. sleep(delay);

break;

case YELLOW:

Thread. sleep(delay / 3);

break;

case GREEN:

Thread. sleep(delay / 2);

break;

}

} catch (Exception e) {

return ERROR;

}

return count;

}

}

class TrafficRegulator {

private static Timer t = new Timer(1);

public static void main(String[] args) {

for (int k = -0; k < 10; k++)

switch (t. shift()) {

case Lights. RED:

System. out. println("Stop!");

break;

case Lights. YELLOW:

System. out. println("Wait!");

break;

case Lights. GREEN:

System. out. println("Go!");

break;

case Lights. ERROR:

System. err. println("Time Error");

break;

default:

System. err. println("Unknown light.");

return;

}

}

}

Здесь, в интерфейсе Lights, определены константы, общие для всего проекта.

Класс Timer реализует этот интерфейс и использует константы напрямую как свои собственные. Метод shift о этого класса подает сигналы переключения светофору с разной задержкой в зависимости от цвета. Задержку осуществляет метод sleep() класса Thread из стандартной библиотеки, которому передается время задержки в миллисекундах. Этот метод нуждается в обработке исключений try{} catch() {} , о которой мы будем говорить в главе 16.

Класс TrafficReguiator не реализует интерфейс Lights и пользуется полными именами Lights. RED и т. д. Это возможно потому, что константы RED, YELLOW и GREEN по умолчанию являются статическими.

Теперь нам известны все средства языка Java, позволяющие проектировать решение поставленной задачи.

5. Классы-оболочки

5.1 Числовые классы

Java — полностью объектно-ориентированный язык. Это означает, что все, что только можно, в Java представлено объектами.

Восемь примитивных типов нарушают это правило. Они оставлены в Java из-за многолетней привычки к числам и символам. Да и арифметические действия удобнее и быстрее производить с обычными числами, а не с объектами классов.

Но и для этих типов в языке Java есть соответствующие классы — классы-оболочки (wrapper) примитивных типов. Конечно, они предназначены не для вычислений, а для действий, типичных при работе с классами — создания объектов, преобразования объектов, получения численных значений объектов в разных формах и передачи объектов в методы по ссылке.

На рис. 12 показана одна из ветвей иерархии классов Java. Для каждого примитивного типа есть соответствующий класс. Числовые классы имеют общего предка — абстрактный класс Number, в котором описаны шесть методов, возвращающих числовое значение, содержащееся в классе, приведенное к соответствующему примитивному типу: bytevalue (), doubievalue () , floatValue (), intValue(), longValue (), shortValue () . Эти методы переопределены в каждом из шести числовых классов-оболочек.

Рис. 12. Классы примитивных типов

Помимо метода сравнения объектов equals о, переопределенного из класса object, все описанные в этой главе классы, кроме Boolean и class, имеют метод compareTo () , сравнивающий числовое значение, содержащееся в данном объекте, с числовым значением объекта — аргумента метода compareTo() . В результате работы метода получается целое значение:

0, если значения равны;

отрицательное число (—1), если числовое значение в данном объекте меньше, чем в объекте-аргументе;

положительное число (+1), если числовое значение в данном объекте больше числового значения, содержащегося в аргументе.

Что полезного в классах-оболочках?

В каждом из шести числовых классов-оболочек есть статические методы преобразования строки символов типа String лредставляющей число, в соответствующий примитивный тип: Byte. parseByte(), Double. parseDouble(), Float. parseFloat(), Integer. parselnt(), Long. parseLong(), Short. parseShort() . Исходная строка типа String, как всегда в статических методах, задается как аргумент метода. Эти методы полезны при вводе данных в поля ввода, обработке параметров командной строки, т. е. всюду, где числа представляются строками цифр со знаками плюс или минус и десятичной точкой.

В каждом из этих классов есть статические константы MAX_VALUE и MIN_VALUE , показывающие диапазон числовых значений соответствующих примитивных типов. В классах Double и Float есть еще константы POSITIVE_INFINITY, NEGATIVE_INFINITY, NaN, о которых шла речь в главе 1, и логические методы проверки isNan() , isInfinite() .

Если вы хорошо знаете двоичное представление вещественных чисел, то можете воспользоваться статическими методами floatTointBits() и doubieToLongBits() , преобразующими вещественное значение в целое. Вещественное число задается как аргумент метода. Затем вы можете изменить отдельные биты побитными операциями и преобразовать измененное целое число обратно в вещественное значение методами intsitsToFioat() и longBitsToDouble() .

Статическими методами toBinaryString(), toHexString() и   toOctalString() классов integer и Long можно преобразовать целые значения типов int и long, заданные как аргумент метода, в строку символов, показывающую двоичное, шестнадцатеричное или восьмеричное представление числа.

В листинге 15 показано применение этих методов, а рис. 13 демонстрирует вывод результатов.

Рис. 13. Методы  числовых классов ;

Листинг 15. Методы числовых классов

class NumberTest {

public static void main(String[] args) {

int i = 0;

short sh = 0;

double d = 0;

Integer kl = new Integer(55);

Integer k2 = new Integer(100);

Double dl = new Double(3.14);

try {

i = Integer. parseInt(args[0]);

sh = Short. parseShort(args[0]);

d = Double. parseDouble(args[1]);

dl = new Double(args[1]);

kl = new Integer(args[0]);

} catch (Exception e) {

}

double x = 1.0;

System. out. println("i = " + i);

System. out. println("sh - " + sh);

System. out. println("d. = " + d);

System. out. println("kl. intValue() = " + kl. intValue());

System. out. println("dl. intValue() = " + dl. intValue());

System. out. println("kl > k2? " + pareTo(k2));

System. out. println("x = " + x);

System. out. println("x isNaN? " + Double. isNaN(x));

System. out. println("x islnfinite? " + Double. isInfinite(x));

System. out. println("x == Infinity? " +

(x == Double. POSITIVE_INFINITY));

System. out. println("d = " + Double. doubleToLongBits(d));

System. out. println("i = " + Integer. toBinaryString(i));

System. out. println("i = " + Integer. toHexString(i));

System. out. println("i = " + Integer. toOctalString(i));

}

}

5.2 Класс Boolean

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

Конструктор Boolean (String s) создает объект, содержащий значение true , если строка s равна " true " в любом сочетании регистров букв, и значение false для любой другой строки.

Логический метод booieanvalue() возвращает логическое значение, хранящееся в объекте.

5.3 Класс Character

В этом классе собраны статические константы и методы для работы с отдельными символами.

Статический метод

digit(char ch, in radix)

переводит цифру ch системы счисления с основанием radix в ее числовое значение типа int.

Статический метод

forDigit(int digit, int radix)

производит обратное преобразование целого числа digit в соответствующую цифру (тип char ) в системе счисления с основанием radix.

Основание системы счисления должно находиться в диапазоне от Character. MIN_RADIX до Character. MAX_RADIX.

Метод toString() переводит символ, содержащийся в классе, в строку с тем же символом.

Статические методы toLowerCase() , touppercase(), toTitieCase() возвращают символ, содержащийся в классе, в указанном регистре. Последний из этих методов предназначен для правильного перевода в верхний регистр четырех кодов Unicode, не выражающихся одним символом.

Из за большого объема этот материал размещен на нескольких страницах:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37