1 Basics of Java Programming

1.1 Introduction

Before embarking on the road to Java programmer certification, it is important to understand the basic terminology and concepts in object-oriented programming (OOP). In this chapter, the emphasis is on providing an introduction rather than exhaustive coverage. In-depth coverage of the concepts follows in subsequent chapters of the book.

Java supports the writing of many different kinds of executables: applications, applets, and servlets. The basic elements of a Java application are introduced in this chapter. The old adage that practice makes perfect is certainly true when learning a programming language. To encourage programming on the computer, the mechanics of compiling and running a Java application are outlined.

1.2 Classes

One of the fundamental ways in which we handle complexity is in abstractions. An abstraction denotes the essential properties and behaviors of an object that differentiate it from other objects. The essence of OOP is modelling abstractions, using classes and objects. The hard part in this endeavor is finding the right abstraction.

A class denotes a category of objects, and acts as a blueprint for creating such objects. A class models an abstraction by defining the properties and behaviors for the objects representing the abstraction. An object exhibits the properties and behaviors defined by its class. The properties of an object of a class are also called attributes, and are defined by fields in Java. A field in a class is a variable which can store a value that represents a particular property of an object. The behaviors of an object of a class are also known as operations, and are defined using methods in Java. Fields and methods in a class declaration are collectively called members.

An important distinction is made between the contract and the implementation that a class provides for its objects. The contract defines what services, and the implementation defines how these services are provided by the class. Clients (i.e., other objects) only need to know the contract of an object, and not its implementation, in order to avail themselves of the object’s services.

As an example, we will implement different versions of a class that models the abstraction of a stack that can push and pop characters. The stack will use an array of characters to store the characters, and a field to indicate the top element in the stack. Using Unified Modeling Language (UML) notation, a class called CharStack is graphically depicted in Figure 1.1, which models the abstraction. Both fields and method names are shown in Figure 1.1a.

Figure 1.1 UML Notation for Classes

UML Notation for Classes

Declaring Members: Fields and Methods

Example 1.1 shows the declaration of the class CharStack depicted in Figure 1.1. Its intention is to illustrate the salient features of a class declaration in Java, and not the effective implementation of stacks.

A class declaration consists of a series of member declarations. In the case of the class CharStack, it has two fields declared at (1):

stackArray, which is an array to hold the elements of the stack (in this case, characters)

topOfStack, which denotes the top element of the stack (i.e., the index of the last character stored in the array)

The class CharStack has five methods, declared at (3), that implement the essential operations on a stack:

push() pushes a character on to the stack

pop() removes and returns the top element of the stack

peek() returns the top element of the stack for inspection

isEmpty() determines whether the stack is empty

isFull() determines whether the stack is full

The class declaration also has a method-like declaration with the same name as the class, (2). Such declarations are called constructors. As we shall see, a constructor is executed when an object is created from the class. However, the implementation details in the example are not important for the present discussion.

Example 1.1 Basic Elements of a Class Declaration

//Source Filename: CharStack.java
public class CharStack {         // Class name
  // Class Declarations:
  // Fields:                                                           (1)
  private char[] stackArray;     // The array implementing the stack.
  private int    topOfStack;     // The top of the stack.

  // Constructor:                                                      (2)
  public CharStack(int capacity) {
    stackArray = new char[capacity];
    topOfStack = -1;
  }

  // Methods:                                                          (3)
  public void push(char element) { stackArray[++topOfStack] = element; }
  public char pop()              { return stackArray[topOfStack--]; }
  public char peek()             { return stackArray[topOfStack]; }
  public boolean isEmpty()       { return topOfStack < 0; }
  public boolean isFull()        { return topOfStack == stackArray.length - 1; }
}

1.3 Objects

Class Instantiation, Reference Values, and References

The process of creating objects from a class is called instantiation. An object is an instance of a class. The object is constructed using the class as a blueprint and is a concrete instance of the abstraction that the class represents. An object must be created before it can be used in a program.

A reference value is returned when an object is created. A reference value denotes a particular object. An object reference (or simply reference) is a variable that can store a reference value. A reference thus provides a handle to an object, as it can indirectly denote an object whose reference value it holds. In Java, an object can only be manipulated via its reference value, or equivalently by a reference that holds its reference value.

The process of creating objects usually involves the following steps:

1. Declaration of a variable to store the reference value of an object.
This involves declaring a reference variable of the appropriate class to store the reference value of the object.

// Declaration of two reference variables that will refer to
// two distinct objects, namely two stacks of characters, respectively.
CharStack stack1, stack2;

