抽象类
抽象类必须使用abstract修饰,有抽象方法的类一定是抽象类,抽象类里可以没有抽象方法.
抽象类有如下注意点:
抽象类和抽象方法必须使用abstract修饰,抽象方法不能有方法体.
抽象类不能被实例化,即无法new构造器来创建抽象类的实例,即使抽象类不包含抽象方法
抽象类不能使用final修饰符,抽象方法不能使用final,private,static修饰符,因为抽象方法被继承才有意义
抽象类可以不包含抽象方法
子类的抽象方法不能和父类的抽象方法重名.
实例
abstract class Shape {
//定义一个计算周长的计算方法
public abstract double getPerimeter();
}
class Triangle extends Shape {
//定义三角形的三边
private double a;
private double b;
private double c;
public Triangle(double a, double b, double c) {
this.setSides(a, b, c);
}
private void setSides(double a, double b, double c) {
if (a >= b + c || b >= a + c || c >= a + b) {
System.out.println("三角形两边之和必须大于第三边");
return;
}
this.a = a;
this.b = b;
this.c = c;
}
//重写计算三角形周长的方法
@Override
public double getPerimeter() {
return a + b + c;
}
}
class Circle extends Shape{
private double radius;
public Circle(double r){
this.radius = r;
}
//重写计算周长的抽象方法
@Override
public double getPerimeter() {
return 2 * Math.PI * radius;
}
}
class test {
public static void main(String[] args) {
Shape a = new Triangle(3,4,5);
Shape b = new Circle(5);
System.out.println("三角形的周长: "+a.getPerimeter());
System.out.println("圆的周长:"+b.getPerimeter());
}
}
Shape包含一个计算周长的抽象方法所以是抽象类,不能创建实例,只能作为父类被其他类继承.Circle和Triangle类继承了Shape类且实现了其中的抽象方法,所以可以创建实例,而且可以把一个Shape类型的引用变量指向Circle和Triangle类创建的实例.
利用抽象类和抽象方法的优势,可以更好地发挥多态的优势,使得程序更加的灵活.
从语意来说,抽象类是多个具体类中抽象出来的父类,它具有更高层次的抽象.抽象方法体现了一种模板模式的设计,抽象类作为多个子类的通用模板,子类在抽象类的基础上进行扩展,改造,但是子类总体上会暴露抽象类的行为方式.
接口
接口多用于描述各个类型的对象间所共有的行为,表示“所有实现了这个接口的类的对象都能这么干”.
在接口中包含成员变量(只能是静态常量)、方法(只能是抽象实例方法、类方法和默认方法)、内部类(包括内部接口、枚举).
接口不能被实例化
接口不包含初始化块和构造器
接口支持多继承.可以实现多个接口
接口里没有普通方法
实现接口相当股继承了一个抽象类,如果不能全部实现其中的抽象方法,则该类也必须定义成抽象类
接口主要由三个作用:
1. 定义变量,也可用于进行强制类型转换
2. 调用接口定义的常量
3. 被实现
默认方法
在1.8以前,接口里只能有抽象方法,不支持默认方法和类方法
为什么有默认方法
在 java 8 之前,接口与其实现类之间的 耦合度 太高了(tightly coupled),当需要为一个接口添加方法时,所有的实现类都必须随之修改。默认方法解决了这个问题,它可以为接口添加新的方法,而不会破坏已有的接口的实现。这在 lambda 表达式作为 java 8 语言的重要特性而出现之际,为升级旧接口且保持向后兼容(backward compatibility)提供了途径。
String[] array = new String[] {
"hello",
", ",
"world",
};
List<String> list = Arrays.asList(array);
list.forEach(System.out::println); // 这是 jdk 1.8 新增的接口默认方法
这个 forEach 方法是 jdk 1.8 新增的接口默认方法,正是因为有了默认方法的引入,才不会因为 Iterable 接口中添加了 forEach 方法就需要修改所有 Iterable 接口的实现类。
默认方法的继承
接口默认方法的继承分三种情况(分别对应上面的 InterfaceB 接口、InterfaceC 接口和 InterfaceD 接口):
不覆写默认方法,直接从父接口中获取方法的默认实现。
覆写默认方法,这跟类与类之间的覆写规则相类似。
覆写默认方法并将它重新声明为抽象方法,这样新接口的子类必须再次覆写并实现这个抽象方法。
默认方法的多继承
默认方法在多继承是会遇到一个问题,当继承的两个父接口中两个相同的默认方法时,怎么解决冲突
interface InterfaceA {
default void foo() {
System.out.println("InterfaceA foo");
}
}
interface InterfaceB {
default void bar() {
System.out.println("InterfaceB bar");
}
}
interface InterfaceC {
default void foo() {
System.out.println("InterfaceC foo");
}
default void bar() {
System.out.println("InterfaceC bar");
}
}
class ClassA implements InterfaceA, InterfaceB {
}
// 错误
//class ClassB implements InterfaceB, InterfaceC {
//}
class ClassB implements InterfaceB, InterfaceC {
@Override
public void bar() {
InterfaceB.super.bar(); // 调用 InterfaceB 的 bar 方法
InterfaceC.super.bar(); // 调用 InterfaceC 的 bar 方法
System.out.println("ClassB bar"); // 做其他的事
}
}
在 ClassA 类中,它实现的 InterfaceA 接口和 InterfaceB 接口中的方法不存在歧义,可以直接多实现。
在 ClassB 类中,它实现的 InterfaceB 接口和 InterfaceC 接口中都存在相同签名的 foo 方法,需要手动解决冲突,必须重写存在歧义的方法,并可以使用 InterfaceName.super.methodName(); 的方式手动调用需要的接口默认方法。
抽象类和接口
相同点:
- 都不能实例化,位于继承树的顶端,用于被其他类继承和实现
- 都可以包含抽象方法,实现接口或者继承抽象类的普通类必选全部实现这些抽象方法
差别:
1. 设计目的:接口体现的一种规范,类似于整个系统的总纲,制订了系统各模块应该遵循的标准,因此接口不应该经常改变,一旦改变对整个系统是辐射性的。抽象类作为多个子类的共同父类,体现的是一种模板式设计,可以当作系统实现过程中的中间产品,已经实现了系统部分功能。
2. 使用不同:
1. 接口只能包含抽象方法和默认方法和类方法,不能为普通方法提供实现,抽象类可以包含普通方法。
2. 接口只能定义静态常量属性不能定义普通属性,抽象类可以。
3. 接口不包含构造器,抽象类可以(不是用于创建对象而是让子类完成初始化)。
4. 接口里不能包含初始化块,抽象类完全可以。
5. 接口多继承,抽象类单继承(只能有一个直接父类)。
在组成形式上抽象类除了包含抽象方法外与普通类无区别。
---------------------
原文:https://blog.csdn.net/dora_310/article/details/77248554
497

被折叠的 条评论
为什么被折叠?



