Ce face Module (module-info.java)
Modulele, apărute în Java 9, sunt un nivel peste pachete. Un modul are un nume și un fișier module-info.java care spune două lucruri: ce alte module îi trebuie (requires) și ce pachete ale lui pot fi folosite din afară (exports). Pachetele neexportate rămân interne, chiar dacă au clase public: alt modul nu le poate importa.
Însuși JDK-ul este împărțit în module: java.base (java.lang, java.util, java.io; inclus automat), java.net.http, java.sql, java.desktop și altele. java --list-modules le afișează. Alte directive: requires transitive, opens (acces prin reflecție, pentru framework-uri), uses/provides (servicii).
Modulele sunt opționale. Programele fără module-info.java, cum sunt toate exemplele de pe această secțiune, rulează în „modulul fără nume” (unnamed module), care vede tot ce exportă JDK-ul. Modulele sunt utile în aplicații și biblioteci mari și pentru jlink, care construiește un runtime Java micșorat, doar cu modulele necesare.
Exemple
Exemplul 1: Două module: o bibliotecă și o aplicație
Modulul ro.dexblog.meteo exportă doar pachetul ro.dexblog.meteo; pachetul intern rămâne ascuns. Aplicația declară requires ro.dexblog.meteo. Se compilează cu --module-source-path și se rulează cu --module-path și -m modul/clasă.
// src/ro.dexblog.meteo/module-info.java
module ro.dexblog.meteo {
exports ro.dexblog.meteo;
}
// src/ro.dexblog.meteo/ro/dexblog/meteo/Prognoza.java
package ro.dexblog.meteo;
import ro.dexblog.meteo.intern.Senzori;
public class Prognoza {
public static String pentru(String oras) {
return oras + ": " + Senzori.temperatura(oras) + " °C";
}
}
// src/ro.dexblog.meteo/ro/dexblog/meteo/intern/Senzori.java
package ro.dexblog.meteo.intern;
public class Senzori {
public static int temperatura(String oras) {
return oras.equals("Iași") ? 14 : 11;
}
}
// src/ro.dexblog.app/module-info.java
module ro.dexblog.app {
requires ro.dexblog.meteo;
}
// src/ro.dexblog.app/ro/dexblog/app/Main.java
package ro.dexblog.app;
import ro.dexblog.meteo.Prognoza;
public class Main {
public static void main(String[] args) {
System.out.println(Prognoza.pentru("Iași"));
System.out.println(Prognoza.pentru("Suceava"));
System.out.println("modul: " + Main.class.getModule().getName());
}
}
Rezultat în terminal
$ javac -d out --module-source-path src -m ro.dexblog.app
$ java --module-path out -m ro.dexblog.app/ro.dexblog.app.Main
Iași: 14 °C
Suceava: 11 °C
modul: ro.dexblog.app
Exemplul 2: Un pachet neexportat nu poate fi importat
Dacă aplicația încearcă să importe ro.dexblog.meteo.intern.Senzori, compilarea eșuează, deși clasa este public: modulul bibliotecii nu exportă pachetul intern.
// src/ro.dexblog.app/ro/dexblog/app/Main.java
package ro.dexblog.app;
import ro.dexblog.meteo.Prognoza;
import ro.dexblog.meteo.intern.Senzori; // pachet neexportat
public class Main {
public static void main(String[] args) {
System.out.println(Prognoza.pentru("Iași"));
System.out.println(Senzori.temperatura("Cluj"));
}
}
Rezultat în terminal
$ javac -d out --module-source-path src -m ro.dexblog.app
src/ro.dexblog.app/ro/dexblog/app/Main.java:4: error: package ro.dexblog.meteo.intern does not exist
import ro.dexblog.meteo.intern.Senzori;
^
1 error
Exemplul 3: Modulele văzute din cod
Programul acesta nu are module-info.java, deci clasa Main este în modulul fără nume. String vine din java.base, iar HttpClient din java.net.http.
import java.net.http.HttpClient;
public class Main {
public static void main(String[] args) {
Module meu = Main.class.getModule();
System.out.println("Main are modul cu nume? " + meu.isNamed());
System.out.println("String: " + String.class.getModule().getName());
System.out.println("HttpClient: " + HttpClient.class.getModule().getName());
Module baza = Object.class.getModule();
System.out.println("java.base exportă java.util? " + baza.isExported("java.util"));
System.out.println("java.base exportă jdk.internal.misc? " + baza.isExported("jdk.internal.misc"));
}
}
Rezultat
Main are modul cu nume? false
String: java.base
HttpClient: java.net.http
java.base exportă java.util? true
java.base exportă jdk.internal.misc? false
Greșeli frecvente
- Pui
module-info.javaîn proiect și uițirequirespentru un modul JDK:package java.net.http is not visible. Fără module-info, toate modulele standard sunt vizibile. - Uiți
exportspentru pachetul pe care vrei să îl folosească alții: pentru celelalte module, pachetul nu există. - Folosești reflecție (de exemplu un framework care citește câmpuri private) pe un pachet doar exportat. Pentru reflecție profundă e nevoie de
opens. - Rulezi un modul cu
-cpîn loc de--module-path:module-infoeste ignorat și clasele rulează în modulul fără nume.
Comenzi înrudite
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.Modificatori de acces (public, protected, private): public, protected, private și lipsa modificatorului stabilesc cine poate vedea o clasă, un câmp, o metodă sau un constructor.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.jar: jar împachetează clasele compilate (și resursele) într-o singură arhivă .jar, care poate fi distribuită și rulată cu java -jar.