Java单例模式详解
一、引言
单例模式是设计模式中的一种,属于创建型模式。在软件工程中,单例模式确保一个类只有一个实例,并提供一个全局访问点。这种模式常用于那些需要频繁实例化然后引用,且创建新实例的开销较大的类,例如数据库连接池、缓存管理等。
二、单例模式定义
意图:保证一个类仅有一个实例,并提供一个访问它的全局访问点。
主要解决:一个全局使用的类频繁地创建与销毁所造成的资源浪费问题。
何时使用:当您想控制实例数目,节省系统资源的时候。
如何解决:判断系统是否已经有这个单例,如果有则返回,如果没有则创建。
关键代码:构造函数是私有的。
三、Java实现单例模式的几种方式
- 懒汉式(线程不安全)
public class Singleton {private static Singleton instance;private Singleton() {}public static Singleton getInstance() {if (instance == null) {instance = new Singleton();}return instance;}
}
这种方式在多线程环境下不能保证单例,因为多个线程可能同时进入if条件判断内部,导致创建多个实例。
- 懒汉式(线程安全,同步方法)
public class Singleton {private static Singleton instance;private Singleton() {}public static synchronized Singleton getInstance() {if (instance == null) {instance = new Singleton();}return instance;}
}
通过在getInstance方法上加synchronized关键字使其变为同步方法,解决了多线程环境下的安全性问题,但每次获取实例都要进行同步,性能损耗较大。
- 懒汉式(线程安全,双重检查锁定 - DCL,推荐使用)
public class Singleton {private volatile static Singleton instance;private Singleton() {}public static Singleton getInstance() {if (instance == null) {synchronized (Singleton.class) {if (instance == null) {instance = new Singleton();}}}return instance;}
}
双重检查锁定机制,首先判断实例是否存在,若不存在才进行同步处理,这样既保证了线程安全,又避免了每次都进行同步带来的性能损耗。
- 饿汉式(静态内部类)
public class Singleton {private Singleton() {}private static class SingletonHolder {private static final Singleton INSTANCE = new Singleton();}public static Singleton getInstance() {return SingletonHolder.INSTANCE;}
}
静态内部类的方式在类加载时就完成了初始化,既保证了线程安全,也避免了同步带来的性能影响,同时也能保证单例对象的唯一性。
- 枚举式(最安全,推荐使用)
public enum Singleton {INSTANCE;public void whateverMethod() {// ...}
}
枚举类型的单例模式不仅能防止反射和反序列化的攻击,而且写法简单,易于理解,是实现单例的最佳选择。
四、注意事项
- 单例模式虽然能节约系统资源,但也可能造成全局状态过多,增加系统的复杂度。
- 对于可序列化类的单例模式,需要重写readResolve方法以防止反序列化时生成新的对象。
以上就是Java单例模式的详细解读,实际开发过程中应根据具体场景选择合适的实现方式。
Java工厂设计模式详解
一、引言
工厂模式是面向对象设计模式中的一种,主要用来解决对象创建的问题,封装了对象的创建过程,使客户端不需要知道具体的实现类就能创建所需的对象。根据抽象程度的不同,工厂模式分为简单工厂模式、工厂方法模式和抽象工厂模式。
二、工厂模式分类与定义
- 简单工厂模式(Simple Factory Pattern)
意图:提供一个用于创建对象的接口,让子类决定实例化哪一个类。
结构:由一个工厂类负责创建不同类的产品对象。
// 简单工厂角色
public class ShapeFactory {public Shape getShape(String type) {if ("CIRCLE".equals(type)) {return new Circle();} else if ("RECTANGLE".equals(type)) {return new Rectangle();} else if ("SQUARE".equals(type)) {return new Square();} else {throw new IllegalArgumentException("Invalid shape type");}}
}// 抽象产品角色
abstract class Shape {abstract void draw();
}// 具体产品角色
class Circle extends Shape {@Overridevoid draw() {System.out.println("Drawing a circle...");}
}// 其他具体产品类...
- 工厂方法模式(Factory Method Pattern)
意图:定义一个用于创建对象的接口,但让子类决定实例化哪个类。工厂方法使一个类的实例化延迟到其子类。
结构:定义一个创建产品对象的接口,让子类决定生产什么产品。
// 抽象工厂角色
interface ShapeFactory {Shape createShape();
}// 具体工厂角色
class CircleFactory implements ShapeFactory {@OverrideShape createShape() {return new Circle();}
}// 其他具体工厂类...// 抽象产品角色
abstract class Shape {abstract void draw();
}// 具体产品角色
class Circle extends Shape {@Overridevoid draw() {System.out.println("Drawing a circle...");}
}// 其他具体产品类...
- 抽象工厂模式(Abstract Factory Pattern)
意图:提供一个创建一系列相关或相互依赖对象的接口,而无需指定它们的具体类。
结构:提供一个接口,用于创建相关或相互依赖对象家族的一系列对象,而无需指定具体类。
// 抽象工厂角色
interface ColorFactory {Color getColor();
}interface ShapeFactory {Shape getShape();
}// 具体工厂角色
class RedColorFactory implements ColorFactory {@OverrideColor getColor() {return new RedColor();}
}class CircleShapeFactory implements ShapeFactory {@OverrideShape getShape() {return new Circle();}
}// 抽象产品角色
interface Color {void fill();
}interface Shape {void draw();
}// 具体产品角色
class RedColor implements Color {@Overridevoid fill() {System.out.println("Filling with red color...");}
}class Circle implements Shape {@Overridevoid draw() {System.out.println("Drawing a circle...");}
}// 其他具体产品类...
三、工厂模式优缺点
-
优点
- 将对象的创建和使用分离,使得系统耦合度降低。
- 提高代码的可扩展性,增加新的产品类型时只需要添加新的工厂或产品类,不影响已有的代码。
-
缺点
- 当产品种类较多时,会导致类爆炸,因为每新增一种产品就需要新增对应的工厂类。
- 违反开闭原则,如果需要新增一个产品,除了新增产品类外,可能还需要修改工厂类或抽象工厂类。
四、适用场景
- 当一个类不知道它所必须创建的对象的确切类时。
- 当一个类希望它的使用者指定它所创建的对象类型时。
- 当类将职责委托给多个帮助子类中的某一个,并且用户希望可以动态地决定这个职责委托给哪个子类时。
通过上述介绍,您应该对Java中的工厂设计模式有了较为深入的理解。在实际开发中,可以根据需求选择合适的工厂模式来组织代码,提高程序的灵活性和可维护性。
Java代理设计模式详解
一、引言
代理设计模式(Proxy Design Pattern)是软件设计中常用的行为型设计模式之一,它为其他对象提供一个代理以控制对这个对象的访问。在Java编程中,代理模式的主要目的是为了在不改变原始目标类代码的基础上,通过引入一个额外的代理类来扩展或增强原有对象的功能。
1.1 定义
代理模式定义如下:
代理模式:给某个对象提供一个代理,并由代理对象控制对原对象的引用。代理模式能够在客户端与目标对象之间增加一层间接层,从而实现对目标对象功能的增强、控制或者过滤。
1.2 模式结构
- Subject(抽象主题): 定义了真实主题和代理主题的公共接口,这样代理可以作为真实主题的替代品。
- RealSubject(真实主题): 实际完成工作的类,也是代理所代表的真实对象。
- Proxy(代理): 保存一个对真实主题的引用,并且提供一个与真实主题相同的接口以便于在任何时候都可以将请求转发给真实主题处理;同时,在转发请求前或后执行附加操作。
1.3 应用场景
- 访问控制:如权限验证,只有经过代理对象确认之后才能访问真实对象的方法。
- 功能增强:例如在方法调用前后添加日志记录、事务管理等。
- 虚拟代理:延迟加载,如图片预加载、数据库连接池中的连接对象创建等。
- 远程代理:用于分布式系统中,代理对象负责与远程对象通信。
- 缓存代理:缓存结果以提高性能,当请求数据时先检查代理是否有缓存的结果。
- AOP(面向切面编程):Spring框架AOP模块底层就使用了动态代理技术。
二、Java中的代理实现方式
在Java中,代理主要分为两种类型:
2.1 静态代理
静态代理是在编译期间就已经确定代理类,代理类和被代理类的关系在代码中硬编码。
public interface Subject {void request();
}public class RealSubject implements Subject {@Overridepublic void request() {// 真实对象的实际业务逻辑}
}public class ProxySubject implements Subject {private RealSubject realSubject;public ProxySubject(RealSubject realSubject) {this.realSubject = realSubject;}@Overridepublic void request() {beforeRequest(); // 在调用实际方法前的操作realSubject.request();afterRequest(); // 在调用实际方法后的操作}private void beforeRequest() {// 添加前置逻辑,比如权限校验}private void afterRequest() {// 添加后置逻辑,比如日志记录}
}
2.2 动态代理
Java提供了动态生成代理类的技术,主要有两种实现机制:
2.2.1 JDK动态代理
JDK动态代理基于接口实现,通过java.lang.reflect.Proxy
类和InvocationHandler
接口。
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;public class JdkProxy implements InvocationHandler {private Object target;public Object bind(Object target) {this.target = target;return Proxy.newProxyInstance(target.getClass().getClassLoader(),target.getClass().getInterfaces(),this);}@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {beforeInvoke(method);Object result = method.invoke(target, args);afterInvoke(method);return result;}private void beforeInvoke(Method method) {// 前置处理}private void afterInvoke(Method method) {// 后置处理}
}// 使用示例
RealSubject realSubject = new RealSubject();
Subject proxy = (Subject) new JdkProxy().bind(realSubject);
proxy.request();
2.2.2 CGLIB代理
CGLIB库是一个强大的高性能代码生成库,可以在运行期扩展Java类与实现Java接口。它可以创建出一个被代理类的子类,因此不需要接口也能进行代理。CGLIB常用于Spring AOP框架中,对于没有实现接口的目标类也能提供代理支持。
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;public class CglibProxy implements MethodInterceptor {@Overridepublic Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {beforeInvoke(method);Object result = proxy.invokeSuper(obj, args);afterInvoke(method);return result;}private void beforeInvoke(Method method) {// 前置处理}private void afterInvoke(Method method) {// 后置处理}public Object createProxy(Class<?> clazz) {Enhancer enhancer = new Enhancer();enhancer.setSuperclass(clazz);enhancer.setCallback(this);return enhancer.create();}// 使用示例RealSubject realSubject = (RealSubject) new CglibProxy().createProxy(RealSubject.class);((Subject) realSubject).request();
}// 注意:这里假设RealSubject并未实现任何接口
三、总结
Java代理设计模式是一种重要的设计思想,它帮助开发者在不影响原始类的情况下,对类的行为进行扩展和控制。在实际开发中,无论是通过静态代理还是动态代理,都能在很多场合发挥重要作用,尤其是在框架设计、服务治理、性能优化以及AOP等领域得到广泛应用。学习并掌握代理模式有助于提升代码的灵活性和可维护性。