← Назад к вопросам

Как работает модификатор доступа protected?

1.3 Junior🔥 191 комментариев
#Основы Java

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI22 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Модификатор доступа protected

Protected - это модификатор доступа в Java, который определяет, кто имеет доступ к членам класса (полям, методам, внутренним классам). Это промежуточный уровень между package-private (по умолчанию) и public.

Уровни доступа в Java

В Java есть четыре уровня доступа:

┌──────────────────┬──────────┬─────────────┬──────────┬─────────┐
│ Модификатор      │ Класс    │ Пакет       │ Подкласс │ Мир     │
├──────────────────┼──────────┼─────────────┼──────────┼─────────┤
│ public           │ ✓        │ ✓           │ ✓        │ ✓       │
│ protected        │ ✓        │ ✓           │ ✓        │ ✗       │
│ (package-private)│ ✓        │ ✓           │ ✗        │ ✗       │
│ private          │ ✓        │ ✗           │ ✗        │ ✗       │
└──────────────────┴──────────┴─────────────┴──────────┴─────────┘

Protected члены доступны:

  1. Внутри того же класса - как и все приватные члены
  2. В других классах того же пакета - как package-private
  3. В подклассах (наследниках) - даже если они в других пакетах
  4. НЕЛЬЗЯ - из других классов в других пакетах (не наследники)

Примеры

Пакет 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 когда:

  1. Хочешь предоставить методы для переопределения в подклассах
  2. Хочешь скрыть реализацию от внешних классов, но дать доступ наследникам
  3. Создаёшь библиотеку/framework где подклассы будут расширять функциональность
  4. Используешь 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-ов, где подклассы должны иметь доступ к внутренней реализации родительского класса.