Module Import Declarations
Import every package a module exports with one import module line, know what it pulls in, and settle the ambiguities it can cause.
import module java.sql; // every package java.sql exports, plus its transitive requirements
import module java.net.http;
import java.sql.Date; // a single-type import settles a clashimport module java.sql; imports, on demand, every public top-level type in every package that the module java.sql exports. One line replaces a list of java.sql and javax.sql imports. The feature was a preview in Java 23 and 24 and is final in Java 25 (JEP 511), so it needs no flags.
What a module import covers
It covers the packages the module exports, plus the packages exported by modules it requires transitive. java.sql requires java.logging, java.xml and java.transaction.xa transitively, so import module java.sql; also makes java.util.logging.Level and javax.xml.parsers.DocumentBuilderFactory available by simple name. import module java.base; covers java.util, java.io, java.nio.file, java.time, java.util.concurrent, java.util.stream, java.net, java.math and the other packages of the base module. Sub-packages are separate packages, but a module usually exports them too, so they are included.
Your code does not have to be in a module to use it. An ordinary class on the class path can start with import module java.base;. Inside a named module the imported module must be one your module reads, so the requires line in module-info.java is still needed. The import is only about names; it adds nothing to the module graph. A library JAR on the class path has no module name, so it cannot be imported this way.
Compact source files get java.base for free
A compact source file (a bare void main() with no class declaration) behaves as if it starts with import module java.base;. That is why such a file can use List, Map, Path and Files without a single import line.
Ambiguity, and how to settle it
Module imports bring in many names at once, so clashes are more likely. java.base has java.util.Date and java.sql has java.sql.Date; with both modules imported, using Date fails with "reference to Date is ambiguous". The fix is a more specific import. A single-type import (import java.sql.Date;) beats every on-demand import, and a package wildcard (import java.sql.*;) beats a module import.
Where it belongs
Module imports suit scripts, small command-line tools, prototypes, teaching material and quick experiments against an API such as java.net.http. In a long-lived service most teams keep explicit single-type imports: the IDE writes them, reviewers see exactly which Date a file means, and a JDK upgrade that adds a new public type to an imported module cannot create an ambiguity in a file nobody touched.
Example
import module java.base;
public class Main {
public static void main(String[] args) {
// List, Map, TreeMap and Collectors all come from the one module import
List<String> lines = List.of(
"GET /orders 200",
"POST /orders 201",
"GET /orders 200",
"GET /customers 404");
Map<String, Long> byStatus = lines.stream()
.map(line -> line.substring(line.lastIndexOf(' ') + 1))
.collect(Collectors.groupingBy(s -> s, TreeMap::new, Collectors.counting()));
System.out.println(byStatus);
}
}
// Output:
// {200=2, 201=1, 404=1}When to use it
- Write a one-file smoke test for a health endpoint using import module java.net.http.
- Prototype a JDBC query against the java.sql API without listing every interface import.
- Show a newcomer working code in a compact source file before explaining individual imports.
More examples
Transitive modules come along
Level and DocumentBuilderFactory are not in java.sql at all; they arrive because java.sql declares requires transitive java.logging and java.xml. That is the rule to remember when a module import makes more names visible than you expected.
import module java.sql;
public class Main {
public static void main(String[] args) {
// java.sql exports java.sql and javax.sql
System.out.println(Connection.class.getName());
System.out.println(DataSource.class.getName());
// java.sql requires java.logging and java.xml transitively
System.out.println(Level.WARNING.getClass().getName());
System.out.println(DocumentBuilderFactory.class.getName());
}
}
// Output:
// java.sql.Connection
// javax.sql.DataSource
// java.util.logging.Level
// javax.xml.parsers.DocumentBuilderFactoryFixing the Date ambiguity in a compact source file
The compact source file already imports java.base, so adding java.sql brings two Date classes into view. The single-type import picks one and the rest of both modules stays available.
import module java.sql;
import java.sql.Date; // without this line, Date is ambiguous with java.util.Date
void main() {
Date day = Date.valueOf("2026-01-15");
List<String> tables = List.of("orders", "customers"); // from the implicit java.base import
IO.println(day.getClass().getName() + " " + day);
IO.println(tables);
}
// Output:
// java.sql.Date 2026-01-15
// [orders, customers]A small HTTP check with two module imports
Two lines replace the half-dozen imports that HttpServer, HttpClient, HttpRequest and HttpResponse would otherwise need, which is exactly the kind of throwaway tool module imports are good for. InetSocketAddress, URI and StandardCharsets come from the implicit java.base import.
import module java.net.http;
import module jdk.httpserver;
void main() throws Exception {
HttpServer server = HttpServer.create(new InetSocketAddress("127.0.0.1", 0), 0);
server.createContext("/health", exchange -> {
byte[] body = "{\"status\":\"UP\"}".getBytes(StandardCharsets.UTF_8);
exchange.getResponseHeaders().set("Content-Type", "application/json");
exchange.sendResponseHeaders(200, body.length);
try (OutputStream out = exchange.getResponseBody()) {
out.write(body);
}
});
server.start();
try (HttpClient client = HttpClient.newHttpClient()) {
URI uri = URI.create("http://127.0.0.1:" + server.getAddress().getPort() + "/health");
HttpResponse<String> res = client.send(HttpRequest.newBuilder(uri).build(),
HttpResponse.BodyHandlers.ofString());
IO.println(res.statusCode() + " " + res.body());
} finally {
server.stop(0);
}
}
// Output:
// 200 {"status":"UP"}
Discussion