Zum Inhalt springen

Java Imports: Klassen, Pakete und ganze Module (JEP 511)

Ein orangefarbener Werkzeugkasten auf Flieder, ganz aufgeklappt – auf jeder Seite zwei Fächer an Stahlhebeln, in jedem der fünf Fächer etwas anderes: Buntstifte, Messingspäne, Garnrollen, Glasmurmeln, Pinsel

In diesem Artikel erfährst du:

  • Was bewirkt ein Import, und welche Arten von Imports kennt Java?
  • Wie können mit import module ganze Java-Module importiert werden?
  • Wie können dabei Mehrdeutigkeiten aufgelöst werden?
  • Was sind transitive Modul-Importe, und wie funktionieren sie?
  • Welche Module werden standardmäßig importiert?

Beginnen wir mit den klassischen Imports, die es seit Java 1.0 gibt.

Java Imports

Was ein Import bewirkt

Eine Klasse aus einem anderen Paket sprichst du über ihren voll qualifizierten Namen an, also mit vorangestelltem Paketnamen:

public class Names {
  java.util.List<String> names = new java.util.ArrayList<>();
}

Ein Import macht eine Klasse unter ihrem einfachen Namen verfügbar. Mit zwei Imports wird aus dem Beispiel:

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

public class Names {
  List<String> names = new ArrayList<>();
}

Ein Import lädt keine Klasse und erzeugt keinen Bytecode. Er sagt nur dem Compiler, welche Klasse mit List bzw. ArrayList gemeint ist. Kompilierst du beide Varianten mit javac -g:none (ohne Debug-Informationen, die die Zeilennummern enthalten), sind die .class-Dateien byte-identisch – in beiden stehen die voll qualifizierten Namen java/util/List und java/util/ArrayList.

Und String? Dafür brauchst du keinen Import, denn Java importiert das Paket java.lang automatisch – mehr dazu unter Automatisch importiert: java.lang.

Klassen und Pakete importieren

Seit Java 1.0 können wir mit dem import-Statement einzelne Klassen („single-type-import declaration“) oder ganze Pakete („type-import-on-demand declaration“) importieren, z. B. wie folgt:

import java.util.*;
import java.util.stream.Stream;

Ein Paket-Import umfasst nur die Klassen genau dieses Pakets, nicht die seiner Unterpakete. import java.util.*; importiert also java.util.List, aber nicht java.util.stream.Stream – dafür brauchst du den zweiten Import aus dem Beispiel. Ohne ihn bricht javac mit „cannot find symbol“ ab, sobald du Stream verwendest.

Statische Imports

Seit Java 5 kannst du mit import static auch statische Felder und Methoden einer Klasse importieren – einzeln („single-static-import declaration“) oder alle auf einmal („static-import-on-demand declaration“):

import static java.lang.Math.PI;  // ← nur die Konstante PI
import static java.lang.Math.*;   // ← alle statischen Member von Math

public class Circle {
  double area = PI * pow(2, 2);
}

Ohne die statischen Imports müsstest du Math.PI und Math.pow(2, 2) schreiben.

Automatisch importiert: java.lang

Die Klassen des Pakets java.lang werden seit jeher automatisch importiert. Deshalb müssen wir für Klassen wie Object, String, Integer, Exception usw. keine import-Statements angeben.

Die Java Language Specification beschreibt das so, als stünde in jeder Quelldatei direkt nach der Package-Deklaration import java.lang.*;. Daraus folgt auch: Unterpakete wie java.lang.reflect werden nicht automatisch importiert.

Wo Imports stehen

Eine Java-Quelldatei hat eine feste Reihenfolge: zuerst die optionale Package-Deklaration, dann die Imports, dann die Klassen. Steht ein Import nach der Klasse, bricht der Compiler ab:

public class Order {}

import java.util.List;
Order.java:3: error: class, interface, annotation type, enum, record, method or field expected
import java.util.List;
^
1 error

Dieselbe Meldung erscheint, wenn die Package-Deklaration erst nach einem Import steht.

Gibt es in Java Import-Aliase („import … as“)?

Nein. Anders als z. B. Python oder Kotlin kennt Java keine Imports unter einem anderen Namen. import java.util.List as JList; quittiert javac mit der Meldung ';' expected.

Brauchst du in einer Klasse zwei Klassen mit demselben einfachen Namen, importierst du eine davon und schreibst die andere voll qualifiziert:

import java.util.List;

public class TwoLists {
  List<String> names;     // ← java.util.List
  java.awt.List awtList;  // ← voll qualifiziert
}

Seit Java 25 kennt die Java Language Specification fünf Arten von Import-Deklarationen: die vier aus diesem Kapitel und die „single-module-import declaration“, um die es im Rest dieses Artikels geht.

Module Import Declarations

Mit import module können wir seit Java 25 (und in Java 23 und Java 24 im Preview-Modus) auch ganze Module importieren. Dadurch können wir alle Klassen, die sich innerhalb eines Moduls befinden und von diesem exportiert werden, direkt verwenden.

