Текст книги "Java: руководство для начинающих (ЛП)"
Автор книги: Герберт Шилдт
Жанр:
Программирование
сообщить о нарушении
Текущая страница: 11 (всего у книги 36 страниц)
Для извлечения элементов из очереди служит метод get (), код которого приведен ниже.// извлечь символ из очереди char get () { if(getloc == putloc) { System.out.println(" – Queue is empty."); return (char) 0; } getloc++; return q[getloc]; } Сначала в данном методе проверяется, пуста ли очередь. Если значения индексов в переменных getloc и putloc совпадают, то в очереди нет ни одного элемента. Именно поэтому в конструкторе Queue переменные getloc и putloc инициализируются нулевыми значениями. Если очередь не пуста, то переменная getloc инкрементируется и из нее извлекается очередной элемент. Следовательно, переменная getloc содержит индекс последнего извлеченного элемента.
Ниже приведен весь исходный код программы из файла QDemo.java. /* Пример для опробования 5.2. Класс, реализующий очередь, для хранения символов. */ class Queue { char q[]; // Массив для хранения элементов очереди int putloc, getloc; // Индексы размещения и извлечения элементов очереди Queue(int size) { q = new char[size+1]; // выделить память для очереди putloc = getloc = 0; } // поместить символ в очередь void put(char ch) { if(putloc==q.length-1) { System.out.println(" – Queue is full."); return; } putloc++; q[putloc] = ch; } // извлечь символ из очереди char get() { if(getloc == putloc) { System.out.println(" – Queue is empty."); return (char) 0; } getloc++; return q[getloc]; } } // продемонстрировать класс Queue class QDemo { public static void main(String args[]) { Queue bigQ = new Queue (100); Queue smallQ = new Queue(4); char ch; int i; System.out.println("Using bigQ to store the alphabet."); // поместить буквенные символы в очередь bigQ for(i=0; i < 26; i++) bigQ.put((char) ('A' + i)); // извлечь буквенные символы из очереди bigQ и отобразить System.out.print("Contents of bigQ: "); for(i=0; i < 26; i++) { ch = bigQ.get(); if(ch != (char) 0) System.out.print(ch); } System.out.println("n"); System.out.println("Using smallQ to generate errors."); // использовать небольшую очередь smallQ для генерации ошибок for(i=0; i < 5; i++) { System.out.print("Attempting to store " + (char) ('Z' – i)); smallQ.put((char) ('Z1 – i)); System.out.println(); } System.out.println(); // дополнительные ошибки при обращении к очереди smallQ System.out.print("Contents of smallQ: "); for(i=0; i < 5; i++) { ch = smallQ.get(); if(ch != (char) 0) System.out.print(ch); } } }
Ниже приведен результат выполнения данной программы. Using bigQ to store the alphabet. Contents of bigQ: ABCDEFGHIJKLMNOPQRSTUVWXYZ Using smallQ to generate errors. Attempting to store Z Attempting to store Y Attempting to store X Attempting to store W Attempting to store V – Queue is full. Contents of smallQ: ZYXW – Queue is empty.
Попробуйте самостоятельно усовершенствовать класс Queue таким образом, чтобы в очереди можно было хранить другие типы данных, например значения типа int или double. Разновидность for-each цикла for
При выполнении операций с массивами очень часто возникают ситуации, когда должен быть обработан каждый элемент массива. Например, для расчета суммы всех значений, содержащихся в массиве, нужно обратиться ко всем его элементам. То же самое приходится делать при расчете среднего значения, поиске элемента и решении многих других задач. В связи с тем что задачи, предполагающие обработку всего массива, встречаются очень часто, в Java была реализована еще одна разновидность цикла for, рационализирующая подобные операции с массивами.
Вторая разновидность оператора for реализует цикл типа for-each. В этом цикле происходит последовательное обращение к каждому элементу совокупности объектов (например, массива). За последние годы циклы for-each появились практически во всех языках программирования. Изначально в Java подобный цикл не был предусмотрен и был реализован лишь в пакете JDK 5. Разновидность for-each цикла for называется также расширенным циклом for. В данной книге употребляются оба эти термина.
Ниже приведена общая форма разновидности for-each цикла for. for(тип intr_var : коллекция) блок_операторов
где тип обозначает конкретный тип intr_var – итерационной переменной, в которой сохраняются перебираемые по очереди элементы набора данных, обозначенного как коллекция. В данной разновидности цикла for могут быть использованы разные типы коллекций, но в этой книге рассматриваются только массивы. На каждом шаге цикла очередной элемент извлекается из коллекции и сохраняется в итерационной переменной. Выполнение гщкла продолжается до тех пор, пока не будут получены все элементы коллекции. Таким образом, при обработке массива размером N в расширенном цикле for будут последовательно извлечены элементы с индексами от 0 до N—1.
Итерационная переменная получает значения из коллекции, и поэтому ее тип должен совпадать (или, по крайней мере, быть совместимым) с типом элементов, которые содержит коллекция. В частности, при обработке массива тип итерационной переменной должен совпадать с типом массива.
Для того чтобы стали понятнее причины, побудившие к внедрению разновидности for-each цикла for в Java, рассмотрим приведенный ниже фрагмент кода, в котором традиционный цикл for используется для вычисления суммы значений элементов массива. int nums[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; int sum = 0; for(int i=0; i < 10; i++) sum += nums[i];
Для того чтобы вычислить упомянутую выше сумму, придется перебрать все элементы массива nums от начала до конца. Перебор элементов осуществляется благодаря использованию переменной цикла i в качестве индекса массива nums. Кроме того, нужно явно указать начальное значение переменной цикла, шаг приращения и условие завершения цикла.
При использовании разновидности for-each данного цикла некоторые перечисленные выше действия выполняются автоматически. В частности, отпадает необходимость в использовании переменной цикла, задании ее исходного значения и условия завершения цикла, а также в индексировании массива. Вместо этого массив автоматически обрабатывается в цикле от начала до конца. Код, позволяющий решить ту же самую задачу с помощью разновидности for-each цикла for, выглядит следующим образом: int nums[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; int sum = 0; for(int x: nums) sum += x;
На каждом шаге этого цикла переменная х автоматически принимает значение, равное очередному элементу массива nums. Сначала ее значение равно 1, на втором шаге цикла итерации оно становится равным 2 и т.д. В данном случае не только упрощается синтаксис, но и исключается ошибка, связанная с превышением границ массива.
Ниже приведен весь исходный код программы, демонстрирующей решение описанной выше задачи с помощью разновидности for-each цикла for. // Использование разновидности for-each цикла for. class ForEach { public static void main(String args[]) { int nums[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; int sum = 0; // использовать разновидность for-each цикла for // для суммирования и отображения значений, for(int х : nums) { System.out.println("Value is: " + x); sum += x; } System.out.println("Summation: " + sum); } }
Результат выполнения данной программы выглядит следующим образом: Value is: 1 Value is: 2 Value is: 3 Value is: 4 Value is: 5 Value is: 6 Value is: 7 Value is: 8 Value is: 9 Value is: 10 Summation: 55
Нетрудно заметить, что в данной разновидности for-each цикла for элементы массива автоматически извлекаются один за другим в порядке возрастания индекса.
Несмотря на то что в разновидности for-each цикла for обрабатываются все элементы массива, этот цикл можно завершить преждевременно, используя оператор break. Так, в цикле, используемом в следующем примере, вычисляется сумма только пяти элементов массива nums: // Суммирование первых 5 элементов массива, for(int х : nums) { System.out.println("Value is: " + x); sum += x; if(x == 5) break; // прервать цикл по достижении значения 5 }
Следует, однако, иметь в виду одну важную особенность разновидности for-each цикла for. Итерационная переменная в этом цикле обеспечивает только чтение элементов массива, но ее нельзя использовать для записи значения в какой-либо элемент массива. Иными словами, изменить содержимое массива, присвоив итерационной переменной новое значение, не удастся. Рассмотрим в качестве примера следующую программу: // Цикл for-each, предназначенный только для чтения, class NoChange { public -static void main (String args [ ] ) { int nums[] = { l, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; for(int x : nums) { System.out.print(x + " "); // Следующая операция не оказывает никакого влияния // на содержимое массива nums. х = х * 10; } System.out.println(); for (int x : nums) System.out.print(x + " "); System.out.println(); } }
В первом цикле for значение итерационной переменной увеличивается на 10, но это не оказывает никакого влияния на содержимое массива nums, что и демонстрирует второй цикл for. Это же подтверждает и результат выполнения программы. 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10 Циклическое обращение к многомерным массивам
Расширенный цикл for можно применять и при обращении к многомерным массивам. Как вам должно быть уже известно, в Java многомерный массив представляет собой массив массивов. (Например, двумерный массив – это массив, элементами которого являются одномерные массивы.) Эту особенность важно помнить, организуя циклическое обращение к многомерным массивам, поскольку на каждом шаге цикла извлекается очередной массив, а не отдельный элемент. Более того, итерационная переменная в расширенном цикле for должна иметь тип, совместимый с типом извлекаемого массива. Так, при обращении к двумерному массиву итерационная переменная должна представлять собой ссылку на одномерный массив. При использовании разновидности for-each цикла for для обработки TV-мерного массива извлекаемый объект представляет собой (Л/"—1)-мерный массив. Для того чтобы сказанное стало более понятным, рассмотрим приведенный ниже пример программы, где для извлечения элементов двумерного массива используются вложенные циклы for. Обратите внимание на то, каким образом объявляется переменная х. // Использование разновидности for-each цикла for // для обработки двумерного массива, class ForEach2 { public static void main(String args[]) { int sum = 0; int nums[][] = new int[3][5]; // ввести ряд значений в массив nums for(int i = 0; i < 3; i++) for(int j=0; j < 5; j++) nums[i][j] = (i+l)*(j+l); // использовать разновидность for-each цикла for // для суммирования и отображения значений // Обратите внимание на объявление переменной х. for (int х[] : nums) { for(int у : x) { System.out.println("Value is: " + y) ; sum += y; } } System.out.println("Summation: " + sum); } }
Выполнение этой программы дает следующий результат: Value is: 1 Value is: 2 Value is: 3 Value is: 4 Value is: 5 Value is: 2 Value is: 4 Value is: 6 Value is: 8 Value is: 10 Value is: 3 Value is: 6 Value is: 9 Value is: 12 Value is: 15 Summation: 90
Обратите особое внимание на следующую строку кода: for(int х[] : nums) {
Не упустите из виду и то, как объявляется переменная х. Она представляет собой ссылку на одномерный целочисленный массив. Это очень важно, поскольку на каждом шаге цикла for из двумерного массива nums извлекается очередной массив, начиная с nums [0]. А во внутреннем цикле for перебираются элементы полученного массива и отображаются их значения. Применение расширенного цикла for
Разновидность for-each цикла for обеспечивает лишь последовательный перебор элементов от начала до конца массива, поэтому может создаться впечатление, будто такой цикл имеет ограниченное применение. Но это совсем не так. Данный механизм циклического обращения применяется в самых разных алгоритмах. Один из самых характерных тому примеров – организация поиска. В приведенном ниже примере программы расширенный цикл for используется для поиска значения в неотсортированном массиве. Выполнение цикла прерывается, если искомый элемент найден. // Поиск в массиве с использованием разновидности for-each цикла for. class Search { public static void main(String args[]) { int nums[] = { 6, 8, 3, 7, 5, 6, 1, 4 }; int val = 5; boolean found = false; // использовать разновидность for-each цикла for // для поиска значения переменной val в массиве nums for(int х : nums) { if (x == val) { found = true; break; } } if(found) System.out.println("Value found!"); } }
В данном случае применение расширенного цикла for вполне оправданно, поскольку найти значение в неотсортированном массиве можно лишь, перебрав все его элементы. (Если бы содержимое массива было предварительно отсортировано, то лучше было бы применить более эффективный алгоритм поиска, например поиск методом дихотомии. В этом случае пришлось бы использовать другой массив.) Расширенным циклом for удобно также пользоваться для расчета среднего значения, нахождения минимального и максимального элементов множества, выявления дублирующихся значений и т.д.
Теперь, когда разновидность for-each цикла for представлена в достаточной степени, она будет еще не раз использоваться там, где это уместно, в примерах программ, представленных в остальной части книги. Символьные строки
В повседневной работе каждый программист обязательно встречается с объектами типа String. Объект типа String определяет символьную строку и поддерживает операции над ней. Во многих языках программирования символьная строка – это лишь массив символов, но в Java это совсем не так, где символьная строка, по существу, является объектом.
Возможно, вы и не заметили, но класс String фактически уже использовался в примерах программ, начиная с главы 1. При создании строкового литерала на самом деле строился объект типа String. Рассмотрим приведенный ниже оператор. System.out.println("In Java, strings are objects.");
Наличие в нем символьной строки "In Java, strings are objects." (B Java строки являются объектами) автоматически приводит к созданию объекта типа String. Таким образом, класс String незримо присутствовал в предыдущих примерах программ. В последующих разделах будет показано, как этим классом пользоваться явным образом. Однако в классе String предусмотрен обширный ряд методов, поэтому здесь будут рассмотрены лишь некоторые из них. Большую часть функциональных возможностей класса String вам предстоит изучить самостоятельно. Построение строк
Объекты типа String создаются таким же образом, как и объекты других типов. Для этой цели используется конструктор, как показано в следующем примере: String str = new String("Hello");
В данном примере создается объект str типа String, содержащий символьную строку "Hello" (Привет). Объект типа String можно создать и на основе другого объекта такого же типа. Например: String str = new String("Hello"); String str2 = new String(str);
После выполнения этих строк кода объект str2 будет также содержать символьную строку "Hello".
Ниже представлен еще один способ создания объекта типа String. String str = "Java strings are powerful.";
В данном случае объект str инициализируется последовательностью символов "Java strings are powerful." (Строки в Java эффективны).
Создав объект типа String, можете использовать его везде, где допускается строковый литерал (последовательность символов, заключенная в кавычки). Например, объект типа String можно передать в качестве параметра методу println () при его вызове, как показано в приведенном ниже примере программы. // Представление класса String, class StringDemo { public static void main(String args[]) { // объявить символьные строки разными способами String strl = new String("Java strings are objects."); String str2 = "They are constructed various ways."; String str3 = new String(str2); System.out.println(strl) ; System.out.println(str2) ; System.out.println(str3) ; } }
Выполнение этой программы дает следующий результат: Java strings are objects. They are constructed various ways. They are constructed various ways. Операции над символьными строками
Класс String содержит ряд методов, предназначенных для выполнения действий над символьными строками. Ниже описаны некоторые из них. boolean equals (str) Возвращает логическое значение true, если текущая строкасодержит ту же последовательность символов, что и параметр str int length () Возвращает длину символьной строки char charAt (index) Возвращает символ, находящийся в строке на позиции, указываемой параметром index int compareTo (String str) Возвращает отрицательное значение, если текущая строкаменьше строки str, нуль, если эти строки равны, и положительное значение, если текущая строка больше строки str int indexOf (String str) Производит в текущей строке поиск подстроки, определяемой параметром str. Возвращает индекс первого вхождения подстроки str или -1, если поиск завершается неудачно int lastlndexOf (String str) Производит в текущей строке поиск подстроки, определяемойпараметром str. Возвращает индекс последнего вхожденияподстроки str или -1, если поиск завершается неудачно
В приведенном ниже примере программы демонстрируется применение перечисленных выше методов, оперирующих символьными строками. // Некоторые операции над символьными строками, class StrOps { public static void main(String args[]) { String strl = "When it comes to Web programming, Java is #1."; String str2 = new String(strl); String str3 = "Java strings are powerful."; int result, idx; char ch; System.out.println("Length of strl: " + strl.length()); // отобразить строку strl посимвольно, for(int i=0; i < strl.length(); i++) System.out.print(strl.charAt(i)); System.out.println(); if (strl.equals(str2) ) System.out.println("strl equals str2"); else System.out.println("strl does not equal str2"); if(strl.equals(str3)) System.out.println("strl equals str3"); else System.out.println("strl does not equal str3"); result = strl.compareTo(str3); if (result == 0) System.out.println("strl and str3 are equal"); else if(result < 0) System.out.println("strl is less than str3"); else System.out.println("strl is greater than str3"); // присвоить переменной str2 новую строку str2 = "One Two Three One"; idx = str2.indexOf("One"); System.out.println("Index of first occurrence of One: " + idx); idx = str2.lastlndexOf("One"); System.out.println("Index of last occurrence of One: " + idx); } }
Выполнение этой программы дает следующий результат: Length of strl: 45 When it comes to Web programming, Java is #1. strl equals str2 strl does not equal str3 strl is greater than str3 Index of first occurrence of One: 0 Index of last occurrence of One: 14
Сцепление (или объединение) двух символьных строк обеспечивает оператор +. Например, в приведенной ниже последовательности операторов присваивания переменная str4 инициализируется символьной строкой "OneTwoThree". String strl = "One"; String str2 = "Two"; String str3 = "Three"; String str4 = strl + str2 + str3; Массивы символьных строк
Подобно другим типам данных, символьные строки можно объединять в массивы.
Ниже приведен характерный тому пример. // Использование массивов символьных строк, class StringArrays { public static void main(String args[]) { // Массив символьных строк. String strs[] = { "This", "is", "a", "test." }; System.out.println("Original array: "); for(String s : strs) System.out.print (s + " "); System.out.println("n"); // изменить символьную строку strs[l] = "was"; strs[3] = "test, too!"; System.out.println("Modified array: "); for(String s : strs) System.out.print(s + " "); } }
Выполнение этого фрагмента кода дает следующий результат: Original array: This is a test. Modified array: This was a test, too! Постоянство строк
Как ни странно, содержимое объекта типа string не подлежит изменению. Это означает, что однажды созданную последовательность символов изменить нельзя. Но данное ограничение способствует более эффективной реализации символьных строк. Поэтому этот, на первый взгляд, очевидный недостаток на самом деле превращается в преимущество. Так, если требуется строка в качестве разновидности уже имеющейся строки, для этой цели следует создать новую строку, содержащую все необходимые изменения. А поскольку неиспользуемые строковые объекты автоматически собираются в “мусор”, то о дальнейшей судьбе ненужных строк можно даже не беспокоиться. Следует, однако, иметь в виду, что переменные ссылки на строки (т.е. объекты типа String) подлежат изменению, а следовательно, могут ссылаться на другой объект. Но содержимое самого объекта типа String не меняется после его создания.
Для того чтобы стало понятнее, почему постоянство строк не является помехой, воспользуемся еще одним методом обращения со строками из класса String. Это метод substring (), возвращающий новую строку, содержащую часть вызывающей строки. В итоге создается новый строковый объект, содержащий выбранную подстроку, тогда как исходная строка не меняется, а следовательно, соблюдается принцип постоянства строк. Ниже приведена рассматриваемая здесь общая форма объявления метода substring(). string substring(int начальный_индекс, int конечный_индекс)
где начальныйиндекс обозначает начало извлекаемой подстроки, а конечныйиндекс – ее окончание. Ниже приведен пример программы, демонстрирующий применение метода substringO и принцип постоянства строк. // Применение метода substringO. class SubStr { public static void main(String args[]) { String orgstr = «Java makes the Web move.»; // сформировать подстроку // Здесь создается новая строка, содержащая нужную подстроку. String substr = orgstr.substring(5, 18); System.out.println("orgstr: " + orgstr); System.out.println("substr: " + substr); } }
Результат выполнения данной программы выглядит следующим образом: orgstr: Java makes the Web move, substr: makes the Web
Как видите, исходная строка orgstr остается без изменения, а новая строка substr содержит сформированную подстроку. Применение строк для управления оператором switch
Как пояснялось в главе 3, до появления версии JDK 7 для управления оператором switch приходилось пользоваться константами типа int или char. Это препятствовало применению оператора switch в тех случаях, когда одно из выполняемых действий выбирается из содержимого символьной строки. В качестве выхода из этого положения зачастую приходилось обращаться к многоступенчатой конструкции if-else-if. И хотя эта конструкция семантически правильна, для организации подобного выбора более естественным было бы применение оператора switch. К счастью, этот недостаток был исправлен. После выпуска комплекта JDK 7 появилась возможность пользоваться объектами типа String для управления оператором switch. И во многих случаях это способствует написанию более удобочитаемого и рационально организованного кода.
Ниже приведен краткий пример программы, демонстрирующий управление оператором switch с помощью объектов типа String. // Использование символьной строки для управления оператором switch, class StringSwitch { public static void main(String args[]) { String command = "cancel"; switch(command) { case "connect": System.out.println("Connecting"); break; case "cancel": System.out.println("Canceling"); break; case "disconnect": System.out.println("Disconnecting"); break; default: System.out.println("Command Error!"); break; } } }
Как и следовало ожидать, выполнение этой программы приводит к следующему результату: Canceling
Символьная строка, содержащаяся в переменной command, а в данном примере это строка "cancel" (отмена), проверяется на совпадение с символьными константами в ветвях case оператора switch. Если совпадение обнаружено, как это имеет место во второй ветви case, выполняется кодовая последовательность, связанная с данной последовательностью действий.
Использование символьных строк в операторе switch может оказаться очень удобным и позволяет сделать код более удобочитаемым. В частности, применение оператора switch, управляемого строками, является более совершенным решением по сравнению с равнозначной последовательностью операторов if/else. Но переход по символьным строкам обходится дороже с точки зрения потребляемых вычислительных ресурсов, чем переход по целочисленным значениям. Поэтому организовывать переход по символьным строкам рекомендуется лишь в тех случаях, когда управление данными уже происходит в строковой форме. Иными словами, пользоваться символьными строками в операторе switch без особой надобности не следует. Употребление аргументов командной строки
А теперь, когда представлен класс String, можно пояснить назначение параметра args метода main () в исходном коде большинства всех рассмотренных ранее примеров программ. Многие программы получают параметры, задаваемые в командной строке. Это так называемы аргументы командной строки. Они представляют собой данные, указываемые непосредственно после имени запускаемой на выполнение программы. Для того чтобы получить доступ к аргументам командной строки из программы на Java, достаточно обратиться к массиву объектов типа String, который передается методу main (). Рассмотрим в качестве примера программу, отображающую параметры командной строки. Ее исходный код приведен ниже. // Отображение всех данных, указываемых в командной строке, class CLDemo { public static void main(String args[]) { System.out.println("There are " + args.length + " command-line arguments."); System.out.println("They are: "); for(int i=0; i Допустим, программа CLDemo была запущена на выполнение из командной строки следующим образом: java CLDemo one two three Тогда ее выполнение приведет к следующему результату: There are 3 command-line arguments. They are: arg[0]: one arg[1]: two arg[2]: three Обратите внимание на то, что первый аргумент содержится в строке, представляющей собой элемент массива с индексом 0. Для доступа ко второму аргументу следует воспользоваться индексом 1 и т.д. Для того чтобы стало понятнее, как пользоваться аргументами командной строки, рассмотрим приведенный ниже пример программы. Эта программа принимает из командной строки один аргумент, определяющий имя абонента, а затем производит поиск имени в двумерном массиве символьных строк. Если имя найдено, программа отображает телефонный номер обнаруженного абонента. // Простейший автоматизированный телефонный справочник. / class Phone { public static void main(String args[]) { String numbers[][] = { { "Tom", "555-3322" }, { "Mary", "555-8976" }, { "Jon", "555-1037" }, { "Rachel", "555-1400" } }; int i; // Для того чтобы воспользоваться программой, // ей нужно передать один аргумент командной строки. if(args.length != 1) System.out.println("Usage: java Phone Выполнение этой программы может дать, например, следующий результат: С>java Phone Mary Mary: 555-8976 Поразрядные операторы В главе 2 были рассмотрены арифметические и логические операторы, а также операторы отношения. Эти три вида операторов используются чаще всего, но в Java предоставляются также поразрядные операторы, которые позволяют расширить границы применения данного языка программирования. Поразрядные операторы можно применять к значениям типа long, int, short, char и byte. А над типами boolean, float, double или типами классов поразрядные операции выполнять нельзя. Эти операторы называются поразрядными потому, что они используются в основном для проверки, установки и сдвига отдельных разрядов числа. Поразрядные операции чрезвычайно важны для решения задач системного программирования, в которых требуется анализировать данные, получаемые из устройства, или формировать значения, передаваемые на устройство. Доступные в Java поразрядные операторы перечислены к табл. 5.1. Таблица 5.1. Поразрядные операторы Оператор Выполняемые действия & Поразрядное И | Поразрядное ИЛИ ^ Поразрядное исключающее ИЛИ >> Сдвиг вправо >>> Сдвиг вправо без знака << Сдвиг влево ~ Дополнение до 1 (унарный оператор НЕ) Поразрядные операторы И, ИЛИ, исключающее ИЛИ и НЕ Поразрядные операторы И, ИЛИ, исключающее ИЛИ и НЕ обозначаются следующим образом: &, |, А и Они выполняют те же функции, что и их логические аналоги, рассмотренные в главе 2. Но в отличие от логических операторов поразрядные операторы действуют на уровне отдельных двоичных разрядов. Ниже приведены результаты поразрядных операций с двоичными единицами и нулями. P Q P & Q P | Q P ^ Q ~P 0 0 0 0 0 1 1 0 0 1 1 0 1 1 1 1 0 0 С точки зрения наиболее распространенного применения поразрядную операцию И можно рассматривать как способ подавления отдельных двоичных разрядов. Это означает, что если какой-нибудь бит в любом из операндов равен 0, то соответствующий бит результата будет сброшен в 0. Например: 1101 ОО11 1010 1010 & 1000 0010 Ниже приведен пример программы, демонстрирующий применение оператора 6. В этой программе строчные буквы английского алфавита преобразуются в прописные путем сброса шестого бита в коде символа. Коды ASCII и уникода (Unicode) строчных букв английского алфавита отличаются от аналогичных кодов прописных букв на вели¬ чину 32. Поэтому для преобразования строчных букв в прописные достаточно сбросить в нуль шестой бит в кодах их символов. // Преобразование строчных букв английского алфавита в прописные, class UpCase { public static void main(String args[]) { char ch; for(int i=0; i < 10; i++) { ch = (char) ('a1 + i) ; System.out.print(ch); // В следующем операторе сбрасывается шестой бит. // После этого в переменной ch будет храниться код // символа прописной буквы, ch = (char) ( (int) ch & 65503); System.out.print(ch + " ") ; } } } Результат выполнения данной программы выглядит следующим образом: аА bВ сС dD еЕ fF gG hH il jJ Значение 65503, используемое в операции поразрядного И, является десятичным представлением двоичного числа 1111111111011111. Таким образом, при выполнении данной операции все биты кода символа в переменой ch, за исключением шестого, остаются без изменения, а шестой бит сбрасывается в нуль. Операция поразрядного И оказывается удобной и в том случае, если требуется выяснить, установлен или сброшен отдельный бит числа. Например, в приведенной ниже строке кода проверяется, установлен ли четвертый бит значения переменной status, if(status & 8) System.out.println("bit 4 is on");