Модификаторы доступа в Java
Управляйте видимостью в Java с помощью модификаторов доступа public, private, protected и package-private (по умолчанию).
Модификаторы доступа определяют, кто может видеть и использовать класс, поле, метод или конструктор. В Java существует четыре уровня — public, protected, package-private (без ключевого слова) и private — от «доступно всем» до «только этот класс». Правильный выбор модификатора позволяет провести границу между интерфейсом объекта и его внутренней реализацией.
Четыре уровня
От наименее ограничительного к наиболее ограничительному:
| Модификатор | Видимость |
|---|---|
public | Откуда угодно |
protected | Тот же пакет или любой подкласс (даже из другого пакета) |
| (без ключевого слова) | Только тот же пакет — package-private |
private | Только сам класс |
Это всё. Никакого «friend», никакого «module-internal» на этом уровне; видимость модулей — это отдельная возможность Java Platform Module System, которая в большинстве случаев не нужна.
public
Член с модификатором public является частью опубликованного API класса — он виден любому коду, который имеет доступ к самому классу:
public class Greeter {
public String greet(String name) {
return "Hello, " + name;
}
}
new Greeter().greet("world"); // anyone can call thisИспользуйте public для тех элементов, которые предназначены для вызова другим кодом — даже из других проектов. Обратная сторона: как только что-то становится public, вы берёте на себя обязательство его поддерживать; изменение сигнатуры сломает всех вызывающих.
private
private ограничивает видимость члена только объявляющим классом. Ничто извне не может его использовать — даже подкласс:
public class Account {
private int balance; // only Account can read/write
public void deposit(int amount) {
if (amount <= 0) throw new IllegalArgumentException();
balance += amount; // ok — same class
}
}
Account a = new Account();
a.balance = -1; // ERROR — balance is privateЭто основной модификатор для полей. Скрытие состояния за private делает проверку в методе deposit действительно значимой. Паттерн подробно описан в главе об инкапсуляции.
Package-private (без ключевого слова)
Если модификатор не указан вовсе, член виден всему коду в том же пакете — но не за его пределами:
// in package com.shop
class CartHelper { // no public — package-private
static int sum(int[] prices) { ... }
}Другие классы в com.shop могут вызывать CartHelper.sum(...); код в com.app не увидит даже существования этого класса. Используйте этот уровень для внутренних вспомогательных элементов, которые нужны внутри пакета, но не должны быть частью внешнего API проекта.
Распространённая ошибка: забыть указать модификатор, а затем удивляться, что класс доступен из main (тоже находящегося в том же пакете во время обучения), но недоступен из теста в другом пакете.
protected
protected — это package-private плюс подклассы. Код в том же пакете может использовать член, и любой подкласс — даже из другого пакета — тоже может его использовать:
public class Shape {
protected double area; // subclasses can read/write
}
public class Circle extends Shape {
Circle(double r) {
this.area = Math.PI * r * r; // ok — subclass access
}
}Этот модификатор нужен, когда вы хотите, чтобы подклассы могли подключаться к механизмам родителя, но не хотите, чтобы к ним мог обратиться произвольный клиентский код. На практике protected встречается намного реже, чем public или private; многие разработчики предпочитают использовать private-поля с protected-методами доступа для аналогичного эффекта с большим контролем.
Где применяются модификаторы
Модификатор можно указать для:
- Класса (
public class Foo). Классы верхнего уровня могут быть толькоpublicили package-private. Вложенные классы могут использовать любой из четырёх модификаторов. - Поля (
private int count). - Метода (
public int get() {...}). - Конструктора (
private Singleton() {...}— см. паттерн Singleton).
Модификатор доступа нельзя указать для локальной переменной или параметра метода. Их область видимости определяется исключительно местом объявления.
Правило «один public-класс на файл»
Файл .java может содержать несколько классов верхнего уровня, но не более одного из них может быть public, и его имя должно совпадать с именем файла:
// file: Order.java
public class Order { ... } // ok — matches filename
class OrderHelper { ... } // ok — package-private
public class Customer { ... } // ERROR — second public classБольшинство проектов придерживаются правила «один класс на файл», однако package-private вспомогательные классы рядом с public-классом иногда бывают полезны.
Как выбрать правильный модификатор
Практическое правило, которое хорошо масштабируется:
- По умолчанию используйте
privateдля полей и вспомогательных методов. publicдля методов, которые действительно должны вызываться извне.- Package-private для элементов, которые нужно разделить внутри пакета, но не раскрывать за его пределами.
protectedтолько тогда, когда нужен доступ из подклассов.
Расширить доступ позже (private → public) легко; сузить его без нарушения совместимости — болезненно. Начинайте с самого ограничительного.
Модификаторы не влияют на поведение
Модификатор доступа — это проверка на этапе компиляции. Во время выполнения два одинаковых поля типа int ведут себя одинаково, независимо от того, объявлены ли они public или private. Модификатор существует исключительно для того, чтобы не дать другому коду скомпилироваться с использованием данного члена — для соблюдения границы, которую вы объявляете.
Рабочий пример
Что дальше
public, protected, private — это модификаторы доступа. В Java есть ещё одно семейство — неакцессорные модификаторы, такие как static, final, abstract, synchronized — они управляют поведением, а не видимостью. Следующая глава, неакцессорные модификаторы, посвящена обзору этих возможностей.