Object-Oriented Programming Basics

Packages & Imports

15 min Lesson 8 of 14

Packages & Imports

As your Java program grows beyond a handful of classes, you need a way to organize them — just as you organize files into folders on your computer. Java solves this with packages. A package is a named group of related classes and interfaces. Packages serve two purposes: they keep code organized, and they prevent name clashes between classes that happen to share the same name.

The package statement

Every Java source file may begin with a package statement that declares which package the class belongs to. The statement must be the very first non-comment line in the file.

package com.mybank.accounts; public class BankAccount { // ... }

The package name uses reverse domain notation by convention. If your domain is mybank.com, your top-level package is com.mybank. Sub-packages separate concerns further: com.mybank.accounts, com.mybank.loans, com.mybank.util.

Package names map to folders. A class declared as package com.mybank.accounts must live in the directory com/mybank/accounts/ relative to your source root. Modern build tools (Maven, Gradle) and IDEs enforce this automatically.

The default package

If you omit the package statement, the class belongs to the default package — an unnamed package. This is fine for tiny one-file experiments, but you should never use it in real projects because classes in the default package cannot be imported by classes in named packages.

Importing classes with import

When you want to use a class from a different package, you have two options. The first is a single-type import: you name the exact class you need.

package com.mybank.reports; import com.mybank.accounts.BankAccount; // single-type import public class MonthlyReport { public void generate(BankAccount account) { System.out.println("Report for: " + account.getOwner()); } }

The second option is a wildcard import that brings in every public type from a package:

import com.mybank.accounts.*; // imports all public classes from that package
Prefer single-type imports. Wildcard imports do not hurt performance (the compiler resolves them at compile time and does not include extra bytecode), but they make it harder to tell at a glance where a class comes from. Most code formatters and IDEs expand wildcards automatically.

Fully-qualified class names

You can always refer to a class by its fully-qualified name — the complete package.ClassName path — without any import statement at all:

public class App { public static void main(String[] args) { com.mybank.accounts.BankAccount acc = new com.mybank.accounts.BankAccount("Alice", 0.0); System.out.println(acc.getOwner()); } }

Fully-qualified names are verbose, so they are mainly used when two packages contain classes with the same name and you need both in the same file:

import com.mybank.util.Date; // your custom Date class public class Transaction { private com.mybank.util.Date txDate; // your class private java.util.Date legacyDate; // JDK class — same simple name, different package }

The java.lang package is always imported

You have been using String, System, Math, and Integer all along without importing them. That is because every Java file automatically imports all classes from java.lang. You never need to write import java.lang.String.

Static imports

A static import lets you use static members (fields and methods) of another class without qualifying them with the class name. The most common use-case is Math constants and methods:

import static java.lang.Math.PI; import static java.lang.Math.sqrt; public class Circle { public double area(double radius) { return PI * radius * radius; // no Math. prefix needed } public double hypotenuse(double a, double b) { return sqrt(a * a + b * b); } }
Do not overuse static imports. If you statically import many members from multiple classes, code becomes hard to read — a reader cannot tell which class sqrt or PI came from. Reserve static imports for well-known constants and utility methods (like Math or JUnit assertion methods).

A realistic package structure

Here is how a small banking application might be organised into packages:

src/ com/mybank/ accounts/ BankAccount.java // package com.mybank.accounts SavingsAccount.java loans/ Loan.java // package com.mybank.loans util/ CurrencyFormatter.java // package com.mybank.util App.java // package com.mybank

Each package has a clear responsibility. accounts holds account-related classes, loans holds loan-related classes, and util holds shared helpers. This mirrors how you already learnt to separate fields, methods, and constructors inside a class — now you are doing the same at a higher level.

Access modifiers and packages revisited

You learnt about public, private, and protected in lesson 4. There is a fourth level you may not have noticed: package-private (no keyword). A class or member with no access modifier is visible only within its own package. This is useful for implementation helpers you want to share within a package but hide from the rest of the application.

package com.mybank.accounts; class AccountValidator { // no modifier = package-private static boolean isValidIban(String iban) { return iban != null && iban.length() == 22; } }

AccountValidator is visible to every class inside com.mybank.accounts but invisible to classes in other packages — a clean way to hide internal details.

Summary

Packages are Java's mechanism for organising classes and controlling visibility. Declare a package with the package statement at the top of every source file, use import to bring in classes from other packages, and rely on fully-qualified names when two packages share a class name. The java.lang package is always available without an import, and static imports let you use static members unqualified — use them sparingly. Together, these tools let you build large applications that stay readable and maintainable.