志在指尖
用双手敲打未来

java设计模式(面试题和答案)

java设计模式

单例规划形式:一个类只允许创立一个方针(或许实例),那这个类便是一个单例类,这种规划形式就叫做单例规划形式
1.怎么完结一个单例
结构函数需求是private访问权限的,这样才能防止外部经过new创立实例;
考虑方针创立时的线程安全问题;
考虑是否支撑推迟加载;
考虑getInstance()功用是否高(是否加锁)
2.常见的几种单例形式:
饿汉式:饿汉式的完结办法比较简略。在类加载的时分,instance静态实例就现已创立并初始化好了,所以,instance实例的创立进程是线程安全的。不过,这样的完结办法不支撑推迟加载(在真实用到的时分,再创立实例)。
有人觉得这种完结办法欠好,由于不支撑推迟加载,假如实例占用资源多(比方占用内存多)或初始化耗时长(比方需求加载各种装备文件),提早初始化实例是一种浪费资源的行为。最好的办法应该在用到的时分再去初始化。不过,我个人并不认同这样的观点。假如初始化耗时长,那咱们最好不要等到真实要用它的时分,才去履行这个耗时长的初始化进程,这会影响到体系的功用(比方,在呼应客户端接口恳求的时分,做这个初始化操作,会导致此恳求的呼应时间变长,甚至超时)。选用饿汉式完结办法,将耗时的初始化操作,提早到程序发动的时分完结,这样就能防止在程序运行的时分,再去初始化导致的功用问题。假如实例占用资源多,按照fail-fast的规划准则(有问题及早露出),那咱们也期望在程序发动时就将这个实例初始化好。假如资源不行,就会在程序发动的时分触发报错(比方Java中的PermGenSpaceOOM),咱们能够当即去修正。这样也能防止在程序运行一段时间后,突然由于初始化这个实例占用资源过多,导致体系崩溃,影响体系的可用性。
懒汉式:有饿汉式,对应的,就有懒汉式。懒汉式相关于饿汉式的优势是支撑推迟加载。
不过懒汉式的缺陷也很明显,咱们给getInstance()这个办法加了一把大锁(synchronzed),导致这个函数的并发度很低。量化一下的话,并发度是1,也就相当于串行操作了。而这个函数是在单例运用期间,一向会被调用。假如这个单例类偶尔会被用到,那这种完结办法还能够承受。可是,假如频频地用到,那频频加锁、释放锁及并发度低等问题,会导致功用瓶颈,这种完结办法就不可取了。
双重检测:饿汉式不支撑推迟加载,懒汉式有功用问题,不支撑高并发。那咱们再来看一种既支撑推迟加载、又支撑高并发的单例完结办法,也便是双重检测完结办法。
在这种完结办法中,只需instance被创立之后,即使再调用getInstance()函数也不会再进入到加锁逻辑中了。所以,这种完结办法处理了懒汉式并发度低的问题。
网上有人说,这种完结办法有些问题。由于指令重排序,或许会导致IdGenerator方针被new出来,并且赋值给instance之后,还没来得及初始化(履行结构函数中的代码逻辑),就被另一个线程运用了。要处理这个问题,咱们需求给instance成员变量加上volatile关键字,禁止指令重排序才行。实际上,只要很低版别的Java才会有这个问题。咱们现在用的高版别的Java现已在JDK内部完结中处理了这个问题(处理的办法很简略,只需把方针new操作和初始化操作规划为原子操作,就天然能禁止重排序)。
静态内部类:
SingletonHolder是一个静态内部类,当外部类IdGenerator被加载的时分,并不会创立SingletonHolder实例方针。只要当调用getInstance()办法时,SingletonHolder才会被加载,这个时分才会创立instance。instance的唯一性、创立进程的线程安全性,都由JVM来确保。所以,这种完结办法既确保了线程安全,又能做到推迟加载。

