The Essence of OOP using Java, Local Classes

Baldwin explains the various relationships that exist among local classes and their enclosing classes.

Published:  November 4, 2003
By Richard G. Baldwin

Java Programming Notes # 1638


Preface

This series of lessons is designed to teach you about the essence of Object-Oriented Programming (OOP) using Java.

The first lesson in the series was entitled The Essence of OOP Using Java, Objects, and Encapsulation.  The previous lesson was entitled The Essence of OOP using Java, Member Classes.

You may find it useful to open another copy of this lesson in a separate browser window.  That will make it easier for you to scroll back and forth among the different figures and listings while you are reading about them.

For further reading, see my extensive collection of online Java tutorials at Gamelan.com. A consolidated index is available at www.DickBaldwin.com.

Preview

What can you include in a class definition?

There are several different kinds of items that can be included in a class definition.  As you learned in the earlier lessons in this series, the list includes:

Can also contain other class definitions

As you learned in the previous lesson, a class definition can also contain the following four kinds of inner classes:

The previous lesson explained member classes.  This lesson will explain local classes.  Subsequent lessons will explain the other two types of inner classes.

(Note that it is questionable whether a nested top-level class or interface should be referred to as an inner class, because an object of a nested top-level class can exist in the absence of an object of the enclosing class.  Regardless of whether the term inner class applies, a nested top-level class is defined within the definition of another class, so its definition is internal to the definition of another class.)

What is a local class?

A local class is a class that is defined within a block of Java code.  While local classes are probably most frequently defined within methods and constructors, they can also be defined inside static initializer blocks and instance initializers.

As is the case for an object of a member class (discussed in the previous lesson), an object of a local class must be internally linked to an object of the enclosing class.  I will often refer to that object as the containing object, and make comments about the containment hierarchy.

Thus, a local class is truly an inner class, because an object of the local class cannot exist in the absence of an object of the enclosing class.

What about a local interface?

Interfaces defined within classes are implicitly static.  This means that they are always top-level.  There is no such thing as a member interface, a local interface, or an anonymous interface.

Why use local classes?

Objects instantiated from local classes share many of the characteristics of objects instantiated from member classes.  However, in some cases, a local class can be defined closer to its point of use than would be possible with a member class, leading to improved code readability.

Probably the most important benefit of local classes has to do with accessing the members of enclosing classes.  Just like with member classes, methods of a local class have direct access to all the members of the enclosing classes, including private members.  Thus the use of local classes can sometimes eliminate the requirement to connect objects together via constructor parameters.

(We will also see in the example program in this lesson that methods of a local class have direct access to protected members of the superclass of the enclosing class.)

Can be particularly useful when ...

A local class can be particularly useful in those cases where

This lesson approaches local classes from a somewhat theoretical viewpoint.  The next lesson will approach local classes from a more practical viewpoint, including a comparison between local classes and anonymous classes.

Local classes versus member classes

A local class has approximately the same relationship to a member class that a local variable in a method has to an instance variable of the class containing the method.

The scope of a local class

As is the case with local variables, the name of a local class is visible and usable only within the block of code in which it is defined (and blocks nested within that block).  Further, the name of the local class is visible and usable only to code following the class definition within that block.

The methods of a local class can use any final local variables or method parameters that are visible from the scope in which the local class is defined.

Similar to member classes

As mentioned earlier, local classes have many characteristics in common with member classes.  This includes access to private fields and methods in the containing class.  The thing that separates local classes from member classes is the fact that local classes have access to local variables in the scope in which the local class is defined.

A big restriction

There is a big restriction, however, on the ability of methods of the local class to access local variables and method parameters.  The methods in a local class can access local variables and method parameters only if they are declared final.

What does Flanagan have to say?

According to one of my favorite authors, David Flanagan, author of Java in a Nutshell, the methods in a local class don't really have access to local variables and method parameters.  Rather, when an object of the local class is instantiated, copies of the final local variables and method parameters referred to by the object's methods are stored as instance variables in the object.  The methods in the object of the local class really access those hidden instance variables.  (See the later section entitled Smoke and mirrors.)

Thus, the local variables and method parameters accessed by the methods of the local class must be declared final to prevent their values from changing after the object is instantiated.

Restrictions on local classes