Die allgemeine Form lautet:

import module <Modulname>;

Im folgenden Beispiel importieren wir das Modul java.base und können dadurch die Klassen List, Map, Stream und Collectors verwenden, ohne sie einzeln oder paketweise importieren zu müssen (String und Character liegen im Paket java.lang und wurden daher schon immer automatisch importiert).

import module java.base;

public class ModuleImportTest {

  public static Map<Character, List<String>> groupByFirstLetter(String... values) {
    return Stream.of(values).collect(
        Collectors.groupingBy(s -> Character.toUpperCase(s.charAt(0))));
  }
}

Wenn du dieses Code-Beispiel in der Datei ModuleImportTest.java speicherst, kannst du die Klasse seit Java 25 wie folgt kompilieren (in Java 23/24 war import module ein Preview-Feature und musste mit --enable-preview --source <Version> aktiviert werden):

javac ModuleImportTest.java

Um import module zu verwenden, ist es – wie im vorangegangenen Beispiel gesehen – nicht nötig, dass sich die importierende Klasse selbst in einem Modul befindet.

Nur benannte Module: Classpath und Module Path

Das importierte Modul muss ein benanntes Modul sein, und es muss im sogenannten Modulgraphen stehen. Das ist die Menge der Module, die der Compiler beim Übersetzen kennt. Er baut sie aus einigen Wurzelmodulen und allem, was diese per requires benötigen. Für Code auf dem Classpath sind fast alle Module des JDK Wurzeln – sie stehen also immer im Graphen.

Eine Bibliothek auf dem Classpath gehört dagegen zum unbenannten Modul (unnamed module) und lässt sich nicht per import module importieren. javac meldet dann „imported module not found“.

Liegt die Bibliothek als Modul auf dem Module Path, steht sie damit noch nicht im Modulgraphen. Der Compiler nimmt ein Modul vom Module Path nur auf, wenn ein Modul im Graphen es per requires benötigt. Fehlt es, meldet javac „unnamed module does not read“, gefolgt vom Modulnamen. Für Code auf dem Classpath machst du so ein Modul mit --add-modules selbst zur Wurzel:

javac --module-path <Pfad zur JAR> --add-modules <Modulname> App.java

Ist dein eigener Code ein Modul, genügt stattdessen ein requires <Modulname>; in seiner module-info.java.

Automatischer java.base-Import in Compact Source Files

Compact Source Files (zuvor „Unnamed Classes“, „Implicitly Declared Classes“ und „Simple Source Files“) importieren automatisch das java.base-Modul, d. h. Klassen wie List und Map kannst du dort ohne explizite Imports verwenden.

Mehrdeutige Klassennamen

Manchmal kommt es vor, dass ein Klassenname nicht eindeutig ist. In folgendem Beispiel gibt es eine List-Klasse sowohl im importierten Modul java.base (java.util.List) als auch im Modul java.desktop (java.awt.List):

import module java.base;     // ← enthält java.util.List
import module java.desktop;  // ← enthält java.awt.List

public class Ambiguous {
  List list;                 // ← mehrdeutige Referenz auf List
}

Wenn du die Datei unter Ambiguous.java abspeicherst und dann wie folgt kompilierst:

javac Ambiguous.java

... dann bricht der Compiler mit folgender Fehlermeldung ab:

Ambiguous.java:5: error: reference to List is ambiguous
  List list;                 // ← mehrdeutige Referenz auf List
  ^
  both class java.awt.List in java.awt and interface java.util.List in java.util match
1 error

Das bedeutet, dass der Compiler nicht weiß, welcher der beiden List-Typen gemeint ist.

Mehrdeutige Klassennamen auflösen

Nehmen wir an, du möchtest java.util.List verwenden (und nicht java.awt.List). Dann hast du zwei Möglichkeiten, um diese Mehrdeutigkeit aufzulösen:

Option 1: Du importierst zusätzlich die Klasse java.util.List direkt:

import module java.base;
import module java.desktop;

import java.util.List;  // ← per Single-Type-Import aufgelöst

public class Ambiguous {
  List list;
}

Option 2: Du importierst zusätzlich das Paket java.util (in der Java-Terminologie heißt das übrigens nicht „Package Import“, sondern „Type-Import-on-Demand Declaration“):

import module java.base;
import module java.desktop;

import java.util.*;  // ← per Type-Import-on-Demand aufgelöst

public class Ambiguous {
  List list;
}

Die zweite Option ist erst seit Java 24 verfügbar. Wenn du das letzte Beispiel mit Java 23 kompilierst, führt dies ebenfalls zur Fehlermeldung „reference to List is ambiguous“.

Hinter beiden Optionen steht dieselbe Regel: Der spezifischere Import hat Vorrang vor dem allgemeineren.

