Spring Semester 2002


Lecture Notes Eleven: Java review by Dilbert. Sharpen your pencils everybody.

Review I: Classes and Objects

1. Introduction

Java programs are built from classes.

From a class definition you can create any number of

objects
that are known as instances of that class.

(Think of a class as a factory with blueprints and instructions to build gadgets - objects are the gadgets the factory makes. Each class is a factory, and the factory can make 0, 1, or more gadgets.)

Here's a declaration of a simple class that might represent a point on a two-dimensional plane:

class Point {
  public double x, y; 
}
Exercise: Compile and run, then explain the following program:
class Play {
  public static void main(String[] args) {
    Point a, b; 
    a = new Point(); 
    b = new Point(); 
  }
} 

class Point {
  public double x, y; 
}  
Objects are created using an expression containing the new keyword.
new Point();
Creating an object from a class definition is also known as instantiation; thus, objects are often called instances.

All objects in Java are accessed via object references - any variable that appears to hold an object actually contains a reference to that object.

p = new Point();
Object references are null when they do not reference any object.

Objects in Java have a type; the type is the object's class.

Point p;
p = new Point(); 
A class can contain two kinds of members:
  1. fields
    Fields are data belonging either to the class itself or to objects of the class. They make up the state of the object or class. Fields store results of computations performed by the methods.
  2. methods
    Methods are collections of statements that operate on the fields to manipulate the state. Methods contain the executable code of a class. The ways in which methods are invoked, and the statements contained within these methods, is what ultimately directs program execution.
Java now supports inner classes but let's ignore that for the time being.

The Point class

But that will change soon.

Exercise: Compile, run, and explain the following program:

class Play {
  public static void main(String[] args) {
    Point a, b;
    a = new Point(); 
    a.x = 1;
    a.y = 2;
    a.report(); 
  } 
} 

class Point {
  double x, y; 
  void report() {
    System.out.println("Point at: (" + x + ", " + y +")");
  } 
} 

The fields in objects are known as instance variables, because there is a unique copy of the field in each object (instance) of the class. Take a look at the following example.

Point lowerLeft = new Point(); 
Point upperRight = new Point(); 
Point middlePoint = new Point(); 

lowerleft.x = 0.0;
lowerleft.y = 0.0;

upperRight.x = 1280.0;
upperRight.y = 1024.0;

middlePoint.x = 640.0;
middlePoint.y = 512.0;

double d = lowerLeft.distanceTo(upperRight); // [1]
Each Point object is unique and has its own copy of the x and y fields. Changing x in lowerLeft, for example, does not affect the value of x in the upperRight object.

Per-object fields are usually what you need. You usually want a field in one object to be distinct from the similarly named field in every other object instantiated from that class.

Notice that we introduced a new method, distanceTo (another Point).

Exercise: Compile, run, and explain the following program:

class Point {
    double x, y; 
    Point() {
	x = 0; 
	y = 0; 
    }
    Point(int initialX, int initialY) {
	x = initialX;
	y = initialY;
    }
    void report() {
	System.out.println("Point at: (" + x + ", " + y +")"); 
    } 
    double distanceTo (Point other) {
	double temp;
	temp = (x - other.x) * (x - other.x) +
	       (y - other.y) * (y - other.y); 

	return Math.sqrt(temp); 
    }
} 

class Play {
    public static void main(String[] args) {
	Point a, b, c; 
	a = new Point(34, -34); 
	a.report(); 

	b = new Point(); 
	c = new Point(); 

	b.x = 1280.0;
	b.y = 1024.0; 

	b.report(); 

	c.x = 640.0;
	c.y = 512.0; 

	c.report(); 

	Point u, i;
 	u = new Point(); 
        i = new Point(10, 10); 
	System.out.println(i.distanceTo(u)); 

    }
}
Sometimes, though, you want fields that are shared among all objects of that class.

The shared variables are known as class variables - variables specific to the class as opposed to objects of the class. In Java, you obtain class-specific fields by declaring them static, and they are therefore sometimes called static fields.

Keep in mind:

  1. (instance) fields == instance variables == one per object (or gadget)

  2. static fields == class variables == one per class (or factory)

    A static field is there no matter how many objects are created, even if none are created. It relates to the class not to the instances (or objects) of the class.

Exercise: Compile, run, and explain the following program:

