Packages, imports, and project layout
Inheritance, interfaces and polymorphism10 min readLesson 3 of 6
3.1 Packages organize classes
A package is a namespace for classes. The package declaration appears first in a source file, before imports: package com.example.shop;. The folder path matches the package, so that file lives in com/example/shop/.
Package names use lowercase and usually start with a reversed domain. The name reduces collisions: two teams can both have a Report class as long as the packages differ.
The default package has no name. A class without a package declaration sits there. Small experiments can use it, but published code and anything another project imports should have a package.
import java.time.LocalDate;
public class Main {
public static void main(String[] args) {
LocalDate date = LocalDate.of(2026, 10, 29);
System.out.println(date.getYear());
System.out.println(date.getMonth());
System.out.println(java.time.LocalDate.of(2026, 1, 1).getDayOfMonth());
}
}Output
2026 OCTOBER 1
3.1.1 Imports
import java.util.ArrayList; lets the source use ArrayList without the full name. import java.util.*; imports the classes in that package, not its subpackages. The wildcard is convenient in examples; explicit imports show a reader which classes the file uses.
java.lang is imported automatically, so String, System, and Math need no import. Other packages must be imported or named in full.
When two imported classes share a simple name, the compiler rejects the ambiguous use. Import one of them and write the other with its full name, or avoid one import entirely.
import java.util.ArrayList;
import java.util.List;
import static java.lang.Math.max;
public class Main {
public static void main(String[] args) {
List<String> names = new ArrayList<>();
names.add("Asha");
names.add("Ravi");
System.out.println(names.size());
System.out.println(max(12, 30));
}
}Output
2 30
3.1.2 Static imports
A static import brings a static member into scope: import static java.lang.Math.PI; lets the file write PI. import static java.lang.Math.*; brings the static members of Math into scope.
Static imports can shorten formulas and test assertions. They can also hide where a name comes from, especially with a wildcard. Use them for constants and well-known methods, and keep the set small.
An import does nothing at runtime. It only gives the compiler a short name for a type or member, and unused imports have no effect on the program.
import java.util.*;
public class Main {
public static void main(String[] args) {
Map<String, Integer> stock = new HashMap<>();
stock.put("pen", 12);
stock.put("book", 4);
System.out.println(stock.get("pen"));
System.out.println(stock.containsKey("book"));
}
}Output
12 true
3.1.3 Your own package
Create a folder for the package, place the source file inside it, and make the first line package name;. Compile with javac -d out path/to/Main.java so the class files go into a matching folder structure. Run with the classpath set to the output folder and the fully qualified name: java -cp out com.example.Main.
An IDE handles the folders for you, but the same rule applies: package name and path must agree. Moving a file without changing its package declaration produces confusing errors.
Classes in one package can use each other's package-private members. A class outside the package cannot, even after an import, because package access is part of the class's boundary.
public class Main {
public static void main(String[] args) {
java.util.List<String> names = new java.util.ArrayList<>();
names.add("Asha");
names.add("Ravi");
System.out.println(names);
System.out.println(names.getClass().getName());
}
}Output
[Asha, Ravi] java.util.ArrayList
3.1.4 Imports and access together
An import gives a name only. Access still depends on the class and member modifiers. A public class can be imported from anywhere, while a package-private class is visible only inside its package, so another package cannot use it even with a full name.
That distinction is useful for helpers. A public OrderService can keep a package-private PriceCalculator beside it, and code in other packages sees only the service.
Keep the public surface small. A package of a few related classes with one or two public entry points is easier to change than a package where every helper is public.
package demo;
public class Main {
public static void main(String[] args) {
System.out.println(Helper.greet("Asha"));
}
}
class Helper {
static String greet(String name) {
return "Hello, " + name;
}
}Output
Hello, Asha
3.1.5 Practice
- Add
package demo;to a small class, put it indemo/Main.java, compile withjavac -d out demo/Main.java, and runjava -cp out demo.Main. - Import
java.time.LocalDateand print the year of a fixed date. - Replace an explicit import with a wildcard and note which class names remain readable.
- Add a static import for
Math.PIand calculate a circle area. - Remove an import that is still needed, read the compiler error, and restore it.
For step 3, compare the result with the explicit import version. The compiled program should behave the same.
