JavaRush /Java 博客 /Random-ZH /Java 中的跳转运算符

Java 中的跳转运算符

已在 Random-ZH 群组中发布
你好!今天我们来谈谈Java中的跳转运算符:
  • return
  • break
  • continue
  • goto
首先,让我们定义它实际上是什么。如您所知,在正常情况下,程序是线性执行的 - 从上到下,逐条命令。程序的线性流程可以通过所谓的控制结构来改变:例如,分支(if)和循环(forwhile)。除了控制结构之外,程序的线性执行还可以通过跳转语句来修改。它们负责将程序执行重定向到特定位置,这取决于上下文和特定语句。Java 中的跳转运算符 - 1让我们仔细看看这四个运算符中的每一个。

返回

新人最先熟悉的就是这个操作符。该语句return终止调用它的方法,程序执行返回到调用该方法的位置。它return有两种形式:
  1. 立即结束该方法的执行。
  2. 立即结束该方法的执行并返回一些值作为该方法的结果。
两种形式的语法都是:
return;
return value; // где value — некоторое возвращаемое meaning
返回值的方法必须至少有一个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;
    }
}
在不返回值的方法 (methods 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);
}

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

标签

在讨论breakand运算符之前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
在上面的示例中definePrintNameloop1:loop2:是标签。 loop1:loop2:“标记”两个周期——外部和内部。我们将在下面的部分中讨论如何使用标签。同时,如果您对该代码是否可以编译的问题回答“否”:
public static void main(String[] args) {
      https://www.google.com/
      System.out.println("Interesting...");
  }
尝试使用 IDE 再次回答该问题。

休息

该运算符break在两种情况下使用:
  1. 完成 switch-case 块中的任何执行分支。
  2. 中断循环的执行。
操作符有两种形式:带标记(标签)和不带标记。两种形式的语法都是:
break labelName; // Синтаксис оператора с меткой
break; // Синтаксис оператора без метки
break在 switch-case 块中,使用的 运算符不带标签:
public static void main(String[] args) {
    int dayOfWeekInt = 4;
    String dayOfWeek;
    switch (dayOfWeekInt) {
        case 1:
            dayOfWeek = "Monday";
            break;
        case 2:
            dayOfWeek = "Tuesday";
            break;
        case 3:
            dayOfWeek = "Wednesday";
            break;
        case 4:
            dayOfWeek = "Thursday";
            break;
        case 5:
            dayOfWeek = "Friday";
            break;
        case 6:
            dayOfWeek = "Saturday";
            break;
        case 7:
            dayOfWeek = "Sunday";
            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, потому что
        мы выяснor то, что нас интересовало,
        и дальнейший перебор элементов не имеет смысла.
        */
       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;
               }
           }
       }
}

连续性

该运算符continue也有两种形式 - 带标签和不带标签:
continue; // форма оператора без метки
continue labelName; // форма оператора с меткой
与中断循环的所有剩余迭代的运算符不同break,运算符continue会中断当前迭代并导致下一次迭代开始。Java 中的跳转运算符 - 2如果您需要对满足某些条件的元素执行某些操作,这会很有用。假设我们有一个字符串,我们想要计算以字母“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);
}
执行此代码后,控制台中将输出以下内容:
Кол-во слов, начинающихся с буквы М в предложении: [Мама мыла раму] = 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,continuereturn可以通过不同的方式使用来实现相同的功能。因此,您可以重写最后一个示例并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;
}
写同样的东西有很多种方法。选择哪一个?在工业编程中,这个问题是由代码的难易程度决定的。写得越简单越好。嵌套循环越多,感知代码就越困难。特别是如果循环标有不同的标记,这些标记用于中断和继续(breakcontinue)。如果可以不使用标签,最好还是使用。否则,尽量写得清楚、漂亮。

在某些编程语言中存在运算符goto。通常,它将代码执行重定向到标有标签的程序的某个部分。但在 Java 中goto,有人可能会说,它是,但又不是。让我们弄清楚一下。 Java 中的关键字列表包括单词goto。然而,该语句被标记为未使用。事实上,Java 语言的创建者 James Gosling 最初在 JVM 中包含了对运算符的支持goto。不过这个功能后来被删掉了。原因之一是包含运算符的代码块的goto可读性不如执行相同功能但没有goto,但采用替代方法(breakcontinue,将代码块放置在方法中)的代码块。事实上,还有其他一些,例如:
  • 难以阅读和理解包含运算符的代码goto
  • 使编译器的代码优化变得复杂(有时甚至是不可能的);
  • 增加在代码中产生细微错误的可能性。
对于许多人来说,在某些编程语言中,运算符的goto功能相当成功,这已不是什么秘密。然而,程序员避免使用它。您可以在Habré 的一篇文章中了解其原因。但为什么要把它留goto在保留字列表中呢?很简单:为了未来。例如,如果在世界各地开发人员的 Java 代码中调用变量、方法或类goto,如果在 Java 的未来版本中返回此语句,则所有旧代码都将崩溃。为了避免这种情况,goto它保留在 Java 关键字列表中,但不带有任何功能。也许有一天goto他会回到我们的队伍中,但这种可能性很低。

结果

我们已经研究了 Java 中的各种跳转运算符:
  1. return— 方法完成,从方法返回一个值。
    • 有返回值:有返回值的方法;
    • 无返回值: void方法。
  2. break— 循环中断、开关盒块。
    • 带标签:各种嵌套的循环;
    • 没有标签:块的 switch-case 分支;中断调用它的循环。
  3. continue
    • 带标签:各种嵌套的循环;
    • 没有标签:调用它的循环的继续。
  4. goto
    • 位于关键字列表中,但未使用。
所有这一切的结论很简单:最好优先选择最简单的方法,使代码更易于阅读。尽量不要使用具有大量标记、中断和延续的相互嵌套的多层循环来使代码过载。
评论
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION