Class regeneration and vectors

xiaoxiao2021-03-06  24

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.

6.11 practice

(1) Create two classes with the default builder (empty variable list): a and b, make them state themselves. Inheriting a new class called C from a, and creates a member B within C. Do not create a builder for C. Create an object of class C and observe the result.

(2) Modifying Exercise 1, making A and B have a builder containing an argument, and is not the default builder. Write a builder for C and perform all initialization works in the C's builder.

(3) Use the file cartoon.java to turn the builder code of the Cartoon class into the annotation content label. What happens to explain.

(4) Use the file chess.java to mark the CHESS class builder code as a comment. What happens to the same explanation.

Chapter 7 Multi-shapes "For object-oriented programming languages, multiplexity is the third most basic feature (the first two are data abstraction and inheritance." "Polymorphism) will be from another angle The interface is separated from a specific implementation detail, that is, "what" is separated from "What to do". With the concept of polymorphism, the organization and readability can be improved. In addition, It can also create "easy to expand" procedures. Whether in the creation of the project, they can easily "growth" when needed to join new features. By merged various features and behaviors, packaging technology can create new Data type. By hidden to specific details, the interface can be separated from the implementation details, making all details "(private). This organization makes those programming background people feel comfortable. But more The shape involves the decomposition of "type". Through the study of the previous chapter, everyone knows that one object can be treated as its own type or its own basic type. This ability is very important because Multiple types (derived from the same basic type) can be treated as the same type. And only one code is required, you can do the same processing for all different types. Use with versatile methods, one Types can separate themselves from another similar type, as long as they are derived from the same basic type. This distinguishing is achieved by various methods in behavior, and can be implemented For those methods, in this chapter, everyone should learn about the problem of vectors from shallow to deeply (also called dynamic binding, postpone binding or running binding). At the same time, some simple examples, All of these are all stripped, only the code related to the versions. 7.1 Tracking in Chapter 6, everyone knows that an object can be used as its own type, or as a basic type of it Object use. A object handle is obtained, and it is called "traceable" as the behavior of the basic type handle - because the painting of the inherited tree is the top right. But this will also encounter a problem. As shown in the following example (if you have trouble, please refer to Chapter 3.1.2 "Assignment": //: Music.java

// Inheritance & Upcasting

Package C07;

Class Note {

Private int value;

Private note (int val) {value = val;}

Public Static Final Note

Middlec = new Note (0),

Csharp = New Note (1),

cflat = new note (2);

} // etc.

Class Instrument {

Public void play (Note N) {

System.out.println ("Instrument.Play ()");

}

}

// Wind Objects Are INSTRUMENTS

// Because They Have The Same Interface:

Class Wind Extends Instrument {

//Refine Interface Method:

Public void play (Note N) {

System.out.println ("Wind.Play ()");

}

}

Public class music {

Public Static Void Tune (Instrument I) {

// ...

I.Play (Note.Middlec);

}

Public static void main (String [] args) {

Wind flute = new window (); tune (flute); // upcasting

}

} ///: ~

Among them, Music.Tune () receives an Instrument handle while also receiving everything from Instrument. This happens when a Wind handle is passed to Tune (). There is no need to shape it. This is acceptable; the interface in Instrument must exist in Wind because Wind is inherited from Instrument. From Wind to Instrument, you may "reduce" that interface, but it is impossible to make it smaller than the full interface of Instrument.

7.1.1 Why is you going back?

This program seems to be a bit strange. Why should everyone have interested in forget the type of object? When you travel back, you may have doubts in this regard. And if tune () simply obtains a Wind handle, use it as its own own variable, it seems to be more simple and intuitive. But pay attention to: If you do it, you need to write a new Tune () for each type of Instrument in the system. Suppose it is in the previous inference, add StrUns and BRASS (Copper Tubes):

//: Music2.java

// overloading instead of upcasting

Class note2 {

Private int value;

Private Note2 (int value = val;)

Public Static Final Note2

Middlec = new note2 (0),

Csharp = new note2 (1),

CFLAT = New Note2 (2);

} // etc.

Class Instrument2 {

Public void play (Note2 N) {

System.out.println ("Instrument2.Play ()");

}

}

Class wind2 extends instruction2 {

Public void play (Note2 N) {

System.out.println ("Wind2.Play ()");

}

}

Class stringed2 extends instructionent2 {

Public void play (Note2 N) {

System.out.println ("Stringed2.Play ()");

}

}

Class Brass2 Extends Instrument2 {

Public void play (Note2 N) {

System.out.println ("BRASS2.PLAY ()");

}

}

Public class music2 {

Public static void tune (wind2 i) {

I.Play (Note2.Middlec);

}

Public static void tune (stringed2 i) {

I.Play (Note2.Middlec);

}

Public Static Void Tune (BRASS2 I) {

I.Play (Note2.Middlec);

}

Public static void main (String [] args) {

Wind2 flute = new window2 ();

Stringed2 Violin = new stringed2 ();

BRASS2 FRENCHORN = New Brass2 ();

Tune (flute); // no upcastingTune (violin);

Tune (French ";

}

} ///: ~

This is to do it, but there is a great drawback: you must prepare a closely related method for each new Instrument2 class. This means that much more programmed amounts are required for the first time. In the future, if you want to add a new method like Tune () or add a new type to INSTRUMENT, you still need a lot of encoding. In addition, even if you forget to overload your own method, the compiler will not prompt any errors. In this way, the entire operation process of the type is extremely difficult to manage, and there is a risk of out-of control.

However, if only one method is written, use the basic class as an argument or parameter, rather than using those specific derivatives, isn't it simple? That is, if we can derive the class, only let your code to deal with the basic class, then the amount of work will be difficult to estimate.

This is the place where "versatile" is characterized. However, most programmers (especially for programming background) are still somewhat raw.

7.2 In-depth understanding

For Music.java's difficulties, it can be experienced by running procedures. The output is Wind.Play (). This is of course the output of our hopes, but it seems that it does not want to act according to our hopes. Please observe the tune () method:

Public Static Void Tune (Instrument I) {

// ...

I.Play (Note.Middlec);

}

It receives the INSTRUMENT handle. So in this case, how can the compiler know that the INSTRUMENT handle points to a Wind, not a BRASS or STRINGED? The compiler is not known. In order to understand this problem, we need to explore the theme of "binding".

7.2.1 Binding of the method call

Calling a method calls the same method body to connect together is called "binding". If the binding is performed before the program runs (by the compiler and linker, if any), it is called "early binding". Everyone I have never heard of this term because it is impossible in any programming language. The C compiler has only one way to call, that is, "early binding".

The most confusing place in the above program is related to early binding, because in the premise of only one Instrument handle, the compiler does not know which method of the specific call.

The method of solving is "post-binding", which means that the binding is performed during operation, based on the type of object. The post-binding is also called "dynamic binding" or "running binding". If a language implements a later binding, some mechanisms must be provided, and the type of object can be determined during operation, and the appropriate method is called separately. That is, the compiler still does not know the type of object, but the method call mechanism can go to investigate, find the correct method body. Different languages ​​have different methods of implementation of later bindings. But we can at least think that they must set some special types of information in the object.

All methods binding in Java use post-binding techniques unless a method has been declared into Final. This means that we usually do not have to decide whether it should be binding - it is automatically occurred.

Why do you declare a way to Final? As the previous chapter pointed out, it prevents others from overwriting that method. But maybe more importantly, it can effectively "close" dynamic binding, or tell the compiler that does not need to perform dynamic binding. In this way, the compiler can call the FINAL method to generate higher code.

7.2.2 Creating the right behavior

All methods of knowing Java bindings are backed by later binding, you can write your own code accordingly, communicate with the base class. At this point, all derivative classes ensure that it can work properly with the same code. Or for another method, we can "send a message to an object, let the object to determine what to do." In the object-oriented program, there is a classic "shape" example. Because it is easy to manifest by visualization, it is often used to explain the problem. But unfortunately, it may mislead beginners think that OOP is just designed for graphical programming, this understanding is of course wrong.

The shape example has a basic class, named Shape; there is a large number of derivative types: Circle, Square (square), Triangle (triangle), etc. Everyone likes this example, because it is easy to understand the concept of "a type of shape". The following inheritors show us their relationships:

Trace shape can be simply manifested by using this statement:

Shape s = new circle ();

Here, we created a Circle object and assigned the result handle to a shape. This surface seems to be an error operation (assigning a type to another), but it is actually feasible - because in inheritance relationship, Circle belongs to the Shape. Therefore, the compiler recognizes the above statement and will not prompt us to prompt an error message.

When we call one of the basic types (overridden in the derived class):

s.draw ();

Similarly, everyone may think that the Shape's Draw () is called because it is a shape handle after all. So how can the compiler know what else should be other? But at this time, Circle.draw () is Circle.Draw () because the later binding has been intervened (versatile).

The following example explains the problem from a slightly different angle:

//: Shapes.java

// Polymorphism in Java

Class shape {

Void Draw () {}

Void erases () {}

}

Class Circle Extends Shape {

Void Draw () {

System.out.println ("Circle.Draw ()");

}

Void erase () {

System.out.println ("Circle.rase ()");

}

}

Class Square Extends Shape {

Void Draw () {

System.out.println ("Square.Draw ()");

}

Void erase () {

System.out.println ("Square.erase ()");

}

}

Class Triangle Extends Shape {

Void Draw () {

System.out.println ("Triangle.Draw ()");

}

Void erase () {

System.out.println ("Triangle.ed ()");

}

}

PUBLIC CLASS shapes {

Public static shape randshape () {

Switch ((int) (math.random () * 3)) {

Default: // to quiet the compiler

Case 0: return new circle ();

Case 1: return new square ();

Case 2: return new triangle ();

}

Public static void main (String [] args) {

Shape [] s = new shape [9];

// Fill Up The Array with Shapes:

For (int i = 0; i

s [i] = randshape ();

// make Polymorphic Method Calls:

For (int i = 0; i

S [i] .draw ();

}

} ///: ~

For all things derived from Shape, Shape has built a general interface - that is, all (geometric) shapes can be depicted and deleted. Derivatives covers these definitions, providing a unique behavior for each special type of geometry.

In the primary class shapes, a Static method is included in Randshape (). Its role is to generate a handle for a random-selected Shape object at a timely call. Please note that the shape is in every returnite statement. This statement acquires a handle to a Circle, Square, or Triangle, and transmits it as a return type shape. So whenever you call this method, you will never have a chance to understand what it is, because it will definitely get a simple Shape handle.

Main () contains an array of Shape handles, where data is filled in the call of Randshape (). At this time, we know that you have Shape, but you don't know any specific situation except for this (the compiler also doesn't know). However, when we step in this array and call DRAW () for each element, the correct behavior of each type will happen, just like the output sample below:

Circle.draw ()

Triangle.draw ()

Circle.draw ()

Circle.draw ()

Circle.draw ()

Square.draw ()

Triangle.draw ()

Square.draw ()

Square.draw ()

Of course, since the geometry is randomly selected, each run may have different results. The random choice to highlight the shape is to let everyone know this: In order to make a correct call when compiling, the compiler does not need to obtain any special intelligence. All calls to DRAW () are made by dynamic binding.

7.2.3 scalability

Now let's still return an example of an instrument (inStrument). Due to the presence, any new type can be added to the system according to your own needs, while do not need to change the true () method. In a well-designed OOP program, most of our or all methods will follow the model of Tune () and only communicate with the base interface. We said that such a program has "scalability" because the new data type can be inherited from the general basic class to add some functions. If it is to adapt to the requirements of the new class, the method for manipulating the basic interface does not need to change,

For the instrument example, suppose we add more methods in the basic class, as well as a series of new categories, what happens? The following is a schematic:

All of these new classes work with the old-Tune (), do not require any adjustments to tune (). Even if tune () is located in a separate file, Tune () can also work correctly without the need to recompile. The following program is the specific implementation of the above-described schematic: //: Music3.java

// an extensible program

Import java.util. *;

Class Instrument3 {

Public void play () {

System.out.println ("Instrument3.Play ()");

}

Public string what () {

Return "Instrument3";

}

Public void adjounce () {}

}

Class Wind3 Extends Instrument3 {

Public void play () {

System.out.println ("Wind3.Play ()");

}

Public string what () {return "wind3";

Public void adjounce () {}

}

Class percussion3 extends instruction3 {

Public void play () {

System.out.println ("Percussion3.Play ()");

}

Public string what () {return "percussion3";

Public void adjounce () {}

}

Class stringed3 extends instruction3 {

Public void play () {

System.out.println ("Stringed3.Play ()");

}

Public string what () {return "stringed3";

Public void adjounce () {}

}

Class Brass3 extends wind3 {

Public void play () {

System.out.println ("BRASS3.PLAY ()");

}

Public void adjounce () {

System.out.println ("BRASS3.ADJUST ()");

}

}

Class Woodwind3 Extends WIND3 {

Public void play () {

System.out.println ("Woodwind3.Play ()");

}

Public String What () {Return "Woodwind3";

}

Public class music3 {

// Doesn't Care About Type, So New Types

// Added to the System Still Work Right:

Static void tune (Instrument3 i) {

// ...

I.Play ();

}

Static void TuneAll (Instrument3 [] E) {

For (int i = 0; i

Tune (e [i]);

}

Public static void main (String [] args) {

Instrument3 [] orchestra = new instruction3 [5];

INT i = 0;

// Upcasting during addition to the array: Orchestra [i ] = new window3 ();

Orchestra [i ] = new percussion3 ();

Orchestra [i ] = new stringed3 ();

Orchestra [i ] = new brass3 ();

Orchestra [i ] = new woodwind3 ();

TuneAll (Orchestra);

}

} ///: ~

The new method is What () and adjounce (). The former returns a String handle while returning the instructions for that class; the latter makes us adjust each instrument.

In Main (), when we put something in an Instrument3 array, it will automatically trace the inStrument3.

It can be seen that while all of all of the other code surrounding the Tune () method changes, the Tune () method is not affected by their influence, and it is still working properly. This is the goal that uses versatility. After modification of the code, we will not affect the parties that should not be affected in the program. In addition, we believe that it is a crucial technology that allows programmers to "change the change in things that have not changed."

7.3 Covering and Overload

Now let's take a look at the first example of this chapter in different eyes. In the following program, the interface of the method play () changes during the covered process. This means that we do not actually have a "overlay" method, but "overload". The compiler allows us to overload the method so that it does not report an error. But this behavior may not be what we hope. Here is this example:

//: Winderror.java

// Accidentally Changing The Interface

Class Notex {

Public Static Final Int

Middle_c = 0, c_sharp = 1, c_flat = 2;

}

Class InstrumentX {

Public void play (int NOTEX) {

System.out.println ("InStrumentX.Play ()");

}

}

Class Windx Extends InstrumentX {

// OOPS! Changes the Method Interface:

Public void play (NOTEX N) {

System.out.println ("Windx.Play (NOTEX N)");

}

}

Public class windrror {

Public Static Void Tune (InstrumentX i) {

// ...

I.Play (NOTEX.MIDDLE_C);

}

Public static void main (String [] args) {

Windx flute = new windX ();

Tune (flute); // NOT The Desired Behavior!

}

} ///: ~

Here is another concept of another easy confusion. In InstrumentX, the Play () method uses an int (integer) value, its identifier is NOTEX. That is, even if Notex is a class name, you can use it as an identifier, the compiler will not report an error. But in WindX, play () uses a NOTEX handle, which has an identifier n. Even if we use "PLAYX NOTEX", the compiler will not report an error. In this way, it looks like a programmer intends to override the play () function, but the type definition of the method is slightly indispensable. However, the compiler is assumed at this time that the programmer is intended to "overload" instead of "override". Please carefully experience the difference between these two terms. "Overload" means a variety of meanings in different places; and "coverage" means that it is only one meaning anytime, anywhere, but the original meaning is completely replaced by later meaning. Please note that if you comply with the standard Java naming specification, the argument identifier should be NOTEX, which can be separated from the class name. In Tune, "InstrumentX I" will issue a Play () message while using a NOTEX member as an argument (Middle_C). Since Notex contains int definitions, the INT version of the overloaded play () method will be called. At the same time, since it has not been "overwritten", the basic class version is used.

The output is:

InstrumentX.play ()

7.4 Abstract and Method

In all of our instruments (Instrument), the methods within the basic type of Instrument are definitely a "pseudo" method. If you want to call these methods, there will be an error. That is because the intent of Instrument is to create a general interface for all classes derived.

The reason why this general interface is to be established is that it can make different representations for different subtypes. It has established a basic form for us to define something "universal" in all derivatives. In order to explain this concept, another method is to refer to INSTRUMENT as "abstract basic class" (referred to as "abstract class"). If you want to process a series of classes through this general interface, you need to create an abstract class. For all derivatives that match the signature of the basic class declaration, they can be called by dynamic binding mechanisms (however, as indicated in the previous section, if the method name is the same as the base class, but the argument or parameters are different. Overloading occurs, then it is not what we are willing).

If there is an abstract class like INSTRUMENT, the object of that class is almost certainly no matter. In other words, the role of Instrument is just an expression interface, rather than expressing some specific implementation details. So create an Instrument object is meaningless, and we usually do it from doing users. To achieve this purpose, all methods in Instrument will display an error message. However, this will delay the information to the running period and require a thorough, reliable test in the user. In any case, the best way is to capture problems during compilation.

In response to this problem, Java specializes in a mechanism called "abstract method." It belongs to an incomplete method, which contains only a statement, there is no method main body. The following is the grammar used when the abstract method declares:

Abstract void x ();

A class containing an abstract method is called "abstract class". If a class contains one or more abstract methods, the class must be specified as Abstract. Otherwise, the compiler will report a mistake message to us.

If an abstract class is incomplete, once someone tries to generate an object of that class, what action will the compiler take? Since it is unable to create an object belonging to an abstract class, a error prompt is obtained from the compiler. In this way, the compiler guarantees "pureness" of abstract classes, we don't have to worry about it. If you inherit from an abstract class, and you want to generate an object of a new type, you must provide a method to define all abstract methods in the underlying class. If you don't do this (you can choose not to do it), the derivative class will also be abstract, and the compiler will force us to use the Abstract keyword flag of "abstract" essentity.

Even if there is no ABSTRACT method, a class can be declared into "abstract classes". If a class does not have to have any abstract method, and we want to ban all instances of the class, this ability will look very useful.

The INSTRUMENT class can easily convert into an abstract class. Only some of the methods will become an abstract method, because after a class abstraction, it does not force us to turn all the methods simultaneously into abstraction. Here is what it looks like:

Below is the example of "Orchestra" instrument we have modified, with abstract classes and methods:

//: Music4.java

// Abstract Classes and Methods

Import java.util. *;

Abstract class instruction4 {

INT I; // Storage Allocated for Each

Public Abstract void play ();

Public string what () {

Return "Instrument4";

}

Public abstract void adjounce ();

}

Class wind4 extends instruction4 {

Public void play () {

System.out.println ("Wind4.Play ()");

}

Public String What () {Return "Wind4";

Public void adjounce () {}

}

Class percussion4 extends instructionent4 {

Public void play () {

System.out.println ("Percussion4.Play ()");

}

Public string what () {return "percussion4";

Public void adjounce () {}

}

Class stringed4 extends instructionent4 {

Public void play () {

System.out.println ("Stringed4.Play ()");

}

Public string what () {return "stringed4";

Public void adjounce () {}

}

Class Brass4 Extends WIND4 {

Public void play () {

System.out.println ("BRASS4.PLAY ()");

}

Public void adjounce () {

System.out.println ("BRASS4.ADJUST ()");

}

}

Class Woodwind4 Extends Wind4 {

Public void play () {

System.out.println ("Woodwind4.Play ()");

}

PUBLIC STRING What () {Return "Woodwind4";}}

Public class music4 {

// Doesn't Care About Type, So New Types

// Added to the System Still Work Right:

Static void tune (Instrument4 i) {

// ...

I.Play ();

}

Static void TuneAll (Instrument4 [] e) {

For (int i = 0; i

Tune (e [i]);

}

Public static void main (String [] args) {

Instrument4 [] orchestra = new instruction4 [5];

INT i = 0;

// Upcasting during addition to the arch:

Orchestra [i ] = new window4 ();

Orchestra [i ] = new percussion4 ();

Orchestra [i ] = new stringed4 ();

Orchestra [i ] = new brass4 ();

Orchestra [i ] = new woodwind4 ();

TuneAll (Orchestra);

}

} ///: ~

It can be seen that there is no change in addition to the basic class.

Creating abstract classes and methods are sometimes useful to us because they make a clear fact that the abstraction becomes obvious, and it can clearly tell the user and the compiler to use it.

7.5 interface

"Interface" keyword makes abstract concepts deeply into a layer. We can imagine it as a "pure" abstraction class. It allows the creator to specify a basic form of a class: method name, argument list, and return type, but does not specify the method main body. The interface also includes data members of the basic data type, but they are default to static and final. The interface only provides a form without providing details.

The interface describes yourself: "For all the classes of my class, it should look like I now." Therefore, all code to use a particular interface know what method may be called for that interface. This is the all meaning of the interface. So we often use interfaces to build a "agreement" between classes and classes. Some object-oriented programming languages ​​use a keyword called "protocol" (protocol), which is the same as the interface.

To create an interface, use the interface keyword without using the Class. Similar to the class, we can add a public keyword in front of the interface keyword (but only the interface is defined within one file of the same name); or omitting it, create a "friendly" state.

In order to generate a class that is consistent with a specific interface (or a set of interfaces), use the imports keyword. What we want to express is that the interface looks like that look, here is the specific work details. " In addition to these, our other work is very similar to inheritance. The following is a schematic of the instrument example:

After the interface is implemented, a normal class is obtained, which can be extended in a standard manner.

Determine the declaration of the method in an interface to be explicitly defined as "public". But even if it is not clear, they will default to public. So when implementing an interface, the method from the interface must be defined as a public. Otherwise, they will default to "friendly", and will limit our access to a method during inheritance - Java compiler does not allow us to do. In the modified version of the Instrument example, you can see this clearly. Note Each method in the interface is strictly a statement, which is the only allowed compiler. In addition, there is no method in Instrument5 being declared as public, but they all automatically get the public attribute. As follows:

//: Music5.java

// Interfaces

Import java.util. *;

Interface instructionent5 {

// Compile-Time Constant:

INT i = 5; // static & final

// Cannot Have Method Definitions:

Void Play (); // Automatically PUBLIC

String what ();

Void adjounce ();

}

Class wind5 imports inStrument5 {

Public void play () {

System.out.println ("Wind5.Play ()");

}

Public string what () {return "wind5";

Public void adjounce () {}

}

Class Percussion5 Implements INSTRUMENT5 {

Public void play () {

System.out.println ("Percussion5.Play ()");

}

Public string what () {return "percussion5";

Public void adjounce () {}

}

Class stringed5 imports instruction5 {

Public void play () {

System.out.println ("Stringed5.Play ()");

}

Public string what () {return "stringed5";

Public void adjounce () {}

}

Class Brass5 extends wind5 {

Public void play () {

System.out.println ("BRASS5.PLAY ()");

}

Public void adjounce () {

System.out.println ("BRASS5.ADJUST ()");

}

}

Class Woodwind5 Extends WIND5 {

Public void play () {

System.out.println ("Woodwind5.Play ()");

}

Public string what () {return "woodwind5";

}

Public class music5 {

// Doesn't Care About Type, So New Types

// Added to the System Still Work Right:

Static void tune (Instrument5 i) {

// ...

I.Play ();

}

Static void TuneAll (Instrument5 [] e) {

For (int i = 0; i

}

Public static void main (String [] args) {

Instrument5 [] orchestra = new instruction5 [5];

INT i = 0;

// Upcasting during addition to the arch:

Orchestra [i ] = new window5 ();

Orchestra [i ] = new percussion5 ();

Orchestra [i ] = new stringed5 ();

Orchestra [i ] = new brass5 ();

Orchestra [i ] = new woodwind5 ();

TuneAll (Orchestra);

}

} ///: ~

The residual part of the code works in the same way. We can use the "normal" class named Instrument5, a "abstract" class named Instrument5, or an "interface" named Instrument5. All behaviors are the same. In fact, we can find that there is no evidence in the tune () method to show that INSTRUMENT5 is a "ordinary" class, "Abstract" class is also an "interface." This is done intention: each method makes programmers to perform different controls for the creation of objects.

7.5.1 Java's "Multiple Inheritance"

The interface is just a form of "more pure" than abstract classes. Its use does not stop those. Since the interface does not have a specific implementation details - that is, there is no association with the storage space and "interface" - so there is no way to prevent multiple interfaces from merged together. This is crucial because we often need to express this: "X belongs to A, which is also from B, which belongs to C". In C , the actions combined with multiple classes are called "multiple inheritance", and the operation is more inconvenient, because each class may have a set of implementation details. In Java, we can take the same actions, but only one of the classes have specific implementation details. So when you merge multiple interfaces, the problem with C will not repeat them in Java. As follows:

In a derivative class, we do not have to have an abstract or specific (there is no abstract method) of the base class. If you really want to inherit from a non-interface, you can only inherit from one. All of the remaining basic elements must be "interface". We placed all interfaces behind the Implements keyword and divide them with commas. Multiple interfaces can be used as needed, and each interface will become a separate type, which can be traced back. The following example shows a "specific" class with several interfaces, which eventually generates a new class:

//: Adventure.java

// Multiple Interfaces

Import java.util. *;

Interface canfight {

Void fight ();

}

Interface canswim {

Void Swim ();

}

Interface canfly {

Void fly ();

}

Class actioncharacter {

Public void fast () {}

}

Class Hero Extends ActionCharacter

Implements canfight, canswim, canfly {

Public void swim () {}

Public void fly () {}

}

Public class adverture {

Static void T (canfight x) {x.fight ();} static void u (canswim x) {x.swim ();

Static void v (canfly x) {x.fly ();}

Static Void W (Actioncharacter X) {x.fight ();

Public static void main (String [] args) {

Hero i = new Hero ();

T (i); // Treat it as a canfight

u (i); // Treat it as a canswim

v (i); // Treat IT as a canfly

W (i); // Treat it as an actioncharacter

}

} ///: ~

As can be seen, HERO combines the specific class Actioncharacter with the interface Canfight, Canswim, and Canfly. When merging a specific class with the interface according to this form, the specific class must appear first, and then the interface (otherwise the compiler will report an error).

Note that the signature of the front () is the same in the Canfight interface and the ActionCharacter class, and there is no specific definition for Fight () in Hero. The rules of the interface are: We can inherit from it (you will see later), but this will be another interface. If you want to create an object of a new type, it must be a class that has been provided all definitions. Although Hero does not explicitly provide a definition, it is defined as ActionCharacter, so this definition will be provided, we can create Hero objects.

In class Adventure, we can see a total of four ways, which use different interfaces and specific classes as their own arguments. Once you have created an HERO object, it can be passed to any of these methods. This means that they are traced back to each interface. Since the interface is designed with Java, there will be no problem in doing this, and the programmer does not have to pay any special attention to this.

Note that the above example has revealed the most critical role of the interface and the most important reason for the interface: can be traced to multiple basic classes. The second reason for using the interface is the same as the use of abstract basic classes: preventing a customer programmer from making this type of object, and stipulating it is just an interface. This brings a problem: What should I use an interface or an abstract class? If you use an interface, we can get the benefits of abstract classes and interfaces. So if you want to create the basic class that doesn't have any method or member variables, then you are willing to use the interface, don't choose an abstract class. In fact, if you know something in advance, the first choice is to turn it into an interface. The abstract class should only be considered if the method definition or member variable must be used.

7.5.2 Extended interface by inheritance

With inheritance technology, it is convenient to add a new method declaration for an interface, and several interfaces can be combined into a new interface. In both cases, the final result is a new interface, as shown in the following example:

//: HorrorShow.java

// extending an interface with inheritance

Interface monster {

Void meNace ();

}

Interface Dangerouster Extends Monster {

Void destroy ();

}

Interface lethal {

Void kill ();

}

Class Dragonzilla Implements Dangerousmonster {

Public void menace () {} public void destroy () {}

}

Interface vampire

Extends Dangerouster, Lethal {

Void DrinkBlood ();

}

Class HorrorShow {

Static void u (monster b) {b.menace ();

Static void v (DangerousMonster D) {

D.menace ();

d.destroy ();

}

Public static void main (String [] args) {

Dragonzilla if2 = new Dragonzilla ();

u (IF2);

v (IF2);

}

} ///: ~

DangerousMonster is a simple extension to Monster and eventually generates a new interface. This is achieved in Dragonzilla.

Vampire's syntax can only be used when inheriting the interface. Typically, we can only apply an Extends keyword for a single class. However, since the interface may consist of multiple other interfaces, Extends may reference multiple foundations when building a new interface. As everyone saw, the name of the interface simply separated by commas.

7.5.3 constant group

Since all fields that are placed have a STATIC and FINAL properties, the interface is a good tool for grouped constant values, which has very similar effects with C or C ENUM. As shown in the following example:

//: Months.java

// Using Interfaces to Create Groups of Constants

Package C07;

Public interface months {

int

January = 1, February = 2, march = 3,

April = 4, May = 5, June = 6, July = 7,

August = 8, september = 9, october = 10,

November = 11, decEmber = 12;

} ///: ~

Note that the Static Final basic data type (i.e., the compile period constant) with a fixed identifier is all in terms of uppercase letters (multiple words in the single identifier).

The fields in the interface automatically have a public property, so it is not necessary to specify.

Now, by importing C07. * Or c07.months, we can use the ordinary use of the package from the package - just like other of the other packets. In addition, values ​​can also be referenced in a representation of the expression similar to MONTHS. JANUARY. Of course, we are just an int, so you don't have additional types of security as C enum. But compared to the number of forced numbers (hard coding) into their own procedure, this (common) technology is undoubtedly a huge progress. We usually refer to the "hardcod" number of "magic numbers", and the code it produces is very difficult to maintain.

If you don't want to give up additional type of security, you can build a class below (notes 1):

//: Month2.java

// a more robust enumeration system

Package C07;

Public final class month2 {

PRIVATE STRING NAME;

Private Month2 (String nm) {name = nm;}

Public string toString () {return name;} public final static month2

Jan = New Month2 ("January"),

Feb = New Month2 ("February"),

Mar = New Month2 ("march"),

Apr = New Month2 ("April"),

May = New Month2 ("May"),

Jun = New Month2 ("June"),

Jul = New Month2 ("July"),

Aug = New Month2 ("August"),

SEP = New Month2 ("September"),

Oct = New Month2 ("October"),

NOV = New Month2 ("November"),

Dec = New Month2 ("december");

Public final static month2 [] month = {

Jan, Jan, Feb, Mar, APR, May, Jun,

Jul, Aug, SEP, OCT, NOV, DEC

}

Public static void main (String [] args) {

Month2 m = month2.jan;

System.out.println (m);

M = MONTH2.MONTH [12];

System.out.println (m);

System.out.println (m == MONTH2.DEC);

System.out.println (m.equals (month2.dec));

}

} ///: ~

1: It is the inspiration of Rich Hoffarth triggered me to write the program like this.

This class is called MONTH2 because there is already a month in the standard Java reservoir. It is a Final class and contains a private builder, so no one can inherit from it, or an instance of it. The only instance is those Final Static objects, which are created inside the class itself, including: Jan, Feb, Mar, etc. These objects are also used in the month array, and the latter allows us to pick up the month by digital, not by name (note that the array provides an extra JAN, making the offset increased by 1, which also makes the december indeed be December) . In Main (), we can notice the type of security: m is a MONTH2 object, so it can only be assigned to Month2. In the previous MONTHS.JAVA example, only the int value is provided, so I originally want to use an int variable that can actually get an integer value, which may not be very safe.

The methods described here also allow us to exchange == or equals (), just like the main () tail display.

7.5.4 Initializing the fields in the interface

The fields defined in the interface automatically have STATIC and FINAL properties. They can't be "blank Final", but can initialize a very number expression. E.g:

//: randvals.java

// Initializing Interface FIELDS with INTERFACE

// non-constant initializers

Import java.util. *;

Public interface randvals {int RINT = (int) (Math.random () * 10);

Long rlong = (long) (Math.random () * 10);

FLOAT RFLOAT = (FLOAT) (Math.random () * 10);

Double rdouble = math.random () * 10;

} ///: ~

Since the field is static, they will get initialization after the first loading class, and before the first time you visit any field. Here is a simple test:

//: Testrandvals.java

Public class testrandvals {

Public static void main (String [] args) {

System.out.println (randvals.rint);

System.out.println (randvals.rlong);

System.out.println (randvals.rfloat);

System.out.println (randvals.rdouble);

}

} ///: ~

Of course, the field is not part of the interface, but is saved in the Static storage area of ​​that interface.

7.6 internal class

In Java 1.1, you can place a class definition into another class definition. This is called "internal class". Internal classes are very useful for us because it can groups those who logically interconnect them and control a "visibility" class in another class. However, we must recognize that there is an fundamental difference between the internal class and the "synthetic" method previously described.

Typically, the need for internal classes is not particularly obvious, at least not immediately feel that the internal class needs to be used. At the end of this chapter, you will find a special example after all the syntax of the internal class. By it should clearly recognize the benefits of the internal class.

The process of creating an internal class is dull: Place the class definition into a class inside the class for encapsulation (if you have trouble, please refer to Chapter 3 3.1.2 "Assignment":

//: Parcel1.java

// CREATING Inner Classes

Package c07.parcel1;

Public class paarcel1 {

Class contents {

Private INT i = 11;

Public int value () {Return I;}

}

Class destination {

PRIVATE STRING LABEL;

Destination (String WHERETO) {

Label = WHERETO;

}

String readlabel () {Return Label;

}

// USING Inner Classes Looks Just Like

// using any other class, within parcel1:

Public Void Ship (String Dest) {

CONTENTS C = New Contents ();

Destination D = New Destination (DEST);

}

Public static void main (String [] args) {

PARCEL1 P = New Parcel1 ();

P.SHIP ("Tanzania");

}

} ///: ~

If you use it within Ship (), the use of the internal class does not look at any other classes. Here, the only distinction is that its name is nesting in PARCEL1. But soon, I will know that this is not the only difference.

One more typical situation is that an external class has a special method that returns a handle pointing to an internal class. Just like this: //: pacel2.java

// Returning a Handle to an inner class

Package c07.parcel2;

Public class parcel2 {

Class contents {

Private INT i = 11;

Public int value () {Return I;}

}

Class destination {

PRIVATE STRING LABEL;

Destination (String WHERETO) {

Label = WHERETO;

}

String readlabel () {Return Label;

}

Public Destination to (String s) {

Return New Destination (s);

}

Public contents last () {

Return new contents ();

}

Public Void Ship (String Dest) {

CONTENTS C = CONT ();

Destination D = To (DEST);

}

Public static void main (String [] args) {

PARCEL2 P = New Parcel2 ();

P.SHIP ("Tanzania");

PARCEL2 Q = New Parcel2 ();

// defining handles to inner classes:

PARCEL2.CONTENTS C = q.cont ();

PARCEL2.DESTINATION D = Q.to ("Borneo");

}

} ///: ~

If you want to generate an internal class in any place within the outside of the external class, it is necessary to set the type of object to "external class name. Internal class name", as shown in main ().

7.6.1 Internal Class and Trace

So far, the internal class looks still nothing special. After all, use it to hide a small question. Java already has a very good hidden mechanism - only allows classes to be "friendly" (visible only in a package), not to create an internal class.

However, when we are ready to stand up to a basic class (especially to an interface), the internal class begins to play its key role (from the object to be implemented to generate an interface handle with the same shape to a base class. effect). This is because the internal class can then enter the invisible or unavailable state - all of them. So we can hide the implementation details very conveniently. All of our rewards is a basic class or interface handle, and it is even possible to know accurate type. Just like this:

//: Parcel3.java

// Returning a Handle to an inner class

Package c07.parcel3;

Abstract class contents {

Abstract public int value ();

}

Interface destination {

String readlabel ();

}

Public class paarcel3 {

Private class pcontents extends contents {

Private INT i = 11;

Public int value () {Return I;}

}

Protected Class PDestination

Implements destination {

Private string label; private pdestination (string whereto) {

Label = WHERETO;

}

Public string readlabel () {return Label;}

}

Public Destination DEST (STRING S) {

Return new pdestination (s);

}

Public contents last () {

Return new PContents ();

}

}

Class test {

Public static void main (String [] args) {

PARCEL3 P = New Parcel3 ();

CONTENTS C = P.cont ();

Destination d = p.dest ("tanzania");

// Illegal - Can't Access Private Class:

//! Pacel3.pcontents c = p.new pcontents ();

}

} ///: ~

Now, Contents and Destination representatives can be used by the client programmer (remember that all all members will turn all the members into public properties). For convenience, they are placed in a single file, but the original Contents and Destination are mutually PUBLIC in their own files.

In PARCEL3, some new things have been added: Internal class PContents is set to Private, so anything else cannot access it in addition to Parcel3. PDESTINA is set to protected, so in addition to the class in PARCEL3, PARCEL3 packs (because protected has given access to the package; that is, protected is also "friendly"), and other things outside of Parcel3, else all things You cannot access PDestination. This means that the customer programmer will be restricted to the understanding and access to these members. In fact, we can't even discontinitate to a private internal class (or a protected internal class unless you are a successor), because we can't access the name, just like it is seen in Classtest. Therefore, using the Private internal class, class designers can completely ban others depend on type encoding, and can completely hide the specific implementation details. In addition, from the perspective of the customer programmer, the scope of an interface is meaningless because they cannot access any additional methods that are not belonging to the public interface class. In this way, the Java compiler also has the opportunity to generate a higher efficiency code.

Ordinary (non-internal) classes are not available to private or protected - only public or "friendly" is allowed.

Note that Contents does not have to be an abstract class. You can also use a common class here, but this most typical starting point is still an "interface."

7.6.2 Internal classes in the method and scope

At this point, we have basically understood the typical use of the internal class. For those who involve internal classes, usually express "simple" internal classes, very simple, easy to understand. However, the design of the internal class is very comprehensive and inevitably encounters their other large usage - false, if we create internal classes in one way or even an arbitrary scope. There are two reasons that prompted us to do this:

(1) As mentioned earlier, we are ready to implement some form of interface, so that you can create and return a handle.

(2) To solve a complex problem and want to create a class to assist yourself. At the same time, I don't want to disclose it. In the following example, you will modify the previous code for use:

(1) Class defined within a method

(2) Class defined in a scope of the method

(3) An anonymous class for implementing an interface

(4) An anonymous class that extends a class with non-default builder

(5) An anonymous class for performing field initialization

(6) An anonymous class, constructs by instance initialization (anonymous internal classes cannot have builder)

All of this happened in the inNerscopes package. First, universal interfaces from the aforementioned code will get definitions in their own files, so they can be used in all examples:

//: destination.java

Package c07.innerscopes;

Interface destination {

String readlabel ();

} ///: ~

Since we already think that Contents may be an abstract class, you can take the following more natural form, just like an interface:

//: Contents.java

Package c07.innerscopes;

Interface contents {

Int value ();

} ///: ~

Although it is a common class that contains specific implementation details, Wrapping also uses a general "interface" of all of its derivatives:

//: wrapping.java

Package c07.innerscopes;

Public class wraping {

Private INT i;

Public wrapping (int x) {i = x;}

Public int value () {Return I;}

} ///: ~

In the above code, we noticed that wrapping has a builder that requires the use of an argument, which makes the situation more interesting.

The first example shows how to create a complete class in a scope of a method (rather than another class):

//: pacel4.java

// Nesting a class within a method

Package c07.innerscopes;

Public class paarcel4 {

Public Destination DEST (STRING S) {

Class PDestination

Implements destination {

PRIVATE STRING LABEL;

Private pdestination (string whereto) {

Label = WHERETO;

}

Public string readlabel () {return Label;}

}

Return new pdestination (s);

}

Public static void main (String [] args) {

PARCEL4 P = New Parcel4 ();

Destination d = p.dest ("tanzania");

}

} ///: ~

The PDestination class belongs to a part of DEST () instead of part of PARCEL4 (pay attention to an internal class PDESTINATION that can be used inside each class in the same directory, which does not have naming conflicts). Therefore, PDESTINATION cannot be accessed from DEST (). Note that the tracery occurring in the return statement - except for a handle pointing to the basic class Destination, there is no thing outside the DEST () boundary. Of course, you can't put it in DEST () because the name of the PDESTINATION is considered to be DEST (), which is not a valid object. The following example shows how to nested an internal class in any scope:

//: PARCEL5.JAVA

// Nesting a class coven a scope

Package c07.innerscopes;

Public class paarcel5 {

Private void internaltracking (boolean b) {

IF (b) {

Class trackingslip {

Private string id;

TRACKINGSLIP (String S) {

ID = S;

}

String getslip () {return id;}

}

TRACKINGSLIP TS = New Trackingslip ("SLIP");

String s = ts.getslip ();

}

// can't use it here! Out of scope:

//! TRACKINGSLIP TS = New TRACKINGSLIP ("X");

}

Public void track () {interfaceTracking (TRUE);

Public static void main (String [] args) {

PARCEL5 P = New paracel5 ();

p.TRACK ();

}

} ///: ~

The Trackingslip class is nesting in the scope of an IF statement. This doesn't mean that classes are creative - it will compile with all other things. However, it is not available outside of the action domain that defines it. In addition to these, it doesn't seem to differ from a common class.

The following example looks a bit weird:

//: Parcel6.java

// a Method That Returns an Anonymous Inner Class

Package c07.innerscopes;

Public class paarcel6 {

Public contents last () {

Return new contents () {

Private INT i = 11;

Public int value () {Return I;}

}; // SEMICOLON REQUIRED IN THIS CASE

}

Public static void main (String [] args) {

PARCEL6 P = New Parcel6 ();

CONTENTS C = P.cont ();

}

} ///: ~

The CONT () method simultaneously combines the creation code of the return value, and the class used to represent the return value. In addition, this class is anonymous - it has no name. And it seems that it seems more to be more mind, we are ready to create a Contents object:

Return New Contents ()

But after this, we said: "Wait until the first, let me first play in a class definition":

Return New Contents () {Private INT i = 11;

Public int value () {Return I;}

}

This strange syntax should express the meaning: "Create an object from the anonymous class derived from Contents." The handle returned by the New Expression will be tracered into a Contents handle. The syntax of an anonymous internal class is actually to express:

Class mycontents extends contents {

Private INT i = 11;

Public int value () {Return I;}

}

Return new mycontents ();

In an anonymous internal class, Contents is created with a default builder. The following code shows what the basic class needs to be made when the builder contains the argument:

//: Parcel7.java

// an anonymous inner class thing calls the

// Base-Class Constructionor

Package c07.innerscopes;

Public class paarcel7 {

Public wrapping wrap (int x) {

// base constructor Call:

Return new wrapping (x) {

Public int value () {

Return super.value () * 47;

}

}; // SEMICOLON Required

}

Public static void main (String [] args) {

PARCEL7 P = New Parcel7 ();

Wrapping w = p.wrap (10);

}

} ///: ~

That is, we simply transmit appropriate self-variables to the base class builder, which is manifested in "New Wrapping (X)". Anonymous classes cannot have a builder, which is different from the general practices when you call Super ().

In the two examples described above, the semicolon does not mark the end of the primary body (and C ). Instead, it marks the end of the expression that contains anonymous class. Therefore, it is completely equivalent to using a semicolon anywhere.

If you want to initialize some form of an object of anonymous internal classes, what happens? Since it is anonymous, there is no name to the builder, so we can't have a builder. However, we can initialize when defining your own fields:

//: pacel8.java

// an anonymous inner class thing performs

// Initialization. A Briefer Version

// of paracel5.java.

Package c07.innerscopes;

Public class paarcel8 {

// argument must be firm to use inside

// Anonymous Inner Class:

Public Destination DEST (FINAL STRING DEST) {

Return new destination () {

PRIVATE STRING LABEL = DEST;

Public string readlabel () {return Label;}

}

}

Public static void main (String [] args) {

PARCEL8 P = New Parcel8 ();

Destination d = p.dest ("tanzania");

}

} ///: ~

If you try to define an anonymous internal class, you want to use an object externally defined in an anonymous internal class, the compiler requires an external object to be final properties. This is why we set DEST ()'s own arguments to Final. If you forget this, you will get a compile time error prompt. As long as you just want to assign a field, the above method is sure. But if you need to take some action similar to the builder, what should I do? Initialization through the instance of Java 1.1, we can effectively create a builder for an anonymous internal class:

//: Parcel9.java

// use "instance initialization" to Perform

// Construction on an Anonymous Inner Class

Package c07.innerscopes;

Public class paarcel9 {

Public Destination

DEST (FINAL STRING DEST, FINAL FLOAT Price) {

Return new destination () {

PRIVATE INT COST;

// Instance Initialization for Each Object:

{

Cost = math.round;

IF (Cost> 100)

System.out.println ("over budget!");

}

PRIVATE STRING LABEL = DEST;

Public string readlabel () {return Label;}

}

}

Public static void main (String [] args) {

PARCEL9 P = New Parcel9 ();

Destination D = P.DEST ("Tanzania", 101.395F);

}

} ///: ~

In the instance initialization module, we can see that the code cannot be executed as part of the class initialization module (ie, if statement). So actually, an instance initialization module is an anonymous internal class builder. Of course, its function is limited; we cannot overload the instance initialization module, so it can only have one of these builders.

7.6.3 Link to the external class

So far, the internal class we have seen is just a name hidden and code organization. Although these functions are very useful, it seems not particularly eye-catching. However, we also ignored another important fact. When you create your own internal class, the object of that class has a link to the package object (these object packages or generated internal classes). So they can access members of the package - no eligibility eligibility. In addition, the internal class has access to all elements of the package (annotation 2). The following example explains this question:

//: Sequence.java

// Holds a Sequence of Objects

Interface selector {

Boolean end ();

Object current ();

Void next ();

}

PUBLIC CLASS Sequence {

Private object [] O;

Private Int next = 0;

PUBLIC SEQUENCE (INT size) {

o = new Object [size];

}

Public Void Add (Object X) {

IF (Next

o [next] = x;

NEXT ;

}

}

Private class sselector imports class selector {int i = 0;

Public boolean end () {

Return I == O.LENGTH;

}

Public Object Current () {

Return O [i];

}

Public void next () {

IF (i

}

}

Public selector getselector () {

Return new sselector ();

}

Public static void main (String [] args) {

Sequence s = new sequence (10);

For (int i = 0; i <10; i )

S.ADD (Integer.Tostring (i));

Selector SL = S.GetSelector ();

While (! Sl.end ()) {

System.out.println (String) sl.current ());

Sl.next ();

}

}

} ///: ~

2: This is quite different from C "nested" design, which is just a simple name hidden mechanism. In C , there is no link to a package object, and there is no default access.

Where SEQUENCE is just a size fixed object array, there is a class to encapsulate it inside. We call Add () to add a new object to the end of Sequence (if there is also a place). In order to get every object in Sequence, use a interface called Selector, which enables us to know if he is at the end (end ()), can watch the current object (Current () object), and movable to Sequence The next object (Next () Object). Since the Selector is an interface, many other classes can be implemented in their own way, and many methods can use the interface as an argument to create a general code.

Here, the SSELector is a private class that provides the Selector function. In Main (), you can see the Sequence creation process, behind it is the addition of a series of string objects. Subsequently, a Selector is generated by a call to getSelector (). And use it to move in Sequence while selecting each item.

From the surface, SSELECTOR seems to be just another internal class. But don't be confused by the surface phenomenon. Note that end (), current () and next () are observed, each of which references O. o is a handle that is not part of the SSELector, but a private field in the package class. However, internal classes can access methods and fields from the package class, just like they have already owned them. This feature is very convenient for us, just like it is seen in the example above.

Therefore, we now know an internal class to access members of the package class. How is this implementation? The internal class must have a reference to a particular object of the package class, and the role of the package class is to create this internal class. Subsequently, when we reference a member of the package, you will use the (hidden) reference to select that member. Fortunately, the compiler will help us take care of all of these details. But we can now understand an object of the internal class can only be created in combination with an object of the package class. During this creation process, the handle of the package class object is required to initialize. If you cannot access that handle, the compiler will report an error. When all of these operations, most of the time is not required to intervene. 7.6.4 Static Internal Class

To correctly understand the meaning of STATIC in the internal class, you must remember that the internal class object default holds a handle of an object that creates it. However, if we say an internal class is static, this statement is not established. Static internal classes mean:

(1) In order to create an object of a Static internal class, we do not need an external class object.

(2) An external class object cannot be accessed from an object of the Static internal class.

But there are some restrictions: Since the Static member can only be in an external level of a class, the internal class cannot own STATIC data or STATIC internal classes.

If you create an object of an internal class, you can set all things to static. In order to work normally, the internal class must be set to Static. As follows:

//: pacel10.java

// static inner classes

Package c07.parcel10;

Abstract class contents {

Abstract public int value ();

}

Interface destination {

String readlabel ();

}

Public class paarcel10 {

Private static class pcontents

Extends contents {

Private INT i = 11;

Public int value () {Return I;}

}

Protected Static Class PDESTINATION

Implements destination {

PRIVATE STRING LABEL;

Private pdestination (string whereto) {

Label = WHERETO;

}

Public string readlabel () {return Label;}

}

Public Static Destination DEST (STRING S) {

Return new pdestination (s);

}

Public static contents last () {

Return new PContents ();

}

Public static void main (String [] args) {

CONTENTS C = CONT ();

Destination D = DEST ("Tanzania");

}

} ///: ~

In Main (), we don't need PARCEL10 objects; in contrast, we use regular syntax to select a Static member to call the way the handle returns the handle of Contents and Destination.

Typically, we don't set any code in an interface, but the STATIC internal class can be part of the interface. Since the class is "static", it does not violate the rules of the interface - the inner class is located only within the interface of the interface:

//: IINTERFACE.JAVA

// static inner classes inside interface {

Static class inner {

INT I, J, K;

Public inner () {}

Void f () {}

}

} ///: ~

Earlier in this book, I suggest that you set a main () in each class, use it as the test bed for that class. A disadvantage of this is that there are too many additional code. If you don't want this, you can consider your test code with a Static internal class. As follows:

//: Testbed.java

// Putting Test Code in a static inner class

Class testbed {

Testbed () {}

Void f () {system.out.println ("f ()");}

Public static class tester {

Public static void main (String [] args) {

Testbed T = New Testbed ();

T.f ();

}

}

} ///: ~

This generates a separate, class called Testbed $ Tester (for running, please use the Java Testbed $ Tester "command). This class can be used to test, but do not need to include it in your final release.

7.6.5 Reference External Class Object

If you want to generate the handle of an external class, you must use a bit and a this to name an external class. For example, in the Sequence.Sselector class, all of its methods can generate the stored handle of external class sequence, and the method is in the form of sequence.this. The result obtained the handle will automatically have the correct type (this will check and verify during the compilation, so there will be no cost period.

Sometimes, we want to tell other objects to create an object of an internal class. To achieve this, a handle pointing to other external classes must be provided in the New expression, just like this:

//: pacel11.java

// CREATING Inner Classes

Package c07.parcel11;

Public class paarcel11 {

Class contents {

Private INT i = 11;

Public int value () {Return I;}

}

Class destination {

PRIVATE STRING LABEL;

Destination (String WHERETO) {

Label = WHERETO;

}

String readlabel () {Return Label;

}

Public static void main (String [] args) {

PARCEL11 P = New Parcel11 ();

// Must use instance of outr class

// TO CREATE AN Instances of the Inner Class:

PARCEL11.CONTENTS C = P.NEW Contents ();

PARCEL11.DESTINATION D =

P.New Destination ("Tanzania");

}

} ///: ~

In order to create an object of the internal class, you can't like everyone may guess - use the same form and reference the external class name PARCEL11. At this point, an object of an external class must be utilized by an external class:

PARCEL11.CONTENTS C = P.NEW Contents ();

Therefore, an object of an internal class is not possible unless an object has an external class. This is because the internal class object has been connected to the object "silently" of the object that creates its external class. However, if a STATIC internal class is generated, it is not necessary to point to a handle of the external class object. 7.6.6 Inheriting from internal class

Since the internal class builder must contact a handle of the encapsulation class, the situation will be slightly complex when inheriting from an internal class. The problem here is that the "secret" handle of the package must be initialized, and no longer a default object can be connected in the derivative class. The way to solve this problem is to adopt a special grammar, clearly establish this association:

//: Inheritinner.java

// inheriting an inner class

Class withinner {

Class inner {}

}

Public Class Inheritinner

Extends withinner.inner {

//! Inheritinner ()} // Won't compile

Inheritinner (WITHINNER WI) {

Wi.super ();

}

Public static void main (String [] args) {

WITHINNER WI = New WITHINNER ();

Inheritinner II = New Inheritinner (Wi);

}

} ///: ~

It can be seen from it, inheritinner extension only, does not extend the external class. But when you need to create a builder, the default object has no meaning, we can't just pass a handle of the package object. In addition, the following syntax must be used in the builder:

EnclosingClasshandle.super ();

It provides the necessary handles so that the program is correctly compiled.

7.6.7 Can the internal class be overwritten?

What happens when you create an internal class and then inherit from the package class and redefine the internal class? In other words, are we possible to cover an internal class? This seems to be a very useful concept, but "override" an internal class - seems to be another method of the external class - this concept does not do anything:

//: Bigegg.java

// an inner class cannot be overriden

// Like a Method

Class egg {

protected class yolk {

Public yolk () {

System.out.println ("Egg.Yolk ()");

}

}

Private yolk y;

Public Egg () {

System.out.Println ("New Egg ()");

Y = new yolk ();

}

}

Public class bigegg extends egg {

Public class yolk {

Public yolk () {

System.out.println ("BiGegg.Yolk ()");

}

}

Public static void main (String [] args) {

New bigegg ();

}

} ///: ~

The default builder is automatically synthesized by the compiler, and the default builder of the base class will be called. Everyone may think that since it is ready to create a BiGeGG, you will use YOLK's "overwrite" version. But the actual situation is not. The output is as follows:

New Egg ()

Egg.yolk ()

This example simply reveals that when we inherit from the outside, there is no additional internal class to continue. However, it is still possible to "clear" from internal class inheritance: //: Bigegg2.java

// Proper Inheritance of an inner class

Class egg2 {

protected class yolk {

Public yolk () {

System.out.println ("egg2.yolk ()");

}

Public void f () {

System.out.println ("egg2.yolk.f ()");

}

}

Private yolk y = new yolk ();

Public Egg2 () {

System.out.println ("New Egg2 ()");

}

Public void insertyolk (yolk yy) {y = yy;

Public void g () {y.f ();

}

Public class bigegg2 extends egg2 {

Public class yolk extends egg2.yolk {

Public yolk () {

System.out.println ("Bigegg2.Yolk ()");

}

Public void f () {

System.out.println ("Bigegg2.yolk.f ()");

}

}

Public BiGegg2 () {INSERTYOLK (New Yolk ());}

Public static void main (String [] args) {

Egg2 E2 = New BiGEGG2 ();

e2.g ();

}

} ///: ~

Now, BiGegg2.yolk explicitly expands Egg2.yolk and covers it. Method INSERTYOLK () allows BiGegg2 to be traced back to the Y handle of EGG2 in a certain YOLK object. So when g () is called Y.f (), F () is overwritten. The output is as follows:

Egg2.yolk ()

New egg2 ()

Egg2.yolk ()

Bigegg2.yolk ()

Bigegg2.yolk.f ()

The second call for Egg2.yolk () is the basic class builder call for the Bigegg2.Yolk builder. transfer

When g (), it can be found that F () covered version is used.

7.6.8 Internal class identifier

Since each class generates a .class file, it is used to accommodate all the information related to how this type of object (this information produces a class class named Class object), so everyone may guess internal classes. The corresponding .class file must be generated to accommodate information related to their Class objects. These files or classes of the names abide by a strict form: first is the name of the package, follow one, follow the name of the internal class. For example, the .class file created by inheritinner.java includes:

Inheritinner.class

WITHINNER $ INNER.CLASS

WITHINNER.CLASS

If the internal class is anonymous, the compiler simply generates numbers and uses them as internal class identifiers. If the internal class is nested in other internal classes, their names are simply appended behind a $ and the external class identifier.

This method of generating internal names is also very simple and intuitive, and it is also very "robust" to accommodate most of the requirements (notes 3). Since it is a standard naming mechanism of Java, the resulting file will automatically have the ability to "unrelated to the platform" (Note that the Java compiler changes the internal class according to the situation so that it can work normally in different platforms). 3: But on the other hand, since "$" is also a metamor of the UNIX housing, sometimes it will be troublesome when it lists .class files. Take this program to be strange to a UNIX-based company - SUN. My guess is that they don't carefully consider this issue, but think we will put all the attention naturally on the source file.

7.6.9 Why use internal classes: control framework

So far, everyone has contacted a lot of grammar and concepts described in the operation of internal classes. But these don't truly explain the reasons for the internal class. Why is Sun to add such a basic language feature in Java 1.1 so troublesome? The answer is what we have to learn here.

A "application framework" refers to one or a series of classes that specifically designed to solve specific types of issues. For the application framework, we can inherit from one or more classes and override some of them. The code we have written in the overlay method is used to customize the regulations provided by those application frameworks to solve their own actual problems. "Control Framework" belongs to a special type of application framework, which is protected by the needs of the event response; the system is mainly used to respond to the event is called "system-driven system". In the application design language, one of the most important issues is "Graphical User Interface" (GUI), which is almost entirely driven by events. As everyone will learn in Chapter 13, Java 1.1 AWT belongs to a control framework that perfectly solves the GUI problem through internal classes.

To understand how internal classes simplify the creation and use of the control framework, you can think that a control framework is to perform them after the event "Ready". Although "ready" means a lot, but in this case, we are based on computer clock. Subsequently, realize that something that needs to be controlled for the control framework is not included in the framework. First, it is a special interface that describes all control events. It can be an abstract class rather than an actual interface. Since the default behavior is based on time control, some implementation details may include:

//: Event.java

// The Common Methods for Any Control Event

Package c07.controller;

Abstract public class evenet {

PRIVATE long evttime;

Public Event (long evenettime) {

EvtTime = eventtime;

}

Public Boolean Ready () {

Return system.currenttimemillis ()> = evttime;

}

Abstract public void action ();

Abstract public string description ();

} ///: ~

When you want Event to run, the builder simply captures time. At the same time, ready () tells us when to run it. Of course, ready () can also be covered in a derivative class and establish an event to other things other than time.

Action () is a method that needs to be called after the event is ready, and Description () provides text information related to the event.

The following file contains the actual control framework for managing and triggering events. The first class is actually just a "assistant" class, and its responsibility is to accommodate the Event object. It can be replaced with any suitable collection. And through the study of Chapter 8, you will know that other collections can simplify our work, don't need we write these extra code: //: Controller.java

// Along with Event, The Generic

// Framework for All Control Systems:

Package c07.controller;

// this is Just A Way to Hold Event Objects.

Class eventset {

Private event [] Events = New Event [100];

Private int index = 0;

Private Int next = 0;

Public Void Add (Event E) {

IF (INDEX> = Events.Length)

Return; // (in Real Life, Throw Exception)

Events [INDEX ] = E;

}

Public evenet getNext () {

Boolean looped = false;

Int start = next;

Do {

Next = (Next 1)% Events.Length;

// see if it has looped to the beginning:

IF (start == next) looped = true;

// if it loops paste Start, The List

// is Empty:

IF ((NEXT == (START 1)% Events.Length)

&& looped)

Return NULL;

} while (events [next] == ​​null);

Return Events [Next];

}

Public void transovecurrent () {

Events [next] = NULL;

}

}

Public class controller {

PRIVATE EVENTSET ES = New Eventset ();

Public void addevent (event c) {es.add (c);}

Public void run () {

Event E;

While ((e = es.getnext ())! = null) {

IF (E.Ready ()) {

e.action ();

System.out.println (E.DESCRIPTION ());

Es.RemoveCurrent ();

}

}

}

} ///: ~

Eventset can accommodate 100 events (if you use a "real" collection from Chapter 8 here, you don't have to worry about its maximum size because it will automatically change the size as appropriate). Index is used here to track the next available space, and next help us find the next event in the list, understand whether he has been looped. This is critical in the call to getNext (), because once run, the Event object will be deleted from the list (using removecurrent ()). So getNext () will encounter "empty cave" when moving forward in the list.

Note that removecurrent () does not just indicate some flags, pointing out that the object is no longer used. Instead, it sets the handle to NULL. This is very important because if the garbage collector finds a handle to still use it, it will not clear the object. If you think that your handle may be hang like this, it is best to set it to NULL, so that the garbage collector can clear them normally. Controller is where practical work. It accommodates your EVENT object with an eventset, and addevent () allows us to join this list to this list. But the most important method is Run (). This method will traverse in EventSet and search for an EVENT object that is ready to run - READY (). For every object it finds ready (), you will call the action () method, print the description (), and then delete the event from the list.

Note that in all the designs so far, we still can't accurately know what "event" is doing. This is the key to the entire design; how do it "will distinguish between things that have changed the same thing"? Or use me, "changed intent" caused different actions of various EVENT objects. We express different actions by creating different EVENT subclasses.

Here is the place where the internal class shows. They allow us to do two things:

(1) Expressing all of the implementation details of a control frame application in a single class, so that all things related to the implementation are completely packaged. Internal classes are used to express a variety of different types of action (), which are used to solve practical problems. In addition, the subsequent example uses the Private internal class, so implementing details will be completely hidden and can be safely modified.

(2) The internal class makes our specific implementation more clever because any member of the external class can be easily accessed. If you don't have this ability, the code may seem to make people feel comfortable, and finally I have to find other ways to solve.

Now I want to think about a specific embodiment of the control framework, it is designed to control the Greenhouse function (annotation 4). Each action is completely different: control light, water supply, and temperature automatic adjustment opening, control bell, and restart the system. However, the design of the control framework is to easily isolate different code. For each type of action, you must inherit a new EVENT internal class and write the corresponding control code in ACTION ().

4: Due to certain special reasons, this is a very interesting question that often needs to be solved. The original example has appeared in the "C Inside & Out" book, but Java provides a more order People comfortable solution.

As a typical behavior of the application framework, the GreenhouseControls class is inherited from Controller:

//: GreenhouseControls.java

// this Products a Specific Application of Thae

// Control System, All IN A Single Class. Inner

// Classes allow you to encapsulate Different

// FunctionAlity for Each Type of Event.

Package c07.controller;

Public Class GreenhouseControls

EXTENDS controller {

PRIVATE BOOLEAN Light = FALSE;

Private boolean water = false;

Private string thermostat = "day";

PRIVATE CLASS LIGHTON EXTENDS Event {Public Lighton (Long EventTime) {

Super (eventtime);

}

Public void action () {

// Put Hardware Control Code Here To

// Physically Turn on the Light.

Light = True;

}

Public string description () {

Return "Light Is On";

}

}

PRIVATE CLASS LIGHTOFF EXTENDS EVENT {

Public lightoff (long evenettime) {

Super (eventtime);

}

Public void action () {

// Put Hardware Control Code Here To

// Physically Turn Off the Light.

Light = false;

}

Public string description () {

Return "Light IS OFF";

}

}

PRIVATE CLASS WATERON EXTENDS Event {

Public Wateron (long evenettime) {

Super (eventtime);

}

Public void action () {

// Put Hardware Control Code Here

Water = TRUE;

}

Public string description () {

Return "Greenhouse Water IS on";

}

}

Private class wateroff extends Event {

Public Wateroff (long evenettime) {

Super (eventtime);

}

Public void action () {

// Put Hardware Control Code Here

Water = false;

}

Public string description () {

Return "Greenhouse Water IS OFF";

}

}

Private class thermostatnight extends event {

Public thermostatnight (long eventtime) {

Super (eventtime);

}

Public void action () {

// Put Hardware Control Code Here

tMostat = "night";

}

Public string description () {

Return "Thermostat on Night Setting";

}

}

Private class thermostday extends Event {

Public thermostatday (long evenettime) {

Super (eventtime);

}

Public void action () {

// Put Hardware Control Code Here

Thermostat = "day";

}

Public string description () {

Return "Thermostat on day setting";

}

}

// an example of an action () That Inserts A

// new one of itself Into the Event List:

Private int rings; private class belful extends event {

Public Bell (long evenettime) {

Super (eventtime);

}

Public void action () {

// Ring Bell Every 2 Seconds, Rings Times:

System.out.println ("Bing!");

IF (- rings> 0)

Addevent (New Bell

System.currentTimeMillis () 2000));

}

Public string description () {

Return "Ring Bell";

}

}

Private class restart extends Event {

Public Restart (long evenettime) {

Super (eventtime);

}

Public void action () {

Long TM = system.currenttimemillis ();

// instead of hard-wiring, you could Parse

// Configuration Information from A Text

// File Here:

Rings = 5;

Addevent (New Thermostatnight (TM));

Addevent (New Lighton (TM 1000));

AddEvent (New Lightoff (TM 2000));

Addevent (New Wateron (TM 3000));

Addevent (New Wateroff (TM 8000));

Addevent (New Bell (TM 9000));

Addevent (New THERMOSTAY (TM 10000));

// Can Even Add A Restart Object!

Addevent (New Restart (TM 20000));

}

Public string description () {

Return "Restarting System";

}

}

Public static void main (String [] args) {

GreenhouseControls GC =

New greenhouseControls ();

Long TM = system.currenttimemillis ();

GC.Addevent (GC.NEW Restart (TM));

gc.run ();

}

} ///: ~

Note Light (light), Water (water supply), thermost (warming), and Rings are part of the external GreenhouseControls, so the internal class can access those fields without obstruction. In addition, most ACTION () methods also involve certain form of hardware control, which usually requires calls to non-Java code.

Most Event classes look similar, but Bell (bell) and Restart are special. Bell will make a sound, if there is no one, it will add a new Bell object in the event list, so the bell will rest later. Note that the internal class looks similar to multiple inheritance: Bell has all methods of Event, and there is also all methods of external GreenhouseControls.

Restart is responsible for initializing the system, so all necessary events will be added. Of course, a more flexible approach is to avoid "hard coding", but from one file (one exercise in Chapter 10, you will ask you to modify this example, thereby achieving this goal). Since Restart () is just another EVENT object, you can also add a RESTART object in Restart.Action () to enable the system to restart regularly. In Main (), all things we need to do is to create a GreenhouseControls object and add a RESTART object to work. This example should make everyone a more profound understanding of the value of the internal class, especially when using them in a control framework. In addition, in the second half of Chapter 13, you will also see how to subtly use the internal class to describe a graphical user interface behavior. After completing the study, the understanding of the internal class will rise to an unprecedented new height.

7.7 Builder and Polymourne

As usual, the builder is different from other types of methods. This method is still established after the problem involving the velocity. Although the builder does not have versatile (even if a "virtual builder" - will be introduced in Chapter 11), it is still necessary to understand how the builder is in complex hierarchical structure and with the same velocity. use. This understanding will help you avoid some unpleasant disputes.

7.7.1 Call order of the builder

The order in which the builder call has been briefly described in Chapter 4, but it is before the inheritance and versatile issues.

Builders for the basic class are certainly called in a derived class builder, and gradually link to enable the builder for each base class to be called. The reason why the reason is to do is because the builder has a special task: check whether the object has been constructed correctly. A derivatized class can only access its own members and cannot access the basic class members (these members usually have a private property). Only the basic class builder knows the correct method and has appropriate permissions when initializing their own elements. Therefore, you must make all builders to be called, otherwise the construction of the entire object may be incorrect. That is exactly why the compiler is forced to build a builder to build a constructor. In the constructor main body of the derived class, if we don't explicitly specify the call to an underlying class builder, it will "silently" the default builder. If there is no default builder, the compiler will report an error (if a class does not have a builder, the compiler will automatically organize a default builder).

Let's take a look at an example, which shows the effects of synthesis, inheritance, and velocity of the build order:

//: Sandwich.java

// Order of Constructionor Calls

Class meal {

MEAL () {system.out.println ("meal ()");}

}

Class Bread {

Bread () {system.out.println ("BREAD ()");}

}

Class cheese {

Cheese () {system.out.println ("cheese ()");}

}

Class lettuce {

Lettuce () {system.out.println ("Lettuce ()");}

}

Class Lunch Extends MEAL {

Lunch () {system.out.println ("Lunch ()");}

}

Class portablelunch eXtends lunch {

Portablelunch () {

System.out.Println ("PortableLunch ()");

}

}

Class Sandwich Extends PortableLunch {

Bread b = new bow ();

Cheese c = new cheese ();

Lettuce l = new lettuce ();

SANDWICH () {

System.out.println ("SANDWICH ()");

}

Public static void main (String [] args) {

New Sandwich ();

}

} ///: ~

This example creates a complex class outside of other classes, and each class has a builder announced by yourself. The most important classes are Sandwich, which reflects three levels of inheritance (if the default amount of Object is inherited, it is four-level) and three member objects. After the main () created a Sandwich object, the output is as follows:

MEAL ()

Lunch ()

Portablelunch ()

BREAD ()

Cheese ()

Lettuce ()

SANDWICH ()

This means that for a complex object, the buffer call follows the following order:

(1) Call the underlying class builder. This step will continue to repeat, first get the root of the hierarchical structure, then the next derived class, and so on. Until the derivative class of the most deepest layer.

(2) Call the member initialization module in the order of declaration.

(3) The main body of the derivative builder is called.

The order in which the builder call is very important. When inheriting, we know everything about the basic class and access any public and protected members of the basic class. This means that when we are derived, you must assume that all members of the basic class are valid. With a standard method, build actions have been carried out, so members of all parts of the object have been built. But inside the builder must ensure that all members used are constructed. In order to achieve this requirement, the only way is to first call the underlying class builder. Then after entering the derivative builder, all members we can access in the basic class have been initialized. In addition, all member objects (which are objects in the class) (such as B, C, and L) in the above example, because we should initialize them as much as possible, so All members inside the builder should also be guaranteed to be effective. If you adhere to this rule, we will help us determine that all basic class members and members of the current object have been properly initialized. But unfortunately, this approach does not apply to all situations, which will be specified in the next section.

7.7.2 Inheritance and Finalize ()

When you create a new class through the Synthesis method, you will never worry about the end of the member object of that class. Each member is an independent object, so it will get normal garbage collection and ending processing - no matter whether it is a member of a class. However, when performing initialization, it is necessary to cover the Finalize () method in the derived class - if a special clear process has been designed, it is required to be part of the garbage collection. When you override Finalize () of the derived class, be sure to remember the basic class version that calls Finalize (). Otherwise, the initialization of the basic class will not occur at all. The following example is that it will:

//: frog.java

// Testing Finalize with Inheritance

Class DobaseFinalization {

Public Static Boolean Flag = False;

}

Class characteristic {

String S;

Characteristic (String C) {

s = C;

System.out.println (

"CREATING Characteristic" S);

protected void finalize () {

System.out.println (

Finalizing characteristic " s);

}

}

Class LivingCreature {

Characteristic P =

New Characteristic ("is alive");

LivingCreature () {

System.out.println ("LivingCreature ()");

}

protected void finalize () {

System.out.println (

"LivingCreature Finalize");

// Call Base-Class Version Last!

IF (DobaseFinalization.Flag)

Try {

Super.Finalize ();

} catch (throwable t) {}

}

}

Class Animal Extends LivingCreature {

Characteristic P =

New Characteristic ("HAS Heart");

Animal () {

System.out.Println ("Animal ()");

}

protected void finalize () {

System.out.println ("Animal Finalize");

IF (DobaseFinalization.Flag)

Try {

Super.Finalize ();

} catch (throwable t) {}

}

}

Class amphibian extends animal {

Characteristic P =

New Characteristic ("Can Live In Water");

Amphibian () {

System.out.println ("amphibian ()");

}

protected void finalize () {

System.out.println ("Amphibian Finalize");

IF (DobaseFinalization.Flag)

Try {

Super.Finalize ();

} catch (throwable t) {}

}

}

Public class frog extends amphibian {

Frog () {

System.out.println ("Frog ()");

}

protected void finalize () {

System.out.println ("Frog Finalize");

IF (DobaseFinalization.Flag)

Try {

Super.Finalize ();

} catch (throwable t) {}

}

Public static void main (String [] args) {

IF (args.length! = 0 &&

ARGS [0]. Equals ("Finalize")))

DobaseFinalization.flag = true;

Else

System.out.Println ("Not femalizing bases");

New frog (); // instantly becomes Garbage

System.out.println ("BYE!");

// must do this to Guarantee That All // Finalizers Will Be Called:

System.RunfinalizersoneXIT (TRUE);

}

} ///: ~

The DobaseFinalization class simply accommodates a flag, indicating whether sar.finalize () should be called to each class in the hierarchical structure. The setting of this flag is based on the command line parameters, so it is possible to view the behavior under the premise that the basic class finishing work is made.

Each class in the grading structure also includes a member object of the Characteristic class. Everyone can see that the CHARACTERISTIC member object will definitely obtain the end (clear) processing regardless of whether the underlying type finished module is called.

Each covered Finalize () must have access to protected members, because the Finalize () method in the Object class has a protected property, and the compiler does not allow us to eliminate access during inheritance ("Friendly" ratio "Protected" has smaller access rights).

In frog.main (), the DobaseFinalization flag will be configured and create a separate Frog object. Remember that garbage collection (especially the end) may not occur for any particular object, so in order to force this action, System.RunfinalizersoneXit (TRUE) adds additional overhead to ensure the normal operation of the end. If there is no basic class initialization, the output result is:

NOT Finalizing Bases

Creating Characteristic IS Alive

LivingCreature ()

Creating Characteristic Has Heart

Animal ()

Creating Characteristic Can Live In Water

Amphibian ()

Frog ()

Bye!

Frog Finalize

Finalizing Characteristic IS Alive

Finalizing Characteristic Has Heart

Finalizing Characteristic Can Live in Water

It can be seen from it indeed not call the final module for the basic class Frog. However, if the "femalize" independent variable is added to the command line, the following results are obtained:

Creating Characteristic IS Alive

LivingCreature ()

Creating Characteristic Has Heart

Animal ()

Creating Characteristic Can Live In Water

Amphibian ()

Frog ()

Bye!

Frog Finalize

Amphibian Finalize

Animal Finalize

LivingCreature Finalize

Finalizing Characteristic IS Alive

Finalizing Characteristic Has Heart

Finalizing Characteristic Can Live in Water

Although the member object is tailing in the same order in the same order as they created, from the technical point of view, there is no designation of the object. But for the basic class, we can control the sequence of ends. The best order employed is the order in which it is used here, and it is opposite to the initialization order. According to the same form as "destroyer" in C , we should first perform the end of the derived class, and then the end of the basic class. This is because the derivative class may call the same method in the base class, requiring the basic class component to still be active. Therefore, they must be removed (destroyed) in advance. 7.7.3 Behavior of a multi-portable method within the builder

The hierarchical structure (order) called us a interesting question, or let us enter a situation in which it is difficult to retreat. If the current is currently located inside the builder, what will happen when you call a dynamic binding method of the object that is prepared? Inside the original method, we can imagine what happens - the dynamic binding call will parse during the run, because the object does not know that it is from the class belonging to the method, or from it derived from it Some classes. In order to maintain consistency, everyone may think that this should occur inside the builder.

But the actual situation is not complete. If a method of dynamically binding within the builder is called, the definition of that method is covered. However, the resulting effect may not be as expected, and may result in some program errors that are difficult to discover.

Conceptually, the responsibility of the builder is to let the object actually enter the presence. In any builder, the entire object may just get some organizations - we only know that the basic class object has been initialized, but I don't know which classes have been inherited. However, a dynamic binding method call is "forward" or "outward" in a hierarchical structure. It calls a method located in the derived class. If this is done inside the builder, then a member of the call may not be properly initialized - it is obviously not what we hope.

By observing the following example, this problem will be clear:

//: polyconstructors.java

// constructors and polymorphism

// don't produve what you might expect.

Abstract class glyph {

Abstract void Draw ();

Glyph () {

System.out.println ("Glyph () Before Draw ()");

Draw ();

System.out.println ("glyph () after draw ()");

}

}

Class roundglyph extends glyph {

INT RADIUS = 1;

Roundglyph (int R) {

RADIUS = R;

System.out.println (

"Roundglyph.Roundglyph (), RADIUS ="

RADIUS;

}

Void Draw () {

System.out.println (

Roundglyph.draw (), Radius = " RADIUS);

}

}

Public class polyconstructors {

Public static void main (String [] args) {

New rounceph (5);

}

} ///: ~

In GLYPH, the DRAW () method is "Abstract", so it can be overwritten by other methods. In fact, we have to cover it in roveglyph. But the Glyph builder calls this method, and the call will be in Roundglyph.Draw (), which seems to be intentionally. But see the output: glyph () before draw ()

Roundglyph.draw (), radius = 0

Glyph () after draw ()

Roundglyph.Roundglyph (), RADIUS = 5

When the GLYPH builder calls DRAW (), the value of RADIUS is not even the default initial value 1, but 0. This may be caused by no paintings at all or on the screen. This has to start the findings in the search program, try to find out the reason why the program cannot work.

The initial order in the previous section is not very complete, and that is the key to solving the problem. The actual process of initialization is this:

(1) The storage space assigned to the object is initialized into binary zero before taking any other operation.

(2) Call the underlying class builder as in the previous description. At this time, the covered DRAW () method will be called (which is indeed before the Roundglyph builder calls), which will find that the value of the RADIUS is 0, which is caused by step (1).

(3) Call the member initialization code in the order in which the original statement is previously declared.

(4) Call the main body of the derived builder.

There is a premise of taking these operations, that is, all things should be initialized into zero (or some special data types and "zero" equivalent value), rather than just to leave garbage. This includes embedding an object handle inside the class via "synthetic" technology. If you forget to initialize the handle, you will have a violation event during the run. Everything else will become zero, which is usually a serious warning signal when viewing results.

On the other hand, the results of this procedure should be vigilant. From a logical perspective, we seem to have been invapable, so it is very incredible. And there is no error message from the compiler (C will show more reasonable behavior in this case). This error will be easily ignored, and it takes a long time to find it.

Therefore, a particularly effective rule when designing the builder is: use as possible to enable objects into the ready state; if possible, avoid calling any method. The only way to be securely invoked in the builder is those methods that have final properties in the base class (also available for the Private method, which automatically have the final property). These methods cannot be covered, so the above potential problems will not occur.

7.8 Design by inheritance

After learning the knowledge of the polymorbility, since the vectors are such a "smart" tool, it seems that all things should be inherited. But if you use inheritance technology, you will make your design unnecessarily complicated. In fact, when we establish a new category based on an existing class, if you first choose Inherit, it will make the situation abnormally complicated.

A better idea is to first select "Synthesis" - if it is not very sure which one should be used. Synthesis does not force our programming to enter inheritance. At the same time, the synthesis is more flexible because one type (and behavior) can be dynamically selected, while inheritance requires accurately known a type during compilation. The following example explains this:

//: TRANSMOGRIFY.JAVA

// Dynamically Changing The Behavior of

// an Object via composition.

Interface actor {void Act ();

}

Class HappyActor Implements actor {

Public void Act () {

System.out.println ("Happyactor");

}

}

Class Sadactor Implements actor {

Public void Act () {

System.out.println ("Sadactor");

}

}

Class stage {

Actor a = new happyactor ();

Void change () = new sadactor ();

Void Go () {a.act ();

}

Public class transmogrify {

Public static void main (String [] args) {

Stage s = new stage ();

S.GO (); //prints "happyactor"

S.change ();

S.GO (); //prints "sadactor"

}

} ///: ~

Here, a Stage object contains a handle pointing to an Actor, which is initialized into a HAPPYACTOR object. This means that Go () produces specific behaviors. However, since the handle can be rerouted or combined with a different object binding during operation, the handle of the Sadactor object can be replaced in A and then a behavior generated by Go () changes. In this way, we have gained great flexibility during operation. In contrast, we cannot inherit in different forms during operation; it requires it to be completely decided during the compilation period.

A routine design guidelines are: use the difference between the behavior of inheritance, and express the state of the state variable. In the above example, both have been applied: two different classes are inherited to express the differences in the ACT () method; while the Stage allows its own state to change by synthetic techniques. In this case, the change in the state has also produced changes in behavior.

7.8.1 Pure inheritance and expansion

When studying inheritance, in order to create inheritance classification, it seems that the most obvious method is to take a "pure" means. That is, only the methods established in the basic class or "interface" can be covered in the derived class, as shown in this picture below:

It can be described as a pure "belonging to" relationship, because a class interface has specified its "what" or "what is". By inheritance, all derivative classes can only have the basic class interface. If you operate on the above-described schematic, the derived class does not have anything else in addition to the basic class interface.

It can be imagined into a "pure replace" because the derivative object can be perfectly replaced by the base class. When using them, we don't have to know any additional information related to subclavab. As follows:

That is, the basic class can receive any message we send to the derived class because both have a fully consistent interface. All of us to do is from derived shape, and never need to go back to check the exact type of object. All details have been perfectly controlled by versions.

If you consider this idea, then a pure "belongs" relationship seems to be the only wise design method, any other design method will lead to confusion, and there is a big difficulty in definition. But this idea is another extreme. After meticulous research, we found that the expansion interface is a particularly effective solution for some specific issues. It is called "similar to" relationship because the extended derivative class "similar to" basic class - they have the same foundation interface - but it adds some features, requiring additional methods to implement. As follows:

Although this is a useful and sensible approach (by specific environmental decision), it also has a disadvantage: the part of the interface extension in the derivative class cannot be used in the base class. Therefore, once you travel, you can call a new method: If you don't touch it, you don't have this problem. However, in many cases, you need to re-verify the exact type of the object, so that you can access the type of extension. In the subsequent section, we specify how this is achieved.

7.8.2 Trace style and running type identification

Since we lost specific type information during tracetability (in the inheritance structure), in order to obtain specific type information - ie move down in a hierarchical structure - we must use "Trace" technology . However, we know that it is definitely safe; the base class is impossible to have a larger interface than the derivative class. Therefore, each message sent by the basic interface will definitely be received. However, when we are still trace, we don't really know that a geometry is actually a circle, it is entirely a triangle, square or other shape.

In order to solve this problem, there must be an approach to ensure that the stracker is correct. Only in this way, we will not take shape into a type of error and then issue a message that an object is impossible. This is very unsafe.

In some languages ​​(such as C ), special operations must be taken in order to ensure "type security". But in Java, all models will automatically check and verify! Therefore, even if we just conduct a normal bracket shape, after entering the runtime, there will be no attention to this model without a message, ensuring that it is indeed the type we want. If not, you will get a ClassCastException (class styling violation). The behavior of checking the type during operation is called "Run Type Identity" (RTTI). The following example demonstrates the behavior of RTTI:

//: Rtti.java

// Downcasting & Run-Time Type

// Identification (RTTI)

Import java.util. *;

Class useful {

Public void f () {}

Public void g () {}

}

Class moreuseful extends useful {

Public void f () {}

Public void g () {}

Public void u () {}

Public void v () {}

Public void w () {}

}

Public class rtti {

Public static void main (String [] args) {

Useful [] x = {

New useful (),

NEW moreuseful ()

}

x [0] .f ();

x [1] .g ();

// compile-time: Method Not Found in Useful:

//! x [1] .u ();

(MoreUseful) x [1]). U (); // Downcast / RTTI

(MoreUseful) x [0]). U (); // Exception thrown

}

} ///: ~

As in the schematic, moreUseful (more useful) extends the useful (useful) interface. But because it is inherited, it can also be traced into a USEful. We can see that this occurs when the array x (located in main () is initialized. Because two objects in the array belong to the USEful class, the F () and g () methods can be simultaneously issued two. And if you try to call U () (it only exists moreUseful), a compile time error message will be received. To access an extended interface of a moreuseful object, you can try a traceability. If it is the right type, this action will succeed. Otherwise, you will get a ClassCastException. We don't have to write any special code for this violation, because it pointed out a programming error that may occur anywhere in the program.

The significance of RTTI is much reflected in the styling. For example, before trying to track, you can understand what type itself is processed. The entire chapter 11 is in the face of the Java run type identification.

7.9 Summary

"Multi-shaped" means "different forms". In object-oriented programming, we have the same appearance (basic type of universal interface) and different forms of using that look: dynamically bind or organize, different versions.

Through this chapter, everyone knows that if you don't use data abstraction and inheritance technology, you can't understand, even an example of creating versions. Parallelism is an indispended feature (just like a Switch statement), only coordinating with other elements. We should look at it as part of the overall relationship. It is often confused from other Java, non-object-oriented features, such as methods overload, and these features sometimes have certain features targeted. But don't be fooled: If there is no binding in the future, it is not a conformal.

To use versatile or even object-oriented technologies, especially in their own procedures, they must extend their programming views to members and messages including alone, but also consistency between classes and classes. Relationship. Although this requires more effort to pay, it is very worthwhile, because only this can really effectively speed up your programming speed, better organize code, and make it easier to make a wide range of procedures and easier The code is maintained and expanded.

7.10 practice

(1) Creating a Rodent: mouse: mouse (mouse), Gerbil (mouse), Hamster (Big Checker) and the like. In the basic class, a method suitable for all RODENTs is provided, and they are covered in the derived class to take different actions according to different types of Rodent. Create a RODENT array where different types of RODENT is populated, then call your own basic class method to see what happens.

(2) Modify the practice 1, so that RODENT becomes an interface.

(3) Reform the problem in Winderror.java.

(4) In GreenhouseControls.java, add the Event internal class to open and close the fan.

转载请注明原文地址:https://www.9cbs.com/read-43187.html

New Post(0)