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

В чем разница между protected и default?

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

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

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

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

Разница между protected и default (package-private) модификаторами

В Java существует четыре уровня доступа (access modifiers): public, protected, default (package-private) и private. Protected и default решают разные задачи и имеют важные отличия.

Модификатор default (package-private)

Это отсутствие явного модификатора. Класс, метод или переменная становятся доступны только в пределах одного пакета.

package com.company.app;

// Без модификатора = default
class UserService {
    void processUser(String id) {  // default
        // доступен только из этого пакета
    }
}

// Используется в одном пакете
class UserController {
    private UserService userService = new UserService();  // Работает!
    
    public void handleRequest() {
        userService.processUser("123");  // Работает!
    }
}

Модификатор protected

Это более открытый, чем default, но более закрытый, чем public. Protected доступен:

  • В пределах своего пакета (как default)
  • В подклассах (наследниках) из других пакетов
package com.company.animals;

public class Animal {
    protected void makeSound() {  // protected
        System.out.println("Generic sound");
    }
}

// Разный пакет
package com.company.pets;

import com.company.animals.Animal;

public class Dog extends Animal {
    @Override
    public void makeSound() {
        super.makeSound();  // Работает, потому что мы наследник!
        System.out.println("Вау!");
    }
}

// Но попытка доступа не через наследство — не работает
class RandomClass {
    public void test() {
        Animal animal = new Animal();
        animal.makeSound();  // ❌ Ошибка! Protected не доступен
    }
}

Сравнительная таблица

Уровень доступаСвой классПакетНаследник (другой пакет)Остальное
public
protected
default
private

Практические примеры

Пример 1: Доступ через наследство

// Пакет: com.framework
public class BaseRepository<T> {
    protected List<T> items = new ArrayList<>();
    
    protected void save(T item) {
        items.add(item);
    }
}

// Пакет: com.myapp
import com.framework.BaseRepository;

public class UserRepository extends BaseRepository<User> {
    public void addUser(User user) {
        save(user);  // ✅ Работает! Protected доступен наследникам
        items.add(user);  // ✅ И поля тоже
    }
}

// Но вне наследства — не работает
class UserController {
    private UserRepository repo = new UserRepository();
    
    public void createUser() {
        repo.save(new User());  // ❌ Ошибка! Protected не для клиентов
    }
}

Пример 2: Default в одном пакете

// Пакет: com.myapp.service
class PaymentService {  // default
    void process() {
        // доступен только в этом пакете
    }
}

class OrderService {  // default
    private PaymentService paymentService = new PaymentService();  // ✅ Работает!
    
    public void checkout() {
        paymentService.process();  // ✅ Работает!
    }
}

// Другой пакет
package com.myapp.controller;

import com.myapp.service.PaymentService;

class OrderController {
    public void handleOrder() {
        PaymentService payment = new PaymentService();  // ❌ Ошибка!
    }
}

Пример 3: Protected в иерархии классов

public abstract class Vehicle {
    protected String model;  // protected поле
    
    protected abstract void start();  // protected метод
    
    protected void maintenance() {
        System.out.println("Обслуживание " + model);
    }
}

public class Car extends Vehicle {
    @Override
    protected void start() {
        System.out.println("Запуск двигателя");
    }
    
    public void driveCar() {
        maintenance();  // ✅ Доступен из подкласса
        System.out.println("Модель: " + model);  // ✅ Protected поле
    }
}

public class Truck extends Vehicle {
    @Override
    protected void start() {
        System.out.println("Запуск грузовика");
    }
}

// Использование
class Main {
    public static void main(String[] args) {
        Car car = new Car();
        car.start();  // ❌ Ошибка! Protected не доступен клиентам
    }
}

Когда использовать что

Используй default (package-private) когда:

  • Класс/метод нужен только для внутреннего использования в пакете
  • Это вспомогательный класс для группы связанных компонентов
  • Хочешь строго контролировать доступ
package com.company.payment;

// Default класс — вспомогательный для PaymentService
class PaymentValidator {
    void validateCard(String cardNumber) {
        // ...
    }
}

public class PaymentService {
    private PaymentValidator validator = new PaymentValidator();
    
    public void processPayment(Payment payment) {
        validator.validateCard(payment.getCardNumber());
    }
}

Используй protected когда:

  • Метод/поле предназначено для переопределения в подклассах
  • Это часть публичного API для наследников
  • Нужна гибкость для расширения функциональности
public abstract class AbstractUserService {
    protected void beforeSave(User user) {
        // Логирование
    }
    
    protected void afterSave(User user) {
        // Уведомления
    }
    
    public final void save(User user) {
        beforeSave(user);
        // основная логика
        afterSave(user);
    }
}

public class AdminUserService extends AbstractUserService {
    @Override
    protected void afterSave(User user) {
        // Дополнительная логика для админа
        notifyAdmins(user);
    }
}

Правило инкапсуляции

// ❌ Плохо — всё public
public class BadDesign {
    public List<User> users;  // Опасно!
    public void loadUsers() { }
    public void saveUsers() { }
}

// ✅ Хорошо — правильные уровни доступа
public class GoodDesign {
    private List<User> users;  // private для изоляции
    
    protected void beforeLoad() { }  // protected для наследников
    protected void afterLoad() { }
    
    public List<User> getUsers() {  // public API
        return new ArrayList<>(users);  // Возвращаем копию!
    }
}

Частые ошибки

Ошибка 1: Забыть, что protected работает в пакете

// Ошибочное предположение
package com.api;

public class APIBase {
    protected String token;  // Думаешь, что защищено от соседей?
}

package com.api;

class APIClient {
    void test() {
        APIBase base = new APIBase();
        base.token = "secret";  // ❌ Ошибка! Это же работает!
    }
}

Да, protected доступен в пакете! Если нужна защита от соседей — используй private.

Ошибка 2: Использовать protected для частной вспомогательной логики

// ❌ Плохо
public class FileProcessor {
    protected void parseXML() { }  // Это вспомогательный метод
    protected void validateSchema() { }  // Зачем его видно наследникам?
}

// ✅ Хорошо
public class FileProcessor {
    private void parseXML() { }  // Приватный, для внутреннего использования
    private void validateSchema() { }
    
    protected void processFile(File file) { }  // Только то, что нужно наследникам
}

Заключение

Default (package-private) — используется для сокрытия вспомогательных классов и методов в пределах пакета. Это строгий контроль доступа.

Protected — используется для методов и полей, которые должны быть доступны наследникам. Это позволяет создавать расширяемые базовые классы.

Выбор между ними зависит от того, планируешь ли ты наследование: если нет — используй default, если да — protected.

В чем разница между protected и default? | PrepBro