Java反射

Posted jztx123

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Java反射相关的知识,希望对你有一定的参考价值。

反射定义

  • 什么是反射?

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

  • 了解Java代码经历的三个阶段

    技术分享图片

  • 使用反射的好处?
  1. 可以在程序运行过程中,操作这些对象
  2. 可以解耦,提高程序的可扩展性。

    使用反射

    使用反射要先获得类的Class对象

  • 三种方式获取Class对象
// 1.对应第一阶段:编译时
Class.forName("包名.类名"):将class字节码文件加载进内存, 返回Class对象
(多用于配置文件,将类名定义在配置文件中;读取文件,加载类)
// 2.对应第二阶段:加载进内存时
类名.class:通过类名的属性class获, 如Person.class
(多用于参数的传递)
// 3.对应第三阶段:Runtime运行时
对象.getClass():getClass()方法在Object类中定义
(多用于对象的获取字节码的方式)
//1.Source源代码阶段:配置文件
Class clazz1 = Class.forName("com.it666.reflect.Person");
//2.Class类对象阶段:参数传递
Class clazz2 = Person.class;
//3.Runtime运行时阶段:通过对象获取class字节码
Person p= new Person();
Class clazz3 = p.getClass();
//判断两个对象是否是同一个字节码文件     
System.out.println(clazz1==clazz2); //true
System.out.println(clazz2==clazz3); //true

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

  • 代码示例

Person类

package com.itlike.reflect;

public class Person {
    //成员变量Field
    private String name;
    private Integer age;
    //构造方法Constructor
    public Person() {
    }
    public Person(String name, Integer age) {
        this.name = name;
        this.age = age;
    }
    //自定义方法Method
    public void show(){
        System.out.println("it is reflect!");
    }
    public void show(String name){
        System.out.println("it is "+name);
    }
    public void say(){
        System.out.println("Hello World!");
    }
    //Getter、Setter、toString
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public Integer getAge() {
        return age;
    }
    public void setAge(Integer age) {
        this.age = age;
    }
    @Override
    public String toString() {
        return "Person{" +
                "name=‘" + name + ‘‘‘ +
                ", age=" + age +
                ‘}‘;
    }
}
  • 测试类
package com.itlike.reflect;

public class reflect2 {
    public static void main(String[] args) throws Exception{
        /**1.Class.forName("全类名")
         * 运行结果:
         * class com.itlike.reflect.Person
         */
        Class clazz1 = Class.forName("com.itlike.reflect.Person");
        System.out.println(clazz1);
        /**
         * 2.类名.class
         * 运行结果:
         * class com.itlike.reflect.Person
         */
        Class clazz2 = Person.class;
        System.out.println(clazz2);
        /**
         * 3.对象.getClass()
         * 运行结果:
         *class com.itlike.reflect.Person
         */
        Person person = new Person();
        Class clazz3 = person.getClass();
        System.out.println(clazz3);
        /**
         * 三个对象是否是同一个呢?
         * ==:比较地址
         * equals():比较内容
         */
        System.out.println(clazz1==clazz2); //true
        System.out.println(clazz1==clazz3); //true 
    }
}

反射对象

  • 成员变量
package com.itlike.reflect;

import java.lang.reflect.Field;

public class reflect2 {
    public static void main(String[] args) throws Exception {
        // 1.获取Class对象
        Class clazz2 = Person.class;
        /**
         * Field[] getFields():获取所有public修饰的成员变量
         * Field getField(String name):获取指定名称的public修饰的成员变量
         * Field[] getDeclaredFields():获取所有的成员变量,不考虑修饰符
         * Field getDeclaredField(String name):获取指定名称的成员变量
         */
        // 2.取得所有成员变量(不仅仅public修饰,数组形式)
        Field[] fields = clazz2.getDeclaredFields();
        // 3.取得指定名称的成员变量(不存在就会跑出抛出异常)
        Field name = clazz2.getDeclaredField("name");
        /**
         * 可以通过反射取得/修改成员变量的值
         * public可以直接获取
         * 非public需要忽略访问修饰符的安全检查
         */
        Field age = clazz2.getDeclaredField("age");
        // 非public修饰属性,需要忽略安全检查(暴力反射)
        age.setAccessible(true);
        // 4.获取值(需要一个使用对象的参数)
        Person p = new Person();
        Object value = age.get(p);
        System.out.println(value); //null,说明成功获取!
        // 5.设置值(age已忽略安全检查)
        age.set(p, 23);
        System.out.println(p);
    }
}
  • 构造方法
package com.itlike.reflect;

import java.lang.reflect.Constructor;

public class reflect2 {
    public static void main(String[] args) throws Exception {
        // 1.获取Class对象
        Class clazz3 = Person.class;
        /**
         * Constructor<?>[] getConstructors()
         * Constructor<T> getConstructor(类<?>... parameterTypes)
         * Constructor<T> getDeclaredConstructor(类<?>... parameterTypes)
         * Constructor<?>[] getDeclaredConstructors()
          */
        // 2.取得单个构造方法(根据参数类型和个数判定)
        Constructor constructor = clazz3.getConstructor(String.class,Integer.class);
        /**
         * 构造方法的用处?
         * 创建对象:newInstance
         */
        // 3.使用反射获取的构造方法创建对象
        Person p1 = (Person)constructor.newInstance("张三",23);
        System.out.println(p1);
        /**
         * 把上面的String.class,Integer.class去除,就是空参构造:
         * Constructor constructor = clazz3.getConstructor();
         * Person p = (Person)constructor.newInstance();
         * System.out.println(p);
         * //Person{name=‘null‘, age=null}
         *
         * 使用空参构造创建对象,Java提供了更简便的方法:         *
         */
        Person p2 = (Person)clazz3.newInstance();
        System.out.println(p2);
        /**
         * getDeclaredConstructor:
         * 使用对象是私有的构造方法,比较少见
         * 如果需要,只要加上忽略安全检查即可。
         */
    }
}
  • 成员方法
package com.itlike.reflect;

import java.lang.reflect.Method;

public class reflect2 {
    public static void main(String[] args) throws Exception {
        // 1.获取Class对象
        Class clazz3 = Person.class;
        /**
         * Method[] getMethods()
         * Method getMethod(String name, 类<?>... parameterTypes)
         * Method[] getDeclaredMethods()
         * Method getDeclaredMethod(String name, 类<?>... parameterTypes)
          */
        // 2.获取指定名称的成员方法
        Method show = clazz3.getDeclaredMethod("show");
        /**
         * 如何执行获取的方法对象?
         * 使用invoke(对象[,参数])
         */
        Person p = new Person();
        show.invoke(p);
        /**
         * 执行带参数的成员方法,要注意:
         * 在获取Method时,需要传入参数类型
         */
        // 3.使用带参数的Method,并执行
        Method show2 = clazz3.getDeclaredMethod("show",String.class);
        show2.invoke(p,"张三");
        /**
         * 其他的,诸如:
         * 获取成员方法集合、单个成员方法
         * 是否public修饰的成员方法
         * 参照成员变量即可!
         *
         * 同样地,要获取非public修饰的方法,
         * 也可以使用暴力反射!
         * 不过要确保使用的是Declared!
         */
        // 4.获取的是全部的方法,连带着Object(默认继承)的方法
        Method[] methods = clazz3.getDeclaredMethods();
        for (Method method : methods) {
            System.out.println(method);
        }
        /** 暴力反射
         * getDeclaredMethod在所有方法里获取say
         * getMethod在public方法里获取say
         */
        System.out.println("------------");
        Method say = clazz3.getDeclaredMethod("say");
        say.setAccessible(true); //暴力反射
        say.invoke(p);
    }
}

获取类名

// 获取类名
Person person = new Person();
String name = person.getClass().getName();
System.out.println(name);

小案例(反射的实际应用!)

目标:写一个"框架",在不改变该类任何代码的情况下, 能够帮助我们创建任意类的对象,并执行其中的任意方法。

  • 步骤:
  1. 将需要创建的对象的全类名和需要执行的方法定义在配置文件当中
  2. 在程序中加载并读取配置文件
  3. 使用反射技术来加载类文件进入内存
  4. 创建对象
  5. 执行方法

    我们使用配置文件来抽取全类名和方法

  • pro.properties配置文件
className=com.itlike.reflect.Student 
methodName=sleep
  • 框架实现
package com.itlike.reflect;

import java.io.InputStream;
import java.lang.reflect.Method;
import java.util.Properties;

public class Test {
    public static void main(String[] args) throws Exception {
        // 1.创建Properties对象
        Properties pro = new Properties();
        // 2.获取类加载路径
        ClassLoader classLoader = Test.class.getClassLoader();
        // 3.加载配置文件
        InputStream is = classLoader.getResourceAsStream("pro.properties");
        pro.load(is);
        // 4.获取配置文件中的数据(类和方法)
        String className = pro.getProperty("className");
        String methodName = pro.getProperty("methodName");
        // 5.加载该类进内存(className是全类名)
        Class cls = Class.forName(className);
        // 6.创建对象
        Object obj = cls.newInstance();
        // 7.获取方法
        Method method = cls.getDeclaredMethod(methodName);
        method.setAccessible(true); //开启暴力反射
        // 8.执行方法
        method.invoke(obj);

    }
}
  • 测试:

    pro.properties配置文件中修改全类名和要执行的方法,直接运行该"框架"类即可!

以上是关于Java反射的主要内容,如果未能解决你的问题,请参考以下文章

反射机制入门

反射机制入门

反射机制入门

使用反射在外部JAR / CLASS上调用包含Hibernate事务的方法(Java EE)

为啥我的 Ray March 片段着色器反射纹理查找会减慢我的帧速率?

OpenGL片段着色器不照亮场景