Java Tutorial: Package System in Java

, , …,

When writing a program, often you need to put several subroutine units (⁖ functions) together in a file as a unit, so that the program is manageable, and not having hundreds of functions all in one file. A set of functions as a unit can be loaded and used as needed, and also distributed for other programers to use. This idea of a set of functions as a unit in a file is variously known as “library”, “package”, “module”, “add-on”.

In Java, it's called “package”. The following shows how it works in Java.

One-Class-Per-File Paradigm

In Java, you can put each class definition in a different file. Save the following line in a file named Chute.java:

class Chute {Chute () {System.out.println("i'm Chute.");}}

Save the following in a file named RunMe.java, in the same dir as Chute.java.

class RunMe {
  public static void main(String[] args) {
    Chute c = new Chute();
  System.out.println("i'm ran!");
  }
}

Now, compile it javac RunMe.java and run it java RunMe. As you can see, RunMe is a program that calls the class Chute in another file. In this way, you can have different files holding different classes, and calling these classes from a main file.

IMPORTANT: In way of OOP in Java, each file is supposed to contain only one class definition that will be visible outside the file. And, the one class's name must be the same as the file name. For example, if you have a file Chute.java, then in that file you must have a public class named Chute. Else it won't compile. If there are more than one class definition in a file, only the one with the file name is available for other program to use. Other classes in the file can not be called by code outside of the file. They are considered helper classes for the main class of that file.

For example try change the Chute.java to the following:

class Chute {
    Chute () {System.out.println("i'm Chute.");}
    Cute c = new Cute();
}

class Cute {
    Cute () {System.out.println("i'm Cute.");}
}

Now compile and run “RunMe” again. It all still work. Now both Chute and Cute are considered your subroutine tucked away neatly in a file. “Chute” is the main one, identified by the file's name, while “Cute” is a helper, not to be used outside of Chute.java.

Now, try to put “public” in front of Cute and see what happens. You'll see that it's a compiler error, because java does not allow a file to have more than 1 class to be visible outside the file. (and the one class visible outside must be the one having same name as the file.)

Try also “private” or “protected” in front of Chute. (see also: Java access specifiers)

A Set of Classes in a Directory

You can organize class files into directories. For example, you can have a directory named “sound” that contain all classes that deals with sound, and another directory named “images” that contains all classes that deals with pictures, of your video game program.

Save the following code into a file named StartMe.java.

class StartMe {
 public static void main(String[] args) {
  pacman.Ladybug lb = new pacman.Ladybug();
  System.out.println("StartMe done");
 }
}

Now, create a directory there named “packman”. Then, save the following code there as Ladybug.java.

package pacman;
public class Ladybug {
 public Ladybug () {System.out.println("Ladybug initialize!");}
}

Now, compile StartMe.java and run it. As you can see, the class StartMe calls the Ladybug class under the pacman dir by this full reference: “pacman.Ladybug”. This is how Java calls classes in another directory.

Also, note that the Ladybug.java file starts with the line “package pacman;”. If you put a file into a directory as part of a package, that file needs to start with the line package dir name; (otherwise, that file won't be part of your package).

Also, note that the two “public” keywords in Ladybug needs to be there. Otherwise it won't compile. (try to experiment by changing the access specifier and compile.)

Java Package System Summary

Here's a summary of Java's library situation so far:

Nested Packages

Suppose you have a collection of codes related to sound in a video game program. You put them under a “sound” directory. Now, some of these are related to processing sounds, while others are playing sounds. So, you can create subdirectories “process” and “play” as nested packages, to further organize your code.

Using our previous example, in the “pacman” dir create a directory “maze”, and in the “maze” dir create a file Labyrinth.java with the following code:

package pacman.maze;
public class Labyrinth {
 public Labyrinth () {System.out.println("i trap Minotaur!");}
}

And add a line to StartMe.java to call “Labyrinth”:

class StartMe {
  public static void main(String[] args) {
    pacman.Ladybug lb = new pacman.Ladybug();
    pacman.maze.Labyrinth rring = new pacman.maze.Labyrinth();
    System.out.println("StartMe done");
  }
}

So now we have these files and dir structure:

StartMe.java
pacman
 Ladybug.java
pacman
 maze
  Labyrinth.java

Now compile and run StartMe.

Note that the Labyrinth class declared its package as “pacman.maze”, not just “maze”. In Java the language, packages can be nested (that is, have a hierarchy relation, such as our “pacman.maze”), however, the language has no notion of nested packages. Even though maze dir and pacman dir both has a directory relation that mirrors its package relation as required by the compiler, the human programer still needs to specify the full relation in each file's package declaration. (we'll see more of this below with the “import” keyword)

Using the “import” keyword to save typing

You get tired quickly typing the full name “pacman.maze.Labyrinth” to refer to classes in different packages. The solution is the “import” keyword. In a file, you can say “import pacman.maze.Labyrinth” and then refer to the class by just its name “Labyrinth”.

Here's a modified version of StartMe.java using the import keyword:

import pacman.maze.Labyrinth;
class StartMe {
  public static void main(String[] args) {
    pacman.Ladybug lb = new pacman.Ladybug();
    Labyrinth rring = new Labyrinth();
    System.out.println("StartMe done");
  }
}

Likewise, you can also import the class “pacman.Ladybug” and refer to it as just “Ladybug”.

If you have a lot classes under pacman, you can use this syntax idiosyncrasy import.pacman.* to import all classes in that directory. (but it does not import classes in subdirectories).

And, in Java, the language import classes, not packages. In the above example, you cannot say “import pacman” to import all definitions that belong to the package pacman. Also, in Java, you cannot import all classes in nested packages. For example, import pacman.* does not import classes in pacman.maze, and import pacman.*.* is illegal syntax.

Summary in this section:

Note that Java interfaces can also be put into a package, just like classes. Like classes, it should be one-interface-per-file.

With technicality, in Java, every file is in some package. If the file didn't declare a package, it is then in a “default package”, which is a package without name.

blog comments powered by Disqus