🔥 🚀 Важно для всех, кто работает с Java! 🔥
На JavaRocks ты найдешь уникальные туториалы, практические задачи и редкие книги, которых не найти в свободном доступе. Присоединяйся к нашему Telegram-каналу JavaRocks — стань частью профессионального сообщества!
Из этой статьи вы узнаете, что такое модификаторы доступа в Java, а также познакомитесь с их работой на примерах.
Модификаторы доступа — это чаще всего ключевые слова, которые регулируют доступ к различным частям вашего кода. Почему “чаще всего”? Потому что один из них устанавливается по умолчанию — без использования ключевого слова 🙂
В Java есть четыре модификатора доступа. Вот они — от самого строгого до самого “мягкого”:
private;default(доступ внутри пакета);protected;public.
А теперь рассмотрим каждый из них подробнее — когда и зачем использовать, и, конечно, посмотрим примеры 🙂
Модификатор private

private — самый строгий модификатор доступа в Java. Он позволяет обращаться к данным и методам только внутри одного класса.
Возможно, вы уже встречали их в статье про геттеры и сеттеры. Вот пример:
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("Meow!");
}
}
public class Main {
public static void main(String[] args) {
Cat cat = new Cat();
cat.name = "";
cat.age = -1000;
cat.weight = 0;
}
}Run CodeЗдесь допущена серьезная ошибка: поля public допускают прямое изменение их значений из любого внешнего класса. Причем без каких-либо проверок. В результате, наша программа может спокойно создать кота с именем "", возрастом -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("Meow!");
}
public String getName() {
return name;
}
public void setName(String name) {
// input parameter check
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
// input parameter check
this.age = age;
}
public int getWeight() {
return weight;
}
public void setWeight(int weight) {
// input parameter check
this.weight = weight;
}
}Run CodeПо сути, ограничение доступа к полям и использование геттеров/сеттеров — это и есть типичные случаи использования private в реальной работе.
Иными словами, главная цель модификатора 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) {
// Reads data in bytes
}
public String[] convertBytesToSymbols(byte[] colliderDataInBytes) {
// Converts bytes to characters
}
public File saveToFile(String[] colliderData) {
// Saves read data to a file
}
public void printColliderData(File fileWithColliderData) {
// Prints data from the file
}
}Run CodeНо вспомним, как устроены интерфейсы: пользователь получает доступ только к внешнему интерфейсу. А наши четыре метода к нему не относятся.
Это вспомогательные методы: мы создали их, чтобы улучшить читаемость кода и не запихивать четыре разные задачи в один метод.
Нет необходимости предоставлять пользователю доступ к этим методам. Если пользователи будут иметь доступ к методу 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) {
// Reads data in bytes
}
private String[] convertBytesToSymbols(byte[] colliderDataInBytes) {
// Converts bytes to characters
}
private File saveToFile(String[] colliderData) {
// Saves read data to a file
}
private void printColliderData(File fileWithColliderData) {
// Prints data from the file
}
}Run CodeМодификатор protected
Следующий по строгости модификатор — protected.

Поля и методы, помеченные модификатором protected, будут видны:
- в рамках всех классов, входящих в тот же пакет, что и наш;
- во всех классах, которые наследуются от нашего класса.
Сначала может быть непонятно, когда это может понадобиться. И действительно: случаев, когда действительно нужен protected, гораздо меньше, чем для private, и все они довольно специфичны.
Представьте, что у нас есть абстрактный класс AbstractSecretAgent, описывающий секретного агента какой-либо спецслужбы, а также пакет top_secret, содержащий этот класс и его потомков. Конкретные классы, такие как FBISecretAgent, MI6SecretAgent, MossadSecretAgent и т. д., наследуют этот базовый класс.
Допустим, внутри абстрактного класса мы хотим реализовать счетчик агентов. Каждый раз, когда в программе появляется новый агент, счетчик увеличивается.
package top_secret;
public abstract class AbstractSecretAgent {
public static int agentCount = 0;
}Run CodeНо наши агенты – секретные! А это значит, что только они должны знать, сколько их на самом деле существует.
Мы можем легко добавить модификатор protected к полю agent_counter. Тогда доступ к его значению смогут получить только экземпляры других секретных агентов и классы из того же пакета top_secret.
public abstract class AbstractSecretAgent {
protected static int agent_counter = 0;
}Run CodeИменно для таких специализированных задач и нужен модификатор protected 🙂
Модификатор доступа по умолчанию (package visible)
Модификатор доступа по умолчанию (также называемый package-private или package visible) — это следующий по строгости модификатор после protected. Он не указывается явно: если вы не указываете public, protected или private, то используется именно этот модификатор.
Если вы напишете в своем коде следующее:
int x = 10то переменная x получит доступ по умолчанию — ее будет видно только в пределах пакета.
Как и в случае с модификатором protected, его применение ограничено. Обычно он встречается в тех пакетах, где есть вспомогательные классы, не реализующие основную бизнес-логику других классов в пакете.
Пример: представим, что у нас есть пакет services. Он содержит различные классы, которые работают с базой данных. Например, UserService — читает данные о пользователях, CarService — данные об автомобилях. Каждый класс работает со своим типом объектов и извлекает соответствующую информацию:
package services;
public class UserService {
}
package services;
public class CarService {
}Run CodeНо бывает, что данные в базе данных хранятся в одном формате, а нам нужен другой.
Представьте, что даты рождения пользователей в базе данных хранятся в формате <TIMESTAMP WITH TIME ZONE>…
2014-04-04 20:32:59.390583+02… а вместо этого нам нужно просто получить объект java.util.Date.
Чтобы решить эту задачу, мы можем создать внутри пакета services специальный класс Mapper, который будет отвечать за преобразование данных из базы в Java-объекты. Простой вспомогательный класс.
Обычно мы объявляем все классы как public class ClassName, но это не обязательно.
Мы можем просто написать:
package services;
class Mapper {
}
package services;
public class CarService {
Mapper mapper;
}Run CodeТакой класс будет доступен только внутри пакета services:
package services;
public class CarService {
Mapper mapper;
}Run CodeИ логика этого проста: зачем другим видеть вспомогательный класс, если он нужен только внутри пакета?
Модификатор public
И, наконец, последний в списке, но не по значимости — модификатор public!

После изучения интерфейсов становится понятно, что public предназначен для открытия метода или класса для внешнего использования.
Допустим, вы написали программу-переводчик, которая умеет переводить текст с русского на английский, и реализовали метод translate(String textInRussian), в котором описана вся логика перевода.
Метод отмечен словом public — значит, он теперь часть интерфейса класса:
public class Translator {
public String translate(String textInRussian) {
// Translates text from Russian to English
}
}Run CodeТеперь можно привязать этот метод к кнопке “Перевести” на экране, и все — любой может им воспользоваться.
Все части программы, отмеченные public, предназначены для конечного пользователя.
Если привести аналогию с реальной жизнью: private — это всё, что происходит внутри телевизора, а public — это кнопки на пульте управления.
Пользователю совершенно не нужно знать, как устроен телевизор изнутри — ему важно, чтобы кнопки работали: on(), off(), nextChannel(), increaseVolume(), и так далее.
Чтобы закрепить полученные знания, мы рекомендуем посмотреть видеоурок (на английском языке):
Теперь, когда вы узнали о модификаторах доступа Java — private, default, protected и public — объединим все вышеописанное в простую и удобную для чтения сравнительную таблицу.
Эта таблица поможет увидеть различия между модификаторами и понять, какой из них использовать в той или иной ситуации.
Сравнительная таблица модификаторов доступа
| Модификатор | Класс | Пакет | Подкласс (в том же пакете) | Подкласс (в другом пакете) | Другие классы |
|---|---|---|---|---|---|
private | ✔️ | ❌ | ❌ | ❌ | ❌ |
default (без модификатора) | ✔️ | ✔️ | ✔️ | ❌ | ❌ |
protected | ✔️ | ✔️ | ✔️ | ✔️ | ❌ |
public | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ |
Что это значит?
- ✔️ – Доступно
- ❌ – Недоступно
Краткие сведения
private: доступен только в пределах текущего класса. Рекомендуется для инкапсуляции.default: доступ ограничен текущим пакетом. Модификатор не требуется.protected: доступен в текущем пакете и в производных классах из других пакетов. Подходит для наследования.public: доступен повсеместно. Используется для публичных интерфейсов и открытых методов.
Рекомендации по использованию
Вот небольшая шпаргалка, которая поможет вам определиться с выбором:
private— если нужно скрыть логику и защитить внутренние данные.default— при использовании компонентов в рамках одного пакета.protected— если нужно дать доступ подклассам (даже из других пакетов), но оставить скрытым для остальных.public— если метод или класс должен быть доступен везде.
Перевод статьи «Access Modifiers in Java».