2. Creating an object.
This involves using the new operator in conjunction with a call to a constructor, to create an instance of the class.

// Create two distinct stacks of chars.
stack1 = new CharStack(10); // Stack length: 10 chars
stack2 = new CharStack(5);  // Stack length: 5 chars

The new operator creates an instance of the CharStack class and returns the reference value of this instance. The reference value can be assigned to a reference variable of the appropriate class. The reference variable can then be used to manipulate the object whose reference value is stored in the reference variable.

Each object has its own copy of the fields declared in the class declaration. The two stacks, referenced by stack1 and stack2, will have their own stackArray and topOfStack fields.

The purpose of the constructor call on the right side of the new operator is to initialize the newly created object. In this particular case, for each new CharStack instance created using the new operator, the constructor creates an array of characters. The length of this array is given by the value of the argument to the constructor. The constructor also initializes the topOfStack field.

The declaration of a reference and the instantiation of the class can also be combined, as in the following declaration statement:

CharStack stack1 = new CharStack(10),
          stack2 = new CharStack(5);

Figure 1.2 shows the UML notation for objects. The graphical representation of an object is very similar to that of a class. Figure 1.2 shows the canonical notation, where the name of the reference variable denoting the object is prefixed to the class name with a colon (':'). If the name of the reference variable is omitted, as in Figure 1.2b, this denotes an anonymous object. Since objects in Java do not have names, but are denoted by references, a more elaborate notation is shown in Figure 1.2c, where objects representing references of the CharStack class explicitly refer to CharStack objects. In most cases, the more compact notation will suffice.

Figure 1.2 UML Notation for Objects

UML Notation for Objects

Object Aliases

An object can be referred by several references, meaning that they store the reference value of the same object. Such references are called aliases. The object can be manipulated via any one of its aliases, as each one refers to the same object.

// Create two distinct stacks of chars.
CharStack stackA = new CharStack(12); // Stack length: 12 chars
CharStack stackB = new CharStack(6);  // Stack length: 6 chars

stackB = stackA;                      // (1) aliases after assignment
// The stack previously referenced by stackB can now be garbage collected.

Two stacks are created in the code above. Before the assignment at (1), the situation is as depicted in Figure 1.3a. After the assignment at (1), the reference variables stackA and stackB will denote the same stack, as depicted in Figure 1.3b. The reference value in stackA is assigned to stackB. The reference variables stackA and stackB are aliases after the assignment, as they refer to the same object. What happens to the stack object that was denoted by the reference variable stackB before the assignment? When objects are no longer in use, their memory is, if necessary, reclaimed and reallocated for other objects. This is called automatic garbage collection. Garbage collection in Java is taken care of by the runtime system.

Figure 1.3 Aliases

Aliases

1.4 Instance Members

Each object created will have its own copies of the fields defined in its class. The fields of an object are called instance variables. The values of the instance variables in an object comprise its state. Two distinct objects can have the same state, if their instance variables have the same values. The methods of an object define its behavior. These methods are called instance methods. It is important to note that these methods pertain to each object of the class. This should not be confused with the implementation of the methods, which is shared by all instances of the class. Instance variables and instance methods, which belong to objects, are collectively called instance members, to distinguish them from static members, which belong to the class only. Static members are discussed in Section 1.5.

Invoking Methods

Objects communicate by message passing. This means that an object can be made to exhibit a particular behavior by sending the appropriate message to the object. In Java, this is done by calling a method on the object using the binary infix dot ('.') operator. A method call spells out the complete message: the object that is the receiver of the message, the method to be invoked, and the arguments to the method, if any. The method invoked on the receiver can also send information back to the sender, via a single return value. The method called must be one that is defined for the object, otherwise the compiler reports an error.

CharStack stack = new CharStack(5);      // Create a stack
stack.push('J'),            // (1) Character 'J' pushed
char c = stack.pop();       // (2) One character popped and returned: 'J'
stack.printStackElements(); // (3) Compile-time error: No such method in CharStack

The sample code above invokes methods on the object denoted by the reference variable stack. The method call at (1) pushes one character on the stack, and the method call at (2) pops one character off the stack. Both push() and pop() methods are defined in the class CharStack. The push() method does not return any value, but the pop() method returns the character popped. Trying to invoke a method named printStackElements on the stack results in a compile-time error, as no such method is defined in the class CharStack.

The dot ('.') notation can also be used with a reference to access the fields of an object. The use of the dot notation is governed by the accessibility of the member. The fields in the class CharStack have private accessibility, indicating that they are not accessible from outside the class:

stack.topOfStack++;     // Compile-time error: topOfStack is a private field.

1.5 Static Members

In some cases, certain members should only belong to the class, and not be part of any object created from the class. An example of such a situation is when a class wants to keep track of how many objects of the class have been created. Defining a counter as an instance variable in the class declaration for tracking the number of objects created does not solve the problem. Each object created will have its own counter field. Which counter should then be updated? The solution is to declare the counter field as being static. Such a field is called a static variable. It belongs to the class, and not to any object of the class. A static variable is initialized when the class is loaded at runtime. Similarly, a class can have static methods that belong to the class, and not to any specific objects of the class. Static variables and static methods are collectively known as static members, and are declared with the keyword static.

Figure 1.4 shows the class diagram for the class CharStack. It has been augmented by two static members that are shown underlined. The augmented definition of the CharStack class is given in Example 1.2. The field counter is a static variable declared at (1). It will be allocated and initialized to the default value 0 when the class is loaded. Each time an object of the CharStack class is created, the constructor at (2) is executed. The constructor explicitly increments the counter in the class. The method getInstanceCount() at (3) is a static method belonging to the class. It returns the counter value when called.

Figure 1.4 Class Diagram Showing Static Members of a Class

Class Diagram Showing Static Members of a Class

Example 1.2 Static Members in Class Declaration

//Filename CharStack.java
public class CharStack {
  // Instance variables:
  private char[] stackArray;     // The array implementing the stack.
  private int    topOfStack;     // The top of the stack.

  // Static variable
  private static int counter;                                      // (1)

  // Constructor now increments the counter for each object created.
  public CharStack(int capacity) {                                 // (2)
    stackArray = new char[capacity];
    topOfStack = -1;
    counter++;
  }

  // Instance methods:
  public void push(char element) { stackArray[++topOfStack] = element; }
  public char pop()              { return stackArray[topOfStack--]; }
  public char peek()             { return stackArray[topOfStack]; }
  public boolean isEmpty()       { return topOfStack < 0; }
  public boolean isFull()        { return topOfStack == stackArray.length - 1; }

  // Static method                                                    (3)
  public static int getInstanceCount() { return counter; }
}

Figure 1.5 shows the classification of the members in the class CharStack using the terminology we have introduced so far. Table 1.1 at the end of this section, provides a summary of the terminology used in defining members of a class.

Table 1.1 Terminology for Class Members

Image

Clients can access static members in the class by using the class name. The following code invokes the getInstanceCount() method in the class CharStack:

int count = CharStack.getInstanceCount(); // Class name to invoke static method

Figure 1.5 Members of a Class

Members of a Class

Static members can also be accessed via object references, but this is considered bad style:

CharStack stack1;
int count1 = stack1.getInstanceCount();   // Reference invokes static method

Static members in a class can be accessed both by the class name and via object references, but instance members can only be accessed by object references.

1.6 Inheritance

There are two fundamental mechanisms for building new classes from existing ones: inheritance and aggregation. It makes sense to inherit from an existing class Vehicle to define a class Car, since a car is a vehicle. The class Vehicle has several parts; therefore, it makes sense to define a composite object of the class Vehicle that has constituent objects of such classes as Motor, Axle, and GearBox, which make up a vehicle.

Inheritance is illustrated by an example that implements a stack of characters that can print its elements on the terminal. This new stack has all the properties and behaviors of the CharStack class, but it also has the additional capability of printing its elements. Given that this printable stack is a stack of characters, it can be derived from the CharStack class. This relationship is shown in Figure 1.6. The class PrintableCharStack is called the subclass, and the class CharStack is called the superclass. The CharStack class is a generalization for all stacks of characters, whereas the class PrintableCharStack is a specialization of stacks of characters that can also print their elements.

Figure 1.6 Class Diagram Depicting Inheritance Relationship

Class Diagram Depicting Inheritance Relationship

In Java, deriving a new class from an existing class requires the use of the extends clause in the subclass declaration. A subclass can extend only one superclass. The subclass can inherit members of the superclass. The following code fragment implements the PrintableCharStack class:

class PrintableCharStack extends CharStack {                       // (1)
  // Instance method
  public void printStackElements() {                               // (2)
    // ... implementation of the method...
  }

  // The constructor calls the constructor of the superclass explicitly.
  public PrintableCharStack(int capacity) { super(capacity); }     // (3)
}

The PrintableCharStack class extends the CharStack class at (1). Implementing the printStackElements() method in the PrintableCharStack class requires access to the field stackArray from the superclass CharStack. However, this field is private and therefore not accessible in the subclass. The subclass can access these fields if the accessibility of the fields is changed to protected in the CharStack class. Example 1.3 uses a version of the class CharStack, which has been modified accordingly. Implementation of the printStackElements() method is shown at (2). The constructor of the PrintableCharStack class at (3) calls the constructor of the superclass CharStack in order to initialize the stack properly.

