Chapter 5. Creating Classes and Methods

If you’re coming to Java from another programming language, you might be struggling with the meaning of the term class. It seems synonymous to the term program, but you might be uncertain of the relationship between the two.

In Java, a program is made up of a main class and any other classes needed to support the main class. These support classes include any of those you might need in the Java class library, such as String, Math, and the like.

Today, the meaning of class is clarified as you create classes and methods, which define the behavior of an object or class. You undertake each of the following:

  • The definition of the parts of a class

  • The creation and use of instance variables

  • The creation and use of methods

  • The use of the main() method in Java applications

  • The creation of overloaded methods that share the same name but have different signatures and definitions

  • The creation of constructor methods that are called when an object is created

Defining Classes

Because you have created classes during each of the previous days, you should be familiar with the basics of their creation at this point. A class is defined via the class keyword and the name of the class, as in the following example:

class Ticker {
    // body of the class
}

By default, classes inherit from the Object class. It’s the superclass of all classes in the Java class hierarchy.

The extends keyword is used to indicate the superclass of a class, as in this example, which is defined as a subclass of Ticker:

class SportsTicker extends Ticker {
    // body of the class
}

Creating Instance and Class Variables

Whenever you create a class, you define behavior that makes the new class different from its superclass.

This behavior is defined by specifying the variables and methods of the new class. In this section, you work with three kinds of variables: class variables, instance variables, and local variables. The subsequent section covers methods.

Defining Instance Variables

On Day 2, “The ABCs of Programming,” you learned how to declare and initialize local variables, which are variables inside method definitions.

Instance variables are declared and defined in almost the same manner as local variables. The main difference is their location in the class definition.

Variables are considered instance variables if they are declared outside a method definition and are not modified by the static keyword.

By programming custom, most instance variables are defined right after the first line of the class definition, but they could just as permissibly be defined at the end.

Listing 5.1 contains a simple class definition for the class VolcanoRobot, which inherits from the superclass ScienceRobot.

Example 5.1. The Full Text of VolcanoRobot.java

 1: class VolcanoRobot extends ScienceRobot {
 2:
 3:     String status;
 4:     int speed;
 5:     float temperature;
 6:     int power;
 7: }

This class definition contains four variables. Because these variables are not defined inside a method, they are instance variables. The variables are as follows:

  • statusA string indicating the current activity of the robot (for example, “exploring” or “returning home”)

  • speedAn integer that indicates the robot’s current rate of travel

  • temperatureA floating-point number that indicates the current temperature of the robot’s environment

  • powerAn integer indicating the robot’s current battery power

Class Variables

As you learned in previous days, class variables apply to a class as a whole, rather than a particular object of that class.

Class variables are good for sharing information between different objects of the same class or for keeping track of classwide information among a set of objects.

The static keyword is used in the class declaration to declare a class variable, as in the following example:

static int sum;
static final int maxObjects = 10;

Creating Methods

As you learned on Day 3, “Working with Objects,” methods define an object’s behavior—that is, anything that happens when the object is created as well as the various tasks the object can perform during its lifetime.

This section introduces method definition and how methods work. Tomorrow’s lesson has more detail about more sophisticated things you can do with methods.

Defining Methods

In Java, a method definition has four basic parts:

  • The name of the method

  • A list of parameters

  • The type of object or primitive type returned by the method

  • The body of the method

The first two parts of the method definition form the method’s signature.

Note

To keep things simpler today, two optional parts of the method definition have been left out: a modifier, such as public or private, and the throws keyword, which indicates the exceptions a method can throw. You learn about these parts of method definition on Day 6, “Packages, Interfaces, and Other Class Features,” and Day 7, “Exceptions, Assertions, and Threads.”

In other languages, the name of the method (which might be called a function, subroutine, or procedure) is enough to distinguish it from other methods in the program.

In Java, you can have several methods in the same class with the same name but different signatures. This practice is called method overloading, and you learn more about it tomorrow.

Here’s what a basic method definition looks like:

returnType methodName(type1 arg1, type2 arg2, type3 arg3 ...) {
    // body of the method
}

The returnType is the primitive type or class of the value returned by the method. It can be one of the primitive types, a class name, or void if the method does not return a value at all.

The method’s parameter list is a set of variable declarations separated by commas and set inside parentheses. These parameters become local variables in the body of the method, receiving their values when the method is called.

Note that if this method returns an array object, the array brackets can go after either the returnType or the closing parenthesis of the parameter list. Because the former way is easier to read, it is used in this book’s examples as in the following, which declares a method that returns an integer array:

int[] makeRange(int lower, int upper) {
    // body of this method
}

You can have statements, expressions, method calls on other objects, conditionals, loops, and so on inside the body of the method.

Unless a method has been declared with void as its return type, the method returns some kind of value when it is completed. This value must be explicitly returned at some exit point inside the method, using the return keyword.

Listing 5.2 shows RangeLister, a class that defines a makeRange() method. This method takes two integers—a lower boundary and an upper boundary—and creates an array that contains all the integers between those two boundaries. The boundaries themselves are included in the array of integers.

Example 5.2. The Full Text of RangeLister.java

 1: class RangeLister {
 2:     int[] makeRange(int lower, int upper) {
 3:         int[] range = new int[(upper-lower) + 1];
 4:
 5:         for (int i = 0; i < range.length; i++) {
 6:             range[i] = lower++;
 7:         }
 8:         return range;
 9:     }
10:
11:     public static void main(String[] arguments) {
12:         int[] range;
13:         RangeLister lister = new RangeLister();
14:
15:         range = lister.makeRange(4, 13);
16:         System.out.print("The array: [ ");
17:         for (int i = 0; i < range.length; i++) {
18:             System.out.print(range[i] + " ");
19:         }
20:         System.out.println("]");
21:     }
22:
23: }

The output of the application is the following:

The array: [ 4 5 6 7 8 9 10 11 12 13 ]

The main() method in this class tests the makeRange() method by calling it with the arguments of 4 and 13. The method creates an empty integer array and uses a for loop to fill the new array with values from 4 through 13 in lines 5–7.

The this Keyword

In the body of a method definition, there are times you might need to refer to the object to which the method belongs. This can be done to use that object’s instance variables and to pass the current object as an argument to another method.

To refer to the object in these cases, use the this keyword where you normally would refer to an object’s name.

The this keyword refers to the current object, and you can use it anywhere a reference to an object might appear: in dot notation, as an argument to a method, as the return value for the current method, and so on. The following are some examples of using this:

t = this.x;           // the x instance variable for this object

this.resetData(this); // call the resetData method, defined in
                      // this class, and pass it the current
                      // object

return this;          // return the current object

In many cases, you might not need to explicitly use the this keyword because it is assumed. For instance, you can refer to both instance variables and method calls defined in the current class simply by name because the this is implicit in those references. Therefore, you could write the first two examples as the following:

t = x;           // the x instance variable for this object

resetData(this); // call the resetData method, defined in this
                 // class

Note

The viability of omitting the this keyword for instance variables depends on whether variables of the same name are declared in the local scope. You see more on this subject in the next section.

Because this is a reference to the current instance of a class, use it only inside the body of an instance method definition. Class methods—which are declared with the static keyword—cannot use this.

Variable Scope and Method Definitions

One thing you must know to use a variable is its scope.

Scope is the part of a program in which a variable or another type of information exists, making it possible to use the variable in statements and expressions. When the part defining the scope has completed execution, the variable ceases to exist.

When you declare a variable in Java, that variable always has a limited scope. A variable with local scope, for example, can be used only inside the block in which it was defined. Instance variables have a scope that extends to the entire class, so they can be used by any of the instance methods within that class.

When you refer to a variable, Java checks for its definition outward, starting with the innermost scope.

The innermost scope could be a block statement, such as the contents of a while loop. The next scope could be the method in which the block is contained.

If the variable hasn’t been found in the method, the class itself is checked.

Because of the way Java checks for the scope of a given variable, it is possible for you to create a variable in a lower scope that hides (or replaces) the original value of that variable and introduces subtle and confusing bugs into your code.

For example, consider the following Java application:

class ScopeTest {
    int test = 10;

    void printTest() {
        int test = 20;
        System.out.println("Test: " + test);
    }

    public static void main(String[] arguments) {
        ScopeTest st = new ScopeTest();
        st.printTest();
    }
}

In this class, you have two variables with the same name, test. The first, an instance variable, is initialized with the value 10. The second is a local variable with the value 20.

The local variable test within the printTest() method hides the instance variable test. When the printTest() method is called from within the main() method, it displays that test equals 20, even though there’s a test instance variable that equals 10. You can avoid this problem by using this.test to refer to the instance variable and simply using test to refer to the local variable, but a better solution might be to avoid the duplication of variable names and definitions.

A more insidious example occurs when you redefine a variable in a subclass that already occurs in a superclass. This can create subtle bugs in your code; for example, you might call methods that are intended to change the value of an instance variable, but the wrong variable is changed. Another bug might occur when you cast an object from one class to another; the value of your instance variable might mysteriously change because it was getting that value from the superclass instead of your class.

The best way to avoid this behavior is to be aware of the variables defined in the superclass of your class. This awareness prevents you from duplicating a variable used higher in the class hierarchy.

Passing Arguments to Methods

When you call a method with an object as a parameter, the object is passed into the body of the method by reference. Any change made to the object inside the method will persist outside of the method.

Keep in mind that such objects include arrays and all objects contained in arrays. When you pass an array into a method and modify its contents, the original array is affected. Primitive types, on the other hand, are passed by value.

The Passer class in Listing 5.3 demonstrates how this works.

Example 5.3. The Full Text of Passer.java

 1: class Passer {
 2:
 3:     void toUpperCase(String[] text) {
 4:         for (int i = 0; i < text.length; i++) {
 5:             text[i] = text[i].toUpperCase();
 6:         }
 7:     }
 8:
 9:     public static void main(String[] arguments) {
10:         Passer passer = new Passer();
11:         passer.toUpperCase(arguments);
12:         for (int i = 0; i < arguments.length; i++) {
13:             System.out.print(arguments[i] + " ");
14:        }
15:        System.out.println();
16:     }
17: }

