Skip to content Skip to navigation

OpenStax_CNX

You are here: Home » Content » Java OOP: Member Classes

Navigation

Recently Viewed

This feature requires Javascript to be enabled.
 

Java OOP: Member Classes

Module by: R.G. (Dick) Baldwin. E-mail the author

Summary: Baldwin explains the various relationships that exist among member classes and their enclosing classes.

Preface

This module is one in a collection of modules designed for teaching ITSE2317 - Java Programming (Intermediate) at Austin Community College in Austin, TX.

(Editor's note: As you read this module, you will see that it was originally written around 2003. However, despite many improvements in Java since then, most of what was true then is still true in 2013.)

This module makes several references to my website, which is located at http://www.dickbaldwin.com/toc.htm .

Viewing tip

I recommend that you open another copy of this module in a separate browser window and use the following links to easily find and view the Images and listings while you are reading about them.

Images

Listings

Preview

What can you include in a class definition ?

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

  • Static variables
  • Instance variables
  • Static methods
  • Instance methods
  • Constructors

As you can learn at http://www.dickbaldwin.com/toc.htm , the list also includes:

  • Static initializer blocks
  • Instance initializers

Can also contain other class definitions

In this and the next two modules, you will learn that a class definition can also contain the following three kinds of inner classes :

  • Member classes
  • Local classes
  • Anonymous classes

A class can also contain Nested top-level classes and interfaces , which you can learn about at http://www.dickbaldwin.com/toc.htm .

(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.)

This module will be dedicated to an explanation of member classes . Subsequent modules will explain the other two types of inner classes in the above list .

What is a member class ?

A member class is a class that is defined inside the definition of another class, (without the use of the static modifier as is the case with a nested top-level class).

An object of the member class must be internally linked to an object of the enclosing class, (which is not the case with a nested top-level class).

Thus, a member class is truly an inner class. (An object of the member class cannot exist in the absence of an object of the enclosing class.)

What about a member 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 member classes ?

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

This is particularly useful in those cases where there is no reason for an object of a member class to exist in the absence of an object of the enclosing class, and where the methods of the object of the member class need access to members of the object of the enclosing class.

Data structures and iterators

For example, there is usually no reason for an Iterator object to exist in the absence of the data-structure object for which it is designed to provide iterator services. Also, the iterator object usually needs to have ready access to the members of the data-structure object, some or all of which may be private. Thus, a class from which an Iterator object can be constructed is a good candidate for inclusion as a member class in the class from which the associated data-structure object is instantiated.

Listener objects

Another common use for inner classes is in the definition of classes from which listener objects (which listen for events fired by other objects) are instantiated. (However, it may be more common to use anonymous classes than member classes for this purpose.)

What does Flanagan have to say?

Here is how David Flanagan, author of Java in a Nutshell, summarizes his discussion of member classes.

"A class defined as a member (non-static) of another. Each instance has an enclosing instance, and can use its members. New syntax for this , new , and super . Cannot have static members. Cannot have same name as containing class."

According to Flanagan, the main features of member classes are:

  • Every instance of a member class is internally associated with an instance of the class that defines or contains the member class.
  • The methods of a member class can implicitly refer to the fields defined within the member class, as well as those defined by any enclosing class, including private fields of the enclosing class.

Smoke and mirrors

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 member class to hold a reference to the containing object. It also inserts a hidden argument in all constructors for the member class, and passes the containing object's reference to the constructor for the member class. The modified constructor saves that reference in the private instance variable of the object of the member class. Thus each object instantiated from the member class contains a private reference to the containing object.

Accessing private members

In those cases where it is necessary for an object of the member 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 of 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.

Discussion and sample code

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

This program illustrates the use of member classes. The program consists of a total of six classes:

  • Top-level classes named
    • InnerClasses06
    • A
    • X
  • Member classes named
    • B
    • C
    • D

When compiled, the program produces the class files shown in Image 1 .

Class containment hierarchy

Once you understand the class file naming convention, you can determine from the file names in Image 1 that class B is a member class of class A.

(The class file named A$B.class indicates that the class named B is a member of the class named A.)

Similarly, class C is a member of class B, and class D is a private member of class C.

(However, you cannot tell from the class file names that class D is private.)

Behavior of the program

An object is instantiated from the class named A. This makes it possible to instantiate an object of the member class named B. The object of the class named B is internally linked to the object of the class named A.

(This causes the instance variable, constructor parameter, and accessor methods discussed above to be automatically created to link the object of the class named B to the object of the class named A.)

The object of the class named B is used to instantiate an object of the member class named C. This object of the class C is linked to the object of the class named B.

Instantiate additional objects of classes A and B, plus an object of class D

When the object of the class named C is instantiated, the constructor for that class instantiates separate objects of the classes named A and B, and also instantiates an object of the private member class named D.

(We will see later that the new and separate object of the class named B continues to be internally linked to the original object of the Class named A, and is not internally linked to the new object of the class named A.)

Instantiation of the object of class D illustrates the use of private member classes.

(Note that while top-level classes cannot be private, member classes can be private.)

Perform a variety of operations

A variety of operations are performed from within the methods belonging to the object of the class C to illustrate the attributes and behavior of objects instantiated from member classes.

Comments in the code explain the purpose of each of those operations.

Many of those operations produce screen output, which will be shown in conjunction with the code that produced the output.

Structure of the program

The main method

The main method of the controlling class named InnerClasses06 , is shown in Listing 1 .

The code in Listing 1 instantiates an object of the member class named C and calls the method named cShow on that object.

(Note that it is necessary to first instantiate objects of the enclosing classes named A and B before the object of the member class named C can be instantiated.)

An independent top-level class named X

Listing 2 shows the definition of an independent top-level class named X.

This class will be extended by the class named C, which is a member of the class named B, which is a member of the class named A. This will illustrate that the inheritance hierarchy is independent of the containment hierarchy.

As you can see in Listing 2 , the class named X overrides the toString method to identify itself when called.

(The toString method is automatically called whenever an object's reference is passed as a parameter to the println method.)

The top-level class named A

Listing 3 shows the beginning of the top-level class named A.

Listing 3 shows the declaration of two instance variables and three class variables in the class named A. All of the variables are private, and some are initialized when declared. The three class variables will be used to maintain a count of the number of objects instantiated from the classes named A, B, and C.

(Because member classes cannot contain static members , the counter variables for the member classes named B and C were placed in the top-level class named A instead of placing them in their respective class definitions.)

Constructor for class A

Listing 4 shows the constructor for the top-level class named A.

Whenever an object of the class named A is instantiated, the constructor does the following:

  • Saves the value of an incoming parameter in a private instance variable named aVar .
  • Increments the object counter named objCntA , maintaining a count of the objects instantiated from class A.
  • Saves the value of the object counter in an instance variable named objNumber to identify the specific object.
  • Displays a message showing the identification of the object being instantiated.

The screen output

The code in Listing 1 instantiates a new object of the class named A, passing the integer value 1 as a parameter to the constructor. As a result, the code in the constructor shown in Listing 4 produces the screen output shown in Image 2 .

As you can see from the value of the object counter in Image 2 , this is the first object instantiated from the class named A.

(The value passed, as a parameter to the constructor, is not displayed by the code in the constructor. That value will be displayed later.)

The method named aShow

The class named A also defines a private method named aShow . I will defer my discussion of that method until later when it is called.

The member class named B

Listing 5 shows the beginning of the member class named B.

If you examine the complete listing of the program in Listing 25 , you will see that the class named B is defined internal to the class named A. In other words, the beginning of the definition of the class named B appears before the curly brace that signals the end of the definition of the class named A. Thus, the class named B is a member class of the class named A.

The code in Listing 5 declares two private instance variables and initializes one of them.

Constructor for class B

Listing 6 shows the entire constructor for the class named B.

Whenever an object of the class named B is instantiated, the constructor does the following:

  • Saves the value of an incoming parameter in a private instance variable named bVar .
  • Increments the object counter named objCntB , which is a class variable of the containing top-level class named A, maintaining a count of objects instantiated from class B.
  • Saves the value of the object counter in an instance variable named objNumber to identify the specific object.
  • Displays a message showing the identification of the object being instantiated.

The screen output

Listing 1 shows the instantiation of a new object of class B, immediately following the instantiation of an object of class A.

The object instantiated from the member class named B is linked to the object instantiated from the top-level class named A.

The constructors for the classes named A and B produce the two lines of output shown in Image 3 , the first of which is a repeat of the output shown in Image 2 .

The method named bShow

The class named B also defines a private method named bShow . As with the method named aShow mentioned earlier, I will defer a discussion of bShow until later when it is called.

The member class named C

Listing 7 shows the beginning of a member class named C.

Class C is a member of the class named B. In other words, the beginning of the definition of the class named C begins before the curly brace that ends the definition of the class named B.

The code in Listing 7 declares several instance variables for the class named C, and initializes two of them. The purpose of these variables will become clear later when they are used.

(Note also that class C extends class X, in order to illustrate that the class containment hierarchy is independent of the inheritance hierarchy.)

Constructor for class C

Listing 8 shows the beginning of the constructor for the class named C.

Whenever an object of the class named C is instantiated, the constructor code shown in Listing 8 does the following:

  • Saves the value of an incoming parameter in a private instance variable named cVar .
  • Increments the object counter named objCntC , which is a class variable of the class named A, maintaining a count of objects instantiated from class C.
  • Saves the value of the object counter in an instance variable named objNumber to identify the specific object.
  • Displays a message showing the identification of the object being instantiated.

Screen output

Listing 1 shows the instantiation of a new object of class C, immediately following the instantiation of an object of class B.

The object instantiated from the member class named C is linked to the object instantiated from the member class named B. Similarly, the object instantiated from the member class named B is linked to the object instantiated from the top-level class named A.

The constructors for the classes named A, B, and C produce the three lines of output shown in Image 4 , the first two of which are repeated from Images 2 and Image 3 .

The output shown in Image 4 demonstrates that the code in Listing 1 causes the constructors for the three classes to be executed in sequence.

At this point, I am going to put the discussion of the class named C on hold and discuss another member class named D.

The private member class named D

Top-level classes cannot be private. However, member classes can be private provided that the using code is consistent with the use of private members. To demonstrate this, the class named C contains a private member class named D, which is shown in its entirety in Listing 9 .

The most significant thing about the class named D is that it is declared private.

When an object is instantiated from the class named D, it displays a couple of messages, one of which provides the name of the class file produced by the compiler to represent the class named D. We will see those messages shortly in conjunction with the instantiation of an object of the class named D.

Returning to the constructor for class C

Listing 10 shows the next statement in the constructor for the class named C. This statement instantiates an object of its private member class named D.

The code in Listing 10 causes the constructor for the class named D to be executed, producing the screen output shown in Image 5 .

As mentioned earlier, comparing the class file name in Image 5 with the class file naming convention for member classes, you can determine that D is a member of C, C is a member of B, and B is a member of A.

Instantiate independent objects of classes A and B

The remaining constructor code for class C is shown in Listing 11 .

The code in Listing 11 instantiates new and independent objects of the classes named A and B, both of which are enclosing classes of the member class named C.

(Note that the parameter values passed to the constructors are different than was the case for the objects instantiated in Listing 1 . We will see the result of that later.)

I will display information about these two objects later. That information will show that the new object of the member class named B is linked to the original object of the enclosing class named A.

The screen output

In the meantime, when these two objects are instantiated, their constructors are executed, producing the screen output shown in Image 6 .

In each case, the value of the object counter shows that this is the second object instantiated from each of these two classes.

Methods aShow, bShow, and cShow

The classes named A, B, and C, each contain display methods named aShow , bShow , and cShow respectively.

The method named cShow is rather long, and I will discuss it in detail shortly. For now, suffice it to say that code in cShow calls the private method named bShow in the containing object to which it is linked. Therefore, this will be an appropriate time to examine the method named bShow , which is defined in the member class named B.

The method named bShow

The bShow method, defined in the member class named B, is shown in Listing 12 . It is important to note that this is a private method.

When this method is called, it does the following:

  • Displays the value of the constructor parameter passed to the object when it was constructed.
  • Displays the identification of the object based on the value of the object counter when it was constructed.
  • Calls the corresponding aShow method of the object of the containing class to which it is linked.

Since the code in the bShow method calls the private aShow method of the containing object to which it is linked, it is also time to take a look at that method.

The method named aShow

The aShow method, defined in the top-level class named A, is shown in Listing 13 . It is also important to note that this is a private method.

When this method is called, it does the following:

  • Displays the value of the constructor parameter passed to the object when it was constructed.
  • Displays the identification of the object based on the value of the object counter when it was constructed.

Containment hierarchy is displayed

Because cShow calls bShow , which in turn calls aShow , we should expect that the call to the cShow method on an object of the member class named C would display information about the containment hierarchy.

(Simply as another reminder, the containment hierarchy is completely independent of the inheritance hierarchy.)

Calling cShow

Referring once more to Listing 1 , we see that the method named cShow is called on the object of the class named C when that object is instantiated. We will see the result of that call shortly.

The cShow method

Listing 14 shows the beginning of the cShow method.

The code in Listing 14

  • Displays a string separator to help locate the specific output in the large quantity of output produced by the program.
  • Displays the object identifier based on the object counter.
  • Displays the value passed to the constructor when the object was instantiated.

The screen output

The code in Listing 14 produces the output shown in Image 7 .

As you can see by comparing this with Listing 1 , this is the first object instantiated from the class named C, and is the object instantiated from the statement in the main method in Listing 1 . (The constructor parameter value is 3.)

Call the bShow method

Continuing with the code in the cShow method, the code in Listing 15 calls the private method named bShow on the containing object of the class B to which this object is linked.

As you will recall from the previous discussion, the code in the bShow method will, in turn, call the aShow method on the containing object of the class named A to which the object of the class B is linked.

The screen output

The code in Listing 15 produces the output shown in Image 8 .

As you can see in Image 8 , the linked objects of the classes B and A are the first objects instantiated from those classes. In addition, the saved values of the constructor parameters show that these are the objects that were instantiated by the statement in the main method of Listing 1 .

Call the aShow method

As I explained earlier, the object of the class C is linked to the containing object of the class named B. The code in Listing 16 shows that the object of the class C is also linked to the containing object of the class A (even though the containing class named A is one level removed in the containment hierarchy).

Implicit access

The methods of a member class have implicit access to all members (including private members) of all containing classes. Thus, the code in the cShow method, belonging to the object of the class named C, can directly call the private aShow method of the containing class named A.

The screen output

Therefore, the code in Listing 16 produces the output shown in Image 9 .

You can tell by the values displayed in Image 9 that the aShow method called in Listing 16 was called on the same object on which the aShow method was called by the code in Listing 15 . However, in Listing 15 , the bShow method was called first, which in turn called the aShow method.

Accessing the object of the class C, and the this keyword

The syntax used with the keyword this is somewhat different for member classes and contained objects than is the case for top-level classes. For example, continuing with the method named cShow , the code in Listing 17 shows five different ways to access the object instantiated from the member class named C in order to get and display the name of the class file that represents the member class named C.

The screen output

All five statements in Listing 17 display the name of the same class file, as shown in Image 10 .

Obviously in this situation, the last three statements in Listing 17 are overly complex. There is no particular problem writing code in the method named cShow to gain access to the object to which the method belongs. It isn't even necessary to use this to refer to that object, although the use of the hidden reference this may make the code more readable.

Accessing the containing object of the class B

However, things get a little more complicated when you need to gain access to a containing object, such as the containing object instantiated from the class named B.

The two statements shown in Listing 18 gain access to the containing object of the class named B. Each statement gets and displays the name of the class file that represents the member class named B. (Note the use of the keyword this in these statements.)

The screen output

The output produced by the code in Listing 18 is shown in Image 11 . Once again, both statements get and display the name of the same class file.

Accessing the containing object of the class named A

Finally, the code in Listing 19 gains access to the containing object of the class named A. (Once again, note the use of the this keyword in the statement in Listing 19 .)

The code in Listing 19 produces the output shown in Image 12 .

(Since the class named A is a top-level class, the name of the class file is the same as the name of the class, with no $ characters inserted by the compiler.)

Investigate independent objects of classes A and B

Recall that when the object of the member class named C was instantiated, the constructor for the class instantiated independent objects of the enclosing classes named A and B, and saved those object's references in instance variables of the class named C.

(See Listing 11 , noting the parameter values of 10 and 20 passed to the constructors for A and B. Recall that the constructors for A and B save those parameter values in private instance variables named aVar and bVar .)

Display variable values and class file names

The code in Listing 20 displays the values stored in the private instance variables belonging to those objects. The code in Listing 20 also displays the names of the class files representing the classes from which those objects were instantiated.

Screen output

The code in Listing 20 produces the output shown in Image 13 .

There should be no surprises in the output shown in Image 13 . The values of the instance variables match the parameter values passed to the constructors in Listing 11 when the objects were instantiated. The class file names match what you already know to be true from previous discussions earlier in this module.

Call the private bShow method

The code in Listing 21 is somewhat more interesting. This code calls the private bShow method on the separate object instantiated from the class named B in order to identify the object to which that object is linked.

The screen output

The output produced by the code in Listing 21 is shown in Image 14 . Even though this object of the member class B was instantiated from within the constructor for the member class named C, the object of the class named B is internally linked to the object of the class named A that was originally used to instantiate the object of the class named C.

(See Listing 1 where the objects of classes named A, B, and C were originally instantiated. This object of the class named B is a different object from the object of the class named B instantiated in Listing 1 . This object of the class named B was instantiated by the code in Listing 11 .)

How is this determined from Image 14?

The second line in Image 14 shows that a parameter value of 20 was received by the constructor when the object of the class named B was instantiated. This corresponds to the instantiation of the object by the code in the constructor in Listing 11 .

The third line in Image 14 shows that this was the second object instantiated from the class named B. (See the definition of the bShow method in Listing 12 , which displays the value stored in a variable that is used to save the object number.)

The proof of the pudding

Now recall that the method named bShow (belonging to an object of the class B) calls the method named aShow belonging to the object of the class named A to which it is internally linked.

The fourth line in Image 14 shows the value of the parameter passed to the constructor for the object of class A when that object was instantiated. (See the definition of the aShow method in Listing 13 .) This value corresponds to the value that was passed to the constructor for the original object of class A when it was constructed in Listing 1 .

(It does not correspond to the value passed to the constructor for the class named A when the object of the class A was constructed in Listing 11 .)

The fifth line in Image 14 shows that the object was the first object instantiated from the class named A.

Both B objects link to the same A object

Thus, both objects instantiated from the class named B in this program are internally linked to the same object instantiated from the class named A, which is the enclosing class for the class named B.

(However, had I instantiated the new object of the class B using a statement such as the following,

new A(100).new B(200).bShow();

the new object of the class B would have been linked to the new object of the class A rather than being linked to the original object of the class A. As you can see, keeping mental track of which object is linked to which other object could become complicated.)

Call the aShow method on the other A object

The code in Listing 22 calls the private aShow method on the independent object of the class A that was instantiated in the constructor for class C, shown in Listing 11 .

The output produced by the code in Listing 22 is shown in Image 15 .

It should come as no surprise that this object of the class A was instantiated with a constructor parameter value of 10, and that it was the second object of the class named A that was constructed.

This is simply a matter of code in the constructor for class C instantiating an object of a top-level class, and is no different in concept from instantiating an object of the member class B, also shown in Listing 11 .

Inheritance and containment hierarchies are independent

The remaining code is designed to demonstrate that the containment hierarchy is completely independent of the inheritance hierarchy.

The class named C is a member of (is contained in) the class named B. That constitutes a part of the containment hierarchy.

The class named C also extends the class named X, which in turn extends the class named Object . That constitutes the inheritance hierarchy.

Overridden toString methods

I'm going to put the discussion of the method named cShow on hold and return to that discussion shortly.

The class named X inherits, and overrides the toString method, as shown in Listing 2 . When this version of the toString method is called, it returns the string " toString in Class X ".

The class named C, which extends the class named X, also overrides the toString method as shown in Listing 23 .

When this version of the toString method is called, it returns the string

" toString in Class C ".

We will see the impact of overriding these two methods later.

Illustrate the inheritance hierarchy

Returning to the cShow method, the code in Listing 24 illustrates the inheritance hierarchy to which the class named C belongs by getting and displaying the value stored in the instance variables named className belonging to the object instantiated from the class named C.

(The object contains two instance variables having the name className . One of these instance variables was contributed to the object by the superclass named X. The other was contributed to the object by the class named C.)

Two instance variables named className

The String value X was stored in one of the instance variables named className by the initialization of the variable shown in Listing 2 .

The String value C was stored in the other instance variable named className by the initialization of the variable shown in Listing 7 .

( Note that the variable named className is protected in the class named X. A subclass method cannot access a private variable in a superclass. To be accessible by a subclass method, the superclass variable must be protected , package-private , or public .)

Two overridden toString methods

An object instantiated from the class named C also contains two overridden versions of the toString method. One version of the method was contributed to the object by the superclass named X. The other version was contributed to the object by the class named C.

Call the toString methods

The code in Listing 24 also calls the two overridden toString methods belonging to the object instantiated from the classes named C. As explained earlier, one version of the toString method is overridden in the class named X and the other version is overridden in the class named C.

(Note the use of the super keyword to access the variable named className and the method named toString contributed to the object by the superclass named X.)

The code in Listing 24 also signals the end of the cShow method.

The screen output

The output produced by the code in Listing 24 is shown in Image 16 .

Image 16 shows that even though the class named C is contained in the class named B, the superclass of C is X, and is not B. To repeat, the containment hierarchy is entirely independent of the inheritance hierarchy.

(Note, however, that there is nothing to prevent you from establishing an inheritance relationship between a member class and one of its containing classes if such a relationship will serve your needs. For example, in this program, it would be technically acceptable for the class named B to extend the class named A provided that either:
  • A noarg constructor is provided for the class named A, or
  • The constructor for the class named B calls the parameterized constructor belonging to the class named A.)

And that is probably more than you ever wanted to know about the detailed relationships involving member classes. However, once you start using member classes, you will need to keep these relationships in mind.

Run the program

I encourage you to copy the code from Listing 25 . Compile the code and execute it. Experiment with the code, making changes, and observing the results of your changes. Make certain that you can explain why your changes behave as they do.

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

This module explains member classes. Future modules will explain local classes and anonymous classes

A member class is a class that is defined inside the definition of another class without being declared static.

An object of the member class must be internally linked to an object of the enclosing class. A member class is truly an inner class because an object of the member class cannot exist in the absence of an object of the enclosing class.

The methods of a member class have direct access to all the members of the enclosing classes, including private members. Thus the use of member classes can eliminate the requirement to connect objects together via constructor parameters. This is particularly useful in those cases where there is no reason for an object of a member class to exist in the absence of an object of the enclosing class, and where the methods of the object of the member class need access to members of the object of the enclosing class.

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

Member classes may be declared private, and may be instantiated from code that would normally have access to a private member at that level.

What's next?

The next module in this series will explain and discuss local classes. A future module will explain anonymous classes.

Complete program listing

A complete listing of the program discussed in this module is show in Listing 25 .

Miscellaneous

This section contains a variety of miscellaneous information.

Note:

Housekeeping material
  • Module name: Java OOP: Member Classes
  • File: Java1636.htm
  • Published: 11/18/13
  • Revised: 11/20/13

Note:

Disclaimers:

Financial : Although the Connexions site makes it possible for you to download a PDF file for this module at no charge, and also makes it possible for you to purchase a pre-printed version of the PDF file, you should be aware that some of the HTML elements in this module may not translate well into PDF.

I also want you to know that, I receive no financial compensation from the Connexions website even if you purchase the PDF version of the module.

In the past, unknown individuals have copied my modules from cnx.org, converted them to Kindle books, and placed them for sale on Amazon.com showing me as the author. I neither receive compensation for those sales nor do I know who does receive compensation. If you purchase such a book, please be aware that it is a copy of a module that is freely available on cnx.org and that it was made and published without my prior knowledge.

Affiliation : I am a professor of Computer Information Technology at Austin Community College in Austin, TX.

Images

Figure 1: Image 1: Class files produced when the program is compiled.
Image 1: Class files produced when the program is compiled.
A$B$C$D.class
A$B$C.class
A$B.class
A.class
InnerClasses06.class
X.class
Figure 2: Image 2: Screen output.
Image 2: Screen output.
In xstr for A, objCntA = 1
Figure 3: Image 3: Screen output.
Image 3: Screen output.
In xstr for A, objCntA = 1
In xstr for B, objCntB = 1
Figure 4: Image 4: Screen output.
Image 4: Screen output.
In xstr for A, objCntA = 1
In xstr for B, objCntB = 1
In xstr for C, objCntC = 1
Figure 5: Image 5: Screen output.
Image 5: Screen output.
Construct obj of private class D.
Private class file name: A$B$C$D
Figure 6: Image 6: Screen output.
Image 6: Screen output.
In xstr for A, objCntA = 2
In xstr for B, objCntB = 2
Figure 7: Image 7: Screen output.
Image 7: Screen output.
-1-
In cShow, objNumber = 1
In cShow, cVar = 3
Figure 8: Image 8: Screen output.
Image 8: Screen output.
-2-
In bShow, bVar = 2
In bShow, objNumber = 1
In aShow, aVar = 1
In aShow, objNumber = 1
Figure 9: Image 9: Screen output.
Image 9: Screen output.
-3-
In aShow, aVar = 1
In aShow, objNumber = 1
Figure 10: Image 10: Screen output.
Image 10: Screen output.
-4-
A$B$C
A$B$C
A$B$C
A$B$C
A$B$C
Figure 11: Image 11: Screen output.
Image 11: Screen output.
-5-
A$B
A$B
Figure 12: Image 12: Screen output.
Image 12: Screen output.
-6-
A
Figure 13: Image 13: Screen output.
Image 13: Screen output.
-7-
In cShow, bVar = 20
A$B
In cShow, aVar = 10
A
Figure 14: Image 14: Screen output.
Image 14: Screen output.
-8-
In bShow, bVar = 20
In bShow, objNumber = 2
In aShow, aVar = 1
In aShow, objNumber = 1
Figure 15: Image 15: Screen output.
Image 15: Screen output.
-9-
In aShow, aVar = 10
In aShow, objNumber = 2
Figure 16: Image 16: Screen output.
Image 16: Screen output.
-10-
className = C
toString in Class C
className = X
toString in Class X

Listings

Listing 1: The main method.

public class InnerClasses06{
  public static void main(String[] args){
    new A(1).new B(2).new C(3).cShow();
  }//end main
}//end class InnerClasses06

Listing 2: The class named X.

class X{//extends Object by default
  protected String className = "X";

  public String toString(){
    return "toString in Class X";
  }//end overridden toString
}//end class X

Listing 3: Beginning of the top-level class named A.

class A{
  private int aVar;
  private int objNumber = 0;

  private static int objCntA = 0;
  private static int objCntB = 0;
  private static int objCntC = 0;

Listing 4: Constructor for class A.

  A(int val){//top-level class constructor
    aVar = val;
    objCntA++;//Increment object counter
    //Record the number of the object being
    // instantiated
    objNumber = objCntA;
    System.out.println(
      "In xstr for A, objCntA = " + objCntA);
  }//end constructor

Listing 5: Beginning of the member class named B.

class B{//member class of A
  private int bVar;
  private int objNumber = 0;

Listing 6: Constructor for class B.

    B(int val){//constructor
      bVar = val;
      //Increment static variable in top-level
      // class named A
      A.objCntB++;
      //Record the number of the object being
      // instantiated
      objNumber = objCntB;
      System.out.println(
          "In xstr for B, objCntB = " + objCntB);
    }//end constructor

Listing 7: Beginning of the member class named C.

    class C extends X{//member class of B
      private int cVar;
      private A refToA;
      private B refToB;
      private String className = "C";
      private int objNumber = 0;

Listing 8: Beginning of constructor for class C.

      C(int val){//constructor
        cVar = val;
        //Increment the object counter in the
        // top-level class named A.
        A.objCntC++;
        objNumber = A.objCntC;
        System.out.println(
                     "In xstr for C, objCntC = "
                                   + A.objCntC);

Listing 9: The private member class named D.

      private class D{//member class of C
        D(){//constructor
          System.out.println(
            "Construct obj of private class D.");
          System.out.println(
                    "  Private class file name: "
                    + this.getClass().getName());
        }//end constructor
      }//end class D

Listing 10: More constructor code for class C.

        new D();

Listing 11: More constructor code for class C.

        refToA = new A(10);
        refToB = new B(20);

      }//end constructor

Listing 12: The method named bShow.

    private void bShow(){
      System.out.println(
                     "In bShow, bVar = " + bVar);
      System.out.println(
           "In bShow, objNumber = " + objNumber);
      aShow();
    }//end bShow

Listing 13: The method named aShow.

  private void aShow(){
    System.out.println(
                     "In aShow, aVar = " + aVar);
    System.out.println(
           "In aShow, objNumber = " + objNumber);
  }//end aShow

Listing 14: Beginning of the cShow method.

      public void cShow(){
        System.out.println("-1-");//separator
        System.out.println(
           "In cShow, objNumber = " + objNumber);
        System.out.println(
                     "In cShow, cVar = " + cVar);

Listing 15: More cShow method code.

        System.out.println("-2-");//separator

        bShow();

Listing 16: Call the aShow method.

        System.out.println("-3-");//separator

        aShow();

Listing 17: More cShow method code.

        System.out.println("-4-");//separator

        System.out.println(getClass().getName());
        System.out.println(
                      this.getClass().getName());
        System.out.println(
                    C.this.getClass().getName());
        System.out.println(
                  B.C.this.getClass().getName());
        System.out.println(
                A.B.C.this.getClass().getName());

Listing 18: More cShow method code.

        System.out.println("-5-");//separator

        System.out.println(
                    B.this.getClass().getName());
        System.out.println(
                  A.B.this.getClass().getName());

Listing 19: More cShow method code.

        System.out.println("-6-");//separator

        System.out.println(
                    A.this.getClass().getName());

Listing 20: More cShow method code.

        System.out.println("-7-");

        System.out.println(
              "In cShow, bVar = " + refToB.bVar);
        System.out.println(
                    refToB.getClass().getName());

        System.out.println(
              "In cShow, aVar = " + refToA.aVar);
        System.out.println(
                    refToA.getClass().getName());

Listing 21: More cShow method code.

        System.out.println("-8-");

        refToB.bShow();

Listing 22: More cShow method code.

        System.out.println("-9-");

        refToA.aShow();

Listing 23: Overridden toString method in class C.

      public String toString(){
        return "toString in Class C";
      }//end overridden toString

Listing 24: More cShow method code.

        System.out.println("-10-");
        System.out.println(
                     "className = " + className);
        System.out.println(toString());

        System.out.println(
               "className = " + super.className);
        System.out.println(super.toString());
      }//end cShow method

Listing 25: Complete program listing.

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

Rev 6/22/03

Illustrates the use of member classes.  Class B
is a member class of class A.  Class C is a
member class of class B.  Class D is a private
member class of class C.  An object is
instantiated from the class named A, which makes
it possible to instantiate an object of the
member class named B.  According to Flanagan,
this causes the object of the Class B to be
internally associated with the object of the
class named A.  The object of the class named B
is used to instantiate an object of the member
class named C.  This object is internally
associated with the object of the class B.

When the object of the class C is instantiated,
the constructor for that class instantiates
separate objects of the classes named A and B and
also instantiates an object of the private member
class named D.  The new and separate object of
the class B continues to be internally associated
with the original object of the Class A.

A variety of operations are performed from within
methods belonging to the object of the Class C to
illustrate the various characteristics of objects
instantiated from member classes.  Comments in
the code explain the purpose of each of those
operations.

The compilation of this program produces the
following class files:

A$B$C$D.class
A$B$C.class
A$B.class
A.class
InnerClasses06.class
X.class

The output from this program is shown below:

In xstr for A, objCntA = 1
In xstr for B, objCntB = 1
In xstr for C, objCntC = 1
Construct obj of private class D.
  Private class file name: A$B$C$D
In xstr for A, objCntA = 2
In xstr for B, objCntB = 2
-1-
In cShow, objNumber = 1
In cShow, cVar = 3
-2-
In bShow, bVar = 2
In bShow, objNumber = 1
In aShow, aVar = 1
In aShow, objNumber = 1
-3-
In aShow, aVar = 1
In aShow, objNumber = 1
-4-
A$B$C
A$B$C
A$B$C
A$B$C
A$B$C
-5-
A$B
A$B
-6-
A
-7-
In cShow, bVar = 20
A$B
In cShow, aVar = 10
A
-8-
In bShow, bVar = 20
In bShow, objNumber = 2
In aShow, aVar = 1
In aShow, objNumber = 1
-9-
In aShow, aVar = 10
In aShow, objNumber = 2
-10-
className = C
toString in Class C
className = X
toString in Class X


Tested using SDK 1.4.1 under WinXP
************************************************/

public class InnerClasses06{
  public static void main(String[] args){
    //Instantiate an object of the member class
    // named C.  Note that it is necessary to
    // instantiate objects of the enclosing
    // classes as well.  Then invoke the public
    // method named cShow on the object of the
    // class named C.
    new A(1).new B(2).new C(3).cShow();
  }//end main
}//end class InnerClasses06
//=============================================//

//This class will be extended by the class named
// C, which is a member of the class named B,
// which is a member of the class named A.  This
// will illustrate that the inheritance
// hierarchy is independent of the containment
// hierarchy.
class X{//extends Object by default
  protected String className = "X";

  //Override the toString method
  public String toString(){
    return "toString in Class X";
  }//end overridden toString
}//end class X
//=============================================//

class A{
  private int aVar;
  private static int objCntA = 0;
  //Cannot place static variable in inner class.
  // Place it here instead.
  private static int objCntB = 0;
  //Cannot place static variable in inner class.
  // Place it here instead.
  private static int objCntC = 0;
  private int objNumber = 0;

  A(int val){//top-level class constructor
    aVar = val;
    objCntA++;//Increment object counter
    //Record the number of the object being
    // instantiated
    objNumber = objCntA;
    System.out.println(
          "In xstr for A, objCntA = " + objCntA);
  }//end constructor
  //-------------------------------------------//

  private void aShow(){
    System.out.println(
                     "In aShow, aVar = " + aVar);
    System.out.println(
           "In aShow, objNumber = " + objNumber);
  }//end aShow
  //===========================================//

  //Note that this class is defined internal to
  // the class named A.
  class B{//member class of A
    private int bVar;
    private int objNumber = 0;

    B(int val){//constructor
      bVar = val;
      //Increment static variable in top-level
      // class
      A.objCntB++;
      //Record the number of the object being
      // instantiated
      objNumber = objCntB;
      System.out.println(
          "In xstr for B, objCntB = " + objCntB);
    }//end constructor
    //-----------------------------------------//

    private void bShow(){
      System.out.println(
                     "In bShow, bVar = " + bVar);
      System.out.println(
           "In bShow, objNumber = " + objNumber);
      //Invoke the private method named aShow
      // belonging to the internally associated
      // object of the class named A.
      aShow();
    }//end bShow
    //=========================================//

    //Note that this class is defined internal to
    // the class named B.
    class C extends X{//member class of B
      private int cVar;
      private A refToA;
      private B refToB;
      private String className = "C";
      private int objNumber = 0;

      C(int val){//constructor

        cVar = val;
        //Instantiate separate objects of the
        // enclosing classes B and C.  Will
        // display info about them later.  The
        // object of the Class B is internally
        // associated with the original object of
        // the Class A.

        //Increment the object counter in the
        // top-level class.
        A.objCntC++;
        objNumber = A.objCntC;
        System.out.println(
                      "In xstr for C, objCntC = "
                                    + A.objCntC);

        //Instantiate object of private member
        // class named D.
        new D();

        //Instantiate objects of enclosing
        // classes named A and B.

        refToA = new A(10);
        refToB = new B(20);

      }//end constructor

      public void cShow(){
        System.out.println("-1-");//separator
        //Display private member variables
        // belonging to this object.
        System.out.println(
           "In cShow, objNumber = " + objNumber);
        System.out.println(
                     "In cShow, cVar = " + cVar);
        System.out.println("-2-");//separator
        //Invoke the private method named bShow
        // in the internally associated object of
        // the class named B.  This method will,
        // in turn invoke the private method
        // named aShow in the object of the class
        // named A to which the object of the
        // class named B is internally
        // associated.
        bShow();
        System.out.println("-3-");//separator

        //Invoke the private method named aShow
        // in the internally associated object
        // of the class named A.
        aShow();
        System.out.println("-4-");//separator

        //Illustrate the syntax required to gain
        // access to the objects instantiated
        // from the classes named C, B, and A.
        // The first five statements produce the
        // same result.  The class names that are
        // displayed match the names of the class
        // files produced by the compilation
        // process.
        System.out.println(getClass().getName());
        System.out.println(
                      this.getClass().getName());
        System.out.println(
                    C.this.getClass().getName());
        System.out.println(
                  B.C.this.getClass().getName());
        System.out.println(
                A.B.C.this.getClass().getName());
        System.out.println("-5-");//separator

        //The following two statements produce
        // the same output
        System.out.println(
                    B.this.getClass().getName());
        System.out.println(
                  A.B.this.getClass().getName());
        System.out.println("-6-");//separator


        System.out.println(
                    A.this.getClass().getName());
        System.out.println("-7-");

        //Display private instance variables and
        // class names belonging to separate
        // objects instantiated from the
        // enclosing classes named A and B.
        System.out.println(
              "In cShow, bVar = " + refToB.bVar);
        System.out.println(
                    refToB.getClass().getName());

        System.out.println(
              "In cShow, aVar = " + refToA.aVar);
        System.out.println(
                    refToA.getClass().getName());
        System.out.println("-8-");

        //Invoke the private bShow method on the
        // separate object instantiated from the
        // class named B in order to show the
        // object to which that object is
        // internally associated.  Even though
        // this object was instantiated from
        // within the constructor for the class
        // named C, it is internally associated
        // with the object of the class A that
        // was originally used to instantiate the
        // object of the class named C.
        refToB.bShow();
        System.out.println("-9-");

        //Invoke the private aShow method on the
        // separate object instantiated from the
        // class named A.
        refToA.aShow();
        System.out.println("-10-");

        //Illustrate the inheritance hierarchy to
        // which the class named C belongs by
        // getting and displaying the variable
        // named className from both the C class
        // and the X class.  Note that the
        // variable is protected in the X class.
        // Also invoke the overridden toString
        // methods belonging to the object
        // instantiated from the class named C.
        // One version is overridden in the class
        // named X and the other version is
        // overridden in the class named C.  Note
        // that the inheritance hierarchy is
        // totally independent of the containment
        // hierarchy.
        System.out.println(
                     "className = " + className);
        System.out.println(toString());
        //Note: cannot access private variable in
        // superclass named X. Must be protected,
        // package, or public.
        System.out.println(
               "className = " + super.className);
        System.out.println(super.toString());
      }//end cShow
      //---------------------------------------//

      //Override the toString method
      public String toString(){
        return "toString in Class C";
      }//end overridden toString
      //=======================================//

      private class D{//member class of C
        D(){//constructor
          System.out.println(
            "Construct obj of private class D.");
          System.out.println(
                    "  Private class file name: "
                    + this.getClass().getName());
        }//end constructor
      //Note that all four class definitions end
      // here in the proper nested order.
      }//end class D
    }//end class C
  }//end class B
}//end class A

-end-

Content actions

Download module as:

PDF | EPUB (?)

What is an EPUB file?

EPUB is an electronic book format that can be read on a variety of mobile devices.

Downloading to a reading device

For detailed instructions on how to download this content's EPUB to your specific device, click the "(?)" link.

| More downloads ...

Add module to:

My Favorites (?)

'My Favorites' is a special kind of lens which you can use to bookmark modules and collections. 'My Favorites' can only be seen by you, and collections saved in 'My Favorites' can remember the last module you were on. You need an account to use 'My Favorites'.

| A lens I own (?)

Definition of a lens

Lenses

A lens is a custom view of the content in the repository. You can think of it as a fancy kind of list that will let you see content through the eyes of organizations and people you trust.

What is in a lens?

Lens makers point to materials (modules and collections), creating a guide that includes their own comments and descriptive tags about the content.

Who can create a lens?

Any individual member, a community, or a respected organization.

What are tags? tag icon

Tags are descriptors added by lens makers to help label content, attaching a vocabulary that is meaningful in the context of the lens.

| External bookmarks