1. Огляд модифікаторів доступу
У Java є чотири рівні доступу для класів, полів, методів і конструкторів:
| Модифікатор | Де доступний? |
|---|---|
|
Скрізь: усередині класу, в інших класах і пакетах |
|
Усередині класу, у підкласах (нащадках) та в інших класах того самого пакета |
|
Лише в межах пакета (якщо модифікатор не вказано явно) |
|
Лише всередині поточного класу |
Розберімо кожен із них докладніше — з прикладами, жартами й неочікуваними поворотами.
public — публічний доступ
public — це як оголошення на весь світ: «Усім вхід дозволено!». Якщо клас, поле, метод або конструктор оголошено як public, до нього можна звернутися з будь-якого іншого класу, навіть із іншого пакета.
Приклад:
public class Cat {
public String name;
public void sayMeow() {
System.out.println("Няв!");
}
}
До цього класу та його полів/методів можна звернутися звідусіль. Це зручно, якщо ви хочете, щоб ваш клас був доступний усім: наприклад, якщо ви розробляєте бібліотеку.
Але! Публічні поля — це не завжди добре (див. попередню лекцію). Зазвичай публічними роблять лише методи, які мають бути доступні ззовні, а от поля майже завжди залишають private.
private — доступ лише всередині класу
private — це як сейф із кодовим замком: ніхто, окрім самого класу, не може отримати доступ до цих членів. Навіть нащадки (підкласи) не бачать приватних полів і методів!
Приклад:
public class Cat {
private String secretName;
public void setSecretName(String name) {
secretName = name;
}
public String getSecretName() {
return secretName;
}
}
Тут secretName не можна прочитати або змінити напряму з іншого класу. Тільки сам Cat може це зробити (або його методи). Це основа інкапсуляції: ми приховуємо внутрішні деталі й надаємо доступ до них лише через методи.
protected — захищений доступ
protected — це як VIP-перепустка: доступ дозволено класу, його нащадкам (навіть якщо вони в інших пакетах) і всім класам у межах поточного пакета.
Приклад:
public class Animal {
protected int age;
protected void growOlder() {
age++;
}
}
Тепер будь-який клас, який наслідує Animal, зможе звертатися до поля age і методу growOlder().
public class Cat extends Animal {
public void haveBirthday() {
growOlder();
System.out.println("Коту виповнилося " + age + " років!");
}
}
Також до protected-членів мають доступ усі класи в межах того самого пакета.
(package-private) — доступ у межах пакета
Якщо ви не вказали модифікатор доступу взагалі, то член класу вважається package-private (або «доступ за замовчуванням»). Це як двері без замка, але лише для своїх: доступ дозволено лише класам з того самого пакета.
Приклад:
class Dog {
String name; // package-private
void bark() { // package-private
System.out.println("Гав!");
}
}
Клас Dog, його поле name і метод bark() доступні лише в межах того ж пакета. Спроба звернутися до них із іншого пакета спричинить помилку компіляції.
2. Застосування модифікаторів до полів і методів
Чому поля майже завжди роблять private
Поля класу — це його внутрішній стан. Якщо їх залишити публічними, будь-який зовнішній код зможе їх змінювати будь-коли. Це якби ви дозволили незнайомим дітям гратися з вашими меблями, як із Lego: одного разу ви прокинетеся, а холодильник стоїть у ванній догори дриґом.
Приклад поганої інкапсуляції:
public class Person {
public String name;
public int age;
}
Приклад хорошої інкапсуляції:
public class Person {
private String name;
private int age;
public void setName(String name) {
this.name = name;
}
public String getName() {
return name;
}
}
Методи (гетери й сетери) дозволяють контролювати, як саме зовнішні класи можуть змінювати поля. Наприклад, можна заборонити відʼємний вік.
Коли методи роблять public, protected або package-private
- public — якщо метод має бути доступний усім. Зазвичай це основна функціональність класу.
- protected — якщо метод потрібен лише нащадкам або в межах пакета (наприклад, допоміжні методи, корисні в дочірніх класах).
- package-private — якщо метод потрібен тільки в межах пакета, але не має бути доступним ззовні (наприклад, внутрішні деталі реалізації).
- private — якщо метод використовується лише всередині самого класу (наприклад, допоміжні методи для внутрішньої логіки).
Приклад:
public class BankAccount {
private double balance;
public void deposit(double amount) {
if (amount > 0) {
balance += amount;
}
}
protected void applyInterest() {
balance *= 1.05;
}
void internalAudit() {
// package-private: лише для класів у межах пакета
}
private void logAction(String action) {
// лише для внутрішніх потреб класу
}
}
3. Приклади коду: клас із різними рівнями доступу
Створімо клас, у якому є все: публічні, приватні, захищені й пакетні члени. Також спробуємо звернутися до них з інших класів і подивимося, що вийде.
package zoo;
public class Animal {
public String publicName = "Доступно всім";
protected String protectedName = "Лише для нащадків і пакета";
String packageName = "Лише для пакета";
private String privateName = "Лише для Animal";
public void publicMethod() {
System.out.println("Публічний метод");
}
protected void protectedMethod() {
System.out.println("Захищений метод");
}
void packageMethod() {
System.out.println("Пакетний метод");
}
private void privateMethod() {
System.out.println("Приватний метод");
}
}
Тепер спробуємо звернутися до цих членів з іншого класу в тому ж пакеті:
package zoo;
public class Main {
public static void main(String[] args) {
Animal animal = new Animal();
System.out.println(animal.publicName); // OK
System.out.println(animal.protectedName); // OK
System.out.println(animal.packageName); // OK
System.out.println(animal.privateName); // Помилка: private
animal.publicMethod(); // OK
animal.protectedMethod(); // OK
animal.packageMethod(); // OK
animal.privateMethod(); // Помилка: private
}
}
А тепер спробуємо звернутися до цих членів з іншого пакета:
package other;
import zoo.Animal;
public class Test {
public static void main(String[] args) {
Animal animal = new Animal();
System.out.println(animal.publicName); // OK
System.out.println(animal.protectedName); // Помилка: protected
System.out.println(animal.packageName); // Помилка: package-private
System.out.println(animal.privateName); // Помилка: private
animal.publicMethod(); // OK
animal.protectedMethod(); // Помилка: protected
animal.packageMethod(); // Помилка: package-private
animal.privateMethod(); // Помилка: private
}
}
Висновок:
- public — доступний скрізь.
- protected — доступний у межах пакета й у нащадках (навіть з інших пакетів за умови наслідування).
- package-private — лише в межах пакета.
- private — лише всередині класу.
4. Найкращі практики: як обирати модифікатор доступу
Мінімізуйте область видимості
Чим меншій кількості коду доступні поле або метод, тим краще. Відкривайте лише те, що справді потрібно зовнішньому коду. Це відповідає принципу найменших повноважень (principle of least privilege).
- Поля майже завжди мають бути private. Винятки — тільки для справжніх констант (public static final), але про це детальніше в наступних лекціях.
- Методи роблять public лише якщо вони — частина зовнішнього інтерфейсу класу.
- Допоміжні методи (внутрішня логіка) — private.
- Методи для нащадків — protected.
- Внутрішні сервісні методи для пакета — package-private.
Чому це важливо?
- Будь-яка зміна в деталях реалізації може зламати чужий код, якщо деталі розкриті.
- Клас стає складнішим для тестування та підтримки.
- Випадкові помилки (наприклад, неправильна зміна поля) можуть призвести до багів.
Іноді новачки думають: «Навіщо мені ці складнощі, нехай усе буде public!» Але потім, коли проєкт розростається, доводиться переписувати половину програми лише тому, що хтось безпосередньо змінював поля класу.
5. Типові помилки під час роботи з модифікаторами доступу
Помилка № 1: Залишили поля public або package-private за замовчуванням.
Якщо не вказати модифікатор, поле або метод буде доступним для всіх класів у межах пакета. Це може призвести до неочікуваних ситуацій, якщо хтось почне безпосередньо змінювати ваші поля.
Помилка № 2: Спроба звернутися до private-члена з іншого класу.
Компілятор цього не дозволить — отримаєте помилку. Але якщо ви раптом вирішите обійти це через reflection (відображення) — ласкаво просимо до світу багів і неочікуваних падінь.
Помилка № 3: Забагато public.
Якщо все підряд оголошувати public, то клас стає схожим на відкриту коробку з дротами — будь-хто може смикнути не за той дріт і все зламати.
Помилка № 4: Не використовуєте protected для методів, які потрібні лише нащадкам.
Якщо метод потрібен тільки для розширення в дочірніх класах, оголосіть його protected, а не public.
Помилка № 5: Неявна видимість package-private.
Іноді забувають указати модифікатор, і метод стає доступним усьому пакету. Це може стати несподіванкою, якщо ви думали, що він private.
ПЕРЕЙДІТЬ В ПОВНУ ВЕРСІЮ