JavaRush /Blog Java /Random-PL /Operatory skoków w Javie

Operatory skoków w Javie

Opublikowano w grupie Random-PL
Cześć! Dzisiaj porozmawiamy o operatorach skoków w Javie:
  • return
  • break
  • continue
  • goto
Najpierw zdefiniujmy, czym właściwie jest. Jak wiadomo, w normalnej sytuacji program wykonywany jest liniowo – od góry do dołu, polecenie po poleceniu. Liniowy przebieg programu można zmieniać za pomocą tak zwanych struktur kontrolnych: na przykład gałęzi ( if) i pętli ( foritd while.). Oprócz konstrukcji sterujących liniowe wykonanie programu można modyfikować za pomocą instrukcji skoku. Odpowiadają za przekierowanie wykonania programu do określonej lokalizacji, która zależy od kontekstu i konkretnej instrukcji. Operatory skoków w Javie — 1Przyjrzyjmy się bliżej każdemu z czterech operatorów.

powrót

To właśnie z tym operatorem nowicjusze najczęściej zapoznają się jako pierwsi. Instrukcja returnkończy metodę, w której została wywołana, a wykonanie programu powraca do lokalizacji, z której metoda została wywołana. Ma returndwie formy:
  1. Natychmiast kończy wykonanie metody.
  2. Natychmiast kończy wykonanie metody i zwraca jakąś wartość jako wynik metody.
Składnia obu form jest następująca:
return;
return value; // где value — некоторое возвращаемое oznaczający
Metody zwracające wartość muszą mieć co najmniej jeden operator returnz gwarantowaną wartością zwracaną i nie mogą mieć operatora returnbez wartości zwracanej. Spójrzmy na poniższe przykłady:
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;
    }
}
W metodach, które nie zwracają wartości (metody void), dopuszczalne jest, ale nie wymagane, posiadanie co najmniej jednej instrukcji returnbez wartości zwracanej, a nie pojedynczej instrukcji returnz wartością zwracaną. Przyjrzyjmy się temu na poniższych przykładach:
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);
}

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

etykiety

Zanim przyjrzymy się operatorom breakand continue, chciałbym porozmawiać o etykietach w Javie. Jest to ważne, ponieważ w niektórych sytuacjach operatory breaki continuesą używane w połączeniu z etykietami. Ale najpierw spróbuj odpowiedzieć na pytanie, czy ten kod się skompiluje:
public static void main(String[] args) {
    https://www.google.com/
    System.out.println("Interesting...");
}
Etykieta to nazwany fragment kodu. Sama etykieta nie zapewnia żadnej funkcjonalności. To coś w rodzaju zakładki w kodzie, z której programista zamierza później skorzystać. Etykietę w kodzie definiuje się w prosty sposób – poprzez nazwę i dwukropek. Na przykład:
  • labelName:
  • outerLoop:
  • printing:
  • anyWordYouLike:
A tak wyglądają etykiety w kodzie 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();
    }
}
Wynik metody mainbędzie następujący:
Таблица Умножения
   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
W powyższym przykładzie definePrintNamei loop1:loop2:etykietami. loop1:i loop2:„zaznacz” dwa cykle - zewnętrzny i wewnętrzny. Przyjrzymy się używaniu etykiet w poniższej sekcji. W międzyczasie, jeśli odpowiedziałeś „nie” na pytanie, czy ten kod się skompiluje:
public static void main(String[] args) {
      https://www.google.com/
      System.out.println("Interesting...");
  }
Spróbuj odpowiedzieć ponownie, używając IDE.

przerwa

Operator breakjest używany w dwóch przypadkach:
  1. Aby zakończyć dowolną gałąź wykonania w bloku skrzynki przełącznika.
  2. Aby przerwać wykonywanie pętli.
Operator ma dwie formy: z oznaczeniami (etykietą) i bez. Składnia obu form jest następująca:
break labelName; // Синтаксис оператора с меткой
break; // Синтаксис оператора без метки
W blokach rozdzielnic operator breakużywany jest bez etykiet:
public static void main(String[] args) {
    int dayOfWeekInt = 4;
    String dayOfWeek;
    switch (dayOfWeekInt) {
        case 1:
            dayOfWeek = "Poniedziałek";
            break;
        case 2:
            dayOfWeek = "Wtorek";
            break;
        case 3:
            dayOfWeek = "Środa";
            break;
        case 4:
            dayOfWeek = "Czwartek";
            break;
        case 5:
            dayOfWeek = "Piątek";
            break;
        case 6:
            dayOfWeek = "Sobota";
            break;
        case 7:
            dayOfWeek = "Niedziela";
            break;
        default:
            dayOfWeek = "Неизвестный день";
            break;
    }

    System.out.println("Сегодня " + dayOfWeek);
}
W pętlach instrukcja breaksłuży do przerwania dalszych iteracji po spełnieniu określonych warunków. Często można to znaleźć, gdy trzeba iterować po tablicy lub zbiorze elementów i znaleźć w nich element spełniający niezbędne warunki. Rozważmy ten przykład. Mamy tablicę i musimy ustalić, czy tablica zawiera elementy ujemne:
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, потому что
        мы выяснLub то, что нас интересовало,
        и дальнейший перебор элементов не имеет смысла.
        */
       arrayHasNegativeElements = true;
       break;
   }
}
Spójrzmy na ten sam przykład z różnymi pętlami. Cykl 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;
        }
    }
}
Cykl 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 ++;
    }
}
Cykl 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);
}
Innym przykładem instrukcji breakw pętlach jest przerwanie nieskończonej pętli po spełnieniu określonych warunków. Oto przykład programu wyświetlającego linię wprowadzoną przez użytkownika do momentu wpisania przez niego słowa „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);
    }
}
Rozważmy użycie operatora breakrazem z etykietą. Przerwanie z etykietą stosuje się w przypadku kilku cykli, ponadto zagnieżdżonych jeden w drugim. W tym przypadku jeden z cykli (lub wszystkie cykle) jest oznaczony etykietą. Następnie operator breakwraz ze wskazaniem etykiety przerywa żądany cykl. Rozważmy przykład, w którym musimy zrozumieć, czy istnieje element ujemny, ale nie w tablicy, ale w macierzy:
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;
               }
           }
       }
}

ciągłość

Operator continuema również dwie formy - z etykietą i bez:
continue; // форма оператора без метки
continue labelName; // форма оператора с меткой
W przeciwieństwie do operatora break, który przerywa wszystkie pozostałe iteracje pętli, operator continueprzerywa bieżącą iterację i powoduje rozpoczęcie kolejnej. Operatory skoków w Javie — 2Może to być przydatne, jeśli trzeba wykonać pewne operacje na elementach spełniających określone warunki. Załóżmy, że mamy ciąg znaków i chcemy policzyć, ile słów zaczyna się na literę „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);
}
Po wykonaniu tego kodu w konsoli wyświetli się następujący komunikat:
Кол-во слов, начинающихся с буквы М в предложении: [Мама мыла раму] = 2
Operator continuewraz z etykietą jest również używany podczas iteracji po elementach. Wyobraźmy sobie macierz, w której musimy policzyć liczbę wierszy z elementami ujemnymi:
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);
}
Dane wyjściowe tego kodu będą następujące:
Rows With Negative Elements Count = 3
Warto powiedzieć, że operatory breaki można ich używać na różne sposoby, aby osiągnąć tę samą funkcjonalność continue. returnMożesz więc przepisać ostatni przykład i continueużyć 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);
}
Różnica między etykietą breaki continueetykietą polega na tym, co breakkończy iteracje pętli, w której jest zapisana. A continuew przypadku etykiety pomija bieżącą iterację cyklu oznaczonego etykietą. W niektórych sytuacjach możesz zastąpić jedno drugim, a wszystko w funkcjonalności programu pozostanie takie samo. O tym, co najlepiej wybrać (spoiler: czytelność kodu) porozmawiamy poniżej. Operator breakmożna zastąpić nie tylko continueetykietą, ale także return. Tuż przed tym musisz przenieść zagnieżdżoną pętlę do osobnej metody:
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;
}
Można pisać to samo na wiele sposobów. Który wybrać? W programowaniu przemysłowym o tej kwestii decyduje łatwość zrozumienia kodu. Im prościej jest napisane, tym lepiej. Im więcej zagnieżdżonych pętli, tym trudniej jest dostrzec kod. Zwłaszcza jeśli pętle są oznaczone różnymi znakami, które są używane w przerwaniach i kontynuacjach ( breaki continue). Jeśli można nie używać tagów, lepiej to zrobić. W przeciwnym razie staraj się pisać tak wyraźnie i pięknie, jak to możliwe.

iść do

W niektórych językach programowania występuje operator goto. Zwykle przekierowuje wykonanie kodu do części programu oznaczonej etykietą. Ale w Javie goto, można powiedzieć, jest i tak nie jest. Rozwiążmy to. Lista słów kluczowych w Javie zawiera słowo goto. Jednakże to stwierdzenie jest oznaczone jako nieużywane. Faktem jest, że James Gosling, twórca języka Java, początkowo zamieścił obsługę operatora w JVM goto. Jednak ta funkcja została później usunięta. Jednym z powodów jest to, że bloki kodu zawierające operator gotonie były tak czytelne jak bloki kodu wykonujące te same funkcje, ale bez goto, ale z alternatywnymi podejściami ( break, continue, umieszczanie bloku kodu w metodach). W rzeczywistości istniały inne, takie jak:
  • trudności w czytaniu i rozumieniu kodu zawierającego operatory goto;
  • skomplikowana optymalizacja kodu dla kompilatora (a czasami wręcz niemożliwa);
  • zwiększając prawdopodobieństwo powstania subtelnych błędów w kodzie.
Dla wielu nie jest tajemnicą, że w niektórych językach programowania operator gotodziała całkiem skutecznie. Jednak programiści unikają jego używania. O przyczynach tego można przeczytać w jednym artykule na temat Habré . Ale po co więc zostawiać to gotona liście słów zastrzeżonych? To proste: na przyszłość. Jeśli na przykład wywołane zostaną zmienne, metody lub klasy w kodzie Java programistów na całym świecie goto, jeśli ta instrukcja zostanie zwrócona w przyszłej wersji Java, cały stary kod ulegnie zniszczeniu. Aby uniknąć takiego scenariusza, gotopozostaje on na liście słów kluczowych Java, ale nie oferuje żadnej funkcjonalności. Być może kiedyś gotopowróci w nasze szeregi, jednak prawdopodobieństwo tego jest nikłe.

Wyniki

Przyjrzeliśmy się różnym operatorom skoków w Javie:
  1. return— zakończenie metody, zwrócenie wartości z metody.
    • z wartością zwracaną: metody zwracające wartości;
    • brak wartości zwracanej: voidmetody.
  2. break— przerwanie cykli, blokady rozdzielnic.
    • z tagami: cykle różnych zagnieżdżeń;
    • bez etykiet: gałęzie rozdzielcze bloku; przerywając pętlę, w której został wywołany.
  3. continue.
    • z tagami: cykle różnych zagnieżdżeń;
    • bez etykiet: kontynuacja pętli, w której została wywołana.
  4. goto.
    • znajduje się na liście słów kluczowych, ale nie jest używany.
Wniosek z tego wszystkiego jest prosty: lepiej jest preferować najprostsze podejścia, które ułatwiają czytanie kodu. Staraj się nie przeciążać kodu wielopoziomowymi pętlami zagnieżdżonymi jedna w drugiej, z dużą ilością znaczników, przerw i kontynuacji.
Komentarze
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION