Chapter 6 Regeneration "Java leadership one feature is the reuse or regeneration of the code. But the most revolutionary meaning is that more other things we can do except for the replication and modification of the code." In the programming language like C, the reuse of the code is early, but the effect is not particularly remarkable. Like elsewhere of Java, this program is solved with problems related to classes. We repeat the code by creating a new class, but you don't have to recreate it, you can directly use others already built and debugged ready-to-class. But doing this must ensure that the original code will not interfere. In this chapter, we will introduce two ways to achieve this goal. The first simplest: simply creates the original class object in the new class. We call this method as "synthesis" because the new class is merged by an existing class. We only simply repeat the functionality of the code, not the form of it. The second approach looks slightly some tips. It creates a new class that as a "type" of the existing class. We can take the existing form of existing classes and join the new code and will not affect the existing classes. This magic behavior is called "inheritance", most of which work involved is done by the compiler. For object-oriented programming, "inheritance" is one of the most important foundations. It produces some additional impact on the content you want to tell in our next chapter. For both methods of synthesis and inheritance, most syntax and behaviors are similar (because they must generate new types according to existing types). In this chapter, we will learn about these code regeneration or reuse mechanisms. 6.1 Synthetic grammar is seen in the previous learning situation, in fact, many "synthetic" operations have been conducted. For synthesis, we only need to simply put the object handle in the new class. For example, it is assumed that there is a need to accommodate several String objects in an object, two basic data types, and an object belonging to another class. For non-basic types of objects, just place the handle in a new class; and for basic data types, you need to define them in your own class. As shown below (if you have trouble, please refer to Chapter 3 3.1.2 "Assignment":
//: Sprinklersystem.java
// composition for code reuse
Package C06;
Class Watersource {
PRIVATE STRING S;
Watersource () {
System.out.println ("Watersource ()");
s = new string ("constructed");
}
PUBLIC STRING TOSTRING () {Return S;
}
Public class sprinklersystem {
Private string Valve1, Valve2, Valve3, Valve4;
Watersource Source;
INT I;
Float f;
Void print () {
System.out.println ("Valve1 =" Valve1);
System.out.println ("Valve2 =" VALVE2);
System.out.println ("Valve3 =" Valve3);
System.out.println ("Valve4 =" Valve4);
System.out.println ("i =" i);
System.out.println ("f =" f);
System.out.println ("Source =" Source);
Public static void main (String [] args) {
Sprinklersystem x = new SprinklerSystem ();
X.Print ();
}
} ///: ~
One way to define within Watersource is quite specialty: toString (). Soon, you will know that every non-basic type of object has a toString () method. If the compiler has hoped a String, it will call this method. So in the following expression:
System.out.println ("Source =" Source);
The compiler will find that we tried to add a String object to a Watersource ("Source ="). This is unacceptable to it because we can only add a string "to another", so it will say: "I want to call toString (), convert the Source into a string!" After processing, it can compile two strings and pass the result string to a System.Out.println (). Every time you have a class that you created by yourself, you only need to write a toString () method.
If it is not investigated, it may be considered to automatically construct the object (due to Java security and cautious image). For example, it is possible that it will call the default builder for Watersource to initialize Source. The output of the printed statement is actually:
Valve1 = NULL
Valve2 = NULL
Valve3 = NULL
Valve4 = NULL
i = 0
f = 0.0
Source = NULL
Basic data used as a field in the class will initialize zero, as indicated in Chapter 2. However, the object handle will initialize into NULL. Moreover, if you try to make any of the call methods, a "violation" will be generated. This result is actually quite good (and useful), we can print them out without discarding a violation.
The compiler is not just to create a default object for each handle, because it will incur unnecessary overhead in many cases. If you want the handle to be initialized, you can do it below:
(1) When the object is defined. This means that they will definitely get initialization before the builder calls.
(2) In the builder of the class.
(3) It is close to the actual use of that object. Doing so can reduce unnecessary overhead - if the object does not need to create.
Here's all three methods:
//: Bath.java
// Constructor Initialization with Composition
Class soap {
PRIVATE STRING S;
SOAP () {
System.out.println ("SOAP ()");
s = new string ("constructed");
}
PUBLIC STRING TOSTRING () {Return S;
}
Public class bath {
Private string
// Initializing At Point of Definition:
S1 = New string ("happy"),
S2 = "Happy",
S3, S4;
SOAP CASTILLE;
INT I;
Float Toy;
Bath () {
System.out.println ("INSIDE BATH ()");
S3 = New String ("JOY");
i = 47;
Toy = 3.14f;
Castille = new soap ();
}
Void print () {
// delayed Initialization:
IF (S4 == NULL)
S4 = New String ("JOY");
System.out.println ("S1 =" S1);
System.out.println ("S2 =" S2);
System.out.println ("S3 =" S3);
System.out.println ("s4 =" s4);
System.out.println ("i =" i);
System.out.println ("Toy =" Toy);
System.out.println ("Castille =" Castille);
}
Public static void main (String [] args) {
Bath b = new bath ();
B.Print ();
}
} ///: ~
Note that in the Bath builder, a statement is performed before all initialization begins. If it is not defined, it is still not guaranteed to perform any initialization before sending a message to an object handle, unless an inevitable period period violation is presented.
Below is the output of the program:
INSIDE BATH ()
SOAP ()
S1 = happy
S2 = happy
S3 = JOY
S4 = JOY
i = 47
Toy = 3.14
Castille = Constructed
When PRINT () is called, it will populate S4 so that all fields get the correct initialization before use.
6.2 Inherited grammar
Inherit is very closely combined with Java (and other OOP languages). We have introduced the concept of inheritance in Chapter 1, and in this chapter to this chapter to this chapter from time to time, because some special occasions must be inherited. In addition, it will definitely inherit when creating a class, because if this is not, inherit from Java's standard root objects Object.
The synthetic synthesis is very simple and intuitive. However, in order to inherit, there must be a completely different form. When we need to inherit, we will say: "This new class is similar to that of the old class." In order to face the surface in your code, you need to give a class name. However, before the starting rigid brackets of the primitive, you need to place a keyword extend, follow the name of the "Basic Class". If this approach is taken, all data members and methods of the basic class can be automatically obtained. Below is an example:
//: detergent.java
// Inheritance Syntax & Properties
Class cleanser {
Private string s = new string ("cleanser");
Public void append (string a) {s = a;
Public void dilute () {append ("dilute ()");}
Public void apply () {append ("apply ()");} public void scrub () {append ("scrub ()");}
Public void print () {system.out.println (s);}
Public static void main (String [] args) {
Cleanser x = new cleanser ();
X.Apply (); x.scrub ();
X.Print ();
}
}
Public class detergent extends cleanser {
// Change a method:
Public void scrub () {
Append ("detergent.scrub ()");
Super.scrub (); // Call Base-Class Version
}
// Add methods to the interface:
Public void foam () {append ("foam ()");}
// Test The New Class:
Public static void main (String [] args) {
DETERGENT X = New detergent ();
x.dilute ();
X.Apply ();
X.SCRUB ();
X.foam ();
X.Print ();
System.out.println ("Testing Base Class:");
Cleanser.main (args);
}
} ///: ~
This example shows you a lot of characteristics. First, in the Cleanser Append () method, the string is connected to the same S. This is implemented with the " =" operator. Like " ", " =" is used by Java for "overload" processing on the string.
Second, a main () method is included regardless of Cleanser or Detergent. We can create a main () for each class. It is usually recommended that you write code like this, so that your test code can be encapsulated into the class. Even if there is a large number of classes in the program, only main () will be called for the public class requested at the command line. So in this case, when we use "Java Detergent", call Degergent.main () - even if Cleanser is not a public class. This kind of ways to place main () into each class can be easily tested for each class. And after completing the test, you need to delete main (); you can keep it, used for future testing.
Here, everyone can see that Deteregent.main () is clear for Cleanser.main ().
It is necessary to emphasize that all classes in the Cleanser are public properties. Keep in mind that if all access indicators are omitted, the members are "friendly". In this way, only the package member is allowed to access. In this package, anyone can use those methods that do not have access indicators. For example, Detergent will not encounter any trouble. However, it is assumed that classes from another package are ready to inherit the Cleanser, which can only access those public members. So when planning, a better rule is set to private and set all the methods to public (Protected members also allow derived classes to access it; will also discuss this problem later) . Of course, in some special occasions, we still have to make some adjustments, but this is not a good practice. Note that Cleanser contains a range of methods in its interface: append (), dilute (), apply (), scrub (), and print (). Since Detergent is derived from Cleanser (via Extends keyword), it will automatically obtain all of these methods in the interface - even if we do not see the explicit definitions of them in Detergent. In this way, the inheritance can be imagined into a "repeated use of the interface" or "regeneration of the interface" (the future implementation details can be freely set, but that is not our emphasis on the focus).
As seen in Scrub (), you can get a method defined in the basic class and modify it. In this case, we usually want to call methods from the basic class in the new version. However, in Scrub (), it cannot be simply issued to Scrub (). That causing recursive calls, we don't want to see this situation. To solve this problem, Java provides a super key that references the current class that has been inherited from it (SuperClass). So the expression super.scrub () is called the basic class version of the method scrub ().
When inheriting, we are not limited to only the basic class. You can also join your new way in the derived class. The approach taken at this time is exactly the same as any other method in a normal class: just simply define it. Extends Keyword Reminds us to prepare to add new methods to the basic class interface to "expand". Foam () is a product of this practice.
In detergent.main (), we can see that all available methods in Cleanser and detergents for Detergent objects (such as foam ()).
6.2.1 Initializing the basic class
Since this involves two classes - basic classes, it is no longer a previous one, there may be some confusion when imagining the results of derivatives. From the outside, it seems that the new class has the same interface as the base class, and some additional methods and fields can be included. But inheritance is not just simply copying the interface of the basic class. When an object of the derived class, it contains a "sub-object" of the base class. This sub-object is like we created an object based on the basic class itself. From the outside, the sub-object of the basic class has been encapsulated into the object of the derived class.
Of course, the basic class sub-object should be initialized correctly, and there is only one way to ensure that initialization is performed in the builder. By calling the underlying builder, the latter has sufficient ability and permissions to perform initialization of the basic class. . In a derived class builder, Java automatically inserts calls to the underlying class builder. The following example shows you the application of this three-level inheritance:
//: Cartoon.java
// constructor calls during inheritance
Class Art {
ART () {
System.out.println ("Art Constructor");
}
}
Class Drawing extends art {
Drawing () {
System.out.Println ("Drawing Construction");
}
}
Public class cartoon extends Drawing {
Cartoon () {
System.out.println ("Cartoon Constructor");
}
Public static void main (String [] args) {
Cartoon x = new cartoon ();
}
} ///: ~
The output of the program displays automatic calls:
Art Constructionor
Drawing constructor
Cartoon Constructionor
It can be seen that the construction is based on the "external" of the basic class, so the basic class will get the correct initialization before derivatives accessed it.
Even if you don't create a builder for Cartoon (), the compiler will automatically synthesize a default builder for us and issue calls to the underlying class builder.
1. Constructing a built-in variable
The above example has its own default builder; that is, they do not contain any arguments. The compiler can easily call them, because there is no problem with which the self-variable is not present. If the class does not have the default argument, or if you want to call a certain basic class builder with an argument, you must clearly write a call code for the base class. This is implemented using the super keyword and the appropriate argument list, as shown below:
//: chess.java
// inheritance, constructors and arguments
Class game {
Game (INT i) {
System.out.println ("Game Constructor");
}
}
Class Boardgame Extends Game {
Boardgame (int i) {
Super (i);
System.out.println ("BoardGame Constructor");
}
}
Public class chess extends boardgame {
Chess () {
SUPER (11);
System.out.println ("Chess Constructor");
}
Public static void main (String [] args) {
Chesces x = new chess ();
}
} ///: ~
If you do not call the underlying class builder in the BoardGames (), the compiler will report yourself not find a builder in the Games (). In addition, in the derived class builder, the call to the basic class builder is the first thing that must be done (such as the operation and loss, the compiler will point out).
2. Capture the violation of the basic builder
As just pointed out, the compiler will force us to first set the call to the base class builder in the main body of the derivative builder. This means that anything can not appear before it. As everyone sees in Chapter 9, this will also prevent the derivative class builder from capturing any violation events from a basic class. Obviously, this will sometimes be inconvenient for us.
6.3 Combination of synthesis and inheritance
Many times require the combination of synthesis to inherit two techniques. The following example shows how to use inheritance and synthetic technology to create a more complex class, while the necessary builder is initialized:
//: Placesetting.java
// Combining Composition & Inheritance
Class Plate {Plate (INT I) {
System.out.println ("Plate Constructor");
}
}
Class DINNERPLATE EXTENDS PLATE {
DINNERPLATE (INT I) {
Super (i);
System.out.println (
"DINNERPLATE CONSTRUctor");
}
}
Class utensil {
Utensil (int i) {
System.out.println ("Utensil Constructor");
}
}
Class Spoon extends utensil {
Spoon (INT i) {
Super (i);
System.out.println ("Spoon Constructor");
}
}
Class fork extends utensil {
Fork (int i) {
Super (i);
System.out.println ("fork constructor");
}
}
Class Knife Extends utensil {
Knife (int i) {
Super (i);
System.out.println ("Knife Constructor");
}
}
// a Cultural Way of Doing Something:
Class Custom {
Custom (INT I) {
System.out.println ("Custom Constructor");
}
}
Public Class PlaceSetting Extends Custom {
Spoon sp;
Fork frk;
Knife KN;
DINNERPLATE PL;
PlaceSetting (INT I) {
Super (i 1);
Sp = New Spoon (i 2);
FRK = New fork (i 3);
KN = New Knife (i 4);
PL = New DINNERPLATE (i 5);
System.out.println (
"Placesetting Construction");
}
Public static void main (String [] args) {
PlaceSetting X = New PlaceSetting (9);
}
} ///: ~
Although the compiler will force us to initialize the basic class and ask us to do this at the beginning of the builder, but it does not monitor if we correctly initialize the member object. So you must pay special attention to this.
6.3.1 Make sure the correct clearance
Java does not have the concept of "destroyer" like C . In C , once a target is broken (cleared), the destroyer method is automatically called. The reason why it will be omitted, which is probably due to simply forgets the object in Java, and does not need to force them. The garbage collector will automatically recover memory when necessary.
Most of the garbage collector can work well, but in some cases, our classes may take some actions in their own presence, and these actions must be clearly cleared. As I have pointed out, we don't know when the garbage collector will be able to be able to call it. So once you want to clear what is clear, you must write a special method, clear, and do this. At the same time, let the customer programmer know that they must call this method. Before all this, as in Chapter 9 (illegal control), it must be placed in a Finally clause in a finally slave, thereby preventing any violation events that may occur. The following describes an example of a computer-aided design system, which can draw graphics on the screen:
//: Cadsystem.java
// ENSURING PROPER CLEANUP
Import java.util. *;
Class shape {
Shape (INT I) {
System.out.println ("Shape Constructor");
}
Void cleanup () {
System.out.println ("Shape Cleanup");
}
}
Class Circle Extends Shape {
Circle (INT i) {
Super (i);
System.out.println ("Drawing a circle");
}
Void cleanup () {
System.out.println ("EraSing a circle");
Super.cleanup ();
}
}
Class Triangle Extends Shape {
Triangle (INT i) {
Super (i);
System.out.Println ("Drawing a Triangle");
}
Void cleanup () {
System.out.println ("EraSing a Triangle");
Super.cleanup ();
}
}
Class Line Extends Shape {
PRIVATE INT START, END;
LINE (int start, int end) {
Super (Start);
THIS.START = START;
THIS.END = End;
System.out.println ("Drawing a line:"
Start "," END);
}
Void cleanup () {
System.out.println ("EraSing a line:"
Start "," END);
Super.cleanup ();
}
}
Public class cadsystem extends shape {
Private circle C;
Private triangle t;
PRIVATE LINE [] lines = new line [10];
Cadsystem (INT I) {
Super (i 1);
For (int J = 0; j <10; j )
LINES [J] = New Line (J, J * J);
C = new circle (1);
T = New Triangle (1);
System.out.println ("Combined Constructor");
}
Void cleanup () {
System.out.println ("CADSystem.cleanup ()");
t.cleanup ();
C.cleanup ();
For (int i = 0; i LINES [i] .cleanup (); Super.cleanup (); } Public static void main (String [] args) { Cadsystem x = new Cadsystem (47); Try { // Code and Exception Handling ... } finally { x.cleanup (); } } } ///: ~ Everything in this system belongs to some Shape (geometric shape). Shape itself is an Object because it is clearly inherited from the root class. Each class redefines the shape's cleanup () method, but also uses the SUPER to call the basic class version of that method. Although all methods called during the presence of objects can be responsible for doing some requirements for clearing, but they have their own builders for specific Shape-Class -circle (circles), Triangle (triangles) and line, they all have their own builder. Complete the "Draw" task. Each class has their own Cleanup () method, which is used to restore non-memory things to the object before the object. In Main (), you can see two new keywords: try and finally. We are going to Chapter 9, will be officially referred to you. Among them, the TRY key is pointed out that the block following followed is a "alert zone". That is, it will be subject to special treatment. One of the treatments is that the code of the finally clause of the Finally slave after the warning area will definitely be executed - regardless of the TRY block to the bottom (via the violation control technology, the TRY block can have a variety of unusual applications). Here, Finally Sentence means "always call cleanup (), no matter what happens." These keywords will be fully, completely explained in Chapter 9. In your own clearance method, you must pay attention to the call sequence of the basic class and the member object clearance method - if a child object is based on another. Typically, the same form of "destroyer" with the C compiler should be taken: first complete all special work related to the class (may require the basic class element still visible), then call the base class cleaning method, just like this Demonstrate. In many cases, clearing may not be a problem; just let the garbage collector can do its responsibilities. But once you must clearly clear it, you must be careful, and you must ask for consideration. 1. The order of garbage collection You can't expect you to know how to start garbage collection. Garbage collectors may never be called. Even if it is called, it may also recover the object in any order you are willing. In addition to this, the garbage collector mechanism implemented by Java 1.0 usually does not call the Finalize () method. In addition to memory recovery, other things are best not to rely on the garbage collector. If you want to clear what it is clear, please make your own clearance method, and don't rely on Finalize (). However, as previously pointed out, Java1.1 can be forced to call all the finals modules (FINALIZER). 6.3.2 Hide of the name Only C programmers may be surprised by the name of the name, because its working principle is completely different in C . If the Java basic class has a method name being "overload" multiple times, the redefine of that method name is not hidden in the derivative class. So whether the method is defined in this order or in a basic class, the overload will take effect: //: hide.java // Overloading a base-class method name // in a derived class does not hide the // base-class versions Class home { Char doh (char c) { System.out.println ("DOH (Char)"); Return 'd'; } Float doh (float f) { System.out.println ("DOH (FLOAT"); Return 1.0F; } } Class Milhouse {} Class bart extends home { Void doh (Milhouse M) {} } Class hide { Public static void main (String [] args) { BART B = New Bart (); B.DOH (1); // doh (float) used B.DOH ('x'); B.DOH (1.0F); B. DOH (New Milhouse ()); } } ///: ~ As mentioned in the following chapter, it is rarely to overwrite the method of the same name and return type in the basic class, otherwise it will make people feel confused (this is the reason why C does not allow it, so Ability to prevent some unnecessary errors). 6.4 Select synthesis or inheritance Regardless of synthesis or inheritance, we will allow our child objects in its own new class. Everyone may be strange to the difference between the two, and how to choose. If you want to use an existing class of existing classes in the new class, you should usually choose synthesis. That is, we can embed an object, so that you can use it to implement the novelty features. But the new class users will see the interface we have defined instead of an interface from an embedded object. Considering this effect, we need to embed the existing class Private object in the new class. Sometimes, we want to let the class users directly access the synthesis of the new class. That is, it is necessary to turn the properties of the member object to public. Member objects will hide themselves, so this is a safe approach. And when the user knows that we are ready to synthesize a series of components, the interface is easier to understand. CAR (car) object is a good example: //: car.java // composition with public objects Class Engine { Public void start () {} Public void rev () {} Public void stop () {} } Class wheel { Public void inflate (int psi) {} } Class window { Public void rollup () {} Public void rolldown () {} } Class door { Public window window = new window (); Public void open () {} Public void close () {} } Public class car { Public Engine Engine = new engine (); Public Wheel [] Wheel = New Wheel [4]; Public Door Left = New Door (), Right = new door (); // 2-door Car () { For (int i = 0; i <4; i ) Wheel [i] = new wheel (); } Public static void main (String [] args) { Car car = new car (); Car.LEFT.WINDOW.ROLLLUP (); Car.wheel [0] .INFLATE (72); } } ///: ~ Since the assembly of the car is a factor that needs to be considered (not only part of the basic design), it helps the customer programmer understand how to use classes, and the programming complexity of class creators will also decrease significantly. If you choose inheritance, you need a ready-made class and make a special version of it. Usually, this means we prepare a class of conventional uses and customize it according to a specific requirement. Just try to imagine, you know that you cannot use a vehicle object to synthesize a car - the car does not "contain" vehicles; instead, it "belongs to" a category of the vehicle. "It belongs to" is expressed in inheritance, and "containing" relationships are expressed in synthesis. 6.5 protected Now we have understood the concept of inheritance, protected, this keyword is finally finally meaningful. Ideally, Private members are "private", anyone is not accessible. But in practical applications, you often want to put some things deeply, but also allow access to the derivative class. Protected keywords help us do this. It means that it is private, but can be accessed by anything from this class or anything in the same package. " That is, Protected in Java will become the "friendly" state. The best practice we take is to keep the members' private status - anyway to reserve the right to implement the implementation details of the foundation. Under this premise, the successor of the class can be permitted by the protected method: //: Orc.java // the protected keyword Import java.util. *; Class villain { Private INT i; protected int} {return i;} Protected Void Set (INT II) {i = II; Public Villain (INT II) {i = II; Public int value (int M) {RETURN M * i;} } Public class orc extends villain { Private int J; Public Orc (INT JJ) {Super (JJ); J = JJ;} Public void change (int x) {set (x); } ///: ~ It can be seen that change () has access to set () because its attribute is protected (protected). 6.6 Cumulative Development One benefit of inheritance is that it supports "accumulation development", allowing us to introduce new code, and will not cause errors for existing code. This can isolate new errors into the new code. Through from an off-the-shelf, functional class inherit, add members new data members and methods (and redefine existing methods), we can maintain the existing code to have an unstachable, and some people may still use it), no Will introduce your programming error. Once an error occurs, you know that it is definitely caused by your own new code. In this way, the time and energy required to correct the error may be much less than the main body of modifying the existing code. The type of isolation is very good, this is what many programmers are not expected in advance. Even the source code of the method is not required to realize the regeneration of the code. You only need to import a package (this is true for inheritance and merge). Everyone should remember such a focus: program development is a process of increasing or accumulating, just like people learning knowledge. Of course, as much analysis can be performed as required, but in the beginning of a project, no one can know all the answers in advance. If you can think of your project as an organic, you can constantly develop and improve it, it is expected to achieve greater success and more direct feedback. Although inheritance is a very useful technique, in some cases, in particular, after the project is stable, it still needs to invest in its own class structure from a new angle, and shrink it into a more flexible structure. Remember, inheritance is the expression of a special relationship, meaning "this new category belongs to the old class". Our procedures should not be entangled in some fine tree, but should focus on creating and operating various types of objects, using them from a model from "problem space". 6.7 traceable Inheriting the most worthless place is that it does not provide methods for new categories. Inheritance is an expression of the relationship between the new category and the basic class. This relationship can be summarized: "New Class is a type of existing class." This expression is not just a visualization of inherits, and inheritance is directly supported by the language. As an example, everyone can consider a basic class named Instrument, which is used to indicate the instrument; another derivative class is called WIND. Since inheritance means that all methods of the basic class can also be used in derived classes, any message we send to the basic class can also be sent to the derived class. If the Instrument class has a Play () method, Wind devices will also have this method. This means that we can affirm that a WIND object is also a type of Instrument. The following example reveals how the compiler provides support for this concept: //: wind.java // Inheritance & Upcasting Import java.util. *; Class Instrument { Public void play () {} Static void Tune (Instrument i) { // ... I.Play (); } } // Wind Objects Are INSTRUMENTS // Because They Have The Same Interface: Class Wind Extends Instrument { Public static void main (String [] args) { Wind flute = new wind (); Instrument.Tune (flute); // upcasting } } ///: ~ The most interesting in this example is the Tune () method, which can accept an Instrument handle. However, in Wind.main (), the Tune () method is called by it to give it a Wind handle. Since Java is particularly strict, everyone may feel very strange, why can I receive another type of method? However, we must realize that a Wind object is also an Instrument object. And for an Instrument in Wind, there is no way to call it by tune (). In Tune (), the code applies to INSTRUMENT and anything derived from INSTRUMENT. Here, we will convert from a Wind handle into an inStrument handle called "traceable". 6.7.1 What is "traceable"? The reason why it is called this name, in addition to having a certain historical reason, it is because of the traditional sense, the painting of the class is located at the top, and gradually expands down (of course, can use any method according to their own habits. Describe this figure). Factors, WIND.JAVA inheritors look like this: Since the direction of the shape is from derived class to the base, the arrow is facing up, so it is usually called "traceable", ie Upcasting. Tracerty is definitely safe because we are from a more special type to a more conventional type. In other words, the derived class is a supercoming of the base class. It can contain more methods than the basic class, but it contains at least the basic class. When tracing, the only problem that may occur in the class interface is that it may lose the method, not to win these methods. This is why the compiler is allowed in the case where there is no clear shape or other special labeling. It is also possible to perform a discontinuation, but it will face a dilemma that will be described in detail in Chapter 11. 1. Conception and inheritance In an object-oriented programming, the most likely taken by creating and using code is: unifying the data and methods into a class and uses the object of that class. Sometimes, you need to construct new categories by "synthetic" technology. Inheritance is one of the best practices. Therefore, although inheritance has been greatly emphasized in the process of learning OOP, it does not mean that it should be used whenever possible. Instead, it is particularly careful when using it. It can only be considered by clearly knowing that inheritance is most effective in all methods. To determine that you should use synthesis or inheritance, a simplest way is to consider whether you need to replace the basic class from the new class. If you have to go back, you need to inherit. But if you don't need to shape, you should remind yourself to prevent the abuse of inheritance. In the next chapter (versatile), it will introduce one of the places that must be traced. But as long as you remember that you often ask yourself "I really need to be traced back", for synthesis or inheritance choices should not be too big. 6.8 final keyword Due to different context (application environment), the meaning of Final keyword may have some differences slightly. But it is the most general meaning that "this thing cannot be changed". The reason why changes may be prohibited may be considering two factors: design or efficiency. Since these two reasons are quite different, it may cause false use of Final keywords. In the next section, we will discuss three applications of Final keywords: data, methods, and classes. 6.8.1 Final Data Many programming languages have their own way to tell the compiler that a data is "constant." The constants are mainly used in the following two aspects: (1) Compile period constant, it will never change (2) a value initialized in the running period, we don't want it to change For constants of the compile period, the compiler (program) can "encapsulate" in the required calculation process. That is, the calculation can be performed in advance during compilation, thereby saving some of the expenses at runtime. In Java, these forms of constants must belong to the primitive data type (Primitives), and use Final keywords. A value must be given when the such constant is defined. No matter the Static or final field, you can only store one data and do not change. If the same object handle is used, not the basic data type, its meaning is slightly confused. For basic data types, Final will turn the value into a constant; but for the object handle, Final will turn the handle into a constant. When making a declaration, you must initialize the handle to a specific object. And never turn the handle to another object. However, the object itself can be modified. Java does not provide any means to this, you can turn a object directly into a constant (however, we can write a class yourself, so that the object has a "constant" effect). This limitation is also suitable for arrays, which also belongs to objects. Below is an example of demonstrating the Final field usage: //: FinalData.java // the Effect of final on Fields Class value { INT i = 1; } Public class finaldata { // Can Be Compile-Time Constants Final Int i1 = 9; Static Final Int I2 = 99; // Typical Public Constant: Public Static Final INT I3 = 39; // Cannot Be Compile-Time Constants: Final Int i4 = (int) (Math.random () * 20); Static Final Int i5 = (int) (Math.random () * 20); Value v1 = new value (); Final Value v2 = new value (); Static Final Value V3 = new value (); //! Final Value V4; // pre-java 1.1 Error: // no Initializer // arrays: Final int [] a = {1, 2, 3, 4, 5, 6}; Public void print (String ID) { System.out.println ( ID ":" "i4 =" i4 ", i5 =" i5); } Public static void main (String [] args) { FinalData fd1 = new finaldata (); //! fd1.i1 ; // error: can't change value Fd1.v2.i ; // Object isn't constant! Fd1.v1 = new value (); // ok - not final For (int I = 0; i Fd1.a [i] ; // Object isn't constant! //! fd1.v2 = new value (); // error: can't //! fd1.v3 = new value (); // Change Handle //! fd1.a = new int rt [3]; FD1.PRINT ("fd1"); System.out.println ("CREATING New FinalData"); FinalData FD2 = New FinalData (); FD1.PRINT ("fd1"); Fd2.print ("fd2"); } } ///: ~ Since I1 and I2 are basic data types with Final properties, they contain values of the compile period, so they do not appear in any way in any import mode in addition to the constant use of the compile period. I3 is a more typical way we experience this constant definition: public means that they can be used outside the package; Static emphasizes that only one; and final indicates that it is a constant. Note For the FIANL Static basic data type containing the fixed initial value (ie, the compile constant constant), their names should be all written according to the rules. Also note that I5 is unknown during compilation, so it has no uppercase. Can't recognize that its value can be known during compile time because of the property of the sample. I4 and I5 prove this. They use randomly generated numbers during operation. This part of the example also reveals the difference between setting the final value to static and non-STATIC. This difference will only be revealed only when the value is initialized during the run. Because the value during compilation is considered the same as the compiler. This difference can be seen from the output results: FD1: i4 = 15, i5 = 9 Creating New FinalData FD1: i4 = 15, i5 = 9 FD2: i4 = 10, i5 = 9 Note For FD1 and FD2, the value of I4 is unique, but the value of I5 does not change since another FinalData object is created. That is because its attribute is static, and initialized at load, not each object to initialize. The variable from V1 to V4 reveals the meaning of the Final handle. As everyone sees in main (), it does not think that because V2 belongs to Final, it is no longer possible to change its value. However, we really can't bind V2 to a new object because it's final. This is the exact meaning of Final for a handle. We will find that the same meaning is also suitable for arrays, which is just another type of handle. It seems better useless to turn the script of the basic data type into Final. 2. Blank Final Java 1.1 allows us to create "blank Final" that belongs to some special fields. Although it is declared to Final, it is not yet given an initial value. No matter which case, blank final must be properly initialized before actual use. And the compiler will take the initiative to ensure that this provision is implemented. However, for various applications of Final keywords, blank Final has the greatest flexibility. For example, a Final field located interior is now different for each object, while still maintaining its "unchanged" essential. One example is listed below: //: Blankfinal.java // "Blank" Final Data MEMBERS Class poppet {} Class blankfinal { Final I = 0; // Initialized Final Final Int J; // Blank Final Final Poppet P; // Blank Final Handle // Blank Finals Must Be Initialized // in the constructor: BlankFinal () { J = 1; // Initialize Blank Final P = new poppet (); } BlankFinal (int x) { J = x; // Initialize Blank Final P = new poppet (); } Public static void main (String [] args) { Blankfinal bf = new blankfinal (); } } ///: ~ Now I am forcibly requesting to assign value for Final - either use an expression when defining fields or in each builder. This ensures that the final field gets correct initialization before use. 3. Final argument Java 1.1 allows us to set the argument to the Final property, and the method is to make appropriate declarations in the argument list. This means that in the inside of a method, we cannot change what the variable handle points to. As follows: //: FinaAlarguments.java // USING "final" with method arguments Class gizmo { Public void spin () {} } Public class finAlaguments { Void with (Final GizMo g) { //! g = new gizmo (); // illegal - g is final g.spin (); } Void without (gizmo g) { g = new gizmo (); // ok - g not final g.spin (); } // Void F (Final INT i) {i ;} // can't change // you can only read from a factory primitive: INT G (Final INT I) {RETURN I 1;} Public static void main (String [] args) { FinaAlarguments bf = new finAlarguments (); bf.without (null); bf.with (null); } } ///: ~ Note that a null (empty) handle can still be assigned to Final arguments, while the compiler does not capture it. This is the same as our operations taken to non-final arguments. Methods f () and g () show us what happens when the basic type of autochangement is final: We can only read the variable and cannot change it. 6.8.2 final method The reason why the Final method is to be used may be considered for two reasons. The first is to "lock" on the method to prevent any inheritance classes from changing its original meaning. This approach can be taken if the behavior of a method is remained unchanged during the inheritance and cannot be overwritten or overwritten. The second reason to adopt the FINAL method is the efficiency of the program execution. After setting a method into Final, the compiler can put all the calls to that method into the "embedded" call. As long as the compiler finds a final method call, it will ignore the regular code insertion method taken for the execution method call mechanism (according to its own judgment) (according to its own judgment) ignore the normal code insertion method (the argument is pressed into the stack; jump to the method code and execute it; jump back; Clear the stack self-variable; finally processes the return value). Instead, it uses a copy of the actual code within the method main body to replace the method call. This can avoid system overhead when the method is called. Of course, if the method is too large, the program will become 雍, may be affected by any performance boost that the embedded code. Because any lifting is spent in the process within the method. The Java compiler can automatically detect these situations and are quite "wise" to decide whether to embed a Final method. However, it is best not to fully believe that the compiler can make all judgments correctly. Typically, only when the amount of code is very small, or when it is clear that the method is covered, it should be considered to be set to Final. All private methods in the class are automatically generated. Since we cannot access a private method, it will never be overwritten by other methods (if you forced this, the compiler gives an error message). You can add a Final indicator for a private method, but you can't provide any additional meaning for that method. 6.8.3 Final class If the entire class is Final (in its definition crown with final keyword), it indicates that you don't want to inherit from this class, or anyone is not allowed to take this action. In other words, for such a reason, our class definitely does not need to make any changes; or for security, we do not want subclasses (subclass processing). In addition, we may also take into account the issue of efficiency, and want to ensure that all actions involving this object should be effective as possible. As follows: //: jurassic.java // MAKING An Entire Class Final Class smallbrain {} Final class dinosaur { INT i = 7; INT j = 1; Smallbrain x = new smallbrain (); Void f () {} } //! Class Further Extends Dinosaur {} // error: Cannot Extend Final Class 'Dinosaur' Public class jurassic { Public static void main (String [] args) { Dinosaur n = new dinosaur (); n.f (); n.i = 40; N.j ; } } ///: ~ Note that the data member can be both Final or not, depending on our specific choice. Rules applied to Final also apply to data members, regardless of whether or not class is defined as Final. After defining the class into Final, the result is only prohibited from inheritance - there is no more restrictions. However, since it is forbidden to inherit, all methods in a final class are deferred as Final. You can no longer cover them because it is now. So, like we clearly declare the FINAL, the compiler has the same efficiency option. You can add a Final indicator to a method in the final class, but there is no meaning. 6.8.4 Final precautions When you design a class, you often need to consider whether a method is set to Final. It may be very important to perform efficiency when using your own class, no one wants to cover your own method. This idea is correct in some time. But you have to make your own assumptions. Usually, it is difficult to predict that a class will regenerate or reuse it in the future. This is especially true of conventional purposes. If a method is defined as final, it may put an end to the way to inherit your own class in other programmers, as we didn't think it would be like it. The standard Java library is the best example of this view. One of the particularly commonly used classes is Vector. If we consider the efficiency of the code, it will find that only if any method is set to FINAL, it can make it greater role. We can easily think that he should inherit and cover a class such as such a useful class, but its designer denies our ideas. But we can use at least two reasons to refute them. First, the Stack is inherited from vector, that is, STACK "is" a vector, which is inappropriate. Secondly, for many important methods, such as addelement () and Elementat (), they have become Synchronized (synchronous). As in Chapter 14, this will cause significant performance overhead, which may improve the performance of FINAL to sell. Therefore, the programmer has to guess where to optimize it. In the standard library, such a clumsy design is actually used, I can't imagine what kind of emotion will cause programmers. Another worth noticing is a HashTable, it is another important standard class. This class does not use any Final method. As we mentioned elsewhere in this book, it is apparent that some types of designers have a complete qualities with other designers (pay attention to comparison of HashTable's very short method names and Vecor's method name). For users of the class library, this obviously should not be seen so easily. The design of a product becomes inconsistent, it will increase the workload of users. This also emphasizes the code design and inspection from another side to emphasize the sense of responsibility. 6.9 Initialization and class loading In many traditional languages, the programs are partially loaded as part of the startup process. The initialization is then initialized, and the official execution program is officially executed. In these languages, the initialization process must be cautious, ensuring that the initialization of Static data will not cause trouble. For example, before a Static data gets initialization, there is another Static data to be a valid value, then there is a problem in C . Java has no such problem because it uses a different load method. Since everything in Java is an object, many activities become more simple, this problem is one of them. As mentioned in the next chapter, the code of each object exists in a separate file. The file will not be loaded unless you really need code. Typically, we can think that the code will not be realized unless an object structure of that class is completed. Since there is some subtle ambiguity in the Static method, it is considered that "the class code is loaded for the first time". The place used for the first time is also where static initialization occurs. When loading, all Static objects and Static code blocks are initialized in order (that is, they are written in class definition code). Of course, STATIC data will only initialize it. 6.9.1 Inheriting Initialization We need to have aware of the entire initialization process, including inheritance, have a holistic concept for things that have occurred in this process. Please observe the following code: //: Beetle.java // The full process of infliure. 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 (); } } ///: ~ The output of the program is as follows: Static Insect.x Initialized Static Beetle.x Initialized Beetle Constructionor i = 9, j = 0 Beetle.kinitialized K = 63 J = 39 When you run Java for Beetle, the first thing that happens is that the loader is found to find that class. During the loading process, the loader pays attention to it has a basic class (ie the extends keyword to express means), so it will be loaded. This process occurs regardless of whether to generate the basic class, this process will happen (please try to take the creative code of the object as a comment, you can confirm). If the base class contains another basic class, the other underlying class will also be loaded, and this type is pushed. Next, Static initialization will be performed in the root base class (which is inSect), and then execute in the next derived class, so push it. It is very important to ensure that this order is critical because the initialization of derived classes may depend on the correct initialization of the basic class members. At this time, the necessary classes have been loaded, so they can create objects. First, all the basic data types in this object are set to their default, and the object handle is set to NULL. The underlying class builder will then be called. In this case, the call is automatically performed. But it is also possible to specify the build buffer call (just like the first operation in the Beetle () builder) in the beetle () builder). The construction of the basic class is used in the same processing process as the derivative class builder. After the base buffer builder is completed, the instance variable is initialized in order. Finally, the main part of the builder remains. 6.10 Summary Regardless of inheritance or synthesis, we can create a new type on the basis of existing types. However, in a typical case, we implement existing type "regeneration" or "reuse" by synthesizing, and use it as part of the new type of basic implementation process. But if you want to realize the "regeneration" of the interface, you should use inheritance. Since derived or derived classes have basic types of interfaces, they can "wind-up" is based on the basics. This is crucial for the multi-shaped problem to tell in the next chapter. Although inheritance is particularly emphasized in the object-oriented programming, it is best to consider using synthetic techniques when actually starting a design. Inheritance technology is considered only when it is especially necessary (the next chapter will talk about this problem). Synthesis is more flexible. However, by applying some inheritance skills to their own member types, the type of member object can be changed to the runtime, thereby changing their behavior. Despite the development of fast projects, the code regeneration through synthesis and inheritance has a great help. However, before allowing other programmers to rely on it, they generally want to re-design their class structure. Our ideal class structure should be that each class has its own specific purpose. They cannot be too large (if there are too many integrated features, it is difficult to achieve its regeneration), and it is not too small (causing yourself to use, or you can't add new features). The ultimately realized class should be able to easily regenerate.