Example 1.3 Defining a Subclass

// Source Filename: CharStack.java
public class CharStack {
  // Instance variables
  protected char[] stackArray;  // The array that implements the stack.
  protected int    topOfStack;  // The top of the stack.

  // The rest of the definition is the same as in Example 1.2.
}

//Filename: PrintableCharStack.java
public class PrintableCharStack extends CharStack {                 // (1)

  // Instance method
  public void printStackElements() {                                // (2)
    for (int i = 0; i <= topOfStack; i++)
      System.out.print(stackArray[i]); // print each char on terminal
    System.out.println();
  }

  // Constructor calls the constructor of the superclass explicitly.
  PrintableCharStack(int capacity) { super(capacity); }             // (3)
}

Objects of the PrintableCharStack class will respond just like the objects of the CharStack class, but they will also have the additional functionality defined in the subclass:

PrintableCharStack pcStack = new PrintableCharStack(3);
pcStack.push('H'),
pcStack.push('i'),
pcStack.push('!'),
pcStack.printStackElements();    // Prints "Hi!" on the terminal

1.7 Aggregation

When building new classes from existing classes using aggregation, a composite object is built from the constituent objects that are its parts.

Java supports aggregation of objects by reference, since objects cannot contain other objects explicitly. The fields can only contain values of primitive data types or reference values to other objects. Each object of the CharStack class has a field to store the reference value of an array object that holds the characters. Each stack object also has a field of primitive data type int to store the index value that denotes the top of stack. This is reflected in the definition of the CharStack class, which contains an instance variable for each of these parts. In contrast to the constituent objects whose reference values are stored in fields, the values of primitive data types are themselves stored in the fields of the composite object. The aggregation relationship is depicted by the UML diagram in Figure 1.7, showing that each object of the CharStack class will have one array object of type char associated with it.

Figure 1.7 Class Diagram Depicting Aggregation

Class Diagram Depicting Aggregation

1.8 Tenets of Java

• Code in Java must be encapsulated in classes.

• There are two kinds of values in Java: there are objects that are instances of classes or arrays, and there are atomic values of primitive data types.

• References denote objects and are used to manipulate objects.

• Objects in Java cannot contain other objects; they can only contain references to other objects.

• During execution, reclamation of objects that are no longer in use is managed by the runtime system.

Review Questions

Review Questions

1.1 Which statement about methods is true?

Select the one correct answer.

(a) A method is an implementation of an abstraction.

(b) A method is an attribute defining the property of a particular abstraction.

(c) A method is a category of objects.

(d) A method is an operation defining the behavior for a particular abstraction.

(e) A method is a blueprint for making operations.

1.2 Which statement about objects is true?

Select the one correct answer.

(a) An object is what classes are instantiated from.

(b) An object is an instance of a class.

(c) An object is a blueprint for creating concrete realization of abstractions.

(d) An object is a reference.

(e) An object is a variable.

1.3 Which is the first line of a constructor declaration in the following code?

public class Counter {                                             // (1)
  int current, step;
  public Counter(int startValue, int stepValue) {                  // (2)
    setCurrent(startValue);
    setStep(stepValue);
  }
  public int getCurrent()          { return current; }           // (3)
  public void setCurrent(int value)  { current = value; }          // (4)
  public void setStep(int stepValue) { step = stepValue; }         // (5)
}

Select the one correct answer.

(a) (1)

(b) (2)

(c) (3)

(d) (4)

(e) (5)

1.4 Given that Thing is a class, how many objects and how many reference variables are created by the following code?

Thing item, stuff;
item = new Thing();
Thing entity = new Thing();

Select the two correct answers.

(a) One object is created.

(b) Two objects are created.

(c) Three objects are created.

(d) One reference variable is created.

(e) Two reference variables are created.

(f) Three reference variables are created.

1.5 Which statement about instance members is true?

Select the one correct answer.

(a) An instance member is also called a static member.

(b) An instance member is always a field.

(c) An instance member is never a method.

(d) An instance member belongs to an instance, not to the class as a whole.

(e) An instance member always represents an operation.

1.6 How do objects communicate in Java?

Select the one correct answer.

(a) They communicate by modifying each other’s fields.

(b) They communicate by modifying the static variables of each other’s classes.

(c) They communicate by calling each other’s instance methods.

(d) They communicate by calling static methods of each other’s classes.