As with member classes, local classes cannot contain static members.

As with local variables, local classes cannot be declared public, protected, private, or static

A local class cannot have the same name as any of its enclosing classes.

Smoke and mirrors

As I mentioned in the previous lesson, every class definition in a Java program, including nested top-level classes, member classes, local classes, and anonymous classes, produces a class file when the program is compiled.  According to Flanagan,

"The Java Virtual Machine knows nothing about nested top-level classes and interfaces or the various types of inner classes.  Therefore, the Java compiler must convert these new types into standard non-nested class files that the Java interpreter can understand.  This is done through source code transformations that insert $ characters into nested class names.  These source code transformations may also insert hidden fields, methods, and constructor arguments into the affected classes."

A reference to the containing object

For example, the compiler automatically inserts a private instance variable in the local class to hold a reference to the containing object.  It also inserts a hidden argument in all constructors for the local class, and passes the containing object's reference to the constructor for the local class.  The modified constructor saves that reference in the private instance variable of the object of the local class.  Thus each object instantiated from the local class contains a private reference to the containing object.

Accessing private members

In those cases where it is necessary for an object of the local class to access private members of the containing object, the compiler automatically creates and uses accessor methods that make such access possible.

Similar to your code

The bottom line is that the code that is automatically produced by the compiler is probably very similar to code that you would write if you were writing the program using only top-level classes.  The good news is that you don't have to write that extra code, and you don't have to maintain it.  The extra code is written for you, and if you modify your class structure, the extra code is automatically modified accordingly.

Enough talk, let's see some code

The paragraphs that follow will explain a program named InnerClasses07, which is designed specifically to illustrate various characteristics of local classes.  I will discuss the program in fragments.  A complete listing is shown in Listing 17 near the end of the lesson.

Discussion and Sample Code

This program illustrates the use of local classes.  The program consists of a total of five classes:
When compiled, the program produces the class files shown in Figure 1.  The file named A$1$B.class represents the local class.  The remaining files in Figure 1 represent the four top-level classes.

A$1$B.class
A.class
InnerClasses07.class
X.class
Y.class
Figure 1

Class hierarchy

In the previous lesson, I explained that once you understand the class file naming convention, you can determine from the file names how top-level classes and member classes are structured.  However, the situation isn't nearly so clear when it comes to local classes and anonymous classes.  This will become more apparent in the next lesson, which combines local classes and anonymous classes.

Overall program structure and behavior

The program named InnerClasses07 defines a local class named B inside an instance method named meth.  The method named meth is an instance method of a top-level class named A.

The method named meth instantiates two separate objects of the local class named B, and invokes a method named showB on each of them.

The method named showB displays certain data values that illustrate the characteristics of local classes, as well as the containment hierarchy among objects of the local class and an object of the containing class.  In this case, objects of the class named B are contained within an object of the class named A(The class named A is an enclosing class of the class named B.)

Inheritance hierarchy

The top-level class named A extends the top-level class named X, (which in turn, extends Object).  The class named B is contained in or enclosed by the top-level class named A, but extends the top-level class named Y, (which in turn, extends Object).

There is no inheritance relationship between the classes X and Y (aside from their common superclass named Object).  There is no inheritance relationship between the classes A and B.

The method named showB also displays data values that are intended to demonstrate that the inheritance hierarchy is independent of the containment hierarchy.
(Note that while the containment hierarchy of local classes is independent of the inheritance hierarchy, it is technically possible to establish an inheritance relationship between a local class and one of its enclosing classes.  For example, by making a couple of minor modifications, it is possible to cause the local class B in this program to extend the enclosing class A instead of the top-level class Y.)
The controlling class

The entire controlling class named InnerClasses07, including the main method and a static variable named baseTime, is shown in Listing 1.

public class InnerClasses07{

static long baseTime = new Date().getTime();

public static void main(String[] args){
new A().meth();
}//end main
}//end class InnerClasses07

Listing 1

The static variable named baseTime is initialized with the current date and time in milliseconds (relative to January 1, 1970).  This static variable is used later as a base to establish the relative points in time that certain activities occur during the execution of the program.

The main method

