单例设计模式
什么是设计模式(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("----------------------------------");
}
}