7、反射机制

反射的理解

Reflection(反射)被视为动态语言的关键,反射机制允许程序在**执行期(Runtime)**借助于Reflection API取得任何类的内部信息,并能直接操作任意对象的内部属性及方法。

框架 = 反射 + 注解 + 设计模式

反射机制能提供的功能

Class类的理解

java.lang.Class

类的加载及反射过程如下

获取Class实例的几种方式

方式一:调用运行时类的属性:class

Class clazz = ClassName.class;

方式二:通过运行时类的对象,调用getClass()方法

Person p = new Person();
Class clazz = p.getClass();

方式三:调用Class的静态方法:forName(String classPath),可能抛出ClassNotFoundException异常

全类名:是类所属包名 + . + 类名,例如java.lang.String

Class clazz = Class.forName("全类名");

方式四:使用类的加载器

ClassLoader cl = this.getClass().getClassLoader(); 
Class clazz = cl.loadClass("全类名");

创建类的对象的方式

方式一:构造方法,new Constructor()

方式二:要创建Xxx类的对象,可以考虑:XxxXxxsXxxFactoryXxxBuilder类中查看是否有静态方法的存在。可以调用其静态方法,创建Xxx对象。

方式三:通过反射

Class实例可以是哪些结构的说明

数组的Class实例中,只要类型和维度一样,那么两个Class实例相等

int[] a = new int[10];
int[] b = new int[100];
a.class == b.class
//结果为true

Class类的常用方法

通过Class实例创建类实例

Class<Person> clazz = Person.class;
Person obj = clazz.newInstance();

newInstance():调用此方法,创建对应的运行时类的对象。内部调用了运行时类的空参的构造器

使用此方法,要求:

  1. 运行时类必须提供空参的构造器
  2. 空参的构造器的访问权限足够。通常,设置为public。

没有无参的构造器就不能创建对象了吗?

**不是!**只要在操作的时候明确的调用类中的构造器,并将参数传递进去之后,才可以实例化操作。

  1. 通过Class类的getDeclaredConstructor(Class … parameterTypes)取得本类的指定参数类型的构造器
  2. 通过Constructor实例化对象。
static class Apple{
	public Apple(int a){ }
}

public static void main(String[] args) throws Exception {
    // 获取Class对象
    Class<Apple> appleClass = Apple.class;
    // 根据参数获取有参构造
    Constructor<Apple> declaredConstructor = appleClass.getDeclaredConstructor(int.class);
    // 设置可见性
    declaredConstructor.setAccessible(true);
    // 创建实例
    Apple apple = declaredConstructor.newInstance(1);
}

获取类结构的方法

接口、父类

构造器

Constructor类

修饰符 返回值
public 1
private 2
protected 4
static 8
final 16
synchronized 32
volatile 64
transient 128
native 256
interface 512
abstract 1024
strict 2048

方法

Method类

属性

Field类

注解

泛型

static class MyList extends ArrayList<Number> {

}

public static void main(String[] args) {
    Class<MyList> myListClass = MyList.class;

    Class<? super MyList> superclass = myListClass.getSuperclass();
    System.out.println(superclass.getName()); // java.util.ArrayList

    Type genericSuperclass = myListClass.getGenericSuperclass();
    System.out.println(genericSuperclass.getTypeName()); // java.util.ArrayList<java.lang.Number>
}

所在包

调用类的指定结构

public class Person{
    
    private String name;
    
    public Person(String name){
        this.name = name;
    }
    
    public String show(){
        System.out.println(name);
    }
}

关于setAccessible方法的使用

调用方法

//一、获取类的Class实例、并获得运行时类的实例
Class clazz = Person.class;
Person p = (Person) clazz.newInstance();
//二、获取需要调用的方法,getDeclaredMethod():
//参数1 :指明获取的方法的名称  参数2:指明获取的方法的形参列表
Method show = clazz.getDeclaredMethod("show", String.class);
//三、保证当前方法可以访问
show.setAccessible(true);
//四、调用方法的invoke()
//参数1:方法的调用者  参数2:给方法形参赋值的实参
//invoke()的返回值即为对应类中调用的方法的返回值。
Object returnValue = show.invoke(p,"CHN");

调用属性

Class clazz = Person.class;
//创建运行时类的对象
Person p = (Person) clazz.newInstance();
//1. getDeclaredField(String fieldName):获取运行时类中指定变量名的属性
Field name = clazz.getDeclaredField("name");
//2.保证当前属性是可访问的
name.setAccessible(true);
//3.设置指定对象的此属性值
name.set(p,"Tom");
//4、获取指定对象的此属性值
System.out.println(name.get(p));

调用指定的构造器

Class clazz = Person.class;
//1.获取指定的构造器
//getDeclaredConstructor():参数:指明构造器的参数列表
Constructor constructor = clazz.getDeclaredConstructor(String.class);
//2.保证此构造器是可访问的
constructor.setAccessible(true);
//3.调用此构造器创建运行时类的对象,此时才会进行类加载
Person per = (Person) constructor.newInstance("Tom");