CodeGym /Kurse /JAVA 25 SELF /Einführung in Module: warum man sie braucht

Einführung in Module: warum man sie braucht

JAVA 25 SELF
Level 60 , Lektion 0
Verfügbar

1. Probleme des alten Ansatzes

„Classpath-Party“: wenn alle in einer Küche sind

Vor der Einführung von Modulen in Java (in Version 9) landeten der gesamte Anwendungscode, Bibliotheken und Abhängigkeiten in einem großen Haufen – dem classpath. Grenzen zwischen Bibliotheken gab es faktisch nicht: Jede Klasse, die im classpath lag, konnte von jedem gefunden und verwendet werden.

Es kam zu Namenskonflikten: Zwei Bibliotheken enthalten eine Klasse mit demselben vollqualifizierten Namen, zum Beispiel com.example.Util – es wurde „irgendeine“ gewählt, und Sie bemerkten das Problem erst am merkwürdigen Verhalten zur Laufzeit.

Klassiker – dependency hell: Eine Bibliothek verlangt den Logger in Version 1.2, eine andere – 1.3, und im classpath landen beide. Die JVM weiß nicht, was sie nehmen soll, und Sie fangen einen rätselhaften NoSuchMethodError.

Und außerdem: Selbst wenn eine Klasse als public deklariert ist, aber für interne Zwecke der Bibliothek gedacht, kann anderer Code sie trotzdem verwenden – und sich dabei etwas kaputtmachen. Große Projekte wurden zu Minenfeldern, und der Betrieb zum Abenteuer.

2. Was ist ein Modul in Java?

Modul: wie eine Schachtel mit Löchern

In Java 9 erschien das modulare System (JPMS). Ein Modul ist eine Schachtel mit Klassen und Paketen, in der Sie selbst „Löcher“ machen: Sie geben explizit an, was Sie nach außen zeigen (exports) und was Sie intern verstecken. Und ja: Selbst eine public-Klasse ist in anderen Modulen nicht sichtbar, wenn ihr Paket nicht exportiert wird.

Formale Definition

Ein Modul ist eine logische Einheit der Codeaufteilung, die zusammengehörige Pakete und Klassen vereint. Jedes Modul deklariert ausdrücklich:

  • was es exportiert – also für andere Module verfügbar macht (exports);
  • und was es importiert – von welchen anderen Modulen es abhängig ist (requires).

Schlüsselmerkmale eines Moduls:

  • Klare Grenzen. Es ist genau definiert, was von außen zugänglich ist und was nicht.
  • Explizite Abhängigkeiten. Ein Modul „sieht“ ein anderes Modul nicht ohne ein explizites requires.
  • Isolation. Interne Details können vollständig vor der Außenwelt verborgen werden.

3. Vorteile der Modularität

Verbesserte Kapselung

Es gibt eine neue Sichtbarkeitsebene – auf Modulebene. Selbst wenn eine Klasse public ist, aber ihr Paket nicht exportiert wird, ist sie nur innerhalb des Moduls sichtbar. Die Interna einer Bibliothek „sickern“ nicht länger nach außen.

Explizite Beschreibung der Abhängigkeiten

Benötigte Abhängigkeiten werden in module-info.java aufgelistet. Compiler und IDE warnen Sie im Voraus, wenn Sie ein Modul, von dem Sie abhängen, nicht angegeben haben.

Verbesserte Sicherheit und Zuverlässigkeit

Der eingeschränkte Zugriff auf interne APIs erschwert zufällige oder absichtliche Eingriffe. Das ist für große Bibliotheken und Plattformmodule entscheidend.

Möglichkeit, eine „abgespeckte“ JRE zu bauen

Mit jlink lässt sich eine minimale Laufzeitumgebung nur aus den benötigten Modulen zusammenstellen. In der Cloud und in Embedded-Szenarien spart das Speicherplatz und Arbeitsspeicher.

Bonus: schnellerer Start und kleinerer Footprint

Es werden nicht alle Module geladen, sondern nur tatsächlich verwendete – Anwendungen starten schneller und verbrauchen weniger Ressourcen.

4. Wo Module eingesetzt werden

In der Java-Standardbibliothek

Seit Java 9 ist die Plattform selbst modular. Beispiele:

  • java.base – das Basismodul (für jedes Programm obligatorisch).
  • java.sql – Arbeit mit Datenbanken.
  • java.xml – Arbeit mit XML.

