Модификаторы доступа. Private, protected, default, public

Private — наиболее строгий модификатор доступа. Он ограничивает видимость данных и методов пределами одного класса. Этот модификатор тебе известен из лекции про геттеры и сеттеры. Помнишь этот пример? Мы рассматривали его в одной из статей раньше. Здесь мы допустили серьезную ошибку: открыли наши данные, в результате чего коллеги-программисты получили доступ напрямую к полям класса и изменению их значения. Более того, эти значения присваивались без проверок, в результате чего в нашей программе можно создать кота с возрастом -1000 лет, именем «» и весом 0. Для решения этой проблемы мы использовали геттеры и сеттеры, а также ограничили доступ к данным с помощью модификатора private . Собственно, ограничение доступа к полям и реализация геттеров-сеттеров — самый распространенный пример использования private в реальной работе. То есть реализация инкапсуляции в программе — главное предназначение этого модификатора. Это касается не только полей, кстати. Представь, что в твоей программе существует метод, который реализует какую-то ОЧЕНЬ сложную функциональность. Что бы придумать такого для примера… Скажем, твой метод readDataFromCollider() принимает на вход адрес с данными, считывает данные из Большого Адронного Коллайдера в байтовом формате, преобразует эти данные в текст, записывает в файл и распечатывает его. Даже описание метода выглядит жутковато, что уж говорить про код 🙂 Чтобы повысить читаемость кода, было бы хорошо не писать сложную логику метода в одном месте, а наоборот — разбить функциональность на отдельные методы. Например, метод readByteData() отвечает за считывание данных, convertBytesToSymbols() конвертирует считанные с коллайдера данные в текст, saveToFile() сохраняет полученный текст в файл, а printColliderData() — распечатывает наш файл с данными. Метод readDataFromCollider() в итоге стал бы намного проще: Однако, как ты помнишь из лекции про интерфейсы, пользователь получает доступ только к конечному интерфейсу. А наши 4 метода не являются его частью. Они вспомогательные: мы создали их, чтобы улучшить читаемость кода и не засовывать четыре разные задачи в один метод. Давать доступ пользователю к этим методам не нужно. Если у пользователя при работе с коллайдером появится доступ к методу convertBytesToSymbols() , он скорее всего просто не поймет, что это за метод и зачем нужен. Какие байты конвертируются? Откуда они взялись? Зачем их конвертировать в текст? Логика, которая выполняется в этом методе, не является частью интерфейса для пользователя. Только метод readDataFromCollider() — часть интерфейса. Что же делать с этими четырьмя «внутренними» методами? Правильно! Ограничить доступ к ним модификатором private . Так они смогут спокойно выполнять свою работу внутри класса и не вводить в заблуждение пользователя, которому логика каждого из них по отдельности не нужна.
Модификатор protected
- в пределах всех классов, находящихся в том же пакете, что и наш;
- в пределах всех классов-наследников нашего класса.
Модификатор package visible
Модификатор public

