看不懂代码看设计模式

1、相关内容

是啥?

在这里插入图片描述在这里插入图片描述
1.2UML
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述在这里插入图片描述

在这里插入图片描述
在这里插入图片描述在这里插入图片描述

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

2创建者模式

在这里插入图片描述

2.1单例

2.1.1饿汉式
在这里插入图片描述在这里插入图片描述

//    私有构造别人无法给访问  
  private demo1 (){}
//    类自己创建自己的对象    
private static demo1 instance=new demo1();
//    公共访问的方式  
  public static demo1 getinstance()
    {
return  instance;//static访问static   
 }
---------------------------------------------
demo1 instance=demo1.getinstance();
demo1 instance2=demo1.getinstance();
System.out.println(instance2==instance);//true:同一块内存地址

在这里插入图片描述

private Singleton2(){}
private static  Singleton2 singleton2;//初始值:null
static {
singleton2=new Singleton2();
}
public static Singleton2 getinstance() {
return singleton2;
}

在这里插入图片描述
e’g:

单例类是一个数据库连接池管理器,需要在初始化时进行如下复杂的操作:
加载数据库驱动程序
建立数据库连接
进行连接池初始化配置
创建初始的连接对象

在这里插入图片描述2.1.2懒汉式

不直接new,用的时候没有再去new

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述


为什么这么做?
问题: 根源在于 Java 内存模型(JMM)中的指令重排序和对象的初始化顺序。在 Java 中,对象的创建和初始化不是原子操作。它包括以下几个步骤:
在这里插入图片描述
解决:
在Singleton类型的变量前加volatile修饰【及时将改动的变量在多个线程中共享】|线程安全且不会有性能问题【禁止了重排序】


在这里插入图片描述
在这里插入图片描述

// 使用静态内部类的单例
OuterClass.SingletonStaticInner instance = OuterClass.SingletonStaticInner.getInstance();
因为私有outer的构造所以只有一个outer.getinstance();

2.1.3恶汉式
在这里插入图片描述

因为在加载enum类型时会使用类加载器的机制确保只会加载一次,
同时enum类型的实例是在加载时被创建的,是不可变的
【类加载是指将.class文件中的字节码数据加载到内存中,并转换为JVM可以使用的Java类。在Java中,类加载器负责加载类的工作,将.class文件中的字节码数据加载到内存中,并对这些字节码数据进行解析、校验、准备和初始化,最终生成可以被JVM执行的Java类对象。】
​另外,enum实例的访问权限是public static final的,也就是说它是一个全局唯一的常量,不会被修改。

##2.2存在的问题
2.2.1
在这里插入图片描述
在这里插入图片描述
解决:重写readResolvee
在这里插入图片描述

2.2.2
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

2.3应用

private构造函数 静态类变量(实例化时间“懒汉、饿汉)| 给外界提供获取单例的方法
*
在这里插入图片描述

2工厂方法模式

在这里插入图片描述

在这里插入图片描述

constructor工厂模式:

  获取Singletond的字节码对象
        Class cla=Singleton7.class;
//        获取无参构造方法对象
        Constructor cons=cla.getDeclaredConstructor();
//        取消访问检查
        cons.setAccessible(true);
          创建对象
        Singleton7 s7=(Singleton7) cons.newInstance();

java.lang.reflect.Constructor源码解析
1、指明了这个类属于 Java 反射机制的一部分。
2、类声明 (public final class Constructor extends Executable) 说明了 Constructor 是一个公共的最终类,不可以被继承,并且它继承自 Executable 类。
3、 构造方法 (Constructor 类的构造方法) 用于创建这个类的实例。通常是包私有的,由 Java 内部的反射机制调用。
4、public T newInstance(Object … initargs) 方法用于通过反射创建类的实例。
5、
··泛型支持

Constructor 使用泛型来支持不同类型实例的创建。Constructor 类可以操作任意类型的构造器。泛型允许在不牺牲类型安全的情况下编写可重用和灵活的代码。

泛型的核心优势之一是保持类型安全。在 Java 中,类型安全意味着编译时就能检查到类型不匹配的错误如果你有一个 List<String>,泛型机制会阻止你往这个列表里添加非字符串类型的元素,从而避免了在列表使用时出现 ClassCastException。】

··类型安全

通过泛型和异常处理,确保了类型安全。
参数类型检查:getParameterTypes() 方法返回构造器的参数类型数组,这些类型在 newInstance() 方法中被用来校验传入参数的类型。
异常处理:newInstance(Object … initargs) 方法在实例化对象时会抛出 IllegalArgumentException,如果参数不匹配。
··性能优化

private volatile ConstructorAccessor constructorAccessor;

