ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

Java 抽象类和接口到底有什么区别?一篇讲清楚

Java 抽象类和接口到底有什么区别?一篇讲清楚 abstract class和interface在语法上长得很像都能声明没有方法体的方法都不能被new都要靠子类去实现都能构成多态。abstractclassAnimal{abstractvoidmakeSound();}interfaceFlyable{voidfly();}两个都只给方法签名具体干什么留给实现方。所以刚开始学的时候很容易把接口当成纯一点的抽象类。这个说法在 Java 8 之前勉强还说得过去8 之后就不成立了因为接口有了default方法也能带方法体。所以只能从设计意图上区分而不是看语法长相。抽象类描述是什么抽象类表达的是继承关系里的是一个。Bird是一个Animal所以Bird extends Animal。它除了给方法签名还能放字段、构造器、已经实现好的方法子类这些东西直接白拿这才是抽象类的价值所在。举个例子所有动物都有名字吃东西的流程也一样只有吃什么不一样abstractclassAnimal{protectedStringname;// 子类直接能用的状态publicAnimal(Stringname){// 构造器子类要调 super(name)this.namename;}publicvoideat(){// 已经实现好子类不用管System.out.println(name is eating food());}protectedabstractStringfood();// 留给子类填的空}classDogextendsAnimal{publicDog(Stringname){super(name);}OverrideprotectedStringfood(){returnbone;}}eat()是不变的部分food()是变化的部分子类只要填变化的那一个。这就是模板方法这种写法只有抽象类能做。接口没有实例字段name不能被存放也没有构造器。接口描述能做什么接口表达的是这个类具备这种能力它不关心这个类是个什么东西。interfaceFlyable{voidfly();}interfaceSwimmable{voidswim();}classDuckextendsAnimalimplementsFlyable,Swimmable{...}Flyable不关心Duck是不是Animal飞机和无人机都能实现它。能力和类型是解耦的而且一个类可以同时具备多种能力不受单继承的限制这是接口最大的价值。接口之间还能多继承interfaceA{voida();}interfaceB{voidb();}interfaceCextendsA,B{voidc();}类只能extends一个父类但implements多少个接口都行。语法上的差异维度抽象类接口继承数量单继承只能extends一个多实现implements任意个字段什么都能放实例字段、静态字段、各种修饰符只能是public static final常量方法抽象方法、具体方法都行Java 8 前只能定义方法不能实现8 加了default/static9 加了private构造器有子类要调super()不能有方法修饰符public/protected/ 包级 /private都行只能public实例状态可以有不能有接口字段为什么只能是public static final三个修饰符各有一个理由final接口是契约不能有可变状态否则实现类之间会互相影响static接口不能实例化字段只能挂在接口本身上public接口是给外部用的实现类必须能看到这三个修饰符就算不写编译器也会自动补上写不写都一样。Java 8 之后接口多了什么default 方法是为了接口演进Java 8 给接口加了default方法这是接口和抽象类在语法上最大的一次拉近。引入它的动机是解决接口的兼容性问题。假设Collection要加一个stream()方法如果加的是抽象方法那所有实现了Collection的类不管是 JDK 里的、第三方库里的还是用户自己写的全都编译不过必须一个个去实现。这个代价承受不了。default的办法是给一个默认实现实现类什么都不改就能编译通过想改行为的再覆盖publicinterfaceCollectionE{defaultStreamEstream(){returnStreamSupport.stream(spliterator(),false);}}不这样会怎样没有default接口想扩展就只能新加一个接口JDK 里Iterable和Collection这种层层叠的关系就是这么来的越堆越多。菱形继承怎么办default方法带来了一个新问题一个类实现了两个接口两个接口里有同名同参数的default方法用哪个编译器不猜直接报错要求子类显式覆盖用接口名.super.方法名()指定调用谁的interfaceA{defaultvoidhello(){System.out.println(A);}}interfaceB{defaultvoidhello(){System.out.println(B);}}classCimplementsA,B{Overridepublicvoidhello(){A.super.hello();// 不写这句就编译不过}}对比一下类为什么不能多继承两个父类如果都有同名的实例字段合并规则根本说不清楚状态冲突不好解决。接口没有状态只有方法冲突了让子类显式选一个就行所以允许。static 和 private 方法Java 8 同一版还给接口加了static方法它属于接口本身用接口名.方法名()调用实现类拿不到也覆盖不了。典型用法是放工具方法比如Comparator.comparing()、Stream.of()。Java 9 又加了private方法只能被接口内部的default或static方法调用。原因是有了default之后好几个default方法之间经常有共用的逻辑这些逻辑如果只能写成public就会污染接口对外的 API所以补了一个只在接口内部可见的修饰符。怎么选场景选哪个多个类共享状态和实现代码抽象类定义一组能力让互不相关的类都能具备接口需要单继承的类型层次抽象类一个类要同时具备两种已有能力接口两个都要的情况很常见标准做法是接口定义契约抽象类给骨架publicinterfaceListE{...}// 契约publicabstractclassAbstractListEimplementsListE{...}// 骨架JDK 里这套组合到处都是List/AbstractList、Map/AbstractMap、Collection/AbstractCollection、Set/AbstractSet。接口定义有什么能力抽象类把通用的部分实现掉具体实现类继承抽象类只需要填剩下那几个方法其余的白拿。
返回列表