跳过正文
  1. 文章/
  2. Java/
  3. JavaSE/
  4. JavaSE高级/

7、反射机制

·3193 字·7 分钟· loading · loading · ·
Java JavaSE JavaSE高级
GradyYoung
作者
GradyYoung
JavaSE高级 - 点击查看当前系列文章
§ 7、反射机制 「 当前文章 」

反射的理解
#

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

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

反射机制能提供的功能
#

  • 在运行时判断任意一个对象所属的类
  • 在运行时构造任意一个类的对象
  • 在运行时判断任意一个类所具有的成员变量和方法
  • 在运行时获取泛型信息
  • 在运行时调用任意一个对象的成员变量和方法
  • 在运行时处理注解
  • 生成动态代理

Class类的理解
#

java.lang.Class

类的加载及反射过程如下

  • 程序经过javac.exe命令编译以后,会生成一个或多个字节码文件(.class结尾)。

  • 接着我们使用java.exe命令对某个字节码文件进行解释运行。字节码文件被ClassLoader加载到JVM内存中,此过程就称为类的加载。加载到内存中的类,我们就称为运行时类(Runtime Class),此运行时类,就作为java.lang.Class的一个实例。

    • 换句话说,Class的实例只对应着加载到内存中的一个运行时类。

    • 加载到内存中的运行时类,会缓存一定的时间。在此时间之内,我们可以通过不同的方式来获取此运行时类。

    • 一个加载的类在 JVM 中只会有一个Class实例

  • 此时就可以通过Class类的实例方法获取FieldMethodConstructor等实例对象完成反射

获取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:类

  • interface:接口

  • []:数组

  • enum:枚举

  • annotation:注解@interface

  • primitive type:基本数据类型

  • void

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

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

Class类的常用方法
#

  • static Class forName(String name):根据全类名返回Class对象
  • Object newInstance():调用默认无参构造,创建实例
  • getName():获取Class所代表的结构全类名
  • get Class[] getInterfaces():返回Class对象实现的所有接口的Class对象
  • ClassLoader getClassLoader():返回该类的类加载器
  • Class getSuperclass():返回Class对象的超类Class对象
  • Constuctor[] getConstrctors():返回类的所有构造方法
  • Field[] getDeciaredFields():返回所有属性
  • Method getMethod(String name,Class ... paramTypes):返回该类中参数为Class ... paramTypes的方法
  • Package getPackage():获取包信息

通过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);
}

获取类结构的方法
#

接口、父类
#

  • Class<?>[] getInterfaces():获取实现的全部接口
  • Class<? Super T> getSuperclass():获取继承的父类

构造器
#

  • Constructor<T>[] getConstructors():获取有所public构造方法
  • Constructor<T>[] getDeclaredConstructors():获取所有构造方法

Constructor类
#

  • int getModifiers():获取所有修饰符之和,例如public static,返回值为1 + 8 = 9
  • String getName():获取方法名称
  • Class<?>[] getParameterTypes():获取参数类型
修饰符 返回值
public 1
private 2
protected 4
static 8
final 16
synchronized 32
volatile 64
transient 128
native 256
interface 512
abstract 1024
strict 2048

方法
#

  • Methos[] getDeclaredMethods():获取所有方法
  • Methos[] getMethos():获取所有public方法

Method类
#

  • Class<?> getReturnType():获取返回值类型对象
  • Class<?>[] getParameterTypes():获取所有参数类型对象
  • int getModifiers():获取修饰符
  • Class<?>[] getExceptionTypes():获取抛出异常类对象

属性
#

  • Field[] getFields():获取所有public的属性
  • Field[] getDeclaredFields():获取所有属性

Field类
#

  • int getModifiers():获取修饰符
  • Class<?> getType():获取属性类型对象
  • String getName():获取属性名称

注解
#

  • <A extends Annotation> A getAnnotation(Class<A> annotationClass):获取指定类型的注解
  • Annotation[] getAnnotations():获取所有注解

泛型
#

  • Type getGenericSuperclass():获取父类泛型类型
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>
}

所在包
#

  • Package getPackage():获取所在包

调用类的指定结构
#

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

关于setAccessible方法的使用
#

  • Method、Field、Constructor对象都有setAccessible(boolean flag)方法
  • 此方法可以启动和禁用访问安全检查(权限)
  • 如果参数flag为true,则可以无视private等修饰符进行访问

调用方法
#

  • Method getDeclaredMethod(String name,Class ... parameterTypes):获取方法
  • Object invoke(Object obj,Object ... args):执行方法
  • 此方法的返回值就是调用方法的返回值,如没有返回值则为null
  • 如果方法为非静态方法,则参数objnull
  • 如果方法权限不足,如private,则需要在调用invoke()之前,显式的调用setAccessible(true)设为可访问
//一、获取类的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");
JavaSE高级 - 点击查看当前系列文章
§ 7、反射机制 「 当前文章 」