The previous section introduced the package and inheritance of Java. If you are a little confused about this kind of knowledge, you can go Package and inheritance of 10000 word parsing Java This chapter may help you solve some doubts!
Today's chapter mainly introduces polymorphic and abstract classes. I hope the next content will be helpful to you!
1, Polymorphism
Before understanding polymorphism, we first understand the following knowledge points
1. Upward transformation
What is upward transformation? Simply put
The subclass object is assigned to the reference of the parent object
What does this mean? We can look at the following code
// Suppose Animal is the parent class and Dog is the child class public class TestDemo{ public static void main(String[] args){ Animal animal=new Animal("animal"); Dog dog=new Dog("Erha"); animal=dog; } }
The object referenced by the subclass dog is assigned to the reference of the parent class, and the above code can also be simplified to
public class TestDemo{ public static void main(String[] args){ Animal animal=new Dog("Erha"); } }
This is actually the same as the above code. This writing method is called "upward transformation", which assigns the reference of the subclass object to the reference of the parent class
In fact, more may be used after upward transformation, so when do we need to use it?
- Direct assignment
- Method transmission parameter
- Method return
Direct assignment is what the above code looks like. Next, let's take a look at an example of method parameter passing
// Suppose Animal is the parent class and Dog is the child class public class TestDemo{ public static void main(String[] args){ Animal animal=new Dog("Erha"); func(animal); } public static void func1(Animal animal){ } }
We wrote a function. The formal parameter is the reference of the parent class, and the passed argument is the object referenced by the child class. It can also be written as
public class TestDemo{ public static void main(String[] args){ Animal animal=new Animal("animal"); Dog dog=new Dog("Erha"); func(dog); } public static void func1(Animal animal){ } }
So what is method return like? In fact, it is also very simple, such as
// Suppose Animal is the parent class and Dog is the child class public class TestDemo{ public static void main(String[] args){ } public static Animal func2(){ Dog dog=new Dog("Erha"); return dog; } }
In the func2 method, the object of the child class is returned to the reference of the parent class. There is also a method return
public class TestDemo{ public static void main(String[] args){ Animal animal=func2(); } public static Dog func2(){ Dog dog=new Dog("Erha"); return dog; } }
The return value of the method is the reference of the subclass, and then assign it to the object of the parent class. This writing is also called "upward transformation".
So since the reference of our parent class points to the object referenced by the child class, can the parent class use some methods of the child class? have a try
class Animal{ public String name; public Animal(String name){ this.name=name; } public void eat(){ System.out.println(this.name+"Eat something"+"(Animal)"); } } class Dog extends Animal{ public Dog(String name){ super(name); } public void eatDog(){ System.out.println(this.name+"Eat something"+"(Dog)"); } } public class TestDemo{ public static void main(String[] args){ Animal animal1=new Animal("animal"); Animal animal2=new Dog("Erha"); animal1.eat(); animal2.eatdog(); } }
The result is No
Because the reference type of Animal is essentially Animal, you can only use the members and methods in your own class
2. Dynamic binding
Can animal2 use the eatDog method in the Dog class? In fact, we can, as long as we change the name of eatDog to eat
class Dog extends Animal{ public Dog(String name){ super(name); } public void eat(){ System.out.println(this.name+"Eat something"+"(Dog)"); } }
The modified part of the code is as above. At this time, animal2 directly calls eat to get the following results
This means that at this time
- animal1.eat() actually calls the method of the parent class
- What animal2.eat() actually calls is the method of the subclass
So why does animal2.eat call subclass methods after changing eatDog to eat?
This is what we're going to talk about rewriting
3. Method rewriting
What is rewriting?
The subclass implements the method with the same name as the parent class, and
- Same method name
- The return value of the method is generally the same
- The parameter list of the method is the same
If the above conditions are met, it is called rewriting, overwriting, and overriding
matters needing attention:
-
The overridden method cannot be a sealed method (that is, a method modified by final). We have learned about the keyword final before, and the method modified by it is called the sealing method, which can no longer be rewritten, such as
// Suppose this is a method in the parent class public final void eat(){ System.out.println(this.name+"Want to eat"); }
Such methods cannot be overridden
-
The access qualifier permission of a subclass must be greater than or equal to the permission of the parent class, but the parent class cannot be modified by private
-
Methods cannot be modified by static
-
Generally, for overridden methods, you can use the @ Override annotation to display the specified. What's the advantage of adding him? Look at the code below
// Suppose the following eat is the overridden method class Dog extends Animal{ @Override private void eat(){ // ... } }
If eat is written as ate, the compiler will find that there is no ate method in the parent class, and will compile and report an error, indicating that rewriting cannot be formed
-
When overridden, the return value can be modified, and the method name, parameter type and number cannot be modified. Only when the return value is a class type, the overridden method can modify the return value type, and must be a subclass of the return value of the parent method; Or it will not be modified, which is the same as the return value type of the parent class
Knowing this, we must have a concept of rewriting. At this point, let's recall the overloading we learned before. We can make a table for comparison
difference | Overload | Override |
---|---|---|
concept | The method name is the same, the parameter list is different, and the return value is not required | The method name is the same, the parameter list is the same, and the return type is generally the same |
Range | A class | Inheritance relationship |
limit | No permission requirements | Overridden methods cannot have more stringent access control permissions than the parent class |
The result of the comparison is that there is no relationship between the two
At this point, it seems that we haven't explained what the title dynamic binding in the previous section is
So what is dynamic binding? The conditions for occurrence are as follows
- Upward transformation occurs (the parent class reference needs to reference the child class object)
- Through the parent class reference, the override method with the same name of the child class and the parent class is called
Why is it called dynamic? After disassembly, we can find
- Compile time: the method of the parent class is called
- But at run time: what is actually called is the method of the subclass
Therefore, this is actually a dynamic process, which can also be called runtime binding
4. Downward transformation
Since the upward transformation has been introduced, there must be downward transformation! When is the downward transition? Think about the upward transformation, and you can guess that it is
The parent class object is assigned to the reference of the child class object
So code is
// Suppose Animal is the parent class and Dog is the child class public class TestDemo{ public static void main(String[] args){ Animal animal=new Animal("animal"); Dog dog=animal; } }
However, it is not enough just to write in this way, and errors will be reported
Why? We can think about it like this
Dogs are animals, but animals cannot be said to be dogs, which is equivalent to an inclusive relationship.
Therefore, the object of the dog can be assigned directly to the animal, but the object of the animal cannot be assigned to the dog
We can use cast so that the above code will not report an error
public class TestDemo{ public static void main(String[] args){ Animal animal=new Animal("animal"); Dog dog=(Dog)animal; } }
We then run the eat method with a dog reference
public class TestDemo{ public static void main(String[] args){ Animal animal=new Animal("animal"); Dog dog=(Dog)animal; dog.eat(); } }
An error occurred after running
Animals cannot be converted into dogs!
What should we do? One thing to remember:
The premise of using downward transformation is that upward transformation must occur
public class TestDemo{ public static void main(String[] args){ Animal animal=new Dog("Erha"); Dog dog=(Dog)animal; dog.eat(); } }
That's no problem!
As mentioned above, the premise of using downward transformation is upward transformation. In fact, we can understand that when we use the upward transformation, some functions cannot be achieved, so we use the downward transformation to improve the code (emmm, it's purely a personal fool). Like
// Suppose I have a housekeeping method guard in my Dog class public class TestDemo{ public static void main(String[] args){ Animal animal=new Dog("Erha"); animal.guard(); } }
The above code will report an error because there is no guard method in the Animal class. Therefore, we need to borrow the downward transformation
public class TestDemo{ public static void main(String[] args){ Animal animal=new Dog("Erha"); Dog dog =animal; dog.guard(); } }
be careful:
In fact, downward transformation is not often used. Using it may accidentally make some mistakes. If we continue to use some unique methods of other animals in the above code, we will report an error if we forget that they have not undergone upward transformation.
To avoid this error: we can use instanceof
instanceof: you can determine whether a reference is an instance of a class. If yes, it returns true. If not, it returns false, such as
public class TestDemo{ public static void main(String[] args){ Animal animal=new Dog("Erha"); if(animal instanceof Bird){ Bird bird=(Bird)animal; bird.fly(); } } }
The above code first judges whether the reference of Animal is an instance of Bird. We know that it should be an instance of Dog, so false is returned
5. Keyword super
In fact, the super keyword was explained in the previous chapter. Here I use a table to compare this and super for easy understanding
difference | this | super |
---|---|---|
concept | Access properties and methods in this class | Access to properties and methods in the parent class by subclasses |
Search range | Find this class first. If this class does not, call the parent class | Call parent class directly |
express | Represents the current object | nothing |
Commonality 1 | Cannot be placed in static decorated methods | Cannot be placed in static decorated methods |
Commonness 2 | To be placed on the first line (cannot be used with super) | To be placed on the first line (cannot be used with this) |
6. call the rewrite method (PIT) in the construction method.
Next, let's look at a piece of code. You can guess what the result is!
class Animal{ public String name; public Animal(String name){ eat(); this.name=name; } public void eat(){ System.out.println(this.name+"Eating food( Animal)"); } } class Dog extends Animal{ public Dog(String name){ super(name); } public void eat(){ System.out.println(this.name+"Eating food( Dog)"); } } public class TestDemo{ public static void main(String[] args){ Dog dog=new Dog("Erha"); } }
The result is
If you don't guess right, you usually have two doubts:
- The eat method is not called, but why is the result like this?
- Why is null?
answer:
- Doubt 1: because a subclass inherits from the parent class and needs to construct methods for the parent class, when a subclass creates an object, it constructs the construction method of the parent class and executes the eat method of the parent class
- Doubt 2: because the parent class construction method executes the eat method first, and the assignment of name is in the next step, most of the name at this time is null
Conclusion:
The overridden method can be called in the construction method, and dynamic binding occurs
7. Understanding polymorphism
That's all. We're finally going to officially introduce one of our key points today! What is polymorphism? In fact, it's the same idea as inheritance. We can look at a piece of code first
class Shape{ public void draw(){ } } class Cycle extends Shape{ @Override public void draw() { System.out.println("Draw a circle⚪"); } } class Rect extends Shape{ @Override public void draw() { System.out.println("Draw a square piece♦"); } } class Flower extends Shape{ @Override public void draw() { System.out.println("Draw a flower❀"); } } public class TestDemo{ public static void main(String[] args) { Cycle shape1=new Cycle(); Rect shape2=new Rect(); Flower shape3=new Flower(); drawMap(shape1); drawMap(shape2); drawMap(shape3); } public static void drawMap(Shape shape){ shape.draw(); } }
We found that when the drawMap method is used by the caller, the draw method is called through the parent class, and the final expression is different. This idea is called polymorphism.
More simply, polymorphism is
A reference can show many different forms
Polymorphism is an idea, and there are two preconditions to realize it
- Upward transformation
- Call an override method with the same name
The inheritance of an idea always has its unique benefits, so what are the benefits of using polymorphism?
1) The cost of using classes by class callers is further reduced
- Encapsulation means that the caller of a class does not need to know the implementation details of the class
- Polymorphism allows the caller of a class not to know what the type of the class is, but only that the object has some method
2) It can reduce the "circle complexity" of the code and avoid using a large number of if else statements
Cycle complexity:
Is a way to describe the complexity of a piece of code. The number of conditional statements and loop statements in a piece of code can be regarded as "cycle complexity". The more this number, the more complex it is.
We can look at a piece of code
public static void drawShapes(){ Rect rect = new Rect(); Cycle cycle = new Cycle(); Flower flower = new Flower(); String[] shapes = {"cycle", "rect", "cycle", "rect", "flower"}; for (String shape : shapes) { if (shape.equals("cycle")) { cycle.draw(); } else if (shape.equals("rect")) { rect.draw(); } else if (shape.equals("flower")) { flower.draw(); } } }
The meaning of this code is to print circle, square piece, circle, square piece and flower respectively. If polymorphism is not used, we will generally write the above method. Using polymorphism, the code will appear very simple, such as
public static void drawShapes() { // We created an array of Shape objects Shape[] shapes = {new Cycle(), new Rect(), new Cycle(), new Rect(), new Flower()}; for (Shape shape : shapes) { shape.draw(); } }
We can understand the above code through the following diagram
On the whole, it is much simpler to use polymorphic code
3) Strong scalability
As in the above drawing code, if we want to add a new shape, the cost of changing it in a polymorphic way is also relatively low, such as
// Add triangle class Triangle extends Shape { @Override public void draw() { System.out.println("△"); } }
Using polymorphism, we can add a new class to our extended code. If polymorphism is not used, the if else statement needs to be modified, so the cost of modification will be higher
8. Summary
So far, the three characteristics of object-oriented: encapsulation, inheritance and polymorphism have been introduced. Because my personal understanding is also limited, I may not speak well and insufficient. I hope you can understand more.
Next, we will introduce abstract classes and interfaces, which will also be further applied to polymorphism. You can practice more and deepen your understanding of ideas.
2, Abstract class
1. Concept
We just wrote a drawing code above, in which the definition of the parent class is as follows
class Shape{ public void draw(){ } }
We found that there is no content in the draw method in the parent class, and the drawing is completed through the draw method of each sub class.
Like the above code, this method without actual work can be designed into an abstract method through abstract, and the class containing the abstract method is an abstract class
This is the code after design
abstract class Shape{ public abstract void draw(); }
2. Precautions
-
Methods and classes should be modified by abstract
-
Other data members and member methods can be defined in abstract classes, such as
abstract class Shape{ public int a; public void b(){ // ... } public abstract void draw(); }
However, to use these members and methods, you need to use subclasses through super
-
Abstract classes cannot be instantiated
-
Abstract methods cannot be private decorated
-
Abstract methods cannot be modified by final, and they cannot coexist with abstract
-
If the subclass inherits the abstract class but does not need to override the abstract method of the parent class, the subclass can be modified with abstract, such as
abstract class Shape{ public abstract void draw(); } abstract Color extends Shape{ }
At this time, both ordinary methods and abstract methods can be defined in this subclass
-
An abstract class A can be inherited by another abstract class B, but if other ordinary classes inherit abstract class B, the ordinary class needs to override all abstract methods in A and B
3. Meaning of abstract class
We should know that the meaning of abstract classes is to be inherited
We know from the precautions that abstract classes themselves cannot be instantiated. If you want to use them, you can only create subclasses to inherit, for example
abstract class Shape{ public int a; public void b(){ // ... } public abstract void draw(); } class Cycle extends Shape{ @Override public void draw(){ System.out.println("Draw one⚪"); } } public class TestDemo{ public static void main(String[] args){ Shape shape=new Cycle(); } }
Note that the subclass needs to override all the abstract methods of the parent class, otherwise the code will report an error
3. Role of abstract classes
So why use an abstract class if it can't be instantiated?
The use of abstract classes is equivalent to an additional level of compiler validation
What do you mean? For example, according to the above drawing code, the actual work is actually completed by the subclass. If you accidentally misuse the parent class, the parent class will not report an error if it is not an abstract class. Therefore, if you design the parent class as an abstract class, it will report an error when the parent class is instantiated, so let's find the error as early as possible
3, Interface
We introduced abstract classes above. In addition to abstract methods, abstract classes can also contain ordinary methods and members.
The interface can also contain methods and fields, but only abstract methods and static constants.
1. Grammar rules
We can rewrite the above Shape into an interface, and the code is as follows
interface IShape{ public static void draw(); }
The specific syntax rules are as follows:
-
Interfaces are defined using interface s
-
Interface naming generally begins with the capital letter I
-
The methods in the interface must be abstract and public modified methods, so the abstract methods can be simplified into
interface IShape{ void draw(); }
Written like this, the default is public abstract
-
The interface can also contain static constants modified by public, and public static final can be omitted, such as
interface IShape{ public static final int a=10; public static int b=10; public int c=10; int d=10; }
-
Interfaces cannot be instantiated separately. Like abstract classes, they need to be inherited and used by subclasses, but implements inheritance is used in interfaces, such as
interface IShape{ public static void draw(); } class Cycle implements IShape{ @Override public void draw(){ System.out.println("Draw a circle⚪"); } }
Unlike extensions, implements means "implementation", which means that there is nothing at present and everything needs to be constructed from scratch
-
The class of the underlying interface needs to override all abstract methods in the interface
-
A class can use implements to implement multiple interfaces, and each interface can be separated by commas, such as
interface A{ void func1(); } interface B{ void func2(); } class C implements A,B{ @Override public void func1(){ } @Override public void func2{ } }
Note that this class needs to override all abstract methods of all inherited interfaces. Use ctrl + i in the IDEA to quickly implement the interface
-
The relationship between interfaces can be maintained using extensions, which means "extension", that is, an interface extends the functions of other interfaces, such as
interface A{ void func1(); } interface B{ void func2(); } interface D implements A,B{ @Override public void func1(){ } @Override public void func2{ } void func3(); }
be careful:
Starting from JDK1.8, the methods in the interface can be ordinary methods, but the premise is that this method is modified by default (that is, the default method of the interface), such as
interface IShape{ void draw(); default public void func(){ System.out.println("Default method"); } }
2. Implement multiple interfaces
As we mentioned earlier, inheritance in Java is single inheritance, that is, a class can only inherit one parent class
However, multiple interfaces can be implemented at the same time, so we can achieve the similar effect of multiple inheritance through multiple interfaces
Next, understand it through the code!
class Animal{ public String name; public Animal(String name){ this.name=name; } } class Bird extends Animal{ public Bird(String name){ super(name); } }
At this time, the child class Bird inherits the parent class Animal, but can no longer inherit other classes, but can continue to implement other interfaces, such as
class Animal{ public String name; public Animal(String name){ this.name=name; } } interface ISwing{ void swing(); } interface IFly{ void fly(); } class Bird extends Animal implements ISwing,IFly{ public Bird(String name){ super(name); } @Override public void swing(){ System.out.println(this.name+"Swimming"); } @Override public void fly(){ System.out.println(this.name+"Flying"); } }
The above code is equivalent to implementing multiple inheritance, so the emergence of the interface solves the problem of Java single inheritance
Moreover, we can feel that the interface seems to have some attributes. Therefore, after having the interface, the user of the class does not have to pay attention to the specific type, but only to whether the class has certain capabilities, such as
public class TestDemo { public static void fly(IFly flying){ flying.fly(); } public static void main(String[] args) { IFly iFly=new Bird("bird"); fly(iFly); } }
Because birds have the attribute of flying, we don't need to pay attention to specific types, because as long as those who can fly can realize the attribute of flying. For example, Superman can also fly, so we can define a superman class
class SuperMan implements IFly{ @Override public void fly(){ System.out.println("Superman is flying"); } } public class TestDemo { public static void fly(IFly flying){ flying.fly(); } public static void main(String[] args) { fly(new SuperMan()); } }
be careful:
Subclasses inherit the parent class before implementing the interface
3. Interface inheritance
As described in the syntax rules, interfaces and interfaces can be maintained using extensions, so that an interface can extend the functions of other interfaces
I won't repeat it here
Let's learn more about interfaces to deepen our understanding of interfaces
4. Comparable interface
We previously introduced the sort method in the Arrays class, which can help us sort, such as
public class TestDemo { public static void main(String[] args) { int[] array={2,9,4,1,7}; System.out.println("Before sorting:"+Arrays.toString(array)); Arrays.sort(array); System.out.println("After sorting:"+Arrays.toString(array)); } }
Next, I want to sort the attributes of a student.
First, I implemented a Student class and overridden the toString method
class Student{ private String name; private int age; private double score; public Student(String name, int age, double score) { this.name = name; this.age = age; this.score = score; } @Override public String toString() { return "Student{" + "name='" + name + '\'' + ", age=" + age + ", score=" + score + '}'; } }
Next, I wrote an array and gave some properties to the student array
public class TestDemo { public static void main(String[] args) { Student[] student=new Student[3]; student[0]=new Student("Zhang San",18,96.5); student[0]=new Student("Li Si",19,99.5); student[0]=new Student("Wang Wu",17,92.0); } }
So can we sort directly through the sort function? Let's write the following code first
public class TestDemo { public static void main(String[] args) { Student[] student=new Student[3]; student[0]=new Student("Zhang San",18,96.5); student[1]=new Student("Li Si",19,99.5); student[2]=new Student("Wang Wu",17,92.0); System.out.println("Before sorting:"+student); Arrays.sort(student); System.out.println("After sorting:"+student); } }
The end result is
Let's analyze it
ClassCastException: type conversion exception, saying that Student cannot be converted to java.lang.Comparable
What does this mean? We think that since Student is a user-defined type and contains multiple types, how can the sort method sort it? There seems to be no basis.
At this time, I found Comparable by reporting an error
You can know that this should be an interface. Then we can try to inherit this interface from our Student class. The following < T > actually means generic. Here, just change it to < Student >
class Student implements Comparable<Student>{ public String name; public int age; public double score; public Student(String name, int age, double score) { this.name = name; this.age = age; this.score = score; } @Override public String toString() { return "Student{" + "name='" + name + '\'' + ", age=" + age + ", score=" + score + '}'; } }
But not at this time, because inheritance needs to rewrite the abstract method of the interface, so after searching, we found it
The added rewrite method is
@Override public int compareTo(Student o) { // New comparison rules }
This should be where the comparison rules are set. Let's take a look at the exchange in the sort method
That is, if the left value is greater than the right value, the exchange is performed
So if I want to sort the students' ages, the rewritten method should be
@Override public int compareTo(Student o) { // New comparison rules return this.age-o.age; }
Run the code again and the result is
Here, we can more deeply feel that the interface is actually a certain attribute or capability, and the above Student class inherits the comparison interface and has the comparison capability
Disadvantages:
-
When we compare the names of the above code, we need to change the rewriting method to
@Override public int compareTo(Student o) { // New comparison rules return this.name.compareTo(o.name); }
-
When we compare the scores of the above code, we need to change the rewriting method to
@Override public int compareTo(Student o) { // New comparison rules return int(this.score-o.score); }
We found that when we want to modify the comparison, we may have to modify the rewriting method again. This limitation is relatively large
In order to solve this defect, the following interface Comparator appears
4. Comparator interface
When we enter the definition of sort method, we can also see a comparison method, in which there are two parameter arrays and Comparator objects
The Comparator interface is used here
What about this interface? We can first define an age comparison class AgeComparator, which is specifically used to compare ages and let him inherit this class
class AgeCompartor implements Comparator<Student>{ }
By holding down ctrl and clicking it, we can jump to its definition. At this time, we can find that there is a method in it
This is different from the compareTo in the above Comparable. Let me rewrite it first
class AgeCompartor implements Comparator<Student>{ @Override public int compare(Student o1, Student o2) { return o1.age-o2.age; } }
We then write the following code according to the description of the sort method
public class TestDemo { public static void main(String[] args) { Student[] student=new Student[3]; student[0]=new Student("Zhang San",18,96.5); student[1]=new Student("Li Si",19,99.5); student[2]=new Student("Wang Wu",17,92.0); System.out.println("Before sorting:"+Arrays.toString(student)); AgeComparator ageComparator=new AgeComparator(); Arrays.sort(student,ageComparator); System.out.println("After sorting:"+Arrays.toString(student)); } }
In this way, we can compare the ages of students normally. At this time, we need to sort the names, and we can create a name comparison class NameComparator
class NameComparator implements Comparator<Student>{ @Override public int compare(Student o1, Student o2) { return o1.name.compareTo(o2.name); } }
We only need to change the parameter ageComparator of the sort method to nameComparator
We can understand the AgeComparator and NameComparator above as comparators, and the limitation of using Comparator is much smaller than that of Comparable. If we want to compare a certain attribute, we just need to add its Comparator
5. Clonable interface and deep copy
First, we can look at such code
class Person{ public String name ="LiXiaobo"; @Override public String toString() { return "Person{" + "name='" + name + '\'' + '}'; } } public class TestDemo { public static void main(String[] args) { Person person=new Person(); } }
What is cloning? It should be to make a copy, for example
Now that I'm talking about clonable interface this time, I'll inherit it!
class Person implements Cloneable{ public String name ="LiXiaobo"; @Override public String toString() { return "Person{" + "name='" + name + '\'' + '}'; } }
But we found that even after inheritance, we can't find a cloning method through the created reference. At this point, we can click the definition of clonable
Great, nothing!
- We found that clonable is an empty interface (also known as tag interface), and the function of this interface is to prove that a class can be cloned if it implements this interface
- Before using it, we also need to override the Object cloning method (all classes inherit from the Object class by default)
How to rewrite the cloning method? By ctrl + o, you can see
Select clone again 🆗, The rewritten code becomes
class Person implements Cloneable{ public String name ="LiXiaobo"; @Override public String toString() { return "Person{" + "name='" + name + '\'' + '}'; } @Override protected Object clone() throws CloneNotSupportedException { return super.clone(); } }
At this point, we can see a clone method
After clicking, we found that we still reported an error
The reason is that the overridden clone method will throw exceptions. There are two methods for this. Today, I will introduce a simpler method
-
Method 1: put the mouse on the clone, press and hold Alt + enter, and you will see
Just click the red box, but you will find that an error is still reported after clicking. This is because the return value of the rewritten method is Object, and the compiler will think it is unsafe, so it can be forcibly converted to Person. At this point, we will output the cloned copy and find that the result is OK
And by printing the address, the copy is different from the original address
So far, the simple cloning process has been introduced. But next, let's think more deeply and add a shaping a to the original code of the Person class
class Person implements Cloneable{ public String name ="LiXiaobo"; public int a=10; @Override public String toString() { return "Person{" + "name='" + name + '\'' + '}'; } @Override protected Object clone() throws CloneNotSupportedException { return super.clone(); } }
At this point, we print through person and person respectively. The code is as follows
public class TestDemo3 { public static void main(String[] args) throws CloneNotSupportedException { Person person=new Person(); Person person1=(Person)person.clone(); System.out.println(person.a); System.out.println(person1.a); System.out.println("#############"); person1.a=50; System.out.println(person.a); System.out.println(person1.a); } }
give the result as follows
We found that in this case, person1 is completely a copy, and its modification has nothing to do with person.
But let's look at the following situation. We define a Money class and create it in Person
class Money{ public int money=10; } class Person implements Cloneable{ public String name ="LiXiaobo"; public Money money=new Money(); @Override public String toString() { return "Person{" + "name='" + name + '\'' + '}'; } @Override protected Object clone() throws CloneNotSupportedException { return super.clone(); } }
Then we modify the value of money in person1. The code is as follows
public class TestDemo3 { public static void main(String[] args) throws CloneNotSupportedException { Person person=new Person(); Person person1=(Person)person.clone(); System.out.println(person.money.money); System.out.println(person1.money.money); System.out.println("#############"); person.money.money=50; System.out.println(person.money.money); System.out.println(person1.money.money); } }
The result this time is
Why? We can analyze the following pictures
Because the cloned object is the person object, only the money of (0x123) is cloned, while the money of (0x456) is not cloned. Therefore, even if the cloned copy of the previous money points to it, the money of the copy will be changed
The above situation is actually called shallow copy, so how to turn it into deep copy?
We just need to clone a copy of the object pointed to by the money reference
Steps:
-
The Money class also implements the clonable interface and overrides the cloning method
class Money implements Cloneable{ public int money=10; @Override protected Object clone() throws CloneNotSupportedException { return super.clone(); } }
-
Modify the cloning method in Person
@Override protected Object clone() throws CloneNotSupportedException { Person personClone=(Person)super.clone(); personClone.money=(Money)this.money.clone(); return personClone; }
This is a deep copy!
4, Summary
The above is my personal understanding of polymorphism, abstract classes and interfaces. It may not be very good, but I have tried my best to explain it. I hope it will be helpful to you!