Перейти на главную   
  helloworld.ru - документация и книги по программированию  
helloworld.ru - документация и книги по программированию
    главная     хостинг    
Поиск по сайту:  
Смотрите также
Языки программирования
C#
MS Visual C++
Borland C++
C++ Builder
Visual Basic
Quick Basic
Turbo Pascal
Delphi
JavaScript
Java
PHP
Perl
Assembler
AutoLisp
Fortran
Python
1C

Интернет-технологии
HTML
VRML
HTTP
CGI
FTP
Proxy
DNS
протоколы TCP/IP
Apache

Web-дизайн
HTML
Дизайн
VRML
PhotoShop
Cookie
CGI
SSI
CSS
ASP
PHP
Perl

Программирование игр
DirectDraw
DirectSound
Direct3D
OpenGL
3D-графика
Графика под DOS

Алгоритмы
Численные методы
Обработка данных

Сис. программирование
Драйверы

Базы данных
MySQL
SQL

Другое

Хостинг


Друзья
demaker.ru
Реклама

Лучший хостинг. Аренда серверов




helloworld.ru

ГЛАВА 14. Блоки и операторы

Последовательность выполнения программы в языке Ява управляется операторами, которые выполняются для разных эффектов и не имеют значений.

Некоторые операторы содержат в себе другие операторы, как часть своей структуры; эти операторы называются подоператорами. Будем говорить, что оператор S непосредственно содержит оператор U, если не имеется такого оператора T, отличного от S и U, что S содержит T, и T содержит U. Таким же самым способом, некоторые операторы содержат выражения (§ 15), как часть своей структуры.

В первом разделе этой главы обсуждается различие между нормальным и преждевременным завершением операторов (§ 14.1). Большинство остающихся разделов объясняет различные виды операторов, описывая подробно как их нормальное поведение так и любую специальную обработку преждевременного завершения.

Блоки описываются первыми (§ 14.2), потому что они могут появляться в тех местах, где другие виды операторов не разрешаются, и потому что один вид оператора, оператор описания локальной переменной (§ 14.3), должен непосредственно содержаться в пределах блока.

Следующий грамматический маневр объясняется необходимостью обойти знакомую проблему “обособленного else (§ 14.4).

Операторы, которые будут знакомы программирующим на языке C и C++, это пустой (§ 14.5), помеченный (§ 14.6), выражение (§ 14.7), if (§14.8), switch (§14.9), while (§14.10), do (§14.11), for (§14.12), break (§14.13), continue (§14.14) и return (§14.15).

В отличие от Cи и Cи++, язык Ява не имеет оператора goto. Однако, операторы break и continue расширены в языке Ява до того, чтобы им разрешено упоминать метки операторов.

Операторы языка Ява, которых нет в языке C, - throw (§14.16), synchronized (§14.17) и try (§14.18).

Последний раздел (§ 14.19) этой главы требует, чтобы каждый оператор был достижим в некотором техническом смысле.

14.1 Нормальное и преждевременное завершение операторов

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

  • операторы break (§14.13), continue (§14.14) и return (§14.15) приводят к передаче управления, которое может предотвращать нормальное завершение операторов, которые их содержат.
  • Вычисление некоторого выражения языка Ява может сгенерировать исключительную ситуацию виртуальной машины языка Ява; эти выражения получаются в §15.5. Явный оператор throw (§ 14.16) также заканчивается исключением. Исключение приводит к передаче управления, которое может прерывать нормальное завершение операторов.

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

  • break без метки
  • break с данной меткой
  • continue без метки
  • continue с данной меткой
  • return без значения
  • return с данным значением
  • throw с данным значением, включая исключения, сгенерированные виртуальной машиной языка Ява

Термины " завершен нормально "и" завершен преждевременно " также представляют собой оценку выражений (§ 15.5). Единственная причина, когда выражение может быть завершенным преждевременно, - это сгенерированное исключение из-за throw с данным значением (§ 14.16) или исключение времени выполнения, или ошибка (§11, §15.5).

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

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

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

14.2 Блоки

Блок - это последовательность операторов и операторов описания локальной переменной в пределах фигурных скобок.

    Block:
    
    	{ BlockStatementsopt }
    
    BlockStatements:
    
    	BlockStatement
    
    	BlockStatements BlockStatement
    
    BlockStatement:
    
    	LocalVariableDeclarationStatement
    
    	Statement
    

Блок выполняется, если выполняются все операторы описания локальной переменной и другие операторы по порядку от первого до последнего (слева направо). Если все эти операторы нормально завершаются, то блок нормально завершается. Если любой из операторов блока преждевременно завершается по любой причине, то блок преждевременно завершается по той же самой причине.

14.3 Операторы описания локальной переменной

Операторы описания локальной переменной, представляют собой одно или более имен локальных переменных.

    LocalVariableDeclarationStatement:
    
    	LocalVariableDeclaration ;
    
    LocalVariableDeclaration:
    
    	Type VariableDeclarators
    

Далее идет повторение из §8.3 для того, чтобы сделать представление более понятным:

    VariableDeclarators:
    
    	VariableDeclarator
    
    	VariableDeclarators , VariableDeclarator
    
    VariableDeclarator:
    
    	VariableDeclaratorId
    
    	VariableDeclaratorId = VariableInitializer
    
    VariableDeclaratorId:
    
    	Identifier
    
    	VariableDeclaratorId [ ]
    
    VariableInitializer:
    
    	Expression
    
    	ArrayInitializer
    

Каждый оператор описания локальной переменной, содержится непосредственно в блоке. Операторы описания локальной переменной могут быть свободно перемешаны с другими видами операторов в блоке.

Описание локальной переменной может также появляться в заголовке оператора for (§ 14.12). В этом случае он выполняется тем же самым способом, как будто бы это была часть оператора описания локальной переменной.

14.3.1 Типы и описатели локальной переменной

Каждый описатель локальной переменной объявляет одну локальную переменную, имя которой является Идентификатором, появляющееся в описателе.

Тип переменной обозначается как Тип, появляющийся в начале описания локальной переменной, сопровождается любым количеством скобок, которые следуют за Идентификатором в описателе Таким образом, описание локальной переменной:

 

int a, b [], c [] [];

является эквивалентным ряду описаний:

 

int a;

int [] b;

int [] [] c;

Скобки разрешаются в описателях как дань традиции языков Cи и Cи ++. Общее правило, однако, также означает, что описание локальной переменной:

 

float[][] f[][], g[][][], h[];

является эквивалентным ряду описаний:

 

float[][][][] f;

float[][][][][] g;

float[][][] h;

Мы не рекомендуем такую "смешанную запись" для описаний множеств.

14.3.2 Область действия описаний локальной переменной

Область действия локальной переменной, объявленной в блоке, - остальная часть блока, включающая инициализатор этой переменной. Имя параметра локальной переменной может повторно не объявляться как локальная переменная или параметр исключения в пределах своей области действия, или же во время компиляции произойдет ошибка; то есть сокрытие имени локальной переменной не разрешается.

Локальная переменная не может повторно упоминаться, используясь квалифицированным именем (§ 6.6), только простым именем.

Пример:


class Test {
	static int x;
	public static void main(String[] args) {
		int x = x;
	}
}

приводит к ошибке времени компиляции, потому что инициализация x - в пределах области действия x происходит как с локальной переменной, а локальная x еще не имеет значения и не может использоваться.

Следующая программа компилируется:


class Test {
	static int x;
	public static void main(String[] args) {
		int x = (x=2)*2;
		System.out.println(x);
	}
}

потому что локальная переменная x определена (§ 16) прежде, чем использована. Печатается:

4

Имеется другой пример:


class Test {
	public static void main(String[] args) {
		System.out.print("2+1=");
		int two = 2, three = two + 1;
		System.out.println(three);
	}
}