The main method shown in Listing 1 instantiates a new object of the class named A and invokes the method named meth on that object. This method will sequentially instantiate two separate objects of a local class named B that is defined inside the method named meth. Then the method named meth will invoke a method named showB on each of those objects, causing them to display data values that illustrate the characteristics of local classes.

The class named X

The top-level class named X is shown in Listing 2.

class X{
protected int xVar = 1000;
}//end class X


Listing 2

The class named A extends this class to illustrate the difference between the inheritance hierarchy and the containment hierarchy.  Note that this class defines and initializes a protected instance variable, which will be accessed later to illustrate the inheritance hierarchy.
(Somewhat surprisingly, you will also see later that this protected instance variable belonging to the superclass of the enclosing class A is also accessible by methods belonging to an object of the local class via the containment hierarchy.)
The class named Y

The top-level class named Y is shown in Listing 3.

class Y{
protected int yVar = 2000;

public String toString(){
return "toString in class Y, yVar = " + yVar;
}//end overridden toString
}//end class Y
Listing 3

The local class named B extends this class to illustrate the difference between the inheritance hierarchy and the containment hierarchy.  Note that this class defines and initializes a protected instance variable.  It also overrides the toString method, which is inherited from the Object class.

The instance variable and the overridden toString method will be accessed later to illustrate the inheritance hierarchy.

The class named A

The code in Listing 4 shows the beginning of the top-level class named A.

class A extends X{
private long aTime = new Date().getTime() -
InnerClasses07.baseTime;

A(){//constructor
System.out.println(
"In xstr for A, aTime = " + aTime);
}//end constructor

Listing 4

The code in Listing 4 declares and initializes a private instance variable named aTime, which establishes the relative time that an object of class A is instantiated.  This private instance variable will be accessed directly by code belonging to an object of the local class B, which is contained by an object of the class A.

Listing 4 also shows the constructor for class A, which displays the time whenever an object is instantiated from class A.

The screen output

The code shown earlier in the main method of Listing 1 instantiates an object of class A.  This causes the screen output shown in Figure 2.

In xstr for A, aTime = 10
Figure 2
(Note that the relative time output value on your system may be different, depending on the speed of your system and the impact of other applications that may be running concurrently.)
The private showA method

Listing 5 shows a private method named showA, which displays the following information about an object instantiated from class A:

  private void showA(){
System.out.println(
"In showA, aTime = " + aTime);
System.out.println(
"In showA, xVar = " + xVar);
System.out.println("In showA, class name = "
+ getClass().getName());
}//end showA

Listing 5

Code in methods belonging to an object of the local class named B, contained in an object of class A, has direct access to private members of objects of the containing or enclosing class.  The private showA method will be invoked by a method named showB, belonging to an object of the local class B, to demonstrate the truth of this statement.

The method named delay

Listing 6 shows a utility method named delay, which is an instance method of class A.

  void delay(){
try{
Thread.currentThread().sleep(30);
}catch(InterruptedException e){
System.out.println(e);
}//end catch
}//end delay

Listing 6

The purpose of the delay method is simply to insert a 30-millisecond delay in the execution of the program.  This method is used to cause certain activities in the execution of the program to be spread far enough apart in time to make them distinguishable, one from the other, on the basis of relative time.

The method named meth

The method named meth is an instance method of the class A.  The beginning of this method is shown in Listing 7.