class One {
  int a = 0;
  static int b = 0;
  void fun() {
    b += 1;
    a += 1;
    System.out.print(a + " " + b + " ");
  }
  public static void main(String[] args) {
    One alpha = new One();
    One beta = new One();
    alpha.fun();
    beta.fun();
    alpha.fun();
  }
}
In addition to these kinds of variables, methods can use (as local workspace, or scratch paper) method variables and parameters. They are gone when the method terminates and they are different from instance and class variables. (Not to mention that instance variables are initialized by default).


Wait, wait: let's try to clarify this. Instance variables and local variables are different. They don't behave in the same way. Yes, instance variables are global to the instance methods, unlike local variables, which are local to the methods they are defined in.

Therefore instance variables are global to succesive invocations of instance methods. (They stay with the object for as long as the object exists). Exactly. But can you prove that to me?

Proof is the bottom line for everyone. If you say so.

What do you think of this one:
public class Three {
    public static void main(String[] args) {
        Three a = new Three(); 
        a.fun(); 
        a.fun(); 
        a.fun(); 
    } 
    void fun() {
        int m = 0; 
        m = m + 1; 
        this.n = this.n + 1; 
        System.out.println("n is: " + this.n  + 
                      " and m is: " +      m  + " now."); 
    } 
    int n; // initialized to zero by default 
} 
Can I see an example with two functions?

How about this one:
public class Four {
    public static void main(String[] args) {
        Four a = new Four(); 
        a.fun1(); 
        a.fun2(); 
        a.fun1(); 
        a.fun2(); 
        a.fun1(); 
    } 
    void fun1() {
        int m = 0; 
        m = m + 1; 
        this.n = this.n + 1; 
        System.out.println("fun1 --> n is: " + this.n   + 
                           " and m is: " + m + " now.");
    } 
    void fun2() {
        int m = 0; 
        m = m + 1; 
        this.n = this.n + 1; 
        System.out.println("fun2 --> n is: " + this.n   + 
                           " and m is: " + m + " now.");
    } 
    int n; // initialized to zero by default 
} 
OK, I am convinced now.

Note how m is so local you can have a variable with this name in every function you define. I can see that. I have a question though, perhaps a bit unrelated, but it keeps bothering me.

What's that? What is a static variable, again?

Since you asked:
public class Five {
    int n; 
    static int m; 
    void fun() {
        this.n = this.n + 1; 
        this.m = this.m + 1; // better write this as Five.m += 1; 
        System.out.println("n = " + this.n + ", m = " + this.m); 
                                                     // Five.m, again...
    } 

} 

class Tester {
    public static void main(String[] args) {
        Five alpha        = new Five(); 
        Five         beta = new Five(); 
        alpha.fun(); 
        alpha.fun(); 
                     beta.fun(); 
        alpha.fun(); 
                     beta.fun(); 
                     beta.fun();
                     beta.fun(); 
        alpha.fun();
    } 
} 
Oh, I see...

It's a factory variable. Global over all objects ever created by the factory. By the way, I suppose we could have kept everything in just one class, Five, no?

Yes, indeed. I used Tester to just make sure I don't clutter the main point of the example. I see. Of course, having Tester, I need to compile the file that contains the source code first, ...

... and given the way I wrote the code (class Five is public) the file must be called Five.java ... ... I can see that, ...

... then run java on Tester, for the results. Very good. We can move on now.

Members of a class can have various levels of visibility. The public declaration of x and y in the Point class means that any code with access to a Point can read or modify those values. (Other levels of visibility limit member access to code in the class itself, or to other related classes).

Objects of the Point class as defined above are exposed to manipulation by any code that has a reference to a Point object, because its fields are declared public. (See the code snippet above). The real benefits of object orientation, however, come from hiding the implementation of a class behind operations performed on its internal data. In Java, operations of a class are declared via its methods - instructions that operate on an object's data to obtain results. Methods access implementation details that are otherwise hidden from other objects. Hiding data behind methods so that it is inaccessible to other objects is the fundamental basis of data encapsulation.


Methods, I like methods... Well, then, here's some:

Let's briefly consider methods.

If we have a mathematical function f from the set of positive integers N to the set of positive integers N and with the following definition:

f(x) = 3x + 1 
then we can write it in Java as follows:
int f(int n) {
  return 3x + 1; 
} 
The domain and codomain of the function as well as the number of arguments are specified in this definition. A return statement is used to pass back the result.

