07.04.04 jar – Klassen zusammenfassen und ausführen

Bis jetzt mussten Ihre Programme immer über die IDE, umständlich über die Konsole, oder einer Startdatei (z. B. ein Bat-Skript für Windows) gestartet werden. Dies ist für die Endanwender natürlich alles andere als komfortabel. Auch schleppen Sie so immer eine Vielzahl an Dateien für ein noch so kleines Programm mit sich herum. Um dem Abhilfe zu schaffen gibt es JAR Dateien, welche Ihnen in diesem Kapitel näher gebracht werden.

Was sind JARs überhaupt?

Eine JAR ist wie ein Archiv, also bspw. eine *.zip-Datei. Ihre Klassen werden in dieses Archiv gepackt, so dass Sie am Ende nur noch eine einzige Datei haben, die Sie verteilen müssen. Das ist aber noch nicht alles. Ist auf dem Zielrechner eine JRE installiert, kann die JAR-Datei wie jedes andere Programm auch einfach per Doppelklick gestartet werden. Die Main-Klasse wird dabei in einem so genannten Manifest spezifiziert. Dazu aber später mehr.

Eine JAR erstellen

Die brennendste Frage ist nun natürlich, wie man denn eine solche JAR erstellt. Legen Sie sich hierzu zu Testzwecken die Klasse JARMethodTest im Package de.jbb mit folgendem Code an:

package de.jbb;

public class JARMethodTest {

  private String text;

  public void setText(String text) {
    this.text = text;
  }

  public String getText() {
    return this.text;
  }
}

Nach der Kompilierung der Klasse über den javac-Befehl, wechseln Sie in Ihr Projektverzeichnis, welches den de-Package-Ordner der JARMethodTest enthält, und tippen folgendes in die Konsole ein:

jar -cf MyFirstJar.jar -C de/jbb/ /

Erläuterung: Der Befehl jar wird zur Verarbeitung von JARs benötigt, c gibt an, dass ein neues Archiv erstellt werden soll, f bedeutet, dass auch der Name des Archivs angegeben wird, MyFirstJar.jar ist der Name der JAR-Datei, durch -C können ganze Verzeichnisse mit ins JAR aufgenommen werden, de/jbb/ / spezifiziert, dass das Verzeichnis de/jbb/ vom Standardverzeichnis / mit aufgenommen werden soll. Anstelle eines gesamten Verzeichnisses, ist es auch möglich nur bestimmte Klassen aufzunehmen. Z. B.

jar -cf MyFirstJar.jar de/jbb/JARMethodTest.class

Es wurde nun in Ihrem Projektverzeichnis eine JAR-Datei erstellt. Dies können Sie bei Interesse mit einem Zip-Programm Ihrer Wahl entpacken, um zu sehen, ob sich alles im JAR befindet.

Ohne Probleme ist es jetzt möglich das JAR in den Classpath einer Klasse mit aufzunehmen. Dies haben Sie bereits im Kapitel 07.03 Einbinden von externen Klassen (Classpath) gelernt. Zur Auffrischung finden Sie anbei ein weiteres, kleines Beispiel.

Eine JAR in den Classpath aufnehmen

Erstellen Sie sich zuerst eine Testklasse

import de.jbb.JARMethodTest;

public class TestJAR {

  public static void main(String[] args) {

    JARMethodTest jmt = new JARMethodTest();
    jmt.setText("Hello World");
    System.out.println(jmt.getText());
  }
}

Diese kompilieren Sie, indem Sie den Pfad zu Ihrer MyFirstJar.jar-Datei im Classpath angeben, da ansonsten die JARMethodTest Klasse nicht gefunden werden würde.

javac TestJAR.java -classpath pfad/zur/jar/MyFirstJar.jar

Beim Ausführen der TestJAR Klasse ist es selbstverständlich wieder erforderlich, den Classpath zu setzten.

java -classpath pfad/zur/jar/MyFirstJar.jar;. TestJAR

Eine JAR ausführen/Manifest

Wie zu Beginn dieses Kapitels erwähnt, können JAR-Dateien über einen Doppelklick ausgeführt werden. Hierzu benötigen Sie aber eine Klasse mit Main-Methode und einem Manifest. Erstellen wir uns nun zuerst eine solche Klasse, die Sie anschließend gewöhnlich kompilieren.

public class JARTest {

  public static void main(String[] args) {
    System.out.println("Hallo Welt");
  }
}

Bevor Sie aus dieser Klasse nun ein JAR machen, legen Sie sich an einer beliebigen Stelle eine neue Textdatei an und nennen diese Manifest.txt. Der Inhalt der Textdatei sieht wie folgt aus:

Manifest-Version: 1.0 
Main-Class: JARTest
Leerzeile

Zuerst wird eine Manifest-Version spezifiziert, anschließend die Klasse mit der Main-Methode festgelegt. Am Ende muss zwingend eine Leerzeile stehen! Falls Ihr Programm auf externe JARs zugreifen würde, könnten Sie diese mit einem zusätzlichen Attribut (Class-Path) kenntlich machen, und so den Classpath auch in einer JAR setzen. Dies würde dann in etwa so aussehen:

Manifest-Version: 1.0 
Main-Class: JARTest
Class-Path: MyFirstJar.jar MySecondJar.jar

Leerzeile nicht vergessen!

Wenn Sie jetzt ein JAR erstellen, müssen Sie noch zusätzlich angeben, dass Sie ein Manifest setzen (Erweiterung um den Parameter m) und wo es sich befindet (Parameter hinter dem Namen des JARs).

jar -cfm ExecutableJar.jar Manifest.txt JARTest.class

Alles in einem einzigen JAR

Für den Fall, dass Sie externe JARs verwenden, müssen diese zusammen mit Ihrem Programm ausgeliefert und über das Class-Path-Attribut im Manifest kenntlich gemacht werden. Im ersten Moment erscheint es wohl einfacher, alle externen JARs zusammen mit Ihrer eigentlichen Anwendung in ein einziges JAR zu integrieren. Hierzu müssen Sie die externen JAR-Dateien entpacken und als Class-Dateien zusammen mit Ihrer Applikation in ein einzelnes JAR verpacken. Um diesen Vorgang zu vereinfachen gibt es bspw. das Eclipse-Plugin FatJAR.

Auch wenn ein einzelnes, großes JAR auf den ersten Blick einfacher als viele kleine JARs erscheint, rate ich Ihnen inständig aus folgenden Gründen davon ab:

  • Lizenzen: Nicht alle Drittanbieter erlauben es ohne weiteres, ihre Bibliotheken zu entpacken und in Ihr JAR zu integrieren.
  • Größe: Wenn Sie alle Programmdaten in ein einziges JAR verpacken, kann es bei großen Projekten durchaus passieren, dass Ihre komplette Anwendung aus einer einzigen Datei besteht, die hunderte von Megabyte groß ist. Möchten Sie das?
  • Updatefähigkeit: Angenommen Sie möchten Ihr Programm aktualisieren, weil sich bspw. Ihr Programmcode verändert, oder ein Drittanbieter eine verwendete Bibliothek aktualisiert hat. Haben Sie alles in einer einzigen JAR, muss wirklich alles aktualisiert werden, obwohl eigentlich nur eine winziger Teil erneuert werden müsste. Verwenden Sie stattdessen mehrere JAR-Dateien, können Sie diese nach belieben einzeln updaten.
  • Gewohnheit: Haben Sie schon einmal ein halbwegs professionelles Programm gesehen, dass aus einer einzigen Datei besteht?

Es empfiehlt sich folglich immer, einzelne JARs auch einzeln auszuliefern – und nicht als großes Ganzes!

Wo ist die Ausgabe?

Starten Sie das so erzeugte JAR nun mit einem Doppelklick. Sie sehen, dass Sie nichts sehen. Es passiert nichts, keine Fehlermeldung, aber auch keine Ausgabe. Dies liegt daran, dass JAR-Dateien standardmäßig ohne Konsole gestartet werden (unter Windows mit javaw, siehe Kapitel 07.04.01 java/javaw Programme ausführen). Das heißt, dass nur Hintergrundverarbeitungen ausgeführt, oder grafische Benutzeroberflächen geladen werden. Ausgaben auf der Konsole mit bspw. System.out.println werden verschluckt. Wenn Sie die JAR dennoch mit Konsolenausgaben starten möchten, müssen Sie wieder den Umweg über die Eingabeaufforderung gehen. Dort können Sie java mit dem Parameter -jar und das auszuführenden JAR-Datei aufrufen, um die Konsolenausgaben zu erhalten.

java -jar ExecutableJar.jar

Sie haben zwar vermutlich noch nichts mit GUI-Programmierung entwickelt, dennoch möchte ich mit Ihnen an dieser Stelle die JARTest-Klasse um eine kleine GUI-Ausgabe erweitern. Ändern Sie die Klasse deshalb so ab:

import javax.swing.*;

public class JARTest {

