Table of Contents

Packages in Java

  • Last Updated: April 3, 2026
  • By: javahandson
  • Series
img

Packages in Java

Packages in Java group related classes into folders, keep class names unique, and decide what the rest of your code may touch. This guide walks through built-in and user-defined packages, the import keyword, static imports, sub-packages, access modifiers, naming rules, common mistakes, a hands-on walkthrough, and the interview questions people actually ask.

1. Introduction

Write your first Java program and you drop one file on the desktop. Write your fiftieth and that desktop turns into a swamp. Java solves this with packages.

A package is a named group of related classes and interfaces. Think of a phone that sorts photos into albums. The photos do not change, but finding one takes seconds instead of minutes.

Packages do more than tidy up, though. They also give every class a globally unique name and they act as a fence that access modifiers can lean on. We will unpack all of that below.

1.1 What This Article Covers

  • Why a growing project needs packages at all
  • Built-in packages, user-defined packages, and the default package
  • Creating, compiling, and running a class that lives in a package
  • How a package name maps onto real folders on disk
  • The import keyword, wildcard imports, and static import
  • Sub-packages and why they are not really “sub” anything
  • Access modifiers, and how each one behaves across a package boundary
  • Naming conventions, pitfalls, best practices, and a small end-to-end example
  • Ten interview questions with short, direct answers

1.2 Packages Are Folders With Rules

Here is the shortest possible example. One line at the top of a file puts that class in a package.

package com.javahandson.user;

public class UserService {
}

The class is no longer just UserService. Its real name is now com.javahandson.user.UserService. That long form is the fully qualified name, and no other class on the planet can claim it.

Since packages hold classes, it helps to be comfortable with Classes and Objects in Java before you go further.

2. Why Do We Need Packages in Java?

Ten classes in one folder feels fine. Three hundred classes in one folder feels like a punishment. But size is only half the story.

2.1 They Stop Name Clashes

Two libraries can each ship a class called Date. Without packages, the compiler would have no way to tell them apart.

java.util.Date
java.sql.Date

Same simple name, different packages, zero confusion. The JDK itself relies on this trick constantly.

2.2 They Group Code by Feature

A new teammate opens your project. Which folder do they click first? With good packages, the answer is obvious.

  • Everything about sign-up and profiles sits in user
  • Card charges and refunds live in payment
  • Monthly summaries belong to report

2.3 They Make Reuse Easy

A tidy package travels well. Zip up com.javahandson.util, drop it into another project, and it still works because nothing outside it had to change.

Scattered classes never travel that cleanly. You end up copying five files and forgetting the sixth.

2.4 They Control Who Sees What

This one surprises beginners. Access modifiers in Java do not just say “open” or “closed” – several of them answer the question “same package or not?”

A helper class with no modifier stays invisible outside its package. That lets you expose a small, clean surface and hide the messy plumbing behind it. Section 10 covers the exact rules.

3. Types of Packages in Java

Java splits packages into two families, plus one special case nobody should rely on.

3.1 Built-in Packages

The JDK ships thousands of ready-made classes, and every one of them sits in a package. You have already used several without thinking about it.

import java.util.ArrayList;
import java.util.Scanner;

A few you will meet early:

  • java.lang holds the core types such as String, Math, and System
  • Collections such as ArrayList and HashMap live in java.util
  • File and stream classes sit in java.io
  • Networking code goes through java.net

One handy quirk: the compiler imports java.lang for you automatically. That is why String works with no import line at the top.

3.2 User-Defined Packages

The second family is the one you create. Add a package line and the class belongs there.

package com.javahandson.payment;

public class PaymentService {
}

Real projects lean on these heavily. Most teams end up with a dozen or more, each holding one slice of the application.

3.3 The Default Package

Skip the package line entirely and your class lands in the unnamed package, usually called the default package.

It works for a quick throwaway file. It falls apart everywhere else, because a class in a named package cannot import a class from the unnamed one. There is no name to import.

