1.在Java中,什么时候用重载,什么时候用重写?
在设计过程中两者的运用以及对设计原则的掌握。
重载是多态的一种实现和形式,在Java中主要是从代码应用层及用户调用者的角度来解决问题;也是追寻开闭原则。
例:在spring中的getBean()方法,名字相同,意味着它的功能是相同的,但getBean有很多场景,可以传入BeanName,也可以传入BeanClass等,因为用户在使用时根据不同的需求来调用,传入的参数不一定直接拿到,所以用户在调用方法的时候,如果拿不到,就不能调用,所以在进行顶层设计的时候可以开发多种场景供用户选择。通过spring的getBean()源码,可以看出最终会归到一个方法,在类的内部重载方法的最终逻辑都是一样的,外部只是一种形式,这样的设计也符合方法的单一职责原则。
重写遵循开闭原则,发生在父子类之间。像抽象类与子类之间,抽象类用在模板模式中主要是用于扩展。像包装器模式,父类可能已经实现了已有的功能,但子类对父类的功能不满意,可进行扩展,进行重写。开闭原则的一种体现,可增强原来的功能也可覆盖原来的功能,这样不会给原来的类带来新的问题,新开发的类只需要单独开发,测试,维护就可以了。
2.举例一个你更倾向于使用抽象类,而不是接口的代码设计场景?
都是定义一个执行额规范。
抽象类可以实现,可以去完成公共的功能,既可定义策略模式的顶层规范,还可以进行模版模式的公共实现。
而接口不能实现,通常只能定义标准,定义策略模式的顶层规范。
3.在Java中,为什么不允许静态方法中访问非静态变量?
从jvm层面:
1.静态方法可以直接通过类名访问,而且静态属性是在类加载后就初始化完毕了。而实例变量则是在创建了实例对象后才初始化的。如果允许静态方法访问实例变量,则很可能后者还没初始化,肯定会访问不到。
2.静态方法通过类名访问,如果这样还能访问到实例变量,那么当实例变量存在多个时,就会无法区分该变量属于哪个实例。
3.这样的设计也可以使得静态的内容在一个jvm中只有一份副本,方便复用从而节省内存空间。
从设计层面:设计之初就有个规定,java类的所有东西的初始化顺序是:先属性后方法,先静态后动态,从上往下进行初始化。因此在静态方法中访问一个还没有初始化的一个变量显然是不合理的。
4.列举你所了解的软件分层场景、软件分层有哪些好处?
软件分层存在于:mvc中的controller,model,view中各层各司其职,结构清晰易于维护;网络分层模型;
优点:
1.单一职责的最佳体现。各层职责明确,结构清晰,易于维护。
2.高内聚低耦合,各层变化时对其临层的影响最低,也明确了职责边界,满足开闭原则。
3.分层设计使得拓展变得容易,而且分层后便与在入口处做一些安全性控制,使得操作更加安全可靠。
4.提炼出层次后,某些公共的部分可以单独提出来作为一层,从而可以复用,提升开发效率。
补充:应用的分层只是相对的,比如系统分为三层:前,中,后。三层架构每一层都有客户端,如果客户端是用户调用时的最顶端,那么此时是应用层;如果客户端是放在后台或者中台,那么就不是应用层,这样看架构是怎样去设计的。
5.符合开闭原则的设计模式
开闭原则:描述的时规范和实现的关系,规范是闭的,实现是开的;也是对扩展开放,对修改关闭。
开放和关闭的界限取决于设计之初时所定义的规范。
- 抽象工厂:工厂的抽象接口和产品族是闭的;比如当新增一个产品时,相应的新增一个工厂进行生产,而不是去改变别人的工厂去生产新增的产品。
- 原型模式:本身结构不符合开闭原则,但是可以用它达到开闭原则的效果。当需要修改原型实例的时候不要直接修改实例本身,而是用原型的构造器构造出新对象,只修改新对象。
- 门面和委派模式都对外提供了统一的接口,而将可能引起变化的结构进行剥离。
- 代理模式:最典型的spring中aop通过动态代理的实现。拓展功能只需要动态的新创建$形式的类就可以了,而不需要去改变原有的代码,造成不必要的影响。
- 装饰着模式:对原有的功能进行装饰时,需要新增一个类去拓展原有的功能,而不是在原来的类中去修改,不然后造成:不仅影响前辈式代码,也会对后辈式代码的拓展造成方向性的误导。
- 适配器模式:为了一个现有的组件去适用另外一个功能,只需要新建一个适配器,把两者进行关联,以达到预期的效果,而不是直接修改最初的组件。
- 策略模式:比如tom写的支付方式的例子,新增支付方式只需要添加接口的实现即可。
- 模板方法模式:spring的jdbcTemplate,获取链接和执行sql的流程都已经封装好,客户端只需要继承这个类,然后实现抽象类的抽象方法即可,对于sql的执行流程不会改变的,只是动态的改变所要执行的sql和返回数据。
- 观察着模式:google的guava发布事件时,对于各个事件时拓展的,各个事件也是独立的,相互之间不会产生影响。
总之,额,我的感觉是所有的设计模式正确的运用,大都会符合开闭原则。它也是对高内聚低耦合的提炼,对聚合部分关闭修改,同时保证低耦合应对变化。
6.设计模式中创建型,结构型,行为型之间的根本区别
创建型模式把对象的创建与使用分离开来,降低了系统的耦合性,使用者不需要关注产品的创造细节。所以创建型模式关注的怎样更合理的创建对象。
结构型模式是把多个类或者对象有机的结合起来,按照某种布局组成更大的结构,以达到实现某种功能的目的。它更关注的是已经创建出来的产品之间的组合方式。
行为型模式是各个结构体之间有动机,有目的,有特点的行为框架,各个结构体之间相互通信配合,根据最初设定的行为流程走向共同的目的地。行为型模式注重的是这套流程的制定。
在解决具体业务需求时,多和实际情况做关联对比,这样才能利用好设计模式,更好的解决实际问题。
7.防止破坏单例的方法?




8.双重检查锁为何要做两次非空检测?
补充:第一层做性能优化,第二层做逻辑控制。

9.你觉得原型模式能够给你解决的最大的麻烦是什么?
- 提高代码重复使用率。原型模式可以帮助你简化新对象的创建,重复使用创建对象的代码片段。
- 提高开发效率。创建新对象比较麻烦时,可使用原型模式,简化了繁琐的数据准备过程,同时省去了许多资源
- 浅克隆,只复制值,不复制引用对象地址,当改变原生对象的引用对象值时,复制对象中的引用值也会被改变。深克隆同样复制引用对象,改变这个引用对象的属性值时,不会影响原生对象的数据。


10.简单介绍下工厂模式,并描述他们各自的优劣以及使用场景?






11.用精简的语句描述产品等级结构和产品族?
产品等级结构:指具有相同属性,相同功能,相同特点的多种产品的纵向比较。具有继承,父子关系。
比如,蔬菜可以根据色泽,营养成分等综合评审,可分为1,2,3,4四个等级,这四个等级就是蔬菜的等级结构
产品族:具有某种关联的产品的一类。具有关联关系。
而蔬菜就是一个产品族,这个族群里包括:黄瓜?西红柿?土豆?
好,现在要有大棚来生产他们了:
工厂方法:需要3(蔬菜种类)✖️4(蔬菜等级)=12个大棚
抽象工厂:只需要三个大棚即可,分别种植:???,每个大棚里每种蔬菜都可以种出4个等级。
当然,在这里等级只是打个比方,并不是等级越高越好,可以理解为同一蔬菜的不同样式?
12.实现代理模式的底层逻辑是什么?
代理模式包括静态代理,也包括动态代理,并且也包括cglib和JDKProxy;
代理模式的底层逻辑,其实就是去找一个替身为目标代理对象去完成事情,并且去做一些增强的功能和动作,所以不管是静态代理和动态代理都是去新创建一个类,去替原来的类做一些事情,并且在原来类的基础上,在前后做一些增强,这样才是代理模式的底层逻辑,只不过静态代理需要人工,手动去new一个类,相当于是手动挡,动态代理是我们用系统程序自动去生成一个类,相当于是自动挡。
13.为什么 JDK Proxy 要求目标类实现接口,而CGLIB对目标类无任何要求?
主要体现在OOP的继承关系上,我们jdk它是用新生成的类去实现目标代理类的接口,因为如果你不实现接口的话,是没办法知道目标对象里面有哪些方法,而且我们去调用代理对象的方法之前,要先把代理对象拿到,并且将代理对象的引用赋值给目标代理对象的接口,如果说我们生成的代理对象不实现目标对象接口的话,这个动作是没有办法赋值的;
而CGLIB的思路还是满足OOP的关系,它不是通过实现接口,而是通过继承,生成一个新的类去继承目标类,他是通过方法重写去对目标类的方法功能的增强;目标对象类不能加final关键字,因为加上final后cglib没有办法继承目标类,从而就不能重写方法进行增强。
14.策略模式的本质是什么?多个策略可以组合使用吗?如果可以该如何设计?
对于某个业务规则,我们定义了一组算法,并将其进行封装,使得这组算法可以根据用户选择动态替换。这种设计模式,我们称为设计模式。使用这种设计模式有很多好处:
- 实现算法的多样化,大大丰富了用户选择。
- 符合开闭原则。如果加入新的策略,不会对原有代码造成入侵。所以拥有良好的扩展性。
- 避免了复杂的条件判断,使得代码易于维护。
- 策略模式的核心,或者说本质,其实可以概括为八个字:分离算法,选择实现。
我们使用策略模式,其实是在为客户端提供不同的选择。一个典型的应用场景是优惠方案
设计模式一般不会单独使用,通常是组合起来使用的,策略模式也不例外,所以当然是可以组合使用的。
1,工厂模式。我们可以定义一个生产策略的工厂,将不同算法统一由算法工厂产生,这样就把“选项”和“客户选择”分离开了,便于后续维护和扩展。上述例子中,可以有一个工厂类生产优惠策略。
2,模板方法模式。如果我们的算法有固定步骤,而只有其中一步或几步需要变化,则可以使用模板方法模式定义骨架。上述例子中,如果折扣和返现有一些公共步骤,比如计算优惠额度之类的,可以放到模板里。
3,装饰器模式。如果算法需要额外扩展,比如颜值8分以上则享受七折优惠,那么可以把折扣算法用装饰器进行扩展。我们也可以直接使用装饰器模式将上述两种算法组合起来,实现“折扣的基础上再返现”的需求,但这种实现不够优雅:我们希望“折扣的基础上返现”作为一个独立策略选项供用户使用。
4,外观模式。如果我们把各个算法看成一个个组件,那么可以用外观模式把这些组件按需求组合起来,比如组合上面的“折扣+返现”,形成新的策略选项供用户选择。
5,代理模式。代理模式应用比较广泛,上面列举的装饰器模式,一定程度上也算是一种特殊的代理,用这种方式当然也可以满足题目需求。
上面列举了一些常见的组合情况。实际使用的话,可以根据具体业务场景灵活把握。




