Java中抽象类与接口的区别

本文探讨了面向对象编程中抽象类与接口的区别及其应用场景。通过具体案例解释了抽象类如何利用模板方法模式分离不变与可变部分,以及接口如何促进模块间的松耦合。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

       应某人的要求在这里以后会多谢一些技术日志。之所以选这个题目是因为这有助于真正理解面向对象的概念。而什么时候用抽象类,什么时候用接口这其实更多的是一个经验问题,我的经验自然是大大不足的。所以在这里只提出我的理解,希望其他人可以多多给出自己的意见。
      事实上,如果单单从这两门语言的语法上来说,区别是显而易见的。接口中只能有方法签名,不能有具体的实现,而抽象类中的抽象方法也不可以有实现,但是其他方法可以实现。另外,接口中的变量只能是static final的,而抽象类没有这个限制。
       相信如果学校开java课程,考试出这道题目,那就是满分了。但事实上这就是中国计算机教学的不足之处。对一门语言仅仅会用,仅仅不会发生编译级的错误是远远不够的。这样的回答很显然会遗留一个问题,那就是为什么要搞出抽象类和接口这两个概念,为什么要由这样的一种语法的机制?
      事实上,这些全都是为了实现一件事:多态!但是接口的多态和抽象类的多态还是有区别的。我个人的理解就是抽象类在表达继承关系,同时是在把在继承关系中的变化的东西和不变的东西分开!这在设计模式中称为Template method(模板方法)。下面我将以一个我在一个项目中实现的一个功能来说明抽象类的这种template method的思路。
      在作数据库查询的程序时,经常会在select查询语句后加where查询条件。通常的做法是这样的,写一个find方法,他的方法签名如下:public find(Map conditions) throws SQLException
      conditions就是存放where查询条件的键值对。但这样的实现是在比较麻烦,我就准备写一个Condition类专门用来存放查询条件,该类有个叫做conditionUnit的成员变量,conditionUnit就是name = 'zsj'的这样一个查询单元,数个查询单元是通过另一个成员变量connector连接的。事实上大多数情况下,connector要么是and要么是or。
       而conditionUnit里面有三个成员变量key,value和operator。对于name = 'zsj',key就是name,value就是zsj,operator就是=。但是这样还是不够,我的最终目的是在find方法里根本不用去拼凑where查询条件,只需将参数Map转成之前的Condition,Condition就能自己为你拼凑查询条件(事实上这样做是不好的,我在以后做了改进,Condition本身不做拼凑sql的工作,他只存放sql查询条件的数据单元,拼凑的工作由另一个类ConditionGenerator完成。这和在做图形化工具时,把界面和逻辑分开是一样的道理,因为能增加软件的可复用性)。为了拼凑查询语句,就必须在conditionUnit类里添加一个generateUnit方法来将key,value和operator连接起来。注意,绝对不是像你想的那么简单,把key+operator+value就可以了。关键在于,对于sql的类型,varchar和char以及其他一些类型的的数值类型两边要加单引号’,比如name如果是varchar型的话,zsj就要写成'zsj',但是对于一些数字类型,则不需要加。问题就在于到底是哪个类型,这个类本身是不知道的,这件事情只有在运行时才能知道。好了,那我就干脆不实现这个方法,而把这个方法改成抽象方法。然后我在写VarcharConditionUnit,NumericConditionUnit类去继承ConditionUnit这个抽象类(因为已经有了抽象方法generateUnit)。在这些子类里我在根据子类所对应的情况去实现generateUnit这个抽象方法,这就是template method的设计模式,也是我所认为的抽象类的最大的作用:将不变的东西同统一在基类里,而将变化的东西做成抽象方法由子类去覆写(overriding),这样的一个抽象基类就是一个template(模板)。这就是所谓的将不变的东西和变的东西分开,不变的东西在编译时已经决定,而变化的东西由Java用RTTI的机制去动态获取。比如ConditionUnit cu = new VarcharConditionUnit; cu.geneateUnit();对于java而言,第二句语句,它是肯定不会去掉用那个抽象类的抽象方法的,因为根本没有实现,那java究竟是怎么知道cu究竟是什么呢?这就是靠动态绑定技术实现的!
      下面在谈谈接口。接口的多态和抽象类不同,是不带有任何继承色彩的。举个例子,若将飞机抽象成一个类,将飞做成一个接口,那我只能说飞机实现了飞,绝对不会说飞机是飞。但是上述的例子VarcharConditionUnit和NumericConditionUnit确实都是ConditionUnit。对于飞来说不一定只有飞机能实现,若把飞艇抽象成一个类,也可以实现飞。显然飞机和飞艇的飞行机制是完全不同的,但是对于一个想要从一个城市飞去另一个城市的人来说(不考虑时间等其他因素),他才不管飞机和飞艇的内部飞行机制是怎么样的,他只知道能非就行!!!更为极端的是,如果把他眼睛蒙住过数个小时从一个城市到了另一个城市,他根本就不知道做的是飞机还是飞艇,也不需要知道。
      这就是接口的作用,连接模块与模块之间的功能而不需要知道各自的内部实现。而面向对象开发的一个原则就是面向接口编程。而我理解的面向接口编程的作用就是为了实现模块于模块之间的松耦合,而接口就是实现松耦合的最好工具。还有一个例子就是台灯永远只需要插在插线板这个接口上而不需要知道插线板内部的排线结构。下面在举个开发的例子。写数据库操作无非就增、删、改、查四种操作。若要进行操作的表名是Classroom那么我可以定义一个ClassroomBean的接口。这样做的好处在于若客户本来要求用的数据库是db2,一个月后突然要求改成Oracle 了(注意不同的数据库所支持的sql语法是有所差异的)那么我只需要将原来的ClassroomBeanDB2Impl类换成我现在新实现的ClassroomBeanOracleImpl类就可以了,如果之前一直是面向ClassroomBean接口开发的话,那么所有的代码就不需要任何改动了。这也就是所谓的可插拔的开发。将不用的ClassroomBeanDB2Impl拔去,插上ClassroomBeanOracle。
      最后总结一下,抽象类和接口都是实现多态的一种工具,而抽象类的目的是将变化的与不变的东西分开,体现一种继承的结构层次,而接口的目的则在于实现模块于模块之间的松耦合。但是多态的特点是一样的那就是同一个表征(接口方法和抽象方法)可以有多个内部实现(实现类的方法和子类的覆写方法)。以上是我个人的理解,还希望大家多多发表评论!
 
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值