Java 反射入门学习

90 阅读4分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第13天,点击查看活动详情

反射

框架设计的灵魂

框架:半成品软件,可以在框架的基础上进行软件开发,简化代码

反射:将类的各个部分封装为其他对象,这就是反射机制

Java代码在计算机中经理的三个阶段

  • 阶段1:source源代码阶段

    • .java文件经过javac编译转化为.class文件
    • 还在硬盘中,未进入内存
  • 阶段2:Class类对象阶段

    • 通过类加载器加载进内存
    • 将其他类的各个组成部分封装为Class类对象
  • 阶段3:运行时阶段

好处

  1. 可以在程序运行过程中,操作这些对象
  2. 可以解耦,提高程序的可扩展性

获取Class类对象的方式

  • 阶段1获取:Class.forName("全类名");将字节码文佳加载进内存,返回Class对象
    • 多用于配置文件,将类名定义在配置文件中,如读取文件,加载类
    • 用于类本身内部是使用反射
      public class Apple {
      
          private int price;
      
          public void setPrice(int price) {
              this.price = price;
          }
      
          public static void main(String[] args) throws   Exception{
              //正常的调用
              Apple apple = new Apple();
              apple.setPrice(5);
      
              //使用反射调用
              Class clz = Class.forName("com.chenshuyi.api.Apple");
              Method setPriceMethod = clz.getMethod("setPrice", int.class);
              Constructor appleConstructor = clz.getConstructor();
              Object appleObj = appleConstructor.newInstance();
              setPriceMethod.invoke(appleObj, 14);
          }
      }
      
  • 阶段2获取:类名.class; 通过类名的属性class获取
    • 多用于参数的传递
    • Class testClass = test.class; test为类名
  • 阶段3获取:对象.getClass();
    • 多用于对象的获取字节码的方式

结论:

同一个字节码文件(*.class)在一次程序运行过程中,只会被加载一次,不论通过那种方式获取的Class对象都是同一个

Class对象的相关方法

获取功能

  1. 获取成员变量们
    1. Filed[] getFileds() -- 获取所有public修饰的成员变量
    2. Filed getFiled(String name) -- 获取指定名称的public修饰的成员变量
    3. Filed[] getDeclaredFileds() -- 获取所有的成员变量,不可考虑修饰符
    4. Filed getDeclaredFiled(String name) -- 获取指定名称的成员变量
  2. 获取构造方法
    1. 同上类似
  3. 获取成员方法
    1. 同上类似
  4. 获取类名 String getNmae()

Filed成员变量对象

操作:

  1. 设置值
    • void set(Object obj,Objevt value)
  2. 获取值
    • get(Object obj)
  3. 忽略访问权限修饰符的安全检查
    • setAccessible(true);暴力反射

Constructor构造方法对象

创建对象:

  1. getConstructor()方法获取空参构造函数,如果传入参数,则返回对应参数的构造器
  2. newInstance(Object... 参数)方法构造对象
    1. 如果构造空参对象,则直接使用Class对象的newInstance()方法即可

Method成员方法对象

调用类的成员方法:

  1. 通过method.invoke(obj,args1,...,argsX)方法
    1. obj为拥有method方法的类
    2. agrs为method方法需要传入的参数
public class Apple {

    private int price;

    public int getPrice() {
        return price;
    }

    public void setPrice(int price) {
        this.price = price;
    }

    public static void main(String[] args) throws Exception{
        //正常的调用
        Apple apple = new Apple();
        apple.setPrice(5);
        System.out.println("Apple Price:" + apple.getPrice());
        //使用反射调用
        Class clz = Class.forName("com.chenshuyi.api.Apple");
        Method setPriceMethod = clz.getMethod("setPrice", int.class);
        Constructor appleConstructor = clz.getConstructor();
        Object appleObj = appleConstructor.newInstance();
        setPriceMethod.invoke(appleObj, 14);
        Method getPriceMethod = clz.getMethod("getPrice");
        System.out.println("Apple Price:" + getPriceMethod.invoke(appleObj));
    }
}

获取反射中的对象(实例化) & 调用类的成员方法

利用反射实例化类对象

步骤:

  1. 加载Class类对象,三种不同时期加载方式,视情况而定
  2. 通过反射创建类对象
    1. 方法一:通过Class对象的newInstance()方法
        Class clz = Apple.class;
        Apple apple = (Apple)clz.newInstance();
    
    1. 方法二:通过Constructor对象的newInstance()方法
        Class clz = Apple.class;
        Constructor constructor = clz.getConstructor();
        Apple apple = (Apple)constructor.newInstance();
    

通过方法一方式创建类对象,只能使用该类的无参构造方法,有参构造方法不能调用;方法二方式,则可以选择特定构造方法,只需要根据传入的对象进行选择

利用反射调用类的成员方法

步骤:

  1. 加载Class类对象 -- clz
  2. 获取方法的Method对象
        Method setPriceMethod = clz.getMethod("setPrice", int.class);
    
  3. 创建类对象 -- obj
  4. 调用invoke(obk,args1,...)方法
    setPriceMethod.invoke(0bj, 14);
    

配置文件加载案例

    public static void main(String[] args)
            throws Exception {
        // 加载配置文件
        // 1. 创建properties对象
        Properties prop = new Properties();
        // 2. 加载配置文件
        ClassLoader classLoader = reflect.class.getClassLoader();
        InputStream is = classLoader.getResourceAsStream(
                "prop.properties"
        );
        prop.load(is);

        // 获取配置文件中的数据
        String className = prop.getProperty("className");
        String methodNmae = prop.getProperty("methodName");

        // 加载该类进内存
        Class cls = Class.forName(className);
        // 创建对象
        Object obj = cls.newInstance();
        // 获取方法对象
        Method method = cls.getMethod(methodNmae);
        // 执行方法
        method.invoke(obj);
    }