Java回顾:用一个Demo来说明继承抽象类和实现接口的简单框架模型
大家都知道,在java应用开发中,要"面向接口编程"。
那么什么是接口?接口有什么作用?接口如何使用?我们一起来回顾一下。
[声明]欢迎转载,但请保留文章原始出处:http://blog.csdn.net/yelangjueqi/article/details/44701369
1,接口回顾:
1.1,Java中接口的概念
在Java中接口是一种特殊的抽象类,跟一般的抽象类相比,接口里面的所有方法都是抽象方法,接口里面的所有属性都是常量。也就是说,接口里面只有方法定义而没有任何方法实现。1.2,接口用来干什么
通常用接口来定义实现类的外观,也就是实现类的行为定义,用来约束实现类的行为。接口就相当于一份契约,根据外部应用需要的功能,约定了实现类应该要实现的功能,但是具体的实现类除了实现接口约定的功能外,还可以根据需要实现其他一些功能,这是允许的,也就是说实现类的功能包含但不仅限于接口约束的功能。通过使用接口,可以实现不相关类的相同行为。而不需要考虑这些类之间的层次关系,接口就是实现类对外的外观。
1.3,接口的思想
根据接口的作用和用于,浓缩下来,接口的思想就是"封装隔离"通常提到的封装是指对数据的封装,但是这里的封装是指"对被隔离体的行为的封装",或者是"对被隔离体的职责的封装";而隔离指的是外部调用和内部实现,外部调用只能通过接口进行调用,外部调用是不知道内部具体实现的,也就是说外部调用和内部实现是被接口隔离开的。
1.4,使用接口的好处
由于外部调用和内部实现被接口隔离开了,那么只要接口不变,内部实现的变化就不会影响到外部应用,从而使得系统更灵活,具有更好的扩展性和可维护性,这就是所谓"接口是系统可插拔的保证"这句话的意思。1.5,接口和抽象类的选择
既然接口是一种特殊的抽象类,那么在开发中,何时选用接口?何时选用抽象类呢?对于它们的选择,在开发中是一个很重要的问题,特别总结两句话给大家;
A、优先选用接口
B、在既要定义子类的行为,又要为子类提供公共的功能时应选择抽象类。
2,简单框架回顾:
下面介绍几种常用的框架结构(第二种框架结构实际上是第一种框架结构的变体,为了对比,分开来说)2.1,第一种框架结构:既继承抽象类,又实现接口(接口是一种特殊的抽象类)的框架结构:
2.1.1,接口:public interface IApi { public void add(); public void remove(); public void update(); } |
2.1.2,抽象类
public abstract class AApi { public abstract void testAdd(); public abstract void testRemove(); public void testUpdate() { System.out.println("AApi/testUpdate"); } } |
2.1.3,实现类
public class ApiImpl extends AApi implements IApi { // 第一种情况:用抽象类来声明,用该抽象类的子类来创建实例对象 public static void main(String[] args) { // 用抽象类AApi声明,而用子类ApiImpl创建实例对象,该实例对象mIApi只能调用抽象类AApi里面的方法(子类ApiImpl新增的方法testIpml()不能调用,也不能调用接口里的方法),否则编译报错:The // method testIpml() is undefined for the type AApi AApi mIApi = new ApiImpl(); mIApi.testUpdate(); mIApi.testIpml();// 编译报错 } // 第二种情况:用接口来声明,用该接口的实现类创建实例对象 public static void main(String[] args) { // 用接口IApi声明,而用实现类ApiImpl创建实例对象,该实例对象mIApi只能调用接口IApi里面的方法(实现类ApiImpl新增的方法testIpml()不能调用,也不能调用抽象类AApi里面方法),否则编译报错:The // method testIpml() is undefined for the type // AApi,现象同第一种情况,因为接口就是一种特殊的抽象类。 IApi mIApi = new ApiImpl(); mIApi.add(); mIApi.testIpml();// 编译报错 } // 第三种情况:用抽象类的子类(或接口的实现类)来声明,用该抽象类的子类(或该接口的实现类)创建实例对象 public static void main(String[] args) { // 用子类(或实现类)ApiImpl来声明,子类(或实现类)ApiImpl创建实例对象,该实例对象mIApi既可以调用抽象类里面的方法又可调用接口里面的方法,也可调用自己新增的方法testIpml()。 ApiImpl mIApi = new ApiImpl(); mIApi.add(); mIApi.testUpdate(); mIApi.testIpml(); } public void testIpml() { System.out.println("ApiImpl/testIpml"); } @Override public void testAdd() { System.out.println("ApiImpl/testAdd"); } @Override public void testRemove() { // TODO Auto-generated method stub System.out.println("ApiImpl/testRemove"); } @Override public void add() { // TODO Auto-generated method stub System.out.println("ApiImpl/add"); } @Override public void remove() { // TODO Auto-generated method stub System.out.println("ApiImpl/remove"); } @Override public void update() { // TODO Auto-generated method stub System.out.println("ApiImpl/update"); } } |
根据上述Demo总结:
(1)、凡是用抽象类或接口声明的实例对象,该实例对象 只能调用该抽象类或该接口里面定义的方法,不在该抽象类或接口里面定义的方法,一律不能调用,否则编译报错。
(2)、用子类或实现类声明的实例对象,既可以调用抽象类里面定义的方法又可调用接口里面定义的方法,也可调用自己新增的方法。
(3)、抽象类或接口都不能直接用本身实例化自己,如下,都会编译报错:
AApi mIApi = new AApi();//编译报错:Cannot instantiate the type AApi
IApi mIApi = new IApi();//编译报错:Cannot instantiate the type IApi
由此可见,对于抽象类(或接口)只能通过子类(或实现类)来实例化。
2.2,第二种框架结构:首先抽象类实现接口,然后继承抽象类(实际上是第一种框架结构的变体)
对上面Demo进行变换下,如下:2.2.1,接口:
public interface IApi { public void add(); public void remove(); public void update(); } |
2.2.2,抽象类(实现接口):实现部分接口方法, 其他接口方法子类必须给与实现。
public abstract class AApi implements IApi { public abstract void testAdd(); public abstract void testRemove(); public void testUpdate() { System.out.println("AApi/testUpdate"); } @Override public void add() {//接口方法 // TODO Auto-generated method stub } } |
2.2.3,子类(继承抽象类)
public class ApiImpl extends AApi { public static void main(String[] args) { } public void testIpml() { System.out.println("ApiImpl/testIpml"); } @Override public void testAdd() { System.out.println("ApiImpl/testAdd"); } @Override public void testRemove() { // TODO Auto-generated method stub System.out.println("ApiImpl/testRemove"); } @Override public void remove() {//接口方法 // TODO Auto-generated method stub } @Override public void update() {//接口方法 // TODO Auto-generated method stub } } |
2.3,第三种框架结构:仅继承抽象类(效果等同于第二种框架结构,一般采用这种方式)
2.3.1,抽象类:public abstract class AApi { public abstract void testAdd(); public abstract void testRemove(); public abstract void add(); public abstract void remove(); public abstract void update(); public void testUpdate() { System.out.println("AApi/testUpdate"); } } |
2.3.2,子类:
public class ApiImpl extends AApi { public static void main(String[] args) { } public void testIpml() { System.out.println("ApiImpl/testIpml"); } @Override public void testAdd() { System.out.println("ApiImpl/testAdd"); } @Override public void testRemove() { // TODO Auto-generated method stub System.out.println("ApiImpl/testRemove"); } @Override public void add() { // TODO Auto-generated method stub } @Override public void remove() { // TODO Auto-generated method stub } @Override public void update() { // TODO Auto-generated method stub } } |
2.4,第四种框架结构:仅实现接口
2.4.1,接口:public interface IApi { public void add(); public void remove(); public void update(); } |
2.4.2,实现类:
public class ApiImpl implements IApi { public static void main(String[] args) { } public void testIpml() { System.out.println("ApiImpl/testIpml"); } @Override public void add() { // TODO Auto-generated method stub } @Override public void remove() { // TODO Auto-generated method stub } @Override public void update() { // TODO Auto-generated method stub } } |
总结:
上面4种框架结构各有特点,分析如下(可根据实际情景和实际需要选择使用):第一种框架:接口的方法实现全部放在实现类里面了。
第二种框架:接口的方法实现一部分可以放在抽象类里面,一部分可以放在实现类里面,未在抽象类里面实现的方法必须在子类里面给与接口方法实现;在抽象类里面实现的接口方法为所有子类实例提供了公共的功能,这与在抽象类里面重新定义一个方法来提供相同的功能是一样的。因为从灵活性和扩展性上说,第二种框架结构与第三种框架结构是相似的,并且第二种框架结构相对第三种框架结构多余实现一个接口,有点画蛇添足的意味(需要子类实现的方法在抽象类里面声明为abstract就行了,根本用不着在接口里面那么声明下),因此一般使用第三种框架结构,一般不使用第二种框架结构。
因此:
1、如果需要为子类提供公共的功能时应选择抽象类,而不用接口。
2、接口实现了不相关类的相同行为。如果系统非常复杂,可能存在很多相似特性的类,同时又存在很多不具备相似特性的类,而这些具备相似特性的类和不具备相似特性的类又存在一些共同行为。此时对于那些具备相似特性的类就可以抽象出一个抽象类,而对它们的相同行为就可以抽象出一个接口来,那么具备相似特性的类最后实现的方式就是第一种框架结构;对那些不具备相似特性的类但是却具有相同行为的类,最后的实现方式就是第四种框架结构。