Как работает модификатор доступа protected?
Комментарии (1)
Ответ сгенерирован нейросетью и может содержать ошибки
Модификатор доступа protected
Protected - это модификатор доступа в Java, который определяет, кто имеет доступ к членам класса (полям, методам, внутренним классам). Это промежуточный уровень между package-private (по умолчанию) и public.
Уровни доступа в Java
В Java есть четыре уровня доступа:
┌──────────────────┬──────────┬─────────────┬──────────┬─────────┐
│ Модификатор │ Класс │ Пакет │ Подкласс │ Мир │
├──────────────────┼──────────┼─────────────┼──────────┼─────────┤
│ public │ ✓ │ ✓ │ ✓ │ ✓ │
│ protected │ ✓ │ ✓ │ ✓ │ ✗ │
│ (package-private)│ ✓ │ ✓ │ ✗ │ ✗ │
│ private │ ✓ │ ✗ │ ✗ │ ✗ │
└──────────────────┴──────────┴─────────────┴──────────┴─────────┘
Protected члены доступны:
- Внутри того же класса - как и все приватные члены
- В других классах того же пакета - как package-private
- В подклассах (наследниках) - даже если они в других пакетах
- НЕЛЬЗЯ - из других классов в других пакетах (не наследники)
Примеры
Пакет 1: com.company.animals
package com.company.animals;
public class Animal {
public String name; // Доступен везде
protected int age; // Доступен в пакете и подклассах
String type; // Доступен только в пакете (package-private)
private String dna; // Доступен только в этом классе
public Animal(String name, int age) {
this.name = name;
this.age = age;
this.type = "unknown";
this.dna = "AGCT";
}
protected void eat() {
System.out.println("Animal eats");
}
protected int getAge() {
return age;
}
}
// Класс в том же пакете
public class Zoo {
public static void main(String[] args) {
Animal dog = new Animal("Rex", 5);
System.out.println(dog.name); // ✓ public
System.out.println(dog.age); // ✓ protected (в пакете)
System.out.println(dog.type); // ✓ package-private
// System.out.println(dog.dna); // ✗ private - ОШИБКА
dog.eat(); // ✓ protected
System.out.println(dog.getAge()); // ✓ protected
}
}
Пакет 2: com.company.wild
package com.company.wild;
import com.company.animals.Animal; // Импортируем из другого пакета
// Подкласс в другом пакете - МОЖЕТ использовать protected
public class Dog extends Animal {
public Dog(String name, int age) {
super(name, age);
}
@Override
protected void eat() {
System.out.println("Dog eats bone");
System.out.println("Dog age: " + age); // ✓ protected от родителя
System.out.println("Dog name: " + name); // ✓ public от родителя
// System.out.println(type); // ✗ package-private
}
}
// Обычный класс в другом пакете - НЕ может использовать protected
public class Hunter {
public static void main(String[] args) {
Dog dog = new Dog("Max", 3);
System.out.println(dog.name); // ✓ public
// System.out.println(dog.age); // ✗ protected - ОШИБКА!
dog.eat(); // ✓ метод public (переопределён)
}
}
Ключевое отличие: protected vs package-private
Package-private (по умолчанию):
package com.company.base;
public class Parent {
int value = 10; // package-private - доступен только в пакете
}
// -------- другой пакет --------
package com.company.derived;
import com.company.base.Parent;
public class Child extends Parent {
public void test() {
System.out.println(value); // ✗ ОШИБКА - package-private
// Даже хотя Child наследует Parent!
}
}
Protected:
package com.company.base;
public class Parent {
protected int value = 10; // protected - доступен в подклассах!
}
// -------- другой пакет --------
package com.company.derived;
import com.company.base.Parent;
public class Child extends Parent {
public void test() {
System.out.println(value); // ✓ OK - protected доступен в подклассе!
}
}
Практический пример: коллекция с защитой
public abstract class SafeCollection<T> {
// Protected - подклассы могут работать с данными напрямую
protected List<T> items = new ArrayList<>();
protected int version = 0;
// Protected метод - подклассы могут вызывать и переопределять
protected void onModify() {
version++;
System.out.println("Collection modified, version: " + version);
}
// Public - доступно всем
public void add(T item) {
items.add(item);
onModify();
}
public int size() {
return items.size();
}
// Protected - только подклассы видят внутреннюю структуру
protected List<T> getItems() {
return items;
}
}
public class UniqueCollection<T> extends SafeCollection<T> {
@Override
protected void onModify() {
// Переопределяем protected метод
super.onModify();
System.out.println("Unique collection now has " + items.size() + " items");
}
public void addUnique(T item) {
if (!items.contains(item)) {
items.add(item);
onModify();
}
}
}
public class Demo {
public static void main(String[] args) {
UniqueCollection<String> collection = new UniqueCollection<>();
collection.add("Java"); // ✓ public
collection.add("Python"); // ✓ public
// collection.items; // ✗ ОШИБКА - protected
// collection.onModify(); // ✗ ОШИБКА - protected
// collection.getItems(); // ✗ ОШИБКА - protected
}
}
Protected в абстрактных классах
public abstract class DatabaseConnection {
// Protected - подклассы ДОЛЖНЫ использовать
protected Connection connection;
protected PreparedStatement stmt;
// Protected - шаблонный метод для подклассов
protected abstract void configureConnection();
// Protected helper метод
protected void executeQuery(String sql) throws SQLException {
stmt = connection.prepareStatement(sql);
// ...
}
public void connect(String url) throws SQLException {
connection = DriverManager.getConnection(url);
configureConnection();
}
}
public class PostgresConnection extends DatabaseConnection {
@Override
protected void configureConnection() {
// Имеем доступ к protected members
connection.setAutoCommit(false);
}
}
Protected конструктор
public class Singleton {
private static Singleton instance;
// Protected конструктор - запрещает создание извне
// но позволяет подклассам
protected Singleton() {
}
public static synchronized Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
}
// Можно расширить
public class ExtendedSingleton extends Singleton {
// ✓ OK - подкласс может вызвать protected конструктор
public ExtendedSingleton() {
super();
}
}
Когда использовать protected
Используй protected когда:
- Хочешь предоставить методы для переопределения в подклассах
- Хочешь скрыть реализацию от внешних классов, но дать доступ наследникам
- Создаёшь библиотеку/framework где подклассы будут расширять функциональность
- Используешь Template Method паттерн
public abstract class ReportGenerator {
public final void generateReport() {
prepareData(); // protected abstract
formatData(); // protected abstract
saveReport(); // protected abstract
}
protected abstract void prepareData();
protected abstract void formatData();
protected abstract void saveReport();
}
НЕ используй protected когда:
- Нужен полный контроль и инкапсуляция (используй private)
- Хочешь использовать public API (используй public)
- Не планируешь наследование (используй package-private)
Правило мизинца (Pinky Rule)
"Protected" - это пятый палец (мизинец) для доступа. Используй его осторожно, потому что он может сломать инкапсуляцию если переусложнить иерархию наследования.
Вывод
Protected - это специальный модификатор, который разрешает доступ:
- В классе (как private)
- В пакете (как package-private)
- В подклассах других пакетов (уникально для protected)
Это основной инструмент для создания расширяемых классов и framework-ов, где подклассы должны иметь доступ к внутренней реализации родительского класса.