JavaRush /จาวาบล็อก /Random-TH /ตัวดำเนินการกระโดดใน Java

ตัวดำเนินการกระโดดใน Java

เผยแพร่ในกลุ่ม
สวัสดี! วันนี้เราจะพูดถึง Jump Operators ใน Java:
  • return
  • break
  • continue
  • goto
ก่อนอื่น เรามานิยามกันก่อนว่าแท้จริงแล้วคืออะไร ดังที่คุณทราบ ในสถานการณ์ปกติ โปรแกรมจะดำเนินการเชิงเส้น - จากบนลงล่าง คำสั่งตามคำสั่ง การไหลเชิงเส้นของโปรแกรมสามารถเปลี่ยนแปลงได้โดยสิ่งที่เรียกว่าโครงสร้างการควบคุม: ตัวอย่างเช่น สาขา ( if) และลูป ( for, whileฯลฯ) นอกเหนือจากโครงสร้างการควบคุมแล้ว การดำเนินการเชิงเส้นของโปรแกรมยังสามารถแก้ไขได้ด้วยคำสั่ง Jump พวกเขามีหน้าที่รับผิดชอบในการเปลี่ยนเส้นทางการทำงานของโปรแกรมไปยังตำแหน่งเฉพาะ ซึ่งขึ้นอยู่กับบริบทและคำสั่งเฉพาะ ตัวดำเนินการกระโดดใน 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
ในตัวอย่างข้างต้นdefinePrintNameและloop1:เป็นloop2:ป้ายกำกับ loop1:และloop2:"ทำเครื่องหมาย" สองรอบ - ภายนอกและภายใน เราจะดูการใช้ป้ายกำกับในส่วนด้านล่าง ในระหว่างนี้ หากคุณตอบว่า "ไม่" สำหรับคำถามว่าโค้ดนี้จะคอมไพล์หรือไม่:
public static void main(String[] args) {
      https://www.google.com/
      System.out.println("Interesting...");
  }
ลองตอบอีกครั้งโดยใช้ IDE

หยุดพัก

โอเปอเรเตอร์breakถูกใช้ในสองกรณี:
  1. เพื่อดำเนินการสาขาการดำเนินการใด ๆ ในบล็อกสวิตช์เคสให้เสร็จสมบูรณ์
  2. เพื่อขัดจังหวะการทำงานของลูป
ตัวดำเนินการมีสองรูปแบบ: มีเครื่องหมาย (ฉลาก) และไม่มี ไวยากรณ์สำหรับทั้งสองรูปแบบคือ:
break labelName; // Синтаксис оператора с меткой
break; // Синтаксис оператора без метки
ในบล็อกสวิตช์เคส ตัวดำเนินbreakการจะใช้โดยไม่มีป้ายกำกับ:
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แบบวนซ้ำคือการขัดจังหวะการวนซ้ำไม่สิ้นสุดเมื่อตรงตามเงื่อนไขบางประการ นี่คือตัวอย่างโปรแกรมที่แสดงบรรทัดที่ผู้ใช้ป้อนจนกระทั่งผู้ใช้ป้อนคำว่า "หยุด":
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และสามารถนำมาใช้ในรูปแบบต่างๆเพื่อให้บรรลุฟังก์ชันการทำงาน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เตอร์ โดยทั่วไปแล้วจะเปลี่ยนเส้นทางการเรียกใช้โค้ดไปยังบางส่วนของโปรแกรมที่มีป้ายกำกับ แต่ใน Java gotoอาจมีคนบอกว่ามันเป็นและไม่ใช่ ลองคิดดูสิ รายการคำหลักใน Javaมีคำว่าgoto. อย่างไรก็ตาม ข้อความนี้ถูกทำเครื่องหมายว่าไม่ได้ใช้ ความจริงก็คือ James Gosling ผู้สร้างภาษา Java ได้รวมการสนับสนุนโอเปอเรเตอร์ไว้ใน JVM ในตอนgotoแรก อย่างไรก็ตาม คุณลักษณะนี้ถูกตัดออกในภายหลัง สาเหตุหนึ่งก็คือ บล็อกของโค้ดที่มีโอเปอเรเตอร์gotoไม่สามารถอ่านได้เท่ากับบล็อกของโค้ดที่ทำหน้าที่เดียวกันแต่ไม่มีgotoแต่ใช้วิธีอื่น ( break, continueวางบล็อกโค้ดในวิธีการ) จริงๆ แล้วยังมีคนอื่นๆ อีก เช่น:
  • gotoความยากในการอ่านและทำความ เข้าใจโค้ดที่มีตัวดำเนินการ
  • การเพิ่มประสิทธิภาพโค้ดที่ซับซ้อนสำหรับคอมไพเลอร์ (และบางครั้งก็เป็นไปไม่ได้ด้วยซ้ำ)
  • เพิ่มโอกาสในการสร้างข้อผิดพลาดเล็กๆ น้อยๆ ในโค้ด
ไม่มีความลับสำหรับหลาย ๆ คนว่าในภาษาการเขียนโปรแกรมบางภาษาตัวดำเนินgotoการทำงานได้ค่อนข้างสำเร็จ อย่างไรก็ตามโปรแกรมเมอร์หลีกเลี่ยงการใช้มัน คุณสามารถอ่านเหตุผลนี้ได้ในบทความเรื่องHabré แต่ทำไมถึงทิ้งมันไว้gotoในรายการคำสงวนล่ะ? ง่ายมาก: เพื่ออนาคต ตัวอย่างเช่น หากตัวแปร วิธีการ หรือคลาสถูกเรียก ในโค้ด Java ของนักพัฒนาทั่วโลกgotoหากคำสั่งนี้ถูกส่งคืนใน Java เวอร์ชันอนาคต โค้ดเก่าทั้งหมดจะใช้งานไม่ได้ เพื่อหลีกเลี่ยงสถานการณ์ดังกล่าว คำสำคัญgotoจะยังคงอยู่ในรายการคีย์เวิร์ด Java แต่ไม่มีฟังก์ชันการทำงานใดๆ บางทีสักวันหนึ่งgotoเขาจะกลับคืนสู่ตำแหน่งของเรา แต่โอกาสที่จะเป็นเช่นนั้นมีน้อย

ผลลัพธ์

เราได้ดูตัวดำเนินการกระโดดต่างๆ ใน ​​Java:
  1. return— เสร็จสิ้นเมธอด โดยส่งคืนค่าจากเมธอด
    • ด้วยค่าส่งคืน: วิธีการที่ส่งคืนค่า;
    • ไม่มีค่าส่งคืน: voidวิธีการ
  2. break- การหยุดชะงักของวงจร บล็อกเคสสวิตช์
    • มีแท็ก: วงจรของการทำรังต่างๆ
    • ไม่มีป้ายกำกับ: สาขาสวิตช์เคสของบล็อก; ขัดจังหวะวงที่มันถูกเรียก
  3. continue.
    • มีแท็ก: วงจรของการทำรังต่างๆ
    • ไม่มีป้ายกำกับ: ความต่อเนื่องของลูปที่ถูกเรียก
  4. goto.
    • อยู่ในรายการคำหลัก แต่ไม่ได้ใช้
ข้อสรุปจากทั้งหมดนี้เป็นเรื่องง่าย: ควรเลือกใช้วิธีที่ง่ายที่สุดที่ทำให้อ่านโค้ดได้ง่ายขึ้น พยายามอย่าโหลดโค้ดของคุณมากเกินไปด้วยการวนซ้ำหลายระดับซ้อนกันโดยมีเครื่องหมาย การขัดจังหวะ และต่อเนื่องกันมากมาย
ความคิดเห็น
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION