Pretius: strategiczna fuzja jako odpowiedź na współczesne wyzwania
Pretius. Budujemy mądrzej:
strategiczna fuzja jako odpowiedź na współczesne wyzwania

Funkcje Javy 17: porównanie wersji 8 i 17. Co zmieniło się na przestrzeni lat?

Dariusz Wawer

Senior IT Consultant

  • 7 grudnia, 2022

Spis

Informacja: ten artykuł został pierwotnie opublikowany 22 października 2021 r. Jednak w grudniu 2022 r. został zaktualizowany o nowe informacje dotyczące pakietu Oracle Enterprise Performance Pack dla Java 8. Warto również zauważyć, że choć Java 17 przyniosła mnóstwo przydatnych i ciekawych nowości, Oracle opublikował już nową wersję LTS – wersję 21. Sprawdź nasz artykuł o nowościach w Java 21, jeśli chcesz wiedzieć, co oferuje ta wersja.

[Uwaga] Ten artykuł został pierwotnie przygotowany w języku angielskim i został przetłumaczony na język polski.

W marcu 2022 r. Java 8 utraciła wsparcie Oracle Premier Support. Nie oznacza to, że nie będzie już otrzymywać żadnych nowych aktualizacji, ale wysiłek Oracle wkładany w jej utrzymanie będzie prawdopodobnie znacznie mniejszy niż obecnie.

To oznacza, że istnieje dobry powód, aby przejść na nowszą wersję. Zwłaszcza że 14 września 2021 r. wydano Javę 17. Jest to nowa wersja Long Term Support (LTS), ze wsparciem Oracle Premier Support trwającym (co najmniej) do września 2026 r.. Co przynosi Java 17? Jak trudna będzie migracja? Czy warto? Spróbuję odpowiedzieć na te pytania w tym artykule.

Warto również zauważyć, że Java 8 wciąż otrzymuje pewne rozszerzenia – choć tylko dla wersji Oracle Java i w ramach kosztownej subskrypcji Java SE. 19 lipca 2022 r. wydano pakiet Oracle Enterprise Performance Pack dla Java 8. Numer wersji to 8u345-PERF-b31, a pełną listę zmian można znaleźć tutaj. Dodatki do tej wersji zostaną wspomniane w dalszej części artykułu przy porównywaniu poszczególnych funkcji Java 8 i 17.

Jeśli interesują Cię nowsze wersje Javy, mój kolega Arek Rosłoniec napisał dwa artykuły: o nowościach w Java 21 oraz o nowościach w Java 23.

Popularność Java 8 – krótka lekcja historii

Ekran pokazujący logo Java ze szkłem powiększającym.

Przyjrzyjmy się niektórym funkcjom.

Java 8, wydana w marcu 2014 r., jest obecnie używana przez 69% programistów w ich głównej aplikacji. Dlaczego po ponad 7 latach wciąż jest to najczęściej używana wersja? Przyczyn jest wiele.

Java 8 dostarczyła mnóstwo funkcji językowych, które sprawiły, że programiści Java chcieli porzucić poprzednie wersje. Lambdy, strumienie (streams), programowanie funkcyjne, rozbudowane rozszerzenia API – nie wspominając o MetaSpace czy rozszerzeniach G1. To była ta wersja Javy, której należało używać.

Java 9 pojawiła się 3 lata później, we wrześniu 2017 r., i dla typowego programisty nie zmieniła niemal nic. Nowy klient HTTP, Process API, drobne ulepszenia operatora diamentowego i try-with-resources.

Owszem, Java 9 przyniosła jedną znaczącą, a wręcz przełomową zmianę – projekt Jigsaw. Zmienił on bardzo, bardzo wiele rzeczy – ale wewnętrznie. Modularyzacja Javy daje ogromne możliwości, rozwiązuje wiele problemów technicznych i dotyczy każdego, ale tylko stosunkowo niewielka grupa użytkowników faktycznie potrzebowała głęboko zrozumieć te zmiany. Ze względu na nowości wprowadzone w Jigsaw wiele bibliotek wymagało dodatkowych modyfikacji, wydawano nowe wersje, a niektóre z nich nie działały poprawnie.