Functions are encapsulating computations. In Java functions are called methods. Let's suppose we now want to implement a (still simple) but more complicated function, that computes the sum of the first n positive numbers.

We can do it in several ways:

a) iterative approach

int sum (int n) { 
  int sum = i = 0; 
  while (i <= n) { 
    sum = sum + i;
    i = i + 1;  
  } 
  return sum; 
} 
b) closed form formula approach

int sum(int n) {
  return n * (n + 1) / 2; 
} 
c) recursive approach
int sum(int n) {
  if (n == 1) return 1; 
  else return n + sum(n - 1); 
} 
The iterative approach is very constructive. It uses a local variable, sum.

The closed form is OK as long as we can prove that the formula we use does indeed compute the sum of the first n positive integers. To prove that the formula is correct we can use mathematical induction. However, coming up with the right formula to prove, is a different story altogether.

Exercises

  1. Write a function that computes the sum of the squares of the first n positive integers.
  2. Write another that computes the sum of the cubes of the first n integer numbers.
The recursive approach is an elegant approach.

Functions can also be mutually recursive, for example consider this definition of when a given positive integer number n is odd or even:

boolean odd(int n) {
  if (n == 0) return false; 
  else return even(n - 1); 
} 

boolean even(int n) {
  if (n == 0) return true; 
  else return odd(n - 1); 
}
We notice that the codomain (or, return type, here) of the function is boolean, so they're both predicates. (A method whose return type is boolean is called a predicate).

Now we know how to write functions, but where do we put them. Functions can't exist on their own in Java. They must be located either inside objects or inside classes. We'll first put them in an object. But for this we need to create an object, so we need to talk about objects and classes.

We create a type of object Abacus and put the methods in it.

class Abacus {
  int sum(int n) {
    if (n == 1) return 1; 
    else return n + sum(n - 1); 
  } 
  boolean odd(int n) { 
    if (n == 0) return false; 
    else return even(n - 1);    
  }
  boolean even (int n) { 
    if (n == 0) return true; 
    else return odd(n - 1); 
  } 
}
Exercise: Compile, run, and explain the following program:
class Abacus {
  int sum(int n) {
    if (n == 1) return 1; 
    else return n + sum(n - 1); 
  } 
  boolean odd(int n) { 
    if (n == 0) return false; 
    else return even(n - 1);    
  }
  boolean even (int n) { 
    if (n == 0) return true; 
    else return odd(n - 1); 
  } 
}

class Example {
  public static void main(String[] args) {
    Abacus a = new Abacus();
    System.out.println(a.sum(10)); 
    System.out.println(a.odd(4)); 
    System.out.println(a.odd(5)); 
    System.out.println(a.odd(a.sum(10))); 
  }
} 
Important things about methods: We enhance the Point class with a simple clear method.
class Point {
  double x, y; 
  public void clear() {
    x = 0; 
    y = 0; 
  } 
  public double distance (Point theOtherPoint) {
    double xDiff, yDiff; 
    xDiff = x - theOtherPoint.x;
    yDiff = y - theOtherPoint.y;
    return Math.sqrt(xDiff * xDiff + yDiff * yDiff); 
  } 
}
The clear method has no parameters, hence the empty ( and ) after its name. In addition clear is declared void because it does not return any value.

Inside a method, fields and other methods of the class can be named directly - we can simply say x and y without an explicit object reference.

Objects in general do not operate directly on the data of other objects although, as we saw in the Point class, a class can make its fields publicly accessible. (In general, though, well-designed classes hide their data so that it can be changed only by methods of that class.)

To invoke a method, you provide an object reference and the method name, separated by a dot. Same rule applies for instance or class variables.

Parameters are passed to the method as a comma-separated list of values enclosed in parentheses. Methods that take no parameters still require the parentheses, with nothing between them.

The object on which the method is invoked (the object receiving the object invocation) is often known as the receiving object (or the receiver).

A method can return a single value as a result. To return more than one value from a method, create an object whose sole purpose is to hold return values in a single unit, and return that object.

We have also enhanced the Point class with a distance method. The distance method accepts another point object as a parameter, computes the Euclidean distance between itself and the other point, and returns a double precision floating-point result.

Based on our lowerLeft and upperRight objects created earlier in this section one could invoke distance as indicated in line [1] in the first code snippet above. Also note that so far we have been talking only about instance (or per object) methods.


Occasionally, the receiving object needs to know its own reference. For example, the receiving object might want to add itself to a list of objects somewhere.

An implicit reference called this is available to (instance) methods, ...and this is a reference to the current (receiving) object.

The following definition of clear is equivalent to the one just presented:

public void clear() {
  this.x = 0; 
  this.y = 0; 
}
You usually use this as a parameter to other methods that need an object reference.

The this reference can also be used to explicitly name the members of the current object. Here's another one of Point's methods called move that sets the x and y fields to specified values.

class Point {
  double x, y; 
  public static Point origin = new Point();  // explain! 
  public void clear() {
    this.x = 0; 
    this.y = 0; 
  } 
  public double distance (Point theOtherPoint) {
    double xDiff, yDiff; 
    xDiff = x - theOtherPoint.x;
    yDiff = y - theOtherPoint.y;
    return Math.sqrt(xDiff * xDiff + yDiff * yDiff); 
  } 
  public void move(double x, double y) {
    this.x = x; 
    this.y = y; 
  } 
}
Hey, a Point is a Robot as seen from an airplane, isn't it?

This move method uses this to clarify which x and y are being referred to. Naming the parameters of move "x" and "y" is reasonable, because you pass x and y coordinates to the method.

But then those parameters have the same names as Point's fields, and therefore the parameter names are said to hide the field names.

