Appendix     A

Introduction to Java

One of the fundamental ways in which complexity can be handled is abstraction, which is applied in almost every discipline. For instance, abstract art is art that does not represent or imitate external reality or the objects of nature. As another example, some words are abstract, existing only in the mind, like truth or justice. In Java, abstraction allows developers to layer complex systems into manageable pieces by using class hierarchies that highlight the essential properties and behaviors of an object, differentiating it from other objects.

Classes and Objects

A class is the fundamental building block of object-oriented (OO) programs. Each class generally represents a real-world object. The class is a template for denoting and creating a category of objects, thus modeling an abstraction by defining the properties and behavior of the objects representing that abstraction. A property of an object is defined by a field, which is a variable that can store the value representing that property. The behavior of an object is defined by a method. The fields and methods of a class are known as its members. A class definition in Java consists of member declarations (variable declarations and method declarations) and begins with the class keyword, as illustrated in Listing A-1.

Listing A-1. A Java Class

class ClassA {
// members declarations
}

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. The process of creating objects from a class is called instantiation. When a class is instantiated, a reference value is returned that denotes the created object. A reference value denotes a particular object. An object reference (or, simply, reference) is a variable that can store a reference value and provide a handle to an object. The code in Listing A-2 creates an object of ClassA. The reference value of this object is stored in the variable var1.

Listing A-2. Creating the Object

ClassA  var1 = new ClassA();

The process of creating objects involves declaring a reference variable to store the reference value of the object and then creating the object using the new keyword followed by initializing the object by a call to a constructor. The concept of constructor is explained later in the appendix. Listing A-3 splits Listing A-2 to show the declaration and creation as separate steps.

Listing A-3. The Declaration and Creation in Separate Steps

1.    ClassA  var1 ;
2.    var1 = new ClassA();
  • Line 1 declares the variable var1. The reference variable var1 can now be used to manipulate the object whose reference value is stored in the reference variable.
  • Line 2 creates the object using the new keyword and initialization by calling a constructor ClassA().

Variables

In Java, variables store values of primitive data types and reference values of objects.

Listing A-4 illustrates variable declarations that can store primitive values.

Listing A-4. Variable Declarations

int a, b, c;  // a, b and c are integer variables.
boolean flag; // flag is a boolean variable.
int i = 10,   // i is an int variable with initial value 10

Variables that store reference values of objects are called reference variables. The reference variables specify the type of reference that can be a class, an array, or an interface. Listing A-5 illustrates the reference variable declaration.

Listing A-5. Reference Variable Declarations

ClassA  var1 ; // Variable var1 can reference objects of class ClassA.

The declaration in the Listing A-5 does not create any object of class ClassA; it just creates variable that can store references of objects of ClassA.

Instance Members

Each object created (as illustrated in Listing A-2) has 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 make up the object’s state. The methods of an object define its behavior. These methods are called instance methods. Instance variables and instance methods, which belong to objects, are called instance members(see Listing A-6) to distinguish them from static members, which belong only to the class.

Listing A-6. The Instance Members

1.    class ClassA{
2.    // instance Members
3.    int i ; // instance variable
4.    void methodA(){// instance method
5.    // do something
6.    }
7.    }
  • In line 3, i is an instance variable of type int, and int is the primitive data type of i.
  • In line 4, methodA(){} is an instance method.

Static Members

Static members, declared with the keyword static, are the members that belong only to the class and not to any specific objects of the class. A class can have static variables and static methods. Static variables are 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, as illustrated in Listing A-7.

Listing A-7. Static Members

1.    class ClassA{
2.    static int i ;
3.    static void methodA(){
4.    // do something
5.    }
6.    }

Unlike instance members, static members in a class can be accessed using the class name, as shown here:

ClassA.i          // accessing static variable in Line 2 of Listing A-7
 
ClassA.methodA(); // accessing static method in Line 3 of Listing A-7

Though static members in a class can be accessed via object references, it is considered bad practice to do so.

Method Overloading

Each method has a name and a formal parameter list. The name of the method and its formal parameter list together with the type and the order of the parameter in the parameter list constitute the signature of the method. As long as the method signatures differ, more than one method can have the same method name. Such methods with the same method names and different signatures are called overloaded methods, and this phenomenon is called method overloading. Thus, overloaded methods are the methods that have the same name but a different parameter list. Listing A-8 shows five implementations of the method methodA.

Listing A-8. Overloaded MethodA( )

1.    void methodA{(int a, double b) }
2.    int methodA(int a) { return a; }
3.    int methodA() { return 1; }
4.    long methodA(double a, int b) { return b; }
5.    long methodA(int c, double d) { return a; } //  Not ok.
  • The first four implementations of method are overloaded correctly, each time with a different parameter list and, therefore, different signatures.
  • The declaration on line 5 has the same signature methodA(int, double) as the declaration on line 1. Changing just the return type  is not enough to overload a method; the parameter list in the declarations must be different.

Note  Only methods declared in the same class and those that are inherited by the class can be overloaded.

Arrays

An array is a data structure that is comprised of a fixed number of data elements essentially of the same data type. Any element in the array can be accessed using an index. The first element is always at index 0, and the last element is at index n-1, where n is the value of the length field in the array. In Java, arrays are objects where all elements in the array can be of a specific primitive data type or of a specific reference type. Listing A-9 declares references that refer to the array objects.

Listing A-9. Array Declarations

int [] intArray;
ClassA[]  classAArray ;

The two declarations in Listing A-9 declare intArray and classAArray to be reference variables that can refer to arrays of int values and arrays of ClassA objects. An array can be constructed for a fixed number of elements of a specific type, using the new operator. Given the previous array declarations, the arrays can be constructed as follows:

intArray = new int[10];      // array for 10 integers
classAArray = new ClassA[5]; // array of 5 objects of ClassA

Constructors

When an object is created using a new operator, the constructors are called to set the initial state of an object. A constructor declaration is comprised of the accessibility modifier followed by the parameter list with the following declarations:

  • Modifiers other than an accessibility modifier are not permitted in the constructor header.
  • Constructors cannot return a value and, therefore, do not specify a return type, not even void, in the constructor header.
  • The constructor name must be the same as the class name.

When no constructors are specified in a class, then an implicit default constructor, that is, an implicit constructor without any parameters, is generated for the class by the compiler that comprises a call to the superclass’s constructor. The compiler inserts this call to a superclass’s constructor to ensure that the inherited state of the object is initialized. Listing A-10 illustrates a call to an implicit default constructor.

Listing A-10. Implicit Default Constructor

class ClassA {
int i;
}
class ClassB {
 
ClassA var1 = new ClassA(); // (1) Call to implicit default constructor.
}

In the listing, the following implicit default constructor is called when a ClassA object is created in ClassB:

ClassA() { super(); }

In Listing A-11, the class ClassA provides an explicit default constructor at line 4.

Listing A-11. Explicit Default Constructor

1.    class ClassA {
2.    int i ;
3.    // Explicit Default Constructor:
4.    ClassA() {
5.    i = 1;
6.    }
7.
8.    }
9.    class ClassB {
10.    // ...
11.    ClassA var1 = new ClassA(); //  Call of explicit default constructor.
12.    }

The explicit default constructor ensures that any object created with the object creation expression new ClassA( ), shown in ClassB, will have its field i initialized to 1. If a class defines any explicit constructors, then the compiler will not generate the implicit default constructor with a call to the superclass’s constructor, and therefore the state of the object will not be set. In such a case, an implementation of the default constructor needs to be provided. In Listing A-12, the class ClassA provides only a nondefault constructor at line 4. It is called at line 8 when an object of the class ClassA is created with the new operator. Any attempt to call the default constructor will be flagged as a compile-time error, as shown on line 11.

Listing A-12. Nondefault Constructor

1.    class ClassA {
2.    int i;
3.    // Only non-default Constructor:
4.    ClassA(int i) {
5.    this.i = i;
6.    }
7.    }
8.    class ClassB {
9.    // ...
10.    ClassA var1 = new ClassA(2);
11.    //ClassA var2 = new ClassA(); // Compile-time error.
12.    }

Constructors can be overloaded, like methods, and because the names of all the constructors are restricted to be the same as the class’s name, the signatures of these constructors can be different only if their parameter lists are different. In Listing A-13, the class ClassA provides both an explicit implementation of the default constructor on line 4 and a nondefault constructor on line 8. The nondefault constructor is called when an object of the class ClassA is created on line 14 and the default constructor is called on line 15.

Listing A-13. Both Default and Nondefault Constructor

1.    class ClassA {
2.    int i;
3.    // Explicit Default Constructor:
4.    ClassA() {
5.    i = 3;
6.    }
7.    // Non-default Constructor:
8.    ClassA(int i) {
9.    this.i = i;
10.    }
11.    }
12.    class ClassB {
13.    // ...
14.    ClassA var1 = new ClassA(4);
15.    ClassA var2 = new ClassA();
16.    }

Encapsulation

Encapsulation is the technique to achieve data hiding by preventing the data and the code from being randomly accessed and manipulated by the external code, that is, from the code outside the class. In terms of implementation, encapsulation is achieved by making the fields in a class private and providing access to the fields via public methods. If a field is declared private, it cannot be accessed by anyone outside the class. Listing A-14 illustrates an encapsulated Book class.

Listing A-14. Encapsulation

public class Book {
       private String title ;
       public String getTitle() {
        return title;
    }
    public void setTitle(String title) {
        this.title = title;
    }
 }

Inheritance

Inheritance is one of the fundamental principles in object-oriented programming. In Java, all classes by default extend the java.lang.Object class. A class can extend another class by using the extends keyword. Java supports single inheritance through implementation inheritance in which a class inherits state and behaviors from another class through class extension. Java also supports multiple inheritance in two ways.

  • A class can inherit behavior from one or more interfaces by means of  implementation.
  • An interface can inherit behavior from one or more interfaces through extension.

Figure A-1 presents a UML1 class diagram that depicts a parent-child relationship between a class, ClassA, and a child class, ClassB. ClassB is a referred to as a subclass of ClassA. Note that a line with an arrow is used to depict generalization, in other words, the parent-child relationship.

9781430259831_AppA-01.jpg

Figure A-1. Parent-child relationship. ClassB extends ClassA

Listing A-15 illustrates how Figure A-1 could be implemented in code. It illustrates implementation inheritance using the keyword extends in line 26.

Listing A-15. Implementation Inheritance

1.    package apress.appendix_A
2.
3.    public class ClassA {
4.
5.        // Instance methods
6.        public void method1() {
7.            System.out.println(" classA - method1");
8.
9.        }
10.
11.        private void method2() {
12.            System.out.println(" classA - method2");
13.
14.        }
15.
16.        // Static methods
17.        public static void method3() {
18.            System.out.println(" classA - method3");
19.
20.        }
21.
22.    }
23.
24.    package apress.appendix_A;
25.
26.    public class ClassB extends ClassA {
27.
28.
29.    }

Listing A-16 is a driver class for testing the inheritance in Listing A-15.

Listing A-16. Testing Inheritance

package apress.appendix_A;
 
public class Test {
    
    public static void main(String[] args) {
        ClassB var1 = new ClassB();
 
        var1.method1();
        // var1.method2(); // private method not Inherited
 
        ClassB.method3();// static method
 
    }
 
}

Listing A-16 illustrates that even if there are no methods defined in ClassB, methods of ClassA are available in ClassB and can be invoked on the reference variable where the actual object type is of ClassB. Line 9 shows that private methods are not inherited.

Here is the output:

classA - method1
classA - method3

Constructor Chaining

When a subclass is instantiated by invoking one of its constructors, the constructor first calls the no-argument constructor of the superclass. In the superclass, the constructor also calls the constructor of its superclass. This process repeats itself until the constructor of the java.lang.Object class is reached. In other words, when you create an object of a subclass, all its superclasses are also instantiated. Listing A-17 illustrates this constructor chaining.

Listing A-17. Constructor Chaining

package apress.appendix_A;
 
public class ClassA {
 
    public ClassA() {
 
        System.out.println("Class A no-arg constructor");
 
    }
 
    public ClassA(String title) {
        System.out.println("Class A constructor");
 
    }
 
}
 
package apress.appendix_A;
 
public class ClassB extends ClassA {
    
    public ClassB(String title){
        System.out.println("Class B constructor ");
        
    }
 
}
 
 
package apress.appendix_A;
 
public class Test {
 
    /**
     * @param args
     */
    public static void main(String[] args) {
        ClassB var1 = new ClassB("classB");
 
    }
 
}

Here is the output:

Class A no-arg constructor
Class B constructor

The output proves that the constructor of the subclass invokes the base class’s no-arg constructor. The Java compiler changed ClassB’s constructor to the following:

public ClassB(String title) {
 super();
 System.out.println("Class B constructor ");
}

The keyword super represents an instance of the direct superclass of the current object. Since super is called from an instance of subclass, super represents an instance of ClassA. You can explicitly call the parent’s constructor from a subclass’s constructor by using the super keyword, but super must be the first statement in the constructor. Using the super keyword is handy if you want another constructor in the superclass to be invoked.

public ClassB(String title) {
 super(title);
 System.out.println("Class B constructor ");
}

Polymorphism

Polymorphism is one of the most important principles of OO programming and as such is the heart of OOP. Polymorphism refers to how an object in Java can take on many forms. To understand how polymorphism works in Java, let’s look at an example of a class that extends another class (see Figure A-2).

9781430259831_AppA-02.jpg

Figure A-2. Extending a class

Figure A-2 shows a superclass-subclass relationship. This relationship allows you to assign an object to a reference variable whose type is different from the object type, as follows:

ClassA var1 = new ClassB();