Migracja na Javę 9 – zwłaszcza w przypadku dużych, korporacyjnych aplikacji – była często trudna, czasochłonna i powodowała problemy z regresją. Po co więc to robić, skoro zysk jest niewielki, a kosztuje to mnóstwo czasu i pieniędzy?

Java Development Kit 17 (JDK 17) został wydany w październiku 2021 r. Czy to dobry moment, by odejść od ośmioletniej Javy 8? Najpierw zobaczmy, co oferuje Java 17. Co przynosi programiście, administratorowi czy SRE w porównaniu do Javy 8?

Java 17 vs Java 8 – zmiany

W tym artykule opisuję tylko te zmiany, które uznałem za wystarczająco ważne lub interesujące. To nie wszystko, co zostało zmienione, ulepszone i zoptymalizowane w ciągu lat ewolucji Javy. Jeśli chcesz zobaczyć pełną listę zmian w JDK, powinieneś wiedzieć, że są one śledzone jako JEP-y (JDK Enhancement Proposals). Listę można znaleźć w JEP-0.

Jeśli natomiast chcesz porównać API Javy między wersjami, istnieje świetne narzędzie o nazwie Java Version Almanac. Do API Javy dodano wiele przydatnych, drobnych elementów i sprawdzenie tej strony jest najlepszą opcją dla kogoś, kto chce poznać je wszystkie.

Na razie przeanalizujmy zmiany i nowe funkcje w każdej iteracji Javy, które są najważniejsze z perspektywy większości z nas, programistów Java.

Nowe słowo kluczowe var

Dodano nowe słowo kluczowe var, które pozwala na deklarowanie zmiennych lokalnych w bardziej zwięzły sposób. Spójrz na ten kod:

// sposób z Java 8
Map<String, List<MyDtoType>> myMap = new HashMap<String, List<MyDtoType>>();
List<MyDomainObjectWithLongName> myList = aDelegate.fetchDomainObjects();
// sposób z Java 10
var myMap = new HashMap<String, List<MyDtoType>>();
var myList = aDelegate.fetchDomainObjects()

Używając var, deklaracja jest znacznie krótsza i być może nieco bardziej czytelna niż wcześniej. Należy jednak pamiętać o czytelności – w niektórych przypadkach ukrywanie typu przed programistą może być błędem. Pamiętaj o właściwym nazywaniu zmiennych.

Niestety nie jest możliwe przypisanie lambdy do zmiennej za pomocą słowa kluczowego var:

// powoduje błąd kompilacji:
//   method reference needs an explicit target-type
var fun = MyObject::mySpecialFunction;

Możliwe jest jednak użycie var w wyrażeniach lambda. Spójrz na poniższy przykład:

boolean isThereAneedle = stringsList.stream()
.anyMatch((@NonNull var s) -> s.equals(“needle”));

Używając var w argumentach lambdy, możemy dodawać do nich adnotacje.

Rekordy (Records)

Można powiedzieć, że rekordy są odpowiedzią Javy na Lombok. Przynajmniej częściowo. Rekord to typ zaprojektowany do przechowywania danych. Pozwolę sobie zacytować fragment JEP 395, który dobrze to opisuje:

[…] rekord automatycznie zyskuje wiele standardowych elementów:

  • Prywatne pole final dla każdego komponentu opisu stanu;
  • Publiczną metodę dostępową (getter) dla każdego komponentu, o tej samej nazwie i typie co komponent;
  • Publiczny konstruktor, którego sygnatura jest taka sama jak opis stanu, i który inicjalizuje każde pole z odpowiadającego mu argumentu;
  • Implementacje equals i hashCode, które mówią, że dwa rekordy są równe, jeśli są tego samego typu i zawierają ten sam stan; oraz
  • Implementację toString, która zawiera tekstową reprezentację wszystkich komponentów rekordu wraz z ich nazwami.

Innymi słowy, jest to z grubsza odpowiednik @Value z Lomboka. Pod względem językowym przypomina nieco enum. Jednak zamiast deklarować możliwe wartości, deklarujesz pola. Java generuje kod na podstawie tej deklaracji i potrafi go obsłużyć w lepszy, zoptymalizowany sposób. Podobnie jak enum, rekord nie może rozszerzać ani być rozszerzanym przez inne klasy, ale może implementować interfejsy oraz posiadać statyczne pola i metody. W przeciwieństwie do enum, rekord można instancjonować słowem kluczowym new.

Rekord może wyglądać tak:

record BankAccount (String bankName, String accountNumber) implements HasAccountNumber {}

I to wszystko. Całkiem krótko. Krótko znaczy dobrze!

Wszelkie automatycznie generowane metody mogą być zadeklarowane ręcznie przez programistę. Można również zadeklarować zestaw konstruktorów. Co więcej, w konstruktorach wszystkie pola, które są wyraźnie nieprzypisane, są niejawnie przypisywane do odpowiadających im parametrów konstruktora. Oznacza to, że przypisanie można całkowicie pominąć w konstruktorze!

record BankAccount (String bankName, String accountNumber) implements HasAccountNumber {
public BankAccount { // <-- to jest konstruktor! brak () !
if (accountNumber == null || accountNumber.length() != 26) {
throw new ValidationException(“Account number invalid”);
}
// przypisanie nie jest tutaj konieczne!
}
}

Wszystkie szczegóły, takie jak formalna gramatyka, uwagi dotyczące użytkowania i implementacji, znajdziesz w JEP 359.

Rozszerzone wyrażenia switch

Switch jest obecny w wielu językach, ale przez lata stawał się coraz mniej użyteczny ze względu na swoje ograniczenia. Inne części Javy ewoluowały, switch nie. Obecnie przypadki switch mogą być grupowane znacznie łatwiej i w bardziej czytelny sposób (zauważ, że nie ma słowa break!), a samo wyrażenie switch faktycznie zwraca wynik.

DayOfWeek dayOfWeek = LocalDate.now().getDayOfWeek();
boolean freeDay = switch (dayOfWeek) {
case MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY -> false;
case SATURDAY, SUNDAY -> true;
};

Jeszcze więcej można osiągnąć dzięki nowemu słowu kluczowemu yield, które pozwala na zwrócenie wartości z wnętrza bloku kodu. Jest to właściwie return, który działa wewnątrz bloku case i ustawia tę wartość jako wynik całego switcha. Może on również przyjmować wyrażenie zamiast pojedynczej wartości. Spójrzmy na przykład:

DayOfWeek dayOfWeek = LocalDate.now().getDayOfWeek();
boolean freeDay = switch (dayOfWeek) {
case MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY -> {
System.out.println("Praca, praca, praca");
yield false;
}
case SATURDAY, SUNDAY -> {
System.out.println("Jej, wolny dzień!");
yield true;
}
};

Dopasowanie wzorców dla instanceof (instanceof pattern matching)

Choć nie jest to zmiana rewolucyjna, moim zdaniem instanceof rozwiązuje jeden z bardziej irytujących problemów języka Java. Czy kiedykolwiek musiałeś używać takiej składni?

if (obj instanceof MyObject) {
MyObject myObject = (MyObject) obj;
// … dalsza logika
}

Teraz już nie będziesz musiał. Java potrafi teraz utworzyć zmienną lokalną wewnątrz if, w ten sposób:

if (obj instanceof MyObject myObject) {
// … ta sama logika
}

To tylko jedna usunięta linia, ale była ona całkowicie zbędna z punktu widzenia przepływu kodu. Co więcej, zadeklarowana zmienna może być użyta w tym samym warunku if:

if (obj instanceof MyObject myObject && myObject.isValid()) {
// … ta sama logika
}

 

Klasy uszczelnione (Sealed classes)

