Zum Inhalt springen

Java main()-Methode – Syntax und Vereinfachung seit Java 25

Sieben Messingzahnräder in einer Reihe auf einer Eichenleiste vor Koralle; das erste links leuchtet bernsteinfarben und trägt als einziges eine Kurbel

In diesem Artikel erfährst du alles über die main()-Methode in Java – den Startpunkt eines jeden Java-Programms. Der Artikel beschreibt auch die in Java 25 finalisierten Vereinfachungen.

Du erfährst im Einzelnen:

  • Was ist eine main()-Methode, und wofür brauchen wir sie?
  • Wie wird die main()-Methode aufgerufen?
  • Was sind die Bestandteile der main()-Methode in Java, und was bedeuten sie?
  • Wie kannst du die main()-Methode in modernen Java-Versionen wesentlich einfacher schreiben?

Was ist eine main()-Methode in Java?

Damit ein Java-Programm gestartet werden kann, benötigt es eine main()-Methode. Diese Methode ist der Einstiegspunkt in das Programm. Die JVM (Java Virtual Machine) ruft beim Start eines Programms diese main()-Methode auf und führt den darin liegenden Java-Code aus.

Beispiel für eine main()-Methode in Java

Ein einfaches Hello-World-Java-Programm mit einer main()-Methode sieht z. B. so aus:

public class HelloWorld {
  public static void main(String[] args) {
    System.out.println("Hello world!");
  }
}

Um dieses Programm zu starten, speichere den Programmcode zunächst in einer Datei mit dem Namen HelloWorld.java.

Gib dann auf der Kommandozeile / in einem Terminal den folgenden Befehl ein:

java HelloWorld.java

Du solltest nun folgende Ausgabe sehen:

Hello world!

Glückwunsch! Du hast dein erstes Java-Programm geschrieben und gestartet.

Aber warum war das so kompliziert?

Was bedeuten all die Begriffe wie public, class, static, void usw. im Programmcode?

Die gute Nachricht: Das musst du als Anfänger:in zunächst gar nicht wissen. Denn: In modernen Java-Versionen geht das viel einfacher!

Wie? Das wirst du im folgenden Abschnitt sehen.

Falls es dich dennoch interessiert, findest du eine detaillierte Beschreibung aller Bestandteile der main()-Methode weiter unten im Abschnitt Syntax der Java-main()-Methode.

Vereinfachte main()-Methode

In Java 25 wurde die main()-Methode stark vereinfacht (die Vereinfachungen waren von Java 21 bis Java 24 als Preview-Feature verfügbar).

In diesem Abschnitt zeige ich dir, was sich für dich ändert. Die technischen Details hinter diesen Änderungen erfährst du im Abschnitt Compact Source Files and Instance Main Methods.

Gehen wir noch einmal zurück zum Hello-World-Beispiel aus dem ersten Abschnitt:

public class HelloWorld {
  public static void main(String[] args) {
    System.out.println("Hello world!");
  }
}

Du musstest hier eine ganze Menge sogenannten „Boilerplate Code“ schreiben – also Code, der in jedem Java-Programm immer wieder der gleiche wäre. Dabei ist das Einzige, was du sagen willst: „Bitte gib den Text ‚Hello world!‘ aus.“

Seit Java 25 (und in Java 23 und 24 mit aktivierten Preview-Features) kannst du das viel kürzer schreiben:

void main() {
  IO.println("Hello world!");
}

Die main()-Methode und der Ausgabebefehl println() sind noch da – doch eine Menge Boilerplate-Code drum herum wurde gelöscht, und System.out.println() wurde zu IO.println() verkürzt.

Somit müssen sich Java-Anfänger:innen keine Gedanken mehr über public, class, static usw. machen. Diese Begriffe können dann eingeführt werden, wenn sie wirklich gebraucht werden.

Im folgenden Abschnitt erfährst du, welche technischen Details hinter den Änderungen stecken.

Compact Source Files and Instance Main Methods

Im vorherigen Abschnitt hast du erfahren, dass eine Java-main()-Methode ohne Klasse, ohne public static und ohne String[] args geschrieben werden kann und dass für die Ausgabe statt System.out.println() das kürzere IO.println() genügt.

Damit ist der folgende Code ein gültiges und vollständiges Java-Programm:

