JavaRush /Blog Java /Random-VI /Toán tử nhảy trong Java

Toán tử nhảy trong Java

Xuất bản trong nhóm
Xin chào! Hôm nay chúng ta sẽ nói về các toán tử nhảy trong Java:
  • return
  • break
  • continue
  • goto
Đầu tiên, hãy xác định nó thực sự là gì. Như bạn đã biết, trong tình huống bình thường, một chương trình được thực thi tuyến tính - từ trên xuống dưới, từng lệnh một. Luồng tuyến tính của chương trình có thể được thay đổi bằng cái gọi là cấu trúc điều khiển: ví dụ: nhánh ( if) và vòng lặp ( for, whilev.v.). Ngoài các cấu trúc điều khiển, việc thực thi tuyến tính của một chương trình có thể được sửa đổi bằng các câu lệnh nhảy. Họ chịu trách nhiệm chuyển hướng thực thi chương trình đến một vị trí cụ thể, điều này phụ thuộc vào ngữ cảnh và câu lệnh cụ thể. Toán tử nhảy trong Java - 1Chúng ta hãy xem xét kỹ hơn về từng nhà khai thác trong số bốn nhà khai thác.

trở lại

Toán tử này là điều mà những người mới sử dụng thường làm quen đầu tiên. Câu lệnh returnkết thúc phương thức mà nó được gọi và việc thực thi chương trình sẽ quay trở lại vị trí mà phương thức đó được gọi. Nó returncó hai hình thức:
  1. Ngay lập tức kết thúc việc thực hiện phương thức.
  2. Ngay lập tức kết thúc việc thực thi phương thức và trả về một số giá trị làm kết quả của phương thức.
Cú pháp cho cả hai biểu mẫu là:
return;
return value; // где value — некоторое возвращаемое meaning
Các phương thức trả về một giá trị phải có ít nhất một toán tử returncó giá trị trả về được đảm bảo được gọi và không được có toán tử returnkhông có giá trị trả về. Hãy xem các ví dụ dưới đây:
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;
    }
}
Trong các phương thức không trả về một giá trị (các phương thức void), có thể chấp nhận, nhưng không bắt buộc, phải có ít nhất một câu lệnh returnkhông có giá trị trả về và không một câu lệnh nào returncó giá trị trả về. Chúng ta hãy xem xét điều này với các ví dụ dưới đây:
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);
}

nhãn

Trước khi xem xét các toán tử breakcontinue, tôi muốn nói về các nhãn trong Java. Điều này rất quan trọng vì trong một số trường hợp, toán tử breakcontinueđược sử dụng cùng với nhãn. Nhưng trước tiên, hãy thử trả lời câu hỏi liệu mã này có được biên dịch hay không:
public static void main(String[] args) {
    https://www.google.com/
    System.out.println("Interesting...");
}
Nhãn là một đoạn mã được đặt tên. Bản thân nhãn không cung cấp bất kỳ chức năng nào. Đây giống như một dấu trang trong đoạn mã mà lập trình viên dự định sử dụng sau này. Nhãn trong mã được xác định khá đơn giản - thông qua tên và dấu hai chấm. Ví dụ:
  • labelName:
  • outerLoop:
  • printing:
  • anyWordYouLike:
Và đây là hình dạng của các nhãn bên trong mã 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();
    }
}
Đầu ra của phương thức mainsẽ như sau:
Таблица Умножения
   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
Trong ví dụ trên definePrintName, loop1:loop2:là nhãn. loop1:loop2:“đánh dấu” hai chu kỳ - bên ngoài và bên trong. Chúng ta sẽ xem xét việc sử dụng nhãn trong phần bên dưới. Trong thời gian chờ đợi, nếu bạn trả lời “không” cho câu hỏi liệu mã này có được biên dịch hay không:
public static void main(String[] args) {
      https://www.google.com/
      System.out.println("Interesting...");
  }
Hãy thử trả lời lại bằng cách sử dụng IDE.

phá vỡ

Toán tử breakđược sử dụng trong hai trường hợp:
  1. Để hoàn thành bất kỳ nhánh thực thi nào trong khối switch-case.
  2. Để làm gián đoạn việc thực hiện một vòng lặp.
Toán tử có hai dạng: có dấu (nhãn) và không có. Cú pháp cho cả hai biểu mẫu là:
break labelName; // Синтаксис оператора с меткой
break; // Синтаксис оператора без метки
Trong khối switch-case, toán tử breakđược sử dụng không có nhãn:
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);
}
Trong vòng lặp, một câu lệnh breakđược sử dụng để ngắt các lần lặp tiếp theo sau khi đáp ứng một số điều kiện nhất định. Điều này thường có thể được tìm thấy khi bạn cần lặp qua một mảng hoặc tập hợp các phần tử và tìm một số phần tử trong đó thỏa mãn các điều kiện cần thiết. Hãy xem xét ví dụ này. Chúng ta có một mảng và cần xác định xem mảng đó có chứa các phần tử âm hay không:
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;
   }
}
Hãy xem cùng một ví dụ với các vòng lặp khác nhau. Xe đạp 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;
        }
    }
}
Xe đạp 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 ++;
    }
}
Xe đạp 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);
}
Một ví dụ khác về câu lệnh breaktrong vòng lặp là ngắt vòng lặp vô hạn khi đạt đến một số điều kiện nhất định. Dưới đây là ví dụ về chương trình hiển thị dòng do người dùng nhập cho đến khi người dùng nhập từ “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);
    }
}
Hãy xem xét việc sử dụng toán tử breakcùng với nhãn. Một ngắt có nhãn được sử dụng trong trường hợp có nhiều chu trình, hơn nữa, chu trình này được lồng vào chu trình kia. Trong trường hợp này, một trong các chu kỳ (hoặc tất cả các chu kỳ) được đánh dấu bằng nhãn. Tiếp theo, toán tử breakcùng với việc chỉ ra nhãn sẽ làm gián đoạn chu trình mong muốn. Hãy xem xét một ví dụ trong đó chúng ta cần hiểu liệu có phần tử âm, nhưng không phải trong mảng mà trong ma trận:
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;
               }
           }
       }
}

liên tục

Toán tử continuecũng có hai dạng - có và không có nhãn:
continue; // форма оператора без метки
continue labelName; // форма оператора с меткой
Không giống như toán tử break, làm gián đoạn tất cả các lần lặp còn lại của vòng lặp, toán tử continuengắt lần lặp hiện tại và khiến lần lặp tiếp theo bắt đầu. Toán tử nhảy trong Java - 2Điều này có thể hữu ích nếu bạn cần thực hiện một số thao tác trên các phần tử thỏa mãn các điều kiện nhất định. Giả sử chúng ta có một chuỗi và muốn đếm số từ bắt đầu bằng chữ "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);
}
Sau khi thực thi mã này, sẽ có kết quả sau trong bảng điều khiển:
Кол-во слов, начинающихся с буквы М в предложении: [Мама мыла раму] = 2
Toán tử continuecùng với nhãn cũng được sử dụng khi lặp qua các phần tử. Hãy tưởng tượng một ma trận trong đó chúng ta cần đếm số hàng có phần tử âm:
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);
}
Đầu ra của mã này sẽ là:
Rows With Negative Elements Count = 3
Điều đáng nói là các toán tử có breakthể được sử dụng theo nhiều cách khác nhau để đạt được cùng một chức năng. Vì vậy, bạn có thể viết lại ví dụ cuối cùng và sử dụng : continuereturncontinuebreak
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);
}
Sự khác biệt giữa breakcontinuevới một nhãn là những gì breakhoàn thành các lần lặp của vòng lặp mà nó được viết. Và continuevới nhãn, bỏ qua vòng lặp hiện tại của chu trình được đánh dấu bằng nhãn. Trong một số trường hợp, bạn có thể thay thế cái này bằng cái kia và mọi thứ trong chức năng của chương trình sẽ vẫn như cũ. Chúng tôi sẽ nói về những gì tốt nhất để chọn (spoiler: khả năng đọc mã) bên dưới. Toán tử breakcó thể được thay thế không chỉ bằng continuenhãn mà còn bằng return. Ngay trước đó, bạn cần di chuyển vòng lặp lồng nhau sang một phương thức riêng:
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;
}
Có rất nhiều cách để viết cùng một điều. Chọn cái nào? Trong lập trình công nghiệp, vấn đề này được quyết định bởi tính dễ hiểu của mã. Nó được viết càng đơn giản thì càng tốt. Càng có nhiều vòng lặp lồng nhau thì càng khó nhận biết mã. Đặc biệt nếu các vòng lặp được đánh dấu bằng các dấu khác nhau, được sử dụng trong các phần ngắt và phần tiếp theo ( breakcontinue). Nếu có thể không sử dụng thẻ thì tốt hơn nên làm như vậy. Nếu không, hãy cố gắng viết rõ ràng và đẹp mắt nhất có thể.

đi đến

Trong một số ngôn ngữ lập trình có toán tử goto. Thông thường, nó chuyển hướng thực thi mã đến một phần nào đó của chương trình được đánh dấu bằng nhãn. Nhưng trong Java goto, người ta có thể nói, có và không. Hãy tìm ra nó. Danh sách từ khóa trong Java bao gồm từ goto. Tuy nhiên, tuyên bố này được đánh dấu là không được sử dụng. Thực tế là James Gosling, người tạo ra ngôn ngữ Java, ban đầu đã đưa vào hỗ trợ toán tử trong JVM goto. Tuy nhiên, tính năng này sau đó đã bị loại bỏ. Một trong những lý do là các khối mã chứa toán tử gotokhông thể đọc được bằng các khối mã thực hiện các chức năng tương tự nhưng không có goto, nhưng với các cách tiếp cận thay thế ( break, continue, đặt khối mã trong các phương thức). Trên thực tế, có những thứ khác, chẳng hạn như:
  • khó đọc và hiểu mã có chứa các toán tử goto;
  • làm phức tạp việc tối ưu hóa mã cho trình biên dịch (và đôi khi thậm chí là không thể);
  • tăng khả năng tạo ra các lỗi tinh vi trong mã.
Nhiều người không có gì bí mật khi trong một số ngôn ngữ lập trình, toán tử gotohoạt động khá thành công. Tuy nhiên, các lập trình viên tránh sử dụng nó. Bạn có thể đọc về lý do của điều này trong một bài viết trên Habré . Nhưng tại sao sau đó lại để nó gototrong danh sách các từ dành riêng? Thật đơn giản: cho tương lai. Ví dụ: nếu các biến, phương thức hoặc lớp được gọi trong mã Java của các nhà phát triển trên toàn thế giới goto, nếu câu lệnh này được trả về trong phiên bản Java trong tương lai thì tất cả mã cũ sẽ bị hỏng. Để tránh tình huống như vậy, gotonó vẫn nằm trong danh sách từ khóa Java nhưng không mang bất kỳ chức năng nào. Có lẽ một ngày nào đó gotoanh ấy sẽ trở lại hàng ngũ của chúng tôi, nhưng khả năng này rất thấp.

Kết quả

Chúng tôi đã xem xét các toán tử nhảy khác nhau trong Java:
  1. return- hoàn thành phương thức, trả về một giá trị từ phương thức.
    • có giá trị trả về: các phương thức trả về giá trị;
    • không có giá trị trả về: voidphương thức.
  2. break- gián đoạn các chu kỳ, khối chuyển mạch.
    • với các thẻ: chu kỳ lồng nhau khác nhau;
    • không có nhãn: các nhánh switch-case của khối; làm gián đoạn vòng lặp mà nó được gọi.
  3. continue.
    • với các thẻ: chu kỳ lồng nhau khác nhau;
    • không có nhãn: tiếp tục vòng lặp mà nó được gọi.
  4. goto.
    • có trong danh sách từ khóa nhưng không được sử dụng.
Kết luận từ tất cả những điều này rất đơn giản: tốt hơn là nên ưu tiên những cách tiếp cận đơn giản nhất giúp mã dễ đọc hơn. Cố gắng không làm quá tải mã của bạn với các vòng lặp đa cấp được lồng vào nhau với vô số dấu hiệu, sự gián đoạn và sự tiếp tục.
Bình luận
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION