JavaRush /Blog Java /Random-ES /Operadores de salto en Java

Operadores de salto en Java

Publicado en el grupo Random-ES
¡Hola! Hoy hablaremos de operadores de salto en Java:
  • return
  • break
  • continue
  • goto
Primero, definamos qué es realmente. Como sabe, en una situación normal, un programa se ejecuta linealmente, de arriba a abajo, comando por comando. El flujo lineal de un programa se puede cambiar mediante las llamadas estructuras de control: por ejemplo, ramas ( if) y bucles ( for, whileetc.). Además de las construcciones de control, la ejecución lineal de un programa se puede modificar mediante declaraciones de salto. Son responsables de redirigir la ejecución del programa a una ubicación específica, lo que depende del contexto y de la declaración específica. Operadores de salto en Java - 1Echemos un vistazo más de cerca a cada uno de los cuatro operadores.

devolver

Es este operador con el que los recién llegados suelen familiarizarse primero. La declaración returnfinaliza el método en el que fue llamada y la ejecución del programa regresa a la ubicación desde la que se llamó el método. Tiene returndos formas:
  1. Finaliza inmediatamente la ejecución del método.
  2. Finaliza inmediatamente la ejecución del método y devuelve algún valor como resultado del método.
La sintaxis para ambas formas es:
return;
return value; // где value — некоторое возвращаемое significado
Los métodos que devuelven un valor deben tener al menos un operador returncon un valor de retorno cuya llamada esté garantizada y no deben tener un operador returnsin un valor de retorno. Veamos los ejemplos a continuación:
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;
    }
}
En los métodos que no devuelven un valor (métodos void), es aceptable, pero no obligatorio, tener al menos una declaración returnsin valor de retorno y ni una sola declaración returncon valor de retorno. Veamos esto con los siguientes ejemplos:
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);
}

// Метод выведет в консоль наибольшее significado из массива
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);
}

etiquetas

Antes de ver los operadores breaky continue, me gustaría hablar sobre las etiquetas en Java. Esto es importante porque, en algunas situaciones, los operadores breaky continuese utilizan junto con etiquetas. Pero primero, intente responder la pregunta de si este código se compilará:
public static void main(String[] args) {
    https://www.google.com/
    System.out.println("Interesting...");
}
Una etiqueta es un fragmento de código con nombre. La etiqueta en sí no proporciona ninguna funcionalidad. Esto es algo así como un marcador en el código que el programador pretende utilizar más adelante. Una etiqueta en el código se define de forma muy sencilla: mediante un nombre y dos puntos. Por ejemplo:
  • labelName:
  • outerLoop:
  • printing:
  • anyWordYouLike:
Y así es como se ven las etiquetas dentro del código 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();
    }
}
La salida del método mainserá la siguiente:
Таблица Умножения
   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
En el ejemplo anterior definePrintName, loop1:y loop2:son etiquetas. loop1:y loop2:"marcar" dos ciclos: externo e interno. Veremos el uso de etiquetas en la sección siguiente. Mientras tanto, si respondió "no" a la pregunta de si este código se compilará:
public static void main(String[] args) {
      https://www.google.com/
      System.out.println("Interesting...");
  }
Intente responder nuevamente, usando el IDE.

romper

El operador breakse utiliza en dos casos:
  1. Para completar cualquier rama de ejecución en un bloque switch-case.
  2. Interrumpir la ejecución de un bucle.
El operador tiene dos formas: con marcas (etiqueta) y sin. La sintaxis para ambas formas es:
break labelName; // Синтаксис оператора с меткой
break; // Синтаксис оператора без метки
En bloques de caja de interruptores, el operador breakse utiliza sin etiquetas:
public static void main(String[] args) {
    int dayOfWeekInt = 4;
    String dayOfWeek;
    switch (dayOfWeekInt) {
        case 1:
            dayOfWeek = "Lunes";
            break;
        case 2:
            dayOfWeek = "Martes";
            break;
        case 3:
            dayOfWeek = "Miércoles";
            break;
        case 4:
            dayOfWeek = "Jueves";
            break;
        case 5:
            dayOfWeek = "Viernes";
            break;
        case 6:
            dayOfWeek = "Sábado";
            break;
        case 7:
            dayOfWeek = "Domingo";
            break;
        default:
            dayOfWeek = "Неизвестный день";
            break;
    }

    System.out.println("Сегодня " + dayOfWeek);
}
En los bucles, breakse utiliza una declaración para interrumpir iteraciones adicionales después de que se cumplan ciertas condiciones. Esto a menudo se puede encontrar cuando necesita iterar a través de una matriz o colección de elementos y encontrar algún elemento que satisfaga las condiciones necesarias. Consideremos este ejemplo. Tenemos una matriz y necesitamos determinar si la matriz contiene elementos negativos:
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, потому что
        мы выяснo то, что нас интересовало,
        и дальнейший перебор элементов не имеет смысла.
        */
       arrayHasNegativeElements = true;
       break;
   }
}
Veamos el mismo ejemplo con diferentes bucles. Ciclo 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;
        }
    }
}
Ciclo 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 ++;
    }
}
Ciclo 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);
}
Otro ejemplo de declaración breaken bucles es interrumpir un bucle infinito cuando se cumplen ciertas condiciones. A continuación se muestra un ejemplo de un programa que muestra la línea ingresada por el usuario hasta que el usuario ingresa la palabra "detener":
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);
    }
}
Consideremos usar el operador breakjunto con una etiqueta. Una interrupción con etiqueta se utiliza en casos con varios ciclos, además, anidados uno dentro del otro. En este caso, uno de los ciclos (o todos los ciclos) está marcado con una etiqueta. A continuación, el operador, breakjunto con la indicación de la etiqueta, interrumpe el ciclo deseado. Consideremos un ejemplo en el que necesitamos entender si hay un elemento negativo, pero no en la matriz, sino en la matriz:
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;
               }
           }
       }
}

continuidad

El operador continuetambién tiene dos formularios, con y sin etiqueta:
continue; // форма оператора без метки
continue labelName; // форма оператора с меткой
A diferencia del operador break, que interrumpe todas las iteraciones restantes del ciclo, el operador continueinterrumpe la iteración actual y hace que comience la siguiente. Operadores de salto en Java - 2Esto puede resultar útil si necesita realizar algunas operaciones en elementos que satisfacen determinadas condiciones. Digamos que tenemos una cadena y queremos contar el número de palabras que comienzan con la letra "m":
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);
}
Después de ejecutar este código, aparecerá el siguiente resultado en la consola:
Кол-во слов, начинающихся с буквы М в предложении: [Мама мыла раму] = 2
El operador continuejunto con la etiqueta también se utiliza al iterar sobre elementos. Imaginemos una matriz en la que necesitamos contar el número de filas con elementos negativos:
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);
}
La salida de este código será:
Rows With Negative Elements Count = 3
Vale decir que los operadores break, continuey returnpueden usarse de diferentes formas para lograr la misma funcionalidad. Entonces, puedes reescribir el último ejemplo y continueusar 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);
}
La diferencia entre breaky continuecon una etiqueta es lo que breakcompleta las iteraciones del bucle en el que está escrita. Y continuecon una etiqueta, omite la iteración actual del ciclo marcado con la etiqueta. En algunas situaciones, puedes reemplazar uno por otro y todo en la funcionalidad del programa seguirá siendo el mismo. Hablaremos sobre qué es mejor elegir (spoiler: legibilidad del código) a continuación. El operador breakse puede sustituir no sólo por continueuna etiqueta, sino también por return. Justo antes de esto necesitas mover el bucle anidado a un método separado:
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;
}
Muchas maneras de escribir lo mismo. ¿Cuál elegir? En programación industrial, esta cuestión la decide la facilidad de comprensión del código. Cuanto más sencillo esté escrito, mejor. Cuantos más bucles anidados, más difícil será percibir el código. Especialmente si los bucles están marcados con etiquetas diferentes, que se utilizan en interrupciones y continuaciones ( breaky continue). Si es posible no utilizar etiquetas, es mejor hacerlo. De lo contrario, intenta escribir de la manera más clara y bella posible.

ir a

En algunos lenguajes de programación existe un operador goto. Normalmente redirige la ejecución del código a alguna parte del programa marcada con una etiqueta. Pero en Java goto, se podría decir, lo es y no lo es. Vamos a resolverlo. La lista de palabras clave en Java incluye la palabra goto. Sin embargo, esta declaración está marcada como no utilizada. El caso es que James Gosling, el creador del lenguaje Java, inicialmente incluyó soporte para el operador en la JVM goto. Sin embargo, esta característica fue eliminada posteriormente. Una de las razones es que los bloques de código que contienen el operador gotono eran tan legibles como los bloques de código que realizaban las mismas funciones pero sin goto, pero con enfoques alternativos ( break,, continuecolocando el bloque de código en métodos). De hecho, hubo otros, como:
  • dificultad para leer y comprender el código que contiene operadores goto;
  • complicar la optimización del código para el compilador (y a veces incluso imposible);
  • aumentando la probabilidad de crear errores sutiles en el código.
Para muchos no es un secreto que en algunos lenguajes de programación el operador gotofunciona con bastante éxito. Sin embargo, los programadores evitan usarlo. Puede leer sobre las razones de esto en un artículo sobre Habré . Pero ¿por qué entonces dejarla gotoen la lista de palabras reservadas? Es simple: para el futuro. Si, por ejemplo, se llaman variables, métodos o clases en el código Java de los desarrolladores de todo el mundo goto, si esta declaración se devuelve en una versión futura de Java, todo el código antiguo se romperá. Para evitar tal escenario, gotopermanece en la lista de palabras clave de Java, pero no tiene ninguna funcionalidad. Quizás algún día gotoregrese a nuestras filas, pero la probabilidad de que esto ocurra es baja.

Resultados

Hemos analizado varios operadores de salto en Java:
  1. return— finalización del método, devolviendo un valor del método.
    • con valor de retorno: métodos que devuelven valores;
    • sin valor de retorno: voidmétodos.
  2. break— interrupción de ciclos, bloques de cajas de interruptores.
    • con etiquetas: ciclos de varios anidamientos;
    • sin etiquetas: ramas de caja de interruptores del bloque; interrumpiendo el bucle en el que fue llamado.
  3. continue.
    • con etiquetas: ciclos de varios anidamientos;
    • sin etiquetas: continuación del bucle en el que fue llamado.
  4. goto.
    • está en la lista de palabras clave, pero no se utiliza.
La conclusión de todo esto es simple: es mejor dar preferencia a los enfoques más simples que faciliten la lectura del código. Trate de no sobrecargar su código con bucles de varios niveles anidados unos dentro de otros con abundantes marcas, interrupciones y continuaciones.
Comentarios
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION