In this article, we will provide an overview of Java Classes. We will explore the details of how a class definition is put together, how to create your own classes, and how to use classes to solve our computing problems. We will describe the the definition of Java classes, including field and method definition. We will also discuss the concept of encapsulation and the benefits derived from it when developing components in Java. This is part of a larger series of articles to help you prepare for the java associate exam. The previous article in the series was, "Tutorial:Review of Java Arrays for the Java Certification Exam".

What is a Java Class

Classes specify the objects we use in object-oriented programming. They form the basic building blocks of any Java program. Every program in Java consists of classes because the code for a program can appear only within a class definition. It is a blueprint for a particular kind of object. It defines a new type. You can use the definition of a class to create objects of that particular class type that incorporates all the components specified as belonging to that class.

In most cases your own classes will be straightforward. You generally define a class to suit the needs of your application, and you will make it as simple or complex as necessary. Some classes, such as a Ship or a Person, may well represent objects that can potentially be very complex, but the application requirements may be very limited. A Person object can contain a name, address, and phone number, for example, if you are just implementing an address book. In another context, such as in a payroll program, you might need to represent a Person with a whole host of properties, such as age, marital status, length of service, job code, pay rate, and so on. The complexity of the class you define depends on what you intend to do with objects of your class. In essence, a class definition is very simple. There are just two kinds of things that you can include in a class definition:
  • Fields: These are variables that store data items that typically differentiate one object of the class from another. They are also referred to as data members of a class.
  • Methods: These define the operations you can perform for the class—so they determine what you can do to, or with, objects of the class. Methods typically operate on the fields or the data members of the class.

The fields in a class definition can be of any of the primitive types, or they can be references to objects of any class type, including the one that you are defining.

The methods in a class definition are named, self-contained blocks of code that typically operate on the fields that appear in the class definition. This doesn’t always apply from the main() methods you see in certain Java classes.

Fields in a Class Definition

An object of a class is also referred to as an instance of that class. When you create an object, the object contains all the fields that were included in the class definition. However, the fields in a class definition are not all the same there are two kinds.
  • Non-static fields, also called instance variables: Each object of the class has its own copy of each of the non-static fields or instance variables that appear in the class definition. Each object has its own values for each instance variable. The name instance variable originates from the fact that an object is an instance or an occurrence of a class, and the values stored in the instance variables for the object differentiate the object from others of the same class type. You declare an instance variable within the class definition in the usual way, with a type name and a variable name, and it can have an initial value specified.
  • Static fields, also called class variables: A given class has only one copy of each of its static fields or class variables, and these are shared between and among all the objects of the class. Each class variable exists even if no objects of the class have been created. Class variables belong to the class, and they can be referenced by any object or class method, not just methods belonging to instances of that class. If the value of a static field is changed, the new value is available equally in all the objects of the class. This is quite different from non-static fields, where changing a value for one object does not affect the values in other objects. A static field must be declared using the keyword static preceding the type name. The figure below illustrates the difference between class variables and instance variables.


Tutorial:In Depth View of Java Classes for the Java Certification Exam I-d6-classandobjectrelationships.jpg
Figure: Class and Object Relationships

In the figure, we show the schematic of a class, Sphere, that has one class variable, PI, and four instance variables, radius, xCenter, yCenter, and zCenter. Each of the objects, globe and ball, has its own set of variables with the names radius, xCenter, yCenter, and zCenter, but both share a single copy of the class variable PI.

The purpose of these two kinds of variables in a class definition is that instance variables are necessary because they store the values that distinguish one particular object from another. The radius and the coordinates of the center of the sphere are fundamental to determining how big a particular Sphere object is, and where it is in space. However, although the variable PI is a fundamental parameter for every sphere to calculate the volume, for example it would be wasteful to store a value for PI in every Sphere object because it is always the same.

Methods in a Class Definition

The methods that you define for a class provide the behaviour that can be carried out using the variables specified in the class definition. Analogous to the variables in a class definition, there are two type of methods as well:
  • instance methods
  • class methods

Class methods can be executed even when no objects of a class exist, whereas instance methods can be executed only in relation to a particular object. Also like class variables, class methods are declared using the keyword static, so they are sometimes referred to as static methods.

Because static methods can be executed when there are no objects in existence, they cannot refer to instance variables. This is because trying to operate with variables that might not exist could cause trouble so the Java compiler forbids this. If you reference an instance variable in the code for a static method, it doesn't compile and you just get an error message. The main() method, where execution of a Java application starts, must always be declared as static. Before an application starts execution, no objects exist, so to start execution, you need a method that is executable even though there are no objects around a static method therefore.

