Topic 4.7
Packages and Imports
In one line
A package is a named group of related classes, stored in a matching folder, that gives every class a unique full name such as java.util.ArrayList. import lets you write the short name instead, and package-private access lets classes in one package share helpers without exposing them.
Think of it like this
A big school has two pupils called Riya. To avoid mix-ups, the register says "Riya, class 5B" and "Riya, class 7A". Inside 5B everyone just says "Riya". A package is the class name like "5B": it makes the full name unique. An import is like saying at the start of a conversation "when I say Riya I mean Riya from 7A".
Words you'll meet
New words in this topic, in plain English. Come back here whenever one feels fuzzy.
- Package
- A named group of related classes, like a folder with a unique name. It prevents name clashes and controls access.
- Fully qualified name
- A class's complete name including its package, like
java.util.ArrayList. It's unique. - Simple name
- Just the class's own name, like
ArrayList, without the package. - `import`
- A line at the top of a file that lets you use a class by its simple name. It doesn't load anything; it's a naming shortcut for the compiler.
- On-demand import
- An import ending in
.*that makes all public types of one package usable by simple name. - Static import
- An import that brings in static fields or methods so you can use them without the class name, e.g.
PIinstead ofMath.PI. - Unnamed package
- The package a class is in when its file has no
packagestatement. Also called the default package. - Classpath
- The list of folders and JAR files where the JVM and javac look for
.classfiles. Set with-cp.
Step by step
01Packages are names and folders
A small shop app might have a model package for data classes and an app package for the entry point. The package line names the package, and the folder path mirrors it.
The compiler enforces the output layout: javac -d out writes out/com/hectal/shop/model/Product.class no matter where the source file sat. The JVM finds classes by turning dots into folders under each classpath entry.
src/
com/hectal/shop/model/Product.java -> package com.hectal.shop.model;
com/hectal/shop/app/Main.java -> package com.hectal.shop.app;
out/ (created by javac -d out)
com/hectal/shop/model/Product.class
com/hectal/shop/app/Main.class02Write two classes in two packages
Product is public, as are its constructor and accessor methods, so other packages can use them. internalCode() has no modifier, so it's package-private: visible to com.hectal.shop.model only.
Main imports Product by its full name once, then uses the short name.
package com.hectal.shop.model;
public class Product {
private final String name;
private final long pricePaise;
public Product(String name, long pricePaise) {
this.name = name;
this.pricePaise = pricePaise;
}
public String name() { return name; }
public long pricePaise() { return pricePaise; }
String internalCode() { return "P-" + name.toUpperCase(); } // package-private
}03Compile to an output folder and run by full name
-d out tells javac where to put class files. To run, put out on the classpath and give the fully qualified name of the class with main.
If you cd into out/com/hectal/shop/app and type java Main, it fails: the class file says its name is com.hectal.shop.app.Main, not Main. Always run from the classpath root.
package com.hectal.shop.app;
import com.hectal.shop.model.Product;
public class Main {
public static void main(String[] args) {
Product p = new Product("tea", 2000);
System.out.println(p.name() + " costs " + p.pricePaise() + " paise");
}
}04Package-private: shared inside, hidden outside
Calling p.internalCode() from Main fails, because Main is in a different package. Any class inside com.hectal.shop.model could call it.
This is how you build a package with a small public surface and private helpers: only public things are the package's API. Topic 4.5 introduced the four access levels; packages are where package-private earns its keep.
05How the compiler resolves a simple name
When javac sees Product, it looks in this order: a type declared in the current file or a single-type import; then types in the same package; then on-demand imports (.*, including the automatic java.lang.*). If two on-demand imports both supply the name, it's ambiguous.
Imports happen entirely at compile time. The .class file contains only fully qualified names, so import java.util.* has zero run-time cost and doesn't make the program bigger or slower. Style guides still prefer explicit imports because they show exactly where each name comes from.
06Name clashes and how to settle them
java.util.Date and java.sql.Date, or java.util.List and java.awt.List: the JDK itself has duplicates. With import java.util.*; and import java.awt.*;, using List is an error.
Fix: add import java.util.List;. A single-type import beats on-demand imports. If you need both in one file, import one and write the other's full name: java.awt.List awtList = ....
Try it yourself
- 1
Remove an import
In "Three ways to name a class", delete
import java.util.*;. Predict which lines fail and the exact error (cannot find symbol ... class Map). Put it back. - 2
Prove .* is not recursive
In "Every class has a full name", replace
java.util.function.Function.classwithFunction.classand addimport java.util.*;. Predict whether it compiles. Then addimport java.util.function.Function;. - 3
Build a two-package project locally
Create the
Product/Mainlayout from the walkthrough, compile with-d out, and run withjava -cp out com.hectal.shop.app.Main. Then trycd out/com/hectal/shop/appandjava Main, and read the error.terminal$ cd out/com/hectal/shop/appjava Main── expected output ──Error: Could not find or load main class MainCaused by: java.lang.NoClassDefFoundError: Main (wrong name: com/hectal/shop/app/Main)
Code & diagrams
Expected output
[x] [y] {k=1}
max = 9
PI from java.lang: 3.141592653589793Main and Helper have no package statement, so they're in the unnamed package (an empty name).
Expected output
java.lang.String
java.util.ArrayList
java.util.function.Function
package of ArrayList: java.util
package of Main: []
Helperjava.sql.Date extends java.util.Date. For new code prefer java.time.LocalDate (Phase 10).
Expected output
java.util.Date
java.sql.Date
sql.Date is a util.Date? trueA fragment that needs JUnit 5 on the classpath; it shows where static imports read best.
import static org.junit.jupiter.api.Assertions.assertEquals;
import static java.util.concurrent.TimeUnit.SECONDS;
class PriceTest {
@org.junit.jupiter.api.Test
void addsTax() {
assertEquals(118, Price.withGst(100)); // instead of Assertions.assertEquals
long timeout = SECONDS.toMillis(5);
}
}Break it on purpose
Errors are the best teachers. Make each change, read the error, guess what went wrong, then reveal the answer.
Break #1
Use a class without importing it
Delete import java.util.ArrayList; and keep ArrayList<String> xs = new ArrayList<>();.
Break #2
Two on-demand imports with the same name
Write import java.util.*; and import java.awt.*;, then declare List xs = null;.
Myth vs fact
Myth
import java.util.* makes the program slower or bigger.
Fact
Imports exist only at compile time. The class file stores fully qualified names either way; there's no run-time cost.
Myth
import java.util.* also imports java.util.function and java.util.concurrent.
Fact
Packages aren't nested for Java's purposes. java.util.function is a separate package that needs its own import.
Myth
import loads or includes the class, like #include in C.
Fact
It only tells the compiler how to expand a short name. Classes are loaded lazily by the JVM when first used, whether imported or written in full.
Pro corner
Extra depth for experienced readers. New to this? Skip it for now and come back later.
- ▸
Packages don't form a hierarchy for access:
com.hectal.shopandcom.hectal.shop.modelare unrelated, and package-private members of one aren't visible in the other. The dots only mirror folders. - ▸
Modules (Java 9, Phase 15) add
exports: a public class in a non-exported package is unreachable from other modules. Two modules containing the same package (a split package) are illegal on the module path, which is a common migration headache. - ▸
Java 25 finalised module import declarations (JEP 511):
import module java.base;imports every exported package of a module on demand. It's handy for scripts and teaching; ambiguity rules still apply (single-type imports win). - ▸
Classes in the unnamed package can't be imported by classes in named packages (since Java 1.4). Real projects always use named packages, and tools like Maven and Gradle expect
src/main/java/<package folders>.
Remember this
- 1
A package is declared with
package com.hectal.shop.model;as the first statement of a file (only comments may come before it). The class's fully qualified name becomescom.hectal.shop.model.Product. Two libraries can both have a class namedProductas long as their packages differ. - 2
By convention package names are lower-case and start with a reversed internet domain you control:
in.hectal.shop,com.google.common. The source file must sit in a matching folder path (com/hectal/shop/model/Product.java), and the compiled.classfile always lands in that path under the output folder. - 3
An
importonly saves typing; it doesn't load or copy anything.import java.util.ArrayList;(single-type import) lets you writeArrayList.import java.util.*;(on-demand import) makes every public type injava.utilavailable by simple name, but not sub-packages:java.util.*does not includejava.util.function.Function. - 4
java.langis imported automatically, which is whyString,Math,SystemandIntegerneed no import. Classes in the same package need no import either. A file with nopackageline is in the unnamed (default) package, fine for small experiments like this course's examples, but classes in named packages can't import it. - 5
A static import (Java 5) brings static members in by simple name:
import static java.lang.Math.max;lets you writemax(a, b). Use it sparingly, for well-known constants and methods; too many make it unclear where a name came from. - 6
When two imports bring in the same simple name (
java.util.Listandjava.awt.List), usingListis a compile error:reference to List is ambiguous. Fix it with a single-type import (which beats an on-demand one) or by writing the fully qualified name in place.
Explain it without notes
What problems do packages solve?
What does import actually do, and what does it cost at run time?
Explain the order in which javac resolves a simple class name, and how an ambiguity arises.
Why does running java Main inside the class's own folder fail for a class in a package?
Practice
Write a program that uses java.util.List, java.util.Map and java.util.function.Function with explicit single-type imports: build a list of three words, map each to its length with a Function<String, Integer>, store the results in a TreeMap, and print it.
Use import static java.lang.Math.*; to print sqrt(144), abs(-7) and pow(2, 10).
Without any import statement, create a java.util.ArrayList<Integer>, add 3 and 4, and print its size and contents.
Trade-offs
- ↔
Explicit single-type imports document exactly where each name comes from and avoid future ambiguity when a library adds a class, at the cost of a longer import list (IDEs manage it). On-demand imports are shorter but can break the build when a new version of a package adds a clashing name.
- ↔
Fine-grained packages (one per feature) keep package-private helpers truly private, but too many tiny packages force you to make things
publicjust to share them. Group by feature, not by layer, so related classes can stay package-private.
Done when you can
Done when you can write
packagelines and place files in the matching folders.Done when you can compile with
-d outand run with-cp outand a fully qualified name.Done when you know what
java.langand the same package give you without imports.Done when you can resolve an ambiguous name with a single-type import or a full name.
Done when you can explain why imports cost nothing at run time.