Студопедия  
Главная страница | Контакты | Случайная страница

АвтомобилиАстрономияБиологияГеографияДом и садДругие языкиДругоеИнформатика
ИсторияКультураЛитератураЛогикаМатематикаМедицинаМеталлургияМеханика
ОбразованиеОхрана трудаПедагогикаПолитикаПравоПсихологияРелигияРиторика
СоциологияСпортСтроительствоТехнологияТуризмФизикаФилософияФинансы
ХимияЧерчениеЭкологияЭкономикаЭлектроника

Целочисленные типы

Читайте также:
  1. Целочисленные переменные.

Целочисленные типы – это byte, short, int, long, также к ним относят и char. Первые четыре типа имеют длину 1, 2, 4 и 8 байт соответственно, длина char – 2 байта, это непосредственно следует из того, что все символы Java описываются стандартом Unicode. Длины типов приведены только для оценки областей значения. Как уже говорилось, память в Java представляется виртуальной и вычислить, сколько физических ресурсов займет та или иная переменная, так прямолинейно не получится.

4 основных типа являются знаковыми. char добавлен к целочисленным типам данных, так как с точки зрения JVM символ и его код – понятия взаимооднозначные. Конечно, код символа всегда положительный, поэтому char – единственный беззнаковый тип. Инициализировать его можно как символьным, так и целочисленным литералом. Во всем остальном char – полноценный числовой тип данных, который может участвовать, например, в арифметических действиях, операциях сравнения и т.п. В таблице 4.1 сведены данные по всем разобранным типам:

Таблица 4.1. Целочисленные типы данных.
Название типа Длина (байты) Область значений
byte   -128.. 127
short   -32.768.. 32.767
int   -2.147.483.648.. 2.147.483.647
long   -9.223.372.036.854.775.808.. 9.223.372.036.854.775.807 (примерно 1019)
char   '\u0000'.. '\uffff', или 0.. 65.535

Обратите внимание, что int вмещает примерно 2 миллиарда, а потому подходит во многих случаях, когда не требуются сверхбольшие числа. Чтобы представить себе размеры типа long, укажем, что именно он используется в Java для отсчета времени. Как и во многих языках, время отсчитывается от 1 января 1970 года в миллисекундах. Так вот, вместимость long позволяет отсчитывать время на протяжении миллионов веков(!), причем как в будущее, так и в прошлое.

Почему были выделены именно эти два типа, int и long? Дело в том, что целочисленные литералы имеют тип int по умолчанию, или тип long, если стоит буква L или l. Именно поэтому корректным литералом считается только такое число, которое укладывается в 4 или 8 байт, соответственно. Иначе компилятор сочтет это ошибкой. Таким образом, следующие литералы являются корректными:

1-21474836482147483648L0L111111111111111111L

Над целочисленными аргументами можно производить следующие операции:

Операторы сравнения вполне очевидны и отдельно мы их рассматривать не будем. Их результат всегда булева типа (true или false).

Работа числовых операторов также понятна, к тому же пояснялась в предыдущей лекции. Единственное уточнение можно сделать относительно операторов + и -, которые могут быть как бинарными (иметь два операнда), так и унарными (иметь один операнд). Бинарные операнды являются операторами сложения и вычитания, соответственно. Унарный оператор + возвращает значение, равное аргументу (+x всегда равно x). Унарный оператор -, примененный к значению x, возвращает результат, равный 0-x. Неожиданный эффект имеет место в том случае, если аргумент равен наименьшему возможному значению примитивного типа.

int x=-2147483648; // наименьшее возможное // значение типа intint y=-x;

Теперь значение переменной y на самом деле равно не 2147483648, поскольку такое число не укладывается в область значений типа int, а в точности равно значению x! Другими словами, в этом примере выражение -x==x истинно!

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

int x= 300000;print(x*x);

Результатом такого примера будет:

-194313216

Возвращаясь к инвертированию числа -2147483648, мы видим, что математический результат равен в точности +231, или, в двоичном формате, 1000 0000 0000 0000 0000 0000 0000 0000 (единица и 31 ноль). Но тип int рассматривает первую единицу как знаковый бит, и результат получается равным -2147483648.