который правильно компилируется и выводит:

2+1=3

Инициализатор для three может правильно обратиться к переменной two, объявленной ранее в описателе, и вызов метода на следующей линии может правильно обратиться к переменной three, объявленный ранее в блоке.

Область действия локальной переменной, объявленной в операторе for есть остальная часть оператора for, включающая собственный инициализатор.

Если описание идентификатора локальной переменной появляется в области действия параметра или локальной переменной с тем же самым именем, во время компиляции происходит ошибка. Таким образом, следующий пример не компилируется:


class Test {
	public static void main(String[] args) {
		int i;
		for (int i = 0; i < 10; i++)
			System.out.println(i);
	}
}

Это ограничение помогает обнаруживать ошибки, поиск которых в другом случае был бы непрост. (Подобное ограничение на сокрытие членов класса локальными переменными оценено как непрактичное, потому что добавление члена в суперкласс могло бы привести к необходимости переименовывать локальные переменные подклассов.)

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


class Test {
	public static void main(String[] args) {
		for (int i = 0; i < 10; i++)
			System.out.print(i + " ");
		for (int i = 10; i > 0; i--)
			System.out.print(i + " ");
		System.out.println();
	}
}

компилируется без ошибки, выполняется и выводит:

0 1 2 3 4 5 6 7 8 9 10 9 8 7 6 5 4 3 2 1

14.3.3 Сокрытие имен локальными переменными

Если имя, объявленное как локальная переменная уже объявлено как поле или имя типа, тогда внешнее объявление скрывается областью видимости локальной переменной. Поле или имя типа по-прежнему почти всегда могут быть доступными (§6.8), употребляя нужное имя ограниченного использования. Например, ключевое слово this может использоваться для доступа к скрытому полю x, применяя форму this.x. На самом деле, эта форма записи типична для конструкторов (§8.6):


class Pair {
	Object first, second;
	public Pair(Object first, Object second) {
		this.first = first;
		this.second = second;
	}
}

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

14.3.4 Выполнение объявлений локальных переменных

Оператор объявления локальной переменной - выполнимый оператор. Он выполняется каждый раз, операторы объявления обрабатываются в порядке слева направо. Если объявление имеет выражение инициализации, то выражение вычисляется и его значение присваивается переменной. Если объявление не имеет выражения инициализации, тогда Ява-компилятор должен показать, используя точный алгоритм, данный в §16, что каждой ссылке на переменную обязательно предшествует присваивание переменной. Если дело обстоит иначе, тогда происходит ошибка времени компиляции.

Каждая инициализация (кроме первой) выполняется только в том случае, если вычисление предшествующего выражения инициализации завершается обычным образом. Выполнение объявления локальной переменной завершается обычным образом, если вычисление последнего выражения инициализации завершается обычным образом; если объявление локальной переменной не содержит выражений инициализации, то выполнение его завершается обычным образом.

14.4 Операторы

В языке Ява существует множество видов операторов. Наблюдается наибольшее сходство с операторами языков Cи и Cи ++, но существуют некоторые операторы, известные только языку Ява.

Как в Cи и Cи ++, в языке Ява оператор if испытывает так называемую "обособленную else проблему, " проиллюстрированную этим нерационально-форматированным примером:


if (door.isOpen())
	if (resident.isVisible())
		resident.greet("Hello!");
else door.bell.ring();								// "обособленное else"

Проблема состоит в том, что и внешний оператор if и внутренний оператор if могли бы, очевидно, иметь свое предложение else. В этом примере, каждый мог бы предположить, что программист намеревался отнести предложение else ко внешнему оператору if. Язык Ява , подобно Cи и Cи++ и многим языкам, созданным прежде, условно устанавливает, что предложение else относится к самому внутреннему оператору if, которому оно могло бы принадлежать. Это правило зафиксировано в следующей грамматике:

    Statement:
    
    	StatementWithoutTrailingSubstatement
    
    	LabeledStatement
    
    	IfThenStatement
    
    	IfThenElseStatement
    
    	WhileStatement
    
    	ForStatement
    
    StatementNoShortIf:
    
    	StatementWithoutTrailingSubstatement
    
    	LabeledStatementNoShortIf
    
    	IfThenElseStatementNoShortIf
    
    	WhileStatementNoShortIf
    
    	ForStatementNoShortIf
    
    StatementWithoutTrailingSubstatement:
    
    	Block
    
    	EmptyStatement
    
    	ExpressionStatement
    
    	SwitchStatement
    
    	DoStatement
    
    	BreakStatement
    
    	ContinueStatement
    
    	ReturnStatement
    
    	SynchronizedStatement
    
    	ThrowStatement
    
    	TryStatement
    

Следующее повторяется из §14.8 для того, чтобы написанное выше стало более понятным:

    IfThenStatement:
    
    	if ( Expression ) Statement
    
    IfThenElseStatement:
    
    	if ( Expression ) StatementNoShortIf else Statement
    
    IfThenElseStatementNoShortIf:
    
    	if ( Expression ) StatementNoShortIf else StatementNoShortIf
    

Таким образом, операторы грамматически делятся на две категории: те, которые могли бы заканчиваться оператором if, который не имеет предложение else (" короткий оператор if ") и те, у которых предложение else присутствует. Только те операторы, которые определенно не заканчиваются коротким оператором if, могут применяться как непосредственный подоператор перед ключевым словом else оператора if, который выполняет имеющееся предложение else. Это простое правило предотвращает "обособленную else" проблему. Характер выполнения оператора с " не коротким if" ограничением идентичен поведению выполнения того же самого вида оператора без "не короткого if " ограничения; различие показано для того, чтобы полностью разрешить синтаксическое затруднение.

14.5 Пустой оператор

Пустой оператор не делает ничего.

EmptyStatement:

;

Выполнение пустого оператора всегда завершается нормально.

14.6 Помеченные операторы

Операторы могут иметь префикс label(метка).

    LabeledStatement:
    
    	Identifier : Statement
    
    LabeledStatementNoShortIf:
    
    	Identifier : StatementNoShortIf
    

Identifier объявляется для того, чтобы стать меткой содержащегося Statement.

В отличие от Cи и Cи++, язык Ява не имеет оператора goto ; метки оператора используются оператором break (§14.13) или оператором continue (§14.14) , появляющимися в пределах помеченного оператора.

Оператор, помеченный идентификатором не должен появляться в пределах другого оператора, помеченного тем же самым идентификатором, иначе произойдет ошибка времени компиляции. Два оператора могут быть помечены одним и тем же идентификатором, только если ни один из операторов не содержит другого.

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

идентификатора в качестве локальной переменной или в качестве параметра обработчика исключительной ситуации (§14.18) не скрывает метку оператора с тем же самым именем.

Помеченный оператор исполняется, немедленно выполняя содержащийся Statement. Если оператор помечен Identifier, и содержащийся Statement завершается преждевременно из-за оператора break с тем же самым Identifier, тогда помеченный оператор завершается обычным образом. Во всех других случаях преждевременного завершения оператора, помеченный оператор завершается преждевременно по той же самой причине.

14.7 Операторы-выражения

Некоторые виды выражений могут использоваться как операторы, сопровождаемые точкой с запятой:

    ExpressionStatement:
    
    	StatementExpression ;
    
    StatementExpression:
    
    	Assignment
    
    	PreIncrementExpression
    
    	PreDecrementExpression
    
    	PostIncrementExpression
    
    	PostDecrementExpression
    
    	MethodInvocation
    
    	ClassInstanceCreationExpression
    

Оператор-выражение начинается с вычисления выражения; если выражение имеет значение, то значение не сохраняется. Выполнение оператор-выражения завешается обычным образом тогда и только тогда, когда вычисление выражения завершается обычным образом.

