Java设计模式
Java设计模式是一种抽象化的思维方式,其目的是为了帮助开发人员更好地组织和设计代码,解决各种不同软件设计问题。设计模式不是完整的代码,而是一种描述问题和解决方案之间关系的模板。
1. 分类
设计模式主要分为三种类型
1.创建型模式:处理对象的创建机制,包括:单例模式、工厂模式、抽象工厂模式、建造者模式和原型模式;
2.结构型模式:处理类和对象的组合方式,包括:适配器模式、装饰器模式、代理模式、桥接模式、外观模式、组合模式和享元模式;
3.行为型模式:处理对象之间的通信和职责分配,包括:观察者模式、策略模式、命令模式、模板方法模式、迭代器模式、访问者模式、中介者模式、备忘录模式和解释器模式;
2. 常用设计模式的详细介绍(创建型模式)
2.1 单例模式(Singleton Pattern)
**定义:**确保一个类只有一个实例,并提供一个全局访问点。
**原理:**通过私有化构造函数和提供一个静态方法来获取实例。
优点:
- 控制实例数量:保证只有一个实例
- 提供全局访问点:方便在全局范围内访问该实例
Java演示:
面试中面试官经常会说:“单例模式了解吗?手写一个单例模式,解释一下双重检验锁方式实现单例模式的原理呗!”
双重校验锁实现对象单例(线程安全):
/**
* 双重校验锁实现对象单例(线程安全)
*/
public class Singleton {
//类加载时不创建实例
private static volatile Singleton uniqueInstance;
//私有构造函数,防止外部实例化
private Singleton(){};
//提供全局访问点,使用双重校验锁保证线程安全
public static Singleton getInstance(){
//先判断对象是否已经实例过,没有实例化过才进入加锁代码
if (uniqueInstance == null){
synchronized (Singleton.class){
if (uniqueInstance == null){
uniqueInstance = new Singleton();
}
}
}
return uniqueInstance;
}
}
uniqueInstance采用volatile关键字修饰也是很有必要的,uniqueInstance = new Singleton();这段代码其实是分为三步执行:
- 为 uniqueInstance 分配内存空间
- 初始化 uniqueInstance
- 将 uniqueInstance指向分配的内存地址
但是由于 JVM 具有指令重排的特性,执行顺序有可能变成1->3->2。指令重排在单线程环境下不会出现问题,但是在多线程环境下会导致一个线程获得还没有初始化的实例。例如,线程T1执行了1和3,此时T2调用 getUniqueInstance()后发现uniqueInstance不为空,因此返回uniqueInstance,但此时的uniqueInstance还未被初始化。
在 Java 中,volatile 关键字除了可以保证变量的可见性,还有一个重要的作用就是防止 JVM 的指令重排序。 如果我们将变量声明为 ,在对这个变量进行读写操作的时候,会通过插入特定的 的方式来禁止指令重排序。
2.2 原型模式(Prototype Pattern)
定义:通过复制现有的实例来创建新实例,而不是通过构造函数
原理:实现Cloneable接口并重写clone方法来复制对象
优点:
- 减少创建新对象的开销:通过复制现有对象创建新对象
- 动态配置对象:可以在运行时配置对象状态。
Java演示:
//原型接口
public interface Prototype extends Cloneable {
Prototype clone();
}
//具体原形类
public class ConcretePrototype implements Prototype {
private String data;
public ConcretePrototype(String data) {
this.data = data;
};
//使用浅拷贝
@Override
public Prototype clone() {
try {
return (Prototype) super.clone();
} catch (CloneNotSupportedException e) {
// 处理克隆不支持异常
return null;
}
}
public String getData() {
return data;
}
public void setData(String data) {
this.data = data;
}
}
// 客户端代码
public class Client {
public static void main(String[] args) {
ConcretePrototype prototype = new ConcretePrototype("Prototype Data");
ConcretePrototype clonedPrototype = (ConcretePrototype) prototype.clone();
System.out.println(clonedPrototype.getData()); // 输出: Prototype Data
clonedPrototype.setData("Modified Data");
System.out.println(prototype.getData()); // 输出: Prototype Data
System.out.println(clonedPrototype.getData()); // 输出: Modified Data
}
}
2.3 建造者模式(Builder Pattern)
定义:使用多个简单的对象一步一步构建一个复杂的对象
原理:定义一个建造者接口和具体建造者类,通过建造者类来创建复杂对象。
优点:
- 解耦:将复杂对象的构建与表示解耦
- 灵活性:可以根据需要创建不同表示的复杂对象
Java演示
// 产品类
public class Product {
private String partA;
private String partB;
// 构造函数
public Product(String partA, String partB) {
this.partA = partA;
this.partB = partB;
}
@Override
public String toString() {
return "Product [partA=" + partA + ", partB=" + partB + "]";
}
}
// 建造者接口
public interface Builder {
void buildPartA();
void buildPartB();
Product getResult();
}
// 具体建造者类
public class ConcreteBuilder implements Builder {
private String partA;
private String partB;
@Override
public void buildPartA() {
partA = "Part A";
}
@Override
public void buildPartB() {
partB = "Part B";
}
@Override
public Product getResult() {
return new Product(partA, partB);
}
}
// 指导者类
public class Director {
private Builder builder;
public Director(Builder builder) {
this.builder = builder;
}
public void construct() {
builder.buildPartA();
builder.buildPartB();
}
}
// 客户端代码
public class Client {
public static void main(String[] args) {
Builder builder = new ConcreteBuilder();
Director director = new Director(builder);
director.construct();
Product product = builder.getResult();
System.out.println(product);
}
}
2.4 工厂方法模式(Factory Method Pattern)
定义:定义一个创建对象的接口,但由子类决定实例化哪个类
原理:将对象的创建逻辑放在子类中,而不是在客户端代码中
优点:
- 灵活性:可以在运行时决定创建对象的类型
- 符合开闭原则:对扩展开放,对修改关闭
Java演示
// 产品接口
public interface Product {
void operation();
}
// 具体产品A
public class ConcreteProductA implements Product {
@Override
public void operation() {
System.out.println("ConcreteProductA operation");
}
}
// 具体产品B
public class ConcreteProductB implements Product {
@Override
public void operation() {
System.out.println("ConcreteProductB operation");
}
}
// 工厂接口
public abstract class Creator {
public abstract Product factoryMethod();
}
// 具体工厂A
public class ConcreteCreatorA extends Creator {
@Override
public Product factoryMethod() {
return new ConcreteProductA();
}
}
// 具体工厂B
public class ConcreteCreatorB extends Creator {
@Override
public Product factoryMethod() {
return new ConcreteProductB();
}
}
// 客户端代码
public class Client {
public static void main(String[] args) {
Creator creator = new ConcreteCreatorA();
Product product = creator.factoryMethod();
product.operation();
}
}
2.5 抽象工厂模式(Abstract Factory Pattern)
定义:提供一个创建一系列相关或相互依赖对象的接口,而无需指定它们具体的类
原理:通过定义多个工厂接口,每个工厂负责创建一组相关的对象
优点:
- 一致性:确保创建的一系列对象具有一致性
- 扩展性:易于扩展产品系列,而不影响现有代码
Java演示
// 产品A接口
public interface ProductA {
void operationA();
}
// 产品B接口
public interface ProductB {
void operationB();
}
// 具体产品A1
public class ConcreteProductA1 implements ProductA {
@Override
public void operationA() {
System.out.println("ConcreteProductA1 operationA");
}
}
// 具体产品B1
public class ConcreteProductB1 implements ProductB {
@Override
public void operationB() {
System.out.println("ConcreteProductB1 operationB");
}
}
// 具体产品A2
public class ConcreteProductA2 implements ProductA {
@Override
public void operationA() {
System.out.println("ConcreteProductA2 operationA");
}
}
// 具体产品B2
public class ConcreteProductB2 implements ProductB {
@Override
public void operationB() {
System.out.println("ConcreteProductB2 operationB");
}
}
// 抽象工厂接口
public interface AbstractFactory {
ProductA createProductA();
ProductB createProductB();
}
// 具体工厂1
public class ConcreteFactory1 implements AbstractFactory {
@Override
public ProductA createProductA() {
return new ConcreteProductA1();
}
@Override
public ProductB createProductB() {
return new ConcreteProductB1();
}
}
// 具体工厂2
public class ConcreteFactory2 implements AbstractFactory {
@Override
public ProductA createProductA() {
return new ConcreteProductA2();
}
@Override
public ProductB createProductB() {
return new ConcreteProductB2();
}
}
// 客户端代码
public class Client {
public static void main(String[] args) {
AbstractFactory factory = new ConcreteFactory1();
ProductA productA = factory.createProductA();
ProductB productB = factory.createProductB();
productA.operationA();
productB.operationB();
}
}
3. 常用设计模式的详细介绍(结构型模式)
3.1 适配器模式(Adapter Pattern)
定义:将一个类的接口转换成客户希望的另一个接口。适配器模式使得原本接口不兼容的类可以合作。
原理:通过引入一个适配器类,将目标接口转化为适配者接口
优点:
- 接口兼容:使得接口不兼容的类可以协作
- 复用性:可以复用现有的类。
Java演示
// 目标接口
public interface Target {
void request();
}
// 适配者类
public class Adaptee {
public void specificRequest() {
System.out.println("SpecificRequest");
}
}
// 适配器类
public class Adapter implements Target {
private Adaptee adaptee;
public Adapter(Adaptee adaptee) {
this.adaptee = adaptee;
}
@Override
public void request() {
adaptee.specificRequest(); // 适配方法
}
}
// 客户端代码
public class Client {
public static void main(String[] args) {
Adaptee adaptee = new Adaptee();
Target target = new Adapter(adaptee);
target.request(); // 通过适配器调用
}
}
3.2 装饰器模式(Decorator Pattern)
定义:动态地给一个对象添加一些额外的职责。装饰器模式提供了比继承更灵活的扩展方式
原理:通过定义装饰类来扩展被装饰对象的功能
优点:
- 灵活性:可以动态的扩展对象的功能
- 避免子类爆炸:通过装饰器而不是继承来扩展功能
Java演示:
// 组件接口
public interface Component {
void operation();
}
// 具体组件
public class ConcreteComponent implements Component {
@Override
public void operation() {
System.out.println("ConcreteComponent operation");
}
}
// 装饰器抽象类
public abstract class Decorator implements Component {
protected Component component;
public Decorator(Component component) {
this.component = component;
}
@Override
public void operation() {
component.operation();
}
}
// 具体装饰器
public class ConcreteDecorator extends Decorator {
public ConcreteDecorator(Component component) {
super(component);
}
@Override
public void operation() {
super.operation();
addedBehavior();
}
private void addedBehavior() {
System.out.println("ConcreteDecorator addedBehavior");
}
}
// 客户端代码
public class Client {
public static void main(String[] args) {
Component component = new ConcreteComponent();
Component decorator = new ConcreteDecorator(component);
decorator.operation(); // 执行装饰后的操作
}
}
3.3 代理模式(Proxy Pattern)
定义:为其他对象提供一种代理以控制对这个对象的访问
原理:通过定义代理类来控制对真实对象的访问
优点:
- 控制访问:可以在代理中实现对真实对象的控制
- 增强功能:可以在代理类中增加额外的功能,比如延迟加载
这里有两种代理模式:一种是静态代理,一种是动态代理
静态代理:
- 在编译时就已经确定了代理类的实现
- 代理类需要实现对目标类相同的接口,并持有目标对象的引用
- 在代理类中实现对目标方法的增强
- 优点是实现简单,可以很好的控制目标对象的行为。缺点是每个目标类都需要创建一个代理类,造成代码冗余。
静态代理Java演示
//目标接口
public interface Product {
void description();
}
//目标接口实现类
public class ProductImp implements Product {
@Override
public void description() {
System.out.println("产品。。。");
}
}
//静态代理代理类
public class StaticProxy implements Product{
//目标对象的引用
private ProductImp productImp;
public StaticProxy(ProductImp productImp){
this.productImp = productImp;
}
//代理的方法,可在其中对方法进行增强或者修改
@Override
public void description() {
System.out.println("静态代理增强方法");
productImp.description();
System.out.println("静态代理增强方法");
}
}
//客户端代码
public class Client {
public static void main(String[] args) {
ProductImp productImp =new ProductImp();
StaticProxy staticProxy = new StaticProxy(productImp);
staticProxy.description();
}
}
动态代理:
- 在运行时动态生成代理类的实现
- 不需要事先知道要目标类的具体实现,而是通过反射机制在运行时动态生成代理类
- 动态代理需要实现InvocationHandler接口,在invoke()方法中实现对目标方法的增强或者修改
- 优点是实现灵活,可以针对不同的目标对象生成代理类,减少了代码冗余。缺点是实现相对复杂,需要一定的反射知识。
动态代理主要通过InvocationHandler(接口)和Proxy(类),下面对两个进行介绍:
InvocationHandler(接口):
InvocationHandler接口是proxy代理实例的调用处理程序实现的一个接口,每一个proxy代理实例都有一个关联的调用处理程序;在代理实例调用方法时,方法调用被编码分派到调用处理程序的invoke方法,每一个动态代理类的调用处理程序都必须实现InvocationHandler接口,并且每个代理类的实例都关联到了实现该接口的动态代理类调用处理程序中,当我们通过动态代理对象调用一个方法时候,这个方法的调用就会被转发到实现InvocationHandler接口类的invoke方法来调用。
Proxy(类):
Proxy类就是用来创建一个代理对象的类,它提供了很多方法,但是我们最常用的是newProxyInstance方法。
public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)
这个方法的作用就是创建一个代理类对象,它接收三个参数:
- ClassLoader :一个类加载器对象,定义了由哪个classloader对象来加载生成的代理类
- Class<?>[]:一个接口对象数组,表示我们将要给我们的代理对象提供一组什么样的接口,相当于代理类实现了这些接口,代理类就可以调用接口中声明的所有方法。
- InvocationHandler :一个InvocationHandler对象,表示的是当动态代理对象(需要执行代理方法的类)调用方法的时候会关联到哪一个InvocationHandler对象上,并最终由这个InvocationHandler对象调用。
动态代理Java演示:
//目标接口
public interface Subject {
void doSomething();
void doRun();
}
//目标接口实现类
public class SubjectImp implements Subject{
@Override
public void doSomething() {
System.out.println("Subject的doSomething方法");
}
@Override
public void doRun() {
System.out.println("Subject的doRun方法");
}
}
//动态代理实现类
//需要继承InvocationHandler接口,重写invoke()方法
public class DynamicProxy implements InvocationHandler {
//导入任意对象引用
private Object target;
public DynamicProxy(Object target){
this.target = target;
}
//动态代理invoke方法
/**
*
* @param proxy 代理对象本身的引用。
* @param method 被调用的方法对象,它包含了方法名、参数类型、返回类型等信息
* @param args 被调用方法的参数数组。
* @return
* @throws Throwable
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("动态代理增强方法");
Object result = method.invoke(this.target, args);
System.out.println("动态代理增强方法");
return result;
}
}
//客户端调用
public class Client {
public static void main(String[] args) {
SubjectImp subjectImp = new SubjectImp();
/**
* 调用Proxy.newProxyInstance实现了动态代理,其中第一个参数无需太多关注,
* 第二个参数为一个接口对象数组,对应的就是返回的方法中提供了哪些接口方法可以被调用,
* 最后一个参数就是我们需要去调用的动态方法。
*/
Subject proxy = (Subject)Proxy.newProxyInstance(SubjectImp.class.getClassLoader(),
new Class[]{Subject.class},
new DynamicProxy(subjectImp));
proxy.doSomething();
proxy.doRun();
}
}
后续更新中。。。
4465

被折叠的 条评论
为什么被折叠?