15.代理模式和装饰器模式的区别和使用场景?


16.观察者模式的设计思想?应用场景?
观察者模式:
目标对象管理所有注册它的客户对象,当自身行为发生变化时,通过调用注册对象的回调函数,主动发出通知。
在这种设计模式里,目标对象我们称为被观察者,客户对象我们称为观察者,而触发的动作,我们称为事件。
本质上,观察者模式也是在实现“观察者”和“被观察者”的解耦,满足“高内聚,低耦合”的设计思想,也体现了单一原则。这种设计的关键,在于被观察者持有观察者的引用和回调函数,然后通过“事件”来实现被观察者与观察者的通信。
这种设计思想有广泛的用途,比如大家熟知的mq产品,消息发布者维护一组消息订阅者的信息,当满足某些场景时需要发送消息,则通过一些策略将消息发送给订阅者,这完全符合观察者模式的设计思想,我们也称这种模式为“发布订阅模式”。
再比如现在流行的reactive响应式编程,也是一种基于事件驱动的异步化编程方式。在这种编程模型里,有两个关键要素:组件和事件。组件在做某些操作时,通过事件触发其它组件的行为。举个简单例子:传统串行化的编程模型里,如果我们要实现一个下单功能,则需要:生成订单,扣减库存。而reactive式编程,则是生成订单后发送事件,扣减库存的操作对订单生成事件作出反应。本质上还是观察者模式的设计理念,但这种方式实现了应用之间的松耦合,而且可以异步化并行执行,在分布式系统里有广泛的应用。
使用场景:
- 异步化执行。当我们需要对某个复杂业务做拆分时,可以使用观察者模式,具体做法如下:复杂业务拆成A,B两部分。B作为观察者提供启动函数并注册到A。A在业务完成时,循环调用注册到自己的观察者的回调函数,则可以实现业务异步执行。
- 消息通知。比如我在做发货操作时,需要通知不同app,告诉他们哪个订单发货了,需要做承运。而目前系统里app很多,那么我使用观察者模式,发货逻辑作为被观察者,当它完成时,循环通知注册的各类app,告诉他们订单发货的消息。
- 框架设计。其实我们看各类源码可以发现观察者模式有广泛用途,比如spring中refresh方法里,容器开始初始化,初始化完成等节点,会触发各种事件用以异步开启其它逻辑。再比如tomcat源码里,外层server初始化完毕后触发内层service等等的各种操作。
- 暴露扩展点。假如你作为服务提供方,返回了标准的对象A给客户使用。但有时候对象A不太能够满足一些比较偏门的需求,而你又不能预知客户具体需要扩展哪些功能,则只能留一个扩展点交给客户自行实现了。具体做法如下:通过配置入口接受一个观察者的注册列表。当配置了观察者时,则在返回标准对象A前,通过观察者模式触发客户自定义的回调函数对对象A进行增强。完成上述设计后,客户如果需要扩展A,则只需要配置观察者和自定义的回调函数即可。
17.什么场景下才应该使用委派模式?为什么双亲委派一般使用继承来实现?
委派模式的出发点很简单,假设我们有一个业务场景,在代码非常杂乱,功能非常复杂的情况下,这个时候当我们做出来之后,在后期维护的时候会造成理解上的麻烦,和维护上的困难,所以我们在设计阶段,通常会把功能比较独特,业务相互隔离的功能进行分开,分离,然后我们再使用委派模式去调用,作为调度器去分发各自的任务,让他们各自去完成,最后再把结果汇总在一起,这个就是委派模式的应用场景。
双亲委派我们通常会想到类加载器,是说某个Class如果被父类所加载,自己就不会去加载,有加载的优先级;
双亲委派,就是会委派多次,主要是遵循单一职责原理,如果已经被加载,在第二次使用的时候就不会重复去加载,这就是双亲委派的应用场景;看到“双亲”,我们很自然想到父子关系。使用继承的方式来实现,可以在保持各层单一职责的同时,将多层功能串联起来,形成一个整体供客户端使用。类比于普通委派模式的“调度器”,双亲委派机制是另一种调度方式。比如负责加载核心包的bootstrap类加载器只专注于加载核心包的事情,ext类加载器专注于加载扩展组件,互不干涉,能够保持单一指责,然后通过继承的方式,并用父类优先的委派方式,可以屏蔽客户端调用的复杂度(只关注子类这个类加载器,核心/扩展等类加载机制,客户端不用关注),这和普通委派达到了相同的效果:简化复杂功能场景,提供统一调度入口。