В отличие от Cи и Cи++, язык Ява допускает только некоторые формы выражений, которые будут использованы как операторы-выражение. Заметим, что язык Ява не допускает "приведение к void"- void -это не тип языка Ява, эта традиционная особенность языка Cи написания оператор-выражения, такого как:

(void) ... ;	             // Это идиоматическое выражение применимо к Cи, но не к Ява!

не действует в языке Ява. С другой стороны, Ява позволяет все наиболее полезные виды выражений в операторах-выражениях и не требует вызова метода, используемого как оператор-выражение для того, чтобы вызвать void метод, так что такой прием практически никогда не нужен. Если же он необходим, то либо оператор присваивания (§15.25), либо оператор объявления локальной переменной (§14.3) могут быть использованы вместо него.

14.8 Оператор if

Оператор if позволяет условное выполнение оператора или условный выбор двух операторов, выполняя один или другой, но не оба сразу.

    IfThenStatement:
    
    	if ( Expression ) Statement
    
    IfThenElseStatement:
    
    	if ( Expression ) StatementNoShortIf else Statement
    
    IfThenElseStatementNoShortIf:
    
    	if ( Expression ) StatementNoShortIf else StatementNoShortIf
    

Выражение должно иметь тип boolean, иначе происходит ошибка времени компиляции.

14.8.1 Оператор if-then

Оператор if-then начинается с вычисления Expression. Если вычисление Expression по некоторым причинам завершается преждевременно, то оператор if-then завершается преждевременно по той же самой причине. Иначе, выполнение продолжается, делая выбор, основанный на значении Expression:

  • Если значение - true, тогда выполняется содержащийся Statement; оператор if-then завершается нормально, только если выполнение Statement завершается нормально.
  • Если значение- false, то никакое дальнейшее действие не выполняется, и оператор if-then завершается нормально.

14.8.2 Оператор if-then-else

Оператор if-then-else начинается с вычисления Expression. Если вычисление Expression по некоторой причине завершается преждевременно, тогда оператор if-then-else завершается преждевременно по той же самой причине. Иначе, выполнение продолжается, делая выбор, основанный на значении Expression:

  • Если значение- true, тогда выполняется Statement, содержащийся первым (перед ключевым словом else); оператор if-then-else завершается нормально, только если выполнение такого оператора завершается нормально.
  • Если значение- false, тогда выполняется Statement, содержащийся вторым (после ключевого слова else); оператор if-then-else завершается нормально, только если выполнение такого оператора завершается нормально.

14.9 Оператор switch

Оператор switch передает управление одному из нескольких операторов в зависимости от значения выражения.

    SwitchStatement:
    
    	switch ( Expression ) SwitchBlock
    
    SwitchBlock:
    
    	{ SwitchBlockStatementGroupsopt SwitchLabelsopt }
    
    SwitchBlockStatementGroups:
    
    	SwitchBlockStatementGroup
    
    	SwitchBlockStatementGroups SwitchBlockStatementGroup
    
    SwitchBlockStatementGroup:
    
    	SwitchLabels BlockStatements
    
    SwitchLabels:
    
    	SwitchLabel
    
    	SwitchLabels SwitchLabel
    
    SwitchLabel:
    
    	case ConstantExpression :
    
    	default :
    

Тип Expression должен быть char, byte, short или int, иначе происходит ошибка времени компиляции.

Телом оператора switch должен быть блок. Любой оператор, непосредственно содержащийся в блоке, может быть помечен одной или более метками case или default. Эти метки связаны с оператором switch значениями константных выражений (§15.27) в метках case.

Все нижесказанное должно быть истинным, иначе результатом будет ошибка времени компиляции:

  • Каждое константное выражение case, связанное с оператором switch, должно быть совместимо по присваиванию с типом выражения, записанного после switch (§5.2).
  • Никакие два константные выражения, связанные с оператором switch, не могут иметь одинаковые значения.
  • Не более одной метки default может быть связано с вышеупомянутым оператором switch

В Cи и Cи++ телом оператора switch может быть оператор или операторы с метками case, которые непосредственно не содержатся в операторе switch. Рассмотрим простой цикл:

for (i = 0; i < n; ++i) foo();

где n-положительное число. Приём, показанный ниже, может использоваться в Cи или Cи++ для того, чтобы развернуть цикл, но он не действует в языке Ява:

 


int q = (n+7)/8;
switch (n%8) {
case 0:			do {		foo();				// Великая проделка Си, Том,
case 7:					foo();				// но не имеющая силы в Ява.
case 6:					foo();
case 5:					foo();
case 4:					foo();
case 3:					foo();
case 2:					foo();
case 1:					foo();
			} while (--q >= 0);
}

К счастью, этот прием, по-видимому, не широко известен и не часто используется. Кроме того, он менее необходим в настоящее время; этот вид преобразования кода должным образом применяется оптимизирующими компиляторами.

Когда выполняется оператор switch, вычисляется первое Expression. Если вычисление Expression по некоторой причине завершается преждевременно, то оператор switch завершается преждевременно по той же самой причине. Иначе, выполнение продолжается сравнением значения Expression с каждой константой case. В этом случае существует выбор:

  • Если одна из констант case равна значению выражения, тогда мы говорим, что метка case подобрана и все операторы после соответствующей метки case в switch-блоке, если таковые имеются, выполняются последовательно. Если все эти операторы завершаются нормально или, если в switch-блоке нет операторов после соответствующей метки case, тогда весь оператор switch завершается нормально.
  • Если нет совпадений case, но есть метка default, тогда все операторы после метки default в switch-блоке, если таковые имеются, выполняются последовательно. Если все эти операторы завершаются нормально или, если после метки default нет никаких операторов, тогда весь оператор switch завершается нормально.
  • Если нет совпадений case и нет метки default, тогда дальнейшие действия не выполняются, и оператор switch завершается нормально.

Если какой-нибудь оператор непосредственно содержащийся в теле Block оператора switch завершается преждевременно, то он обрабатывается следующим образом:

  • Если выполнение Statement завершается преждевременно из-за оператора break без метки, то дальнейшее действие не выполняется и оператор switch завершается нормально.
  • Если выполнение Statement завершается преждевременно по любой другой причине, то оператор switch завершается преждевременно по той же самой причине. Случай преждевременного завершения из-за оператора break с меткой обрабатывается в соответствии с общим правилом для помеченных операторов (§14.6).

Как в Cи и Cи++, в языке Ява при выполнении операторов в switch-блоке происходит "падение сквозь метки". Например, программа:


class Toomany {

	static void howMany(int k) {
		switch (k) {
		case 1:			System.out.print("one ");
		case 2:			System.out.print("too ");
		case 3:			System.out.println("many");
		}
	}


	public static void main(String[] args) {
		howMany(3);
		howMany(2);
		howMany(1);
	}

}

содержит switch-блок, в котором код для каждого случая продолжается в коде для следующего случая. В результате, программа печатает:

 


many
too many
one too many

В случае, когда “падение сквозь метки” не происходит, должен использоваться оператор break, как показано в этом примере:

 


class Twomany {

	static void howMany(int k) {
		switch (k) {
		case 1:			System.out.println("one");
					break;					// выход из switch
		case 2:			System.out.println("two");
					break;					// выход из switch
		case 3:			System.out.println("many");
					break;					// ненужный, но хороший стиль
		}
	}


	public static void main(String[] args) {
		howMany(1);
		howMany(2);
		howMany(3);
	}

}

Программа печатает следующее:


one
two
many

14.10 Оператор while

Оператор while неоднократно выполняет Expression и Statement, пока значение Expression - не false.

    WhileStatement:
    
    	while ( Expression ) Statement
    
    WhileStatementNoShortIf:
    
    	while ( Expression ) StatementNoShortIf
    

