1 Star 0 Fork 31

moyu3390 / Ebooks_1

forked from Java精选 / Ebooks 
加入 Gitee
与超过 1200万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
该仓库未声明开源许可证文件(LICENSE),使用请关注具体项目描述及其代码上游依赖。
克隆/下载
最新设计模式面试题及答案附答案汇总.md 21.32 KB
一键复制 编辑 原始数据 按行查看 历史

最新设计模式面试题及答案附答案汇总

全部面试题答案,更新日期:01月30日,直接下载吧!

下载链接:高清500+份面试题资料及电子书,累计 10000+ 页大厂面试题 PDF

设计模式

题1:设计模式有多少种,都有哪些设计模式?

Java有23种设计模式

设计模式总体分为三大类:

创建型模式,共五种:工厂方法模式、抽象工厂模式、单例模式、建造者模式、原型模式。

结构型模式,共七种:适配器模式、装饰器模式、代理模式、外观模式、桥接模式、组合模式、享元模式。

行为型模式,共十一种:策略模式、模板方法模式、观察者模式、迭代子模式、责任链模式、命令模式、备忘录模式、状态模式、访问者模式、中介者模式、解释器模式。

其实还有两类:并发型模式和线程池模式。

题2:Java 中代理模式如何实现静态代理?

如果代理类在程序运行前就已经存在,那么这种代理方式被成为静态代理,这种情况下的代理类一般都是在Java代码中定义的。

通常情况下, 静态代理中的代理类和委托类会实现同一接口或是派生自相同的父类。下面说一下如何实现静态代理模式。

接口(或抽象类),具体代码如下:

package com.yoodb;
 
public interface Business {
    public void doAction();
}

此处是真正意义上实现业务逻辑的类,具体代码如下:

package com.yoodb;
 
public class BusinessImpl implements Business {
 
    @Override
    public void doAction() {
        // TODO Auto-generated method stub
        System.out.println("实现业务逻辑.");
    }
 
}

自己并未实现业务逻辑接口,而是调用实现,具体代码如下:

package com.yoodb;
 
public class BusinessImplProxy implements Business{
     
    private BusinessImpl impl;
     
    @Override
    public void doAction() {
        // TODO Auto-generated method stub
        if(impl == null){
            impl = new BusinessImpl();
        }
        doBefore();
        impl.doAction();
        doAfter();
    }
    private void doBefore(){  
System.out.println("调用目标对象前做相关操作");  
    }  
     
    private void doAfter(){  
System.out.println("调用目标对象后做相关操作");  
    } 
}

单元测试,具体代码如下:

package com.yoodb;
 
public class MainTest {
    public static void main(String[] args) {
        Business business = new BusinessImplProxy();
        business.doAction();
    }
}

运行之后结果如下:

调用目标对象前做相关操作
实现业务逻辑.
调用目标对象后做相关操作

题3:Java 中代理模式如何实现 CGLIB 动态代理?

CGLIB动态代理和jdk代理一样,使用反射完成代理,不同的是他可以直接代理类(jdk动态代理不行,他必须目标业务类必须实现接口),CGLIB动态代理底层使用字节码技术,CGLIB动态代理不能对 final类进行继承。(CGLIB动态代理需要导入jar包)。

CGLIB动态代理原理是利用asm开源包,对代理对象类的class文件加载进来,通过修改其字节码生成子类来处理。

1)接口,具体代码如下:

package com.yoodb.blog;

public interface UserDao {
    void save();
}

2)实现业务逻辑类,具体代码如下:

package com.yoodb.blog;

//接口实现类
public class UserDaoImpl implements UserDao {
	public void save() {
		System.out.println("保存数据方法");
	}
}

3)代理主要类,具体代码如下:

package com.yoodb.blog;

import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;


public class CglibProxy implements MethodInterceptor {
	private Object targetObject;
	// 这里的目标类型为Object,则可以接受任意一种参数作为被代理类,实现了动态代理
	public Object getInstance(Object target) {
		// 设置需要创建子类的类
		this.targetObject = target;
		Enhancer enhancer = new Enhancer();
		enhancer.setSuperclass(target.getClass());
		enhancer.setCallback(this);
		return enhancer.create();
	}

	//代理实际方法
	public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
		System.out.println("开启事物");
		Object result = proxy.invoke(targetObject, args);
		System.out.println("关闭事物");
		// 返回代理对象
		return result;
	}
}

4)测试CGLIB动态代理,具体代码如下:

package com.yoodb.blog;

