# Java 25 LTS i Java 26 — co naprawdę zmieniło się w Javie i czy warto migrować

Opublikowano: 9 lipca 2026 | Czas czytania: 13 min | Autor: Nightcode

> Java 25 to nowe wydanie LTS, a Java 26 dołożyła HTTP/3 i kolejne usprawnienia startu aplikacji. Przechodzimy przez najważniejsze JEP-y, mierzalne zyski w produkcji i plan migracji z Java 17 i 21.

Przy projektach integracyjnych bardzo często trafiamy na systemy napisane w Javie: ERP-y, systemy magazynowe, bankowe API, wewnętrzne aplikacje korporacyjne sprzed dekady. Część z nich wciąż działa na Javie 8 lub 11. Część przeszła na 17 lub 21. I prawie wszędzie pada pytanie: **czy przechodzić na Javę 25, a jeśli tak — kiedy i jak?**

Java 25 ukazała się we wrześniu 2025 roku jako kolejne wydanie z długoterminowym wsparciem (LTS). W marcu 2026 dołączyła do niej Java 26. W tym artykule zbieramy zmiany, które mają realne znaczenie dla zespołów utrzymujących aplikacje produkcyjne — z pominięciem ciekawostek, które nie wpływają na codzienną pracę.

## Kalendarz wydań w pigułce

Od 2017 roku Java wychodzi co pół roku — w marcu i we wrześniu. Co dwa lata jedno z wydań otrzymuje status LTS, czyli wieloletnie wsparcie od dostawców (Oracle, Eclipse Temurin, Amazon Corretto, Azul, Microsoft, Red Hat).

| Wersja | Data wydania | Status |
|---|---|---|
| Java 17 | wrzesień 2021 | LTS |
| Java 21 | wrzesień 2023 | LTS |
| Java 25 | wrzesień 2025 | **LTS** |
| Java 26 | marzec 2026 | wydanie krótkoterminowe |
| Java 27 | wrzesień 2026 (planowo) | wydanie krótkoterminowe |
| Java 29 | wrzesień 2027 (planowo) | kolejne LTS |

Dla większości firm praktyczna zasada brzmi: **produkcja na LTS, wydania pośrednie do testowania nowych funkcji**. Jeśli dziś działasz na Javie 17 lub 21, naturalnym celem migracji jest Java 25.

## Co przyniosła droga od Javy 21 do 25

Jeśli przeskakujesz z 21 na 25, dostajesz wszystkie zmiany z wydań 22, 23, 24 i 25. Najważniejsze z nich, pogrupowane według tego, na co wpływają.

### Wirtualne wątki bez przypinania (JEP 491)

Wirtualne wątki (Project Loom) weszły jako stabilna funkcja w Javie 21 i pozwalają obsłużyć dziesiątki tysięcy równoległych operacji I/O bez puli wątków i bez programowania reaktywnego. Miały jednak poważne ograniczenie: wirtualny wątek blokujący się wewnątrz bloku `synchronized` był „przypinany" do wątku platformowego. W praktyce biblioteki intensywnie korzystające z `synchronized` (np. starsze sterowniki JDBC czy klienci HTTP) potrafiły doprowadzić do zakleszczeń lub spadku wydajności.

Od Javy 24 ten problem zniknął. **To jeden z najważniejszych argumentów za migracją z 21** — jeśli rezygnowaliście z wirtualnych wątków z powodu pinningu, warto do nich wrócić.

### Scoped Values — następca ThreadLocal (JEP 506, finalne w 25)

`ThreadLocal` od lat służy do przekazywania kontekstu (np. zalogowanego użytkownika, ID żądania, transakcji) w głąb wywołań. Przy milionach wirtualnych wątków jest jednak kosztowny i podatny na błędy — wartość jest mutowalna i łatwo zapomnieć o jej wyczyszczeniu.

**Scoped Values** to niemutowalny kontekst o jasno określonym zasięgu:

```java
private static final ScopedValue<User> CURRENT_USER = ScopedValue.newInstance();

void handle(Request request) {
    User user = authenticate(request);
    ScopedValue.where(CURRENT_USER, user).run(() -> processOrder(request));
}

void processOrder(Request request) {
    User user = CURRENT_USER.get(); // dostępne w całym drzewie wywołań
    // ...
}
```

Wartość jest widoczna tylko w obrębie wywołania `run`, nie da się jej nadpisać, a po zakończeniu znika automatycznie. Dla frameworków webowych i bibliotek do obserwowalności to spora zmiana na lepsze.

