W3docs

Область видимости переменных в 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, booleanfalse, ссылки на объекты — 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— editable, runs on the server

Что дальше

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

Практика

Практика
Где видна переменная, объявленная внутри блока if?
Где видна переменная, объявленная внутри блока if?
Was this page helpful?