Rheinwerk Computing < openbook >


 
Inhaltsverzeichnis
Materialien zum Buch
Vorwort
1 Java ist auch eine Sprache
2 Imperative Sprachkonzepte
3 Klassen und Objekte
4 Arrays und ihre Anwendungen
5 Der Umgang mit Zeichen und Zeichenketten
6 Eigene Klassen schreiben
7 Objektorientierte Beziehungsfragen
8 Schnittstellen, Aufzählungen, versiegelte Klassen, Records
9 Ausnahmen müssen sein
10 Geschachtelte Typen
11 Besondere Typen der Java SE
12 Generics<T>
13 Lambda-Ausdrücke und funktionale Programmierung
14 Architektur, Design und angewandte Objektorientierung
15 Java Platform Module System
16 Die Klassenbibliothek
17 Einführung in die nebenläufige Programmierung
18 Einführung in Datenstrukturen und Algorithmen
19 Einführung in grafische Oberflächen
20 Einführung in Dateien und Datenströme
21 Einführung ins Datenbankmanagement mit JDBC
22 Bits und Bytes, Mathematisches und Geld
23 Testen mit JUnit
24 Die Werkzeuge des JDK
A Java SE-Module und Paketübersicht
Stichwortverzeichnis


Buch bestellen
Ihre Meinung?



Spacer
<< zurück
Java ist auch eine Insel von Christian Ullenboom

Einführung, Ausbildung, Praxis
Buch: Java ist auch eine Insel


Java ist auch eine Insel

Pfeil 15 Java Platform Module System
Pfeil 15.1 Klassenlader (Class Loader) und Modul-/Klassenpfad
Pfeil 15.1.1 Klassenladen auf Abruf
Pfeil 15.1.2 Klassenlader bei der Arbeit zusehen
Pfeil 15.1.3 JMOD-Dateien und JAR-Dateien
Pfeil 15.1.4 Woher die Klassen kommen: Suchorte und spezielle Klassenlader
Pfeil 15.1.5 Setzen des Modulpfades
Pfeil 15.2 Module entwickeln und einbinden
Pfeil 15.2.1 Wer sieht wen?
Pfeil 15.2.2 Plattform-Module und ein JMOD-Beispiel
Pfeil 15.2.3 Interne Plattformeigenschaften nutzen, --add-exports
Pfeil 15.2.4 Neue Module einbinden, --add-modules und --add-opens
Pfeil 15.2.5 Projektabhängigkeiten in Eclipse
Pfeil 15.2.6 Benannte Module und module-info.java
Pfeil 15.2.7 Automatische Module
Pfeil 15.2.8 Unbenanntes Modul
Pfeil 15.2.9 Lesbarkeit und Zugreifbarkeit
Pfeil 15.2.10 Modul-Migration
Pfeil 15.3 Zum Weiterlesen
 

Zum Seitenanfang

15    Java Platform Module System Zur vorigen ÜberschriftZur nächsten Überschrift

»Das Ganze ist mehr als die Summe seiner Teile.«

– Aristoteles (384–322 v. u. Z.)

Nachdem wir uns das Zusammenspiel von Klassen angeschaut haben, wollen wir uns in diesem Kapitel mit der Frage beschäftigen, wie einzelne Typen oder Verbände von anderen Programmen gut wiederverwendet werden können. Wir wollen uns das bei einzelnen Komponenten anschauen und bei einer Sammlung von Klassen, die zu Archiven zusammengebunden werden.

 

Zum Seitenanfang

15.1    Klassenlader (Class Loader) und Modul-/Klassenpfad Zur vorigen ÜberschriftZur nächsten Überschrift

Ein Klassenlader ist dafür verantwortlich, die Binärrepräsentation einer Klasse aus einem Hintergrundspeicher oder Hauptspeicher zu laden. Aus der Datenquelle (im Allgemeinen ist das die .class-Datei) liefert der Klassenlader ein Byte-Array mit den Informationen, die im zweiten Schritt dazu verwendet werden, die Klasse ins Laufzeitsystem einzubringen; das ist Linking. Es gibt vordefinierte Klassenlader und die Möglichkeit, eigene Klassenlader zu schreiben, um etwa verschlüsselte Klassendateien vom Netzwerk zu beziehen oder komprimierte .class-Dateien aus Datenbanken zu laden.

 

