JavaRush /Java блог /Random UA /Оператори переходу до Java

Оператори переходу до Java

Стаття з групи Random UA
Вітання! Сьогодні поговоримо про операторів переходу в мові Java:
  • return
  • break
  • continue
  • goto
Спочатку визначимося з тим, що це взагалі таке. Як відомо, у звичайній ситуації програма виконується лінійно – зверху донизу, команда за командою. Лінійний хід програми можуть змінити звані управляючі конструкції: наприклад, розгалуження ( if) і цикли ( for, whileі тд). Крім керуючих конструкцій, лінійне виконання програми можуть змінити оператори переходу. Вони відповідають за перенаправлення виконання програми у певне місце, яке залежить від контексту та конкретного оператора. Оператори переходу в Java - 1Давай розглянемо кожен із чотирьох операторів уважніше.

return

Саме з цим оператором найчастіше насамперед знайомляться новачки. Оператор returnзавершує роботу методу, в якому його викликали, та виконання програми повертається туди, звідки цей метод викликано. Є returnдві форми:
  1. Негайно закінчує виконання методу.
  2. Негайно закінчує виконання методу та повертає якесь значення як результат роботи методу.
Синтаксис обох форм:
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:
А ось так виглядають мітки всередині Java-коду:
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використовується у двох випадках:
  1. Для завершення будь-якої гілки виконання у блоці switch-case.
  2. Для переривання виконання циклу.
Оператор має дві форми: з маркуванням (міткою) і без. Синтаксис обох форм такий:
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перериває поточну ітерацію і призводить до запуску наступної. Оператори переходу в Java - 2Таке може бути корисним, якщо потрібно провести деякі операції над елементами, які задовольняють певним умовам. Скажімо, ми маємо рядок, і ми хочемо порахувати кількість слів, що починаються з літери “м”:
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:
  1. return- Завершення методу, повернення значення з методу.
    • з значенням, що повертається: методи, які повертають значення;
    • без значення, що повертається: voidметоди.
  2. break- Переривання циклів, switch-case блоки.
    • з мітками: цикли різної вкладеності;
    • без міток: гілки switch-case блоку; переривання циклу, у якому було викликано.
  3. continue.
    • з мітками: цикли різної вкладеності;
    • без позначок: продовження циклу, в якому був викликаний.
  4. goto.
    • є у списку ключових слів, але не використовується.
Висновок з усього цього простий: краще віддавати перевагу найпростішим підходам, які полегшують читання коду. Намагайся не перевантажувати код багаторівневими циклами, вкладеними один в одного з великою кількістю міток, переривань та продовжень.
Коментарі
ЩОБ ПОДИВИТИСЯ ВСІ КОМЕНТАРІ АБО ЗАЛИШИТИ КОМЕНТАР,
ПЕРЕЙДІТЬ В ПОВНУ ВЕРСІЮ