CORE CHAPTER 8: USER-DEFINED CLASSES AND ADTs



CORE CHAPTER 8: USER-DEFINED CLASSES AND ADTs

|Overview |

|[pic] |

|Core Chapter 8 explains how to create a Java class. This includes declaring instance and static variables and methods. There are several modifiers that are |

|used for classes and their members, such as private and public. 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. |

|The chapter uses the example of a clock to fully implement a class. The class definition includes constructors, instance variables and methods, a main |

|method, and optionally a finalizer. |

|Another important step in implementing a class is to override the equals and toString methods. The equals method is used to determine whether two objects are|

|logically equivalent to each other. It is often a good idea to define this method rather than using the default behavior inherited from the Object class. |

|The toString method is called when an object is printed using System.out.println. If you wish to print out certain information about your object, it is a |

|good idea to implement this method. |

|Finally, the chapter introduces abstract data types. 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. One example of an ADT is|

|a Vector which is a structure that functions like a list, that is, you can add, delete, and find items. |

|This core chapter has the following core sections:  |

Core Section 8.1: Classes

[pic]

The basic element of object-oriented programming in Java is a class.  A class defines the behavior of objects of the class and also determines what data the objects contain.  Any feature that you wish to represent in your Java program is encapsulated in a class.

The form of a class is as follows:

class classname{

    type data member1;

    type data member 2;

     ....

    return type method1(parameter-list1){

        // body of the method

    }

    return type method2(parameter-list2){

        // body of the method

    }

    ....

}

In the structure shown above, access modifiers are optional.  Access modifiers determine accessibility (or lack of it) to members on the basis of the type of access modifier.  We'll discuss access modifiers in later parts of this core section.

|Observation—It is not necessary to have a main method in every class; the main method merely provides a method for the starting point of a program.  |

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.

|Observation—In this Core of Java, any reference to objects is a reference to object references. |

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. 

When an object is created using the new keyword, a method called constructor is invoked.  This method is special as it does not have a return type and is named after the class itself.  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.  Constructors have a wide variety of uses but are normally used to initialize the data members of an object.  Constructors define what happens when the object of a class is created.  If a class does not define a constructor, the Java system provides one called a default constructor that 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 and is a special case of method overloading.  The topic of constructors will be elaborately dealt with later in this section.

Let us now consider how the Member class is implemented and pay special attention to constructors in this class.  Sample Program 2 is the definition of the Member class:

class Member extends User{ // Declare data members

    private String pswd;

    public Member(){ // The default constructor

        this.udm = new UserDM(this);

    }

    public Member(String id, String pd){ 

   // The constructor that accepts two String objects.

        this.udm = new UserDM(this);

        this.uid = id;

        this.pswd = pd;

    }

    public Member(String mem_id, String mem_pwd, Vector user_details){ 

    // The constructor that accepts

this(mem_id,mem_pwd);

// two String objects and a Vector

        this.user_details = user_details;

// object representing user details.

        this.udm = new UserDM(this);

    }

    public String getPwd(){

        return pswd; 

    }

    public void setPwd(String pswd){

        this.pswd = pswd;

    }

    public char displayUserMenu(UserWin os, boolean menu_first_time){

        char ret = os.memberMainMenu(getFirstName(), menu_first_time);

        menu_first_time = false;

        return ret;

    }

    public User validateUserId(Thread initFiles){

        Vector ud = udm.checkIfMem(initFiles);

        if(ud != null){

            this.user_details = ud;

            return this;

        }

        else{

            return null ;

        }

    }

    public void deleteMembership(Thread initFiles){

        udm.removeMember(initFiles, false);

    }

The Member class contains some unfamiliar syntax.  However, we shall focus only on the constructors of the Member class and how the behavior of an object is determined by its constructors.

The Member class has three overloaded constructors: a default constructor that initializes the data member that is an object of the UserDM class, a two-argument constructor that accepts two String objects and initializes the data members (member-ID code and password) with these arguments, and a three-argument constructor that initializes the user ID, password and the user's details upon accepting two String objects and a Vector as its arguments.

The objects uid, udm, and user_details are inherited from the Member class's parent class, User.  Hence, the extends keyword is placed after the name of the Member class in the definition.  We shall discuss inheritance in detail in Core Chapter 11.  The getFirstName() method is also inherited from the User class.

Consider Sample Program 3 where we have a class called MemberExample.

import java.util.*;

public class MemberExample{

