Packages, directories and where classes go
Why a packaged class must be run by its full name, and what -d is for.
A package is a namespace. It exists so that two libraries can both have a class called Order without colliding, and so that a reader can tell which List you meant. It also imposes a rule about directories that catches everybody exactly once.
The rule
A class declares its package on the first line:
package com.shop;
public class Order { … }Its full name is now com.shop.Order, and that is its real name — Order is shorthand that only works inside the same package or after an import.
The compiler expects the directory structure to mirror the package:
com/shop/Order.java package com.shop;And the JVM expects the same of the compiled output. That is what produces the failure people meet on their first packaged class:
$ javac com/shop/Order.java
$ java Order
Error: Could not find or load main class Order
$ java com.shop.Order
okThe short name does not work. You run a class by its full name, because the full name is the name.
-d, and what a build tool does
Compiling in place mixes source and output. -d separates them, and recreates the package directories underneath:
$ javac -d out com/shop/Order.java
$ find out -name "*.class"
out/com/shop/Order.classThen out is the root the JVM should search — the classpath:
java -cp out com.shop.OrderThat is the whole mechanism, and every build tool is doing exactly this with more ceremony: sources under src/main/java, classes under target/classes or build/classes, and a classpath assembled from those plus every dependency.
Naming, and the convention that is not arbitrary
Packages are lower case and read as reversed domain names:
com.shop.billing
org.springframework.boot
java.utilReversing a domain guarantees uniqueness without a registry: nobody else can legitimately publish under com.yourcompany. That convention is why every library you import looks like this, and why com.example appears in every tutorial.
Two rules worth having:
- Never use the default package — a class with no
packageline. It cannot be imported by anything in a package, which makes it unusable from real code. Tutorials use it for brevity; nothing else should. - Group by feature, not by layer.
com.shop.billingcontaining its controller, service and repository beatscom.shop.controllerscontaining thirty unrelated controllers. Things that change together should live together, and a feature changes together.
The layout you will see everywhere
project/
├── pom.xml or build.gradle the build's configuration
└── src/
├── main/
│ ├── java/ your code, in package directories
│ │ └── com/shop/Order.java
│ └── resources/ config, templates, static files
└── test/
├── java/ tests, mirroring main's packages
└── resources/This is Maven's convention, which Gradle adopted, which everything else now assumes. Nothing enforces it beyond the tools defaulting to it — and that is enough, because a new engineer opening your project knows where to look without asking.
src/test/java mirroring src/main/java matters more than it looks: a test for com.shop.Order in package com.shop can reach that class's package-private members, which is how you test something without making it public just for the test.
Misconceptions
- "The package is just a folder." The folder mirrors it. The package is part of the class's identity — two classes with the same simple name in different packages are entirely different types.
- "Imports load the class." An import is a compile-time shorthand so you can write
Orderinstead ofcom.shop.Order. Nothing is loaded because of it; loading happens when the class is first used. - "
import com.shop.*is slower." It is not; it makes no run-time difference. It is discouraged because it hides which class you meant, and two wildcard imports can collide.