1.多态的概念
同一操作作用于不同的对象,可以有不同的解释,产生不同的执行结果,这就是多态性。
- 多态是同一个行为具有多个不同表现形式或形态的能力。
- 多态是方法的多态,不是属性的多态(多态与属性无关)
- 父类引用指向子类对象后,用该父类引用调用子类重写的方法,此时多态就出现了。
1.1 多态的优势
- 继承
- 方法重写
- 父类引用指向子类对象
Parent p = new Child();
当使用多态方式调用方法时,首先检查父类中是否有该方法,如果没有,则编译错误;如果有,再去调用子类的同名方法。
多态的好处:可以使程序有良好的扩展,并可以对所有类的对象进行通用处理。
2.多态实现方式
接口实现、抽象类、继承父类进行方法重写、同一个类中进行方法重载。
2.1 抽象类和抽象方法
延伸拓展,如果在抽象类中增加方法,底层所有继承此类的子类都不需要修改,如果需调用,则直接调用父类方法即可。
抽象类是对整个类整体(属性、行为)进行抽象,但是接口却是对类局部(行为)进行抽象,所以抽象类一个是在项目开始就得确定好的公共类。
2.2 接口实现
辐射拓展,如果接口中新增加了方法,则需要修改所有实现此接口的实现方法。
- 接口中的变量会被隐式地指定为
public static final
变量。 - 接口中的方法都是抽象方法。
- 一个类可继承多个接口。
-
2.3 方法重写
2.4 方法重载
3.抽象类与接口
Ref: https://pdai.tech/md/java/basic/java-basic-lan-basic.html
3.1 抽象类
抽象类和抽象方法都使用 abstract 关键字进行声明。抽象类一般会包含抽象方法,抽象方法一定位于抽象类中。
抽象类和普通类最大的区别是,抽象类不能被实例化,需要继承抽象类才能实例化其子类。 ```java public abstract class AbstractClassExample {protected int x; private int y;
public abstract void func1();
public void func2() {
System.out.println("func2");
} }
public class AbstractExtendClassExample extends AbstractClassExample { @Override public void func1() { System.out.println(“func1”); } }
// AbstractClassExample ac1 = new AbstractClassExample(); // ‘AbstractClassExample’ is abstract; cannot be instantiated AbstractClassExample ac2 = new AbstractExtendClassExample(); ac2.func1();
<a name="vXp9b"></a>
### 3.2 接口
接口是抽象类的延伸,在 Java 8 之前,它可以看成是一个完全抽象的类,也就是说它不能有任何的方法实现。<br />从 Java 8 开始,接口也可以拥有默认的方法实现,这是因为不支持默认方法的接口的维护成本太高了。在 Java 8 之前,如果一个接口想要添加新的方法,那么要修改所有实现了该接口的类。<br />接口的成员 (字段 + 方法) 默认都是 public 的,并且不允许定义为 private 或者 protected。<br />接口的字段默认都是 static 和 final 的。
```java
public interface InterfaceExample {
void func1();
default void func2(){
System.out.println("func2");
}
int x = 123;
// int y; // Variable 'y' might not have been initialized
public int z = 0; // Modifier 'public' is redundant for interface fields
// private int k = 0; // Modifier 'private' not allowed here
// protected int l = 0; // Modifier 'protected' not allowed here
// private void fun3(); // Modifier 'private' not allowed here
}
public class InterfaceImplementExample implements InterfaceExample {
@Override
public void func1() {
System.out.println("func1");
}
}
// InterfaceExample ie1 = new InterfaceExample(); // 'InterfaceExample' is abstract; cannot be instantiated
InterfaceExample ie2 = new InterfaceImplementExample();
ie2.func1();
System.out.println(InterfaceExample.x);
3.3 比较
从设计层面上看:
- 抽象类提供了一种 IS-A 关系,那么就必须满足里式替换原则,即子类对象必须能够替换掉所有父类对象(是不是)。而接口更像是一种 LIKE-A 关系,它只是提供一种方法实现契约(有没有),并不要求接口和实现接口的类具有 IS-A 关系。
- 抽象类是对整个类整体(属性、行为)进行抽象,但是接口却是对类局部(行为)进行抽象,所以抽象类一个是在项目开始就得确定好的公共类。
从使用上来看:
- 一个类可以实现多个接口,但是不能继承多个抽象类。
- 接口的字段只能是 static 和 final 类型的,而抽象类的字段没有这种限制。
接口的成员只能是 public 的,而抽象类的成员可以有多种访问权限。
3.4 使用选择
使用接口:
需要让不相关的类都实现一个方法,例如不相关的类都可以实现 Compareable 接口中的 compareTo() 方法;
- 需要使用多重继承;
使用抽象类:
- 需要在几个相关的类中共享代码。
- 需要能控制继承来的成员的访问权限,而不是都为 public。
- 需要继承非静态和非常量字段。
在很多情况下,接口优先于抽象类,因为接口没有抽象类严格的类层次结构要求,可以灵活地为一个类添加行为。并且从 Java 8 开始,接口也可以有默认的方法实现,使得修改接口的成本也变的很低。