    public static void main(String args[]){

        Member m1 = new Member(); // Default constructor..

        Member m2 = new Member("s_m123", "pv456"); 

        // Constructor using two arguments.

        System.out.println("\n");

        System.out.println("Password of member m2 is: " + m2.getPwd()); 

        // Use m2 to display password.

        System.out.println("\n");

        Vector user_details = new Vector(); // Create new Vector object.

        user_details.addElement("John"); 

        // Add a String denoting the first name to the Vector.

        UserWin os = new UserWin(new ClubSession()); 

        // Create new UI object.

        Member m3 = new Member("s_m123", "pv456",  user_details);  

        // Constructor using three-arguments.

       m3.displayUserMenu(os, true);

    }

}

Three different Member objects are created with the use of three constructors.  However, the two-argument constructor sets the member-ID code and password and therefore, the object referenced by m2 can be used to get the password with the use of the getPwd() method.  Before the third constructor is used to create the third Member object referenced by m3, the user's first name is set and wrapped up in a Vector.  The UserWin object is created so that it can be used to display the menus.

Object m3 is used to display the User Menu.  It also invokes the getFirstName() method of the User class to get the first name of the user. 

We have seen three different ways of creating a Member class.  You might ask why we would want to create three members in three different ways?  The answer lies in the different ways the Member object is used in the Authorization, Registration, and FileInitializer classes.  In the authorizeMember() method of the Authorization class, a new Member object is created with the use of the two-argument constructor.  The new Member object is used to authorize itself.  In the registerNewMember() method of the Registration class, a new Member class is created with the default constructor and this object is used to register itself and provide the user with a new user ID and password.  In the initFiles() method of the FileInitializer class, the Member object is created with the three-argument constructor to create a "dummy" member so that file operations will not be a nuisance later on.

Therefore, with overloaded constructors, one has the liberty to create the members of the same class differently to serve different purposes.

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.

[pic]

Figure 2—Illustration showing object reference

|Observation—In this Core of Java, any reference to objects is a reference to object references. |

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

    }

}

Click the button below Sample Program 4 to load it into CodeSaw.

The class definitions can be saved in a single file: the class definition for Rectangle in Rectangle.java and the definition for RectangleExample in RectangleExample.java.  However, it is not mandatory to have both class definitions in separate files.  The two classes can be saved in a single file called RectangleExample.java as the main() method is defined in the RectangleExample class.

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.  Each object will have its own copy of data members.  Changes to the data members of one object do not affect the data members of another.

Core Subsection 8.1.5: Built-in Operations on Classes

Most of the built-in operators of Java cannot be used on class objects.  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.  Modification to any of the 2 objects,  will reflect in the other objects.

The other type of copy is called the deep copy, in which each object will have it's own copy of the data member objects, and hence, the operations on one object will not affect the other.  There can be various methods to achieve this,  one of them being implementing methods in the Rectangle class,  that will make copies of the data members,  and refer the new object to these objects.

Core Subsection 8.1.7: Class Scope

An access modifier allows a programmer to control access to a feature.  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, but a feature cannot be both public and private at the same time.  Modifiers are specified by including the appropriate keyword before the name of the feature.  If an access modifier is not specified, access defaults to friendly.  However, friendly is not a keyword.

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.  If a top-level class is not qualified by the public modifier, it will have the default friendly access.

A public class, variable or method is used in any Java program without any restrictions.  For instance, an application declares its main() method to be public so that the method may be invoked from any Java run-time environment.  In the RectangleExample class, you'll see

public static void main(String args[]){

. . .

Some of the other examples of public declarations are

public void startSession(

public int guideThruRegistration(boolean update, String fld)

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

    ....

}

There is a subtle issue concerning the use of private.  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());

    }

}

