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!
- byte -> Byte
- boolean -> Boolean
- short -> Short
- char -> Character
- int -> Integer
- long -> Long
- float -> Float
- 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!