Team LiB
Previous Section Next Section

3.2. Fields and Methods

A class can be viewed as a collection of data and code to operate on that data. The data is stored in fields, and the code is organized into methods. This section covers fields and methods, the two most important kinds of class members. Fields and methods come in two distinct types: class members (also known as static members) are associated with the class itself, while instance members are associated with individual instances of the class (i.e., with objects). This gives us four kinds of members:

  • Class fields

  • Class methods

  • Instance fields

  • Instance methods

The simple class definition for the class Circle, shown in Example 3-1, contains all four types of members.

Example 3-1. A simple class and its members
public class Circle {
  // A class field
  public static final double PI= 3.14159;     // A useful constant

  // A class method: just compute a value based on the arguments
  public static double radiansToDegrees(double rads) { 
    return rads * 180 / PI; 
  }

  // An instance field
  public double r;                  // The radius of the circle

  // Two instance methods: they operate on the instance fields of an object
  public double area() {            // Compute the area of the circle
    return PI * r * r; 
  }
  public double circumference() {   // Compute the circumference of the circle
    return 2 * PI * r; 
  }
}

The following sections explain all four kinds of members. First, however, we cover field declaration syntax. (Method declaration syntax is covered in Section 2.6 later in this chapter.)

3.2.1. Field Declaration Syntax

Field declaration syntax is much like the syntax for declaring local variables (see Chapter 2) except that field definitions may also include modifiers. The simplest field declaration consists of the field type followed by the field name. The type may be preceded by zero or more modifier keywords or annotations (see Chapter 4), and the name may be followed by an equals sign and initializer expression that provides the initial value of the field. If two or more fields share the same type and modifiers, the type may be followed by a comma-separated list of field names and initializers. Here are some valid field declarations:

int x = 1;
private String name;
public static final DAYS_PER_WEEK = 7;
String[] daynames = new String[DAYS_PER_WEEK];
private int a = 17, b = 37, c = 53;

Field modifiers are comprised of zero or more of the following keywords:


public , protected, private

These access modifiers specify whether and where a field can be used outside of the class that defines it. These important modifiers are covered in Section 3.6 later in this chapter. No more than one of these access modifiers may appear in any field declaration.


static

If present, this modifier specifies that the field is associated with the defining class itself rather than with each instance of the class.


final

This modifier specifies that once the field has been initialized, its value may never be changed. Fields that are both static and final are compile-time constants that the compiler can inline. final fields can also be used to create classes whose instances are immutable.


transient

This modifier specifies that a field is not part of the persistent state of an object and that it need not be serialized along with the rest of the object. Serialization is covered in Chapter 5.


volatile

Roughly speaking, a volatile field is like a synchronized method: safe for concurrent use by two or more threads. More accurately, volatile says that the value of a field must always be read from and flushed to main memory, and that it may not be cached by a thread (in a register or CPU cache).

3.2.2. Class Fields

A class field is associated with the class in which it is defined rather than with an instance of the class. The following line declares a class field:

public static final double PI = 3.14159;

This line declares a field of type double named PI and assigns it a value of 3.14159. As you can see, a field declaration looks quite a bit like a local variable declaration. The difference, of course, is that variables are defined within methods while fields are members of classes.

The static modifier says that the field is a class field. Class fields are sometimes called static fields because of this static modifier. The final modifier says that the value of the field does not change. Since the field PI represents a constant, we declare it final so that it cannot be changed. It is a convention in Java (and many other languages) that constants are named with capital letters, which is why our field is named PI, not pi. Defining constants like this is a common use for class fields, meaning that the static and final modifiers are often used together. Not all class fields are constants, however. In other words, a field can be declared static without being declared final. Finally, the public modifier says that anyone can use the field. This is a visibility modifier, and we'll discuss it and related modifiers in more detail later in this chapter.

The key point to understand about a static field is that there is only a single copy of it. This field is associated with the class itself, not with instances of the class. If you look at the various methods of the Circle class, you'll see that they use this field. From inside the Circle class, the field can be referred to simply as PI. Outside the class, however, both class and field names are required to uniquely specify the field. Methods that are not part of Circle access this field as Circle.PI.

A public class field is essentially a global variable. The names of class fields are qualified by the unique names of the classes that contain them, however. Thus, Java does not suffer from the name collisions that can affect other languages when different modules of code define global variables with the same name.

