接口 和抽象类 的区别在面试中问的比较广泛,同时也是开发者必须要明白的基础性知识,下面就来捋一下:
从特征上比较
(1)接口可被类实现(implement),也可以被接口扩展继承(extends),抽象类只能被子类继承(extends)。
(2)接口只能有方法声明和不可变常量:
方法声明:会被自动设置为public abstract,接口中方法平时会省略掉public,但是实现方法必须显示声明public;
不可变常量:定义的变量只能是公共的静态的常量,域变量会被自动设置为public static final;
/** * 菜单接口 */ public interface MenuService { //接口名 补全public int defaultTotal=33; //1.1:一般写法 public static final int defaultTotal2=40; //1.2:常量补全public static final,和上面类型相同 Double getFoodTotal(String restaurantName); //2.1:一般写法 abstract Double getFoodPrice(String foodName); //2.2:方法声明补全抽象方法关键词abstract public abstract int getFoodNum(String foodName); //2.3:方法声明补全抽象方法关键词public abstract default String getBaseInfo(){ //3.1:java8 引入默认方法,必须实现 //defaultTotal=defaultTotal+1; //错误,长岭不能更改 return "This is a menu"; } static String getRestaurantName(){ //3.2:java8 引入静态方法,必须实现 return "MFC"; } }
抽象类可以有方法声明(即抽象方法)、具体的方法和属性(可以是静态常量和普通变量)。
/** * 菜单抽象类 //补充public,abstract可以省略,只要有抽象方法就是一个抽象类 */ public abstract class MenuAbstract { int defaultTotal=33; //1.1:变量 public static final int defaultTotal2=40; //1.2:常量,和上面的变量不同 //Double getFoodTotal(String restaurantName); //2.1:方法声明错误用法,abstract声明的类中抽象方法必须使用abstract abstract Double getFoodPrice(String foodName); //2.2:方法声明一般写法 public abstract int getFoodNum(String foodName); //2.3:方法声明补全关键词public public String getBaseInfo(){ //2.4:具体方法,public可省略 defaultTotal=defaultTotal+1; return "This is a menu"; } static String getRestaurantName(){ //2.5 return "MFC"; } // default String getBaseInfo(){ //2:抽象方法不支持默认default方法 // return "This is a menu"; // } }
(3)用instanceof可以检查一个对象是否属于某个特定类或是否实现了某个接口。可用来检测是否实现了目标接口或目标类。
(4)Java8新添加相关特性:
》》》接口可以含有静态方法,但必须实现该静态方法。
值得注意的是:实现类实现多个接口时,可能会实现多个接口,各个接口可能含有同参同名的方法,使用该方法时,要使用“接口.接口静态方法”,而不是“实现类.接口静态方法”;如果接口要定义同名同参的静态方法,但是不能使用@Override重写接口中的方法,使用“实现类.实现类静态方法”的形式访问即可实现类中的静态方法。
》》》接口中可以含有默认方法,但必须实现它。
从设计上做比较
(1)接口和抽象类都是用来抽象具体对象的,两者均能体现多态性,但是接口的抽象级别最高,两者均不可直接实例化,若要实例化,必须先实现所有抽象方法。
(2)接口是设计的结果,即从无到有,大家制定契约和规则,签约类必须要完全遵守、实现该契约中的方法,即所有方法都是有意义且必须要实现的;侧重于行为约束,主要抽象功能。
抽象类是重构的结果,即从有到有 的设计,更侧重与内部优化与合理性,是为了继承扩展,实现代码的重用,主要抽象了事物(包含属性和方法)。
如何理解上面代码中的菜单的设计?
当一家餐厅可能有多个门店,每个门店可能需要根据当地的实际消费,人数等情况自定义价格,折扣等操作,所以就出现了不同的需求。
菜单接口:由专门人员去设计,3个开发者根据接口去开发,每个人负责一种门店菜单的开发,接口从无到有,要求所有门店菜单开发者(实现者)必须遵守这样的接口,实现每个方法,侧重于契约精神、规则精神。
菜单抽象类:可以将所有门店的共同行为或属性抽取出来放在抽象类,派生的门店菜单实现类就不需要多次重写相同的行为代码了;并且,不同门店的个性化需求,可以通过实现抽象方法自定义;
为什么要设计接口和抽象类?
接口设计不是必须的,如果一个系统较为复杂,需要多人合作,则可以使用接口,设计者制定接口后,就不再轻易改变接口(除非改动对接口有意义),其他人根据接口具体实现即可。如果系统较为简单,一个人即可实现,无需设计接口,否则就要牵一发动全身。不用为了以后的扩展怎么滴怎么滴的大费周章的去设计,真到那个时候再去重构设计也不迟。
抽象类的设计也不是必须的,它的设计师为了更好的优化重构代码,实现代码的扩展、复用。