Читайте также:
|
|
Внутри тела любой инструкции итераций вы также можете использовать управление течением цикла, используя break и continue. break прерывает цикл без выполнения оставшихся инструкций в цикле. continue останавливает выполнение текущей итерации и возвращается к началу цикла, начиная следующую итерацию.
Эта программа показывает пример для break и continue внутри циклов for и while:
//: c03:BreakAndContinue.java// Демонстрирует break и continue. public class BreakAndContinue { public static void main(String[] args) { for(int i = 0; i < 100; i++) { if(i == 74) break; // выход из цикла for if(i % 9!= 0) continue; // Следующая итерация System.out.println(i); } int i = 0; // "Бесконечный цикл": while(true) { i++; int j = i * 27; if(j == 1269) break; // Выход из цикла if(i % 10!= 0) continue; // В начало цикла System.out.println(i); } }} ///:~В цикле for значение i никогда не дойдет до 100, потому, что инструкция break прервет выполнение цикла, когда i будет равно 74. Обычно, вы будете использовать break как здесь, если вы не будете знать, когда возникнет прерывающее условие. Инструкция continue влечет за собой возврат к началу цикла (при этом инкрементируя i) в любом случае, когда i не делится на 9 без остатка. Если это так, значение печатается.
Второй раздел показывает “бесконечный цикл”, который, теоретически, никогда не закончится. Однако внутри цикла есть инструкция break, которая оборвет цикл. Дополнительно, вы увидите, что continue возвращает назад, к началу цикла, не завершив оставшегося. (Таким образом, печать происходит во втором цикле только тогда, когда значение i делится на 10.) Вот результаты:
091827364554637210203040Значение 0 печатается, потому что 0 % 9 равно 0.
Вторая форма бесконечного цикла: for(;;). Компилятор трактует и while(true) и for(;;) одинаково, что бы вы ни использовали - это вопрос стиля программирования.
Имеющий дурную славу “goto”
Ключевое слово goto существовало в языках программирования с самого начала. Несомненно, goto было рождено из ассемблерных языков программирования: “Если условие A, то перейти сюда, в противном случае, перейти сюда”. Если вы читаете ассемблерный код, который, в конце концов, генерируется практически каждым компилятором, вы увидите, что такое управление программой содержит много переходов. Однако goto - это переход на уровне исходного кода, и это то, что снискало дурную славу. Если программа будет всегда перепрыгивать из одного места в другое, то будет ли способ реорганизовать код так, чтобы управление не было таким прыгающим? goto попал в немилость после известной газетной публикации “Goto considered harmful” Edsger Dijkstra, и с тех пор избиение goto было популярным занятием.
Как обычно в такой ситуации середина наиболее плодотворна. Проблема не в использовании goto, а в перегрузке операторами goto — в редких ситуациях goto действительно лучший способ для структурирования управления течением программы.
Хотя goto - это зарезервированное слово в Java, оно не используется в языке; в Java нет goto. Однако здесь есть кое-что, что выглядит немного как переход при использовании ключевых слов break и continue. Это не переход, а способ прервать инструкцию итерации. Объяснение часто всплывает в дискуссии о goto: потому что тут используется тот же механизм: метка.
Метка - это идентификатор, за которым следует двоеточие, например:
label1:Только в одном месте в Java метки полезны: прямо перед итерационными инструкциями. А сразу перед означает, что нехорошо помещать любые другие инструкции между меткой и итерацией. И единственная причина помещать метку перед итерацией есть в том случае, если вы заходите в группу другой итерации или внутри есть переключатель. Это потому, что ключевые слова break и continue обычно прерывают только текущий цикл, но когда вы используете метку, они прервут внешний цикл, где стоит метка:
label1: outer-iteration { inner-iteration { //... break; // 1 //... continue; // 2 //... continue label1; // 3 //... break label1; // 4 }}В случае 1, break прерывает внутреннюю итерацию, и вы выходите во внешнюю итерацию. В случае 2, continue перемещает к началу внутренней итерации. Но в случае 3, continue label1 прерывает внутреннюю итерацию и внешнюю итерацию, все пути ведут к label1. Затем фактически продолжаются итерации, но начиная с внешней итерации. В случае 4, break label1 также прерывает все пути к метке label1, но не происходит повторного входа в итерацию. Реально происходит прерывание обеих итераций.
Вот пример использования цикла for:
//: c03:LabeledFor.java// "Помеченный цикл for" в Java. public class LabeledFor { public static void main(String[] args) { int i = 0; outer: // Здесь не может быть инструкций for(; true;) { // бесконечный цикл inner: // Здесь не может быть инструкций for(; i < 10; i++) { prt("i = " + i); if(i == 2) { prt("continue"); continue; } if(i == 3) { prt("break"); i++; // В противном случае i никогда // не получит инкремент. break; } if(i == 7) { prt("continue outer"); i++; // В противном случае i никогда // не получит инкремент. continue outer; } if(i == 8) { prt("break outer"); break outer; } for(int k = 0; k < 5; k++) { if(k == 3) { prt("continue inner"); continue inner; } } } } // Здесь нельзя использовать break или continue // с меткой } static void prt(String s) { System.out.println(s); }} ///:~Здесь используется метод prt(), который был использован в других примерах.
Обратите внимание, что break прерывает цикл for, и при этом не происходит инкрементации, пока не будет завершен проход цикла for. Так как break пропускает выражение инкремента, инкремент выполняется прямо в случае i == 3. Инструкция continue outer в случае i == 7 также переходит к началу цикла и также пропускает инкремент, так что нужно инкрементировать в ручную.
Вот результат работы:
i = 0continue inneri = 1continue inneri = 2continuei = 3breaki = 4continue inneri = 5continue inneri = 6continue inneri = 7continue outeri = 8break outerЕсли не использовать инструкцию break outer, то нет способа выйти во внешний цикл из внутреннего цикла, так как break сам по себе прерывает только самый внутренний цикл. (То же самое верно и для continue.)
Конечно, в случае, когда нужно прервать цикл и одновременно выйти из метода, вы можете просто использовать return.
Вот демонстрация использования помеченных инструкций break и continue с циклом while:
//: c03:LabeledWhile.java// "Помеченный цикл while" в Java. public class LabeledWhile { public static void main(String[] args) { int i = 0; outer: while(true) { prt("Outer while loop"); while(true) { i++; prt("i = " + i); if(i == 1) { prt("continue"); continue; } if(i == 3) { prt("continue outer"); continue outer; } if(i == 5) { prt("break"); break; } if(i == 7) { prt("break outer"); break outer; } } } } static void prt(String s) { System.out.println(s); }} ///:~Те же правила применимы для while:
Вывод этого метода становится достаточно ясным:
Outer while loopi = 1continuei = 2i = 3continue outerOuter while loopi = 4i = 5breakOuter while loopi = 6i = 7break outerВажно запомнить, что есть только одна причина использования меток в Java, когда вы имеете группу циклов и вы хотите использовать break или continue через группу, содержащую более одного уровня циклов.
В газетной статье Dijkstra “Goto considered harmful”, то, против чего он действительно возражал - это метки, а не goto. Он заметил, что число ошибок увеличивается с увеличением числа меток в программе. Метки и переходы делают программу трудной для статического анализа, так как это вводит в программу циклы графов исполнения. Обратите внимание, что метки Java не испытывают этой проблемы, так как они ограничены своим местом и не могут быть использованы для передачи управления другим образом. Также интересно заметить, что это тот случай, когда особенности языка становятся более полезными при ограничении инструкции.
Switch
switch иногда классифицируется как инструкция переключения. Инструкция switch выбирает из нескольких частей кода на основании значения целочисленного выражения. Вот его форма:
switch(целочисленный_переключатель) { case целочисленное_значение1: инструкция; break; case целочисленное_значение2: инструкция; break; case целочисленное_значение3: инструкция; break; case целочисленное_значение4: инструкция; break; case целочисленное_значение5: инструкция; break; //... default: инструкция; }Целочисленный_переключатель - это выражение, которое производит целое значение. switch сравнивает результат целочисленного_переключателя с каждым целочисленным_значением. Если он находит совпадение, выполняется соответственная инструкция (простая или составная). Если нет совпадений, выполняется инструкция default.
Обратите внимание, что в приведенном выше определении каждый case заканчивается break, который является причиной того, что выполнение перепрыгивает на конец тела switch. Это традиционный способ для построения инструкции switch, но break не обязателен. Если его нет, выполняется код случая следующей инструкции, пока не обнаружится break. Хотя обычно поведение такого рода не нужно, это может быть полезно для опытных программистов. Обратите внимание, что последняя инструкция, следующая за default, не имеет break, потому что выполнение переходит туда же, куда оно и так перейдет после break. Вы можете поместить break в конце инструкции default без всякого ущерба, если вы решите, что это важно для стиля.
Инструкция switch - это ясный способ для реализации множественного выбора (т.е., выбора из большого числа разных путей выполнения), но это требует переключателя, при вычислении которого получается целое значение типа int или char. Если вы хотите использовать, например, строку или число с плавающей точкой в качестве переключателя, они не будут работать в инструкции switch. Для не целых типов вы должны использовать серию инструкций if.
Вот пример, в котором в случайном порядке создаются буквы и проверяются, являются ли они гласными или согласными:
//: c03:VowelsAndConsonants.java// Демонстрация инструкции switch. public class VowelsAndConsonants { public static void main(String[] args) { for(int i = 0; i < 100; i++) { char c = (char)(Math.random() * 26 + 'a'); System.out.print(c + ": "); switch(c) { case 'a': case 'e': case 'i': case 'o': case 'u': System.out.println("vowel"); break; case 'y': case 'w': System.out.println("Sometimes a vowel"); break; default: System.out.println("consonant"); } } }} ///:~Так как Math.random() генерирует значения в пределах от 0 до 1, вам необходимо только умножить его на верхний предел границы чисел, которые вы хотите производить (26 для букв алфавита) и прибавлять смещение для установки нижней границы.
Хотя здесь используется переключение для символов (char), инструкция switch на самом деле использует целое значение для символов. Символы в одинарных кавычках в инструкциях case также производят целочисленные значения, которые также используются для сравнения.
Обратите внимание, как расположены case 'ы друг над другом, чтобы обеспечить выравнивание определенным частям кода. Вы можете также осознавать, что важно помещать инструкцию break в конце соответствующего case, в противном случае управление пройдет дальше и продолжится выполнение следующего case.
Дата добавления: 2015-09-11; просмотров: 89 | Поможем написать вашу работу | Нарушение авторских прав |