Java面试题--设计模式

这篇具有很好参考价值的文章主要介绍了Java面试题--设计模式。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

一、Java 中有几种设计模式?

Java 中一般认为有 23 种设计模式

分为三大类:

1. 创建型模式 5 种

① 工厂方法模式

② 抽象工厂模式

③ 单例模式

④ 建造者模式

⑤ 原型模式

2. 结构型模式 7 种

① 适配器模式

② 装饰器模式

③ 代理模式

④ 外观模式

⑤ 桥接模式

⑥ 组合模式

⑦ 享元模式

3. 行为型模式 11 种

① 策略模式

② 模板方法模式

③ 观察者模式

④ 迭代子模式

⑤ 责任链模式

⑥ 命令模式

⑦ 备忘录模式

⑧ 状态模式

⑨ 访问者模式

⑩ 中介者模式

⑪ 解释器模式

 二、什么是单例设计模式?

1. 单例模式定义

单例模式确保某个类只有一个实例,而

且自行实例化并向整个系统提供这个实

在计算机系统中,线程池、缓存、日志

对象、对话框、打印机、显卡的驱动程

序对象常被设计成单例,选择单例模式

就是为了避免不一致状态

2. 单例模式的特点

① 单例类只能有一个实例

② 单例类必须自己创建自己的唯一实例

③ 单例类必须给所有其他对象提供这一

    实例

④ 单例模式保证了全局对象的唯一性

    比如系统启动读取配置文件就需要单

    例保证配置的一致性

3. 单例的四大原则

① 构造器私有化

② 以静态方法或者枚举返回实例

③ 确保实例只有一个,尤其是多线程

    环境

④ 确保反序列化时不会重新构建对象

4.  实现单例模式的方式
(1) 饿汉式 (立即加载):

饿汉式单例在类加载初始化时就创建

一个静态的对象供外部使用,除非系统

重启,这个对象不会改变,所以本身就

是线程安全的

Singleton 通过将构造方法限定为 private

避免了类在外部被实例化,在同一个虚拟

机范围内,Singleton 的唯一实例只能通

过 getInstance() 方法访问 (事实上,通过

Java 反射机制是能够实例化构造方法为

private 的类的,会使 Java 单例实现失效)

/**
 * 饿汉式(立即加载)
 */
public class Singleton1 {

    /**
     * 私有构造
     */
    private Singleton1() {
        System.out.println("构造函数Singleton1");
    }

    /**
     * 初始值为实例对象
     */
     private static Singleton1 single = new Singleton1();

     /**
      * 静态工厂方法
      * @return 单例对象
      */
     public static Singleton1 getInstance() {
        System.out.println("getInstance");
        return single;
     }

     public static void main(String[] args){
        System.out.println("初始化");
        Singleton1 instance = Singleton1.getInstance();
     }
}
(2) 懒汉式 (延迟加载):

该示例虽然用延迟加载方式实现了懒汉

式单例,但在多线程环境下会产生多个

Singleton 对象


/**
 * 懒汉式(延迟加载)
 */
public class Singleton2 {

    /**
     * 私有构造
     */
    private Singleton2() {
        System.out.println("构造函数Singleton2");
    }

    /**
     * 初始值为null
     */
    private static Singleton2 single = null;

    /**
     * 静态工厂方法
     * @return 单例对象
     */
    public static Singleton2 getInstance() {
        if(single == null){
            System.out.println("getInstance");
            single = new Singleton2();
        }
        return single;
    }

    public static void main(String[] args){
        System.out.println("初始化");
        Singleton2 instance = Singleton2.getInstance();
    }
}
(3) 同步锁 (解决线程安全问题):

在方法上加 synchronized 同步锁或是

用同步代码块对类加同步锁,此种方

式虽然解决了多个实例对象问题,但

是该方式运行效率却很低下,下一个

线程想要获取对象,就必须等待上一

个线程释放锁之后,才可以继续运行

/**
 *
 * 同步锁(解决线程安全问题)
 */
public class Singleton3 {

    /**
     * 私有构造
     */
    private Singleton3() {}

    /**
     * 初始值为null
     */
    private static Singleton3 single = null;
    
    public static Singleton3 getInstance() {
        // 等同于 synchronized public static Singleton3 getInstance()
        synchronized(Singleton3.class){
            // 注意:里面的判断是一定要加的,否则出现线程安全问题
            if(single == null){
                single = new Singleton3();
            }
        }
        return single;
    }
}
(4) 双重检查锁 (提高同步锁的效率):

使用双重检查锁进一步做了优化,可

以避免整个方法被锁,只对需要锁的

代码部分加锁,可以提高执行效率

/**
 * 双重检查锁(提高同步锁的效率)
 */
public class Singleton4 {

