Java面试题8 设计模式&Spring基础
自己尝试通过打字来回答一些网上常见的面试题,答案仅代表我自己的观点
简单解释各种设计模式
创建型
创建型设计模式是在一些特殊需求下如何创建对象的最佳实践
- 工厂模式:对使用者隐藏如何构造组件的细节,工厂可以以任何方式构造组件对象,可以返回组件的任意实现类、子类、代理类等等
- 抽象工厂模式:允许提供一批可能需要被替换的组件实现,对使用者隐藏工厂以及组件的实现细节,用户使用的完全是接口(GoF中GUI库的例子)
- 构建者模式:允许分步构建一个复杂的,有很多非必要属性的对象(Effective Java)。传统构建者模式中还允许被构造的对象具有不同表示(GoF中RTF文档转换器的例子)
- 原型模式:原型模式是通过一个对象的原型来克隆出需要的对象,常见于框架系统,框架系统常常要构建使用者编写的类的对象,而它往往不知道怎么构建,这时使用者需要提供一个原型(Spring的BeanDefinition的例子)
- 单例模式:允许提供一个整个系统的生命周期中只有一个实例的对象。(MyBatis的SqlSessionFactory,Android开发中的数据库实例)
结构型
结构型设计模式是在一些特殊需求下如何组合对象的最佳实践
- 适配器模式:在两个不兼容的组件之中提供适配。(Spring中的HandlerAdapter)
- 桥接模式:如果一个接口中的一部分实现是可替换的(比如基于特定GUI库的Window实现类),并且该接口中的大量实现都依赖这部分实现(比如提供滚动功能的ScrollableWindow、具有Icon的IconWindow),那么它们就要针对这部分被依赖的实现生成大量的类(如XWindowScrollableWindow、PMScrollableWindow),桥接模式将这些被依赖的实现移动到单独的接口中,并使用委托来选择一个实现,让其它接口共用且无需考虑当前使用的实现,此时,那些被生成的特定于某种实现的类就被消除了
- 组合模式:在组件之间提供层级关系,屏蔽层级关系中不同组件之间的差异(文件树的例子)
- 装饰器模式:与原有的类实现同一个接口,委托原有的类实现核心功能,在其之上进行扩展(IO流、MyBatis的例子)
- 代理模式:与原有类实现同一个接口,将调用转发给原有类,并在其调用前后横向扩展或添加访问控制(AOP、事务的例子)与装饰器模式的区别是,装饰器可以使用被委托的类提供新的方法,比如
LineNumberReader
在其之上实现了readLine
,而代理模式往往是只关心那些已有方法的扩展和访问控制 - 外观模式:对外部隐藏系统内部庞大、复杂的组织结构,提供一个简单的API给外界使用(Service层整合很多Repository层和其它Service,分布式系统)
- 享元模式:对于一些在程序中经常被使用的不可变对象,可以预先将它们创建好,并在整个程序中共享,消除对象构建的成本(Boolean.TRUE,Redis中的数字0~9999是在服务启动就预先建立的,后面使用都是共享的)
行为型
行为型设计模式是在一些特殊需求下如何组织对象的行为的最佳实践
- 职责链模式:而系统并不了解当前有哪些任务处理器,也不了解哪些处理器能够处理哪些任务,此时将处理器连接成链,让任务依次通过链中的每个处理器,并让它们自己检查能否处理这个任务。(Filter、Inteceptor、Converter)
- 命令模式:当系统中的某个组件需要在某些时刻执行一个命令,但又不知道该命令的细节,此时可以抽象一个命令接口,让外部传入要执行的命令(GUI库中的OnClickListener)
- 解释器模式:将对象组织成AST,调用根节点的
interpret
方法对整个AST进行解释(正则表达式解析器,计算器) - 迭代器模式:将容器对象希望将它的遍历方式与容器解耦,提供可复用的迭代策略(Java的Iterator)
- 中介者模式:解耦组件之间的依赖关系,组件间不直接交互,而是与中介交互,组件发生事件时通知中介,中介操作这个事件发生后要修改的组件,这样,依赖关系被从组件中拿了出来,提高组件的复用性(Android的Activity,Controller)
- 观察者模式:将数据抽取出来,变成可观察的对象,各种组件可以作为观察者来观察这个数据,当数据改变时它们会接到通知(新的响应式UI框架的思路,比如Vue、Flutter、JetpackCompose)
- 策略模式:某种任务可能有多种执行策略,将执行策略抽象成策略接口,方便切换执行策略的实现(SpringMVC中的视图解析器、MultipartResolver、Handler等都是策略,我开发的Mybatis分页插件也用到了策略模式)
- 模板模式:在父子继承关系中,某些行为是所有子类相同的,某些行为是特定于子类的,在父类中提供这些相同行为的模板,在子类中实现这些特定行为(Servlet、Spring框架中的各种BeanFactory和ApplicationContext)
- 访问者模式:当一种数据结构中的元素需要被外界访问时,可以使用访问者模式。比如AST中的节点可以提供
Accept
方法接受一个访问器的访问,你可以实现用于类型检查和代码生成或更多的访问器。元素并不知道访问者干了什么。 - 备忘录模式:存储对象在一瞬间的状态,用于稍后恢复(Undo、Redo)
- 状态模式:当一个对象在不同的状态对不同的方法具有不同行为时,可以将状态抽象成接口,在状态实现类中实现这些不同行为(比如TCP链接在不同状态对于Open、Close等方法有不同行为)
89. 简单工厂和抽象工厂有什么区别?
简单工厂模式就是提供一个构造对象的工厂,对调用者隐藏构造对象的细节,可以返回任何与返回类型兼容的对象。
抽象工厂模式用于当你有一批可能需要共同工作的组件需要创建时,你应该为每一类组件创建一个工厂,但这样对于调用者来说它需要使用的API太杂乱,抽象工厂用于提供一个统一的接口来创建这些组件。
90. 为什么要使用 spring?
简化开发呗....
spring只用简单的POJO来完成企业应用的开发,并且提供依赖注入,你不用考虑如何组建对象间的依赖关系,这都给spring来实现。提供开箱即用的AOP来对应用组件进行横向扩展。提供各种常用工具的整合,比如JPA、各种ORM框架、安全框架等
91. 解释一下什么是 aop?
面向切面编程,即不修改组件代码就对组件添加功能。其原理是使用动态代理对组件进行横向扩展,这种扩展对组件的使用者来说是透明的。
92. 解释一下什么是 ioc?
IoC就是控制反转,即程序员不需要手动负责对象的创建、对象依赖关系的满足,你不需要为之编写业务代码,反之,你要做的是对Spring框架描述这些对象之间的关系,Spring框架来帮你创建对象,满足依赖关系。这让应用的可测试性变得更强,且促进了组件间的解耦。
93. spring 有哪些主要模块?
- core: 提供资源访问
- bean: 提供bean定义、创建、解析
- context: 提供bean容器
- spEL: 提供spring中使用的领域特定语言的解析
- AOP/Aspect: 提供aop功能
- Data: 包括JPA、ORM、事务等模块,用于持久层数据访问
- Web: 提供Web功能
- Test: 提供测试功能
94. spring 常用的注入方式有哪些?
- 属性注入
- 构造函数注入
- 方法注入
95. spring 中的 bean 是线程安全的吗?
这和Bean自己有关系,和Spring有鸡毛关系,问这问题的是不是脑缺钙啊是不是我姐姐啊
96. spring 支持几种 bean 的作用域?
- singleton
- prototype
- request
- session
- globalsession
97. spring 自动装配 bean 有哪些方式?
- 通过xml或java代码显式定义bean的依赖关系
- 通过注解自动定义Bean,自动发现并装配Bean