Rheinwerk Computing < openbook >


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


Download:

- Listings, ca. 2,7 MB


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 14 Java Platform Module System
Pfeil 14.1 Klassenlader (Class Loader) und Modul-/Klassenpfad
Pfeil 14.1.1 Klassenladen auf Abruf
Pfeil 14.1.2 Klassenlader bei der Arbeit zusehen
Pfeil 14.1.3 JMOD-Dateien und JAR-Dateien
Pfeil 14.1.4 Woher die kleinen Klassen kommen: Die Suchorte und spezielle Klassenlader
Pfeil 14.1.5 Setzen des Modulpfades
Pfeil 14.2 Module entwickeln und einbinden
Pfeil 14.2.1 Wer sieht wen?
Pfeil 14.2.2 Plattform-Module und ein JMOD-Beispiel
Pfeil 14.2.3 Interne Plattformeigenschaften nutzen, --add-exports
Pfeil 14.2.4 Neue Module einbinden, --add-modules und --add-opens
Pfeil 14.2.5 Projektabhängigkeiten in Eclipse
Pfeil 14.2.6 Benannte Module und module-info.java
Pfeil 14.2.7 Automatische Module
Pfeil 14.2.8 Unbenanntes Modul
Pfeil 14.2.9 Lesbarkeit und Zugreifbarkeit
Pfeil 14.2.10 Modul-Migration
Pfeil 14.3 Zum Weiterlesen
 

Zum Seitenanfang

14    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

14.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

14.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 = true;

Forrest hambachForest = new Forrest();

if ( rweWantsToCutTrees ) {

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

p1.believeIn = hambachForest;

}

}

}



class Forrest { }



class Protest<T> {

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.

 

Zum Seitenanfang

14.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 unter Java 11 fast 500 Zeilen lang. Hier folgt ein Ausschnitt:

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

[0.010s][info][class,load] opened: C:\Program Files\Java\jdk-11\lib\modules

[0.032s][info][class,load] java.lang.Object source: jrt:/java.base

[0.032s][info][class,load] java.io.Serializable source: jrt:/java.base

[0.033s][info][class,load] java.lang.Comparable source: jrt:/java.base

[0.036s][info][class,load] java.lang.CharSequence source: jrt:/java.base

[0.037s][info][class,load] java.lang.String source: jrt:/java.base



[0.684s][info][class,load] sun.security.util.Debug source: jrt:/java.base

[0.685s][info][class,load] com.tutego.insel.tool.HambachForest source: file:/C:/ inline image

Inselprogramme/target/classes/

[0.687s][info][class,load] java.lang.PublicMethods$MethodList source: jrt:/java.base

[0.687s][info][class,load] java.lang.PublicMethods$Key source: jrt:/java.base

[0.689s][info][class,load] java.lang.Void source: jrt:/java.base

[0.690s][info][class,load] com.tutego.insel.tool.Forrest source: file:/C:/ inline image

Inselprogramme/target/classes/

[0.691s][info][class,load] jdk.internal.misc.TerminatingThreadLocal$1 source: jrt:/ inline image

java.base

[0.692s][info][class,load] java.lang.Shutdown source: jrt:/java.base

[0.692s][info][class,load] java.lang.Shutdown$Lock source: jrt:/java.base

Ä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 LocalDate. Doch ein LocalDate wird 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 Attribut. Wenn wir LocalDate mit zum Beispiel LocalDate.now() initialisieren, kommen stattliche 200 Klassendateien hinzu.

 

Zum Seitenanfang

14.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 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.

[»]  Hinweis

Die JVM greift selbst nicht auf diese Module zurück. Achten wir auf die Ausgabe des letzten Programms, dann steht in der ersten Zeile:

[0.007s][info][class,load] opened: C:\Program Files\Java\jdk-11\lib\modules

Die Datei module ist ca. 170 MiB groß und liegt in einem proprietären Dateiformat vor.

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.[ 231 ](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

14.1.4    Woher die kleinen Klassen kommen: Die 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

14.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

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

Nimm ein Java-Archiv library.jar im aktuellen Verzeichnis, die Ressourcen unter dem bin-Verzeichnis und alle JAR-Dateien im Verzeichnis lib in den Klassenpfad mit auf:

$ 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.[ 232 ](Weitere Details unter 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

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 ».«.

inline image  Um in Eclipse den Klassenpfad zu erweitern, damit etwa die Klassendateien von Java-Archiven berücksichtigt werden, ist Folgendes zu tun: Im Projekt das Kontaktmenü öffnen und Properties aufrufen, dann links unter Java Build Path gehen und anschließend im Reiter Libraries entweder Add JARs… (JARs sind im Projekt) oder Add External JARs… (JAR-Dateien liegen nicht im Projekt, sondern irgendwo anders im Dateisystem) nutzen.

[»]  Hinweis

Die sogenannten Bootstrap-Klassen aus den Paketen java(x).* (wie Object, String) stehen nicht im CLASSPATH.

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: Java SE 9 Standard-Bibliothek

Java SE 9 Standard-Bibliothek




Zum Rheinwerk-Shop: Algorithmen in Java

Algorithmen in Java




Zum Rheinwerk-Shop: Objektorientierte Programmierung

Objektorientierte Programmierung




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

InfoInfo



 

 


Copyright © Rheinwerk Verlag GmbH 2021

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