(If we simply write

x = x
we would assign the value of the x parameter to itself, not to the x field as required. The expression
this.x
refers to the object's x field, not the x parameter of move).

So we review and say once again that variables could belong to

In addition you also have parameters, which are like local variables, except their values are provided by whoever calls the method (and supplies the values of the parameters, or arguments).

It is important to keep this distinction in mind.

Just as you can have per-class (static) fields, you can also have per-class (static) methods, often known as class methods.

And Math.sqrt(...) is also a class method.

Class methods are usually intended to do class-like operations specific to the class itself, and usually on static fields, not on specific instances of that class. Class methods are declared using the static keyword, and are therefore also known as static methods.

A static method cannot directly access non-static members. (One normally fully understands this only at compile time, and not just by reading it here). When a static method is invoked, there's no specific object reference for the method to operate on, (so using this in a static method is completely inappropriate and causes an error).

An explicit object reference can be passed to the static method as a parameter if we want to work on (or with) a certain specific instance, but in general static methods do class kind of operations and non-static methods do object kind of things.

This is the end of the introduction. But every end is a new beginning so let's go over the same things again now, and in a somewhat more systematic manner.

All Over Again

The fundamental unit of programming in Java is the class. Classes contain methods - collections of executable code that are the focus of computation. Classes also provide the structure for objects, plus the mechanism to manufacture objects from the class definitions (the so-called constructors).

You can compute with only primitive types - integer, floating-point, and so on - but almost any interesting Java program will create and manipulate objects.

Each object is an instance of a class.

A newly created object is given an initial state.

When a method is invoked on an object, the class is examined to find the code to be run.

The basics of a class are:

Here's a simple class called Body that could be used to store data about celestial bodies such as comets, asteroids, planets, and stars.

Note: One should not argue for (or against) the usefulness of this example. We are not trying to be practical at all cost, we simply want to understand the nature of things. At the same time, one should always remember that Java was, after all, invented from people from Sun. (As everybody knows the Sun is located behind a huge firewall and has lots of intranets inside, but that's already known).

So here's a simple class called Body that could be used to store data about celestial bodies such as comets, asteroids, planets, and stars.

class Body {
  public long idNum; 
  public String nameFor;
  public Body orbits; 

  public static long nextID = 0; 
}
First we declare the name of the class.

A class declaration creates a type name in Java, so that references to objects of that type can be declared with a simple

Body mercury;
This declaration states that mercury is a reference to an object of class Body.

The declaration does not create an object - it declares only a reference to a Body object. The reference is initially null, and the object referenced by mercury does not actually exist until you create it explicitly. (In this respect Java is different from languages where objects are created when you declare variables - this just for the record.)

This first version of Body is poorly designed. This is intentional: we will demonstrate the value of certain language features as we improve the class below. Recall that a class's variables are called fields; the Body class's nameFor and orbits are examples. They are instance variables.

Every Body object has its own specific instances of these fields:

Giving each separate object a different instance of the fields means that each object has its own unique state.

Changing the orbits field in one Body object does not affect the orbits field in any other Body object.

Sometimes, though, you want only one instance of a field shared by all objects of a class.

You obtain such fields by declaring them static, so they are called static fields or class variables.

When you declare a static field in a class, all objects created from that class share a single copy of that field.

In our case Body has one static field, nextID, which contains the next body identifier to use.

The nextID field is initialized to zero when the class is initialized after it is loaded and linked.

We will see below that each newly created Body object will have the current value of nextID as its identifier.

All fields and methods of a class are always available to (the) code (that is described) in the class itself. Members declared private are accessible only in the class itself. Members declared public are accessible anywhere the class is accessible, and they are inherited by subclasses.

We declared the Body class's fields public because programmers need access to them to do the work the class is designed for. In a later version of the Body class, we will see that such a design is not usually a good idea.

Body sun = new Body(); 
sun.idNum = Body.nextID++;
sun.nameFor = "Sol"; 
sun.orbits = null; // in solar system sun is in the middle

Body earth = new Body();
earth.idNum = Body.nextID++;
earth.nameFor = "Earth"; 
earth.orbits(sun); 
In this first version of Body, objects that represent particular celestial bodies are created and initialized.

First we declare two references (sun and earth) to hold objects of type Body. As mentioned before, these declarations do not create objects; they only declare variables that reference objects. The references are initially null and the objects they may reference must be created explicitly.

We create the sun using the new operator. When you create an object with the new operator, you specify the type of object you want to create and any parameters to its constructor.

The Java runtime system allocates enough space to store the fields of the object and initializes it in ways you will soon see. When initialization is complete, the runtime system returns a reference to the new object.

Having created a new Body object, we initialize its variables. Each Body object needs a unique identifier, which it gets from the static nextID field of Body. The code must increment nextID so that the next Body object created will get a unique identifier.

This example builds a solar system model. In this model, the Sun is in the center, and sun's orbits field is null because it doesn't orbit anything. When we create and initialize earth, we set its orbits field to sun. A Moon object that orbited the Earth would have its orbits field set to earth.

Constructors A newly created object is given an initial state. Fields can be initialized with a value when they are declared, which is sometimes sufficient to ensure a correct initial state (the rule is that if no value is assigned to a field it will be a zero, \u0000, false or null, depending on its type).

But often you need more than simple data initialization to create the initial state; the creating code may need to supply initial data, or perform operations that cannot be expressed as simple assignment. For purposes other than simple initialization, classes can have constructors.

Constructors have the same name as the class they initialize. Like methods they take zero or more parameters, but constructors are not methods and thus have no return type. Parameters, if any, are provided between the parentheses that follow the type name when the object is created with new. Constructors are invoked after the instance variables of a newly created object of the class have been assigned their default initial values, and after their explicit initializers are executed.

This improved version of the Body class uses constructors to set up the initial state, partly by initialization and partly by the constructor:

class Body {
  public long idNum; 
  public String name = "<unnamed>"; 
  public Body orbits = null; 

  private static long nextID = 0; 

  Body() {
    idNum = nextID++; 
  } 
}
The constructor for Body takes no arguments, but it performs an important function, namely, assigning a proper idNum to the newly created object. In the original code, a simple programmer error -- forgetting to assign the idNum, or not incrementing nextID after use -- could result in different Body objects with the same idNum, creating bugs in code relying on the part of the contract that says "all idNum values are different".

By moving responsibility for idNum generation inside the Body class, we have prevented errors of this kind. The Body constructor is now the only entity that assigns idNum, and is therefore the only entity needing access to nextID. We can and should make nextID private, so that only the Body class can access it. By doing so, we remove a source of error for programmers using the Body class.

You can call this encapsulation, if you want. We are now free to change the way idNums are assigned to Body objects. A future implementation of this class might, for example, look up the name in a database of known astronomical entities and assign a new idNum only if an idNum had not already been assigned. This change would not affect any existing code, because that existing code wouldn't have been involved at all in the mechanism for idNum allocation.

The data initializations for name and orbits set them to reasonable values. Therefore, when the constructor returns from the invocation shown below, all data fields in the new Body object have been set to some reasonable initial state. You can then set state in the object to the values you want:

Body sun = new Body();   // idNum is 0
sun.name = "Sol"; 

Body earth = new Body(); // idNum is 1
earth.name = "Earth";
earth.orbits = sun;
The Body constructor is invoked while the new operator creates the object, but after name and orbits have been set to their initial values. Initializing orbits to null means that sun.orbits doesn't need to be set in our code.

The case shown here, where you create a body knowing its name and what it orbits, is likely to be fairly common. You can provide another constructor that takes both the name and the orbited body:

Body(String bodyName, Body orbitsAround) {
  this(); 
  name = bodyName; 
  orbits = orbitsAround; 
}
As shown here, one constructor can invoke another constructor from the same class using the this() invocation as its first executable statement. This is called an explicit constructor invocation. If the constructor you want to invoke has parameters, they can be passed to the constructor invocation. Here we use it to call the constructor that has no arguments in order to set up the idNum. Now the allocation code is much simpler:
Body sun = new Body("Sol", null); 
Body earth = new Body("Earth", sun); 
You could, if you wanted, provide a one-argument constructor for those cases where you're constructing a Body object that doesn't orbit anything, rather than invoking the two-argument Body constructor with a second argument of null.

Some classes always require that the creator supply certain kinds of data. For example, your application might require that all Body objects have a name. To ensure that all statements creating Body objects supply a name, you would define all Body constructors with a name parameter.

Here are some common reasons for providing specialized constructors:

Constructors without arguments are so common that there is a term for them: they are called no-arg (for "no arguments") constructors. How many of them can you have?

If you don't provide any constructors of any kind in a class, the language provides a default no-arg constructor that does nothing. This constructor is provided automatically only if no other constructors exist because there are classes for which a no-arg constructor would be incorrect! Think, for example, of a Fractions class, with only two instance variables (a numerator and a denominator, both of type int,) where zero divided by zero (which is what the default constructor would give you) would induce severe discomfort.

If you want both a no-arg constructor and one or more constructors with arguments, you can explicitly provide a no-arg constructor. The automatically provided no-arg constructor for a class that has no superclass is equivalent to the following
public class SimpleClass { 
  public SimpleClass () { 
    // same functionality as the default constructor  
  } 
} // a Potato, by any other name... 
The default constructor is public if the class is, and not if the class isn't.

Methods A class's methods typically contain the code that understands and manipulates an object's state. Some classes have public fields for programmers to manipulate directly, but in most cases this isn't a very good idea. Many objects have tasks that cannot be represented as a simple value to be read or modified, but require computation.

Methods are invoked as operations on objects via references using the . (dot) operator:

objectReference.methodName(parameters)
Each method takes a specific number of parameters. Java does not include methods that can accept a variable number of parameters. Each parameter has a specified type, either a primitive type or a reference type. Methods also have a return type, which is declared before the method name. For example here is a method of the Body class to create a String that describes a particular Body object:
public String toString() {
  String desc = idNum + " (" + name + ")"; 
  if (orbits != null) 
    desc += " orbits " + orbits.toString(); 
  return desc; 
}
This method uses += and -= to concatenate String objects. It first builds a string that describes the identifier and name. If the body orbits another body, we append the string that describes that body by invoking its toString method. This recursion builds a string of bodies orbiting other bodies until the chain ends with some object that doesn't orbit anything.

The toString method is special. If an object has a method named toString that takes no parameters and returns a String, it is invoked to get a String when that object is used in a string concatenation using the + operator. In these expressions:

System.out.println("Body " + sun); 
System.out.println("Body " + earth);
the toString methods of sun and earth are invoked implicitly, and produce the following output:
Body 0 (Sol)
Body 1 (Earth) orbits 0 (Sol)
Methods can return more than one result in several ways: return references to objects that store results as fields, take one or more parameters that reference objects in which to store the results, or return an array containg the results.

If a method does not return any value, the place where a return type would go is filled with a void. In methods that return a value, every path through the method must return a value assignable to a variable of the declared return type.

Parameter Values to Methods All parameters to methods Java are "call by value" That is, values of parameter variables in a method are copies of the values the invoker specified. If you pass a boolean to a method, its parameter is a copy of whatever value was being passed, and the method can change it without affecting values in the code that invoked the method. For example:

class PassByValue {
  public static void main(String[] args) {
    double one = 1.0; 

    System.out.println("before: one = " + one); 
    halveIt(one); 
    System.out.println("after:  one = " + one); 
  } 

  public static void halveIt (double arg) {
    arg /= 2.0; // divide the arg(ument) by two
    System.out.println("halved: arg = " + arg); 
  }
}
The following output illustrates that the value of arg inside halveIt is divided by two without affecting the value of the variable one in main:
before: one = 1
halved: arg = 0.5
after:  one = 1
When the parameter is an object reference, however, the object reference is what is passed "by value," not the object itself. Thus, you can change which object a parameter refers to inside the method without affecting the reference that was passed. But if you change any fields of the objects, or invoke methods that change the object's state, the object is changed for every part of the program that holds a reference to it. Here's an example to show this distinction:

class PassRef {
  public static void main(String[] args) {
    Body sirius = new Body("Sirius", null); 

    System.out.println("before: " + sirius); 
    /*PassRef.*/commonName(sirius); 
    System.out.println("after:  " + sirius); 
  } 
  public static void commonName (Body bodyRef) {
    bodyRef.name = "Dog Star"; 
    bodyRef = null; 
  } 
}
This produces the following output:

before: 0 (Sirius)
after:  0 (Dog Star)
Notice that the contents of the object have been modified with a name change, while the reference bodyRef still refers to the Body object, even though commonName changed the value of its bodyRef parameter to null. One could draw a picture to show the state of the references just as main invokes commonName. At that point, the two references sirius (in main) and bodyRef (in commonName) both refer to the same underlying object. When commonName changes the field bodyRef.name, the name is changed in the underlying object that the two references share. When commonName changes the value of bodyRef to null, only the value of the bodyRef reference is changed, while the value of sirius remains unchanged, since the parameter bodyRef is a pass by value copy of sirius. Inside the method commonName all you are changing is the value in the parameter variable bodyRef, just as all you changed in halveIt was the value in the parameter variable, arg. If changing bodyRef affected the value of sirius in main, the "after" line would say "null". However, the variables bodyRef in commonName and sirius in main both refer to the same underlying object so the change made inside commonName is reflected in the object that sirius refers to.

Using Methods to Control Access The Body class with its various constructors is considerably easier to use than its simple data-only form, and we have ensured that the idNum is set both automatically and correctly. But a programmer could still mess up the object by setting its idNum field after construction, because the idNum field is public and therefore exposed to change.

The idNum should be read-only data.

Read-only in objects is common, but there is no keyword to apply to a field that allows read-only access outside the class.

To enforce read-only access, you must

  1. first hide the field, then
  2. provide regulated access to it
You do this by
  1. making the idNum field private and
  2. providing a new method say, id()
so that code outside the class can read its value using that method.
class Body {
  private long idNum; // now "private" 

  public String name = "<unnamed>"; 
  public Body orbits = null;
  private static long nextID = 0; 

  Body() {
    idNume = nextID++; 
  }

  public long id() {
    return idNum; 
  }

  // ... 

}
Now programmers who want to use the body's identifier will invoke the id() method, which returns the value (recall that a value can be used in an expression). There is no longer any way for the programmers to modify the identifier directly at all: it has effectively become a read-only value outside the class. It can be modified only by the internal methods of the Body class.

Methods that regulate access to internal data are sometimes called accessor methods. You could also use accessor methods to protect the name and orbits fields, and you probably should.

Even if an application doesn't require fields to be read-only, making fields private and adding methods to set and fetch them enables you to add actions that may be needed in the future. If programmers can access a class's fields directly, you have no control over what values they will use or what happens when values are changed, and that should make you very nervous.

To summarize:

Most methods are effectors even more so when they do something else besides setting or fetching.

The this (Host) Reference We have already seen how you can use an explicit constructor invocation to invoke another one of your class's constructors at the beginning of a constructor. You can also use the special object reference this inside a non-static method, where it refers to the current object on which the method was invoked. The this reference is most commonly used as a way to pass a reference to the current object as a parameter to other methods. Suppose a method requires adding the object to a list of objects awaiting some service. It might look something like this:

Service.add(this);
In this example the method's name is add and by the conventions we always use Service is a class and therefore the method is a static member of that class. The statement above would occur in an instance method, somewhere (belonging to some object that says: "this object also needs service").

Let's look at another example.

The assignment to str in this class:

class Name {
  public String str; 
  Name() {
    str = "<unnamed>"; 
  } 
}
is equivalent to the following:
this.str = "<unnamed>"
Conventionally, you use this only when needed which is when the name of the field you need to access is hidden by a variable or parameter declaration. For example:

class SwedishChef {
  String chefsName;

  SwedishChef(String chefsName) {
    this.chefsName = chefsName; 
  } 
}
The chefsName field is hidden inside the constructor by the parameter of the same name. To ensure we access the chefsName field instead the chefsName parameter, we prefix it with this to specify that the field is the one belonging to this object, the one that is currently being created.

Deliberately hiding identifiers in this manner is considered good programming practice only in this idiomatic use in constructors and accessor methods. But when you start programming it's better to always use an object reference to refer to instance variables of any object, and a class name to refer to class (static) variables of a class, for any class, thus leaving local variables and parameters easily detectable by their not having to use any dots in qualifying their names.

Overloading Methods In Java, each method has a signature, which is its name together with the number and types of its parameters. Two methods can have the same name if their signatures have different numbers or types of parameters. This feature is called overloading, because the simple name of the method has overloaded (more than one) meaning. When a programmer invokes a method, the compiler compares the number and type of parameters to find the method that best matches the available signatures. (It works the same with constructors).

Here are some orbitsAround methods for our Body class.

public Body orbitsAround() {
  return orbits; 
}
public void orbitsAround(Body around) {
  orbits = around; 
}
This example deserves careful attention. The methods are written using a programming style that uses overloading to differentiate between fetching a value (no parameters) and setting the value (a parameter with the new value). The number of parameters is different so the overload resolution is simple. If orbitsAround is invoked with no parameters, the method that returns the current value is used. If orbitsAround is invoked with one argument that is a Body, the method that sets the value is used. If the invocation matches neither of these signatures, it is invalid, and the code will not compile.

Static Members A class has two kinds of members: fields and methods. Each member specifies how it may be accessed and how it may be inherited. Each member can also be made static if so desired.

A static member is a member that is only one per class, rather than one in every object created from that class. For static fields (class variables), there is exactly one variable, no matter how many objects (even zero) there are of the class. The nextID field in our Body class is an example.

The static fields of a class are initialized before any static field in that class is used or any method of that class is run.

A class can also have static initialization blocks to set up static fields or other necessary states. A static initializer is most useful when simple initialization clauses on the field declaration aren't up to the task of initialization. For example creating a static array and initializing its members often must be done with executable statements.

The order of static initialization within a class is left-to-right and top-to-bottom.

A static method is invoked on behalf of an entire class, not on a specific object instantiated from that class. Such methods are also known as class methods. A static method might perform a general task for all objects of the class -- such as returning the next available serial number or something of that nature. (You know this has to be done by Toyota "The Factory" and not by individual retailers).

A static method can access only static variables and static methods of the class. There is no this reference, because there is no specific object being operated upon.

Outside of a class, a static member is usually accessed by using the class name, rather than through an object reference.

The main Method of a Class Details of invoking a Java application vary from system to system, but whatever the details, you must always provide the name of a Java class that drives the application. When you run a Java program, the system locates and runs the main method for that class. The main method must be public, static, and void (it returns nothing), and it must accept a single argument of type String[] args. Here's an example that prints its parameters:

class Echo {
  public static void main(String[] args) {
    for (int i = 0; i < args.length; i++) 
      System.out.print(args[i] + " "); 
    System.out.println(); 
  } 
}
The arguments in the string array are the "program arguments." These are usually typed by users when they run the program. For example, on a command-line system such as UNIX or a DOS shell, you might invoke the Echo application like this:

java Echo in here
In this command, java is the Java bytecode interpreter, Echo is the name of the class, and the rest of the parameters are the program arguments. The java command finds the compiled bytecodes for the class Echo, loads them into a runtime in a virtual machine, and invokes Echo.main with the program arguments contained in strings in the String array. The result is the following output:
in here
The name of the class is not included in the strings passed to main. You already know the name because it is the name of the enclosing class, where you wrote main.

An application can have any number of main methods, since each class can have one. Only one main is used for any given program. The main that's actually used is specified when the program is run, as Echo was above. Being able to have multiple main methods has one salutary effect -- each class can have a main that tests its own code, providing an excellent hook for unit-testing a class.


Boy, this lecture was long. Yes, thank Goodness it's not double!

What's next? For the short term: chapters 5, 6, and 7.

Which one will be the most important of those? I'd say Chapter 6, and by a long shot.


Last updated: Feb 9, 2002 by Adrian German for A201