In the tradition of introductory programming texts, we will begin
with Java’s equivalent of the archetypal “Hello World” application,
HelloJava
.
We’ll end up taking four passes at this example before we’re done
(HelloJava
, HelloJava2
, etc.), adding features and
introducing new concepts along the way. But let’s start with the
minimalist version:
public
class
HelloJava
{
public
static
void
main
(
String
[]
args
)
{
System
.
out
.
println
(
"Hello, Java!"
);
}
}
This five-line program declares a class called HelloJava
and a method called main()
. It uses a predefined method called
println()
to write some
text as output. This is a command-line program, which
means that it runs in a shell or DOS window and prints its output there.
That’s a bit old-school for our taste, so before we go any further, we’re
going to give HelloJava
a
graphical user interface (GUI). Don’t worry about the
code yet; just follow along with the progression here, and we’ll come back
for explanations in a moment.
In place of the line containing the println()
method, we’re
going to use a JFrame
object to put a
window on the screen. We can start by replacing the println
line with the following three
lines:
JFrame
frame
=
new
JFrame
(
"Hello, Java!"
);
frame
.
setSize
(
300
,
300
);
frame
.
setVisible
(
true
);
This snippet creates a JFrame
object with the title “Hello, Java!” The JFrame
is a graphical window. To display it, we
simply configure its size on the screen using the setSize()
method and make
it visible by calling the setVisible()
method.
If we stopped here, we would see an empty window on the screen with
our “Hello, Java!” banner as its title. We’d like our message inside the
window, not just scrawled at the top of it. To put something in the
window, we need a couple more lines. The following complete example adds a
JLabel
object to display
the text centered in our window. The additional import
line at the top is
necessary to tell Java where to find the JFrame
and JLabel
classes (the definitions of the JFrame
and JLabel
objects that we’re using).
import
javax.swing.*
;
public
class
HelloJava
{
public
static
void
main
(
String
[]
args
)
{
JFrame
frame
=
new
JFrame
(
"Hello, Java!"
);
JLabel
label
=
new
JLabel
(
"Hello, Java!"
,
JLabel
.
CENTER
);
frame
.
add
(
label
);
frame
.
setSize
(
300
,
300
);
frame
.
setVisible
(
true
);
}
}
Now to compile and run this source, select the ch02/HelloJava.java class from the package explorer along the left and click the Run button in the toolbar along the top. The Run button is a green circle with a white arrow pointing to the right. See Figure 2-4.
You should see the proclamation shown in Figure 2-5. Congratulations, you have run your first Java application! Take a moment to bask in the glow of your monitor.
Be aware that when you click on the window’s close box, the window goes away, but your program is still running. (We’ll fix this shutdown behavior in a later version of the example.) To stop the Java application in Eclipse, click the big red button in the console window. If you are running the example on the command line, type Ctrl-C. Note that nothing stops you from running more than one instance (copy) of the application at a time.
HelloJava
may be a small program,
but there is quite a bit going on behind the scenes. Those few lines
represent the tip of an iceberg. What lies under the surface are the
layers of functionality provided by the Java language and its foundation
class libraries. Remember that in this chapter, we’re going to cover a lot
of ground quickly in an effort to show you the big picture. We’ll try to
offer enough detail for a good understanding of what is happening in each
example, but will defer detailed explanations until the appropriate
chapters. This holds for both elements of the Java language and the
object-oriented concepts that apply to them. With that said, let’s take a
look now at what’s going on in our first example.
The first example defines a class named HelloJava
.
public
class
HelloJava
{
...
Classes are the fundamental building blocks of most object-oriented languages. A class is a group of data items with associated functions that can perform operations on that data. The data items in a class are called variables, or sometimes fields; in Java, functions are called methods. The primary benefits of an object-oriented language are this association between data and functionality in class units and also the ability of classes to encapsulate or hide details, freeing the developer from worrying about low-level details.
In an application, a class might represent something concrete, such as a button on a screen or the information in a spreadsheet, or it could be something more abstract, such as a sorting algorithm or perhaps the sense of ennui in a video game character. A class representing a spreadsheet might, for example, have variables that represent the values of its individual cells and methods that perform operations on those cells, such as “clear a row” or “compute values.”
Our HelloJava
class is an
entire Java application in a single class. It defines just one method,
main()
, which holds the body of our
program:
public
class
HelloJava
{
public
static
void
main
(
String
[]
args
)
{
...
It is this main()
method that
is called first when the application is started. The bit labeled
String [] args
allows us to pass
command-line arguments to the application. We’ll
walk through the main()
method in the
next section. Finally, we’ll note that although this version of HelloJava
does not define any variables as
part of its class, it does use two variables, frame
and label
, inside its main()
method. We’ll have more to say about
variables soon as well.
As we saw when we ran our example, running a Java
application means picking a particular class and passing its name as an
argument to the Java virtual machine. When we did this, the java
command looked in our HelloJava
class to see if it contained the
special method named main()
of just
the right form. It did, and so it was executed. If it had not been
there, we would have received an error message. The main()
method is the entry point for
applications. Every standalone Java application includes at least one
class with a main()
method that
performs the necessary actions to start the rest of the program.
Our main()
method sets up a
window (a JFrame
) to hold the visual
output of the HelloJava
class. Right
now, it’s doing all the work in the application. But in an
object-oriented application, we normally delegate responsibilities to
many different classes. In the next incarnation of our example, we’re
going to perform just such a split—creating a second class—and we’ll see
that as the example subsequently evolves, the main()
method remains more or less the same,
simply holding the startup procedure.
Let’s quickly walk through our main()
method, just so we know what it does.
First, main()
creates a JFrame
, the window that will hold our
example:
JFrame
frame
=
new
JFrame
(
"Hello, Java!"
);
The word new
in this line of
code is very important. JFrame
is the
name of a class that represents a window on the screen, but the class
itself is just a template, like a building plan. The new
keyword tells Java to allocate memory and
actually create a particular JFrame
object. In this
case, the argument inside the parentheses tells the JFrame
what to display in its title bar. We
could have left out the “Hello, Java” text and used empty parentheses to
create a JFrame
with no title, but
only because the JFrame
specifically
allows us to do that.
When frame windows are first created, they are very small. Before
we show the JFrame
, we set its size
to something reasonable:
frame
.
setSize
(
300
,
300
);
This is an example of invoking a method on a particular object. In
this case, the setSize()
method is
defined by the JFrame
class, and it
affects the particular JFrame
object
we’ve placed in the variable frame
.
Like the frame, we also create an instance of JLabel
to hold our text inside the
window:
JLabel
label
=
new
JLabel
(
"Hello, Java!"
,
JLabel
.
CENTER
);
JLabel
is much like a physical
label. It holds some text at a particular position—in this case, on our
frame. This is a very object-oriented concept: using an object to hold
some text, instead of simply invoking a method to “draw” the text and
moving on. The rationale for this will become clearer later.
Next, we have to place the label into the frame we created:
frame
.
add
(
label
);
Here, we’re calling a method named add()
to place our label inside the JFrame
. The JFrame
is a kind of container that can hold
things. We’ll talk more about that later. main()
’s final task is to show the frame
window and its contents, which otherwise would be invisible. An
invisible window makes for a pretty boring application.
frame
.
setVisible
(
true
);
That’s the whole main()
method.
As we progress through the examples in this chapter, it will remain
mostly unchanged as the HelloJava
class evolves around it.
A class is a blueprint for a part of an application; it holds methods and variables that make up that component. Many individual working copies of a given class can exist while an application is active. These individual incarnations are called instances of the class, or objects. Two instances of a given class may contain different data, but they always have the same methods.
As an example, consider a Button
class. There is only one Button
class, but an application can create
many different Button
objects, each
one an instance of the same class. Furthermore, two Button
instances might contain different data,
perhaps giving each a different appearance and performing a different
action. In this sense, a class can be considered a mold for making the
object it represents, something like a cookie cutter stamping out
working instances of itself in the memory of the computer. As you’ll see
later, there’s a bit more to it than that—a class can in fact share
information among its instances—but this explanation suffices for now.
Chapter 5 has the whole story on classes and
objects.
The term object is very general and in some
other contexts is used almost interchangeably with
class. Objects are the abstract entities that all
object-oriented languages refer to in one form or another. We will use
object as a generic term for an instance of a
class. We might, therefore, refer to an instance of the Button
class as a button, a Button
object, or, indiscriminately, as an
object.
The main()
method in the
previous example creates a single instance of the JLabel
class and shows it in an instance of
the JFrame
class. You could modify
main()
to create many instances of
JLabel
, perhaps each in a separate
window.
In Java, every class defines a new type (data type). A variable can be
declared to be of this type and then hold instances of that class. A
variable could, for example, be of type Button
and hold an instance of the Button
class, or of type SpreadSheetCell
and hold a SpreadSheetCell
object, just as it could be
any of the simpler types, such as int
or float
, that represent
numbers. The fact that variables have types and cannot simply hold any
kind of object is another important feature of the language that ensures
the safety and correctness of code.
Ignoring the variables used inside the main()
method for the moment, only one other
variable is declared in our simple HelloJava
example. It’s found in the
declaration of the main()
method
itself:
public
static
void
main
(
String
[]
args
)
{
Just like functions in other languages, a method in Java declares
a list of variables that it accepts as arguments or parameters, and it specifies the
types of those variables. In this case, the main method
is requiring that when it is invoked,
it be passed a list of String
objects
in the variable named args
. The
String
is the fundamental object
representing text in Java. As we hinted earlier, Java uses the
args
parameter to pass
any command-line arguments supplied to the Java virtual machine (VM)
into your application. (We don’t use them here.)
Up to this point, we have loosely referred to variables as holding
objects. In reality, variables that have class types don’t so much
contain objects as point to them. Class-type variables are references to
objects. A reference is a pointer to or a handle
for an object. If you declare a class-type variable without assigning it
an object, it doesn’t point to anything. It’s assigned the default value
of null
, meaning “no
value.” If you try to use a variable with a null value as if it were
pointing to a real object, a runtime error, NullPointerException
,
occurs.
Of course, object references have to come from somewhere. In our
example, we created two objects using the new
operator. We’ll examine object creation in
more detail a little later in the chapter.
Thus far, our HelloJava
example has contained itself in a single class. In fact, because of its
simple nature, it has really just served as a single, large method.
Although we have used a couple of objects to display our GUI message,
our own code does not illustrate any object-oriented structure. Well,
we’re going to correct that right now by adding a second class. To give
us something to build on throughout this chapter, we’re going to take
over the job of the JLabel
class (bye
bye, JLabel
!) and replace it with our
own graphical class: HelloComponent
.
Our HelloComponent
class will start
simple, just displaying our “Hello, Java!” message at a fixed position.
We’ll add capabilities later.
The code for our new class is very simple; we added just a few more lines:
import
java.awt.*
;
class
HelloComponent
extends
JComponent
{
public
void
paintComponent
(
Graphics
g
)
{
g
.
drawString
(
"Hello, Java!"
,
125
,
95
);
}
}
You can add this text to the HelloJava.java
file, or you can place it in its own file called
HelloComponent.java. If you put it in the same
file, you must move the new import
statement to the top of the file, along with the other one. To use our
new class in place of the JLabel
,
simply replace the two lines referencing the label with:
frame
.
add
(
new
HelloComponent
()
);
This time when you compile HelloJava.java,
you will see two binary class files:
HelloJava.class and
HelloComponent.class (regardless of how you
arranged the source). Running the code should look much like the
JLabel
version, but if you resize the
window, you’ll notice that our class does not automatically adjust to
center the code.
So what have we done, and why have we gone to such lengths to
insult the perfectly good JLabel
component? We’ve created our new HelloComponent
class, extending a generic graphical class
called JComponent
. To extend a
class simply means to add functionality to an existing class, creating a
new one. We’ll get into that in the next section. Here we have created a
new kind of JComponent
that contains
a method called paintComponent()
, which
is responsible for drawing our message. Our paintComponent()
method takes one argument
named (somewhat tersely) g
, which is
of type Graphics
. When the
paintComponent()
method is invoked, a
Graphics
object is assigned to
g
, which we use in the body of the
method. We’ll say more about paintComponent()
and the Graphics
class in a moment. As for why, you’ll
understand when we add all sorts of new features to our new component
later on.
Java classes are arranged in a parent-child hierarchy in
which the parent and child are known as the superclass and
subclass, respectively. We’ll explore these
concepts fully in Chapter 6. In Java, every
class has exactly one superclass (a single parent), but possibly many
subclasses. The only exception to this rule is the Object
class, which
sits atop the entire class hierarchy; it has no superclass.
The declaration of our class in the previous example uses the
keyword extends
to specify that
HelloComponent
is a subclass of the
JComponent
class:
public
class
HelloComponent
extends
JComponent
{
...
}
A subclass may inherit some or all the variables and methods of its superclass. Through inheritance, the subclass can use those variables and methods as if it has declared them itself. A subclass can add variables and methods of its own, and it can also override or change the meaning of inherited methods. When we use a subclass, overridden methods are hidden (replaced) by the subclass’s own versions of them. In this way, inheritance provides a powerful mechanism whereby a subclass can refine or extend the functionality of its superclass.
For example, the hypothetical spreadsheet class might be subclassed to produce a new scientific spreadsheet class with extra mathematical functions and special built-in constants. In this case, the source code for the scientific spreadsheet might declare methods for the added mathematical functions and variables for the special constants, but the new class automatically has all the variables and methods that constitute the normal functionality of a spreadsheet; they are inherited from the parent spreadsheet class. This also means that the scientific spreadsheet maintains its identity as a spreadsheet, and we can use the extended version anywhere the simpler spreadsheet could be used. That last sentence has profound implications, which we’ll explore throughout the book. It means that specialized objects can be used in place of more generic objects, customizing their behavior without changing the underlying application. This is called polymorphism and is one of the foundations of object-oriented programming.
Our HelloComponent
class is a
subclass of the JComponent
class and
inherits many variables and methods not explicitly declared in our
source code. This is what allows our tiny class to serve as a component
in a JFrame
, with just a few
customizations.
The JComponent
class
provides the framework for building all kinds of user interface
components. Particular components—such as buttons, labels, and list
boxes—are implemented as subclasses of JComponent
.
We override methods in such a subclass to implement the behavior
of our particular component. This may sound restrictive, as if we are
limited to some predefined set of routines, but that is not the case at
all. Keep in mind that the methods we are talking about are ways to
interact with the windowing system. We don’t have to squeeze our whole
application in there. A realistic application might involve hundreds or
thousands of classes, with legions of methods and variables and many
threads of execution. The vast majority of these are related to the
particulars of our job (these are called domain objects). The JComponent
class and other predefined classes
serve only as a framework on which to base code that handles certain
types of user interface events and displays information to the
user.
The paintComponent()
method
is an important method of the JComponent
class; we override it to implement
the way our particular component displays itself on the screen. The
default behavior of paintComponent()
doesn’t do any drawing at all. If we hadn’t overridden it in our
subclass, our component would simply have been invisible. Here, we’re
overriding paintComponent()
to do
something only slightly more interesting. We don’t override any of the
other inherited members of JComponent
because they provide basic functionality and reasonable defaults for
this (trivial) example. As HelloJava
grows, we’ll delve deeper into the inherited members and use additional
methods. We will also add some application-specific methods and
variables specifically for the needs of HelloComponent
.
JComponent
is really the tip of
another iceberg called Swing. Swing is Java’s user interface toolkit,
represented in our example by the import
statement at the top; we’ll discuss it
in some detail in Chapters 16 through 18.
We can correctly refer to HelloComponent
as a JComponent
because subclassing can be thought
of as creating an “is a” relationship, in which the subclass “is a” kind
of its superclass. HelloComponent
is
therefore a kind of JComponent
. When
we refer to a kind of object, we mean any instance of that object’s
class or any of its subclasses. Later, we will look more closely at the
Java class hierarchy and see that JComponent
is itself a subclass of the
Container
class, which
is further derived from a class called Component
, and so on,
as shown in Figure 2-6.
In this sense, a HelloComponent
object is a kind of JComponent
, which
is a kind of Container
, and each of
these can ultimately be considered to be a kind of Component
. It’s from these classes that
HelloComponent
inherits its basic GUI
functionality and (as we’ll discuss later) the ability to have other
graphical components embedded within it as well.
Component
is a subclass of the
top-level Object
class, so all
these classes are types of Object
.
Every other class in the Java API inherits behavior from Object
, which defines a few basic methods, as
you’ll see in Chapter 7. We’ll continue to use
the word object (lowercase o)
in a generic way to refer to an instance of any class; we’ll use
Object
to refer specifically to the
type of that class.
We mentioned earlier that the first line of our example tells Java where to find some of the classes that we’ve been using:
import
javax.swing.*
;
Specifically, it tells the compiler that we are going to be using
classes from the Swing GUI toolkit (in this case, JFrame
, JLabel
, and JComponent
). These classes are organized into
a Java package called javax.swing
. A Java
package is a group of classes that are related by purpose or by
application. Classes in the same package have special access privileges
with respect to one another and may be designed to work together
closely.
Packages are named in a hierarchical fashion with
dot-separated components, such as java.util
and java.util.zip
. Classes in a package must
follow conventions about where they are located in the classpath. They
also take on the name of the package as part of their “full name” or, to
use the proper terminology, their fully qualified
name. For example, the fully qualified name of the JComponent
class is javax.swing.JComponent
. We could have referred
to it by that name directly, in lieu of using the import
statement:
public
class
HelloComponent
extends
javax
.
swing
.
JComponent
{...}
The statement import
javax.swing.*
enables us to refer to all the classes in the
javax.swing
package by their simple
names. So we don’t have to use fully qualified names to refer to the
JComponent
, JLabel
, and JFrame
classes.
As we saw when we added our second example class, there may be one
or more import
statements in a given
Java source file. The import
s
effectively create a “search path” that tells Java where to look for
classes that we refer to by their simple, unqualified names. (It’s not
really a path, but it avoids ambiguous names that can create errors.)
The import
s we’ve seen use the dot
star (.*
) notation to indicate that
the entire package should be imported. But you can also specify just a
single class. For example, our current example uses only the Graphics
class from the java.awt
package. So we
could have used import
java.awt.Graphics
instead of using the wildcard *
to import all the
Abstract Window Toolkit (AWT) package’s classes. However,
we are anticipating using several more classes from this package
later.
The java.
and javax.
package hierarchies are special. Any
package that begins with java.
is
part of the core Java API and is available on any platform that supports
Java. The javax.
package normally
denotes a standard extension to the core platform, which may or may not
be installed. However, in recent years, many standard extensions have
been added to the core Java API without renaming them. The javax.swing
package is an example; it is part
of the core API in spite of its name. Figure 2-7 illustrates some of the core Java
packages, showing a representative class or two from each.
java.lang
contains fundamental
classes needed by the Java language itself; this package is imported
automatically and that is why we didn’t need an import
statement to use class names such as
String
or System
in our examples. The java.awt
package contains classes of the
older, graphical Abstract Window Toolkit; java.net
contains the networking classes; and
so on.
As you gain more experience with Java, you will come to realize that having a command of the packages available to you, what they do, when to use them, and how to use them is a critical part of becoming a successful Java developer.
The source for our HelloComponent
class defines a method,
paintComponent()
, that overrides the
paintComponent()
method of the
JComponent
class:
public
void
paintComponent
(
Graphics
g
)
{
g
.
drawString
(
"Hello, Java!"
,
125
,
95
);
}
The paintComponent()
method is
called when it’s time for our example to draw itself on the screen. It
takes a single argument, a Graphics
object, and doesn’t return any type of value (void
) to its caller.
Modifiers are keywords placed before
classes, variables, and methods to alter their accessibility, behavior,
or semantics. paintComponent()
is
declared as public
, which means it
can be invoked (called) by methods in classes other than HelloComponent
. In this case, it’s the Java
windowing environment that is calling our paintComponent()
method. A method or variable
declared as private
is accessible
only from its own class.
The Graphics
object, an
instance of the Graphics
class,
represents a particular graphical drawing area. (It is also called a
graphics context.) It contains methods that can be
used to draw in this area, and variables that represent characteristics
such as clipping or drawing modes. The particular Graphics
object we are passed in the paintComponent()
method corresponds to our
HelloComponent
’s area of the screen,
inside our frame.
The Graphics
class provides
methods for rendering shapes, images, and text. In HelloComponent
, we invoke the drawString()
method of
our Graphics
object to scrawl our
message at the specified coordinates. (For a description of the methods
available in the Graphics
class, see
Chapter 20.)
As we’ve seen earlier, we access a method of an object by
appending a dot (.
) and its name to
the object that holds it. We invoked the drawString()
method of the Graphics
object (referenced by our g
variable) in this way:
g
.
drawString
(
"Hello, Java!"
,
125
,
95
);
It may be difficult to get used to the idea that our application is drawn by a method that is called by an outside agent at arbitrary times. How can we do anything useful with this? How do we control what gets done and when? These answers are forthcoming. For now, just think about how you would begin to structure applications that respond on command instead of by their own initiative.
Get Learning Java, 4th Edition now with the O’Reilly learning platform.
O’Reilly members experience books, live events, courses curated by job role, and more from O’Reilly and nearly 200 top publishers.