【Day14-单例设计模式动态代理】

news2024/9/30 11:22:49

 单例设计模式

 什么是设计模式(Design pattern) ?

  • 一个问题通常有n种解法,其中肯定有一种解法是最优的,这个最优的解法被人总结出来了,称之为设计模式
  • 设计模式有20多种,对应20多种软件开发中会遇到的问题。

单例设计模式 

作用:确保一个类只有一个对象。 

场景:计算机中的回收站、任务管理器、Java中的Runtime类等

写法

  • 把类的构造器私有(保证别人不能new)
  • 在类中自己创建一个对象,并赋值到一个变量
  • 定义一个静态方法,返回自己创建的这个对象

/*
单例设计模式
    作用:确保一个类只有一个对象。
    场景:计算机中的回收站、任务管理器、Java中的Runtime类等

饿汉式(提前创建对象)
    把类的构造器私有(保证别人不能new)
    在类中自己创建一个对象,并赋值到一个变量
    定义一个静态方法,返回自己创建的这个对象
*/
public class Demo {
    public static void main(String[] args) {
        new Thread(new Runnable() {
            @Override
            public void run() {
                User user = User.getUser();
                System.out.println(Thread.currentThread().getName() + "--" + user);
            }
        },"小白").start();
        new Thread(() -> {
                User user = User.getUser();
                System.out.println(Thread.currentThread().getName() + "--" + user);
        },"小紫").start();
    }
}

class User{
    private String name;
    private Integer age;

    //2.创建自己的对象
    private static User user = new User();
    //构造器私有
    private User() {
    }
    //提供一个方法,返回对象
    public static User getUser(){
        return user;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }

    public static void setUser(User user) {
        User.user = user;
    }
}

1、单例模式解决了什么问题 ?有啥场景和好处?

确保一个类只有一个对象。

任务管理器对象、获取运行时对象。

在这些业务场景下,使用单例模式,可以避免浪费内存。

2、单例怎么写?

把类的构造器私有;
定义一个类变量存储类的一个对象;
提供一个类方法返回对象。

3、饿汉式单例的特点是什么?

在获取类的对象时,对象已经创建好了。

懒汉式单例设计模式

第一次拿对象时,才开始创建对象

写法

  • 把类的构造器私有(保证别人不能new)
  • 在类中定义一个类变量用于存储对象(注意:此时只定义,不创建)
  • 提供一个类方法,在方法中创建并返回对象(要保证只创建一次)

/*
单例设计模式
    作用:确保一个类只有一个对象。
    场景:计算机中的回收站、任务管理器、Java中的Runtime类等

懒汉式(第一次获取时创建对象)
    把类的构造器私有(保证别人不能new)
    在类中定义一个类变量用于存储对象(注意:此时只定义,不创建)
    提供一个类方法,在方法中创建并返回对象(要保证只创建一次)

注意
    获取方法需要使用synchronized修饰,以保证只有一个线程可以成功创建出对象
*/
public class Demo {
    public static void main(String[] args) {
        new Thread(() -> {
            Teacher teacher = Teacher.getTeacher();
            System.out.println(Thread.currentThread().getName() + "--" + teacher);
        }).start();

        new Thread(() -> {
            Teacher teacher1 = Teacher.getTeacher();
            System.out.println(Thread.currentThread().getName() + "--" + teacher1);
        }).start();
    }
}

class Teacher{
    private String name;
    private Integer age;

    private Teacher(){}

    //定义变量,不要创建
    private static volatile Teacher teacher;

    //提供静态方法,返回当前类的对象
//    public static Teacher getTeacher() {
//        if (teacher == null) {
//            teacher = new Teacher();
//        }
//        return teacher;
//    }
    //同步方法
//    public static synchronized Teacher getTeacher() {
//        if (teacher == null) {
//            teacher = new Teacher();
//        }
//        return teacher;
//    }
    //同步代码块
    public static Teacher getTeacher() {
        if (teacher == null) {
            synchronized (Teacher.class) {
                if (teacher == null) {
                    teacher = new Teacher();
                }
            }
        }
        return teacher;
    }
}

1、懒汉单例模式的特点是什么?

要用类的对象时才创建对象(延迟加载对象)

2、懒汉单例模式怎么写?

  • 把构造器私有
  • 定义一个类变量用于存储对象
  • 提供一个类方法,保证返回的是同一个对象

使用枚举实现单例设计模式

