Supplement CHAPTER 8: USER-DEFINED CLASSES AND ADTs

private indicates that a method or variable is only accessible from within the declaring class. public indicates that the method or variable is accessible from any class.

If you wish to print out certain information about your object, it is a good idea to implement this method.

An ADT is a logical structure without any implementation details. One of the goals of using an ADT is information hiding. This means that a programmer can use a class and its methods without being aware of the implementation details.

Core Section 8.1: Classes

A class defines the behavior of objects of the class and also determines what data the objects contain.

The form of a class is as follows:

<access specifier> class classname{

<access specifier> type data member1;

<access specifier> type data member 2;

....

<access specifier> return type method1(parameter-list1){

// body of the method

}

<access specifier> return type method2(parameter-list2){

// body of the method

}

....

}

Let's now consider Sample Program 1, an example of a very simple class:

class Rectangle{

double breadth;

double length;

public void setBreadth(double b){

breadth = b;

}

public void setLength(double l){

length = l;

}

public double getBreadth(){

return breadth;

}

public double getLength(){

return length;

}

}

The class depicts a rectangle with breadth and length. The class contains methods that permit the retrieval and storage of values that represent the breadth and length of a rectangle. The Rectangle class is a template or blueprint that determines the nature and behavior of objects in the Rectangle class.

Core Subsection 8.1.1: Constructors

Constructors are special methods that do not have a return type, have the same name as the class they belong to, and are invoked when an object of the class to which they belong is created. One of the main functions of a constructor is to initialize the object's data members. When a new object is created, the appropriate constructor is invoked.

Every class has a constructor. If the programmer does not define a constructor for a class, the system provides a default constructor that accepts no arguments and also does nothing. The default constructor, also known as the no-arguments constructor, does not take any parameters. The default constructor is used in the statement that creates the Rectangle object.

Parameters are passed to the constructor from within the parentheses in the statement that creates an object.

A class can define any arbitrary number of constructors. This is called constructor overloading.

Core Subsection 8.1.2: Unified Modeling Language Diagrams

The Unified Modeling Language (UML) is a notation used for graphically representing classes. Different information that can be captured in UML diagrams are data members, their data types, access modifiers, method and constructor definitions, etc.

Core Subsection 8.1.3: Variable Declaration and Object Instantiation

To create an object of type Rectangle, you have to use the new keyword as shown below:

Rectangle r = new Rectangle();

The preceding statement creates an object of type Rectangle in the heap of the computer's memory. The variable r is an object reference that refers to the actual object created in memory. An object is an instance of a class. When an object is created using new, memory is allocated for the object. An object reference is a variable that points to such an object in memory and is used in programs to access the object's data members or to invoke the methods of the object. Figure 2 is an illustration depicting object reference.

Figure 2—Illustration showing object reference

Core Subsection 8.1.4: Accessing Class Members

The members of a class are accessed using the dot (.) operator. The general form of the dot operator is

objectreference.variablename

For example, the following statement sets the length of r to 80.0:

r.length = 80;

The length of the rectangle can also be set using the setLength() method as follows:

r.setLength(80);

Consider Sample Program 4 that uses the Rectangle object with a main() method. I'll define another class called RectangleExample that contains a main() method, creates an instance of the Rectangle class, and performs operations on it.

class RectangleExample{

public static void main(String args[]){

Rectangle r = new Rectangle();

r.setBreadth(40.0); // Set the breadth to 40.0.

r.setLength(80.0); // Set the length to 80.0.

System.out.println("Breadth is: " + r.getBreadth());

// Get and display breadth

System.out.println("Length is: " + r.getLength());

// Get and display length

}

}

In either case, upon successful completion, two separate class files, corresponding to the two classes are created named Rectangle.class and RectangleExample.class. These files contain the bytecode for the corresponding classes.

You can create any number of objects of a class once the class is defined.

Core Subsection 8.1.5: Built-in Operations on Classes

Only the dot(.) operator is valid for class objects.

Core Subsection 8.1.6: The Assignment Operator and Classes: A Precaution

Consider Sample Program 5, an example of a Rectangle class:

class Rectangle{

double breadth;

double length;

public void setBreadth(double b){

breadth = b;

}

public void setLength(double l){

length = l;

}

public double getBreadth(){

return breadth;

}

public double getLength(){

return length;

}

}

//Consider usage of this class in the following method

class RectangleExample{

public static void main(String args[]){

Rectangle r1 = new Rectangle();

Rectangle r2 = r1;

r1.setBreadth(40.0); // Set the breadth to 40.0.

r1.setLength(80.0); // Set the length to 80.0.

System.out.println("Breadth is: " + r2.getBreadth());

// Get and display breadth

System.out.println("Length is: " + r2.getLength());

// Get and display length

}

}

In the example above, the reference variable r2 gets the value of r1, but both are pointing to the same same object in memory. This is an example of shallow copying.

The other type of copy is called the deep copy, the operations on one object will not affect the other.

Core Subsection 8.1.7: Class Scope

Access modifiers are public, private, and protected. A feature is a class, method or variable. Access modifiers determine which classes may access a feature. A feature can have only one access modifier qualifying it. For example, a feature can be public, private or protected.

A class that is not an inner class is called a top-level class. The only access modifier that can qualify a top-level class is public.

A public class, variable or method is used in any Java program without any restrictions.

A private variable or method may only be used by an object of the class that declares the variable or method. Let me explain.

For example, if there is no need to disclose a password of a Member object to methods outside the Member class itself. Therefore, the password can be declared as private.

class Member extends User{ // Declare data members

private String pswd; // Declare password as a private String

....

}

Consider Sample Program 6:

class Prv{

private int x = 20; // Initialize private data member

public Prv( int i ){ // Constructor with int as parameter

x = i;

}

public Prv(Prv p){ // Constructor with Prv as parameter

x = p.x; // Accessing x of p is ok here

}

public int getValue(){ // Return value of data member

return x;

}

}

public class PrvExample1{

public static void main(String args[]){

Prv p1 = new Prv(30); // create Prv object with an int

Prv p2 = new Prv(p1);

// create Prv object with the Prv object obtained above

System.out.println("The value returned is " + p2.getValue());

}

}

Pay special attention to the constructor of the Prv class that receives a Prv object reference as its argument. Access to the Prv class's data member, x, is granted with the use of the following statement:

x = p.x;

The question is, how can this work after x has been declared as a private data member? Remember that access modifiers dictate which classes, not which objects of the classes, may use features. Since the above statement is being used in the definition of the constructor of the class itself, there is no problem here. However, in Sample Program 7 consider a case where p2.x is accessed in PrvExample2 as follows:

public class PrvExample2{

public static void main(String args[]){

Prv p1 = new Prv(30); // Create Prv object with an int

Prv p2 = new Prv(p1);

// Create Prv object with the Prv object obtained above

System.out.println("The value received is " + p2.x); // Error

}

}

Sample Program 7 will not compile. We are trying to access a data member declared private in another class, PrvExample2. The compiler will display an error message declaring that a variable of Prv class is not accessible in the PrvExample2 class.

Private data can be hidden from the very class that holds the data.

By giving protected access, features of a class in a particular package are accessible to all classes within that package as well as classes that inherit from it. However, classes that do not inherit from that particular class and do not exist in the same package, cannot access features.

Figure 3 describes the protected access modifier.

Figure 3—Protected modifier

Friendly features of a class are accessible to any class in the same package as the class in which it is present and is not available in classes outside the package. For example, consider the figure below that displays two packages, Package1 and Package2. Package1 contains classes A, B, C, and D. Package2 contains classes E and F.

Friendly features of a class are accessible to any class in the same package as the class in which it is present and is not available to classes outside the package. Figure 4 is a depiction of friendly access.

Figure 4—Friendly access

The class F in Package2 inherits from class D in Package1. Friendly features of class D are accessible to any class in Package1; however, no class in Package2, including class F have access to these features.

Core Section 8.3: Classes and the Method toString

All classes implicitly inherit form the class object, which is at the root of the Java object hierarchy. Based on this hierarchy, all the methods of the class object can be invoked on any object. toString is one such method, which is defined in the class object.

The toString method prints the String representation of any object. Consider the following piece of code:

System.out.println (rectangle);

When this statement gets executed, the toString method of Rectangle class is invoked. The Rectangle class has to define the semantics of this method, and write code to print the intended string. If the user does not implement this method, the default implementation of object class is executed, which prints the object reference of the object.

Core Section 8.4: Static Members of a Class

A static modifier can be applied to variables and methods, but not to classes. It can also be applied to stand-alone blocks of code that do not belong to any method, called static initializers.

A static variable or method can be considered to belong to a class rather than an object of the class. A static variable or method is specified by the static keyword before the name of the variable or method.

static int x;

public static void main(String args)

A static feature may be shared by all objects of that class. A static variable is normally qualified by its class name rather than an object reference, since it belongs to the class of all objects of that type. For example, if we have a class called Base that contains a static variable as one of its data members called x, it is referred in code as

Base.x

A static variable belongs to the class and not a particular object, so when an object of the class modifies the variable, the change is reflected in all objects of that class. This is in contrast to non-static variables of which each object has its own individual copy.

Sample Program 1 illustrates static variables.

class St{

static int var = 10; // Initialize static int var to 10

void change(int x){ // Method that changes var

var = x;

}

}

public class StExample{

public static void main(String args[]){

St a = new St();

St b = new St();

System.out.println("Value of static variable in object b: " + b.var);

// Display static variable using object.

System.out.println("Value of static variable using class: " + St.var);

// Display static variable using St.

a.change(20);

// Static variable incremented with the use of object a.

System.out.println("After changing var using a, value in b: " + b.var);

// Display static variable with the use of object b.

}

}

The above code shows that var can be referred to with the use of either an object of the St class or with the name of the class itself. It also shows that there is only one var irrespective of how many objects of type St exist. If one object changes the value of var, it is reflected in all other objects of similar type, so the value of b.var would also have changed.

We have already seen that methods can also be declared static. An example was already shown in the form of the main() method which is static. A static method can access only static variables of the class in which it is defined. Therefore, the main() method which is static cannot access data members that are not static. Consider Sample Program 2 as follows:

class Nst{

static int x = 0; // Initialize non-static int.

int y = 0; // Initialize static int.

public static void main(String args[]){

x++; // This is ok

System.out.println("The value of y is " + y++); // Error!

}

}

The above code will not compile since y is not a static variable and cannot be accessed by the static main() method.

How then, can the main() method access non-static variables like y? It cannot, at least not directly. To do so, an instance of the class can be created and the value of the non-static variable can be referred within the body of the main() method. Here is Sample Program 3 that does just this.

class Mn{

int y = 0; // Initialize static int.

public static void main(String args[]){

Mn m = new Mn(); // Create an object of Mn itself.