🔥 🚀 Важно для всех, кто работает с Java! 🔥
На JavaRocks ты найдешь уникальные туториалы, практические задачи и редкие книги, которых не найти в свободном доступе. Присоединяйся к нашему Telegram-каналу JavaRocks — стань частью профессионального сообщества!
В этой статье мы познакомимся с понятием модификаторов доступа и рассмотрим примеры их использования.
Что такое модификаторы доступа?
Модификаторы доступа в Java – это ключевые слова, которые определяют, кто может использовать или изменять данные и методы в вашем коде. Это как уровни доступа к вещам в реальной жизни: что-то можно показать всем, что-то – только своим близким, а что-то оставить в секрете.
В Java есть четыре модификатора доступа, и вот они в порядке от самого закрытого до самого открытого:
- private – доступно только внутри одного класса. Если что-то помечено как private, это видно и используется только внутри текущего класса.
- default (package visible) – доступно по умолчанию. Если не указывать модификатор, доступ будет открыт только для классов в том же пакете.
- protected – доступно для подклассов и внутри одного пакета.
- public – доступно для всех.
Теперь давайте подробнее посмотрим, что каждый из них делает и когда его лучше использовать на примерах.
Модификатор 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("Meow!");
}
}
public class Main {
public static void main(String[] args) {
Cat cat = new Cat();
cat.name = "";
cat.age = -1000;
cat.weight = 0;
}
}
Run CodeВ коде выше мы допустили серьезную ошибку: сделали наши данные публичными, из-за чего другие программисты могли напрямую изменять их значения. Более того… значения устанавливались без проверок! В итоге программа могла создать кота с именем – ""
, возрастом – 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) {
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;
}
}
Run CodeЗакрытие полей и использование геттеров/сеттеров – один из самых частых примеров применения private
в реальной разработке. Главная цель private
– инкапсуляция: защита данных от некорректных изменений извне.
Но private
применяется не только к полям!
Представьте, что в программе есть очень сложный метод. Допустим, метод readDataFromCollider()
выполняет следующие задачи:
- принимает адрес данных;
- считывает данные с Большого адронного коллайдера в байтовом формате;
- конвертирует байты в текст;
- сохраняет результат в файл;
- выводит данные на экран.
Даже описание метода выглядит страшно, не говоря уже о коде!
Вместо того, чтобы писать всю логику в одном месте, мы разобьем ее на отдельные методы:
readByteData()
– считывает данные;convertBytesToSymbols()
– преобразует байты в текст;saveToFile()
– сохраняет текст в файл;printColliderData()
– выводит данные на экран.
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) {
}
}
Run CodeТеперь метод readDataFromCollider()
станет намного проще.
Но пользователь взаимодействует только с внешним интерфейсом. А наши вспомогательные методы не являются частью интерфейса, они созданы для внутренней логики. Если дать пользователям доступ к convertBytesToSymbols()
, они запутаются: Почему их нужно конвертировать? Что это за байты? Откуда они взялись?
Правильное решение?
Скрываем внутреннюю логику с помощью модификатора 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) {
}
}
Run CodeИспользование private
позволяет:
- Защитить данные от прямого изменения.
- Скрыть сложную внутреннюю логику и не перегружать пользователя ненужной информацией.
- Сделать код чище, понятнее и удобнее для работы.
Инкапсуляция = безопасность + структурированность!
Модификатор protected
Следующим по значимости ограничивающим параметром является protected.

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

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