Main Page

Previous Next

Defining Methods

We have been producing versions of the method main() since Chapter 1, so you already have an idea of how a method is constructed. Nonetheless, we will go through from the beginning to make sure everything is clear.

We'll start with the fundamental concepts. A method is a self-contained block of code that has a name, and has the property that it is reusable - the same method can be executed from as many different points in a program as you require. Methods also serve to break up large and complex calculations that might involve many lines of code into more manageable chunks. A method is executed by calling its name, as we will see, and the method may or may not return a value. Methods that do not return a value are called in a statement that just does the call. Methods that do return a value are usually called from within an expression, and the value that is returned by such a method is used in the evaluation of the expression.

The basic structure of a method is shown below.

Click To expand

When you specify the return type for a method, you are defining the type for the value that will be returned by the method when you execute it. The method must always return a value of this type. To define a method that does not return a value, you specify the return type as void. Something called an access attribute can optionally precede the return type in a method definition, but we will defer looking into this until later in this chapter.

The parameters to a method appear in its definition between parentheses following the method name. These specify what information is to be passed to the method when you execute it. Your methods do not have to have parameters specified. A method that does not require any information to be passed to it when it is executed has an empty pair of parentheses after the name.

Running from a Method

To return a value from a method when its execution is complete you use a return statement, for example:

return return_value;   // Return a value from a method

After executing the return statement, the program continues from the point where the method was called. The value, return_value, that is returned by the method can be any expression that produces a value of the type specified for the return value in the declaration of the method. Methods that return a value - that is methods declared with a return type other than void - must always finish by executing a return statement that returns a value of the appropriate type. Note, though, that you can put several return statements within a method if the logic requires this. If a method does not return a value, you can just use the keyword return by itself to end execution of the method:

return;    // Return from a method

Note that, for methods that do not return a value, falling through the closing brace enclosing the body of the method is equivalent to executing a return statement.

The Parameter List

The parameter list appears between the parentheses following the method name. This specifies the type of each value that can be passed as an argument to the method, and the variable name that is to be used in the body of the method to refer to each value passed. The difference between a parameter and an argument is sometimes confusing because people often, incorrectly, use them interchangeably. We will try to differentiate them consistently, as follows:

  • A parameter has a name and appears in the parameter list in the definition of a method. A parameter defines the type of value that can be passed to the method, and the name that is used to reference it within the code for the method.

  • An argument is a value that is passed to a method when it is executed, and the value of the argument is referenced by the parameter name during execution of the method.

This is illustrated in the following diagram.

Click To expand

Here we have the definition of a method mean(). This can only appear within the definition of a class, but the rest of the class definition has been omitted so as not to clutter up the diagram. You can see that the method has two parameters, value1, and value2, both of which are of type double, that are used to refer to the arguments 3.0 and 5.0 respectively within the body of the method. Since this method has not been defined as static, you can only call it for an object of the class. We call mean() in our example for the object, obj.

When you call the method from another method (from main() in this case, but it could be from some other method), the values of the arguments passed are the initial values assigned to the corresponding parameters. You can use any expression you like for an argument when you call a method, as long as the value it produces is of the same type as the corresponding parameter in the definition of the method. With our method mean(), both parameters are of type double, so both argument values must always be of type double.

The method mean() declares the variable result, which only exists within the body of the method. The variable is created each time you execute the method and it is destroyed when execution of the method ends. All the variables that you declare within the body of a method are local to the method, and are only around while the method is being executed. Variables declared within a method are called local variables because they are local to the method. The scope of a local variable is as we discussed in Chapter 2, and local variables are not initialized automatically. If you want your local variables to have initial values you must supply the initial value when you declare them.

How Argument Values Are Passed to a Method

You need to be clear about how your argument values are passed to a method, otherwise you may run into problems. In Java, all argument values that belong to one of the basic types are transferred to a method using what is called the pass-by-value mechanism. How this works is illustrated below.

Click To expand

This just means that for each argument value that you pass to a method, a copy is made, and it is the copy that is passed to the method and referenced through the parameter name, not the original value. This implies that if you use a variable of any of the basic types as an argument, the method cannot modify the value of this variable in the calling program. In the example shown, the method change() will modify the copy of i that is created automatically, so the value of j that is returned will be 11 and this will be stored in x. However, the original value of i will remain at 10.

Important 

While the pass-by-value mechanism applies to all types of arguments, the effect for objects is different from that for variables of the basic types. You can change an object, as we shall see a little later in this chapter, because a copy of a reference to the object is passed to the method, not a copy of the object itself.

Final Parameters

