Вітання! Сьогодні поговоримо про операторів переходу в мові 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 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
.- є у списку ключових слів, але не використовується.
ПЕРЕЙДІТЬ В ПОВНУ ВЕРСІЮ