Import-ArtBeispielhat Vorrang vor
Single-Type-Importimport java.util.List;Paket- und Modul-Importen
Type-Import-on-Demandimport java.util.*;Modul-Importen (seit Java 24)
Module Importimport module java.base;keinem anderen Import

Transitive Modul-Importe

Wenn ein mit import module importiertes Modul ein anderes Modul mit requires transitive benötigt, dann sind auch alle Klassen der exportierten Pakete dieses anderen Moduls ohne explizite Imports nutzbar.

Ich möchte dir das am Beispiel der Module java.sql und java.xml erklären.

  • Das Modul java.sql hat (u. a.) eine transitive Abhängigkeit auf das Modul java.xml.
  • Das Modul java.sql exportiert die Pakete java.sql und javax.sql.
  • Das Modul java.xml exportiert u. a. die Pakete javax.xml, javax.xml.parsers und org.w3c.dom.

Die folgende Grafik zeigt die Module, ihre Abhängigkeiten und die exportierten Pakete:

Anwendungscode importiert das Modul java.sql, das das Modul java.xml mit requires transitive benötigt; darunter die exportierten Pakete beider Module
import module java.sql importiert auch die Pakete von java.xml, weil java.sql dieses Modul mit requires transitive benötigt.

In der Modul-Deklaration des java.sql-Moduls sieht das so aus:

module java.sql {
  . . .
  requires transitive java.xml;

  exports java.sql;
  exports javax.sql;
  . . .
}

Und in der Modul-Deklaration von java.xml so:

module java.xml {
  exports javax.xml;
  exports javax.xml.parsers;
  . . .
}

Wenn wir nun ein Programm schreiben, das das Modul java.sql importiert, dann benötigen wir beispielsweise keine expliziten Imports für die Klassen SAXParserFactory und SAXParser aus dem Paket javax.xml.parsers des java.xml-Moduls – und auch keinen expliziten Import dieses Moduls:

import module java.sql;  // ← importiert auch die Pakete von java.xml,
                         //   etwa javax.xml.parsers
. . .
SAXParserFactory factory = SAXParserFactory.newInstance();
SAXParser saxParser = factory.newSAXParser();
. . .

Denn aus der transitiven Abhängigkeit von Modul java.sql auf Modul java.xml und der Tatsache, dass java.xml das Paket javax.xml.parsers exportiert, folgt, dass das Programm auch ohne explizite Imports auf alle Klassen des Pakets javax.xml.parsers zugreifen kann.

Automatischer java.base-Import in JShell

Seit Java 25 importiert JShell das Modul java.base automatisch:

$ jshell
|  Welcome to JShell -- Version 27
|  For an introduction type: /help intro

jshell> /imports
|    import java.base

Wenn du JShell mit Java 24 oder älter startest und das /imports-Kommando eingibst, wirst du hingegen die folgenden zehn standardmäßigen Paket-Importe sehen:

$ jshell
|  Welcome to JShell -- Version 24
|  For an introduction type: /help intro

jshell> /imports
|    import java.io.*
|    import java.math.*
|    import java.net.*
|    import java.nio.file.*
|    import java.util.*
|    import java.util.concurrent.*
|    import java.util.function.*
|    import java.util.prefs.*
|    import java.util.regex.*
|    import java.util.stream.*

Fazit

Ein Import macht Klassen und statische Member unter ihrem einfachen Namen verfügbar – einzeln, paketweise und seit Java 25 auch modulweise. Zur Laufzeit ändert er nichts.

Module Import Declarations können Java-Programme kürzer und leichter wartbar machen, indem nicht mehr einzelne Klassen und Pakete, sondern ganze Module importiert werden. Andererseits können sie auch die ewige Diskussion darüber, ob man Klassen einzeln oder paketweise importieren soll, weiter ausufern lassen.

In Compact Source Files (zuvor Unnamed Classes, Implicitly Declared Classes und Simple Source Files) und JShell wird automatisch das java.base-Modul importiert, sodass hier alle Klassen dieses Moduls ohne Imports direkt verwendet werden können.

Ich empfehle import module dort, wo du ausprobierst: in JShell, in Compact Source Files und beim Prototyping. In großen Codebasen bleiben Single-Type-Imports die klarere Wahl, denn mit ihnen zeigt jede Datei, aus welchem Paket eine Klasse stammt – und eine neue Klasse in einem importierten Modul kann einen bisher eindeutigen Namen nicht mehrdeutig machen.

Module Import Declarations waren bis Java 24 ein Preview-Feature und mussten mit --enable-preview --source <Version> aktiviert werden. Seit Java 25 sind sie finalisiert und fester Bestandteil der Sprache – ohne zusätzliche Flags nutzbar.

Was soll ich als Nächstes vertiefen? Das erfahre ich am besten durch dein Feedback: Eine Bewertung auf meinem ProvenExpert-Profil zeigt mir, welche Themen dir wichtig sind – und motiviert mich, weitere 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