Building
a Java program
There
are several other issues you must understand before seeing your first Java
program.
Name
visibility
A
problem in any programming language is the control of names. If you use a name
in one module of the program, and another programmer uses the same name in
another module, how do you distinguish one name from another and prevent the
two names from “clashing”? In C this is a particular problem
because a program is often an unmanageable sea of names. C++ classes (on which
Java classes are based) nest functions within classes so they cannot clash with
function names nested within other classes. However, C++ still allowed global
data and global functions, so clashing was still possible. To solve this
problem, C++ introduced
namespaces
using additional keywords.
Java
was able to avoid all of this by taking a fresh approach. To produce an
unambiguous name for a library, the specifier used is not unlike an Internet
domain name. In fact, the Java creators want you to use your Internet domain
name in reverse since those are guaranteed to be unique. Since my domain name is
BruceEckel.com,
my utility library of foibles would be named
com.bruceeckel.utility.foibles.
After your reversed domain name, the dots are intended to represent
subdirectories.
In
Java
1.0 and Java 1.1
the domain extension
com,
edu,
org,
net,
etc., was capitalized
by convention, so the library would appear:
COM.bruceeckel.utility.foibles.
Partway through the development of Java 1.2,
however, it was discovered that this caused problems and so now the entire
package name is lowercase.
This
mechanism in Java means that all of your files automatically live in their own
namespaces, and each class within a file automatically has a unique identifier.
(Class names within a file must be unique, of course.) So you do not need to
learn special language features to solve this problem – the language
takes care of it for you.
Using
other components
Whenever
you want to use a predefined class in your program, the compiler must know how
to locate it. Of course, the class might already exist in the same source code
file that it’s being called from. In that case, you simply use the class
– even if the class doesn’t get defined until later in the file.
Java eliminates the “forward referencing” problem so you
don’t need to think about it.
What
about a class that exists in some other file? You might think that the compiler
should be smart enough to simply go and find it, but there is a problem.
Imagine that you want to use a class of a particular name, but the definition
for that class exists in more than one file. Or worse, imagine that
you’re writing a program, and as you’re building it you add a new
class to your library that conflicts with the name of an existing class.
To
solve this problem, you must eliminate all potential ambiguities. This is
accomplished by telling the Java compiler exactly what classes you want using
the
import
keyword.
import
tells
the compiler to bring in a
package,
which is a library of classes. (In other languages, a library could consist of
functions and data as well as classes, but remember that all code in Java must
be written inside a class.)
Most
of the time you’ll be using components from the standard Java libraries
that come with your compiler. With these, you don’t need to worry about
long, reversed domain names; you just say, for example:
to
tell the compiler that you want to use Java’s
Vector
class. However,
util
contains a number of classes and you might want to use several of them without
declaring them all explicitly. This is easily accomplished by using ‘
*’
to indicate a wildcard:
It
is more common to import a collection of classes in this manner than to import
classes individually.
The
static keyword
Ordinarily,
when you create a class you are describing how objects of that class look and
how they will behave. You don’t actually get anything until you create an
object of that class with
new,
and at that point data storage is created and methods become available.
But
there are two situations in which this approach is not sufficient. One is if
you want to have only one piece of storage for a particular piece of data,
regardless of how many objects are created, or even if no objects are created.
The other is if you need a method that isn’t associated with any
particular object of this class. That is, you need a method that you can call
even if no objects are created. You can achieve both of these effects with the
static
keyword. When you say something is
static,
it means that data or method is not tied to any particular object instance of
that class. So even if you’ve never created an object of that class you
can call a
static
method or access a piece of
static
data. With ordinary, non-
static
data and methods you must create an object and use that object to access the
data or method, since non-
static
data and methods must know the particular object they are working with. Of
course, since
static
methods don’t need any objects to be created before they are used, they
cannot
directly
access
non-
static
members or methods by simply calling those other members without referring to a
named object (since non-
static
members and methods must be tied to a particular object).
Some
object-oriented languages use the terms
class
data
and
class
methods
,
meaning that the data and methods exist only for the class as a whole, and not
for any particular objects of the class. Sometimes the Java literature uses
these terms too.
To
make a data member or method
static,
you simply place the keyword before the definition. For example, this produces a
static
data member and initializes it:
class StaticTest {
static int i = 47;
}
Now
even if you make two
StaticTest
objects, there will still be only one piece of storage for
StaticTest.i.
Both objects will share the same
i.
Consider:
StaticTest st1 = new StaticTest();
StaticTest st2 = new StaticTest();
At
this point, both
st1.i
and
st2.i
have the same value of 47 since they refer to the same piece of memory.
There
are two ways to refer to a
static
variable. As indicated above, you can name it via an object, by saying, for
example,
st2.i.
You can also refer to it directly through its class name, something you cannot
do with a non-static member. (This is the preferred way to refer to a
static
variable since it emphasizes that variable’s
static
nature.)
The
++
operator increments the variable. At this point, both
st1.i
and
st2.i
will have the value 48.
Similar
logic applies to static methods. You can refer to a static method either
through an object as you can with any method, or with the special additional
syntax
classname.method( ).
You define a static method in a similar way:
class StaticFun {
static void incr() { StaticTest.i++; }
}
You
can see that the
StaticFun
method
incr( )
increments the
static
data
i.
You can call
incr( )
in the typical way, through an object:
StaticFun sf = new StaticFun();
sf.incr();
Or,
because
incr( )
is
a static method, you can call it directly through its class:
While
static,
when applied to a data member, definitely changes the way the data is created
(one for each class vs. the non-
static
one
for each object), when applied to a method it’s not so dramatic. An
important use of
static
for methods is to allow you to call that method without creating an object.
This is essential, as we will see, in defining the
main( )
method that is the entry point for running an application.
Like
any method, a
static
method can create or use named objects of its type, so a
static
method is often used as a “shepherd” for a flock of instances of
its own type.