Java --- object and polymorphism

Posted by brain on Thu, 24 Feb 2022 10:47:15 +0100

Three characteristics of object-oriented: encapsulation, inheritance and polymorphism

encapsulation

In order to ensure the security of variables, we use encapsulation, do not care about the specific implementation details, but only access the members of the class through external excuses.

public class Student {
    private String name;
    private int age;
  
    public Student(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public int getAge() {
        return age;
    }

    public String getName() {
        return name;
    }
}

In this class, if an external wants to obtain the member properties, it can only call the defined methods.

public void setName(String name) {
    if(name.contains("Small")) return;
    this.name = name;
}

In fact, the encapsulation idea is to hide the implementation details. The outside only needs to know what the function of this method is, and there is no need to care about the implementation.

inherit

When defining different classes, there are some same attributes. For convenience, these common attributes can be abstracted into a parent class. When defining other subclasses, they can inherit from the parent class to reduce repeated code definition. Subclasses can use non private members in the parent class.

Inheritance keyword: extends

The subclass has all the attributes of the parent class. protected is visible, but cannot be used externally

Subclasses have all the attributes of the parent class. protected is visible but cannot be used externally (including private attributes, invisible and unusable). At the same time, subclasses can also have their own methods. Inheritance can only inherit one parent class. Multiple inheritance is not supported!

Each subclass must define a construction method that implements the construction method of the parent class, that is, it needs to use super() at the beginning of the construction method. If the parent class uses the default construction method, the subclass does not need to indicate it manually.

polymorphic

Polymorphism is the ability of the same behavior to have multiple different forms or forms. That is, for the same method, due to different implementation classes, the execution results are also different!

Method rewriting

We learned about method overloading before. Method rewriting and overloading are different. Overloading supports the implementation of more parameters when the original method logic remains unchanged, and rewriting directly covers the original method!

//study in parent class
public void study(){
    System.out.println("study");
}

//study in subclass
@Override  //Declare that this method is rewritten, but it can't be. We won't touch it at this stage
public void study(){
    System.out.println("Show you something good");
}

After defining the same method again, the method of the parent class will be overwritten! Subclasses can also increase access rights to parent class methods!

public static void main(String[] args) {
     SportsStudent student = new SportsStudent("lbw", 20);
     student.study();   //Output the content of subclass definition
}

On type conversion

We have learned the type conversion of basic data types, which supports the conversion from one data type to another, and our classes also support type conversion (only between classes with kinship). For example, subclasses can be directly transformed upward:

Student student = new SportsStudent("lbw", 20);  //Parent variables refer to subclass instances
student.study();     //The result is still the result of concrete implementation, not the result of the current type

We can also forcibly convert the parent class reference that has been clearly implemented by which class to the corresponding type (downward transformation):

Student student = new SportsStudent("lbw", 20);  //It is implemented by SportsStudent
//... do something...

SportsStudent ps = (SportsStudent)student;  //Make it a concrete subclass
ps.sport();  //Call the method of the concrete implementation class

instanceof keyword

So what if we just get a parent class reference, but don't know which subclass it is? We can use the instanceof keyword to realize it, which can judge the type!

private static void test(Student student){
    if (student instanceof SportsStudent){
        SportsStudent sportsStudent = (SportsStudent) student;
        sportsStudent.sport();
    }else if (student instanceof ArtStudent){
        ArtStudent artStudent = (ArtStudent) student;
        artStudent.art();
    }
}

Through type judgment, we can determine which class is the specific implementation of the class.

final keyword

public final class Student {   //If a class is declared as a final state, can it be inherited
  	
}

Once a class is declared as the final state, it can no longer be inherited and subclasses are not allowed to exist.

If the member attribute of a class is declared as final, it must be given an initial value in the construction method or at the time of definition!

private final String name;   //Reference type is not allowed to point to other objects
private final int age;    //The basic type value cannot be changed

public Student(String name, int age) {
    this.name = name;
    this.age = age;
}

abstract class

Abstract classes can only retain features, not specific presentation forms. For example, methods can be defined, but I can leave it to subclasses instead of implementing it!

public abstract class Student {    //abstract class
		public abstract void test();  //Abstract method
}

By using the abstract keyword to indicate that a class is an abstract class, an abstract class can use the abstract keyword to indicate that a method is an abstract method, or you can define a common method. An abstract method does not need to write a concrete implementation (no method body), but must be implemented by a subclass (unless the subclass is also an abstract class)!

Because the abstract class is not a concrete class definition, it is impossible to create an object directly through the new keyword. Therefore, abstract classes are generally only used for inheritance! Abstract classes make inheritance relationships more explicit.

Interface

The interface only represents an exact function and only contains the definition of methods. The interface contains the specific definitions of some columns of methods. The class can implement this interface, indicating that the class supports the functions represented by the interface (similar to a plug-in, it can only be added to the main body as a subsidiary function, and the specific implementation needs to be implemented by the main body)

public interface Eat {
	void eat(); 
}

Use the interface keyword to indicate that it is an interface (note that the class keyword is replaced with interface here). The interface can only contain abstract methods with public permissions! (there can be a default implementation after Java 8) we can give the abstract method a default implementation by declaring the default keyword:

public interface Eat {
    default void eat(){
        //do something...
    }
}

The variables defined in the interface are public static final by default

A class can implement many interfaces, but it cannot be understood as multiple inheritance! (in fact, the implementation interface is an additional function, which is different from the concept of inheritance. At most, it is an alternative to multi inheritance.) a class can attach many functions! Class declares the implemented interface through the implements keyword! Each interface is separated by commas!

public class SportsStudent extends Student implements Eat, ...{
		@Override
    public void eat() {
        
    }
}

Inner class

A class can exist in a class.

Member inner class

One class can be nested in our class:

public class Test {
    class Inner{   //Class
        
    }
}

Member internal classes and member variables, like member methods, belong to objects, that is, external objects must exist to create objects of internal classes!

Static inner class

Static internal classes, like static variables and static methods in a class, belong to a class. We can use the class name directly To access:

Local inner class

Reference local variable

Anonymous Inner Class

Anonymous inner classes are actually the direct implementation of interfaces or abstract classes during new:

public static void main(String[] args) {
        Eat eat = new Eat() {
            @Override
            public void eat() {
                //DO something...
            }
        };
    }

lambda expressions

It is a simplification of the anonymous implementation of our interface, for example:

public static void main(String[] args) {
        Eat eat = new Eat() {
            @Override
            public void eat() {
                //DO something...
            }
        };
    }

public static void main(String[] args) {
        Eat eat = () -> {};   //Equivalent to the above
    }

lambda expressions (anonymous inner classes) can only access external final types or local variables of implicit final types.

Enumeration class

Suppose we want to add a status (running, learning, sleeping) to Xiao Ming, and the external can get Xiao Ming's status in real time:

public class Student {
    private final String name;
    private final int age;
    private String status;
  
  	//...
  
  	public void setStatus(String status) {
        this.status = status;
    }

    public String getStatus() {
        return status;
    }
}

But there will be a problem. If we only store strings, it seems that the outside can pass in some other strings instead of our rules. This is obviously not rigorous enough!

Is there a better way to implement such status markers? We hope that developers can use the defined state. We can use enumeration classes!

public enum Status {
    RUNNING, STUDY, SLEEP    //Just write the name of each status directly. You can not type a semicolon, but it is recommended to type it
}

It is also very convenient to use enumeration classes. We only need to access them directly

public class Student {
    private final String name;
    private final int age;
    private Status status;
  
 		//...
  
  	public void setStatus(Status status) {   //It is no longer String, but the enumeration type we specify
        this.status = status;
    }

    public Status getStatus() {
        return status;
    }
}

public static void main(String[] args) {
    Student student = new Student("Xiao Ming", 18);
    student.setStatus(Status.RUNNING);
    System.out.println(student.getStatus());
}

Enumeration type is very convenient to use. In fact, the essence of enumeration type is an ordinary class, but it inherits from Enum class. Each state we define is actually a member variable of Status type of public static final!

public enum Status {
    RUNNING("sleep"), STUDY("study"), SLEEP("sleep");   //The parameterless constructor is overwritten, and parameters need to be added to create enumeration (essentially the constructor to be called!)

    private final String name;    //Enumerated member variables
    Status(String name){    //Overwrite the original construction method (private by default, can only be used internally!)
        this.name = name;
    }
  
  	public String getName() {   //Get encapsulated member variables
        return name;
    }
}

public static void main(String[] args) {
    Student student = new Student("Xiao Ming", 18);
    student.setStatus(Status.RUNNING);
    System.out.println(student.getStatus().getName());
}
Status.valueOf("")   //Converts a string with the same name to an enumeration
Status.values()   //Get all enumerations quickly

Basic type packing class

Java language is an object-oriented language, but the basic data types in Java are not. At this time, we need to use the basic type wrapper class. If we want to use our basic types in the form of objects, the basic type wrapper class provided by Java enables Java to better reflect the idea of object-oriented, and also enables the basic types to support object operation!

  1. byte -> Byte
  2. boolean -> Boolean
  3. short -> Short
  4. char -> Character
  5. int -> Integer
  6. long -> Long
  7. float -> Float
  8. double -> Double

The wrapper class actually encapsulates our basic data type into a class

private final int value;   //In fact, there is a basic type of data in Integer, but we can't operate it directly

public Integer(int value) {
    this.value = value;
}

Automatic packing and unpacking

The packaging type can directly use a specific value for assignment operation. The seventy-one opening is the automatic packing and automatic unpacking mechanism.

Integer i = 1;    //Actually, it's just a shorthand here
Integer i = Integer.valueOf(1);  //What it really looks like after compilation

Call valueOf to generate an Integer object.

public static Integer valueOf(int i) {
    if (i >= IntegerCache.low && i <= IntegerCache.high)   //Note that Java has a caching mechanism for optimization. If the number is between - 128 and 127, it will directly use the cached objects instead of creating new ones! (regular interview)
       return IntegerCache.cache[i + (-IntegerCache.low)];
  	return new Integer(i);   //Returns a newly created object
}

If the packing class is used for operation or assigned to a basic type variable, it will be unpacked automatically:

public static void main(String[] args) {
    Integer i = Integer.valueOf(1);
    int a = i;    //Abbreviation
    int a = i.intValue();   //Actual code after compilation
  
  	long c = i.longValue();   //Other types are also available!
}

Packing objects with = = is actually used to judge whether two objects are the same object (whether the memory address is the same), and the value judgment uses equals

public static void main(String[] args) {
    Integer i1 = 28914;
    Integer i2 = 28914;

    System.out.println(i1 == i2);   //In fact, it is judged whether the two objects are the same object (whether the memory address is the same)
    System.out.println(i1.equals(i2));   //This is the real value judgment!
}

Quick sort (sorting algorithm, recursive divide and conquer)

Quick sort is actually a sort algorithm with high efficiency. It uses divide and conquer to sort the sort sequence. Its idea is to separate the records to be sorted into two independent parts through one-time sorting. One part is smaller than the key word and the latter part is larger than the keyword, Then the two parts before and after this are sorted in this way, and the whole sequence is finally ordered through recursive operation. Quick sort is like its name, quick!
 

Topics: Java Back-end JavaSE IDEA