这行代码声明了一个 ConstructorAccessor 类型的私有成员变量,它的作用是优化构造器的调用。 作用:当 newInstance() 被调用时,首先检查是否存在 ConstructorAccessor,如果不存在,则创建一个并缓存,以便后续调用。

  • volatile 关键字 确保当 constructorAccessor 被一个线程修改后,新值对其他线程立即可见,这在多线程环境中很有用。

  • ConstructorAccessor 是一个内部类,用于高效地调用构造器。、

    newInstance() 方法被调用时,如果 constructorAccessor 尚未初始化,Java 反射机制会创建一个 ConstructorAccessor 对象并缓存它。它不是直接缓存对象实例,而是缓存构造器调用的逻辑,以便快速创建新实例。

    后续的调用将重用这个访问器,从而减少每次减少反射调用构造器时的性能开销。

    ConstructorAccessor 的使用是为了优化反射调用的性能,它并不关心单例模式的设计。ConstructorAccessor 用于存储构造方法的访问信息,以便快速创建对象实例。每次调用 newInstance() 时,如果已经存在 ConstructorAccessor,则直接使用它来创建实例,而不是每次都通过反射来查找和调用构造方法。

    外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

    T 是一个泛型参数,它代表了你想要通过反射创建的类的类型。

    newInstance 方法中的类型转换是为了确保返回的对象与 Constructor 对象的泛型类型参数 T 匹配。

    这个过程是安全的,因为 Constructor 对象是在编译时类型安全的。尽管 newInstance 方法返回 T 类型的实例,但在某些情况下,

    由于类型擦除,Java 泛型会在运行时被擦除为 Object 类型。因此,返回的结果需要被显式地转换为 T 类型,以满足方法的返回类型要求。

··反射工厂模式:**

通过 getFactory 和相关方法,实现了工厂模式,用于创建和管理反射对象。

。工厂模式是一种常用的设计模式,用于封装对象的创建过程。

  • 为什么使用工厂模式:工厂模式可以提供一个统一的接口来创建对象,隐藏具体的实现细节,同时提高代码的模块化和可维护性。

  • 处理流程:通常包括定义一个用于创建对象的接口(GenericsFactory 用于生成和管理这些反射对象的泛型信息。),然后实现具体的工厂类(CoreReflectionFactory),这些类负责根据提供的参数创建并返回对象。

    GenericsFactory 是一个用于生成和管理反射对象泛型信息的工厂类。它根据类的泛型签名来创建和提供泛型类型信息。这个过程涉及到解析类的泛型签名,并将这些信息封装成 ConstructorRepository 或其他类似的存储结构,以供后续使用。

  • 尽管 Constructor 对象代表了一个构造器,但它还需要管理与反射相关的其他方面,如泛型信息、注解等。GenericsFactory 用于生成和管理这些反射对象的泛型信息。

  • 什么是泛型信息?】:

    包括类型参数、实际类型参数的上下限等。

    private transient ConstructorRepository genericInfo;

    这行代码声明了一个用于存储构造器泛型信息的 ConstructorRepository 对象。

    • transient 关键字 表示该字段不会被序列化。

    • ConstructorRepository 是一个存储构造器泛型签名信息的容器(私有辅助类)。

      外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

    • volatile 关键字在这里确保 genericInfo 在多线程环境中的可见性和有序性,防止双重检查锁定(double-checked locking)问题。

    • getGenericInfo的作用:

      • 延迟初始化 genericInfo,只有当真正需要泛型信息时才创建 ConstructorRepository
      • 确保多线程环境下泛型信息的线程安全,通过 volatile 关键字实现。
      • 返回一个 ConstructorRepository 实例,它包含了构造器的泛型信息。

      外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

··访问控制

通过检查调用者的权限来实现访问控制。

权限检查:Reflection 检查:在 newInstance() 方法中,通过调用 Reflection.quickCheckMemberAccess()checkAccess() 来检查调用者是否有权限访问特定的构造器。

@CallerSensitive 注解 表示方法的实现对调用者敏感,即它会检查是谁调用了这个方法。Java 虚拟机提供了栈上的方法调用信息来确定调用者。

  • Java 虚拟机使用【调用栈】来存储方法调用信息,Reflection.getCallerClass() 方法可以获取调用者的信息。当一个方法被调用时,它的信息会被推送到调用栈上。Reflection.getCallerClass() 就是通过这个调用栈来获取调用者的信息。

6、
getDeclaringClass():返回声明该构造器的类。
getName():返回构造器声明的类的二进制名称。
getModifiers():返回构造器的访问修饰符。
getTypeParameters():返回构造器的泛型类型参数。
getParameterTypes():返回构造器的参数类型数组。
getExceptionTypes():返回构造器声明的异常类型数组。
newInstance(Object … initargs):使用构造器创建类的实例。如果参数不匹配或构造器不可访问,会抛出异常。

newInstance(Object … initargs)

newInstance(Object ... initargs) 方法的参数 Object ... initargs 是一个可变参数,表示你可以传递任意数量的参数来匹配构造器的参数列表。

  • 这些参数在调用构造器之前会进行类型检查和转换(如自动拆箱和装箱)。

  • 这些参数 initargs 需要与构造器的参数类型相匹配。如果参数不匹配,将抛出 IllegalArgumentException

  • 反射调用构造器时需要传递参数,因为构造器可能需要初始化对象的字段。

7、

  • 泛型信息的延迟初始化getGenericInfo() 方法确保泛型信息只有在需要时【等于null的时候,有点像单例模式】才被解析和创建,这有助于性能。
  • 构造器访问器的共享:通过 ConstructorAccessor 缓存和 root 引用,多个 Constructor 对象可以共享同一个访问器,减少了资源消耗。
  • 异常链:在 newInstance() 中,通过 InvocationTargetException 保留了由构造器抛出的异常链,这有助于调试。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值