By the end of this module, you will be able to:
package statementimportAs your program grows, you'll end up with many classes. Without any organisation, they all pile up in the same place — making the codebase hard to navigate and increasing the chance of name conflicts.
A package is a way to group related classes together under a common namespace. Think of it like a folder system for your classes.
🔍 Coming from JavaScript/Node.js, you're used to organising code into separate files and folders, then connecting them with
import/require. Java packages serve the same purpose — the main difference is that Java formalises this structure as a first-class language feature, not just a file system convention.
A real-world Java project might be organised like this:
com.bookstore
├── model → Book, Author, Customer
├── service → BookService, OrderService
├── repository → BookRepository
└── util → PriceFormatter, DateHelper
Each of these groups is a package. Classes inside model handle data structures; classes inside service contain business logic, and so on. This separation makes large codebases navigable and maintainable.
The package statement goes on the very first line of a Java file, before anything else — before imports, before the class declaration.
package com.bookstore.model;
public class Book {
private String title;
private String author;
public Book(String title, String author) {
this.title = title;
this.author = author;
}
public String getTitle() {
return title;
}
}
Rules for the package statement:
package statement per file⚠️ A class with no
packagestatement is in the default package. Avoid this in any project with more than a handful of classes — classes in the default package cannot be imported by classes in named packages.
When you need to use a class from a different package, you bring it into scope with an import statement. Imports go after the package statement and before the class declaration.
package com.bookstore.service;
import com.bookstore.model.Book; // import a specific class
public class BookService {
public void printBook(Book book) {
System.out.println(book.getTitle());
}
}
You can import them one by one, or use the wildcard * to import all public classes from a package at once:
// One by one — explicit, recommended
import com.bookstore.model.Book;
import com.bookstore.model.Author;
// Wildcard — imports everything in the package
import com.bookstore.model.*;
💡 Prefer explicit imports over wildcards. They make it immediately clear which classes a file depends on, and IDEs like IntelliJ will manage them for you automatically.
The java.lang package — which contains everyday classes like String, Math, System, and Integer — is automatically imported by Java. That's why you've been using System.out.println() and String all along without any import statement.
// These are always available — no import needed
String name = "Alice";
System.out.println(Math.abs(-5));
Everything else — Scanner, Arrays, ArrayList, and so on — requires an explicit import:
import java.util.Scanner;
import java.util.Arrays;
Java uses a reverse domain name convention to guarantee globally unique package names. This matters when your code is shared as a library or used alongside third-party libraries.
| Organisation | Domain | Package prefix |
|---|---|---|
| HackYourFuture | hackyourfuture.net | net.hackyourfuture |
| google.com | com.google |
|
| Apache | apache.org | org.apache |
| Your project | — | com.yourcompany.projectname |
The full package name then adds sub-packages for the specific area of the codebase:
com.hackyourfuture.backend.model
com.hackyourfuture.backend.service
com.hackyourfuture.backend.util
Additional naming rules:
. separate levels, nothing elsecom.bookstore.model is a sub-package of com.bookstore// ✅ Correct
package com.hackyourfuture.backend.model;
// ❌ Incorrect — uppercase, hyphen, wrong separator
package com.HackYourFuture.Back-End.Model;
💡 For learning projects where there's no real domain, it's common to use
com.exampleor just your name:nl.yourname.projectname.
This is where packages become concrete — the package name must exactly match the directory path of the file on disk. Java enforces this mapping; mismatches cause compile errors.
Given this package declaration:
package com.bookstore.model;
The file must live at this path:
src/
└── com/
└── bookstore/
└── model/
└── Book.java ← lives here
A typical project layout with multiple packages looks like this:
src/
└── com/
└── bookstore/
├── model/
│ ├── Book.java → package com.bookstore.model
│ └── Author.java → package com.bookstore.model
├── service/
│ └── BookService.java → package com.bookstore.service
└── util/
└── PriceFormatter.java → package com.bookstore.util
🔍 In Node.js, the file path and the module path are the same thing — you
require('./model/book')and the file is literally at that path. Java works the same way, except the package name in the source code must match the directory structure. Your IDE (IntelliJ) enforces and manages this for you automatically when you create new classes.
When you right-click a package folder in IntelliJ and choose New → Java Class, it:
package statement at the topYou rarely need to manage this manually — but understanding the underlying mapping is essential for reading error messages, navigating unfamiliar codebases, and working with build tools.
The following project has three errors related to packages and imports. Identify each one.
File location: src/Model/Book.java
import java.util.Scanner;
package com.bookstore.model;
public class Book {
private String title;
}
File location: src/com/bookstore/service/BookService.java
package com.bookstore.service;
public class BookService {
public void display(Book book) { // using Book from another package
System.out.println(book);
}
}
Set up the following package structure in IntelliJ for a library management system: