当前位置: 首页 > article >正文

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

 单例设计模式

 什么是设计模式(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.kler.cn/news/309243.html

相关文章:

  • 一文吃透JVM面试八股文
  • 每日学习一个数据结构-DFA确定有限状态机
  • 【linux】VisiData:强大的命令行数据处理工具
  • 跟李沐学AI:序列到序列seq2seq
  • 本地部署大模型并使用知识库Windows下Ollama+Docker+MaxKB安装的记录
  • 影刀RPE学习——自动化
  • 地大信息-基础信息平台 GetImg 任意文件读取漏洞复现
  • http和https分别是什么?区别是什么?
  • GO GIN SSE DEMO
  • Springboot项目打war包运行及错误解决
  • SpringCloud Alibaba入门简介
  • 最优化理论与自动驾驶(一):概述
  • 你认为嵌入式软件开发的尽头是什么?
  • 了解 React 应用程序中的渲染和重新渲染:它们如何工作以及如何优化它们
  • NEXT.js 中间件 NextResponse.redirect 无效
  • 2576. 求出最多标记下标(24.9.12)
  • 【C/C++】涉及string类的经典OJ编程题
  • Mina protocol - 体验教程
  • 【每日一题】LeetCode 1184.公交站间的距离问题(数组)
  • 【大模型技术教程】FastGPT一站式解决方案[1-部署篇]:轻松实现RAG-智能问答系统
  • C语言习题~day32
  • 密码学---easy_hash
  • 论文阅读: SigLit | SigLip |Sigmoid Loss for Language Image Pre-Training
  • 【Kubernetes】常见面试题汇总(二十一)
  • 51单片机 - DS18B20实验1-读取温度
  • 硬件工程师笔试面试——变压器
  • 二.Oracle每周运维操作
  • 在Android中如何进行多渠道打包
  • Linux基础---07文件传输及解决yum安装失效的方法
  • 【Linux】探索文件I/O奥秘,解锁软硬链接与生成动静态库知识