JavaRush /Курсы /JAVA 25 SELF /Ошибки с модификаторами доступа

Ошибки с модификаторами доступа

JAVA 25 SELF
23 уровень , 2 лекция
Открыта

1. Введение

В Java модификаторы доступа — это как система замков в доме. Они определяют, кто и откуда может "зайти" к вам в комнату (или к полю/методу вашего класса). Если все двери открыты — любой может прийти и что-то поменять. Если всё закрыто — никто не сможет ничего сломать, но и вы сами в какой-то момент можете оказаться взаперти.

Напомним, в Java есть четыре основных уровня доступа:

Модификатор Доступен внутри класса Доступен в пакете Доступен в подклассах Доступен в других пакетах
private
(package)
protected
(через наслед.)
public

(package) — это когда модификатор не указан явно. Такой член класса виден только внутри одного пакета.

2. Типичные ошибки с модификаторами доступа

Ошибка 1: Поля и методы с модификатором по умолчанию (package-private)

Самая частая ошибка новичков — забыть указать модификатор доступа. В результате поле или метод становится доступным во всём пакете, хотя вы этого не планировали. Это может привести к тому, что другой класс (в том же пакете, но не связанный с вашим) сможет изменить внутреннее состояние вашего объекта.

// Ошибка: поле name не защищено!
class User {
    String name; // package-private!
}

В результате любой класс из этого пакета может написать:

User user = new User();
user.name = "Вася"; // без ограничений!

Ошибка 2: Нарушение инкапсуляции — открытые поля (public)

Вторая по популярности ошибка — объявлять поля класса как public. Это удобно, когда вы только учитесь или пишете короткий пример, но в реальных проектах это почти всегда плохо. Вы теряете контроль над тем, кто и как меняет ваши данные.

public class Account {
    public double balance; // ОПАСНО!
}

Теперь любой код может сделать:

Account acc = new Account();
acc.balance = -1000000; // И кто теперь виноват?

Ошибка 3: Отсутствие геттеров и сеттеров

Иногда разработчик делает поля private, но забывает добавить методы для управления ими. В результате получить или изменить значение невозможно даже там, где это было бы уместно.

public class Product {
    private String name;
    // Нет ни getName(), ни setName()
}

Ошибка 4: Попытка доступа к private-членам из другого класса

Если вы объявили поле или метод как private, то к нему нельзя обратиться из другого класса, даже если он находится в том же пакете. Новички часто удивляются, почему "не видит" поле.

public class User {
    private String password;
}

public class UserService {
    public void resetPassword(User user) {
        // user.password = "123"; // Ошибка компиляции!
    }
}

Ошибка 5: Ошибки с protected

Многие считают, что protected означает "видно везде, где есть наследование". Но в Java доступ к protected-членам вне пакета возможен только через наследование и только для подкласса. Это тонкость, которую легко упустить.

package animals;

public class Animal {
    protected void sleep() {}
}

package zoo;
import animals.Animal;

public class Dog extends Animal {
    public void test() {
        sleep(); // ОК — наследник
    }
}

public class NotADog {
    public void test() {
        Animal a = new Animal();
        // a.sleep(); // Ошибка: не наследник!
    }
}

3. Как правильно: Best practices

Правило 1: По умолчанию делайте поля private

Это главный принцип инкапсуляции. Поля должны быть скрыты от всех, кроме самого класса. Если нужно дать доступ — используйте геттеры/сеттеры.

public class Book {
    private String title;
    private int pages;

    public String getTitle() {
        return title;
    }
    public void setTitle(String title) {
        this.title = title;
    }
}

Правило 2: Открывайте только необходимые методы

Если метод должен быть доступен извне — делайте его public. Если нужен только внутри пакета — оставляйте package-private. Если метод предназначен только для наследников — используйте protected.

Правило 3: Минимизируйте область видимости

Чем меньше область видимости, тем меньше вероятность случайных ошибок и "неожиданных гостей". Не делайте методы и поля public, если в этом нет необходимости.

Правило 4: Используйте геттеры и сеттеры для контроля доступа

Это позволяет добавить дополнительную логику при чтении/записи поля, например, валидацию.

public class Account {
    private double balance;

    public void setBalance(double balance) {
        if (balance < 0) {
            throw new IllegalArgumentException("Баланс не может быть отрицательным!");
        }
        this.balance = balance;
    }

    public double getBalance() {
        return balance;
    }
}

Правило 5: Не открывайте внутреннюю реализацию

Если у вас есть массив или список как поле, не возвращайте его напрямую через геттер — возвращайте копию или предоставляйте только нужные методы.

public class Team {
    private List<String> members = new ArrayList<>();

    // Правильно:
    public List<String> getMembers() {
        return new ArrayList<>(members); // возвращаем копию
    }
}

4. Примеры на практике

Допустим, у нас есть класс LibraryUser, который описывает пользователя библиотеки.

Пример неправильной реализации

public class LibraryUser {
    public String name;
    public int borrowedBooks;
}

В таком виде любой код может сделать с объектом всё, что угодно:

LibraryUser user = new LibraryUser();
user.name = null;
user.borrowedBooks = -10; // Логика? Какая логика?

Пример правильной реализации с инкапсуляцией

public class LibraryUser {
    private String name;
    private int borrowedBooks;

    public LibraryUser(String name) {
        this.name = name;
        this.borrowedBooks = 0;
    }

    public String getName() {
        return name;
    }

    public int getBorrowedBooks() {
        return borrowedBooks;
    }

    public void borrowBook() {
        borrowedBooks++;
    }

    public void returnBook() {
        if (borrowedBooks > 0) {
            borrowedBooks--;
        }
    }
}

Теперь внешний код не может напрямую изменить количество взятых книг или имя пользователя. Всё контролируется только через методы класса.

5. Особенности и нюансы реализации

Иногда кажется, что проще сделать поле public, чем писать кучу геттеров и сеттеров. Но это ловушка! Открытое поле — это как открытая дверь в квартиру: да, удобно, но не очень безопасно.

Ещё одна тонкость — не всегда нужно делать геттеры и сеттеры для всех полей. Если значение поля не должно меняться после создания объекта, делайте только геттер, а поле — final:

public class Passport {
    private final String number;

    public Passport(String number) {
        this.number = number;
    }

    public String getNumber() {
        return number;
    }
}

Также помните: если класс объявлен как public, имя файла должно совпадать с именем класса! Это не совсем про модификаторы доступа, но очень частая ошибка новичков.

6. Типичные ошибки при работе с модификаторами доступа

Ошибка №1: забыли указать модификатор доступа у поля или метода. В результате поле или метод становится доступным во всём пакете, даже если вы этого не хотели. Всегда явно указывайте модификатор, даже если IDE не ругается.

Ошибка №2: все поля объявлены как public. Это убивает инкапсуляцию, делает ваш код уязвимым и непредсказуемым. Привычка из примеров "для простоты" не должна попадать в рабочий код.

Ошибка №3: попытка обратиться к private-полю из другого класса. Java не позволит это сделать — компилятор вас защитит, но если вы вдруг захотели "обойти" это через рефлексию — задумайтесь, почему вообще возникла такая необходимость.

Ошибка №4: ожидание, что protected-члены будут доступны везде, где есть наследование. На самом деле, вне пакета к ним можно обратиться только из подкласса и только через this или через объект подкласса.

Ошибка №5: возвращение внутренней коллекции через геттер. Если вернуть ссылку на внутренний массив или список, внешний код сможет его изменить, нарушая инварианты класса.

Ошибка №6: отсутствие контроля при установке значения через сеттер. Если не проверить входящее значение, можно получить некорректное состояние объекта (например, отрицательный баланс).

Ошибка №7: слишком широкая область видимости методов. Иногда методы делают public, хотя они нужны только внутри пакета или класса. Это открывает лишний API и усложняет поддержку.

1
Задача
JAVA 25 SELF, 23 уровень, 2 лекция
Недоступна
Простая картотека автосалона 🚗
Простая картотека автосалона 🚗
1
Задача
JAVA 25 SELF, 23 уровень, 2 лекция
Недоступна
Безопасное управление ценами в онлайн-магазине 💰
Безопасное управление ценами в онлайн-магазине 💰
1
Задача
JAVA 25 SELF, 23 уровень, 2 лекция
Недоступна
Защита паролей: Никаких лазеек! 🔐
Защита паролей: Никаких лазеек! 🔐
1
Задача
JAVA 25 SELF, 23 уровень, 2 лекция
Недоступна
Кто может "говорить" в зоопарке? 🐾
Кто может "говорить" в зоопарке? 🐾
Комментарии
ЧТОБЫ ПОСМОТРЕТЬ ВСЕ КОММЕНТАРИИ ИЛИ ОСТАВИТЬ КОММЕНТАРИЙ,
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