Scud rakiety mobilne rakiety balistyczne krótkiego zasięgu na klawiaturze laptopa renderowanie 3D
Komputery, Technologie

Zmiany w Java 9 – modułowość

Java 9, wydana we wrześniu 2017 roku, wprowadziła zestaw zmian, które realnie unowocześniły ekosystem języka.

Największą nowością jest modułowość (Java Platform Module System, JPMS), która rozwiązuje problem monolitycznego JDK, poprawia bezpieczeństwo i upraszcza zarządzanie zależnościami.

W tym materiale pokazujemy, jak pracować z modułami krok po kroku oraz omawiamy praktyczne usprawnienia: JShell, Stream API, nowy HTTP Client i Process API.

Java Platform Module System (JPMS) – rewolucja w modularyzacji

Przed Java 9 JDK było monolitem – ogromnym zbiorem klas, w tym wewnętrznych API, do których aplikacje nie powinny mieć dostępu. To skutkowało problemami z wydajnością, bezpieczeństwem i rozmiarem aplikacji.

JPMS, rozwijany w projekcie Jigsaw, enkapsuluje wewnętrzne API i przekształca JDK z monolitu w zestaw jasno zdefiniowanych modułów.

Jak podkreślają oficjalne materiały i niezależne źródła, skala zmiany jest wyjątkowa:

„jedną z największych zmian od Java 8”

Baeldung zwraca uwagę na podobieństwa do OSGi, ale z natywnym wsparciem JVM, a GeeksforGeeks wskazuje na przejście od monolitycznych JAR-ów do modułów.

Dlaczego modułowość jest ważna?

Kluczowe korzyści z wprowadzenia JPMS to:

  • lepsza enkapsulacja – moduły eksportują tylko publiczne API, ukrywając implementacje;
  • mniejsze runtime – JVM ładuje wyłącznie potrzebne moduły, co jest idealne dla systemów o ograniczonych zasobach (np. IoT);
  • zależności jawne – koniec z „classpath hell”, konfliktami wersji i niejasnymi relacjami;
  • bezpieczeństwo – aplikacje nie mają dostępu do wewnętrznych klas, jak sun.misc.Unsafe.

Krok po kroku – tworzenie pierwszego modułu

Aby korzystać z JPMS, projekt musi mieć strukturę modularną. Użyj Mavena lub Gradle’a z włączonym wsparciem JPMS; każdy moduł to osobny podprojekt z plikiem module-info.java w src/main/java.

Wykonaj poniższe kroki, aby zbudować minimalny przykład z dwoma modułami:

  1. Utwórz moduł bazowy (helloworld) – w ramach tego kroku przygotuj pliki i klasy:
    • module-info.java – definicja modułu i eksportu pakietu: module helloworld { exports jdk.java9.jigsaw.hello; };
    • HelloInterface – interfejs usługi: package jdk.java9.jigsaw.hello; public interface HelloInterface { void sayHello(); };
    • HelloWorld – implementacja: package jdk.java9.jigsaw.hello; public class HelloWorld implements HelloInterface { public static void sayHelloWorld() { System.out.println("Hello World!"); } public void sayHello() { System.out.println("Hello!"); } }.
  2. Utwórz moduł zależny (hellomain) – zadeklaruj zależność i użycie usługi:

    module-info.java – deklaracja wymagania i użycia SPI:

    module hellomain { requires helloworld; uses jdk.java9.jigsaw.hello.HelloInterface; }

    ModulesMain – klasa startowa, która korzysta z API i ServiceLoader:

    package jdk.java9.jigsaw.main;
    import java.util.ServiceLoader;
    import jdk.java9.jigsaw.hello.HelloInterface;
    import jdk.java9.jigsaw.hello.HelloWorld;

    public class ModulesMain {
    public static void main(String[] args) {
    HelloWorld.sayHelloWorld();
    Iterable<HelloInterface> services = ServiceLoader.load(HelloInterface.class);
    HelloInterface service = services.iterator().next();
    service.sayHello();
    }
    }

  3. Kompilacja i uruchamianie – użyj poniższych poleceń:
    • kompiluj – z uwzględnieniem ścieżki modułów: javac --module-source-path src $(find src -name "*.java");
    • pakuj – utwórz JAR-y modułów: jar --create --file mods/helloworld.jar -C mods/helloworld .;
    • uruchamiaj – wskaż --module-path i moduł/klasę: java --module-path mods -m hellomain/jdk.java9.jigsaw.main.ModulesMain.