    /**
     * 私有构造
     */
    private Singleton4() {}

    /**
     * 初始值为null
     */
    private static Singleton4 single = null;

    /**
     * 双重检查锁
     * @return 单例对象
     */
    public static Singleton4 getInstance() {
        if (single == null) {
            synchronized (Singleton4.class) {
                if (single == null) {
                    single = new Singleton4();
                }
            }
        }
        return single;
    }
}
(5) 静态内部类:

引入了一个内部静态类 (static class),静

态内部类只有在调用时才会加载,它保证

了 Singleton 实例的延迟初始化,又保证

了实例的唯一性

它把 singleton 的实例化操作放到一个静

态内部类中,在第一次调用 getInstance()

方法时,JVM 才会去加载 InnerObject 类,

同时初始化 singleton 实例,所以能让

getInstance() 方法线程安全

   特点:即能延迟加载,也能保证线程安全

静态内部类虽然保证了单例在多线程并发

下的线程安全性,但是在遇到序列化对象

时,默认的方式运行得到的结果就是多例

/**
 *
 * 静态内部类(延迟加载,线程安全)
 */
public class Singleton5 {
    /**
     * 私有构造
     */
    private Singleton5() {}

    /**
     * 静态内部类
     */
    private static class InnerObject{
        private static Singleton5 single = new Singleton5();
    }

    public static Singleton5 getInstance() {
        return InnerObject.single;
    }
}
(6) 内部枚举类实现 (防止反射攻击):

事实上,通过 Java 反射机制是能够实例

化构造方法为 private 的类的,这也就是

我们现在需要引入的枚举单例模式

public class SingletonFactory {
    /**
     * 内部枚举类
     */
    private enum EnumSingleton{
        Singleton;
        private Singleton6 singleton;

        //枚举类的构造方法在类加载是被实例化
        private EnumSingleton(){
            singleton = new Singleton6();
        }

        public Singleton6 getInstance(){
            return singleton;
        }
    }
 
    public static Singleton6 getInstance() {
        return EnumSingleton.Singleton.getInstance();
    }
}

class Singleton6 {
    public Singleton6(){}
}

三、什么是工厂设计模式?

工厂设计模式就是用来生产对象的,在

java 中,万物皆对象,这些对象都需要

创建,如果创建的时候直接 new 该对象,

就会对该对象耦合严重,假如我们要更

换对象,所有 new 对象的地方都需要修

改一遍,这显然违背了软件设计的开闭

原则,如果我们使用工厂来生产对象,

我们就只和工厂打交道就可以了,彻底

和对象解耦,如果要更换对象,直接在

工厂里更换该对象即可,达到了与对象

解耦的目的;所以说,工厂模式最大的

优点就是:解耦

1. 简单工厂 (Simple Factory)

定义:

一个工厂方法,依据传入的参数,生成对

应的产品对象;

角色:

① 抽象产品
② 具体产品
③ 具体工厂
④ 产品使用者

使用说明:

先将产品类抽象出来,比如,苹果和梨都属

于水果,抽象出来一个水果类 Fruit,苹果和

梨就是具体的产品类,然后创建一个水果工

厂,分别用来创建苹果和梨

代码如下:

// 水果接口:
public interface Fruit {
    void whatIm();
}

// 苹果类:
public class Apple implements Fruit {
    @Override
    public void whatIm() {
        System.out.println("苹果");
    }
}

// 梨类:
public class Pear implements Fruit {
    @Override
    public void whatIm() {
        System.out.println("梨");
    }
}

//水果工厂:

public class FruitFactory {
    public Fruit createFruit(String type) {
        if (type.equals("apple")) {//生产苹果
            return new Apple();
        } else if (type.equals("pear")) {//生产梨
            return new Pear();
        }
        return null;
    }
}

// 使用工厂生产产品:
public class FruitApp {
    public static void main(String[] args) {
        FruitFactory mFactory = new FruitFactory();
        Apple apple = (Apple) mFactory.createFruit("apple");//获得苹果
        Pear pear = (Pear) mFactory.createFruit("pear");//获得梨
        apple.whatIm();
        pear.whatIm();
    }
}

以上的这种方式,每当添加一种水果,就必

然要修改工厂类,违反了开闭原则;

所以简单工厂只适合于产品对象较少,且产

品固定的需求,对于产品变化无常的需求来

说显然不合适

2. 工厂方法 (Factory Method)

定义:

将工厂提取成一个接口或抽象类,具体生

产什么产品由子类决定

角色:

① 抽象产品
② 具体产品
③ 抽象工厂
④ 具体工厂

使用说明:

和上例中一样,产品类抽象出来,这次我们

把工厂类也抽象出来,生产什么样的产品由

子类来决定

