1. Введение
В Java, как и в жизни, не всё должно быть доступно всем и всегда. Представьте себе квартиру: вы не хотите, чтобы соседи ходили по вашей спальне, правда? Вот и в программах мы иногда хотим “закрыть дверь” к каким-то переменным или методам, чтобы к ним нельзя было обратиться снаружи.
Для этого в Java есть модификаторы доступа — специальные слова, которые указывают, где можно использовать ту или иную переменную или метод: например, public или private.
Основные модификаторы доступа
| Модификатор | Где видно? |
|---|---|
|
Везде, где виден класс (и в других пакетах, и в других файлах) |
|
Только внутри того же класса |
|
Только внутри того же пакета (т.е. внутри классов в той же директории) |
Существуют ещё модификаторы, но о них мы поговорим позже, когда доберёмся до наследования.
Пример на практике
public class User
{
public String name; // видно всем
private int age; // видно только внутри класса User
public void sayHello()
{
System.out.println("Привет, меня зовут " + name);
}
private void secretMethod()
{
System.out.println("Это секретный метод!");
}
}
Пояснение:
- name — видно везде, где можно использовать класс User.
- age — видно только внутри самого класса User.
- sayHello() — публичный метод, его можно вызвать из любого другого класса.
- secretMethod() — приватный метод, его нельзя вызвать снаружи.
Как это работает в реальных задачах?
Допустим, у нас есть класс, описывающий банковский счёт. Мы явно не хотим, чтобы кто угодно мог менять баланс напрямую! Поэтому переменная баланса будет private, а для работы с ней будут специальные методы.
Почему не стоит делать всё public?
Может возникнуть соблазн: «А давайте всё сделаем public, чтобы не мучиться!» Но это путь к хаосу. Представьте, что кто угодно может менять ваш баланс, имя пользователя или даже вызвать метод, который должен был быть только для внутреннего использования. В больших программах это приводит к ошибкам и «магическим» багам.
Золотое правило Java-разработчика:
Сначала делайте всё private, а потом открывайте наружу только то, что действительно нужно.
2. Область видимости переменных
Область видимости — это зона кода, где переменная «существует» и может быть использована. Если выйти за эту зону, переменная «пропадает» — как будто её и не было.
В Java есть несколько видов переменных по области видимости:
- Локальные переменные — объявлены внутри метода или блока кода { ... }.
- Параметры метода — объявлены в круглых скобках метода.
- Поля класса — объявлены внутри класса, но вне методов.
Если переменная объявлена внутри { блока },
она видна только внутри этого блока
и недоступна вне его.
Локальные переменные
Локальная переменная живёт только внутри метода или блока, в котором объявлена.
void printSum(int a, int b)
{
int sum = a + b; // локальная переменная
System.out.println(sum);
}
// sum тут уже не существует!
Попытка обратиться к sum вне метода вызовет ошибку компиляции: «Не могу найти переменную sum».
Параметры метода
Параметры — это тоже переменные, но они живут только внутри метода.
void greet(String name)
{
System.out.println("Привет, " + name);
}
// name тут уже не существует!
Поля класса (переменные-члены)
Поля класса объявляются внутри класса, но вне методов. Они видны во всех методах этого класса.
public class Counter
{
private int count = 0; // поле класса
public void increment()
{
count++; // можем использовать поле
}
public int getCount()
{
return count; // тоже можем использовать поле
}
}
3. Затенение переменных (Shadowing)
Затенение (shadowing) — это ситуация, когда в одной области видимости объявляется переменная (или параметр) с тем же именем, что и во внешней области. Внутри этого блока «новое» имя затеняет старое, и к внешнему значению уже нельзя обратиться напрямую.
Пример затенения:
class ShadowDemo
{
int value = 10; // поле класса
void printValue()
{
System.out.println(value); // 10 — выводит поле класса
int value = 5; // локальная переменная затеняет поле класса
System.out.println(value); // выводит 5, а не 10
}
}
В этом примере, когда мы пишем int value = 5;, мы объявляем новую локальную переменную, которая имеет приоритет над полем класса с тем же именем. При обращении к value внутри метода printValue(), будет использоваться локальная переменная, а не поле класса.
Если всё-таки нужно обратиться к статическому полю класса, то используйте имя класса как префикс:
class ShadowDemo
{
static int value = 10; // статическое поле класса
void printValue()
{
System.out.println(value); // 10 — поле класса
int value = 5;
System.out.println(value); // 5 — локальная переменная
System.out.println(ShadowDemo.value); // 10 — статическое поле класса, доступ через 'ShadowDemo'
}
}
Если нужно обратиться к нестатическому полю класса, используется ключевое слово this. Оно указывает на текущий экземпляр объекта.
class ShadowDemo
{
int value = 10;
void printValue()
{
System.out.println(value); // 10 — поле класса
int value = 5;
System.out.println(value); // 5 — локальная переменная
System.out.println(this.value); // 10 — поле класса, доступ через 'this'
}
}
4. Практика: модификаторы доступа и область видимости
Давайте продолжим развивать учебное приложение: простая система учёта студентов. Сделаем поля и методы с разными модификаторами доступа.
Пример: класс Student
public class Student
{
public String name; // имя студента (видно всем)
private int age; // возраст (видно только внутри класса)
public Student(String name, int age)
{
this.name = name;
this.age = age;
}
public void sayHello()
{
System.out.println("Привет, меня зовут " + name);
}
private void printSecret()
{
System.out.println("Мой возраст: " + age);
}
public void revealSecret()
{
printSecret(); // можно вызвать приватный метод внутри класса
}
}
Использование класса Student
public class Main
{
public static void main(String[] args)
{
Student s = new Student("Вася", 20);
s.sayHello(); // ОК: публичный метод
s.revealSecret(); // ОК: публичный метод, который вызывает приватный внутри
s.age = 30; // Ошибка! Поле age приватное
s.printSecret(); // Ошибка! Метод printSecret приватный
}
}
5. Типичные ошибки при работе с модификаторами доступа и областью видимости
Ошибка №1: Всё public — и здравствуй, хаос!
Если сделать все поля и методы публичными, вы рискуете потерять контроль над тем, кто и как меняет ваши данные. Делайте поля private и открывайте наружу только необходимое.
Ошибка №2: Попытка использовать переменную вне её области видимости.
Например, объявили переменную внутри метода, а потом пытаетесь обратиться к ней снаружи — получите ошибку компиляции. Локальные переменные живут только в своём блоке { ... }.
Ошибка №3: Конфликт имён (затенение переменных).
Если в методе объявить переменную с тем же именем, что и поле класса, легко запутаться, какую переменную вы используете. Используйте this, чтобы явно указывать на поле класса: this.value.
Ошибка №4: Попытка обратиться к private-методу или полю из другого класса.
Если метод или поле помечены как private, они доступны только внутри того же класса. Попытка обратиться к ним снаружи вызовет ошибку компиляции.
Ошибка №5: Забыли про область видимости в цикле или блоке.
Переменная, объявленная внутри цикла или любого блока { }, живёт только внутри этого блока. Вне блока она не существует.
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