Java——反射(reflection)详解

文章目录

      • 反射(reflection)
        • 引出反射
        • Java Reflection
          • java反射机制可以完成
          • 反射相关的主要类:
          • 反射优点和缺点
          • 反射调用优化-关闭访问检查
        • Class类
          • 基本介绍
          • Class类的常用方法:
          • 获取Class 对象:
          • 拓展:哪些类型有Class对象
        • 类加载
          • 基本说明
          • 类加载时机
          • 加载阶段
          • 连接阶段-解析
          • Initialiaztion(初始化)
        • 通过反射获取类的结构信息
          • 第一组:java.lang.Class 类
          • 第二组: java.lang.reflect.Field 类
        • 通过反射创建对象
        • 通过反射访问类中的成员
          • 访问属性
          • 访问方法
        • 应用案例

反射(reflection)

引出反射
  1. 使用Propertier 类,可以读写配置文件
Propertier properties = new Properties();
properties.load(new FileInputStream("src\\re.properties"))//原路径
String classfullPath = properties.get("classfullpath").toString();//得到类信息
String method = properties.get("method").toString();//得到方法信息
System.out.println("classfullpath="+ classfulllpath);
System,out.println("method="+ method);
  1. 创建对象,传统的方法,行不通 ====> 反射机制
  2. 使用反射机制解决
//(1)加载类,返回Class类型的对象cls
Class acalss = Class.forName(classfullpath);
//(2)通过 cls 得到你加载的类 Cat 的对象实例
Object o = cls.newInstance();
System.out.println(o.getClass());// 运行类型
//(3)通过 cls 得到你加载的类 Cat 的methodName "hi" 的方法对象
// 即:在反射中,可以把方法视为对象(万物皆对象)
Method method1 = cls.getMethod(methodName); 
//(4)通过method1 调用方法: 即通过方法对象来实现调用方法
method1.invoke(o); //传统方法 对象.方法(), 反射机制 方法.invoke(对象)


说明:即通过外部文件配置,在不修改源码情况下,来控制程序,也符合设计模式的ocp原则(开闭原则:不修改源码,扩容功能)
Java Reflection
  1. 反射机制允许程序在执行期借助于Reflection API 取得任何类的内部信息(比如成员变量,构造器,成员方法等等),并能操作对象的属性及方法。反射在设计模式和框架底层都会用到。
  2. 加载完类之后,在堆中就产生了一个Class 类型的对象(一个类只有一个Class对象),这个对象包含了类的完整结构信息。通过这个对象得到类的结构。这个对象就像一面镜子,透过一个镜子看见类的结构,所以,形象的称之为:反射。
  3. Java反射机制原理示意图

Java——反射(reflection)详解_第1张图片

java反射机制可以完成
  1. 在运行时判断任意一个对象所属的类
  2. 在运行时构造任意一个类的对象
  3. 在运行时的得到任意一个类所具有的成员变量和方法
  4. 在运行时调用任意一个对象的成员变量和方法
  5. 生成动态代理
反射相关的主要类:
  1. java.lang.Class:代表一个类,Class对象表示某个类加载后在堆中的对象
  2. java.lang.reflect.Method:代表类的方法,Method对象表示某个类的方法
  3. java.lang.reflect.Field:代表类的成员变量,Field对象表示某个类的成员变量
  4. java.lang.reflect.Constructor:代表类的构造方法,Constructor对象表示构造器
反射优点和缺点
  1. 优点:可以动态的创建和使用对象(也是框架底层核心),使用灵活,没有反射机制,框架技术就失去底层支撑。
  2. 缺点:使用反射基本是解释执行,对执行速度有影响。
反射调用优化-关闭访问检查
  1. Method和Field、Constructor对象都有setAccessible() 方法
  2. setAccessible作用和禁用访问安全检查的开关
  3. 参数值为true表示 反射的对象在使用时取消访问检查,提高反射的效率。参数值为fasle则表示反射的对象执行访问检查
Class类
基本介绍
  1. Class类也是类,因此也继承Object类
  2. Class类对象不是new出来的,而是系统创建的
  3. 对于某个类的Class类对象,在内存中只有一份,因为类只加载一次
  4. 每个类的实例都会记得自己是由哪个Class实例所生成的
  5. 通过Class对象可以完整地得到一个类的完成结构,通过一系列API
  6. Class对象时存放在堆的
  7. 类的字节码二进制数据,是放在方法区的,有的地方称为类的元数据(包括 方法代码, 变量名, 方法名, 请求权限等等)
Class类的常用方法:

