Область определения и время существования переменных

До сих пор все использованные в примерах переменные были объявлены в начале метода main (). Однако Java допускает объявление переменных внутри любого блока. Как было сказано в главе 2, блок начинается открывающей фигурной скобкой и завершается закрывающей фигурной скобкой. Блок задает область определения. Таким образом, при открытии каждого нового блока мы создаем новую область определения. Область определения задает то, какие объекты видимы другим частям программы. Она определяет также время существования этих объектов.

Многие другие языки программирования различают две основных категории областей определения: глобальную и локальную. Однако эти традиционные области определения не очень хорошо вписываются в строгую объектно-ориентированную модель Java. Хотя глобальную область определения и можно задать, в настоящее время такой подход является скорее исключением, нежели правилом. В Java две основных области определения — это определяемая классом и определяемая методом. Но даже это разделение несколько искусственно. Однако поскольку область определения класса обладает несколькими уникальными свойствами и атрибутами, не применимыми к области определения метода, такое разделение имеет определенный смысл. В связи отличиями мы отложим рассмотрение область определения класса (и объявленных внутри нее переменных) до главы 6, в которой описаны классы. А пока рассмотрим только те области определения, которые определяются методом или внутри него.

Определенная методом область определения начинается с его открывающей фигурной скобки. Однако если данный метод обладает параметрами, они также включаются в область определения метода. Хота подробнее параметры рассмотрены в главе 6, пока отметим, что они работают точно так же, как любая другая переменная метода.

Основное правило, которое следует запомнить: переменные, объявленные внутри области определения, не видны (т.е. недоступны) коду, который находится за пределами этой области. Таким образом, объявление переменной внутри области определения ведет к ее локализации и защите от несанкционированного доступа и/или изменений. Действительно, правила обработки области определения — основа инкапсуляции.

Области определения могут быть вложенными. Например, при каждом создании блока кода мы создаем новую, вложенную область определения. В этих случаях внешняя область определения заключает в себя внутреннюю область. Это означает, что объекты, объявленные во внешней области, будут видны коду, определенному во внутренней области. Тем не менее, обратное не верно. Объекты, которые объявлены во внутренней области определения, не будут видны за ее пределами.

Чтобы понять эффект применения вложенных областей определения рассмотрим следующую программу:

// Демонстрация области определения блока.
class Scope {
public static void main(String args[]) {
int x; // эта переменная известка всему коду внутри метода main
х = 10;
if(x==10) { // начало новой области определения
int у = 20; // известной только этому блоку
// и х, и у известны в этой области определения.
System.out.println ("х и у: " + х + " " + у) ;
х = у * 2;
}
// у = 100;
// Ошибка! у не известна в этой области определения
// переменная х известна и здесь. System.out.println("х равна " + х) ;
}
}

Как видно из комментариев, переменная х объявлена в начале области определения метода main () и доступна всему последующему коду, находящемуся внутри этого метода. Объявление переменной у осуществляется внутри блока if. Поскольку блок задает область определения, переменная у видна только коду внутри этого блока. Именно поэтому строка у=100;, расположенная вне этого блока, помещена в комментарий. Если удалить символ комментария, это приведет к ошибке времени компиляции, поскольку переменная у не видна за пределами своего блока. Переменную х можно использовать внутри блока if, поскольку код внутри блока (т.е. во вложенной области определения) имеет доступ к переменным, которые объявлены внешней областью.

Внутри блока переменные можно объявлять в любом месте, но они становятся допустимыми только после объявления. Таким образом, если переменная объявлена в начале метода, она доступна всему коду внутри этого метода. И наоборот, если переменная объявлена в конце блока, она, по сути, бесполезна, поскольку никакой код не получит к ней доступ. Например, следующий фрагмент кода неправилен, поскольку переменную count нельзя использовать до ее объявления:

// Этот фрагмент неправилен!
count = 100;
// Стоп! Переменную count нельзя использовать до того,
// как она будет объявлена!
int count;

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

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

// Демонстрация времени существования переменной. class LifeTime {
public static void main(String args[]) {
int x;
for(x = 0; x < 3; x++) {
int у = -1; // у инициализируется при каждом вхождении в блок
System.out.println("y равна: " + у); // эта строка всегда выводит
// значение -1
у = 100;
System.out.println ("у теперь равна: " + у);
}
}
}

Эта программа генерирует следующий вывод:

у равна: -1
у теперь равна: 100
у равна: -1
у теперь равна: 100
у равна: -1
у теперь равна: 100

Как видите, переменная у повторно инициализируется значением -1 при каждом вхождении во внутренний цикл for. Несмотря на то что впоследствии переменной присваивается значение 100, это значение теряется.

И последнее: хотя блоки могут быть вложенными, во внутреннем блоке нельзя объявлять переменные с тем же именем, что и во внешней области. Например, следующая программа ошибочна:

// Компиляция этой программы будет невозможна
class ScopeErr {
public static void main(String args[]) {
int bar = 1;
{ // создание новой области определения
int bar = 2;
//Ошибка времени компиляции — переменная bar уже определена!
}
}




Rambler's Top100