355 500 произведений, 25 200 авторов.

Электронная библиотека книг » Герберт Шилдт » Java: руководство для начинающих (ЛП) » Текст книги (страница 12)
Java: руководство для начинающих (ЛП)
  • Текст добавлен: 6 октября 2016, 05:33

Текст книги "Java: руководство для начинающих (ЛП)"


Автор книги: Герберт Шилдт



сообщить о нарушении

Текущая страница: 12 (всего у книги 36 страниц)

В данном примере число 8 использует потому, что в его двоичном представлении установлен только четвертый бит. Таким образом, в условном операторе if логическое значение true будет получено только в том случае, если четвертый бит значения переменной status также установлен. Подобный подход можно применить и для преобразования значения типа byte в двоичный формат, как показано ниже. // Отображение битов, составляющих байт, class ShowBits { public static void main(String args[]) { int t; byte val; val = 123; for(t=128; t > 0; t = t/2) { if((val & t) != 0) System.out.print("1 "); else System.out.print("0 ") ; } } }

Выполнение этой программы дает следующий результат: 01111011

В цикле for последовательно проверяется каждый бит значения переменной val. Для того чтобы выяснить, установлен ли бит, выполняется операция поразрядного И. Если бит установлен, отображается цифра 1, иначе – 0. В примере для опробования 5.3 будет показано, как расширить этот элементарный принцип для создания класса, в котором будут отображаться биты двоичного представления целого числа любого типа.

Операция поразрядного ИЛИ выполняет действия, противоположные операции поразрядного И, и служит для установки отдельных битов. Любой бит, значение которого равно единице хотя бы в одном из двух операндов, будет равен единице и в результирующем значении. Например: 1101 ОО11 1010 1010 | 1111 1011

Операцию поразрядного ИЛИ можно использовать для преобразования прописных букв английского алфавита в строчные. Ниже приведен пример программы, решающей эту задачу. // Преобразование прописных букв английского алфавита в строчные, class LowCase { 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 | 32); System.out.print(ch + " ") ; } } }

Выполнение этой программы дает следующий результат: Аа Bb Сс Dd Ее Ff Gg Hh Ii Jj

В приведенном выше примере программы операция поразрядного ИЛИ выполняется над кодом символа и значением 32, имеющим двоичное представление 0000000000100000. Как видите, в двоичном представлении значения 32 установлен только шестой бит. Используя это значение в качестве одного операнда в операции поразрядного ИЛИ с любым другим значением в качестве другого операнда, получим результат, в котором устанавливается шестой бит, а состояние всех остальных битов остается без изменения. Таким образом, любая прописная буква будет преобразована в строчную.

Операция поразрядного исключающего ИЛИ дает результат, в котором отдельный бит устанавливается в том и только в том случае, если соответствующие биты в двух операндах имеют разные значения. Ниже приведен пример выполнения операции поразрядного исключающего ИЛИ. 0111 1111 1011 1001 ^ 1100 0110

Операция поразрядного исключающего ИЛИ имеет одну интересную особенность, которая позволяет очень просто кодировать сообщения. Если выполнить данную oneрацию сначала над некоторыми значениями X и Y, а затем над ее результатом и значением Y, то снова получится значение X. Например, при выполнении приведенной ниже последовательности операторов переменная R2 получит то же значение, что и X. Таким образом, в результате выполнения подряд двух операций поразрядного исключающего ИЛИ восстанавливается исходное значение. R1 = X А Y; R2 = R1 А Y;