Java——反射(reflection)详解_第2张图片

    String classAllPath = "reflection.Car";
    //获取到Car 类对应的 Class 对象
    // 表示不确定的java 类型
    Class<?> aClass = Class.forName(classAllPath);
    //输出cls
    System.out.println(aClass); //显示aClass 对象,是哪个类的Class reflection.Car
    System.out.println(Class.class); //输出aClass 运行类型 java.lang.Class
    //3. 得到包名
    System.out.println(aClass.getPackage().getName());
    //4.全类名
    System.out.println(aClass.getName());
    //5.通过aClass 创建一个对象实例
    Car car =(Car) aClass.getConstructor().newInstance();
    System.out.println(car);
    //6. 通过反射获取属性 brand
    Field color = aClass.getField("color");
    System.out.println(color.get(car));
    //7. 通过反射给属性赋值
    color.set(car,"黄色");
    System.out.println(color.get(car));
    //8. 遍历所有属性
    System.out.println("====================");
    Field[] fields = aClass.getFields();
    for(  Field n : fields){
        System.out.println(n.getName());
    }
获取Class 对象:
public static void main(String[] args) throws Exception {

    //1.Class.forName
    String classAllPath = "reflection.Car";
    Class<?> aClass1 = Class.forName(classAllPath);
    Object o = aClass1.getConstructor().newInstance();
    Field color = aClass1.getField("color");
    System.out.println(color.get(o));
    //2. 类名.class
    Class carClass2 = Car.class;
    System.out.println(carClass2);
    //3.对象.getClass(),应用场景,有对象实例
    Car car = new Car();
    Class aClass3 = car.getClass();
    System.out.println(aClass3);
    //4.通过类的加载器(4中类加载器)来获取到类的Class 对象
        //  (1)先得到类加载器 car
    ClassLoader classLoader = car.getClass().getClassLoader();
        //  (2) 通过类加载器得到Class 对象
    Class<?> aClass4 = classLoader.loadClass(classAllPath);
    System.out.println(aClass4);
    // 上四个均为同一个对象
    System.out.println(aClass1.hashCode());
    System.out.println(carClass2.hashCode());
    System.out.println(aClass3.hashCode());
    System.out.println(aClass4.hashCode());
    //5.基本数据类型(int,char,boolean,float,double,byte,long,short)按如下方式得到Class类对象
    //Class aClass = 基本数据类型.class
    Class<Integer> integerClass = int.class;
    System.out.println(integerClass);
    Class<Character> characterClass = char.class;
    //6.基本数据类型对应的包装类,可以通过.type得到Class类对象
    //Class aClass = 包装类.TYPE;
    Class<Integer> integerClass1 = Integer.class;
    System.out.println(integerClass1);
    Class<Boolean> booleanClass = Boolean.class;
}
拓展:哪些类型有Class对象
  1. 外部类,成员内部类,静态内部类,局部内部类,匿名内部类
  2. interface: 接口
  3. 数组
  4. enum: 枚举
  5. annotation : 注解
  6. 基本数据类型
  7. void
类加载
基本说明

反射机制是java实现动态语言的关键,也就是通过反射实现类动态加载.

  1. 静态加载:编译时加载相关的类, 如果没有则报错,依赖性太强
  2. 动态加载: 运行时加载需要的类,如果运行时不用该类,则不报错,降低了依赖性 ( 反射动态加载)
类加载时机
  1. 当创建对象时(new) //静态加载
  2. 当子类被加载时 //静态加载
  3. 调用类中的静态成员时 //静态加载
  4. 通过反射 //动态加载

Java——反射(reflection)详解_第3张图片

加载阶段

JVM 在该阶段的主要目的是将字节码从不同的数据源(可能是class 文件、也可能是jar包,甚至网络) 转化为二进制字节流加载到内存中,并生成一个代表该类的java.lang.Class 对象

public int n1 = 10;
public static int n2 = 20;
public static final int n3 = 30;

// n1 是实例属性,不是静态变量,因此在准备阶段,是不会分配内存
// n2 是静态变量,分配内存 n2 是默认初始化 0,而不是20
// n3 是static final 是常量,他和静态变量不一样,因为一旦赋值就不变 n3 = 30
连接阶段-解析

虚拟机将常量池内的符号引用替换为直接引用的过程

Initialiaztion(初始化)
  1. 到初始化阶段,才真正开始执行类中定义的java 程序代码,此阶段是执行 () 方法的过程
  2. () 方法是由编译器按语句在源文件中出现的顺序,依次自动收集类中所有静态变量的赋值动作和静态代码块中的语句,并进行合并.
  3. 虚拟机会保证一个类的() 方法在多线程环境中被正确地加锁、同步、如果多个线程同时去初始化一个类,那么只会有一个线程去执行这个类的() 方法,其他线程都需要阻塞等待,知道活动线程执行() 方法完毕
