Abstract classes and interfaces in java (often tested in interviews, important)!!

abstract class

rule of grammar

In the example of polymorphic code about Shape, we find that the draw method in the parent class Shape does not seem to have any actual work, and the main drawing graphics are completed by the draw methods of various subclasses of Shape. Like this method without actual work, we can design it as an abstract method, Classes containing abstract methods are called abstract classes

abstract class Shape {
            abstract public void draw();
        }
  • Add the abstract keyword before the draw method to indicate that it is an abstract method. At the same time, the abstract method has no method body (without {}, it cannot execute concrete code)
  • For a class containing abstract methods, the abstract keyword must be added to indicate that it is an abstract class

Precautions (important, all mastered)

  • Abstract method: if a method is modified by abstract, then the method is an abstract method. Abstract methods can have no concrete implementation.

    At the same time, abstract methods cannot be modified by private, because once modified by private, non Abstract subclasses cannot override the abstract methods private to the parent class.

  • A class containing abstract methods is called an abstract class, which must be modified by abstract. An abstract class may not have abstract methods, but if there are abstract methods in a class, the class must be an abstract class, which must be modified by abstract

  • Abstract classes cannot be instantiated. You cannot use, for example, Shape shape = new Shape(); Such a statement

    However, it does not affect the upward transformation of abstract classes, so abstract classes cannot be instantiated, but upward transformation can occur

  • Data members in a class are no different from ordinary classes. They can contain other non abstract methods or fields. The rules of this non abstract method and ordinary methods are the same and can be overridden or called directly by subclasses

  • Abstract classes are mainly used to be inherited

  • If a non abstract class inherits the abstract class, the class must override the abstract methods in the abstract class. (important)

  • When abstract class A inherits abstract class B, A may not override the abstract methods in B, but once A is inherited by A non abstract class c, the abstract methods in A and B must be overridden in class c
    Code example:

abstract class A {
    abstract public void eat();

    public void drink() {

    }
}

abstract class C extends A {
    abstract public void fly();
}

class b extends C {
    @Override
    public void eat() {
        System.out.println("eat");
    }

    @Override
    public void fly() {
        System.out.println("fly");
    }
}
  • Abstract classes and abstract methods must not be modified by final, because once a class is modified by final, it cannot inherit, and methods are modified by final and cannot be overridden
    Abstract classes cannot be instantiated for the purpose of inheritance and rewriting, so they cannot be used at the same time
  • When an abstract class implements an interface, it does not need to rewrite the interface method, that is, it can rewrite a part without rewriting a part
  • Abstract classes have construction methods, but they cannot be used, that is, they cannot create concrete objects

Role of abstract classes

 Abstract classes exist in order to be inherited.

The abstract class itself cannot be instantiated. If you want to use it, you can only create a subclass of the abstract class. Then let the subclass override the abstract methods in the abstract class
Some students may say that ordinary classes can also be inherited and ordinary methods can also be rewritten. Why do you have to use abstract classes and abstract methods?

A: that's true. But using abstract classes is equivalent to an extra check of the compiler
The scenario of using abstract classes is like the above code. The actual work should not be completed by the parent class, but by the child class
If the parent class is accidentally misused at this time, the ordinary class compiler will not report an error. However, if the parent class is an abstract class, it will prompt an error when instantiating, so that we can find the problem as soon as possible

The meaning of many grammars is to "prevent errors". For example, the "nal" we have used is similar. If the user does not modify the created variable, it is equivalent to a constant? But with "nal", the compiler can remind us in time when we accidentally modify it
Making full use of compiler verification is very meaningful in practical development

In actual development, the role of abstract classes is also very important:

Abstract classes can reduce the difficulty of interface implementation by interface implementation classes, because in actual development, many interfaces may not be used in an interface. When a non abstract class inherits this interface, it is necessary to rewrite all abstract methods in this interface, resulting in code redundancy. In order to avoid this situation, it is necessary to use abstract classes in the interface Do not need to use the abstract method to rewrite, will need to use the abstract method inherited
In this way, other classes only need to inherit different abstract classes, find the abstract classes they need according to their own business requirements, and then rewrite the abstract methods in the abstract classes, so as to reduce the difficulty in the process of interface implementation.

Interface

An interface is a further step of an abstract class. An abstract class can also contain non abstract methods and fields. While the methods contained in an interface are abstract methods, and fields can only contain static constants

rule of grammar

Let's summarize directly through a piece of code:

