java反射(复盘-补充)

发布于 2022-05-20  64.89k 次阅读


一,反射的概念

反射是JAVA或者说是JVM的一种机制,通过反射可以使程序逆周期执行

反射机制官方定义:在程序运行时能动态获取类的结构数据,这种动态获取信息以及动态调用对象方法的功能称为java语言的反射机制

java代码执行的三大阶段:

  • 源代码阶段
  • Class对象阶段
  • Runtime运行时阶段

反射可以可以通过.class逆向获取信息和生成类,将类中的各个组件部分封装为其他对象

反射的重要性:框架开发的灵魂,基本上所有的java框架都有反射的影子,如:ORM的封装产生的Mybatis,MVC封装的SpringMVC等

反射是特定场景的产物,在使用框架时基本上使用不到反射,但假如要自定义封装一套框架就必然会到,假如是在甲方公司写业务时反射也能有很多应用

注:在一些很多的资料和教程中,对注解和反射都不够深刻,它们只讲怎么通过class对象通过反射获取信息和基本的注解创建,没有讲到怎么通过反射去解析注解

二,获取Class对象三种方式

获取Class对象的三种方式:

  1. Class.forName("类全名"):将字节码文件加载进内存,返回Class对象
  2. 类名.class:通过类名的属性class获取
  3. 对象.getClass:getClass()方法获取,这个方法是在Object类中定义的

这三种方式是在不同阶段获取class的手段:

  • 当class子字节码文件还保存在本地,没有加载到JVM中时通过Class.forName("类全名")
  • 当class文件已经在内存中被解析,可以直接通过类名.class获取
  • 当class文件已经进入RunTime执行阶段,可以直接通过对象.getClass获取
  • 在使用上一般没有区别,但在场景中有适应区分

三种方式的应用场景:

  • Class.forName("类全名"):多用于配置文件,将类名定义在配置文件中,读取配置文件,加载类
  • 类名.class:多用于参数的传递
  • 对象.getClass:多用于对象的获取字节码的方式

例:分别通过三种方式获取

public static void main(String[] args) throws ClassNotFoundException {
    //方式1
    Class<?> parentClass1 = Class.forName("test.parent");
    //方式2
    Class<parent> parentClass2 = parent.class;
    //方式3
    parent parent = new parent();
    Class<? extends test.parent> parentClass3 = parent.getClass();
    System.out.print((parentClass1==parentClass2)?(parentClass2==parentClass3):"false");
}
注:同一个字节码文件(*.class)在一次程序运行过程中,之会被加载一次,不论通过哪一种方式获取class对象结果都是同一个

三,Class对象的功能

功能:

  1. 获取和设置成员变量Field
  2. 获取构造方法Constructor
  3. 获取成员方法Method
  4. 获取注解Annotation
  5. 获取类信息

一,获取设置成员变量

Class类获取Field的方法:

  • Field[] getFields()  :获取所有非private的成员属性变量
  • Field getField(String name)  :通过name获取非private的单个成员属性变量
  • Field[] getDeclaredFields()  :获取所有的成员变量属性
  • Field getDeclaredField(String name):通过name获取单个成员变量

例:

public static void main(String[] args) throws NoSuchFieldException {

    Class<parent> parentClass = parent.class;
    //获取所有非private的成员属性变量
    Field[] classFields = parentClass.getFields();
    //获取所有的成员变量属性
    Field[] declaredFields = parentClass.getDeclaredFields();
    //通过name获取非private的单个成员属性变量,如果没有直接报错
    Field sex = parentClass.getField("sex"); //获取不到报错
    //通过name获取单个成员变量,如果没有直接报错
    Field name = parentClass.getDeclaredField("name");
    
    //打印
    System.out.println("----------getFields-----------");
    for (Field s:classFields){ System.out.println(s); }
    System.out.println("----------getDeclaredFields-----------");
    for (Field s:declaredFields){ System.out.println(s);}
    System.out.println("----------getField-----------");
    System.out.println(sex);
    System.out.println("----------getDeclaredField-----------");
    System.out.println(name);
}

Field类的方法:

  • Object get(Object obj) :返回指定对象上该所表示的字段的值
  • Object set(Object obj,Object value) :设置指定对象上该字段的值
  • Annotation[] getDeclaredAnnotations() :返回直接存在于此属性上的注解  
  • void setAccessible(boolean flag):忽略访问权限修饰符的安全检查,如果不忽略private修饰的属性不能获取和设置
  • …………
Field name = parentClass.getDeclaredField("name");
//忽略访问权限修饰符的安全检查
name.setAccessible(true);//暴力反射
parent x1= new parent();
name.set(x1,"空想家");
Object o = name.get(x1);
结果:
空想家

二,获取构造方法

Class类获取构造的方法:

  • Constructor<T> getDeclaredConstructor(类<?>... parameterTypes):获取指定的有参和无参构造
  • Constructor<?>[] getDeclaredConstructors():获取无参和有参构造列表
  • Constructor<T> getConstructor(类<?>... parameterTypes)  :获取指定的非private的有参和无参构造 
  • Constructor<?>[] getConstructors():获取无参构造 

例:

public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {

    Class<parent> parentClass = parent.class;

    Constructor<?>[] constructors = parentClass.getConstructors();
    Constructor<parent> constructor = parentClass.getDeclaredConstructor(String.class, Integer.class);//参数实际形参的类型
    Constructor<parent> constructor1 = parentClass.getConstructor();

    for (Constructor a:constructors){ System.out.println(a); }

    System.out.println(constructor.newInstance("空想家",100));
    System.out.println(constructor1.newInstance());
}