Accessing Java Class Variables and Methods

You may want to access fields and methods that are defined within a class from outside it. Generally you should never make the fields available directly for access. There are some exceptions but there are generally in some standard libraries. We will look at accessing static members (i.e. static fields and methods) and instance members separately.

You can access a static member of a class using the class name, followed by a period, followed by the member name. With a class method you also need to supply the parentheses enclosing any arguments to the method after the method name. The period here is called the dot operator. For example to calculate the square root of π you can access the class method sqrt() and the class variable PI that are defined in the Math class as follows:

[CODE-JAVA;Static Method and Field Access]double rootPi = Math.sqrt(Math.PI);[/CODE]

The means of calling a static method is prefix it with the class name and put the dot operator between them. You also reference the static data member, PI, in the same manner as we did with Math.PI. If you have a reference to an object of a class type available then you can also use that to access a static member of the class because every object always has access to the static members of its class. You just use the variable name, followed by the dot operator, followed by the member name.

Instance variables and methods can be called only using an object reference, because by definition they relate to a particular object. The syntax is exactly the same as I have outlined for static members. You put the name of the variable referencing the object followed by a period, followed by the member name. To use a method volume() that has been declared as an instance method in the Sphere class, you would write the following:

Java Code: Instance Method Call
double ballVolume = ball.volume();
Here the variable ball is of type Sphere and it contains a reference to an object of this type. You call its volume() method, which calculates the volume of the ball object, and the result that is returned is stored in the variable ballVolume.

Defining Final Fields

You can also declare a field in a class as final in order to mean that the field cannot be modified by the methods in the class. You can provide an initial value for a final field when you declare it. This is shown in the listing below:

Java Code: final Field Example
final double PI = 3.14;
If you don't provide an initial value for a final field when you declare it, you must initialize it in a constructor, which you learn about a little later in this chapter.

Defining Java Classes

Defining a class requires the use of the keyword class followed by the name of the class followed by a pair of braces enclosing the details of the definition. Below we provide a concrete example to see how this works in practice. The definition of the Sphere class that I mentioned earlier could be:

Java Code: Class Definition
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

   // Plus the rest of the class definition...
}
You name a class using an identifier of the same sort you've been using for variables. By convention, though, class names in Java begin with a capital letter, so the class name is Sphere with a capital S. If you adopt this approach, you will be consistent with most of the code you come across. You could enter this source code and save it as the file Sphere.java.

Note the use of the keyword public in this context precedes the keyword class in the first line of the class definition. The keyword static in the first line of the Sphere class definition specifies the variable PI as a class variable rather than an instance variable. The variable PI is also initialized with the value 3.14. The keyword final tells the compiler that you do not want the value of this variable to be changed, so the compiler checks that this variable is not modified anywhere in your program. You have also declared the next variable, count, using the keyword static. All objects of the Sphere class have access to and share the one copy of count and the one copy of PI that exist. You have initialized the variable count to 0, but because you have not declared it using the keyword final, you can change its value.

The next four variables in the class definition are instance variables, as they don't have the keyword static applied to them. Each object of the class has its own separate set of these variables, storing the radius and the coordinates of the center of the sphere. Although you haven't put initial values for these variables here, you can do so if you want. If you don't specify an initial value, a default value is assigned automatically when the object is created. Fields of numeric types are initialized with zero, fields of type char are initialized with '\u0000,' and fields that store class references or references to arrays are initialized with null.

Defining Java Class Methods

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 needed. Methods also allow us to divide up complex calculations into manageable chunks. You execute a method by calling it using its name, and the method may or may not return a value when its execution finishes. Methods that do not return a value are always called in a statement that just specifies 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. If a method that returns a value is called by itself in a statement then the value it returns is discarded. The basic structure of a method is shown in the figure below:

Tutorial:In Depth View of Java Classes for the Java Certification Exam I-d6-basicstructuremethod.jpg
Figure: Basic Structure of a Method

When you specify the return type for a method, you are defining the type of the value that will be returned by the method when you execute it. The method must always return a value of this particular type. To define a method that does not return a value, you specify the return type as void. You can also provide an access attribute if you wish to precede the return type in a method definition.

