• 【Java】设计模式之单例模式与工厂模式


    ✅作者简介:热爱后端语言的大学生,CSDN内容合伙人
    ✨精品专栏:C++面向对象
    🔥系列专栏:JavaSE精品总结


      前言

    国庆节快乐!今天家里的风很凉爽,就好像在为国庆而欢呼!与此同时我决定把Java的设计模式总结一番,为以后能够书写清晰的项目结构打下基础。


    1、设计模式概念及分类

    简单来说设计模式是被广大程序员们总结并认可的编码套路,其中最常用的莫过于单例模式工厂模式,而单例模式也有更加细的分类,一起来学习一下这些模式的用法和特点吧。

    2、单例模式

    • 一个类只能被实例化出来一个对象

    2.1、饿汉式

    • 无论如何,都会创建出来一个对象
    • 思路:
      在类中直接实例化一个用来返回的对象,再为外界提供一个获取该对象的方法
    • 缺点:有可能造成空间浪费

    代码解释:

    /**
     * 单例模式-饿汉式
     */
    public class ClassA {
        //唯一的、全局的、私有的、用来返回的对象实例
       private static ClassA ca=new ClassA();
    
        //方法:用来被外界调用,从而获取该类的唯一实例
        //static:为了使外界直接通过类名调用该方法
        public static ClassA getClassA(){
            return ca;
        }
    
       //私有化构造:避免外界通过构造创建该类的对象
       private ClassA(){}
    
    }
    
    public class Test {
        public static void main(String[] args) {
            ClassA ca1=ClassA.getClassA();
            ClassA ca2=ClassA.getClassA();
            System.out.println(ca1==ca2);//true
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25

    相当于类加载,ca1ca2都是类对象,为同一个对象,要与类的对象有所区分。

    2.2、懒汉式

    • 思路:只有当需要创建唯一实例时,才会在对应方法中进行实例化
      • 使用synchronized来同步方法
    • 缺点:同步方法效率太慢,线程效率低

    代码解释:

    /**
     * 单例模式-懒汉式
     */
    public class ClassB {
        //声明用来返回的对象引用
        private static ClassB cb=null;
    
        //synchronized:避免线程安全问题
        public synchronized static ClassB getClassB(){
            if (cb==null){//非空判断,避免重复创建
                cb=new ClassB();
            }
            return cb;
        }
    
        //私有化构造
        private ClassB(){}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    这里利用了synchronized来防止重复创建实例化对象:如果事先没有创建,那就新创建,不会浪费空间。

    2.2.1、懒汉式进阶版

    • 思路:在保证线程安全的基础上,最大程度提高线程效率
    • 使用synchronized来同步代码块

    代码演示:

    /**
     * 单例模式-懒汉式进阶版
     */
    public class ClassB2 {
        //声明用来返回的对象引用
        private static ClassB2 cb=null;
    
        //synchronized:避免线程安全问题
        public static ClassB2 getClassB2(){
            if (cb==null){//非空判断,避免重复创建
                synchronized (ClassB2.class){
                    if (cb==null){//二次校验,如果出现了线程安全问题,最大程度保证数据安全
                        cb=new ClassB2();
                    }
                }
            }
            return cb;
        }
    
        //私有化构造
        private ClassB2(){}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    同步代码块会使程序运行效率提升,因为此时只需时间片就可以执行此线程。

    2.2.2、懒汉式之懒加载

    • 思路:在懒汉式的基础上,将获取自己类实例的任务交给静态内部类完成
    public class ClassC {
        //声明用来返回的对象引用
        private static ClassC cc=null;
    
        //静态内部类:获取ClassC的唯一实例
        private static class ClassC2{
            //synchronized:避免线程安全问题
            public static ClassC get(){
                if (cc==null){//非空判断,避免重复创建
                    synchronized (ClassC.class){
                        if (cc==null){//二次校验,如果出现了线程安全问题,最大程度保证数据安全
                            cc=new ClassC();
                        }
                    }
                }
                return cc;
            }
        }
    
        public static ClassC getClassC(){
            return  ClassC2.get();
        }
        
        //私有化构造
        private ClassC(){}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26

    这种方式效果跟懒汉式的进阶类似,只不过是将加载交给了静态内部类,效率更高。

    3、工厂模式

    特点:

    • 常用于框架
    • 自身不再直接创建对象,交给 “工厂” 完成,需要对象时直接调用工厂的指定方法获取

    步骤:

    1. 书写实体类,用来构建对象
    2. 书写.properties配置文件,存放工厂使用反射时需要的类信息
    3. 书写工厂类,创建对象
    4. 书写测试类

    用一个实例演示:

    3.1、书写实体类

    public class Student {
        private String name;
        private int age;
        private double score;
        
        //此处省略getter与setter方法
    
        public Student() {
        }
    
        public Student(String name, int age, double score) {
            this.name = name;
            this.age = age;
            this.score = score;
        }
    
        @Override
        public String toString() {
            return "Student{" +
                    "name='" + name + '\'' +
                    ", age=" + age +
                    ", score=" + score +
                    '}';
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25

    3.2、新建配置文件.properties

    1. 右键项目名创建一个后缀名为.properties的配置文件
    2. 文件内容:
      • 键(自定义)=值(类的全限定名)
      • 例如:StudentClassName=com.bz.entity.Student
    3. 结构特点:
      1. 键不可重复
      2. 等号左右无双引号
      3. 整条语句不要存在多余空格
      4. 末尾无分号
      5. 一行只能有一个键值对

    3.3、书写工厂类并创建对象

    /**
     * 工厂类
     */
    public class MyFactory {
        //书写获取Student实例的方法
        //static:方便直接通过类名调用
        public static Student getStudent(){
            Student stu=null;
            try (
                    //创建字节输入流对象
                    FileInputStream fis = new FileInputStream("Factory.properties");
                    //添加缓冲流
                    BufferedInputStream bis = new BufferedInputStream(fis);
            ) {
                //创建用来接收配置文件信息的Properties集合
                Properties p = new Properties();
                //通过load方法将配置文件读取值集合中
                p.load(bis);
                //获取全限定名
                String str= p.getProperty("StudentClassName");
                //获取类对象
                Class c = Class.forName(str);
                //利用无参构造构建类的对象
                stu=(Student) c.newInstance();
    
            }catch (FileNotFoundException e){
                System.out.println("文件路径不正确");
            }catch (IOException e){
                System.out.println("读取失败");
            }catch (Exception e){
                System.out.println("未知异常!");
                e.printStackTrace();
            }
    
            return stu;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37

    3.4、对工厂类测试

    public class TestMyFactory {
        public static void main(String[] args) {
            //利用工厂获取学生对象
            Student stu = MyFactory.getStudent();
            stu.setName("张三");
            stu.setAge(20);
            stu.setScore(78);
            System.out.println(stu);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    到这里有关设计模式单例模式与工厂模式就分享结束了,最后祝大家国庆节high起来,心情愉快!

  • 相关阅读:
    《我要进大厂》- Spring框架 夺命连环22问,你能坚持到第几问?(Spring高频问题)
    FPGA - 科学设计复位信号(XILINX)
    Gradle 新建项目及Gradle常用命令&镜像源修改
    从php页面插入MySQL的数据变为乱码如何解决?
    【树莓派】yolov5 Lite,目标检测,行人检测入侵报警
    你真的了解IP地址吗?
    rviz添加qt插件
    如何系统地学习 JavaScript?
    设计模式6、适配器模式 Adapter
    Hadoop根据加工数据量计算Datanode数量
  • 原文地址:https://blog.csdn.net/m0_58618795/article/details/127132499