Treat the default package as scratch paper. Anything you plan to keep deserves a proper name.

4. How to Create a Package in Java

Creating a package takes one keyword and one compiler flag. Let us walk the whole loop, from source file to running program.

4.1 The package Statement

The package keyword must come first in the file. Comments may sit above it, but no code may.

package com.javahandson.util;

public class Calculator {

    public int add(int a, int b) {
        return a + b;
    }
}

One file may declare only one package. That single line applies to every type in the file.

The statement itself is part of the standard Structure of a Java Program, right above the imports.

4.2 Compiling With the -d Option

Now compile it. The -d flag tells javac where to drop the output, and the compiler builds the folder tree for you.

javac -d . Calculator.java

Two things happen when you run that command:

  • The compiler creates one folder per dot in the package name
  • Your .class file lands at the bottom of that tree

So you end up with this:

com/
 javahandson/
   util/
     Calculator.class

4.3 Running the Class

Here is where most beginners trip. You cannot run java Calculator any more. The class needs its full name.

java com.javahandson.util.Calculator

Run that from the folder that contains com, not from inside util. The JVM walks down the tree itself.

4.4 Rules Worth Memorising

  • Put package on the first line of code, above every import
  • Only one package declaration may appear per file
  • Folder names must match the package name, piece for piece
  • Always pass -d so the compiler does the folder work
  • Run the program with the fully qualified class name

One extra tip: a file named package-info.java can hold documentation and annotations for the package itself. Frameworks read it, and it costs you nothing.

5. Directory Structure of Packages in Java

A package is not only an idea in the compiler’s head. It has a physical shape on disk, and that shape matters.

5.1 Every Dot Becomes a Folder

Read the package name left to right and you have read the path.

package com.javahandson.util;   ->   com/javahandson/util/
  • com becomes the top folder
  • Inside it sits javahandson
  • And util nests one level deeper

5.2 Source Folders vs Output Folders

Two trees exist side by side in most projects. One holds .java files, the other holds compiled .class files.

src/com/javahandson/util/Calculator.java
out/com/javahandson/util/Calculator.class

Notice the package part repeats in both. Only the root differs, which is exactly what -d out controls.

5.3 Where the Classpath Fits In

How does the JVM find com/javahandson/util/Calculator.class? It searches the classpath, which is simply a list of root folders and jar files.

java -cp out com.javahandson.util.Calculator

Read that as: start at out, then follow the package name down. If the folders do not match the package, the JVM gives up with ClassNotFoundException or NoClassDefFoundError.

6. Accessing Classes From Another Package

Your Main class sits in one package. The Calculator it needs sits in another. Java offers exactly two ways to bridge that gap.

6.1 Using the Fully Qualified Name

Spell out the whole path every time you mention the class.

public class Main {
    public static void main(String[] args) {
        com.javahandson.util.Calculator calc = new com.javahandson.util.Calculator();
        System.out.println(calc.add(2, 3)); // Output: 5
    }
}

It compiles, and it needs no import line. It also reads terribly once you use the class more than once.

6.2 Using the import Statement

Declare the long name once at the top, then use the short name below.

import com.javahandson.util.Calculator;

public class Main {
    public static void main(String[] args) {
        Calculator calc = new Calculator();
        System.out.println(calc.add(2, 3)); // Output: 5
    }
}

Worth knowing: import costs nothing at runtime. It does not load a class or copy any code. The compiler simply learns which long name your short name refers to.

6.3 So Which One Do You Pick?

Use import almost always. Reach for the fully qualified name only when two classes share a simple name and you need to point at one of them without ambiguity.

7. The import Keyword in Detail

The keyword looks simple, and mostly it is. A few details still catch people out in interviews.

7.1 Importing One Class

import java.util.ArrayList;

This form names exactly one class. Most teams prefer it because a reader can see every dependency at a glance.

7.2 Importing a Whole Package

import java.util.*;

The star covers every class sitting directly in java.util. It does not walk any deeper, so java.util.concurrent stays out of reach.

