JavaRush/Java блог/Random/Операторы перехода в Java
Автор
Владимир Портянко
Java-разработчик в Playtika

Операторы перехода в Java

Статья из группы Random
участников
Привет! Сегодня поговорим об операторах перехода в языке 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. Это важно, потому что в некоторых ситуациях операторы 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.
    • есть в списке ключевых слов, но не используется.
Вывод из всего этого простой: лучше отдавать предпочтение наиболее простым подходам, которые облегчают читаемость кода. Старайся не перегружать код многоуровневыми циклами, вложенными друг в друга с обилием меток, прерываний и продолжений.
Комментарии (12)
  • популярные
  • новые
  • старые
Для того, чтобы оставить комментарий Вы должны авторизоваться
Дмитрий
Уровень 2
14 августа 2020, 19:55
int max = array[1];
    for (int i = 1; i < array.length; i++) {
В методе поиска максимального элемента в массиве, решили 0 элемент не рассматривать? Надо поправить на
int max = array[0];
Ivan Vdovin
Уровень 9
13 августа 2020, 18:11
Такс, я ввёл программу и ничего, ссылку браузер не открыл, просто ничего не произошло
wan-derer.ru
Уровень 40
18 августа 2020, 04:33
И IDE, наверно, пометил что-то там цветом или ещё как :)
bighugеmistеr Android Developer
12 августа 2020, 21:12
Меня очень смутила статья. Неужели важнее писать красивый и простой код на Java, а не ориентироваться на производительность? Я вообще много раз читал, что использование всех этих операторов (кроме return) — быдлокод, и нормальная программа с продуманным алгоритмом не нуждается в них
Иван Ганжа
Уровень 41
13 августа 2020, 08:49
И так и сяк бывает, смотря какая задача, как правило конечно надо побыстрее и попроще, а пользователь докупит еще плашку памяти. Но код заточенный на эффективность читать не очень приятно.
bighugеmistеr Android Developer
13 августа 2020, 12:12
о_О Джависты — быдлокодеры (походу)
wan-derer.ru
Уровень 40
18 августа 2020, 04:32
Ты идёшь по массиву в поиске нужного значения. Нашёл, сделал нужные манипуляции. Всё, дальше идти нет смысла, делаешь break. Можно и не делать и пройти до конца массива, но тогда ты сделаешь кучу пустых операций (минус той самой производительности). Можно (в некоторых случаях) усложнить условие самого цикла, но они и так бывают слишком сложными для восприятия. Проще в нужном месте написать break и код сразу станет более понятным и тебе, и тем кто его будет читать потом.
bighugеmistеr Android Developer
18 августа 2020, 14:30
так только в Java?
wan-derer.ru
Уровень 40
18 августа 2020, 15:13
Да нет, это стандатные элементы структурного программирования
Justinian Judge в Mega City One Master
23 августа 2020, 09:06
Языков программирования сотни, если не тысячи и они разные и для разных целей. Есть языки, где важна заточенность на скорость, есть языки где важна заточенность на данные и тд. В джаве нету прямого обращения с памятью, зато есть JVM которая в многих случаях оптимизирует программу, даже если программист написал неоптимальный алгоритм, JVM сама его переиначит, использует другой механизм более подходящий и код станет быстрее. То о чем пишешь ты, звучит так "Я начал работать на заводе Toyota/Mercedes/BMW...И заметил, что они делают какие-то джипы, кроссоверы, какие-то малолитражки ...но в автомобиле же главное скорость? Почему из каждой машины не сделать Феррари/гоночный болид Ф1?". Ответ: потому что покупателям нужно другое. Если это семья с 6 -7 человек, им нужен минивен, если у кого-то дача в селе с разбитыми дорогами им нужен джип и тд. На "зачем" мне скорость авто, если я еду с семьей в парк, и не смогу положить детскую коляску для ребенка в багажник, поскольку багажник уменьшили "чтобы была больше скорость"? Поэтому о производительности нужно думать, но у джавы другое назначение. Джава это язык, который применяется для построения архитектурно сложных, но в то же время гибких к изменению и адекватно легких в поддержке корпоративных приложений. Которые могут состоять из десятков и сотен модулей, десятков тысяч классов. И это все работает на реальный бизнес. Ты не сможешь выполнить эту задачу на джаве, если будешь писать код в ассемблер/си стиле "лишь бы быстро работало". Бизнес обанкротится, программа зафейлится, зато когда-то оно работало быстро. Поэтому читабельность для джавы - это все. Читабельность кода помогает строить сложные системы, их поддерживать и менять. Сам подумай, если были более производительные языки, отчего джаву придумали и она столько лет на коне? Потому что более производительные языки подходят под свои задачи, но не подходят под другие, которые решает джава. Разные задачи - разные подходы к программированию.
bighugеmistеr Android Developer
12 августа 2020, 20:54
Есть break, continue... А нет в Java какого-нибудь exit? Помню, в Pascal он был
Учиха Шисуи Junior Android Developer в SoftWeather Expert
12 августа 2020, 21:42
exit'ы существуют или существовали в ранних версиях. Я не читал еще саму статью, поэтому затрудняюсь, с ответом, однако предположу, что такие меры были убраны из языка по мере возможности, в пользу сохранения функциональности, целости выполнения задач. Задачи не должны прерываться на середине и забрасываться, всё должно быть обработано. Если же у вас есть необходимость в том, чтобы действительно просто прервать выполнение той или иной задачи, на месте, то для вас это не составит хлопот (при помощи тех же расставленных доп флагов можно выбраться из циклом любой глубины). Однако язык больше ориентирован на те случаи, когда идет приоритет на целостность данных. Примером убранного из языка метода крушащего работу, нити, могу привести Thread.exit() Примером метода немедленного завершения программы System.exit()