Java — полностью объектно-ориентированный язык. Рто означает, что РІСЃРµ, что только можно, РІ Java представлено объектами.
Восемь примитивных типов нарушают это правило. Они оставлены в Java не только из-за многолетней привычки к числам и символам. Арифметические действия удобнее и быстрее производить с обычными числами, а не с объектами классов, которые требуют много ресурсов от компьютера.
Но и для этих типов в языке Java есть соответствующие классы — классы-оболочки (wrapper) примитивных типов. Конечно, они предназначены не для вычислений, а для действий, типичных при работе с классами, — создания объектов, преобразования типов объектов, получения численных значений объектов в разных формах и передачи объектов в методы по ссылке.
РќР° СЂРёСЃ. 4.1 показана РѕРґРЅР° РёР· ветвей иерархии классов Java. Для каждого примитивного типа РІ пакете j ava. lang есть соответствующий класс. Числовые классы имеют общего предка — абстрактный класс Number, РІ котором описаны шесть методов, возвращающих числовое значение, содержащееся РІ классе, приведенное Рє соответствующему примитивному типу: byteValue(), doubleValue(), floatValue(), intValue(), longVaiue (), shortValue (). Рти методы переопределены РІ каждом РёР· шести числовых классов-оболочек Byte, Short, Integer, Long, Float Рё Double. Рмена классов-оболочек, Р·Р° исключением класса Integer, совпадают СЃ именами соответствующих примитивных типов, РЅРѕ начинаются СЃ заглавной Р±СѓРєРІС‹.
Помимо метода сравнения объектов equals(), переопределенного из класса Object, все описанные в этой главе числовые классы, класс Character и класс Boolean имеют метод
Object—р Number-
- Boolean
-Character
-Class
■— BigDecimal —Blglnteger
— Byte
— Double —Float —Integer
— Long
— Short
L Character.Subset-i— InputSubset
Character.UnicodeBlock
Рис. 4.1. Классы примитивных типов
compareTo (), сравнивающий числовое значение, символ или булево значение, содержащееся в данном объекте, с числовым значением объекта — аргументом метода compareTo (). В результате работы метода получается целое значение:
□ 0, если сравниваемые значения равны;
□ отрицательное число (-1), если числовое значение в данном объекте меньше, чем в объекте-аргументе или, для класса Boolean, в данном объекте false, а в аргументе — true;
□ положительное число (+1), если числовое значение в данном объекте больше числового значения, содержащегося в аргументе или в данном объекте true, а в аргументе — false.
В каждом из этих классов есть статический метод
int compare(xxx a, xxx b);
который сравнивает значения двух чисел, символов или логических переменных a и b, заданных простыми типами boolean, byte, short, char, int, long, float, double, так же, как и метод compareTo (), и возвращает те же значения.
Еще один полезный статический метод
Xxx valueOf(xxx a);
в котором xxx — это один из простых типов boolean, byte, short, char, int, long, float, double, возвращает объект соответствующего типа. Документация настоятельно рекомендует применять этот метод для создания объектов из простых типов, а не конструктор соответствующего класса.
Что полезного можно найти в классах-оболочках?
Числовые классы
В каждом из шести числовых классов-оболочек есть статические методы преобразования строки символов типа String, представляющей число, в соответствующий примитивный
тип: Byte.parseByte(), Double.parseDouble(), Float.parseFloat(), Integer.parseInt(), Long.parseLong(), Short.parseShort ( ). Рсходная строка типа String, как всегда РІ статических методах, служит параметром метода. Рти методы полезны РїСЂРё РІРІРѕРґРµ данных РІ поля РІРІРѕРґР°, обработке аргументов командной строки, С‚. Рµ. РІСЃСЋРґСѓ, РіРґРµ числа представляются строками символов, состоящими РёР· цифр СЃРѕ знаками плюс или РјРёРЅСѓСЃ Рё десятичной точкой.
В каждом из этих классов есть статические константы MAX_VALUE и MIN_VALUE, показывающие диапазон числовых значений соответствующих примитивных типов. В классах
Double и Float есть еще константы POSITIVE_INFINITY, NEGATIVE_INFINITY, NaN, о которых шла речь в главе 1, и логические методы проверки isNaN ( ), isInfinite ( ).
Если РІС‹ хорошо знаете двоичное представление вещественных чисел, то можете воспользоваться статическими методами floatToIntBits ( ) Рё doubleToLongBits ( ), представляющими последовательность битов, РёР· которых состоит двоичное представление вещественного числа, РІ РІРёРґРµ целого числа типа int или long соответственно. РСЃС…РѕРґРЅРѕРµ вещественное число задается как аргумент метода. Получив целочисленное представление, РІС‹ можете изменить отдельные биты получившегося целого числа побитовыми операциями Рё преобразовать измененное целое число обратно РІ вещественное значение методами intBitsToFloat ( ) Рё longBitsToDouble ().
Статическими методами toBinaryString(), toHexString() и toOctalString() классов Integer и Long можно преобразовать целые значения типов int и long, заданные как аргумент метода, в строку символов, показывающую двоичное, шестнадцатеричное или восьмеричное представление числа.
В листинге 4.1 показано применение этих методов, а рис. 4.2 демонстрирует вывод результатов.
Рис. 4.2. Методы числовых классов |
Листинг 4.1. Методы числовых классов
class NumberTest{
public static void main(String[] args){ int i = 0; short sh = 0;
double d = 0;
Integer k1 = Integer.valueOf(55);
Integer k2 = Integer.valueOf(100); Double d1 = Double.valueOf(3.14); try{
i = Integer.parseInt(args[0]); sh = Short.parseShort(args[0]);
d = Double.parseDouble(args[1]); d1 = new Double(args[1]); k1 = new Integer(args[0]); }catch(Exception e){} double x = 1.0 / 0.0; System.out.println("i = " + i); System.out.println("sh = " + sh);
System.out.println("d = " + d);
System.out.println("k1.intValue() = " + k1.intValue()); System.out.println("d1.intValue() = " + d1.intValue());
System.out.println("k1 > k2? " + k1.compareTo(k2));
System.out.println("x = " + x);
System.out.println("x isNaN? " + Double.isNaN(x));
System.out.println("x isInfinite? " + 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));
}
}
Методы parseInt () и конструкторы классов требуют обработки исключений, поэтому в листинг 4.1 вставлен блок try{}catch(){}. Обработку исключительных ситуаций мы подробно разберем в главе 21.
Начиная с версии Java SE 5 в JDK входит пакет java.util.concurrent.atomic, в котором, в частности, есть классы AtomicInteger и AtomicLong, обеспечивающие изменение числового значения этих классов на уровне машинных команд. Начальное значение задается конструкторами этих классов. Затем методами addAndGet ( ), getAndAdd ( ), incrementAndGet ( ), getAndnIncrement(), decrementAndGet(), getAndDecrement, getAndSet(), set() можно изменять это значение.
Автоматическая упаковка и распаковка типов
В листинге 4.1 объекты числовых классов создавались статическим методом, в котором указывалось числовое значение объекта:
Integer k1 = Integer.valueOf(55);
Рто правильно СЃ точки зрения объектно-ориентированного программирования, РЅРѕ утомительно для программиста. Начиная СЃ пятой версии Java, было решено упростить такую запись. Теперь можно писать
Integer k1 = 55;
как будто k1 — простая числовая переменная примитивного типа. Ничего нового в язык Java такая запись не вносит: компилятор, увидев ее, тут же восстановит применение статического метода. Но она облегчает работу программиста, предоставляя ему привычную форму определения переменной. Как говорят, компилятор делает автоматическую упаковку (auto boxing) числового значения в объект. Компилятор может сделать и автоматическую распаковку. После приведенных ранее определений объекта k1 можно написать, например,
int n = k1;
и компилятор извлечет из объекта k1 класса Integer числовое значение 55. Конечно, для этого компилятор обратится к методу intValue () класса Integer, но это незаметно для программиста.
Автоматическая упаковка и распаковка возможна и в методах классов. Рассмотрим простой класс.
class AutoBox{ static int f(Integer value){ return value; // Распаковка.
}
public static void main(String[] args){
Integer n = f(55);
}
}
В методе main() этого примера сначала число 55 приводится к типу параметра метода f() с помощью упаковки. Затем результат работы метода f () упаковывается в объект n класса Integer.
Автоматическую упаковку и распаковку можно использовать в выражениях, написав k1++ или даже (k1 + k2 / k1), но это уже слишком! Представьте себе, сколько упаковок и распаковок вставит компилятор и насколько это замедлит работу программы!
Настраиваемые типы (generics)
Введение в язык Java автоматической упаковки типов позволило определить еще одну новую конструкцию — настраиваемые типы (generics), позволяющие создавать шаблоны классов, интерфейсов и методов. Например, можно записать обобщенный настраиваемый (generic) класс
class MyGenericClass
public MyGenericClass(){}
public MyGenericClass(T data){ this.data = data;
}
public T getData(){ return data;
}
public void setData(T data){ this.data = data;
}
}
в котором есть поле data неопределенного пока типа, обозначенного буквой T. Разумеется, можно написать другую букву или даже идентификатор. Буква T появилась просто как первая буква слова Type.
Перед использованием такого класса-шаблона его надо настроить, задав при обращении к его конструктору определенный тип в угловых скобках. Например:
class MyGenericClassDemo{
public static void main(String[] args){
MyGenericClass
Integer n = iMyGen.getData();
MyGenericClass
Double x = dMyGen.getData();
}
}
Если РїСЂРё определении экземпляра настраиваемого класса Рё слева Рё справа РѕС‚ знака равенства РІ угловых скобках записан РѕРґРёРЅ Рё тот же тип, то справа его можно опустить для краткости записи, оставив только пару угловых СЃРєРѕР±РѕРє (так называемый "ромбовидный оператор", "diamond operator"). Рспользуя это РЅРѕРІРѕРµ, введенное РІ Java 7, сокращение, предыдущий класс можно записать так:
class MyGenericClassDemo{
public static void main(String[] args){
MyGenericClass
Integer n = iMyGen.getData();
MyGenericClass
Double x = dMyGen.getData();
}
}
Рассмотрим более содержательный пример. Пусть нам надо вычислять среднее арифметическое значение нескольких чисел, причем в одном случае это целые числа, в другом — вещественные, в третьем — короткие или, наоборот, длинные целые числа. У среднего значения в любом случае будет тип double. В листинге 4.2 написан один общий класс-шаблон для всех этих случаев.
Листинг 4.2. Настраиваемый класс
class Average
public Average(T[] data) { this.data = data; }
public double average(){ double result = 0.0;
for (T t: data) result += t.doubleValue(); return result / data.length;
}
public static void main(String[] args){
Integer[] iArray = {1, 2, 3, 4};
Double[] dArray = {3.4, 5.6, 2.3, 1.24};
Average
}
Обратите внимание РЅР° то, что РІ заголовке класса РІ угловых скобках указано, что тип T — подкласс класса Number. Рто сделано потому, что здесь тип T РЅРµ может быть произвольным. Действительно, РІ методе average ( ) использован метод doubleValue ( ) класса Number, Р° это означает, что тип T ограничен классом Number Рё его подклассами. РљСЂРѕРјРµ того, операции сложения Рё деления тоже допустимы только для чисел.
Конструкция
У настраиваемого типа может быть более одного параметра. Они перечисляются в угловых скобках через запятую:
class MyGenericClass2{ private S id; private T data;
public MyGenericClass2() {}
public MyGenericClass2(S id, T data){ this.id = id; this.data = data;
}
public S getId(){ return id;
}
public void setId(S data){ this.id = id;
}
public T getData(){ return data;
}
public void setData(T data){ this.data = data;
}
}
РР· этих примеров РІРёРґРЅРѕ, что неопределенные типы S, T РјРѕРіСѓС‚ быть типами параметров конструкторов Рё типами возвращаемых методами значений. Разумеется, РѕРЅРё РјРѕРіСѓС‚ быть типами параметров РЅРµ только конструкторов, РЅРѕ Рё любых методов. Более того, типами параметров Рё типами возвращаемых значений методов РјРѕРіСѓС‚ быть настраиваемые типы. Можно написать метод РІ такой форме:
public MyGenericClass2 makeClass2(S id,
MyGenericClass
}
и обратиться к нему так, как показано в листинге 4.3.
Листинг 4.3. Настраиваемые классы — параметры методов
public class MyGenericClass2Demo{
public MyGenericClass2
makeClass2(S id, MyGenericClass
return new MyGenericClass2(id, data.getData());
}
public static void main(String[] args){
MyGenericClass
MyGenericClass2Demo
MyGenericClass2
}
}
Шаблон типа (wildcard type)
В предыдущих главах мы часто пользовались тем, что можно определить ссылку типа суперкласса, ссылающуюся на объект подкласса, например:
Number n = new Long(123456L);
Number d = new Double(27.346);
Более того, это свойство распространяется на массивы:
Number[] n = new Long[100];
Можно ли распространить эту возможность на настраиваемые типы? Например, можно ли написать последний оператор листинга 4.3 так:
MyGenericClass2
d.makeClass2(123456L, dMyGen);
Ответ отрицательный. РР· того, что какой-то класс B является подклассом класса A, РЅРµ следует, что класс g будет подклассом класса g.
Рто непривычное обстоятельство вынудило ввести дополнительную конструкцию — шаблон типа (wildcard type), применяемую РІ процессе настройки типа. Шаблон типа обозначается вопросительным знаком Рё означает "неизвестный тип" или "произвольный тип". Предыдущий РєРѕРґ РЅРµ вызовет возражений Сѓ компилятора, если написать его РІ таком РІРёРґРµ:
MyGenericClass2 extends Number, ? extends Number> n = // Верно.
d.makeClass2(123456L, dMyGen);
или
MyGenericClass2
d.makeClass2(123456L, dMyGen);
Можно написать даже неограниченный шаблон типа
MyGenericClass2, ?> n =
d.makeClass2(123456L, dMyGen);
Такая запись будет почти эквивалентна записи
MyGenericClass2 n =
d.makeClass2(123456L, dMyGen);
за тем исключением, что в первом случае компилятор сделает более строгие проверки.
Кроме записи extends Type>, означающей "произвольный подтип типа Type, включая сам тип Type", можно написать выражение super Type>, означающее "произвольный супертип типа Type, включая сам тип Type".
Шаблон типа можно использовать в тех местах кода, где настраивается тип, в том числе в параметрах метода:
public MyGenericClass2 makeClass2(S id,
MyGenericClass extends Number> data){
return new MyGenericClass2(id, data.getData());
}
но, поскольку шаблон типа не является типом, его нельзя применять для создания объектов и массивов. Следующие определения неверны:
Average extends Number> a = // Ошибка!
new Average extends Number>(iArray);
Average extends Number>[] a = // Ошибка!
new Average extends Number>[10];
Тем не менее при определении массива (но не объекта) можно записать неограниченный шаблон типа:
Average extends Number>[] a = // Верно.
new Average>[10];
Настраиваемые методы
Настраиваемыми РјРѕРіСѓС‚ быть РЅРµ только типы, РЅРѕ Рё методы. Параметры настраиваемого метода (type parameters) указываются РІ заголовке метода РІ угловых скобках перед типом возвращаемого значения. Рто выглядит так, как показано РІ листинге 4.4.
Листинг 4.4. Настраиваемый метод
public class MyGenericClass2Demo{
public MyGenericClass2
makeClass2(S id, MyGenericClass
return new MyGenericClass2(id, data.getData());
} public static void main(String[] args){
MyGenericClass
MyGenericClass2Demo d =
new MyGenericClass2Demo();
MyGenericClass2
}
}
Метод makeClass2 () описан в простом, ненастраиваемом, классе MyGenericClass2Demo, и его параметры задаются в угловых скобках . Здесь можно записывать ограниченные параметры
public
MyGenericClass2 makeClass2(S id, MyGenericClass
return new MyGenericClass2(id, data.getData());
}
Как видно из листинга 4.4, специально настраивать метод не нужно, конкретные типы его параметров и возвращаемого значения определяются компилятором по переданным в метод аргументам.
Как вы убедились из приведенных примеров, настраиваемые типы и методы допускают сложную структуру параметров, так же как и вложенные классы. Мы еще не касались вопросов наследования настраиваемых типов, реализации настраиваемых интерфейсов, создания массивов настраиваемых типов. Все эти вопросы подробно рассмотрены на сайте Анжелики Лангер (Angelika Langer), в ее Java Generics FAQ, http:// .
Класс Boolean
Рто очень небольшой класс, предназначенный главным образом для того, чтобы передавать логические значения РІ методы РїРѕ ссылке.
Конструктор Boolean (String s) создает объект, содержащий значение true, если строка s равна "true" в произвольном сочетании регистров букв, и значение false — для любой другой строки.
Статический метод valueOf(boolean b) позволяет получить объект класса Boolean из значения примитивного типа boolean.
Пользуясь автоматической упаковкой, можно определение
Boolean b = new Boolean("true");
или
Boolean b = Boolean.valueOf(true);
сократить до
Boolean b = true;
Метод booleanValue () возвращает логическое значение, хранящееся в объекте.
Статический метод parseBoolean(String s) возвращает значение true, если строка s равна "true" в произвольном сочетании регистров букв, и значение false — для любой другой строки.
Класс 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(), toTitleCase() возвращают символ, содержащийся в классе, в указанном регистре. Последний из этих методов предназначен для правильного перевода в верхний регистр четырех кодов Unicode, не выражающихся одним символом.
Статический метод
getName(int code);
возвращает полное Unicode-имя символа по его коду code.
Множество статических логических методов проверяют различные характеристики символа, переданного в качестве аргумента метода:
□ isDefined () — выясняет, определен ли символ в кодировке Unicode;
□ isDigit () — проверяет, является ли символ цифрой Unicode;
□ isIdentifierIgnorable () — выясняет, нельзя ли использовать символ в идентификаторах;
□ isISOControl () — определяет, является ли символ управляющим;
□ isBmpCodePoint () — определяет, лежит ли код символа в диапазоне \u0000-\uFFFF;
□ isSupplementaryCodePoint () — определяет, что код символа больше \uFFFF;
□ isJavaIdentifierPart ( ) - выясняет, можно ли использовать символ в идентифика
торах;
□ isJavaIdentifierStart () — определяет, может ли символ начинать идентификатор;
□ isLetter () — проверяет, является ли символ буквой Java;
□ isLetterOrDigit () — проверяет, является ли символ буквой или цифрой Unicode;
□ isLowerCase () — определяет, записан ли символ в нижнем регистре;
□ isSpaceChar () — выясняет, является ли символ пробелом в смысле Unicode;
□ isTitleCase () — проверяет, является ли символ титульным;
□ isUnicodeIdentifierPart ( ) - выясняет, можно ли использовать символ в именах
Unicode;
□ isUnicodeIdentifierStart () — проверяет, является ли символ буквой Unicode;
□ isUpperCase () — проверяет, записан ли символ в верхнем регистре;
□ isWhitespace () — выясняет, является ли символ пробельным.
Точные диапазоны управляющих символов, понятия верхнего и нижнего регистра, титульного символа, пробельных символов лучше всего посмотреть в документации Java API.
Листинг 4.5 демонстрирует использование этих методов, а на рис. 4.3 показан вывод этой программы.
Листинг 4.5. Методы класса Character в программе CharacterTest
class CharacterTest{
public static void main(String[] args){
char ch = ’9’;
Character cl = Character.valueOf(ch);
System.out.println("ch = " + ch);
System.out.println("c1.charValue() = " + cl.charValue());
System.out.println("number of ’A’ = " + Character.digit('A', 16));
System.out.println("digit for 12 = " +
Character.forDigit(12, 16));
System.out.println("c1 = " + c1.toString());
System.out.println("ch isDefined? " +
Character.isDefined(ch));
System.out.println("ch isDigit? " +
Character.isDigit(ch));
System.out.println("ch isIdentifierIgnorable? " + Character.isIdentifierIgnorable(ch));
System.out.println("ch isISOControl? " + Character.isISOControl(ch));
System.out.println("ch isJavaIdentifierPart? " + Character.isJavaIdentifierPart(ch));
System.out.println("ch isJavaIdentifierStart? " + Character.isJavaIdentifierStart(ch)) ;
System.out.println("ch isLetter? " + Character.isLetter(ch));
System.out.println("ch isLetterOrDigit? " + Character.isLetterOrDigit(ch));
System.out.println("ch isLowerCase? " + Character.isLowerCase(ch));
System.out.println("ch isSpaceChar? " + Character.isSpaceChar(ch));
System.out.println("ch isTitleCase? " + Character.isTitleCase(ch)) ;
System.out.println("ch isUnicodeIdentifierPart? " + Character.isUnicodeIdentifierPart(ch));
System.out.println("ch isUnicodeIdentifierStart? " + Character.isUnicodeIdentifierStart(ch)) ;
System.out.println("ch isUpperCase? " + Character.isUpperCase(ch));
System.out.println("ch isWhitespace? " + Character.isWhitespace(ch));
}
}
Рис. 4.3. Методы класса Character в программе CharacterTest |
В класс Character вложены классы Subset и UnicodeBlock, причем класс UnicodeBlock и еще один класс, InputSubset, являются расширениями класса Subset, как это видно на рис. 4.1. Объекты этого класса содержат подмножества кодировки Unicode.
Следует заметить, что каждая новая версия Java добавляет новые методы в класс Character, поэтому точный состав методов лучше посмотреть по документации.
Вместе с классами-оболочками удобно рассмотреть два класса для работы со сколь угодно большими числами.
Класс BigInteger
Все примитивные целые типы имеют ограниченный диапазон значений. В целочисленной арифметике Java нет переполнения, целые числа приводятся по модулю, равному диапазону значений.
Для того чтобы было можно производить целочисленные вычисления СЃ любой разрядностью, РІ состав Java API введен класс BigInteger, хранящийся РІ пакете java.math. Ртот класс расширяет класс Number, следовательно, РІ нем переопределены методы
doubleValue(), floatValue(), intValue(), longValue(). Методы byteValue() и shortValue() не переопределены, а прямо наследуются от класса Number.
Действия с объектами класса BigInteger не приводят ни к переполнению, ни к приведению по модулю. Если результат операции велик, то число разрядов просто наращивается. Числа хранятся в двоичной форме с дополнительным кодом.
Перед выполнением операции числа выравниваются по длине распространением знакового разряда.
Шесть конструкторов класса создают объект класса BigInteger из строки символов (знака числа и цифр), массива байтов или задают случайное число. Чаще всего используются три конструктора:
□ BigInteger(String value) — объект будет хранить большое целое число, заданное строкой цифр, перед которыми может стоять знак минус;
□ BigInteger(String value, int radix) — задается строка цифр со знаком value, записанная в системе счисления с основанием radix;
□ BigInteger(byte[] value) — объект будет хранить большое целое число, заданное массивом value, содержащим двоичное представление числа в дополнительном коде.
Три константы — zero, one и ten — моделируют нуль, единицу и число десять в операциях с объектами класса BigInteger.
Метод toByteArray() преобразует объект в массив байтов.
Большинство методов класса BigInteger моделируют целочисленные операции и функции, возвращая объект класса BigInteger:
□ abs () — возвращает объект, содержащий абсолютное значение числа, хранящегося в данном объекте this;
□ add (x) — операция сложения this + x;
□ and(x) — операция побитовой конъюнкции this & x;
□ andNot(x) — операция побитовой дизъюнкции с дополнением this & (~x);
□ divide (x) — операция деления this / x;
□ divideAndRemainder (x) - возвращает массив из двух объектов класса BigInteger, со
держащих частное и остаток от деления this на x;
□ gcd(x) — наибольший общий делитель абсолютных значений объекта this и аргумента x;
□ max(x) — наибольшее из значений объекта this и аргумента x;
□ min(x) — наименьшее из значений объекта this и аргумента x;
□ mod(x) — остаток от деления объекта this на аргумент метода x;
□ modInverse (x) — остаток от деления числа, обратного объекту this, на аргумент x;
□ modPow(n, m) — остаток от деления объекта this, возведенного в степень n, на m;
□ multiply(x) — операция умножения this * x;
□ negate () — перемена знака числа, хранящегося в объекте;
□ not () — операция отрицания -this;
□ or(x) — операция побитовой дизъюнкции this | x;
□ pow(n) — операция возведения числа, хранящегося в объекте, в степень n;
□ remainder(x) — операция взятия остатка от деления this % x;
□ shiftLeft (n) — операция сдвига влево this << n;
□ shiftRight (n) — операция арифметического сдвига вправо this >> n;
□ signum() — функция sign(x);
□ subtract (x) — операция вычитания this - x;
в–Ў xor(x ) — операция "исключающее РР›Р" this Р» x.
В листинге 4.6 приведены примеры использования данных методов, а рис. 4.4 показывает результаты выполнения этого листинга.
Листинг 4.6. Методы класса BigInteger В Программе BiglntegerTest
import java.math.BigInteger; class BigIntegerTest{
public static void main(String[] args){
BigInteger a = new BigInteger("99999999999999999"); BigInteger b = new BigInteger("88888888888888888888"); System.out.println("bits in a = " + a.bitLength()); System.out.println("bits in b = " + b.bitLength()); System.out.println("a + b = " + a.add(b)); System.out.println("a & b = " + a.and(b)); System.out.println("a & ~b = " + a.andNot(b)); System.out.println("a / b = " + a.divide(b));
BigInteger[] r = a.divideAndRemainder(b);
System.out.println("a / b: q = " + r[0] + ", r = " + r[1]); System.out.println("gcd(a, b) = " + a.gcd(b)); System.out.println("max(a, b) = " + a.max(b)); System.out.println("min(a, b) = " + a.min(b)); System.out.println("a mod b = " + a.mod(b)); System.out.println("1/a mod b = " + a.modInverse(b)); System.out.println("aAn mod b = " + a.modPow(a, b));
System.out.println("a * b = " + a.multiply(b)); System.out.println("-a = " + a.negate()); System.out.println("~a = " + a.not()); System.out.println("a | b = " + a.or(b)); System.out.println("a л 3 = " + a.pow(3)); System.out.println("a % b = " + a.remainder(b)); System.out.println("a << 3 = " + a.shiftLeft(3)); System.out.println("a >> 3 = " + a.shiftRight(3)); System.out.println("sign(a) = " + a.signum()); System.out.println("a — b = " + a.subtract(b)); System.out.println("a л b = " + a.xor(b));
}
}
Рис. 4.4. Методы класса BigInteger в программе BigIntegerTest |
Обратите внимание на то, что в программу листинга 4.6 надо импортировать пакет
j ava.math.
Класс BigDecimal
Класс BigDecimal расположен в пакете j ava.math. Каждый объект этого класса хранит два целочисленных значения: мантиссу вещественного числа в виде объекта класса BigInteger и неотрицательный десятичный порядок числа типа int. Например, для числа 76,34862 будет храниться мантисса 7 634 862 в объекте класса BigInteger и порядок 5 как целое число типа int. Таким образом, мантисса может содержать любое количество цифр, а порядок ограничен значением константы Integer.MAX_VALUE.
Результат операции над объектами класса BigDecimal округляется по одному из восьми правил, определяемых следующими статическими целыми константами:
□ round_ceiling — округление в сторону большего целого;
□ round_down — округление к нулю, к меньшему по модулю целому значению;
□ round_floor — округление к меньшему целому;
□ round_half_down — округление к ближайшему целому, среднее значение округляется к меньшему целому;
□ round_half_even — округление к ближайшему целому, среднее значение округляется к четному числу;
□ round_half_up — округление к ближайшему целому, среднее значение округляется к большему целому;
□ round_unnecessary — предполагается, что результат будет целым, и округление не понадобится;
□ round_up — округление от нуля, к большему по модулю целому значению.
Три константы — zero, one и ten — моделируют вещественные нуль, единицу и вещественное число десять в операциях с объектами класса BigDecimal.
В классе BigDecimal около двадцати конструкторов. Четыре из них были введены еще в Java 2.
□ BigDecimal (BigInteger bi) - объект будет хранить большое целое bi, порядок равен
нулю;
□ BigDecimal(BigInteger mantissa, int scale) — задается мантисса mantissa и неотрицательный порядок scale объекта; если порядок scale отрицателен, возникает исключительная ситуация;
□ BigDecimal(double d) — объект будет содержать вещественное число удвоенной точности d; если значение d бесконечно или NaN, то возникает исключительная ситуация;
□ BigDecimal (String val) - число задается строкой символов val, которая должна со
держать запись числа по правилам языка Java.
При использовании третьего из перечисленных конструкторов возникает неприятная особенность, отмеченная в документации. Поскольку вещественное число при переводе в двоичную форму представляется, как правило, бесконечной двоичной дробью, то при создании объекта, например BigDecimal (0.1), мантисса, хранящаяся в объекте, окажется очень большой. Она показана на рис. 4.5. Но при создании такого же объекта четвертым конструктором, BigDecimal ("0.1"), мантисса будет равна просто 1.
Остальные конструкторы определяют точность представления числового значения объекта и правила его округления с помощью объекта класса MathContext или непосредственно.
В классе переопределены методы doubleValue (), floatValue (), intValue (), longValue ( ).
Три константы — zero, one и ten — моделируют нуль, единицу и число десять в операциях с объектами класса BigDecimal.
Большинство методов этого класса моделируют операции с вещественными числами. Они возвращают объект класса BigDecimal. Ниже в описании методов буква x обозначает объект класса BigDecimal, буква n — целое значение типа int, буква r — способ округления, одну из восьми перечисленных ранее констант:
□ abs () — абсолютное значение объекта this;
□ add (x) — операция сложения this + x;
□ divide (x, r) — операция деления this / x с округлением по способу r;
□ divide (x, n, r) — операция деления this / x с изменением порядка и округлением по способу r;
□ max(x) — наибольшее из this и x;
□ min(x) — наименьшее из this и x;
□ movePointLeft (n) — сдвиг влево на n разрядов;
□ movePointRight(n) — сдвиг вправо на n разрядов;
□ multiply(x) — операция умножения this * x;
□ negate () — возвращает объект с обратным знаком;
□ scale () — возвращает порядок числа;
□ setScale(n) — устанавливает новый порядок n;
□ setScale (n, r) — устанавливает новый порядок n и округляет число при необходимости по способу r;
□ signum () — знак числа, хранящегося в объекте;
□ subtract (x) — операция вычитания this — x;
□ toBiginteger () — округление числа, хранящегося в объекте;
□ unscaledValue () — возвращает мантиссу числа;
□ upl () — возвращает расстояние до следующего числа.
Листинг 4.7 показывает примеры использования этих методов, а рис. 4.5 — вывод результатов.
Начиная с версии Java SE 5 в класс BigDecimal введено еще много методов преобразования объекта и получения его характеристик.
Листинг 4.7. Методы класса BigDecimal в программе BigDecimalTest
import java.math.*; class BigDecimalTest{
public static void main(String[] args){
BigDecimal x = new BigDecimal("-12345.67890123456789");
BigDecimal y = new BigDecimal("345.7896e-4");
BigDecimal z = new BigDecimal(new BigInteger("123456789"), 8); System.out.println("|x| = " + x.abs());
System.out.println("x + y = " + x.add(y));
System.out.println("x / y = " + x.divide(y, BigDecimal.ROUND DOWN)); System.out.println("x / y = " + x.divide(y, 6, BigDecimal.ROUND HALF EVEN)); System.out.println("max(x, y) = " + x.max(y));
System.out.println("min(x, y) = " + x.min(y));
System.out.println("x << 3 = " + x.movePointLeft(3)); System.out.println("x >> 3 = " + x.movePointRight(3)); System.out.println("x * y = " + x.multiply(y)); System.out.println("-x = " + x.negate());
System.out.println("scale of x = " + x.scale());
System.out.println("increase scale of x to 20 = " + x.setScale(20)); System.out.println("decrease scale of x to 10 = " + x.setScale(10, BigDecimal.ROUND HALF UP));
System.out.println("sign(x) = " + x.signum());
System.out.println("x — y = " + x.subtract(y)); System.out.println("round x = " + x.toBigInteger()); System.out.println("mantissa of x = " + x.unscaledValue()); System.out.println("mantissa of 0.1 =\n= " +
new BigDecimal(0.1).unscaledValue());
}
}
Рис. 4.5. Методы класса BigDecimal в программе BigDecimalTest |
Приведем еще один пример. Напишем простенький калькулятор, выполняющий четыре арифметических действия с числами любой величины. Он работает из командной строки. Программа представлена в листинге 4.8, а примеры использования калькулятора — на рис. 4.6.
Листинг 4.8. Простейший калькулятор
import java.math.*; class Calc{
public static void main(String[] args){ if (args.length < 3){
System.err.println("Usage: java Calc operand operator operand"); return;
}
BigDecimal a = new BigDecimal(args[0]);
BigDecimal b = new BigDecimal(args[2]); switch (args[1].charAt(0)){
case | ' + ': |
case | '-': |
case | '*': |
case | в– /' : |
default : |
System.out.println(a.add(b)); break;
System.out.println(a.subtract(b)); break; System.out.println(a.multiply(b)); break; System.out.println(a.divide(b,
BigDecimal.ROUND_HAL F_EVEN)); break; System.out.println("Invalid operator");
}
Рис. 4.6. Результаты работы калькулятора |
Почему символ умножения — звездочка — заключен на рис. 4.6 в кавычки? Приверженцам ОС UNIX это понятно, а для других дадим краткое пояснение.
Рто особенность операционной системы, Р° РЅРµ языка Java. Введенную СЃ клавиатуры строку вначале просматривает командная оболочка (shell) операционной системы, Р° звездочка для нее — указание подставить РЅР° это место РІСЃРµ имена файлов РёР· текущего каталога. Оболочка сделает это, Рё интерпретатор Java получит РѕС‚ нее длинную строку, РІ которой вместо звездочки стоят имена файлов, отделенные РґСЂСѓРі РѕС‚ РґСЂСѓРіР° пробелом.
Звездочка в кавычках понимается командной оболочкой как обычный символ. Командная оболочка снимает кавычки и передает интерпретатору Java звездочку, что нам и надо.
Класс Class
Класс Object, стоящий во главе иерархии классов Java, представляет все объекты, действующие в системе, является их общей оболочкой. Всякий объект можно считать экземпляром класса Object.
Класс с именем Class представляет характеристики класса, экземпляром которого является объект. Он хранит информацию о том, не является ли объект на самом деле интерфейсом, массивом, перечислением или примитивным типом, каков суперкласс объекта, каково имя класса, какие в нем конструкторы, поля, методы и вложенные классы.
В классе Class нет конструкторов, экземпляр этого класса создается исполняющей системой Java во время загрузки класса и предоставляется методом getclass () класса Object, например:
String s = "Рто строка";
Class c = s.getClass();
Таким образом, у каждого действующего в программе объекта есть ссылка на экземпляр класса Class, содержащий описание класса этого объекта. Такое свойство объекта называется рефлексией (reflection). Кроме того, мы можем получить такую ссылку на классы по их имени.
Статический метод forName(String class) класса Class возвращает объект класса Class для класса, указанного в аргументе, например:
Class c1 = Class.forName("java.lang.String");
Третий способ получения экземпляра класса Class — к имени класса через точку добавить слово class:
Class c2 = java.lang.String.class;
Логические методы isAnnotation(), isArray(), isInterface(), isEnum(), isPrimitive() позволяют уточнить, не является ли объект аннотацией, массивом, интерфейсом, перечислением или примитивным типом.
Если объект ссылочного типа, то можно извлечь сведения о вложенных классах, конструкторах, методах и полях методами getDeclaredClasses(), getDeclaredConstructors(), getDeclaredMethods(), getDeclaredFields() в виде массива классов: Class, Constructor, Method, Field соответственно. Последние три класса расположены в пакете j ava. lang.reflect и содержат сведения о конструкторах, полях и методах аналогично тому, как класс Class хранит сведения о классах.
Методы getClasses (), getConstructors (), getInterfaces (), getMethods (), getFields ( ) возвращают такие же массивы, но не всех, а только открытых членов класса.
Метод getSuperclass() возвращает суперкласс объекта ссылочного типа, getPackage ( ) — пакет, getModifiers() — модификаторы класса в битовой форме. Модификаторы можно затем расшифровать методами класса Modifier из пакета java.lang.reflect.
Листинг 4.9 показывает применение этих методов, а рис. 4.7 — вывод результатов.
Листинг 4.9. Методы класса class в программе ciassTest
import java.lang.reflect.*; class ClassTest{
public static void main(String[] args){
Class c = null, c1 = null, c2 = null;
Field[] fld = null;
String s = "Some string";
c = s.getClass();
try{
cl = Class.forName("java.lang.String"); // Старый стиль
c2 = java.lang.String.class; if (!c1.isPrimitive())
fld = c1.getDeclaredFields(); }catch(Exception e){}
System.out.println("Superclass c: " System.out.println("Package c: ' System.out.println("Modi fiers c: ' for(int i = 0; i < fld.length; i++) System.out.println(fld[i]);
}
}
// Новый стиль
// Все поля класса String
+ c);
+ c1);
+ c2);
+ c.getSuperclass()); + c.getPackage());
+ c.getModifiers());
Методы, возвращающие свойства классов, вызывают исключительные ситуации, требующие обработки. Поэтому в программу введен блок try{}catch(){}. Рассмотрение обработки исключительных ситуаций мы откладываем до главы 21.
Рис. 4.7. Методы класса Class в программе ClassTest |
Начиная с версии Java SE 5 класс Class сделан настраиваемым: Class
Вопросы для самопроверки
1. Зачем кроме примитивных типов в язык Java введены еще соответствующие классы-оболочки?
2. Можно ли использовать объекты числовых классов-оболочек в арифметических выражениях?
3. Какое наибольшее целое значение можно занести в объект класса BigInteger?
4. Какое наибольшее вещественное значение можно занести в объект класса BigDecimal?
5. Можно ли использовать в одном выражении значения примитивных типов и распакованные значения числовых классов-оболочек?
6. Для чего в язык Java введены настраиваемые типы?
7. Можно ли создавать настраиваемые интерфейсы или настраиваемыми могут быть только классы?
8. Должны ли методы настраиваемого класса быть настраиваемыми?
9. Можно ли создавать настраиваемые методы в обычных, не настраиваемых классах?
ГЛАВА 5