Bruce Eckel’s Thinking in Java | Contents | Prev | Next |
class
loading
many more traditional languages, programs are loaded all at once as part of the
startup process. This is followed by initialization, and then the program
begins. The process of initialization in these languages must be carefully
controlled so that the order of initialization of
statics
doesn’t cause trouble. C++, for example, has problems if one
static
expects another
static
to
be valid before the second one has been initialized.
doesn’t have this problem because it takes a different approach to
loading. Because everything in Java is an object, many activities become
easier, and this is one of them. As you will learn in the next chapter, the
code for each object exists in a separate file. That file isn’t loaded
until the code is needed. In general, you can say that until an object of that
class is constructed, the class code doesn’t get loaded. Since there can
be some subtleties with
static
methods, you can also say, “Class code is loaded at the point of first
use.”
point of first use is also where the
static
initialization takes place. All the
static
objects and the
static
code block will be initialized in textual order
(that is, the order that you write them down in the class definition) at the
point of loading. The
statics,
of course, are initialized only once.
Initialization
with inheritance
helpful to look at the whole initialization
process, including inheritance, to get a full picture of what happens. Consider
the following code:
//: Beetle.java // The full process of initialization. class Insect { int i = 9; int j; Insect() { prt("i = " + i + ", j = " + j); j = 39; } static int x1 = prt("static Insect.x1 initialized"); static int prt(String s) { System.out.println(s); return 47; } } public class Beetle extends Insect { int k = prt("Beetle.k initialized"); Beetle() { prt("k = " + k); prt("j = " + j); } static int x2 = prt("static Beetle.x2 initialized"); static int prt(String s) { System.out.println(s); return 63; } public static void main(String[] args) { prt("Beetle constructor"); Beetle b = new Beetle(); } } ///:~
output for this program is:
static Insect.x initialized static Beetle.x initialized Beetle constructor i = 9, j = 0 Beetle.k initialized k = 63 j = 39
first thing that happens when you run Java on
Beetle
is that the loader goes out and finds that class.
In the process of loading it, the loader notices that it has a base class
(that’s what the
extends
keyword
says), which it then loads. This will happen whether or not you’re going
to make an object of that base class. (Try commenting out the object creation
to prove it to yourself.)
the base class has a base class, that second base class would then be loaded,
and so on. Next, the static
initialization in the root base class (in this case,
Insect)
is performed, and then the next derived class, and so on. This is important
because the derived-class static initialization might depend on the base class
member being initialized properly.
this point, the necessary classes have all been loaded so the object can be
created. First, all the primitives in this object are set to their default
values and the object handles are set to
null.
Then
the base-class constructor will be called. In this case the call is automatic,
but you can also specify the constructor call (as the first operation in the
Beetle( )
constructor) using
super.
The base class construction goes through the same process in the same order as
the derived-class constructor. After the base-class constructor completes, the
instance variables are initialized in textual order. Finally, the rest of the
body of the constructor is executed.