Таким образом, явное выписывание в коде литералов, которые слишком велики для используемых типов, приводит к ошибке компиляции (см. лекцию 3). Если же переполнение возникает в результате выполнения операции, "лишние" биты просто отбрасываются.

Подчеркнем, что выражение типа -5 не является целочисленным литералом. На самом деле оно состоит из литерала 5 и оператора -. Напомним, что некоторые литералы (например, 2147483648) могут встречаться только в сочетании с унарным оператором -.

Кроме того, числовые операции в Java обладают еще одной особенностью. Хотя целочисленные типы имеют длину 8, 16, 32 и 64 бита, вычисления проводятся только с 32-х и 64-х битной точностью. А это значит, что перед вычислениями может потребоваться преобразовать тип одного или нескольких операндов.

Если хотя бы один аргумент операции имеет тип long, то все аргументы приводятся к этому типу и результат операции также будет типа long. Вычисление будет произведено с точностью в 64 бита, а более старшие биты, если таковые появляются в результате, отбрасываются.

Если же аргументов типа long нет, то вычисление производится с точностью в 32 бита, и все аргументы преобразуются в int (это относится к byte, short, char). Результат также имеет тип int. Все биты старше 32-го игнорируются.

Никакого способа узнать, произошло ли переполнение, нет. Расширим рассмотренный пример:

int i=300000;print(i*i); // умножение с точностью 32 битаlong m=i;print(m*m); // умножение с точностью 64 битаprint(1/(m-i)); // попробуем получить разность // значений int и long

Результатом такого примера будет:

-19431321690000000000

затем мы получим ошибку деления на ноль, поскольку переменные i и m хоть и разных типов, но хранят одинаковое математическое значение и их разность равна нулю. Первое умножение производилось с точностью в 32 бита, более старшие биты были отброшены. Второе – с точностью в 64 бита, ответ не исказился.

Вопрос приведения типов, и в том числе специальный оператор для такого действия, подробно рассматривается в следующих лекциях. Однако здесь хотелось бы отметить несколько примеров, которые не столь очевидны и могут создать проблемы при написании программ. Во-первых, подчеркнем, что результатом операции с целочисленными аргументами всегда является целое число. А значит, в следующем примере

double x = 1/2;

переменной x будет присвоено значение 0, а не 0.5, как можно было бы ожидать. Подробно операции с дробными аргументами рассматриваются ниже, но чтобы получить значение 0.5, достаточно написать 1./2 (теперь первый аргумент дробный и результат не будет округлен).

Как уже упоминалось, время в Java измеряется в миллисекундах. Попробуем вычислить, сколько миллисекунд содержится в неделе и в месяце:

print(1000*60*60*24*7); // вычисление для неделиprint(1000*60*60*24*30); // вычисление для месяца

Необходимо перемножить количество миллисекунд в одной секунде (1000), секунд – в минуте (60), минут – в часе (60), часов – в дне (24) и дней — в неделе и месяце (7 и 30, соответственно). Получаем:

604800000-1702967296

Очевидно, во втором вычислении произошло переполнение. Достаточно сделать последний аргумент величиной типа long:

print(1000*60*60*24*30L); // вычисление для месяца

Получаем правильный результат:

2592000000

Подобные вычисления разумно переводить на 64-битную точность не на последней операции, а заранее, чтобы избежать переполнения.

Понятно, что типы большей длины могут хранить больший спектр значений, а потому Java не позволяет присвоить переменной меньшего типа значение большего типа. Например, такие строки вызовут ошибку компиляции:

// пример вызовет ошибку компиляцииint x=1;byte b=x;

Хотя для программиста и очевидно, что переменная b должна получить значение 1, что легко укладывается в тип byte, однако компилятор не может вычислять значение переменной x при обработке второй строки, он знает лишь, что ее тип – int.

А вот менее очевидный пример:

// пример вызовет ошибку компиляцииbyte b=1;byte c=b+1;

И здесь компилятор не сможет успешно завершить работу. При операции сложения значение переменной b будет преобразовано в тип int и таким же будет результат сложения, а значит, его нельзя так просто присвоить переменной типа byte.

Аналогично:

// пример вызовет ошибку компиляцииint x=2;long y=3;int z=x+y;

Здесь результат сложения будет уже типа long. Точно так же некорректна такая инициализация:

// пример вызовет ошибку компиляцииbyte b=5;byte c=-b;

Даже унарный оператор " - " проводит вычисления с точностью не меньше 32 бит.

Хотя во всех случаях инициализация переменных приводилась только для примера, а предметом рассмотрения были числовые операции, укажем корректный способ преобразовать тип числового значения:

byte b=1;byte c=(byte)-b;

Итак, все числовые операторы возвращают результат типа int или long. Однако существует два исключения.

Первое из них – операторы инкрементации и декрементации. Их действие заключается в прибавлении или вычитании единицы из значения переменной, после чего результат сохраняется в этой переменной и значение всей операции равно значению переменной (до или после изменения, в зависимости от того, является оператор префиксным или постфиксным). А значит, и тип значения совпадает с типом переменной. (На самом деле, вычисления все равно производятся с точностью минимум 32 бита, однако при присвоении переменной результата его тип понижается.)

byte x=5;byte y1=x++; // на момент начала исполнения x равен 5byte y2=x--; // на момент начала исполнения x равен 6byte y3=++x; // на момент начала исполнения x равен 5byte y4=--x; // на момент начала исполнения x равен 6print(y1);print(y2);print(y3);print(y4);

В результате получаем:

5665

Никаких проблем с присвоением результата операторов ++ и -- переменным типа byte. Завершая рассмотрение этих операторов, приведем еще один пример:

byte x=-128;print(-x); byte y=127;print(++y);

Результатом будет:

128-128

Этот пример иллюстрирует вопросы преобразования типов при вычислениях и случаи переполнения.

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

byte x=2;byte y=3;byte z=(x>y)? x: y; // верно, x и y одинакового типаbyte abs=(x>0)? x: -x; // неверно!

Последняя строка неверна, так как третий аргумент содержит числовую операцию, стало быть, его тип int, а значит, и тип всей операции будет int, и присвоение некорректно. Даже если второй аргумент имеет тип byte, а третий – short, значение будет типа int.

Наконец, рассмотрим оператор конкатенации со строкой. Оператор + может принимать в качестве аргумента строковые величины. Если одним из аргументов является строка, а вторым – целое число, то число будет преобразовано в текст и строки объединятся.

int x=1;print("x="+x);

Результатом будет:

x=1

Обратите внимание на следующий пример:

print(1+2+"text");print("text"+1+2);

Его результатом будет:

3texttext12

Отдельно рассмотрим работу с типом char. Значения этого типа могут полноценно участвовать в числовых операциях:

char c1=10;char c2='A'; // латинская буква A (\u0041, код 65)int i=c1+c2-'B';

Переменная i получит значение 9.

Рассмотрим следующий пример:

char c='A';print(c);print(c+1);print("c="+c);print('c'+'='+с);

Его результатом будет:

A66c=A225

В первом случае в метод print было передано значение типа char, поэтому отобразился символ. Во втором случае был передан результат сложения, то есть число, и именно число появилось на экране. Далее при сложении со строкой тип char был преобразован в текст в виде символа. Наконец в последней строке произошло сложение трех чисел: 'c' (код 99), '=' (код 61) и переменной c (т.е. код 'A' - 65).

Для каждого примитивного типа существуют специальные вспомогательные классы-обертки (wrapper classes). Для типов byte, short, int, long, char это Byte, Short, Integer, Long, Character. Эти классы содержат многие полезные методы для работы с целочисленными значениями. Например, преобразование из текста в число. Кроме того, есть класс Math, который хоть и предназначен в основном для работы с дробными числами, но также предоставляет некоторые возможности и для целых.

В заключение подчеркнем, что единственные операции с целыми числами, при которых Java генерирует ошибки,– это деление на ноль (операторы / и %).




Дата добавления: 2014-11-24; просмотров: 27 | Поможем написать вашу работу | Нарушение авторских прав




lektsii.net - Лекции.Нет - 2014-2024 год. (0.01 сек.) Все материалы представленные на сайте исключительно с целью ознакомления читателями и не преследуют коммерческих целей или нарушение авторских прав