Expression должно иметь тип boolean, иначе происходит ошибка времени компиляции.

Оператор while начинается с вычисления Expression. Если вычисление Expression по некоторой причине завершается преждевременно, оператор while завершается преждевременно по той же самой причине. Иначе, выполнение продолжается, делая выбор, основанный на значении Expression:

  • Если значение- true, тогда содержащийся Statement выполняется. В этом случае имеется выбор:
    • Если выполнение Statement завершается нормально, тогда весь оператор while приводится в исполнение снова, начиная дальнейшее вычисление Expression.
    • Если выполнение Statement завершается преждевременно, смотрите ниже §14.10.1.
  • Если значение Выражения - false, то дальнейшее действие не выполняется и оператор while завершается нормально.

Если значение Expression - false, то в первый раз оно вычисляется, а затем Statement не выполняется.

14.10.1 Преждевременное завершение

Преждевременное завершение содержащегося Statement обрабатывается следующим образом:

  • Если выполнение Statement завершается преждевременно из-за оператора break без метки, то дальнейшее действие не выполняется, и оператор while завершается нормально.
  • Если выполнение Statement завершается преждевременно из-за оператора continue без метки, тогда весь оператор while выполняется снова.
  • Если выполнение Statement завершается преждевременно из-за оператора continue c меткой L, тогда имеется выбор:
  • Если оператор while имеет метку L, тогда весь оператор while выполняется снова.
  • Если оператор while не имеет метки L, тогда оператор while завершается преждевременно из-за оператора continue с меткой L.
  • Если выполнение Statement завершается преждевременно по любой другой причине, то оператор while завершается преждевременно по той же самой причине. Заметим, что случай преждевременного завершения из-за оператора break с меткой обрабатывается в соответствии с общим правилом для помеченных операторов (§14.6).

14.11 Оператор do

Оператор do неоднократно выполняет Statement и Expression, пока значение Expression - не false.

    
    DoStatement:
    
    	do Statement while ( Expression ) ;
    

Expression должно иметь тип boolean, иначе происходит ошибка времени компиляции.

Оператор do начинается с вычисления Statement. Далее существует несколько вариантов:

  • Если выполнение Statement завершается нормально, тогда Expression вычисляется. Если вычисление Expression завершается преждевременно по некоторой причине, то оператор do завершается преждевременно по той же самой причине. Иначе, существует несколько вариантов, основанных на значении Expression:
    • Если значение- true, тогда весь оператор do выполняется снова.
    • Если значение- false, тогда дальнейшее действие не выполняется, и оператор do завершается нормально.
  • Если выполнение Statement завершается преждевременно, смотрите ниже §14.11.1.

При выполнении оператора do всегда, по крайней мере один раз, выполняется содержащийся Statement.

14.11.1 Преждевременное завершение

Преждевременное завершение содержащегося Statement обрабатывается следующим образом:

  • Если выполнение Statement завершается преждевременно из-за оператора break без метки, тогда дальнейшее действие не выполняется, и оператор do завершается нормально.
  • Если выполнение Statement завершается преждевременно из-за оператора continue без метки, тогда вычисляется Statement. Далее существует выбор, основанный на значении Expression:
    • Если значение- true, тогда весь оператор do выполняется снова.
    • Если значение- false, тогда дальнейшее действие не выполняется, и оператор do завершается нормально.
  • Если выполнение Statement завершается преждевременно из-за оператора continue с меткой L, тогда существует выбор:
    • Если оператор do имеет метку L, тогда вычисляется Expression. Далее существует несколько вариантов:
      • Если значение Expression - true, тогда весь оператор do выполняется снова.
      • Если значение Expression - false, тогда никакое дальнейшее действие не выполняется, и оператор do завершается нормально.
    • Если оператор do не имеет метки L, то оператор do завершается преждевременно из-за оператора continue с меткой L.
  • Если выполнение Statement завершается преждевременно по любой другой причине, то оператор do завершается преждевременно по той же самой причине. Случай преждевременного завершения из-за оператора break с меткой обрабатывается в соответствии с общим правилом (§14.6).

14.11.2 Пример оператора do

Следующий код - первая возможная реализация метода toHexString (§20.7.14) класса Integer:


public static String toHexString(int i) {
	StringBuffer buf = new StringBuffer(8);
	do {
		buf.append(Character.forDigit(i & 0xF, 16));
		i >>>= 4;
	} while (i != 0);
	return buf.reverse().toString();
}

Так как должна быть сгенерирована по крайней мере одна цифра, оператор do - подходящая структура управления.

14.12 Оператор for

Оператор for выполняет некоторый код инициализации, затем неоднократно выполняет Expression, Statement и некоторый код обновления, пока значение Expression - не false.

    ForStatement:
    
    	for ( ForInitopt ; Expressionopt ; ForUpdateopt )
    
    		Statement
    
    ForStatementNoShortIf:
    
    	for ( ForInitopt ; Expressionopt ; ForUpdateopt )
    
    		StatementNoShortIf
    
    ForInit:
    
    	StatementExpressionList
    
    	LocalVariableDeclaration
    
    ForUpdate:
    
    	StatementExpressionList
    
    StatementExpressionList:
    
    	StatementExpression
    
    	StatementExpressionList , StatementExpression
    

Expression должно иметь тип boolean, иначе происходит ошибка времени компиляции.

14.12.1 Инициализация оператора for

Оператор for начинается с выполнения кода ForInit:

  • Если код ForInit - это список операторов-выражений (§14.7), то выражения вычисляются в последовательности слева направо; их значения, если таковые имеются, отбрасываются. Если вычисление какого-нибудь выражения по некоторой причине завершается преждевременно, то оператор for завершается преждевременно по той же самой причине; любые ForInit оператор-выражения справа от того, который завершен преждевременно, не вычисляются.
  • Если код ForInit - объявление локальной переменной, то он выполняется так, как если бы он был оператором объявления локальной переменной (§14.3) , появившейся в блоке. В этом случае, область видимости объявленной локальной переменной - это ее собственный инициализатор и любые дальнейшие объявления в ForInit части, плюс Expression, ForUpdate и Statement, содержащийся в операторе for. Если выполнение объявления локальной переменной по какой-то причине завершается преждевременно, то оператор for завершается преждевременно по той же самой причине.
  • Если часть ForInit отсутствует, то никакое действие не выполняется.

14.12.2 Итерация оператора for

Далее, итеративный шаг for выполняется следующим образом:

  • Если Expression присутствует, то оно вычисляется и, если вычисление Expression завершается преждевременно, оператор for завершается преждевременно по той же самой причине. Иначе, имеется выбор, основанный на присутствии или отсутствии Expression и на значении Expression, если Expression присутствует:
    • Если Expression отсутствует, или присутствует и значение при его вычислении- true, тогда содержащийся Statement выполняется. Далее существует выбор:
      • Если выполнение Statement завершается нормально, тогда следующие два шага выполняются в последовательности:
        • Во-первых, если ForUpdate часть присутствует, выражения вычисляются слева направо; их значения, если не нужны, отбрасываются. Если вычисление какого-нибудь выражения завершается преждевременно по некоторой причине, то оператор for завершается преждевременно по той же самой причине; любые ForUpdate оператор-выражения справа от того, который завершен преждевременно, не вычисляются. Если ForUpdate часть отсутствует, то никакое действие не выполняется.
        • Во-вторых, выполняется другой итеративный шаг for.
      • Если выполнение Statement завершается преждевременно, то смотрите ниже §14.12.3.
    • Если Expression присутствует и значение при его вычислении - false, то никакое действие не выполняется и оператор for завершается нормально.

Если значение Expression - false, то сначала оно вычисляется, а далее Statement не выполняется.

