Creating Classes and Applications in Java

  • Creating Classes and Applications in Java
    • Defining Classes
    • Creating Instance and Class Variables
      • Defining Instance Variables
      • Constants
      • Class Variables
  • Creating Methods
    • Defining Methods
    • The this Keyword
    • Variable Scope and Method Definitions
    • Passing Arguments to Methods
    • Class Methods
  • Creating Java Applications
  • Java Applications and Command-Line Arguments
    • Passing Arguments to Java Programs
    • Handling Arguments in Your Java Program
  • Summary
  • Q&A


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:
  • The parts of a class definition
  • Declaring and using instance variables
  • Defining and using methods
  • Creating Java applications, including the main() method and how to pass arguments to a Java program from a command line

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:
  • bikeType: the kind of bicycle this bicycle is—for example, Mountain or Street
  • chainGear, the number of gears in the front
  • rearCogs, the number of minor gears on the rear axle
  • currentGearFront and currentGearRear: the gears the bike is currently in, both front and rear
  • Listing 6.1. The bicycle class.
1: class Bicycle extends PersonPoweredVehicle {

2:     String bikeType;

3:     int chainGear;

4:     int rearCogs;

5:     int currentGearFront;

6:     int currentGearRear;

7: }

Constants

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

               ...

               break;

    case RIGHT: // deal with right alignment

                ...

                break;

    case CENTER: // deal with center alignment

                 ...

                 break;

}

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 name of the method
  • The type of object or primitive type the method returns
  • A list of parameters
  • The body of the method
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).
    Listing 6.2. The RangeClass class.
 1: class RangeClass {

 2:     int[] makeRange(int lower, int upper) {

 3:         int arr[] = new int[ (upper - lower) + 1 ];

4: 

 5:         for (int i = 0; i < arr.length; i++) {

 6:             arr[i] = lower++;

 7:         }

 8:         return arr;

 9:     }

10:         

11:     public static void main(String arg[]) {

12:         int theArray[];

13:         RangeClass theRange = new RangeClass();

14: 

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:     }

22: 

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).
    Listing 6.3. The PassByReference class.
 1: class PassByReference {

 2:     int onetoZero(int arg[]) {

 3:         int count = 0;

 4: 

 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:
  • It counts the number of ones in the array and returns that value.
  • If it finds a one, it substitutes a zero in its place in the array.
Listing 6.4 shows the main() method for the PassByReference class, which tests the onetoZero() method:
    Listing 6.4. The main() method in PassByReference.
 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;

 5:     

 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("]");

11:     

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:
  • public means that this method is available to other classes and objects. The main() method must be declared public. You'll learn more about public and private methods in Week 3.
  • static means that this is a class method.
  • void means the main() method doesn't return anything.
  • main() takes one parameter: an array of strings. This argument is used for command-line arguments, which you'll learn about in the next section.
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.
    Listing 6.5. The EchoArgs class.
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.
    Listing 6.6. First try at the SumAverage class.
 1: class SumAverage {

 2:     public static void main (String args[]) {

 3:         int sum = 0;

 4: 

 5:         for (int i = 0; i < args.length; i++) {

 6:             sum += args[i];

 7:         }

 8: 

 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:
SumAverage.java:9: 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

Summary

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:
  • Instance and class variables, which hold the attributes of the class and its instances. You learned how to declare them, how they are different from regular local variables, and how to declare constants.
  • Instance and class methods, which define a class's behavior. You learned how to define methods, including the parts of a method's signature, how to return values from a method, how arguments are passed in and out of methods, and how to use the this keyword to refer to the current object
  • Java applications—all about the main() method and how it works, as well as how to pass arguments into a Java application from a command line.

Q&A

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 this.name 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

Arrays, Conditionals, and Loops

  • Arrays, Conditionals, and Loops
    • Arrays
      • Declaring Array Variables
      • Creating Array Objects
      • Accessing Array Elements
      • Changing Array Elements
      • Multidimensional Arrays
    • Block Statements
    • if Conditionals
      • The Conditional Operator
    • switch Conditionals
    • for Loops
    • while and do Loops
      • while Loops
      • do...while Loops
    • Breaking Out of Loops
      • Labeled Loops
    • Summary
    • Q&A


Arrays, Conditionals, and Loops

by Laura Lemay
Although you could write Java programs using what you've learned so far, those programs would be pretty dull. Much of the good stuff in Java or in any programming language results when you have arrays to store values in and control-flow constructs (loops and conditionals) to execute different bits of a program based on tests. Today, you'll find out about the following:

  • Arrays, one of the most useful objects in Java, which enable you to collect objects or prmitive types into an easy-to-manage list
  • Block statements, for grouping together related statements
  • if and switch, for conditional tests
  • for and while loops, for iteration or repeating a statement or statements multiple times

Arrays

Arrays in Java are different than they are in other languages. Arrays in Java are actual objects that can be passed around and treated just like other objects. Arrays are a way to store a list of items. Each slot of the array holds an individual element, and you can place elements into or change the contents or those slots as you need to.
Arrays can contain any type of element value (primitive types or objects), but you can't store different types in a single array. You can have an array of integers, or an array of strings, or an array of arrays, but you can't have an array that contains, for example, both strings and integers.
To create an array in Java, you use three steps:

  1. Declare a variable to hold the array.
  2. Create a new array object and assign it to the array variable.
  3. Store things in that array.

Declaring Array Variables

The first step to creating an array is creating a variable that will hold the array, just as you would any other variable. Array variables indicate the type of object the array will hold (just as they do for any variable) and the name of the array, followed by empty brackets ([]). The following are all typical array variable declarations:
String difficultWords[];

Point hits[];

int temps[];
An alternate method of defining an array variable is to put the brackets after the type instead of after the variable. They are equivalent, but this latter form is often much more readable. So, for example, these three declarations could be written like this:
String[] difficultWords;

Point[] hits;

int[] temps;

Creating Array Objects

The second step is to create an array object and assign it to that variable. There are two ways to do this:
  • Using new
  • Directly initializing the contents of that array
The first way is to use the new operator to create a new instance of an array:
String[] names = new String[10];
That line creates a new array of Strings with ten slots, containing elements. When you create a new array object using new, you must indicate how many slots that array will hold.
Array objects can contain primitive types such as integers or booleans, just as they can contain objects:

int[] temps = new int[99];
When you create an array object using new, all its slots are initialized for you (0 for numeric arrays, false for boolean, '\0' for character arrays, and null for objects). You can also create and initialize an array at the same time. Instead of using new to create the new array object, enclose the elements of the array inside braces, separated by commas:
String[] chiles = { "jalapeno", "anaheim", "serrano",

    "habanero", "thai" };
Each of the elements inside the braces must be of the same type and must be the same type as the variable that holds that array. An array the size of the number of elements you've included will be automatically created for you. This example creates an array of String objects named chiles that contains five elements.

Accessing Array Elements

Once you have an array with initial values, you can test and change the values in each slot of that array. To get at a value stored within an array, use the array subscript expression:
myArray[subscript];
The myArray part of this expression is a variable holding an array object, although it can also be an expression that results in an array). The subscript expression specifies the slot within the array to access. Array subscripts start with 0, as they do in C and C++. So, an array with ten elements has ten array slots accessed using subscript 0 to 9.
Note that all array subscripts are checked to make sure that they are inside the boundaries of the array (greater than or equal to 0 but less than the array's length) either when your Java program is compiled or when it is run. It is impossible in Java to access or assign a value to an array slot outside of the boundaries of the array. Note the following two statements, for example:

String[] arr = new String[10];

arr[10] = "eggplant";
A program with that last statement in it produces a compiler error at that line when you try to compile it. The array stored in arr has only ten slots numbered from 0, the element at subscript 10 doesn't exist, and the Java compiler will check for that.
If the array subscript is calculated at run-time (for example, as part of a loop) and ends up outside the boundaries of the array, the Java interpreter also produces an error (actually, to be technically correct, it throws an exception). You'll learn more about exceptions later on next week and on Day 18.
How can you keep from overrunning the end of an array accidentally in your own programs? You can test for the length of the array in your programs using the length instance variable—it's available for all array objects, regardless of type:

int len = arr.length // returns 10

Changing Array Elements

To assign an element value to a particular array slot, merely put an assignment statement after the array access expression:
myarray[1] = 15;

sentence[0] = "The";

sentence[10] = sentence[0];
An important thing to note is that an array of objects in Java is an array of references to those objects (similar in some ways to an array of pointers in C or C++). When you assign a value to a slot in an array, you're creating a reference to that object, just as you do for a plain variable. When you move values around inside arrays (as in that last line), you just reassign the reference; you don't copy the value from one slot to another. Arrays of primitive types such as ints or floats do copy the values from one slot to another.
Arrays of references to objects, as opposed to the objects themselves, are particularly useful because it means you can have multiple references to the same objects both inside and outside arrays—for example, you can assign an object contained in an array to a variable and refer to that same object by using either the variable or the array position.

Multidimensional Arrays

Java does not support multidimensional arrays. However, you can declare and create an array of arrays (and those arrays can contain arrays, and so on, for however many dimensions you need), and access them as you would C-style multidimensional arrays:
int coords[][] = new int[12][12];

coords[0][0] = 1;

coords[0][1] = 2;

Block Statements

A block statement is a group of other statements surrounded by braces ({}). You can use a block anywhere a single statement would go, and the new block creates a new local scope for the statements inside it. This means that you can declare and use local variables inside a block, and those variables will cease to exist after the block is finished executing. For example, here's a block inside a method definition that declares a new variable y. You cannot use y outside the block in which it's declared:
void testblock() {

    int x = 10;

    { // start of block

      int y = 50;

      System.out.println("inside the block:");

      System.out.println("x:" + x);

      System.out.println("y:" + y);

    } // end of block

}
Blocks are not usually used in this way—alone in a method definition. You've mostly seen blocks up to this point surrounding class and method definitions, but another very common use of block statements is in the control flow constructs you'll learn about in the remainder of today's lesson.

if Conditionals

The if conditional, which enables you to execute different bits of code based on a simple test in Java, is nearly identical to if statements in C. if conditionals contain the keyword if, followed by a boolean test, followed by a statement (often a block statement) to execute if the test is true:
if (x < y)

    System.out.println("x is smaller than y");
An optional else keyword provides the statement to execute if the test is false:
if (x < y)

    System.out.println("x is smaller than y");

else System.out.println("y is bigger");

Technical Note: The difference between if conditionals in Java and C or C++ is that the test must return a boolean value (true or false). Unlike in C, the test cannot return an integer.

if (engineState == true ) 

    System.out.println("Engine is already on.");

else {

    System.out.println("Now starting Engine.");

    if (gasLevel >= 1)

        engineState = true;

    else System.out.println("Low on gas! Can't start engine.");

}
This example uses the test (engineState == true). For boolean tests of this type, a common shortcut is merely to include the first part of the expression, rather than explicitly testing its value against true or false:
if (engineState)

    System.out.println("Engine is on.");

else System.out.println("Engine is off.");

The Conditional Operator

An alternative to using the if and else keywords in a conditional statement is to use the conditional operator, sometimes called the ternary operator.
The conditional operator is a ternary operator because it has three terms.
The conditional operator is an expression, meaning that it returns a value (unlike the more general if, which only can result in a statement or block being executed). The conditional operator is most useful for very short or simple conditionals, and looks like this:

test ? trueresult : falseresult
The test is an expression that returns true or false, just like the test in the if statement. If the test is true, the conditional operator returns the value of trueresult; if it's false, it returns the value of falseresult. For example, the following conditional tests the values of x and y, returns the smaller of the two, and assigns that value to the variable smaller:
int smaller = x < y ? x : y;
The conditional operator has a very low precedence; that is, it's usually evaluated only after all its subexpressions are evaluated. The only operators lower in precedence are the assignment operators. See the precedence chart in Day 3's lesson for a refresher on precedence of all the operators.

switch Conditionals

A common programming practice in any language is to test a variable against some value, and if it doesn't match that value, to test it again against a different value, and if it doesn't match that one to make yet another test, and so on. Using only if statements, this can become unwieldy, depending on how it's formatted and how many different options you have to test. For example, you might end up with a set of if statements something like this or longer:
if (oper == '+')

  addargs(arg1, arg2);

else if (oper == '-')

   subargs(arg1, arg2);

else if (oper == '*')

   multargs(arg1, arg2);

else if (oper == '/')

   divargs(arg1, arg2);
This form of if statement is called a nested if, because each else statement in turn contains yet another if, and so on, until all possible tests have been made.
A common shorthand mechanism for nested ifs that you can use in some cases allows you to group tests and actions together in a single statement. This is the switch or case statement; in Java it's switch and behaves as it does in C:

switch (test) {

    case valueOne:     

            resultOne;

      break;

    case valueTwo:     

            resultTwo;

      break;

    case valueThree:   

            resultThree;

      break;

    ...

    default: defaultresult;

}
In the switch statement, the test (a primitive type of byte, char, short, or int) is compared with each of the case values in turn. If a match is found, the statement, or statements after the test is executed. If no match is found, the default statement is executed. The default is optional, so if there isn't a match in any of the cases and default doesn't exist, the switch statement completes without doing anything.
Note that the significant limitation of the switch in Java is that the tests and values can be only simple primitive types (and then only primitive types that are castable to int). You cannot use larger primitive types (long, float), strings, or other objects within a switch, nor can you test for any relationship other than equality. This limits the usefulness of switch to all but the simplest cases; nested ifs can work for any kind of test on any type.
Here's a simple example of a switch statement similar to the nested if shown earlier:

switch (oper) {

    case '+':

        addargs(arg1, arg2);

        break;

    case '*':

        subargs(arg1, arg2);

        break;

    case '-':

        multargs(arg1, arg2);

        break;

    case '/':

        divargs(arg1, arg2);

        break;

}
Note the break statement included in every line. Without the explicit break, once a match is made, the statements for that match and also all the statements further down in the switch are executed until a break or the end of the switch is found (and then execution continues after the end of the switch). In some cases, this may be exactly what you want to do, but in most cases, you'll want to make sure to include the break so that only the statements you want to be executed are executed.
One handy use of falling through occurs when you want multiple values to execute the same statements. In this instance, you can use multiple case lines with no result, and the switch will execute the first statements it finds. For example, in the following switch statement, the string "x is an even number." is printed if x has values of 2, 4, 6, or 8. All other values of x print the string "x is an odd number."

switch (x) {

    case 2:

    case 4:

    case 6:

    case 8: 

       System.out.println("x is an even number.");

       break;

    default: System.out.println("x is an odd number.");

}

for Loops

The for loop, as in C, repeats a statement or block of statements some number of times until a condition is matched. for loops are frequently used for simple iteration in which you repeat a block of statements a certain number of times and then stop, but you can use for loops for just about any kind of loop.
The for loop in Java looks roughly like this:

for (initialization; test; increment) {

    statements;

}
The start of the for loop has three parts:
  • initialization is an expression that initializes the start of the loop. If you have a loop index, this expression might declare and initialize it, for example, int i = 0. Variables that you declare in this part of the for loop are local to the loop itself; they cease existing after the loop is finished executing.
  • test is the test that occurs after each pass of the loop. The test must be a boolean expression or function that returns a boolean value, for example, i < 10. If the test is true, the loop executes. Once the test is false, the loop stops executing.
  • increment is any expression or function call. Commonly, the increment is used to change the value of the loop index to bring the state of the loop closer to returning false and completing.
The statement part of the for loop is the statements that are executed each time the loop iterates. Just as with if, you can include either a single statement here or a block; the previous example used a block because that is more common. Here's an example of a for loop that initializes all the values of a String array to null strings:
String strArray[] = new String[10];

int i; // loop index

for (i = 0; i < strArray.length; i++)

    strArray[i] = "";
Any of the parts of the for loop can be empty statements, that is, you can simply include a semicolon with no expression or statement, and that part of the for loop will be ignored. Note that if you do use a null statement in your for loop, you may have to initialize or increment any loop variables or loop indices yourself elsewhere in the program.
You can also have an empty statement for the body of your for loop, if everything you want to do is in the first line of that loop. For example, here's one that finds the first prime number higher than 4000:

for (i = 4001; notPrime(i); i += 2)

    ;
Note that a common mistake in C that also occurs in Java is to accidentally put a semicolon after the first line of the for loop:
for (i = 0; i < 10; i++);

    System.out.println("Loop!");
Because the first semicolon ends the loop with an empty statement, the loop doesn't actually do anything. The println function will be printed only once, because it's actually outside the for loop entirely. Be careful not to make this mistake in your own Java programs.

while and do Loops

Finally, there are while and do loops. while and do loops, like for loops, enable a block of Java code to be executed repeatedly until a specific condition is met. Whether you use a for loop, a while, or a do is mostly a matter of your programming style.
while and do loop, are exactly the same as in C and C++ except their test condition must be a boolean.

while Loops

The while loop is used to repeat a statement or block of statements as long as a particular condition is true. while loops look like this:
while (condition) {

    bodyOfLoop;

}
The condition is a boolean expression. If it returns true, the while loop executes the statements in bodyOfLoop and then tests the condition again, repeating until the condition is false. I've shown the while loop here with a block statement, because it's most commonly used, although you can use a single statement in place of the block.
Here's an example of a while loop that copies the elements of an array of integers (in array1) to an array of floats (in array2), casting each element to a float as it goes. The one catch is that if any of the elements in the first array is 0, the loop will immediately exit at that point. To cover both the cases wherein all the elements have been copied and an element is 0, you can use a compound test with the && operator:

int count = 0;

while ( count < array1.length && array1[count] !=0) {

    array2[count] = (float) array1[count++];

}
Note that if the condition is initially false the first time it is tested (for example, if the first element in that first array is 0), the body of the while loop will never be executed. If you need to execute the loop at least once, you can do one of two things:
  • Duplicate the body of the loop outside the while loop.
  • Use a do loop (described below).
The do loop is considered the better solution of the two.

do...while Loops

The do loop is just like a while loop, except that do executes a given statement or block until the condition is false. The main difference is that while loops test the condition before looping, making it possible that the body of the loop will never execute if the condition is false the first time it's tested. do loops run the body of the loop at least once before testing the condition. do loops look like this:
do {

    bodyOfLoop;

} while (condition);
Here, the bodyOfLoop part is the statements that are executed with each iteration. It's shown here with a block statement because it's most commonly used that way, but you can substitute the braces for a single statement as you can with the other control-flow constructs. The condition is a boolean test. If it returns true, the loop is run again. If it returns false, the loop exits. Keep in mind that with do loops, the body of the loop executes at least once.
Here's a simple example of a do loop that prints a message each time the loop iterates:

int x = 1;

do {

    System.out.println("Looping, round " + x);

    x++;

} while (x <= 10);
Here's the output of these statements:
Looping, round 1

Looping, round 2

Looping, round 3

Looping, round 4

Looping, round 5

Looping, round 6

Looping, round 7

Looping, round 8

Looping, round 9

Looping, round 10

Breaking Out of Loops

In all the loops (for, while, and do), the loop ends when the condition you're testing for is met. What happens if something odd occurs within the body of the loop and you want to exit the loop early? For that, you can use the break and continue keywords.
You've already seen break as part of the switch statement; it stops execution of the switch, and the program continues. The break keyword, when used with a loop, does the same thing—it immediately halts execution of the current loop. If you've nested loops within loops, execution picks up in the next outer loop; otherwise, the program merely continues executing the next statement after the loop.
For example, take that while loop that copied elements from an integer array into an array of floats until the end of the array or until a 0 is reached. You can test for that latter case inside the body of the while and then use a break to exit the loop:

int count = 0;

while (count < array1.length) {

    if (array1[count] == 0) {

        break;

    }

    array2[count] = (float) array1[count++];

}
continue is similar to break except that instead of halting execution of the loop entirely, the loop starts over at the next iteration. For do and while loops, this means the execution of the block starts over again; for for loops, the increment expression is evaluated and then the block is executed. continue is useful when you want to special-case elements within a loop. With the previous example of copying one array to another, you can test for whether the current element is 0 and restart the loop if you find it so that the resulting array will never contain zero. Note that because you're skipping elements in the first array, you now have to keep track of two different array counters:
int count1 = 0;

int count2 = 0;

while (count < array1.length) {

    if (array1[count] == 0) 

       continue;

    array2[count2++] = (float)array1[count++];

}

Labeled Loops

Both break and continue can have an optional label that tells Java where to break to. Without a label, break jumps outside the nearest loop (to an enclosing loop or to the next statement outside the loop), and continue restarts the enclosing loop. Using labeled breaks and continues enables you to break outside nested loops or to continue a loop outside the current loop.
To use a labeled loop, add the label before the initial part of the loop, with a colon between them. Then, when you use break or continue, add the name of the label after the keyword itself:

out:

    for (int i = 0; i <10; i++) {

        while (x < 50) {

            if (i * x == 400)

                break out;

            ...

        }

        ...

    }
In this snippet of code, the label out labels the outer loop. Then, inside both the for and the while loop, when a particular condition is met, a break causes the execution to break out of both loops.
Here's another example: the following program contains a nested for loop. Inside the innermost loop, if the summed values of the two counters is greater than four, both loops exit at once:

foo:

    for (int i = 1; i <= 5; i++)

        for (int j = 1; j <= 3; j++) {

            System.out.println("i is " + i + ", j is " + j);

            if ((i + j) > 4)

                break foo;

        }

System.out.println("end of loops");
Here's the output from this program:
i is 1, j is 1

i is 1, j is 2

i is 1, j is 3

i is 2, j is 1

i is 2, j is 2

i is 2, j is 3

end of loops
As you can see, the loop iterated until the sum of i and j was greater than 4, and then both loops exited back to the outer block and the final message was printed.

Summary

Today, you learned about three main topics that you'll most likely use quite often in your own Java programs: arrays, conditionals, and loops.
You learned how to declare an array variable, create and assign an array object to that variable, and access and change elements within that array.
Conditionals include the if and switch statements, with which you can branch to different parts of your program based on a boolean test.
Finally, you learned about the for, while, and do loops, each of which enable you to execute a portion of your program repeatedly until a given condition is met.
Now that you've learned the small stuff, all that's left is to go over the bigger issues of declaring classes and creating methods within which instances of those classes can communicate with each other by calling methods. Get to bed early tonight, because tomorrow is going to be a wild ride.

Q&A

Q: If arrays are objects, and you use new to create them, and they have an instance variable length, where is the Array class? I didn't see it in the Java class libraries. A: Arrays are implemented kind of weirdly in Java. The Array class is constructed automatically when your Java program runs; Array provides the basic framework for arrays, including the length variable. Additionally, each primitive type and object has an implicit subclass of Array that represents an array of that class or object. When you create a new array object, it may not have an actual class, but it behaves as if it does. Q: Does Java have gotos? A: The Java language defines the keyword goto, but it is not currently used for anything. In other words, no, Java does not have gotos. Q: I declared a variable inside a block statement for an if. When the if was done, the definition of that variable vanished. Where did it go? A: In technical terms, block statements inside braces form a new lexical scope. What this means is that if you declare a variable inside a block, it's only visible and usable inside that block. Once the block finishes executing, all the variables you declared go away.
It's a good idea to declare most of your variables in the outermost block in which they'll be needed—usually at the top of a block statement. The exception might be very simple variables, such as index counters in for loops, where declaring them in the first line of the for loop is an easy shortcut.

You'll learn more about variables and scope tomorrow.
Q: Why can't you use switch with strings? A: Strings are objects, and switch in Java works only for the primitive types byte, char, short, and int. To compare strings, you have to use nested ifs, which enable more general expression tests, including string comparison. Q: It seems to me that a lot of for loops could be written as while loops, and vice versa. A: True. The for loop is actually a special case of while that enables you to iterate a loop a specific number of times. You could just as easily do this with a while and then increment a counter inside the loop. Either works equally well. This is mostly just a question of programming style and personal choice.

Working with Objects


    • Working with Objects
      • Creating New Objects
        • Using new
        • What new Does
        • A Note on Memory Management
      • Accessing and Setting Class and Instance Variables
        • Getting Values
        • Changing Values
        • Class Variables
      • Calling Methods
        • Class Methods
      • References to Objects
      • Casting and Converting Objects and Primitive Types
        • Casting Primitive Types
        • Casting Objects
        • Converting Primitive Types to Objects and Vice Versa
      • Odds and Ends
        • Comparing Objects
        • Determining the Class of an Object
      • The Java Class Library
      • Summary
      • Q&A


 

Working with Objects

by Laura Lemay
Let's start today's lesson with an obvious statement: because Java is an object-oriented language, you're going to be dealing with a lot of objects. You'll create them, modify them, move them around, change their variables, call their methods, combine them with other objects—and, of course, develop classes and use your own objects in the mix.
Today, therefore, you'll learn all about the Java object in its natural habitat. Today's topics include:

  • Creating instances of classes
  • Testing and modifying class and instance variables in your new instance
  • Calling methods in that object
  • Casting (converting) objects and other data types from one class to another
  • Other odds and ends about working with objects
  • An overview of the Java class libraries

Creating New Objects

When you write a Java program, you define a set of classes. As you learned on Day 2, classes are templates for objects; for the most part, you merely use the class to create instances and then work with those instances. In this section, therefore, you'll learn how to create a new object from any given class.
Remember strings from yesterday? You learned that using a string literal—a series of characters enclosed in double-quotes—creates a new instance of the class String with the value of that string.
The String class is unusual in that respect—although it's a class, there's an easy way to create instances of that class using a literal. The other classes don't have that shortcut; to create instances of those classes you have to do so explicitly by using the new operator.


Note: What about the literals for numbers and characters? Don't they create objects, too? Actually, they don't. The primitive data types for numbers and characters create numbers and characters, but for efficiency, they aren't actually objects. You can put object-wrappers around them if you need to treat them like objects (you'll learn how to do this later).

Using new

To create a new object, you use new with the name of the class you want to create an instance of, then parentheses after that:
String str = new String();

Random r = new Random();

Motorcycle m2 = new Motorcycle();
The parentheses are important; don't leave them off. The parentheses can be empty, in which case the most simple, basic object is created, or the parentheses can contain arguments that determine the initial values of instance variables or other initial qualities of that object. The number and type of arguments you can use with new are defined by the class itself by using a special method called a constructor; you'll learn about how to create constructors in your own classes later on this week.

Caution: Some classes may not allow you to create instances without any arguments. Check the class to make sure.

For example, take the Date class, which creates date objects. Listing 4.1 is a Java program that shows three different ways of creating a Date object using new:
    Listing 4.1. Laura's Date program.
1: import java.util.Date;

 2: 

 3: class CreateDates {

 4: 

 5:     public static void main(String args[]) {

 6:         Date d1, d2, d3;

 7: 

 8:         d1 = new Date();

 9:         System.out.println("Date 1: " + d1);

10: 

11:         d2 = new Date(71, 7, 1, 7, 30);

12:         System.out.println("Date 2: " + d2);

13: 

14:         d3 = new Date("April 3 1993 3:24 PM");

15:         System.out.println("Date 3: " + d3);

16:     }

17: }

Date 1: Tue Feb 13 09:36:56 PST 1996

Date 2: Sun Aug 01 07:30:00 PDT 1971

Date 3: Sat Apr 03 15:24:00 PST 1993
In this example, three different dates are created by using different arguments to new. The first instance (line 8) uses new with no arguments, which creates a Date object for today's date (as the first line of the output shows).
The second Date object you create in this example has five integer arguments. The arguments represent a date: year, month, day, hours, and seconds. And, as the output shows, this creates a Date object for that particular date: Sunday, August first, 1971, at 7:30 AM.
The third version of Date takes one argument, a string, representing the date as a text string. When the Date object is created, that string is parsed, and a Date object with that date and time is created (see the third line of output). The date string can take many different formats; see the API documentation for the Date class (part of the java.util package) for information about what strings you can use.

What new Does

What does new do? When you use the new operator, several things happen: first, the new instance of the given class is created, and memory is allocated for it. In addition (and most importantly), when the new object is created, a special method defined in the given class is called. This special method is called a constructor. Constructors are special methods for creating and initializing new instances of classes. Constructors initialize the new object and its variables, create any other objects that object needs, and generally perform any other operations the object needs to initialize itself.
Multiple constructor definitions in a class can each have a different number or type of arguments—then, when you use new, you can specify different arguments in the argument list, and the right constructor for those arguments will be called. That's how each of those different versions of new that were listed previously can create different things.
When you create your own classes, you can define as many constructors as you need to implement that class's behavior. You'll learn how to create constructors on Day 7.

A Note on Memory Management

Memory management in Java is dynamic and automatic. When you create a new object in Java, Java automatically allocates the right amount of memory for that object in the heap. You don't have to allocate any memory for any objects explicitly; Java does it for you.
What happens when you're finished with that object? How do you de-allocate the memory that object uses? The answer is again: memory management is automatic. Once you finish with an object, that object no longer has any live references to it (it won't be assigned to any variables you're still using or stored in any arrays). Java has a garbage collector that looks for unused objects and reclaims the memory that those objects are using. You don't have to do any explicit freeing of memory; you just have to make sure you're not still holding onto an object you want to get rid of. You'll learn more specific details about the Java garbage collector and how it works on Day 21.

Accessing and Setting Class and Instance Variables

Now you have your very own object, and that object may have class or instance variables defined in it. How do you work with those variables? Easy! Class and instance variables behave in exactly the same ways as the local variables you learned about yesterday; you just refer to them slightly differently than you do regular variables in your code.

Getting Values

To get to the value of an instance variable, you use dot notation.
With dot notation, an instance or class variable name has two parts: the object on the left side of the dot, and the variable on the right side of the dot.
For example, if you have an object assigned to the variable myObject, and that object has a variable called var, you refer to that variable's value like this:

myObject.var;
This form for accessing variables is an expression (it returns a value), and both sides of the dot are also expressions. This means that you can nest instance variable access. If that var instance variable itself holds an object, and that object has its own instance variable called state, you can refer to it like this:
myObject.var.state;
Dot expressions are evaluated left to right, so you start with myObject's variable var, which points to another object with the variable state. You end up with the value of that state variable.

Changing Values

Assigning a value to that variable is equally easy—just tack an assignment operator on the right side of the expression:
myObject.var.state = true;
Listing 4.2 is an example of a program that tests and modifies the instance variables in a Point object. Point is part of the java.awt package and refers to a coordinate point with an x and a y value.
    Listing 4.2. The TestPoint Class.
1: import java.awt.Point;

 2: 

 3: class TestPoint {

 4: 

 5:     public static void main(String args[]) {

 6:         Point thePoint = new Point(10,10);

 7: 

 8:         System.out.println("X is " + thePoint.x);

 9:         System.out.println("Y is " + thePoint.y);

10: 

11:         System.out.println("Setting X to 5.");

12:         thePoint.x = 5;

13:         System.out.println("Setting Y to 15.");

14:         thePoint.y = 15;

15: 

16:         System.out.println("X is " + thePoint.x);

17:         System.out.println("Y is " + thePoint.y);

18: 

19:     }

20: }

X is 10

Y is 10

Setting X to 5.

Setting Y to 15.

X is 5

Y is 15
In this example, you first create an instance of Point where X and Y are both 10 (line 6). Lines 8 and 9 print out those individual values, and you can see dot notation at work there. Lines 11 through 14 change the values of those variables to 5 and 15, respectively. Finally, lines 16 and 17 print out the values of X and Y again to show how they've changed.

Class Variables

Class variables, as you learned before, are variables that are defined and stored in the class itself. Their values, therefore, apply to the class and to all its instances.
With instance variables, each new instance of the class gets a new copy of the instance variables that class defines. Each instance can then change the values of those instance variables without affecting any other instances. With class variables, there is only one copy of that variable. Every instance of the class has access to that variable, but there is only one value. Changing the value of that variable changes it for all the instances of that class.
You define class variables by including the static keyword before the variable itself. You'll learn more about this on Day 6. For example, take the following partial class definition:

class FamilyMember {

    static String surname = "Johnson";

    String name;

    int age;

    ...

}
Instances of the class FamilyMember each have their own values for name and age. But the class variable surname has only one value for all family members. Change surname, and all the instances of FamilyMember are affected.
To access class variables, you use the same dot notation as you do with instance variables. To get or change the value of the class variable, you can use either the instance or the name of the class on the left side of the dot. Both the lines of output in this example print the same value):

FamilyMember dad = new FamilyMember()

System.out.println("Family's surname is: " + dad.surname);

System.out.println("Family's surname is: " + FamilyMember.surname);
Because you can use an instance to change the value of a class variable, it's easy to become confused about class variables and where their values are coming from (remember, the value of a class variable affects all the instances). For this reason, it's a good idea to use the name of the class when you refer to a class variable—it makes your code easier to read and strange results easier to debug.

Calling Methods

Calling a method in objects is similar to referring to its instance variables: method calls also use dot notation. The object whose method you're calling is on the left side of the dot; the name of the method and its arguments is on the right side of the dot:
myObject.methodOne(arg1, arg2, arg3);
Note that all methods must have parentheses after them, even if that method takes no arguments:
myObject.methodNoArgs();
If the method you've called results in an object that itself has methods, you can nest methods as you would variables:
myObject.getClass().getName();
You can combine nested method calls and instance variable references as well:
myObject.var.methodTwo(arg1, arg2);
System.out.println(), the method you've been using all through the book this far, is a great example of nesting variables and methods. The System class (part of the java.lang package) describes system-specific behavior. System.out is a class variable that contains an instance of the class PrintStream that points to the standard output of the system. PrintStream instances have a println() method that prints a string to that output stream.
Listing 4.3 shows an example of calling some methods defined in the String class. Strings include methods for string tests and modification, similar to what you would expect in a string library in other languages.

    Listing 4.3. Several Uses of String methods.
1: class TestString {

 2: 

 3:     public static void main(String args[]) {

 4:         String str = "Now is the winter of our discontent";

 5: 

 6:         System.out.println("The string is: " + str);

 7:         System.out.println("Length of this string: "

 8:                 + str.length());

 9:         System.out.println("The character at position 5: "

10:                 + str.charAt(5));

11:         System.out.println("The substring from 11 to 17: "

12:                 + str.substring(11, 17));

13:         System.out.println("The index of the character d: "

14:                 + str.indexOf('d'));

15:         System.out.print("The index of the beginning of the ");

16:         System.out.println("substring \"winter\":"

17:                 + str.indexOf("winter"));

18:         System.out.println("The string in upper case: "

19:                 + str.toUpperCase());

20:     }

21: }

The string is: Now is the winter of our discontent

Length of this string: 35

The character at position 5: s

The substring from positions 11 to 17: winter

The index of the character d: 25

The index of the beginning of the substring "winter": 11

The string in upper case: NOW IS THE WINTER OF OUR DISCONTENT
In line 4, you create a new instance of String by using a string literal (it's easier that way than using new and then putting the characters in individually). The remainder of the program simply calls different string methods to do different operations on that string:
  • Line 6 prints the value of the string we created in line 4: "Now is the winter of our discontent".
  • Line 7 calls the length() method in the new String object. This string has 35 characters.
  • Line 9 calls the charAt() method, which returns the character at the given position in the string. Note that string positions start at 0, so the character at position 5 is s.
  • Line 11 calls the substring() method, which takes two integers indicating a range and returns the substring at those starting and ending points. The substring() method can also be called with only one argument, which returns the substring from that position to the end of the string.
  • Line 13 calls the indexOf() method, which returns the position of the first instance of the given character (here, 'd').
  • Line 15 shows a different use of the indexOf() method, which takes a string argument and returns the index of the beginning of that string.
  • Finally, line 18 uses the toUpperCase() method to return a copy of the string in all uppercase.

Class Methods

Class methods, like class variables, apply to the class as a whole and not to its instances. Class methods are commonly used for general utility methods that may not operate directly on an instance of that class, but fit with that class conceptually. For example, the String class contains a class method called valueOf(), which can take one of many different types of arguments (integers, booleans, other objects, and so on). The valueOf() method then returns a new instance of String containing the string value of the argument it was given. This method doesn't operate directly on an existing instance of String, but getting a string from another object or data type is definitely a String-like operation, and it makes sense to define it in the String class.
Class methods can also be useful for gathering general methods together in one place (the class). For example, the Math class, defined in the java.lang package, contains a large set of mathematical operations as class methods—there are no instances of the class Math, but you can still use its methods with numeric or boolean arguments.
To call a class method, use dot notation as you do with instance methods. As with class variables, you can use either an instance of the class or the class itself on the left site of the dot. However, for the same reasons noted in the discussion on class variables, using the name of the class for class variables makes your code easier to read. The last two lines in this example produce the same result:

String s, s2;

s = "foo";

s2 = s.valueOf(5);

s2 = String.valueOf(5);

References to Objects

As you work with objects, one important thing going on behind the scenes is the use of references to those objects. When you assign objects to variables, or pass objects as arguments to methods, you are passing references to those objects, not the objects themselves or copies of those objects.
An example should make this clearer. Examine the following snippet of code:

import java.awt.Point;

class ReferencesTest {

    public static void main (String args[]) {

        Point pt1, pt2;

        pt1 = new Point(100, 100);

        pt2 = pt1;

        pt1.x = 200;

        pt1.y = 200;

        System.out.println("Point1: " + pt1.x + ", " + pt1.y);

        System.out.println("Point2: " + pt2.x + ", " + pt2.y);

    }

}
In this program, you declare two variables of type Point, and assign a new Point object to pt1. Then you assign the value of pt1 to pt2.
Now, here's the challenge. After changing pt1's x and y instance variables, what will pt2 look like?
Here's the output of that program:

Point1: 200, 200

Point2: 200, 200
As you can see, pt2 was also changed. When you assign the value of pt1 to pt2, you actually create a reference from p2 to the same object to which pt1 refers. Change the object that pt2 refers to, and you also change the object that pt1 points to, because both are references to the same object.
The fact that Java uses references becomes particularly important when you pass arguments to methods. You'll learn more about this later on today, but keep these references in mind.


Technical Note: There are no explicit pointers or pointer arithmetic in Java—just references. However, with these references, and with Java arrays, you have most of the capabilities that you have with pointers without the confusion and lurking bugs that explicit pointers can create.

Casting and Converting Objects and Primitive Types

Sometimes in your Java programs you may have a value stored somewhere that is the wrong type. Maybe it's an instance of the wrong class, or perhaps it's a float and you want it to be an int. To convert the value of one type to another, you use a mechanism called casting. Casting is a mechanism of converting the value of an object or primitive type into another type. The result of a cast is a new reference or value; casting does not affect the original object or value.
Although the concept of casting is a simple one, the rules for what types in Java can be converted to what other types are complicated by the fact that Java has both primitive types (int, float, boolean), and object types (String, Point, Window, and so on). There are three forms of casts and conversions to talk about in this section:

  • Casting between primitive types: int to float to boolean
  • Casting between object types: an instance of a class to an instance of another class
  • Converting primitive types to objects and then extracting primitive values back out of those objects

Casting Primitive Types

Casting between primitive types enables you to "convert" the value of one type to another primitive type—for example, to assign a number of one type to a variable of another type. Casting between primitive types most commonly occurs with the numeric types; boolean values cannot be cast to any other primitive type.
Often, if the type you are casting to is "larger" than the type of the value you're converting, you may not have to use an explicit cast. You can often automatically treat a byte or a character as an int, for example, or an int as a long, an int as a float, or anything as a double automatically. In most cases, because the larger type provides more precision than the smaller, no loss of information occurs when the value is cast. The exception is casting integers to floating-point values; casting an int or a long to a float or a long to a double may cause some loss of precision.
To convert a large value to smaller type, you must use an explicit cast, because converting that value may result in a loss of precision. Explicit casts look like this:

(typename) value
In this form, typename is the name of the type you're converting to (for example: short, int, float, boolean), and value is an expression that results in the value you want to convert. This expression divides the values of x by the value of y and casts the result to an int:
(int) (x / y);
Note that because the precedence of casting is higher than that of arithmetic, you have to use parentheses so that the result of the division is what gets cast to an int.

Casting Objects

Instances of classes can also be cast to instances of other classes, with one restriction: the class of the object you're casting and the class you're casting it to must be related by inheritance; that is, you can cast an object only to an instance of its class's sub- or superclass—not to any random class.
Analogous to converting a primitive value to a larger type, some objects may not need to be cast explicitly. In particular, because subclasses contain all the information in the superclass, you can use an instance of a subclass anywhere a superclass is expected. Suppose you have a method that takes two arguments: one of type Object, and one of type Number. You don't have to pass instances of those particular classes to that method. For the Object argument, you can pass any subclass of Object (any object, in other words), and for the Number argument you can pass in any instance of any subclass of Number (Integer, Boolean, Float, and so on).
Casting an object to an instance of one of that object's superclasses loses the information the original subclass provided and requires a specific cast. To cast an object to another class, you use the same casting operation that you used for base types:

(classname) object
In this case, classname is the name of the class you want to cast the object to, and object is a reference to the object you're casting. Note that casting creates a reference to the old object of the type classname; the old object still continues to exist as it did before.
Here's a (fictitious) example of a cast of an instance of the class GreenApple to an instance of the class Apple (where GreenApple is theoretically a subclass of Apple):

GreenApple a;

Apple a2;

a = new GreenApple();

a2 = (Apple) a;
In addition to casting objects to classes, you can also cast objects to interfaces—but only if that object's class or one of its superclasses actually implements that interface. Casting an object to an interface then enables you to call one of that interface's methods even if that object's class does not directly implement that interface. You'll learn more about interfaces in Week 3.

Converting Primitive Types to Objects and Vice Versa

Now you know how to cast a primitive type to another primitive type and how to cast between classes. How can you cast one to the other?
You can't! Primitive types and objects are very different things in Java and you can't automatically cast or convert between the two. However, the java.lang package includes several special classes that correspond to each primitive data type: Integer for ints, Float for floats, Boolean for booleans, and so on.
Using class methods defined in these classes, you can create an object-equivalent for all the primitive types using new. The following line of code creates an instance of the Integer class with the value 35:

Integer intObject = new Integer(35);
Once you have actual objects, you can treat those values as objects. Then, when you want the primitive values back again, there are methods for that as well—for example, the intValue() method extracts an int primitive value from an Integer object:
int theInt = intObject.intValue();  // returns 35
See the Java API documentation for these special classes for specifics on the methods for converting primitives to and from objects.

Odds and Ends

This section is a catch-all for other information about working with objects, in particular:
  • Comparing objects
  • Copying objects
  • Finding out the class of any given object
  • Testing to see whether an object is an instance of a given class

Comparing Objects

Yesterday, you learned about operators for comparing values: equals, not equals, less than, and so on. Most of these operators work only on primitive types, not on objects. If you try to use other values as operands, the Java compiler produces errors.
The exception to this rule is with the operators for equality: == (equal) and != (not equal). These operators, when used with objects, tests whether the two operands refer to exactly the same object.
What should you do if you want to be able to compare instances of your class and have meaningful results? You have to implement special methods in your class, and you have to call those methods using those method names.


Technical Note: Java does not have the concept of operator overloading—that is, the capability of defining the behavior of the built-in operators by defining methods in your own classes. The built-in operators remain defined only for numbers.

A good example of this is the String class. It is possible to have two strings, two independent objects in memory with the same values—that is, the same characters in the same order. According to the == operator, however, those two String objects will not be equal, because, although their contents are the same, they are not the same object.
The String class, therefore, defines a method called equals() that tests each character in the string and returns true if the two strings have the same values. Listing 4.4 illustrates this.

    Listing 4.4. A Test of String Equality.
1: class EqualsTest {

 2: 

 3:     public static void main(String args[]) {

 4:         String str1, str2;

 5:         str1 = "she sells sea shells by the sea shore.";

 6:         str2 = str1;

 7: 

 8:         System.out.println("String1: " + str1);

 9:         System.out.println("String2: " + str2);

10:         System.out.println("Same object? " + (str1 == str2));

11: 

12:         str2 = new String(str1);

13: 

14:         System.out.println("String1: " + str1);

15:         System.out.println("String2: " + str2);

16:         System.out.println("Same object? " + (str1 == str2));

17:         System.out.println("Same value? " + str1.equals(str2));

18:     }

19: }

String1: she sells sea shells by the sea shore.

String2: she sells sea shells by the sea shore.

Same object? true

String1: she sells sea shells by the sea shore.

String2: she sells sea shells by the sea shore.

Same object? false

Same value? true
The first part of this program (lines 4 through 6) declares two variables, str1 and str2, assigns the literal she sells sea shells by the sea shore. to str1, and then assigns that value to str2. As you know from object references, now str1 and str2 point to the same object, and the test at line 10 proves that.
In the second part, you create a new string object with the value of str1. Now you have two different string objects with the same value. Testing them to see whether they're the same object by using the == operator (line 16) returns the expected answer, as does testing them using the equals method (line 17) to compare their values.


Technical Note: Why can't you just use another literal when you change str2, rather than using new? String literals are optimized in Java—if you create a string using a literal, and then use another literal with the same characters, Java knows enough to give you the first String object back. Both strings are the same objects—to create two separate objects you have to go out of your way.

Determining the Class of an Object

Want to find out the class of an object? Here's the way to do it for an object assigned to the variable obj:
String name = obj.getClass().getName();
What does this do? The getClass() method is defined in the Object class, and as such is available for all objects. The result of that method is a Class object (where Class is itself a class), which has a method called getName(). getName() returns a string representing the name of the class.
Another test that might be useful to you is the instanceof operator. instanceof has two operands: an object on the left, and the name of a class on the right. The expression returns true or false based on whether the object is an instance of the named class or any of that class's subclasses:

"foo" instanceof String // true

Point pt = new Point(10, 10);

pt instanceof String // false
The instanceof operator can also be used for interfaces; if an object implements an interface, the instanceof operator with that interface name on the right side returns true. You'll learn all about interfaces in Week 3.

The Java Class Library

To finish up today, let's look at the some of the Java class library. Actually, you've had some experience with them already, so they shouldn't seem that strange.
The Java class library provides the set of classes that are guaranteed to be available in any commercial Java environment (for example, in HotJava or in Netscape 2.0). Those classes are in the java package and include all the classes you've seen so far in this book, plus a whole lot more classes you'll learn about later on in this book (and more you won't learn about at all).
The Java Developer's Kit comes with documentation for all of the Java class library, which includes descriptions of each class's instance variables, methods, constructors, interfaces, and so on. A shorter summary of the Java API is in Appendix C as well. Exploring the Java class library and its methods and instance variables is a great way to figure out what Java can and cannot do, as well as a starting point for your own development.
Here are the class packages that are part of the Java class library:

  • java.lang: Classes that apply to the language itself, which includes the Object class, the String class, and the System class. It also contains the special classes for the primitive types (Integer, Character, Float, and so on).
  • java.util: Utility classes, such as Date, as well as simple collection classes, such as Vector and Hashtable.
  • java.io: Input and output classes for writing to and reading from streams (such as standard input and output) and for handling files.
  • java.net: Classes for networking support, including Socket and URL (a class to represent references to documents on the World Wide Web).
  • java.awt: (the Abstract Window Toolkit): Classes to implement a graphical user interface, including classes for Window, Menu, Button, Font, CheckBox, and so on. This package also includes classes for processing images (in the java.awt.Image package).
  • java.applet: Classes to implement Java applets, including the Applet class itself, as well as the AudioClip interface.
In addition to the Java classes, your development environment may also include additional classes that provide other utilities or functionality. Although these classes may be useful, because they are not part of the standard Java library, they won't be available to other people trying to run your Java program. This is particularly important for applets, because applets are expected to be able to run on any platform, using any Java-capable browser. Only classes inside the java package are guaranteed to be available on all browsers and Java environments.

Summary

Objects, objects everywhere. Today, you learned all about how to deal with objects: how to create them, how to find out and change the values of their variables, and how to call their methods. You also learned how to copy and compare them, and how to convert them into other objects. Finally, you learned a bit about the Java class libraries—which give you a whole slew of classes to play with in your own programs.
You now have the fundamentals of how to deal with most simple things in the Java language. All you have left are arrays, conditionals, and loops, which you'll learn about tomorrow. Then you'll learn how to define and use classes in Java applications on Day 6, and launch directly into applets next week. With just about everything you do in your Java programs, you'll always come back to objects.

Q&A

Q: I'm confused about the differences between objects and the primitive data types, such as int and boolean. A: The primitive types in the language (byte, short, int, long, float, double, boolean and char) represent the smallest things in the language. They are not objects, although in many ways they can be handled like objects—they can be assigned to variables and passed in and out of methods. Most of the operations that work exclusively on objects, however, will not work on them.
Objects are instances of classes and as such, are usually much more complex data types than simple numbers and characters, often containing numbers and characters as instance or class variables.
Q: In the section on calling methods, you had examples of calling a method with a different number of arguments each time—and it gave a different kind of result. How is that possible? A: That's called method overloading. Overloading enables the same function name to have different behavior based on the arguments it's called with—and the number and type of arguments can vary. When you define methods in your own classes, you define separate method signatures with different sets of arguments and different definitions. When a method is called, Java figures out which definition to execute based on the number and type of arguments with which you called it.
You'll learn all about this on Day 6.
Q: No operator overloading in Java? Why not? I thought Java was based on C++, and C++ has operator overloading. A: Java was indeed based on C++, but it was also designed to be simple, so many of C++'s features have been removed. The argument against operator overloading is that because the operator can be defined to mean anything, it makes it very difficult to figure out what any given operator is doing at any one time. This can result in entirely unreadable code. When you use a method, you know it can mean many things to many classes, but when you use an operator you would like to know that it always means the same thing. Given the potential for abuse, the designers of Java felt it was one of the C++ features that was best left out.