Free lesson · Java Basic To Advance
Why Packages
Organizing the World's Java Code
Olena Marchenko here, picking up Module 11 after Brandon's deep OOP arc through Modules 7-10. This module covers Java's organizational mechanism: packages and the imports that connect them.
If Modules 7-10 covered how to build classes, this module covers how to organize them. Real Java codebases have thousands of classes. Without organization, every class name would have to be globally unique, namespace conflicts would be everywhere, and finding anything would be hopeless. Packages solve this.
This is a syntax-and-organization module. The conceptual depth is lower than OOP modules, but the mechanics matter — every Java file starts with a package declaration, every class name belongs to a package, every import navigates the package system. Getting these right is foundational.
The Problem Packages Solve
Imagine Java without packages. Every class would need a globally unique name. Two libraries both wanting to name their class User would collide. The Java standard library alone has hundreds of classes — String, List, File, Random, Date, Connection, Driver — names that show up in many contexts.
Worse: developers across the world write classes all the time. Two projects accidentally creating Helper or Utils classes would conflict if they shared a runtime. Library authors would have to coordinate globally on class names.
This was actually a problem in early programming systems. Some languages still struggle with it. Java solved it from day one with packages.
How Packages Solve It
A package is a namespace — a container that gives classes a qualified name based on where they live. The full name of a Java class isn't just its short name like User. It's the fully qualified name combining the package and the class:
com.example.users.User
com.example.products.User ← different class, same short name, different package
java.util.Date
java.sql.Date ← yes, there are two Date classes in the JDK
Two User classes in different packages are different types. They don't conflict. The package name disambiguates them.
The package structure mirrors a directory structure on disk. The class com.example.users.User lives in com/example/users/User.java. This isn't just convention — the compiler and JVM use it to find classes.
A Concrete Example
Imagine an e-commerce application with these packages:
com.shop.users ← User-related classes
com.shop.products ← Product-related classes
com.shop.orders ← Order-related classes
com.shop.payments ← Payment processing
com.shop.shipping ← Shipping logic
com.shop.utils ← Cross-cutting utilities
Inside each package, classes can have any name without worrying about conflicts across packages:
com.shop.users.User
com.shop.products.User ← could exist — different concept, different package
com.shop.users.Address
com.shop.shipping.Address ← could exist — shipping might have its own Address concept
Both User classes might make sense — a User in the users package is the customer profile, while a User in the products package might be a product reviewer or other context-specific user. They're different types serving different needs.
The Standard Library Example
The Java standard library itself uses packages extensively:
java.lang - core types: String, Object, Math, System
java.util - collections: List, Map, Set, ArrayList
java.io - I/O: File, InputStream, Reader, Writer
java.nio - newer I/O: Path, Files, ByteBuffer
java.net - networking: URL, Socket, ServerSocket
java.sql - database: Connection, Statement, ResultSet
java.time - dates: LocalDate, Instant, Duration
java.util.concurrent - concurrency: ExecutorService, ConcurrentHashMap
Each package groups related functionality. When you need date handling, you reach for java.time. When you need collections, java.util. The organization is intuitive once you learn it.
There's even java.sql.Date AND java.util.Date — historically related but different types. The packages keep them distinct.
Packages Are More Than Just Naming
Packages provide three benefits beyond namespace organization:
1. Access control. Package-private access (no modifier) means "visible only within this package." Classes in the same package can access each other's package-private members; classes in other packages cannot. This lets you create internal APIs visible only within a package — a kind of "team-only" access.
We covered package-private in Topic 7.5. Topic 11.7 revisits it from the package perspective.
2. Logical grouping. Even ignoring access, packages tell developers what classes belong together. When you see import com.shop.payments.PaymentProcessor, you know it's payment-related. When you see import java.util.List, you know it's a collection.
This makes code navigation faster. IDEs use packages to organize tree views. Documentation tools generate package overview pages. Build tools group test classes by package. Packages aren't just for the compiler — they're for humans reading the code.
3. Distribution and modularity. JAR files (Java's distribution format) typically organize classes by package. Modular systems (Java 9+ Module system) operate on packages. Build tools and IDEs use packages as units of work.
What Defines a Package
Every Java source file declares a package on its first non-comment line:
package com.shop.users;
public class User {
// ...
}
This file:
- Lives in
com/shop/users/User.java(mirroring the package path) - Belongs to the
com.shop.userspackage - Its fully qualified class name is
com.shop.users.User
The package declaration is the only thing that determines what package a class belongs to. Without it, the class is in the default package — an unnamed root package that's strongly discouraged for any real code.
A Real Project Structure
Here's the directory structure of a real Java project:
src/main/java/
└── com/
└── shop/
├── App.java (com.shop.App)
├── users/
│ ├── User.java (com.shop.users.User)
│ ├── UserService.java (com.shop.users.UserService)
│ └── UserRepository.java (com.shop.users.UserRepository)
├── products/
│ ├── Product.java (com.shop.products.Product)
│ ├── ProductCatalog.java (com.shop.products.ProductCatalog)
│ └── pricing/
│ └── PriceCalculator.java (com.shop.products.pricing.PriceCalculator)
└── orders/
├── Order.java (com.shop.orders.Order)
└── OrderService.java (com.shop.orders.OrderService)
The directory hierarchy mirrors the package hierarchy. The compiler verifies this — if the package declaration doesn't match the directory, you get an error.
This is how real Java codebases are organized. Once you internalize this, navigating any Java project becomes natural.
graph TD
A["com.shop"] --> B["users"]
A --> C["products"]
A --> D["orders"]
B --> B1["User"]
B --> B2["UserService"]
B --> B3["UserRepository"]
C --> C1["Product"]
C --> C2["ProductCatalog"]
C --> C3["pricing"]
C3 --> C3a["PriceCalculator"]
D --> D1["Order"]
D --> D2["OrderService"]
style A fill:#7c3aed,color:#fff
style B fill:#3b82f6,color:#fff
style C fill:#10b981,color:#fff
style D fill:#f59e0b,color:#fff
This diagram shows a typical Java package structure for an e-commerce application. The root package com.shop branches into three main sub-packages: users, products, and orders. Each sub-package contains classes related to that domain. Products has a further nested package called pricing, containing the PriceCalculator class. This hierarchy mirrors the directory structure on disk — the User class lives at src/main/java/com/shop/users/User.java. The dots in the package name correspond to directory separators in the file system. This is how every Java project organizes its code, from small applications to massive frameworks.
The Coffee Shop / freeCodeCamp Moment
When I was teaching myself from freeCodeCamp back in 2018, packages were one of the things that confused me the most early on. The tutorials would show single-file examples that "just worked," then suddenly real code had package declarations everywhere and import statements at the top.
The breakthrough for me was understanding that packages are LITERALLY just folders. The package com.example.users means "the folder com/example/users in the source tree." Once I saw the 1:1 correspondence between the dot-separated package name and the folder path, everything clicked.
If you're new to Java and struggling with packages, try this: open any Java project in a file explorer. See how the folder structure goes src/main/java/com/.... Look at the package declaration at the top of any class file. Notice they match exactly. That correspondence IS the package system. Everything else is convention layered on top.
Common Pitfalls
Warning: Forgetting the package declaration. Classes without one are in the default package — bad practice and incompatible with modular systems.
Package declaration not matching the folder structure. Compile error. The folder path and package name must match exactly.
Treating packages as decoration. They're functional — they control access (package-private), influence imports, and determine class identity.
Putting unrelated classes in the same package. Defeats the organizational purpose. Group related classes together.
Creating packages with single classes. Sometimes you need that, but usually a package with one class is over-organized. Combine related concepts.
Why This Matters in Interviews
Interview: Package questions are foundational. "What's a Java package?" "Why do we use packages?" "What's the fully qualified name of a class?" These are universal interview opener questions.
Topic 11.9 covers the Interview Lens.
Try It
Exercise: Explore each:
- Open any Java project you have. Note the folder structure under
src/main/java/. Verify the package declaration in each class matches the folder path.- Create a simple class without a package declaration. Note that it's in the default package.
- Add a
package com.example;declaration. Move the file tocom/example/ClassName.java. Verify it still compiles.- Try mismatching the package declaration with the folder path. Observe the compile error.
- Create two packages with classes of the same name. Note that they don't conflict.
Each exercise reinforces the package-as-folder mental model.