  public static void main(String[] args) {
    System.out.println("Hallo Welt");
    JOptionPane.showMessageDialog(null, "Hallo Welt", "GUI Ausgabe", JOptionPane.INFORMATION_MESSAGE);
  }
}

Nachdem Sie diese Klasse kompiliert und in ein JAR verpackt haben, öffnet sich beim Ausführen des JARs mit einem Doppelklick eine kleine Box, die den Text „Hallo Welt“ enthält.

JAR-Dateien sollten Sie also nur dann einsetzen, wenn sichergestellt ist, dass die JARs über die Konsole mit java -jar ausgeführt, eine grafische Benutzeroberfläche geladen, ein Hintergrundprozess gestartet, oder die JAR als Bibliothek für ein anderes Programm eingesetzt wird.

Parameter bei der JAR-Erstellung

Sie kennen bereits die Parameter c, f, m und -C. Es gibt allerdings noch mehr davon:

Befehl Beschreibung
-c Neues Archiv erstellen
-t Inhaltsverzeichnis für Archiv auflisten
-x Genannte (oder alle) Dateien aus Archiv extrahieren
-u Vorhandenes Archiv aktualisieren
-v Ausführliche Ausgabe auf der Konsole erzeugen
-f Name der Archivdatei angeben
-m Manifest angeben und verwenden
-e Angabe eines Anwendungs-Einstiegspunkts für die, in einer ausführbaren jar-Datei gebündelte, eigenständige Anwendung
-0 Die JAR wird nicht komprimiert
-M Manifestdatei wird nicht automatisch erstellt
-i Erstellt index-Informationen für die angegebene JAR
-C Einbinden von Ordner und Verzeichnissen

Hinweise zur Verwendung von JARs

Beachten Sie, dass Sie einer JAR – im Gegensatz zu anderen, nativen Programmen – kein eigenes Logo/Icon setzen können. Auch kann die JAR nicht ausgeführt werden, wenn keine JRE installiert ist, bzw. sich ein anderes Programm wie z. B. WinRAR sich die Dateiendung *.jar „geschnappt“ hat. Es ist ebenfalls nicht möglich, VM-Parameter wie z. B. zur Erhöhung des zur Verfügung stehenden Speichers anzugeben. Und zu guter Letzt können Sie Dateien nicht so einfach aus dem JAR laden (siehe hierzu Kapitel 09. Input / Output (IO)). Dies muss immer über den ClassLoader geschehen:

InputStream is = getClass().getClassLoader().getResourceAsStream("properties.txt");

5 Replies to “07.04.04 jar – Klassen zusammenfassen und ausführen”

  1. abdollah

    Hallo,
    die Zeile: java -classpath pfad/zur/jar/MyFirstJar.jar;. TestJAR
    funktioniert nicht bei mir. Die folgende Fehlermeldung wird geworfen:
    Exception in thread „main“ java.lang.NoClassDefFoundError: TestJar (wrong name:
    de/importe/TestJar)

    de.importe ist der Package

  2. Stefan Kiesel

    Hallo abdollah,

    wie schaut Ihre Verzeichnisstruktur genau aus? Wie schaut die JAR-Struktur genau aus? Auf welcher Ebene führen Sie diesen Befehl aus?

    Grüße
    Stefan

  3. abdollah

    Hallo,

    ich habe einen Package erzeugt de. Und als Unterverzeichnisse sind: jbb, in dem sich die Klasse befindet, die importiert werden soll. importe, in dem sich die Klasse befindet, die die andere Klasse importiert:“ import de.jbb.JarMethodTest;“und die Main-Methode.

    Kompilieren und Ausführen tue ich in dem Pfad, …\importe. Und JAR-Datei befindet sich in dem selben Verzeichnis wie de.

    Danke sehr für deine Antwort

    Beste Grüsse

  4. Stefan Kiesel

    Hallo abdollah,

    so kann ich damit leider immer noch nicht viel Anfangen. Lade am Besten Screenshots von der Packagestruktur die das JAR benötigt, Screenshots vom Inhalt des JARs, dein Konsolenfenster sowie deinen kompletten Code bei einem FileHoster hoch und verweise darauf. Das ist das Einfachste.

    Grüße
    Stefan

  5. Andreas

    Vielen vielen Dank für diesen Post, hat mir bei einem technischen Problem heute sehr weitergeholfen!

Schreibe einen Kommentar

Diese Website verwendet Akismet, um Spam zu reduzieren. Erfahre mehr darüber, wie deine Kommentardaten verarbeitet werden.