/*
单例设计模式
    作用:确保一个类只有一个对象。
    场景:计算机中的回收站、任务管理器、Java中的Runtime类等

枚举实现单例
    直接在枚举中提供一个枚举项就可以实现单例

注意
    Google首席Java架构师、(Effective Java》 一书作者Java集合框架的开创者Joshua Bloch在Effective Java一书中提到
        单元素的枚举类型,已经成为实现singleton的最佳方法
        在这种实现方式中,既可以避免多线程同步问题
        还可以防止通过反射和反序列化来重新创建新的对象
        在很多优秀的开源代码中,我们经常可以看到使用枚举方式来实现的单例模式类
*/
public class Demo {
    public static void main(String[] args) {
        new Thread(new Runnable() {
            @Override
            public void run() {
                School wangba = School.Wangba;
                System.out.println(Thread.currentThread().getName() + "--" + wangba.hashCode());
            }
        }).start();
        new Thread(() -> {
                School wangba = School.Wangba;
                System.out.println(Thread.currentThread().getName() + "--" + wangba.hashCode());
        }).start();
        new Thread(new Runnable() {
            @Override
            public void run() {
                School wangba = School.Wangba;
                System.out.println(Thread.currentThread().getName() + "--" + wangba.hashCode());
            }
        }).start();
    }
}

enum School{
    Wangba
}

动态代理

 如何为Java对象创建一个代理对象?

  • java.lang.reflect.Proxy类:提供了为对象产生代理对象的方法: 

/*
接口
*/
public interface Star {
     String sing(String name);

     void dance();
}
public class ProxyUtil {
    public static Star createProxy(Star star){
        //1.获取被代理对象,参数中已经传入
        //2.编写代理类的业务逻辑
        InvocationHandler invocationHandler = new InvocationHandler() {
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                //调用代理对象的方法名字
                String methodName = method.getName();
                if (methodName.equals("sing")) {
                    System.out.println("经纪人---买个话筒,收钱");
                } else {
                    System.out.println("经纪人---盘一块地,收钱");
                }
                Object obj = method.invoke(star, args);
                return obj;
            }
        };
        //3.生成代理对象
        Object obj = Proxy.newProxyInstance(
                star.getClass().getClassLoader(),
                star.getClass().getInterfaces(),
                invocationHandler
        );
        //返回代理对象
        return (Star) obj;
    }
}
public class Cln implements Star{

    @Override
    public String sing(String name) {
        System.out.println("ccc唱:" + name);
        return name;
    }

    @Override
    public void dance() {
        System.out.println("ccc💃💃💃💃💃");
    }
}
public class ProxyUtilTest {
    public static void main(String[] args) {
        //创建被代理对象
        Cln cln = new Cln();
        //生成代理对象
        Star star = ProxyUtil.createProxy(cln);
        //调用代理对象,让被代理对象干活
        star.sing("大香蕉");
        star.dance();
    }
}

案例:

使用代理优化用户管理类

场景

某系统有一个用户管理类,包含用户登录,删除用户,查询用户等功能,系统要求统计每个功能的执行耗时情况,以便后期观察程序性能。

需求

现在,某个初级程序员已经开发好了该模块,请观察该模块的代码,找出目前存在的问题,并对其进行改造。
public class UserServiceProxyUtil {
    public static UserService createProxy(UserService userService) {
        //获得被代理对象

        //编写相关业务逻辑代码
        InvocationHandler invocationHandler = new InvocationHandler() {
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                //调用代理对象的方法名字
                String methodName = method.getName();
                if (methodName.equals("login")) {
                    System.out.println(new Date() + "==" + Arrays.toString(args) + "登录了");
                }
                //进行计时
                long begin = System.currentTimeMillis();
                //调用真正的方法
                Object obj = method.invoke(userService, args);
                //进行计时,结束
                long end = System.currentTimeMillis();
                //计算差值
                long time = end - begin;
                System.out.println("【" + methodName + "】方法,执行了:【" + time + "毫秒】");
                return obj;
            }
        };
        //调用Proxy生成代理对象
        UserService user = (UserService) Proxy.newProxyInstance(
                userService.getClass().getClassLoader(),
                userService.getClass().getInterfaces(),
                invocationHandler
        );

        //返回代理对象
        return user;
    }
}
/**
 * 用户业务实现类
 */
public class UserServiceImpl implements UserService {
    @Override
    public void login(String loginName, String passWord) throws Exception {
        if ("admin".equals(loginName) && "123456".equals(passWord)) {
            System.out.println("您登录成功,欢迎光临本系统~");
        } else {
            System.out.println("您登录失败,用户名或密码错误~");
        }
        Thread.sleep(1000);
    }

    @Override
    public void deleteUsers() throws Exception {
        System.out.println("成功删除了1万个用户~");
        Thread.sleep(1500);
    }

    @Override
    public String[] selectUsers() throws Exception {
        System.out.println("查询出了3个用户");
        String[] names = {"张全蛋", "李二狗", "牛爱花"};
        Thread.sleep(500);
        return names;
    }
}

/**
 * 用户业务接口
 */
public interface UserService {
    // 登录功能
    void login(String loginName, String passWord) throws Exception;

    // 删除用户
    void deleteUsers() throws Exception;

    // 查询用户,返回数组的形式。
    String[] selectUsers() throws Exception;
}
/**
 * 目标:使用动态代理解决实际问题,并掌握使用代理的好处。
 */
public class Test {
    public static void main(String[] args) throws Exception{
        // 1、创建用户业务对象
        UserService userService = new UserServiceImpl();
        UserService proxy = UserServiceProxyUtil.createProxy(userService);
        // 2、调用用户业务的功能。
        proxy.login("admin", "123456");
        System.out.println("----------------------------------");

        proxy.deleteUsers();
        System.out.println("----------------------------------");

        String[] names = proxy.selectUsers();
        System.out.println("查询到的用户是:" + Arrays.toString(names));
        System.out.println("----------------------------------");

    }
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2136620.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

记录开发一个英语听力训练网站

背景 在当前全球经济衰退的背景下,IT相关的工作在国内的竞争也是越来越激烈,为了能够获得更多的可能性,英语的学习也许能为程序员打开一扇新的窗户,比如很多远程的工作尤其是国际化背景的工作团队,英语的协作沟通是必…

Modbus通信

Modbus是一种经典的工业通信协议,由Modicon(现为施耐德电气)在1979年首次发布。它广泛应用于各种工业自动化系统中,尤其是在PLC(可编程逻辑控制器)与其他设备之间的通信。Modbus的主要特点是其简单性和开放…

《Oracle(一)- 基础》

文章目录 一、Oracle简介(一)什么是ORACLE(二)ORACLE 体系结构1.数据库2.实例3.数据文件(dbf)4.表空间5.用户 二、ORACLE 安装与配置(一)VMware 挂载 windows server 2003&#xff0…

海外短剧系统一站式开发+h5,app双端

前言: 海外短剧是指那些制作时间短、剧情紧凑、内容丰富的剧集,主要在海外市场(如北美、欧洲、东南亚等)播放并受到欢迎。 而海外短剧系统是指一种用于制作和播放海外短剧的系统。该系统通常由电视台、制片公司或在线视频平台使…

C++:STL详解(一)string类的基本介绍与使用方式

✨ Blog’s 主页: 白乐天_ξ( ✿>◡❛) 🌈 个人Motto:实践是检验真理的唯一标准!!!敲代码需要勤快点!!!! 💫 欢迎来到我的学习笔记&#xff0…

十二,Spring Boot 异常处理(自定义异常页面,全局异常,自定义异常)

十二,Spring Boot 异常处理(自定义异常页面,全局异常,自定义异常) 文章目录 十二,Spring Boot 异常处理(自定义异常页面,全局异常,自定义异常)1. 基本介绍2. 自定义异常页面3. 全局异常4. 自定义异常5. 补充…

LineageOS刷机教程

版权归作者所有,如有转发,请注明文章出处:https://cyrus-studio.github.io/blog/ LineageOS 是一个基于 Android 开源项目(AOSP)的开源操作系统,主要由社区开发者维护。它起源于 CyanogenMod 项目&#xff…

数据库索引底层数据结构之B+树MySQL中的页索引分类【纯理论知识,干货分享,面试必备】

目录 1、索引简介 1.1 什么是索引 1.2 使用索引的原因 2、索引中数据结构的设计 —— B树 2.1 哈希 2.2 二叉搜索树 2.3 B树 2.4 最终选择之——B树 2.4.1 B树与B树的对比(面向索引)【面试题】 3、MySQL中的页 3.1 页的使用原因 3.2 页的结构 3.2.1 页文件头和页文件…

解锁定位服务:Flutter应用中的高德地图定位

前言 在现代移动应用开发中,定位服务已成为一项基本功能,它使得应用能够获取用户的地理位置信息,为用户提供更加个性化的服务。 Flutter 作为跨平台的移动应用开发框架,支持集成多种服务,包括定位服务。 本文将介绍如…

HR8870:可PWM控制,4.5A直流有刷电机驱动数据手册

HR8870芯片描述 HR8870是一款直流有刷电机驱动器,适用于打印机、电器、工业设备以及其他小型机器。两个逻辑输入控制H桥驱动器,该驱动器由四个N-MOS组成,能够以高达4.5A的峰值电流双向控制电机。利用电流衰减模式,可通过对输入进行…

故障码格式解析

中,诊断故障码(DTC, Diagnostic Trouble Code)是由一个字母前缀和三个后续字符组成的。这些字母前缀根据故障所属的系统类别来区分,具体如下: B0 -- B3:表示车身系统(Body)的故障码…

Linux CTF逆向入门

1.ELF格式 我们先来看看 ELF 文件头,如果想详细了解,可以查看ELF的man page文档。 关于ELF更详细的说明: e_shoff:节头表的文件偏移量(字节)。如果文件没有节头表,则此成员值为零。 sh_offset&…

Qt 菜单、工具栏 的基本使用

效果 代码 #include "mainwindow.h" #include "ui_mainwindow.h" #include<QToolBar> #include<QDebug> #include<QPushButton>MainWindow::MainWindow(QWidget *parent): QMainWindow(parent), ui(new Ui::MainWindow) {ui->setupU…

【JAVA入门】Day45 - 压缩流 / 解压缩流

【JAVA入门】Day45 - 压缩流 / 解压缩流 文章目录 【JAVA入门】Day45 - 压缩流 / 解压缩流一、解压缩流二、压缩流 在文件传输过程中&#xff0c;文件体积比较大&#xff0c;传输较慢&#xff0c;因此我们发明了一种方法&#xff0c;把文件里的数据压缩到一种压缩文件中&#x…

【LLMs对抗性提示:提示泄漏、非法行为、DAN、Waluigi效应、 游戏模拟器、防御策略————】

对抗性提示 目录 对抗性提示 提示注入 提示泄漏 非法行为 DAN Waluigi效应 GPT-4模拟器 游戏模拟器 防御策略 在指令中添加防御 参数化提示组件 引用和其他格式 对抗提示检测器 模型类型 参考文献 Adversarial prompting是提示工程中的一个重要主题&#xff0c…

每日OJ_牛客_NC313 两个数组的交集

目录 牛客_NC313 两个数组的交集 解析代码 牛客_NC313 两个数组的交集 两个数组的交集_牛客题霸_牛客网 class Solution { public:/*** 代码中的类名、方法名、参数名已经指定&#xff0c;请勿修改&#xff0c;直接返回方法规定的值即可** * param nums1 int整型vector * pa…

统计/nginx/access.log中每个ip的访问次数,按高到低排列

/nginx/access.log具体内容长这样&#xff1a; 第一个元素就是ip。 awk {print $1} /nginx/access.log | sort | uniq -c | sort -r首先&#xff0c;awk {print $1} /nginx/access.log 从 /nginx/access.log文件的每行中提取出第一个字段。然后&#xff0c;sort 对提取出的第…

【有哪些坑】Apollo配置中心FAQ常见问题列表

使用某个框架之前&#xff0c;得先看看前辈们踩过的坑。 他人的间接经验 -> 自己的直接经验 前车之鉴&#xff0c;后事之师。比喻前人失败了&#xff0c;后人应该从中吸取教训&#xff0c;避免再犯同样的错误。 常见问题回答 1. Apollo是什么&#xff1f; Apollo&#xff…

关于STM32项目面试题01:电源

博客的风格是&#xff1a;答案一定不能在问题的后面&#xff0c;要自己想、自己背&#xff1b;回答都是最精简、最精简、最精简&#xff0c;可能就几个字&#xff0c;你要自己自信的展开。 面试官01&#xff1a;说说你知道的开关电源的拓扑结构&#xff1f; 面试官02&#xff1…

Nacos下载和启动

Nacos是什么&#xff1f; 一个更易于构建云原生应用的动态服务发现、配置管理和服务管理平台 下载 https://github.com/alibaba/nacos/releases/tag/2.1.1启动 将下载好的Nacos解压缩&#xff0c;然后到bin目录下打开cmd 输入指令&#xff1a;startup.cmd -m standalone 出…