Эту особенность операции поразрядного исключающего ИЛИ можно использовать для создания простейшей шифрующей программы, в которой некоторое целое число будет выполнять роль ключа, применяемого как при шифровании, так и дешифровании сообщений. Над всеми символами сообщения и данным числом будет выполняться операция поразрядного исключающего ИЛИ. В первый раз данная операция будет выполняться при шифровании, формируя кодированный текст, а второй раз – при дешифровании, в результате чего восстанавливается исходный текст сообщения. Ниже приведен пример простой программы, выполняющей шифрование и дешифрование коротких сообщений. // Использование операции поразрядного исключающего ИЛИ // для шифрования и дешифрования сообщений, class Encode { public static void main(String args[]) { String msg = "This is a test"; String encmsg = ""; String decmsg = ""; int key = 88; System.out.print("Original message: "); System.out.println(msg); // зашифровать сообщение for (int i=0; i < msg.length(); i++) // Построение зашифрованной строки сообщения, encmsg = encmsg + (char) (msg.charAt(i) A key); System.out.print("Encoded message: "); System.out.println(encmsg) ; // дешифровать сообщение for(int i=0; i < msg.length(); i++) // Построение дешифрованной строки сообщения. decmsg = decmsg + (char) (encmsg.charAt(i) A key); System.out.print("Decoded message: "); System.out.println(decmsg); } }

Выполнение этой программы дает следующий результат: Original message: This is a test Encoded message: 01+xl+x9x,=+, Decoded message: This is a test

Как видите, в результате двух операций поразрядного исключающего ИЛИ с одним и тем же ключом получается дешифрованное сообщение, совпадающее с исходным.

Унарная операция поразрядного НЕ (или дополнения до 1) изменяет на обратное состояние всех битов операнда. Так, если некоторая целочисленная переменная А содержит значение с двоичным представлением 10010110, то в результате поразрядной операции ~А получится двоичная комбинация 01101001.

Ниже приведен пример программы, демонстрирующий применение операции поразрядного НЕ. Эта программа отображает число и его дополнение в двоичном представлении. // Демонстрация операции поразрядного НЕ. class NotDemo { public static void main(String args[]) { byte b = -34; for(int t=128; t > 0; t = t/2) { if((b & t) != 0) System.out.print("1 "); else System.out.print("0 ") ; } System.out.println() ; // изменить на обратное состояние всех битов b = (byte) ~b; for(int t=128; t > 0; t = t/2) { if((b & t) != 0) System.out.print("1 ") ; else System.out.print("0 "); } } }

Выполнение этой программы дает следующий результат: 1 1 0 1 1 1 1 0 0 0 1 0 0 0 0 1 Операторы сдвига

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

Ниже приведена общая форма для этих операторов. значение << число_битов значение >> число_битов значение >>> число_битов

где число_битов – это число позиций двоичных разрядов, на которое сдвигается указанное значение.

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

Помимо сохранения знакового разряда, необходимо помнить еще об одной особенности сдвига вправо. Отрицательные числа в Java (как, впрочем, и в других языках программирования) представлены в виде дополнения до двух. Для того чтобы преобразовать положительное число в отрицательное, нужно изменить на обратное состояние всех битов его двоичного представления, а к полученному результату прибавить единицу. Так, значение -1 имеет байтовое представление 11111111. Сдвинув это значение вправо на любое число позиций, мы снова получим -1!

Если при сдвиге вправо не требуется сохранять знаковый разряд, то можно воспользоваться сдвигом вправо без знака (оператором »>). В этом случае освободившиеся старшие разряды всегда будут заполняться нулями. Именно поэтому такая операция называется еще сдвигом с заполнением нулями. Сдвигом вправо без знака удобно пользоваться для обработки нечисловых значений, в том числе кодов состояния.

При любом сдвиге теряются те биты, которые сдвигаются. Циклический сдвиг в Java не поддерживается, и поэтому нет возможности восстановить потерянные разряды.

Ниже приведен пример программы, демонстрирующий эффект от применения операторов сдвига влево и вправо. В двоичном представлении исходного целочисленного значения 1 установлен лишь младший разряд. К этому значению восемь раз применяется операция сдвига влево. После каждого сдвига на экран выводится восемь младших разрядов числа. Затем единица устанавливается в восьмом двоичном разряде числа и производятся его сдвиги вправо. // Демонстрация операторов « и ». class ShiftDemo { public static void main(String args[]) { int val = 1; for(int i = 0; i < 8; i++) { for(int t=128; t > 0; t = t/2) { if((val & t) != 0) System.out.print("1 "); else System.out.print("0 "); } System.out.println() ; val = val « 1; // сдвинуть влево } System.out.println(); val = 128; for(int i = 0; i < 8; i++) { for(int t=128; t > 0; t = t/2) { if((val & t) != 0) System.out.print("1 "); else System.out.print("0 "); } System.out.println() ; val = val >> 1; // сдвинуть вправо } } }