java
枚举:
最后,咱们介绍一种最简略的完结办法,根据枚举类型的单例完结。这种完结办法经过Java枚举类型本身的特性,确保了实例创立的线程安全性和实例的唯一性
工厂形式分为三种愈加细分的类型:简略工厂、工厂办法和笼统工厂
1.工厂形式:界说一个用于创立方针的接口,让子类决议实例化哪一个类。使一个类的实例化推迟到其子类
适用:当一个类不知道它所有必要创立的方针的类的时分
publicinterfaceIRuleConfigParserFactory{
IRuleConfigParsercreateParser();
}
publicclassJsonRuleConfigParserFactoryimplementsIRuleConfigParserFactory{
@Override
publicIRuleConfigParsercreateParser(){
returnnewJsonRuleConfigParser();
}
}
publicclassXmlRuleConfigParserFactoryimplementsIRuleConfigParserFactory{
@Override
publicIRuleConfigParsercreateParser(){
returnnewXmlRuleConfigParser();
}
}
publicclassYamlRuleConfigParserFactoryimplementsIRuleConfigParserFactory{
@Override
publicIRuleConfigParsercreateParser(){
returnnewYamlRuleConfigParser();
}
}
publicclassPropertiesRuleConfigParserFactoryimplementsIRuleConfigParserFactory{
@Override
publicIRuleConfigParsercreateParser(){
returnnewPropertiesRuleConfigParser();
}
}
从上面的工厂办法的完结来看,全部都很完美,可是实际上存在挺大的问题。问题存在于这些工厂类的运用上。接下来,咱们看一下,怎么用这些工厂类来完结RuleConfigSource的load()函数。详细的代码如下所示:
publicclassRuleConfigSource{
publicRuleConfigload(StringruleConfigFilePath){
StringruleConfigFileExtension=getFileExtension(ruleConfigFilePath);
IRuleConfigParserFactoryparserFactory=RuleConfigParserFactoryMap.getParserFactory(ruleConfigFileExtension);
if(parserFactory==null){
thrownewInvalidRuleConfigException(“Ruleconfigfileformatisnotsupported:”+ruleConfigFilePath);
}
IRuleConfigParserparser=parserFactory.createParser();
StringconfigText=””;
//从ruleConfigFilePath文件中读取装备文本到configText中
RuleConfigruleConfig=parser.parse(configText);
returnruleConfig;
}
privateStringgetFileExtension(StringfilePath){
//…解析文件名获取扩展名,比方rule.json,回来json
return”json”;
}
}
//由于工厂类只包含办法,不包含成员变量,完全能够复用,
//不需求每次都创立新的工厂类方针,所以,简略工厂形式的第二种完结思路愈加适宜。
publicclassRuleConfigParserFactoryMap{//工厂的工厂
privatestaticfinalMapcachedFactories=newHashMap<>();
static{
cachedFactories.put(“json”,newJsonRuleConfigParserFactory());
cachedFactories.put(“xml”,newXmlRuleConfigParserFactory());
cachedFactories.put(“yaml”,newYamlRuleConfigParserFactory());
cachedFactories.put(“properties”,newPropertiesRuleConfigParserFactory());
}
publicstaticIRuleConfigParserFactorygetParserFactory(Stringtype){
if(type==null||type.isEmpty()){
returnnull;
}
IRuleConfigParserFactoryparserFactory=cachedFactories.get(type.toLowerCase());
returnparserFactory;
}
}
当咱们需求添加新的规矩装备解析器的时分,咱们只需求创立新的parser类和parserfactory类,并且在RuleConfigParserFactoryMap类中,将新的parserfactory方针添加到cachedFactories中即可。代码的改动十分少,根本上符合开闭准则。实际上,关于规矩装备文件解析这个运用场景来说,工厂形式需求额定创立许多Factory类,也会添加代码的杂乱性,并且,每个Factory类仅仅做简略的new操作,功用十分单薄(只要一行代码),也没必要规划成独立的类,所以,在这个运用场景下,简略工厂形式简略好用,比工厂办法形式愈加适宜。
2.笼统工厂形式:界说一个用于创立方针的接口,让子类决议实例化哪一个类。使一个类的实例化推迟到其子类
适用:一个体系要独立于它的产品的创立、组合和表明时
//产品1interfaceIProduct1{}
classProduct1AimplementsIProduct1{}
//扩展产品1B系列
classProduct1BimplementsIProduct1{}
//产品2
interfaceIProduct2{}
classProduct2AimplementsIProduct2{}
//扩展产品2B系列
classProduct2BimplementsIProduct2{}
//工厂
interfaceIFactory{
publicIProduct1getProduct1();
publicIProduct2getProduct2();
};
//工厂A,生产A系列产品
classFactoryAimplementsIFactory{
publicIProduct1getProduct1(){
returnnewProduct1A();
};
publicIProduct2getProduct2(){
returnnewProduct2A();
};
}
//工厂B,生产B系列产品
classFactoryBimplementsIFactory{
publicIProduct1getProduct1(){
returnnewProduct1B();
};
publicIProduct2getProduct2(){
returnnewProduct2B();
};
}
publicclasstestAbstractFactory{
publicvoidtest(){
IFactoryfactory=newFactoryA();
IProduct1product1A=(IProduct1)factory.getProduct1();
IProduct2product2A=(IProduct2)factory.getProduct2();
//假如扩展产品系列B时,添加FactoryB、ProductB即可,不需求修正本来代码
factory=newFactoryB();
IProduct1product1B=(IProduct1)factory.getProduct1();
IProduct2product2B=(IProduct2)factory.getProduct2();
}
}
当咱们需求添加新的规矩装备解析器的时分,咱们只需求创立新的parser类和parserfactory类,并且在RuleConfigParserFactoryMap类中,将新的parserfactory方针添加到cachedFactories中即可。代码的改动十分少,根本上符合开闭准则。实际上,关于规矩装备文件解析这个运用场景来说,工厂形式需求额定创立许多Factory类,也会添加代码的杂乱性,并且,每个Factory类仅仅做简略的new操作,功用十分单薄(只要一行代码),也没必要规划成独立的类,所以,在这个运用场景下,简略工厂形式简略好用,比工厂办法形式愈加适宜。
判别是否用工厂形式的标准:
封装改变:创立逻辑有或许改变,封装成工厂类之后,创立逻辑的变更对调用者通明。
代码复用:创立代码抽离到独立的工厂类之后能够复用。
阻隔杂乱性:封装杂乱的创立逻辑,调用者无需了解怎么创立方针。
控制杂乱度:将创立代码抽离出来,让原本的函数或类职责更单一,代码更简洁。
结构者形式:将一个杂乱方针的构建与它的表明别离,使得同样的构建进程能够创立不同的表明
适用:当创立杂乱方针的算法应该独立于该方针的组成部分以及它们的装配办法时
publicclassResourcePoolConfig{
privateStringname;
privateintmaxTotal;
privateintmaxIdle;
privateintminIdle;
privateResourcePoolConfig(Builderbuilder){
this.name=builder.name;
this.maxTotal=builder.maxTotal;
this.maxIdle=builder.maxIdle;
this.minIdle=builder.minIdle;
}
//…省掉getter办法…
//咱们将Builder类规划成了ResourcePoolConfig的内部类。
//咱们也能够将Builder类规划成独立的非内部类ResourcePoolConfigBuilder。
publicstaticclassBuilder{
privatestaticfinalintDEFAULT_MAX_TOTAL=8;
privatestaticfinalintDEFAULT_MAX_IDLE=8;
privatestaticfinalintDEFAULT_MIN_IDLE=0;
privateStringname;
privateintmaxTotal=DEFAULT_MAX_TOTAL;
privateintmaxIdle=DEFAULT_MAX_IDLE;
privateintminIdle=DEFAULT_MIN_IDLE;
publicResourcePoolConfigbuild(){
//校验逻辑放到这儿来做,包含必填项校验、依靠联系校验、约束条件校验等
if(StringUtils.isBlank(name)){
thrownewIllegalArgumentException(“…”);
}
if(maxIdle>maxTotal){
thrownewIllegalArgumentException(“…”);
}
if(minIdle>maxTotal||minIdle>maxIdle){
thrownewIllegalArgumentException(“…”);
}
returnnewResourcePoolConfig(this);
}
publicBuildersetName(Stringname){
if(StringUtils.isBlank(name)){
thrownewIllegalArgumentException(“…”);
}
this.name=name;
returnthis;
}
publicBuildersetMaxTotal(intmaxTotal){
if(maxTotal<=0){
thrownewIllegalArgumentException(“…”);
}
this.maxTotal=maxTotal;
returnthis;
}
publicBuildersetMaxIdle(intmaxIdle){
if(maxIdle<0){
thrownewIllegalArgumentException(“…”);
}
this.maxIdle=maxIdle;
returnthis;
}
publicBuildersetMinIdle(intminIdle){
if(minIdle<0){
thrownewIllegalArgumentException(“…”);
}
this.minIdle=minIdle;
returnthis;
}
}
}
//这段代码会抛出IllegalArgumentException,由于minIdle>maxIdle
ResourcePoolConfigconfig=newResourcePoolConfig.Builder()
.setName(“dbconnectionpool”)
.setMaxTotal(16)
.setMaxIdle(10)
.setMinIdle(12)
.build();
假如一个类中有许多特点,为了防止结构函数的参数列表过长,影响代码的可读性和易用性,咱们能够经过结构函数配合set()办法来处理。可是,假如存在下面情况中的恣意一种,咱们就要考虑运用建造者形式了。
咱们把类的必填特点放到结构函数中,强制创立方针的时分就设置。假如必填的特点有许多,把这些必填特点都放到结构函数中设置,那结构函数就又会呈现参数列表很长的问题。假如咱们把必填特点经过set()办法设置,那校验这些必填特点是否现已填写的逻辑就无处安放了。
假如类的特点之间有一定的依靠联系或许约束条件,咱们持续运用结构函数配合set()办法的规划思路,那这些依靠联系或约束条件的校验逻辑就无处安放了。
假如咱们期望创立不可变方针,也便是说,方针在创立好之后,就不能再修正内部的特点值,要完结这个功用,咱们就不能在类中露出set()办法。结构函数配合set()办法来设置特点值的办法就不适用了。
原型形式:用原型实例指定创立方针的品种,并且经过仿制这些原型创立新的方针
适用:当要实例化的类是在运行时间指守时;或许需求创立多个方针并且这些方针内部状况相差不大
原型形式有两种完结办法,深仿制和浅仿制。浅仿制只会仿制方针中根本数据类型数据和引证方针的内存地址,不会递归地仿制引证方针,以及引证方针的引证方针……而深仿制得到的是一份完完全全独立的方针。所以,深仿制比起浅仿制来说,愈加耗时,愈加耗内存空间。假如要仿制的方针是不可变方针,浅仿制共享不可变方针是没问题的,但关于可变方针来说,浅仿制得到的方针和原始方针会共享部分数据,就有或许呈现数据被修正的风险,也就变得杂乱多了。
classCarimplementsCloneable{
privateintid;
publicintgetId(){returnid;}
publicvoidsetId(intid){this.id=id;}
publicCarclone(){
try{
return(Car)super.clone();
}catch(CloneNotSupportedExceptione){
e.printStackTrace();
returnnull;
}
}
}
classPrototypeimplementsCloneable{
privateintid;
privateCarcar;
publicCargetCar(){returncar;}
publicvoidsetCar(Carcar){this.car=car;}
publicintgetId(){returnid;}
publicvoidsetId(intid){this.id=id;}
publicObjectclone(){
try{
booleandeep=true;
if(deep){
/**
*深仿制,仿制出了两辆车
**/
Prototypeprototype=(Prototype)super.clone();
prototype.setCar((Car)this.car.clone());
//持续仿制其他引证方针
returnprototype;
}else{
/**
*浅仿制,是同一辆车
**/
returnsuper.clone();
}
}catch(CloneNotSupportedExceptione){
e.printStackTrace();
returnnull;
}
}
}
publicclassTestPrototype{
publicvoidtest(){
Prototypep1=newPrototype();
p1.setCar(newCar());
p1.setId(1);
//仿制
Prototypep2=(Prototype)p1.clone();
p2.setId(2);
}
}
署理形式:署理是一种常用的规划形式,署理形式能够对原有的类进行扩展,即经过署理方针的形式来访问方针类。
1.静态署理:静态署理需求署理类与方针类有一样的承继父类和完结接口
接口:
publicinterfaceUserDao{
publicvoidaddUser();
publicvoiddeleteUser();
}
方针类:
publicclassDbUserimplementsUserDao{
@Override
publicvoidaddUser(){
System.out.println(“添加用户操作”);
}
@Override
publicvoiddeleteUser(){
System.out.println(“删去用户操作”);
}
}
静态署理类:
publicclassStaticProxyUserimplementsUserDao{
privateDbUsertarget;
publicStaticProxyUser(DbUserdbUser){
target=dbUser;
}
@Override
publicvoidaddUser(){
System.out.println(“添加前的操作”);
target.addUser();
System.out.println(“添加后的操作”);
}
@Override
publicvoiddeleteUser(){
System.out.println(“删去前的操作”);
target.deleteUser();
System.out.println(“删去后的操作”);
}
}
测试:
publicclassMyTest{
@Test
publicvoiddemo(){
StaticProxyUserstaticProxyUser=newStaticProxyUser(newDbUser());
staticProxyUser.addUser();
staticProxyUser.deleteUser();
}
}
静态署理总结:
1.能够做到在不修正方针方针的功用前提下,对方针功用扩展.
2.缺陷:
由于署理方针需求与方针方针完结一样的接口,所以会有许多署理类,类太多.一起,一旦接口添加办法,方针方针与署理方针都要保护.
2动态署理(JDK署理)
动态署理不用完结方针类的接口,不会呈现许多署理类的现象,一般情况下创立一个署理类就能够了。
动态署理方针的生成,是运用JDK的API,动态的在内存中构建署理方针(需求咱们指定创立署理方针/方针方针完结的接口的类型)
动态署理也叫做:JDK署理,接口署理
动态署理需求运用newProxyInstance办法,该办法结构为staticObjectnewProxyInstance(ClassLoaderloader,Class<问号>]interfaces,InvocationHandlerh),能够看到该办法需求三个参数
参数1:ClassLoader需求一个类加载器,Java中常用的类加载器有三品种型
发动类加载器(BootstrapClassLoader):
这个类加载器担任将\lib目录下的类库加载到虚拟机内存中,用来加载java的中心库,此类加载器并不承继于java.lang.ClassLoader,不能被java程序直接调用,代码是运用C++编写的.是虚拟机自身的一部分.
扩展类加载器(ExtendsionClassLoader):
这个类加载器担任加载\lib\ext目录下的类库,用来加载java的扩展库,开发者能够直接运用这个类加载器.
运用程序类加载器(ApplicationClassLoader):
这个类加载器担任加载用户类途径(CLASSPATH)下的类库,一般咱们编写的java类都是由这个类加载器加载,这个类加载器是CLassLoader中的getSystemClassLoader()办法的回来值,所以也称为体系类加载器.一般情况下这便是体系默认的类加载器.
除此之外,咱们还能够加入自己界说的类加载器,以满意特别的需求,需求承继java.lang.ClassLoader类.
参数2:Class<问好>[]interfaces,:方针方针完结的接口的类型,运用泛型办法承认类型
参数3:InvocationHandlerh:事情处理,履行方针方针的办法时,会触发事情处理器的办法,会把当时履行方针方针的办法作为参数传入
接口:
publicinterfaceUserService{
voidaddUser();
voidupdateUser();
voiddelUser();
}
方针类:
publicclassUserServiceImplimplementsUserService{
@Override
publicvoidaddUser(){
System.out.println(“添加用户”);
}
@Override
publicvoidupdateUser(){
System.out.println(“更新用户”);
}
@Override
publicvoiddelUser(){
System.out.println(“删去用户”);
}
}
署理类:
publicclassProxyFactory{
//保护一个方针方针
privateObjecttarget;
publicProxyFactory(Objecttarget){
this.target=target;
}
publicObjectgetProxyInstance(){
returnProxy.newProxyInstance(target.getClass().getClassLoader(),
target.getClass().getInterfaces(),newInvocationHandler(){
@Override
publicObjectinvoke(Objecto,Methodmethod,Object[]args)throwsThrowable{
System.out.println(“开端事务2”);
//履行方针方针
Objectinvoke=method.invoke(target,args);
System.out.println(“提交事务2”);
returninvoke;
}
});
}
}
测试:
@Test
publicvoidtestUserService(){
//方针方针
UserServicetarget=newUserServiceImpl();
UserServiceproxy=(UserService)newProxyFactory(target).getProxyInstance();
proxy.addUser();
}
3CGLIB署理
上面的静态署理和动态署理形式都是要求方针方针是完结一个接口的方针方针,可是有时分方针方针仅仅一个独自的方针,并没有完结任何的接口,这个时分就能够运用以方针方针子类的办法类完结署理,这种办法就叫做:Cglib署理
Cglib署理,也叫作子类署理,它是在内存中构建一个子类方针从而完结对方针方针功用的扩展.
JDK的动态署理有一个限制,便是运用动态署理的方针有必要完结一个或多个接口,假如想署理没有完结接口的类,就能够运用Cglib完结.
Cglib是一个强大的高功用的代码生成包,它能够在运行期扩展java类与完结java接口.它广泛的被许多AOP的结构运用,例如SpringAOP和synaop,为他们提供办法的interception(拦截)
Cglib包的底层是经过运用一个小而块的字节码处理结构ASM来转换字节码并生成新的类.不鼓励直接运用ASM,由于它要求你有必要对JVM内部结构包含class文件的格局和指令集都很了解.
Cglib子类署理完结办法:
1.需求引进cglib的jar文件,可是Spring的中心包中现已包含了Cglib功用,所以直接引进pring-core-3.2.5.jar即可.
2.引进功用包后,就能够在内存中动态构建子类
3.署理的类不能为final,不然报错
4.方针方针的办法假如为final/static,那么就不会被拦截,即不会履行方针方针额定的事务办法.
接口和方针类同上
署理类:
publicclassMyBeanFactory{
//cglib署理
publicstaticUserServicecreateUserService(){
//方针类
finalUserServiceuserService=newUserServiceImpl();
/**
*署理类
*回掉函数中的intercept同jdk动态署理的invoke办法
*4个参数,前三个同jdk动态署理的参数
*第四个参数:methodProxy是方针类的办法的署理
*
*/
//1.1中心类
Enhancerenhancer=newEnhancer();
//1.2确认父类
enhancer.setSuperclass(UserService.class);
//1.3设置回掉函数
enhancer.setCallback(newMethodInterceptor(){
@Override
publicObjectintercept(Objectproxy,Methodmethod,Object[]args,MethodProxymethodProxy)throwsThrowable{
System.out.println(“履行前”);
Objectinvoke=method.invoke(userService,args);
System.out.println(“履行后”);
returninvoke;
}
});
//1.4创立署理方针
UserServiceproxyUserService=(UserService)enhancer.create();
returnproxyUserService;
}
}
测试:
publicclasstest{
@Test
publicvoiddemo(){
UserServiceuserService=MyBeanFactory.createUserService();
userService.addUser();
userService.updateUser();
userService.deleteUser();
}
}
运用时假如方针方针没有完结接口能够选用cglib署理,方针类完结了接口选用jdk动态署理,静态署理一般不建议运用。
桥接形式:将笼统部分与它的完结部分别离,使它能够独立的变更
角色:笼统层接口(Abstraction)、详细笼统层、完结者接口、详细完结者
什么时分用:1.不想在笼统与完结之间形成固定的绑定联系(这样就能在运行时切换完结)。2.笼统与完结都应能够经过子类化独立进行扩展。
3.对笼统的完结进行修正不应影响客户端代码。4.假如每个完结需求额定的子类以细化笼统,则阐明有必要把它们分成两个部分。5.想在带有不同笼统接口的多个方针之间共享一个完结。
桥接形式有两种了解办法。第一种了解办法是“将笼统和完结解耦,让它们能独立开发”。这种了解办法比较特别,运用场景也不多。另一种了解办法愈加简略,类似“组合优于承继”规划准则
interfaceILeader{
publicvoiddoSomething();
}
classLeaderAimplementsILeader{
@Override
publicvoiddoSomething(){}
}
classLeaderBimplementsILeader{
@Override
publicvoiddoSomething(){}
}
classBoss{
ILeaderleader;
publicvoidsetLeader(ILeaderleader){
this.leader=leader;
}
publicvoiddoSomething(){
this.leader.doSomething();
}
}
publicclassTestBirdge{
publicvoidtest(){
Bossboss=newBoss();
LeaderAleaderA=newLeaderA();
boss.setLeader(leaderA);
boss.doSomething();
//当某个经理离任的时分,老板能够再找一个有经验的经理来做事,
LeaderBleaderB=newLeaderB();
boss.setLeader(leaderB);
boss.doSomething();
}
}
装修器形式:动态的给方针添加一些额定的职责,就添加功用来说,装修比生成子类更为灵敏
角色:组件接口(Component)、详细的组件、承继至Component的润饰接口(Decorator)、详细的润饰
了解:润饰接口Decorator承继Component,并持有Component的一个引证,所以起到了复用Component并添加新的功用。
什么时分用:1.想要在不影响其他方针的情况下,以动态、通明的办法给单个方针添加职责。2.想要扩展一个类的行为,却做不到。类界说或许被躲藏,无法进行子类化;或许对类的每个行为的扩展,哦支撑每种功用组合,将产生许多的子类。
interfaceICar{
publicvoidrun();
}
classCarimplementsICar{
@Override
publicvoidrun(){
}
}
//现在想给轿车添加氮气加快
//下面用子类化办法完结
classSubClassCarextendsCar{
@Override
publicvoidrun(){
this.addNitrogen();
super.run();
}
publicvoidaddNitrogen(){}
}
//下面用装修形式完结
classDecoratorCarimplementsICar{
privateCarcar;
@Override
publicvoidrun(){
this.addNitrogen();
car.run();
}
publicvoidaddNitrogen(){}
}
publicclassTestDecorator{
publicvoidtest(){
}
}
适配器形式:用来做适配的,它将不兼容的接口转换为可兼容的接口,让原本由于接口不兼容而不能一起作业的类能够一起作业
适配器形式有两种完结办法:类适配器和方针适配器。其中,类适配器运用承继联系来完结,方针适配器运用组合联系来完结
//类适配器:根据承继
publicinterfaceITarget{
voidf1();
voidf2();
voidfc();
}
publicclassAdaptee{
publicvoidfa(){//…}
publicvoidfb(){//…}
publicvoidfc(){//…}
}
publicclassAdaptorextendsAdapteeimplementsITarget{
publicvoidf1(){
super.fa();
}
publicvoidf2(){
//…从头完结f2()…
}
//这儿fc()不需求完结,直接承继自Adaptee,这是跟方针适配器最大的不同点
}
//方针适配器:根据组合
publicinterfaceITarget{
voidf1();
voidf2();
voidfc();
}
publicclassAdaptee{
publicvoidfa(){//…}
publicvoidfb(){//…}
publicvoidfc(){//…}
}
publicclassAdaptorimplementsITarget{
privateAdapteeadaptee;
publicAdaptor(Adapteeadaptee){
this.adaptee=adaptee;
}
publicvoidf1(){
adaptee.fa();//委托给Adaptee
}
publicvoidf2(){
//…从头完结f2()…
}
publicvoidfc(){
adaptee.fc();
}
}
针对这两种完结办法,在实际的开发中,到底该怎么选择运用哪一种呢?判别的标准主要有两个,一个是Adaptee接口的个数,另一个是Adaptee和ITarget的符合程度。
假如Adaptee接口并不多,那两种完结办法都能够。
假如Adaptee接口许多,并且Adaptee和ITarget接口界说大部分都相同,那咱们引荐运用类适配器,由于Adaptor复用父类Adaptee的接口,比起方针适配器的完结办法,Adaptor的代码量要少一些。
假如Adaptee接口许多,并且Adaptee和ITarget接口界说大部分都不相同,那咱们引荐运用方针适配器,由于组合结构相关于承继愈加灵敏。

java设计模式面试题和答案

1.规划形式是什么?你是否在代码中运用过?
规划形式(DesignPattern)是长辈们对代码开发经验的总结,是解决特定问题的一系列套路。它不是语法规定,而是一套用来提高代码可复用性、可维护性、可读性、稳健性以及安全性的解决方案。
规划形式便是程序员总结出来的一套用来提高代码可复用性、可维护性、可读性、稳健性以及安全性的解决方案。,
2.JDK中常用的规划形式有哪些?
3.单例形式是什么?请用Java写出线程安全的单例形式
单例(Singleton)形式:指一个类只要一个实例,且该类能自行创建这个实例的一种形式。Java.lang.Runtime是单例形式的经典比如。
publicclassSingleton5{
privateSingleton5(){}
privatestaticclassInstanceHolder{
privatestaticSingleton5instance=newSingleton5();
}
publicstaticSingleton5getInstance(){
returnInstanceHolder.instance;
}
}
便是套了一个私有的静态内部类。java语言标准确保了,一个类或许接口的静态成员在被赋值的时分,这个类会初始化(有个初始化锁,每个线程都会至少获取一次初始化锁确保初始化),这个进程比较复杂,成果便是对恣意线程,内部能够重排序,但是这种重排序对其他线程不可见。这个也是Google推荐的写法。
4.在Java中,什么叫观察者形式(observerdesignpattern)?
观察者(Observer)形式:指多个方针间存在一对多的依靠联系,当一个方针的状况发生改变时,所有依靠于它的方针都得到告诉并被自动更新。这种形式有时又称作发布-订阅形式、模型-视图形式,它是方针行为型形式。
观察者形式是一种方针行为型形式,其首要长处如下:
降低了方针与观察者之间的耦合联系,两者之间是笼统耦合联系。契合依靠倒置准则。
方针与观察者之间建立了一套触发机制。
它的首要缺点如下:
方针与观察者之间的依靠联系并没有完全解除,而且有可能呈现循环引证。
当观察者方针许多时,告诉的发布会花费许多时刻,影响程序的功率。
5.运用工厂形式有哪些好处?说说它的运用场景?
工厂形式包含简略工厂形式、工厂办法形式和笼统工厂形式三种。
6.举一个Java中完成装饰形式的比如?
7.在Java中,什么时分用重载,什么时分用重写?
8.Java是否能够从静态办法中拜访非静态变量?为什么?
9.什么情况下更倾向于运用笼统类而非接口?
接口和笼统类都遵从”面向接口而不是完成编码”规划准则,它能够添加代码的灵活性,能够适应不断变化的需求。下面有几个点能够协助你答复这个问题:
在Java中,你只能承继一个类,但能够完成多个接口。所以一旦你承继了一个类,你就失去了承继其他类的机会了。
接口通常被用来表示隶属描绘或行为如:Runnable、Clonable、Serializable
等等,因此当你运用笼统类来表示行为时,你的类就不能一起是Runnable和Clonable(注:这里的意思是指如果把Runnable
等完成为笼统类的情况),由于在Java中你不能承继两个类,但当你运用接口时,你的类就能够一起拥有多个不同的行为。

未经允许不得转载:IT技术网站 » java设计模式(面试题和答案)
分享到: 更多 (0)

评论 抢沙发

评论前必须登录!

 

志在指尖 用双手敲打未来

登录/注册IT技术大全

热门IT技术

C#基础入门   SQL server数据库   系统SEO学习教程   WordPress小技巧   WordPress插件   脚本与源码下载