This assigns an object of type ClassB to a reference variable var1 whose reference type is ClassA. This assignment has different implications at compile time and at runtime. At the compile time, since the type of the var1 is ClassA, the compiler will not allow calling a method on var1, which is not in ClassA, even if that method is in ClassB. Listing A-18 shows the code implementation of Figure A-2.

Listing A-18. Inheritance

package apress.appendix_A;
public class ClassA {
 
    public void methodA() {
        System.out.println("methodA() in ClassA");
    }
 
}
 
package apress.appendix_A;
 
public class ClassB extends ClassA {
 
    public void methodB() {
        System.out.println("methodB() in ClassB");
    }
 
}

Listing A-19 illustrates the test.

Listing A-19. Driver for Listing A-18

package apress.appendix_A;
 
public class Test {
    public static void main(String[] args) {
 
        ClassA var1 = new ClassB();
        // var1.methodB(); uncommenting this code will result in compile time
        // error
        var1.methodA();
 
    }
}

On line 2 of Listing A-19, it is not possible to call methodB() on var1 even if methodB() is ClassB, because the reference type of var1 is ClassA and ClassA does not have methodB().

Now let’s consider the case where ClassB overrides methodA() of ClassA (see Figure A-3).

9781430259831_AppA-03.jpg

Figure A-3. Overriding a method

Now the code of ClassB looks like that shown in Listing A-20.

Listing A-20. Overriding methodA( )

package apress.appendix_A;
 
public class ClassB extends ClassA {
 
    public void methodA() {
        System.out.println("methodA() in ClassB");
    }
 
}

Now both ClassA and ClassB have the same method, methodA().

The following assignment:

ClassA var1 = new ClassB();

confirms that it is possible to call methodA( ) on var1 because ClassA has methodA( ). So, the following test will compile:

package apress.appendix_A;
 
public class Test {
    public static void main(String[] args) {
 
        ClassA var1 = new ClassB();
        var1.methodA();
 
    }
}

This is, at compile time, the call methodA( ) on var1 will be checked against a reference type of var1, ClassA, by the compiler, and the compiler will allow it because the methodA( ) exists in ClassA.

But what will happen if we run the test; that is, which methodA( ) will be called, methodA( ) in ClassA or methodA( ) in ClassB? When the test is run, it gives the following output:

methodA() in ClassB

The compiler checked methodA( ) in ClassA but executed methodA( ) in ClassB. This is because at runtime, the JVM verifies, instead of compiles, methodA( ) against the actual object type. The actual object type in the code (ClassA var1 = new ClassB();) is ClassB, while ClassA is a reference type. So, the JVM checks whether the call methodA( ) is in ClassB( ) and calls it. This phenomenon is called polymorphism. What will happen if methodA( ) is not in ClassB? To understand this, we implement the code for Figure A-4.

9781430259831_AppA-04.jpg

Figure A-4. Hierarchy of ClassB

Figure A-4 shows InterfaceA, just to illustrate that the reference type could be an interface or a class (or abstract class), and the compiler will check the existence of the method against the reference type of any of these in the similar manner. Listing A-21 implements the hierarchy shown in Figure A-4.

Listing A-21. Hierachy of Class B

package apress.appendix_A;
public interface InterfaceA {
 
    public void methodA();
    
 
}
 
package apress.appendix_A;
public class ClassA implements InterfaceA{
 
    @Override
    public void methodA() {
        System.out.println("methodA() in ClassA");
    }
}
 
package apress.appendix_A;
public class ClassB extends ClassA {
    public void methodB() {
        System.out.println("methodB() in ClassB");
    }
}

In Listing A-21, the call methodA( ) is verified against reference type InterfaceA, and after checking that methodA( ) exists in InterfaceA, the compiler approves the call; that is, there is no compile-time error. Run the test shown in Listing A-22.

Listing A-22. Test Application

package apress.appendix_A;
public class Test {
    public static void main(String[] args) {
        InterfaceA var1 = new ClassB();
        var1.methodA();
    }
}

You get the following output:

methodA() in ClassA

The actual object type in line 4 of Listing A-22 is ClassB, so at runtime the JVM checks whether methodA( ) exists in ClassB. On not finding methodA( ) in ClassB (because it does not exist in ClassB), the JVM checks for the existence of methodA( ) in the hierarchy of ClassB, because the JVM considers the fact that there must be methodA( ) existing somewhere in the hierarchy of ClassB; otherwise, the compiler would not have approved the call. Since methodA( ) exists in ClassA, the JVM executes methodA( ) in ClassA at runtime.

Summary

This appendix introduced you to the basics of Java and object-oriented programming. You learned how classes are the basic building blocks of object-oriented programs and how you can instantiate objects from classes. Next, you were introduced to the three pillars of object-oriented programming: encapsulation, inheritance, and polymorphism.

1www.uml.org/

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

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