Результат выполнения данной программы выглядит следующим образом: 00000001 00000010 00000100 00001000 00010000 00100000 01000000 10000000 10000000 01000000 00100000 00010000 00001000 00000100 00000010 00000001

Выполняя сдвиг значений типа byte и short, необходимо соблюдать осторожность, поскольку исполняющая система Java автоматически преобразует их в тип int и лишь затем вычисляет выражение с оператором сдвига. Так, если сдвинуть вправо значение типа byte, оно будет сначала продвинуто к типу int, а результат сдвига будет также отнесен к типу int. Обычно такое преобразование не влечет за собой никаких последствий. Но если попытаться сдвинуть отрицательное значение типа byte или short, то при продвижении к типу int оно будет дополнено знаком, а следовательно, старшие его разряды будут заполнены единицами. Это вполне оправдано при обычном сдвиге вправо. Но при выполнении сдвига с заполнением нулями в байтовом представлении числа неожиданно появятся 24 единицы, которые придется дополнительно сдвинуть, прежде чем в нем появятся нули. Поразрядные составные операторы присваивания

составные операторы присваивания Все двоичные поразрядные операторы имеют укороченную форму и могут быть использованы в составных операциях присваивания. Например, в двух приведенных ниже операторах переменной х присваивается результат выполнения операции исключающее ИЛИ над первоначальным значением переменной х и числовым значением 127. х = х А 127; х А= 127;

Пример для опробования 5.3. Создание класса ShowBits

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

Последовательность действий

Создайте новый файл ShowBits Demo. j ava.

Создайте класс ShowBits, начав его со следующего кода: class ShowBits { int numbits; ShowBits(int n) { numbits = n; } }

Конструктор класса ShowBits позволяет создавать объекты, отображающие заданное число битов. Например, для создания объекта, отображающего 8 младших битов некоторого значения, служит следующее выражение: ShowBits byteval = new ShowBits(8)

Число битов, предназначаемых для отображения, сохраняется в переменной экземпляра numbits.

Для вывода двоичных значений в классе ShowBits определен метод show (), код которого приведен ниже. void show(long val) { long mask = 1; // сдвинуть значение 1 влево на нужную позицию mask <<= numbits-1; int spacer = 0; for(; mask != 0; mask >>>= 1) { if((val & mask) != 0) System.out.print("I"); else System.out.print("0"); spacer++; if((spacer % 8) == 0) { System.out.print(" "); spacer = 0; } } } System.out.println();

Обратите внимание на то, что данному методу передается один параметр типа long. Но это совсем не означает, что при вызове ему нужно всегда передавать значение типа long. Правила автоматического продвижения типов в Java допускают передавать методу show () любое целочисленное значение. А количество отображаемых битов определяется переменной numbits. Группы из 8 битов разделяются в методе show () пробелами. Это упрощает восприятие длинных двоичных комбинаций.

Ниже приведен весь исходный код программы из файла ShowBit sDemo. j ava. /* Пример для опробования 5.3 Создание класса для отображения значений в двоичном виде. */ class ShowBits { int numbits; ShowBits(int n) { numbits = n; } void show(long val) { long mask = 1; // сдвинуть значения 1 влево на нужную позицию mask <<= numbits-1; int spacer = 0; for(; mask != 0; mask >>>= 1) { if ((val & mask) != 0) System.out.print ("1") ; else System.out.print("0"); spacer++; if((spacer % 8) == 0) { System.out.print(" "); spacer = 0; } } System.out.println(); } } // продемонстрировать класс ShowBits class ShowBitsDemo { public static void main(String args[]) { ShowBits b = new ShowBits(8); ShowBits i = new ShowBits(32); ShowBits li = new ShowBits(64); System.out.println("123 in binary: "); b.show(123); System.out.println("n87987 in binary: "); i.show(87987); System.out.println("n237658768 in binary: "); li.show(237658768); // можно также отобразить младшие разряды любого целого числа System.out.println("nLow order 8 bits of 87987 in binary: "); b.show(87987); } }

Результат выполнения программы ShowBitsDemo выгладит следующим образом: 123 in binary: 01111011 87987 in binary: 00000000 00000001 01010111 10110011 237658768 in binary: 00000000 00000000 00000000 00000000 00001110 00101010 01100010 10010000 Low order 8 bits of 87987 in binary: 10110011 Оператор ?

Оператор ? – один из самых удобных в Java. Он часто используется вместо операторов if-else в следующей общей форме: if (условие) var = выражение_1; else var = выражение_2;

где значение, присваиваемое переменной var, определяет условие, управляющее выполнением оператора if. Оператор ? называется тернарным, поскольку он обрабатывает три операнда. Этот оператор записывается в следующей общей форме: выражение_ 1 ? выражение_2 : выражение_3;

где выражение_1 должно быть логическим, т.е. возвращать тип boolean, а выражение_2 и выражение_3, разделяемые двоеточием, могут быть любого типа, за исключением void. Но типы второго и третьего выражений должны непременно совпадать.

Значение выражения ? определяется следующим образом. Сначала вычисляется выражение_1. Если оно дает логическое значение true, то вычисляется выражение_2, а его значение становится результирующим для всего выражения ?. Если же выражение_1 дает логическое значение false, то вычисляется выражение_3, а его значение стано вится результирующим для всего выражения ?. Рассмотрим пример, в котором сначала вычисляется абсолютное значение переменной val, а затем оно присваивается переменной absval. absval = val < 0 ? -val : val; // получить абсолютное значение переменной val

В данном примере переменной absval присваивается значение переменной val, если это значение больше или равно нулю. А если значение переменной val отрицательное, то переменной absval присваивается значение val со знаком “минус”, что в итоге дает положительную величину. Код, выполняющий ту же самую функцию, но с помощью логической конструкции if-else, будет выглядеть следующим образом: if(val < 0) absval = -val; else absval = val;

Рассмотрим еще один пример применения оператора ?. В этом примере программы выполняется деление двух чисел, но не допускается деление на нуль: // Предотвращение деления на нуль с помощью оператора ?. class NoZeroDiv { public static void main(String args[]) { int result; for(int i = -5; i < 6; i++) { // Деление на нуль предотвращается. result = i != 0 ? 100 / i : 0; if(i != 0) System.out.println("100 / " + i + " is " + result); } } }

Ниже приведен результат выполнения данной программы. 100 / -5 is -20 100 / -4 is -25 100 / -3 is -33 100 / -2 is -50 100 / -1 is -100 100 / 1 is 100 100 / 2 is 50 100 / 3 is 33 100 / 4 is 25 100 / 5 is 20

Обратите особое внимание на следующую строку кода: result = i != 0 ? 100 / i : 0;

где переменной result присваивается результат деления числа 100 на значение переменной i. Но деление выполняется только в том случае, если значение переменной i не равно нулю. В противном случае переменной result присваивается нулевое значение.

Значение, возвращаемое оператором ?, не обязательно присваивать переменной. Его можно, например, использовать в качестве параметра при вызове метода. Если же все три выражения оператора ? имеют тип boolean, то сам оператор ? может быть использован в качестве условия для выполнения цикла или оператора if. Ниже приведена немного видоизмененная версия предыдущего примера программы. Ее выполнение дает такой же результат, как и прежде. // Предотвращение деления на нуль с помощью оператора ?. class NoZeroDiv2 { public static void main(String args[]) { for(int i = -5; i < 6; i++) if(i != 0 ? true : false) System.out.println("100 / " + i + " is " + 100 / i); } }

Обратите внимание на выражение, определяющее условие выполнения оператора if. Если значение переменной i равно нулю, то оператор ? возвращает логическое значение false, что предотвращает деление на нуль, и результат не отображается. В противном случае осуществляется обычное деление. Упражнение для самопроверки по материалу главы 5

Покажите два способа объявления одномерного массива, состоящего из 12 элементов типа double.

Покажите, как инициализировать одномерный массив целочисленными значениями от 1 до 5.

Напишите программу, в которой массив используется для нахождения среднего арифметического десяти значений типа double. Используйте любые десять чисел.

Измените программу, написанную в примере для опробования 5.1, таким образом, чтобы она сортировала массив символьных строк. Продемонстрируйте ее работоспособность.

В чем отличие методов indexOf () и lastlndexOf () из класса String?

Все символьные строки являются объектами типа String. Покажите, как вызываются методы length () и charAt () для строкового литерала "I like Java" (Мне нравится Java).

Расширьте класс Encode таким образом, чтобы в качестве ключа шифрования использовалась строка из восьми символов.

Можно ли применять поразрядные операторы к значениям типа double?

Перепишите приведенную ниже последовательность операторов, воспользовавшись оператором ?.if (х < 0) у = 10; else у = 20;

В приведенном ниже фрагменте кода содержится знак &. Какой оператор он обозначает: поразрядный или логический? Обоснуйте свой ответ.boolean а, Ь; // ... if (а & Ь) ...

Является ли ошибкой превышение верхней границы массива? Является ли ошибкой использование отрицательных значений для доступа к элементам массива?

Как обозначается оператор сдвига вправо без знака?

Перепишите рассмотренный ранее в этой главе класс MinMax таким образом, чтобы в нем использовалась разновидность for-each цикла for.

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

Можно ли управлять оператором switch с помощью объектов типа String?

Глава 6 Дополнительные сведения о методах и классах

Основные навыки и понятия

Управление доступом к членам классов

Передача объектов при вызове методов

Возврат объектов из методов

Перегрузка методов

Перегрузка конструкторов

Применение рекурсии

Использование ключевого слова static

Применение внутренних классов

Использование аргументов переменной длины

В этой главе возобновляется рассмотрение классов и методов. Сначала будет показано, каким образом контролируется доступ к членам класса, а затем рассмотрены особенности передачи и возврата объектов из методов, перегрузки методов, использования рекурсии и ключевого слова static. Кроме того, в этой главе будут представлены вложенные классы и методы с аргументами переменной длины. Управление доступом к членам класса

Поддержка свойства инкапсуляции в классе дает два главных преимущества. Во-первых, класс связывает данные с кодом. Это преимущество использовалось в предыдущих примерах программ, начиная с главы 4. И во-вторых, класс предоставляет средства для управления доступом к его членам. Именно эта, вторая преимущественная особенность и будет рассмотрена ниже.

В языке Java, по существу, имеются два типа членов класса: открытые (public) и закрытые (private), хотя в действительности дело обстоит немного сложнее. Доступ к открытому члену свободно осуществляется из кода, определенного за пределами класса. Именно этот тип члена класса использовался в рассматривавшихся до сих пор примерах программ. А закрытый член класса доступен только методам, определенным в самом классе. С помощью закрытых членов и организуется управление доступом.

Ограничение доступа к членам класса является основополагающей частью объектно-ориентированного программирования, поскольку оно позволяет исключить неверное использование объекта. Разрешая доступ к закрытым данным только с помощью строго определенного ряда методов, можно предупредить присваивание неверных значений этим данным, выполняя, например, проверку диапазона представления чисел. Для закрытого члена класса нельзя задать значение непосредственно в коде за пределами класса. Но в то же время можно полностью управлять тем, как и когда данные используются в объекте. Следовательно, правильно реализованный класс образует некий “черный ящик”, которым можно пользоваться, но внутренний механизм «его действия закрыт для вмешательства извне.

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

Управление доступом к членам класса в Java осуществляется с помощью трех модификаторов доступа (называемых также спецификаторами): public, private и protected. Если модификатор не указан, то принимается тип доступа по умолчанию. В этой главе будут рассмотрены модификаторы public и private. Модификатор protected непосредственно связан с наследованием, и поэтому он будет обсуждаться в главе 8.

Когда член класса обозначается модификатором public, он становится доступным из любого другого кода в программе, включая и методы, определенные в других классах. Когда же член класса обозначается модификатором private, он может быть доступен только другим членам этого класса. Следовательно, методы из других классов не имеют доступа к закрытому члену данного класса.

Если все классы в программе относятся к одному пакету, то отсутствие модификатора доступа равнозначно указанию модификатора public по умолчанию. Пакет представляет собой группу классов, предназначенных как для организации классов, так и для управления доступом. Рассмотрение пакетов откладывается до главы 8, а для примеров программ, представленных в этой и предыдущих главах, тип доступа по умолчанию не отличается от public.

Модификатор доступа указывается перед остальной частью описания типа отдельного члена класса. Это означает, что именно с него должен начинаться оператор объявления члена класса. Ниже приведены соответствующие примеры. public String errMsg; private accountBalance bal; private boolean isError(byte status) { // ...

Для того чтобы стал понятнее эффект от применения модификаторов доступа public и private, рассмотрим следующий пример программы: // Открытый и закрытый доступ к членам класса, class MyClass { private int alpha; // закрытый доступ public int beta; // открытый доступ int gamma; // тип доступа по умолчанию (по существу, открытый) /* Методы доступа к переменной alpha. Члены класса могут обращаться к закрытым членам того же класса. */ void setAlpha(int а) { alpha = а; } int getAlpha() { return alpha; } } class AccessDemo { public static void main(String args[]) { MyClass ob = new MyClass(); /* Доступ к переменной alpha возможен только с помощью специально предназначенных для этой цели методов. */ ob.setAlpha(-99); System.out.println("ob.alpha is " + ob.getAlpha()); // Обратиться к переменной alpha так, как показано ниже, нельзя. // ob.alpha =10; // Ошибка! alpha – закрытая переменная! // Следующие обращения вполне допустимы, так как // переменные beta и gamma являются открытыми, ob.beta = 88; ob.gamma = 99; } }

Нетрудно заметить, что в классе MyClass переменная alpha определена как private, переменная beta – как public, а перед переменной gamma модификатор доступа отсутствует, т.е. в данном примере она ведет себя как открытый член класса, которому по умолчанию присваивается модификатор доступа public. Переменная alpha закрыта, и поэтому к ней невозможно обратиться за пределами ее класса. Следовательно, в классе AccessDemo нельзя пользоваться переменной alpha непосредственно. Доступ к ней организуется с помощью открытых методов доступа setAlpha () и getAlpha (), определенных в одном с ней классе. Если удалить комментарии в начале следующей строки кода, то скомпилировать рассматриваемую здесь программу не удастся: // ob.alpha = 10; // Ошибка! alpha – закрытая переменная!

Компилятор выдаст сообщение об ошибке, связанной с нарушением правил доступа. Несмотря на то что переменная alpha недоступна для кода за пределами класса MyClass, пользоваться ею можно с помощью открытых методов доступа setAlpha () и getAlpha ().

Таким образом, закрытые переменные могут быть свободно использованы другими членами класса, но недоступны за пределами этого класса.

Рассмотрим практическое применение средств управления доступом на примере приведенной ниже программы. Во время ее выполнения предотвращается возникновение ошибок нарушения границ отказоустойчивого целочисленного массива. Это достигается следующим образом. Массив объявляется как закрытый член класса, а доступ к нему осуществляется с помощью специально предназначенных для этой цели методов. Эти методы отслеживают попытки обращения к элементам, не входящим в массив, и вместо аварийной остановки программы возвращают сообщение об ошибке. Массив определяется в классе FailSof tArray, код которого приведен ниже. /* В этом классе реализуется отказоустойчивый массив, предотвращающий ошибки при выполнении программы. */ class FailSoftArray { private int a[]; // Ссылка на массив. private int errval; // Значение, возвращаемое в том случае, если // в методе get() будет обнаружена ошибка, public int length; // Переменная length открыта. /* Конструктору данного класса передается размер массива и значение, которое должен возвращать метод get () при обнаружении ошибки. */ public FailSoftArray(int size, int errv) { a = new int[size]; errval = errv; length = size; } // возвратить значение элемента массива по заданному индексу public int get(int index) { // Отслеживание попытки обращения за границы массива. if(ok(index)) return a[index]; return errval; } // установить значение элемента no заданному индексу, // если возникнет ошибка, возвратить логическое значение false public boolean put(int index, int val) { // Отслеживание попытки обращения эа границы массива. if(ok(index)) { a[index] = val; return true; } return false; } // возвратить логическое значение true, если индекс // не выходит за границы массива private boolean ok(int index) { if(index >= 0 & index < length) return true; return false; } } // продемонстрировать обращение к отказоустойчивому массиву class FSDemo { public static void main(String args[]) { FailSoftArray fs = new FailSoftArray(5, -1); int x; // выявить скрытые сбои при обращении к массиву System.out.println("Fail quietly.") ; for(int i=0; i < (fs.length * 2); i++) // Доступ к массиву должен осуществляться с помощью // специально предназначенных для этого методов, fs.put(i, i*10); for (int i=0; i < (fs.length * 2); i++) { // Доступ к массиву должен осуществляться с помощью // специально предназначенных для этого методов. х = fs.get (i); if(x != -1) System.out.print(x + " "); } System.out.println ("") ; // а теперь обработать сбои и вывести сообщения об ошибках System.out.println("nFail with error reports."); for (int i=0; i < (fs.length * 2); i++) if (!fs.put(i, i*10)) System.out.println("Index " + i + " out-of-bounds"); for(int i=0; i < (fs.length * 2); i++) { x = fs.get (i); if(x != -1) System.out.print(x + " ") ; else System.out.println("Index " + i + " out-of-bounds"); } } }

Выполнение этой программы дает следующий результат: Fail quietly. 0 10 20 30 40 Fail with error reports. Index 5 out-of-bounds Index 6 out-of-bounds Index 7 out-of-bounds Index 8 out-of-bounds Index 9 out-of-bounds 0 10 20 30 40 Index 5 out-of-bounds Index 6 out-of-bounds Index 7 out-of-bounds Index 8 out-of-bounds Index 9 out-of-bounds

Рассмотрим подробнее приведенный выше пример программы. В классе Fail So ft Array определены три закрытых члена. Первым из них является перемен¬ ная а, в которой содержится ссылка на массив, предназначенный для хранения данных. Вторым членом является переменная errval, в которой хранится значение, возвращае¬ мое вызывающей части программы в том случае, если вызов метода get () оказывает¬ ся неудачным. И третьим членом является метод ok (), в котором определяется, нахо¬ дится ли индекс в границах массива. Эти три члена могут быть использованы только другими членами класса FailSof tArray. Остальные члены данного класса объявлены открытыми и могут быть вызваны из любой части программы, где используется класс FailSoftArray.

При построении объекта типа FailSof tArray следует указать размер массива и значение, которое должно быть возвращено, если вызов get () окажется неудачным. Ошибочное значение должно отличаться от тех значений, которые могут храниться в массиве. Конкретный массив, обращение к которому осуществляется по ссылке в переменной а, а также ошибочное значение в переменной errval не могут быть непосредственно доступны пользователям построенного объекта типа FailSoftArray, и благодаря этому неправильное их употребление исключается. В частности, пользователь не может непосредственно обратиться к массиву по ссылке в переменной а, указав индекс нужного элемента и не нарушив, возможно, при этом границы массива. Это можно сделать только с помощью методов get () и put ().


    Ваша оценка произведения:

Популярные книги за неделю

    wait_for_cache