javac în Java

javac este compilatorul Java: transformă fișierele .java în fișiere .class (bytecode) și raportează erorile și avertismentele.

Sintaxă
javac Main.java                      # compilează, rezultă Main.class
javac -d out src/ro/dexblog/*.java   # pune .class-urile în out/
javac -Xlint:all -Werror Main.java   # toate avertismentele, tratate ca erori
javac --release 21 Main.java         # bytecode pentru Java 21

Ce face javac

javac citește codul sursă, verifică sintaxa și tipurile și produce câte un fișier .class pentru fiecare clasă (inclusiv cele interne, de forma Exterior$Interioara.class). Fișierele .class conțin bytecode, pe care îl rulează comanda java pe orice sistem de operare.

Opțiuni des folosite: -d dir (unde se scriu clasele, în directoare după pachet), -cp / --class-path (unde se caută clasele deja compilate și bibliotecile .jar), --release N (compilează pentru o versiune mai veche de Java), -encoding UTF-8, -g (informații pentru depanare), --enable-preview (funcții încă în previzualizare).

-Xlint:all activează toate avertismentele utile (tipuri brute, conversii nesigure, serialVersionUID lipsă, cazuri switch fără break), iar -Werror le transformă în erori. Exemplele din această secțiune sunt compilate exact așa. În proiectele mari, javac este apelat de Maven sau Gradle, nu manual.

Exemple

Exemplul 1: Compilare și rulare

javac nu afișează nimic dacă totul e în regulă; rezultatul este Main.class. Din Java 11 poți rula direct și sursa, cu java Main.java, fără un pas separat de compilare.

// Main.java
public class Main {
    public static void main(String[] args) {
        String nume = args.length > 0 ? args[0] : "lume";
        System.out.println("Salut, " + nume + "!");
    }
}

Rezultat în terminal

$ javac Main.java
$ ls
Main.class
Main.java
$ java Main Ana
Salut, Ana!
$ javac -version
javac 27

Exemplul 2: Erori și avertismente

Fișierul Raport.java are două probleme: lista este declarată fără tipul elementelor (List în loc de List<String>) și lipsește ; pe linia 8. Prima compilare se oprește la ; lipsă. După corectare, fără opțiuni javac doar menționează operațiile nesigure; cu -Xlint:all arată fiecare avertisment, iar cu -Werror compilarea eșuează (cod de ieșire 1).

import java.util.ArrayList;
import java.util.List;

public class Raport {
    public static void main(String[] args) {
        List lista = new ArrayList();
        lista.add("Iași");
        int total = 0
        System.out.println(lista);
    }
}

Rezultat în terminal

$ javac Raport.java
Raport.java:8: error: ';' expected
        int total = 0
                     ^
1 error

# după ce adăugăm ;
$ javac Raport.java
Note: Raport.java uses unchecked or unsafe operations.
Note: Recompile with -Xlint:unchecked for details.

$ javac -Xlint:all -Werror Raport.java
Raport.java:6: warning: [rawtypes] found raw type: List
        List lista = new ArrayList();
        ^
  missing type arguments for generic class List<E>
  where E is a type-variable:
    E extends Object declared in interface List
Raport.java:6: warning: [rawtypes] found raw type: ArrayList
        List lista = new ArrayList();
                         ^
  missing type arguments for generic class ArrayList<E>
  where E is a type-variable:
    E extends Object declared in class ArrayList
Raport.java:7: warning: [unchecked] unchecked call to add(E) as a member of the raw type List
        lista.add("Iași");
                 ^
  where E is a type-variable:
    E extends Object declared in interface List
error: warnings found and -Werror specified
1 error
3 warnings

Exemplul 3: Compilatorul apelat din cod (ToolProvider)

Compilatorul este disponibil și ca API: ToolProvider.getSystemJavaCompiler() primește aceleași argumente ca linia de comandă. Programul scrie un fișier sursă în directorul curent, îl compilează și verifică dacă a apărut fișierul .class.

import java.io.ByteArrayOutputStream;
import java.nio.file.Files;
import java.nio.file.Path;
import javax.tools.JavaCompiler;
import javax.tools.ToolProvider;

public class Main {
    public static void main(String[] args) throws Exception {
        Files.writeString(Path.of("Salut.java"), """
            public class Salut {
                public static void main(String[] args) {
                    System.out.println("Salut din Salut.class!");
                }
            }
            """);
        Files.writeString(Path.of("Gresit.java"), "public class Gresit { int x = }");

        JavaCompiler javac = ToolProvider.getSystemJavaCompiler();
        int cod = javac.run(null, null, null, "-d", ".", "Salut.java");
        System.out.println("Salut.java -> cod de ieșire " + cod + ", Salut.class există: " + Files.exists(Path.of("Salut.class")));

        ByteArrayOutputStream erori = new ByteArrayOutputStream();
        int cod2 = javac.run(null, null, erori, "-d", ".", "Gresit.java");
        System.out.println("Gresit.java -> cod de ieșire " + cod2);
        System.out.println(erori.toString().lines().findFirst().orElse(""));
    }
}

Rezultat

Salut.java -> cod de ieșire 0, Salut.class există: true
Gresit.java -> cod de ieșire 1
Gresit.java:1: error: illegal start of expression

Greșeli frecvente

  • Scrii javac Main fără extensie: error: Class names, 'Main', are only accepted if annotation processing is explicitly requested. Compilezi fișierul: javac Main.java.
  • Compilezi cu un JDK mai nou și rulezi cu o versiune mai veche de Java: UnsupportedClassVersionError. Folosește --release cu versiunea țintă.
  • Fișiere sursă cu diacritice salvate în altă codificare decât UTF-8: unmappable character for encoding. Salvează în UTF-8 sau adaugă -encoding UTF-8.
  • Uiți -cp pentru o bibliotecă externă: package com.exemplu does not exist.

Comenzi înrudite

  • 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.
  • jar: jar împachetează clasele compilate (și resursele) într-o singură arhivă .jar, care poate fi distribuită și rulată cu java -jar.
  • main și structura unui program: Orice program Java pornește din metoda main a unei clase: public static void main(String[] args).
  • 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.
  • 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).