Dit is in principe iets wat je in het INF1 vak onbewust reeds uitvoerde door op de groene “Compile” knop te drukken van je NetBeans/IntelliJ IDE. Het is belangrijk om te weten welke principes hier achter zitten. Hieronder volgt ter herhaling een kort overzicht over het compileren van Java programma’s zonder een buildtool.
Een minimaal programma compileren
Als je je JAVA_HOME
en PATH
omgevingsvariabelen goed hebt ingesteld (zie installatie van gebruikte software), dan kan je in een CMD window de commando’s:
javac
om te compileren; enjava
om te runnen
uitvoeren. Een simpel voorbeeld:
public class Main {
public static void main(String[] args) {
System.out.println("Hallo Wereld!");
}
}
Dit programma heeft geen package en moet bewaard worden onder Main.java
om de naam van de klasse te respecteren. Compileren en uitvoeren:
$ javac Main.java
$ java Main
Hallo Wereld
Het javac
commando heeft vanuit Main.java
een bytecode bestand aangemaakt genaamd Main.class
, dat met java
wordt uitgevoerd.
Verschillende bestanden compileren
Stel dat onze Main
klasse een Student
instantie aanmaakt en daar de naam van afdrukt. De code wordt dus als volgt:
Main.java
:
import student.*;
public class Main {
public static void main(String[] args) {
var student = new Student("Jos");
System.out.println("Hekyes " + student.getName());
}
}
Student.java
:
package student;
public class Student {
private String name;
public Student(String name) {
this.name = name;
}
public String getName() {
return name;
}
}
LET OP: de Student
klasse leeft in package student
—die op zijn beurt wordt geïmporteerd in Main.java
. Dat betekent dat we Student.java
moeten bewaren in de juiste subfolder ofwel package. Dit zou je moeten herkennen vanuit INF1, waar de structuur ook src/main/java/[pkg1]/[pkg2]
is. We hebben dus nu twee bestanden:
oefening/
Main.java
student/
Student.java
Alle files apart compileren levert .class
files op in diezelfde folders. java Main
zoekt dan nog steeds de student/Student.class
file vanwege de import. Dit betekent dat je je programma moeilijker kan delen met anderen: er zijn nu twee bestanden én een subdirectory met juiste naamgeving vereist.
Gelukkig kan je met de juiste argumenten alle .class
files in één keer genereren en die in een aparte folder—meestal genaamd build
—plaatsen:
$ javac -d ./build *.java
$ cd build
$ ls
Main.class student
$ java Main
Heykes Jos
Java programma’s packagen
Omdat het vervelend is om verschillende bestanden te kopiëren naar andere computers worden Java programma’s typisch verpakt in een .jar
bestand: een veredelde .zip
met metadata informatie zoals de auteur, de java versie die gebruikt werd om te compileren, welke klasse te starten (die de main()
methode bevat), … Indien deze metadata, in de META-INF
subfolder, niet bestaat, worden defaults aangemaakt. Zie de JDK Jar file specification voor meer informatie.
We gebruiken een derde commando, jar
, om, na het compileren naar de build
folder, alles te verpakkken in één kant-en-klaar programma:
$ cd build
$ jar cvf programma.jar *
added manifest
adding: Main.class(in = 957) (out= 527)(deflated 44%)
adding: student/(in = 0) (out= 0)(stored 0%)
adding: student/Student.class(in = 358) (out= 243)(deflated 32%)
$ ls
Main.class programma.jar student
Nu kunnen we programma.jar
makkelijk delen. De vraag is echter: hoe voeren we dit uit, ook met java
? Ja, maar met de juiste parameters, want deze moet nu IN het bestand gaan zoeken naar de juiste .class
files om die bytecode uit te kunnen voeren:
$ java -cp "programma.jar;." Main
Heykes Jos
Java classpath separators zijn OS-specifiek! Unix: :
in plaats van Windows: ;
.
Die ;.
is nodig om aan te geven dat java
binnenin de .jar
file moet zoeken. Dit is enkel nodig als je Main klasse niet in een package zit (we hebben geen package main
in onze Main.java
file). Anders is de fully qualified classname nodig als argument.
Voor meer informatie over de -cp
(classpath) parameter, zie de note in sectie Dependency Management.
Vanaf nu kan je programma.jar
ook uploaden naar een Maven repository of gebruiken als dependency in een ander project. Merk opnieuw op dat dit handmatig aanroepen van javac
in de praktijk wordt overgelaten aan de gebruikte build tool—in ons geval, Gradle.