The parameters to a method appear in its definition between the parentheses following the method name. These specify what information is to be passed to the method when you execute it, and the values that you supply for the parameters when you call a method are described as arguments. The parameter names are used in the body of the method to refer to the corresponding argument values that you supply when you call the method. 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.

Returning from a Java Class Method

When you return a value from a method, you need to use a statement with the following syntax:

Java Code: Return Statement Syntax
return return_value;	// Return a value from a method
After executing the return statement in a method, 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 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 as shown below:

Java Code: Return from a method
return;
The Parameter List in a Java Class Method

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 used in the body of the method to refer to each argument value passed to the method when it is called. The difference between a parameter and an argument is sometimes confusing because people often, incorrectly, use them interchangeably. You can differentiate them consistently as follows:
A parameter has a name and a type 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 when it is called.
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. Of course, the type of the argument value must be consistent with the type specified for the corresponding parameter in the definition of the method.
This is illustrated in the figure below:

Tutorial:In Depth View of Java Classes for the Java Certification Exam I-d6-parameterlist.jpg
Figure: The Parameter List

In the figure above, you have the definition of a method mean(). The definition of this method appears within the definition of the class MyClass. You can see that the method has two parameters, value1 and value2, both of which are of type double. The parameter names are used to refer to the arguments 3.0 and 5.0, respectively, within the body of the method when it is called by the statement shown. Because this method has been defined as static, you can call it only using the class name.

How Argument Values Are Passed to a Method

You need to be clear about how the argument values are passed to a method; otherwise, you may run into problems. In Java, all argument values are transferred to a method using what is called the pass-by-value mechanism. The figure below illustrates how this works.

Tutorial:In Depth View of Java Classes for the Java Certification Exam I-d6-argumentvaluespasstometh.jpg
Figure: Argument Values Passed to Method

Pass-by-value just means that for each argument value that you pass to a method, a copy of the value is made, and it is the copy that is passed to the method and referenced through the parameter name, not the original variable. This implies that if you use a variable of any of the primitive types as an argument, the method cannot modify the value of this variable in the calling program. In the figure above, the change() method modifies the copy of i that is created automatically and referenced using the parameter name j. Thus, the value of j that is returned is 11, and this is stored in the variable x when the return from the method executes. However, the original value of i remains at 10.

Specifying Final Parameters

You can specify any of the parameters for a method 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 checks that your code in the body of the method does not attempt to change any final parameters. Because the pass-by-value mechanism makes copies of values of the basic types, final really makes sense only when it is applied to parameters that are references to class objects, as you see later on. Specifying a parameter of a method as final prevents accidental modification of the object reference that is passed to the method, but it does not prevent modification of the object itself.

Defining Java Class Methods

You define a class method by adding the keyword static to its definition. In the listing below, we have defined the class Sphere with a class method that returns the value stored in the static variable count:

Java Code: Class Method with Static Variable
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 you want to be able to get at the count of the number of objects that exist even when it is zero. You can amend the Sphere.java file to include the definition of getCount().

Accessing Java 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:

Java Code: Sphere Class
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...
}
The listing shows that the volume() method is an instance method since there is no declaration as static. It has no parameters, but it does return the calculated volume as a type double. The method uses the class variable PI and the instance variable radius in the volume calculation which corresponds to the formula (4/3)πr3 for the volume of a sphere in the return statement. The value that results from this expression is returned to the point where the method is called for a Sphere object.

Using the Variable this

Every instance method has a variable with the name this that 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 inserts the this object reference so that the reference is equivalent to this.radius. The return statement in the definition of the volume() method is actually the following:

Java Code: Return Statement
return 4.0/3.0*PI*this.radius*this.radius*this.radius;
The statement actually refers to the radius field for the object referenced by the variable this. 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.

You have seen that there are four different potential sources of data available 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 names
  • Local variables that you declare 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 an instance variable. If you find it necessary or convenient 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 always refers to the variable that is local to the method, not the instance variable.

For example, to add a method to change the radius of a Sphere object to a new radius value, you would code the following:

Java Code: Method to Change Radius
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. No confusion in the duplication of names exists here. It is clear that you are receiving a radius value as a parameter with the name radius and storing it in the radius variable for the class object.

Initializing Java Class Data Members

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

Java Code: Initializing Data 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

   // Rest of the class...
}
You can also initialize ordinary non-static data members in the same way. For example:

Java Code: Initialization of non-static data members
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 starts out with a radius of 5.0 and has the center at the point (10.0, 10.0, 10.0).