Если Expression отсутствует, тогда оператор for может завершаться нормально единственным путем, т.е. при помощи оператора break.

14.12.3 Преждевременное завершение оператора for

Преждевременное завершение содержащегося Statement обрабатывается следующим образом:

  • Если выполнение Statement завершается преждевременно из-за оператора break без метки, тогда дальнейшее действие не выполняется и оператор for завершается нормально.
  • Если выполнение Statement завершается преждевременно из-за оператора continue без метки, тогда следующие два шага выполняются в последовательности:
    • Во-первых, если присутствует часть ForUpdate, то выражения вычисляются в последовательности слева направо; их значения, если не нужны, отбрасываются. Если же часть ForUpdate отсутствует, то никакое действие не выполняется.
    • Во-вторых, выполняется другой итеративный шаг for.
  • Если выполнение Statement завершается преждевременно из-за оператора continue с меткой L, тогда существует выбор:
    • Если оператор for имеет метку L, тогда следующие два шага выполняются в последовательности:
      • Во-первых, если присутствует часть ForUpdate, то выражения вычисляются слева направо; их значения, если не нужны, отбрасываются. Если ForUpdate часть отсутствует, то никакое действие не выполняется.
      • Во-вторых, выполняется другой итеративный шаг for.
    • Если оператор for не имеет метки L, то оператор for завершается преждевременно из-за оператора continue с меткой L.
  • Если выполнение Statement завершается преждевременно по любой другой причине, то оператор for завершается преждевременно по той же самой причине. Отметим, что случай преждевременного завершения из-за оператора break с меткой обрабатывается в соответствии с общим правилом для помеченных операторов (§14.6).

14.13 Оператор break

Оператор break передает управление наружу оператора, внутри которого он находится.

    BreakStatement:
    
    	break Identifieropt ;
    

Оператор break без метки пытается передать управление внутренним операторам switch, while, do или for, внутри которых он находится, этот оператор, который называется цель прерывания, затем немедленно нормально завершается . Чтобы быть точным, оператор break без метки всегда завершается преждевременно, причина в операторе break без метки. Если оператор break содержится не в операторе switch, while, do или for, то происходит ошибка времени компиляции.

Оператор break с меткой Identifier пытается передать управление помеченному оператору, внутри которого он находится (§14.6), который содержит тот же самый Identifier в качестве метки; этот оператор, который называется цель прерывания, затем немедленно завершается обычным образом. В этом случае, цель прерывания не должна быть while, do, for или switch оператором. Чтобы быть точным, оператор break с меткой Identifier всегда завершается преждевременно, причина в операторе break с меткой Identifier. Если оператор break содержится вне оператора с меткой Identifier, то происходит ошибка времени компиляции.

Можно заметить, что оператор break всегда завершается преждевременно.

Предшествующие описания, говорящие "пытается передать управление" более точны, чем "передает управление" потому что, если имеются некоторые операторы try (§14.18) в пределах цели прерывания, чьи try-блоки содержат оператор break, тогда любые предложения finally этих операторов try выполняются в порядке от самого внутреннего к внешнему прежде, чем управление передается цели прерывания. Преждевременное завершение предложения finally может прервать передачу управления, инициированную оператором break.

В следующем примере, математический граф представлен массивом из массивов. Граф состоит из множества вершин и множества ребер; каждое ребро является стрелкой, которая направлена от некоторой вершины до другой вершины, или от некоторой вершины до этой же вершины. В этом примере предполагается, что нет никаких лишних ребер; то есть для любых двух вершин P и Q, где Q может совпадать с P, существует максимум одно ребро от P до Q. Вершины представлены целыми числами, и есть ребро от вершины i до вершины edges[i][j] для каждого i и j, для которых ссылка на массив edges[i][j] не генерирует IndexOutOfBoundsException.

Задача метода loseEdges для данных целых чисел i и j состоит в том, чтобы построить новый граф, копируя данный, опуская ребро от точки i до точки j, и ребро от точки j до точки i, если таковые имеются:

class Graph {
	int edges[][];
	public Graph(int[][] edges) { this.edges = edges; }

	public Graph loseEdges(int i, int j) {
		int n = edges.length;
		int[][] newedges = new int[n][];
		for (int k = 0; k < n; ++k) {

			edgelist: {
				int z;

				search: {
					if (k == i) {
						for (z = 0; z < edges[k].length; ++z)
							if (edges[k][z] == j)
								break search;
					} else if (k == j) {
						for (z = 0; z < edges[k].length; ++z)
							if (edges[k][z] == i)
								break search;
					}

//Никакое ребро не

будет удалено; разделите этот

// список.

					newedges[k] = edges[k];
					break edgelist;
				}//search

// Копируют список, опуская ребро в позиции z.

				int m = edges[k].length - 1;
				int ne[] = new int[m];
				System.arraycopy(edges[k], 0, ne, 0, z);
				System.arraycopy(edges[k], z+1, ne, z, m-z);
				newedges[k] = ne;
			}//edgelist
		}
		return new Graph(newedges);
	}
}

Обратите внимание на использование двух операторов с метками edgelist и search и на использование оператора break. Это допускает код, который копирует список, опуская одно ребро, для того, чтобы быть разделенным между двумя отдельными проверками, проверка для ребра от точки i до точки j и проверка для ребра от точки j до точки i.

14.14 Оператор continue

Оператор continue может встретиться только в операторах while, do или for; операторы этих трех видов называются итерационными операторами. Управление передается к месту продолжения цикла итерационного оператора.

    ContinueStatement:
    
    	continue Identifieropt ;
    

Оператор continue без метки пытается передать управление самому внутреннему оператору while, do или for, внутри которого он находится; этот оператор, который называется цель продолжения затем немедленно заканчивает текущее повторение и начинает новое. Чтобы быть точным, такой оператор continue всегда завершается преждевременно, причина в операторе continue без метки. Если оператор continue содержится не в операторе while, do или for, то происходит ошибка времени компиляции.

Оператор continue с меткой Identifier пытается передать управление помеченному оператору, внутри которого он находится (§14.6), который содержит тот же самый Identifier в качестве метки; этот оператор, который называется цель продолжения, затем немедленно заканчивает текущее повторение и начинает новое. Цель продолжения должна быть оператором while, do или for, или же происходит ошибка времени компиляции. Чтобы быть более точным, оператор continue с меткой Identifier всегда завершается преждевременно, причина в самом операторе continue с меткой Identifier. Если оператор continue содержится вне оператора с меткой Identifier, то происходит ошибка времени компиляции.

Можно заметить, что оператор continue всегда завершается преждевременно.

Смотрите описания оператора while (§14.10), оператора do (§14.11) и оператора for (§14.12) для обсуждения обработки преждевременного завершения из-за оператора continue.

Предшествующие описания, говорящие " пытается передать управление" более точны, чем

"передает управление" потому что, если имеются некоторые операторы try (§14.18) в пределах цели продолжения, чьи try-блоки содержат оператор continue, тогда любые предложения finally этих операторов try выполняются в порядке от самого внутреннего к внешнему, прежде чем управление передается цели продолжения. Преждевременное завершение предложения finally может прервать передачу управления, начатого оператором continue.

В примере Graph в предшествующем разделе, один из операторов break используется для того, чтобы закончить выполнение всего тела внешнего цикла for. Этот оператор break может быть заменен оператором continue, если сам цикл for помечен:


class Graph {
	. . .
	public Graph loseEdges(int i, int j) {
		int n = edges.length;
		int[][] newedges = new int[n][];

		edgelists: for (int k = 0; k < n; ++k) {
			int z;

			search: {
				if (k == i) {
					. . .
				} else if (k == j) {
					. . .
				}
				newedges[k] = edges[k];
				continue edgelists;
			}//search
			. . .
		}//edgelists
		return new Graph(newedges);
	}
}

