Java 接口和抽象类区别
很多刚接触Java的初学者,都会被“接口”和“抽象类”这两个概念绕晕——明明都是“不能直接new的东西”,为啥要搞两个?就像出门买早餐,包子和烧麦都是带馅的面食,为啥不能归成一类用一样的方法做?其实这俩的区别,就藏在咱们平时的生活习惯里。
能不能装“具体的东西”?
你可以把抽象类当成一个“半成品的模板”,就像你想做一款专属的奶茶配方,基础款是“茶底+奶+糖”,但具体要选红茶还是绿茶、加多少糖,得自己填。这个模板里,不仅能写“所有奶茶都得有茶底”这种确定的代码(比如给属性赋值、写已经实现的方法),还能留“你自己决定加什么料”的空白(抽象方法,只有声明没有实现)。举个例子,做咖啡的抽象类,你可以写个已经实现的“洗杯子”方法——不管做美式还是拿铁,都得先洗杯子,这部分是固定的;再写个“煮咖啡”的抽象方法,具体是用萃取机还是手冲,留给不同的子类(比如美式类、拿铁类)去实现。

而接口呢,更像一个“行为规则清单”,它只定规矩,不做具体事。就像咱们小区的宠物公约,只说“所有养宠物的都得牵绳、清理粪便”,但没说“你家的狗是金毛还是柯基”,也没说“怎么牵绳、怎么清理”——这些都是你自己的事。接口里的所有方法,默认都是“抽象的”,从Java 8开始虽然能加默认方法,但也是“必须按规则用的通用操作”,比如所有可跳转的组件都有“跳转”方法,具体怎么跳(跳页面、跳链接)还是子类自己定,接口只保证所有实现它的东西都有这个行为。
一个类能“用几次”?
假设你要做一个“自己的早餐店”,你可以用抽象类做个“早餐模板”,比如定义了“做包子”“做豆浆”的半成品方法,然后你的包子店子类继承这个模板,还能再继承一个“面点店通用模板”——毕竟你既做包子也做馒头,这俩都是面点,能共用一套揉面的模板。但要是用接口呢?你可以同时遵守“卖热食”“卖饮品”“不使用一次性餐具”好几个规则,比如包子类既实现“卖热食”接口,又实现“不使用一次性餐具”接口,还能再实现“可外带”接口,最多能加好几个。
为啥会有这个区别?因为抽象类是“是一种”的关系,你做的包子是一种早餐,也是一种面点,属于不同层级的同类,所以能继承多个抽象类?不对,不对——Java里一个类最多只能继承一个抽象类,就像你不能同时是包子和馒头的子类,你只能是包子的子类,同时又属于面点这个大类,所以只能有一个“父类模板”。而接口是“能做什么”的关系,包子既“能卖热食”,又“能外带”,还“属于面点”,所以能加好几个接口,这就像你既能当学生,又能当班长,还能当志愿者,是不同的“角色属性”,可以有好几个。
谁更“灵活”?
刚才说的,接口能多实现,就比抽象类灵活多了。比如你要做一个“可充电的交通工具”,可以给电动车、平衡车、滑板车都加“可充电”接口,不管它们是两轮还是四轮,都能实现这个行为。要是用抽象类的话,你得做个“交通工具抽象类”,然后让电动车继承它,平衡车又继承另一个子类,这样就乱了——平衡车不是汽车,不能继承交通工具,也没法同时继承“可充电”和“可移动”两个模板,只能要么选一个抽象类,要么写很多重复代码。
还有,抽象类里能定义普通变量,比如“早餐的价格是5元”,子类能直接用;而接口里的变量默认是“常量”,不能改,比如“充电功率是50W”,所有实现接口的东西都只能用这个数,不能自己改,就像小区宠物公约里的“牵绳长度不超过1米”,是固定规矩,谁都不能改。
这么看来,抽象类适合做“同类的基础模板”,接口适合做“跨类的行为规范”。就像你要做一套自己的代码,要是只是做同一类的东西,比如不同形状的图形,用抽象类就够了;要是这些图形又都能“画”“移动”,还能“导出文件”,那就用接口把这些行为分开,让不同形状的类去实现,既不会重复代码,也不会混乱。