Привет! В сегодняшней лекции мы познакомимся с понятием «модификаторы доступа» и рассмотрим примеры работы с ними.
Хотя слово «познакомимся» будет не совсем правильным: с большинством из них ты уже знаком по предыдущим лекциям. На всякий случай освежим в памяти главное.
Модификаторы доступа — это чаще всего ключевые слова, которые регулируют уровень доступа к разным частям твоего кода. Почему «чаще всего»? Потому что один из них установлен по умолчанию и не обозначается ключевым словом :)
Всего в Java есть четыре модификатора доступа. Перечислим их в порядке от самых строгих до самых «мягких»:
![Модификаторы доступа. Private, protected, default, public - 2]()
Поля и методы, обозначенные модификатором доступа
Теперь, когда ты изучил лекции об интерфейсах, для тебя очевидно его предназначение :) Ведь

- 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()
и т.д.
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ
Всего в Java есть четыре модификатора доступа. Перечислим их в порядке от самых строгих до самых «мягких»:
private;
default (package visible);
protected;
public.