И последний по списку, но не по значимости — модификатор public ! С ним ты познакомился в первый день учебы на JavaRush, впервые в жизни запустив public static void main(String[] args) . Теперь, когда ты изучил лекции об интерфейсах, для тебя очевидно его предназначение 🙂 Ведь public создан для того, чтобы отдавать что-то пользователям. Например, интерфейс твоей программы. Допустим, ты написал программу-переводчик, и она умеет переводить русский текст в английский. Ты создал метод translate(String textInRussian) , внутри которого реализована необходимая логика. Этот метод ты отметил словом public , и теперь он станет частью интерфейса: Можно связать вызов этого метода с кнопкой «перевести» на экране программы — и все! Кто угодно может этим пользоваться. Части кода, помеченные модификатором public , предназначаются для конечного пользователя. Если привести пример из жизни, private — это все процессы, происходящие внутри телевизора, когда он работает, а public — это кнопки на пульте телевизора, с помощью которых пользователь может им управлять. При этом ему не нужно знать как устроен телевизор и за счет чего он работает. Пульт — это набор public -методов: on() , off() , nextChannel() , previousChannel() , increaseVolume() , decreaseVolume() и т.д.
Подготовка к экзамену Oracle Certified Professional Java Programmer — Часть 1
Хочу продолжить делиться приобретенными знаниями и своими впечатлениями от подготовки к экзамену. Огромное спасибо всем тем, кто дал рекомендации к нулевой части этой серии! Сегодня я поговорю еще немножко о модификаторах доступа и их взаимоотношениях с наследованием и пакетами, рассмотрю varargs и перечисления, а также массивы и способы их инициализации. Я надеюсь, что хабражители снова откликнутся и дополнят то, о чем я забыл упомянуть или попросту не знал.
Продолжаем готовиться к экзамену под катом.
Содержание для всей серии
Методы, поля, локальные переменные и их модификаторы
Как я уже говорил, в Java существуют четыре модификатора доступа: public, private, protected и отсутствие модификатора (он же модификатор по умолчанию). К невложенным классам и интерфейсам применимы только два из них: public и модификатор по умолчанию. К методам и полям класса применим весь набор.
- Если метод или поле имеют модификатор public, то они потенциально доступны всей вселенной.
- Если метод или поле имеют модификатор доступа private, то они доступны только в рамках класса. Такие члены класса не наследуются, поэтому их невозомжно заместить в подклассах. Помните об этом.
- Если метод или поле имеют модификатор доступа по умолчанию, то они доступны только в рамках пакета.
- Если метод или поле имеют модификатор доступа protected, то они, прежде всего, доступны самому классу и его наследникам. Кроме того, доступ к этим членам класса могут получить их собратья по пакету.
Проверяя предложенный в рамках экзамена код, следует быть аккуратным. Всегда обращайте внимание как на модификатор доступа метода или поля, так и на модификатор доступа класса. Часто можно встретить ситуацию, когда метод имеет модификатор public, в то время как класс, его содержащий, доступен только из пакета. В этой ситуации метод из вне пакета доступен не будет. Можно легко получить минус, не обратив на эту деталь внимания.
Хочу также обратить внимание на некоторые особенности, которые возникают при использовании доступа по умолчанию и модификатора protected. Рассмотрим следующуий пример. Пусть имеется базовый класс, объявленный в пакете test. Этот класс обладает двумя полями. Первое объявлено с доступом по умолчанию, второе — protected.
Если объявить в этом пакете класс SamePackageAccess, который не будет наследоваься от BaseClass, то он все равно получит доступ и к полю defaultValue, и к полю protectedValue. Об этой особенности модификатора protected стоит помнить: члены класса, объявленные как protected, в рамках пакета доступны как через наследование, так и через ссылку. Пример:
В случае с наследованием в этом пакете доступ по-прежнему сохраняется к обоим полям, причем как по ссылке, так и через наследование.
Теперь давайте посмотрим, что будет, если мы выйдем за пределы пакета. Первое, что случится — мы потеряем доступ к полю, объявленному без явного указания модификатора доступа. Его не будут видеть абсолютно все классы вне родного пакета, в том числе и прямые наследники BaseClass. Поле же с модификатором protected будет доступно через наследование всем своим подклассам. Однако даже наследник не сможет его использовать через ссылку. Кроме того, будучи однажды унаследованным классом вне пакета, поле становится закрытым для любых классов, за исключением дальнейших наследников.
- Код будет успешно скомпилирован
- Возникнет ошибка компиляции на строке с номером 8
- Возникнет ошибка компиляции на строке с номером 12
Среди модификаторов, связанных с наследованием, следует также рассмотреть final. На методы final действует также, как на классы: запрещает их переопределение наследниками. При этом расширять сам класс, в котром находится final метод, по-прежнему можно.
Разрешается применять модификатор final к полям, аргументам методов и локальным переменным. В случае примитивных типов будет запрещено любое изменение значения переменной, кромее ее инициализации. Тут следует помнить, что моментом инициализации локальных переменных считается первое присваивание им значения в рамках метода. До этого переменную использовать нельзя: получите ошибку при компиляции. Помеченное final поле также придется явным образом инициализировать. Это можно сделать либо непосредственно при объявлении, в инициализационном блоке, либо в конструкторе того класса, в котором оно объявлено. Оставлять инициализацию final полей на совести наследников не разрешается. В случае ссылок модификатор final запретит переприсваивать ссылку. Сам объект, на который ссылка указывает, все еще можно изменять: вызывать изменяющие его состояния методы, присваивать полям новое значение и так далее.
- К полям, как я уже говорил, применимы все четыре уровня доступа.
- Поле может быть помечено как final.
- Поле может быть помечено как transient.
- Поле может быть помечено как static.
- Поле может быть помечено как volatile.
- Поле не может быть помечено как abstract.
- Поле не может быть помечено как synchronized.
- Поле не может быть помечено как strictfp.
- Поле не может быть помечено как native.
Методы с переменным количеством аргументов
- Когда вы указываете параметр vararg, то базовым типом может быть любой тип: примитивный или нет.
- Чтобы объявить такой параметр, вы пишите тип, потом три точки, пробел, затем имя массива, который будет использоваться в рамках метода: void f (int. a) . Можно также разделить тип, три точки и идентифкаторы пробелами, так: void f(int . a) . Внимательно следите за точками. Авторы экзамена любят переносить их за идентификатор. Такой подход не работает.
- В метод могут передаваться другие параметры, но в этом случае параметр vararg должен быть последним: void f (double x, int. a)
- В методе может быть один и только один vararg параметр.
- static void doSomething(int. values) <>
- static void doSomething(int[] values) <>
- static void doSomething(int x, int. values) <>
Правильными являются первый и третий варианты. И тот, и другой корректны как с точки семантики вызова, так и с точки зрения синтаксиса. Использовать же массив как тип для передачи нескольких параметров так просто не получится. А вот обратное не верно. Пример:
Все чудесно соберется и отработает. Формального объяснения этому я не знаю, но предполагаю, что это связано с тем, что vararg-параметр является всего лишь синтаксическим сахаром и воспринимается компилятором как ссылка на массив, поэтому никаких проблем не возникает.
Перечисления
- У перечислений могут быть конструкторы.
- У перечислений могут быть поля.
- У перечислений могут быть методы.
- Если перечисление объявляется вне класса, оно может получить только два уровня доступа: public или по умолчанию.
- У перечислений есть статический метод values() , который возвращает массив, содержащий все возможные значения перечисления, причем строго в том порядке, в котором они были объявлены.
В результате выполения этого кода в стандартный поток вывода будет помещена строка «USD». Обратите внимание на метод getSomethingElse(). Он объявлен для значения USD, однако не упоминается для всего перечисления. Не смотря на то, что в объявлении стоит public, никто из вне доступ к этому методу получить не сможет. Если строку за номером 44 раскомментировать, то код даже не скомпилируется.
Немного о массивах
В Java допустимы два варианта объявления массивов. Квадратные скобки могут быть размещены после имени типа, так: int[] a , — или после идентификатора, так: int a[] . Важно понимать, что оба способа абсолютно равноправны с точки зрения синтаксиса, хотя первый из них и является рекомендуемым. Таким образом, String[] s[] — это ни что иное, как двумерный массив строк. Скомпилируется без вопросов.
При объявлении массива нельзя указать его размер, так как память выделяется только в момент создания массива: int[] a = new int [4] . Поэтому код int a[4] вызовет ошибку компиляции. В случае с массивом ссылок на объекты важно помнить, что при создании массива сами объекты не создаются. К примеру, код Thread threads = new Thread [20] создаст массив из двадцати null’ов, никаких конструкторов вызываться не будет.
При построении многомерных массивов о них нужно думать, как о массивах, каждый элемент которых ссылается снова на массив. Абстрактные конструкции вроде матриц и кубов упрощают программирование, но могут усложнить сдачу экзамена. К примеру, конструкция int [][]a = new int [10][] вполне допустима и создаст двумерный массив, элементы которого могут быть проинициализированы позже: a[0] = new int [100] , — причем совсем не обязательно массивами равной длины: a[1] = new int [200] .
Для того, чтобы проинициализировать массив быстро (не элемент за элементом), можно применять синтаксис вроде этого: int[] x = <1, 2, 3>. В фигурных скобках могут стоять не только константы, но и переменные и даже выражения. Можно также создавать анонимные массивы, что часто используется когда нужно передать строго определенный массив в функцию: f(new int [] <2, 4, 8>) . Если вы видите на экзамене такую конструкцию, то обязательно присмотритесь внимательнее. Есть вероятность, что будет написано что-то вроде этого: f(new int[3] <2, 4, 8>) . Такой код не будет скомпилирован, так как размер анонимного массива вычисляется исходя из его объявления и не должен указываться явным образом.
На этом я закончу на сегодня. В ближайшее время обязательно поговорим об особенностях некоторых важных операций в Java (присваивание, сравнение, instanceOf, арифметика), а также о неявных классах и потоках.
Controlling Access to Members of a Class
Access level modifiers determine whether other classes can use a particular field or invoke a particular method. There are two levels of access control:
- At the top level— public , or package-private (no explicit modifier).
- At the member level— public , private , protected , or package-private (no explicit modifier).
A class may be declared with the modifier public , in which case that class is visible to all classes everywhere. If a class has no modifier (the default, also known as package-private), it is visible only within its own package (packages are named groups of related classes — you will learn about them in a later lesson.)
At the member level, you can also use the public modifier or no modifier (package-private) just as with top-level classes, and with the same meaning. For members, there are two additional access modifiers: private and protected . The private modifier specifies that the member can only be accessed in its own class. The protected modifier specifies that the member can only be accessed within its own package (as with package-private) and, in addition, by a subclass of its class in another package.
The following table shows the access to members permitted by each modifier.
| Modifier | Class | Package | Subclass | World |
|---|---|---|---|---|
| public | Y | Y | Y | Y |
| protected | Y | Y | Y | N |
| no modifier | Y | Y | N | N |
| private | Y | N | N | N |
The first data column indicates whether the class itself has access to the member defined by the access level. As you can see, a class always has access to its own members. The second column indicates whether classes in the same package as the class (regardless of their parentage) have access to the member. The third column indicates whether subclasses of the class declared outside this package have access to the member. The fourth column indicates whether all classes have access to the member.
Access levels affect you in two ways. First, when you use classes that come from another source, such as the classes in the Java platform, access levels determine which members of those classes your own classes can use. Second, when you write a class, you need to decide what access level every member variable and every method in your class should have.
Let's look at a collection of classes and see how access levels affect visibility. The following figure shows the four classes in this example and how they are related.
Classes and Packages of the Example Used to Illustrate Access Levels
The following table shows where the members of the Alpha class are visible for each of the access modifiers that can be applied to them.
| Modifier | Alpha | Beta | Alphasub | Gamma |
|---|---|---|---|---|
| public | Y | Y | Y | Y |
| protected | Y | Y | Y | N |
| no modifier | Y | Y | N | N |
| private | Y | N | N | N |
If other programmers use your class, you want to ensure that errors from misuse cannot happen. Access levels can help you do this.
Java Access Modifiers: public, protected, default, private
![]()
In this post, we will cover another of the Basic concepts in Java and OOP in general. If you are a Java dev you (hopefully ) already know this stuff. But the reason why I still chose to cover this topic is that it is so crucial. I’ve seen it many times on internship and junior position interviews. Also, Java Modifiers knowledge is a requirement before we go into discussing things like Encapsulation or Design Patterns.
So Java Modifiers, what are they? Modifiers are the keywords we use in Java to describe visibility or a specific behavior. You might have seen keywords such as: public , static , private and such…
We have two groups of modifiers:
- Access Modifiers
- Other (non-Access) Modifiers
Access Modifiers
In this post, I’ll focus on the first group — Access Modifiers.
In Java, we can define the visibility of class attributes and methods. For this purpose, we have 4 access modifiers at our disposal: public , protected , private , and default (no keyword specified).
public
When we use public modifier there is no restriction to the visibility of a method or attribute. Method or attribute with this modifier can be accessed from any other class in our app. Example:
First, take a look at Person class. It has an attribute height , declared with public access modifier. It also contains sayHeight method with public access modifier. Both attribute and the method should be accessible from the other classes. We show this by calling the method sayHeight on line 10: person.sayHeight(); and accessing height attribute on line 12: System.out.println(“Person's height is: “ + person.height); from the App class. Also notice that App class does not inherit Person class, nor are they in the same package.
protected
When a method or an attribute of the class has protected modifier, only subclasses or classes in the same package can access that method or attribute. Example:
Here we have Person class in model package. Person has height attribute and sayHeight method both declared with protected access modifier.
Next, take a look at the Developer class. It is located in util package, different one than Person class is in. But it also extends Person class. So although Developer and Person classes are in different packages, Developer class can access methods and attributes from Person class which are declared as protected , we can see this on line 13: System.out.println("Developers height is: “ + height);
Now check out HeightDisplayer class. It is not extending Person class but it is located in model package, same as Person class. This allows HeightDisplayer class to access Person class’ attributes and methods which are declared as protected , example on line 6: person.sayHeight();
default (no modifier specified)
When a method or an attribute has no modifier specified then the default modifier is used. This means that method or attribute can be accessed only by classes in the same package. Using the same example as above:
Both classes are in the same model package. But if we had Developer class that is not in the same package as Person but extends it, we would not be able to access height and sayHeight from Developer . That’s because default modifier forbids accessing attributes and methods from subclasses which are not in the same package as the super-class.
private
Method or attribute with private modifier can be accessed only from the class that contains them. Example:
Here we can see that height attribute has private modifier. That means we can only access it within Person class, line 12: System.out.println("My height is: " + height); Trying to access it from any other class would result in an error.
Class or Interface Modifiers
public and default modifiers can be also used on class and interface definitions. They have the same meaning as when they are used on attributes or methods — to define a visibility of a class or an interface. If a class is not visible from another class, we are not able to use it in any way, even if it has public attributes and methods, they won’t be visible to us because the whole class is not visible to us — it cannot be imported.
Access Modifiers Conclusion
So just to recap all the access modifiers and their meanings :
In the next post, I’ll focus on non-Access Modifiers in Java. Feel free to leave a suggestion or a comment and stay tuned for more…