【前言】
extends 和 implements 是 JAVA 里面非常重要的两个概念,发现 百度 里面搜的博客很多写的好差,特意回头再梳理一下。
extends
extends表示继承,无法继承声明为final或 abstract 的类,并且只能继承 一个父类(不像 C语言 可以继承多个),继承后可以使用父类的方法,也可以重写父类的方法、写自己的方法。
- abstract 抽象类 可以继承其它类和实现其他接口,而且不需要实现或者重写其中的方法;
- 抽象类不能实例化, 实例化的话只能使用匿名内部类 , 但是可以声明一个抽象类的引用;
- abstract不能与 static 、 private 、final一起修饰方法,因为这些方法不能被重写;
- abstract不能作用于final,因为 final类 不能被继承;
- 如果一个子类没有实现父类所有的abstract方法,那么这个子类必须要定义为抽象类;
- 当一个非抽象子类实现了其抽象父类所有abstract方法(包括父类的父类中的abstract方法, 父类实现的接口中的abstract方法), 这个子类就可以定义为普通类
代码实例:
# 下面的A叫父类,B,C继承了A,叫子类
class A{
int i;
void f(){
}
}
class B extends A{
int j;
void f(){
System.out.println("我是B"); } //重写方法
void g(){}
}
class C extends A{
int j;
void f(){
System.out.println("我是C"); } //重写方法
void g(){}
}
父类调用子类实例化的过程:
A a = new B();
如上代码, 实际上有三个过程:
(1) A a;
将a声明为父类对象,只是一个引用,未分配空间。
(2) B temp = new B();
通过B类的构造函数建立了一个B类对象的实例,也就是初始化。
(3) a = (A)temp;
将子类对象temp转换为父类对象并赋给a,向上转型( upcast ),是安全的。
经过以上3个过程,a就彻底成为了一个A类的实例。
总结:
子类往往比父类有更多的属性和方法,上传只是舍弃,是安全的;而向下转型( downcast )有时会增加方法,通常是不安全的。这也是 instanceof 不能判断一个对象是否是真正的父类对象的原因。
继承导致的 多态 :
A a1 = new B();
A a2 = new C();
如上代码所示, 父类A调用了子类B、C,同时子类在继承之后的实现方式又不同,
a1,a2两个虽然都是父类对象,但各自的f()不同。这就是子类型多态的体现。
implements
implements表示实现,可以实现多个接口,通过 重写 他们的方法去实现各种功能。
说到这里,必须了解接口和类、接口与抽象类的区别。
接口与类的区别:
- 接口不能用于实例化对象。
- 接口没有 构造方法 。
- 接口中所有的方法必须是抽象方法 , Java 8 之后 接口中可以使用 default 关键字修饰的非抽象方法。
- 接口不能包含成员变量,除了 static 和 final 变量。
- 接口不是被类继承了,而是要被类实现。
- 接口支持多继承 。
接口和抽象类:
- 接口可以继承接口,不能实现接口;抽象类不可以继承接口,但可以实现接口。
- 抽象类可以继承实体类。抽象类可以实现(implements)接口,抽象类是否可继承实体类,取决于实体类必须是否有明确的构造函数。
- 抽象类可以继承实体类,这是因为抽象类可继承性且有方法。
- 一个接口可以继承多个接口,interface C extends A, B {}是可以的(Java类是单继承的,但接口是可以多继承的)。 不允许类 多重继承 的主要原因是,如果A同时继承B和C,而B和C同时有一个D方法,A如何决定该继承那一个呢?但接口不存在这样的问题,接口全都是抽象方法继承谁都无所谓,所以接口可以继承多个接口 。
抽象类和接口的区别:
- 抽象类中的方法可以有方法体,就是能实现方法的具体功能,但是接口中的方法不行。
- 抽象类中的成员变量可以是各种类型的,而接口中的成员变量只能是 public static final 类型的。
- 接口中不能含有静态代码块以及 静态方法 (用 static 修饰的方法),而抽象类是可以有静态代码块和静态方法。
注: JDK 1.8 以后,
接口里可以有静态方法和方法体了。
接口允许包含具体实现的方法,该方法称为"默认方法",默认方法使用 default 关键字修饰。
注:JDK 1.9 以后,允许将方法定义为 private,使得某些复用的代码不会把方法暴露出去。
代码实例:
//定义一个接口
public interface InterA {
void show(); //抽象方法
default void method() { //默认方法,必须写方法体
System.out.println("默认方法被实现了"); }
static void test(){ //静态方法,必须写方法体
System.out.println("静态方法被实现了"); }
}
//定义接口的一个实现类
public class Interlmpl implements InterA {
@Override
public void show() {
System.out.println("show方法"); }
}
//定义测试类
public class InterDemo {
public static void main(String[] args) {
InterA i = new Interlmpl();
i.show(); //抽象方法强制重写
i.method(); //默认方法不强制重写,但可以被重写,重写时去掉default关键字
Inter.test(); //静态方法只能通过接口名调用,不能通过实现类名或者对象名调用
}
}