You can specify any method parameter as final. This has the effect of preventing modification of any argument value that is substituted for the parameter when you call the method. The compiler will check that your code in the body of the method does not attempt to change any final parameters. Since the pass-by-value mechanism makes copies of values of the basic types, final really only makes sense when it is applied to parameters that are references to class objects, as we will see later on.

Specifying a parameter of a class as final is of limited value. It does prevent accidental modification of the object reference that is passed to the method, but it does not prevent modification of the object itself.

Defining Class Methods

You define a class method by adding the keyword static to its definition. For example, the class Sphere could have a class method to return the value stored in the static variable, count:

class Sphere {
  // Class definition as before...
  // Static method to report the number of objects created
  static int getCount() {
    return count;                           // Return current object count
  }
}

This method needs to be a class method because we want to be able to get at the count of the number of objects even when it is zero. You can amend the Sphere.java file to include the definition of getCount().

Important 

Note that you cannot directly refer to any of the instance variables in the class within a static method. This is because your static method may be executed when no objects of the class have been created, and therefore no instance variables exist.

Accessing Class Data Members in a Method

An instance method can access any of the data members of the class, just by using the appropriate name. Let's extend the class Sphere a little further by adding a method to calculate the volume of a Sphere object:

class Sphere {
  static final double PI = 3.14; // Class variable that has a fixed value
  static int count = 0;          // Class variable to count objects

  // Instance variables
  double radius;                 // Radius of a sphere

  double xCenter;                // 3D coordinates
  double yCenter;                // of the center
  double zCenter;                // of a sphere

  // Static method to report the number of objects created
  static int getCount(){
    return count;                // Return current object count
  }

  // Instance method to calculate volume
  double volume() {
    return 4.0/3.0*PI*radius*radius*radius;
  }

  // Plus the rest of the class definition...
}