18.软件设计阶段可能应用适配器模式吗?如果用,在什么场景之下才应用?
适配器模式(Adapter模式)便是结构型中的其中一个模式,它的作用是:将一个类的接口转换成客户希望的另外一个接口。Adapter模式使原本由于接口不兼容而不能在一起工作的类可以一起工作。
适配器模式应用场景 :
- 想要使用一个已经存在的类,但是它却不符合现有的接口规范,导致无法直接去访问,这时创建一个适配器就能间接去访问这个类中的方法。
- 我们有一个类,想将其设计为可重用的类(可被多处访问),我们可以创建适配器来将这个类来适配其他没有提供合适接口的类。
以上两个场景其实就是从两个角度来描述一类问题,那就是要访问的方法不在合适的接口里,一个从接口出发(被访问),一个从访问出发(主动访问)。 一般在软件设计阶段不会考虑适配器模式,而是在随着软件维护,由于不同产品不同厂家造成功能相同而接口不同的情况下的解决方案。 而在设计阶段中,如果想要使用接口中的某个或某些方法,但是接口中有太多方法,我们要使用时必须实现接口并实现其中的所有方法,可以使用抽象类来实现接口,并不对方法进行实现(仅置空),然后我们再继承这个抽象类来通过重写想用的方法的方式来实现。这个抽象类就是适配器。



19.模版方法模式如何与其他设计模式结合?
模板方法模式是行为型模式的一种。在父类中定义算法的骨架,把具体实延迟到子类中去,可以在不改变一个算法的结构时可重定义该算法的某些步骤。通常,我们会用它来定义一些公共模板用于一些特定操作,比如大家熟知的jdbcTemplate,再比如spring整合各种中间件提供的模板工具,如rabbitTemplate,redisTemplate等等。
常见组合:
- 模板方法模式结合策略模式。如果算法骨架中的内容需要客户根据情况进行选择,那么,我们可以再实现算法骨架时,采用策略模式提供不同选项。如jdbcTemplate里,如果有多种数据源,可以考虑封装成不同策略,使得客户端能够根据自己的需要进行个性化选择。
- 模板方法模式结合工厂模式。如果我们的模板需要经常使用,那么可以用一个工厂类专门生产模板供用户使用。
以上两种是比较常见的组合,还有一些组合不常见,但也有使用场景:
- 结合装饰者模式。如果我们的模板方法需要增强,那么可以用装饰者模式给它套一层,来丰富我们的功能。
- 结合代理模式。比如,我们的模板里需要提前加载一些资源,或者需要一些前置处理,可以用代理模式来做前置或后置的增强。
- 结合委派模式。算法骨架里,可能有好几个操作,很复杂,我们可以用委派模式将功能拆分,提供简化的访问入口。




20.责任链模式能解决什么问题?如何解决?
责任链模式:使多个对象都有机会处理请求,从而避免请求的发送者与请求处理者耦合在一起。将这些对象连接成一条链,并且沿着这条链传递请求,直到有对象处理它为止。它属于行为模式。客户无需关注处理细节和请求的传递,实现请求者和处理者的解偶。
优点:
- 降低耦合度,使请求的发送者和接收者解耦,便于灵活的、可插拔的定义请求处理过程。
- 简化、封装了请求的处理过程,并且这个过程对客户端而言是透明的,以便于动态地重新组织链以及分配责任,增强请求处理的灵活性。











2174

被折叠的 条评论
为什么被折叠?



