Если все классы в программе относятся к одному пакету, то отсутствие модификатора доступа равнозначно указанию модификатора 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 ().
Метод ok () объявлен как закрытый главным образом для того, чтобы проиллюстрировать управление доступом. Даже если бы он и был открытым, это не представляло бы никакой опасности, поскольку он не видоизменяет объект. Но этот метод используется только членами класса FailSoftArray, поэтому он и объявлен закрытым.
Обратите внимание на то, что переменная экземпляра length открыта. Это согласуется с правилами реализации массивов в Java. Для того чтобы получить данные о длине массива типа FailSoftArray, достаточно прочитать значение переменной экземпляра length.