Н.А. Вязовик - Программирование на Java
Скачивание начинается... Если скачивание не началось автоматически, пожалуйста нажмите на эту ссылку.
Жалоба
Напишите нам, и мы в срочном порядке примем меры.
Описание книги "Программирование на Java"
Описание и краткое содержание "Программирование на Java" читать бесплатно онлайн.
Теперь значение переменной y на самом деле равно не 2147483648, поскольку такое число не укладывается в область значений типа int, а в точности равно значению x! Другими словами, в этом примере выражение -x==x истинно!
Дело в том, что если при выполнении числовых операций над целыми числами возникает переполнение и результат не может быть сохранен в данном примитивном типе, то Java не создает никаких ошибок. Вместо этого все старшие биты, которые превышают вместимость типа, просто отбрасываются. Это может привести не только к потере точной абсолютной величины результата, но даже к искажению его знака, если на месте знакового бита окажется противоположное значение.
int x= 300000;
print(xx);
Результатом такого примера будет:
-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
Результатом такого примера будет:
-194313216
90000000000
затем мы получим ошибку деления на ноль, поскольку переменные 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 равен 5
byte y2=x--;
// на момент начала исполнения x равен 6
byte y3=++x;
// на момент начала исполнения x равен 5
byte y4=--x;
// на момент начала исполнения x равен 6
print(y1);
print(y2);
print(y3);
print(y4);
В результате получаем:
5
6
6
5
Никаких проблем с присвоением результата операторов ++ и -- переменным типа 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);
Его результатом будет:
3text
text12
Отдельно рассмотрим работу с типом 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'+'='+с);
Его результатом будет:
A
66
c=A
225
В первом случае в метод print было передано значение типа char, поэтому отобразился символ. Во втором случае был передан результат сложения, то есть число, и именно число появилось на экране. Далее при сложении со строкой тип char был преобразован в текст в виде символа. Наконец в последней строке произошло сложение трех чисел: 'c' (код 99), '=' (код 61) и переменной c (т.е. код 'A' - 65 ).
Подписывайтесь на наши страницы в социальных сетях.
Будьте в курсе последних книжных новинок, комментируйте, обсуждайте. Мы ждём Вас!
Похожие книги на "Программирование на Java"
Книги похожие на "Программирование на Java" читать онлайн или скачать бесплатно полные версии.
Мы рекомендуем Вам зарегистрироваться либо войти на сайт под своим именем.
Отзывы о "Н.А. Вязовик - Программирование на Java"
Отзывы читателей о книге "Программирование на Java", комментарии и мнения людей о произведении.