  void meth(){
final long methTime;
Listing 7

The method named meth contains a local class definition for a class named B, which we will examine later.  As you can see in Listing 7, it also declares a final local variable named methTime.

As you will see later, the method named meth instantiates two separate objects of local class B and invokes a method named showB on each of those objects. The method named showB displays various data values that illustrate the characteristics of local classes, including the value of the final local variable named methTime.

Access to final local variables

One of the characteristics of a local class is that objects of a local class have access to local variables within the same scope, provided that those local variables are declared final.

Blank final variables

The code in Listing 7 declares a final local variable named methTime.  Because of the syntax used, this variable is known as a blank final variable.  A blank final variable is not initialized when it is declared.  However, as with all local variables, the variable cannot be used until a value has been assigned to it, and once a value has been assigned to the variable, the value cannot be changed throughout the remaining life of the variable.

Set value of blank final variable

The code in Listing 8
The value that is set in the variable named methTime is the relative time that the statement is executed.

    delay();
methTime = new Date().getTime() -
InnerClasses07.baseTime;
System.out.println(
"In meth, methTime = " + methTime);
delay();

Listing 8

The screen output

The code in the main method of Listing 1 invokes meth on a new object of class A, causing the screen output shown in Figure 3.  (Once again, the actual time value displayed by your system may be different, depending on the speed of your system and other factors as well.)

In meth, methTime = 40
Figure 3

The local class named B

The next block of code in the method named meth is the definition of a local class named B.  The beginning of the class definition for local class B is shown in Listing 9.

    class B extends Y{
private long bTime;

B(){//constructor
bTime = new Date().getTime() -
InnerClasses07.baseTime;
System.out.println(
"In xstr for B, bTime = " + bTime);
}//end constructor

Listing 9

As with local variables, the class definition for a local class must appear before the code that attempts to instantiate the class.  Code in the method named meth following the local class definition will instantiate and exercise objects of class B.

Local class B extends top-level class Y.  This was done to illustrate that the inheritance hierarchy is independent of the containment hierarchy.

The constructor

The code in Listing 9 declares a private instance variable named bTime, which is used to store the relative time that an object of class B is constructed.


Listing 9 also shows the constructor for local class B, which establishes, saves, and then displays the relative time that that an object is instantiated.  I will show you the screen output produced by this constructor shortly as I discuss code that instantiates objects of this class.

Instantiate two objects of class B

At this point, I am going to momentarily set aside the discussion of local class B and discuss code in the method named meth that immediately follows the definition of local class B.  This code is shown in Listing 10.

    System.out.println("----------------------");
System.out.println(
"Instantiate first B-object");
final B obj1 = new B();
System.out.println(
"Delay and instantiate second B-object");
delay();
final B obj2 = new B();

Listing 10

Listing 10 shows the beginning of code that is invoked when the method named meth is invoked.  This code begins by instantiating two objects from the class named B, with a delay inserted between the instantiation of the two objects.

The screen output

The code in Listing 10, in conjunction with the constructor code in Listing 9 produces the screen output shown in Figure 4 as each of the two objects of local class B are instantiated.

----------------------
Instantiate first B-object
In xstr for B, bTime = 70
Delay and instantiate second B-object
In xstr for B, bTime = 100
Figure 4

Invoke showB on the first object

Following this, the code in the method named meth invokes the method named showB on each of the two objects.  The method named showB will, in turn, invoke the method named showA on the containing object instantiated from the class named A.

The third line of code in Listing 11 invokes the method named showB on the first object instantiated in Listing 10.

    System.out.println("----------------------");
System.out.println("Display first B-Object");
obj1.showB();

Listing 11

The method named showB

That brings us back to a discussion of the method named showB, which is an instance method of local class B.  The beginning of the showB method is shown in Listing 12.

This method displays several private and protected variables, some of which belong to the containing object instantiated from the top-level class named A.

//Continuing with local class B definition
void showB(){
System.out.println("-1-");
System.out.println(
"In showB, private bTime = " + bTime);
System.out.println(
"In showB, private aTime = " + aTime);
System.out.println(
"In showB, final methTime = " +
methTime);
System.out.println(
"In showB, protected xVar = " + xVar);
System.out.println(
"In showB, protected yVar = " + yVar);
System.out.println(
"In showB, class name = " +
getClass().getName());

Listing 12
(Note that code in this method has direct access to xVar, which is a protected member variable of the superclass of the class named A.)
Items that are displayed

The code in Listing 12 displays
The screen output

The combined code in Listings 11 and 12 produced the output shown in Figure 5 for the first object instantiated from local class B.

----------------------
Display first B-Object
-1-
In showB, private bTime = 70
In showB, private aTime = 10
In showB, final methTime = 40
In showB, protected xVar = 1000
In showB, protected yVar = 2000
In showB, class name = A$1$B
Figure 5

Invoke private method showA

One of the important characteristics of local classes is that the methods of objects instantiated from local classes have direct access to all of the members, including private members, of all the containing classes in the containment hierarchy.
(In this case, an object of the local class B has only one containing class, an object instantiated from the top-level class A.)
Continuing with the method named showB, in local class B, the code in Listing 13 displays a line separator, -2-, and then invokes the private method named showA on the containing object.  This illustrates the containment hierarchy.

        System.out.println("-2-");
showA();

Listing 13

The screen output

The method named showA is shown in Listing 5.  The combination of the code in Listing 13 and Listing 5 produced the screen output shown in Figure 6.

-2-
In showA, aTime = 10
In showA, xVar = 1000
In showA, class name = A
Figure 6

Figure 6 displays
Use of the keyword this

As is the case with member classes, (discussed in the previous lesson), objects of local classes use a special syntax of the keyword this to gain access to objects in the containment hierarchy.

The code in Listing 14 shows how to use regular syntax to gain access to the current object, and how to use special syntax to gain access to the containing object.

Having gained access to the two objects, the code in Listing 14 gets and displays the values of private instance variables belonging to those objects.

        System.out.println("-3-");
System.out.println(
"In showB, bTime = " + this.bTime);
System.out.println(
"In showB, aTime = " + A.this.aTime);
Listing 14

The screen output

The code in Listing 14 produced the screen output shown in Figure 7.


-3-
In showB, bTime = 70
In showB, aTime = 10
Figure 7

You saw these same two values displayed earlier in Figure 4 and Figure 2.

Illustrate the inheritance hierarchy

Remember that the inheritance hierarchy is independent of the containment hierarchy.  The code in Listing 15 illustrates the inheritance hierarchy by
Listing 14 also defines the overridden toString method belonging to local class B.  The overridden toString method belonging to class Y is shown in Listing 3.

        System.out.println("-4-");
System.out.println(toString());
System.out.println(super.toString());
}//end showB
//---------------------------------------//

//Overridden toString method
public String toString(){
return
"toString in class B, bTime = " + bTime;
}//end overridden toString
}//end local class B

Listing 15

The screen output

The combined code in Listing 15 and Listing 3 produced the screen output shown in Figure 8.  (
Once again, you have seen these same values displayed in earlier figures.)

-4-
toString in class B, bTime = 70
toString in class Y, yVar = 2000
Figure 8

That concludes the results of invoking the showB method on the first object instantiated from local class B.

Invoke showB method on second object

The code in Listing 10 instantiated two objects from local class B, and saved the object's references in instance variables named obj1 and obj2.  The screen output that I have been discussing resulted from invoking the showB method on obj1 in Listing 11.

Listing 16 invokes the showB method on obj2.  Listing 16 also signals the end of the method named meth, and the end of the class named A.

    System.out.println("----------------------");
System.out.println(
"Display second B-Object");
obj2.showB();

}// end meth
}//end class A