1.7 Given the following code, which statements are true?

class A {
  int value1;
}

class B extends A {
  int value2;
}

Select the two correct answers.

(a) Class A extends class B.

(b) Class B is the superclass of class A.

(c) Class A inherits from class B.

(d) Class B is a subclass of class A.

(e) Objects of class A have a field named value2.

(f) Objects of class B have a field named value1.

1.9 Java Programs

A Java source file can contain more than one class declaration. Each source file name has the extension .java. The JDK enforces the rule that any class in the source file that has public accessibility must be declared in its own file; meaning that such a public class must be declared in a source file whose file name comprises the name of this public class with .java as its extension. The above rule implies that a source file can only contain at the most one public class. If the source file contains a public class, the file naming rule must be obeyed.

Each class declaration in a source file is compiled into a separate class file, containing Java byte code. The name of this file comprises the name of the class with .class as its extension. The JDK provides tools for compiling and running programs, as explained in the next section. The classes in the Java standard library are already compiled, and the JDK tools know where to find them.

1.10 Sample Java Application

An application is just a synonym for a program: source code that is compiled and directly executed. In order to create an application in Java, the program must have a class that defines a method named main, which is the starting point for the execution of any application.

Essential Elements of a Java Application

Example 1.4 is an example of an application in which a client uses the CharStack class to reverse a string of characters.

Example 1.4 An Application

// Source Filename: CharStack.java
public class CharStack {
  // Same as in Example 1.2.
}

//Filename: Client.java
public class Client {

  public static void main(String[] args) {

    // Create a stack.
    CharStack stack = new CharStack(40);

    // Create a string to push on the stack:
    String str = "!no tis ot nuf era skcatS";
    int length = str.length();
    System.out.println("Original string: " + str);

    // Push the string char by char onto the stack:
    for (int i = 0; i < length; i++) {
      stack.push(str.charAt(i));
    }

    System.out.print("Reversed string: ");
    // Pop and print each char from the stack:
    while (!stack.isEmpty()) { // Check if the stack is not empty.
      System.out.print(stack.pop());
    }
    System.out.println();
  }
}

Output from the program:

Original string: !no tis ot nuf era skcatS
Reversed string: Stacks are fun to sit on!

The public class Client defines a method with the name main. To start the application, the main() method in this public class is invoked by the Java interpreter, also called the Java Virtual Machine (JVM). The method header of this main() method should be declared as shown in the following method stub:

public static void main(String[] args) // Method header
{ /* Implementation */ }

The main() method has public accessibility, i.e., it is accessible from any class. The keyword static means the method belongs to the class. The keyword void means the method does not return any value. The parameter list, (String[] args), is an array of strings that can be used to pass information to the main() method when the application is started.

Compiling and Running an Application

Java source files can be compiled using the Java compiler tool javac, which is part of the JDK.

The source file Client.java contains the declaration of the Client class. The source file can be compiled by giving the following command at the command line. (The character > is the command prompt.)

>javac Client.java

This creates the class file Client.class containing the Java byte code for the Client class. The Client class uses the CharStack class, and if the file CharStack.class does not already exist, the compiler will also compile the source file CharStack.java.

Compiled classes can be executed by the Java interpreter java, which is also part of the JDK. Example 1.4 can be run by giving the following command in the command line:

>java Client

Note that only the name of the class is specified, resulting in the execution starting in the main() method of the specified class. The application in Example 1.4 terminates when the execution of the main() method is completed.

Review Questions

Review Questions

1.8 Which command from the JDK should be used to compile the following source code contained in a file named SmallProg.java?

public class SmallProg {
  public static void main(String[] args) { System.out.println("Good luck!"); }
}

Select the one correct answer.

(a) java SmallProg

(b) javac SmallProg

(c) java SmallProg.java

(d) javac SmallProg.java

(e) java SmallProg main

1.9 Which command from the JDK should be used to execute the main() method of a class named SmallProg?

Select the one correct answer.

(a) java SmallProg

(b) javac SmallProg

(c) java SmallProg.java

(d) java SmallProg.class

(e) java SmallProg.main()

Chapter Summary

Chapter Summary

The following information was included in this chapter:

• basic concepts in OOP, and how they are supported in Java

• essential elements of a Java application

• compiling and running Java applications

Programming Exercise

Programming Exercise

1.1 Modify the program from Example 1.4 to use the PrintableCharStack class, rather than the CharStack class from Example 1.2. Utilize the printStackElements() method from the PrintableCharStack class. Is the new program behavior-wise any different from Example 1.4?

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.129.39.252