By now you should be familiar with Java, and you have probably written several small projects or assignments consisting of a few separate Java files stored in one directory. This is fine for small projects, but as the number of files grows, organization and grouping of the files becomes important. You may also wish to use the same names for different classes. That's where packages in Java become very useful.
A package is a collection of related Java files stored in a directory or folder.
Ideally, each package represents a logical grouping of functionality. For instance, SaM's
packages group classes for the user interface, simulator, and machine instructions.
A package can also contain another package. In this case, the directory that holds the parent
package will contain a subdirectory corresponding to the child package. Thus, each package has
a unique name based on the names of the directories in which they are stored. The package name is
the path from the root directory of your code to the directory containing that package, using
.
to separate names instead of \
or /
.
For instance, if I have the following directories:
cis300\game\control\ cis300\game\graphics\ cis300\raytracer\
then cis300
, cis300.game
, cis300.game.control
,
cis300.game.graphics
, and cis300.raytracer
can all
be packages. There's an additional, unnamed default package, that corresponds to the root of the
tree. In the above example, the default package is the directory that contains the cis300
.
If a Java file belongs to a package other than the default unnamed one, then you must declare
so in the file. This is done via a package statement, which is a statement that alerts Java that
classes in the current file belong to a particular package. For example, if I have a Java file
Car3d.java
in the cis300.game.graphics
package, then at the top of the file (before
any classes or import
statements, I need to write the following line:
package cis300.game.graphics;
Note that Java will complain if a file is in a different package than what the package statement declares.
For instance, if Car3d.java
was actually stored in the cis300\raytracer\
directory,
then the above package statement would cause an error. If a file is in the default package, then no package
statement is necessary (or allowed).
Since any directory could be a package, how does Java know where to look to find your code?
It does so by looking at the CLASSPATH
. If you use an IDE to develop code, this should already
be taken care of. If you compile and run on the command line, however, you'll need to manually
change the CLASSPATH
. On Windows machines, you can run the following command:
set CLASSPATH=C:\directory1;C:\directory2
which sets CLASSPATH
to a list of directories separated by semicolons. Each
directory in that list corresponds to a root directory in the hierarchy.
Java files in those directories are in their respective default packages, and subdirectories in those
directories can correspond to packages.
The easiest way to do set CLASSPATH
is using the current directory, which is a single
period .
. For example, if I store all my code in C:\MyStuff\MyCode
, I can run the
following commands at the command line:
C:\MyStuff\MyCode> set CLASSPATH=. C:\MyStuff\MyCode> javac cis300\game\*.java C:\MyStuff\MyCode> java cis300.game.MainGame
presuming the package structure above. Note: the change in CLASSPATH
is only effective while
that command window is open. If you close that window and open another one, you'll have to reset your
CLASSPATH
. To permanently change your system settings, refer to
Applications.
Suppose I have two classes in the above hierarchy: cis300.game.MainGame
and
cis300.game.graphics.Display
. I go into MainGame
and write the following code:
package cis300.game; public class MainGame { public MainGame() { Display d = new Display(); } }
If you try and compile this code, Java will give you an error. Why? After all, you've told it where
your packages are! The answer is that you aren't using the proper name for the class Display
.
The correct name for Display
is actually cis300.game.graphics.Display
, which
is as the fully-qualified class name. If I want to create a Display
object, I need to change that line of code to
cis300.game.graphics.Display d = new cis300.game.graphics.Display();
Such long names get cumbersome very quickly. Instead, you should use import statements instead. An
import
allows you to use one or more classes in a given package without using
the fully-qualified class name. For instance, if I add the line
import cis300.game.graphics.Display;
inbetween the package statement and the class statement, then I can use the class name Display
without the package name. If I want to import all of the classes in a given package
(say, cis300.game.graphics
), I can use a star *
instead:
import cis300.game.graphics.*;
The above statement is equivalent to writing an import statement for every class in that package.
However, the *
is usually frowned upon. Why? You are likely importing classes you do not use,
creating potential collisions between names. For instance, suppose the package you import has a class named
Sprite
, which is the name of an entirely different class in the current package.
If you wish to use a class in the same package as another class, you do not need an
import
statement. For example, if package cis300.game
contains classes
HighScores
and MainGame
, either class can "see" each other with out importing the
other.
Some of you may be familiar with the public
/ private
/ protected
access modifiers. Much of the detail will be skipped here, but as it relates to packages, classes within
the same package can access public
, protected
, and package-level
(those without any of the other three modifiers) methods, classes, and variables. Classes in different
packages can only access the public
classes, methods, and variables in the respective packages.