Все члены класса в языке Java — поля и методы, свойства — имеют модификаторы доступа. В прошлых темах мы уже сталкивались с модификатором public
. Модификаторы доступа позволяют задать допустимую область видимости для членов класса, то есть контекст, в котором можно употреблять данную переменную или метод.
В Java используются следующие модификаторы доступа:
- public: публичный, общедоступный класс или член класса. Поля и методы, объявленные с модификатором public, видны другим классам из текущего пакета и из внешних пакетов.
- private: закрытый класс или член класса, противоположность модификатору public. Закрытый класс или член класса доступен только из кода в том же классе.
- protected: такой класс или член класса доступен из любого места в текущем классе или пакете или в производных классах, даже если они находятся в других пакетах
- Модификатор по умолчанию (default). Отсутствие модификатора у поля или метода класса предполагает применение к нему модификатора по умолчанию. Такие поля или методы видны всем классам в текущем пакете.
Рассмотрим все возможные случаи. Допустим, у нас есть проект со следующей структурой:
В данном случае имеется три пакета. Пакет encapsulation содержит главный класс программы, в котором и прописан метод main. Его мы трогать не будем.
И также определены два пакета encapsulation .package1, который содержит классы ClassA и ClassB, и пакет encapsulation .package2, который содержит классы ClassC и ClassD.
Класс ClassA содержит весь возможный набор модификаторов доступа:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
package accessModifiersAndEncapsulation.encapsulation.package1; public class ClassA { private int num1 = 3; int num2 = 2; protected int num3 = 3; public int num4 = 4; public void displayNum1() { System.out.println(num1); } protected void displayNum2() { System.out.println(num2); } void displayNum3() { System.out.println(num3); } private void displayNum4() { System.out.println(num4); } } |
Модификатор доступа должен предшествовать остальной часть определения переменной или метода.
Пусть класс ClassB использует ClassA:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
package accessModifiersAndEncapsulation.encapsulation.package1; public class ClassB { public void result() { ClassA classA = new ClassA(); //System.out.println(classA.num1); // ошибка, так как num1 - private classA.displayNum1(); // public System.out.println(classA.num2); // идентификатор по умолчанию classA.displayNum2(); // protected System.out.println(classA.num3); // protected classA.displayNum3(); // идентификатор по умолчанию System.out.println(classA.num4); // public //classA.displayNum4(); // ошибка, так как displayNum4() - private } } |
Так как классы ClassB и ClassA находятся в одном пакете, то мы сможем использовать все поля и методы класса ClassA в ClassB кроме тех, что объявлены как private
.
Теперь используем класс ClassA в классе ClassC:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
package accessModifiersAndEncapsulation.encapsulation.package2; import accessModifiersAndEncapsulation.encapsulation.package1.ClassA; public class ClassC{ public void result(){ ClassA classA = new ClassA(); //System.out.println(classA.num1); // ошибка, так как num1 - private classA.displayNum1(); // public //System.out.println(classA.num2); // ошибка, так как num2 - идентификатор по умолчанию //classA.displayNum2(); //ошибка, так как доступ - protected //System.out.println(classA.num3); // ошибка, так как доступ - protected //classA.displayNum3(); //ошибка, так как доступ - идентификатор по умолчанию System.out.println(classA.num4); // public //classA.displayNum4(); // ошибка, так как displayNum4() - private } } |
Так как класс ClassC находится в другом пакете и не является наследником класса ClassA, то в нем можно использовать только те поля и методы класса ClassA, которые объявлены с модификатором public.
И последний случай — ClassD является наследником класса ClassA, но находится в другом пакете (чуть позже мы более подробно разберем наследование):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
package accessModifiersAndEncapsulation.encapsulation.package2; import accessModifiersAndEncapsulation.encapsulation.package1.ClassA; public class ClassD extends ClassA { public void result() { //System.out.println(num1); // ошибка, так как num1 - private displayNum1(); // public //System.out.println(num2); // ошибка, так как доступ - идентификатор по умолчанию displayNum2(); // protected System.out.println(num3); // protected //displayNum3(); //ошибка, так как доступ по умолчанию System.out.println(num4); // public //classA.displayNum4(); // ошибка, так как displayNum4() - private } } |
Так как ClassD — наследник класса ClassA, то мы можем напрямую использовать методы и поля ClassA без создания объекта. И здесь нам опять недоступны поля и методы private, и также нам недоступны поля и методы с модификатором доступа по умолчанию. В то же время в отличие от находящегося в том же пакете класса ClassC в классе ClassD мы можем использовать методы и поля с доступом protected.
Инкапсуляция
Казалось бы, почему бы не объявить все переменные и методы с модификатором public
? Однако использование различных модификаторов гарантирует, что данные не будут искажены или изменены не надлежащим образом. Подобное сокрытие данных называется инкапсуляцией.
А вместо непосредственного использования полей, как правило, используют методы доступа:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
class Book{ private String name; public void setName(String name){ this.name=name; } public String getName(){ return name; } } |
И затем вместо непосредственной работы с полем name в классе Book мы будем работать с методами, которые устанавливает значение этого поля и возвращают его.