Операторы управления ходом программы java
Вы конечно заметили, что наш «квадрат» не совсем квадратный — он больше прямоугольник из-за того, что по вертикали строки больше. Но будем называть его квадратом, т.к. количество символов по вертикали и горизонтали одинаковое.
Как я только что говорил, наша программа в обоих вариантах не самая удачная. Первый вариант длинный и обладает следующим недостатком — если мы захотим сделать квадрат например 7×7, то нам придется добавлять много строк.
Второй вариант конечно короче. Но у него есть тот же недостаток — для рисования квадрата другого размера нам придется все равно менять программу. Да и код выглядит пока страшненько.
Думаю, достаточно очевидно, что требуется конструкция цикла. Для большинства языков программирования таких конструкций бывает несколько. Давайте их и рассмотрим.
Операторы циклов
В Java существует три способа организации цикла.
1. Цикл for
Цикл for использует следующую форму записи
Давайте сразу рассмотрим в качестве примера использования цикла for нашу задачу — нарисовать квадрат.
Внутри скобок у слова for мы видим три разделенных точкой с запятой части.
int i=0 — этот оператор не должен нас удивить — мы объявили переменную «i» целого типа и присвоили ей значение 0. Этот оператор будет выполняться ТОЛЬКО ОДИН раз в начале цикла.
i i++ — этот оператор выполняется ПОСЛЕ КАЖДОГО цикла.
Все достаточно очевидно — мы выводим квадрат со стороной в 10 символов. Переменная «i» в данном случае исполняет роль счетчика — при каждом проходе цикла она увеличивается на 1, сравнивается со значением 10 и если меньше — выполняется операция печати строки из 10 символов.
Как видим, наша программа все-таки не так хороша. При желании вывести квадрат со стороной в 20 символов, нам придется менять значение 10 на 20 и еще изменить строку в печатью — заменить 10 символов «*» на 20.
Давайте сначала попробуем написать программу, которая выводит в ряд столько «звездочек», сколько мы захотим. Это несложно. Вот так:
Обратите внимание — мы ввели новую переменную «count» и используем ее в цикле. Сейчас этот шаг может показаться не совсем понятным и нужным, но на самом деле чуть позже мы увидим, почему это удобно. Давайте разберем наш код по шагам. Итак:
Этот оператор вводит переменную «count» и присваивает ей значение 10. Именно столько символов мы хотим напечатать. Если потребуется изменить количество символов, то будет достаточно просто поменять 10 на что-то другое.
Этот цикл напечатает 10 символов «*» в одну строку. Есть отличие между командами System.out.print и System.out.println. Первая выводит символы и не переходит на следующую строку, вторая — напечатает символы и курсор перейдет на следующую строку.
В нашем случае мы напечатаем 10 символов и курсор останется на этой же строке. Если мы захотим напечатать что-то еще, то печать начнется на той же строке. И наконец:
Эта команда просто переводит курсор на следующую строку
Если собрать наши программы LoopForOne и LoopForTwo вместе, то мы увидим, что в них есть все, что нам нужно — первая умеет печатать нужной количество строк, вторая — нужную нам строку.
Давайте попробуем провернуть это действие. Но сначала нам надо познакомиться еще с одним видом оператора — составной оператор. В другой литературе он может называться как-то иначе — я не могу точно сказать, как он должен называться по науке.
Его идея очень проста — заключив несколько операций в фигурные скобки, их можно рассматривать как один. Давайте опять посмотрим пример.
Операторы перехода в Java
Привет! Сегодня поговорим об операторах перехода в языке Java:
- return
- break
- continue
- goto
Для начала определимся с тем, что это вообще такое. Как известно, в обычной ситуации программа выполняется линейно — сверху вниз, команда за командой. Линейный ход программы могут изменить так называемые управляющие конструкции: например, ветвления ( if ) и циклы ( for , while и тд). Помимо управляющих конструкций, линейное выполнение программы могут изменить операторы перехода. Они отвечают за перенаправление выполнения программы в определенное место, которое зависит от контекста и конкретного оператора.Давай рассмотрим каждый из четырех операторов повнимательнее.
return
- Немедленно заканчивает выполнение метода.
- Немедленно заканчивает выполнение метода и возвращает какое-то значение в качестве результата работы метода.
return; return value; // где value — некоторое возвращаемое значение
В методах, которые возвращают некоторое значение, обязателен как минимум один оператор return с возвращаемым значением, который гарантированно вызовется, и недопустимо наличие оператора return без возвращаемого значения. Рассмотрим примеры ниже:
public int sum(int a, int b) < return a + b; >public String getGreetings(String name) < return "Hello " + name; >public int max(int x, int y) < if (x >y) < return x; >else < return y; >>
В методах, которые не возвращают значений (методы void ) допустимо, но не обязательно, наличие как минимум одного оператора return без возвращаемого значения, и недопустимо наличие ни одного оператора return с возвращаемым значением. Рассмотрим это на примерах ниже:
public void print(String s) < // наличие return в void методах не обязательно System.out.println(s); >//Метод выведет в консоль число, если оно нечетное public void printIfOdd(int number) < if (number % 2 == 0) < // Если число четное, метод завершит свою работу // Наличие return в void методах опционально return; >System.out.println(number); > // Метод выведет в консоль наибольшее значение из массива private void printMaxInArray(int[] array) < if (array == null || array.length == 0) < /* Если массив пуст, метод завершит свою работу. Иногда полезно проверять подобным образом аргументы метода вначале и прерывать выполнение метода, если аргументы не подходят для дальнейшей корректной работы */ System.out.println("Empty array"); return; >int max = array[1]; for (int i = 1; i < array.length; i++) < if (array[i] >max) < max = array[i]; >> System.out.println(max); >
labels (метки)
Прежде чем рассматривать операторы break и continue , хотелось бы поговорить о метках в ЯП Java. Это важно, потому что в некоторых ситуациях операторы break и continue используются совместно с метками. Но для начала попробуйте ответить на вопрос, скомпилируется ли такой код:
public static void main(String[] args) < https://www.google.com/ System.out.println("Interesting. "); >
public static void main(String[] args) < definePrintName: System.out.println("Таблица Умножения"); loop1: for (int i = 1; i System.out.println(); > >
Таблица Умножения 1 2 3 4 5 6 7 8 9 10 2 4 6 8 10 12 14 16 18 20 3 6 9 12 15 18 21 24 27 30 4 8 12 16 20 24 28 32 36 40 5 10 15 20 25 30 35 40 45 50 6 12 18 24 30 36 42 48 54 60 7 14 21 28 35 42 49 56 63 70 8 16 24 32 40 48 56 64 72 80 9 18 27 36 45 54 63 72 81 90 10 20 30 40 50 60 70 80 90 100 Process finished with exit code 0
В примере выше definePrintName , loop1: и loop2: — это метки. loop1: и loop2: “отмечают” два цикла — внешний и внутренний. Использование меток мы рассмотрим в разделе ниже. А пока, если вы ответили “нет” на вопрос скомпилируется ли такой код:
public static void main(String[] args) < https://www.google.com/ System.out.println("Interesting. "); >
break
- Для завершения какой-либо ветки выполнения в блоке switch-case.
- Для прерывания выполнения цикла.
break labelName; // Синтаксис оператора с меткой break; // Синтаксис оператора без метки
public static void main(String[] args) < int dayOfWeekInt = 4; String dayOfWeek; switch (dayOfWeekInt) < case 1: dayOfWeek = "Понедельник"; break; case 2: dayOfWeek = "Вторник"; break; case 3: dayOfWeek = "Среда"; break; case 4: dayOfWeek = "Четверг"; break; case 5: dayOfWeek = "Пятница"; break; case 6: dayOfWeek = "Суббота"; break; case 7: dayOfWeek = "Воскресенье"; break; default: dayOfWeek = "Неизвестный день"; break; >System.out.println("Сегодня " + dayOfWeek); >
В циклах оператор break используют для прерывания дальнейших итераций после того, как достигнуты определенные условия. Часто такое можно встретить, когда необходимо перебрать массив или коллекцию элементов и найти в ней какой-то элемент, удовлетворяющий нужным условиям. Рассмотрим такой пример. У нас есть массив и нам необходимо определить, содержит ли массив отрицательные элементы:
int a[] = ; boolean arrayHasNegativeElements = false; for (int i = 0; i < a.length; i++) < if (a[i] < 0) < /* Как только найдется хотя бы один отрицательный элемент, мы прервем цикл с помощью оператора break, потому что мы выяснили то, что нас интересовало, и дальнейший перебор элементов не имеет смысла. */ arrayHasNegativeElements = true; break; >>
public static void main(String[] args) < int a[] = ; boolean arrayHasNegativeElements = false; for (int number : a) < if (number < 0) < arrayHasNegativeElements = true; break; >> >
public static void main(String[] args) < int a[] = ; boolean arrayHasNegativeElements = false; int counter = 0; while (counter < a.length) < if (a[counter] < 0) < arrayHasNegativeElements = true; break; >counter ++; > >
public static void main(String[] args) < int a[] = ; boolean arrayHasNegativeElements = false; int counter = 0; do < if (a[counter] < 0) < arrayHasNegativeElements = true; break; >counter ++; > while (counter
Еще одним примером оператора break в циклах является прерывание бесконечного цикла при достижении определенных условий. Приведем пример программы, выводящей строку, которую ввел пользователь до тех пор, пока юзер не введёт слово “stop”:
public static void main(String[] args) < Scanner scanner = new Scanner(System.in); String line; while (true) < line = scanner.nextLine(); if ("stop".equals(line))< /* Прерываем бесконечный цикл, при достижении определенного условия */ break; >System.out.println("Пользователь ввел: " + line); > >
Рассмотрим использование оператора break совместно с меткой. Прерывание с меткой используется в случаях с несколькими циклами, причем вложенными один в другой. В таком случае один из циклов (или же все циклы) помечается меткой. Далее оператор break совместно с указанием метки прерывает нужный цикл. Рассмотрим пример, в котором нам необходимо понять, есть ли отрицательный элемент, но только не в массиве, а в матрице:
public static void main(String[] args) < int[][] a = < , , >; boolean hasNegative = false; searchNegative: for (int i = 0; i < a.length; i++) < for (int j = 0; j < a[i].length; j++) < if (a[i][j] < 0) < /* Если использовать break без метки, тогда прервется вложенный цикл for, но внешний продолжит выполнять свои итерации и поиск продолжится. Поэтому мы "помечаем" внешний цикл меткой `searchNegative` и прерываем внешний цикл оператором break совместно с нужной меткой. */ hasNegative = true; break searchNegative; >> > >
сontinue
continue; // форма оператора без метки continue labelName; // форма оператора с меткой
В отличии от оператора break , который прерывает все оставшиеся итерации цикла, оператор continue прерывает текущую итерацию и приводит к запуску следующей.Такое может быть полезно, если нужно провести некоторые операции над элементами, которые удовлетворяют определенным условиям. Скажем, у нас есть строка, и мы хотим посчитать количество слов, начинающихся с буквы “м”:
public static void main(String[] args) < String sentence = "Мама мыла раму"; String[] words = sentence.split(" "); int mWordsCount = 0; for (int i = 0; i < words.length; i++) < if ( ! words[i].toLowerCase().startsWith("м")) < /* Если слово не начинается с буквы м, то текущая итерация прервется и цикл ПРОДОЛЖИТ выполнение со следующей итерации */ continue; >mWordsCount ++; > System.out.println("Кол-во слов, начинающихся с буквы М в предложении: " + "[" + sentence + "] lang-java line-numbers"> Кол-во слов, начинающихся с буквы М в предложении: [Мама мыла раму] = 2
Оператор continue совместно с меткой также используется при переборе элементов. Представим себе матрицу, в которой нам нужно посчитать количество строк с отрицательными элементами:
public static void main(String[] args) < int[][] a = < , , , , >; int rowsWithNegativeElementsCount = 0; rowsLoop: // Проходим по каждой строке for (int[] arr : a) < for (int number : arr) < if (number < 0) < /* Если в текущей строке найдется хотя бы 1 отрицательный элемент, тогда мы увеличим переменную счетчик, и с помощью оператора continue rowsLoop прервем текущую итерацию внешнего цикла и принудительно начнем следующую */ rowsWithNegativeElementsCount ++; continue rowsLoop; >> > System.out.println("Rows With Negative Elements Count lang-java line-numbers"> Rows With Negative Elements Count = 3
Стоит сказать, что операторы break , continue и return можно по-разному использовать для достижения одной и той же функциональности. Так, можно переписать последний пример и вместо continue использовать break :
public static void main(String[] args) < int[][] a = < , , , , >; int rowsWithNegativeElementsCount = 0; for (int[] arr : a) < for (int number : arr) < if (number < 0) < rowsWithNegativeElementsCount ++; break; >> > System.out.println("Rows With Negative Elements Count lang-java line-numbers">public static void main(String[] args) < int[][] a = < , , , , >; int rowsWithNegativeElementsCount = 0; for (int[] arr : a) < if (arrayHasNegativeElements(arr)) < rowsWithNegativeElementsCount ++; >> System.out.println("Rows With Negative Elements Count https://docs.oracle.com/javase/tutorial/java/nutsandbolts/_keywords.html" rel="nofollow" target="_blank">Список ключевых слов в Java включает слово goto
. Однако данный оператор помечен как not used (не используется). Дело в том, что Джеймс Гослинг, создатель ЯП Java изначально заложил в JVM поддержку оператора goto
. Однако впоследствии эту фичу выпилили. Одна из причин заключается в том, что блоки кода содержащие оператор goto
, читались не так хорошо, как блоки кода, выполняющие те же функции, но без goto
, а с альтернативными подходами (break
, continue
, вынесение блока кода в методы). Были, собственно и другие, как например:
- сложность чтения и понимания кода, в котором есть операторы
goto
;
- усложнение оптимизации кода компилятору (а иногда и невозможность);
- повышение вероятности создания трудно уловимых ошибок в коде.
Для многих не секрет, что в некоторых языках программирования оператор goto
вполне успешно функционирует. Однако программисты избегают его использование. Почитать о причинах этого можно в одной статье на хабре. Но зачем тогда оставлять goto
в списке зарезервированных слов? Все просто: на будущее. Если, к примеру, по всему миру в коде Java разработчиков переменные, методы или классы будут называться goto
, если этот оператор вернут в одной из будущих версий Java, весь старый код сломается. Чтобы избежать такого сценария, goto
остался в списке ключевых слов Java, но не несет в себе никакой функциональности. Возможно когда-нибудь goto
вернется в наши ряды, но вероятность этого невысока.Итоги
Мы рассмотрели различные операторы перехода в Java:
return
— завершение метода, возвращение значения из метода.
- с возвращаемым значением: методы, которые возвращают значения;
- без возвращаемого значения:
void
методы.
break
— прерывание циклов, switch-case блоки.
- с метками: циклы различной вложенности;
- без меток: ветки switch-case блока; прерывание цикла, в котором был вызван.
continue
.
- с метками: циклы различной вложенности;
- без меток: продолжение цикла, в котором был вызван.
goto
.
- есть в списке ключевых слов, но не используется.
Вывод из всего этого простой: лучше отдавать предпочтение наиболее простым подходам, которые облегчают читаемость кода. Старайся не перегружать код многоуровневыми циклами, вложенными друг в друга с обилием меток, прерываний и продолжений.