Zum Seitenanfang

15.1.1    Klassenladen auf Abruf Zur vorigen ÜberschriftZur nächsten Überschrift

Nehmen wir zu Beginn ein einfaches Programm mit drei Klassen:

package com.tutego.insel.tool;



public class HambachForest {

public static void main( String[] args ) {

boolean rweWantsToCutTrees = false;

Forest hambachForest = new Forest();

if ( rweWantsToCutTrees ) {

Protest<Forest> p1 = new Protest<>();

p1.believeIn = hambachForest;

}

}

}



class Forest { }



class Protest<T> {

static java.awt.Rectangle MIN_BOUNDARY;

T believeIn;

java.time.LocalDate since;

}

Wenn die Laufzeitumgebung das Programm HambachForest startet, muss sie eine Reihe von Klassen laden. Das tut sie dynamisch zur Laufzeit. Sofort wird klar, dass es zumindest HambachForest sein muss. Und da die JVM die statische main(String[])-Methode aufruft und Optionen übergibt, muss auch String geladen sein. Unsichtbar stecken noch andere referenzierte Klassen dahinter, die nicht direkt sichtbar sind. So wird zum Beispiel Object geladen, da implizit in der Klassendeklaration von HambachForest steht: class HambachForest extends Object. Intern ziehen die Typen viele weitere Typen nach sich. String implementiert Serializable, CharSequence und Comparable, also müssen diese drei Schnittstellen auch geladen werden. Und so geht das weiter, je nachdem, welche Programmpfade abgelaufen werden. Wichtig ist aber, zu verstehen, dass diese Klassendateien so spät wie möglich geladen werden.

Arbeit beim Laden der Typen: Wer den Gitarristen will, muss auch den Verstärker laden, muss auch den Bus laden …

Abbildung 15.1     Arbeit beim Laden der Typen: Wer den Gitarristen will, muss auch den Verstärker laden, muss auch den Bus laden …

 

Zum Seitenanfang

15.1.2    Klassenlader bei der Arbeit zusehen Zur vorigen ÜberschriftZur nächsten Überschrift

Im Beispiel lädt die Laufzeitumgebung selbstständig die Klassen (implizites Klassenladen). Klassen lassen sich auch mit Class.forName(String) über ihren Namen laden (explizites Klassenladen).

Um zu sehen, welche Klassen überhaupt geladen werden, lässt sich der virtuellen Maschine beim Start der Laufzeitumgebung ein Schalter mitgeben: -verbose:class. Dann gibt die Maschine beim Lauf alle Typen aus, die sie lädt. Nehmen wir das Beispiel von eben, so ist die Ausgabe mit dem aktivierten Schalter rund 450 Zeilen lang. Hier folgt ein Ausschnitt:

$ java -verbose:class com.tutego.insel.tool.HambachForest

[0.008s][info][class,load] java.lang.Object source: shared objects file

[0.008s][info][class,load] java.io.Serializable source: shared objects file

[0.008s][info][class,load] java.lang.Comparable source: shared objects file

[0.009s][info][class,load] java.lang.CharSequence source: shared objects file

[0.009s][info][class,load] java.lang.constant.Constable source: shared objects file

...

[0.034s][info][class,load] sun.security.util.Debug source: shared objects file

[0.034s][info][class,load] com.tutego.insel.tool.HambachForest source: file:/C:/.../target/classes/

[0.035s][info][class,load] java.lang.PublicMethods$MethodList source: shared objects file

[0.035s][info][class,load] java.lang.PublicMethods$Key source: shared objects file

[0.035s][info][class,load] java.lang.Void source: shared objects file

[0.035s][info][class,load] com.tutego.insel.tool.Forest source: file:/C:/.../target/classes/

[0.035s][info][class,load] java.lang.Shutdown source: shared objects file

[0.035s][info][class,load] java.lang.Shutdown$Lock source: shared objects file