public class Test {
    public static void main(String[] args) {
        CglibProxy cglibProxy = new CglibProxy();
        UserDao userDao = (UserDao) cglibProxy.getInstance(new UserDaoImpl());
        userDao.save();
    }
}

题4:说说你理解的 Spring 中工厂模式?

Spring IOC

看过Spring源码就知道,在Spring IOC容器创建bean的过程是使用了工厂设计模式。

Spring中无论是通过xml配置还是通过配置类还是注解进行创建bean,大部分都是通过简单工厂来进行创建的。

当容器拿到了beanName和class类型后,动态的通过反射创建具体的某个对象,最后将创建的对象放到Map中。

题5:Java 中原型模式如何实现深拷贝?

深拷贝是指对值类型的成员变量进行值的复制,对引用类型的成员变量也进行引用对象的复制。

深拷贝除了浅度复制要复制的值外,还负责复制引用类型的数据。那些引用其他对象的变量将指向被复制过的新对象,而不再是原有的那些被引用的对象。换言之,深度复制把要复制的对象所引用的对象都复制了一遍,而这种对被引用到的对象的复制叫做间接复制。

方式一

1、实体类,具体代码如下:

//实体一
package com.yoodb;
 
public class Prototype implements Cloneable{
    private String name;
 
    public String getName() {
        return name;
    }
 
    public void setName(String name) {
        this.name = name;
    }
 
    @Override
    protected Object clone() {
        // TODO Auto-generated method stub
        try {
            return super.clone();
        } catch (CloneNotSupportedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        return null;
    }
     
     
}
//实体二
package com.yoodb;
 
public class NewPrototype implements Cloneable{
    private String id;
     
    private Prototype prototype;
 
    public String getId() {
        return id;
    }
 
    public void setId(String id) {
        this.id = id;
    }
 
    public Prototype getPrototype() {
        return prototype;
    }
 
    public void setPrototype(Prototype prototype) {
        this.prototype = prototype;
    }
 
    @Override
    protected Object clone() {
        NewPrototype prot = null;
        try {
            prot = (NewPrototype) super.clone();
            prot.prototype = (Prototype) this.getPrototype().clone();
            return prot;
        } catch (Exception e) {
            // TODO: handle exception
        }
        return null;
    }
     
     
}

2、测试函数,具体代码如下:

package com.yoodb;
 
public class TestMain {
    public static void main(String[] args) {
        //普通赋值
        Prototype pro = new Prototype();
        pro.setName("欢迎收藏blog.yoodb.com");
        NewPrototype newPro = new NewPrototype();
        newPro.setId("yoodb");
        newPro.setPrototype(pro);
        //克隆赋值
        NewPrototype proc = (NewPrototype) newPro.clone();
        proc.setId("yoodb");
        proc.getPrototype().setName("欢迎收藏blog.yoodb.com");
         
        System.out.println("original object id:" + newPro.getId());
        System.out.println("original object name:" + newPro.getPrototype().getName());
          
        System.out.println("cloned object id:" + proc.getId());
        System.out.println("cloned object name:" + proc.getPrototype().getName());
    }
     
}

3、运行结果如下:

original object id:yoodb
original object name:欢迎收藏blog.yoodb.com
cloned object id:yoodb
cloned object name:欢迎收藏blog.yoodb.com

方式二

利用串行化来实现深克隆,把对象写道流里的过程是串行化(Serilization)过程;把对象从流中读出来是并行化(Deserialization)过程。

1、实体类,具体代码如下:

//实体一
package com.yoodb;
 
import java.io.Serializable;
 
public class Prototype implements Serializable{
     
    private static final long serialVersionUID = 1L;
     
    private String name;
 
    public String getName() {
        return name;
    }
 
    public void setName(String name) {
        this.name = name;
    }
     
}
//实体二
package com.yoodb;
 
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
 
public class NewPrototype implements Serializable{
     
    private static final long serialVersionUID = 1L;
 
    private String id;
     
    private Prototype prototype;
 
    public String getId() {
        return id;
    }
 
    public void setId(String id) {
        this.id = id;
    }
 
    public Prototype getPrototype() {
        return prototype;
    }
 
    public void setPrototype(Prototype prototype) {
        this.prototype = prototype;
    }
 
