Модификаторы доступа в Java регулируют, откуда виден код: поле, метод или класс. Их четыре, от самого строгого к самому открытому:
Давай рассмотрим каждый из них, определимся, когда они могут нам пригодиться и приведем примеры :)
![Модификатор private: доступ к полям и методам только внутри своего класса]()
Поля и методы, обозначенные модификатором доступа
Теперь, когда ты изучил лекции об интерфейсах, для тебя очевидно его предназначение :) Ведь
private (виден только внутри своего класса), default или package visible (виден всему пакету, пишется без ключевого слова), protected (виден в пакете и в классах-наследниках) и public (виден отовсюду).
Привет! В сегодняшней лекции мы познакомимся с понятием «модификаторы доступа» и рассмотрим примеры работы с ними.Кратко
- Модификаторы доступа регулируют, откуда виден код (поле, метод, класс).
- В Java их четыре:
private,default(package visible),protected,public, от самого строгого к самому открытому. private: виден только внутри своего класса (основа инкапсуляции, геттеры и сеттеры).default(без ключевого слова): виден всем классам своего пакета.protected: виден в своем пакете и во всех классах-наследниках.public: виден отовсюду (интерфейс программы для пользователя).
- private;
- default (package visible);
- protected;
- public.
| Модификатор | Тот же класс | Тот же пакет | Классы-наследники | Все остальные |
|---|---|---|---|---|
private | да | нет | нет | нет |
default (package visible) | да | да | нет | нет |
protected | да | да | да | нет |
public | да | да | да | да |
Модификатор private
Private наиболее строгий модификатор доступа. Он ограничивает видимость данных и методов пределами одного класса.
Этот модификатор тебе известен из лекции про геттеры и сеттеры. Помнишь этот пример?
public class Cat {
public String name;
public int age;
public int weight;
public Cat(String name, int age, int weight) {
this.name = name;
this.age = age;
this.weight = weight;
}
public Cat() {
}
public void sayMeow() {
System.out.println("Мяу!");
}
}
public class Main {
public static void main(String[] args) {
Cat cat = new Cat();
cat.name = "";
cat.age = -1000;
cat.weight = 0;
}
}
Мы рассматривали его в одной из статей раньше.
Здесь мы допустили серьезную ошибку: открыли наши данные, в результате чего коллеги-программисты получили доступ напрямую к полям класса и изменению их значения. Более того, эти значения присваивались без проверок, в результате чего в нашей программе можно создать кота с возрастом -1000 лет, именем «» и весом 0.
Для решения этой проблемы мы использовали геттеры и сеттеры, а также ограничили доступ к данным с помощью модификатора private.
public class Cat {
private String name;
private int age;
private int weight;
public Cat(String name, int age, int weight) {
this.name = name;
this.age = age;
this.weight = weight;
}
public Cat() {
}
public void sayMeow() {
System.out.println("Мяу!");
}
public String getName() {
return name;
}
public void setName(String name) {
// проверка входящего параметра
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
// проверка входящего параметра
this.age = age;
}
public int getWeight() {
return weight;
}
public void setWeight(int weight) {
// проверка входящего параметра
this.weight = weight;
}
}
Собственно, ограничение доступа к полям и реализация геттеров-сеттеров это самый распространенный пример использования private в реальной работе.
То есть реализация инкапсуляции в программе это главное предназначение этого модификатора.
Это касается не только полей, кстати. Представь, что в твоей программе существует метод, который реализует какую-то ОЧЕНЬ сложную функциональность.
Что бы придумать такого для примера…
Скажем, твой метод readDataFromCollider() принимает на вход адрес с данными, считывает данные из Большого Адронного Коллайдера в байтовом формате, преобразует эти данные в текст, записывает в файл и распечатывает его.
Даже описание метода выглядит жутковато, что уж говорить про код :)
Чтобы повысить читаемость кода, было бы хорошо не писать сложную логику метода в одном месте, а наоборот, разбить функциональность на отдельные методы.
Например, метод readByteData() отвечает за считывание данных, convertBytesToSymbols() конвертирует считанные с коллайдера данные в текст, saveToFile() сохраняет полученный текст в файл, а printColliderData() распечатывает наш файл с данными.
Метод readDataFromCollider() в итоге стал бы намного проще:
public class ColliderUtil {
public void readDataFromCollider(Path pathToData) {
byte[] colliderData = readByteData(pathToData);
String[] textData = convertBytesToSymbols(colliderData);
File fileWithData = saveToFile(textData);
printColliderData(fileWithData);
}
public byte[] readByteData(Path pathToData) {
// считывает данные в байтах
}
public String[] convertBytesToSymbols(byte[] colliderDataInBytes) {
// конвертирует байты в символы
}
public File saveToFile(String[] colliderData) {
// сохраняет считанные данные в файл
}
public void printColliderData(File fileWithColliderData) {
// печатает данные из файла
}
}
Однако, как ты помнишь из лекции про интерфейсы, пользователь получает доступ только к конечному интерфейсу. А наши 4 метода не являются его частью. Они вспомогательные: мы создали их, чтобы улучшить читаемость кода и не засовывать четыре разные задачи в один метод.
Давать доступ пользователю к этим методам не нужно. Если у пользователя при работе с коллайдером появится доступ к методу convertBytesToSymbols(), он скорее всего просто не поймет, что это за метод и зачем нужен. Какие байты конвертируются? Откуда они взялись? Зачем их конвертировать в текст?
Логика, которая выполняется в этом методе, не является частью интерфейса для пользователя. Только метод readDataFromCollider() это часть интерфейса.
Что же делать с этими четырьмя «внутренними» методами? Правильно! Ограничить доступ к ним модификатором private.
Так они смогут спокойно выполнять свою работу внутри класса и не вводить в заблуждение пользователя, которому логика каждого из них по отдельности не нужна.
public class ColliderUtil {
public void readDataFromCollider(Path pathToData) {
byte[] colliderData = readByteData(pathToData);
String[] textData = convertBytesToSymbols(colliderData);
File fileWithData = saveToFile(textData);
printColliderData(fileWithData);
}
private byte[] readByteData(Path pathToData) {
// считывает данные в байтах
}
private String[] convertBytesToSymbols(byte[] colliderDataInBytes) {
// конвертирует байты в символы
}
private File saveToFile(String[] colliderData) {
// сохраняет считанные данные в файл
}
private void printColliderData(File fileWithColliderData) {
// печатает данные из файла
}
}
Модификатор package visible
Дальше у нас по списку идет модификаторdefault или, как его еще называют, package visible. Он не обозначается ключевым словом, поскольку установлен в Java по умолчанию для всех полей и методов.
Если написать в твоем коде:
int x = 10;
… у переменной x будет этот самый package visible доступ.
Если метод (или переменная) не помечены никаким модификатором, считается, что они помечены «модификатором по умолчанию». Переменные или методы с таким модификатором (т.е. вообще без какого-нибудь) видны всем классам пакета, в котором они объявлены. И только им.
Случаи его применения ограничены, как и у модификатора protected. Чаще всего default-доступ используется в пакете, где есть какие-то классы-утилиты, не реализующие функциональность всех остальных классов в этом пакете.
Приведем пример. Представь, что у нас есть пакет «services». Внутри него лежат различные классы, которые работают с базой данных. Например, есть класс UserService, считывающий данные пользователей из БД, класс CarService, считывающий из этой же БД данные об автомобилях, и другие классы, каждый из которых работает со своим типом объектов и читает данные о них из базы.
package services;
public class UserService {
}
package services;
public class CarService {
}
Однако легко может случиться ситуация, когда данные в базе данных лежат в одном формате, а нам они нужны в другом.
Представь, что дата рождения пользователя в БД хранится в формате TIMESTAMP WITH TIME ZONE...
2014-04-04 20:32:59.390583+02
...нам вместо этого нужен самый простой объект, java.util.Date.
Для этой цели можем создать внутри пакета services специальный класс Mapper. Он будет отвечать за конвертацию данных из базы в привычные нам Java-объекты. Простой вспомогательный класс.
Обычно мы создаем все классы как public class ClassName, но это не обязательно.
Мы можем объявить наш вспомогательный класс просто как class Mapper. В таком случае он все равно делает свою работу, но не виден никому за пределами пакета services!
package services;
class Mapper {
}
package services;
public class CarService {
Mapper mapper;
}
А это, по сути, правильная логика: зачем кому-то за пределами пакета видеть вспомогательный класс, работающий только с классами этого же пакета?Модификатор protected
Следующий по строгости модификатор доступа этоprotected.
protected, будут видны:
- в пределах всех классов, находящихся в том же пакете, что и наш;
- в пределах всех классов-наследников нашего класса.
protected гораздо меньше, чем private, и они специфические.
Представь, что у нас есть абстрактный класс AbstractSecretAgent, обозначающий секретного агента какой-то спецслужбы, а также пакет top_secret, в котором лежит этот класс и его наследники. От него наследуются конкретные классы: FBISecretAgent, MI6SecretAgent, MossadSecretAgent и т.п.
Внутри абстрактного класса мы хотим реализовать счетчик агентов. При создании где-то в программе нового объекта-агента он будет увеличиваться.
package top_secret;
public abstract class AbstractSecretAgent {
public static int agentCount = 0;
}
Но агенты-то у нас секретные! А значит, об их числе должны знать только они и никто другой.
Мы легко можем добавить модификатор protected к полю agentCount, и тогда получить его значение смогут либо объекты других классов секретных агентов, либо те классы, которые расположены в нашем «секретном» пакете top_secret.
public abstract class AbstractSecretAgent {
protected static int agentCount = 0;
}
Вот для таких специфических задач и нужен модификатор protected :)Модификатор public
И последний по списку, но не по значимости, модификаторpublic! С ним ты познакомился в первый день учебы на JavaRush, впервые в жизни запустив public static void main(String[] args).
public создан для того, чтобы отдавать что-то пользователям. Например, интерфейс твоей программы.
Допустим, ты написал программу-переводчик, и она умеет переводить русский текст в английский. Ты создал метод translate(String textInRussian), внутри которого реализована необходимая логика.
Этот метод ты отметил словом public, и теперь он станет частью интерфейса:
public class Translator {
public String translate(String textInRussian) {
// переводит текст с русского на английский
}
}
Можно связать вызов этого метода с кнопкой «перевести» на экране программы, и все! Кто угодно может этим пользоваться.
Части кода, помеченные модификатором public, предназначаются для конечного пользователя.
Если привести пример из жизни, private это все процессы, происходящие внутри телевизора, когда он работает, а public это кнопки на пульте телевизора, с помощью которых пользователь может им управлять. При этом ему не нужно знать как устроен телевизор и за счет чего он работает. Пульт это набор public-методов: on(), off(), nextChannel(), previousChannel(), increaseVolume(), decreaseVolume() и т.д.
Больше о разграничении доступа к членам класса можно прочитать в документации Oracle (Controlling Access to Members of a Class).
Читайте также
- Принципы ООП: инкапсуляция, ради которой чаще всего и нужен
private. - Интерфейсы в Java: что именно отдается пользователю через
public.
Вопросы и ответы
Сколько модификаторов доступа в Java?
Четыре:private, default (package visible), protected и public. Они перечислены от самого строгого к самому открытому.
Что делает модификатор private?
Ограничивает видимость поля или метода пределами одного класса. Это основа инкапсуляции: доступ кprivate-полям дают через геттеры и сеттеры.
Что такое default (package visible)?
Это модификатор по умолчанию, который не пишется ключевым словом. Поле или метод без модификатора видны всем классам того же пакета и только им.Чем protected отличается от default?
default виден только внутри пакета, а protected дополнительно виден в классах-наследниках, даже если они находятся в другом пакете.
Что делает модификатор public?
Делает поле, метод или класс видимыми отовсюду, из любого пакета.public используют для того, что должно быть частью интерфейса программы для пользователя.
Какой модификатор доступа стоит по умолчанию?
Если модификатор не указан, применяетсяdefault (package visible): элемент виден только внутри своего пакета.