Auffällig ist, dass die Klasse Protest nicht geladen wird. Ändern wir die Variable rweWantsToCutTrees in true, so wird unsere Klasse Protest geladen, und in der Ausgabe kommt nur eine Zeile hinzu! Das wundert auf den ersten Blick, denn die Klasse referenziert Rectangle und LocalDate. Doch beide Typen werden nicht benötigt, also auch nicht geladen. Der Klassenlader bezieht nur Klassen, wenn sie für den Programmablauf benötigt werden, nicht aber durch die reine Deklaration als Klassen-/Objektvariablen. Wenn wir LocalDate mit zum Beispiel LocalDate.now() initialisieren, kommen stattliche 200 Klassendateien hinzu.

 

Zum Seitenanfang

15.1.3    JMOD-Dateien und JAR-Dateien Zur vorigen ÜberschriftZur nächsten Überschrift

Der Klassenlader bezieht .class-Dateien nicht nur aus Verzeichnissen, sondern in der Regel aus Containern. So müssen keine Verzeichnisse ausgetauscht werden, sondern nur einzelne Dateien. Als Containerformate finden wir JMOD (neu eingeführt in Java 9) und JAR. Wenn Java-Software ausgeliefert wird, bieten sich JAR- oder JMOD-Dateien an, denn es ist einfacher und platzsparender, nur ein komprimiertes Archiv weiterzugeben als einen großen Dateibaum.

JAR-Dateien

Sammlungen von Java-Klassendateien und Ressourcen werden in der Regel in Java-Archiven, kurz JAR-Dateien, zusammengefasst. Diese Dateien sind im Grunde ganz normale ZIP-Archive mit einem besonderen Verzeichnis META-INF für Metadateien. Das JDK bringt im bin-Verzeichnis das Werkzeug jar zum Aufbau und Extrahieren von JAR-Dateien mit.

JAR-Dateien behandelt die Laufzeitumgebung wie Verzeichnisse von Klassendateien und Ressourcen. Zudem haben Java-Archive den Vorteil, dass sie signiert werden können und illegale Änderungen auffallen. JAR-Dateien können Modulinformationen beinhalten. Dann heißen sie auf Englisch modular JAR.

JMOD-Dateien

Das Format JMOD ist speziell für Module, und es organisiert Typen und Ressourcen. Zum Auslesen und Packen gibt es im bin-Verzeichnis des JDK das Werkzeug jmod. Die JVM greift selbst nicht auf diese Module zurück.

JAR vs. JMOD

Module können in JMOD- und JAR-Container gepackt werden. Wenn ein JAR kein Modular JAR ist, also keine Modulinformationen enthält, so fehlen zentrale Informationen, wie Abhängigkeiten oder eine Version. Ein JMOD ist immer ein benanntes Modul.