### Structured Concurrency (JEP 505, podgląd w 25)

Współbieżność strukturalna traktuje grupę zadań wykonywanych równolegle jako jedną jednostkę pracy. Jeśli jedno zadanie się nie powiedzie, pozostałe są anulowane; jeśli anulowane zostanie zadanie nadrzędne, anulowane są wszystkie podzadania. Koniec z „osieroconymi" wątkami, które działają dalej po błędzie.

```java
Response fetchDashboard(long customerId) throws InterruptedException {
    try (var scope = StructuredTaskScope.open()) {
        var customer = scope.fork(() -> crm.findCustomer(customerId));
        var orders   = scope.fork(() -> erp.findOrders(customerId));
        scope.join(); // czeka na oba; błąd jednego anuluje drugi
        return new Response(customer.get(), orders.get());
    }
}
```

W Javie 25 i 26 to wciąż funkcja w wersji podglądowej (wymaga flagi `--enable-preview`), a API zmieniało się między wydaniami. Warto ją poznać, ale z użyciem w kodzie produkcyjnym radzimy poczekać na wersję finalną.

### Szybszy start aplikacji — AOT cache (Project Leyden)

To zmiana, która dla wielu zespołów jest najbardziej odczuwalna. Projekt Leyden stopniowo przenosi część pracy wykonywanej przy starcie JVM (ładowanie i linkowanie klas, profilowanie metod) do wcześniejszego „przebiegu treningowego".

- **Java 24 (JEP 483):** cache załadowanych i zlinkowanych klas.
- **Java 25 (JEP 514):** prostsza obsługa — jedna flaga zamiast kilku kroków.
- **Java 25 (JEP 515):** w cache zapisywane są także profile metod, więc kompilator JIT szybciej osiąga pełną wydajność.
- **Java 26 (JEP 516):** cache obiektów działa z dowolnym garbage collectorem, w tym z ZGC.

W praktyce wygląda to tak:

```bash
# przebieg treningowy — aplikacja uruchamia się i wykonuje typowe operacje
java -XX:AOTCacheOutput=app.aot -jar app.jar

# produkcja — start z gotowym cache
java -XX:AOTCache=app.aot -jar app.jar
```

Dla aplikacji Spring Boot oznacza to wyraźnie krótszy czas startu — bez przepisywania kodu i bez ograniczeń kompilacji natywnej (GraalVM Native Image). W środowiskach kontenerowych, gdzie instancje są często uruchamiane i zatrzymywane (autoskalowanie, serverless), różnica jest bardzo odczuwalna.

### Compact Object Headers (JEP 519) — mniej pamięci za darmo

Każdy obiekt w Javie ma nagłówek z metadanymi. Na 64-bitowej JVM zajmował on zwykle 12 bajtów; dzięki Compact Object Headers — 8 bajtów. Brzmi niewinnie, ale typowa aplikacja tworzy miliony małych obiektów. W benchmarkach opisanych w JEP-ie oszczędność sterty sięgała około 20%, a zużycie CPU również spadało.

W Javie 25 funkcja przestała być eksperymentalna. Włącza się ją flagą:

```bash
java -XX:+UseCompactObjectHeaders -jar app.jar
```

To jedna z niewielu optymalizacji, które nie wymagają żadnych zmian w kodzie. Po migracji warto ją przetestować na swoim obciążeniu — w środowiskach chmurowych mniej pamięci to po prostu mniejszy rachunek.

### Mniej ceremonii dla prostych programów (JEP 512, 511)

Java przez lata była krytykowana za rozwlekłość prostych programów. Od Javy 25 skrypt lub narzędzie pomocnicze może wyglądać tak:

```java
void main() {
    String name = IO.readln("Jak masz na imię? ");
    IO.println("Cześć, " + name + "!");
}
```

Bez deklaracji klasy, bez `public static void main(String[] args)`. Do tego **import modułów** (`import module java.base;`) zastępuje kilkanaście pojedynczych importów. Dla doświadczonych zespołów to kosmetyka, ale przy skryptach narzędziowych, prototypach i nauce języka — bardzo przyjemna zmiana. W połączeniu z uruchamianiem programów wieloplikowych bezpośrednio z kodu źródłowego (JEP 458) Java staje się rozsądną alternatywą dla skryptów w Pythonie czy Bashu w zespołach, które na co dzień piszą w Javie.

