HelloJava

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.

Running the HelloJava application

Figure 2-4. Running the HelloJava application

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.

The output of the HelloJava application

Figure 2-5. The output of the HelloJava application

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.

Classes

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.

The main() Method

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.

Classes and Objects

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.

Variables and Class Types

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.

HelloComponent

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.

Inheritance

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

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.

Relationships and Finger Pointing

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.

Part of the Java class hierarchy

Figure 2-6. Part of the Java class hierarchy

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.

Package and Imports

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 imports 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 imports 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.

Some core Java packages

Figure 2-7. Some core Java packages

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 paintComponent() Method

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.