Imports
Imports let you reference a type by its simple name instead of writing its full package path every time — a purely compile-time convenience that has nothing to do with runtime loading or performance.
An import statement tells the compiler which fully qualified type a simple name should resolve to, so code can write List instead of java.util.List. Imports are resolved entirely at compile time and have zero effect on runtime behavior, class loading, or performance.
Single-Type Import
The most common form imports one specific type by its fully qualified name, after which the simple name refers to it throughout the file.
import java.util.ArrayList;
import java.util.List;
public class Main {
public static void main(String[] args) {
List<String> names = new ArrayList<>();
names.add("Ada");
}
}Wildcard Import
A wildcard import brings every public type in a package into scope by simple name — but it does not import subpackages. import java.util.*; makes List, Map, HashMap, and so on available, but not java.util.concurrent.ConcurrentHashMap, which lives in a different (sub)package entirely.
import java.util.*;
public class Main {
public static void main(String[] args) {
List<String> list = new ArrayList<>(); // from java.util -- fine
Map<String, Integer> map = new HashMap<>(); // from java.util -- fine
// ConcurrentHashMap requires its own import: java.util.concurrent.ConcurrentHashMap
}
}Wildcard Imports: Style Tradeoffs
Wildcard imports reduce boilerplate but make it less obvious, just from reading the imports, exactly which types a file depends on — and they increase the chance of an import-ambiguity conflict when two wildcard-imported packages happen to export a same-named type. Many style guides and IDE defaults favor explicit single-type imports for clarity and to keep diffs minimal when adding a new type reference. Either style compiles identically; this is a readability and maintainability choice, not a technical requirement.
Static Import
A static import brings a class's static members (fields and/or methods) into scope, so they can be used without the class-name prefix. It comes in single-member and wildcard forms, mirroring ordinary imports.
import static java.lang.Math.PI;
import static java.lang.Math.pow;
import static java.util.Collections.*; // static wildcard: all static members of Collections
public class Main {
public static void main(String[] args) {
double area = PI * pow(5, 2); // instead of Math.PI, Math.pow(...)
java.util.List<Integer> nums = new java.util.ArrayList<>(java.util.List.of(3, 1, 2));
sort(nums); // instead of Collections.sort(nums)
System.out.println(area + " " + nums);
}
}Static Import: When to Use It
Static imports are most valuable for frequently used, unambiguous utility members — Math constants/functions, JUnit's assertion methods (assertEquals, assertTrue), or Collectors factory methods in stream code. Overusing static imports for many different classes' members in one file can make it unclear, at the call site, which class a bare method name actually belongs to, hurting readability rather than helping it.
Imports Are Purely Compile-Time
An import statement has no effect on the compiled .class bytecode's runtime behavior — it does not "load" anything at runtime, does not affect performance, and does not itself cause a class to be loaded by the JVM (class loading happens lazily, on first active use, independent of import statements). Imports exist solely to let the compiler resolve simple names to fully qualified types while compiling the source file.
Import Ambiguity
If two wildcard-imported packages both contain a public type with the same simple name, and code refers to that simple name without qualification, the compiler reports an ambiguity error — it will not guess which one you meant. The fix is to add an explicit single-type import for the intended one (which takes precedence over a wildcard) or to fully qualify at least one usage.
import java.util.*;
import java.sql.*;
public class Main {
public static void main(String[] args) {
// Date date = new Date(); // ambiguous: java.util.Date or java.sql.Date?
java.util.Date date = new java.util.Date(); // resolved explicitly
}
}A Single-Type Import Always Wins Over a Wildcard
If a file has both an explicit single-type import and a wildcard import that would also match, the explicit import takes precedence for that simple name — this lets you use a wildcard import for convenience while still pinning down one specific type from an otherwise-conflicting name.
import java.sql.Date; // explicit import wins for the name "Date"
import java.util.*; // also brings in java.util.Date, but loses to the explicit import
public class Main {
public static void main(String[] args) {
Date d = new Date(); // resolves to java.sql.Date, thanks to the explicit import
}
}Imports vs. the Package Declaration
A package declaration states which package the current file's own types belong to; an import statement brings other packages' types into scope for use within the file. They serve entirely different purposes and both, when present, appear near the top of the file — package declaration first, then imports, then type declarations.
Types in the Same Package Need No Import
Any type in the same package as the current file is automatically visible by its simple name, with no import statement required — imports exist only to reach types outside the current package.
Unused Imports
An import for a type that is never referenced in the file compiles without error — javac does not reject unused imports — but most IDEs and linters flag them, since they add clutter and can mislead readers about the file's real dependencies. Cleaning up unused imports is good hygiene, not a compiler requirement.
Practical Usage
In everyday code, prefer targeted single-type imports for anything ambiguous or infrequently used, reserve wildcard imports (if used at all) for packages where nearly every type is genuinely needed, and use static imports sparingly for a small set of extremely common, unambiguous members (test assertions, math functions).
Edge Cases
A wildcard import does not reach into subpackages — import com.example.*; does not make com.example.util.Helper available; that requires its own import (import com.example.util.Helper; or import com.example.util.*;).
You cannot import a type from the unnamed (default) package into a named-package file — this is one concrete reason to avoid leaving real classes in the default package.
Key Takeaways
Imports let code refer to a type by simple name instead of its fully qualified name; they resolve entirely at compile time.
A single-type import brings in one specific type; a wildcard import brings in all public types directly in a package, but not its subpackages.
A static import brings a class's static members into scope for unqualified use, useful for a small set of frequently used, unambiguous members.
An explicit single-type import always takes precedence over a wildcard import for the same simple name.
Two wildcard-imported same-named types cause a compile-time ambiguity error unless resolved with an explicit import or full qualification.
Imports have no runtime effect — they do not trigger class loading or affect performance.
import java.util.*; import java.sql.*; public class Main { Date created; // ambiguous: java.util.Date or java.sql.Date -- compile error } import java.util.Date; // explicit import wins over any wildcard for the same name import java.sql.*; public class Main { Date created; // resolves to java.util.Date }
When two wildcard imports both offer a type with the same simple name, the compiler cannot pick one -- an explicit single-type import (which always takes precedence) or full qualification is required.
// Assuming this import "loads java.util.concurrent" for use: import java.util.*; //... java.util.concurrent.ConcurrentHashMap<String, Integer> map; // requires its own import import java.util.*; import java.util.concurrent.ConcurrentHashMap; ConcurrentHashMap<String, Integer> map = new ConcurrentHashMap<>();
A wildcard import only covers types directly in the named package, never in its subpackages -- java.util.* does not reach java.util.concurrent, which is a separate package.
import static java.lang.Math.*; import static java.util.Collections.*; // later, deep in a large file: int m = max(3, 7); // is this Math.max or Collections.max? unclear at a glance import static java.lang.Math.max; // explicit, single member -- clear at the call site
Overusing wildcard static imports from multiple classes makes it unclear, purely from reading a call site, which class a bare method name actually resolves to -- hurting readability rather than helping it.
Does import java.util.*; also bring in classes from java.util.concurrent? Why or why not?
No. A wildcard import only covers public types declared directly in the named package, not any subpackage -- java.util.concurrent is a separate package and requires its own import.
If a file has both 'import java.sql.Date;' and 'import java.util.*;', which Date does an unqualified 'Date' refer to?
java.sql.Date. An explicit single-type import always takes precedence over a wildcard import for the same simple name.
Do import statements have any effect on runtime performance or when a class gets loaded by the JVM?
No. Imports are resolved entirely at compile time to let the compiler map simple names to fully qualified types; they have no effect on runtime class loading (which happens lazily, on first active use) or performance.
What happens if two wildcard-imported packages each contain a public type with the same simple name, and the code refers to that name without qualification?
The compiler reports an ambiguity error and refuses to guess; the fix is an explicit single-type import for the intended type, or fully qualifying the reference.
Does an unused import cause a compilation error?
No. javac compiles unused imports without error, though IDEs and linters commonly flag them as a style/cleanliness issue.
What does a static import bring into scope, and give an example of a good use case for it.
It brings a class's static members (fields and/or methods) into scope so they can be referenced without the class-name prefix. A good use case is Math's constants and functions (PI, sqrt, pow) or common test-assertion methods, where the originating class is unambiguous and the prefix adds little value.
Interview-style: why doesn't Java automatically make every package's types available everywhere, the way some other languages import an entire standard library implicitly?
Explicit imports keep dependency information visible and localized to each file, avoid unnecessary namespace pollution and naming collisions across the entire codebase, and let the compiler give precise, early errors when a name is ambiguous or missing -- consistent with Java's general preference for explicitness over implicit magic.
Write a small program that uses static imports for Math.PI and Math.pow to compute the area of a circle, without prefixing either with Math.
import static java.lang.Math.PI; import static java.lang.Math.pow; public class Main { public static void main(String[] args) { double radius = 4.0; double area = PI * pow(radius, 2); System.out.println(area); } }
Static imports bring PI and pow directly into scope as if they were declared locally, letting the expression read naturally without the Math. prefix, while still referring to exactly the same java.lang.Math members.
What does 'List' resolve to here, and why?
It resolves to java.awt.List, because it is an explicit single-type import, and an explicit import always takes precedence over any wildcard import (here, java.util.*, which also offers a List) for the same simple name.
Write a file that deliberately triggers an import ambiguity compile error by wildcard-importing both java.util and java.sql and referencing Date unqualified, then show the one-line fix.
import java.util.*; import java.sql.*; public class Main { public static void main(String[] args) { // Date d = new Date(); // ambiguous -- does not compile java.util.Date d = new java.util.Date(); // FIX: fully qualify (or add an explicit single-type import) System.out.println(d); } }
With both java.util.* and java.sql.* wildcard-imported, the bare name Date is ambiguous between java.util.Date and java.sql.Date, so the compiler rejects it. Fully qualifying the reference (or adding "import java.util.Date;" explicitly) resolves the ambiguity.
Does Main need to import anything from package 'app' itself (its own package) to use other classes declared there?
No. Types declared in the same package as the current file (here, package app) are automatically visible by their simple name with no import required. Only app.helpers.Formatter -- a different, sub-, package -- needs an explicit import, exactly as shown.
import java.util.*;
import java.awt.List; // note: java.awt also has a class named List
public class Main {
public static void main(String[] args) {
List l = new List();
}
}package app;
import app.helpers.Formatter; // app.helpers is a subpackage of app
public class Main {
public static void main(String[] args) {
System.out.println(Formatter.format("hi"));
}
}