Java高级特性之反射是什么?


反射是框架设计的灵魂
想要通过Java的反射机制获取类的信息,就必须先获取Class对象

一、Java代码在计算机中经历的三个阶段

在这里插入图片描述

(一):源代码阶段

    创建一个Person类对象。此时,计算机硬盘上会生成一个Person.java文件,再通过javac编译器编译Person.java文件,会生成一个字节码person.class文件。
字节码person.class文件包含了Person.java文件中的所有内容,但不仅仅这些内容。

(二):Class类对象

    通过类加载器ClassLoader,将字节码文件person.class,加载到内存中,生成一个Class类对象。
    想要使用反射,就必须先得到Class类对象。

(三):运行时阶段

    我们创建对象的时候new Person();就是运行时阶段。

综上所述:Java的反射机制,就是将Java类文件>>>>通过javac编译器,生成>>>>字节码文件>>>>通过类加载器ClassLoader,生成>>>>一个Class类对象。Class类对象中包含了Java类文件中所有的内容,通过Class类对象操作类的过程称之为反射。

二、常用API的运用

(一)获取class对象的三种方式

1.Class.forName(String className)

    将字节码文件加载至内存,返回class对象;
    多用于配置文件,将类名定义在配置文件中,读取文件加载类。

/**
 * className:包名+类名
 */
Class<?> cls01 = Class.forName("cn.zj.domain.Person");
System.out.println(cls01);

2.类名.class

    通过类名的属性class获取;
    多用于参数的传递

Class<Person> cls02 = Person.class;
System.out.println(cls02);

3.对象.getClass()

    getClass()在Object中定义,也就是说,所有的类的有这个方法;
    多用于有对象的情况下,需要获取该对象的字节码对象。

Person person = new Person();
Class cls03 = person.getClass();
System.out.println(cls03);

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

(二)获取成员变量

1.Field[] getFields()

    获取所有public修饰的成员变量

Class<Person> personClass = Person.class;
/**
 * 获取所有public修饰的成员变量
 */
Field[] fields = personClass.getFields();
for (Field field : fields) {
    System.out.println(field);
}

2.Field getField(String name)

    获取指定的public修饰的成员变量

Class<Person> personClass = Person.class;
/**
 * 获取指定的public修饰的成员变量
 */
Field a = personClass.getField("a");

3.Field[] getDeclaredFields()

    获取所有的成员变量

Class<Person> personClass = Person.class;
/**
 * 获取所有成员变量
 */
Field[] declaredFields = personClass.getDeclaredFields();
for (Field field :
        declaredFields) {
    System.out.println(field);
}

4.Field getDeclaredField(String name)

    获取指定的成员变量,不管指定的成员变量的修饰符是什么。

Class<Person> personClass = Person.class;
/**
 * 获取指定的成员变量,不管是何总修饰符修饰的
 */
Field d = personClass.getDeclaredField("d");
System.out.println(d);

5.Field:成员变量

5.1.获取值

    Object get(Object obj)
    获取成员变量的值;

/**
 * 获取指定的public修饰的成员变量
 */
Field a = personClass.getField("a");

Person person = new Person();
/**
 * 获取成员变量a的值
 */
Object value = a.get(person);
System.out.println(value);

        如果访问的成员变量受到访问权限修饰符的限制,比如是private修饰的成员变量,则需要暴力反射;

Class<Person> personClass = Person.class;
/**
 * 获取指定的成员变量,不管是何种访问权限修饰符
 */
Field d = personClass.getDeclaredField("d");
System.out.println(d);
Person person = new Person();
//忽略访问全限修饰符的检查,
//访问权限修饰符为private,需要设置为true
d.setAccessible(true);
//获取成员变量的值
Object obj = d.get(person);
System.out.println(obj);
5.2.设置值

    void set(Object obj, Object value)
    设置指定对象成员变量的值

    参数 :
        obj的字段应该被修改的对象
        value为新值

Field a = personClass.getField("a");
Person person = new Person();
/**
 * 设置指定成员变量的值
 */
a.set(person,"张三");
System.out.println(person.toString());

(三)获取构造方法

1.Constructor getConstructor(类<?>… parameterTypes)

    获取指定参数的public修饰的构造函数

Class<Person> personClass = Person.class;
/**
 * 获取指定参数的public修饰的构造函数
 */
Constructor<Person> constructor = personClass.getConstructor(String.class, Integer.class);
System.out.println(constructor);

2.Constructor<?>[] getConstructors()

    获取所有public修饰的构造函数

Class<Person> personClass = Person.class;
/**
 * 获取所有public修饰的构造函数
 */
Constructor<?>[] constructors = personClass.getConstructors();
for (Constructor con :
        constructors) {
    System.out.println(con);
}

3.Constructor getDeclaredConstructor(类<?>… parameterTypes)

    获取指定参数的构造函数,不考虑权限修饰符

Class<Person> personClass = Person.class;
/**
 * 获取指定参数的构造函数,不考虑权限修饰符
 */
Constructor<Person> declaredConstructor = personClass.getDeclaredConstructor(Integer.class);
System.out.println(declaredConstructor);

4.Constructor<?>[] getDeclaredConstructors()

    获取所有的构造函数,不考虑权限修饰符

Class<Person> personClass = Person.class;
/**
 * 获取所有的构造函数,不考虑权限修饰符
 */
Constructor<?>[] declaredConstructors = personClass.getDeclaredConstructors();
for (Constructor con :
        declaredConstructors) {
    System.out.println(con);
}

5.Constructor:构造函数

5.1.创建对象

    T newInstance(Object… initargs)

Class<Person> personClass = Person.class;
/**
 * 获取指定参数的构造函数
 */