To trudniejsza do wyjaśnienia funkcja. Zacznijmy od tego – czy ostrzeżenie „no default” w switchu kiedykolwiek Cię irytowało? Uwzględniłeś wszystkie opcje akceptowane przez domenę, ale ostrzeżenie wciąż tam było. Klasy uszczelnione pozwalają pozbyć się takich ostrzeżeń przy sprawdzaniu typów instanceof .

Jeśli masz hierarchię taką jak ta:

public abstract sealed class Animal
permits Dog, Cat {
}
public final class Dog extends Animal {
}
public final class Cat extends Animal {
}

Będziesz teraz mógł zrobić coś takiego:

if (animal instanceof Dog d) {
return d.hauHau();
}
else if (animal instanceof Cat c) {
return c.miau();
}

I nie otrzymasz ostrzeżenia. Pozwól, że ujmę to inaczej: jeśli otrzymasz ostrzeżenie przy podobnej sekwencji, to ostrzeżenie będzie znaczące! A więcej informacji to zawsze dobra rzecz.

Mam mieszane uczucia co do tej zmiany. Wprowadzanie cyklicznych referencji nie wydaje się dobrą praktyką. Gdybym użył tego w kodzie produkcyjnym, starałbym się ukryć to gdzieś głęboko i nigdy nie pokazywać światu zewnętrznemu – mam na myśli nieeksponowanie tego przez API.

Bloki tekstu (TextBlocks)

Deklarowanie długich ciągów znaków nie zdarza się często w programowaniu Java, ale kiedy już się zdarzy, jest męczące i mylące. Java 13 wprowadziła na to sposób, ulepszony w późniejszych wydaniach. Wieloliniowy blok tekstu można teraz zadeklarować następująco:

String myWallOfText = ”””
______         _   _           
| ___ \       | | (_)          
| |_/ / __ ___| |_ _ _   _ ___ 
|  __/ '__/ _ \ __| | | | / __|
| |  | | |  __/ |_| | |_| \__ \
\_|  |_|  \___|\__|_|\__,_|___/
”””

Nie ma potrzeby „uciekania z” (escaping) cudzysłowów ani znaków nowej linii. Można jednak „uciec” nową linię i zachować ciąg w jednej linii:

String myPoem = ”””
Róże są czerwone, fiołki są niebieskie -
Pretius robi najlepszy software, to jasne jak słońce
”””

Co jest odpowiednikiem:

String myPoem = ”Róże są czerwone, fiołki są niebieskie - Pretius robi najlepszy soft, to jasne jak słońce”.

Bloki tekstu mogą być używane do przechowywania w miarę czytelnych szablonów JSON lub XML w kodzie. Pliki zewnętrzne nadal są prawdopodobnie lepszym pomysłem, ale to miła opcja, by zrobić to w czystej Javie, jeśli zajdzie potrzeba.

Lepsze NullPointerExceptions

Zdarzyło mi się kiedyś mieć taki łańcuch wywołań w aplikacji. Myślę, że Tobie również może wydać się znajomy:

company.getOwner().getAddress().getCity();

I otrzymałem NPE, który mówił mi dokładnie, w której linii napotkano null. Tak, to była ta linia. Bez debuggera nie mogłem stwierdzić, który obiekt był nullem, a raczej, która operacja wywołania (invoke) faktycznie spowodowała problem. Teraz wiadomość będzie konkretna i powie nam, że JVM „cannot invoke Person.getAddress()”.

Właściwie jest to bardziej zmiana w JVM niż w samej Javie – ponieważ analiza bajtkodu w celu zbudowania szczegółowego komunikatu jest wykonywana w czasie wykonywania przez JVM – ale bardzo przemawia ona do programistów.

Nowy HttpClient

Istnieje wiele bibliotek, które robią to samo, ale miło jest mieć porządnego klienta HTTP w samej Javie. Dobre wprowadzenie do nowych API można znaleźć w serwisie Baeldung.

Nowa metoda Optional.orElseThrow()

Metoda get() na obiekcie Optional służy do pobrania wartości. Jeśli wartości nie ma, metoda rzuca wyjątek. Tak jak w poniższym kodzie.