JMOD-Dateien sind nicht so flexibel wie JAR-Dateien, denn sie können nur zur Übersetzungszeit und zum Linken eines Runtime-Images (dafür gibt es das Kommandozeilenwerkzeug jlink) genutzt werden. JMOD-Dateien können nicht wie JAR-Dateien zur Laufzeit verwendet werden. Das Dateiformat ist proprietär und kann sich jederzeit ändern – es ist nichts Genaues spezifiziert.[ 233 ](Die http://openjdk.java.net/jeps/261 besagt, dass es ein ZIP ist. ) Der einziger Vorteil von JMOD: Native Bibliotheken lassen sich standardisiert einbinden.

 

Zum Seitenanfang

15.1.4    Woher die Klassen kommen: Suchorte und spezielle Klassenlader Zur vorigen ÜberschriftZur nächsten Überschrift

Die Laufzeitumgebung nutzt zum Laden nicht nur einen Klassenlader, sondern mehrere. Das ermöglicht es, unterschiedliche Orte für die Klassendateien festzulegen. Ein festes Schema bestimmt die Suche nach den Klassen:

  1. Klassentypen wie String, Object oder Point stehen in einem ganz speziellen Archiv. Wenn ein eigenes Java-Programm gestartet wird, so sucht die virtuelle Maschine die angeforderten Klassen zuerst in diesem Archiv. Da es elementare Klassen sind, die zum Hochfahren eines Systems gehören, werden sie Bootstrap-Klassen genannt. Die Implementierung dieses Bootstrap-Klassenladers ist Teil der Laufzeitumgebung.

  2. Ist eine Klasse keine Bootstrap-Klasse, beginnt der System-Klassenlader bzw. Applikations-Klassenlader die Suche im Modulpfad (ehemals Klassenpfad/Classpath). Diese Pfadangabe besteht aus einer Aufzählung von Modulen, in denen die Laufzeitumgebung nach den Klassendateien und Ressourcen sucht.

 

Zum Seitenanfang

15.1.5    Setzen des Modulpfades Zur vorigen ÜberschriftZur nächsten Überschrift

Wo die JVM die Klassen findet, muss ihr mitgeteilt werden, und das ist in der Praxis elementar für die Auslieferung, die auch mit dem englischen Begriff als deployment bezeichnet wird. Java wartet mit dem Laden der Klassen so lange, bis sie benötigt werden. Es gibt zum Beispiel Programmabläufe nur zu besonderen Bedingungen, und wenn dann erst spät ein neuer Typ referenziert wird, der nicht vorhanden ist, fällt dieser Fehler erst sehr spät auf. Dem Compiler müssen folglich nicht nur die Quellen für Klassen und Ressourcen der eigenen Applikation mitgeteilt werden, sondern alle vom Programm referenzierten Typen aus zum Beispiel quelloffenen und kommerziellen Bibliotheken.

Sollen in einem Java-Projekt Dateien aus einem Verzeichnis oder einem externen Modul geholt werden, so besteht der übliche Weg darin, diese Dateien im Modulpfad anzugeben. Diese Angabe ist für alle SDK-Werkzeuge notwendig – am häufigsten ist sie beim Compiler und bei der Laufzeitumgebung zu sehen.

Setzen des Klassenpfades

Vor Java 9 gab es nur JAR-Dateien und Verzeichnisse im Klassenpfad. Auch wenn es ab Java 9 weiterhin den Klassenpfad gibt, sollte er auf lange Sicht leer sein.

Es gibt zwei Möglichkeiten zur Aufnahme von Verzeichnissen und JAR-Dateien in den Klassenpfad:

  • ein Schalter

  • eine Umgebungsvariable

[»]  Hinweis

Klassen aus den java-Paketen (wie Object, String) und einigen javax-Paketen nennen sich Bootstrap-Klassen. Sie stehen nicht im benutzerdefinierten Klassenpfad, sondern werden von der Installation vorgegeben.

Der Schalter -classpath

Die Suchorte lassen sich flexibel angeben, wobei die erste Variante einem SDK-Werkzeug über den Schalter -classpath (kurz -cp) die Klassendateien bzw. Archive liefert:

$ java -classpath classpath1;classpath2 mein.paket.MainClass

Der Klassenpfad enthält Wurzelverzeichnisse der Pakete und JAR-Dateien, also Archive von Klassendateien und Ressourcen.

[zB]  Beispiel

Ein Java-Archiv library.jar im aktuellen Verzeichnis, die Ressourcen unter dem bin-Verzeichnis und alle JAR-Dateien im Verzeichnis lib werden so in den Klassenpfad aufgenommen:

$ java -cp "library.jar;bin/.;lib/*" mein.paket.MainClass

Unter Windows ist der Trenner ein Semikolon, unter Unix ein Doppelpunkt. Das Sternchen steht für alle JAR-Dateien, es ist keine übliche Wildcard, wie z. B. parser*.jar.[ 234 ](Weitere Details unter der nun etwas angestaubten Seite https://docs.oracle.com/javase/8/docs/technotes/tools/windows/classpath.html) Sehen Kommandozeilen der Betriebssysteme ein *, beginnen sie in der Regel eine eigene Verarbeitung. Daher muss hier die gesamte Pfadangabe in doppelten Anführungszeichen stehen.

Die Umgebungsvariable CLASSPATH

Eine Alternative zum Schalter -cp ist das Setzen der Umgebungsvariablen CLASSPATH mit einer Zeichenfolge, die Pfadangaben spezifiziert:

$ SET CLASSPATH=classpath1;classpath2

$ java mein.paket.MeinClass

inline image  Problematisch ist der globale Charakter der Variablen, sodass lokale -cp-Angaben besser sind. Außerdem »überschreiben« die -cp-Optionen die Einträge in CLASSPATH. Zu guter Letzt: Ist weder CLASSPATH noch eine -cp-Option gesetzt, besteht der Klassenpfad für die JVM nur aus dem aktuellen Verzeichnis, also ».«.

[»]  Hinweis

Es ist eher ungewöhnlich, in der Entwicklungsumgebung den Klassenpfad direkt zu erweitern, denn in der Regel bestimmen Maven- oder Gradle-Projekte die Abhängigkeiten, und dann nimmt die IDE die Pfade zu den Java-Dateien automatisch in den Klassenpfad mit auf.

Classpath-Hell

Java-Klassen in JAR-Dateien auszuliefern, ist der übliche Weg. Es gibt aber zwei Probleme:

  1. Aus Versehen können zwei JAR-Dateien mit unterschiedlichen Versionen im Klassenpfad liegen. Nehmen wir an, es sind parser-1.2.jar und parser-2.0.jar, wobei sich bei der neuen Version API und Implementierung leicht geändert haben. Das fällt vielleicht am Anfang nicht auf, denn einen Ladefehler gibt es für den Typ nicht, er ist ja da – die JVM nimmt den ersten Typ, den sie findet. Nur wenn ein Programm auf die neue API zurückgreift, aber die geladene Klasse vom alten JAR stammt, knallt es zur Laufzeit. Bei doppelten JARs mit unterschiedlichen Versionen führt eine Umsortierung im Klassenpfad zu einem ganz anderen Ergebnis. Zum Glück lässt sich das Problem relativ schnell lösen.

  2. Zwei Java-Bibliotheken – nennen wir sie vw.jar und audi.jar – benötigen je eine Neben-JAR zum Arbeiten. Doch während vw.jar die Version bosch-1.jar benötigt, benötigt audi.jar die Version bosch-2.jar. Das ist ein Problem, denn JARs sind im Standard-Klassenpfad immer global, aber nicht hierarchisch. Es kann also kein JAR ein »lokales« Unter-JAR haben.

Lösungen für das zweite Problem gibt es einige, wobei zu neuen Klassenladern gegriffen wird. Bekannt ist OSGi, das in der Java-Welt aber etwas an Fahrt verloren hat.

 


Ihre Meinung?

Wie hat Ihnen das Openbook gefallen? Wir freuen uns immer über Ihre Rückmeldung. Schreiben Sie uns gerne Ihr Feedback als E-Mail an kommunikation@rheinwerk-verlag.de

<< zurück
 Zum Rheinwerk-Shop
Zum Rheinwerk-Shop: Java ist auch eine Insel Java ist auch eine Insel

Jetzt Buch bestellen


 Buchempfehlungen
Zum Rheinwerk-Shop: Captain CiaoCiao erobert Java

Captain CiaoCiao erobert Java




Zum Rheinwerk-Shop: Algorithmen in Java

Algorithmen in Java




Zum Rheinwerk-Shop: Spring Boot 3 und Spring Framework 6

Spring Boot 3 und Spring Framework 6




Zum Rheinwerk-Shop: Java SE 9 Standard-Bibliothek

Java SE 9 Standard-Bibliothek




 Lieferung
Versandkostenfrei bestellen in Deutschland, Österreich und in die Schweiz

InfoInfo



 

 


Copyright © Rheinwerk Verlag GmbH 2024

Für Ihren privaten Gebrauch dürfen Sie die Online-Version natürlich ausdrucken. Ansonsten unterliegt das Openbook denselben Bestimmungen, wie die gebundene Ausgabe: Das Werk einschließlich aller seiner Teile ist urheberrechtlich geschützt.

Alle Rechte vorbehalten einschließlich der Vervielfältigung, Übersetzung, Mikroverfilmung sowie Einspeicherung und Verarbeitung in elektronischen Systemen.

 

[Rheinwerk Computing]



Rheinwerk Verlag GmbH, Rheinwerkallee 4, 53227 Bonn, Tel.: 0228.42150.0, Fax 0228.42150.77, service@rheinwerk-verlag.de



Cookie-Einstellungen ändern