Click the button below Sample Program 1 to load it into CodeSaw.

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.  For example, password is declared private in the Member class.  Therefore, the password is also owned by the Admin class, but can never access it!  We know that the Admin class inherits from the Member class.  However, password is declared private in the Member class and no other class, including the Admin class, has access to it.

The term protected has a restrictive connotation.  It might make you think that it is next only to private in its restrictive quality.  On the contrary, protected features are even more accessible than than friendly features.  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.

[pic]

Figure 3—Protected modifier

Friendly is the default access for classes, variables, and methods when an access modifier is not specified.  Friendly is not a keyword; it simply refers to the access level that results from not specifying an access 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.

[pic]

Figure 4—Friendly access

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

Core Section 8.2: The Copy Constructor

[pic]

In Core Section 8.1,  we understood the concept of constructors and looked at different ways of creating an object.  Another type of constructor that can be used to create an object is a copy constructor.  The syntax of the copy constructor is:

public ClassName (ClassName otherObject)    // copy constructor heading

Consider the following class Rectangle, with the copy constructor defined for the class.

class Rectangle{

    double breadth;

    double length;

      public Rectangle (Rectangle oldRectangle){ // copy constructor

          breadth = oldRectangle.getBreadth();

          length = oldRectangle.getLength();

      }

    public void setBreadth(double b){

        breadth = b;

    }

    public void setLength(double l){

        length = l;

    }

    public double getBreadth(){

        return breadth;

    }

    public double getLength(){

        return length;

    }

}  

Core Section 8.3: Classes and the Method toString

[pic]

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

[pic]

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

Now, suppose we have an object of type Base called bs.  It is still not incorrect to refer to x as 

bs.x

Both mean the same thing.  However, it makes more sense to refer to a static variable by the class it belongs to rather than with respect to an object of that class.  Therefore, referring to static variables the former way is the norm.

There is one more important situation that should be considered here.  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.  Figure 1 illustrates the concept of static variables. Click the button labeled "Run" to view the illustration.

[pic]

Figure 1—Illustration depicting static variable

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.

}

}

Click the button below Sample Program 1 to load it into CodeSaw.

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.

          System.out.println("The value of y is " + m.y++); 

         // Obtain value of y via the object m.

     }

}

This above code looks rather strange—we have created an object of type Mn inside the main() method of Mn itself.  This is perfectly valid and is common practice. 

Non-static methods do not have to create an instance of the class they belong to in order to access data members of that class.  The data members are directly accessible by all non-static methods of the class.  This is because non-static methods have a reference to the current object—the this keyword.  We have already seen how this is used.  However, static methods have no such reference; they have to use objects to access non-static data members of their class.  Therefore, with the use of this in a static method would result in an error.  A statement such as

this.y 

in the main() method of the Mn class in Sample Program 3 shown earlier will cause an error.

Static methods, cannot invoke non-static methods of the same class either.  However, they can invoke other static methods in the class directly.  In other words, if a static method needs to access a non-static variable or call a non-static method, it must specify which instance of it's class owns the variable or runs the method. 

A static method may not be overridden to be non-static.  Consider the following piece of code:

class Stat{

    static void add(){}

}

class Nonstat extends Stat{

      void add(){}

}

The code above will not compile.  The compiler displays a message declaring that static methods cannot be overridden.  However, changing the add() method to a static method results in successful compilation.

Core Section 8.5: Finalizers

[pic]

In some cases, an object needs to perform an action when it is destroyed.  The object might be holding up some system resources which need to be freed before it is removed from memory.  To handle such situations, Java provides a technique called finalization.  Using finalization, certain actions that should take place before the memory held by the object is reclaimed can be specified.  To do so, the actions are coded in the program in a method called finalize().  This method is run just before the object is garbage-collected.  This method can be regarded as loosely similar to the destructor function in C++.

The finalize() method has the following structure:

protected void finalize(){

// Body of finalization code.

}

Since the time of occurrence of garbage collection is unpredictable, it is not known when the finalize() method is run.  It is also possible that it might not get executed at all.

Core Section 8.6: Creating your Own Packages

[pic]

