Chapter Seven: Reusing Classes,
Reusing Classes
There are two common methods to achieve class reuse, component (create an object with a class in the new class) and inheritance.
Composition syntax
Every non-primitive object has a toString () method, and it's called in special situations when the compiler wants a String but it has an object.
Inheritance syntax
You're always doing inheritance when you create a class, because unless you explicitly inherit from some other class, you implicitly inherit from Java's standard root class Object.
To allow for inheritance, as a general rule make all fields private and all methods public. Of course, in particle cases you must adjustments, but this is a useful guideline.
| _ Initializing the base class
Even if you don't create a constructor for derived-class, the compiler will synthesize a default constructor for you that callthe base constructor.
If your class doesn' t have default arguments, or if you want to call a base-class constructor that has an argument, you must explicitly write the callto the base-class constructor using the super keyword and the appropriate argument list. in addition, the call to the base-class constructor must be the first thing you do in the derived-class constructor.
Delegation
Delegation is the third method of reuse, between inheritance and components. Place a member object (like a component) in the new class and expose all methods (like inheritance) of the object ). You have more control with delegation because you can choose to provide only a subset of the methods in the member object.
Combining composition and inherit
| _ Guaranteeing proper cleanup
Note that in your cleanup method, you must also pay attention to the calling order for the base-class and member-object cleanup methods in case one subobject depends on another. in general, you shoshould follow the same form that is imposed by a C ++ compiler on its destructors: First perform all of the cleanup work specific to your class, in the reverse order of creation.
There can be used cases in which the cleanup issue is not a problem; you just let the garbage collector do the work. but when you must do it explicitly, diligence and attention are required, because there's not much you can rely on when it comes to garbage collection. the garbage collector might never be called. if it is, it can reclaim objects in any order it wants. you can't rely on garbage collection for anything but memory reclamation. if you want cleanup to take place, make your own cleanup methods and don't use on finalize ().
Choosing composition vs. inheritance
Both composition and inheritance allow you to place subobject inside your new class (composition explicitly does this-with inheritance it's implicit ).
The is-a relationship is expressed with inheritance, and the has-a relationship is expressed with composition.
Upcasting
In object-oriented programming, the most likely way that you'll create and use code is by simply packaging data and methods together into a class, and using objects of that class. you'll also use existing classes to build new classes with composition. less frequently, you'll use inheritance. so although inheritance gets a lot of emphasis while learning OOP, it doesn' t mean that you should use it ev Erywhere you possibly can. on the contrary, you should use it sparingly, only when it's clear that inheritance is useful. one of the clearest ways to determine whether you shoshould use composition or inheritance is to ask whether you'll ever need to upcast from your new class to the base class. if you must upcast, then inheritance is necessary. the Polymorphism provides one of the most compelling re Asons for upcasting, but if you remember to ask "Do I need to upcast ?" You'll hava a good tool for deciding between composition and inheritance.
The final keyword
There are there places where final can be used: for data, methods and classes.
| _ Final data
That a piece of data is constant is useful for two reasons:
1. It can be a compile-time constant that won't ever change.
2. It can be a value initialized at run time that you don't want changed.
In the case of a compile-time constant, the compiler is allowed to "fold" the constant value into any calculations in which it's used; that is, the calculation can be completed MED at compile time, eliminating some run-time overhead. in Java, these sorts of constants must be primitives and are expressed with the final keyword. A value must be given at the time of definition of such a constant.
When final is used for object reference, the object reference cannot be changed, that is, the reference cannot point to other objects, but the object itself can be modified. This rule applies to array references of objects.
Final variables without initial values must be initialized before use. For example, static final cannot be initialized in the constructor, because static does not depend on objects.) otherwise, an error is reported during compilation. You're forced to perform assignments to finals either with an expression at the point of definition of the field or in every constructor. that way it's guaranteed that the final field is always initialized before use.
| _ Final arguments
When the parameter is modified by final, it is readable and cannot be written. This feature is primarily used to pass data to anonymous inner classes.
| _ Final methods
Methods Modified with final cannot be overwritten.
| _ Final and private
Any private methods in a class are implicitly final. because you can't access a private method, you can't override it. you can add the final specifier to a private method, but it doesn't give that method any extra meaning.
"Overriding" can only occur if something is part of the base-class interface. that is, you must be able to upcast an object to its base type and call the same method. if a method is private, it isn' t part of the base-class interface. it is just some code that's s den away inside the class, and it just happens to hava the name, but if you create a public, protected, or package-access method with the same name in the derived class, there's no connection to the method that might happen to have that name in the base class. you haven't overridden the method; you 've got just created a new method. since a private method is unreachable and specified tively invisible, it doesn' t factor into anything organization t for the code organization of the class for which it was defined.
| _ Final classes
When you say that an entire class is final (by preceding its definition with the final keyword ), you state that you don't want to inherit from this class or allow anyone else to do so. in other words, for some reason the design of your class is such that there is never a need to make any changes, or for safety or security reasons you don't want subclassing.
Note that the field of a final class can be final or not, as you choose. the same rules apply to final for fields regardless of whether the class is defined as final. however, because it prevents inheritance, all methods in a final class are implicitly final, since there's no way to override them. you can add the final specifier to a method in a final class, but it doesn' t add any meaning.
Initialization and class loading
In Java, the compiled code for each class exists in its own separate file. that file isn't loaded until the code is needed. in general, you can say that "class code is loaded at the point of first use. "This is usually when the first object of that class is constructed, but loading also occurs when a static field or static method is accessed (The constructor is also a static methods even though the static keyword is not explicit. so to be precise, a class is first loaded when any one of its static members is accessed .).
| _ Initialization with inheritance
When the main method of the main class is run for the first time, the loader finds the class file corresponding to the class. If it finds that it has a parent class during the loading process, it continues to load its parent class, the parent class also has a parent class. Continue loading. Next, start static initialization in the root class, and then its derived class. This is important because the static initialization of the derived class may depend on the proper initialization of the parent class members.
At this point, the necessary classes hava 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 references are set to null-this happens in one fell swoop by setting the memory in the object to binary zero. then the base-class constructor will be called. in this case the call is automatic, but you can also specify the base-class constructor call by 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. Composition reuses existing types as part of the underlying implementation of the new type, and inheritance reuses the interface.
(END_XPJIANG)