Constructor<Person> constructor = personClass.getConstructor(String.class, Integer.class);
System.out.println(constructor);
Person person = constructor.newInstance("张三", 12);
System.out.println(person);

        如果访问的成员变量受到访问权限修饰符的限制,比如是private修饰的成员变量,则需要暴力反射;

/**
 * 获取指定参数的构造函数,不考虑权限修饰符
 */
Constructor<Person> declaredConstructor = personClass.getDeclaredConstructor(Integer.class);
System.out.println(declaredConstructor);
//此处的构造函数为protected修饰的,需要暴力反射
declaredConstructor.setAccessible(true);
Person person2 = declaredConstructor.newInstance(20);
System.out.println("==========person2==========="+person2);

    如果使用空参数构造函数创建对象,上面的方法可以实现,即不传参数即可,但可以简化操作,
    Class对象也有创建对象的方法:T newInstance()

Class<Person> personClass = Person.class;
Person person1 = personClass.newInstance();
System.out.println(person1);

(四)获取成员方法

1.Method getMethod(String name, 类<?>… parameterTypes)

    获取指定名称和参数的public修饰的成员方法
    参数
        name:方法名
        parameterTypes:方法参数,有就写,没有就不写

Class<Person> personClass = Person.class;
/**
 * 获取指定成员方法名称和参数的public修饰的方法
 */
 //无参
Method eat = personClass.getMethod("eat");
System.out.println(eat);
//有参
Method eat1 = personClass.getMethod("eat",String.class);
System.out.println(eat1);

2.Method[] getMethods()

        获取所有public修饰的成员方法,包含它的父类Object里的方法

Class<Person> personClass = Person.class;
/**
 * 获取所有public修饰的成员方法,包含它的父类Object里的方法
 */
Method[] methods = personClass.getMethods();
for (Method method :
        methods) {
    System.out.println(method);
}

3.Method getDeclaredMethod(String name, 类<?>… parameterTypes)

    获取指定的成员方法,不考虑权限修饰符

Class<Person> personClass = Person.class;
/**
 * 获取指定的成员方法,不考虑权限修饰符
 */
Method sing = personClass.getDeclaredMethod("sing", Integer.class);
System.out.println(sing);

4.Method[] getDeclaredMethods()

    获取所有成员方法,不考虑权限修饰符。不包含父类Object中的方法

Class<Person> personClass = Person.class;
/**
 * 获取所有成员方法,不考虑权限修饰符。不包含父类Object中的方法
 */
Method[] declaredMethods = personClass.getDeclaredMethods();
for (Method met :
        declaredMethods) {
    System.out.println(met);
}

5.Method:方法对象

5.1.执行方法

    Object invoke(Object obj, Object… args)
    调用底层的方法
    参数
        obj:指定对象
        args:指定成员方法的参数

Class<Person> personClass = Person.class;
/**
 * 获取指定成员方法名称和参数的public修饰的方法
 */
 //无参
Method eat = personClass.getMethod("eat");
System.out.println(eat);
Person person = new Person();
Object obj = eat.invoke(person);

//有参
Method eat1 = personClass.getMethod("eat",String.class);
System.out.println(eat1);
Person person1 = new Person();
Object obj1 = eat1.invoke(person,"饭");

        如果访问的成员方法受到访问权限修饰符的限制,比如是private修饰的成员方法,则需要暴力反射;

Class<Person> personClass = Person.class;
/**
 * 获取指定的成员方法,不考虑权限修饰符
 */
Method sing = personClass.getDeclaredMethod("sing", Integer.class);
System.out.println(sing);
sing.setAccessible(true);
Person person = new Person();
sing.invoke(person,100);
5.2.获取方法名称

    String getName()

Class<Person> personClass = Person.class;
/**
 * 获取所有成员方法,不考虑权限修饰符。不包含父类Object中的方法
 */
Method[] declaredMethods = personClass.getDeclaredMethods();
for (Method met :
        declaredMethods) {
    System.out.println(met);
    //获取方法名称
    String name = met.getName();
    System.out.println(name);
}

(五)获取类名

1.String getName()

    获取类名,返回为:包名+类名

 Class<Person> personClass = Person.class;
//获取类名
String name1 = personClass.getName();
System.out.println("name1: "+name1);

返回值:包名+类名
在这里插入图片描述

三、运用案例

    需求描述:写一个”框架“,可以创建任意类的对象,并且执行其对象中的任意的方法。
    步骤:1.读取配置文件中的内容;
               2.将需要运行的类加载进内存
               3.利用反射创建对象
               4.利用反射获取方法
               5.利用反射执行方法

  1. 创建pro.properties
    在这里插入图片描述
  2. 案例代码
package cn.zj.refelect;

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

/**
 * 注释
 * 框架类
 * @author 49906
 * @date 2020/3/21
 */
public class RefelectTest {
    public static void main(String[] args) throws Exception {
        //1.加载配置文件
        //1.1.创建Properties对象
        Properties properties =new Properties();
        //1.2.加载配置文件,转换为一个集合
        //获取class目录下的配置文件
        ClassLoader classLoader = RefelectTest.class.getClassLoader();
        InputStream in = classLoader.getResourceAsStream("pro.properties");
        properties.load(in);

        //2.获取配置文件中定义的数据
        String className = properties.getProperty("className");
        String methodName = properties.getProperty("methodName");

        //3.加载该类进内存
        Class<?> cls = Class.forName(className);

        //4.创建对象
        Object obj = cls.newInstance();

        //5.获取方法对象
        Method method = cls.getMethod(methodName);

        //6.执行方法
        method.invoke(obj);
    }

}

发布了23 篇原创文章 · 获赞 5 · 访问量 1430

猜你喜欢

转载自blog.csdn.net/zj499063104/article/details/104993279