    protected Object deepClone(){
        try {
            ByteArrayOutputStream bo = new ByteArrayOutputStream();
            ObjectOutputStream oo = new ObjectOutputStream(bo);
            oo.writeObject(this);
             
            ByteArrayInputStream bi = new ByteArrayInputStream(bo.toByteArray());
            ObjectInputStream oi = new ObjectInputStream(bi);
            return oi.readObject();
        } catch (Exception e) {
            // TODO: handle exception
        }
        return null;
    }
     
}

2、测试函数,具体代码如下:

package com.yoodb;
 
public class TestMain {
    public static void main(String[] args) {
        //普通赋值
        Prototype pro = new Prototype();
        pro.setName("欢迎收藏www.yoodb.com");
        NewPrototype newPro = new NewPrototype();
        newPro.setId("yoodb");
        newPro.setPrototype(pro);
        //克隆赋值
        NewPrototype proc = (NewPrototype) newPro.deepClone();
        proc.setId("yoodb");
        proc.getPrototype().setName("欢迎收藏www.yoodb.com");
         
        System.out.println("original object id:" + newPro.getId());
        System.out.println("original object name:" + newPro.getPrototype().getName());
          
        System.out.println("cloned object id:" + proc.getId());
        System.out.println("cloned object name:" + proc.getPrototype().getName());
    }
     
}

3、运行结果如下:

original object id:yoodb
original object name:欢迎收藏www.yoodb.com
cloned object id:yoodb
cloned object name:欢迎收藏www.yoodb.com

题6:Java 中原型模式有哪些使用方式?

1、实现Cloneable接口。在java语言有一个Cloneable接口,它的作用只有一个,就是在运行时通知虚拟机可以安全地在实现了此接口的类上使用clone方法。在java虚拟机中,只有实现了这个接口的类才可以被拷贝,否则在运行时会抛出CloneNotSupportedException异常。

2、重写Object类中的clone方法。Java中,所有类的父类都是Object类,Object类中有一个clone方法,作用是返回对象的一个拷贝,但是其作用域protected类型的,一般的类无法调用,因此Prototype类需要将clone方法的作用域修改为public类型。

题7:Java 中简单工厂模式有什么优缺点?

简单工厂模式优点:简单工厂模式能够根据外界给定的信息,决定究竟应该创建哪个具体类的对象。明确区分了各自的职责和权力,有利于整个软件体系结构的优化。

简单工厂模式缺点:很明显工厂类集中了所有实例的创建逻辑,容易违反GRASPR的高内聚的责任分配原则。

题8:Java 中什么是观察者模式?

观察者模式又称为发布/订阅(Publish/Subscribe)模式,观察者模式定义了对象间的一种一对多依赖关系,使得每当一个对象改变状态,则所有依赖于它的对象都会得到通知并被自动更新。

观察者(Observer)相当于事件监听者,被观察者(Observable)相当于事件源和事件,将观察者和被观察者的对象分离开,提高了应用程序的可维护性和重用性。执行逻辑时通知observer即可触发oberver的update,同时可传被观察者和参数。

观察者Observer:所有潜在的观察者必须实现观察者接口,这个接口只有update方法,当主题改变时,它被调用。

具体观察者ConcreteObserver: 具体观察者可以是任何实现了Observer接口的类。观察者必须注册具体主题,一边接收更新。

可观察者Subject: 主题接口,即可观察者Observable,对象使用此接口注册为观察者,或者把自己从观察者中删除,每个主题可以有多个观察者。

具体可观察者ConcreteSubject: 一个具体主题实现了主题接口,除了注册和撤销之外,具体主题还实现了notifyObservers()方法,这个方法用来在主题状态改变时更新所有观察者。具体主题也可能有设置和获取状态的方法。

Subject(被观察者)包含了一些需要在其状态改变时通知的观察者。因此,他应该提供给观察者可以register(注册)自己和unregister(注销)自己的方法。当Subject(被观察者)发生变化的时候,也需要包含一个方法来通知所有观察者。当通知观察者的时候,可以推送更新内容,或者提供另外一个方法来获得更新内容。

JAVA提供了内置的方式来实现观察者模式,java.util.Observable和java.util.Observer接口。然而他们用的不是很广泛。因为此实现过于简单,大多数时候我们都不想最后扩展的类仅仅是实现了观察者模式,因为JAVA类不能多继承。

Java Messages Service(JMS)消息服务使用观察者模式与命令模式来实现不同的程序之间的数据的发布和订阅。MVC模型-视图-控制框架也使用观察者模式,把模型当做被观察者,视图视为观察者。视图能够注册自己到模型上来获得模型的改变。

题9:什么是单例模式?

单例模式的定义就是确保某一个类仅有一个实例,而且自行实例化并向整个系统提供这个实例,这个类称为单例类,它提供全局访问的方法。

单例模式分为:懒汉式单例、饿汉式单例、登记式单例三种。

单例模式特点:

1)单例类只能有一个实例。

2)单例类必须自己创建自己的唯一实例。

3)单例类必须给所有其他对象提供这一实例。

单例模式的优点是内存中只有一个对象,节省内存空间;避免频繁的创建销毁对象,可以提高性能;避免对资源的多重占用,简化访问;为整个系统提供一个全局访问点。

单例模式的缺点是不适用于变化频繁的对象;滥用单利将带来一些问题,如为了节省资源将数据库连接池对象设计为的单利类,可能会导致共享连接池对象的程序过多而出现连接池溢出;如果实例化的对象长时间不被利用,系统会认为该对象是垃圾而被回收,这可能会导致对象状态的丢失。

从名字上来说饿汉和懒汉,饿汉就是类一旦加载,就把单例初始化完成,保证getInstance的时候单例是已经被初始化,而懒汉比较懒,只有当调用getInstance的时候,才回去初始化这个单例。

1、懒汉式单例,具体代码如下:

package com.yoodb;
//懒汉式单例类.在第一次调用的时候实例化自己
public class Singleton {
    //私有的无参构造方法
    private Singleton(){
         
    }
    //注意,这里没有final
    private static Singleton single = null;
    //静态工厂方法
    public synchronized  static Singleton getInstance(){
        if(single == null){
            single = new Singleton();
        }
        return single;
    }
}

2、饿汉式单例,具体代码如下:

package com.yoodb;
 
public class Singleton {
    //私有的无参构造方法
    private Singleton(){
         
    }
    //自动实例化
    private final static Singleton single = new Singleton();
    //静态工厂方法
    public static synchronized Singleton getInstance(){
        return single;
    }
}

3、登记式单例,具体代码如下:

package com.yoodb;
 
import java.util.HashMap;
import java.util.Map;
 
public class Singleton {
    public static Map<String,Singleton> map = new HashMap<String,Singleton>();
    static{
        //将类名注入下次直接获取
        Singleton single = new Singleton();
        map.put(single.getClass().getName(),single);
    }
    //保护式无参构造方法
    protected Singleton(){}
     
    public static Singleton getInstance(String name){
        if(name == null){
            name = Singleton.class.getName(); 
        }
        if(map.get(name) == null){
            try {
                map.put(name, Singleton.class.newInstance());
            } catch (InstantiationException | IllegalAccessException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
        return map.get(name);
    }
     
    public String create() {      
return "创建一个单例!";      
    }  
     
    public static void main(String[] args) {
         Singleton single = Singleton.getInstance(null);  
         System.out.println(single.create());  
    }
}

题10:为什么要使用设计模式?

1)设计模式是前人根据经验总结出来的,使用设计模式,就相当于是站在了前人的肩膀上。

2)设计模式使程序易读。熟悉设计模式的人应该能够很容易读懂运用设计模式编写的程序。

3)设计模式能使编写的程序具有良好的可扩展性,满足系统设计的开闭原则。比如策略模式,就是将不同的算法封装在子类中,在需要添加新的算法时,只需添加新的子类,实现规定的接口,即可在不改变现有系统源码的情况下加入新的系统行为。

4)设计模式能降低系统中类与类之间的耦合度。比如工厂模式,使依赖类只需知道被依赖类所实现的接口或继承的抽象类,使依赖类与被依赖类之间的耦合度降低。

5)设计模式能提高代码的重用度。比如适配器模式,就能将系统中已经存在的符合新需求的功能代码兼容新的需求提出的接口 。

6)设计模式能为常见的一些问题提供现成的解决方案。

7)设计模式增加了重用代码的方式。比如装饰器模式,在不使用继承的前提下重用系统中已存在的代码。

题11:java-中什么是解释器模式

题12:java-中装饰模式有什么优缺点

题13:java-中什么是外观模式

题14:单例模式都有哪些应用场景

题15:java-中三种代理模式有什么区别

题16:java-中策略模式有什么优缺点

题17:java-中为什么不允许从静态方法中访问非静态变量

题18:java-中什么是简单工厂模式

题19:java-中什么时候使用模板方法模式

题20:java-中外观模式有什么使用场景

题21:什么是设计模式

题22:java-中原型模式如何实现浅拷贝

题23:java-中如何实现类的适配器模式

题24:java-中代理模式有什么应用场景

题25:java-中什么是原型模式

大厂面试题

大厂面试题

大厂面试题

Java
1
https://gitee.com/moyu3390/ebooks_1.git
git@gitee.com:moyu3390/ebooks_1.git
moyu3390
ebooks_1
Ebooks_1
master

搜索帮助

53164aa7 5694891 3bd8fe86 5694891