This application takes one or more command-line arguments and displays them in all uppercase letters. Here’s an example of running the program and the resulting output:

java Passer Athos Aramis Porthos

ATHOS ARAMIS PORTHOS

The Passer application uses command-line arguments stored in the arguments array of strings.

The application creates a Passer object and calls its toUpperCase() method with the arguments array as an argument (lines 10–11).

Because a reference to the array object is passed to the method, changing the value of each array element in line 5 changes the actual element (rather than a copy of it). Displaying the array with lines 12–14 demonstrates this.

Class Methods

The relationship between class and instance variables is directly comparable to how class and instance methods work.

Class methods are available to any instance of the class itself and can be made available to other classes. In addition, unlike an instance method, a class does not require an instance of the class for its methods to be called.

For example, the Java class library includes the System class, which defines a set of methods that are useful when displaying text, retrieving configuration information, and accomplishing other tasks. Here are two statements that use its class methods:

System.exit(0);

int now = System.exit();

The exit(int) method closes an application with a status code that indicates success (0) or failure (any other value). The currentTimeMillis() method returns a long holding the number of milliseconds since midnight on Jan. 1, 1970, the numeric representation of the current date and time.

To define class methods, use the static keyword in front of the method definition as you would in front of a class variable. For example, the class method currentTimeMillis() in the preceding example might have the following signature:

static void exit(int arg1) {
    // body of the method
}

As you have learned, Java supplies wrapper classes such as Integer and Float for each of the primitive types. By using class methods defined in those classes, you can convert objects to primitive types and convert primitive types to objects.

For example, the parseInt() class method in the Integer class can be used with a string argument, returning an int representation of that string.

The following statement shows how the parseInt() method can be used:

int count = Integer.parseInt("42");

In the preceding statement, the String value “42” is returned by parseInt() as an integer with a value of 42, and this is stored in the count variable.

The lack of a static keyword in front of a method name makes it an instance method. Instance methods operate in a particular object, rather than a class of objects. On Day 1, “Getting Started with Java,” you created an instance method called checkTemperature() that checked the temperature in the robot’s environment.

Tip

Most methods that affect a particular object should be defined as instance methods. Methods that provide some general capability but do not directly affect an instance of the class should be declared as class methods.

Creating Java Applications

Now that you know how to create classes, objects, class and instance variables, and class and instance methods, you can put it all together in a Java program.

To refresh your memory, applications are Java classes that can be run on their own.

Note

Applications are different from applets, which are run by a Java-enabled browser as part of a web page. You can find out how to develop applets in “Writing Java Applets,” a bonus chapter included on this book’s CD.

A Java application consists of one or more classes and can be as large or as small as you want it to be. Although all the applications you’ve created up to this point do nothing but output some characters to the screen, you also can create Java applications that use windows, graphics, and a graphical user interface.

The only thing you need to make a Java application run, however, is one class that serves as the starting point.

The class needs only one thing: a main() method. When the application is run, the main() method is the first thing called.

The signature for the main() method takes the following form:

public static void main(String[] arguments) {
    // body of method
}

Here’s a rundown of the parts of the main() method:

  • public means that this method is available to other classes and objects, which is a form of access control. The main() method must be declared public. You learn more about access methods during Day 6.

  • static means that main() is a class method.

  • void means that the main() method doesn’t return a value.

  • main() takes one parameter, which is an array of strings. This argument holds command-line arguments, which you learn more about in the next section.

The body of the main() method contains any code you need to start your application, such as the initialization of variables or the creation of class instances.

When Java executes the main() method, keep in mind that main() is a class method. An instance of the class that holds main() is not created automatically when your program runs. If you want to treat that class as an object, you have to create an instance of it in the main() method (as you did in the Passer and RangeLister applications).

Helper Classes

Your Java application may consist of a single class—the one with the main() method—or several classes that use each other. (In reality, even a simple tutorial program is actually using numerous classes in the Java class library.) You can create as many classes as you want for your program.

Note

If you’re using the JDK, the classes can be found if they are accessible from a folder listed in your Classpath environment variable.

As long as Java can find the class, your program uses it when it runs. Note, however, that only the starting-point class needs a main() method. After it is called, the methods inside the various classes and objects used in your program take over. Although you can include main() methods in helper classes, they are ignored when the program runs.

Java Applications and Command-line Arguments

Because Java applications are standalone programs, it’s useful to pass arguments or options to an application.

You can use arguments to determine how an application is going to run or to enable a generic application to operate on different kinds of input. You can use program arguments for many different purposes, such as to turn on debugging input or to indicate a filename to load.

Passing Arguments to Java Applications

How you pass arguments to a Java application varies based on the computer and virtual machine on which Java is being run.

To pass arguments to a Java program with the java interpreter included with the JDK, the arguments should be appended to the command line when the program is run. For example:

java EchoArgs April 450 -10

