JavaRush /Blog Java /Random-PL /Ostateczne, stałe i niezmienne w Javie

Ostateczne, stałe i niezmienne w Javie

Opublikowano w grupie Random-PL
Cześć! Słowo „modyfikator” jest już Ci znane. Jako minimum natknąłeś się na modyfikatory dostępu (publiczne, prywatne) i modyfikator statyczny. Dzisiaj porozmawiamy o specjalnym modyfikatorze końcowym . Można powiedzieć, że „cementuje” te obszary naszego programu, w których potrzebujemy stałego, jednoznacznego i niezmiennego zachowania. Można go zastosować w trzech obszarach naszego programu: klasach, metodach i zmiennych. Niezmienne w Javie: final, stałe i niezmienne - 2 Przejrzyjmy je jeden po drugim. Jeśli deklaracja klasy zawiera modyfikator final , oznacza to, że nie można dziedziczyć po tej klasie. W poprzednich wykładach widzieliśmy prosty przykład dziedziczenia: mieliśmy klasę nadrzędną Animali dwie klasy podrzędne – CatiDog
public class Animal {
}

public class Cat extends Animal {
   //..поля и методы класса Cat
}

public class Dog extends Animal {

   //..поля и методы класса Dog
}
Jeśli jednak określimy Animalmodyfikator dla klasy, klasy również finalnie będą mogły po niej dziedziczyć . CatDog
public final class Animal {

}

public class Cat extends Animal {

   //ошибка! Cannot inherit from final Animal
}
Kompilator natychmiast generuje błąd. W Javie zaimplementowano już wiele klas - final. Najbardziej znanym z tych, których stale używasz, jest String. Dodatkowo, jeśli klasa jest zadeklarowana jako final, wszystkie jej metody również stają się final. Co to znaczy? Jeśli dla metody określono modyfikator final, tej metody nie można zastąpić. Na przykład mamy klasę Animal, która definiuje metodę voice(). Jednak psy i koty wyraźnie „mówią” inaczej. Dlatego w każdej z klas - Cati Dog- stworzymy metodę voice(), ale będziemy ją implementować inaczej.
public class Animal {

   public void voice() {
       System.out.println("Głos!");
   }
}

public class Cat extends Animal {

   @Override
   public void voice() {
       System.out.println("Miauczeć!");
   }
}

public class Dog extends Animal {

   @Override
   public void voice() {
       System.out.println("Wątek!");
   }
}
W klasach Cati Dognadpisaliśmy metodę klasy nadrzędnej. Teraz zwierzę będzie wydawać dźwięki w zależności od tego, jaki to obiekt klasy:
public class Main {

   public static void main(String[] args) {

       Cat cat = new Cat();
       Dog dog = new Dog();

       cat.voice();
       dog.voice();
   }
}
Wniosek: Miau! Wątek! Jeśli jednak Animalzadeklarujemy metodę w klasie voice()jako final, nie będzie możliwości jej przedefiniowania w innych klasach:
public class Animal {

   public final void voice() {
       System.out.println("Głos!");
   }
}


public class Cat extends Animal {

   @Override
   public void voice() {//ошибка! final-метод не может быть переопределен!
       System.out.println("Miauczeć!");
   }
}
Wtedy nasze obiekty będą zmuszone zastosować metodę voice()zdefiniowaną w klasie nadrzędnej:
public static void main(String[] args) {

   Cat cat = new Cat();
   Dog dog = new Dog();

   cat.voice();
   dog.voice();
}
Wniosek: głos! Głos! Teraz o final-zmiennych. W przeciwnym razie nazywane są stałymi . Po pierwsze (i najważniejsze) nie można zmienić pierwszej wartości przypisanej do stałej. Jest przypisany raz na zawsze.
public class Main {

   private static final int CONSTANT_EXAMPLE = 333;

   public static void main(String[] args) {

       CONSTANT_EXAMPLE = 999;//ошибка! Нельзя присвоить новое oznaczający final-переменной!
   }
}
Stała nie musi być inicjowana natychmiast. Można to zrobić później. Ale wartość przypisana jako pierwsza pozostanie na zawsze.
public static void main(String[] args) {

   final int CONSTANT_EXAMPLE;

   CONSTANT_EXAMPLE = 999;//так делать можно
}
Po drugie zwróć uwagę na nazwę naszej zmiennej. Stałe Java mają inną konwencję nazewnictwa. To nie jest CamelCase, do którego jesteśmy przyzwyczajeni. W przypadku zmiennej zwykłej nazwalibyśmy ją stałąPrzykładem, ale nazwy stałych pisane są wielkimi literami, a pomiędzy słowami (jeśli jest ich kilka) znajduje się podkreślenie - „STAŁA_PRZYKŁAD”. Dlaczego potrzebne są stałe? Przydadzą się na przykład, jeśli w programie stale używasz jakiejś stałej wartości. Załóżmy, że zdecydowałeś się przejść do historii i sam napisać grę „Wiedźmin 4”. Gra oczywiście będzie stale posługiwała się imieniem głównego bohatera – „Geraltem z Rivii”. Lepiej oddzielić tę linię od imion innych bohaterów stałą: potrzebna wartość będzie przechowywana w jednym miejscu i na pewno nie popełnisz błędu wpisując ją milionowy raz.
public class TheWitcher4 {

   private static final String GERALT_NAME = "Геральт из Ривии";
   private static final String YENNEFER_NAME = "Йеннифэр из Венгерберга";
   private static final String TRISS_NAME = "Трисс Меригольд";

   public static void main(String[] args) {

       System.out.println("Ведьмак 4");
       System.out.println("Это уже четвертая часть Ведьмака, а " + GERALT_NAME + " ниJak не определится кто ему" +
               " нравится больше: " + YENNEFER_NAME + " Lub " + TRISS_NAME);

       System.out.println("Но если вы никогда не играли в Ведьмака - начнем сначала.");
       System.out.println("Главного героя зовут " + GERALT_NAME);
       System.out.println(GERALT_NAME + " - ведьмак, охотник на чудовищ");
   }
}
Wniosek:
Ведьмак 4
Это уже четвертая часть Ведьмака, а Геральт из Ривии ниJak не определится, кто ему нравится больше: Йеннифэр из Венгерберга Lub Трисс Меригольд.
Но если вы никогда не играли в Ведьмака — начнем сначала.
Главного героя зовут Геральт из Ривии
Геральт из Ривии — ведьмак, охотник на чудовищ
Nazwy znaków rozdzieliliśmy na stałe i teraz na pewno nie pomylimy ich pisowni i nie będzie potrzeby wpisywania ich za każdym razem ręcznie. Kolejny plus: jeśli w końcu będziemy musieli zmienić wartość zmiennej w całym programie, wystarczy zrobić to w jednym miejscu, zamiast ręcznie przerabiać cały kod :)

Niezmienne typy

Pracując w Javie zapewne przyzwyczaiłeś się już do tego, że programista niemal całkowicie kontroluje stan wszystkich obiektów. Poszukiwany - stworzył obiekt Cat. Jeśli chciałem, zmieniłem jego nazwę. Jeśli chciał, zmienił wiek lub coś innego. Ale w Javie istnieje kilka typów danych, które mają specjalny stan. Są niezmienne lub Niezmienne . Oznacza to, że jeśli klasa jest niezmienna, nie można zmienić stanu jej obiektów. Przykłady? Możesz być zaskoczony, ale najsłynniejszym przykładem klasy Immutable jest String! Wydawałoby się, że nie możemy zmienić wartości ciągu znaków? Spróbujmy:
public static void main(String[] args) {

   String str1 = "I love Java";

   String str2 = str1;//обе переменные-ссылки указывают на одну строку.
   System.out.println(str2);

   str1 = "I love Python";//но поведение str1 ниJak не влияет на str2
   System.out.println(str2);//str2 продолжает указывать на строку "I love Java", хотя str1 уже указывает на другой obiekt
}
Wniosek: kocham Javę. Kocham Javę. Po napisaniu:
str1 = "I love Python";
obiekt z ciągiem „Kocham Javę” nie zmienił się i nigdzie nie zniknął. Istnieje bezpiecznie i zawiera dokładnie ten sam tekst, co poprzednio. Kod:
str1 = "I love Python";
właśnie utworzyłem kolejny obiekt i teraz zmienna str1wskazuje na niego. Nie możemy jednak w żaden sposób wpłynąć na obiekt „Kocham Javę” . OK, spróbujmy inaczej! Klasa Stringjest pełna metod, a niektóre z nich wydają się zmieniać stan wiersza! Na przykład istnieje metoda replace(). Zamieńmy w naszej linijce słowo „Java” na słowo „Python”!
public static void main(String[] args) {

   String str1 = "I love Java";

   String str2 = str1;//обе переменные-ссылки указывают на одну строку.
   System.out.println(str2);

   str1.replace("Java", "Python");//попробуем изменить состояние str1, заменив слово "Java" на “Python”
   System.out.println(str2);
}
Wniosek: Kocham Javę. Kocham Javę. Znowu nie wyszło! Może metoda krzywej nie działa? Spróbujmy innego. Na przykład, substring(). Przycina ciąg na podstawie liczby przesyłanych znaków. Przytnijmy nasze do pierwszych 10 znaków:
public static void main(String[] args) {

   String str1 = "I love Java";

   String str2 = str1;//обе переменные-ссылки указывают на одну строку.
   System.out.println(str2);

   str1.substring(10);//обрезаем исходную строку
   System.out.println(str2);
}
Wniosek: Kocham Javę. Kocham Javę. Niezmienne w Javie: final, stałe i niezmienne - 3 Nic się nie zmieniło. I nie powinno. Jak powiedzieliśmy, obiekty Stringsą niezmienne. Czym w takim razie są te wszystkie metody klasowe String? Mogą przyciąć linię, zmienić zawarte w niej znaki itp. Po co więc są potrzebne, skoro nic się nie dzieje? Mogą! Ale za każdym razem zwracają nowy obiekt typu string. Nie ma sensu pisać:
str1.replace("Java", "Python");
- nie zmienisz oryginalnego obiektu. Ale jeśli zapiszesz wynik metody do nowej zmiennej referencyjnej, natychmiast zobaczysz różnicę!
public static void main(String[] args) {

   String str1 = "I love Java";

   String str2 = str1;//обе переменные-ссылки указывают на одну строку.
   System.out.println(str2);

   String str1AfterReplacement =  str1.replace("Java", "Python");
   System.out.println(str2);

   System.out.println(str1AfterReplacement);
}
Tylko w ten sposób Stringdziałają wszystkie te metody. Z obiektem „Kocham Javę” nie można nic zrobić . Po prostu utwórz nowy obiekt i napisz: „Nowy obiekt = wynik manipulacji obiektem „Kocham Javę ”. Jakie inne typy są niezmienne? Z tego, co na pewno musisz teraz zapamiętać - wszystkie klasy opakowań nad typami pierwotnymi są niezmienne. Integer, Byte, Character, Short, Boolean, Long, - Doublewszystkie Floatte klasy tworzą obiekty niezmienne . Dotyczy to również klas używanych do tworzenia dużych liczb - BigIntegeri BigDecimal. Niedawno omawialiśmy wyjątki i poruszaliśmy temat StackTrace. Zatem: obiekty klasy java.lang.StackTraceElement są również niezmienne. Jest to logiczne: jeśli ktoś mógłby zmienić dane na naszym stosie, mogłoby to zanegować całą pracę z nimi. Wyobraź sobie, że ktoś wchodzi do StackTrace i zmienia OutOfMemoryError na FileNotFoundException . Powinieneś popracować z tym stosem i poszukać przyczyny błędu. A program w ogóle nie korzysta z plików :) Dlatego dla bezpieczeństwa obiekty te zostały uczynione niezmienialnymi. Cóż, dzięki StackTraceElement jest to mniej więcej jasne. Dlaczego ktoś miałby chcieć, aby ciągi znaków były niezmienne? Jaki byłby problem, gdyby można było zmienić ich wartości. Pewnie byłoby to jeszcze wygodniejsze :/ Powodów jest kilka. Po pierwsze, oszczędzanie pamięci. Można umieszczać niezmienne ciągi znaków String Pooli za każdym razem używać tych samych, zamiast tworzyć nowe. Po drugie, bezpieczeństwo. Na przykład większość loginów i haseł w dowolnym programie to ciągi znaków. Możliwość ich zmiany może skutkować problemami z autoryzacją. Są inne powody, ale jeszcze do nich nie dotarliśmy podczas nauki języka Java — wrócimy do tego później.
Komentarze
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION