第十四天、设计模板
什么是设计模板(Design pattern) ?
-
一个问题通常有n种解法,其中肯定有一种解法是最优的,这个最优的解法被人总结出来了,称之为设计模式
-
设计模式有20多种,对应20多种软件开发中会遇到的问题
单例设计模式
单例设计模式
-
作用:确保一个类只有一个对象
-
场景:计算机中的回收站、任务管理器、Java中的Runtime类等
饿汉式单例
-
拿对象时,对象早就创建好了。
写法
-
把类的构造器私有(保证别人不能new)
-
在类中自己创建一个对象,并赋值到一个变量
-
定义一个静态方法,返回自己创建的这个对象
-
// 单例类 public class A { // 2、定义一个类变量记住类的一个对象 private static A a = new A(); // 1、私有构造器 private A(){ } // 3、定义一个类方法返回对象 public static A getObject(){ return a; } }
懒汉式单例设计模式
-
第一次拿对象时,才开始创建对象
写法
-
把类的构造器私有(保证别人不能new)
-
在类中定义一个类变量用于存储对象(注意:此时只定义,不创建)
-
提供一个类方法,在方法中创建并返回对象(要保证只创建一次)
-
/** * 主类,用于演示单例模式的线程安全性。 */ public class Main { public static void main(String[] args) { // 获取B类的实例,验证单例模式是否生效 B b1 = B.getInstance(); B b2 = B.getInstance(); // 检查两个实例是否相同,预期输出为true System.out.println(b1 == b2); // 启动两个线程,每个线程都会尝试获取B类的实例,用于测试单例模式的线程安全性 new Thread(() -> { B b = B.getInstance(); // 输出线程名和实例引用,用于验证是否为同一个实例 System.out.println(Thread.currentThread().getName() + "==" + b); }).start(); new Thread(() -> { B b = B.getInstance(); // 输出线程名和实例引用,用于验证是否为同一个实例 System.out.println(Thread.currentThread().getName() + "==" + b); }).start(); } } /** * 使用懒汉模式实现的单例类,确保线程安全。 * 该类的实例化将在第一次调用getInstance方法时完成,并且之后的所有调用都将返回相同的实例。 */ class B { // 私有静态实例变量,用于存储单例实例 // 定义私有静态的变量 private static B b;//默认:null // 私有构造方法,防止外部实例化对象 // 私有构造 private B() { } /** * 静态方法,用于获取B类的单例实例。 * 方法加同步锁,确保在多线程环境下仍然能正确地返回相同的实例。 * * @return B类的单例实例 */ // 定义方法,供外部调用,返回b public synchronized static B getInstance() { // 如果尚未实例化,则创建新实例 // 在第一次调用时需要创建对象 if (b == null) { b = new B(); } // 返回已有的实例 return b; } }
多学一招
-
使用枚举类实现单例设计模式
-
/** * 主程序入口。 * 本程序演示了枚举类型的单例特性。 * 通过比较两个枚举实例是否相等,验证了枚举的单例性质。 */ public static void main(String[] args) { C instance1 = C.INSTANCE; // 获取C的实例 C instance2 = C.INSTANCE; // 再次获取C的实例 System.out.println(instance1 == instance2); // 比较两个实例是否相同 } /** * 枚举C,实现了单例模式。 * 由于枚举的天然单例属性,INSTANCE是C类的唯一实例。 */ enum C { INSTANCE; // C类的唯一实例 }
动态代理
如何为Java对象创建一个代理对象?
-
java.lang.reflect.Proxy类:提供了为对象产生代理对象的方法:
-
public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) 参数一:用于指定用哪个类加载器,去加载生成的代理类 参数二:指定接口,这些接口用于指定生成的代理长什么,也就是有哪些方法 参数三:用来指定生成的代理对象要干什么事情
入门案例 :
//测试
public class Demo {
//使用动态代理的方式,创建代理对象
public static void main(String[] args) {
//1.创建被代理对象
Star star = new YcyStarImpl();
//2.创建动态被代理对象,调用被代理对象
Star proxy = StarProxyUtil.getProxy(star);
//3.调用代理对象的方法,进行测试
proxy.dance();
// String sing = proxy.sing("");
// System.out.println("返回:"+sing);
System.out.println(proxy.sing("鸡你太美"));
}
}
//接口
public interface Star {
//唱歌
String sing(String name);
//跳舞
void dance();
}
//创建代理对象
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
/**
* 使用动态代理实现明星代理类
* 该类提供了一个方法来创建明星的代理对象,代理对象可以在执行明星的方法前后添加额外的操作。
*/
public class StarProxyUtil {
/**
* 创建一个明星的代理对象。
*
* @param star 需要被代理的明星对象。
* @return 返回一个代理对象,该对象在调用明星的方法时会添加额外的操作。
*/
public static Star getProxy(Star star) {
// 创建一个InvocationHandler实现类,用于处理代理对象的方法调用。
//1创建InvocationHandler,编写代理对象的方法逻辑
InvocationHandler handler = new InvocationHandler() {
/**
* 当调用代理对象的方法时,该方法会被执行。
*
* @param proxy 代理对象。
* @param method 被调用的方法。
* @param args 方法的参数。
* @return 方法的返回值。
* @throws Throwable 方法执行中抛出的异常。
*/
/*
执行代理方法中,要完成的业务逻辑
proxy:代理对象 一般不用
method:当前执行的方法对象
args:当前执行的方法的参数
返回object:返回类型的值
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// 在执行明星的方法前,添加额外的操作,例如准备场地和收取劳务报酬。
//1.非核心功能
System.out.println("准备场地,收劳务报酬");
// 调用原始明星对象的方法。
//2.调用被代理对象方法
Object obj = method.invoke(star, args);
// 返回方法的执行结果。
return obj;
}
//return method.invoke(star, args);
};
// 使用动态代理创建明星的代理对象。
//2使用Proxy的newProxyInstance方法创建动态代理对象
return (Star) Proxy.newProxyInstance(
star.getClass().getClassLoader(),
star.getClass().getInterfaces(),
handler
);
}
// Star proxy = (Star) Proxy.newProxyInstance(
// star.getClass().getClassLoader(),
// star.getClass().getInterfaces(),
// handler
// );
// return proxy;
}
//创建实现类
/**
* YcyStarImpl 类实现了 Star 接口,代表了一个名为 Ycy 的明星的具体实现。
* 该类提供了唱歌和跳舞的方法,模拟了明星在舞台上的表演行为。
*/
public class YcyStarImpl implements Star{
/**
* 让 Ycy 唱歌。
* 此方法模拟了 Ycy 在舞台上唱歌的场景,首先通过控制台输出表现唱歌的场景,然后返回唱歌的曲目名称。
*
* @param name 歌曲名称,表示 Ycy 正在演唱的歌曲。
* @return 返回一个字符串,表明 Ycy 唱的是哪首歌。
*/
@Override
public String sing(String name) {
System.out.println("Ycy在舞台上正在唱"+name);
return "Ycy唱的是:"+name;
}
/**
* 让 Ycy 跳舞。
* 此方法模拟了 Ycy 在舞台上跳舞的场景,通过控制台输出表现跳舞的场景。
*/
@Override
public void dance() {
System.out.println("Ycy在舞台上跳舞");
}
}
应用案例 :
/**
* 测试类,用于演示用户服务的代理模式应用。
*/
public class Test {
/**
* 程序入口。
* @param args 命令行参数
* @throws Exception 如果操作失败抛出异常
*/
public static void main(String[] args) throws Exception {
// 创建 UserService 实例,用于后续的用户操作
// 1、创建用户业务对象
UserService userService = new UserServiceImpl();
// 获取 UserService 的代理实例,用于动态增强 UserService 的功能
// 2、调用用户业务的功能。
UserService proxy = UserServiceProxy.getProxy(userService);
// 通过代理实例调用登录方法,演示基本的用户操作
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("----------------------------------");
}
}
/**
* 用户服务接口定义了用户模块的基本操作。
* 包括用户登录、删除用户和查询用户信息等功能。
*/
public interface UserService {
/**
* 用户登录功能。
*
* @param loginName 用户登录名,用于标识用户。
* @param passWord 用户密码,用于验证用户身份。
* @throws Exception 如果登录名或密码不正确,或登录过程中出现其他错误,抛出异常。
*/
// 登录功能
void login(String loginName, String passWord) throws Exception;
/**
* 删除用户功能。
*
* @throws Exception 如果删除过程中出现错误,抛出异常。
*/
// 删除用户
void deleteUsers() throws Exception;
/**
* 查询用户信息功能。
*
* @return 返回用户信息数组,每个元素代表一个用户。
* @throws Exception 如果查询过程中出现错误,抛出异常。
*/
// 查询用户,返回数组的形式。
String[] selectUsers() throws Exception;
}
/**
* 用户服务的实现类,提供用户登录、删除用户和查询用户等功能。
*/
public class UserServiceImpl implements UserService {
/**
* 用户登录方法。
* 模拟登录验证过程,通过比较用户名和密码来确定登录是否成功。
*
* @param loginName 用户名
* @param passWord 密码
* @throws Exception 如果登录失败或线程被中断
*/
@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);
}
/**
* 删除用户方法。
* 模拟删除用户的过程,并给出反馈信息。
*
* @throws Exception 如果删除过程出错或线程被中断
*/
@Override
public void deleteUsers() throws Exception {
System.out.println("成功删除了1万个用户~");
// 模拟删除操作的处理时间
Thread.sleep(1500);
}
/**
* 查询用户方法。
* 返回一个用户名称数组,模拟查询用户的过程。
*
* @return 用户名称数组
* @throws Exception 如果查询出错或线程被中断
*/
@Override
public String[] selectUsers() throws Exception {
System.out.println("查询出了3个用户");
String[] names = {"张全蛋", "李二狗", "牛爱花"};
// 模拟查询操作的处理时间
Thread.sleep(500);
return names;
}
}
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
/**
* 用户服务代理类,用于生成UserService接口的动态代理实例。
* 动态代理在运行时创建一个类,该类实现指定的接口,并将调用转发给指定的调用处理程序。
*/
public class UserServiceProxy {
/**
* 生成UserService接口的动态代理实例。
* 动态代理的作用是在不修改原有业务逻辑的情况下,增加额外的功能,例如日志、事务等。
*
* @param userService 被代理的UserService实例。
* @return 返回一个代理对象,该对象实现了UserService接口,并在方法调用前后添加了日志记录功能。
*/
/*
创建动态代理对象
返回值UserService
参数:被代理对象UserService
*/
public static UserService getProxy(UserService userService) {
// 创建一个InvocationHandler实例,用于处理方法调用。
//1创建InvocationHandler
InvocationHandler handler = new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// 记录方法开始执行的时间。
// 记录开始时间
long beg = System.currentTimeMillis();
// 调用被代理对象的相同方法。
// 执行被代理对象
Object invoke = method.invoke(userService, args);
// 记录方法执行结束的时间,并计算执行耗时。
// 记录结束时间计算总耗时
long end = System.currentTimeMillis();
// 输出方法执行的日志信息,包括方法名和执行耗时。
System.out.println("执行"+method.getName()+"方法,总耗时:"+(end-beg)+"毫秒");
// 返回方法执行的结果。
// 返回值(和被代理对象方法返回值一样
return invoke;
}
};
// 使用动态代理生成UserService接口的实现类实例。
// 这里的参数分别指定了类加载器、接口列表和调用处理程序。
//2.使用proxy的new方法创建代理对象
UserService proxy = (UserService) Proxy.newProxyInstance(
userService.getClass().getClassLoader(),
userService.getClass().getInterfaces(),
handler
);
// 返回代理对象。
return proxy;
}
}
标签:JAVA,14,对象,代理,System,实例,方法,public,进阶
From: https://blog.csdn.net/2402_84667776/article/details/140085228