15

15


Сдвиги можно совмещать со знаком равенства («=, или »=, или »>=). Име¬нующее выражение заменяется им же, но с проведенными над ним операциями сдвига. Однако при этом возникает проблема с оператором беззнакового право¬го сдвига, совмещенного с присвоением. При использовании его с типом byte или short вы не получите правильных результатов. Вместо этого они сначала будут преобразованы к типу int и сдвинуты вправо, а затем обрезаны при воз¬вращении к исходному типу, и результатом станет -1. Следующий пример де¬монстрирует это:
//: operators/URShi ft java // Проверка беззнакового сдвига вправо, import static net.mindview.util Print *;
public class URShift {
public static void main(String[] args) { int i = -1;
print(Integer.toBinaryString(i)); i »>= 10;
print(Integer.toBinaryString(i)), long 1 = -1;
print(Long.toBinaryString(l)); 1 »>= 10;
print(Long.toBinaryString(l)); short s = -1;
printdnteger toBinaryString(s)); s »>= 10;
pri nt(Integer.toBi na ryStri ng(s)), byte b = -1;
printdnteger toBinaryString(b)); b »>= 10;
pri nt(Integer.toBi na ryString(b)); b = -1;
pri nt(Integer.toBi na rySt ring(b)), pri nt (I nteger. toBi narySt ri ng( b»>10));
}
} /* Output; 11111111111111111111111111111111 1111111111111111111111 1111111111111111111111111111111111111111111111111111111111111111 111111111111111111111111111111111111111111111111111111 11111111111111111111111111111111 11111111111111111111111111111111 11111111111111111111111111111111 11111111111111111111111111111111 11111111111111111111111111111111 1111111111111111111111
В последней команде программы полученное значение не приводится обрат¬но к Ь, поэтому получается верное действие.
Следующий пример демонстрирует использование всех операторов, так или иначе связанных с поразрядными операциями:
//; operators/BitManipulation.java // Использование поразрядных операторов, import java.util.*;
import static net.mindview.util Print.*;
public class BitManipulation {
public static void main(String[] args) { Random rand = new Random(47);
int i = rand nextlntO, int j = rand.nextlntO. printBinarylntC'-l", -1); printBinarylntC'+l", +1); int maxpos = 2147483647; printBinaryInt("макс положит.", maxpos); int maxneg = -2147483648; printBinarylntC'MaKc отрицат.". maxneg); printBinarylntC'i". i); printBinaryInt("~i", ~i); printBinaryInt("-i", -i); printBinarylntC'j", j), printBinarylntC'i & j". i & j), printBinarylntC'i | j", i | j); printBinarylntC'i A j". i A j); printBinarylntC'i « 5", i «5); printBinarylntC'i » 5", i » 5): printBinarylntC'M) » 5", (~i) » 5); printBinarylntC'i »> 5", i »> 5); printBinarylntC'M) »> 5", (~i) »> 5);
long 1 = rand.nextLongO; long m = rand.nextLongO; printBinaryLong("-lL", -1L); printBinaryLong("+lL", +1L): long 11 = 9223372036854775807L, printBinaryLongC'MaKC. положит.", 11); long 1 In = -9223372036854775808L; printBinaryLongC'MaKC. отрицат ", lln); printBinaryLongC'l", 1); printBinaryLong("~l", -1), printBinaryLong("-l", -1), printBi naryLongC'm", m), printBinaryLongC'l & m", 1 & m); printBinaryLongC'l | m", 1 | m); printBinaryLongC'l A m", 1 A m); printBinaryLongC'l « 5", 1 «5); printBinaryLongC'l » 5". 1 » 5); printBinaryLong("(~l) » 5", (~1) » 5). printBinaryLongC'l »> 5". 1 »> 5); printBi naryLongC'H) »> 5", (~1) »> 5); moni tor.expect("Bi tMani pulati on.out");
}
static void printBinaryInt(String s, int i) {
print(s + ". int: " + i + двоичное: \n " + Integer toBinaryString(i));
}
static void printBinaryLong(String s, long 1) {
print(s + \ long: " + 1 + ", двоичное:\п " + Long.toBinaryStringd));
}
} /* Output-
-1, int- -1, двоичное.
11111111111111111111111111111111 +1, int. 1, двоичное. 1
макс, положит . int- 2147483647. двоичное: 1111111111111111111111111111111
макс, отрицат., int: -2147483648, двоичное- 10000000000000000000000000000000 i, int- -1172028779. двоичное: 10111010001001000100001010010101 ~i, int. 1172028778, двоичное. 1000101110110111011110101101010 -i, int. 1172028779. двоичное: 1000101110110111011110101101011 j. int: 1717241110, двоичное: 1100110010110110000010100010110 i & j, int- 570425364, двоичное: 100010000000000000000000010100 i | j, int. -25213033, двоичное. 11111110011111110100011110010111 i A j. int: -595638397, двоичное. 11011100011111110100011110000011 i «5, int: 1149784736, двоичное- 1000100100010000101001010100000 i » 5, int -36625900, двоичное. 11111101110100010010001000010100 (~i) » 5, int 36625899, двоичное. 10001011101101110111101011 i »> 5, int. 97591828, двоичное. 101110100010010001000010100 (~i) »> 5, int- 36625899, двоичное. 10001011101101110111101011
*///.-
Два метода в конце, printBinaryInt() и printBinaryl_ong(), получают в качестве параметров, соответственно, числа int и long и выводят их в двоичном формате вместе с сопроводительным текстом. Вместе с демонстрацией поразрядных опе¬раций для типов int и long этот пример также выводит минимальное и макси¬мальное значение, +1 и -1 для этих типов, чтобы вы лучше понимали, как они выглядят в двоичном представлении. Заметьте, что старший бит обозначает знак: 0 соответствует положительному и 1 — отрицательному числам. Резуль¬тат работы для типа int приведен в конце листинга
Тернарный оператор «если-иначе»
Тернарный оператор необычен тем, что он использует три операнда. И все же это действительно оператор, так как он производит значение, в отличие от обычной конструкции выбора if-else, описанной в следующем разделе. Выра¬жение записывается в такой форме:
логическое-условие ? выражениеО : выражение1
Если логическое-условие истинно (true), то затем вычисляется выражениеО, и именно его результат становится результатом выполнения всего оператора. Если же логическое-условие ложно (false), то вычисляется выражение1, и его зна¬чение становится результатом работы оператора.
Конечно, здесь можно было бы использовать стандартную конструкцию if-else (описываемую чуть позже), но тернарный оператор гораздо компактнее. Хотя С (где этот оператор впервые появился) претендует на звание лаконичного языка, и тернарный оператор вводился отчасти для достижения этой цели, будьте благоразумны и не используйте его всюду и постоянно — он может ухуд¬шить читаемость программы.
Операторы + и += для String
В Java существует особый случай использования оператора: операторы + и += могут применяться для конкатенации (объединения) строк, и вы уже это видели. Такое действие для этих операторов выглядит вполне естественно, хотя оно и не соответствует традиционным принципам их использования.
При создании С++ в язык была добавлена возможность перегрузки операто¬ров, позволяющей программистам С++ изменять и расширять смысл почти лю¬бого оператора. К сожалению, перегрузка операторов, в сочетании с некоторы¬ми ограничениями С++, создала немало проблем при проектировании классов. Хотя реализацию перегрузки операторов в Java можно было осуществить проще, чем в С++ (это доказывает язык С#, где существует простой механиз перегруз¬ки), эту возможность все же посчитали излишне сложной, и поэтому програм¬мистам на Java не дано реализовать свои собственные перегруженные опера¬торы, как это делают программисты на С++.
Использование + и += для строк (String) имеет интересные особенности. Если выражение начинается строкой, то все последующие операнды также должны быть строками (помните, что компилятор превращает символы в ка¬вычках в объект String).
int х = 0. у = 1. z = 2;
String s = "х. у, z
System.out.println(s + x + у + z),
В данном случае компилятор Java приводит переменные х, у и z к их строко¬вому представлению, вместо того чтобы сначала арифметически сложить их. А если вы запишете
System.out.println(x + s); то и здесь Java преобразует х в строку.
Типичные ошибки при использовании операторов
Многие программисты склонны второпях записывать выражение без скобок, даже когда они не уверены в последовательности вычисления выражения. Это верно и для Java.
Еще одна распространенная ошибка в С и С++ выглядит следующим об¬разом:
while(x = у) { // .
}
Программист хотел выполнить сравнение (==), а не присвоение. В С и С++ результат этого выражения всегда будет истинным, если только у не окажется нулем; вероятно, возникнет бесконечный цикл. В языке Java результат такого выражения не будет являться логическим типом (boolean), а компилятор ожи¬дает в этом выражении именно boolean и не разрешает использовать целочис¬ленный тип int, поэтому вовремя сообщит вам об ошибке времени компиляции, упредив проблему еще перед запуском программы. Поэтому подобная ошибка в Java никогда не происходит. (Программа откомпилируется только в одном случае: если х и у одновременно являются типами boolean, и тогда выражение х = у будет допустимо, что может привести к ошибке.)
Похожая проблема возникает в С и С++ при использовании поразрядных операторов И и ИЛИ вместо их логических аналогов. Поразрядные И и ИЛИ записываются одним символом (& и |), в то время как логические И и ИЛИ тре¬буют в написании двух символов (&& и ||). Так же, как и в случае с операторами = и ==, легко ошибиться и набрать один символ вместо двух. В Java компилятор предотвращает такие ошибки, так как он не позволяет использовать тип данных в неподходящем контексте.
Операторы приведения
Слово приведение используется в смысле «приведение к другому типу». В опре¬деленных ситуациях Java самостоятельно преобразует данные к другим типам. Например, если вещественной переменной присваивается целое значение, ком¬пилятор автоматически выполняет соответствующее преобразование (int пре¬образуется во float). Приведение позволяет сделать замену типа более очевид¬ной или выполнить ее принудительно в случаях, где это не происходит в обыч¬ном порядке.
Чтобы выполнить приведение явно, запишите необходимый тип данных (включая все модификаторы) в круглых скобках слева от преобразуемого зна¬чения. Пример:
//: operators/Casting.java
public class Casting {
public static void main(String[] args) { int i = 200; long lng = (long)i;
Ing = i; // "Расширение", явное преобразование не обязательно long lng2 = (long)200; lng2 = 200;
// "Сужающее" преобразование i = (int)lng2; // Преобразование необходимо
}
} ///:-
Как видите, приведение может выполняться и для чисел, и для переменных. Впрочем, в указанных примерах приведение является излишним, поскольку компилятор при необходимости автоматически преобразует целое int к типу long. Однако это не мешает вам выполнять необязательные приведения — на¬пример, чтобы подчеркнуть какое-то обстоятельство или просто для того, что¬бы сделать программу более понятной. В других ситуациях приведение может быть необходимо для нормальной компиляции программы.
В С и С++ приведение могло стать источником ошибок и неоднозначности. В Java приведение безопасно, за одним исключением: при выполнении так на¬зываемого сужающего приведения (то есть от типа данных, способного хранить больше информации, к менее содержательному типу данных), то есть при опас¬ности потери данных. В таком случае компилятор заставляет вас выполнить яв¬ное приведение; фактически он говорит: «это может быть опасно, но, если вы уверены в своей правоте, опишите действие явно». В случае с расширяющим приведением явное описание не понадобится, так как новый тип данных спосо¬бен хранить больше информации, чем прежний, и поэтому потеря данных ис¬ключена.

Report Page