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
保留了由构造器抛出的异常链,这有助于调试。