Применение которых, если таковые имеются, в значительной степени является вопросом стиля программирования.

14.15 Оператор return

Оператор return возвращает управление в то место, откуда был вызван метод (§8.4,§15.11) или конструктор (§8.6, §15.8).

    ReturnStatement:
    
    	return Expressionopt ;
    

Оператор return без Expression должен содержаться в теле метода, который объявляется при использовании ключевого слова void, не для возврата какого-нибудь значения (§8.4), или же он (оператор return) должен содержаться в теле конструктора (§8.6). Ошибка времени компиляции происходит в том случае, если оператор return появляется в пределах статического инициализатора (§8.5). Оператор return без Expression пытается передать управление в то место, откуда был вызван метод или конструктор, которые содержат его. Чтобы быть точным, оператор return без Expression всегда завершается преждевременно, причина в самом операторе return без значения.

Оператор return с Expression должен содержаться в объявлении метода, который объявляется для того, чтобы возвратить значение (§8.4), или же происходит ошибка времени компиляции. Expression должно означать переменную или значение некоторого типа T, или происходит ошибка времени компиляции. Тип T должен быть совместим по присваиванию с типом объявленного результата метода (§5.2), или происходит ошибка времени компиляции.

Оператор return c Expression пытается передать управление в то место, откуда был вызван метод, который его содержит; значение Expression становится значением вызова метода. Чтобы быть более точным, выполнение такого оператора return сначала вычисляет Expression. Если вычисление Expression по некоторой причине завершается преждевременно, тогда оператор return завершается преждевременно по той же причине. Если вычисление Expression завершается нормально, порождая значение V, тогда оператор return завершается преждевременно, причина в операторе return со значением V.

Можно заметить, что оператор return всегда завершается преждевременно.

Предшествующие описания, говорящие "пытается передать управление" более точны, чем "передает управление" потому что, если имеются некоторые операторы try (§14.18) в пределах метода или конструктора, чьи try-блоки содержат оператор return, тогда любые предложения finally этих операторов try будут выполняться в порядке от самого внутреннего к внешнему прежде, чем управление передается в то место, откуда был вызван метод или конструктор. Преждевременное завершение предложения finally может прервать передачу управления, введенную оператором return.

14.16 Оператор throw

Оператор throw вызывает для генерации исключительную ситуацию (§11). Результат - немедленная передача управления (§11.3), которая может выводить составные операторы и составные конструкторы, вычисления статических инициализаторов и инициализаторов полей и вызовы метода до тех пор, пока не обнаружен оператор try, который захватывает сгенерированное значение (§14.18). Если такой оператор try не обнаружен, тогда выполнение потока (§17, §20.20), который исполнял оператор throw, завершается (§11.3) после вызова метода UncaughtException (§20.21.31) для группы потоков, к которой данный поток принадлежит.

    
    ThrowStatement:
    
    	throw Expression ;
    

Expression в операторе throw должно означать переменную или значение ссылочного типа, который совместим по присваиванию с типом Throwable(§5.2), или происходит ошибка времени компиляции. Кроме того, по крайней мере одно из следующих трёх условий должно быть истинным, иначе происходит ошибка времени компиляции:

  • Исключительная ситуация - неконтролируемая исключительная ситуация (§11.2) - точнее, одна из следующих ситуаций истинна:
  • Тип Expression - класс RuntimeException или подкласс класса RuntimeException.

    Тип Expression - класс Error или подкласс класса Error.

  • Оператор throw содержится в try-блоке оператора try(§14.18) и тип Expression совместим по присваиванию с типом параметра по крайней мере одного предложения catch оператора try(§5.2). (В этом случае мы говорим, что сгенерированное значение захватывается оператором try).
  • Оператор throw содержится в объявлении метода или конструктора и тип Expression (§5.2) совместим по присваиванию по крайней мере с одним типом, внесенным в предложение throw объявления (§8.4.4, §8.6.4).

Оператор throw сначала вычисляет Expression. Если вычисление Expression по некоторой причине завершается преждевременно, тогда оператор throw завершается преждевременно по той же причине. Если вычисление Expression завершается нормально с получением значения V, тогда оператор throw завершается преждевременно, причина в операторе throw со значением V.

Можно заметить, что оператор throw всегда завершается преждевременно.

Если имеются некоторые охватывающие операторы try (§14.18), чьи try-блоки содержат оператор throw, тогда любые предложения finally этих операторов try выполняются, поскольку управление передается наружу до тех пор, пока сгенерированное значение не захватывается. Заметим, что преждевременное завершение предложения finally может прервать передачу управления, инициированную оператором throw.

Если оператор throw содержится в объявлении метода, но его значение не захватывается некоторым оператором try, который его содержит, тогда вызов метода завершается преждевременно из-за оператора throw.

Если оператор throw содержится в объявлении конструктора, но его значение не захватывается некоторым оператором try, который его содержит, тогда выражение создания экземпляра класса (или вызов метода NewInstance класса Class) которое вызвало конструктор, завершится преждевременно из-за оператора throw.

Если оператор throw содержится в статическом инициализаторе (§8.5), тогда проверка времени компиляции гарантирует, что любое его значение - всегда неконтролируемая исключительная ситуация, или же его значение всегда обнаруживается некоторым оператором try, который его содержит. Если, несмотря на эту проверку, значение не захватывается некоторым оператором try, который содержит оператор trow, тогда значение генерируется повторно в том случае, если оно - экземпляр класса Error или одного из его подклассов; иначе, оно заключается в объект ExceptionInInitializerError, который затем генерируется (§12.4.2).

В соответствии с соглашением, объявленные пользователем генерирующие типы обычно должны объявляться как подклассы класса Exception, который в свою очередь является подклассом класса Throwable (§11.5, §20.22).

14.17 Оператор synchronized

Оператор synchronized завладевает замком (§17.13) в интересах выполняемого потока, выполняет блок, затем освобождает замок. Пока выполняемый поток обладает замком, никакой другой поток не может завладеть замком.

    SynchronizedStatement:
    
    	synchronized ( Expression ) Block
    

Тип Expression должен быть ссылочным типом, иначе происходит ошибка времени компиляции.

Оператор synchronized начинается с вычисления Expression.

Если вычисление Expression по некоторой причине завершается преждевременно, тогда оператор synchronized завершается преждевременно по той же причине.

Иначе, если значение Expression - null, тогда генерируется NullPointerException.

Иначе, пусть V - не null значение Expression. Выполняемый поток захватывает замок, связанный с V. Затем выполняется Block. Если выполнение Block завершается нормально, тогда замок освобождается, и оператор synchronized завершается обычным образом. Если выполнение Block завершается преждевременно по какой-нибудь причине, тогда замок освобождается, и затем оператор synchronized завершается преждевременно по той же причине.

Завладевание замком, связанным с объектом само не предохраняет другие потоки от обращения к полям объекта или от вызова несинхронизированных методов объекта. Другие потоки также могут использовать методы synchronized или оператор synchronized обычным способом для того, чтобы достичь взаимного исключения.

Замки, которыми завладевают операторы synchronized - такие же как замки, какими неявно завладевают методы synchronized; см. §8.4.3.5. Одиночный поток может захватывать замок более одного раза. Пример:


class Test {
	public static void main(String[] args) {
		Test t = new Test();
		synchronized(t) {
			synchronized(t) {
				System.out.println("made it!");
			}
		}
	}
}

печатает:

made it!

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

14.18 Оператор try

