Import and Static Import in Java
Import lets you use a short class name instead of its full package path. Static import goes one step further and lets you use a class's static members without the class name. This guide covers both, their ambiguity problems, resolution rules, and when to use (or avoid) each.
Part 1 — The Import Statement
Why we need it
Without an import, using a class from another package by its short name fails:
class Test {
public static void main(String[] args) {
ArrayList<String> names = new ArrayList<>();
}
}
Error: cannot find symbol — class ArrayList.
One fix is the fully qualified name (FQN) everywhere:
java.util.ArrayList<String> names = new java.util.ArrayList<>();
That gets long and noisy. The import statement lets you write the short name:
import java.util.ArrayList;
class Test {
public static void main(String[] args) {
ArrayList<String> names = new ArrayList<>();
names.add("Asha");
System.out.println(names); // [Asha]
}
}
In short: import is a typing shortcut. Source file order is always: package → import statements → class declaration.
Explicit vs implicit import
- Explicit — recommended. Dependencies are visible at the top of the file, so large projects stay readable.
- Implicit — shorter to type, but you can't tell which classes are actually used. Fine for quick experiments; avoid it in big codebases.
Which import statements are valid?
| Statement | Result |
|---|---|
import java.util.ArrayList; | Valid (explicit) |
import java.util.*; | Valid (implicit) |
import java.util.ArrayList.*; | Compiles, but pointless: it imports only nested types of ArrayList, not ArrayList itself |
import java.util; | Error: util is a package, not a class |
import java.lang.Math.sqrt; | Error: sqrt is a method, not a type |
import java.lang; | Error: package name without .* |
Rule: a normal import names a type (class, interface, enum) or uses .* on a package. Members like methods and fields need a static import (Part 2).
Fully qualified names need no import
class Report {
java.time.LocalDate created = java.time.LocalDate.now(); // no import needed
}
Using an FQN means no import is needed, and having an import means you don't need the FQN.
The ambiguity problem
Date exists in both java.util and java.sql; List exists in both java.util and java.awt. Wildcard-importing both packages is ambiguous:
import java.util.*;
import java.sql.*;
class Test {
public static void main(String[] args) {
Date d = new Date(); // which Date?
}
}
Compile-time error: reference to Date is ambiguous.
Two fixes:
// Fix 1: explicit import for the one you want
import java.util.Date;
import java.sql.*;
// Fix 2: FQN for the clashing class
java.util.Date now = new java.util.Date();
java.sql.Date sqlDate = new java.sql.Date(now.getTime());
The error only appears when the ambiguous name is actually used; importing both packages alone is fine.
Resolution precedence
When a short name could refer to several classes, the compiler picks in this order:
- Explicit class import
- A class in the same package (the current directory, if it's the default package)
- Implicit (on-demand) import
import java.util.Date;
import java.sql.*;
class Test {
public static void main(String[] args) {
Date d = new Date();
System.out.println(d.getClass().getName());
}
}
Output: java.util.Date — the explicit import beats java.sql.*.
Sub-packages are not included
import java.util.*; reaches ArrayList and Date, but not Pattern.To use Pattern | Works? |
|---|---|
import java.*; | No |
import java.util.*; | No |
import java.util.regex.*; (or java.util.regex.Pattern) | Yes |
| No import | No |
What needs no import
- Everything in
java.lang(String,Math,System,Integer…) - Classes in the same package as the current class
Import is compile-time only
More imports mean slightly more work for the compiler, but have no effect on runtime performance. Also, unlike C's #include, which pulls in header contents up front, a Java import loads nothing; a .class file is loaded on demand the first time the class is actually used.
Part 2 — The Static Import Statement
Static import was added in Java 1.5, alongside the for-each loop, var-args, autoboxing, generics, covariant return types, annotations and enums. It lets you use static members (fields, methods, nested types) of a class without writing the class name.
Without and with static import
// Without
class Geometry {
public static void main(String[] args) {
double r = 2;
System.out.println(Math.PI * Math.pow(r, 2));
System.out.println(Math.sqrt(Math.pow(3, 2) + Math.pow(4, 2)));
}
}
// With
import static java.lang.Math.*;
class Geometry {
public static void main(String[] args) {
double r = 2;
System.out.println(PI * pow(r, 2)); // 12.566370614359172
System.out.println(sqrt(pow(3, 2) + pow(4, 2))); // 5.0
}
}
Understanding System.out.println()
- System — the class
- out — a
staticvariable of typePrintStreaminsideSystem - println() — a method of
PrintStream
Since out is static, it can be statically imported:
import static java.lang.System.out;
class Test {
public static void main(String[] args) {
out.println("hello");
out.println("hi");
}
}
Same idea for your own static members: with class Config { static String env = "prod"; }, the expression Config.env.length() reads as class → static variable (a String) → method of String.
Where static import actually earns its keep
import static java.util.stream.Collectors.toList;
List<String> upper = names.stream()
.map(String::toUpperCase)
.collect(toList());
Well-known constants and builder-style helpers (Collectors.toList(), assertion methods in test frameworks) read better with it. Overusing it hides where a name comes from.
import static java.lang.Math.max; imports all overloads of max. Static import only works for static members; instance members can't be imported this way.The ambiguity problem
Both Integer and Byte define MAX_VALUE. Wildcard-importing both is ambiguous when the name is used:
import static java.lang.Integer.*;
import static java.lang.Byte.*;
class Test {
public static void main(String[] args) {
System.out.println(MAX_VALUE);
}
}
Compile-time error: reference to MAX_VALUE is ambiguous.
Ambiguity is rare with normal import (two packages with an identically named class) but common with static import (many classes share member names like MAX_VALUE, valueOf, parse…).
Resolution precedence
- The current class's own static members
- Explicit static import
- Implicit (wildcard) static import
Case 1 — own member wins
import static java.lang.Integer.MAX_VALUE;
import static java.lang.Byte.*;
class Test {
static int MAX_VALUE = 999;
public static void main(String[] args) {
System.out.println(MAX_VALUE); // 999
}
}
Output: 999
Case 2 — explicit static import wins (remove the class's own MAX_VALUE)
import static java.lang.Integer.MAX_VALUE;
import static java.lang.Byte.*;
class Test {
public static void main(String[] args) {
System.out.println(MAX_VALUE); // Integer.MAX_VALUE
}
}
Output: 2147483647
Case 3 — only the implicit import is left (also remove the explicit import)
import static java.lang.Byte.*;
class Test {
public static void main(String[] args) {
System.out.println(MAX_VALUE); // Byte.MAX_VALUE
}
}
Output: 127
Which static import statements are valid?
| Statement | Result |
|---|---|
import static java.lang.Math.*; | Valid |
import static java.lang.Math.sqrt; | Valid |
import static java.lang.Math.sqrt(); | Error: no parentheses allowed |
import static java.lang; | Error: needs a class, not a package |
import static java.lang.*; | Error: java.lang is a package; name a class |
Syntax and comparison
| Normal import | Static import | |
|---|---|---|
| Explicit | import java.util.ArrayList; | import static java.lang.Math.sqrt; |
| Implicit | import java.util.*; | import static java.lang.Math.*; |
| Imports | Classes and interfaces of a package | Static members of a class or interface |
| Lets you skip | The package name | The class name |
| Ambiguity | Rare (same class name in two packages) | Common (same member name in two classes) |
| Precedence | Explicit → same package → implicit | Own member → explicit → implicit |
Conclusion
Import is purely a compile-time convenience: an FQN always works without it, and it never changes runtime behavior. Prefer explicit imports in real projects, remember that wildcards skip sub-packages, and resolve clashes like java.util.Date vs java.sql.Date with an explicit import or an FQN.
Static import trims code that leans on one utility class, but hides where a name comes from and is far more prone to ambiguity. Use it where it clearly improves readability, not as a habit.
0 Comments