void main() {
  IO.println("Hello world!");
}

Im Folgenden beschreibe ich die fünf Bestandteile des Features, die diese Vereinfachung ermöglichen: kompakte Quelldateien, implizit deklarierte Klassen, Instanz-Main-Methoden, die neue Klasse java.lang.IO für die Konsoleninteraktion und den automatischen Import des java.base-Moduls.

Compact Source Files

Eine Java-Datei ohne explizite Klassendeklaration, also ohne beispielsweise public class HelloWorld, wird als „Compact Source File“, also „kompakte Quelldatei“, bezeichnet.

Implizit deklarierte Klassen

Aus dem in einer kompakten Quelldatei enthaltenen Code erzeugt der Java-Compiler eine sogenannte „implizit deklarierte Klasse“ mit einem vom Compiler festgelegten Namen. In der Praxis ist das der Name der Datei ohne die .java-Endung.

Wenn du beispielsweise die Datei HelloWorld.java kompilierst, erzeugt der Compiler die Datei HelloWorld.class – und wenn du diese z. B. mit deiner IDE dekompilierst, siehst du, dass auch der Klassenname HelloWorld ist.

Auf diesen Namen darfst du dich allerdings nicht verlassen: Er ist implementierungsspezifisch und kann sich ändern.

Für implizit deklarierte Klassen gelten die folgenden Besonderheiten:

  • Eine implizit deklarierte Klasse liegt immer im unbenannten Paket (so wie eine reguläre Klasse ohne package-Deklaration).
  • Eine implizit deklarierte Klasse ist grundsätzlich final, von ihr kann also nicht geerbt werden.
  • Eine implizit deklarierte Klasse kann keine Interfaces implementieren oder von anderen Klassen erben.
  • Auf eine implizit deklarierte Klasse kann nicht über den vom Compiler festgelegten Namen zugegriffen werden, d. h. andere Klassen können eine implizit deklarierte Klasse nicht instanziieren und keine Methoden darauf aufrufen, auch keine statischen.

Eine implizit deklarierte Klasse kann jedoch Methoden auf sich selbst aufrufen, d. h. Methoden, die in derselben .java-Datei definiert sind, so wie in folgendem Beispiel:

void main() {
  IO.println(greeting());
}

String greeting() {
  return "Hello world!";
}

Neben Methoden darf eine kompakte Quelldatei auch Felder enthalten – sie werden zu Feldern der implizit deklarierten Klasse.

Da auf eine implizit deklarierte Klasse von außerhalb nicht zugegriffen werden kann, muss sie immer eine main()-Methode enthalten.

Instanz-Main-Methoden

Instanz-Main-Methoden sind nicht-statische main()-Methoden, also main()-Methoden ohne das static-Keyword. Seit Java 25 sind auch folgende main()-Methoden erlaubt:

  • nicht-statische Methoden,
  • Methoden mit dem Sichtbarkeitsmodifikator public oder protected oder ohne Sichtbarkeitsmodifikator (package-private) – nur private ist nicht erlaubt,
  • Methoden mit oder ohne String[]-Parameter.

Hier sind ein paar Beispiele:

  • void main()
  • void main(String[] args)
  • public void main()
  • protected static void main(String[] args)

Eine Instanz-Main-Methode kannst du auch in eine reguläre Klasse schreiben – sie braucht keine kompakte Quelldatei:

class HelloWorld {
  void main() {
    IO.println("Hello world!");
  }
}

Statische und nicht-statische Methoden mit gleicher Signatur sowie Methoden mit unterschiedlichen Sichtbarkeitsmodifikatoren bei gleicher Signatur schließen sich gegenseitig aus und führen zu einem „method is already defined“-Compiler-Fehler.

Es ist allerdings möglich, dass gleichzeitig eine main()-Methode mit String[]-Parameter und eine main()-Methode ohne Parameter in derselben .java-Datei existieren:

void main(String[] args) {
  // . . .
}

protected static void main() {
  // . . .
}

Für diesen Fall wurde festgelegt, dass die Methode mit String[]-Parameter Priorität hat – im Beispiel wird also void main(String[] args) gestartet.