Оператор try выполняет блок. Если генерируется значение и оператор try имеет одно или более предложений catch, которые могут захватить это значение, то управление будет передано первому такому предложению catch. Если оператор try имеет предложение finally, тогда выполняется другой блок кода, независимо от того, завершается ли try-блок обычным образом или преждевременно, и независимо от того, первому ли предложению catch передаётся управление.

    TryStatement:
    
    	try Block Catches
    
    	try Block Catchesopt Finally
    
    Catches:
    
    	CatchClause
    
    	Catches CatchClause
    
    CatchClause:
    
    	catch ( FormalParameter ) Block
    
    Finally:
    
    	finally Block
    

Следующее повторяется из §8.4.1 для того, чтобы написанное выше стало более понятным:

    FormalParameter:
    
    	Type VariableDeclaratorId
    

Следующее повторяется из §8.3 для того, чтобы написанное выше стало более понятным:

    VariableDeclaratorId:
    
    	Identifier
    
    	VariableDeclaratorId [ ]
    

Block, стоящий после ключевого слова try, назван try-блоком оператора try. Block, стоящий после ключевого слова finally, назван finally-блоком оператора try.

Оператор try может иметь предложения catch (так называемые обработчики исключительной ситуации). Предложение catch должно иметь точно один параметр (который называется параметром исключительной ситуации); объявленный тип параметра исключительной ситуации должен быть классом Throwable или подклассом класса Throwable, или происходит ошибка времени компиляции. Область видимости параметра -переменной - Block предложения catch. Параметр исключительной ситуации не должен иметь то же самое имя что и локальная переменная или параметр, в чей области видимости он (параметр исключительной ситуации) объявляется, или происходит ошибка времени компиляции.

Область видимости имени параметра исключительной ситуации - Block предложения catch. Имя параметра не может быть объявлено повторно как локальная переменная или параметр исключительной ситуации в пределах Block предложения catch; то есть сокрытие имени параметра исключительной ситуации не допускается.

Параметры исключительной ситуации при применении не могут быть обозначены квалифицированными именами (§6.6), они обозначаются только простыми именами.

Обработчики исключительной ситуации рассматриваются в порядке слева направо: самое раннее возможное предложение catch принимает исключение, получая как фактический аргумент объект сгенерированной исключительной ситуации.

Предложение finally гарантирует, что finally-блок выполняется после try-блока и любого catch-блока, который мог бы быть выполненным, независимо от того, как управление покидает try-блок или catch-блок.

Обработка finally-блока достаточно сложна, поэтому оба случая оператора try с finally-блоком и без него описаны отдельно.

14.18.1 Выполнение try-catch

Оператор try без finally-блока начинается с выполнения try-блока. Далее существует выбор:

  • Если выполнение try-блока завершается обычным образом, тогда дальнейшие действия не выполняются, и оператор try завершается обычным образом.
  • Если выполнение try-блока завершается преждевременно из-за оператора throw со значением V, тогда существует выбор:
    • Если тип значения V во время выполнения (§5.2) совместим по присваиванию с Parameter какого-нибудь предложения catch оператора try, тогда выбирается первое (крайнее левое) такое предложение catch. Значение V присваивается параметру выбранного предложения catch, и Block этого предложения catch выполняется. Если тот блок завершается обычным образом, тогда оператор try завершается обычным образом; если же блок по какой-то причине завершается преждевременно, тогда оператор try завершается преждевременно по той же причине.
    • Если тип значения V во время выполнения не совместим по присваиванию с параметром предложения catch оператора try, тогда оператор try завершается преждевременно из-за оператора throw со значением V.
  • Если выполнение try-блока завершается преждевременно по любой другой причине, тогда оператор try завершается преждевременно по той же причине.

В примере:

class BlewIt extends Exception {
	BlewIt() { }
	BlewIt(String s) { super(s); }
}
class Test {
	static void blowUp() throws BlewIt { throw new BlewIt(); }
	public static void main(String[] args) {

		try {
			blowUp();
		} catch (RuntimeException r) {
			System.out.println("RuntimeException:" + r);
		} catch (BlewIt b) {
			System.out.println("BlewIt");
		}
	}

}

исключение BlewIt генерируется методом blowUp. Оператор try-catch в теле main имеет два предложения catch. Тип исключительной ситуации во время выполнения - BlewIt, который не совместим по присваиванию с переменной типа RuntimeException, но совместим по присваиванию с переменной типа BlewIt, поэтому результат примера:

BlewIt

14.18.2 Выполнение try-catch-finally

Оператор try с finally-блоком начинается с выполнением try-блока. Далее существует выбор:

  • Если выполнение try-блока завершается обычным образом, то выполняется finally-блок, и далее существует выбор:
  • Если выполнение try-блока завершается преждевременно из-за оператора throw со значением V, тогда имеется выбор:
    • Если тип значения V во время выполнения совместим по присваиванию с параметром какого-нибудь предложения catch оператора try, тогда выбирается первое (крайнее левое) такое предложение catch. Значение V присваивается параметру выбранного предложения catch, и Блок такого предложения catch выполняется. Далее существует выбор:
      • Если catch-блок завершается обычным образом, тогда выполняется finally-блок. Далее существует выбор:
        • Если finally-блок завершается обычным образом, тогда оператор try завершается обычным образом.
        • Если finally-блок завершается преждевременно по какой-то причине, тогда оператор try завершается преждевременно по тот же самой причине.
      • Если catch-блок завершается преждевременно по причине R, тогда выполняется finally-блок. Далее существует выбор:
        • Если finally-блок завершается обычным образом, тогда оператор try завершается преждевременно по причине R.
        • Если finally-блок завершается преждевременно по причине S, тогда оператор try завершается преждевременно по причине S (и причина R отбрасывается).
    • Если тип значения V во время выполнения не совместим по присваиванию с параметром предложения catch оператора try, тогда выполняется finally-блок. Далее существует выбор:
      • Если finally-блок завершается нормально, тогда оператор try завершается преждевременно из-за оператора throw со значением V.
      • Если finally-блок завершается преждевременно по причине S, тогда оператор try завершается преждевременно по причине S (и оператор throw со значением V отбрасывается и забывается).
  • Если выполнение try-блока завершается преждевременно по какой-нибудь другой причине R, тогда выполняется finally-блок. Далее существует выбор:
    • Если finally-блок завершается обычным образом, тогда оператор try завершается преждевременно по причине R.
    • Если finally-блок завершается преждевременно по причине S, тогда оператор try завершается преждевременно по причине S (и причина R отбрасывается).

Пример:

class BlewIt extends Exception {
	BlewIt() { }
	BlewIt(String s) { super(s); }
}

class Test {

	static void blowUp() throws BlewIt {

		throw new NullPointerException();

	}

	public static void main(String[] args) {
		try {
			blowUp();
		} catch (BlewIt b) {
			System.out.println("BlewIt");
		} finally {
			System.out.println("Uncaught Exception");
		}
	}

}

выводит на экран :


Uncaught Exception
java.lang.NullPointerException
	at Test.blowUp(Test.java:7)
	at Test.main(Test.java:11)

NullPointerException (который является видом RuntimeException), который генерируется методом blowUp, не захватывается оператором try в теле main, потому что NullPointerException не совместим по присваиванию с переменной типа BlewIt. Это заставляет предложение finally выполняться, после чего поток, выполняющий main, который является потоком только испытательной программы, заканчивается из-за не захваченной исключительной ситуации(§20.21.31), в результате чего печатается имя исключительной ситуации и небольшая трассировка.

14.19 Недостижимые операторы

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

Этот раздел посвящен точному объяснению слова "достижимый". Идея состоит в том, что каждый оператор должен иметь некоторый возможный путь выполнения от начала конструктора, метода, или статического инициализатора, который содержит оператор к следующему оператору. Анализ операторов принимает во внимание его структуру. Кроме специальной обработки операторов while, do, и for, у которых выражение условия имеет постоянное значение true, то значения этих выражений не принимаются во внимание в потоковом анализе. Например, транслятор Ява примет код:


{
	int n = 5;
	while (n > 7) n = 2;
}

Даже если значение n известно во времени компиляции, и в принципе это может быть известно во времени компиляции, что присваивание на k не может быть выполнено. Транслятор Ява должен функционировать согласно правилам, описанными в этом разделе.

Правила в этом разделе определяют два технических условия:

  • является ли оператор достижимым
  • может ли оператор завершиться нормально

Определенные здесь правила позволяют оператору завершаться нормально только, если оператор достижим.

Введем обозначение: " <=> " означает "тогда и только тогда". Это обозначение будем использовать для описания правил.

Правила следующие:

  • Блок, который является телом конструктора, метода, или статического инициализатора, достижим.
    • Пустой блок, который не является swich-блоком, может завершаться нормально <=>, когда он достижим. Непустой блок, который - не является swich-блоком, может завершаться нормально <=>, когда последний оператор в этом блоке может завершаться нормально. Первый оператор в непустом блоке, который - не является swich блоком достижим <=>, когда блок достижим. Каждый другой оператор S в непустом блоке, который не является swich блоком - достижим <=>, когда оператор, предшествующий S может завершаться нормально.
    • Оператор объявления локальной переменной может завершаться нормально <=>, когда он достижим.
    • Пустой оператор может завершаться нормально <=>, когда он достижим.
    • Помеченный оператор может завершаться нормально, если по крайней мере одно из следующих условий истинно:
      • Содержащиеся операторы могут завершаться нормально.
      • Имеется достижимый break оператор, который позволяет выйти из блока операторов на метку.

Содержащийся оператор достижим <=>, когда помеченный оператор достижим.

    • Оператор - выражения может завершаться нормально <=>, когда он достижим.
    • Оператор if обрабатывается другим способом. По этой причине, этот случай обсужден отдельно, в конце этого раздела.
    • Оператор switch может завершаться нормально <=>, когда по крайней мере одно из следующих условий истинно:
    • Последний оператор в switch-блоке может завершаться нормально.
    • Switch-блок пуст или содержит только switch-метки.
    • Имеется по крайней мере одна switch-метка после последней группы блока switch оператора.
    • Имеется достижимый оператор break для выхода из switch оператора.
    • Switch блок достижимый <=>, когда оператор switch достижим.
    • Оператор в switch блоке достижим <=>, когда оператор switch достижим и по крайней мере одно из следующих условий истинно:
      • Это удовлетворяет списку или заданной по умолчанию метке.
      • Имеется оператор, предшествующий этому оператору в switch блоке и предшествующий оператор может завершаться нормально.
    • Оператор while может завершаться нормально <=>, когда по крайней мере одно из следующих условий истинно:
      • Оператор while достижим и выражение условия в операторе не состоит из выражений со значением true.
      • Имеется достижимый оператор break для выхода из while оператора.

Содержащийся оператор - достижим <=>, когда оператор while достижим, и выражение условия не состоит из выражения со значением false.

    • Оператор do может завершаться нормально <=>, когда по крайней мере одно из следующих условий истинно:
      • Содержащийся оператор может завершаться нормально и выражение условия не имеет выражение с значением true.
      • Имеется достижимый оператор break для выхода из do оператора.

Содержащийся оператор достижим <=>, когда оператор do достижим.

    • Оператор for может завершаться нормально <=>, когда по крайней мере одно из следующих условий истинно:
      • Оператор for достижим, если имеется выражение условия, и выражение условия не имеет выражение с значением true.
      • Имеется достижимый оператор break для выхода из for оператора.

Содержащийся оператор достижим <=>,когда оператор for достижим, и выражение условия не содержит выражение со значением false.

    • Break, continue, return, или оператор throw, не может завершаться нормально.
    • Оператор syncronized может завершаться нормально <=>, когда содержащийся оператор может завершаться нормально. Содержащийся оператор достижим <=>, когда оператор syncronized достижим.
    • Оператор try может завершаться нормально <=>, когда оба из следующих условий истины:
    • Try блок может завершаться, нормально или любой сatch блок может завершиться нормально.
    • Если оператор try имеет finally блок, который может завершиться нормально.
    • Try блок достижим <=>, когда оператор try достижим.
    • Catch блок C достижим <=>, когда оба из следующих условий истинны:
    • Некоторое выражение или оператор throw в try блоке достижим и может вызывать исключение, чей тип присваиваемый параметру, оператора catch, C. (выражение рассматривается достижимым <=>, когда самый внутренний оператор, содержащийся в нем достижим.)
    • Не имеется никакого более раннего catch блока А в операторе try такого, что тип параметра C? одинаков или тип параметра C является подклассом типа параметра A? .

    Если finally блок присутствует, он достижим <=>, когда оператор try достижим.

Можно было ожидать, что оператор if будет обработан следующим способом, но то, что Ява фактически использует его, вообще говоря неверно:

    • ТЕОРИТИЧЕСКИ: Оператор if-then может завершаться нормально <=>, когда по крайней мере одно из следующих условий истинно:
      • оператор if-then достижим, и выражение условия не содержит выражение с значением true.
      • Then оператор может завершаться нормально.

Оператор then достижим <=>, когда оператор if-then достижим, и выражение условия не состоит из выражения с значением false.

    • ТЕОРИТИЧЕСКИ: Оператор if-then-else может завершаться нормально <=>, когда then оператор может завершаться нормально или else оператор может завершаться нормально. Then оператор достижим <=>, когда оператор if-then-else достижим, и выражение условия не имеет выражение с значением false. Else оператор достижим <=>, когда оператор if-then-else достижим, и выражение условия не имеет выражение с значением true.

Этот подход был бы непротиворечив с обработкой других структур управления в Ява. Однако, чтобы поставить оператор if в наилучшие, для "трансляций условия", то практически правила следующие:

    • ПРАКТИЧЕСКИ: Оператор if-then может завершаться нормально <=>, когда он достижим. Then оператор достижим <=>, когда оператор if-then достижим.
    • ПРАКТИЧЕСКИ: Оператор if-then-else может завершаться нормально <=> then оператор может завершаться нормально или else оператор может завершаться нормально. Then оператор достижим <=> оператор if-then-else достижим. Else оператор достижим <=>, когда оператор if-then-else достижим.

Для примера, результат следующего оператора: ошибка времени компиляции:

while (false) { x=3; }

Потому что оператор x=3; не достижим; но вообще подобный случай:

if (false) { x=3; }

не приводит к ошибке времени компиляции. Оптимизирующий транслятор может понять что оператор x=3, никогда не будет выполнен и может опустить код, для этого оператора, из сгенерированного файла класса, но оператора x=3, не расценен как "недостижимый" в техническом смысле, определенном здесь.

Основная причина различной обработки этих операторов состоит в том, чтобы позволить программистам определять переменные типа "флаг":

static final boolean DEBUG = false;

И затем можно написать код типа:

if (DEBUG) { x=3; }

Идея состоит в том, что можно изменить значение DEBUG с false на true или с true на false и затем компилировать код без других изменений в тексте программы.

Эта способность к "условной компиляции" имеет значительное влияние на связь и двоичную совместимость (§13). Если установка классов, которые используют переменную типа "флаг", откомпилируется, и условный код будет опущен, то код не будет удовлетворяет позже, чтобы распределить только новую версию класса или интерфейса, который содержит определение флагов. Изменение значения флага, следовательно приведет к двоичной несовместимости с предыдущим двоичным кодом (§13.4.8). (Имеются другие причины для такой несовместимости, например использования констант в case метках в swich операторах; смотри (§13.4.8).


[ Назад | Оглавление | Далее ]










helloworld.ru © 2001-2021
Все права защищены