3.2.3. Class Methods

As with class fields, class methods are declared with the static modifier:

public static double radiansToDegrees(double rads) { return rads * 180 / PI; }

This line declares a class method named radiansToDegrees(). It has a single parameter of type double and returns a double value. The body of the method is quite short; it performs a simple computation and returns the result.

Like class fields, class methods are associated with a class, rather than with an object. When invoking a class method from code that exists outside the class, you must specify both the name of the class and the method. For example:

// How many degrees is 2.0 radians?
double d = Circle.radiansToDegrees(2.0);

If you want to invoke a class method from inside the class in which it is defined, you don't have to specify the class name. However, it is often good style to specify the class name anyway, to make it clear that a class method is being invoked.

Note that the body of our Circle.radiansToDegrees( ) method uses the class field PI. A class method can use any class fields and class methods of its own class (or of any other class). But it cannot use any instance fields or instance methods because class methods are not associated with an instance of the class. In other words, although the radiansToDegrees() method is defined in the Circle class, it does not use any Circle objects. The instance fields and instance methods of the class are associated with Circle objects, not with the class itself. Since a class method is not associated with an instance of its class, it cannot use any instance methods or fields.

As we discussed earlier, a class field is essentially a global variable. In a similar way, a class method is a global method, or global function. Although radiansToDegrees() does not operate on Circle objects, it is defined within the Circle class because it is a utility method that is sometimes useful when working with circles. In many nonobject-oriented programming languages, all methods, or functions, are global. You can write complex Java programs using only class methods. This is not object-oriented programming, however, and does not take advantage of the power of the Java language. To do true object-oriented programming, we need to add instance fields and instance methods to our repertoire.

3.2.4. Instance Fields

Any field declared without the static modifier is an instance field:

public double r;    // The radius of the circle

Instance fields are associated with instances of the class, rather than with the class itself. Thus, every Circle object we create has its own copy of the double field r. In our example, r represents the radius of a circle. Thus, each Circle object can have a radius independent of all other Circle objects.

Inside a class definition, instance fields are referred to by name alone. You can see an example of this if you look at the method body of the circumference() instance method. In code outside the class, the name of an instance method must be prefixed with a reference to the object that contains it. For example, if the variable c holds a reference to a Circle object, we use the expression c.r to refer to the radius of that circle:

Circle c = new Circle(); // Create a Circle object; store a reference in c
c.r = 2.0;               // Assign a value to its instance field r
Circle d = new Circle(); // Create a different Circle object
d.r = c.r * 2;           // Make this one twice as big

Instance fields are key to object-oriented programming. Instance fields hold the state of an object; the values of those fields make one object distinct from another.

3.2.5. Instance Methods

Any method not declared with the static keyword is an instance method. An instance method operates on an instance of a class (an object) instead of operating on the class itself. It is with instance methods that object-oriented programming starts to get interesting. The Circle class defined in Example 3-1 contains two instance methods, area( ) and circumference(), that compute and return the area and circumference of the circle represented by a given Circle object.

To use an instance method from outside the class in which it is defined, we must prefix it with a reference to the instance that is to be operated on. For example:

Circle c = new Circle();   // Create a Circle object; store in variable c
c.r = 2.0;                 // Set an instance field of the object
double a = c.area();       // Invoke an instance method of the object

If you're new to object-oriented programming, that last line of code may look a little strange. We do not write:

a = area(c);

Instead, we write:

a = c.area();

This is why it is called object-oriented programming; the object is the focus here, not the function call. This small syntactic difference is perhaps the single most important feature of the object-oriented paradigm.

The point here is that we don't have to pass an argument to c.area(). The object we are operating on, c, is implicit in the syntax. Take a look at Example 3-1 again. You'll notice the same thing in the signature of the area( ) method: it doesn't have a parameter. Now look at the body of the area( ) method: it uses the instance field r. Because the area() method is part of the same class that defines this instance field, the method can use the unqualified name r. It is understood that this refers to the radius of whatever Circle instance invokes the method.

Another important thing to notice about the bodies of the area( ) and circumference() methods is that they both use the class field PI. We saw earlier that class methods can use only class fields and class methods, not instance fields or methods. Instance methods are not restricted in this way: they can use any member of a class, whether it is declared static or not.

