Ludzie pragną czasami się rozstawać, żeby móc tęsknić, czekać i cieszyć się z powrotem.
out.println(s);
return 63;
}
public static void main(String[] args) {
prt("Beetle constructor");
Beetle b = new Beetle();
}
} ///:~
The output for this program is:
static Insect.x initialized
Chapter 6: Reusing Code & Classes
207
static Beetle.x initialized
Beetle constructor
i = 9, j = 0
Beetle.k initialized
k = 63
j = 39
The 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.)
If 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.
At 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.
Summary
Both inheritance and composition allow you to create a new type from existing types.
Typically, however, you use composition to reuse existing types as part of the underlying implementation of the new type and inheritance when you want to reuse the interface. Since the derived class has the base-class interface, it can be upcast to the base, which is critical for polymorphism, as you’ll see in the next chapter.
Despite the strong emphasis on inheritance in object-oriented programming, when you start a design you should generally prefer composition during the first cut and use inheritance only when it is clearly necessary. (As you’ll see in the next chapter.) Composition tends to be more flexible. In addition, by using the added artifice of inheritance with your member type, you can change the exact type, and thus the behavior, of those member objects at run-time.
Therefore, you can change the behavior of the composed object at run-time.
Although code reuse through composition and inheritance is helpful for rapid project development, you’ll generally want to redesign your class hierarchy before allowing other programmers to become dependent on it. Your goal is a hierarchy in which each class has a specific use and is neither too big (encompassing so much functionality that it’s unwieldy to reuse) nor annoyingly small (you can’t use it by itself or without adding functionality). Your finished classes should be easily reused.
208
Thinking in Java
www.BruceEckel.com
Exercises
1. Create two classes, A and B, with default constructors (empty argument lists) that announce themselves. Inherit a new class called C from A, and create a member B inside C.
Do not create a constructor for C. Create an object of class C and observe the results.
2. Modify Exercise 1 so that A and B have constructors with arguments instead of default constructors. Write a constructor for C and perform all initialization within C’s constructor.
3. Take the file Cartoon.java and comment out the constructor for the Cartoon class.
Explain what happens.
4. Take the file Chess.java and comment out the constructor for the Chess class. Explain what happens.
Chapter 6: Reusing Code & Classes
209
3
7: Polymorphism
Polymorphism is the third essential feature of an object-oriented
programming language, after data abstraction and inheritance.
It provides another dimension of separation of interface from implementation, to decouple what from how. Polymorphism allows improved code organization and readability as well as the creation of extensible programs that can be “grown” not only during the original creation of the project but also when new features are desired.
Encapsulation creates new data types by combining characteristics and behaviors.