You can see that the method volume() is an instance method because it is not declared as static. It has no parameters but it does return a value of type double - the required volume. The method uses the class variable PI and the instance variable radius in the volume calculation - this is the expression 4.0/3.0*PI*radius*radius*radius (4/3)(r3 in the return statement. The value that results from this expression will be returned to the point where the method is called for a Sphere object.

We know that each object of the class will have its own separate set of instance variables, so how is an instance variable for a particular object selected in a method? How does our volume() method pick up the radius for a particular Sphere object?

The Variable this

Every instance method has a variable with the name, this, which refers to the current object for which the method is being called. The compiler uses this implicitly when your method refers to an instance variable of the class. For example, when the method volume() refers to the instance variable radius, the compiler will insert the this object reference, so that the reference will be equivalent to this.radius. The return statement in the definition of the volume() method is actually:

return 4.0/3.0*PI*this.radius*this.radius*this.radius;

In general, every reference to an instance variable is in reality prefixed with this. You could put it in yourself, but there's no need, the compiler does it for you. In fact, it is not good practice to clutter up your code with this unnecessarily. However, there are occasions where you have to include it, as we shall see.

When you execute a statement such as:

double ballVolume = ball.volume();

where ball is an object of the class Sphere, the variable this in the method volume() will refer to the object ball, so the instance variable radius for this particular object will be used in the calculation.

Important 

We mentioned earlier that only one copy of each instance method for a class exists in memory, even though there may be many different objects. You can see that the variable this allows the same instance method to work for different class objects. Each time an instance method is called, the this variable is set to reference the particular class object to which it is being applied. The code in the method will then relate to the specific data members of the object referred to by this.

We have seen that there are four different potential sources of data available to you when you write the code for a method:

  • Arguments passed to the method, which you refer to by using the parameter names.

  • Data members, both instance variables and class variables, which you refer to by their variable names.

  • Local variables declared in the body of the method.

  • Values that are returned by other methods that are called from within the method.

The names of variables that are declared within a method are local to the method. You can use a name for a local variable or a parameter in a method that is the same as that of a class data member. If you find it necessary to do this then you must use the name this when you refer to the data member of the class from within the method. The variable name by itself will always refer to the variable that is local to the method, not the instance variable.

For example, let us suppose we wanted to add a method to change the radius of a Sphere object to a new radius value that is passed as an argument. We could code this as:

void changeRadius(double radius) {
  // Change the instance variable to the argument value
  this.radius = radius; 
}

In the body of the changeRadius() method, this.radius refers to the instance variable, and radius by itself refers to the parameter. There is no confusion in the duplication of names here. It is clear that we are receiving a radius value as a parameter and storing it in the radius variable for the class object.

Initializing Data Members

We have seen how we were able to supply an initial value for the static members PI and count in the Sphere class with the declaration:

class Sphere {
  static final double PI = 3.14;          // Class variable that has a fixed value
  static int count = 0;                   // Class variable to count objects

  // Rest of the class...
}

We can also initialize ordinary non-static data members in the same way. For example:

class Sphere {
  static final double PI = 3.14;          // Class variable that has a fixed value
  static int count = 0;                   // Class variable to count objects

  // Instance variables
  double radius = 5.0;                    // Radius of a sphere

  double xCenter = 10.0;                  // 3D coordinates
  double yCenter = 10.0;                  // of the center
  double zCenter = 10.0;                  // of a sphere

  // Rest of the class...
}

Now every object of type Sphere will start out with a radius of 5.0 and have the center at the point 10.0, 10.0, 10.0.

There are some things that can't be initialized with a single expression. If you had a large array as a data member for example, that you wanted to initialize, with a range of values that required some kind of calculation, this would be a job for an initialization block.

Using Initialization Blocks

An initialization block is a block of code between braces that is executed before an object of the class is created. There are two kinds of initialization blocks. A static initialization block is a block defined using the keyword, static, and that is executed once when the class is loaded and can only initialize static data members of the class. A non-static initialization block is executed for each object that is created and thus can initialize instance variables in a class. This is easiest to understand by considering specific code.

Try It Out - Using an Initialization Block

Let's define a simple class with a static initialization block first of all:

class TryInitialization {
  static int[] values = new int[10];               // Static array  member

  // Initialization block
  static {
    System.out.println("Running initialization block.");
    for(int i=0; i<values.length; i++)
      values[i] = (int)(100.0*Math.random());
  }
  
  // List values in the array for an object
  void listValues() {
    System.out.println();                        // Start a new line
    for(int i=0; i<values.length; i++)
      System.out.print(" " + values[i]);         // Display values

    System.out.println();                        // Start a new line
  }

  public static void main(String[] args) {
    TryInitialization example = new TryInitialization();
    System.out.println("\nFirst object:");
    example.listValues();

    example = new TryInitialization();
    System.out.println("\nSecond object:");
    example.listValues();
  }
}

When you compile and run this you will get identical sets of values for the two objects - as might be expected since the values array is static:

Running initialization block.

First object:

 40 97 88 63 58 48 84 5 32 67

Second object:

 40 97 88 63 58 48 84 5 32 67

How It Works

The TryInitialization class has a static member, values, that is an array of 10 integers. The static initialization block is the code:

  static {
    System.out.println("Running initialization block.");
    for(int i=0; i<values.length; i++)
      values[i] = (int)(100.0*Math.random());
  }

This initializes the values array with pseudo-random integer values generated in the for loop. The output statement in the block is there just to record when the initialization block executes. Because this initialization block is static, it is only ever executed once during program execution, when the class is loaded.

The listValues() method provides us with a means of outputting the values in the array. The print() method we are using in the listValues() method works just like println(), but without starting a new line before displaying the output, so we get all the values on the same line.

In main(), we generate an object of type TryInitialization, and then call its listValues() method. We then create a second object and call the listValues() method for that. The output demonstrates that the initialization block only executes once, and that the values reported for both objects are the same.

If you delete the modifier static from before the initialization block, and recompile and run the program again, you will get the output along the lines of:

Running initialization block.

First object:

 66 17 98 59 99 18 40 96 40 21

Running initialization block.

Second object:

 57 86 79 31 75 99 51 5 31 44

Now we have a non-static initialization block. You can see from the output that the values are different for the second object because the non-static initialization block is executed each time an object is created. In fact, the values array is static, so the array is shared between all objects of the class. You could demonstrate this by amending main() to store each object separately, and calling listValues() for the first object after the second object has been created. Amend the main() method in the program to read as follows:

  public static void main(String[] args) {
    TryInitialization example = new TryInitialization();
    System.out.println("\nFirst object:");
    example.listValues();
    TryInitialization nextexample = new TryInitialization();
    System.out.println("\nSecond object:");
    nextexample.listValues();

    example.listValues();
  }

While we have demonstrated that this is possible, you will not normally want to initialize static variables with a non-static initialization block.

As we said at the outset, a non-static initialization block can initialize instance variables too. If you want to demonstrate this too, you just need to remove the static modifier from the declaration of values and compile and run the program once more.

You can have multiple initialization blocks in a class, in which case they execute in the sequence in which they appear. The static blocks execute when the class is loaded and the non-static blocks execute when each object is created. Initialization blocks are useful, but you need more than that to create objects properly.

Previous Next
JavaScript Editor Java Tutorials Free JavaScript Editor