MyObject myObject = myList.stream()
.filter(MyObject::someBoolean)
.filter((b) -> false)
.findFirst()
.get();

Java 10 wprowadziła nową metodę w Optional, o nazwie orElseThrow(). Co ona robi? Dokładnie to samo! Ale rozważ zmianę czytelności dla programisty.

MyObject myObject = myList.stream()
.filter(MyObject::someBoolean)
.filter((b) -> false)
.findFirst()
.orElseThrow();

Teraz programista dokładnie wie, co się stanie, gdy obiekt nie zostanie znaleziony. W rzeczywistości zaleca się używanie tej metody zamiast prostej – choć wszechobecnej – metody get().

Inne małe, ale miłe zmiany w API

Szkoda słów, oto kod. Pogrubione zostały nowości.

// odwrócenie Predicate, będzie jeszcze krótsze przy imporcie statycznym
collection.stream()
.filter(Predicate.not(MyObject::isEmpty))
.collect(Collectors.toList());
// String też dostał nowe rzeczy
“\nPretius\n rules\n  all!”.repeat(10).lines().
.filter(Predictions.not(String::isBlank))
.map(String::strip)
.map(s -> s.indent(2))
.collect(Collectors.toList());
// nie trzeba przekazywać instancji tablicy jako argumentu
String[] myArray= aList.toArray(String[]::new);
// szybkie czytanie i pisanie do plików!
// pamiętaj jednak o łapaniu wszystkich możliwych wyjątków
Path path = Files.writeString(myFile, "Pretius Rules All !");
String fileContent = Files.readString(path);
// .toList() bezpośrednio na stream()
String[] arr={"a", "b", "c"};
var list = Arrays.stream(arr).toList();

Zmiany w JVM 17 vs JVM 8

Project Jigsaw

Ekran pokazujący układankę (jigsaw).

Project Jigsaw wywrócił niektóre rzeczy do góry nogami, ale teraz wszystko działa dobrze.

Projekt Jigsaw wprowadzony w JDK 9 znacząco zmienił wnętrze maszyny wirtualnej Java (JVM). Zmienił on zarówno JLS (Java Language Specification), jak i JVMS (Java Virtual Machine Specification), dodał kilka JEP-ów (lista dostępna pod linkiem powyżej) i, co najważniejsze, wprowadził zmiany naruszające kompatybilność (breaking changes), które nie były zgodne z poprzednimi wersjami Javy.

Wprowadzono moduły Java 9 jako dodatkowy, najwyższy poziom organizacji plików JAR i klas. Istnieje mnóstwo materiałów wprowadzających do tego tematu, takich jak ten na Baeldung lub te slajdy autorstwa Yuichi Sakuraba.

Zyski były znaczące, choć niewidoczne gołym okiem. Tak zwane JAR hell przestało istnieć (byłeś tam? ja tak… i to było naprawdę piekło), choć teraz realną możliwością stało się module hell.

Z punktu widzenia typowego programisty zmiany te są teraz niemal niewidoczne. Tylko największe i najbardziej złożone projekty mogą w jakiś sposób odczuć ich skutki. Nowe wersje niemal wszystkich powszechnie używanych bibliotek przestrzegają nowych zasad i uwzględniają je wewnętrznie.

Garbage Collectors (Mechanizmy odśmiecania pamięci)

Począwszy od Javy 9, G1 jest domyślnym odśmiecaczem pamięci. Skraca on czasy pauz w porównaniu do Parallel GC, choć może mieć nieco niższą ogólną przepustowość. Przeszedł on szereg zmian, odkąd stał się domyślny, w tym zyskał możliwość zwracania nieużywanej przydzielonej pamięci do systemu operacyjnego (JEP 346).

W Javie 11 wprowadzono odśmiecacz ZGC, który osiągnął status produkcyjny w Javie 15 (JEP 377). Jego celem jest jeszcze większa redukcja przestojów. Od Javy 13 potrafi on również zwracać nieużywaną pamięć do systemu (JEP 351).