Listing 16

The screen output

The code in Listing 16 produced the screen output shown in Figure 9


----------------------
Display second B-Object
-1-
In showB, private bTime = 100
In showB, private aTime = 10
In showB, final methTime = 40
In showB, protected xVar = 1000
In showB, protected yVar = 2000
In showB, class name = A$1$B
-2-
In showA, aTime = 10
In showA, xVar = 1000
In showA, class name = A
-3-
In showB, bTime = 100
In showB, aTime = 10
-4-
toString in class B, bTime = 100
toString in class Y, yVar = 2000
Figure 9

Important considerations

The important things to note about the output shown in Listing 16 include:

Run the Program

At this point, you may find it useful to compile and run the program shown in Listing 17 near the end of the lesson.

Summary

In addition to a number of other items, a class definition can contain:
Member classes were explained in the previous lesson.  This lesson explains local classes.  Subsequent lessons will explain anonymous classes, as well as nested top-level classes and interfaces.

A local class is a class that is defined within a block of Java code.  While local classes are probably most frequently defined within method and constructors, they can also be defined inside static initializer blocks and instance initializers.

An object of the local class must be internally linked to an object of the enclosing class (which I often refer to herein as the containing object).

A local class is truly an inner class because an object of the local class cannot exist in the absence of an object of the enclosing class.

The methods of a local class have direct access to all the members in the hierarchy of enclosing classes, including private members.  In addition, the methods of local classes have access to final local variables and final method parameters in the scope in which the local class is defined.

The containment hierarchy of local classes is independent of the inheritance hierarchy.  However, it is technically possible to establish an inheritance relationship between a local class and one of its enclosing classes.