Does the wildcard slow your program down? No. The compiler resolves it at build time and the bytecode looks identical either way.

7.3 Imports Java Gives You Free

Every source file behaves as though it starts with an invisible import java.lang.*;. That covers String, System, Math, Integer, Thread, and the rest of the core set.

Classes in your own package come free too. Two classes in com.javahandson.user can see each other with no import line at all.

7.4 When Two Imports Clash

Try to import two classes with the same simple name and the compiler stops you right there.

import java.util.Date;
import java.sql.Date;   // compile error: Date is already defined

Two wildcards behave differently. They compile happily until you actually write Date, and then the compiler calls the reference ambiguous.

The fix is the same in both cases. Import the one you use often and spell out the other in full.

import java.util.Date;

public class Report {
    Date created = new Date();
    java.sql.Date dueDate;
}

8. Static Import in Java

Normal imports bring in a class. A static import brings in the static members of a class, so you can drop the class name from the call.

System.out.println(Math.sqrt(16)); // Output: 4.0

With a static import, the same call gets shorter.

import static java.lang.Math.*;

public class Test {
    public static void main(String[] args) {
        System.out.println(sqrt(16)); // Output: 4.0
        System.out.println(max(3, 9)); // Output: 9
    }
}

8.1 Importing One Static Member

import static java.lang.Math.sqrt;

Now only sqrt() loses its prefix. Everything else on Math still needs the class name, which keeps the code honest.

The same syntax works for constants, and that is where it shines. Test code full of assertEquals and assertTrue reads far better without a class prefix on every line. If static members feel fuzzy, the static keyword in Java guide is worth a detour.

8.2 When Static Import Helps

  • Maths-heavy code that calls sqrt, abs, or pow repeatedly
  • Unit tests built on assertion helpers
  • Files that reach for the same handful of constants over and over

Use it sparingly, though. A bare sqrt(16) with no visible owner leaves the next reader hunting for where that method came from.

9. Sub-packages in Java

Names like java.util.concurrent look nested, and on disk they truly are. The language, however, sees something flatter.

9.1 A Sub-package Is a Separate Package

Java has no concept of a parent-child relationship between packages. java.util and java.util.concurrent are two unrelated packages that happen to share a prefix.

That has one very practical consequence.

import java.util.*;              // ArrayList, HashMap, Scanner...
import java.util.concurrent.*;   // still needed for ExecutorService

The first line does nothing for the second package. You must import each one you actually use.

9.2 Why Java Works This Way

Imagine the alternative. A single import com.acme.*; would drag in hundreds of classes from dozens of nested folders, and name clashes would explode.

  • Each import stays narrow and predictable
  • Nothing sneaks into your file without a line naming it
  • Package-private members stay private to their own folder, not to a whole branch

That last point matters more than it sounds. A class in com.javahandson.billing.model gets no special privileges inside com.javahandson.billing, even though the name suggests family ties.

10. Access Modifiers and Packages in Java

Now for the part interviewers love. Java has four access levels, and two of them change behaviour depending on the package.

10.1 public

A public member opens the door to everyone, inside the package or outside it.

package com.javahandson.user;

public class User {
    public String name = "Shweta";
}
package com.javahandson.app;

import com.javahandson.user.User;

public class Test {
    public static void main(String[] args) {
        User user = new User();
        System.out.println(user.name); // Output: Shweta
    }
}

Reach for it when the class or member is genuinely part of your public API. Everything else deserves something tighter.

10.2 protected

A protected member is open to the whole package, plus subclasses anywhere else.

package com.javahandson.user;

public class User {
    protected int age = 30;
}
package com.javahandson.app;

import com.javahandson.user.User;

public class ChildUser extends User {
    public void show() {
        System.out.println(age); // Output: 30
    }
}

There is a catch that trips up even senior developers. From a different package, a subclass may touch the inherited member only through itself or its own subtype. Create a plain User object there and the field turns invisible again.

