Summary:
Based on the reuse of Java classes, this paper presents a comprehensive description of the inheritance and polymorphism of object-oriented two major features.
First of all, we introduce the essence and significance of inheritance, and discuss the similarities and differences of inheritance, composition and proxy in the reuse of classes. Shortly thereafter, we introduce polymorphism based on inheritance. This paper introduces its implementation mechanism and detailed application. In addition, in order to better understand inheritance and polymorphism. We have a comprehensive introduction to the final keyword.
On this basis. We introduce the loading and initialization order of classes in Java. At last. We have detailed descriptions of three very important concepts in object-oriented design – overloading, overwriting and hiding.
Points:
- Inherited
- Combination, inheritance, agent
- Polymorphic
- Final keyword
- class loading and initialization sequence
- Overloading, overwriting, and hiding
I. Inheritance
Inheritance is an integral part of all OOP languages, and in Java, the extends keyword is used to represent an inheritance relationship.
When you create a class, it is always inherited. Assuming that you do not explicitly indicate which class to inherit, you are always implicitly inheriting from the root class Object. Assuming that two classes have an inheritance relationship, the subclass will voluntarily inherit the methods and variables of the parent class, and can invoke the methods and variables of the parent class directly in the subclass. What needs to be pointed out is. In Java. Simply agree to single inheritance, that is, a class can only explicitly inherit from a parent class at most.
However, a class can be inherited by multiple classes, which means that a class can have multiple subclasses. In addition, we need to pay special attention to the following points:
1. Inheritance of member variables
When a subclass inherits a class. is able to use member variables in the parent class, but not all of the member variables of the parent class are completely inherited.
Detailed principles such as the following:
subclasses can inherit from the parent class. Public and protected member variables . cannot inherit from the parent class. Private member variable, but can be interviewed by the Getter/setter method corresponding to the parent class .
A package access permission member variable for the parent class . if the subclass and the parent class are under the same package, the subclass can inherit. Otherwise, subclasses are not able to inherit .
For a parent class member variable that the subclass can inherit, it is assumed that a member variable of the same name appears in the subclass , and a hidden behavior occurs. That is, the member variable of the subclass masks the member variable of the parent class with the same name. Suppose you want to access a member variable of the same name in a parent class in a subclass, you need to use the Super keyword to refer to it.
2. Inheritance of member methods
In the same way. When a subclass inherits a class. Can use the member methods in the parent class, but the subclass does not completely inherit all the methods of the parent class. Detailed principles such as the following:
subclasses can inherit from the parent class. The public and protected member methods cannot inherit the Private member method of the parent class .
for the parent class's package access permission member Method , the subclass can inherit if the child class and parent class are under the same package. Otherwise, subclasses are not able to inherit.
A parent class member method that can inherit from a subclass. Suppose that a member method of the same name appears in a subclass , which is called overwrite , that is, the member method of the subclass overrides the member method of the parent class with that name. Suppose you want to access a member method of the same name in a parent class in a subclass, you need to use the Super keyword to refer to it.
Sample Program Demo:
Class Person { PublicString Gentle ="Father";} Public class Student extends person { PublicString Gentle ="Son"; PublicStringPrint(){return Super. Gentle;//In subclasses, visit a member of the same name in the parent class to change} Public Static void Main(string[] args)throwsclassnotfoundexception {Student Student =NewStudent (); System.out.println ("##### "+ Student.gentle); Person P = student; System.out.println ("***** "+ P.gentle);//Hide: Compile-time decision. Does not occur polymorphicSystem.out.println ("----- "+ Student.print ()); System.out.println ("----- "+ P.print ());This method is not defined in//error:person}}/* Output: ##### Son * * * * * Father-----Father * *//:~
hiding and overwriting are different. shadowing is for member variables and static methods , and overrides are for common methods .
3. Initialization of base class and constructor
We know. The export class is like a new class with the same interface as the base class, and there may be additional methods and domains.
However, inheritance is not just an interface to replicate a base class. when you create an export class object. The object will include a child object of the base class. This sub-object is the same as the object that we created directly from the base class.
The difference is that the latter comes from the outside. The child objects of the base class are wrapped inside the exported class object.
Therefore, proper initialization of base class sub-objects is critical, and Java provides a corresponding method to ensure that the export class must call the base class constructor in the constructor to perform the initialization , while the base class constructor has all the knowledge and capabilities required to perform the base class initialization.
When a base class contains a default constructor, Java itself actively inserts a call to the base class's default constructor in the constructor of the exported class. Because the compiler does not have to consider the issue of what parameters to pass. However, if the parent class does not contain a default constructor. Or if the export class wants to invoke a parent class constructor with a parameter, the constructor for the corresponding base class must be called explicitly in the constructor of the exported class using the Super keyword. and the calling statement must be the first statement of the export class constructor.
Two. Combination, inheritance, agent
In Java, the combination, inheritance, and proxy of three technologies enable code reuse.
(1) combination (has-a)
A combination can be implemented by adding an object of an existing class to the new class. that is. The new class is made up of objects of existing classes. This technique is often used in situations where you want to use the functionality of an existing class in a new class rather than its interface. in other words, embed an object in the new class and let it implement the functionality that is required. But the user of the new class sees only the interface defined for the new class, not the interface of the embedded object.
(2) Succession (is-a)
Inheritance enables us to create new classes according to the types of existing classes. That We take the form of existing classes and add new code to them. Usually. This means that we are using a generic class. and specialize it for a particular need. in essence, both composition and inheritance agree to place child objects in the new class, and the combination is explicitly done, whereas inheritance is implicitly done.
(3) proxy (a middle way between inheritance and composition: Use existing class functions like a combination, using existing class interfaces like inheritance at the same time)
An agent is a middle way between inheritance and composition. Java does not provide direct support for it. In the proxy, we place a member object in the class we want to construct (just like a combination), but at the same time we expose the Member object's interface/method (like inheritance) in the new class.
Sample Program Demo:
//control module public class spaceshipcontrols { void up (int velocity) {} void Down (int velocity) {} vo ID Left (int velocity) {} void right (
int velocity) {}
void forward (
int velocity" {}
void back (
int vel ocity) {}
void turboboost () {}}
A spacecraft needs a control module, so one way to construct a spaceship is to use inheritance:
Public class spaceship extends spaceshipcontrols { PrivateString name; Public Spaceship(String name) { This. name = name; } PublicStringtoString() {returnName } Public Static void Main(string[] args) {Spaceship Protector =NewSpaceship ("Nsea Protector"); Protector.forward ( -); } }
However. Spaceship is not a true spaceshipcontrols type, even if you can "tell" spaceship forward movement (forward ()).
To be more precise, spaceship includes Spaceshipcontrols, and at the same time, all of Spaceshipcontrols's methods are exposed in spaceship.
The agent (spaceship's motion behavior is completed by the Spaceshipcontrols agent) just can solve such a problem:
//Spaceship behavior by Spaceshipcontrols agent completed Public class spaceshipdelegation { PrivateString name;PrivateSpaceshipcontrols controls =NewSpaceshipcontrols (); Public spaceshipdelegation(String name) { This. name = name; }//Agent method: Public void Back(intVelocity) {controls.back (velocity); } Public void Down(intVelocity) {controls.down (velocity); } Public void forward(intVelocity) {controls.forward (velocity); } Public void Left(intVelocity) {controls.left (velocity); } Public void Right(intVelocity) {controls.right (velocity); } Public void Turboboost() {controls.turboboost (); } Public void up(intVelocity) {controls.up (velocity); } Public Static void Main(string[] args) {Spaceshipdelegation Protector =NewSpaceshipdelegation ("Nsea Protector"); Protector.forward ( -); } }
In fact, we can have a lot of other controls when we use proxies. Because we can choose to provide only a subset of the methods in the member object.
Three. Final keyword
Many programming languages require some way of telling the compiler that a piece of data is constant. Sometimes. It is very practical to have constant data. Example:
- A never-changing compile-time constant.
A value that is initialized at execution time. And you don't want it to be changed.
for cases such as compile-time constants. The compiler is able to bring the constant value into the calculation of whatever might be used in it. That is, the ability to perform calculations at compile time reduces some of the implementation burden. in Java, this type of constant must meet two conditions:
is the basic type. and with final modification;
You must assign a value to this constant when it is defined.
In addition When a final decorated object is referenced, final makes its reference constant. once a reference is initialized to an object, it is no longer possible to point to an object. However, the object itself can be altered, and the same applies to arrays, because it is also an object.
It is particularly important to note that. We can't because some data is final. It feels like you can know its value at compile time. Like what:
publicclass Test { finalint i4 = rand.nextInt(20);}
1. Blank Final
Java agrees to generate a blank final , that is, a domain that declares final but does not have a given initial value. However, the compiler will ensure that the blank final is initialized before it is used, regardless of the situation. However, the blank final provides greater flexibility in the use of the keyword final: the final domain in a class can be different from the object, but retains its constant characteristics.
for example.
You must use an expression to assign a value to final at the definition of the domain or in each constructor. This is why the final domain is always initialized before it is used.
2. Final number of references
final parameters are mainly used in local inner classes and anonymous inner classes, and there are a number of other details. I have another article: a summary of the Java internals.
3. Final method
Final keyword when the scope method is used. Used to lock the method in case whatever inheritance class changes its meaning.
This is a design consideration: you want to make sure that the method behavior remains the same in inheritance and is not overwritten.
For member methods, there is only a clear prohibition on overwriting. Before the method is set to final.
4. Final class
when you define a class as final, you are not going to inherit the class. And I do not agree with others to do so. in other words, for some reason, your design for the class never needs to be changed, or for security reasons, you don't want it to have subclasses.
It is important to note that thefinal class field can be selected as final based on the actual situation. Whether or not it is defined as final. The same rules apply to the domain that defines the final.
However. because the final class prohibits inheritance, all methods in the final class are implicitly specified as final, because they cannot be overwritten. You can add a final decoration to a method in the final class. But that does not add up to whatever it means.
5. Final and private
all private methods in a class are implicitly specified as final. because the private method cannot be taken, it cannot be overwritten. The ability to add a final modification to the private method does not add any extra meaning to the method.
It is particularly important to note that overrides occur only when a method is part of a base class interface . If a method is private, it is not part of the base class interface, but only some program code that is hidden from the class. However, if a non-private method is generated with the same name in the export class, we do not overwrite the method at this time. Just a new method is generated.
Because the private method is inaccessible and can be effectively hidden, there is no need to take it into account in any situation other than to think of it as a reason for the organization of the class it belongs to.
6. Final and Static
Static when modifying a variable. It has a default value and can be changed , and it can only modify member variables and member methods.
A static final domain occupies only a certain amount of storage space that cannot be changed, and can only be initialized at the time of declaration .
Because it is final, there is no default value, and it is static. Therefore, when the class is not instantiated. It has been assigned, so it can only be initialized at the time of declaration.
Four. polymorphic
We know that inheritance agrees to treat an object as its own type or its base type, so that the same code can be executed on top of these different types without distinction. , a polymorphic method call agrees that a type behaves differently from other similar types, only those types are exported by the same base class. Therefore, the role of polymorphism in the main body now two aspects:
- Polymorphism by separating what to do and how to do, from there is an angle to separate the interface and implementation, so as to achieve the change of things and the unchanging things apart;
- eliminate the coupling between types (similar, in Java.) Generics are also used to eliminate the coupling between a class or method and the type used.
1. Realization mechanism
We know that the coverage of the method is very good for polymorphism, but when using a base class reference to invoke an overlay method, which method is called correctly?
associating a method call with the same method body is called a binding .
Bind before the program executes. Called early binding .
But. Obviously, such a mechanism does not solve the above problem. Because at compile time the compiler does not know exactly which object the base class reference refers to. The solution is late binding (dynamic binding/execution-time binding): Binding at execution time based on the object's detailed type .
In fact, in Java, except for the static method and the final method (the private method belongs to the final method), all other methods are late-bound.
Such Once a method is declared final, it can prevent others from overwriting the method. But more importantly, this can effectively turn off dynamic binding. Or, tell the compiler that you don't need to bind it dynamically. To generate more efficient code for final method calls.
based on the dynamic binding mechanism, we are able to write code that deals only with the base class, and the code is executed correctly for all export classes. or, send a message to an object. Let the object decide what to do.
2. Downward transformation and type recognition during execution
Because the upward transformation loses detailed type information, we might think that by going down the transformation should also be able to get type information.
However. We know that the upward transformation is safe because the base class does not have an interface that is larger than the exported class.
Therefore, the messages we send through the base class interface are accepted. But for the downward transformation. We won't be able to guarantee it.
To solve the problem, there must be some way to ensure the correctness of the downward transformation, so that we do not rashly transition to a wrong type, and then issue the object unacceptable message.
In Java, the execution-time type recognition (RTTI) mechanism is able to handle this problem, which guarantees that all transformations in Java will be checked.
So, even if we're just going to do a normal type conversion in parentheses, we'll check it when we get to the execution, to make sure it's really the type we want.
Suppose not. We're going to get a type conversion exception: ClassCastException.
3. Examples of polymorphic applications
- policy mode .
- Adapter mode ;
Five. Class loading and initialization sequence
First, you must indicate that the class loading and initialization sequence are: Parent class Static code block, subclass static code block, parent class non-static code block-parent class constructor-subclass non-static code block-subclass Constructor
That is, first. Initializes static member variables and static code blocks in the parent class, initialized in the order in which they appear in the program. And then. Initializes static member variables and static blocks of code in subclasses, initializes them in the order in which they appear in the program, and then initializes the normal member variables and code blocks of the parent class, and then executes the parent class's construction method. Finally, initialize the normal member variables and code blocks of the subclass, and then execute the subclass's constructor method.
We use the following procedure to illustrate:
Class Superclass {Private StaticString STR ="Super Class Static Variable";Static{System.out.println ("Super Class Static Block:"+ STR); } Public Superclass() {System.out.println ("Super Class Constructor Method"); } {System.out.println ("Super Class Block"); }} Public class objectinit extends superclass { Private StaticString STR ="Class Static Variable";Static{System.out.println ("Class Static Block:"+ STR); } Public Objectinit() {System.out.println ("Constructor Method"); } {System.out.println ("Class Block"); } Public Static void Main(string[] args) {@SuppressWarnings("Unused") Objectinit A =NewObjectinit (); }}/ * Output:super class static Block:super class static Variable class static Block:class static Variable Super class Block Super Class Constructor method Class Block Constructor method * ///:~
The first thing that happens when you execute the program is to try to access the Objectinit.main () method (a static method), and the loader starts and loads the Objectinit class. When loaded, the compiler notices that it has a base class (which is known by the keyword extends) and then loads its base class first. Assuming that the base class has its own base class, load the parent base class first, and so on (in this case, load the object class, load the superclass class, and finally load the Objectinit class). Next, the static domain and the static code block in the base class are executed, followed by the next export class. Such a way is very important. Because the static initialization of the exported class may depend on whether the base class member can be initialized correctly. this ends. All of the classes have been loaded. Object can be created.
First, initialize all of the normal member variables and code blocks of the base class, and then execute the root class constructor to create one, and then the next export class. In turn. Until the initialization is complete.
Six. Overloading, overwriting and hiding
1. Overloading and covering
(1) definition and Difference
overloading : Suppose that multiple methods with the same name are defined in a class, but they have different parameters (including three aspects: the number of parameters, the type of the parameter, and the order of the parameters), which are called overloads of the method. among them. You cannot overload by visiting permissions, return types, and throwing exceptions.
Overwrite : a method defined in a subclass has the same method signature as a method in its parent class (including the same name and list of references), which is called the override of the method.
When a subclass object uses this method, the definition of the method in the subclass is called, and the definition of the method in the parent class is masked.
Overall. Overloading and overwriting are different manifestations of Java polymorphism. The former is a representation of polymorphism in a class, which is a representation of polymorphism between a parent class and a subclass.
(2) implementation mechanism
overloading is a parameter polymorphism mechanism , which realizes polymorphic mechanism by the difference of method parameters. Moreover, it belongs to a static binding mechanism , which at compile time knows which method to execute in detail.
Overlay is a polymorphic mechanism of dynamic binding. that is, a method with the same signature in the parent class and the child class has a different detail implementation. As to which method to implement at last depends on the actual situation of execution.
(3) Summary
We should pay attention to the following points:
The final method cannot be overwritten;
Subclasses cannot overwrite the private method of the parent class. Otherwise, only a completely new method is defined in the subclass that has the same name as the parent class, without any overriding effect.
Other places that need attention, such as what you see:
2. Covering and hiding
(1) definition
overwrite : Refers to the method defined in the execution- time type of the system call of the current object reference , which is the execution period binding.
Hide : Refers to the method defined in the compile-time type when the system invokes the current object at execution time . That is, the declaration or conversion of a type to a method or variable in the corresponding type is a compile-time binding.
(2) scope
Overwrite : only for instance method;
Hidden : only for static methods and member variables.
(3) Summary
Instance methods of subclasses cannot hide static methods of the parent class, as well. A static method of a subclass also cannot overwrite an instance method of the parent class. otherwise compile error;
a member variable with the same name can be hidden, regardless of whether it is a static member or an instance member.
The following Program demo sample is a great way to interpret the overloads, covering and hiding three concepts:
references
The idea of Java programming
Java: Classes and inheritance
Java inheritance, polymorphism and reuse of classes