Lesson 44Phase 5: Errors, files and the standard library
Packages and imports
Organizing classes into folders with names, importing what you need, and the classpath.
By BasithUpdated 50 min
- package
- import
- classpath
- fully qualified name
- java.util
- access across packages
Every program in this course so far has fit in one file. A real Java project has hundreds of classes, written by different people, and now and then two of them want the same name. Java keeps them apart by sorting classes into named groups called packages. You have used packages since your first ArrayList, through the import lines at the top of your programs. This lesson explains what those lines do, how to put your own classes into packages, and how Java finds a class while a program runs.
- Say what a
packageis, and how its dotted name matches folders on disk. - Write a
packagedeclaration and the kinds ofimportline you will meet: one class, a whole package, static members and a whole module. - Use a fully qualified name in place of an import, and predict which class a short name means when two classes share it.
- Compile and run a program spread over several packages, and say what the classpath and a JAR file are.
- Decide whether a class, field or method can be used from another package:
public,protectedor package access.
import java.util.ArrayList; since ArrayList, and import java.util.Scanner; for keyboard input since if, else and switch. In encapsulation you met public, private and package access, and inheritance added protected. Both lessons said the differences only show once a program is split into packages. This is where that happens.Start with the clutter#
Picture a banking application. It has a class for a customer, a class for an account, a class for a transaction, a class that prints reports, a class that talks to the database, and dozens more. Now imagine all of those sitting loose in a single folder, every .java file shoulder to shoulder with no grouping. Finding the one you want means scrolling through a wall of names. Worse, two different parts of the project might both want a class called Reader: one that reads bank records, one that reads a file. With everything in one pile, those two names collide and the language has no way to tell them apart.
Key ideaA package is a named group of related classes. It works like a folder, with one twist: the package name is written into the code itself, so Java always knows which group a class belongs to.
import java.util.ArrayList; plays the part of from collections import deque. Two differences: a Java import runs no code and only lets you write a shorter name, and the package name must match the folders the file sits in.Declaring a package#
To put a class into a package, you write one line at the very top of its file, above everything except comments. It names the package the class belongs to.
package com.bank.model; public class Customer { private String name; private String email;}That first line, package com.bank.model;, says “this class lives in the package com.bank.model”. Each dot in the name is one step deeper into a folder, so the package com.bank.model matches the folder path com/bank/model, and the file Customer.java sits inside that last folder. When Java needs a class, it turns the dots of its package into folders and looks there. A file in the wrong folder is a file Java cannot find, and the compiler reports package com.bank.model does not exist.
Here is how the bank's classes lay out on disk. The dotted names become nested folders under src, the folder that holds the source code.
src/ com/ bank/ app/ Main.java (package com.bank.app;) model/ Customer.java (package com.bank.model;) Account.java (package com.bank.model;) report/ Printer.java (package com.bank.report;)package declaration must come before any import line and any class. Only comments may sit above it. A file belongs to exactly one package, so it has at most one package line. A file with no package line puts its classes in the unnamed package, also called the default package. Every runnable program in this course lives there, which is fine for programs of one file. A real project gives every class a named package.package com.shop.cart; and declares public class Basket. Where does Basket.java belong?The full name of a class#
Once a class lives in a package, its complete name includes the package. The class above is Customer for short, and its full name is com.bank.model.Customer: the package, a dot, then the class. That long form is called the fully qualified name. It names one class and no other, anywhere in the world, because the package pins down which Customer you mean.
Nobody wants to type com.bank.model.Customer every time, so Java lets you use the short name, Customer, as long as the compiler can tell which class you mean. Inside the same package it can tell on its own. From another package you have to say, and that is what import is for.
Customer.com.bank.model.Customer.Scanner in;java.util.Scanner.An import line is a convenience, never a requirement. You can always skip it and write the full name in place. This program uses three classes from java.util and imports none of them. It reads two names from the keyboard input box and prints them in alphabetical order:
public class Main { public static void main(String[] args) { java.util.Scanner in = new java.util.Scanner(System.in); java.util.ArrayList<String> names = new java.util.ArrayList<>(); names.add(in.nextLine()); names.add(in.nextLine()); java.util.Collections.sort(names); System.out.println(names); }}It prints [Ada, Grace]. It also shows why hardly anyone writes Java like this: every class name drags its package along with it.
Bringing a class into scope with import#
An importline tells the compiler: “when I write this short name, I mean the class from that package”. Here is the same program with three imports at the top.
import java.util.ArrayList;import java.util.Collections;import java.util.Scanner; public class Main { public static void main(String[] args) { Scanner in = new Scanner(System.in); ArrayList<String> names = new ArrayList<>(); names.add(in.nextLine()); names.add(in.nextLine()); Collections.sort(names); System.out.println(names); }}The output is the same. Read the top of the file as the list of classes it borrows. After import java.util.Scanner;, writing Scanner is enough, because you said which one you meant. An import copies no code into your file and loads nothing while the program runs. It only tells the compiler how to read a short name. The compiled .class file refers to every class by its full name, so the import lines are gone once compiling is done.
import java.util.Scanner; and javac stops with cannot find symbol. Write every import as if javac were checking, because on your own computer it is.Importing a whole package#
You write one import per class. When a file uses many classes from one package, a star imports them all: import java.util.*; makes every public class in java.util available by its short name. This is called an on-demand import, because the compiler only looks in java.util when it meets a short name it does not otherwise know.
import java.util.ArrayList; // one classimport java.util.Scanner; // and another import java.util.*; // or every public class in java.utilA star brings in classes, never other packages. Package names look nested, as in java.util and java.util.function, but Java treats them as two unrelated packages that happen to share the start of their names. So import java.util.*; does not cover Function, which lives in java.util.function. Here is javac's answer to a file that imports java.util.* and then uses Function:
$ javac Main.javaMain.java:6: error: cannot find symbol Function<String, Integer> size = String::length; ^ symbol: class Function location: class Main1 errorThe fix is one more line: import java.util.function.Function;.
import java.util.*; and nothing else. Which line still needs an import of its own?java.lang: the package you never import#
You have used String, System and Math since your very first program, and you never wrote an import for them. They live in a package called java.lang, and Java imports it into every file for you, as if each one began with import java.lang.*;. It holds the classes almost every program needs, so the language spares you the line.
public class Main { public static void main(String[] args) { String s = "hello"; int max = Math.max(2, 5); System.out.println(s.toUpperCase() + " " + max); }}It prints HELLO 5. Every class in it, String, Math and System, comes from java.lang. Anything outside java.lang must be imported or written out in full. These are the packages you have met so far, and some you will meet soon:
| Package | Some of its classes | Where you meet it |
|---|---|---|
java.lang | String, Math, System, Integer, Object | Every program. Imported for you. |
java.util | ArrayList, HashMap, List, Scanner, Collections | ArrayList and every collection since |
java.util.function | Function, Predicate, Supplier | Enums and lambdas |
org.junit.jupiter.api | Test, Assertions | Testing with JUnit. A library, not part of Java itself. |
java.io | BufferedReader, FileWriter, IOException | Reading files, later in this phase |
java.time | LocalDate, Duration | Dates and times, later in this phase |
Static imports#
A normal import brings in a class. A static import brings in the static members of a class, its static methods and constants, so you can use them with no class name in front. You met one in testing with JUnit: import static org.junit.jupiter.api.Assertions.assertEquals; is why a test can say assertEquals(...) where it would otherwise need Assertions.assertEquals(...). Math works the same way.
import static java.lang.Math.PI;import static java.lang.Math.max;import static java.lang.Math.sqrt; public class Main { public static void main(String[] args) { double radius = 3.0; System.out.printf("Area: %.2f%n", PI * radius * radius); int best = max(72, 91); System.out.println("Best score: " + best); System.out.println("Square root of 49: " + sqrt(49)); }}It prints Area: 28.27, Best score: 91 and Square root of 49: 7.0. Each line at the top names one member: PI is a constant, and max and sqrt are methods. import static java.lang.Math.*; would bring in every static member of Math at once. Math itself needs no import, since it is in java.lang. The static imports only save you writing Math. in front of each member.
max(72, 91) has to scroll to the top of the file to learn where max comes from. Static imports pay off for a few members a file uses again and again, such as assertEquals in a test class, or the collectors in stream code: in Collectors and stream recipes you will see lines like import static java.util.stream.Collectors.groupingBy;.tableArea so it returns the area of a round table top, PI times the radius times the radius, and diagonal so it returns the diagonal of a rug: the square root of the width squared plus the length squared. PI is already imported; add a static import for sqrt so you can call it without Math. in front.When two classes share a short name#
Packages exist so that two classes can share a short name, and now and then two of them meet in one file. Java decides which one a short name means with a few rules:
- A class declared in your own file, or elsewhere in your own package, beats a class that a star import offers. Java does not warn you.
- An import that names one class beats a star import.
- If two star imports both offer the name,
javacrefuses to guess, and using the short name is an error. - A fully qualified name always means exactly one class, whatever the imports say.
import java.util.*; public class Main { public static void main(String[] args) { Random die = new Random(); List<Integer> rolls = new ArrayList<>(); for (int i = 0; i < 3; i++) { rolls.add(die.nextInt(6) + 1); } System.out.println(rolls); }} class Random { int nextInt(int bound) { return 3; }}This program imports java.util.*, which includes Java's own Random class, and it also declares a class called Random. What does it print?
It prints [4, 4, 4] on every run. The class Random at the bottom of the file beats the Random that the star import offers, so dieis the program's own loaded die, and each roll is 3 plus 1. Java gives no warning. With import java.util.Random; at the top instead, javac would refuse the file with Random is already defined in this compilation unit, which at least tells you about the clash.
The third rule shows up with two star imports. java.awt, Java's older toolkit for windows and buttons, has a class called List too: a list box drawn on the screen. Import both packages with stars, and javac cannot tell which List you mean.
import java.awt.*;import java.util.*; public class Main { public static void main(String[] args) { List<String> names = new ArrayList<>(); names.add("Ada"); System.out.println(names); }}$ javac Main.javaMain.java:6: error: reference to List is ambiguous List<String> names = new ArrayList<>(); ^ both interface java.util.List in java.util and class java.awt.List in java.awt match1 errorEither of two fixes works. Add import java.util.List; below the star imports, and the single-class import wins the short name. Or write the full name, java.util.List<String> names, and leave the short name out of it. With either change the program compiles and prints [Ada].
One import for all of java.base#
Java 25 adds a wider import still. A moduleis a named group of packages that ship together. Modules arrived in Java 9, and the core of Java's library is a module called java.base, which holds java.lang, java.util, java.util.function, java.io, java.time, java.math and dozens more. The line import module java.base; imports every public class of every one of those packages.
import module java.base; public class Main { public static void main(String[] args) { List<String> payees = new ArrayList<>(List.of("Rent", "Gym", "Bakery")); Collections.sort(payees); Map<String, Integer> visits = new TreeMap<>(); visits.put("Bakery", 3); LocalDate payday = LocalDate.of(2025, 9, 26); Function<String, Integer> letters = String::length; System.out.println(payees + " " + visits); System.out.println(payday.getDayOfWeek() + " " + letters.apply("Bakery")); }}One line, and List, Map, LocalDate and Function all work, from four different packages. The wider an import, the more names it can clash on, and a single-class import settles a clash here just as it does for star imports. Most projects still list their classes one by one, so import module suits small programs and experiments best. It also explains something from your first Java program: a compact source file, the kind that starts with void main(), gets import module java.base; automatically, so it can use List or Scanner without a single import line.
The classpath: where Java goes looking#
There is a gap to close. When your program says new Account("Maya"), the running Java machine has to find the compiled Accountcode somewhere on the computer and load it. Java's own classes, such as Scanner, come with the JDK, and Java always knows where they are. For every other class, your own and those of any library you use, Java searches the classpath: the list of places where it looks for compiled classes.
Remember that the Java machine runs bytecode, not source code. javac compiles each .java file into a .class file, and the .class files are what run. When the program needs the class com.bank.model.Account, Java goes through each place on the classpath and looks for com/bank/model/Account.class inside it. It is the same mirror-the-folders rule, used again while the program runs.
Here is the whole trip for a two-file version of the bank. The account lives in one package and the program that uses it in another.
package com.bank.model; public class Account { private final String owner; private double balance; public Account(String owner) { this.owner = owner; } public void deposit(double amount) { balance = balance + amount; } public String getOwner() { return owner; } public double getBalance() { return balance; }}package com.bank.app; import com.bank.model.Account; public class Main { public static void main(String[] args) { Account acc = new Account("Maya"); acc.deposit(250.0); System.out.println(acc.getOwner() + " has " + acc.getBalance()); }}From the folder that holds src, two commands compile the program and run it:
$ javac -d out src/com/bank/model/Account.java src/com/bank/app/Main.java$ java -cp out com.bank.app.MainMaya has 250.0-d out tells javac to put the compiled files in a folder called out, and it builds the package folders inside it for you:
out/ com/ bank/ app/ Main.class model/ Account.class-cp out, short for classpath, makes out the one place Java searches. And java takes the class's full name, com.bank.app.Main, never a file name. It turns the dots into folders and loads out/com/bank/app/Main.class. When main first uses Account, Java finds out/com/bank/model/Account.class the same way.
Key ideaThe classpath is the list of folders and JAR files Java searches when a running program needs a class that is not part of Java itself. In each one, Java looks for the folders that match the class's package.
java -cp out com.bank.app.Main. Which file does Java load to start the program?When Java cannot find a class#
Give java a short name, or leave out the classpath, and it cannot find your program at all:
$ java -cp out MainError: Could not find or load main class MainCaused by: java.lang.ClassNotFoundException: Main$ java com.bank.app.MainError: Could not find or load main class com.bank.app.MainCaused by: java.lang.ClassNotFoundException: com.bank.app.MainThe first command asks for a class called Main in no package, so Java looks for out/Main.class, which does not exist. The second command has no -cp, so Java searches the default classpath: the folder you are standing in. There is no com/bank/app/Main.class directly inside it.
JAR files#
Projects rarely hand around folders of .class files. They pack them into a JAR file (short for Java ARchive): a single zip file holding .class files in their package folders. The jar tool that comes with the JDK makes one. Here the model package goes into a JAR, its loose class files are deleted, and the program runs twice:
$ jar --create --file lib/bank-model.jar -C out com/bank/model$ rm -r out/com/bank/model$ java -cp out com.bank.app.MainException in thread "main" java.lang.NoClassDefFoundError: com/bank/model/Account at com.bank.app.Main.main(Main.java:7)Caused by: java.lang.ClassNotFoundException: com.bank.model.Account at java.base/jdk.internal.loader.BuiltinClassLoader.loadClass(BuiltinClassLoader.java:580) at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:502) ... 1 more$ java -cp out:lib/bank-model.jar com.bank.app.MainMaya has 250.0Read the first run closely. The program starts, then crashes on line 7, the first line that uses Account. Java loads each class the first time it is needed, and Account is no longer in out. A NoClassDefFoundError means a class that was there when the code was compiled is missing while it runs. (The line numbers inside java.base depend on your Java version.) Put the JAR on the classpath after a colon, and the program works again: Java searches a JAR exactly as if it were a folder. On Windows the entries are separated by a semicolon instead, as in out;lib\bank-model.jar.
Reaching across a package boundary#
Packages do more than tidy your files. They draw a boundary that the access modifiers from encapsulation and inheritance respect. In a one-file program every class shares the unnamed package, so the boundary never showed. Split the bank into packages and it does.
A class with no modifier in front of the word class has package access, also called package-private: only classes in the same package can use it. To use a class from another package, it must be public. Fields and methods follow the same rule, and protected opens one more door: a protected member can also be used inside a subclass in another package. Here is an account with one field of each kind, a program that uses it, and a savings account that extends it. Main and SavingsAccount are both in com.bank.app.
package com.bank.model; public class Account { public String owner; protected double balance; String branch = "Leeds"; public Account(String owner) { this.owner = owner; }}package com.bank.app; import com.bank.model.Account; public class Main { public static void main(String[] args) { Account acc = new Account("Maya"); System.out.println(acc.owner); acc.balance = 500.0; acc.branch = "York"; }}package com.bank.app; import com.bank.model.Account; public class SavingsAccount extends Account { public SavingsAccount(String owner) { super(owner); } public void addInterest() { balance = balance * 1.02; } public void moveBranch() { branch = "York"; }}$ javac -d out src/com/bank/model/Account.java src/com/bank/app/Main.java src/com/bank/app/SavingsAccount.javasrc/com/bank/app/Main.java:9: error: balance has protected access in Account acc.balance = 500.0; ^src/com/bank/app/Main.java:10: error: branch is not public in Account; cannot be accessed from outside package acc.branch = "York"; ^src/com/bank/app/SavingsAccount.java:15: error: branch is not public in Account; cannot be accessed from outside package branch = "York"; ^3 errorsWalk the boundary one field at a time. owner is public, so line 8 of Main is fine. balance is protected: Main is neither in com.bank.model nor a subclass, so line 9 fails, while line 11 of SavingsAccount, which uses balance inside a subclass, compiles without complaint. branch has package access, so only classes in com.bank.model may touch it. Both other classes fail on it, and extending Account does not help.
| Modifier | Same class | Same package | Subclass in another package | Any other class |
|---|---|---|---|---|
public | Yes | Yes | Yes | Yes |
protected | Yes | Yes | Yes | No |
| none (package access) | Yes | Yes | No | No |
private | Yes | No | No | No |
class Account in one package, then cannot understand why another package will not compile against it. Without public the class has package access, so the other package cannot see it at all, and javac says so: Account is not public in com.bank.model; cannot be accessed from outside package. The fix is one word, public class Account. The same trap catches constructors and methods you meant to share. If another package needs it, it must say public.Audit in package com.bank.model, next to Account, runs acc.branch = "York"; on an Account called acc. Does it compile?Putting the whole picture together#
Here is the full life of a name, from the file that defines it to the program that uses it, in four steps.
- 1A class declares its package on the first line:
package com.bank.model;. On disk it sits in the matching folder,com/bank/model. - 2That gives the class a fully qualified name,
com.bank.model.Account, which no other class in the world shares. - 3A class in another package writes
import com.bank.model.Account;so it can use the short nameAccount. For that to be allowed,Accountmust bepublic. - 4When the program runs, Java searches the classpath for
com/bank/model/Account.classand loads it. The folders mirror the package name once more.
The same correspondence shows up three times: in the folders that hold the source files, in the fully qualified name, and on the classpath while the program runs. Hold onto it and packages stop being mysterious. They are folders whose names the language keeps track of.
Best: 91.Tap a line, then tap where it should go. Or use the arrows.
Try it
Answer each one in your head or on paper first, then open the answer.
src/org/library/loans/Loan.java holds a class called Loan. What is the class's fully qualified name, and what is the first line of the file?System.out.println(...) and never imported System. Why does it work with no import?Scanner class? Write the two ways to create one: with an import, and without.Account in package com.bank.model is written as class Account with no modifier. A class in com.bank.app imports it and fails to compile. What single word fixes it, and why?import static java.lang.Math.max; let you write, and why does Math itself never need an import?java -cp out com.bank.app.Main stops with NoClassDefFoundError: com/bank/model/Account. What has gone wrong, in one sentence?Practice
Work through these on your own. Each one builds on this page.
Book, Member, Loan, LoanReport and a Main that runs it. Choose package names, draw the folder tree under src showing which .java file sits where, and write the package line at the top of each file.Main, write every import line it needs to use the other four classes by their short names. Then decide, for each class, whether it must be public, and say why.javac -d out and run it with java -cp out com.bank.app.Main. Then break it on purpose in three ways, one at a time: remove public from class Account, run it with the short name Main, and delete Account.class from out. Match each message you get to the explanation on this page.LocalDate, ArrayList and Function. Give it the narrowest imports that work, one class per line, then rewrite the top with import module java.base;. Write two sentences on which version you would rather read in a large project, and why.Further reading#
- Packages on dev.java, the official Java learning site, with naming conventions, imports and static imports
- Packages and modules in the Java Language Specification, chapter 7, the exact rules for package and import declarations
- The java command in the JDK 25 documentation, including --class-path and running a program from source files
- JEP 511: Module Import Declarations, the proposal behind import module, final in Java 25
Short guides on this topic
Quick answers to what people search for about it, with code to run.
Spotted a mistake, or a part that did not make sense? Report it by email