Dahinter steckt die Rückwärtskompatibilität: Schon vor den Vereinfachungen durfte eine Klasse neben der klassischen Einstiegsmethode public static void main(String[] args) eine weitere, überladene main()-Methode ohne Parameter enthalten – als Einstiegspunkt diente aber immer nur die Variante mit String[]-Parameter. Genau dieses Verhalten bleibt erhalten, damit bestehende Programme unverändert starten.

So ruft die JVM die main()-Methode auf

Beim Start eines Programms geht die JVM in zwei Schritten vor. Die folgende Grafik zeigt den Ablauf: Die JVM wählt zuerst die main()-Methode aus und entscheidet dann, wie sie diese aufruft.

Ablauf beim Programmstart: Die JVM sucht zuerst main(String[] args), sonst main(); findet sie keine der beiden, endet der Start mit einem Fehler. Eine statische Methode ruft sie direkt auf; für eine Instanz-Main-Methode erzeugt sie ein Objekt der Klasse und ruft die Methode auf diesem Objekt auf
Die JVM bevorzugt die main()-Methode mit String[]-Parameter – für eine Instanz-Main-Methode erzeugt sie zuerst ein Objekt der Klasse

Um das Objekt für eine Instanz-Main-Methode zu erzeugen, ruft die JVM den Konstruktor ohne Parameter auf. Die Klasse muss deshalb einen Konstruktor ohne Parameter haben, der nicht private ist.

Eine implizit deklarierte Klasse hat immer genau einen Konstruktor: den Standardkonstruktor ohne Parameter. Er hat keinen Sichtbarkeitsmodifikator, ist also package-private. Eine reguläre Klasse hat einen Standardkonstruktor hingegen nur, solange du keinen eigenen Konstruktor schreibst.

Hat eine reguläre Klasse nur Konstruktoren mit Parametern, bricht java HelloWorld.java mit dieser Meldung ab:

error: can't find no argument constructor in class: HelloWorld

Hat sie einen Konstruktor ohne Parameter, der aber privat ist, lautet die Meldung:

error: no non-private zero argument constructor found in class HelloWorld
remove private from existing constructor or define as:
   public HelloWorld()

Mit einem nicht-privaten Konstruktor ohne Parameter startet das Programm in beiden Fällen.

Konsoleninteraktion mit java.lang.IO

In der dritten Preview-Phase der Änderungen, in Java 23, wurde die neue IO-Klasse eingeführt – zunächst mit drei Methoden. Seit Java 24 stellt sie fünf statische Methoden bereit:

  • void print(Object obj) – gibt den übergebenen Text oder die Text-Repräsentation des übergebenen Objekts auf der Konsole aus – ohne Zeilenumbruch am Ende.
  • void println(Object obj) – gibt den übergebenen Text oder die Text-Repräsentation des übergebenen Objekts auf der Konsole aus – mit Zeilenumbruch am Ende.
  • void println() – gibt nur einen Zeilenumbruch aus, also eine leere Zeile.
  • String readln(String prompt) – zeigt den übergebenen Prompt an, nimmt eine Benutzereingabe entgegen und gibt diese zurück.
  • String readln() – nimmt eine Benutzereingabe ohne vorherigen Prompt entgegen und gibt diese zurück.

Das folgende Beispiel aus dem JDK Enhancement Proposal 512 fragt nach einem Namen und begrüßt die Person:

void main() {
  String name = IO.readln("Please enter your name: ");
  IO.print("Pleased to meet you, ");
  IO.println(name);
}

Wenn du „Sven“ eingibst, sieht das im Terminal so aus:

Please enter your name: Sven
Pleased to meet you, Sven

Da die IO-Klasse im Paket java.lang liegt, kannst du sie in jedem Java-Programm ohne Import verwenden – nicht nur in kompakten Quelldateien.

Automatischer Import des java.base-Moduls

Viele Standardklassen sind auch in kleinen Programmen nützlich. Damit du dafür nicht jedes Mal einen Import schreiben musst, importiert eine kompakte Quelldatei automatisch das gesamte java.base-Modul – so, als stünde am Dateianfang die Deklaration import module java.base;.

Klassen aus häufig genutzten Paketen wie java.util, java.io oder java.math kannst du dadurch ohne expliziten Import verwenden:

void main() {
  var names = List.of("Anna", "Ben", "Carla");
  for (var name : names) {
    IO.println(name);
  }
}

