Table of Contents
Name Spaces and Reusability
A major problem when developing reusable code is that of name conflicts.
Vendor B might develop a library of classes, while Vendor M might develop another library of classes. Customer C wants to use both libraries, but discovers that a number of class names have been duplicated across the two libraries.
What to do?
Qualified Names
A solution is to organize code into named packages.
In C++, the feature equivalent to Java’s packages is C++ namespaces.
Then, when class names conflict among different code libraries, we can explicitly place the code in separate packages, and qualify the name references with the appropriate package name.
The namespace represented is arranged hierarchically in terms of packages and sub-packages of a package, etc. For example, here is a reference to a method in the Java class library:
java.lang.String.substring()
where:
- java is the package name
- lang is the sub-package name
- String is the class name
- substring is the method name
And here is a reference to a class library method for a (mythical) vendor, Aardvark:
COM.Aardvark.eng.gui.Menu.add(String s)
where:
- COM.Aardvark is from the vendor’s Internet domain name, aardvark.com
- eng.gui is the vendor’s internal naming convention:
- eng — Engineering
- gui — Graphical User Interface development
- Menu is the class name
- add is the method name within that class
Note that the convention is to use the organization’s Internet domain names in reverse order. For example, common names might look like:
COM.Microsoft... COM.Oracle... COM.Borland... COM.Symantec...
However, lots of vendors omit the COM., or just use their company name as the first name.
Also, some vendors use com, rather than COM.
The import Statement
It’s a royal pain to have to fully qualify every class name in your program.
So the Java folks added the import statement as a convenience:
import java.util.Hashtable; // import just HashTable
or:
import java.util.*; // import all classes in java.util
which allows the Java compiler to make a name accessible through an abbreviated name — the class name itself (in this case Hashtable).
Note: I try to discourage the use of the ‘*‘ method of importing, because I consider it bad — often sloppy/lazy — software engineering practice. Please don’t use it, at least in my course!
If you wish, you can use a mix of import statements and fully qualified references.
If two packages imported in this way contain classes with the same name, you cannot use either of those classes without using the fully qualified name for each.
Note: Remember:
- There may be any number of import statements in a Java program.
- They must follow any package statement in the source
- They must precede the first class definition (or interface definition — more later) in the file.
Static imports
Starting in Java 5, the import statement was enhanced to allow the importing of static methods and fields from a class.
For example:
import static java.lang.System.exit;
import static java.lang.System.out;
import static java.lang.Math.sqrt;
import static java.lang.Math.PI;
public class StaticImportTest
{
public static void main(String[] args)
{
System.out.println("Using System.out.println(...)");
out.println("Using out.println(...)");
out.println("The square root of 64 is " + Math.sqrt(64));
out.println("The square root of 91 is " + sqrt(91));
out.println("The value of PI is " + PI);
}
}
which, when run in a Java Virtual Machine supporting Java 5 or above, produces the following output:
Using System.out.println(...) Using out.println(...) The square root of 64 is 8.0 The square root of 91 is 9.539392014169456 The value of PI is 3.141592653589793
In a JVM supporting an earlier version of Java than 5, the program will produce compilation errors.
Automatic import
PackageĀ java.lang
The following import statement is implicit in every Java program:
import java.lang.*; // import all classes in java.lang
so, you never have to add that particular import statement to your program source.
The following public types are defined in java.lang (this is not a complete list, and is subject to augmentation in every release of Java):
| AbstractMethodError ArithmeticException ArrayStoreException Boolean Character Class ClassCastException ClassCircularityError ClassFormatError ClassLoader ClassNotFoundException CloneNotSupportedException Cloneable Compiler Double Error Exception ExceptionInInitializerError Float IllegalAccessError IllegalAccessException IllegalArgumentException IllegalMonitorStateException IllegalThreadStateException IncompatibleClassChangeError IndexOutOfBoundsException InstantiationError InstantiationException Integer InternalError InterruptedException | LinkageError Long Math NegativeArraySizeException NoClassDefFoundError NoSuchFieldError NoSuchMethodError NullPointerException Number NumberFormatException Object OutOfMemoryError Process Runnable Runtime RuntimeException SecurityException SecurityManager StackOverflowError String StringBuffer System Thread ThreadDeath ThreadGroup Throwable UnknownError UnsatisfiedLinkError VerifyError VirtualMachineError |
The Default Package
Another package that is implicitly and automatically imported in every Java program is the default package. The default package is the package which contains every Java class that does not have an explicit package definition.
Note: I strongly discourage the use of the default package for other than truly trivial (i.e. not intended for any serious application) Java code.
The Current Package
Finally, one more package which is automatically imported is the package specified for the current Java class.
In other words, if you place your class Fontleroy into the little.lord package, then, when the Java compiler compiles your class, it implicitly does the following import:
import little.lord.*;
The package Statement
When you wish to create a class that is to belong to a named Java package, you must place a package statement as the first statement (i.e. first text other than comments and whitespace) in the class’s Java source code file.
Here is a package statement:
package COM.Aardvark.eng.gui;
where the package name must be fully qualified — that is, it must include the top-level package name and all intervening sub-package names .
A compilation unit that has no package statement is part of an unnamed package — the default package.
- A Java system is required to support at least one unnamed package
- It may support more than one, but is not required to do so.
- Unnamed packages are a convenience feature when developing simple or temporary development, or when just starting to learn Java.
- Unnamed packages are strongly discouraged in real-world Java code.
Note: From this point on, I will expect you to use only named packages in your Java code!
Compilation Units
A Java compilation unit is defined in the Java Language Specification as:
CompilationUnit : PackageDeclarationopt ImportDeclarationsopt TypeDeclarationsopt
TypeDeclarations : TypeDeclaration TypeDeclarations TypeDeclaration
TypeDeclaration : ClassDeclaration InterfaceDeclaration
Notice the subscript “opt”, which means optional.
The above means that a Java compilation unit consists of:
- Optionally, a package declaration, followed by
- Optionally, a set of one or more import declarations, followed by
- Optionally, a set of one or more type declarations, each type declaration may comprise:
- A class declaration, or
- An interface declaration (more about interfaces, soon)
Host Support for Packages
Each Java host determines:
- How packages, compilation units, and sub-packages are created and stored,
- Which top-level package names are in scope in a particular compilation, and
- Which packages are accessible
The packages may be stored:
- In a local file system, or:
- In a distributed file system, or:
- In some form of database
On Microsoft Windows and on Unix systems, they are typically stored in a directory hierarchy that matches the naming hierarchy. This typically means that a package is a directory containing .class files and/or subdirectories. The subdirectories are sub-packages of that package.
Note: Remember:
- Package and class names are case-sensitive!
- Somehow, the package directory must be accessible through the
CLASSPATHenvironment variable.
Type Definitions
Within a compilation unit you may have several TypeDeclarations. However, note the following:From “The Java Language Specification”, by James Gosling, Bill Joy and Guy Steele, Addison-Wesley:
“When Java packages are stored in the file system, the host system may choose to enforce the restriction that it is a compile-time error if a type is not found in a file under a name composed of the type name plus an extension (such as .java or .jav) if either of the following is true:
- The type is referred to by code in other compilation units of the package in which the type is declared, or:
- The type is declared public (and therefore is potentially accessible from code in other packages).
This restriction implies that there must be at most one such type per compilation unit. This restriction makes it easy for a Java compiler and Java Virtual Machine to find a named class within a package. For example, the code for a public type wet.sprocket.Toad would be found in a file Toad.java in the directory wet/sprocket. The corresponding object code would be found in the file Toad.class in the same directory.
When Java packages are stored in a database, the host system need not enforce such restrictions.
In practice, many Java programmers choose to put each class or interface type in its own compilation unit, whether or not it is public, or is referred to by code in other compilation units.”
Access Control
Access to Package Contents
A package is accessible if the corresponding files and directories are accessible.
All classes (and interfaces) in a package are accessible to all other classes (and interfaces) in the same package.
A class declared public in one package is accessible from within another package. A non-public class is not accessible from outside of its package.
Members of a class are accessible from a different class within the same package, as long as they are not declared private.
private members are accessible only within their own class.
All members of a class are accessible from within that class.
Visibility Modifiers
The keywords public and private (and protected — later) are visibility modifiers, which have the following effect:
A private member of a class is visible only in methods defined within that class.
A public member of a class is visible to all class methods.
If a member is declared with no visibility modifiers, then it has default package visibility, and is visible only within the class that defines it and within classes defined in the same package. (This is analogous to the friend concept in C++ — all classes within a package are ‘friendly’ to each other.)
To summarize:
| Member | Visibility | ||
|---|---|---|---|
| Accessible to: | public | package | private |
| Same class | yes | yes | yes |
| Class in same Package | yes | yes | no |
| Subclass in different package | yes | no | no |
| Non-subclass, different package | yes | no | no |
Here are some simple rules to help you choose which visibility modifiers to use, when:
- Use public only for methods and constants that form part of the public interface (sometimes called the API) of the class.
- Use the default package visibility for fields and methods that you want to be hidden from outside of the package, but which you want cooperating (‘friend’) classes within the package to have access to.
- Use private for fields and methods that are only used inside the class and should be hidden from everywhere else.
Note: Try to make all class/instance data (fields) private, except for static final fields (i.e. class constants).
