Java Programming Notes # 1634
- Preface
- Preview
- Discussion and Sample Code
- Run the Program
- Summary
- What's Next
- Complete Program
Listing
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, Static Initializer Blocks.
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
Proper initialization is important
As I mentioned in the previous lesson in this series, proper initialization
of variables is an important aspect of programming. Unlike other
programming languages, it is not possible to write a Java program in which
the variables are accidentally initialized with the garbage left over
in memory from the programs that previously ran in the computer.
Automatic initialization to default values
Instance variables and class (static) variables are automatically
initialized to standard default values if you fail to purposely initialize
them. Although local variables are not automatically initialized,
you cannot compile a program that fails to either initialize a local
variable or assign a value to that local variable before it is used.
Thus, Java programmers are prevented from committing the cardinal sin of
allowing their variables to be initialized with random garbage through programming
negligence.
Initialization during declaration
You should already know that you can initialize instance variables and
class variables when you declare them, by including an initialization
expression in the variable declaration statement. Figure 1 shows
an example of a primitive instance variable named simpleInitTime
that is purposely initialized to a long value obtained by invoking
a static method of the Init02 class named relTime. (You
will learn more about this method later.)
long simpleInitTime = Init02.relTime(); Figure 1 |
While the expression in Figure 1 is a little complex, it is still just an expression and is perfectly suitable for use in initializing an instance variable when it is declared.
Constructor
What if your initialization requirements are more complex than can be
satisfied with a single initialization expression? You should already
know that you can write one or more overloaded constructors to purposely
initialize all instance variables when an object is instantiated from the
class. The code in a constructor can be as complex as you need it
to be.
Not all classes allow constructors
What you may not know, however, is that you cannot always write a constructor
for a class when you define it. For example, anonymous classes,
which we will study in a subsequent lesson, do not allow the definition
of a constructor.
However, even anonymous classes allow you to write instance initializer
blocks when you define the class. The code in an instance initializer
block, which can also be quite complex, is executed when an object is
instantiated from the class.
Not as powerful as a constructor
You can write any number of instance initializer blocks in your class definition.
However, unlike constructors, instance initializer blocks do not receive
parameters. Therefore, they are less powerful than constructors.
In terms of power, instance initializer blocks fall between simple initialization
expressions (such as that shown in Figure 1) and constructors.
Because they can execute complex code, they are more powerful than simple
initialization expressions. Because they cannot receive parameters,
they are less powerful than constructors.
Similar to noarg constructors
An instance initializer block is similar to a constructor that doesn't
receive any parameters, except that you can write any number of instance
initializer blocks into your class definition, and you can only write
one noarg constructor in your class definition.
The order of execution
The code in an instance initializer block is executed after the constructor
for the superclass is executed, and before the constructor for the class
to which the initializer belongs is executed.
If the class definition contains a combination of instance initializer
blocks in combination with the declaration of instance variables with
initialization expressions, the code that comprises those items is executed
in the order in which it appears in the class definition.
The order of execution of instance initializers in combination with instance
variable initializations, constructors, and static initializer blocks
will be illustrated in the sample program that I will discuss later in
this lesson.
What does Flanagan have to say?
Here is how one of my favorite authors, David Flanagan of Java in a
Nutshell fame, summarizes the situation:
"An instance initializer is simply a block of code inside curly braces that is embedded in a class definition, where a field or method definition normally appears. A class (any class -- not just anonymous classes) can have any number of instance initializers. The instance initializers and any variable initializers that appear in field definitions for the class are executed in the order they appear in the Java source code. These initializers are automatically run after the superclass constructor has returned but before the constructor, if any, of the current class runs."
Why do we need instance initializers?
Flanagan goes on to explain the value of instance initializers, not
only for anonymous classes, but also for non-anonymous classes.
According to Flanagan,
"Instance initializers allow you to initialize an object's fields near the definition of those fields, rather than deferring that initialization to a constructor defined further away in the class. Used in this way, they can sometimes improve code readability."
The sample program that I will discuss in the next section will illustrate many aspects of instance initializers in combination with static initializer blocks, constructors, and simple instance variable initializations.
Discussion and Sample Code
I will discuss and explain a Java program named Init02 in this lesson.
(A complete listing of the program is provided in Listing 15 near the
end of the lesson.) As mentioned above, this program illustrates
many aspects of instance initializers in combination with static initializer
blocks, constructors, and simple instance variable initializations.
Description of the program
The class hierarchy
This program defines a class named B that extends a class named A. Parameterized constructors are used in both A and B to instantiate an object of the class named B.
The base time is recorded
The controlling class defines and initializes a static variable containing the time that the program starts running in milliseconds relative to 00:00:00 GMT, Jan 1, 1970. This value is used as the base for computing time intervals later as the execution of the program progresses.
The times that are computed and displayed later are in milliseconds relative to the time at which the program started running.
The class loading process
Static initializers are defined in both A and B to display the time that the two classes are loaded and the order in which they are loaded. You will see that both classes are loaded when an attempt is made to instantiate an object of the subclass B. You will also see that the superclass is loaded before the subclass is loaded, and both are loaded before the object is instantiated.
An initialized instance variable
An instance variable is defined in the class named B and is initialized (using a simple initialization expression) with the time in milliseconds that the variable is initialized. In addition, two separate instance initializers are defined in the class named B that perform initialization after the constructor for A completes execution and before the constructor for B begins execution.
In terms of physical location, the instance variable mentioned above follows the first of the two instance initializers and appears before the second instance initializer in the class definition.
The order of execution
The first of the two instance initializers executes before the instance variable is initialized. The second of the two initializers executes after the instance variable is initialized, demonstrating that initialization based on simple initialization expressions and instance initializers occurs in the order that the code appears in the class definition.
Initialization time is displayed
The two constructors (for classes A and B) and the two initializers each display time information when they are executed to show the order in which the constructors and the initializers are executed.
Two separate objects
Two separate instances (objects) of the class named B are created, showing not only the order in which the instance initializers and the constructors are executed, but also showing that the static initializers are executed one time only when the classes are first loaded.
Display values of instance variables
Each time an object of the class named B is instantiated, an instance method of the class is invoked to display the values of the instance variables initialized during the process of instantiating the object.
One-hundred millisecond delays
Several one-hundred millisecond time delays are purposely inserted at strategic points within the program to force the time intervals between the different steps in the program to be measurable. Otherwise, the time intervals between steps would be so small that it would not be possible to distinguish between them on the basis of time recorded in milliseconds.
Will discuss in fragments
I will discuss the program code in fragments. In discussing the fragments, I will present much of the code in the order that it is executed, which is not necessarily the same order that the code appears in the program.
As mentioned earlier, a complete listing of the program can be viewed
in Listing 15 near the end of the lesson.
Two utility methods
I will begin by presenting two static utility methods that are used
to simplify the code in the body of the program. Both of these methods
are defined in the controlling class named Init02.
Relative time in milliseconds
The utility method named relTime, shown in Listing 1, is used to
compute and return the current time in milliseconds relative to a time
value stored in a static variable of the controlling class named
baseTime. As you will see later, baseTime contains
the time that the program started running. Thus, each time this method
is called, it returns the current time relative to the time that the program
started running.
static long relTime(){ |
I relegated this code to a utility method simply due to the length and complexity of the expression, and the large number of times that the relative time is needed throughout the program.
Insert a delay
The utility method shown in Listing 2 causes the current thread to sleep for one-hundred milliseconds. Thus, each time this method is called, it inserts a one-hundred millisecond delay in the execution of the program.
static void delay(){ |
This method is also called numerous times throughout the program. Once again, therefore, I relegated this code to a utility method to simplify the code in the body of the program.
Establish the start time
Listing 3 shows the beginning of the controlling class named Init02, including the declaration and initialization of the class variable named baseTime.
public class Init02{ |
According to the Sun documentation, the getTime method of the Date class in Listing 3
"Returns the number of milliseconds since January 1, 1970, 00:00:00 GMT represented by this Date object."Thus, this variable will contains the time in milliseconds that the class named Init02 is loaded, which is also the time that the program starts running. This time will serve as the base time against which various time intervals will be computed during the running of the program.
The main method
The code in Listing 4 shows the beginning of the main method, which displays the current time relative to the start time, and instantiates a new object of the class named B. In addition, the code in Listing 4 invokes the method named showData on the new object. The showData method displays the values stored in several instance values as various initialization steps are completed during the instantiation of the object.
public static void main(String[] args){ |
The output
As you have probably already guessed, the print statement in Listing 4 produces the output shown in Figure 2. In other words, there was less than one millisecond of elapsed time between the initialization of the static variable named baseTime and the invocation of the relTime method in Listing 4.
Instantiate first obj: 0 Figure 2 |
Load classes A and B
The instantiation of the new object in Listing 4 triggers a whole chain of events. I will discuss those events in the sequence in which they occur in the paragraphs that follow. The first pair of interesting events is the loading of the classes named A and B.
What does it mean to say that a class is loaded?
As I explained in a previous lesson, I can't provide a description of exactly what happens from a technical viewpoint. However, I can tell you what seems to happen from a functional viewpoint.
Functionally, an object of the class whose name is Class is created and saved in memory. This object represents the class that is being loaded (in this case, two classes named A and B are loaded, so two separate Class objects are created). From that point forward, all static members of the class are available to the program by referring to the name of the class and the name of the member. Other information about the class is also available by invoking methods, such as the method named getSuperclass, on a reference to the Class object.
As you will see, when an attempt is made to instantiate an object of the subclass named B, that class and its superclass named A are both loaded. Furthermore, the superclass named A is loaded first. The loading of both classes takes place before the other steps required to instantiate the object take place.
A static initializer in the class named A
Listing 5 shows the beginning of the class named A. The code in Listing 5 declares an instance variable named aXstrTime, which will be used later to record the time that the constructor for the class named A is executed. More important for this part of the discussion, however, is the static initializer shown in Listing 5.
class A{ |
You will recall from the previous lesson on static initializers that they execute one time only when the class is loaded. The static initializer in Listing 5 prints a message showing the time that the class named A is loaded.
Class A load time
On my machine the code in Listing 5 produced the output shown in Figure 3. Presumably, the ten-millisecond delay between the start of the program and the point in time that the class named A was loaded was due primarily to the time required for the program to find the class file on the disk and to load it into memory. Your system may produce a different result depending on the speed of your computer.
Class A loaded: 10 Figure 3 |
A static initializer in the class named B
Listing 6 shows the beginning of the class named B. The code in Listing 6 declares three instance variables, which will be used later to record the time that the constructor and the instance initializers are executed. More important for this part of the discussion, however, is the static initializer shown in Listing 6.
class B extends A{ |
The output
The static initializer in Listing 6 purposely inserts a one-hundred millisecond delay and then prints the time that it finishes executing. This is the time that the class named B finishes loading.
Class B loaded: 110 Figure 4 |
Figure 4 shows that the class named B was loaded immediately following the loading of the class named A.
Create the new object of the class named B
After the classes are loaded, the system proceeds to create the new instance of the class named B. You should recall however that objects are actually created beginning with the contribution from the class named Object, and proceeding down the inheritance hierarchy to the class from which the object is actually being instantiated. Thus, the next identifiable significant event is the execution of the constructor for the class named A, which is the superclass of the class named B. (Nothing in the program makes it possible for us to identify the construction of that portion of the object attributable to the superclass named Object.)
The constructor for the class named A
Listing 7 shows the constructor for the class named A. The code in the constructor purposely inserts a one-hundred millisecond delay, and then gets and saves the time that the constructor is executed. (The time is saved in the instance variable named aXstrTime, which was declared in Listing 5 earlier.)
A(String str){//constructor |
Then the code in the constructor prints that time, producing the output shown in Figure 5.
Construct 1A: 210 Figure 5 |
The important point here is that the constructor for the superclass is executed after the classes named A and B are loaded, but before the instance initializers for the subclass named B are executed.
Instance initializers in the subclass named B
If you examine Listing 15 near the end of the lesson you will see that the class named B contains two separate instance initializers, which are physically separated by an ordinary instance variable declaration (with initialization) and a constructor. As you will see in the discussion that follows, the execution of the first instance initializer follows the execution of the constructor for the superclass named A shown in Listing 7.
Then the initialization of the ordinary instance variable takes place, following the execution of the first instance initializer. This is followed by the execution of the second instance initializer.
Despite their physical placement in the code, the execution of both instance initializers and the initialization of the ordinary instance variable all take place before the constructor for the class named B is executed.
The first instance initializer
The first instance initializer is shown in Listing 8. Once again, as described by David Flanagan,
"An instance initializer is simply a block of code inside curly braces that is embedded in a class definition, where a field or method definition normally appears. ... The instance initializers and any variable initializers that appear in field definitions for the class are executed in the order they appear in the Java source code. These initializers are automatically run after the superclass constructor has returned but before the constructor, if any, of the current class runs."
{//This is an instance initializer |
Insert a delay
The code in the instance initializer in Listing 8 begins by inserting a one-hundred millisecond delay to force the time interval between the execution of the constructor for the class named A and the execution of the instance initializer to be distinguishable.
Get, save, and display the relative time
After sleeping for one-hundred milliseconds, the code in the initializer gets and saves the current time relative to the start of the program.
Then the code in the initializer displays that time, producing the output shown in Figure 6.
Initializer-1: 310 Figure 6 |
If you compare Figure 6 with Figure 5 showing the time that the constructor for the class named A was executed, you will see that the printout produced by the initializer followed the printout produced by the constructor by the one-hundred millisecond delay introduced at the beginning of the initializer. This confirms that the first initializer in the class named B was executed following the execution of the constructor for the superclass named A.
Insert another time delay
Finally the instance initializer shown in Listing 8 inserts an additional one-hundred millisecond delay. This makes it possible to distinguish the time that the ordinary instance variable (to be discussed next) was initialized from the time that the print statement in the first instance initializer was executed.
An ordinary instance variable
Listing 9 shows the declaration and initialization of an ordinary instance variable named simpleInitTime. Recall that the physical location of this variable declaration is after the first instance initializer and before the constructor and the second instance initializer.
long simpleInitTime = Init02.relTime(); |
Although the code in Listing 9 doesn't display the time of initialization, code later in the program causes the value stored in the variable named simpleInitTime to be displayed, producing the output shown in Figure 7.
class B simple init: 410 Figure 7 |
As you can see from the relative time shown in Figure 7, the instance variable was initialized immediately following completion of execution of the first instance initializer shown in Listing 8 (compare Figure 7 with Figure 6).
The second instance initializer
An examination of Listing 15 near the end of the lesson shows that the class named B contains a second instance initializer, separated from the first instance initializer by an ordinary instance variable declaration and a constructor. The second instance initializer is shown in Listing 10.
{//This is another instance initializer |
Insert a delay
This initializer begins by inserting a one-hundred millisecond delay. Then it gets, saves, and displays the time relative to the start time for the program as shown in Figure 8.
Initializer-2: 510 Figure 8 |
A comparison of the relative time shown in Figure 8 with Figure 7 confirms that the second instance initializer was executed after the initialization of the ordinary instance variable shown in Listing 9. This confirms that the instance initializers and variable initializers are executed in the order they appear in the Java source code.
The constructor for the class named B
The constructor for the class named B physically separates the two instance initializers in the class definition. The code for the constructor begins in Listing 11.
B(String str){ |
The constructor begins by using the super keyword to invoke a parameterized constructor on the superclass named A.
(If you are unfamiliar with this use of the super keyword, see Lesson 1628 entitled The Essence of OOP using Java, The this and super Keywords at www.DickBaldwin.com.)Insert a time delay
The remaining code in the constructor, as shown in Listing 12, inserts a one-hundred millisecond time delay.
Init02.delay(); |
Get and display the time
Then the constructor gets, saves, and displays the time, producing the output shown in Figure 9. A comparison of the relative time shown in Figure 9 with the previous figures confirms that the initializers are run after the superclass constructor has returned but before the constructor of the current class runs.
Construct 1B: 611 Figure 9 |
The showData method
The class named B contains a method named showData. This method is shown in its entirety in Listing 13. The purpose of this method is to summarize the order of initialization by displaying the values stored in the various instance variables as the object was being instantiated.
void showData(){ |
The output of the showData method
The values stored in the instance variables are displayed in the order that the initialization steps took place during the instantiation of the object. Recall that the showData method was invoked on the object when it was instantiated in the main method in Listing 4, producing the output shown in Figure 10.
Initialization values: Figure 10 |
An examination of Figure 10 makes it clear that the superclass constructor was executed first, at a relative time of 210 milliseconds. This was followed by execution of the first instance initializer at 310 milliseconds, initialization of the ordinary instance variable at 410 milliseconds, and execution of the second instance initializer at 510 milliseconds. Finally the constructor for the class named B was executed at a relative time of 611 milliseconds.
Instantiate another object
Listing 14 shows the remaining code in the main method, which was not previously discussed.
delay(); |
The remaining code in the main method inserts another one-hundred millisecond delay, and then instantiates another object of the class named B. As before, this triggers a whole series of events, many of which produce output on the screen.
The screen output
The output produced by instantiating another object is shown in its entirety in Figure 11.
Instantiate second obj: 711 Figure 11 |
Except for the differences in the relative time values, the output shown in Figure 11 matches that shown in Figure 2 and Figures 5 through 10. Note, however, that there is nothing in Figure 11 corresponding to the output previously shown in Figures 3 and 4.
Classes are not reloaded
Figures 3 and 4 show the output produced by the execution of the static initializers in the classes named A and B. The process of instantiating another object from a set of previously loaded classes does not cause those classes to be reloaded. Since static initializers are executed one time only when the class is first loaded, those initializers are not executed when the second object is instantiated from the class named B.
Constructors and instance initializers are executed for each object
However, constructors and instance initializers are executed, and ordinary instance variables are initialized each time a new object is instantiated. Therefore, the output shown in Figure 11 contains messages and time tags corresponding to the execution of both constructors, the execution of both instance initializers, and the initialization of an ordinary instance variable, all in the proper order.
Run the Program
At this point, you may find it useful to compile and run the program
shown in Listing 15 near the end of the lesson.
Summary
Static initializer blocks
A static initializer is a block of code surrounded by curly braces that
is embedded in a class definition and is qualified by the keyword static.
You can include any number of static initializer blocks within your class
definition. They can be separated by other code such as method
definitions and constructors. The static initializer blocks will
be executed in the order that they appear in the code, regardless of
the other code that may separate them.
The static initializers belonging to a class are executed one time
only when the class is loaded.
An instance initializer is a block of code surrounded by curly braces that is embedded in a class definition. (It is not qualified by the keyword static.) You can include any number of instance initializers in your class definition, and the initializers may be physically separated by other items, such as constructors, method definitions, variable declarations, etc.
Instance initializers and variable initializers, along with constructors, are executed each time a new object of the class is instantiated. The instance initializers and variable initializers are executed in the order that they appear in the Java source code. They are executed after the constructor for the superclass is executed, and before the constructor for the current class is executed.
Instance initializers are especially useful in anonymous classes (to be explained in a future lesson). However, they can be included in any class definition, and may make code more readable by initializing instance variables near the declaration of those variables rather than deferring that initialization to a constructor that is located further away in the class definition.
Instance initializers are very similar to noarg constructors, except that a class can define only one noarg constructor, but can define any number of instance initializers. A class definition can contain instance initializers in addition to a noarg constructor, in which case, the instance initializers will be executed before the noarg constructor is executed.
What's Next?
The next lesson in this series will explain and discuss inner classes,
with special emphasis on member classes. Subsequent lessons
will explain local classes, anonymous classes, and top-level
nested classes.
Complete Program Listing
A complete listing of the program discussed in this lesson is show in Listing 15 below.
/*File Init02.java |
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.
-end-