Kolejnym rozwiązaniem jest Shenandoah GC, wprowadzony w JDK 14 i uznany za gotowy do użytku produkcyjnego w Javie 15 (JEP 379). Dąży on do utrzymania krótkich czasów pauz, niezależnie od wielkości sterty (heap).

Warto zauważyć, że w Javie 8 wybór był znacznie mniejszy i jeśli nie zmieniłeś GC ręcznie, prawdopodobnie nadal używałeś Parallel GC. Samo przejście na Javę 17 może sprawić, że Twoja aplikacja będzie działać szybciej i mieć bardziej przewidywalne czasy wykonywania metod. Przejście na ZGC lub Shenandoah może dać jeszcze lepsze rezultaty.

Na koniec warto wspomnieć o nowym No-Op Garbage Collector (JEP 318), choć jest to funkcja eksperymentalna. Ten mechanizm właściwie nic nie robi – co pozwala na precyzyjny pomiar wykorzystania pamięci przez aplikację. Przydatne, jeśli chcesz zredukować narzut operacji pamięciowych do minimum.

Jeśli chcesz dowiedzieć się więcej o dostępnych opcjach, polecam świetną serię artykułów autorstwa Marko Topolnika, która porównuje poszczególne GC.

Wspomniany odśmiecacz G1 został dodany do Oracle Java 8 w wersji 8u345 pakietu Oracle Enterprise Performance Pack. Wraz z Compact Strings, może on znacząco wpłynąć na zużycie pamięci przez aplikacje Java.

Świadomość kontenerów (Container awareness)

Możesz tego nie wiedzieć, ale był czas, kiedy Java nie była świadoma tego, że działa w kontenerze. Nie brała pod uwagę ograniczeń pamięci kontenera i zamiast tego odczytywała dostępną pamięć systemu. Gdy miałeś maszynę z 16 GB RAM, ograniczyłeś kontener do 1 GB i uruchomiłeś w nim aplikację Java, często kończyło się to błędem, ponieważ JVM próbowała alokować więcej pamięci, niż faktycznie oferował kontener. Świetny artykuł Carlosa Sancheza wyjaśnia to szczegółowo.

Te problemy to już przeszłość. Od Javy 10 integracja z kontenerami jest domyślnie włączona. Może to jednak nie być dla Ciebie zauważalna zmiana, ponieważ poprawkę wprowadzono również w Java 8 update 131, choć wymagało to włączenia opcji eksperymentalnych i użycia flagi -XX:+UseCGroupMemoryLimitForHeap.

PS: Często dobrym pomysłem jest jawne określenie maksymalnej pamięci dla Javy za pomocą parametru -Xmx. W takich przypadkach problem z kontenerami nie występuje.

Archiwa CDS

W ramach starań o szybszy start JVM, archiwa CDS (Class Data Sharing) przeszły wiele zmian od czasu wydania Javy 8. Począwszy od JDK 12, tworzenie archiwów CDS podczas procesu budowania jest domyślnie włączone (JEP 341). Ulepszenie w JDK 13 (JEP 350) umożliwiło aktualizację archiwów po każdym uruchomieniu aplikacji.

Class Data Sharing zostało również zaimplementowane dla Javy 8 w pakiecie Oracle Enterprise Performance Pack w wersji 8u345. Nie jest jednak jasne, jak głębokie są te zmiany; opis sugeruje, że dodano jedynie zakres objęty przez JEP 310. Nie udało mi się jednak tego potwierdzić.

Świetny artykuł Nicolaia Parloga pokazuje, jak użyć tej funkcji, by skrócić czas uruchamiania Twojej aplikacji.

Java Flight Recorder i Java Mission Control

Java Flight Recorder (JEP 328) pozwala na monitorowanie i profilowanie działającej aplikacji Java przy bardzo niskim (docelowo 1%) narzucie wydajnościowym. Java Mission Control umożliwia pobieranie i wizualizację danych z JFR. Zajrzyj do poradnika Baeldung, aby dowiedzieć się, jak z tego korzystać i co można dzięki temu zyskać.

Czy powinieneś migrować z Java 8 na Java 17?

Krótko mówiąc: tak, powinieneś. Jeśli posiadasz dużą, mocno obciążoną aplikację korporacyjną i wciąż używasz Javy 8, po migracji z pewnością zauważysz lepszą wydajność, szybszy start i mniejsze zużycie pamięci. Programiści pracujący nad aplikacją również będą zadowoleni dzięki licznym usprawnieniom w samym języku.

Koszt takiej operacji jest jednak trudny do oszacowania i różni się znacznie w zależności od używanych serwerów aplikacji, bibliotek oraz złożoności samej aplikacji (a właściwie liczby niskopoziomowych funkcji, których używa lub które reimplementuje).

Jeśli Twoje aplikacje to mikrousługi, prawdopodobnie wystarczy zmienić bazowy obraz Docker na 17-alpine, wersję kodu w Maven na 17 i wszystko powinno działać poprawnie. Niektóre aktualizacje frameworków lub bibliotek mogą się przydać (ale i tak robisz je okresowo, prawda?).

Wszystkie popularne serwery i frameworki posiadają już wsparcie dla projektu Jigsaw z Javy 9. Jest ono gotowe do użytku produkcyjnego, zostało solidnie przetestowane i poprawione przez lata. Wiele produktów oferuje przewodniki migracji lub przynajmniej obszerne informacje o wydaniu dla wersji kompatybilnych z Javą 9. Zobacz ciekawy artykuł od OSGI lub informacje o wydaniu Wildfly 15 wspomominające o wsparciu dla modułów.

Jeśli używasz Spring Boot, dostępnych jest wiele artykułów z poradami dotyczącymi migracji, jak ten w wiki spring-boot, ten na Baeldung lub jeszcze inny na DZone. Znajdziesz też interesujące studium przypadku na InfoQ. Migracja ze Spring Boot 1 na Spring Boot 2 to oddzielny temat, który również warto rozważyć. Istnieje oficjalny poradnik od Spring Boota oraz artykuł na Baeldung na ten temat.

Jeśli Twoja aplikacja nie posiadała własnych classloaderów, nie polegała mocno na klasie Unsafe ani na bibliotekach sun.misc czy sun.security – prawdopodobnie wszystko będzie w porządku. Skorzystaj z tego artykułu o narzędziu JDEP (Java Dependency Analysis Tool), aby dowiedzieć się o zmianach, które być może będziesz musiał wprowadzić.

Niektóre elementy zostały usunięte z Javy od wersji 8, m.in. silnik Nashorn JS, API i narzędzia Pack200, porty Solaris/Sparc, kompilatory AOT i JIT oraz moduły Java EE i Corba. Niektóre rzeczy wciąż pozostają, ale są oznaczone jako przestarzałe (deprecated), jak Applet API czy Security Manager. Skoro istnieją ważne powody ich usunięcia, i tak powinieneś rozważyć rezygnację z nich w swojej aplikacji.

Zapytałem liderów technicznych w Pretius o ich doświadczenia z migracjami z Java 8 na Java 9+. Padło kilka przykładów i żaden nie był problematyczny. Tu biblioteka nie działała i wymagała aktualizacji, tam potrzebna była dodatkowa konfiguracja, ale ogólnie nie było to wcale złe doświadczenie.

Podsumowanie

Java 17 LTS będzie wspierana przez lata. Z drugiej strony, wsparcie dla Javy 8 dobiegło końca. To z pewnością solidny powód, by rozważyć przejście na nową wersję Javy. W tym artykule omówiłem najważniejsze zmiany w języku i JVM między wersjami 8 i 17, aby łatwiej było zrozumieć różnice między nimi, a także ocenić ryzyko i korzyści płynące z migracji.

