A package in Java is a namespace used to organize related classes, interfaces, and other types. Packages help keep large applications structured, prevent naming conflicts, and support access control.
Think of a package like a well-organized folder system in a large software project. Instead of placing hundreds of classes in one giant location, related classes can be grouped into meaningful packages.
Why Do We Need Packages?
Imagine an enterprise application containing classes for customers, payments, reports, security, and database operations. Keeping every class in one package would quickly become difficult to navigate.
Packages allow developers to organize these responsibilities logically.
com.company.app.customer com.company.app.payment com.company.app.report com.company.app.security com.company.app.database
Now a developer can immediately understand where a class belongs and what responsibility it is likely to have.
Remember: A package is not merely a folder. It is a Java namespace that also participates in access control and type organization.
Creating a Package
A package is declared using the package statement.
package com.company.student;
public class Student {
public void display() {
System.out.println("Student details");
}
}
The package declaration tells Java that the Student class belongs to the com.company.student package.
Package Naming Convention
Java package names are conventionally written in lowercase letters. Organizations commonly use a reversed domain name as the beginning of a package name to reduce the possibility of naming conflicts.
com.example.payment org.example.library in.company.project
The convention produces names that are descriptive and usually unique across large codebases.
Package Statement Must Come First
When a source file contains a package declaration, it must appear before ordinary type declarations in that source file.
package com.example.app;
public class Main {
public static void main(String[] args) {
System.out.println("Hello");
}
}
The package declaration is followed by optional import declarations and then the class or other type declarations.
Using a Class from Another Package
Suppose one package contains a Student class.
package com.example.student;
public class Student {
public void display() {
System.out.println("Student");
}
}
A class in another package can use it by importing the type.
package com.example.app;
import com.example.student.Student;
public class Main {
public static void main(String[] args) {
Student student = new Student();
student.display();
}
}
The import statement tells the compiler which package contains the Student type.
Importing Multiple Classes
You can import multiple types from the same package.
import com.example.student.Student; import com.example.student.Teacher;
Each imported type can then be referenced by its simple class name instead of its fully qualified name.
Wildcard Import
Java also supports wildcard imports.
import com.example.student.*;
This allows types from that package to be referenced without writing individual import statements for each type.
A wildcard import does not recursively import types from subpackages. For example, importing com.example.student.* does not automatically import classes from com.example.student.admin.
Important: Packages and subpackages are separate namespaces. A wildcard import applies to the specified package only; it does not include its subpackages.
Fully Qualified Class Name
You do not have to use an import statement. You can also use a class's fully qualified name directly.
public class Main {
public static void main(String[] args) {
com.example.student.Student student =
new com.example.student.Student();
student.display();
}
}
This approach is useful when two packages contain classes with the same simple name and you need to make the exact type explicit.
Packages and Naming Conflicts
Packages help prevent class-name collisions by placing classes in different namespaces.
com.example.payment.Invoice com.example.sales.Invoice
Both classes can be named Invoice because their fully qualified names are different.
| Simple Name | Fully Qualified Name |
|---|---|
| Invoice | com.example.payment.Invoice |
| Invoice | com.example.sales.Invoice |
The package provides the additional context Java needs to distinguish between the two types.
Built-in Java Packages
Java provides a large standard library organized into packages.
| Package | Common Purpose |
|---|---|
| java.lang | Core language classes such as String, System, and Math |
| java.util | Collections, utilities, scanners, and related classes |
| java.io | Input and output functionality |
| java.time | Date and time API |
| java.net | Networking-related functionality |
These packages provide reusable functionality so developers do not have to build common infrastructure from scratch.
The Special java.lang Package
Classes from java.lang are automatically available to Java source code without an explicit import statement.
public class Main {
public static void main(String[] args) {
String message = "Hello Java";
System.out.println(message);
}
}
You do not normally need to write import java.lang.String; or import java.lang.System; because Java automatically makes the java.lang package available.
Subpackages
A package can have subpackages that form a related naming structure.
com.company com.company.customer com.company.customer.service
Although the names appear hierarchical, Java treats each package as a separate namespace. A class in com.company.customer does not automatically have access to package-private types in com.company merely because the names share a prefix.
Packages and Access Control
Packages also work closely with Java access modifiers. A member or class with package-private access can be accessed by code in the same package.
package com.example.account;
class AccountHelper {
void process() {
System.out.println("Processing");
}
}
Because AccountHelper has no explicit access modifier, it has package-private access. Code outside its package cannot directly use that class.
This package-level visibility becomes especially useful when a group of classes needs to cooperate internally while keeping implementation details hidden from the rest of the application.
Package-Private Members
The same idea applies to members without an explicit access modifier.
package com.example.account;
public class Account {
double balance;
void calculateInterest() {
System.out.println("Calculating interest");
}
}
The class itself is public, but balance and calculateInterest() have package-private access because no modifier was specified.
Access modifiers are explored in detail in a later chapter, where this package-level behavior becomes much easier to compare with private, protected, and public.
Organizing a Real Project with Packages
A professional application might separate responsibilities like this:
com.example.app com.example.app.controller com.example.app.service com.example.app.repository com.example.app.model com.example.app.exception com.example.app.util
For example, controller classes can handle incoming requests, service classes can contain business logic, repository classes can handle persistence operations, and model classes can represent application data.
The exact structure depends on the architecture and project requirements. Packages should organize code around meaningful responsibilities rather than simply creating a package for every few classes.
Package Declaration and Directory Structure
In a conventional Java project, the source directory structure mirrors the package name.
src/
└── com/
└── example/
└── student/
└── Student.java
If the source file declares:
package com.example.student;
the file is conventionally located in the corresponding package directory. Modern Java build tools such as Maven and Gradle rely heavily on this standard project structure.
Package vs Import
| Package | Import |
|---|---|
| Defines the namespace of a type | Makes a type easier to reference in source code |
| Declared using the package keyword | Declared using the import keyword |
| Usually appears at the beginning of the source file | Appears after the package declaration and before type declarations |
| Helps organize and distinguish types | Reduces the need to write fully qualified names |
Common Beginner Mistakes
- Thinking a package and a subpackage automatically share package-private access.
- Forgetting that the package declaration must match the intended package structure.
- Assuming wildcard imports include subpackages.
- Using package names with inconsistent capitalization instead of the conventional lowercase style.
- Creating excessive packages that make a small project harder to navigate.
- Confusing the package statement with the import statement.
Best Practices
- Use meaningful, lowercase package names.
- Use a consistent naming convention across the entire project.
- Organize classes around clear responsibilities and boundaries.
- Use packages as part of your access-control design when appropriate.
- Prefer explicit imports when they make dependencies clearer.
- Avoid creating package structures that are deeper or more complicated than the project needs.
Interview Insights
A common interview question is: “What is a package in Java?”
A strong answer is: A package is a namespace used to organize related Java types, avoid naming conflicts, and support package-level access control.
Another common question is: “Does importing a package also import its subpackages?” No. Java treats each package as a separate namespace, so importing one package does not automatically make its subpackages available.
Quick Learning Check
Before moving to access modifiers, make sure you can answer these questions:
- What is the purpose of a Java package?
- How do you declare a package?
- What is the difference between a package and an import?
- Why are package names commonly written in lowercase?
- Does importing a package include its subpackages?
- How do packages help with access control?
Final Takeaway
Packages give Java applications structure, naming boundaries, and an additional layer of access control. They make large codebases easier to navigate and allow classes with the same simple name to coexist in different namespaces. Once you understand packages as more than just folders—as organizational and access-control boundaries—the purpose of package declarations, imports, and package-private access becomes much clearer.
