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

day14-单例设计模式动态代理

一、单例设计模式

单例设计模式
    作用:确保一个类只有一个对象。
    场景:计算机中的回收站、任务管理器、Java中的Runtime类等
    好处:在这些业务场景下,使用单例模式,可以避免浪费内存。

1.1 饿汉式

饿汉式(提前创建对象)
    把类的构造器私有(保证别人不能new)
    在类中自己创建一个对象,并赋值到一个变量
    定义一个静态方法,返回自己创建的这个对象
    
饿汉式单例的特点:    
    在获取类的对象时,对象已经创建好了。
​
    单例设计模式:单线程下是一个对象,多线程下也是一个对象
public class Demo {
    public static void main(String[] args) {
        //单线程下,只有一个对象
//        User user = User.getUser();
//        System.out.println(Thread.currentThread().getName()+":"+user);
//        User user1 = User.getUser();
//        System.out.println(Thread.currentThread().getName()+":"+user1);
​
        //多线程下,也是一个对象
        new Thread(new Runnable() {
            @Override
            public void run() {
                User user = User.getUser();
                System.out.println(Thread.currentThread().getName()+":"+user);
            }
        },"A").start();
​
        new Thread(() -> {
                User user = User.getUser();
                System.out.println(Thread.currentThread().getName()+":"+user);
        },"B").start();
​
    }
}
class User{
    private String name;
    private Integer age;
    //2. 创建一个自己的对象
    private static User user = new User();
​
    // 1. 私有的构造器
    private User(){}
​
    // 3. 定义一个静态方法,返回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;
    }
}

1.2 懒汉式

懒汉式(第一次获取时创建对象)
    把类的构造器私有(保证别人不能new)
    在类中定义一个类变量用于存储对象(注意:此时只定义,不创建)
    提供一个类方法,在方法中创建并返回对象(要保证只创建一次)
​
注意
    获取方法需要使用synchronized修饰,以保证只有一个线程可以成功创建出对象
public class Demo {
    public static void main(String[] args) {
        // 单线程
//        Teacher teacher1 = Teacher.getTeacher();
//        System.out.println(teacher1);
//        Teacher teacher2 = Teacher.getTeacher();
//        System.out.println(teacher2);
​
        //多线程,不能保证返回的是同一个对象(可以加锁解决该问题)
        for (int i = 0; i < 100; i++) {
            new Thread(new Runnable() {
                @Override
                public void run() {
                    Teacher teacher1 = Teacher.getTeacher();
                    System.out.println(Thread.currentThread().getName()+":"+teacher1);
                }
            }).start();
        }
//        new Thread(new Runnable() {
//            @Override
//            public void run() {
//                Teacher teacher1 = Teacher.getTeacher();
//                System.out.println(Thread.currentThread().getName()+":"+teacher1);
//            }
//        },"t1").start();
//
//        new Thread(new Runnable(){
//            @Override
//            public void run() {
//                Teacher teacher2 = Teacher.getTeacher();
//                System.out.println(Thread.currentThread().getName()+":"+teacher2);
//            }
//        },"t2").start();
​
​
​
    }
​
}
class Teacher{
    private String name;
    private Integer age;
    //1. 私有化构造器
    private Teacher(){
    }
​
    //2. 定义一个类变量,用于存储对象
    private static Teacher teacher;
​
    //3. 定义一个静态方法,返回当前类的对象
    //3.1 同步方法
    public static synchronized Teacher getTeacher(){
        if(teacher==null){
            teacher = new Teacher();
        }
        return teacher;
    }
//    //3.2 同步代码块
//    public static Teacher getTeacher(){
//        if (teacher == null) {
//            synchronized (Teacher.class) {
//                if (teacher == null) {
//                    teacher = new Teacher();
//                }
//            }
//        }
//        return teacher;
//    }
    
}

1.3 枚举实现单例

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

二、动态代理

/**
 * ClassName:Star
 * Description:
 *  明星接口
 *      唱歌
 *      跳舞
 */
public interface Star {
    // 唱歌
    public String sing(String name);
    // 跳舞
    void dance();
}
/**
 * ClassName:Yct
 * Description:
 *  明星实现类
 */
public class Yct implements Star{
    @Override
    public String sing(String name) {
        System.out.println("Yct开始唱歌,歌名" + name);
        return name;
    }
​
    @Override
    public void dance() {
        System.out.println("Yct开始跳舞");
    }
}
/**
 * ClassName:ProxyUtil
 * Description:
 *  创建明星的代理类
 */
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 {//生成的代理对象,代理的方法,代理方法的参数
                // 1. 获取方法名(调用代理对象的方法名字)
                String name = method.getName();
                // 2. 判断是否是唱歌方法
                if ("sing".equals(name)){
                    // 3. 执行代理方法
                    System.out.println("经纪人-准备话筒-收钱");
                }else {
                    System.out.println("经纪人-准备场地-收钱");
                }
                Object o = method.invoke(star, args);//调用目标对象的方法
                return o;
            }
        };
        //3. 生成代理对象
        Object obj = Proxy.newProxyInstance(
                //用于指定用哪个类加载器,去加载生成的代理类
                star.getClass().getClassLoader(),//获取当前类加载器,把类加载到JVM中
                //用于指定代理类需要实现的接口
                star.getClass().getInterfaces(),//获取当前类实现的接口,代理有哪些行为
                //用于指定代理类对应的处理器,处理代理类上的方法调用(用来指定生成的代理对象要干什么事情)
                invocationHandler
                );
        //4. 返回代理对象
        return (Star) obj;
    }
​
}
/**
 * ClassName:ProxyUtilTest
 * Description:
 *  测试类
 */
public class ProxyUtilTest {
    public static void main(String[] args) {
        //1. 创建明星对象
        Yct yct = new Yct();
        //2. 创建代理对象
        Star proxy = new ProxyUtil().createProxy(yct);
​
        //3. 调用代理对象的方法
        proxy.dance();
        proxy.sing("小苹果");
    }
}

使用代理优化用户管理类

场景
    某系统有一个用户管理类,包含用户登录,删除用户,查询用户等功能,系统要求统计每个功能的执行耗时情况,以便后期观察程序性能。
需求
    现在,某个初级程序员已经开发好了该模块,请观察该模块的代码,找出目前存在的问题,并对其进行改造。
​
/**
 * 用户业务接口
 */
public interface UserService {
    // 登录功能
    void login(String loginName, String passWord) throws Exception;
​
    // 删除用户
    void deleteUsers() throws Exception;
​
    // 查询用户,返回数组的形式。
    String[] selectUsers() throws Exception;
}
​
/**
 * 用户业务实现类
 */
public class UserServiceImpl implements UserService {
    @Override
    public void login(String loginName, String passWord) throws Exception {
      //  long time1 = System.currentTimeMillis();
        if ("admin".equals(loginName) && "123456".equals(passWord)) {
            System.out.println("您登录成功,欢迎光临本系统~");
        } else {
            System.out.println("您登录失败,用户名或密码错误~");
        }
        Thread.sleep(1000);
       // long time2 = System.currentTimeMillis();
       // System.out.println("login方法耗时:" + (time2 - time1));
    }
​
    @Override
    public void deleteUsers() throws Exception {
        //long time1 = System.currentTimeMillis();
        System.out.println("成功删除了1万个用户~");
        Thread.sleep(1500);
        //long time2 = System.currentTimeMillis();
        //System.out.println("deleteUsers方法耗时:" + (time2 - time1));
    }
​
    @Override
    public String[] selectUsers() throws Exception {
        //long time1 = System.currentTimeMillis();
        System.out.println("查询出了3个用户");
        String[] names = {"张全蛋", "李二狗", "牛爱花"};
        Thread.sleep(500);
        //long time2 = System.currentTimeMillis();
        //System.out.println("selectUsers方法耗时:" + (time2 - time1));
        return names;
    }
}
/**
 * ClassName:UserServiceProxy
 * Description:
 *  生成代理对象
 * @version 1.0
 * @Author wang
 * @Creat 2024/9/13 14:52
 */
public class UserServiceProxyUtil {
    public static UserService createProxy(UserService userService){
        // 1.获取被代理对象----userService
​
        // 2.创建代理的业务逻辑
        InvocationHandler invocationHandler = new InvocationHandler(){
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                // 2.1 获取调用代理对象的方法名
                String methodName = method.getName();
                if(methodName.equals("login")){
                    System.out.println( new Date() + "--" + Arrays.toString(args) + "登录了");
                }
                // 2.2 进行计时开始
                long begin = System.currentTimeMillis();
​
                // 2.3 调用被代理对象的方法
                Object o = method.invoke(userService, args);
​
                // 2.4 进行计时结束
                long end = System.currentTimeMillis();
​
                // 2.5 计算差值
                long time = end - begin;
                System.out.println("方法" + methodName + "耗时:" + time + "毫秒");
​
                return o;
            }
        };
        // 3.调用Java的API,生成代理对象
        UserService userService1 = (UserService)Proxy.newProxyInstance(
                userService.getClass().getClassLoader(), // 类加载器
                userService.getClass().getInterfaces(),  // 接口
                invocationHandler
        );
        // 4.返回代理对象
        return userService1;
    }
}
/**
 * 目标:使用动态代理解决实际问题,并掌握使用代理的好处。
 */
public class Test {
    public static void main(String[] args) throws Exception{
        // 1、创建用户业务对象
        UserService userService = new UserServiceImpl();
        // 2、创建代理对象
        UserService userServiceProxy = UserServiceProxyUtil.createProxy(userService);
​
        // 2、调用用户业务的功能。
        userServiceProxy.login("admin", "123456");
        System.out.println("----------------------------------");
​
        userServiceProxy.deleteUsers();
        System.out.println("----------------------------------");
​
        String[] names = userServiceProxy.selectUsers();
        System.out.println("查询到的用户是:" + Arrays.toString(names));
        System.out.println("----------------------------------");
​
    }
}


http://www.kler.cn/a/303190.html

相关文章:

  • 高美GULMAY高压发生器维修X射线源维修CF160
  • 深度解析:Android APP集成与拉起微信小程序开发全攻略
  • 38配置管理工具(如Ansible、Puppet、Chef)
  • 【Kafka】集成案例:与Spark大数据组件的协同应用
  • 必修 -- 常用笔试题
  • 1.两数之和-力扣(LeetCode)
  • Qt 学习第八天:菜单栏、工具栏、状态栏、模态和非模态对话框创建
  • RabbitMQ延迟消息——DelayExchange插件
  • Python之 条件与循环(Python‘s Conditions and loops)
  • 在麒麟系统 v10 SP3 上运行自带的 MariaDB
  • 【鸿蒙】HarmonyOS NEXT星河入门到实战6-组件化开发-样式结构重用常见组件
  • Oracle中VARCHAR和VARCHAR2的区别
  • CSS框架 Tailwind CSS
  • Leetcode3276. 选择矩阵中单元格的最大得分
  • CNN中的conv
  • ASP.net core 8.0网站发布
  • 房产销售系统|基于java和vue的房产销售系统(源码+数据库+文档)
  • 利用apache-pdfbox库修改pdf文件模板,进行信息替换
  • 【基础算法总结】二分查找
  • 在Python的Pandas库中,`df.iloc[::500]`是一个用于数据选择的索引器,它允许我们从DataFrame中选择特定的行和列。
  • golang学习笔记19——golang做服务发现与注册的深度剖析
  • 从安装ffmpeg开始,把一个视频按照每秒30帧fps剪切为图片
  • Vue组件:模板引用ref属性的使用
  • 微信小程序之轮播图组件封装
  • CTF常见编码及加解密(超全)第二篇
  • java程序员入行科目一之CRUD轻松入门教程(二)