In the preceding example, three arguments were passed to a program: April, 450, and -10. Note that a space separates each of the arguments.

To group arguments that include spaces, the arguments should be surrounded with quotation marks. For example, note the following command line:

java EchoArgs Wilhelm Niekro Hough "Tim Wakefield" 49

Putting quotation marks around Tim Wakefield causes that text to be treated as a single argument. The EchoArgs application would receive five arguments: Wilhelm, Niekro, Hough, Tim Wakefield, and 49. The quotation marks prevent the spaces from being used to separate one argument from another; they are not included as part of the argument when it is sent to the program and received using the main() method.

Caution

One thing the quotation marks are not used for is to identify strings. Every argument passed to an application is stored in an array of String objects, even if it has a numeric value (such as 450, -10, and 49 in the preceding examples).

Handling Arguments in Your Java Application

When an application is run with arguments, Java stores the arguments as an array of strings and passes the array to the application’s main() method. Take another look at the signature for main():

public static void main(String[] arguments) {
    // body of method
}

Here, arguments is the name of the array of strings that contains the list of arguments. You can call this array anything you want.

Inside the main() method, you then can handle the arguments your program was given by iterating over the array of arguments and handling them in some manner. For example, Listing 5.4 is a simple Java program that takes any number of numeric arguments and returns the sum and the average of those arguments.

Example 5.4. The Full Text of Averager.java

 1: class Averager {
 2:     public static void main(String[] arguments) {
 3:         int sum = 0;
 4:
 5:         if (arguments.length > 0) {
 6:             for (int i = 0; i < arguments.length; i++) {
 7:                 sum += Integer.parseInt(arguments[i]);
 8:             }
 9:             System.out.println("Sum is: " + sum);
10:             System.out.println("Average is: " +
11:                 (float)sum / arguments.length);
12:         }
13:     }
14: }

The Averager application makes sure that in line 5 at least one argument was passed to the program. This is handled through length, the instance variable that contains the number of elements in the arguments array.

You must always do things like this when dealing with command-line arguments. Otherwise, your programs crash with ArrayIndexOutOfBoundsException errors whenever the user supplies fewer command-line arguments than you were expecting.

If at least one argument is passed, the for loop iterates through all the strings stored in the arguments array (lines 6–8).

Because all command-line arguments are passed to a Java application as String objects, you must convert them to numeric values before using them in any mathematical expressions. The parseInt() class method of the Integer class takes a String object as input and returns an int (line 7).

If you can run Java classes on your system with a command line, type the following:

java Averager 1 4 13

You should see the following output:

Sum is: 18
Average is: 6.0

Creating Methods with the Same Name, Different Arguments

When you work with Java’s class library, you often encounter classes that have numerous methods with the same name.

Two things differentiate methods with the same name:

  • The number of arguments they take

  • The data type or objects of each argument

These two things are part of a method’s signature. Using several methods with the same name and different signatures is called overloading.

Method overloading can eliminate the need for entirely different methods that do essentially the same thing. Overloading also makes it possible for methods to behave differently based on the arguments they receive.

When you call a method in an object, Java matches the method name and arguments to choose which method definition to execute.

To create an overloaded method, you create different method definitions in a class, each with the same name but different argument lists. The difference can be the number, the type of arguments, or both. Java allows method overloading as long as each argument list is unique for the same method name.

Caution

Java does not consider the return type when differentiating among overloaded methods. If you attempt to create two methods with the same signature and different return types, the class won’t compile. In addition, the variable names that you choose for each argument to the method are irrelevant. The number and the type of arguments are the two things that matter.

The next project creates an overloaded method. It begins with a simple class definition for a class called Box, which defines a rectangular shape with four instance variables to define the upper-left and lower-right corners of the rectangle, x1, y1, x2, and y2:

class Box {
    int x1 = 0;
    int y1 = 0;
    int x2 = 0;
    int y2 = 0;
}

When a new instance of the Box class is created, all its instance variables are initialized to 0.

A buildBox() instance method sets the variables to their correct values:

Box buildBox(int x1, int y1, int x2, int y2) {
    this.x1 = x1;
    this.y1 = y1;
    this.x2 = x2;
    this.y2 = y2;
    return this;
}

This method takes four integer arguments and returns a reference to the resulting Box object. Because the arguments have the same names as the instance variables, the keyword this is used inside the method when referring to the instance variables.

This method can be used to create rectangles, but what if you wanted to define a rectangle’s dimensions in a different way? An alternative would be to use Point objects rather than individual coordinates because Point objects contain both an x and y value as instance variables.

You can overload buildBox() by creating a second version of the method with an argument list that takes two Point objects:

Box buildBox(Point topLeft, Point bottomRight) {
    x1 = topLeft.x;
    y1 = topLeft.y;
    x2 = bottomRight.x;
    y2 = bottomRight.y;
    return this;
}

For the preceding method to work, the java.awt.Point class must be imported so that the Java compiler can find it.

Another possible way to define the rectangle is to use a top corner, a height, and a width:

Box buildBox(Point topLeft, int w, int h) {
    x1 = topLeft.x;
    y1 = topLeft.y;
    x2 = (x1 + w);
    y2 = (y1 + h);
    return this;
}

To finish this example, a printBox() is created to display the rectangle’s coordinates, and a main() method tries everything out. Listing 5.5 shows the completed class definition.

Example 5.5. The Full Text of Box.java

 1: import java.awt.Point;
 2:
 3: class Box {
 4:     int x1 = 0;
 5:     int y1 = 0;
 6:     int x2 = 0;
 7:     int y2 = 0;
 8:
 9:     Box buildBox(int x1, int y1, int x2, int y2) {
10:         this.x1 = x1;
11:         this.y1 = y1;
12:         this.x2 = x2;
13:         this.y2 = y2;
14:         return this;
15:     }
16:
17:     Box buildBox(Point topLeft, Point bottomRight) {
18:         x1 = topLeft.x;
19:         y1 = topLeft.y;
20:         x2 = bottomRight.x;
21:         y2 = bottomRight.y;
22:         return this;
23:     }
24:
25:     Box buildBox(Point topLeft, int w, int h) {
26:         x1 = topLeft.x;
27:         y1 = topLeft.y;
28:         x2 = (x1 + w);
29:         y2 = (y1 + h);
30:         return this;
31:     }
32:
33:     void printBox(){
34:         System.out.print("Box: <" + x1 + ", " + y1);
35:         System.out.println(", " + x2 + ", " + y2 + ">");
36:     }
37:
38:     public static void main(String[] arguments) {
39:         Box rect = new Box();
40:
41:         System.out.println("Calling buildBox with coordinates "
42:             + "(25,25) and (50,50):");
43:         rect.buildBox(25, 25, 50, 50);
44:         rect.printBox();
45:
46:         System.out.println("
Calling buildBox with points "
47:             + "(10,10) and (20,20):");
48:         rect.buildBox(new Point(10, 10), new Point(20, 20));
49:         rect.printBox();
50:
51:         System.out.println("
Calling buildBox with 1 point "
52:             + "(10,10), width 50 and height 50:");
53:
54:         rect.buildBox(new Point(10, 10), 50, 50);
55:         rect.printBox();
56:    }
57: }

The following is this program’s output:

Calling buildBox with coordinates (25,25) and (50,50):
Box: <25, 25, 50, 50>

Calling buildBox with points (10,10) and (20,20):
Box: <10, 10, 20, 20>

Calling buildBox with 1 point (10,10), width 50 and height 50:
Box: <10, 10, 60, 60>

You can define as many versions of a method as you need to implement the behavior needed for that class.

When you have several methods that do similar things, using one method to call another is a shortcut technique to consider. For example, the buildBox() method in lines 17–23 can be replaced with the following, much shorter method:

Box buildBox(Point topLeft, Point bottomRight) {
    return buildBox(topLeft.x, topLeft.y,
        bottomRight.x, bottomRight.y);
}

The return statement in this method calls the buildBox() method in lines 9–15 with four integer arguments, producing the same result in fewer statements.

Constructor Methods

You also can define constructor methods in your class definition that are called automatically when objects of that class are created.

A constructor method is a method called on an object when it is created—in other words, when it is constructed.

Unlike other methods, a constructor cannot be called directly. Java does three things when new is used to create an instance of a class:

  • Allocates memory for the object

  • Initializes that object’s instance variables, either to initial values or to a default (0 for numbers, null for objects, false for Booleans, or '' for characters)

  • Calls the constructor method of the class, which might be one of several methods

If a class doesn’t have any constructor methods defined, an object still is created when the new operator is used in conjunction with the class. However, you might have to set its instance variables or call other methods that the object needs to initialize itself.

By defining constructor methods in your own classes, you can set initial values of instance variables, call methods based on those variables, call methods on other objects, and set the initial properties of an object.

You also can overload constructor methods, as you can do with regular methods, to create an object that has specific properties based on the arguments you give to new.

Basic Constructor Methods

Constructors look a lot like regular methods, with three basic differences:

  • They always have the same name as the class.

  • They don’t have a return type.

  • They cannot return a value in the method by using the return statement.

For example, the following class uses a constructor method to initialize its instance variables based on arguments for new:

class VolcanoRobot {
    String status;
    int speed;
    int power;

    VolcanoRobot(String in1, int in2, int in3) {
        status = in1;
        speed = in2;
        power = in3;
    }
}

You could create an object of this class with the following statement:

VolcanoRobot vic = new VolcanoRobot("exploring", 5, 200);

The status instance variable would be set to exploring, speed to 5, and power to 200.

Calling Another Constructor Method

If you have a constructor method that duplicates some of the behavior of an existing constructor method, you can call the first constructor from inside the body of the second constructor. Java provides a special syntax for doing this. Use the following code to call a constructor method defined in the current class:

this(arg1, arg2, arg3);

The use of this with a constructor method is similar to how this can be used to access a current object’s variables. In the preceding statement, the arguments with this() are the arguments for the constructor method.

