java反射机制

本文详细介绍了Java反射机制,包括概念、获取字节码的三种方式、Class字节码文件的功能,如获取属性、方法和构造器,并通过实例展示了如何使用反射执行方法和创建对象。此外,还提供了一个简单的框架案例,演示了如何利用反射和配置文件实现动态调用任意类的方法,强调了反射在框架开发中的重要性和灵活性。
摘要由CSDN通过智能技术生成

java反射机制

1、反射的概念

概述

  • Java的反射(reflection)机制是指在程序的运行状态中,可以构造任意一个类的对象,可以了解任意一个对象所属的类,可以了解任意一个类的成员变量和方法,可以调用任意一个对象的属性和方法。这种动态获取程序信息以及动态调用对象的功能称为Java语言的反射机制。反射被视为动态语言的关键。
  • 当我们拿到了一个类的字节码文件时,就可以通过反射来拿到该类中,所有的属性、方法、及其构造方法等等。
  • 框架的底层一般都涉及到反射机制,反射是框架开发的灵魂。
  • 解耦,增强程序的可扩展性。

2、获取字节码

环境 类

package com.qiumin.library_management_system.test;

/**
 * @Author: qiumin
 * @Description: love code
 * @Date: Created in 22:13 2022/4/19
 */
public class Fanshe {

    public String name;
    private Integer code;

    public Fanshe() {
    }

    public Fanshe(String name, Integer code) {
        this.name = name;
        this.code = code;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Integer getCode() {
        return code;
    }

    public void setCode(Integer code) {
        this.code = code;
    }

    @Override
    public String toString() {
        return "Fanshe{" +
                "name='" + name + '\'' +
                ", code=" + code +
                '}';
    }

    public void eat(){
        System.out.println("eat--------方法");
    }

    public void eat(Integer d){
        System.out.println("eat--------方法"+d);
    }
}

获取Class字节码的三种方式

  • Class aClass = Class.forName(“com.qiumin.library_management_system.test.Fanshe”);·//括号里填类的全类名即包加类的全限定名
@Test
void testFanshe(){
    try {
        //括号里填类的全类名即包加类的全限定名
        Class aClass = Class.forName("com.qiumin.library_management_system.test.Fanshe");
        System.out.println(aClass);
    } catch (ClassNotFoundException | NoSuchFieldException e) {
        e.printStackTrace();
    }
}