代码如下:

// 水果接口、苹果类和梨类:代码和上例一样

// 抽象工厂接口:
public interface FruitFactory {
    Fruit createFruit();//生产水果
}

// 苹果工厂:
public class AppleFactory implements FruitFactory {
    @Override
    public Apple createFruit() {
        return new Apple();
    }
}

// 梨工厂:
public class PearFactory implements FruitFactory {
    @Override
    public Pear createFruit() {
        return new Pear();
    }
}

// 使用工厂生产产品:
public class FruitApp {
    public static void main(String[] args){
        AppleFactory appleFactory = new AppleFactory();
        PearFactory pearFactory = new PearFactory();
        Apple apple = appleFactory.createFruit();//获得苹果
        Pear pear = pearFactory.createFruit();//获得梨
        apple.whatIm();
        pear.whatIm();
    }
}

以上这种方式,虽然解耦了,也遵循了开闭

原则,但是如果我需要的产品很多的话,需

要创建非常多的工厂,所以这种方式的缺点

也很明显

3. 抽象工厂 (Abstract Factory)

定义:

为创建一组相关或者是相互依赖的对象提供

的一个接口,而不需要指定它们的具体类

角色:

① 抽象产品
② 具体产品

③ 抽象工厂

④ 具体工厂

使用说明:

抽象工厂和工厂方法的模式基本一样,区别

在于,工厂方法是生产一个具体的产品,而

抽象工厂可以用来生产一组相同,有相对关

系的产品

用抽象工厂来实现:

// cpu接口和实现类:
public interface Cpu {
    void run();

    class Cpu650 implements Cpu {
        @Override
        public void run() {
            System.out.println("650 也厉害");
        }
    }

    class Cpu825 implements Cpu {
        @Override
        public void run() {
            System.out.println("825 更强劲");
        }
    }
}

// 屏幕接口和实现类:
public interface Screen {
    void size();
    class Screen5 implements Screen {
        @Override
        public void size() {
            System.out.println("" + "5寸");
        }
    }

    class Screen6 implements Screen {
        @Override
        public void size() {
            System.out.println("6寸");
        }
    }
}

// 抽象工厂接口:
public interface PhoneFactory {
    Cpu getCpu();//使用的cpu
    Screen getScreen();//使用的屏幕
}

// 小米手机工厂:
public class XiaoMiFactory implements PhoneFactory {
    @Override
    public Cpu.Cpu825 getCpu() {
        return new Cpu.Cpu825();//高性能处理器
    }
 
    @Override
    public Screen.Screen6 getScreen() {
        return new Screen.Screen6();//6寸大屏
    }
}

//红米手机工厂:
public class HongMiFactory implements PhoneFactory {
    @Override
    public Cpu.Cpu650 getCpu() {
        return new Cpu.Cpu650();//高效处理器
    }

    @Override
    public Screen.Screen5 getScreen() {
        return new Screen.Screen5();//小屏手机
    }
}

// 使用工厂生产产品:
public class PhoneApp {
    public static void main(String[] args){
        HongMiFactory hongMiFactory = new HongMiFactory();
        XiaoMiFactory xiaoMiFactory = new XiaoMiFactory();
        Cpu.Cpu650 cpu650 = hongMiFactory.getCpu();
        Cpu.Cpu825 cpu825 = xiaoMiFactory.getCpu();
        cpu650.run();
        cpu825.run();
        Screen.Screen5 screen5 = hongMiFactory.getScreen();
        Screen.Screen6 screen6 = xiaoMiFactory.getScreen();
        screen5.size();
        screen6.size();
    }
}

以上例子可以看出,抽象工厂可以解决一

系列的产品生产的需求,对于大批量,多

系列的产品,用抽象工厂可以更好地管理

和扩展

4. 三种工厂方式总结

① 对于简单工厂和工厂方法来说,两者的

    使用方式实际上是一样的,如果对于产

    品的分类和名称是确定的,数量是相对

    固定的,推荐使用简单工厂模式;

② 抽象工厂用来解决相对复杂的问题,适用于

    一系列、大批量的对象生产文章来源地址https://www.toymoban.com/news/detail-663061.html