通过反射获取类的结构信息
第一组:java.lang.Class 类
  1. getName:获取全类名
  2. getSimpleName:获取简单类名
  3. getFields: 获取所有public 修饰的属性, 包含本类一级父类的
  4. getDeclaredFields: 获取本类中所有属性
  5. getMethods: 获取所有public 修饰的方法,包含本类以及父类的
  6. getDeclaredMethods: 获取本类中所有方法
  7. getConstructors: 获取所有public 修饰的构造器,包含本类所有public
  8. getDeclaredConstructors: 获取本类中 所有的构造器
  9. getPackage: 以Package 形式返回 包信息
  10. getSuperClass: 以Class 形式返回父类信息
  11. getInterfaces: 以Class[] 形式返回接口信息
  12. getAnnotations: 以Annotation[] 形式返回注解信息
第二组: java.lang.reflect.Field 类
  1. getModfiers:以int 形式返回修饰符

    [说明: 默认修饰符 是0, public 是1 ,private 是 2, protected 是4, static 是8 ,final 是 16]

  2. getType:

  3. 以Class 形式返回类型

  4. getName: 返回属性名

通过反射创建对象
  1. 方式一: 调用类中的public 修饰的无参构造器
  2. 方式二: 调用类中的指定构造器
  3. Class类相关方法
  • newInstance : 调用类中的无参构造器, 获取对应类的对象
  • getConstructor(Class…cls): 根据参数列表,获取对应的public 构造器对象
  • getDecalaredConstructor(Class…cls ): 根据参数列表,获取对应的构造器对象
  1. Constructor 类相关方法
  • setAccessible: 暴破
  • newInstance(Object…obj): 调用构造器
通过反射访问类中的成员
访问属性
  1. 根据属性名获取Field对象

Field f = cls 对象.getDeclareField(属性名);

  1. 暴力破解: f.setAccseeible(true); //f 是Field
  2. 访问 f.set(o, 值); syso(f.get(o)); //o表示对象
  3. 注意: 如果是静态属性,则set 和 get中的参数o, 可以写成null
        Class<?> aClass = Class.forName("reflection.Student");
        Object o = aClass.getConstructor().newInstance();
        System.out.println(o.getClass());
        //3.使用反射得到 age 属性
        Field age = aClass.getField("age");
        age.set(o,100);
        System.out.println(o);
        //4.使用反射操作name 属性
        Field name = aClass.getDeclaredField("name");
//        name.setAccessible(true);
        name.set(o,"Pink");// 因为name是static 属性,因此 o 也可以写出null
        System.out.println(o);
访问方法
  1. 根据方法名和参数列表获取Method方法对象 : Method m = cls.getDeclaredMethod(方法名, xx.class);
  2. 获取对象 : Object o = clazz.newInstance();
  3. 暴破: m.setAccessible(true);
  4. 访问: Object returnValue = m.invlke(o, 实参列表);
  5. 注意: 如果是静态方法, 则invoke 的参数o, 也可以写成null;
        //1.得到Boss类对应的Class对象
        Class<?> aClass = Class.forName("reflection.Boss");
        //2.创建对象
        Object o = aClass.getConstructor().newInstance();
        //3. 得到hi方法对象
        Method hi = aClass.getMethod("hi", String.class);
        //4. 调用
        System.out.println(hi.invoke(o,"Tom"));
        //5.调用private static 方法
        Method declaredMethod = aClass.getDeclaredMethod("say", int.class, String.class,char.class);
        declaredMethod.setAccessible(true);
        System.out.println(declaredMethod.invoke(o,20,"Jack",'w'));
        //在反射中,如果方法有返回值,统一按照 Object 返回
应用案例
        Class<?> aClass = Class.forName("java.io.File");
        Constructor<?>[] declaredConstructors = aClass.getDeclaredConstructors();
        for (Constructor<?> declaredConstructor : declaredConstructors) {
            System.out.println("File类中的构造器为=" + declaredConstructor);
        }
        Constructor<?> declaredConstructor = aClass.getDeclaredConstructor(String.class);
        String FilePath ="E:\\myNew.txt";
        Object file = declaredConstructor.newInstance(FilePath);
        Method createNewFile = aClass.getMethod("createNewFile");//得到方法的对象
        createNewFile.invoke(file);

你可能感兴趣的:(java,java,jvm,开发语言)