Previous Page TOC Index Next Page Home

Day 6

Creating Classes and Applications in Java

By Laura Lemay

In just about every lesson up to this point you've been creating Java applications—writing classes, creating instance variables and methods, and running those applications to perform simple tasks. Also up to this point, you've focused either on the very broad (general object-oriented theory) or the very minute (arithmetic and other expressions). Today, you pull it all together and learn how and why to create classes by using the following basics:

Defining Classes

Defining classes is pretty easy; you've seen how to do it a bunch of times in previous lessons. To define a class, use the class keyword and the name of the class:

class MyClassName {

If this class is a subclass of another class, use extends to indicate the superclass of this class:

class myClassName extends mySuperClassName {

If this class implements a specific interface, use implements to refer to that interface:

class MyRunnableClassName implements Runnable {

Both extends and implements are optional. You'll learn about using and defining interfaces in Week 3.

Creating Instance and Class Variables

A class definition with nothing in it is pretty dull; usually, when you create a class, you have something you want to add to make that class different from its superclasses. Inside each class definition are declarations and definitions for variables or methods or both 7Äfor the class and for each instance. In this section, you'll learn all about instance and class variables; the next section talks about methods.

Defining Instance Variables

On Day 3, you learned how to declare and initialize local variables—that is, variables inside method definitions. Instance variables, fortunately, are declared and defined in almost exactly the same way 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. Customarily, however, most instance variables are defined just after the first line of the class definition. For example, Listing 6.1 shows a simple class definition for the class Bicycle, which inherits from the class PersonPoweredVehicle. This class definition contains four instance variables:

1: class Bicycle extends PersonPoweredVehicle {
2:     String bikeType;
3:     int chainGear;
4:     int rearCogs;
5:     int currentGearFront;
6:     int currentGearRear;
7: }


Constants are useful for defining shared values for all the mothods of an object— for giving meaningful names to object-wide values that will never change. In Java, you can create constants only for instance or class variables, not for local variables.

A constant variable or constant is a variable whose value never changes (which may seem strange given the meaning of the word "variable").

To declare a constant, use the final keyword before the variable declaration and include an initial value for that variable:

final float pi = 3.141592;
final boolean debug = false;
final int maxsize = 40000;

Technical Note: The only way to define constants in Java is by using the final keyword. Neither the C and C++ constructs for #define nor const are available in Java, though const is reserved to help catch its accidental use.

Constants can be useful for naming various states of an object and then testing for those states. For example, suppose you have a test label that can be aligned left, right, or center. You can define those values as constant integers:

final int LEFT = 0;
final int RIGHT = 1;
final int CENTER = 2;

The variable alignment is then also declared as an int:

int alignment;

Then, later on in the body of a method definition, you can either set the alignment:

this.alignment = CENTER;

or test for a given alignment:

switch (this.alignment) {
    case LEFT: // deal with left alignment
    case RIGHT: // deal with right alignment
    case CENTER: // deal with center alignment

Class Variables

As you learned in previous lessons, class variables are global to a class and to all that class's instances. You can think of class variables as being even more global than instance variables. Class variables are good for communicating between different objects with the same class, or for keeping track of global states among a set of objects.

To declare a class variable, use the static keyword in the class declaration:

static int sum;
static final int maxObjects = 10;

Creating Methods

Methods, as you learned on Day 2, define an object's behavior—what happens when that object is created and the various operations that object can perform during its lifetime. In this section, you'll get a basic introduction to method definition and how methods work; tomorrow, you'll go into more detail about advanced things you can do with methods.

Defining Methods

Method definitions have four basic parts:

The method's signature is a combination of the name of the method, the type of object or base type this method returns, and a list of parameters.

Note: To keep things simple today, I've left off two optional parts of the method definition: a modifier such as public or private, and the throws keyword, which indicates the exceptions a method can throw. You'll learn about these parts of a method definition in Week 3.

In other languages, the name of the method (or function, subroutine, or procedure) is enough to distinguish it from other methods in the program. In Java, you can have different methods that have the same name but a different return type or argument list. This is called method overloading, and you'll learn more about it tomorrow.

Here's what a basic method definition looks like:

returntype methodname(type1 arg1, type2 arg2, type3 arg3..) {

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

Note that if this method returns an array object, the array brackets can go either after the return type or after the parameter list; because the former way is considerably easier to read, it is used in the examples today (and throughout this book):

int[] makeRange(int lower, int upper) {...}

The method's parameter list is a set of variable declarations, separated by commas, inside parentheses. These parameters become local variables in the body of the method, whose values are the objects or values of primitives passed in when the method is called.

Inside the body of the method you can have statements, expressions, method calls to other objects, conditionals, loops, and so on—everything you've learned about in the previous lessons.

If your method has a real return type (that is, it has not been declared to return void), somewhere inside the body of the method you need to return a value. Use the return keyword to do this. Listing 6.2 shows an example of a class that defines a makeRange() method. makeRange() takes two integers—a lower bound and an upper bound—and creates an array that contains all the integers between those two boundaries (inclusive).

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

Here's the output of this program:

The array: [ 1 2 3 4 5 6 7 8 9 10 ]

The main() method in this class tests the makeRange() method by creating a range where the lower and upper boundaries of the range are 1 and 10, respectively (see line 6), and then uses a for loop to print the values of the new array.

The this Keyword

In the body of a method definition, you may want to refer to the current object—the object the method was called on— to refer to that object's instance variables or to pass the current object as an argument to another method. To refer to the current object in these cases, you can use the this keyword. this refers to the current object, and you can use it anywhere that object might appear—in dot notation to refer to the object's instance variables, as an argument to a method, as the return value for the current method, and so on. Here's an example:

t = this.x          // the x instance variable for this object
this.myMethod(this) // call the mymethod method, defined in
                    // this class, and pass it the current
                    // object
return this;        // return the current object

In many cases, however, you may be able to omit the this keyword. You can refer to both instance variables and method calls defined in the current class simply by name; the this is implicit in those references. So, the first two examples could be written like this:

t = x          // the x instance variable for this object
myMethod(this) // call the myMethod method, defined in this
               // class

Note: Omitting the this keyword for instance variables depends on whether there are no variables of the same name declared in the local scope. See the next section for details.

Keep in mind that because this is a reference to the current instance of a class, therefore you should only use it inside the body of an instance method definition. Class methods, that is, methods declared with the static keyword, cannot use this.

Variable Scope and Method Definitions

When you refer to a variable within your method definitions, Java checks for a definition of that variable first in the current scope (which may be a block), then in the outer scopes up to the current method definition. If that variable is not a local variable, Java then checks for a definition of that variable as an instance or class variable in the current class, and then, finally, in each superclass in turn.

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 such that a definition of that same variable "hides" the original value of that variable. This can introduce subtle and confusing bugs into your code.

For example, note this small Java program:

class ScopeTest {
    int test = 10;
    void printTest () {
        int test = 20;
        System.out.println("test = " + test);

In this class, you have two variables with the same name and definition: the first, an instance variable, has the name test and is initialized to the value 10. The second is a local variable with the same name, but with the value 20. Because the local variable hides the instance variable, the println() method will print that test is 20.

You can get around this particular problem by using this.test to refer to the instance variable, and just test to refer to the local variable.

A more insidious example of this occurs when you redefine a variable in a subclass that already occurs in a superclass. This can create very subtle bugs in your code—for example, you may call methods that are intended to change the value of an instance variable, but that change the wrong one. Another bug might occur when you cast an object from one class to another—the value of your instance variable may mysteriously change (because it was getting that value from the superclass instead of from your class). The best way to avoid this behavior is to make sure that, when you define variables in a subclass, you're aware of the variables in each of that class's superclasses and you don't duplicate what is already there.

Passing Arguments to Methods

When you call a method with object parameters, the variables you pass into the body of the method are passed by reference, which means that whatever you do to those objects inside the method affects the original objects as well. This includes arrays and all the objects that arrays contain; when you pass an array into a method and modify its contents, the original array is affected. (Note that primitive types are passed by value.)

Here's an example to demonstrate how this works. First, you have a simple class definition, which includes a single method called OneToZero() (see Listing 6.3).

 1: class PassByReference {
 2:     int onetoZero(int arg[]) {
 3:         int count = 0;
 5:         for (int i = 0; i < arg.length; i++) {
 6:             if (arg[i] == 1) {
 7:                 count++;
 8:                 arg[i] = 0;
 9:             }
10:         }
11:         return count;
12:     }
13: }

The onetoZero() method does two things:

Listing 6.4 shows the main() method for the PassByReference class, which tests the onetoZero() method:

 1: public static void main (String arg[]) {
 2:     int arr[] = { 1, 3, 4, 5, 1, 1, 7 };
 3:     PassByReference test = new PassByReference();
 4:     int numOnes;
 6:     System.out.print("Values of the array: [ ");
 7:     for (int i = 0; i < arr.length; i++) {
 8:         System.out.print(arr[i] + " ");
 9:     }
10:     System.out.println("]");
12:     numOnes = test.onetoZero(arr);
13:     System.out.println("Number of Ones = " + numOnes);
14:     System.out.print("New values of the array: [ ");
15:     for (int i = 0; i < arr.length; i++) {
16:         System.out.print(arr[i] + " ");
17:     }
18:    System.out.println("]");
19: }

Here is the output of this program:

Values of the array: [ 1 3 4 5 1 1 7 ]
Number of Ones = 3
New values of the array: [ 0 3 4 5 0 0 7 ]

Let's go over the main() method line by line so that you can see what is going on.

Lines 2 through 4 set up the initial variables for this example. The first one is an array of integers; the second one is an instance of the class PassByReference, which is stored in the variable test. The third is a simple integer to hold the number of ones in the array.

Lines 6 through 11 print out the initial values of the array; you can see the output of these lines in the first line of the output.

Line 12 is where the real work takes place; this is where you call the onetoZero() method, defined in the object test, and pass it the array stored in arr. This method returns the number of ones in the array, which you'll then assign to the variable numOnes.

Got it so far? Line 13 prints out the number of ones, that is, the value you got back from the onetoZero() method. It returns three, as you would expect.

The last bunch of lines print out the array values. Because a reference to the array object is passed to the method, changing the array inside that method changes that original copy of the array. Printing out the values in lines 14 through 18 proves this—that last line of output shows that all the 1s in the array have been changed to 0s.

Class Methods

Just as you have class and instance variables, you also have class and instance methods, and the difference between the two types of methods are analogous. Class methods are available to any instance of the class itself and can be made available to other classes. Therefore, some class methods can be used anywhere regardless of whether an instance of the class exists or not.

For example, the Java class libraries include a class called Math. The Math class defines a whole set of math operations that can be used in any program or the various number types:

float root = Math.sqrt(453.0);
System.out.print("The larger of x and y is " + Math.max(x, y));

To define class methods, use the static keyword in front of the method definition, just as you would create a class variable. For example, that max class method might have a signature like this:

static int max(int arg1, int arg2) { ... }

Java supplies "wrapper" classes for each of the base types—for example, classes for Integer, Float, and Boolean. Using class methods defined in those classes, you can convert to and from objects and primitive types. For example, the parseInt() class method in the Integer class takes a string and a radix (base) and returns the value of that string as an integer:

int count = Integer.parseInt("42", 10) // returns 42

Most methods that operate on a particular object, or that affect that object, should be defined as instance methods. Methods that provide some general utility but do not directly affect an instance of that class are better declared as class methods.

Creating Java Applications

Now that you know how to create classes, objects, and class and instance variables and methods, all that's left is to put it together into something that can actually run—in other words, to create a Java application.

Applications, to refresh your memory, are Java programs that run on their own. Applications are different from applets, which require HotJava or a Java-capable browser to view them. Much of what you've been creating up to this point have been Java applications; next week you'll dive into how to create applets. (Applets require a bit more background in order to get them to interact with the browser and draw and update with the graphics system. You'll learn all of this next week.)

A Java application consists of one of more classes and can be as large or as small as you want it to be. HotJava is an example of a Java application. The only thing you need to make a Java application run is one class that serves as the "jumping-off" point for the rest of your Java program. If your program is small enough, it may need only the one class.

The jumping-off class for your program needs one thing: a main() method. When you run your compiled Java class (using the Java interpreter), the main() method is the first thing that gets called. None of this should be much of a surprise to you at this point; you've been creating Java applications with main() methods all along.

The signature for the main() method always looks like this:

public static void main(String args[]) {...}

Here's a run-down of the parts of the main() method:

The body of the main() method contains any code you need to get your application started: initializing variables or creating instances of any classes you may have declared.

When Java executes the main() method, keep in mind that main() is a class method—the class that holds it is not automatically instantiated when your program runs. If you want to treat that class as an object, you have to instantiate it in the main() method yourself (all the examples up to this point have done this).

Java Applications and Command-Line Arguments

Because Java applications are stand-alone programs, it's useful to be able to pass arguments or options to that program to determine how the program is going to run, or to enable a generic program to operate on many different kinds of input. Command-line arguments can be used for many different purposes—for example, to turn on debugging input, to indicate a filename to read or write from, or for any other information that you might want your Java program to know.

Passing Arguments to Java Programs

To pass arguments to a Java program, you merely append them to the command line when you run your Java program:

java Myprogram argumentOne 2 three

On this command line, you have three arguments: argumentOne, the number 2, and three. Note that a space separates arguments, so this command line produces three arguments:

java myprogram Java is cool

To group arguments, surround them with double-quotes. This command line produces one argument:

java myprogram "Java is cool"

The double-quotes are stripped off before the argument gets to your Java program.

Handling Arguments in Your Java Program

How does Java handle arguments? It stores them in an array of strings, which is passed to the main() method in your Java program. Remember the signature for main():

public static void main (String args[]) {...}

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

Inside your main() method, you can then handle the arguments your program was given by iterating over the array of arguments and handling those arguments any way you want. For example, Listing 6.5 is a really simple class that prints out the arguments it gets, one per line.

1: class EchoArgs {
2:     public static void main(String args[]) {
3:         for (int i = 0; i < args.length; i++) {
4:             System.out.println("Argument " + i + ": " + args[i]);
5:         }
6:     }
7: }

The following is some sample input and output from this program:

java EchoArgs 1 2 3 jump
Argument 0: 1
Argument 1: 2
Argument 2: 3
Argument 3: jump
java EchoArgs "foo bar" zap twaddle 5
Argument 0: foo bar
Argument 1: zap
Argument 2: twaddle
Argument 3: 5

Note how the arguments are grouped in the listing; putting quotes around foo bar causes that argument to be treated as one unit inside the argument array.

Technical Note: The array of arguments in Java is not analogous to argv in C and Unix. In particular, arg[0], the first element in the array of arguments, is the first command-line argument after the name of the class—not the name of the program as it would be in C. Be careful of this as you write your Java programs.

An important thing to note about the arguments you pass into a Java program is that those arguments will be stored in an array of strings. This means that any arguments you pass to your Java program are strings stored in the argument array. To treat them as non-strings, you'll have to convert them to whatever type you want them to be.

For example, suppose you have a very simple Java program called SumAverage that takes any number of numeric arguments and returns the sum and the average of those arguments. Listing 6.6 shows a first pass at this program.

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

At first glance, this program seems rather straightforward—a for loop iterates over the array of arguments, summing them, and then the sum and the average are printed out as the last step.

What happens when you try and compile this? You get the following error: Incompatible type for +=. Can't convert java.lang.String to int.
    sum += args[i];

You get this error because the argument array is an array of strings. Even though you passed integers into the program from the command line, those integers were converted to strings before they were stored in the array. To be able to sum those integers, you have to convert them back from strings to integers. There's a class method for the Integer class, called parseInt, that does just this. If you change line 7 to use that method, everything works just fine:

sum += Integer.parseInt(args[i]);

Now, compiling the program produces no errors and running it with various arguments returns the expected results. For example, java SumAverage 1 2 3 returns the following output:

Sum is: 6
Average is: 2


Today, you put together everything you've come across in the preceding days of this week about how to create Java classes and use them in Java applications. This included the following:


Q: I tried creating a constant variable inside a method, and I got a compiler error when I tried it. What was I doing wrong?

A: You can create only constant (final) class or instance variables; local variables cannot be constant.

Q: static and final are not exactly the most descriptive words for creating class variables, class methods, and constants. Why not use class and const?

A: static comes from Java's C++ heritage; C++ uses the static keyword to retain memory for class variables and methods (and, in fact, they aren't called class methods and variables in C++: static member functions and variables are more common terms).

final, however, is new. final is used in a more general way for classes and methods to indicate that those things cannot be subclassed or overridden. Using the final keyword for variables is consistent with that behavior. final variables are not quite the same as constant variables in C++, which is why the const keyword is not used.

Q: In my class, I have an instance variable called name. I also have a local variable called name in a method, which, because of variable scope, gets hidden by the local variable. Is there any way to get hold of the instance variable's value?

A: The easiest way is not to name your local variables the same names as your instance variables. If you feel you must, you can use to refer to the instance variable and name to refer to the local variable.

Q: I want to pass command-line arguments to an applet. How do I do this?

A: You're writing applets already? Been skipping ahead, have you? The answer is that you use HTML attributes to pass arguments to an applet, not the command line (you don't have a command line for applets). You'll learn how to do this next week.

Q: I wrote a program to take four arguments, but if I give it too few arguments, it crashes with a run-time error.

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, and return an error message if you haven't.

Previous Page TOC Index Next Page Home