Java 常用设计模式
单例模式
单例模式用于确保一个类只有一个实例。有两种常见的实现方式:懒汉模式(Lazy Initialization)和饿汉模式(Eager Initialization),主要区别在实例化的时机和线程安全性方面。
懒汉
在第一次获取单例实例时才进行实例化,如果没有使用到单例,那么单例实例就不会被创建,实现了延迟加载。
懒汉模式在多线程环境下需要考虑线程安全性,需要使用 synchronized 关键字或者双重检查锁定(Double-Checked Locking)来保证线程安全。
public class Singleton {
private volatile static Singleton instance = null;
private Singleton() {}
public static Singleton getInstance() {
if (instance == null) {
synchronized (Singleton.class) {
if (instance == null) {
instance = new Singleton();
}
}
}
return instance;
}
}
饿汉
在类加载的时候就进行实例化,一直存在于内存中,不管是否被调用。饿汉模式天生线程安全,因为在类加载的过程中就已经创建了单例实例,不存在竞态条件。
public class Singleton {
private static Singleton instance = new Singleton();
private Singleton() {}
public static Singleton getInstance() {
return instance;
}
}
选择那种方式?
如果对象实例比较简单,不需要耗费多少资源和时间,直接使用恶汉模式即可;
如果对象实例较复杂,并且可能阻塞主线程,那么应该使用懒汉模式。例如创建一个Http客户端时,对象复杂,耗时长,可能因为连接超时问题阻塞主线程。
代理模式
代理模式用于控制对对象的访问,为一个对象提供代理以控制对这个对象的访问。代理对象持有对真实对象的引用,可以在访问真实对象时进行控制和扩展。
适用于需要在访问真实对象时添加额外的逻辑处理,比如日志记录、权限校验等。Spring AOP就是基于动态代理实现的,默认使用JDK动态代理方案。
代理分为静态代理和动态代理
静态代理就是手动创建一个代理对象,然后内部包含一个真实对象。
更常用的是动态代理,即运行时动态的进行代理,有两种实现方案
JDK动态代理
Java自带的代理机制,基于接口实现,只能代理实现了接口的类。原理是利用反射机制,动态生成匿名类实现要代理的接口,并且继承Proxy类,由于java不支持多继承,所以JDK动态代理不能代理类。
在匿名类内部,使用反射机制调用真实对象目标方法,在JDK7、8之前的版本存在性能问题。
/**代理接口*/
public interface IHello {
String hi(String key);
}
/**代理接口实现类*/
public class HelloImpl implements IHello {
@Override
public String hi(String key) {
String str = "hello:" + key;
System.out.println("HelloImpl! " + str);
return str;
}
}
/**jdk动态代理类*/
public class JdkProxy implements InvocationHandler {
private Object target;
public JdkProxy(Object target) {
this.target = target;
}
/**
* 获取被代理接口实例对象
*
* @param <T>
* @return
*/
public <T> T getProxy() {
return (T) Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), this);
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println(">>> JdkProxy start");
Object result = method.invoke(target, args);
System.out.println(">>> JdkProxy end");
return result;
}
}
/**测试*/
public class Demo {
public static void main(String[] args) {
JdkProxy proxy = new JdkProxy(new HelloImpl());
IHello helloProxy = proxy.getProxy();
helloProxy.hi(" jdk proxy !");
}
}
Cglib动态代理
基于字节码技术实现的代理机制,可以代理没有实现接口的类,可以对类的任意方法进行代理,需要导入cglib库。
原理是通过继承目标类或者通过目标类的接口生成子类,没有使用反射机制,性能相对较高。
final类无法被继承,所以无法被代理
引入cglib依赖
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib-nodep</artifactId>
<version>3.1.0</version>
</dependency>
代理示例
/**目标类*/
public class ServiceImpl {
public void doSomething() {
System.out.println("do something");
}
}
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
/**cglib代理类*/
public class CglibProxy {
public static <T> T getProxy(Class<?> clazz) {
// 创建一个 Enhancer 对象。
Enhancer enhancer = new Enhancer();
// 设置要代理的类。
enhancer.setSuperclass(clazz);
// 设置代理逻辑。
enhancer.setCallback((MethodInterceptor) (o, method, args, methodProxy) -> {
System.out.println("before do something");
// 调用原始对象的方法。
Object result = methodProxy.invokeSuper(o, args);
System.out.println("after do something");
return result;
});
// 创建代理对象。
return (T) enhancer.create();
}
}
/**测试*/
public class Main {
public static void main(String[] args) throws Exception {
// 创建一个 Service 对象。
ServiceImpl service = new ServiceImpl();
// 创建一个代理对象。
ServiceImpl proxy = CglibProxy.getProxy(service.getClass());
// 通过代理对象调用方法。
// proxy.getClass().getMethod("doSomething").invoke(proxy);
proxy.doSomething();
}
}
装饰器模式
装饰器模式用于增强对象的功能,可以向一个现有对象添加新功能,同时又不改变其结构。
具体应用
一个比较具体的应用实例是在使用Spring中的ThreadPoolTaskExecutor中,通过实现一个上下文装饰器,可以将主线程中的信息复制到线程池里面的线程。
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setTaskDecorator(new ContextDecorator());
// ContextDecorator
public class ContextDecorator implements TaskDecorator {
@Override
public Runnable decorate(Runnable runnable) {
RequestAttributes context;
try{
context = RequestContextHolder.currentRequestAttributes();
} catch (IllegalStateException e) {
context = new NonWebRequestAttributes();
}
RequestAttributes finalContext = context;
return () -> {
try {
RequestContextHolder.setRequestAttributes(finalContext);
runnable.run();
} finally {
RequestContextHolder.resetRequestAttributes();
}
};
}
}
装饰器示例
public interface Pizza {
public String getDescription();
public double getCost();
}
// 具体组件
public class PlainPizza implements Pizza {
public String getDescription() {
return "薄饼";
}
public double getCost() {
return 4.00;
}
}
// 装饰器
public abstract class ToppingDecorator implements Pizza {
protected Pizza pizza;
public ToppingDecorator(Pizza pizza) {
this.pizza = pizza;
}
public String getDescription() {
return pizza.getDescription();
}
public double getCost() {
return pizza.getCost();
}
}
// 具体装饰器
public class Cheese extends ToppingDecorator {
public Cheese(Pizza pizza) {
super(pizza);
}
public String getDescription() {
return pizza.getDescription() + ",马苏里拉奶酪";
}
public double getCost() {
return pizza.getCost() + 0.50;
}
}
策略模式
策略模式是一种行为设计模式,它可以定义一系列算法,然后再将每个算法封装起来。在运行时,根据需要选择相应的算法来完成任务。
结构
- Context(环境类):维护一个对策略对象的引用,在必要时会调用具体的策略对象来完成任务。
- Strategy(策略接口):定义了算法的统一接口,具体的算法实现类都实现了该接口。
- ConcreteStrategy(具体策略类):具体的算法实现类,实现了策略接口中定义的算法。
工作原理
- 环境类通过持有策略接口的引用,可以动态切换不同的具体策略类。
- 客户端在创建环境类时可以选择不同的具体策略类来传入,从而实现不同的算法行为
示例
// 定义一个排序的策略接口
interface SortStrategy {
void sort(int[] arr);
}
// 具体策略类 - 快速排序
class QuickSortStrategy implements SortStrategy {
@Override
public void sort(int[] arr) {
// 使用快速排序算法进行排序
}
}
// 具体策略类 - 冒泡排序
class BubbleSortStrategy implements SortStrategy {
@Override
public void sort(int[] arr) {
// 使用冒泡排序算法进行排序
}
}
// 包装类
class SortContext {
private SortStrategy strategy;
public SortContext(SortStrategy strategy) {
this.strategy = strategy;
}
public void sortArray(int[] arr) {
strategy.sort(arr);
}
}
// 客户端代码
class Demo {
public static void main(String[] args) {
int[] array = {5, 2, 8, 4, 1};
SortContext context = new SortContext(new QuickSortStrategy());
context.sortArray(array); // 使用快速排序算法排序
context = new SortContext(new BubbleSortStrategy());
context.sortArray(array); // 使用冒泡排序算法排序
}
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享4款.NET开源、免费、实用的商城系统
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
· 上周热点回顾(2.24-3.2)