3.2.5.1 How instance methods work

Consider this line of code again:

a = c.area();

What's going on here? How can a method that has no parameters know what data to operate on? In fact, the area( ) method does have a parameter. All instance methods are implemented with an implicit parameter not shown in the method signature. The implicit argument is named this; it holds a reference to the object through which the method is invoked. In our example, that object is a Circle.

The implicit this parameter is not shown in method signatures because it is usually not needed; whenever a Java method accesses the instance fields in its class, it is implicit that it is accessing fields in the object referred to by the this parameter. The same is true when an instance method invokes another instance method in the same class. I said earlier that to invoke an instance method you must prepend a reference to the object to be operated on. When an instance method is invoked within another instance method in the same class, however, you don't need to specify an object. In this case, it is implicit that the method is being invoked on the this object.

You can use the this keyword explicitly when you want to make it clear that a method is accessing its own fields and/or methods. For example, we can rewrite the area() method to use this explicitly to refer to instance fields:

public double area() { return Circle.PI * this.r * this.r; }

This code also uses the class name explicitly to refer to class field PI. In a method this simple, it is not necessary to be explicit. In more complicated cases, however, you may find that it increases the clarity of your code to use an explicit this where it is not strictly required.

In some cases, the this keyword is required, however. For example, when a method parameter or local variable in a method has the same name as one of the fields of the class, you must use this to refer to the field since the field name used alone refers to the method parameter or local variable. For example, we can add the following method to the Circle class:

public void setRadius(double r) {
  this.r = r;      // Assign the argument (r) to the field (this.r)
                   // Note that we cannot just say r = r
}

Finally, note that while instance methods can use the this keyword, class methods cannot. This is because class methods are not associated with objects.

3.2.5.2 Instance methods or class methods?

Instance methods are one of the key features of object-oriented programming. That doesn't mean, however, that you should shun class methods. In many cases, it is perfectly reasonable to define class methods. When working with the Circle class, for example, you might find that you often want to compute the area of a circle with a given radius but don't want to bother creating a Circle object to represent that circle. In this case, a class method is more convenient:

public static double area(double r) { return PI * r * r; }

It is perfectly legal for a class to define more than one method with the same name, as long as the methods have different parameters. Since this version of the area() method is a class method, it does not have an implicit this parameter and must have a parameter that specifies the radius of the circle. This parameter keeps it distinct from the instance method of the same name.

As another example of the choice between instance methods and class methods, consider defining a method named bigger( ) that examines two Circle objects and returns whichever has the larger radius. We can write bigger( ) as an instance method as follows:

// Compare the implicit "this" circle to the "that" circle passed
// explicitly as an argument and return the bigger one. 
public Circle bigger(Circle that) {
  if (this.r > that.r) return this;
  else return that;
}

We can also implement bigger( ) as a class method as follows:

// Compare circle a to circle b and return the one with the larger radius
public static Circle bigger(Circle a, Circle b) {
  if (a.r > b.r) return a;
  else return b;
}

Given two Circle objects, x and y, we can use either the instance method or the class method to determine which is bigger. The invocation syntax differs significantly for the two methods, however:

Circle biggest = x.bigger(y);          // Instance method: also y.bigger(x)
Circle biggest = Circle.bigger(x, y);  // Static method

Both methods work well, and, from an object-oriented design standpoint, neither of these methods is "more correct" than the other. The instance method is more formally object-oriented, but its invocation syntax suffers from a kind of asymmetry. In a case like this, the choice between an instance method and a class method is simply a design decision. Depending on the circumstances, one or the other will likely be the more natural choice.

3.2.6. Case Study: System.out.println( )

Throughout this book, we've seen a method named System.out.println() used to display output to the terminal window or console. We've never explained why this method has such an long, awkward name or what those two periods are doing in it. Now that you understand class and instance fields and class and instance methods, it is easier to understand what is going on: System is a class. It has a class field named out. The field System.out refers to an object. The object System.out has an instance method named println( ). If you want to explore this in more detail, you can look up the java.lang.System class in the reference section. The class synopsis there tells you that the field out is of type java.io.PrintStream, and you can look up that class to find out about the println( ) method.

    Team LiB
    Previous Section Next Section