Jeśli podejmujesz decyzje w swojej firmie, zadaj sobie pytanie: czy kiedykolwiek będzie „dobry czas”, aby zostawić Javę 8 za sobą? Zawsze trzeba będzie wydać jakieś pieniądze, poświęcić czas i zawsze będzie istniało ryzyko dodatkowej pracy. Skoro nigdy nie ma „idealnego momentu”, teraz jest prawdopodobnie tak dobry moment, jak żaden inny.

Potrzebujesz firmy programistycznej Java?

Pretius posiada świetny zespół programistów Java i duże doświadczenie w wykorzystywaniu tej technologii w systemach klasy enterprise. Znamy się również na wielu różnych branżach. Potrzebujesz oprogramowania opartego na Javie? Napisz do nas na adres hello@pretius.com (lub skorzystaj z poniższego formularza). Odezwiemy się w ciągu 48 godzin. Możesz również sprawdzić moje inne artykuły na blogu Pretius:

  1. JVM Kubernetes: Optimizing Kubernetes for Java Developers
  2. Project Valhalla – Java on the path to better performance
  3. Clean-architecture Java: How ArchUnit can help your application

Funkcje Java 17 FAQ

Kiedy wydano Javę 8?

Java 8 została wydana w marcu 2014 roku.

Kiedy wydano Javę 17?

Java 17 została wydana 15 września 2021 roku.

Jaka jest najnowsza wersja Javy?

Najnowszą wersją Javy jest obecnie Java 23 (stan na koniec 2024 r.). Jednak najnowszą wersją z długoterminowym wsparciem (LTS) jest Java 21 (i Java 17 przed nią).

Jaką wersję Javy posiadam?

Możesz sprawdzić swoją obecną wersję Javy w sekcji Informacje (About) w Panelu Sterowania Java lub wpisując polecenie w terminalu:

java -version

Jak zaktualizować do Javy 17?

Instalacja oprogramowania sprowadza się do prostego uruchomienia pliku wykonywalnego (executable), ale przygotowanie systemu na tę zmianę może być bardziej skomplikowane.

Jakie są nowe funkcje w JDK 17?

JDK 17 to duża aktualizacja Javy z mnóstwem usprawnień i nowości. Oferuje następujące funkcje:

  1. Enhanced Pseudo-Random Number Generators
  2. Restore Always-Strict Floating-Point Semantics
  3. macOS/AArch64 Port
  4. New macOS Rendering Pipelines
  5. Strongly Encapsulated JDK Internals
  6. Deprecate the Applet API for Removal
  7. Pattern Matching for Switch (Preview)
  8. Sealed Classes
  9. Removal of RMI Activation
  10. Deprecate the Security Manager for Removal
  11. Foreign Function & Memory API (Incubator)
  12. Removal of Experimental AOT and JIT Compiler
  13. Context-Specific Deserialization Filters
  14. Vector API (Second Incubator)

Jaka jest różnica między Javą 17 a Javą 18?

Java 17 to wersja z długim wsparciem (long-term support) – będzie wspierana przez co najmniej osiem lat. Java 18 z kolei to mniejsza aktualizacja z kilkoma dodatkowymi funkcjami i jedynie 6-miesięcznym wsparciem.

Czy JRE jest dołączone do JDK 17?

Tak, podobnie jak wszystkie wersje JDK, JDK 17 zawiera Java 17 JRE.

Co to jest Enterprise Performance Pack dla Javy 8?

To płatna subskrypcja, którą można wykupić, aby przenieść niektóre funkcje Javy 17 – takie jak garbage collector G1 – do Javy 8.

Szukasz firmy tworzącej oprogramowanie?

Pracuj z zespołem, który pomógł już dziesiątkom rynkowych liderów. Umów spotkanie, by dowiedzieć się:

  • Jak działają nasze produkty
  • Jak możesz oszczędzić czas i pieniądze
  • Czym nasze rozwiązania różnią się od konkurencji

Przebieg kontaktu z Pretius

Dbamy o bezpieczeństwo Twoich danych: Certyfikat ISO

Działamy zgodnie z normą ISO 27001, zapewniając najwyższy poziom bezpieczeństwa Twoich danych.
certified dekra 27001
© 2026 Pretius. All right reserved.