CodeGym /Blog Java /Random-PL /Porozmawiajmy o końcowym słowie kluczowym w Javie
Autor
Oleksandr Miadelets
Head of Developers Team at CodeGym

Porozmawiajmy o końcowym słowie kluczowym w Javie

Opublikowano w grupie Random-PL
Java ma to słowo kluczowe — final. Można go zastosować do klas, metod, zmiennych (w tym parametrów metod). Dla klasy słowo kluczowe final oznacza, że ​​klasa nie może mieć podklas, tzn. dziedziczenie jest zabronione... Jest to przydatne przy tworzeniu niezmiennych (niezmiennych) obiektów. Na przykład klasa String jest zadeklarowana jako ostateczna.

    public final class String {
    }
    
    class SubString extends String { // Compilation error
    }
Powinienem również zauważyć, że modyfikator final nie może być zastosowany do klas abstrakcyjnych (tych ze słowem kluczowym abstract), ponieważ są to pojęcia wzajemnie się wykluczające. W przypadku ostatecznej metody modyfikator oznacza, że ​​metody nie można przesłonić w podklasach. Jest to przydatne, gdy chcemy zapobiec zmianie oryginalnej implementacji.

    public class SuperClass {
        public final void printReport() {
            System.out.println("Report");
        }
    }

    class SubClass extends SuperClass { 
        public void printReport() { //Compilation error
            System.out.println("MyReport");
        }
    }
W przypadku zmiennych typu pierwotnego słowo kluczowe final oznacza, że ​​raz przypisanej wartości nie można zmienić. W przypadku zmiennych referencyjnych oznacza to, że po przypisaniu obiektu nie można zmienić odniesienia do tego obiektu. To jest ważne! Nie można zmienić odniesienia, ale można zmienić stan obiektu. Java 8 wprowadziła nową koncepcję: faktycznie ostateczną. Dotyczy to tylko zmiennych (w tym parametrów metod). Najważniejsze jest to, że pomimo wyraźnego braku słowa kluczowego final wartość zmiennej nie zmienia się po inicjalizacji. Innymi słowy, słowo kluczowe final można zastosować do takiej zmiennej bez błędu kompilacji. Efektywnie ostateczne zmienne mogą być używane w klasach lokalnych (lokalne klasy wewnętrzne), klasach anonimowych (anonimowe klasy wewnętrzne) i strumieniach (Stream API).

        public void someMethod() {
            // In the example below, both a and b are effectively final, since they are assigned values only once:
            int a = 1;
            int b;
            if (a == 2) b = 3;
            else b = 4;
            // c is NOT effectively final since its value changes
            int c = 10;
            c++;
            
            Stream.of(1, 2).forEach(s-> System.out.println(s + a)); // OK
            Stream.of(1, 2).forEach(s-> System.out.println(s + c)); // Compilation error
        }
A teraz zróbmy mały wywiad. W końcu celem ukończenia kursu CodeGym jest zostanie programistą Java oraz znalezienie ciekawej i dobrze płatnej pracy. Zacznijmy więc.
  1. Co możemy powiedzieć o tablicy, która została zadeklarowana jako ostateczna?

  2. Wiemy, że klasa String jest niezmienna: klasa jest deklarowana jako ostateczna. Wartość ciągu jest przechowywana w tablicy char, która jest oznaczona słowem kluczowym final.


public final class String
    implements java.io.Serializable, Comparable<String>, CharSequence {
    /** The value is used for character storage. */
    private final char value[];

Czy możemy zastąpić wartość obiektu String (bez zmiany odniesienia do obiektu)? To są prawdziwe pytania do wywiadu. A praktyka pokazuje, że wielu kandydatów nie odpowiada na nie poprawnie. Bardzo ważne jest zrozumienie, w jaki sposób używane jest słowo kluczowe final, zwłaszcza w przypadku zmiennych referencyjnych. Gdy będziesz to rozważać, złożę małą prośbę do zespołu CodeGym. Daj edytorowi tekstu możliwość dodania bloku, którego zawartość można pokazać/ukryć po kliknięciu. Odpowiedzi:
  1. Tablica jest obiektem, więc słowo kluczowe final oznacza, że ​​po przypisaniu odwołania do tablicy nie można go zmienić. To powiedziawszy, możesz zmienić stan obiektu.

    
            final int[] array = {1, 2, 3, 4, 5};
            array[0] = 9;	 // OK, because we're changing the contents of the array: {9, 2, 3, 4, 5}
            array = new int[5]; // Compilation error
    
  2. Tak możemy. Najważniejsze jest, aby zrozumieć, co oznacza cierniste słowo kluczowe final, gdy jest używane z przedmiotami. Interfejs API Reflection może służyć do zastępowania wartości.


import java.lang.reflect.Field;

class B {
    public static void main(String[] args) throws Exception {
        String value = "Old value";
        System.out.println(value);

        // Get the String class's value field
        Field field = value.getClass().getDeclaredField("value");
        // Make it mutable
        field.setAccessible(true);
        // Set a new value
        field.set(value, "CodeGym".toCharArray());

        System.out.println(value);

        /* Output:
         * Old value
         * CodeGym
         */
    }
}
Zauważ, że gdybyśmy próbowali zmienić w ten sposób zmienną final typu pierwotnego, nic by się nie stało. Proponuję samemu się przekonać: stworzyć klasę Javy np. z końcowym polem int i spróbować zmienić jej wartość za pomocą Reflection API. Powodzenia wszystkim!
Komentarze
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION