Czternastego września 2021 roku nastąpiła premiera Javy 17, najnowszej wersji z długoterminowym wsparciem (LTS) środowiska wykonawczego Java. Przyjrzyjmy się nowościom, które wprowadza ta wersja i rozważmy, czy warto dokonać aktualizacji.
Wiele aplikacji nadal korzysta z wcześniejszych wydań Javy, w tym popularnych wersji LTS: Javy 11 i Javy 8.
Dlaczego firmy powinny rozważyć przejście na najświeższą wersję? Aktualizacja do Javy 17 jest wyzwaniem, ale umożliwia pełne wykorzystanie innowacyjnych funkcji i możliwości wewnątrz maszyny wirtualnej JVM.
Wiele przedsiębiorstw ułatwia sobie migrację na Javę 17, stosując kontenery Docker. Deweloperzy mogą definiować swoje potoki CI/CD, uruchamiając wszystko w kontenerach Docker. Takie podejście nie wpływa na zespoły korzystające ze starszych wersji Javy, ponieważ mogą one nadal używać starszych obrazów Dockera.
Nowości w Javie 17
Wsparcie dla macOS i architektury AArch64
Jedną z istotnych innowacji w JVM jest rozszerzenie wsparcia dla systemu macOS na architekturze AArch64, wprowadzone za pomocą JEP 391. Dzięki temu najnowsze procesory Apple (M1), które zadebiutowały w zeszłorocznych komputerach, są w pełni obsługiwane.
Choć niektórzy dostawcy już wcześniej oferowali JDK z obsługą tej architektury, a nawet wsparcie z Javy 8, oficjalne zatwierdzenie jest kluczowe dla zapewnienia długotrwałej konserwacji i wsparcia. Dla porównania, wsparcie dla Linux/AArch64 pojawiło się w Javie 9, a dla Windows/AArch64 w Javie 16.
Klasy zapieczętowane
W Javie 17 finalnie wprowadzono klasy zapieczętowane. Ta funkcja, która przeszła etap testowy, stała się oficjalną częścią języka i platformy. Klasy zapieczętowane pozwalają programiście na określenie precyzyjnej listy dopuszczalnych podtypów, uniemożliwiając nieautoryzowane rozszerzanie lub implementowanie danego typu.
Klasy zapieczętowane pozwalają kompilatorowi na wykrywanie nieprawidłowych prób konwersji typu na niedozwolony podtyp. Java 17 wprowadza również nowy mechanizm renderowania dla aplikacji AWT/Swing na macOS, korzystający z Apple Metal API zamiast OpenGL. Ulepszono także API i funkcje generowania liczb losowych.
Zmiany, usunięcia i ograniczenia w Javie 17
Java 17 niesie ze sobą również pewne zmiany, usunięcia i nowe ograniczenia.
Hermetyzacja elementów wewnętrznych JDK
Jedną z ważnych zmian jest zakończenie procesu hermetyzacji wewnętrznych elementów JDK. W Javie 9 wprowadzono ostrzeżenia w czasie wykonywania, gdy próbowano obejść ograniczenia dostępu do wewnętrznych interfejsów API za pomocą refleksji. Dodano również argumenty wiersza poleceń do regulacji tego zachowania.
Od Javy 9 wprowadzano alternatywne API do wykonywania najczęstszych zadań w sposób ujednolicony. W Javie 16 domyślne zachowanie zmieniono z ostrzeżenia na zgłaszanie wyjątku w przypadku próby nieautoryzowanego dostępu. Umożliwiono jednak zmianę tego zachowania za pomocą argumentu wiersza poleceń.
W Javie 17 usunięto argument wiersza poleceń, co oznacza, że nie można już dezaktywować tego ograniczenia. Nieautoryzowany dostęp do wewnętrznych interfejsów API jest teraz bezwzględnie chroniony.
Zawsze ścisła semantyka zmiennoprzecinkowa
Dodatkowe „usunięcie” można opisać jako ponowne wprowadzenie ścisłej semantyki zmiennoprzecinkowej. Java 1.2 wprowadziła zmiany, które umożliwiały JVM wymianę części precyzji obliczeń na wydajność. W klasach i metodach, w których wymagana była ścisła semantyka, używano słowa kluczowego `strictfp`. Wraz z rozwojem procesorów, ścisła semantyka stała się możliwa bez dodatkowych kosztów, co sprawiło, że podział na domyślną i ścisłą semantykę stał się zbędny.
Java 17 porzuca domyślną semantykę, a wszystkie operacje zmiennoprzecinkowe są wykonywane w sposób ścisły. Słowo kluczowe `strictfp` nadal istnieje, ale nie ma już wpływu na działanie i powoduje tylko ostrzeżenie w czasie kompilacji.
Kompilacja AOT (Ahead-of-Time)
W Javie 9 wprowadzono kompilację AOT jako funkcję eksperymentalną, wykorzystującą kompilator Graal. Kod JIT napisano w Javie. W Javie 10 kompilator Graal stał się użyteczny jako kompilator JIT w OpenJDK poprzez włączenie interfejsu JVMCI. Kompilator Graal przeszedł dużą ewolucję i ma swoją JVM o nazwie GraalVM.
Aktywacja RMI
Aktywacja RMI została usunięta w JEP 407 po jej usunięciu z Javy 8 i oznaczeniu do usunięcia w Javie 15. Aktywacja RMI umożliwiała włączanie rozproszonych obiektów na żądanie. Ponieważ jednak stała się przestarzała i istnieją lepsze alternatywy, zrezygnowano z niej. Usunięcie aktywacji nie wpływa na pozostałą część RMI.
Usunięcie API apletów
API apletów zostało ostatecznie usunięte przez JEP 398, choć pierwotnie zostało wycofane już w Javie 9. API apletów umożliwiało integrację kontrolek Java AWT/Swing ze stroną internetową w przeglądarce. Jednak, ponieważ żadna współczesna przeglądarka tego nie wspiera, aplety stały się niedostępne w ciągu ostatniej dekady.
Menadżer Bezpieczeństwa
Najważniejszą zmianą jest wycofanie menadżera bezpieczeństwa (JEP 411). Menadżer bezpieczeństwa, używany od Javy 1.0, miał ograniczać działania Javy na komputerze, np. dostęp do sieci, plików i innych zasobów. Miał również na celu izolowanie kodu z nieznanych źródeł.
Proces wycofywania menadżera bezpieczeństwa rozpoczął się w Javie 12. Dodano argument wiersza poleceń, aby zablokować jego użycie w czasie wykonywania. W Javie 17 próba ustawienia menadżera bezpieczeństwa generuje ostrzeżenie w czasie wykonywania.
Funkcje inkubatora i podglądu
Mimo że Java 17 jest wersją LTS, zawiera dwa moduły inkubatora i jedną funkcję podglądu!
Wektorowe API
Wektorowe API (JEP 414) znajduje się obecnie w drugiej fazie inkubatora. To API pozwala na definiowanie obliczeń wektorowych, które kompilator JIT przekształca w instrukcje wektorowe obsługiwane przez architekturę procesora (np. SSE lub AVX).
Wcześniej programiści musieli używać funkcji skalarnych lub budować natywne biblioteki specyficzne dla platformy. Wektorowe API w Javie zapewnia też niezawodny mechanizm awaryjny, który był trudny do osiągnięcia we wcześniejszych wersjach.
Standaryzacja Wektorowego API pozwala klasom w JDK na jego użycie. Na przykład metoda `mismatch()` w Java Arrays może być zoptymalizowana, eliminując potrzebę utrzymywania wielu implementacji dla różnych platform.
API Funkcji Obcych i Pamięci
Dodatkową funkcją inkubatora jest Foreign Function & Memory API (JEP 412). Jest to połączenie dwóch wcześniejszych modułów inkubatora z Javy 16: Foreign Linker API (JEP 389) i API pamięci obcej (JEP 393). Oba te API umożliwiają dostęp do natywnej pamięci i kodu, przy użyciu statycznie typowanego kodu Java.
Dopasowywanie wzorców dla przełącznika
Ostatnią funkcją podglądu w Javie 17 jest Dopasowywanie wzorców dla przełącznika (JEP 406). Ta funkcja rozszerza instrukcje `switch` o możliwość analizy typu, podobnie jak w przypadku Dopasowywania Wzorców (JEP 394), wprowadzonego w Javie 16.
W przeszłości, aby wykonywać różne działania na podstawie dynamicznego typu obiektu, konieczne było budowanie łańcucha instrukcji `if-else`, na przykład:
String type(Object o) { if (o instanceof List) { return "Lista elementów."; } else if (o instanceof Map) { return "Mapa! Zawiera klucze i wartości."; } else if (o instanceof String) { return "To jest tekst."; } else { return "To coś innego."; } }
Dzięki połączeniu wyrażenia `switch` z nową funkcją dopasowywania wzorców dla przełączników, powyższy kod można uprościć:
String type(Object o) { return switch (o) { case List l -> "Lista elementów."; case Map m -> "Mapa! Zawiera klucze i wartości."; case String s -> "To jest tekst."; default -> "To coś innego."; }; }
Zauważmy, że deklaracja zmiennej następuje w trakcie sprawdzania typu. Podobnie jak w przypadku innych zmiennych w Dopasowywaniu Wzorców, zmienna jest dostępna i od razu rzutowana do danego typu w swoim bieżącym zasięgu.
Funkcja podglądu jest kolejnym krokiem w rozwoju Dopasowywania Wzorców. Następnym krokiem jest rozszerzenie możliwości o dekonstrukcję tablic i rekordów.
Czy warto przejść na Javę 17?
Zdecydowanie warto dążyć do aktualizacji do najnowszej wersji, jednak niekoniecznie od razu w dniu premiery. Biblioteki, z których korzystamy, mogą jeszcze nie być w pełni kompatybilne z Javą 17, więc warto poczekać, aż wszystko będzie gotowe.
Jeśli utknęliśmy z wersją LTS, taką jak Java 8 lub Java 11, Java 17 oferuje wiele nowych funkcji w języku i JVM, które uzasadniają aktualizację. Ze względu na długoterminowe wsparcie, istnieje duża szansa, że nasze środowisko produkcyjne zostanie ostatecznie zaktualizowane do Javy 17.
Jeśli rozpoczynamy nowy projekt lub przygotowujemy projekt do obsługi Javy 17, przejście na nią wcześniej jest efektywniejsze, ponieważ zmniejsza koszty przyszłej migracji. Ponadto programiści pracujący nad projektem mogą wykorzystać najnowsze funkcje.
Możemy skorzystać z wielu udoskonaleń, które pojawiły się w ostatnich latach, takich jak lepsze wsparcie dla kontenerów, a także nowe implementacje modułu odśmiecania pamięci o niskim opóźnieniu.