Область видимости переменных в Java
Узнайте, где видна переменная Java: локальная, экземплярная, классовая (static) область видимости и область видимости блока.
Область видимости переменной — это часть программы, где разрешено использовать её имя. Имя, объявленное внутри одного метода, не видно из другого; имя, объявленное внутри цикла for, недоступно после цикла. Компилятор строго соблюдает эти правила — если попытаться обратиться к имени вне его области видимости, код не скомпилируется.
В Java есть три основных вида переменных, каждый со своей областью видимости: локальные переменные (включая параметры), поля экземпляра и поля класса. В этой главе основное внимание уделяется локальной области видимости, поскольку именно она формирует структуру написанных вами методов.
Локальные переменные
Локальная переменная объявляется внутри метода или блока и видна только с момента объявления до конца охватывающего блока:
public static int demo() {
int x = 1; // x is in scope from here...
System.out.println(x);
return x;
} // ...to here
// System.out.println(x); // ERROR: x not visible outside demo()Локальные переменные Java не инициализирует — вы должны присвоить значение перед первым чтением. Компилятор отслеживает определённое присваивание и отказывается компилировать чтение переменной, которая могла не быть инициализирована:
public static void unassigned() {
int n;
// System.out.println(n); // ERROR: variable n might not have been initialized
n = 5;
System.out.println(n); // OK now
}Это отличается от полей экземпляра и полей класса, которые Java инициализирует по умолчанию (числа — значением 0, boolean — false, ссылки на объекты — null).
Область видимости блока
Фигурные скобки определяют блок. Переменные, объявленные внутри блока, видны только внутри этого блока, включая вложенные блоки, но не снаружи:
public static void blocks() {
int outer = 10;
if (outer > 0) {
int inner = 20;
System.out.println(outer + inner); // both visible
}
// System.out.println(inner); // ERROR: inner is out of scope here
}Это применяется к if, for, while, do-while, switch и к отдельным блокам { ... }, которые вы пишете для группировки. Переменная цикла for находится в области видимости только внутри цикла:
for (int i = 0; i < 3; i++) {
System.out.println(i);
}
// System.out.println(i); // ERROR: i is out of scopeЕсли индекс нужен после цикла, объявите i снаружи:
int i = 0;
for (; i < 3; i++) {
System.out.println(i);
}
System.out.println("stopped at " + i);Параметры методов
Параметры — это локальные переменные, которые инициализируются вызывающей стороной. Их область видимости — всё тело метода, от открывающей фигурной скобки до закрывающей:
public static int square(int n) { // n is in scope from here...
return n * n;
} // ...to hereКаждый вызов получает собственный набор слотов для параметров, поэтому рекурсивные вызовы не мешают друг другу.
Перекрытие имён
Java запрещает объявлять локальную переменную с тем же именем, что уже находится в области видимости в том же блоке или в охватывающем блоке того же метода:
public static void clash() {
int x = 1;
// int x = 2; // ERROR: variable x is already defined
if (true) {
// int x = 3; // ERROR: x is in scope from the outer block
}
}Это строже, чем допускают многие языки — Java считает повторное использование имени внутри метода ошибкой. (Поля экземпляра и класса могут перекрываться локальными переменными с тем же именем; мы рассмотрим это в разделе об ООП.)
Обратите внимание: два соседних блока могут использовать одно и то же имя, поскольку их области видимости не пересекаются — ни одна не видна внутри другой:
public static void siblings() {
for (int i = 0; i < 3; i++) { /* first i */ }
for (int i = 0; i < 3; i++) { /* a fresh i — legal, the first is gone */ }
}Время жизни и область видимости
Область видимости — это место, где можно использовать имя. Время жизни — это то, как долго существует фактическое хранилище. Обычно они совпадают — когда локальная переменная выходит из области видимости, её слот исчезает — но для объектов это различие важно:
public static String makeName() {
String s = new String("Ada"); // s in scope here
return s;
} // s out of scope, but the String object lives onПеременная s исчезает, но объект String, на который она ссылалась, продолжает жить — вызывающая сторона держит ссылку, поэтому сборщик мусора не освободит его. Локальные переменные ограничивают имена, но не время жизни объектов.
Краткий обзор полей экземпляра и класса
Внутри класса, наряду с методами, можно объявлять поля — переменные, область видимости которых распространяется на весь класс. Существует два вида:
- Поля экземпляра принадлежат каждому объекту, созданному из класса. У каждого
Dogесть своёnameиage. - Поля класса (объявленные
static) принадлежат самому классу; существует одна общая копия.
public class Counter {
private static int totalCreated = 0; // class field — one shared
private int value = 0; // instance field — one per Counter
public Counter() {
totalCreated++;
}
public void inc() {
value++; // refers to this instance's field
}
}Подробнее мы не будем здесь углубляться — Классы и объекты знакомит с самим классом, а Атрибуты класса подробно рассматривает поля экземпляра. Коротко: поля видны везде в классе; локальные переменные — только в том блоке, где они объявлены.
Рабочий пример
Что дальше
Область видимости определяет, где живёт имя. Следующий вопрос — после того как вы передали аргумент в метод — это что именно метод получает. Глава передача по значению разъясняет самое распространённое заблуждение в Java: что на самом деле происходит при передаче ссылки на объект.