List ist hier sofort verfügbar, obwohl kein import java.util.List; im Code steht.

Sobald dein Programm wächst und du es in eine reguläre Klasse überführst, fällt dieser automatische Import weg. Dann ergänzt du die nötigen Imports selbst – beispielsweise mit einer Modul-Import-Deklaration import module java.base; am Dateianfang. Wie diese Modul-Import-Deklarationen genau funktionieren, erfährst du im Artikel import module in Java: Module Import Declarations.

Die Historie der Änderungen

Zum Abschluss noch ein Blick darauf, wie sich das Feature über die Java-Versionen hinweg entwickelt hat:

Die folgende Tabelle zeigt, welche Vereinfachung in welcher Java-Version funktioniert. „Preview“ bedeutet: nur mit aktivierten Preview-Features. Ein ✗ bedeutet, dass es die Vereinfachung in dieser Version nicht gibt.

VereinfachungJava 21Java 22Java 23Java 24seit Java 25
void main() ohne KlassePreviewPreviewPreviewPreview✓
IO.println()✗✗PreviewPreview✓
println() ohne IO.✗✗PreviewPreviewmit import static
automatischer Import von java.base✗✗PreviewPreview✓

Das IO.println()-Beispiel aus diesem Artikel läuft also erst ab Java 23.

println() ohne vorangestelltes IO. gab es nur in Java 23 und 24 – seit Java 25 brauchst du dafür den statischen Import.

Syntax der Java-main()-Methode

Dieser Abschnitt beschreibt die klassische Syntax der main()-Methode, wie sie vor den Vereinfachungen galt – und in größeren Programmen weiterhin gilt. Klassischerweise ist eine main()-Methode in eine Klasse eingebettet, und ihre Syntax ist fest vorgegeben:

public class MyMainMethodDemo {
  public static void main(String[] args) {
    // code to execute
  }
  // possibly more code
}

Nur der Name der Klasse, im Beispiel MyMainMethodDemo, und der Name des Parameters, im Beispiel args, dürfen frei gewählt werden.

Wenn ein Programm aus mehreren Klassen besteht, dürfen beliebig viele dieser Klassen eine main()-Methode enthalten. Zum Start eines Programms gibst du die Datei an, deren main()-Methode gestartet werden soll (java MyMainMethodDemo.java) – oder, nach dem Kompilieren mit javac, den Namen der Klasse (java MyMainMethodDemo).

Was bedeuten die einzelnen Elemente?

public class MyMainMethodDemo

Mit dieser ersten Codezeile wird eine Klasse im Sinne der objektorientierten Programmierung eingeleitet. MyMainMethodDemo ist der Name der Klasse. Java-Code ist immer innerhalb von Klassen angeordnet.

public static void main(String[] args)

Mit der zweiten Zeile, dem sogenannten Methodenkopf (oft auch „Methodensignatur“ genannt), wird eine Methode eingeleitet. Methoden enthalten den auszuführenden Programmcode.

Der Methodenkopf der main()-Methode besteht aus fünf Bestandteilen. Die folgende Grafik zeigt sie – blau die Bestandteile, die weiterhin erforderlich sind, grau die Bestandteile, die seit Java 25 entfallen dürfen:

Der Methodenkopf public static void main(String[] args) mit seinen fünf Bestandteilen: public – der Sichtbarkeitsmodifikator, static – ohne Objekt aufrufbar, void – kein Rückgabewert, main – der Name der Methode, String[] args – die Kommandozeilenparameter. void, main und die Klammern sind weiterhin erforderlich; public, static und String[] args dürfen seit Java 25 entfallen
Die fünf Bestandteile der klassischen main()-Methode – seit Java 25 sind nur noch void und main() erforderlich

Was die einzelnen Bestandteile bedeuten, erfährst du in den folgenden Abschnitten.

public

public ist ein sogenannter Sichtbarkeitsmodifikator. Die main()-Methode muss public, also öffentlich, sein, damit die JVM sie aufrufen und den darin enthaltenen Programmcode ausführen kann. Für die Klasse gilt das nicht: Auch eine Klasse ohne public lässt sich starten.

static

In der Objektorientierung wird zwischen statischen Methoden und Instanzmethoden unterschieden. Statische Methoden können aufgerufen werden, ohne dass eine Instanz der sie umgebenden Klasse – also ein Objekt – erzeugt werden muss. Instanzmethoden können hingegen nur auf einem Objekt aufgerufen werden.

Die main()-Methode in Java muss statisch sein, um ohne Instanziierung der Klasse – also ohne Erzeugung eines Objekts dieser Klasse – aufrufbar zu sein.

void

Methoden können Werte zurückgeben, z. B. gibt die Methode Math.random() eine Zufallszahl zurück. Eine main()-Methode hat jedoch keinen Rückgabewert. Und genau das wird durch das Schlüsselwort void angegeben.

String[] args

Hierbei handelt es sich um einen Parameter der Methode. String[] ist der Typ des Parameters: ein String-Array. Und args ist der Name des Parameters. Dieser Name darf geändert werden.

Beim Start eines Programms können sogenannte Kommandozeilenparameter übergeben werden, z. B. so:

java HelloWorld.java happy coders out there

Diese Parameter werden als String-Array an die main()-Methode übergeben und können dort z. B. so ausgelesen und ausgegeben werden:

public class HelloWorld {
  public static void main(String[] args) {
    System.out.print("Hello");
    for (String arg : args) {
      System.out.print(" " + arg);
    }
    System.out.println("!");
  }
}

Wenn du dieses Programm wie oben aufrufst, bekommst du folgende Ausgabe:

Hello happy coders out there!

IntelliJ-Hinweis: „Modifier ‚public‘ is redundant for ‚main‘ method on Java 25“

IntelliJ IDEA zeigt diesen Hinweis an einer klassischen main()-Methode, wenn das Language Level des Projekts auf 25 oder höher steht:

Modifier 'public' is redundant for 'main' method on Java 25

Die Zahl am Ende ist das Language Level deines Projekts – bei Language Level 26 endet der Hinweis auf „on Java 26“.

Der Hinweis ist kein Fehler. Er bedeutet: Seit Java 25 startet die JVM eine main()-Methode auch ohne public. Dein Programm läuft mit und ohne den Modifikator.

Du kannst public also entfernen. Stellst du das Projekt später auf ein niedrigeres Language Level zurück, brauchst du den Modifikator wieder: Ohne Preview-Features starten Java 24 und ältere Versionen ausschließlich public static void main(String[] args).

Fazit

Die main()-Methode ist der Startpunkt eines jeden Java-Programms. Ohne diese kann kein Java-Programm starten. Klassischerweise ist die Syntax einer main()-Methode starr vorgegeben:

public class HelloWorld {
  public static void main(String[] args) {
    System.out.println("Hello world!");
  }
}

Seit Java 25 (und in Java 23 und 24 mit aktivierten Preview-Features) kannst du dieselbe main()-Methode wie folgt schreiben:

void main() {
  IO.println("Hello world!");
}

Das macht es insbesondere Java-Anfänger:innen leichter, die Sprache zu erlernen. Konzepte, die erst für größere Programme relevant werden, wie Klassen, die Unterscheidung in statische und Instanzmethoden, Sichtbarkeitsmodifikatoren wie public, protected und private sowie grobgranulare Strukturen wie Pakete und Module können so dann eingeführt werden, wenn sie gebraucht werden.

Wenn dir der Artikel weitergeholfen hat, würde ich mich sehr über eine positive Bewertung auf meinem ProvenExpert-Profil freuen. Dein Feedback hilft mir, meine Inhalte weiter zu verbessern und motiviert mich, neue informative Artikel zu schreiben.

👉 Bewertung abgeben

Dieses Thema im eigenen Code?

Du hast den Artikel gelesen – im Training arbeitet ihr damit. 2 Tage lang gehen wir die Themen an euren eigenen Projekten durch, statt an konstruierten Beispielen.

Praxisnah, verständlich und direkt auf euren Projektalltag übertragbar. Statt Theorie vermittle ich Prinzipien, die euch helfen, Code langfristig besser, wartbarer und performanter zu schreiben.

Java 25 SchulungAlle Trainings ansehen

Werde ein:e bessere:r Java-Entwickler:in

Mit meinem kostenlosen Newsletter bleibst du vorn. Modernes Java: neue Versionen & Features, Performance und JVM-Insights – 1x im Monat.

Suche