Local classes may not be declared public, protected, private, or static.

Local classes cannot contain static members.

A local class has approximately the same relationship to a member class that a local variable in a method has to an instance variable of the class containing the method.

What's Next?

The next lesson in this series will explain and discuss anonymous classes, and will also compare anonymous classes to local classes.  Subsequent lessons will explain top-level nested classes.

Complete Program Listing

A complete listing of the program discussed in this lesson is show in Listing 17 below.

/*File InnerClasses07.java
Copyright 2003 R.G.Baldwin

Illustrates the use of local classes.

This program defines a local class named B inside
an instance method named meth. The method named
meth is an instance method of a class named A.

The method named meth instantiates two separate
objects of the local class named B, and invokes
on each of them a method named showB, which
displays certain data values that illustrate the
characteristics of local classes, a well as the
relationships among objects of a local class and
an object to which that object is internally
associated. In this case, objects of the class
named B are internally associated with an object
of the class named A.

This program produces the following class files
when compiled:

A$1$B.class
A.class
InnerClasses07.class
X.class
Y.class

The file named A$1$B.class represents the local
class named B.

This program produces the following output:

In xstr for A, aTime = 10
In meth, methTime = 40
----------------------
Instantiate first B-object
In xstr for B, bTime = 70
Delay and instantiate second B-object
In xstr for B, bTime = 100
----------------------
Display first B-Object
-1-
In showB, private bTime = 70
In showB, private aTime = 10
In showB, final methTime = 40
In showB, protected xVar = 1000
In showB, protected yVar = 2000
In showB, class name = A$1$B
-2-
In showA, aTime = 10
In showA, xVar = 1000
In showA, class name = A
-3-
In showB, bTime = 70
In showB, aTime = 10
-4-
toString in class B, bTime = 70
toString in class Y, yVar = 2000
----------------------
Display second B-Object
-1-
In showB, private bTime = 100
In showB, private aTime = 10
In showB, final methTime = 40
In showB, protected xVar = 1000
In showB, protected yVar = 2000
In showB, class name = A$1$B
-2-
In showA, aTime = 10
In showA, xVar = 1000
In showA, class name = A
-3-
In showB, bTime = 100
In showB, aTime = 10
-4-
toString in class B, bTime = 100
toString in class Y, yVar = 2000


Tested using SDK 1.4.1 under WinXP
************************************************/
import java.util.Date;

public class InnerClasses07{
//The following static variable is used as a
// base to establish the relative points in
// time that certain activities occur during
// the execution of the program.
static long baseTime = new Date().getTime();

public static void main(String[] args){
//Instantiate a new object of the class named
// A and invoke the method named meth on that
// object. This method will sequentially
// instantiate two separate objects of a
// local class named B that is defined inside
// the method named meth. Then it will
// invoke a method named showB on each of
// those objects to cause them to display
// various data values that illustrate the
// characteristics of local classes.
new A().meth();
}//end main
}//end class InnerClasses07
//=============================================//

//The class named A extends this class to
// illustrate the difference between the
// inheritance hierarchy and the containment
// hierarchy.
class X{
protected int xVar = 1000;
}//end class X
//=============================================//

//The local class named B extends this class to
// illustrate the difference between the
// inheritance hierarchy and the containment
// hierarchy.
class Y{
protected int yVar = 2000;

//Overridden toString method
public String toString(){
return "toString in class Y, yVar = " + yVar;
}//end overridden toString
}//end class Y
//=============================================//

class A extends X{
//Establish the relative time that the object
// of class A is instantiated.
private long aTime = new Date().getTime() -
InnerClasses07.baseTime;

A(){//constructor
System.out.println(
"In xstr for A, aTime = " + aTime);
}//end constructor
//-------------------------------------------//

//Displays information about the object
// instantiated from class A.
private void showA(){
System.out.println(
"In showA, aTime = " + aTime);
System.out.println(
"In showA, xVar = " + xVar);
System.out.println("In showA, class name = "
+ getClass().getName());
}//end showA
//-------------------------------------------//

//Method used to insert a time delay of 30
// milliseconds.
void delay(){
try{
Thread.currentThread().sleep(30);
}catch(InterruptedException e){
System.out.println(e);
}//end catch
}//end delay
//-------------------------------------------//

//This method contains a local class definition
// for a class named B. The method
// instantiates two separate objects of class B
// and invokes a method named showB on each of
// the objects. The method named showB
// displays various data values that illustrate
// the characteristics of local classes.
void meth(){
//The following local variable must be final
// to be accessible from a local class. This
// is a blank final variable whose value can
// be set once and never changed after that.
final long methTime;

//Delay and then set the value of the blank
// final local variable to a relative time.
// Then delay again before continuing.
delay();
methTime = new Date().getTime() -
InnerClasses07.baseTime;
System.out.println(
"In meth, methTime = " + methTime);
delay();
//-------------------------------------------//

//This is the definition of a local class
// named B. Note that as with local
// variables, the class definition must
// appear before the code that attempts to
// instantiate the class. The class extends
// class Y to illustrate that the inheritance
// hierarchy is independent of the
// containment hierarchy.
class B extends Y{
private long bTime;

B(){//constructor
//Establish the relative time that the
// object is instantiated.
bTime = new Date().getTime() -
InnerClasses07.baseTime;
System.out.println(
"In xstr for B, bTime = " + bTime);
}//end constructor

void showB(){
//Display private and protected
// variables, some of which belong to the
// internally associated object
// instantiated from the class named A.
// Note that code in this method has
// access to xVar, which is a protected
// member variable of a superclass of the
// class named A.
System.out.println("-1-");
System.out.println(
"In showB, private bTime = " + bTime);
System.out.println(
"In showB, private aTime = " + aTime);
System.out.println(
"In showB, final methTime = " +
methTime);
System.out.println(
"In showB, protected xVar = " + xVar);
System.out.println(
"In showB, protected yVar = " + yVar);
System.out.println(
"In showB, class name = " +
getClass().getName());

System.out.println("-2-");
//Invoke the private method named showA
// in the internally associated object
// instantiated from the class named A,
// to illustrate the containment
// hierarchy.
showA();

System.out.println("-3-");
//Show how to access individual objects,
// including the internally associated
// object instantiated from the class
// named A.
System.out.println(
"In showB, bTime = " + this.bTime);
System.out.println(
"In showB, aTime = " + A.this.aTime);

System.out.println("-4-");
//Illustrate the inheritance hierarchy by
// invoking the overridden toString
// methods belonging to the class named B
// and its superclass named Y.
System.out.println(toString());
System.out.println(super.toString());
}//end showB
//---------------------------------------//

//Overridden toString method
public String toString(){
return
"toString in class B, bTime = " + bTime;
}//end overridden toString
}//end local class B

//This is the code that is executed when this
// method named meth is invoked. Instantiate
// two objects from the class named B and
// invoke the method named showB on each of
// them. Those methods will, in turn invoke
// the method named showA on the internally
// associated object instantiated from the
// class named A. Insert a delay between the
// instantiation of the two objects.
System.out.println("----------------------");
System.out.println(
"Instantiate first B-object");
final B obj1 = new B();
System.out.println(
"Delay and instantiate second B-object");
delay();
final B obj2 = new B();

System.out.println("----------------------");
System.out.println("Display first B-Object");
obj1.showB();
System.out.println("----------------------");
System.out.println(
"Display second B-Object");
obj2.showB();

}// end meth
}//end class A
//=============================================//

Listing 17

Copyright 2003, Richard G. Baldwin.  Reproduction in whole or in part in any form or medium without express written permission from Richard Baldwin is prohibited.

About the author

Richard Baldwin is a college professor (at Austin Community College in Austin, Texas) and private consultant whose primary focus is a combination of Java, C#, and XML. In addition to the many platform and/or language independent benefits of Java and C# applications, he believes that a combination of Java, C#, and XML will become the primary driving force in the delivery of structured information on the Web.

Richard has participated in numerous consulting projects, and he frequently provides onsite training at the high-tech companies located in and around Austin, Texas.  He is the author of Baldwin's Programming Tutorials, which has gained a worldwide following among experienced and aspiring programmers. He has also published articles in JavaPro magazine.

Richard holds an MSEE degree from Southern Methodist University and has many years of experience in the application of computer technology to real-world problems.

baldwin@DickBaldwin.com

-end-