Кому позволено трогать ваш код
Модификатор доступа - это ключевое слово перед классом, полем, методом или конструктором, которое решает, кому ещё разрешено его использовать. Это основа инкапсуляции: вы прячете беспорядочные внутренности класса и открываете лишь те части, которые доверяете вызывать другому коду.
В Java четыре уровня, от самого открытого до самого закрытого: public, protected, default (вообще без ключевого слова) и private. Если выбрать их правильно, класс становится безопасным для последующих изменений, потому что ничто снаружи не зависит от деталей, сохранять которые вы никогда не обещали.
private: виден только внутри класса
private - самый строгий уровень. К private члену может обращаться только код, написанный внутри того же класса, - ни подклассы, ни другие классы в том же пакете, никто. Именно здесь место большинству ваших полей.
balance является private, поэтому единственный способ изменить его - через deposit, который отвергает отрицательные суммы. Если бы balance был public, любой код мог бы написать account.balance = -9999 и полностью обойти проверку. Эта защита - и есть весь смысл сокрытия поля.
Попробуйте раскомментировать account.balance = 500; в main, и вы получите ошибку компиляции: balance has private access in BankAccount.
public: виден везде
public - противоположная крайность: кто угодно и где угодно может использовать член. Имена ваших методов, которые другой код должен вызывать, обычно public. Таким же является и сам класс, когда он должен быть доступен из других пакетов.
Распространённый шаблон - иногда называемый публичным API класса - таков: приватные поля, публичные методы. Поля хранят состояние, в которое никто другой не должен лезть; методы - это контролируемые двери внутрь и наружу. getBalance и deposit выше - именно это.
default (package-private): вообще без ключевого слова
Если вы не пишете никакого модификатора, вы получаете default доступ, который также называют package-private. Член виден всем классам в том же пакете и невидим всему, что за его пределами. Для этого уровня нет ключевого слова - отсутствие его и есть уровень.
class Invoice { // нет модификатора -> виден только внутри этого пакета
int amount; // нет модификатора -> поле package-private
}
На этом попадается множество новичков: опустить public не значит сделать что-то private, это делает его package-private. Поле, которое вы хотели сохранить в секрете, всё ещё полностью доступно для чтения и записи любым классом, который оказался в том же пакете. Если вы хотите, чтобы оно было действительно скрыто, нужно явно написать private.
Default доступ по-настоящему полезен для вспомогательных классов, которые являются деталью реализации одного пакета, - вы их не экспортируете, но классы, сотрудничающие внутри пакета, по-прежнему могут свободно их использовать.
protected: пакет плюс подклассы
protected на одну ступень свободнее, чем default. protected член виден везде, где виден default (тот же пакет), а также в подклассах - даже в подклассах из другого пакета. Он предназначен для того, чем вы хотите поделиться с классами, расширяющими ваш, но не со всем миром.
Dog обращается к name и sound(), потому что они protected, а Dog - подкласс. Внешний код, который не является подклассом и не находится в пакете, по-прежнему не может их трогать. Прибегайте к protected, когда проектируете класс, предназначенный для расширения; в остальных случаях предпочитайте private.
Четыре уровня с одного взгляда
Вот полная картина, от самого ограничивающего до самого открытого:
| Модификатор | Тот же класс | Тот же пакет | Подкласс (другой пакет) | Везде |
|---|---|---|---|---|
private | да | нет | нет | нет |
| default | да | да | нет | нет |
protected | да | да | да | нет |
public | да | да | да | да |
Простое практическое правило: начинайте с private и ослабляйте только тогда, когда чему-то действительно нужен более широкий доступ. Расширить доступ позже гораздо проще, чем сузить его после того, как другой код начал зависеть от члена.
Модификаторы у самих классов
Модификаторы доступа применимы и к классам, а не только к членам, - но класс верхнего уровня (не вложенный в другой) может быть только public или default. Нельзя написать класс верхнего уровня private или protected.
public class Order { ... } // доступен из любого пакета
class LineItem { ... } // default - виден только этому пакету
Класс верхнего уровня public должен находиться в файле, названном по его имени (Order.java). Вложенные же классы могут использовать все четыре модификатора - именно поэтому примеры выше могли помечать внутренние классы как private или default. Тонкий момент: public метод, возвращающий тип package-private, не очень полезен для внешних вызывающих, поэтому сохраняйте публичную поверхность согласованной.
Далее: статические члены
До сих пор каждое поле и метод здесь принадлежат экземпляру - вы создаёте объект через new, а затем вызываете на нём методы. Но иногда значение или поведение принадлежит классу в целом, общему для всех экземпляров, - например, счётчик того, сколько объектов существует. Именно для этого нужно ключевое слово static, и оно идёт следующим.
Часто задаваемые вопросы
Какие четыре модификатора доступа есть в Java?
public, private, protected и default (без ключевого слова, его ещё называют package-private). public виден везде, private - только внутри того же класса, protected - внутри пакета плюс в подклассах, а default - только внутри того же пакета.
В чём разница между public и private в Java?
public означает, что любой код где угодно может обратиться к члену. private означает, что это может сделать только код внутри того же класса - подклассы и другие классы его даже не видят. Обычно поля делают private и открывают доступ к ним через public методы.
Что значит, если у поля в Java нет модификатора доступа?
Отсутствие ключевого слова означает default (package-private) доступ: член виден всем классам в том же пакете, но невидим за его пределами. Это строже, чем public, но свободнее, чем private - и его легко опустить по случайности, поэтому подходите к этому осознанно.