Привет! Сегодня поговорим об операторах перехода в языке Java:
return
break
continue
goto
if
) и циклы (for
, while
и тд).
Помимо управляющих конструкций, линейное выполнение программы могут изменить операторы перехода. Они отвечают за перенаправление выполнения программы в определенное место, которое зависит от контекста и конкретного оператора.Давай рассмотрим каждый из четырех операторов повнимательнее.return
Именно с этим оператором чаще всего в первую очередь знакомятся новички. Операторreturn
завершает работу метода, в котором его вызвали, и выполнение программы возвращается туда, откуда данный метод вызван.
У 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...");
}
Метка — это именованная часть кода. Сама по себе метка не несет никакой функциональности. Это что-то вроде закладки в коде, которую программист намерен использовать впоследствии.
Метка в коде определяется весьма просто — через имя и двоеточие. Например:
labelName:
outerLoop:
printing:
anyWordYouLike:
public static void main(String[] args) {
definePrintName:
System.out.println("Таблица Умножения");
loop1:
for (int i = 1; i <= 10; i++) {
loop2:
for (int j = 1; j <= 10; j++) {
System.out.printf("%4d", i * j);
}
System.out.println();
}
}
Вывод метода main
будет следующим:
Таблица Умножения
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...");
}
Попробуйте ответить на него еще раз, с помощью IDE.break
Операторbreak
используется в двух случаях:
- Для завершения какой-либо ветки выполнения в блоке switch-case.
- Для прерывания выполнения цикла.
break labelName; // Синтаксис оператора с меткой
break; // Синтаксис оператора без метки
В блоках switch-case оператор 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[] = {1,2,234,-123,12,-2,312,0,412,433};
boolean arrayHasNegativeElements = false;
for (int i = 0; i < a.length; i++) {
if (a[i] < 0) {
/*
Как только найдется
хотя бы один отрицательный элемент,
мы прервем цикл с помощью
оператора break, потому что
мы выяснили то, что нас интересовало,
и дальнейший перебор элементов не имеет смысла.
*/
arrayHasNegativeElements = true;
break;
}
}
Рассмотрим тот же пример с различными циклами.
Цикл for-each
:
public static void main(String[] args) {
int a[] = {1,2,234,-123,12,-2,312,0,412,433};
boolean arrayHasNegativeElements = false;
for (int number : a) {
if (number < 0) {
arrayHasNegativeElements = true;
break;
}
}
}
Цикл while
:
public static void main(String[] args) {
int a[] = {1,2,234,-123,12,-2,312,0,412,433};
boolean arrayHasNegativeElements = false;
int counter = 0;
while (counter < a.length) {
if (a[counter] < 0) {
arrayHasNegativeElements = true;
break;
}
counter ++;
}
}
Цикл do-while
:
public static void main(String[] args) {
int a[] = {1,2,234,-123,12,-2,312,0,412,433};
boolean arrayHasNegativeElements = false;
int counter = 0;
do {
if (a[counter] < 0) {
arrayHasNegativeElements = true;
break;
}
counter ++;
} while (counter < a.length);
}
Еще одним примером оператора 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 = {
{1, 2, 3},
{-412, 12, 0},
{1223, 474, -54}
};
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; // форма оператора без метки
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 + "] = " + mWordsCount);
}
После выполнения данного кода будет такой вывод в консоли:
Кол-во слов, начинающихся с буквы М в предложении: [Мама мыла раму] = 2
Оператор continue
совместно с меткой также используется при переборе элементов.
Представим себе матрицу, в которой нам нужно посчитать количество строк с отрицательными элементами:
public static void main(String[] args) {
int[][] a = {
{1, 23, -1, 23, -12},
{21, 21, 0, 23, 123, 45},
{123, 3},
{123, -5, 4, -3},
{-1, -2, -3}
};
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 = " + rowsWithNegativeElementsCount);
}
Выводом данного кода будет:
Rows With Negative Elements Count = 3
Стоит сказать, что операторы break
, continue
и return
можно по-разному использовать для достижения одной и той же функциональности.
Так, можно переписать последний пример и вместо continue
использовать break
:
public static void main(String[] args) {
int[][] a = {
{1, 23, -1, 23, -12},
{21, 21, 0, 23, 123, 45},
{123, 3},
{123, -5, 4, -3},
{-1, -2, -3}
};
int rowsWithNegativeElementsCount = 0;
for (int[] arr : a) {
for (int number : arr) {
if (number < 0) {
rowsWithNegativeElementsCount ++;
break;
}
}
}
System.out.println("Rows With Negative Elements Count = " + rowsWithNegativeElementsCount);
}
Разница между break
и continue
с меткой заключается в том, что break
завершает итерации цикла, в котором он написан. А continue
с меткой пропускает текущую итерацию цикла отмеченного меткой. В некоторых ситуациях можно заменять одно на другое, и в функциональности программы все останется прежним. О том, что лучше выбрать (спойлер: читаемость кода), расскажем немного ниже.
Оператор break
можно заменить не только на continue
с меткой, но и на return
. Только перед этим нужно вынести вложенный цикл в отдельный метод:
public static void main(String[] args) {
int[][] a = {
{1, 23, -1, 23, -12},
{21, 21, 0, 23, 123, 45},
{123, 3},
{123, -5, 4, -3},
{-1, -2, -3}
};
int rowsWithNegativeElementsCount = 0;
for (int[] arr : a) {
if (arrayHasNegativeElements(arr)) {
rowsWithNegativeElementsCount ++;
}
}
System.out.println("Rows With Negative Elements Count = " + rowsWithNegativeElementsCount);
}
static boolean arrayHasNegativeElements(int[] array) {
for (int number : array) {
if (number < 0) {
return true;
}
}
return false;
}
Уйма способов написать одно и то же. Какой же выбрать? В промышленном программировании в этом вопросе решает легкость восприятия кода. Чем проще написано, тем лучше.
Чем больше вложенных циклов, тем сложнее воспринимать код. Особенно если циклы отмечены различными метками, которые используются в прерываниях и продолжениях (break
и continue
).
Если есть возможность не использовать метки, лучше так и делать. В ином случае старайтесь писать максимально понятно и красиво.goto
В некоторых языках программирования существует операторgoto
. Обычно он перенаправляет выполнение кода в какую-либо часть программы, помеченную меткой. Но в Java goto
, можно сказать, и есть, и нет. Давайте разбираться.
Список ключевых слов в 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
.- есть в списке ключевых слов, но не используется.