Some things can't be initialized with a single expression. For example, if you have a large array as a data member that you want to initialize, with a range of values that required some kind of calculation, this could be a job for an initialization block.

Passing Objects to a Java Method

When you pass an object as an argument to a method, the mechanism that applies is called pass-by-reference, because a copy of the reference contained in the variable is transferred to the method, not a copy of the object itself. The effect of this is shown in the figure below:

Tutorial:In Depth View of Java Classes for the Java Certification Exam I-d6-passingobjecttometh.jpg
Figure: Passing Object to a Method

The figure presumes the definition of a method, changeRadius(), in the class Sphere, that alters the radius value for an object, and that you have a method change() in some other class that calls changeRadius(). When the variable ball is used as an argument to the method change(), the pass-by-reference mechanism causes a copy of the contents of ball to be made and stored in s. The variable ball just stores a reference to the Sphere object, and the copy contains that same reference and therefore refers to the same object. No copying of the actual object occurs. This is a major plus in terms of efficiency when passing arguments to a method. Objects can be very complex, involving a lot of instance variables. If objects themselves were always copied when passed as arguments, it could be very time-consuming and make the code very slow.

The Lifetime of an Java Object

The lifetime of an object is determined by the variable that holds the reference to it. For example, the Sphere object that the variable ball refers to in the previous listing dies when the variable ball goes out of scope at the end of the block containing this declaration. Where an instance variable is the only one referencing an object, the object survives as long as the instance variable owning the object survives. The declaration for the variable ball is shown again below:

Java Code: Sphere Creation
Sphere ball = new Sphere(10.0, 1.0, 1.0, 1.0);
You can also reset a variable to refer to nothing by setting its value to null. If you write the statement:

Java Code: Resetting a Variable
ball = null;
the variable ball no longer refers to an object, and assuming there is no other variable referencing it, the Sphere object that it originally referenced is destroyed. Once destroyed, the object is discarded, but the variable ball still continues to exist and can now be used to store a reference to another Sphere object. When there is no more references to an object, the object can be garbage collected.

The process of garbage collection is automatic in Java. It doesn't necessarily mean that objects disappear from memory straight away. It just means you can't rely on memory occupied by an object as being immediately available. In cases where your objects are very large, (i.e. millions of bytes or if you were creating and getting rid of very large numbers of objects) you can try to call the static gc() method that is defined in the System class to encourage the Java Virtual Machine (JVM) to do some garbage collecting and recover the memory that the objects occupy:

Java Code: System Call for Garbage Collection
System.gc();
This is a best efforts deal on the part of the JVM. When the gc() method returns, the JVM has tried to reclaim the space occupied by discarded objects, but there's no guarantee that it has all been recovered. There's also the possibility that calling the gc() method may make things worse. If the garbage collector is executing some preparations for recovering memory, your call undoes that and in this way slows things up.

Method Overloading in Java Classes

Java enables you to define several methods in a class with the same name, as long as each method has a unique set of parameters. Defining two or more methods with the same name in a class is called method overloading.
The name of a method together with the types and sequence of the parameters form the signature of the method; the signature of each method in a class must be distinct to allow the compiler to determine exactly which method you are calling at any particular point. The return type has no effect on the signature of a method. You cannot differentiate between two methods just by the return type. This is because the return type is not necessarily apparent when you call a method. For example, suppose you write a statement such as:
Math.round(value);

Although the preceding statement is pointless because it discards the value that the round() method produces, it does illustrate why the return type cannot be part of the signature for a method. The compiler has no way to know from this statement what the return type of the method round() is supposed to be. Thus, if there were several different versions of the method round(), and the return type were the only distinguishing aspect of the method signature, the compiler would be unable to determine which version of round() you wanted to use.

You will find many circumstances where it is convenient to use method overloading. You have already seen that the Math class contains two versions of the method round(), one that accepts an argument of type float and the other that accepts an argument of type double. You can see now that method overloading makes this possible. It would be rather tedious to have to use a different name for each version of round() when they both do essentially the same thing. The valueOf() method in the String class is another example. There is a version of this method for each of the basic types. One context in which you regularly need to use overloading is when you write constructors for your classes, which will I explain now.

Summary

In this article we have reviewed some of the essentials of a class. Specifically we have looked at fields and methods of a class and what you can do with them. Before we will be able to define our own classes, we will need to look more in depth at Constructors, without which it would be impossible to create objects from classes. See you in the next article.