Skip to main content

Section 12.4 Organizing the Code: Packages

In the previous sections, we established our goal: to build a dynamic list (ArrayList<T>) that implements standard behaviors defined by interfaces (CollectionADT<T> and ListADT<T>). This immediately tells us our project will involve multiple .java files: at least one for each interface and one for our implementing class, plus the test code.
Now, imagine a real-world software project. It might contain dozens, hundreds, or even thousands of classes and interfaces! What happens if we just dump all these .java files into a single directory? It quickly becomes difficult to manage:
  • Finding Code: Locating the specific file you need (e.g., the ListADT interface versus the ArrayList implementation) becomes tedious and error-prone among potentially many other files.
  • Naming Collisions: What if you decide to name your list implementation ArrayList, but Java’s standard library already provides a class named java.util.ArrayList? How does the Java compiler, and later the Java runtime, know which one you intend to use? What if two different teams working on the same large project both need a helper class called Utils? Without organization, their code cannot easily coexist.
Just like you organize files on your computer into folders and subfolders, Java provides a mechanism to organize code into logical groups called packages. Packages serve two primary purposes:
  1. Organization: They group related classes and interfaces together, making the codebase easier to navigate, understand, and maintain.
  2. Namespace Management: They create unique "spaces" for names. This prevents naming conflicts because a class’s full identity includes its package name.
You declare that a class or interface belongs to a specific package using the package keyword as the very first statement in the .java file (before any imports or class/interface definitions).
package name.of.package; // Package declaration MUST be the first line

// ... rest of the file (imports, class/interface definition) ...

public class MyClass {
    // ...
}
Crucially, Java mandates that the package structure declared in your code must exactly match the directory structure on your file system, relative to a designated source root directory (often named src). If a file declares package com.mycompany.utilities;, then that file must reside inside a folder structure like src/com/mycompany/utilities/. Furthermore, if that file contains a public class or interface named MyUtil, the file itself must be named MyUtil.java. This strict mapping isn’t just arbitrary; it’s how Java tools work. Both the Java compiler (javac), when translating your .java files into .class files, and the Java Virtual Machine (JVM) runtime (java), when loading those .class files to execute your program, rely on this package-to-directory structure to locate the necessary code components.
Let’s apply this to our project’s structure:
YourProjectFolder/
└── src/  <-- Source Root
    ├── ADTs/
    │   └── ListADT.java      (Declares: package ADTs; Contains: public interface ListADT ...)
    └── DataStructures/
        └── ArrayList.java    (Will declare: package DataStructures; Will contain: public class ArrayList ...)
Here, src is the source root. The public interface ListADT is correctly placed in src/ADTs/ListADT.java and declares package ADTs;. Similarly, your public class ArrayList must be in src/DataStructures/ArrayList.java and declare package DataStructures;.
Because packages provide this structured namespace, the fully qualified name of a type includes its package path. This resolves naming ambiguity:
  • Our list interface’s full name is ADTs.ListADT.
  • Our implementation class’s full name will be DataStructures.ArrayList.
  • Java’s built-in ArrayList’s full name is java.util.ArrayList.
The compiler and runtime can now clearly distinguish between DataStructures.ArrayList and java.util.ArrayList thanks to the package structure.
So, packages provide the organizational structure. But now we have a new question: if ArrayList.java is in the DataStructures package, how does it refer to ListADT, which is in the different ADTs package? We need a way to make types from one package visible to another. That’s the role of the import statement, which we’ll cover in the next section.
You have attempted of activities on this page.