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:
- Static variables
- Instance variables
- Static methods
- Instance methods
- Constructors
- Static initializer blocks
- Instance initializers
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:
- Member classes
- Local classes
- Anonymous classes
- Nested top-level classes and interfaces
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
- There is no reason for an object of the local class to exist
in the absence of an object of the enclosing class
- There is no reason for an object of the local class to exist
outside a method of the enclosing class
- Methods of the object of the local class need access to members
of the object of the enclosing class
- Methods of the object of the local class need access to final
local variables and method parameters belonging to the method in which
the local class is defined
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:
- Top-level classes named InnerClasses07, A, X, and Y
- A local class named B.
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:
- The relative time that the object was
instantiated.
- The value of the instance variable named xVar,
which is an instance variable of the superclass of class A.
- The name of the class file that represents
the class named 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
- Inserts a delay
- Sets a value for the final local
variable named methTime
- Displays the value that was set in methTime
- Inserts an additional delay before
continuing
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
- A visual line separator, -1-
- A private instance variable, bTime,
belonging to the object instantiated from local class B
- A private instance variable, aTime,
belonging to the containing object instantiated from the top-level
class A
- A final local variable, methTime,
belonging to the method named meth, in which the local class B
is defined
- A protected instance variable, xVar,
of the superclass of the class A, from which the containing
object was instantiated.
- A protected instance variable, yVar,
of the superclass of the local class B
- The name of the class file that represents
the local class B
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
- The relative time that the containing object of the class A
was instantiated
- A protected instance variable in the superclass of the class from
which the containing object was instantiated
- The name of the class file that represents the top-level class
named A
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
- Invoking the overridden toString method belonging to the
local class B
- Using the super keyword to invoke the overridden toString
method belonging to the class Y, which is the superclass of the
local class B
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:
- The second object instantiated from local class B is
definitely a different object from the first object instantiated from
local class B, as evidenced by a different value for bTime.
(Compare the value of bTime in Figure 9 with the value
of bTime in Figure 5.) In other words, the second
object was instantiated after the first object was instantiated.
- Even though the two objects instantiated from local class B
are different objects, they both belong to the same containing object,
as evidenced by the same values for aTime in Figure 9 and
Figure 5.
- The two objects instantiated from local class B each
access the same final local variable belonging to the method
named meth, as evidenced by the same values for methTime
in Figure 9 and Figure 5.
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
- Local classes
- Anonymous classes
- Nested top-level classes and interfaces
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-