  • 直接类名.class Fanshe.class
    @Test
    void testFanshe(){
        try {
            Class<Fanshe> aClass = Fanshe.class;  //类名.class
            System.out.println(aClass);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

  • 当有该类的对象时可以用 **对象名.getClass()**得到
    @Test
    void testFanshe(){
        try {
            Fanshe fanshe = new Fanshe();  //new出了对象
            Class aClass = fanshe.getClass();  //用对象得到class
            System.out.println(aClass);

        } catch (Exception e) {
            e.printStackTrace();
        }
    }

在这里插入图片描述

无论用那种方式得到的class文件都是同一份,即相等

3、class字节码文件的功能

获取类中的属性

  • Field name1 = aClass.getField(“name”); 【获取名为name的属性 public

  • Field[] fields1 = aClass.getFields(); 【获取所有的属性 public

  • Field name = aClass.getDeclaredField(“name”); 【获取名为name属性 包括非public的属性】

  • Field[] fields = aClass.getDeclaredFields(); 【获取所有的属性 包括非public的属性】

  • Object o = name1.get(fanshe); 【查看属性的值 里面需要传入该类的对象fanshe】

  • name1.set(fanshe,“qiumin”); 【设置属性的值,第一个参数为该类的对象fanshe,第二个是设置的值】

@Test
void testFanshe(){
    try {
        //得到class
        Fanshe fanshe = new Fanshe();
        Class aClass = fanshe.getClass();
        System.out.println(aClass);

        //获取属性
        Field name1 = aClass.getField("name");
        //查看属性的值
        Object o = name1.get(fanshe);  //里面需要传入该类的对象
        System.out.println(o);
        name1.set(fanshe,"qiumin");    //设置属性的值,第一个参数为该类的对象,第二个是设置的值
        System.out.println(fanshe);
        //查看上述输出结果 下图
        Field[] fields1 = aClass.getFields();
        Field[] fields = aClass.getDeclaredField("name");
        Field[] fields = aClass.getDeclaredFields();
        System.out.println(fields);
    } catch (Exception e) {
        e.printStackTrace();
    }
}

在这里插入图片描述

注意:

  1. 没有带有**Declared**的方法得到的属性只能是public修饰的 如:Field name1 = aClass.getField(“name”);只能获取public修饰的。
  2. 当通过带有Declared 的方法得到的private修饰属性要设置值时我们要设置暴力反射,否则就不能设置值
  3. 设置暴力反射:属性.setAccessible(true);

获取类中的方法

  • Method eat = aClass.getMethod(参数1方法名,参数2方法名中的参数列表); 【获取指定非构造方法的类方法 public
  • Method[] methods = aClass.getMethods(); 【获取所有非构造方法的类方法 public
  • Method methods2 = aClass.getDeclaredMethod(参数1方法名,参数2方法名中的参数列表); 【获取所有非构造方法的指定类方法 】
  • Method[] methods3 = aClass.getDeclaredMethods(); 【获取所有非构造方法的类方法 】
  • 执行方法 method.invoke(参数1该类对象名,参数2参数列表值 ) 相当于调用方法
@Test
void testFanshe(){
    try {
        //得到class
        Fanshe fanshe = new Fanshe();
        Class aClass = fanshe.getClass();
        System.out.println(aClass);

        Method eat = aClass.getMethod("eat");   //得到类中的无参的eat方法
        Method eat2 = aClass.getMethod("eat",Integer.class);  //得到类中的有参的eat方法
        System.out.println(eat);
        System.out.println(eat2);
        eat.invoke(fanshe);    //执行方法  因为调用无参的eat所以只传入该类对象即可
        eat2.invoke(fanshe,6);  //执行方法   调用有参数的eat

        Method[] methods = aClass.getMethods();
        Method methods2 = aClass.getDeclaredMethod("eat");
        Method[] methods3 = aClass.getDeclaredMethods();

    } catch (Exception e) {
        e.printStackTrace();
    }
}

在这里插入图片描述

注意:

  1. 没有带有**Declared**的方法得到的构造方法只能是public修饰的
  2. 设置暴力反射:方法名.setAccessible(true);
  3. 执行方法 方法名.invoke(对象名,参数)

获取类的构造方法

  • Constructor constructor = aClass.getConstructor(String.class, Integer.class); 【获取参数类型为括号中类型的构造器,不填为无参构造 public
  • Constructor[] constructors = aClass.getConstructors(); 【获取无参的构造器 public
  • Constructor declaredConstructor = aClass.getDeclaredConstructor(String.class, Integer.class); 【获取所有参数类型为括号中类型的构造器,不填为无参构造】
  • Constructor[] declaredConstructors = aClass.getDeclaredConstructors(); 【获取无参的构造器】
  • 利用构造方法创建对象 Object o = constructor.newInstance(“qiumin”,200); //利用构造器创建对象
@Test
void testFanshe(){
    try {
        //得到class
        Fanshe fanshe = new Fanshe();
        Class aClass = fanshe.getClass();
        System.out.println(aClass);
        //得到类中的有参构造方法 括号传入构造方法参数的类型,不写为空参构造
        Constructor constructor = aClass.getConstructor(String.class, Integer.class);
        Object o = constructor.newInstance("qiumin",200);  //利用构造器创建对象
        System.out.println(o);

        Constructor[] constructors = aClass.getConstructors();
        Constructor declaredConstructor = aClass.getDeclaredConstructor(String.class, Integer.class);
        Constructor[] declaredConstructors = aClass.getDeclaredConstructors();
    } catch (Exception e) {
        e.printStackTrace();
    }
}

在这里插入图片描述

注意:

  1. 没有带有**Declared**的方法得到的构造方法只能是public修饰的
  2. 设置暴力反射:构造方法.setAccessible(true);
  3. 当是无参构造创建对象时可以简化 直接 字节码对象.newInstance()

获取类名

  • String name = aClass.getName(); 【获取全限定类名 com.qiumin.library_management_system.test.Fanshe】
  • String simpleName = aClass.getSimpleName(); 【获取简短类名 Fanshe】
    @Test
    void testFanshe(){
        try {
            //得到class
            Fanshe fanshe = new Fanshe();
            Class aClass = fanshe.getClass();
            System.out.println(aClass);

            String name = aClass.getName();  //获取全限定类名
            System.out.println(name);
            String simpleName = aClass.getSimpleName();
            System.out.println(simpleName);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

在这里插入图片描述

4、反射案例

业务需求:写一个框架,我们可以获取任意类即任意方法

实现:

  1. 配置文件【properties】
  2. 反射机制

步骤:

  • 将需要创建的对象的全类名和需要执行的的方法写入配置文件
  • 加载配置文件
  • 反射机制将该类进内存
  • 创建对象
  • 执行方法

1、类

package com.qiumin.library_management_system.test;

/**
 * @Author: qiumin
 * @Description: love code
 * @Date: Created in 22:13 2022/4/19
 */
public class Fanshe {

    public void sleep(){
        System.out.println("sleep.......");
    }
}

package com.qiumin.library_management_system.test;

/**
 * @Author: qiumin
 * @Description: love code
 * @Date: Created in 11:44 2022/4/20
 */
public class Fanshe2 {

    public void eat(){
        System.out.println("eat.......");
    }
}

2、properties

className=com.qiumin.library_management_system.test.Fanshe
method=sleep
  • 注意:spring boot中改文件需放在resource下面
  • 将需要的类和方法配置在properties文件下
  • 我们只要改配置文件,不需要改程序代码

测试

package com.qiumin.library_management_system.test;

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

/**
 * @Author: qiumin
 * @Description: love code
 * @Date: Created in 12:01 2022/4/20
 */
public class Test {
    public static void main(String[] args) throws Exception {
        //new 出properties对象
        Properties pro = new Properties();
        //得到该类的类加载器
        ClassLoader classLoader = Test.class.getClassLoader();
        //通过类加载器加载properties配置文件流文件
        InputStream stream = classLoader.getResourceAsStream("Fanshe.properties");
        //加载
        pro.load(stream);

        //获取配置文件的指定值
        String className = pro.getProperty("className");
        String sleep = pro.getProperty("method");

        //通过方式得到自定类的字节码文件
        Class aClass = Class.forName(className);
        //创建对象
        Object o = aClass.newInstance();
        //获取方法
        Method sleep1 = aClass.getMethod(sleep);
        //执行方法
        sleep1.invoke(o);

    }
}

在这里插入图片描述

需要扩展改配置文件即可,不需动程序代码,提高可扩展性,这就是反射的好处之一

5、总结

  • 反射机制是编写框架的基础,反射是框架的灵魂。
  • 通过反射可以了解一个类的所有的东西。
  • 反射是java基础的的基础,一般来说反射跟注解一起使用会简化开发。
  • 不需动程序代码,提高可扩展性,这就是反射的好处之一。
  • 一般的使用场景为:反射机制+properties配置文件

qiumin

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值