jar în Java

Module și unelte JDK JDK 1.1 (opțiunile lungi --create, --main-class: Java 9)

jar împachetează clasele compilate (și resursele) într-o singură arhivă .jar, care poate fi distribuită și rulată cu java -jar.

Sintaxă
jar --create --file app.jar --main-class ro.dexblog.App -C out .
jar cf app.jar -C out .      # forma scurtă: c = create, f = file
jar tf app.jar               # listează conținutul
jar xf app.jar               # extrage
java -jar app.jar            # rulează

Ce face jar

Un fișier .jar (Java ARchive) este o arhivă ZIP cu fișiere .class, resurse (imagini, fișiere de configurare) și un manifest, META-INF/MANIFEST.MF. Bibliotecile Java se distribuie aproape mereu ca .jar, iar aplicațiile la fel.

Comanda are o formă scurtă, în stilul lui tar (c = creează, t = listează, x = extrage, f = fișierul, v = detaliat, e = clasa principală), și una lungă, mai lizibilă (--create, --file, --main-class). -C dir . adaugă conținutul directorului dir, fără prefixul lui.

Dacă manifestul are Main-Class, arhiva este „executabilă”: java -jar app.jar. Altfel, jar-ul se pune în classpath (java -cp app.jar ro.dexblog.App). Jar-urile din care depinde aplicația nu sunt incluse automat; uneltele de build (Maven, Gradle) pot crea un „fat jar” cu totul inclus.

Exemple

Exemplul 1: De la surse la un jar executabil

Compilăm în out/, creăm jar-ul cu clasa principală setată în manifest, apoi îl listăm și îl rulăm. Argumentele de după numele jar-ului ajung în args.

// src/ro/dexblog/meteo/Prognoza.java
package ro.dexblog.meteo;

public class Prognoza {
    public static String pentru(String oras) {
        return switch (oras) {
            case "Iași" -> "senin, 14 °C";
            case "Cluj" -> "ploaie, 9 °C";
            default -> "date indisponibile";
        };
    }
}

// src/ro/dexblog/meteo/App.java
package ro.dexblog.meteo;

public class App {
    public static void main(String[] args) {
        String[] orase = args.length > 0 ? args : new String[] {"Iași", "Cluj"};
        for (String oras : orase) {
            System.out.println(oras + ": " + Prognoza.pentru(oras));
        }
    }
}

Rezultat în terminal

$ javac -d out src/ro/dexblog/meteo/*.java
$ jar --create --file meteo.jar --main-class ro.dexblog.meteo.App -C out .
$ jar tf meteo.jar
META-INF/
META-INF/MANIFEST.MF
ro/
ro/dexblog/
ro/dexblog/meteo/
ro/dexblog/meteo/App.class
ro/dexblog/meteo/Prognoza.class
$ java -jar meteo.jar Iași Brașov
Iași: senin, 14 °C
Brașov: date indisponibile

Exemplul 2: Manifestul, forma scurtă și jar-ul în classpath

Manifestul conține linia Main-Class adăugată de --main-class. Forma scurtă cvf arată fiecare fișier adăugat și cât s-a comprimat. Fără Main-Class, jar-ul se folosește cu -cp și numele clasei.

$ unzip -p meteo.jar META-INF/MANIFEST.MF
$ jar cvf meteo-v.jar -C out .
$ java -cp meteo-v.jar ro.dexblog.meteo.App Cluj

Rezultat în terminal

Manifest-Version: 1.0
Created-By: 27 (Homebrew)
Main-Class: ro.dexblog.meteo.App

added manifest
adding: ro/(in = 0) (out= 0)(stored 0%)
adding: ro/dexblog/(in = 0) (out= 0)(stored 0%)
adding: ro/dexblog/meteo/(in = 0) (out= 0)(stored 0%)
adding: ro/dexblog/meteo/App.class(in = 1160) (out= 643)(deflated 44%)
adding: ro/dexblog/meteo/Prognoza.class(in = 636) (out= 457)(deflated 28%)

Cluj: ploaie, 9 °C

Greșeli frecvente

  • Creezi jar-ul din directorul greșit (jar cf app.jar out): clasele ajung ca out/ro/dexblog/..., iar java nu le mai găsește. Folosește -C out ..
  • Rulezi java -jar pe un jar fără Main-Class: no main manifest attribute, in app.jar.
  • Folosești java -jar app.jar împreună cu -cp lib/* pentru dependențe: cu -jar, opțiunea -cp este ignorată. Pune dependențele în Class-Path din manifest sau rulează cu -cp și numele clasei.

Comenzi înrudite

  • javac: javac este compilatorul Java: transformă fișierele .java în fișiere .class (bytecode) și raportează erorile și avertismentele.
  • java (lansatorul): java pornește mașina virtuală Java și rulează un program: o clasă compilată, un fișier .jar sau direct un fișier sursă .java.
  • Module (module-info.java): Un modul (module-info.java) grupează pachete și declară explicit de ce module depinde (requires) și ce pachete oferă altora (exports).
  • Pachete și importuri (package, import): package așază clasa într-un pachet (un spațiu de nume, ca un director); import permite folosirea claselor din alte pachete după numele scurt.