1. Was ist module-info.java und wo befindet es sich?
Ein Modul in Java beginnt immer mit der Datei module-info.java. Sie ist so etwas wie der „Pass“ des Moduls, in dem Sie seinen Namen, exportierte Pakete und Abhängigkeiten von anderen Modulen deklarieren.
Wo findet man sie?
Die Datei module-info.java sollte sich im Wurzelverzeichnis des Source-Ordners Ihres Moduls befinden. Wenn Ihre Projektstruktur zum Beispiel so aussieht:
project-root/
└── src/
└── my.module.name/
├── module-info.java
└── com/
└── example/
└── api/
└── MyClass.java
Hier ist my.module.name der Modulname (zu den Namensregeln gleich mehr).
Ohne diese Datei gilt ein Modul schlicht nicht als Modul!
Wenn sie fehlt, ist es ein normales „altes“ Java‑Projekt, selbst wenn es viele Ordner und Klassen hat.
2. Syntax von module-info.java: grundlegende Elemente
Schauen wir uns gleich ein Beispiel für die einfachste Datei an – das ist wirklich nicht schlimm, versprochen!
module my.module.name {
exports com.example.api;
requires java.sql;
}
Schauen wir uns die Teile an:
module my.module.name { ... }
Das ist die Deklaration eines Moduls mit dem Namen my.module.name. Der Modulname ist ein eindeutiger Bezeichner und stimmt üblicherweise mit dem Wurzelpaket überein (zum Beispiel com.example.app).
Fun Fact: Wenn Sie ein Modul java.base nennen, ist der Compiler wenig begeistert. Versuchen Sie nicht, Standardmodule zu ersetzen.
exports com.example.api;
Diese Zeile sagt: „Ich, das Modul, gebe alles frei, was im Paket com.example.api liegt.“ Alles, was in diesem Paket steht und als public markiert ist, ist für andere Module sichtbar. Alles andere bleibt intern.
requires java.sql;
Hier geben wir dem Compiler ehrlich zu: „Für meine Arbeit brauche ich das Standardmodul java.sql.“ Ohne diese Deklaration lässt der Compiler uns die Klassen dieses Moduls nicht verwenden.
Zusätzliche Schlüsselwörter (für den Überblick)
- opens <package>; – öffnet ein Paket für Reflection (z. B. für Serialisierungsbibliotheken wie Jackson).
- uses <service-interface>; – gibt an, dass das Modul einen Service (ein Interface) verwendet.
- provides <service-interface> with <implementation-class>; – teilt mit, dass das Modul eine Service-Implementierung bereitstellt.
In dieser Vorlesung konzentrieren wir uns auf exports und requires – sie werden in der überwiegenden Mehrzahl der Übungs- und Produktivprojekte benötigt.
3. Beispiele zu module-info.java
Beispiel 1. Minimalmodul
module com.example.hello {
exports com.example.hello.api;
}
- Dieses Modul exportiert nur das Paket com.example.hello.api.
- Alles, was zum Beispiel in com.example.hello.internal liegt, bleibt vor anderen Modulen verborgen, selbst wenn es dort public-Klassen gibt.
Beispiel 2. Modul mit Abhängigkeit
module com.example.dbclient {
exports com.example.db.api;
requires java.sql;
}
Wir können JDBC verwenden – aber nur, weil wir die Abhängigkeit von java.sql explizit deklariert haben.
Beispiel 3. Mehrere exportierte Pakete
module com.example.library {
exports com.example.library.api;
exports com.example.library.utils;
}
Man kann beliebig viele Pakete exportieren (aber exportieren Sie nicht alles wahllos – darin liegt ja der Sinn von Modulen!).
Einschränkungen und Regeln
Modulname
- Stimmt üblicherweise mit dem Wurzelpaket überein (zum Beispiel com.example.app).
- Darf nicht mit Namen von Standardmodulen übereinstimmen (java.base, java.sql usw.).
- Darf keine Leerzeichen, Sonderzeichen enthalten, nicht mit einer Ziffer beginnen usw.
- Empfehlung: Verwenden Sie den umgekehrten Domainnamen Ihrer Organisation oder Ihres Projekts, um Konflikte zu vermeiden.
Ein Modul – eine module-info.java
In einem Modul darf es nur eine solche Datei geben. Gibt es zwei, veranstaltet der Compiler einen „modularen Aufstand“.
Export eines Pakets nur aus genau einem Modul
Dasselbe Paket darf nicht aus zwei verschiedenen Modulen exportiert werden. Das ist, als hätten Sie zwei Pässe auf denselben Namen – der Staat wird das nicht gutheißen.
Pakete innerhalb des Moduls
Es können nur Pakete exportiert werden, die in der tatsächlichen Source-Struktur dieses Moduls existieren. Das Exportieren eines nicht existierenden Pakets führt zu einem Compilerfehler.
5. Praxis: Wir erstellen module-info.java im Projekt
Nehmen wir an, wir haben ein einfaches Projekt mit folgendem Inhalt:
project-root/
└── src/
└── com.example.greetings/
├── module-info.java
└── com/
└── example/
└── greetings/
├── api/
│ └── Greeter.java
└── internal/
└── SecretSauce.java
Schritt 1. module-info.java erstellen
module com.example.greetings {
exports com.example.greetings.api;
}
Schritt 2. Wir versuchen, eine Klasse aus dem internal‑Paket in einem anderen Modul zu verwenden
Nehmen wir an, wir haben ein zweites Modul com.example.app, das auf SecretSauce zugreifen möchte:
module com.example.app {
requires com.example.greetings;
}
import com.example.greetings.internal.SecretSauce; // FEHLER!
Ergebnis:
Der Compiler meldet: „Das Paket com.example.greetings.internal wird vom Modul com.example.greetings nicht exportiert.“ Selbst wenn die Klasse SecretSauce public ist, ist sie für andere Module nicht zugänglich.
Das ist echte Kapselung auf Modulebene!
Schritt 3. Wir lassen requires weg
Wenn wir in com.example.app kein requires com.example.greetings; schreiben und versuchen, eine Klasse aus com.example.greetings.api zu verwenden, meldet der Compiler einen Fehler:
package com.example.greetings.api is not visible
6. Typische Fehler im Umgang mit module-info.java
Fehler Nr. 1: Modulname passt nicht zur Projektstruktur.
Wenn Sie das Modul com.example.app nennen, die Ordnerstruktur aber src/main/java/app lautet, versteht der Compiler nicht, was Sie von ihm wollen. Der Modulname entspricht in der Regel dem Wurzelpaket, und die Ordner sollten das widerspiegeln.
Fehler Nr. 2: Alles wahllos exportieren.
Exportieren Sie nur das, was wirklich für andere Module sichtbar sein soll. Machen Sie nicht exports com.example;, nur weil „es einfacher ist“. Das verletzt die Kapselung.
Fehler Nr. 3: requires vergessen.
Wenn Sie Klassen aus einem anderen Modul oder der Standardbibliothek verwenden (zum Beispiel java.sql), die Abhängigkeit aber nicht deklarieren – gibt es einen Compilerfehler.
Fehler Nr. 4: Nicht existentes Paket in exports.
Wenn Sie exports com.example.foo; schreiben, dieses Paket aber nicht existiert – sagt der Compiler sinngemäß, dass Sie „Luft exportieren“.
Fehler Nr. 5: Public-Klassen in einem nicht exportierten Paket.
Wenn eine Klasse public ist, aber in einem Paket liegt, das nicht exportiert wird, ist diese Klasse nur innerhalb des Moduls sichtbar. Das ist kein Fehler, überrascht Einsteiger aber oft.
Fehler Nr. 6: Mehrere module-info.java in einem Modul.
In einem Modul sollte es nur eine Datei module-info.java geben. Wenn es zwei gibt, kann der Compiler das Projekt nicht bauen.
GO TO FULL VERSION