public class ChildUser extends User {
    public void show() {
        System.out.println(this.age);   // fine
        User other = new User();
        // System.out.println(other.age); // compile error
    }
}

10.3 default (Package-Private)

Write no modifier at all and you get default access, better known as package-private. Only classes in the very same package may look.

package com.javahandson.user;

public class User {
    String city = "Hyderabad";   // package-private
}

A class in com.javahandson.user reads city without trouble. A class in com.javahandson.app cannot see it, and neither can a subclass sitting outside the package.

This level deserves more love than it gets. It is the natural home for helper classes that support a feature but should never leak out of it. The idea sits at the heart of encapsulation in Java.

10.4 private

A private member never leaves its own class. Packages do not soften this at all.

package com.javahandson.user;

public class User {
    private String password = "secret";

    public String getPassword() {
        return password;
    }
}
User user = new User();
// System.out.println(user.password);   // compile error
System.out.println(user.getPassword()); // Output: secret

10.5 Access Rules at a Glance

ModifierSame classSame packageSubclass, other packageAnywhere else
publicYesYesYesYes
protectedYesYesYes, through the subclass typeNo
defaultYesYesNoNo
privateYesNoNoNo

Read the table top to bottom and you see one idea: each row narrows the circle a little further.

11. Naming Conventions for Packages in Java

The compiler accepts almost any legal name. Your teammates will not. These four conventions cover the whole industry.

11.1 Stick to Lowercase

package com.javahandson.util;   // good
package com.JavaHandsOn.Util;   // avoid

Lowercase avoids a nasty trap. Windows treats Util and util as the same folder, while Linux does not, so mixed case breaks builds that cross machines.

Java keywords are also off limits. A package called com.acme.new or com.acme.int simply will not compile, which is one reason keywords and identifiers in Java are worth knowing cold.

11.2 Use a Reverse Domain Name

Own javahandson.com? Then flip it and start every package with com.javahandson.

package com.javahandson.project;

Domains are unique, so this convention hands you a namespace nobody else can accidentally collide with. Every serious library on Maven Central follows it.

11.3 Name the Package After Its Job

com.javahandson.user
com.javahandson.payment
com.javahandson.report

Someone hunting a refund bug knows exactly where to click. Vague names such as misc, common, or stuff turn into dumping grounds within a month.

11.4 Keep the Tree Shallow

com.company.project.module.submodule.feature.impl.util   // too deep

Nobody enjoys scrolling through seven folders to reach one file. Three or four levels below the domain prefix handles almost every project.

12. Packages vs Modules

Java 9 added modules, and beginners often wonder whether packages still matter. They do. A module is a layer above packages, not a replacement for them.

12.1 What a Module Adds

A module is a named bundle of packages with a small descriptor file that says which packages the outside world may use.

module com.javahandson.billing {
    exports com.javahandson.billing.api;
    requires java.sql;
}

Notice what that buys you. A public class inside a package that the module never exports stays unreachable, even though it carries the public keyword. Packages alone could never enforce that.

12.2 Packages vs Modules at a Glance

PointPackageModule
Arrived inJava 1.0Java 9
GroupsClasses and interfacesPackages
Declared inThe package line of each filemodule-info.java
Hides codeThrough default accessBy not exporting a package
Needed?AlwaysOnly for modular projects

Most day-to-day Java still runs on the classpath with no module descriptor in sight. Learn packages properly first, then read up on module import declarations in Java 25 when you meet them.

13. Common Mistakes and Pitfalls

Almost every package problem comes from one of six small misunderstandings. Recognise them once and you will never lose an afternoon to them again.

13.1 Compiling Without -d

javac Calculator.java      // class file lands right next to the source
javac -d . Calculator.java // folders appear, class file goes inside

Both commands compile. Only the second one puts the .class file where the JVM will look for it later.

13.2 Folders That Do Not Match the Package

Declare package com.javahandson.util; and the class file must end up under com/javahandson/util/. Rename a folder by hand and the program dies at startup, not at compile time, which makes the error feel mysterious.

13.3 Expecting the Wildcard to Go Deeper

We covered this in section 9, and it still bites people every week. import java.util.*; gives you nothing from java.util.concurrent. Add a second import line.

13.4 Putting package in the Wrong Place

import java.util.List;
package com.javahandson.util;   // compile error: wrong order

Order is fixed: package, then imports, then types. Comments may go anywhere.

13.5 Leaving Classes in the Default Package

A file with no package line cannot be imported by anything that does have one. Beginners hit this the moment they split their first project into two folders, and the compiler error rarely explains why.

13.6 Blaming the Package for an Access Error

Sometimes the import line is perfect and the code still refuses to compile. Check the modifier before you check the folders.

  • Package-private members never cross a package boundary
  • A protected member needs inheritance once you leave the package
  • Only one top-level class per file may carry public, and its name must match the file

14. Real-World Usage of Packages in Java

Textbook examples stop at com.example.demo. Production codebases go further, and they tend to settle into a few familiar shapes.

14.1 Grouping by Feature

com.javahandson.user
com.javahandson.payment
com.javahandson.order
com.javahandson.report

One package equals one business capability. Delete the feature, delete the folder, and almost nothing else moves.

14.2 Grouping by Layer

com.javahandson.controller
com.javahandson.service
com.javahandson.repository
com.javahandson.model
  • Requests arrive at controller
  • Business rules live in service
  • Database work happens in repository
  • Plain data classes fill model

Spring projects use this shape constantly. Larger systems often combine both styles: feature first, then layers inside each feature.

14.3 Splitting Work Across Teams

Clear package boundaries reduce merge conflicts. One team owns payment, another owns user, and the two rarely touch the same file on the same day.

Package-private classes help here too. A team can refactor its own internals freely, because nothing outside the folder could depend on them in the first place.

14.4 Build Tools Expect the Same Shape

src/main/java/com/javahandson/...
src/test/java/com/javahandson/...

Maven and Gradle both assume that layout. Follow it and the tools need almost no configuration; fight it and every build file grows a paragraph of overrides.

15. Best Practices for Packages in Java

Everything above boils down to a short checklist you can apply to your next project.

  • Give every package one clear job, and name it after that job
  • Start from a reverse domain prefix and stay lowercase throughout
  • Three or four levels of nesting beat seven, every time
  • Avoid the opposite extreme too, since a package holding one class adds noise
  • Import the exact classes you need rather than reaching for the wildcard
  • Default access should be your reflex; promote to public only on purpose
  • Let the package tree mirror the architecture, so the folders explain the design
  • Never ship real code from the default package

One more rule of thumb: if you cannot describe a package in a single short sentence, it probably holds two packages that have not been separated yet.

16. A Practical Walkthrough

Theory sticks better once you build something. Let us wire up a tiny billing program that spans three packages.

16.1 The Plan

src/com/javahandson/billing/model/Invoice.java
src/com/javahandson/billing/service/InvoiceService.java
src/com/javahandson/billing/app/Main.java

Three packages, three responsibilities. Data lives in model, the calculation lives in service, and app starts everything.

16.2 The Model Class

package com.javahandson.billing.model;

public class Invoice {

    private final String customer;
    private final double amount;

    public Invoice(String customer, double amount) {
        this.customer = customer;
        this.amount = amount;
    }

    public String getCustomer() {
        return customer;
    }

    public double getAmount() {
        return amount;
    }
}

Both fields carry private, so nothing outside this class can reach them directly. The getters are public because two other packages need them.

16.3 The Service Class

package com.javahandson.billing.service;

import com.javahandson.billing.model.Invoice;

public class InvoiceService {

    private static final double TAX_RATE = 0.18;

    public double totalWithTax(Invoice invoice) {
        return invoice.getAmount() * (1 + TAX_RATE);
    }
}

Look at that import line. Invoice sits in a different package, so the service must name it explicitly before using the short form.

16.4 The Main Class

package com.javahandson.billing.app;

