反射

反射

1、java代码在计算机中的三个阶段:

**× 源代码阶段 ×--------------->× Class 类对象 ×----------------> × Runtime阶段 **

第一阶段:java源代码,java字节码阶段,这个的Java程序还是在硬盘上

第二计算:通过类加载器(ClassLoader)将字节码文件加载到内存,在内存中用Class类对象来表示Java程序,该Class对象主要包含三个对象,描述成员变量的Filed[]、描述构造方法的Constructor[]、描述成员方法的Method[]。注意到他们都是对象数组,这是因为一个类可能有个多个成员变量,多个构造方法,多个成员方法。反射机制就是将类的这几个部分封装为Field、Constructor、Method对象。

2、获得字节码文件对象(class对象)方式

  • Class.forName(“全类名”)静态方法,参数是全类名(包名.类名,将字节码文件加载进内存,返回Class对象,多用于配置文件,类名一般定义在配置文件中,读取文件,加载类
  • 通过类名的属性Class获取:类名称.class,多用于参数的传递
  • 通过对象的方法getClass(),这个方法是在Object类中定义的:对象名.getClass()

注意:其实同一个字节码文件在一次程序的运行过程中只会被加载一次,也就是如果是在同一种程序中,无论使用上面的哪一种方法获取字节码文件对象,都是返回的同一个字节码对象,都是在相同的内存位置。

比如要获得Person类的class对象

Class<Person> personClass = Person.class;

3、Class对象的功能

无论是成员变量,构造方法,方法,都有一个setAccessible(true);调用这个方法,支持暴力反射(忽略权限修饰符的安全检查),使得可以访问获得的私有(private)修饰的方法,成员变量等,否则就会抛出异常

  • 获取成员变量们
方法 描述
Fileds[] getFileds() 返回所有由public修饰的成员变量的对象组成的数组
Field getFiled(String name) 返回指定的由public修饰的成员变量的对象
Fields[] getDeclaredFields() 返回所有成员变量的对象的数组,无论修饰符是什么
Field getDeclaredField(String name) 返回指定的成员变量的对象,无论修饰符是什么
package cn.zhuobo.reflect;

import java.lang.reflect.Array;
import java.lang.reflect.Field;
import java.util.Arrays;

public class Demo01 {
    public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException {
        //
        Class<Person> personClass = Person.class;

        Field[] fields = personClass.getFields();

        System.out.println(Arrays.toString(fields));
        // 拿到的其中一个成员变量StringB name的对象
        Field name = personClass.getField("name");

        Person p = new Person();

        // get,获取成员变量的值
        Object o = name.get(p);// 传递一个对象,要获得这个对象的成员变量的值
        System.out.println(o);// 这样就获取了成员变量的值

        //设置成员变量的值
        name.set(p, "zhuobo");// 传递两个参数,第一个是要设置成员变量值的对象,第二个是他的值
        System.out.println(p);

        Field s = personClass.getDeclaredField("s");
        s.setAccessible(true);// 调用这个方法,暴力反射(忽略权限修饰符的安全检查),使得可以访问获得的私有(private)成员变量,否则就会抛出异常
        Object o1 = s.get(p);
        System.out.println(o1);

    }
}
  • 获取构造方法
方法 描述
Constructor<?>[] getConstructors() 获取所有的构造方法
Constructor getConstructor(类<?>....参数列表) 获取特定的构造方法
Constructor<?>[] getDeclaredConstructors()
Constructor[] getDeclaredConstructor(类<?>....参数列表)

用来获取构造方法,获得构造方法后调用方法newInstance(参数列表)创建对象,如果是无参数的构造方法,其实也可以使用Class对象的newInstance方法创建对象。

package cn.zhuobo.reflect;

import java.lang.reflect.Constructor;

public class Demo02Constructor {
    public static void main(String[] args) throws Exception {
        Class<Person> personClass = Person.class;
        // get all costructor
        Constructor<?>[] constructors = personClass.getConstructors();
        for (Constructor<?> constructor : constructors) {
            System.out.println(constructor);
        }
        // get a constructor for given fields
        Constructor<Person> constructor = personClass.getConstructor(String.class, int.class);
        System.out.println(constructor);

        // use the method newInstance() to creat a object
        Person person = constructor.newInstance("猪八戒", 367);
        System.out.println(person);

        // construct a object without param
        Constructor<Person> constructor1 = personClass.getConstructor();
        Person person1 = constructor1.newInstance();
        System.out.println(person1);

        // 创建无参数对象的另一种方法
        Person person2 = personClass.newInstance();
        System.out.println(person2);
    }
}
  • 获取成员方法
方法 描述
Method[] getMethods() 获取所有public修饰的成员方法,还包括继承自父类Object的方法,wait、notify、hashCode等方法
Method getMethods(String name, 类<?>...参数类型) 获取特定的构造方法
Method[] getDeclaredMethods()
Method getDeclaredMethod(String name, 类<?>...参数类型)
package cn.zhuobo.reflect;

import java.lang.reflect.Method;

public class Demo03Method {
    public static void main(String[] args) throws Exception {
        Class<Person> personClass = Person.class;

        //获得并调用带参数的方法
        Method say = personClass.getMethod("say", String.class);
        Person p = new Person("蜘蛛侠",369);
        say.invoke(p, "chifanfa");

        // 获得并调动空参数的方法
        Method say1 = personClass.getMethod("say");
        say1.invoke(p);

        Method[] methods = personClass.getMethods();
        for (Method method : methods) {
            //System.out.println(method);
            System.out.println(method.getName());// 用来获取方法名的getName
        }
    }
}
  • 获取类名
String getName()

4、反射的一个应用

在还没有知道要使用什么类,使用什么类的什么方法的时候,可以在运行是才决定具体是使用什么了什么方法。看起来好炫的样子,什么都不知道就可以用了。可以利用放射机制可以在某些途径获得要使用的类,要使用的方法。

  1. 创建一个pro.properties配置文件,文件里面定义要使用的类的名称以及方法(当代码想要使用不同的类,就修改这个配置文件就可以了,不用修改代码)

    • 使用全类名的配置文件,这种情况下,代码回调用Student的eat方法

      className=cn.zhuobo.reflect.Student
      methodName=eat
      
    • 这种情况下,调用Person类的say方法

      className=cn.zhuobo.reflect.Person
      methodName=eat
      
  2. 使用反射的过程

    package cn.zhuobo.reflect;
    
    import java.io.IOException;
    import java.io.InputStream;
    import java.lang.reflect.Method;
    import java.util.Properties;
    
    public class ReflectTest {
        public static void main(String[] args) throws Exception {
            // 创建Properties对象
            Properties properties = new Properties();
    
            // 获取配置文件,使用类加载器
            ClassLoader classLoader = ReflectTest.class.getClassLoader();
            InputStream is = classLoader.getResourceAsStream("pro.properties");
            properties.load(is);
    
            // 获取配置文件中定义的数据
            String className = properties.getProperty("className");
            String methodName = properties.getProperty("methodName");
    
            // 获取对象,创建对象
            Class aClass = Class.forName(className);
            Object o = aClass.newInstance();
    
            //获取方法,调用方法
            Method method = aClass.getMethod(methodName);
            method.invoke(o);
    
        }
    }
    
原文地址:https://www.cnblogs.com/zhuobo/p/10675831.html