1.interface Shape1 {  
 -    //All member variables defined in the interface will be constant by default and modified by public static final by default, so it doesn't matter if you don't write public static final,  
 -    int a = 10;  
 -    public static final String name = "sss";  
 -  
 -    //Almost all the methods in the interface are abstract methods, which are modified by public abstract by default, so it doesn't matter if you don't write public abstract  
 -    void draw();  
 -  
 -    //Of course, non abstract methods can also be defined in the interface. Just use the default keyword. Default is the keyword introduced in java8. See the csdn blog for details  
 -    default void drink() {  
 -        System.out.println("drink water");  
 -    }  
13.}  
 -  
15.class Cycle1 implements Shape1 {  
 -    @Override  
 -    public void draw() {  
 -        System.out.println("Draw one⚪");  
 -    }  
 -  
21.}  
 -  
23.class React1 implements Shape1 {  
 -    @Override//annotation  
 -    public void draw() {  
 -        System.out.println("Draw one□");  
 -    }  
 -  
29.}  
 -  
31.public class TestMain {  
 -    public static void fun(Shape1 shape) {  
 -        shape.draw();  
 -    }  
 -  
 -    public static void main(String[] args) {  
 -        //Interfaces can also be transformed upward, provided that a class must implement this interface  
 -        //For example, in the following code, because the Cycle1 class implements the Shape1 interface, the shape reference of the interface type can point to the instance of Cycle1 class  
 -        Shape1 shape = new Cycle1();  
 -        Shape1 shape1=new React1();  
 -        shape.draw();  
 -        shape1.draw();  
 -    }  
44.}  
  • Define an interface using interface
  • The methods in the interface must be abstract methods, so abstract can be omitted
  • The methods in the interface must be public, so public can be omitted
  • Cycle uses implements to inherit the interface. At this time, the meaning expressed by implements is no longer "extension", but "implementation"
  • When calling, you can also create an interface reference corresponding to an instance of a subclass
  • Interfaces cannot be instantiated alone

matters needing attention

  • All methods in the interface are abstract methods. The default prefix is public abstract, which can be omitted when writing, because the compiler defaults to public abstract
  • Abstract classes can actually have concrete implementation methods. This method is modified by default (added in JDK 1.8)
  • The interface can only contain static constants. The prefix of all constants is public static by default
    Final, which can be omitted when writing, because the compiler defaults to public static final as the member variable
  • The default member variable in the interface is public static final. The member method is public abstract
  • Interfaces cannot be instantiated. Shape shape = new Shape(); (not allowed)
  • Relationship between interface and class: implements. When a non abstract class implements this interface and there are abstract methods in the interface, this class must override the abstract methods in the interface
  • Interfaces appear to implement multiple inheritance. A class can implement multiple interfaces. However, it can only inherit one parent class
  • As long as the class implements the interface, it can be transformed upward.
  • Of course, an interface can also inherit (extend) multiple interfaces

The difference between extensions and implementations

Extension refers to the further expansion of functions when certain functions already exist
Implementation means that there is nothing at present and needs to be constructed from scratch

Tips

1. When we create an interface, the name of the interface generally starts with the capital letter I
2. The naming of the interface generally uses the word of "adjective"
3. Alibaba code specification stipulates that methods and attributes in the interface should not be decorated with any symbols to keep the code concise
Without any modifiers, it means that constants omit public static final and abstract methods omit the prefix public abstract
An error code:

interface IShape {
    // Even if you do not write public, it defaults to public permission
    abstract void draw();
}

class Rect implements IShape {
    void draw() {
        //The permission is stricter, so it cannot be overridden. This means that public must be added when overriding the draw method in the Rect class
        System.out.println("□");
    }
}

Class implements multiple interfaces

Sometimes we need to make a class inherit from multiple parent classes at the same time. This is realized by multiple inheritance in some programming languages
However, only single inheritance is supported in Java, and a class can only extend one parent class. However, multiple interfaces can be implemented at the same time, and the similar effect of multiple inheritance can be achieved. Now we represent a group of animals through classes

class Animal {
    protected String name;

    public Animal(String name) {
        this.name = name;
    }
}

In addition, we provide another set of interfaces, which respectively mean "can fly", "can run" and "can swim"

interface IFlying {
    void fly();
}

interface IRunning {
    void run();
}

interface ISwimming {
    void swim();
}

Next, we create several specific animals
Cats can run

class Cat extends Animal implements IRunning {
    public Cat(String name) {
        super(name);
    }

    @Override
    public void run() {
        System.out.println(this.name + "Running on four legs");
    }
}

Fish can swim

class Fish extends Animal implements ISwimming {
    public Fish(String name) {
        super(name);
    }

    @Override
    public void swim() {
        System.out.println(this.name + "He is swimming with his tail");
    }
}

Frogs can run and swim (amphibians)

class Frog extends Animal implements IRunning, ISwimming {
    public Frog(String name) {
        super(name);
    }

    @Override
    public void run() {
        System.out.println(this.name + "Jumping forward");
    }