Wenn Sie kein XML verwenden, landet das Modul java.xml nicht einmal in Ihrer Laufzeitumgebung.

In großen Anwendungen und Bibliotheken

Ein Must-have für Unternehmenssysteme, in denen Dutzende Teams Teile eines Monorepos entwickeln. Modularität reduziert Konflikte und vereinfacht die Wartung.

In eigenen Projekten

Selbst in einem Hobbyprojekt helfen Module, das architektonische Denken zu trainieren und den Code sauber zu halten, um „Spaghetti“ zu vermeiden.

5. Kurzer Syntaxüberblick: module-info.java

Das Wichtigste – die Datei module-info.java

Die Datei module-info.java liegt im Quellwurzelverzeichnis des Moduls und deklariert dessen Grenzen und Abhängigkeiten.

module my.awesome.module {
    exports com.example.api;        // Exportiertes Paket
    requires java.sql;              // Abhängigkeit vom Standardmodul
}

Wesentliche Schlüsselwörter:

  • module <Name> – deklariert ein Modul.
  • exports <Paket> – macht das Paket für andere Module verfügbar.
  • requires <Modul> – deklariert eine Abhängigkeit von einem anderen Modul.

Minimalbeispiel:

module com.myproject.core {
    exports com.myproject.core.api;
}

Beispiel mit Abhängigkeit:

module com.myproject.app {
    requires com.myproject.core;
    requires java.sql;
}

Zusätzliche Möglichkeiten (kurz): für Reflection gibt es opens, für Services – uses und provides ... with ... (Details – in fortgeschrittenen Vorlesungen).

6. Nützliche Feinheiten

Module sind wie „Pässe“ für Klassen. Früher konnte jede Klasse mit dem Visum public durch die Anwendung „reisen“. Jetzt braucht es zusätzlich einen modularen „Pass“ – den Export des Pakets (exports). Ohne ihn bleibt die Klasse „zu Hause“.

Dependency hell ist ein echter Begriff. Wenn Sie ClassNotFoundException: com.google.common.base.Strings gesehen haben, waren Sie dort. Module sollen diese „Dämonen“ mit strengen Grenzen und Abhängigkeiten vertreiben.

Ganz Java ist jetzt modular. Selbst wenn Sie keine eigenen Module schreiben, ist die Plattform bereits in Dutzende aufgeteilt. Probieren Sie den Befehl:

java --list-modules

Wie sich das auf die Entwicklung auswirkt

Sie steuern die Sichtbarkeit des Codes explizit, und interne Pakete werden nicht mehr „aus Versehen“ für alle sichtbar. IDE und Compiler fangen Fehler früher ab: Vergessen Sie, eine Abhängigkeit zu deklarieren, baut das Projekt nicht. Die Wartung großer Systeme wird einfacher: Es ist leichter zu verstehen, wer von wem abhängt und was sich bei Änderungen bricht.

7. Typische Fehler beim Umstieg auf Module

Fehler Nr. 1: Paket nicht exportiert, Klasse ist jedoch public. Sie haben die Klasse als public deklariert, aber ihr Paket nicht exportiert – andere Module können sie nicht verwenden. Der Compiler meldet: „Paket wird vom Modul nicht exportiert“.

Fehler Nr. 2: Abhängigkeit nicht per requires deklariert. Sie verwenden einen Typ aus einem anderen Modul, haben aber requires in module-info.java vergessen. Ergebnis – ein Kompilierfehler „Modul kann die benötigte Klasse nicht finden“.

Fehler Nr. 3: doppelte Modulnamen. In einem großen Projekt haben zwei Module versehentlich denselben Namen erhalten. Die JVM verzeiht das nicht – benennen Sie um und halten Sie ein einheitliches Namensschema ein.

Fehler Nr. 4: Standardmodule vergessen. Zum Beispiel verwenden Sie JDBC, haben aber requires java.sql; nicht hinzugefügt. In Java 8 war das „auch so sichtbar“, in 9+ – nicht.

Fehler Nr. 5: Versuch, eine interne Klasse eines anderen Moduls zu verwenden. Wenn ein Paket nicht exportiert wird, bleibt selbst eine public-Klasse von außen unsichtbar. Exportieren Sie das Paket oder legen Sie die API in eine „öffentliche“ Schicht.

Kommentare
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION