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

单例设计模式

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

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

单例设计模式

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

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

写法

  • 把类的构造器私有(保证别人不能new)
  • 在类中自己创建一个对象,并赋值到一个变量
  • 定义一个静态方法,返回自己创建的这个对象
java 复制代码
/*
单例设计模式
    作用:确保一个类只有一个对象。
    场景:计算机中的回收站、任务管理器、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 复制代码
/*
单例设计模式
    作用:确保一个类只有一个对象。
    场景:计算机中的回收站、任务管理器、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 复制代码
/*
单例设计模式
    作用:确保一个类只有一个对象。
    场景:计算机中的回收站、任务管理器、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类:提供了为对象产生代理对象的方法:
java 复制代码
/*
接口
*/
public interface Star {
     String sing(String name);

     void dance();
}
java 复制代码
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;
    }
}
java 复制代码
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💃💃💃💃💃");
    }
}
java 复制代码
public class ProxyUtilTest {
    public static void main(String[] args) {
        //创建被代理对象
        Cln cln = new Cln();
        //生成代理对象
        Star star = ProxyUtil.createProxy(cln);
        //调用代理对象,让被代理对象干活
        star.sing("大香蕉");
        star.dance();
    }
}

案例:

使用代理优化用户管理类

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

需求
现在,某个初级程序员已经开发好了该模块,请观察该模块的代码,找出目前存在的问题,并对其进行改造。

java 复制代码
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;
    }
}
java 复制代码
/**
 * 用户业务实现类
 */
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;
    }
}
java 复制代码
/**
 * 用户业务接口
 */
public interface UserService {
    // 登录功能
    void login(String loginName, String passWord) throws Exception;

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

    // 查询用户,返回数组的形式。
    String[] selectUsers() throws Exception;
}
java 复制代码
/**
 * 目标:使用动态代理解决实际问题,并掌握使用代理的好处。
 */
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("----------------------------------");

    }
}
相关推荐
草莓base几秒前
【手写一个spring】spring源码的简单实现--bean对象的创建
java·spring·rpc
LateBloomer7779 分钟前
FreeRTOS——信号量
笔记·stm32·学习·freertos
legend_jz12 分钟前
【Linux】线程控制
linux·服务器·开发语言·c++·笔记·学习·学习方法
Komorebi.py13 分钟前
【Linux】-学习笔记04
linux·笔记·学习
drebander25 分钟前
使用 Java Stream 优雅实现List 转化为Map<key,Map<key,value>>
java·python·list
乌啼霜满天24928 分钟前
Spring 与 Spring MVC 与 Spring Boot三者之间的区别与联系
java·spring boot·spring·mvc
tangliang_cn33 分钟前
java入门 自定义springboot starter
java·开发语言·spring boot
程序猿阿伟34 分钟前
《智能指针频繁创建销毁:程序性能的“隐形杀手”》
java·开发语言·前端
Grey_fantasy44 分钟前
高级编程之结构化代码
java·spring boot·spring cloud
新知图书1 小时前
Rust编程与项目实战-模块std::thread(之一)
开发语言·后端·rust