import com.javahandson.billing.model.Invoice;
import com.javahandson.billing.service.InvoiceService;

public class Main {
    public static void main(String[] args) {
        Invoice invoice = new Invoice("Shweta", 1000.0);
        InvoiceService service = new InvoiceService();

        System.out.println(invoice.getCustomer());        // Output: Shweta
        System.out.println(service.totalWithTax(invoice)); // Output: 1180.0
    }
}

Two imports, because this file touches two foreign packages. Its own package needs no import at all.

16.5 Compiling and Running

javac -d out src/com/javahandson/billing/model/Invoice.java src/com/javahandson/billing/service/InvoiceService.java src/com/javahandson/billing/app/Main.java

java -cp out com.javahandson.billing.app.Main

The first command fills out with a matching folder tree. The second points the JVM at that root and asks for one fully qualified class.

Shweta
1180.0

16.6 What the Walkthrough Proves

  • Package names and folder names stayed identical the whole way through
  • Crossing a package boundary always cost one import line
  • The private fields survived untouched, while the getters carried the data across
  • Running the program required the fully qualified name plus a classpath root

Try one experiment. Drop public from getAmount() and recompile. The build fails immediately, because package-private cannot reach across from model into service.

17. Interview Questions

Q: What is a package in Java?

A: A package is a named group of related classes and interfaces. It gives every class a unique full name, maps onto a folder on disk, and forms the boundary that default and protected access rely on.

Q: Why do we use packages in Java?

A: Packages prevent class-name clashes, group code by feature so a project stays navigable, make a slice of code easy to reuse elsewhere, and let you hide helper classes behind package-private access.

Q: What is the difference between import and a fully qualified name?

A: A fully qualified name spells out the package path at every single use. An import declares that path once at the top of the file so you can write the short class name below. Neither costs anything at runtime.

Q: Does import java.util.* also import sub-packages?

A: No. The wildcard covers only the classes sitting directly in java.util. To use ExecutorService you still need a separate import for java.util.concurrent, because Java treats every package as independent.

Q: What is the default package in Java?

A: Any class without a package statement belongs to the unnamed, or default, package. It works for quick experiments, but a class in a named package can never import from it, so real code should always declare a package.

Q: Can packages be nested in Java?

A: Folders nest, packages do not. Names such as com.example.util and com.example.util.text look like parent and child, yet the compiler treats them as two unrelated packages with no shared access rights.

Q: What is static import in Java?

A: A static import pulls in the static members of a class, letting you write sqrt(16) instead of Math.sqrt(16). It suits maths code and unit tests, but heavy use hides where each method actually comes from.

Q: Can two classes have the same name in Java?

A: Yes, as long as they sit in different packages. java.util.Date and java.sql.Date prove the point. If one file needs both, import one and write the other with its full package path.

Q: How does protected access behave across packages?

A: Inside the package, protected behaves like default access. Outside it, only a subclass may touch the member, and only through a reference of its own type. A plain superclass object gives no access at all.

Q: What is the difference between a package and a module?

A: Packages group classes and have existed since Java 1.0. Modules arrived in Java 9 and group packages instead, declaring in module-info.java which ones the outside world may use. Any public class inside a package that the module never exports stays hidden.

18. Conclusion

Let us wrap up what we covered. A package groups related classes, gives each one a globally unique name, and turns into a real folder tree the moment you compile.

Two families exist: the built-in packages that ship with the JDK, and the ones you write yourself. Skipping the package line drops a class into the unnamed package, which named code can never import.

Crossing a package boundary takes either a fully qualified name or an import. Wildcards stay shallow, static imports shorten calls to static members, and sub-packages get no special treatment from the compiler.

Access modifiers give packages their real power. Default access keeps a class inside its folder, protected extends the reach to subclasses, and public throws the door wide open.

Keep names lowercase, start from a reverse domain, name each package after its job, and stay shallow. Do that and your folder tree will explain the design before anyone opens a single file.

19. Further Reading

Leave a Comment