Imagine what happens when there are two classes with the same name in the same directory.  The earlier class is overwritten.  Obviously, there cannot be two classes with the same name in the same directory.  So, a new name should be given to each new class that is created.  This name should not match any other class name in the same directory.  Having the same name creates an undesirable situation called name-space collision.

Java solves this problem with the provision of packages.  A package is an entity that can contain any arbitrary number of classes.  It also provides a way of grouping a set of related classes.  Essentially, packages are a mechanism for partitioning the class name-space into more manageable chunks.  A name-space relates to names of classes and the directories they are currently present in.  A package is both a naming and a visibility control mechanism.  Classes within a package can be defined so they are not visible outside the package.

A package helps a programmer give meaningful names to classes and provides the advantage of not having the programmer bother about name collisions.

Core Subsection 8.6.1: package Keyword

The question is how does one create a package where all classes can be placed?  It's quite simple.  Just include a package command as the first statement (before or after comments, since comments can occur anywhere in code) in a Java source file.  All classes declared in that file will belong to the specified package.  If the package command is omitted, class names are placed in the default package that has no name.  For simple applications, where the number of classes are limited, there is generally no need to specify a package; however, with large applications it becomes a necessity.

The following structure depicts the form of a package statement:

package name;

where name is the name of the package.

A system uses directories to store packages.  Therefore, all .class files for any class that you declare belong to a particular package, say Pack, and must be stored in a directory called Pack.

|Important—All names given to packages are case-sensitive.  Directory names must exactly match the name of the package. |

Within a directory, more than one file can contain the same package statement.  The package statement specifies which package the classes defined in that particular file belong to.  Most packages are spread across several files and directories.

It is possible to create a hierarchy of packages.  The period (.) is used to separate each package name from the one above it.  The general form of a multileveled package statement is

package pack1.pack2.pack3.....;

Of course, the package hierarchy must exactly match the file system of the OS you are developing your Java application on.  For example, a package called java.util must be stored in java\util on a Windows file system.

Core Subsection 8.6.2: CLASSPATH Variable

An environment variable called CLASSPATH controls the nature in which packages are used.  The specific location that a Java compiler considers as the root of any package-hierarchy is controlled by this variable.  The default CLASSPATH  environment variable defined by the Java run time environment is the present working directory.  However, when a package is involved, the situation changes dramatically. 

Core Section 8.7: The Reference this

[pic]

The this keyword is used to refer to the present object.  The this keyword works with methods as well as data members.  The dot operator is used to qualify the method or data keyword with the this keyword.  In a class,  the call

method ();

can be replaced by

this.method ();

Also,  the access to local data member

var ++;

can be replaced by

this.var ++;

This is particularly useful to improve readability of code.  We will examine another situation where this can be used.  In a class that has several constructors defined, if the code in one constructor requires that the code of another constructor be invoked, it can be done so with the use of this.  The form of this is

constructor1(){

     this(parameter list);

    // code of constructor1.

}

Upon reaching the statement that uses this, the appropriate constructor is invoked.  As in the case of super, the call with the use of this if it exists, must be the first statement in the constructor.  Since we have said that if super or this is used, they must be the first statement in the constructor, you might wonder what happens if there is a need to use both.  Which would be the first statement then?  Such a situation will never arise.  Remember that the use of this refers to constructors within the same class, whereas the use of super implies two classes, one of which is a subclass of the other.

Consider Sample Program 1.  The Polygon class has been redefined to contain two constructors (the Polygon class is renamed to Polygon1).

class Polygon1{ // definition of a Polygon class

     double dim1, dim2; // data members

     Polygon1(int i){ 

     // constructor that accepts an int value as an argument;

          System.out.println("In constructor that accepts an int value as an argument");

     } 

     Polygon1(double a, double b){  // constructor initializes data members

          this(10); //  this invokes the constructor above

          dim1 = a;

          dim2 = b;

     }

     double getCircum(){ // a method that returns the circumference 

          return (dim1 + dim2); //  by adding all the data members

     }

}

public class PlyExample{

     public static void main(String args[]){

          Polygon1 p = new Polygon1(1.0, 2.0); 

          // This constructor here will invoke the other.

     }

}

Core Section 8.8: Inner Classes

[pic]

Inner classes were added as a feature in Java 1.1.  They are also known as nested classes and are useful in creating programs that are concise, efficient, and enhance clarity.

Inner classes are classes defined inside the body of the definition of another class.  The class in which the inner class is defined is termed outer class.  Here is an example (Sample Program 1) of an inner class In defined inside a class called Out:

class Out{

     private int i = 10;

     public class In{ // Definition of the inner class begins here.

          private int k = 20;  // private data member initialized here.

          public void inMethod(){ // Method of the inner class.

               System.out.println("k is " + k);

               System.out.println("\n" );

               System.out.println("i is " + i);

          } // End of inner class method.

      }  // End of definition of the inner class.

      public void outMethod(){ // Method of the outer class.

           System.out.println("In outMethod, i is " + i);

      }

}

When an inner class is defined like this, the outer class's name becomes part of the fully qualified name of the inner class.  In the above case, the full name of the inner class is not In; rather it's Out.In.

Objects that are instances of inner classes have the ability to access the members of the outer class.  That is why int i, which is defined in the outer class, can be accessed in the inMethod() of the inner class, even though i has been declared private.  This is so because the inner class has a hidden reference to the outer class instance that was the current context when the object was created.  The presence of the hidden reference to the outer class object ensures that the inner class and outer class belong together.

Here is an example (Sample Program 2) that creates an inner class and accesses its methods:

public class InExample{

     public static void main(String args[]){

          Out.In in = new Out().new In(); 

         // inner class object must have a

        // reference to an outer class method

          in.inMethod();

    }

}

Click the button below Sample Program 2 to load it into CodeSaw.

Note how an inner class object is created.  The name, as was mentioned earlier is qualified by the outer class too, therefore it's full name is Out.In.  Moreover, before using the new keyword to create an instance of In, it must have a reference to an outer class object; therefore, the outer class object is created first.  Upon creation of an object of the inner class, the inMethod() method can be invoked.  Having a hidden reference to the outer class results in the inner object knowing the data members of the outer class; hence the int value i can be printed out.

One more final point to be noted is this: compilation of the above classes results in two class files—Out.class corresponding to the outer class and Out$In.class corresponding to the inner class.  Note that the class file of the In class in not just In.class; it is qualified by it's outer class with a dollar sign ($) between the names of the two classes.

Where are inner classes useful?  In most cases, inner classes are useful when you need to hide some classes from other classes in the same package.  A certain type of inner classes called anonymous inner class is useful in event-driven programming like creating forms and windows.

Core Section 8.9: Abstract Data Types

[pic]

An Abstract Data Type (ADT) is a data type, which defines the operations on common data structures without knowing the implementation details.

This is commonly used in places,  where various different data structures can be used,  without the knowledge of the underlying implementation.  A Vector,  LinkedList,  SortedList, etc. are various types of Collection objects which share operations like:

• Insert an item

• Delete an item

• Modify an item

• Find an item

The caller of operations on any of these implementations can use these objects as Collection objects,  irrespective of the actual data structure being used.

Summary

[pic]

In this core chapter, you learned the following

• A programmer-defined data type that specifies logical properties without implementation details is known as Abstract data type (ADT).

• A Class is a collection of a fixed number of components, called members; members can be data or methods.

• A class method that is automatically called when an object of the class is created; a constructor should initialize the data members of an object is known as Constructor.

• A copy of one object to another which does not result in any duplicate references; each reference variable refers to its own object is known as Deep copy.

• The data members of a class is called as Fields.

• A method that is called when a class passes out of scope is called as Finalizer.

• A class defined inside another class is known as Inner class.

• A non-static member methods of a class is known as Instance methods.

• A non-static data members of a class is known as Instance variables.

• A statement that is true after a method executes is known as Postcondition.

• A statement specifying the conditions that should be true before a method is called is known as Precondition.

• A copy of one object to another which results in duplicate references (that is, the destination object contains references to values in the source object instead of the actual values themselves) is known as Shallow copy.

................
................

In order to avoid copyright disputes, this page is only a partial summary.

Google Online Preview   Download