面试题答案
一键面试适合使用接口的情况
- 实现多继承:Java 不支持类的多继承,但一个类可以实现多个接口。当一个类需要从多个来源获取行为定义时,接口是很好的选择。例如,在 Spring 的
ApplicationContextAware
、BeanNameAware
等接口,一个 Spring 管理的 Bean 可以同时实现多个这样的接口,从而获得不同的功能,如获取应用上下文或获取自身 Bean 名称。 - 定义行为契约:当需要定义一组方法的签名,而不关心具体实现,且不同实现之间没有共同状态和通用实现逻辑时,使用接口。比如 Spring 的
InitializingBean
接口,它定义了afterPropertiesSet
方法,用于在 Bean 属性设置完成后执行一些初始化操作。不同的 Bean 对该方法的实现差异很大,没有通用实现逻辑,只需要遵循这个契约。 - 降低耦合度:接口可以实现模块之间的松耦合。例如在 Spring 的依赖注入中,服务层接口与实现类分离。业务代码依赖接口而不是具体实现类,这样在更换实现类时,业务代码无需修改。比如一个
UserService
接口,有UserServiceImpl
和UserServiceMock
等不同实现,通过接口注入,业务层代码不会因为实现类的变化而受到影响。
适合使用抽象类的情况
- 有共同状态和通用实现:当多个子类有共同的成员变量和通用的方法实现时,抽象类更为合适。例如在 Spring 的
AbstractApplicationContext
抽象类,它实现了ApplicationContext
接口的部分通用方法,如资源加载等,为其子类ClassPathXmlApplicationContext
等提供了基础实现,子类可以在此基础上进行扩展,减少代码重复。 - 模板方法模式:抽象类常被用于实现模板方法模式。比如 Spring 的
JdbcTemplate
类,它是一个抽象类,定义了一系列操作数据库的模板方法,如query
、update
等,其中部分方法有通用实现,而一些关键步骤如mapRow
等由具体子类实现,这种方式将通用流程和具体实现分离,提高代码复用性和灵活性。 - 演进式设计:如果接口可能会不断增加新方法,使用抽象类可以避免实现类因为接口方法的增加而被迫修改。在框架的演进过程中,抽象类可以在不破坏已有实现类的情况下添加新的默认实现方法。例如在 Spring 框架的发展过程中,
AbstractBeanDefinition
抽象类随着功能的扩展可以添加新的方法并提供默认实现,而不会影响到大量的子类。