栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

设计模式面试必问(单例、工厂、代理)

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

设计模式面试必问(单例、工厂、代理)

设计模式

目录
    • 设计模式
      • 一、设计模式
        • 1.单例模式
          • 1.1 饿汉式
          • 1.2 懒汉式
          • 1.3 双重检查
          • 1.4 静态内部类
          • 1.5 枚举
          • 1.6 总结
          • 1.7 JDK中单例模式的使用
        • 2.工厂模式
          • 2.1 简单工厂模式
          • 2.2 工厂方法模式
          • 2.3 抽象工厂模式
            • 2.3.1 关系图
            • 2.3.2 代码
        • 3.代理模式
          • 3.1 静态代理
            • 3.1.1 关系图
            • 3.1.2 代码
          • 3.2 动态代理
            • 3.2.1 关系图
            • 3.2.1 代码

一、设计模式 1.单例模式

​ 单例模式中保证了系统内存中该类只有一个对象,节省了系统资源,对于一些需要频繁创建销毁的对象,使用单例模式可以系统系统性能。

1.1 饿汉式

饿汉式静态变量实现

【优点】

  • 这种写法比较简单,在类装载的时候就完成实例化,避免了线程同步的问题。

【缺点】

  • 在类装载的时候就完成了实例化,没有达到Lazy Loading(懒加载)的效果,如果从开始到结束都没有使用过这个类,那么会造成内存浪费。
  • 这种方式基于classloader机制避免了线程的同步问题,不过,对象user在类装载的时候就实例化,在单例模式中都是调用getUser方法,但是导致类加载的原因有很多,因此不确定有没有其他的方式导致类加载,这时候初始化就没有达到懒加载的效果。

【结论】

  • 这种单例模式中可用,可能会造成内存浪费。
public class singleTest01 {
public static void main(String[] args) {
    //测试
    Singleton singleton1 = Singleton.getSingleton();
    Singleton singleton2 = Singleton.getSingleton();
    System.out.println(singleton1==singleton2);//instance
}
}


//饿汉式第一种写法:静态变量直接new对象
class Singleton{

//1.设置私有构造器
private Singleton(){

}

//2.本类中创建对象实例
private final static Singleton singleton=new Singleton();

//3.创建一个返回实例对象的方法
public static Singleton getSingleton(){
    return singleton;
}
}

饿汉式静态代码块实现

​ 这种方式跟上面的静态变量的优缺点是一样的。

public class singleTest01 {
public static void main(String[] args) {
    //测试
    Singleton singleton1 = Singleton.getSingleton();
    Singleton singleton2 = Singleton.getSingleton();
    System.out.println(singleton1==singleton2);//instance
}
}


//饿汉式第二种写法:静态代码块中new对象
class Singleton{

//1.设置私有构造器
private Singleton(){}

//2.本类中创建对象引用
    private  static Singleton singleton;

//3.使用静态代码块new对象实例
static {
    Singleton=new Singleton();
}

//4.创建一个返回实例对象的方法
public static Singleton getSingleton(){
    return singleton;
}
}
1.2 懒汉式

懒汉式第一种写法,线程不安全

【优点】

  • 起到了懒加载的效果,但只能在单线程下使用

【缺点】

  • 如果在多线程下,一个线程进入了if(user==null),还没来得及往下进行,另外一个线程也进入了这个判断语句,那么会产生多个实例,所以在多线程环境下不可使用这种方式。

【结论】

  • 在实际开发中,不要使用这种方式。
public class singleTest01 {
public static void main(String[] args) {
    //1.通过User中的静态方法获取User对象
    //当第一次调用gerUser()方法时,user没有被创建那么先会被创建
    Singleton singleton1 = Singleton.getSingleton();
    //当第二次调用getUser()方法时,user已经被创建,不为null,会将原先创建好的user返回
    Singleton singleton2 = Singleton.getSingleton();
    //两次的user1和user2所指向的实例对象是同一个
    System.out.println(singleton1 == singleton2);//true
}
}


//懒汉式第一种写法,即当需要的时候才会创建实例对象
//会出现线程不安全问题
class Singleton{

//1.创建一个私有的构造方法
private Singleton(){}

//2.定义一个静态User引用
private static Singleton singleton;

//3.写一个公共静态方法,当需要时再创建
public static Singleton getSingleton(){
    if (singleton==null){
        singleton=new Singleton();
    }
    return singleton;
}
}

【证明懒汉式第一种写法是线程不安全的】

​ 测试

public class singleTest01 {
public static void main(String[] args) {
    //创建线程t1和t2
    Thread t1=new Thread(new myThread01());
    Thread t2=new Thread(new myThread02());
    //改名
    t1.setName("t1");
    t2.setName("t2");
    //启动线程
    t1.start();
    t2.start();
}
}

class Singleton{
private static Singleton singleton;

private Singleton(){}

public static Singleton getSingleton(){
    if (singleton==null){
        //为了使另外一个线程成功进入if语句中,在这里设置睡眠
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        singleton=new Singleton();
    }
    return singleton;
}
}


class myThread01 implements Runnable{
@Override
public void run() {
    Singleton singleton = Singleton.getSingleton();
    System.out.println(Thread.currentThread().getName()+"==>"+singleton.hashCode());
}
}

class myThread02 implements Runnable{
@Override
public void run() {
    Singleton singleton = Singleton.getSingleton();
    System.out.println(Thread.currentThread().getName()+"==>"+singleton.hashCode());
}
}

​ 结果

懒汉式第二种写法,线程安全,同步方法

​ 给getUser()方法加上"synchronized",使得这个方法线程同步,当有一个线程计入方法时,其他线程就无法进入这个方法。

【优点】

  • 解决了第一种写法中线程不安全的问题

【缺点】

  • 加入"synchronized"之后,效率会太低,每个线程在获得类的实例时候,执行getUser()方法都要进行同步,而其实这个方法只执行一次实例化代码就够了,后面的想获得该类实例,直接return就行
public class singleTest02 {
public static void main(String[] args) {
	Singleton s1=new Singleton();
    Singleton s2=new Singleton();
    System.out.println(s1==s2);
}
}


//懒汉式的第二种写法,线程同步,没有线程安全问题
class Singleton{

//创建一个静态引用
private static Singleton singleton;
//创建一个私有的构造方法
private Singleton(){}
//get方法
public synchronized static Singleton getSingleton(){
    if (singleton==null){
        singleton=new Singleton();
    }
    return singleton;
}
}

【证明懒汉式第二种写法是线程安全的】

​ 测试

public class singleTest02 {
public static void main(String[] args) {
    //创建线程t1和t2
    Thread t1=new Thread(new myThread03());
    Thread t2=new Thread(new myThread04());
    //改名
    t1.setName("t1");
    t2.setName("t2");
    //开启线程
    t1.start();
    t2.start();
}
}


//懒汉式的第二种写法,线程同步,没有线程安全问题
class Singleton{

//创建一个静态引用
private static Singleton singleton;
//创建一个私有的构造方法
private Singleton(){}
//get方法
public synchronized static Singleton getSingleton(){
    if (singleton==null){
        singleton=new Singleton();
    }
    return singleton;
}
}

class myThread03 implements Runnable{
@Override
public void run() {
    //调用Singleton的getSingleton方法
    Singleton singleton = User1.getSingleton();
    System.out.println(Thread.currentThread().getName()+"==>"+singleton.hashCode());
}
}

class myThread04 implements Runnable{
@Override
public void run() {
    //调用Singleton的getSingleton方法
    Singleton singleton = User1.getSingleton();
    System.out.println(Thread.currentThread().getName()+"==>"+singleton.hashCode());
}
}

​ 结果

1.3 双重检查

【优点】

  • 不会造成线程安全问题
  • 解决懒加载问题
  • 效率不会受影响

【结论】

  • 在实际开发中,推荐双重加载实现单例模式
public class singleTest03 {
public static void main(String[] args) {
    Singleton singleton1=Singleton.getSingleton();
    Singleton singleton2=Singleton.getSingleton();
    System.out.println(singleton1==singleton2);//true
}
}

//使用双重检查机制实现单例模式
class Singleton{
//1.创建一个私有静态Singleton引用
private volatile static Singleton singleton;
//2.创建一个私有构造方法
private Singleton(){}
//3.使用双重检查机制来实现单例模式
public static Singleton getSingleton(){
    if (singleton==null){
        synchronized(Singleton.class){
            if (singleton==null){
                singleton=new Singleton();
            }
        }
    }
    return singleton;
}

}

【疑问】:为什么要使用双判断呢?一次不可以吗?

​ 答:在java中线程安全同步有两个办法,第一个就是使用同步代码块(synchronized)。第二种方式就是volatile,这种方式是轻量级的,不会引起线程上下文的切换和调度,但容易出错。经过实验,只进行一次判断会出现错误,而在里面进行两次就不会出现错误。

1.4 静态内部类

【优点】

  • 这种方式采用类加载的机制来保证初始化实例时只有一个线程
  • 静态内部类在Singleton被装载的时候并不会立即实例化,而是在需要实例化,调用getSingleton()方法时,才会装载内部类
  • 类的静态属性只会在第一次加载类的时候初始化,在类的初始化时,别的线程是无法进入的

【结论】

  • 避免了线程不安全的问题,利用静态内部类特点实现延迟加载,效率高,推荐使用
public class singleTest04 {
    public static void main(String[] args) {
        Singleton singleton1=Singleton.getSingleton();
        Singleton singleton2=Singleton.getSingleton();
        System.out.println(singleton1==singleton2);
    }
}

//通过静态内部类实现单例模式
class Singleton{
    //1.创建一个私有静态引用,也可也不创建,对程序没有影响
    private static Singleton singleton;
    //2.创建一个私有构造方法
    private Singleton(){}
    //3.创建一个内部类
    private static class SingletonInstance{
        private static final Singleton INSTANCE=new Singleton();
    }
    //4.创建获取singleton的方法,直接返回SingletonInstance.INSTANCE
    public static Singleton getSingleton(){
        return SingletonInstance.INSTANCE;
    }
}
1.5 枚举

【优点】

  • 不仅能避免多线程同步问题,还能防止反序列化重新创建新的对象
public class singleTest05 {
    public static void main(String[] args) {
        Singleton singleton=Singleton.INSTANCE;
        Singleton singleton1=Singleton.INSTANCE;
        System.out.println(singleton==singleton1);
    }
}


//使用枚举实现单例模式
enum Singleton{
    INSTANCE;
    public void method(){
        System.out.println("枚举!");
    }
}
1.6 总结
package com.drl.design.single.singleTest05;


public class singleTest05 {
    public static void main(String[] args) {
        Singleton singleton=Singleton.INSTANCE;
        Singleton singleton1=Singleton.INSTANCE;
        System.out.println(singleton==singleton1);
    }
}

//饿汉式
//class Singleton{
//    //1.直接在静态变量中new
//    private static Singleton singleton=new Singleton();
//    private Singleton(){}
//    public static Singleton getSingleton(){
//        return singleton;
//    }
//    //2.在静态代码块中
//    private static Singleton singleton;
//    private Singleton(){}
//    static {
//        singleton=new Singleton();
//    }
//    public static Singleton getSingleton(){
//        return singleton;
//    }
//}

//懒汉式
//class Singleton{
//    //1.线程不安全,但是效率高
    private static Singleton singleton;
    private Singleton(){}
    public static Singleton getSingleton(){
        if (singleton==null){
            singleton=new Singleton();
        }
        return singleton;
    }
//
//    //2.线程安全,但是效率不高
//    private static Singleton singleton;
//    private Singleton(){}
//    public synchronized static Singleton getSingleton(){
//        if (singleton==null){
//            singleton=new Singleton();
//        }
//        return singleton;
//    }
//}

//双重检查
//class Singleton{
//    private static volatile Singleton singleton;
//    private Singleton(){}
//    public static Singleton getSingleton(){
//        if (singleton==null){
//            synchronized (Singleton.class){
//                if (singleton==null){
//                    singleton=new Singleton();
//                }
//            }
//        }
//        return singleton;
//    }
//}

//静态内部类
//class Singleton{
//    private Singleton(){}
//
//    private static class SingletonInstance{
//        private static final Singleton INSTANCE=new Singleton();
//    }
//    public static Singleton getInstance(){
//        return SingletonInstance.INSTANCE;
//    }
//}

//枚举
enum  Singleton{
    INSTANCE;
}
1.7 JDK中单例模式的使用

​ 在JDK中java.lang.Runtime中Runtime就使用了单例模式,并且是饿汉式实现的单例模式,如下图:

2.工厂模式 2.1 简单工厂模式

​ 以顾客买车、品牌方造车为案例:

平常情况,顾客直接和品牌方打交道

//造汽车接口
interface Car{
    public void makeCar();
}

//五菱宏光品牌方造车
class WuLing implements Car{
    @Override
    public void makeCar() {
        System.out.println("五菱宏光造车!");
    }
}

//吉利品牌方造车
class JiLi implements Car{
    @Override
    public void makeCar() {
        System.out.println("吉利品牌方造车!");
    }
}

//顾客
class Consumer{
    public static void main(String[] args) {
        //要买五菱宏光
        Car car1=new WuLing();
        car1.makeCar();
        //要买吉利
        Car car2=new JiLi();
        car2.makeCar();
    }
}

使用工厂模式,顾客和中介打交道

//造汽车接口
interface Car{
    public void makeCar();
}

//五菱宏光品牌方造车
class WuLing implements Car{
    @Override
    public void makeCar() {
        System.out.println("五菱宏光造车!");
    }
}

//吉利品牌方造车
class JiLi implements Car{
    @Override
    public void makeCar() {
        System.out.println("吉利品牌方造车!");
    }
}

//工厂
class CarFactory {
    //返回汽车
    public static Car getCar(String car){
        if (car.equals("五菱宏光")){
            return new WuLing();
        }else {
            return new JiLi();
        }
    }
}

//顾客
class Consumer{
    public static void main(String[] args) {
        //使用工厂模式之后,顾客直接说想买什么车就可以
        Car car = CarFactory.getCar("五菱宏光");
        car.makeCar();
    }
}
2.2 工厂方法模式

​ 使用工厂方法模式会解耦合,当添加另外一个车品牌方时,不用修改代码,只需要添加即可,满足开闭原则。

//造汽车接口
interface Car{
    public void makeCar();
}

//车工厂接口
interface CarFactory{
    public Car getCar();
}

//五菱宏光品牌方造车
class WuLing implements Car{
    @Override
    public void makeCar() {
        System.out.println("五菱宏光造车!");
    }
}

//吉利品牌方造车
class JiLi implements Car{
    @Override
    public void makeCar() {
        System.out.println("吉利品牌方造车!");
    }
}

//五菱宏光工厂
class WuLingCarFactory implements CarFactory {

    @Override
    public Car getCar() {
        return new WuLing();
    }
}

//吉利工厂
class JiLiCarFactory implements CarFactory{

    @Override
    public Car getCar() {
        return new JiLi();
    }
}

//顾客
class Consumer{
    public static void main(String[] args) {
        //工厂方法模式
        Car car1=new WuLingCarFactory().getCar();
        car1.makeCar();
        Car car2=new JiLiCarFactory().getCar();
        car2.makeCar();
    }
}
2.3 抽象工厂模式

​ 围绕一个超级工厂创建其他工厂,该超级工厂又称为其他工厂的工厂。以下使用小米和华为品牌方的手机、路由器工厂为例子

2.3.1 关系图

2.3.2 代码

超级抽象接口工厂

//超级抽象工厂,制造工厂的工厂
public interface IProductFactory {

    //制造手机的工厂
    public IphoneProduct iphoneProduct();

    //制造路由器的工厂
    public IrouterProduct irouterProduct();

}

小米厂商工厂,实现超级抽象接口工厂

//小米工厂,可以制造手机,也可以制造路由器
public class XiaomiFactory implements IProductFactory {
    @Override
    public IphoneProduct iphoneProduct() {
        return new XiaomiPhone();
    }

    @Override
    public IrouterProduct irouterProduct() {
        return new XiaomiRouter();
    }
}

华为厂商工厂,实现超级抽象接口工厂

//华为厂商工厂,实现超级抽象接口工厂,分为创建出华为手机工厂和华为路由器工厂
public class HuaweiFactory implements IProductFactory {
    @Override
    public IphoneProduct iphoneProduct() {
        return new HuaweiPhone();
    }

    @Override
    public IrouterProduct irouterProduct() {
        return new HuaweiRouter();
    }
}

小米手机工厂和小米路由器工厂

//小米手机工厂,实现手机规范接口
public class XiaomiPhone implements IphoneProduct {
    @Override
    public void start() {
        System.out.println("小米手机开启!");
    }

    @Override
    public void shutUp() {
        System.out.println("小米手机关机!");
    }

    @Override
    public void call() {
        System.out.println("小米手机打电话!");
    }
}

//小米路由器工厂,实现路由器规范接口
public class XiaomiRouter implements IrouterProduct {
    @Override
    public void startWifi() {
        System.out.println("小米路由器开启wifi");
    }

    @Override
    public void setting() {
        System.out.println("小米路由器设置参数");
    }
}

华为手机工厂和华为路由器工厂

//华为手机工厂,实现手机规范接口
public class HuaweiPhone implements IphoneProduct {
    @Override
    public void start() {
        System.out.println("华为手机开启!");
    }

    @Override
    public void shutUp() {
        System.out.println("华为手机关机!");
    }

    @Override
    public void call() {
        System.out.println("华为手机打电话!");
    }
}

//华为路由器工厂,实现路由器接口
public class HuaweiRouter implements IrouterProduct {
    @Override
    public void startWifi() {
        System.out.println("华为路由器开启wifi");
    }

    @Override
    public void setting() {
        System.out.println("华为路由器设置参数");
    }
}

手机规范接口和路由器规范接口

//手机规范接口
public interface IphoneProduct {
    void start();
    void shutUp();
    void call();
}

//路由器规范接口
public interface IrouterProduct {
    void startWifi();
    void setting();
}
3.代理模式

​ 为对象提供一个替身,以控制对这个对象的访问,即通过代理对象访问目标对象。这样做的好处是可以在目标对象实现的基础上增加额外的功能操作,即扩展目标对象的功能。

3.1 静态代理

​ 静态代理在使用时,需要定义接口或父类,被代理对象(即目标对象)与代理对象一起实现相同的接口或是继承相同父类。

【优点】

  • 在不修改目标对象的功能前提下,能通过代理对象对目标功能扩展

【缺点】

  • 因为目标对象和代理对象都要实现一样的接口,所以会有很多代理类
  • 一旦增加方法,目标对象与代理对象都要维护
3.1.1 关系图

3.1.2 代码

ITeacher接口

//接口
public interface ITeacherDao {

    //教授方法
    public void teach();
}

TeacherDao类,即目标对象,被代理对象

//目标对象(即被代理的对象)
public class TeacherDao implements ITeacherDao {

    public TeacherDao(){}

    @Override
    public void teach() {
        System.out.println("开始授课!");
    }
}

TeacherDaoProxy类,即代理对象

//代理对象
public class TeacherDaoProxy implements ITeacherDao {

    private ITeacherDao iTeacherDao;

    public TeacherDaoProxy(ITeacherDao iTeacherDao) {
        this.iTeacherDao = iTeacherDao;
    }

    @Override
    public void teach() {
        System.out.println("开始代理....");
        iTeacherDao.teach();
        System.out.println("代理提交....");
    }
}

客户端

public class client {
    public static void main(String[] args) {
        //目标对象(被代理对象)
        TeacherDao teacherDao=new TeacherDao();
        //代理对象
        TeacherDaoProxy teacherDaoProxy=new TeacherDaoProxy(teacherDao);
        //通过代理对象调用方法
        teacherDaoProxy.teach();
    }
}
3.2 动态代理

​ 代理对象不用实现接口,但是目标对象需要实现接口,否则不能用动态代理。代理对象的生成,是利用JDK的API,动态的在内存中构建代理对象。

3.2.1 关系图

3.2.1 代码

ITeacher接口

//接口
public interface ITeacher {
    public void teach();
}

TeacherDao目标对象,被代理的对象

//目标对象,被代理的对象
public class TeacherDao implements ITeacher {
    @Override
    public void teach() {
        System.out.println("开始授课!");
    }
}

代理对象工厂

//获得代理对象工厂
public class TeacherDaoProxyFactory {

    private ITeacher target;

    public TeacherDaoProxyFactory(ITeacher iTeacher){
            this.target=iTeacher;
    }

    public Object getProxy(){
        
        return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), new InvocationHandler() {

            
            @Override
            public Object invoke(Object o, Method method, Object[] objects) throws Throwable {
                return method.invoke(target,objects);
            }
        });
    }
}

客户端

public static void main(String[] args) {
        //创建一个目标对象
        ITeacher teacherDao=new TeacherDao();
        //通过代理工厂获取代理类
        TeacherDaoProxyFactory proxyFactory=new TeacherDaoProxyFactory(teacherDao);
        ITeacher proxy = (ITeacher)proxyFactory.getProxy();
        //通过代理对象调用方法
        proxy.teach();
    }
转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/351702.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号