18

18


import static net.mindview.util.Range.*,
import static net.mindview.util Print.*;
public class ForEachlnt {
public static void main(String[] args) { for(int i : range(10)) // 0..9
printnbCi + „ v).
printO;
for(int i : range(5, 10)) // 5..9
printnb(i +
printO;
for(int i : range(5. 20. 3)) // 5
printnbCi +
printO:
}
} /* Output: 0 12 3 4 5 6 7 8 9 5 6 7 8 9 5 8 11 14 17 *///:-
Обратите внимание на использование printnb() вместо print(). Метод printnb() не выводит символ новой строки, что позовляет построить строку по фраг¬ментам.
return
Следующая группа ключевых слов обеспечивает безусловный переход, то есть передачу управления без проверки каких-либо условий. К их числу относятся команды return, break и continue, а также конструкция перехода по метке, анало¬гичная goto в других языках.
У ключевого слова return имеется два предназначения: оно указывает, какое значение возвращается методом (если только он не возвращает тип void), а так¬же используется для немедленного выхода из метода. Метод test() из предыду¬щего примера можно переписать так, чтобы он воспользовался новыми возмож¬ностями:
//: control/IfElse2.java
import static net.mindview.util.Print.*:
public class IfElse2 {
static int test(int testval. int target) { if(testval <@062> target)
return +1; else if(testval < target) return -1;
else
return 0; // Одинаковые значения
}
public static void main(String[] args) { prmt(test(10. 5)); print(test(5. 10)): print(test(5. 5));
}
} /* Output: 1 -1 0
В данном случае секция else не нужна, поскольку работа метода не продол¬жается после выполнения инструкции return.
Если метод, возвращающий void, не содержит команды return, такая команда неявно выполняется в конце метода. Тем не менее, если метод возвращает лю¬бой тип, кроме void, проследите за тем, чтобы каждая логическая ветвь возвра¬щала конкретное значение.
break и continue
В теле любого из циклов вы можете управлять потоком программы, используя специальные ключевые слова break и continue. Команда break завершает цикл, при этом оставшиеся операторы цикла не выполняются. Команда continue оста¬навливает выполнение текущей итерации цикла и переходит к началу цикла, чтобы начать выполнение нового шага.
Следующая программа показывает пример использования команд break и con¬tinue внутри циклов for и while:
//: control/BreakAndContinue.java // Применение ключевых слов break и continue import static net.mindview.util.Range.*;
public class BreakAndContinue {
public static void main(String[] args) { for(int i = 0: i < 100; i++) {
if(i == 74) break; // Выход из цикла
if(i % 9 != 0) continue; // Следующая итерация
System.out print(i + " ");
}
System out.printlnO; // Использование foreach: for(int i : range(lOO)) {
if(i == 74) break; // Выход из цикла
if(i % 9 != 0) continue; // Следующая итерация
System.out.print(i + " ");
}
System.out.println(); int i = 0;
// "Бесконечный цикл": while(true) { i++;
int j = i * 27,
if(j == 1269) break; // Выход из цикла
if(i % 10 != 0) continue; 11 Возврат в начало цикла
System.out.print(i + " ");
}
}
} /* Output:
0 9 18 27 36 45 54 63 72 0 9 18 27 36 45 54 63 72 10 20 30 40 *///:-
В цикле for переменная i никогда не достигает значения 100 — команда break прерывает цикл, когда значение переменной становится равным 74. Обычно break используется только тогда, когда вы точно знаете, что условие выхода из цикла действительно достигнуто. Команда continue переводит исполнение в на¬чало цикла (и таким образом увеличивает значение i), когда i не делится без ос¬татка на 9. Если деление производится без остатка, значение выводится на эк¬ран.
Второй цикл for демонстрирует использование «синтаксиса foreach» с тем же результатом.
Последняя часть программы демонстрирует «бесконечный цикл», который теоретически должен исполняться вечно. Однако в теле цикла вызывается ко¬манда break, которая и завершает цикл. Команда continue переводит исполнение к началу цикла, и при этом остаток цикла не выполняется. (Таким образом, вы¬вод на экран в последнем цикле происходит только в том случае, если значе¬ние i делится на 10 без остатка.) Значение 0 выводится, так как 0 % 9 дает в ре¬зультате 0.
Вторая форма бесконечного цикла — for(;;). Компилятор реализует конст¬рукции while(true) и for(;;) одинаково, так что выбор является делом вкуса.
Нехорошая команда goto
Ключевое слово goto появилось одновременно с языками программирования. Действительно, безусловный переход заложил основы принятия решений в языке ассемблера: «если условие А, перейти туда, а иначе перейти сюда». Если вам доводилось читать код на ассемблере, который генерируют фактически все компиляторы, наверняка вы замечали многочисленные переходы, управ¬ляющие выполнением программы (компилятор Java производит свой собствен¬ный «ассемблерный» код, но последний выполняется виртуальной-машиной Java, а не аппаратным процессором).
Команда goto реализует безусловный переход на уровне исходного текста программы, и именно это обстоятельство принесло ей дурную славу. Если программа постоянно «прыгает» из одного места в другое, нет ли способа реор¬ганизовать ее код так, чтобы управление программой перестало быть таким «прыгучим»? Команда goto впала в настоящую немилость с опубликованием знаменитой статьи Эдгара Дейкстры «Команда GOTO вредна» (Goto considered harmful), их тех пор порицание команды goto стало чуть ли не спортом, а за¬щитники репутации многострадального оператора разбежались по укромным углам.
Как всегда в ситуациях такого рода, существует «золотая середина». Про¬блема состоит не в использовании goto вообще, но в злоупотреблении — все же иногда именно оператор goto позволяет лучше всего организовать управление программой.
1 Оригинал статьи Go То Statement considered harmful имеет постоянный адрес в Интернете: http://www.acm.org/classics/oct95. — Примеч. ред.
Хотя слово goto зарезервировано в языке Java, оно там не используется; Java не имеет команды goto. Однако существует механизм, чем-то похожий на безус¬ловный переход и осуществляемый командами break и continue. Скорее, это способ прервать итерацию цикла, а не передать управление в другую точку про¬граммы. Причина его обсуждения вместе с goto состоит в том, что он использу¬ет тот же механизм — метки.
Метка представляет собой идентификатор с последующим двоеточием:
label 1•
Единственное место, где в Java метка может оказаться полезной, — прямо пе¬ред телом цикла. Причем никаких дополнительных команд между меткой и те¬лом цикла быть не должно. Причина помещения метки перед телом цикла мо¬жет быть лишь одна — вложение внутри цикла другого цикла или конструкции выбора. Обычные версии break и continue прерывают только текущий цикл, в то время как их версии с метками способны досрочно завершать циклы и пе¬редавать выполнение в точку, адресуемую меткой:
labell:
внешний-цикл {
внутренний-цикл { //.
break; // 1 // .
continue; // 2 //..
continue labell; // 3 //...
break labell; // 4

В первом случае (1) команда break прерывает выполнение внутреннего цикла, и управление переходит к внешнему циклу. Во втором случае (2) опера¬тор continue передает управление к началу внутреннего цикла. Но в третьем ва¬рианте (3) команда continue labell влечет выход из внутреннего и внешнего цик¬лов и возврат к метке labell. Далее выполнение цикла фактически продолжается, но с внешнего цикла. В четвертом случае (4) команда break labell также вызыва¬ет переход к метке labell, но на этот раз повторный вход в итерацию не проис¬ходит. Это действие останавливает выполнение обоих циклов. Пример использования цикла for с метками:
//: control/LabeledFor.java
// Цикл for с метками
import static net.mindview.util.Print.*;
public class LabeledFor {
public static void main(String[] args) { int i = 0;
outer- // Другие команды недопустимы for(; true ,) { // infinite loop
inner: // Другие команды недопустимы for(; i < 10; i++) {
printC'i = " + i);
if(i == 2) {
print("continue"); continue;
}
if(i == 3) {
printC'break");
i++; // В противном случае значение i
// не увеличивается break;
}
if(i - 7) {
printC'continue outer");
i++; // В противном случае значение i
// не увеличивается continue outer;
}
if(i == 8) {
printC'break outer"); break outer;
}
for(int k = 0; k < 5; k++) {• if (k == 3) {
printC'continue inner"); continue inner;
}
}
}
}
// Использовать break или continue // с метками здесь не разрешается
}
} /* Output: i = 0
continue inner i = 1
continue inner i = 2 continue i = 3 break i = 4
continue inner i = 5
continue inner i = 6
continue inner i = 7
continue outer i = 8
break outer *///:-
Заметьте, что оператор break завершает цикл for, вследствие этого выраже¬ние с инкрементом не выполняется до завершения очередного шага. Поэтому из-за пропуска операции инкремента в цикле переменная непосредственно уве¬личивается на единицу, когда i — 3. При выполнении условия i == 7 команда continue outer переводит выполнение на начало цикла; инкремент опять пропус¬кается, поэтому и в этом случае переменная увеличивается явно.
Без команды break outer программе не удалось бы покинуть внешний цикл из внутреннего цикла, так как команда break сама по себе завершает выполнение только текущего цикла (это справедливо и для continue).
Конечно, если завершение цикла также приводит к завершению работы ме¬тода, можно просто применить команду return.
Теперь рассмотрим пример, в котором используются команды break и con¬tinue с метками в цикле while:
//: control/LabeledWhile java
// Цикл while с метками
import static net mindview.util.Print.*;
public class LabeledWhile {
public static void main(String[] args) { int i = 0; outer:
while(true) {
print("Внешний цикл while"); while(true) { i++;
printC'i = " + i); if(i == 1) {
print("continue"); continue;
}
if(i == 3) {
print("continue outer"); continue outer;
}
if(i == 5) {
printC'break"); break,
}
if(i == 7) {
printC'break outer"); break outer;
}
}
}
}
} /* Output: Внешний цикл while i = 1 continue i = 2 i = 3
continue outer Внешний цикл while i = 4 i = 5 break
Внешний цикл while i = 6
i = 7
break outer
*///-
Те же правила верны и для цикла while:
• Обычная команда continue переводит исполнение к началу текущего внутреннего цикла, программа продолжает работу.
• Команда continue с меткой вызывает переход к метке и повторный вход в цикл, следующий прямо за этой меткой.
• Команда break завершает выполнение текущего цикла.
• Команда break с меткой завершает выполнение внутреннего цикла и цик¬ла, который находится после указанной метки.
Важно помнить, что единственная причина для существования меток в Ja¬va — наличие вложенных циклов и необходимость выхода по break и продолже¬ния по continue не только для внутренних, но и для внешних циклов.
В статье Дейкстры особенно критикуются метки, а не сам оператор goto. Дейкстра отмечает, что, как правило, количество ошибок в программе растет с увеличением количества меток в этой программе. Метки затрудняют анализ программного кода. Заметьте, что метки Java не страдают этими пороками, по¬тому что их место расположения ограничено и они не могут использоваться для беспорядочной передачи управления. В данном случае от ограничения возмож¬ностей функциональность языка только выигрывает.