    @Override
    public void swim() {
        System.out.println(this.name + "He is kicking his legs to swim");
    }
}

There is a magical animal called "duck", which lives in water, land and air

class Duck extends Animal implements IRunning, ISwimming, IFlying {
    public Duck(String name) {
        super(name);
    }

    @Override
    public void fly() {
        System.out.println(this.name + "Flying with wings");

    }

    @Override
    public void run() {
        System.out.println(this.name + "He is running on two legs");
    }

    @Override
    public void swim() {
        System.out.println(this.name + "Floating on the water");
    }
}

The above code shows the most common usage in Java object - oriented programming: a class inherits a parent class and implements multiple interfaces at the same time
The meaning of inheritance expression is is - a semantics, while the meaning of interface expression is xxx

A cat is an animal that can run
Frog is also an animal. It can run and swim
Duck is also an animal. It can run, swim and fly

What are the benefits of this design? Always keep the benefits of polymorphism in mind and let the program forget the type. With an interface, the user of a class does not have to focus on the specific type, but only on whether a class has some ability
For example, now implement a method called "walking"

public static void walk(IRunning running) {
        running.run();
}

Inside the walk method, we don't pay attention to what kind of animal it is. As long as the parameter can run, it should be noted that the premise of running is that this class must implement the IRunning interface

        //Because the Cat class implements the IRunning interface at this time, the upward transformation can be used at this time, as shown below. If the IRunning interface is not implemented, an error will be reported
        IRunning iRunning = new Cat("Cat cat");
        walk(iRunning);

        //Similarly, because the Frog class implements the IRunning interface at this time, the upward transformation can be used at this time, as shown below. If the IRunning interface is not implemented, an error will also be reported
        IRunning iRunning1 = new Frog("frog");
        walk(iRunning1);

Interface usage instance (Comparable interface and Comparator interface)

Comparable interface

The example just now is more abstract. Let's take another more practical example
Sort an array of objects
Given a student class

class Student {
    private String name;
    private int score;

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

    @Override
    public String toString() {
        return "[" + this.name + ":" + this.score + "]";
    }
}

Give another student object array:

Student[]students=new Student[]{
        new Student("Zhang San",95),
        new Student("Li Si",96),
        new Student("Wang Wu",97),
        new Student("Zhao Liu",92),
        };

Now sort the elements in this object array (in descending order of scores)
According to our previous understanding, we have a ready-made sort method for arrays. Can we use this method directly?

Arrays.sort(students);
System.out.println(Arrays.toString(students));

// Run error, throw exception
        Exception in thread"main"java.lang.ClassCastException:Student cannot be cast to java.lang.Comparable

We will find that type conversion exceptions will occur at this time. After careful consideration, it is not difficult to find that, unlike ordinary integers, two integers can be directly compared and the size relationship is clear. How to determine the size relationship of two student objects? We need to specify it additionally
If we don't specify, we don't know whether to compare according to the student's name or the student's age
At this point, let our Student class implement the Comparable interface and rewrite the abstract compareTo method. Let's look at the code below:

1.//Comparing the size of a custom type requires the implementation of a Comparable interface, < > is a generic type  
2.//For Comparable interfaces, they are generally defined inside the class
3.class Student implements Comparable<Student> {  
4.    public String name;  
5.    public int age;  
6.    public int score;  
7.  
8.    public Student(String name, int age, int score) {  
9.        this.name = name;  
10.        this.age = age;  
11.        this.score = score;  
12.    }  
13.  
14.    //Override toString method  
15.    @Override  
16.    public String toString() {  
17.        return "Student{" +  
18.                "name='" + name + '\'' +  
19.                ", age=" + age +  
20.                ", score=" + score +  
21.                '}';  
22.    }  
23.  
24.    //Because the comparable interface is implemented at this time, you need to rewrite its internal abstract method compareTo  
25.    @Override  
26.    public int compareTo(Student o) {  
27.        //Sort by score. If you want to directly modify score by age, replace it with age  
28.        //If it is greater than return 1 and less than return -1, it indicates that it is arranged from small to large
29.        //If it is greater than return -1 and less than return 1, it indicates that it is arranged in descending order
30.        if(this.score > o.score) {  
31.            return 1;  
32.        }else if(this.score == o.score) {  
33.            return 0;  
34.        }else {  
35.            return -1;  
36.        }  
37.    }  
38.}  
39.public class TestDemo2 {  
40.  
41.    public static void main(String[] args) {  
42.        //If you want to compare the size of the Student class reference, you need the Student class to implement the Comparable interface  
43.        Student student1 = new Student("bit",18,79);  
44.        Student student2 = new Student("gao",29,70);  
45.        Student student3 = new Student("shasha",17,99);  
46.          
47.        Student[] students = new Student[3];  
48.        students[0] = student1;  
49.        students[1] = student2;  
50.        students[2] = student3;  
51.  
52.        //The sort method sorts from small to small by default  
53.        Arrays.sort(students);  
54.        System.out.println(Arrays.toString(students));  
55.    }  
56.}

At this time, we sort by score, and the compareTo method will be called automatically in the sort method. The parameter of compareTo is Object, which is actually an Object of Student type. Then compare the size relationship between the current Object and the parameter Object (calculated by score)

If the current object should be placed before the parameter object, a number less than 0 is returned;
If the current object should be placed after the parameter object, a number greater than 0 is returned;
If the current object and the parameter object are in no order, return 0;

Execute the program again and the result will be as expected

//Execution results
[Student{name='gao', age=29, score=70}, Student{name='bit', age=18, score=79}, Student{name='shasha', age=17, score=99}]

be careful:
However, the above comparison method has limitations, because the above comparison method directly writes the comparison dead. It can be said that only age can be compared, not names and other things. But now I want to compare what to do with other things other than age. At this time, the comparator is used
For the sort method, each object of the array to be passed in is "comparable" and needs the ability of compareTo. By overriding the compareTo method, you can define the comparison rules

Comparator interface (comparator)

Differences between Comparable interface and Comparator interface

First of all, we know that the comparable interface is defined inside the class to implement object comparison, and our Comparator is specifically defined outside the class

Code example

It is the same as the previous Student class. The difference is that the Student class no longer implements the comparable interface:

At this time, we want to sort the students according to the scores, so redefine a public class outside the class to implement the Comparator interface, and rewrite the compare method in the Comparator interface to realize our comparison and sorting of scores: look at the code:
First, let's look at the compare method in the Comparator interface:

Look at the code for score comparison

//ScoreComparator.java class
public class ScoreComparator implements Comparator<Student> {
    @Override
    public int compare(Student o1, Student o2) {
        return o1.score - o2.score;
    }
}

If you want to compare the names of students, look at the code implementation:

public class NameComparator implements Comparator<Student> {

    @Override
    public int compare(Student o1, Student o2) {

        return o1.name.compareTo(o2.name);
    }
}

This code can no longer use o1.name-o2.name. The reason is that our name is a String type and cannot be added or subtracted. Therefore, we should use the compareTo method here. Some students may wonder that the compareTo method appears in the comparable interface. Why do we use this here?
The answer is as follows:

Let's first look at the source code of the String class:

You can see that the String class implements our comparable interface and rewrites the compareTo method in our comparable interface

Therefore, as for the name Comparator, we can know that although it implements the Comparator interface, the compareTo method used internally comes from the comparable interface
Here's how to compare names and scores in the main function:

class Student {
    public String name;
    public int score;

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

    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", score=" + score +
                '}';
    }

}

public class TestDemo2 {

    public static void main(String[] args) {
        Student[] students = new Student[3];
        students[0] = new Student("bit", 89);
        students[1] = new Student("abc", 19);
        students[2] = new Student("htf", 59);
        System.out.println("===============Before no sorting==============");
        System.out.println(Arrays.toString(students));
        System.out.println("===============After sorting by score==============");
        ScoreComparator scoreComparator = new ScoreComparator();
        //Pass in two parameters
        Arrays.sort(students, scoreComparator);
        //The output result is: [Student{name='abc', score=19}, Student{name='htf', score=59}, Student{name='bit', score=89}]
        System.out.println(Arrays.toString(students));

        System.out.println("===============After sorting by name==============");
        NameComparator nameComparator = new NameComparator();
        Arrays.sort(students, nameComparator);
        //The output result is: [Student{name='abc', score=19}, Student{name='bit', score=89}, Student{name='htf', score=59}]
        //The above names are sorted alphabetically
        System.out.println(Arrays.toString(students));
    }
}

Inheritance (extension) between interfaces

Interfaces can inherit one interface or inherit (extend) multiple interfaces, which has achieved the effect of reuse. Use the extends keyword. Of course, the inheritance relationship here is more accurate when we understand it as extension

interface IRunning { void run();
}

interface ISwimming { void swim();
}

// Amphibians can run and swim
interface IAmphibious extends IRunning, ISwimming {
  //At this time, the IAmphibious interface is equivalent to having two methods, one is run and the other is swim
}

class Frog implements IAmphibious {
 //At this time, the Frog class must override the two abstract methods of the IAmphibious interface, a run method and a swim method
}

Create a new interface through interface inheritance. IAmphibious means "amphibious". At this time, to implement the Frog class created by the interface, you need to continue to implement the run method and the swim method

Tags: Java interface JavaSE abstract class

Posted on Wed, 29 Sep 2021 18:23:15 -0400 by jjbarnone