For example, consider a simple class that defines a circle using the (x,y) coordinate of its center and the length of its radius. The class, Circle, could have two constructors: one where the radius is defined and one where the radius is set to a default value of 1:

class Circle {
    int x, y, radius;

    Circle(int xPoint, int yPoint, int radiusLength) {
        this.x = xPoint;
        this.y = yPoint;
        this.radius = radiusLength;
    }

    Circle(int xPoint, int yPoint) {
        this(xPoint, yPoint, 1);
    }
}

The second constructor in Circle takes only the x and y coordinates of the circle’s center. Because no radius is defined, the default value of 1 is used—the first constructor is called with the arguments xPoint, yPoint, and the integer literal 1.

Overloading Constructor Methods

Like regular methods, constructor methods also can take varying numbers and types of parameters. This capability enables you to create an object with exactly the properties you want it to have or lets the object calculate properties from different kinds of input.

For example, the buildBox() methods that you defined in the Box class earlier today would make excellent constructor methods because they are being used to initialize an object’s instance variables to the appropriate values. So instead of the original buildBox() method that you defined (which took four parameters for the coordinates of the corners), you could create a constructor.

Listing 5.6 shows a new class, Box2, that has the same functionality of the original Box class, except that it uses overloaded constructor methods instead of overloaded buildBox() methods.

Example 5.6. The Full Text of Box2.java

 1: import java.awt.Point;
 2:
 3: class Box2 {
 4:     int x1 = 0;
 5:     int y1 = 0;
 6:     int x2 = 0;
 7:     int y2 = 0;
 8:
 9:     Box2(int x1, int y1, int x2, int y2) {
10:         this.x1 = x1;
11:         this.y1 = y1;
12:         this.x2 = x2;
13:         this.y2 = y2;
14:     }
15:
16:     Box2(Point topLeft, Point bottomRight) {
17:         x1 = topLeft.x;
18:         y1 = topLeft.y;
19:         x2 = bottomRight.x;
20:         y2 = bottomRight.y;
21:     }
22:
23:     Box2(Point topLeft, int w, int h) {
24:         x1 = topLeft.x;
25:         y1 = topLeft.y;
26:         x2 = (x1 + w);
27:         y2 = (y1 + h);
28:     }
29:
30:     void printBox() {
31:         System.out.print("Box: <" + x1 + ", " + y1);
32:         System.out.println(", " + x2 + ", " + y2 + ">");
33:     }
34:
35:     public static void main(String[] arguments) {
36:         Box2 rect;
37:
38:         System.out.println("Calling Box2 with coordinates "
39:             + "(25,25) and (50,50):");
40:         rect = new Box2(25, 25, 50, 50);
41:         rect.printBox();
42:
43:         System.out.println("
Calling Box2 with points "
44:             + "(10,10) and (20,20):");
45:         rect= new Box2(new Point(10, 10), new Point(20, 20));
46:         rect.printBox();
47:
48:         System.out.println("
Calling Box2 with 1 point "
49:             + "(10,10), width 50 and height 50:");
50:         rect = new Box2(new Point(10, 10), 50, 50);
51:         rect.printBox();
52:
53:      }
54: }

This application produces the same output as the Box application in Listing 5.5.

Overriding Methods

When you call an object’s method, Java looks for that method definition in the object’s class. If it doesn’t find one, it passes the method call up the class hierarchy until a method definition is found. Method inheritance enables you to define and use methods repeatedly in subclasses without having to duplicate the code.

However, there might be times when you want an object to respond to the same methods but have different behavior when that method is called. In that case, you can override the method. To override a method, define a method in a subclass with the same signature as a method in a superclass. Then, when the method is called, the subclass method is found and executed instead of the one in the superclass.

Creating Methods That Override Existing Methods

To override a method, all you have to do is create a method in your subclass that has the same signature (name and argument list) as a method defined by your class’s superclass. Because Java executes the first method definition it finds that matches the signature, the new signature hides the original method definition.

Here’s a simple example; Listing 5.7 contains two classes: Printer, which contains a method called printMe() that displays information about objects of that class, and SubPrinter, a subclass that adds a z instance variable to the class.

Example 5.7. The Full Text of Printer.java

 1: class Printer {
 2:     int x = 0;
 3:     int y = 1;
 4:
 5:     void printMe() {
 6:         System.out.println("x is " + x + ", y is " + y);
 7:         System.out.println("I am an instance of the class " +
 8:         this.getClass().getName());
 9:     }
10: }
11:
12: class SubPrinter extends Printer {
13:     int z = 3;
14:
15:     public static void main(String[] arguments) {
16:         SubPrinter obj = new SubPrinter();
17:         obj.printMe();
18:      }
19: }

Compiling this file produces two class files rather than one, as you might expect from previous projects. Because the source file defines the Printer and SubPrinter classes, both are produced by the compiler. Run SubPrinter with the Java interpreter to see the following output:

x is 0, y is 1
I am an instance of the class SubPrinter

Caution

Make sure that you run SubPrinter with the interpreter rather than Printer. The Printer class does not have a main() method, so it cannot be run as an application.

A SubPrinter object was created, and the printMe() method was called in the main() method of SubPrinter. Because the SubPrinter does not define this method, Java looks for it in the superclasses of SubPrinter, starting with Printer. Printer has a printMe() method, so it is executed. Unfortunately, this method does not display the z instance variable, as you can see from the preceding output.

To correct the problem, you could override the printMe() method of Printer in SubPrinter, adding a statement to display the z instance variable:

void printMe() {
    System.out.println("x is " + x + ", y is " + y +
        ", z is " + z);
    System.out.println("I am an instance of the class " +
        this.getClass().getName());
}

Calling the Original Method

Usually, there are two reasons why you want to override a method that a superclass already has implemented:

  • To replace the definition of that original method completely

  • To augment the original method with additional behavior

Overriding a method and giving the method a new definition hides the original method definition. There are times, however, when behavior should be added to the original definition instead of replacing it completely, particularly when behavior is duplicated in both the original method and the method that overrides it. By calling the original method in the body of the overriding method, you can add only what you need.

Use the super keyword to call the original method from inside a method definition. This keyword passes the method call up the hierarchy, as shown in the following:

void doMethod(String a, String b) {
    // do stuff here
    super.doMethod(a, b);
    // do more stuff here
}

The super keyword, similar to the this keyword, is a placeholder for the class’s superclass. You can use it anywhere that you use this, but super refers to the superclass rather than the current object.

Overriding Constructors

Technically, constructor methods cannot be overridden. Because they always have the same name as the current class, new constructor methods are created instead of being inherited. This system is fine much of the time; when your class’s constructor method is called, the constructor method with the same signature for all your superclasses also is called. Therefore, initialization can happen for all parts of a class that you inherit.

However, when you are defining constructor methods for your own class, you might want to change how your object is initialized, not only by initializing new variables added by your class, but also by changing the contents of variables that are already there. To do this, explicitly call the constructor methods of the superclass and subsequently change whatever variables need to be changed.

To call a regular method in a superclass, you use super.methodname(arguments). Because constructor methods don’t have a method name to call, the following form is used:

super(arg1, arg2, ...);

Note that Java has a specific rule for the use of super(): It must be the first statement in your constructor definition. If you don’t call super() explicitly in your constructor, Java does it for you—automatically calling super() with no arguments before the first statement in the constructor.

Because a call to a super() method must be the first statement, you can’t do something like the following in your overriding constructor:

if (condition == true)
    super(1,2,3); // call one superclass constructor
else
    super(1,2); // call a different constructor

Similar to using this() in a constructor method, super() calls the constructor method for the immediate superclass (which might, in turn, call the constructor of its superclass, and so on). Note that a constructor with that signature has to exist in the superclass for the call to super() to work. The Java compiler checks this when you try to compile the source file.

You don’t have to call the constructor in your superclass that has the same signature as the constructor in your class; you have to call the constructor only for the values you need initialized. In fact, you can create a class that has constructors with entirely different signatures from any of the superclass’s constructors.

Listing 5.8 shows a class called NamedPoint, which extends the class Point from the java.awt package. The Point class has only one constructor, which takes an x and a y argument and returns a Point object. NamedPoint has an additional instance variable (a string for the name) and defines a constructor to initialize x, y, and the name.

Example 5.8. The NamedPoint Class

 1: import java.awt.Point;
 2:
 3: class NamedPoint extends Point {
 4:     String name;
 5:
 6:     NamedPoint(int x, int y, String name) {
 7:         super(x,y);
 8:         this.name = name;
 9:     }
10:
11:     public static void main(String[] arguments) {
12:         NamedPoint np = new NamedPoint(5, 5, "SmallPoint");
13:         System.out.println("x is " + np.x);
14:         System.out.println("y is " + np.y);
15:         System.out.println("Name is " + np.name);
16:     }
17: }

The output of the program is as follows:

x is 5
y is 5
Name is SmallPoint

The constructor method defined here for NamedPoint calls Point’s constructor method to initialize the instance variables of Point (x and y). Although you can just as easily initialize x and y yourself, you might not know what other things Point is doing to initialize itself. Therefore, it is always a good idea to pass constructor methods up the hierarchy to make sure that everything is set up correctly.

Finalizer Methods

Finalizer methods are almost the opposite of constructor methods. A constructor method is used to initialize an object, and finalizer methods are called just before the object is removed by the garbage collector, freeing up the memory for use.

The finalizer method is finalize(). The Object class defines a default finalizer method that does nothing. To create a finalizer method for your own classes, override the finalize() method using this signature:

protected void finalize() throws Throwable {
    super.finalize();
}

Note

The throws Throwable part of this method definition refers to the errors that might occur when this method is called. Errors in Java are called exceptions; you learn more about them on Day 7.

Include any cleaning up that you want to do for that object inside the body of that finalize() method. In the method, you always should call super.finalize() to enable your class’s superclasses to finalize the object.