### Elastyczne konstruktory (JEP 513)

Przed wywołaniem `super(...)` lub `this(...)` można teraz wykonywać instrukcje — np. walidować argumenty. Koniec z obchodzeniem tego ograniczenia statycznymi metodami pomocniczymi:

```java
public PositiveAmount(BigDecimal value) {
    if (value.signum() <= 0) {
        throw new IllegalArgumentException("Kwota musi być dodatnia");
    }
    super(value);
}
```

### Pozostałe zmiany warte uwagi

- **Stream Gatherers (JEP 485, finalne w 24)** — własne operacje pośrednie w strumieniach, np. okna przesuwne czy grupowanie w partie. Wiele pętli, które „nie pasowały" do Stream API, da się teraz zapisać czytelnie.
- **Foreign Function & Memory API (JEP 454, finalne w 22)** — nowoczesny następca JNI do wywoływania bibliotek natywnych w C. Istotne np. przy integracji z bibliotekami AI i sprzętem.
- **Generational ZGC i Generational Shenandoah** — kolektory o bardzo krótkich pauzach, teraz w trybie generacyjnym, z lepszą przepustowością.
- **Kryptografia postkwantowa (JEP 496, 497)** — algorytmy ML-KEM i ML-DSA w standardowej bibliotece. Dla systemów, które muszą chronić dane przez wiele lat, to początek przygotowań do ery komputerów kwantowych.
- **API funkcji wyprowadzania kluczy (JEP 510)** — standardowe KDF, m.in. HKDF.
- **Nowe możliwości JFR (JEP 509, 518, 520)** — profilowanie czasu CPU, dokładniejsze próbkowanie i pomiar czasu wybranych metod bez modyfikowania kodu.
- **Dokumentacja w Markdownie (JEP 467)** — komentarze Javadoc można pisać w Markdownie zamiast HTML.

## Co nowego w Javie 26

Java 26 z marca 2026 to wydanie krótkoterminowe, ale z kilkoma istotnymi zmianami:

- **HTTP/3 w HttpClient (JEP 517)** — wbudowany klient HTTP obsługuje protokół HTTP/3 oparty na QUIC. Przydatne przy komunikacji z nowoczesnymi API i w sieciach o wysokich opóźnieniach.
- **AOT cache z dowolnym GC (JEP 516)** — szybki start aplikacji również przy ZGC.
- **Lepsza przepustowość G1 (JEP 522)** — mniej synchronizacji między wątkami aplikacji a kolektorem.
- **„Final ma znaczyć final" (JEP 500)** — JVM zaczyna ostrzegać przy modyfikowaniu pól `final` przez refleksję. Kolejny krok w stronę bezpieczniejszej i lepiej optymalizowanej platformy. Warto sprawdzić, czy Twoje biblioteki (serializacja, mockowanie, wstrzykiwanie zależności) nie korzystają z takich sztuczek.
- **Usunięcie Applet API (JEP 504)** — symboliczne zamknięcie pewnej epoki.
- **Kolejne podglądy** — Structured Concurrency, Lazy Constants (dawniej Stable Values), wzorce z typami prostymi, kodowanie PEM oraz Vector API w inkubatorze.

## Ekosystem: Spring Boot 4 i reszta

Sama JVM to połowa historii. Równie ważne jest to, co dzieje się w frameworkach:

- **Spring Boot 4 i Spring Framework 7** (listopad 2025) — minimalną wersją pozostaje Java 17, ale framework jest projektowany pod Javę 25. Przejście na Jakarta EE 11, adnotacje null-safety JSpecify, wbudowane wersjonowanie API, deklaratywne klienty HTTP i bardziej modułowa autokonfiguracja. Migracja ze Spring Boot 3 wymaga pracy, ale znacznie mniej niż skok z 2 na 3.
- **Quarkus i Micronaut** — wspierają Javę 25 i od lat stawiają na szybki start aplikacji, co dobrze łączy się z AOT cache.
- **Narzędzia budowania** — Maven i Gradle w aktualnych wersjach obsługują Javę 25. Starsze wersje Gradle mogą wymagać aktualizacji, zanim w ogóle uruchomisz build.

## Na co uważać przy migracji

Większość migracji z 17 lub 21 na 25 przebiega spokojnie, ale kilka rzeczy regularnie sprawia kłopoty:

1. **Biblioteki operujące na bajtkodzie.** Lombok, Mockito, ByteBuddy, AspectJ, agenty APM — starsze wersje nie rozpoznają nowego formatu klas. Zaktualizuj je jako pierwsze.
2. **`sun.misc.Unsafe`.** Metody dostępu do pamięci w `Unsafe` generują ostrzeżenia (JEP 498) i w przyszłości zostaną usunięte. Dotyczy to głównie starszych bibliotek — sprawdź, czy mają nowsze wersje.
3. **Security Manager** jest trwale wyłączony od Javy 24. Jeśli Twoja aplikacja go używała (rzadkie, ale zdarza się w starych systemach), potrzebne są zmiany.
4. **JNI i dostęp natywny** — kolejne ostrzeżenia przy ładowaniu bibliotek natywnych bez jawnego zezwolenia.
5. **Brak 32-bitowego portu x86** (JEP 503) — jeśli gdzieś w firmie wciąż działa 32-bitowa JVM, Java 25 nie będzie opcją.
6. **Obrazy kontenerów.** Zaktualizuj obraz bazowy, sprawdź ustawienia pamięci i ergonomię GC — domyślne zachowanie JVM w kontenerach poprawiało się z każdym wydaniem.

## Plan migracji krok po kroku

Tak wygląda proces, który stosujemy przy migracjach:

1. **Inwentaryzacja.** Wersja JDK, frameworków, bibliotek i narzędzi budowania. `jdeps` pokaże zależności od wewnętrznych API JDK, a `jdeprscan` — użycie przestarzałych API.
2. **Aktualizacja zależności na obecnej wersji Javy.** Najpierw podnieś biblioteki i frameworki, nie zmieniając JDK. Dzięki temu oddzielasz problemy z bibliotekami od problemów z JVM.
3. **Build i testy na Javie 25.** Kompilacja z `--release 25`, pełny zestaw testów, uruchomienie w środowisku testowym. Automatyczne recepty OpenRewrite potrafią wykonać dużą część mechanicznych zmian w kodzie.
4. **Testy wydajności.** Porównaj czas startu, zużycie pamięci, opóźnienia i przepustowość. To dobry moment, żeby przetestować Compact Object Headers i AOT cache.
5. **Wdrożenie stopniowe.** Najpierw mniej krytyczne usługi, potem reszta. Monitoruj logi pod kątem nowych ostrzeżeń JVM.
6. **Modernizacja kodu.** Dopiero gdy wszystko działa, zacznij korzystać z nowych funkcji: wirtualnych wątków, Scoped Values, wzorców w `switch`, rekordów.

## Czy warto migrować? Nasza rekomendacja

- **Z Javy 8 lub 11:** tak, i to jak najszybciej. To już nie kwestia nowych funkcji, tylko bezpieczeństwa, wsparcia i dostępności bibliotek. Skok jest duży, więc warto przejść przez niego etapami lub z pomocą narzędzi do automatycznej migracji.
- **Z Javy 17:** tak, zaplanuj migrację w ciągu najbliższych kwartałów. Zyskujesz wirtualne wątki bez pinningu, szybszy start i mniejsze zużycie pamięci — a Spring Boot 4 i tak skłania do aktualizacji.
- **Z Javy 21:** warto, zwłaszcza jeśli używasz wirtualnych wątków albo działasz w chmurze, gdzie pamięć i czas startu przekładają się bezpośrednio na koszty. Bez pośpiechu, ale w ramach normalnego cyklu utrzymania.
- **Java 26 na produkcji:** tylko jeśli macie proces regularnych aktualizacji co pół roku. W przeciwnym razie zostańcie przy 25 LTS.

## Podsumowanie

Java 25 to najbardziej „odczuwalne" wydanie LTS od lat. Nie przez spektakularne zmiany w składni, tylko przez rzeczy, które widać w metrykach: wirtualne wątki, które wreszcie działają bez kompromisów, szybszy start dzięki AOT cache, mniej pamięci dzięki kompaktowym nagłówkom obiektów i prostszy model współbieżności z Scoped Values. Java 26 kontynuuje ten kierunek i dokłada HTTP/3.

Utrzymujesz system w Javie, który trzeba zintegrować z nowymi narzędziami, zautomatyzować lub zmigrować? W Nightcode zajmujemy się integracjami i rozbudową istniejących systemów — także tych, których nikt w firmie nie chce już dotykać. [Porozmawiajmy](https://www.nightcode.pl/darmowa-konsultacja).