到了这里,关于Java面试题--设计模式的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 【Java笔记+踩坑汇总】Java基础+JavaWeb+SSM+SpringBoot+SpringCloud+瑞吉外卖/黑马旅游/谷粒商城/学成在线+设计模式+面试题汇总+性能调优/架构设计+源码

    本文是“Java学习路线”专栏的导航文章,目标是为Java工程师提供一套 完整的Java学习路线 。 目录 0.摘要/资料/代码整理 1.Java基础+进阶 2.MySQL,JavaWeb,Mybatis,前端 3.Git 4.SSM(Spring,SpringMVC,Mybatis)框架 5.Maven高级 6.Springboot,MybatisPlus,JPA框架 7.瑞吉外卖、Redis、Nginx、Linux、mysql主从复制

    2024年02月08日
    浏览(68)
  • 【策略设计模式详解】C/Java/JS/Go/Python/TS不同语言实现

    策略模式(Strategy Pattern)属于行为型设计模式。将每一个算法封装到具有共同接口的独立类中,根据需要来绑定策略,使得具体实现和策略解耦。 当你想使用对象中各种不同的算法变体,使用if...else 所带来的复杂和难以维护,可使用策略模式。或者当有许多相同类,它们仅

    2024年02月01日
    浏览(46)
  • 【代理设计模式详解】C/Java/JS/Go/Python/TS不同语言实现

    代理模式(Proxy Pattern)是一种结构型设计模式,用一个类来代理另一个类或几个类的功能。 在代理模式中,我们创建具有现有对象的对象,以便向外界提供功能接口。 延迟初始化(虚拟代理)。如果你有一个偶尔使用的重量级服务对象,一直保持该对象运行会消耗系统资源

    2023年04月25日
    浏览(89)
  • 【单例设计模式原理详解】Java/JS/Go/Python/TS不同语言实现

    单例模式(Singleton Pattern)属于创建型设计模式,这种模式只创建一个单一的类,保证一个类只有一个实例,并提供一个访问该实例的全局节点。 当您想控制实例数目,节省系统资源,并不想混用的时候,可以使用单例模式。单例有很多种实现方式,主要分为懒汉和饿汉模式

    2023年04月27日
    浏览(93)
  • 【原型设计模式详解】C/Java/JS/Go/Python/TS不同语言实现

    原型模式(Prototype Pattern)是一种创建型设计模式,使你能够复制已有对象,而无需使代码依赖它们所属的类,同时又能保证性能。 这种模式是实现了一个原型接口,该接口用于创建当前对象的克隆。当直接创建对象的代价比较大时,则采用这种模式。 如果你需要复制一些对

    2023年04月24日
    浏览(85)
  • 【迭代器设计模式详解】C/Java/JS/Go/Python/TS不同语言实现

    迭代器模式(Iterator Pattern),是一种结构型设计模式。给数据对象构建一套按顺序访问集合对象元素的方式,而不需要知道数据对象的底层表示。 迭代器模式是与集合共存的,我们只要实现一个集合,就需要同时提供这个集合的迭代器,就像Java中的Collection,List、Set、Map等

    2023年04月17日
    浏览(49)
  • 【模板方法设计模式详解】C/Java/JS/Go/Python/TS不同语言实现

    模板方法模式(Template Method Pattern)也叫模板模式,是一种行为型模式。它定义了一个抽象公开类,包含基本的算法骨架,而将一些步骤延迟到子类中,模板方法使得子类可以不改变算法的结构,只是重定义该算法的某些特定步骤。不同的子类以不同的方式实现这些抽象方法

    2024年02月01日
    浏览(86)
  • 【享元设计模式详解】C/Java/JS/Go/Python/TS不同语言实现

    享元模式(Flyweight Pattern),是一种结构型设计模式。主要用于减少创建对象的数量,以减少内存占用和提高性能。它摒弃了在每个对象中保存所有数据的方式,通过共享多个对象所共有的相同状态,让你能在有限的内存容量中载入更多对象。 当程序需要生成数量巨大的相似

    2023年04月10日
    浏览(39)
  • 【Java笔记+踩坑汇总】Java基础+进阶+JavaWeb+SSM+SpringBoot+瑞吉外卖+SpringCloud+黑马旅游+谷粒商城+学成在线+MySQL高级篇+设计模式+面试题汇总+源码

    本文是“Java学习路线”专栏的导航文章,目标是为Java工程师提供一套 完整的Java学习路线 。 目录 0.摘要/资料/代码整理 1.Java基础+进阶 2.MySQL,JavaWeb,Mybatis,前端 3.Git 4.SSM(Spring,SpringMVC,Mybatis)框架 5.Maven高级 6.Springboot,MybatisPlus,JPA框架 7.瑞吉外卖、Redis、Nginx、Linux、mysql主从复制

    2024年02月16日
    浏览(242)
  • 【访问者设计模式详解】C/Java/JS/Go/Python/TS不同语言实现

    访问者模式(Visitor Pattern)是一种行为型模式。它封装一个访问者类,把各元素类的操作集合起来,目的是将数据结构与数据操作分离。在不改变原有元素类数据结构的前提下,改变了元素类的执行算法。 当某些较为稳定的东西(数据结构或算法),不想直接被改变但又想扩

    2024年02月02日
    浏览(81)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包