You can call the finalize() method yourself at any time; it’s a method just like any other. However, calling finalize() does not trigger an object to be garbage collected. Only removing all references to an object causes it to be marked for deletion.

When you’re optimizing a Java class, one of the ways to reduce its memory use is to remove references to class and instance variables as soon as they are no longer needed. To remove a reference, set it to null.

For example, if you have a class that uses a NamedPoint object in a variable called mainPoint, you could free up that object for garbage collection with the following statement:

mainPoint = null;

Finalizer methods are valuable for optimizing the removal of an object—for example, by removing references to other objects. However, it’s important to note that the time a garbage collector takes to call an object’s finalize() method is not standard in all implementations of the Java interpreter. This could take place long after the last reference to the object was removed. In most cases, you don’t need to use finalize() at all.

Summary

After finishing today’s lesson, you should have a pretty good idea of the relationship among classes in Java and programs you create using the language.

Everything you create in Java involves the use of a main class that interacts with other classes as needed. It’s a different programming mindset than you might be used to with other languages.

Today, you put together everything you have learned about creating Java classes. Each of the following topics was covered:

  • Instance and class variables, which hold the attributes of a class and objects created from it.

  • Instance and class methods, which define the behavior of a class. You learned how to define methods—including the parts of a method signature, how to return values from a method, how arguments are passed to methods, and how to use the this keyword to refer to the current object.

  • The main() method of Java applications and how to pass arguments to it from the command line.

  • Overloaded methods, which reuse a method name by giving it different arguments.

  • Constructor methods, which define the initial variables and other starting conditions of an object.

Q&A

Q

In my class, I have an instance variable called origin. I also have a local variable called origin in a method, which, because of variable scope, gets hidden by the local variable. Is there any way to access the instance variable’s value?

A

The easiest way to avoid this problem is not to give your local variables the same names that your instance variables have. Otherwise, you can use this.origin to refer to the instance variable and origin to refer to the local variable.

Q

I created two methods with the following signatures:

int total(int arg1, int arg2, int arg3) {...}
float total(int arg1, int arg2, int arg3) {...}

The Java compiler complains when I try to compile the class with these method definitions, but their signatures are different. What have I done wrong?

A

Method overloading in Java works only if the parameter lists are different—either in number or type of arguments. Return type is not part of a method signature, so it’s not considered when methods have been overloaded. Looking at it from the point at which a method is called, this makes sense: If two methods have exactly the same parameter list, how would Java know which one to call?

Q

I wrote a program to take four arguments, but when I give it too few arguments, it crashes with a runtime error. Why?

A

Testing for the number and type of arguments your program expects is up to you in your Java program; Java won’t do it for you. If your program requires four arguments, test that you have indeed been given four arguments by using the length variable of an array and return an error message if you haven’t.

Quiz

Review today’s material by taking this three-question quiz.

Questions

1.

If a local variable has the same name as an instance variable, how can you refer to the instance variable in the scope of the local variable?

  1. You can’t; you should rename one of the variables.

  2. Use the keyword this before the instance variable name.

  3. Use the keyword super before the name.

2.

Where are instance variables declared in a class?

  1. Anywhere in the class

  2. Outside all methods in the class

  3. After the class declaration and above the first method

3.

How can you send an argument to a program that includes a space character?

  1. Surround it with quotes.

  2. Separate the arguments with commas.

  3. Separate the arguments with period characters.

Answers

1.

b. Answer (a.) is a good idea, though variable name conflicts can be a source of subtle errors in your Java programs.

2.

b. Customarily, instance variables are declared right after the class declaration and before any methods. It’s necessary only that they be outside all methods.

3.

a. The quotation marks are not included in the argument when it is passed to the program.

Certification Practice

The following question is the kind of thing you could expect to be asked on a Java programming certification test. Answer it without looking at today’s material or using the Java compiler to test the code.

Given:

public class BigValue {
    float result;

    public BigValue(int a, int b) {
        result = calculateResult(a, b);
    }

    float calculateResult(int a, int b) {
        return (a * 10) + (b * 2);
    }

    public static void main(String[] arguments) {
        BiggerValue bgr = new BiggerValue(2, 3, 4);
        System.out.println("The result is " + bgr.result);
    }
}

class BiggerValue extends BigValue {

    BiggerValue(int a, int b, int c) {
        super(a, b);
        result = calculateResult(a, b, c);
    }

   // answer goes here
       return (c * 3) * result;
   }
}

What statement should replace // answer goes here so that the result variable equals 312.0?

  1. float calculateResult(int c) {

  2. float calculateResult(int a, int b) {

  3. float calculateResult(int a, int b, int c) {

  4. float calculateResult() {

The answer is available on the book’s website at http://www.java21days.com. Visit the Day 5 page and click the Certification Practice link.

Exercises

To extend your knowledge of the subjects covered today, try the following exercises:

  1. Modify the VolcanoRobot project from Day 1 so that it includes constructor methods.

  2. Create a class for four-dimensional points called FourDPoint that is a subclass of Point from the java.awt package.

Where applicable, exercise solutions are offered on the book’s website at http://www.java21days.com.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset