Lista nowości z Javy od wersji 17 do 21
W tym artykule przedstawię Ci, co zmieniło się w poszczególnych wersjach Javy (od wersji 17 do 21), abyś dokładnie wiedział, czego możesz się spodziewać, pracując na konkretnej wersji.
Jest to już drugi wpis z serii ze zmianami w języku programowania Java. jeżeli nie widziałeś poprzedniej części, w której opisuję co się zmieniło w wersjach Java od 8 do wersji 16, to koniecznie zapraszam to przeczytania.
Java 17 (LTS)
Java 17 jest wersją o długoterminowym wsparciu (LTS), co oznacza, iż otrzymuje aktualizacje i poprawki przez wiele lat. Dla większości firm jest to wersja, na którą warto migrować, by korzystać z najnowszych funkcji i jednocześnie mieć pewność stabilności.
Lista nowości:
- Pattern Matching dla switch (preview, JEP 406)
- Sealed Classes (release, JEP 409)
- Ulepszenie generowania liczb pseudolosowych (JEP 356)
- Inne drobne zmiany
Data wydania: 14 wrzesień 2021r.
Pattern Matching dla switch (preview)
Pattern Matching dla switch znacząco upraszcza kod, szczególnie przy pracy z różnymi typami danych. Wprowadzenie tej funkcji w wersji preview umożliwia stosowanie wzorców w konstrukcji switch, co poprawia czytelność i redukuje liczbę koniecznych rzutowań. Przykład:
public class Main { public static void main(String[] args) { print("test"); // Output: Str: test print(123); // Output: Int: 123 print(true); // Output: Dowolny inny typ } private static void print(Object obj) { switch (obj) { case String s -> System.out.println("Str: " + s); case Integer i -> System.out.println("Int: " + i); default -> System.out.println("Dowolny inny typ"); } } }Dzięki temu możesz w prosty sposób obsługiwać różne przypadki bez konieczności manualnego sprawdzania i rzutowania typów.
Sealed Classes (release)
Sealed Classes, wprowadzone na stałe do Java 17, umożliwiają ograniczenie dziedziczenia klas. Dzięki tej funkcji możesz jasno zdefiniować, które klasy mogą być podklasami danej klasy. Przykład:
public sealed class Shape permits Square, Rectangle { } // Robiąc taką klasę dziedziczenie zadziała final class Square extends Shape { } // Natomiast taka już nie zadziała final class Circle extends Shape { }W tym przypadku tylko klasy Square i Rectangle mogą dziedziczyć po klasie Shape. Jest to przydatne w projektowaniu systemów, gdzie chcemy zachować kontrolę nad hierarchią dziedziczenia.
Ulepszenie generowania liczb pseudolosowych
W wersji 17 ulepszono mechanizmy generowania liczb pseudolosowych. Dodano nowe interfejsy, takie jak RandomGenerator, który ujednolica pracę z różnymi generatorami liczb pseudolosowych. Przykład użycia:
RandomGenerator generator = RandomGenerator.of("L64X128MixRandom"); long randomValue = generator.nextLong();Dzięki temu łatwiej mieć większą elastyczność i kontrolę nad wyborem algorytmów generowania liczb losowych.
Inne drobne zmiany
W Java 17 dokonano również kilku pomniejszych usprawnień, które choć nie są rewolucyjne, znacząco wpływają na porządek i efektywność pracy z językiem.
- Usunięcie Applet API (JEP 398) – Technologia Applet, która nie była już szeroko używana, została ostatecznie usunięta. Dzięki temu redukujemy techniczne zadłużenie w ekosystemie Javy.
- Wycofanie RMI Activation (JEP 407) – Mechanizm aktywacji w RMI został uznany za przestarzały i wycofany. Był rzadko stosowany w nowoczesnych aplikacjach.
- Usprawnienia Garbage Collectorów – Wprowadzone zmiany poprawiają wydajność pracy GC, zwłaszcza w kontekście aplikacji o dużym zapotrzebowaniu na zasoby pamięci.
- Lepsze wsparcie dla MacOS/AArch64 (JEP 391) – Wprowadzono optymalizacje zwiększające wydajność Javy na komputerach Apple z procesorami ARM (M1/M2).
Java 18
Lista nowości wprowadzone w Java 18:
- UTF-8 jako domyślne kodowanie (JEP 400)
- Simple Web Server (JEP 408)
- Kod Snippets w dokumentacji API Java (JEP 413)
- Inne drobne poprawki i wersje eksperymentalne
Data wydania: 22 marzec 2022r.
UTF-8 jako domyślne kodowanie
Od wersji 18 UTF-8 jest domyślnym kodowaniem znaków w Javie. To ważna zmiana, ponieważ zapewnia spójność działania aplikacji niezależnie od ustawień systemowych. Dzięki temu kod napisany w Javie zachowuje tę samą interpretację znaków bez względu na lokalne ustawienia systemu operacyjnego.
Jeśli jednak nie odpowiada Ci UTF-8 zawsze możesz zmienić domyślne kodowanie poprzez użycie flagi -Dfile.encoding
Simple Web Server
Dodano prosty serwer HTTP, który możesz uruchomić bez dodatkowych bibliotek. Jest to świetne narzędzie do szybkich testów i prezentacji aplikacji bez konieczności instalowania dodatkowego oprogramowania.
// Domyślnie odpali się na porcie 8000 jwebserver // Oczywiście możemy ustawić port jwebserver -p 9000Dzięki temu narzędziu można w prosty sposób hostować statyczne pliki i przetestować drobne funkcjonalności aplikacji webowej.
Oczywiście w większości przypadków i tak pewnie będziemy korzystać ze Springa, ale dla takich prostych testów zawsze jest to jakaś alternatywa.
Kod Snippets w dokumentacji API Java
W dokumentacji API Javy wprowadzono obsługę kodów snippets, co umożliwia bardziej przejrzyste i praktyczne przedstawienie przykładów użycia. Kod jest teraz automatycznie testowany, co zapewnia jego poprawność i aktualność w kolejnych wersjach Javy.
Przykład snippetów w dokumentacji pozwala gwałtownie zrozumieć, jak korzystać z danej funkcji, bez konieczności eksperymentowania.
/** * Oblicza sumę dwóch liczb całkowitych. * * @param a pierwsza liczba * @param b druga liczba * @return suma liczb a i b * @snippet : * {@code * int result = MyClass.add(2, 3); * System.out.println(result); // Output: 5 * } */ public static int add(int a, int b) { return a + b; }Inne drobne poprawki i wersje eksperymentalne
- Eksperymentalne funkcje takie jak kolejne iteracje mechanizmów pattern matching for switch (JEP 420) oraz Foreign Function and Memory API (JEP 419), przygotowują grunt pod przyszłe stabilne wersje.
- Usprawnienia diagnostyczne oraz optymalizacje w kompilatorze HotSpot, co przekłada się na lepsze działanie aplikacji w środowiskach produkcyjnych.
Java 19
Lista nowości wprowadzone w Java 19:
- Structured Concurrency (inkubacja, JEP 428)
- Foreign Function & Memory API (preview, JEP 424)
- Wirtualne wątki (preview, JEP 425)
- Pattern Matching dla switch (preview 3, JEP 427)
Data wydania: 20 wrzesień 2022r.
Structured Concurrency (inkubacja)
Structured Concurrency to eksperymentalna funkcjonalność mająca na celu uproszczenie zarządzania współbieżnymi zadaniami. Dzięki niej zadania mogą być grupowane w logiczne jednostki, co poprawia czytelność kodu i ułatwia debugowanie.
import java.util.concurrent.Future; import java.util.concurrent.StructuredTaskScope; public class Main { public static void main(String[] args) { try { String result = fetchStringsByStructuredConcurrency(); System.out.println("Wynik: " + result); // Wynik: Test 1 Test 2 } catch (Exception e) { System.err.println("Error: " + e.getMessage()); } } private static String fetchStringsByStructuredConcurrency() { try (var scope = new StructuredTaskScope.ShutdownOnFailure()) { Future<String> result1 = scope.fork(() -> stringA()); Future<String> result2 = scope.fork(() -> stringB()); scope.join(); // czekaj na zakończenie zadań scope.throwIfFailed(); // rzuć wyjątek przy błędzie return result1.resultNow() + " " + result2.resultNow(); // Połącz wyniki } } private static String stringA() { return "Test 1"; } private static String stringB() { return "Test 2"; } }Do uruchomienia wymagane jest dodanie flagi --enable-preview.
Foreign Function & Memory API (inkubacja)
API pozwala na bezpieczną i wydajną interakcję z kodem natywnym oraz zarządzanie pamięcią poza JVM. Wersja ta wprowadza dodatkowe ulepszenia i stabilizacje funkcjonalności, co czyni ją bardziej użyteczną w aplikacjach wymagających integracji z bibliotekami C/C++.
try (MemorySegment segment = MemorySegment.allocateNative(100, ResourceScope.newConfinedScope())) { MemoryAccess.setIntAtOffset(segment, 0, 42); int value = MemoryAccess.getIntAtOffset(segment, 0); System.out.println("Wartość: " + value); }Wirtualne wątki (preview)
Wirtualne wątki (Virtual Threads) rewolucjonizują zarządzanie współbieżnością w Javie. Umożliwiają tworzenie tysięcy lekkich wątków, co jest idealne w aplikacjach wymagających wysokiej skalowalności.
import java.util.concurrent.Executors; public class Main { public static void main(String[] args) { try (var executor = Executors.newVirtualThreadPerTaskExecutor()) { executor.submit(() -> System.out.println("Wirtualny wątek!")); } } }Pattern Matching dla switch (preview 3)
W tej wersji wprowadzono dodatkowe ulepszenia w dopasowywaniu wzorców. Można użyć słowa kluczowego when, aby oprócz sprawdzania typu móc dodać dodatkowy warunek dla danego typu parametru wejściowego. W przykładzie poniżej sprawdzamy czy parametr wejściowy jest typu String, ale żeby mógł wejść w ten warunek musi dodatkowo mieć więcej niż 5 znaków.
public class Main { public static void main(String[] args) { print("test 5 znakow"); // Output: Str: test 5 znakow print("test"); // Output: Dowolny inny typ } private static void print(Object obj) { switch (obj) { case String s when s.length() > 5 -> System.out.println("Str: " + s); case Integer i -> System.out.println("Int: " + i); default -> System.out.println("Dowolny inny typ"); } } }Java 20
Lista nowości wprowadzone w Java 20:
- Wartości Zakresowe (inkubator, JEP 429)
- Wzorce Rekordów – Record Patterns (preview 2, JEP 432)
- Pattern Matching for switch (preview 4, JEP 433)
- Foreign Function & Memory API (preview 2, JEP 434)
- Wirtualne wątki (preview 2, JEP 436)
- Structured Concurrency (inkubacja 2, JEP 437)
Data wydania: 21 marzec 2023r.
Wartości Zakresowe (inkubator)
Wartości Zakresowe (ang. Scoped Values) wprowadzają możliwość deklarowania danych, które są widoczne tylko w obrębie określonego zakresu. To przydatne rozwiązanie w aplikacjach wielowątkowych, gdzie chcemy przekazywać kontekst w sposób bardziej bezpieczny i wydajny niż przy użyciu zmiennych wątkowych (ThreadLocal).
ScopedValue<String> CURRENT_USER = ScopedValue.newInstance(); ScopedValue.where(CURRENT_USER, "admin", () -> { System.out.println("Current user: " + CURRENT_USER.get()); });Wzorce Rekordów -Record Patterns (preview 2)
Druga iteracja Wzorców Rekordów pozwala na bardziej przejrzyste i zwięzłe operowanie danymi w strukturach rekordów. Dzięki tej funkcji możemy jeszcze bardziej uprościć dopasowywanie do wzorców w Javie.
public class Main { public static void main(String[] args) { printPoint(new Point(5, 7)); // Output: Point: (5, 7) } record Point(int x, int y) { } static void printPoint(Object obj) { if (obj instanceof Point(int x, int y)) { System.out.println("Point: (" + x + ", " + y + ")"); } } }Pattern Matching for switch (preview 4)
Czwarta iteracja Pattern Matching dla switch przynosi dalsze ulepszenia, takie jak lepsze wsparcie dla zagnieżdżonych wzorców i jeszcze bardziej zoptymalizowane działanie na poziomie JVM.
public class Main { public static void main(String[] args) { String text1 = getText("test 1"); System.out.println(text1); // Output: Str: test 1 String text2 = getText(new Point(5, 7)); System.out.println(text2); // Output: Point: (5, 7) String text3 = getText(new Point(-1, 7)); System.out.println(text3); // Output: Wartość nieznana } record Point(int x, int y) {} private static String getText(Object obj) { return switch (obj) { case Point(int x, int y) when x > 0 && y > 0 -> "Point(" + x + ", " + y + ")"; case String s -> "Str: " + s; default -> "Wartość nieznana"; }; } }Foreign Function & Memory API (preview 2)
Druga iteracja Foreign Function & Memory API wprowadza dalsze ulepszenia w pracy z pamięcią poza JVM i integracji z kodem natywnym. Dzięki temu w API można w prostszy sposób tworzyć wydajne aplikacje wymagające operacji niskopoziomowych.
try (MemorySegment segment = MemorySegment.allocateNative(100)) { MemoryAccess.setIntAtOffset(segment, 0, 42); int value = MemoryAccess.getIntAtOffset(segment, 0); System.out.println("Wartość: " + value); }W porównaniu do tego co widzieliśmy w Java 19, w tej wersji zostało to trochę ułatwione.
Wirtualne wątki (preview 2)
Wirtualne Wątki w wersji drugiej zyskały dodatkowe usprawnienia, poprawiając ich stabilność i wydajność. W połączeniu z innymi funkcjami, takimi jak Structured Concurrency, otwierają drzwi do budowy nowoczesnych, wysoko skalowalnych aplikacji.
Structured Concurrency (inkubacja 2)
Druga iteracja Structured Concurrency wprowadza drobne ulepszenia i poprawki stabilności. Umożliwia to jeszcze bardziej intuicyjne grupowanie zadań współbieżnych w logiczne jednostki.
- W Java 19 mieliśmy tylko metodę ShutdownOnFailure. W tej wersji doszła metoda ShutdownOnSuccess.
- Dopracowane obsługę wyjątków w StructuredTaskScope – lepsze raportowanie wyjątków i zarządzanie stanami błędów.
- Poprawiono dokumentację opisującą jak korzystać z danej metody.
- Poprawienie wydajności w anulowaniu zadań.
Java 21 (LTS)
Java 21 to kolejna wersja o długoterminowym wsparcie (LTS). jeżeli chcesz pracować na najnowszej stabilnej wersji Javy to koniecznie powinieneś wybrać właśnie tą wersję. Wersja ta wprowadza szereg istotnych usprawnień, które upraszczają programowanie współbieżne, integrację z kodem natywnym oraz operacje na danych
Lista nowości:
- Wirtualne wątki (release, JEP 444)
- Wzorce Rekordów – Record Patterns (release, JEP 440)
- Pattern Matching for switch (release, JEP 441)
- String Templates (preview, JEP 430)
- Sequenced Collections (JEP 431)
- Foreign Function & Memory API (preview 3, JEP 442)
- Structured Concurrency (preview, JEP 453)
Data wydania: 19 wrzesień 2023r.
Wirtualne wątki (release)
Wirtualne wątki w Javie 21 zostały oficjalnie wydane. Wprowadzenie wirtualnych wątków (projekt Loom) rewolucjonizuje zarządzanie współbieżnością, umożliwiając tworzenie tysięcy lekkich wątków z minimalnym narzutem. Dzięki tej zmianie aplikacje oparte na intensywnym przetwarzaniu współbieżnym stają się znacznie prostsze do implementacji.
import java.util.concurrent.Executors; public class Main { public static void main(String[] args) { try (var executor = Executors.newVirtualThreadPerTaskExecutor()) { executor.submit(() -> System.out.println("Wirtualny wątek!")); } } }Pattern Matching for switch (release)
Pattern Matching dla switch został ostatecznie wydany w Javie 21. Ta funkcjonalność umożliwia bardziej zaawansowane i czytelne zarządzanie logiką aplikacji poprzez dopasowywanie wzorców w strukturze switch.
switch (obj) { case Point(int x, int y) when x > 0 && y > 0 -> System.out.println("Point (" + x + ", " + y + ")"); case String s -> System.out.println("String: " + s); default -> System.out.println("Wartość nieznana"); }Pełny przykład można znaleźć w opisie do Java 20.
Wzorce Rekordów – Record Patterns (release)
O tej wersji Javy, z wzorców rekordów, można już korzystać bez żadnych dodatkowych flag.
record Point(int x, int y) {} static void printPoint(Object obj) { if (obj instanceof Point(int x, int y)) { System.out.println("Point: (" + x + ", " + y + ")"); } }Pełny przykład można znaleźć w opisie do Java 20.
String Templates (preview)
Szablony tekstowe wprowadzają możliwość tworzenia dynamicznych i bezpiecznych pod względem typów ciągów znaków z użyciem specjalnych wyrażeń wbudowanych. Ciekawym przykładek na użycie tego może być przy tworzeniu zapytań SQL.
import java.lang.StringTemplate.STR; public class Main { public static void main(String[] args) { String name = "ma kota"; String greeting = STR."Ala \{name}!"; System.out.println(greeting); // Output: Ala ma kota! } }Do uruchomienia wymagane jest dodanie flagi --enable-preview.
Sequenced Collections
Nowy interfejs kolekcji uporządkowanych (sequenced collections) umożliwia jednolite zarządzanie kolekcjami, które mają przewidywalną kolejność elementów, zarówno wstawiania, jak i iteracji.
import java.util.ArrayList; import java.util.SequencedCollection; public class Main { public static void main(String[] args) { SequencedCollection<String> sequenced = new ArrayList<>(); sequenced.add("A"); sequenced.add("B"); sequenced.addLast("C"); sequenced.addFirst("D"); System.out.println(sequenced.getFirst()); // Output: D System.out.println(sequenced.getLast()); // Output: C sequenced.removeFirst(); sequenced.removeLast(); System.out.println(sequenced.getFirst()); // Output: A System.out.println(sequenced.getLast()); // Output: B } }Oprócz SequencedCollection<K>, dostaliśmy również SequencedMap<K, V>, SequencedSet<K>, SequencedSet<K, V>.
Foreign Function & Memory API (preview 3)
Dodano kolejne usprawnienie dla Foreign Function & Memory API m.in. Intuicyjniejsze zarządzanie pamięcią dzięki Arena, lepsze wsparcie dla wywołań funkcji natywnych z bardziej skomplikowanymi typami danych i nowe tryby wielowątkowości, takie jak Arena.openShared.
import java.lang.foreign.Arena; import java.lang.foreign.MemorySegment; import java.lang.foreign.ValueLayout; public class Main { public static void main(String[] args) { try (Arena arena = Arena.ofConfined()) { // Alokacja pamięci w ramach Areny MemorySegment segment = arena.allocate(ValueLayout.JAVA_INT); segment.set(ValueLayout.JAVA_INT, 0, 42); // Zapisujemy wartość System.out.println("Wartość: " + segment.get(ValueLayout.JAVA_INT, 0)); // Odczytujemy wartość } // Pamięć jest automatycznie zwolniona po zamknięciu Areny } }Do uruchomienia wymagane jest dodanie flagi --enable-preview.
Structured Concurrency (preview)
Structured Concurrency w wersji preview wprowadza jeszcze lepsze wsparcie dla zarządzania współbieżnymi zadaniami, grupując je w logiczne jednostki i zwiększając czytelność kodu.
Z najciekawszych elementów jakie doszło to jest metoda scope.hasFailed(), która sprawdza, czy przynajmniej jedno zadanie zakończyło się błędem.
Podsumowanie
Podsumowując, Java od wersji 17 do 21 przyniosła wiele istotnych zmian, które z pewnością warto poznać i wykorzystać w swoich projektach. Znacznie uproszczono zarządzanie współbieżnością dzięki wirtualnym wątkom i Structured Concurrency. Wzorce rekordów (Record patterns) i Pattern Matching for switch pozwalają na bardziej czytelny i intuicyjny kod. Nowe API, takie jak Foreign Function & Memory czy String Templates, otwierają możliwości integracji i dynamicznego generowania kodu.
Następny wpis zostanie opublikowany przy okazji kolejnej wersji LTS, którą (na dzisiaj) ma być Java 25, planowana na wrzesień 2025 roku.