天天看点

【Java——反射】1、反射:框架的灵魂2、获取Class对象3、Class对象功能4、反射机制的应用案例

反射

  • 1、反射:框架的灵魂
    • 1.1 概述
  • 2、获取Class对象
    • 2.1 获取Class对象方式
  • 3、Class对象功能
    • 3.1 获取功能(部分,具体参考api文档)
      • 1、获取成员变量
      • 2、获取构造方法
      • 3、获取成员方法
      • 4、获取全类名
    • 3.2 java.lang.reflect.Field:成员变量
    • 3.3 java.lang.reflect.Method:方法对象
  • 4、反射机制的应用案例
    • 需求
    • 实现
    • 思路
    • 代码实现

1、反射:框架的灵魂

1.1 概述

  • 框架:半成品软件。可以在框架的基础上进行软件开发,简化编码
  • 反射:将类的各个组成部分封装为其他对象,这就是反射机制;学习使用框架并不需要了解反射,但是如果想要自己写一个框架,那么就需要对反射机制有很深入的了解。

    好处:

    1. 可以在程序运行过程中,操作这些对象。

    2. 可以解耦,提高程序的可扩展性。

在学习反射以前,我们先来了解一下Java代码在计算机中所经历的三个阶段:

【Java——反射】1、反射:框架的灵魂2、获取Class对象3、Class对象功能4、反射机制的应用案例

1、Source源代码阶段:.java被编译成*.class字节码文件。

2、Class类对象阶段:.class字节码文件被类加载器加载进内存,并将其封装成Class对象(用于在内存中描述字节码文件),Class对象将原字节码文件中的成员变量抽取出来封装成数组Field[],将原字节码文件中的构造函数抽取出来封装成数组Construction[],将成员方法封装成数组Method[]。当然Class类内不止这三个,还封装了很多,我们常用的就这三个。

3、RunTime运行时阶段:使用new创建对象的过程。

2、获取Class对象

2.1 获取Class对象方式

  1. Class.forName(“全类名”):将字节码文件加载进内存,返回Class对象

    * 多用于配置文件,将类名定义在配置文件中。读取文件,加载类

    2. 类名.class:通过类名的属性class获取

    * 多用于参数的传递

    3. 对象.getClass():getClass()方法在Object类中定义着。

    * 多用于对象的获取字节码的方式

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

代码实现:

//方法一:对象.getClass()
public void test01(){
        Person p = new Person();
        Class<? extends Person> aClass = p.getClass();
        System.out.println(aClass);
    }
    
//方法二:类名.class
public void test02(){
        Class<Person> personClass = Person.class;
        System.out.println(personClass);
    }
           
test01和test02获取类对象,都需要依赖api Person
//方法三:Class.forName("全类名");
  public void test03() throws ClassNotFoundException {
        //手动加载Person.class文件进内存
        Class aClass = Class.forName("net.zretc.reflect.Person");//传入Person的路径
        System.out.println(aClass);
    }
           

3、Class对象功能

3.1 获取功能(部分,具体参考api文档)

1、获取成员变量

Field[] getFields()          //获取所有public修饰的成员变量
Field getField(String name)  //获取指定名称的public修饰的成员变量

Field[] getDeclaredFields()  //获取所有的成员变量,不考虑修饰符
Field getDeclaredField(String name)  //获取指定的成员变量,不考虑修饰符
           

2、获取构造方法

Constructor<?>[] getConstructors() //获取所有public修饰的构造函数
Constructor<T> getConstructor(类<?>... parameterTypes)  //获取指定的public修饰的构造函数

Constructor<?>[] getDeclaredConstructors()  //获取所有的构造函数,不考虑修饰符
Constructor<T> getDeclaredConstructor(类<?>... parameterTypes)  //获取指定的构造函数,不考虑修饰符
           

3、获取成员方法

Method[] getMethods()           //获取所有public修饰的成员方法
Method getMethod(String name, 类<?>... parameterTypes) //获取指定名称的public修饰的成员方法

Method[] getDeclaredMethods()  //获取所有的成员方法,不考虑修饰符
Method getDeclaredMethod(String name, 类<?>... parameterTypes) //获取指定名称的成员方法,不考虑修饰符
           

4、获取全类名

String getName() 
           

3.2 java.lang.reflect.Field:成员变量

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

Field的get/set方法举例代码:

测试实体类:

class SupClass{
    public String gendar;
}
public class Person {
    public String name;
    byte[] b;
    private int age;
    public void show(){
        System.out.println("show------null----");
    }
    public void show(String name,int age){
        System.out.println("show------name---age----");
    }
    private void sup(){
        System.out.println("sup---");
    }
}
           

测试类:

public void test06() throws Exception {
        Class pClass = Class.forName("net.zretc.reflect.Person");
        Field name_field = pClass.getDeclaredField("name");//私有变量
        Person person = (Person) pClass.newInstance();//实例化对象

        //设置访问权限:暴力反射
        name_field.setAccessible(true);

        Object name_value = name_field.get(person);//person.getName()
        System.out.println(name_value);
    }
    
           
public void test05() throws Exception {
        Class pClass = Class.forName("net.zretc.reflect.Person");
        Field age_field = pClass.getField("age");
        
        //实例化Person
        Person person = (Person) pClass.newInstance();//newInstance相当于执行的空参数的构造器

        //设置值
        age_field.set(person,20);               //person.setAge(20);

        //查看age变量的值
        Object age_value = age_field.get(person);       //  person.getAge()

        System.out.println(age_value);
    }
           

3.3 java.lang.reflect.Method:方法对象

  • 执行方法:Object invoke(Object obj, Object… args) 没有返回值,为null
  • 获取方法名称:String getName:获取方法名

    代码展示

public void test08() throws Exception {
        //类对象
        Class pClass = Class.forName("net.zretc.reflect.Person");
        //实例化Person
        Person person = (Person) pClass.newInstance();

        Method test = pClass.getDeclaredMethod("test");//私有化方法

        //暴力反射
        test.setAccessible(true);
        test.invoke(person);

        //show方法对象
        Method show = pClass.getMethod("show", String.class, int.class);
        //执行show方法: person.show("hehe",30)
        Object result = show.invoke(person, "hehe", 30);
        System.out.println(result);//没有返回值,为null
    }
           

4、反射机制的应用案例

需求

写一个"框架",在不改变该类的任何代码的前提下,可以帮我们创建任意类的对象,并且执行其中的任意方法。

实现

(1)配置文件

(2)反射机制

思路

(1)将需要创建的对象的全类名和需要执行的方法定义在配置文件中

(2)在程序中加载读取配置文件

(3)使用反射技术把类文件加载进内存

(4)创建对象

(5)执行方法

代码实现

实体类:

Person 类

public class Person {
    //无参方法
    public void eat(){
        System.out.println("eat...");
    }
}

           

Student类

public class Student {
    //无参方法
    public void study(){
        System.out.println("I am a Student");
    }
}
           

配置文件编写:

className =zretc.edu.cn.Person
methodName = eat
           

代码:

public class ReflectTest {
    public static void main(String[] args) throws Exception {
    
        //1.加载配置文件
        //1.1创建Properties对象
        Properties pro = new Properties();
        //1.2加载配置文件
        //1.2.1获取class目录下的配置文件(使用类加载器)
        ClassLoader classLoader = ReflectTest.class.getClassLoader();
        InputStream inputStream = classLoader.getResourceAsStream("pro.properties");
        pro.load(inputStream);

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

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