Po uruchomieniu zobaczysz wynik działania programu:

Hello World!
Hello!

Dla bardziej zaawansowanych scenariuszy warto znać następujące konstrukcje:

  • provides/uses – mechanizm usług (SPI) do luźnego wiązania implementacji z interfejsem;
  • automatic modules – tymczasowe wsparcie dla bibliotek bez własnego module-info.java;
  • requires transitive – propagowanie zależności modułowych do dalszych konsumentów.

W praktyce JPMS upraszcza architekturę, ale w dużych monolitach migracja może być złożona (zwraca na to uwagę m.in. Developeronthego.pl).

JShell – interaktywny REPL dla Javy

JShell to interaktywna powłoka (Read–Eval–Print Loop), dzięki której testujesz fragmenty kodu bez tworzenia projektu.

Uruchom JShell i wpisz komendy, aby natychmiast zobaczyć rezultat:

jshell> int x = 10;
x ==> 10
jshell> System.out.println("Hello JShell!");
Hello JShell!

To idealne narzędzie do szybkich testów, prototypowania, nauki API oraz sprawdzania hipotez bez narzutu buildów.

Ulepszenia Stream API i kolekcji

Najważniejsze zmiany w strumieniach i kolekcjach to:

  • Stream.iterate – nowa postać z predykatem zatrzymania, wygodna do ograniczonych sekwencji;
  • metody fabryczne kolekcji – List.of(...), Set.of(...), Map.of(...) tworzą niezmienne kolekcje;
  • Optional.stream() – łatwe „spłaszczanie” opcjonalnych wartości w potokach.

Przykład użycia nowego Stream.iterate z predykatem:

java.util.stream.IntStream
.iterate(0, i -> i < 10, i -> i + 1)
.forEach(System.out::println);

Nowy klient HTTP/2 i Process API

HTTP Client (java.net.http) wprowadza wsparcie dla HTTP/2, asynchronicznego programowania i WebSocket, upraszczając obsługę sieci w nowoczesnych aplikacjach. Przykładowe wywołanie: HttpClient.newHttpClient().send(...).

Process API udostępnia wygodne metody monitorowania procesów systemowych, np. ProcessHandle.current().info().commandLine(), co ułatwia audyt i diagnostykę uruchomionych aplikacji.

Inne nowości

Poniższa tabela zbiera wybrane, przydatne dodatki z Java 9 wraz z krótkimi przykładami:

Cecha Opis Przykład użycia
Prywatne metody w interfejsach Prywatne metody private w interfejsach, również pomocnicze dla default. private void helper() { /* ... */ }
Ulepszenia CompletableFuture Nowe fabryki, opóźnienia i transformacje. CompletableFuture.delayedExecutor(1, TimeUnit.SECONDS)
VarHandle Wydajna, bezpieczna alternatywa dla Unsafe i atomików w java.lang.invoke. Dostęp do pól z gwarancjami pamięci.
Reactive Streams Interfejsy java.util.concurrent.Flow dla modelu publish–subscribe. Integracja z reaktywnymi frameworkami.
Multi-Resolution Images Wsparcie obrazów w wielu rozdzielczościach dla UI. @MRImage w komponentach graficznych.

Usprawniono G1GC (m.in. obsługa „humongous objects”) oraz Javadoc (indeksowanie, wyszukiwanie, linki do modułów).

Dodaj komentarz

Twój adres email nie zostanie opublikowany. Wymagane pola są oznaczone *