Java基础之反射

目录

​编辑

一、反射的应用场景

二、反射不适用场景

三、动态加载类创建类的实例(对象)

四、通过反射构造函数创建类的实例(对象)

五、反射操作成员变量

六、反射操作方法


 

一、反射的应用场景

        Java 反射机制在动态加载类、探索类的属性和方法、处理注解、序列化等方面具有广泛的应用。它为开发人员提供了更大的灵活性和可扩展性,使得在运行时动态地操作和扩展类成为可能。

        下面是反射的适用场景:

        1、需要动态加载类

        使用反射可以在运行时动态加载类,而不需要在编译时确定类的名称。这在某些情况下非常有用,例如在插件化系统中,可以根据配置或用户需求动态加载并实例化不同的类。

        2、探索和操作类的属性

        反射提供了一种检查和操作类的属性(字段)的能力。通过反射,可以获取类的字段名称、类型、修饰符等信息,并且可以读取和修改类的字段值。

        3、调用类的方法

        反射可以用于调用类的方法,包括公共方法、私有方法和静态方法。这使得在运行时根据条件或配置动态地调用不同的方法成为可能。

        3、注解处理

        反射可以用于处理注解。可以检查类、方法、字段上的注解,并根据注解的信息执行相应的逻辑。这在编写通用框架或编写基于注解的扩展机制时非常有用。

        5、序列化和反序列化:

        Java 的序列化和反序列化机制依赖于反射。通过反射,可以动态地读取和写入对象的字段值,实现对象的序列化和反序列化。

        6、单元测试:

        在编写单元测试时,反射可以用于访问和修改私有字段和方法,以便进行测试。这样可以绕过访问限制,使得测试更加灵活和全面。

二、反射不适用场景

        尽管反射提供了强大的功能,但它也带来了一些性能和安全方面的考虑。使用反射时,需要小心处理访问权限,避免滥用反射导致性能下降或不安全的操作。在性能要求高、安全敏感、编译时确定类和方法、对象创建和代码混淆等场景下,可能不适合使用反射。

        下面是反射的不适用场景:

        1、性能要求高的场景

        相对于直接调用方法或访问字段,使用反射会导致性能上的损失。反射需要进行额外的查找和验证操作,因此在对性能要求非常高的场景下,应该避免过度使用反射。

        2、安全敏感的场景

        反射可以绕过访问修饰符的限制,访问和修改私有成员。在安全敏感的场景中,可能不希望代码有这种能力,因为它可以破坏封装性和引入潜在的安全风险。

        3、编译时确定类和方法的场景

        如果在编译时已经知道要使用的类和方法,并且它们的结构是固定的,那么使用反射可能是不必要的。反射更适合于在运行时根据动态条件或配置来处理不同的类和方法。

        4、对象创建的场景

        在大多数情况下,可以使用普通的类实例化方式(通过构造函数或静态工厂方法)来创建对象。反射的对象实例化方法(newInstance())相对较慢且不够直观,因此在对象创建的场景中可能不需要使用反射。

        5、混淆代码的场景

        如果计划对代码进行混淆,以增加代码的安全性和减小代码体积,那么使用反射将会遇到困难。因为反射依赖于类和方法的名称,混淆会改变这些名称,导致反射无法正常工作。

三、动态加载类创建类的实例(对象)

        反射机制允许在运行时动态地获取类的信息、调用类的方法和访问类的字段,而不需要在编译时确定类的具体名称。

        Class.forName() 方法接受一个字符串参数,该字符串表示要加载的类的完全限定名(Fully Qualified Name)。

先提供一个测试类,后面所有代码均需此类不再赘述:

package com.test;

    public class TestInfo{
        public TestInfo(){

        }

        public TestInfo(int id, String name) {
            this.id = id;
            this.name = name;
        }

        private String Test1;
        private String Test2;
        public String Test3;
        public String Test4;

        private int id;
        private String name;

        public int getId() {
            return id;
        }

        public void setId(int id) {
            this.id = id;
        }

        public String getName() {
            return name;
        }

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


        private void   Test1() {

        }
        public void   Test2() {

        }
        public String  Test3(String value){
            return value + "-成功执行";
        }

    }

测试代码:

        try {

            //1、通过 完全限定名 加载类的Class对象
            Class c1 = Class.forName("com.test.TestInfo");

            //2、通过类的 class 属性获取类的Class对象
            Class c2 = TestInfo.class;

            //3、通过对象获取类的Class对象
            TestInfo testInfo = new TestInfo();
            Class c3 = testInfo.getClass();

            //4、通过Class对象创建类的实例
            TestInfo testInfo1 =  (TestInfo) c1.newInstance();
            TestInfo testInfo2 =  (TestInfo) c1.newInstance();
            TestInfo testInfo3 =  (TestInfo) c1.newInstance();

        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InstantiationException e) {
            e.printStackTrace();
        }

四、通过反射构造函数创建类的实例(对象)

        首先获取TestInfo的 Class 对象,然后使用 getConstructor() 方法获取默认无参构造函数的 Constructor 对象。接最后使用 newInstance() 方法创建实例,并强制类型转换为 TestInfo类型。        

测试代码:

        Class c = TestInfo.class;
        try {
            Constructor constructor1 = c.getConstructor();
            Constructor constructor2 = c.getConstructor(new Class[]{ int.class, String.class });
            TestInfo testInfo1 = (TestInfo)constructor1.newInstance();
            TestInfo testInfo2 = (TestInfo)constructor2.newInstance(new Object[]{ 1, "构造方法传入值"});

            System.out.println("通过无参构造函数创建的类的值:"+testInfo1.getName());
            System.out.println("通过有参构造函数创建的类的值:"+testInfo2.getName());
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }

五、反射操作成员变量

        使用反射可以获取和操作类的字段(成员变量)。

        以下是几种常用的反射字段操作方法:

        1、getDeclaredField(String name): 获取指定名称的类的字段(包括私有字段)。
        2、getField(String name): 获取指定名称的类的公共字段。
        3、getDeclaredFields(): 获取类中声明的所有字段(包括私有字段)。
        4、getFields(): 获取类中的所有公共字段。
        5、set(Object obj, Object value): 设置指定对象的字段值。
        6、get(Object obj): 获取指定对象的字段值。

测试代码:


        TestInfo testInfo = new TestInfo();
        testInfo.Test3 = "初始值";
        Class c = testInfo.getClass();

        //获取类的公共(Public)字段(Field)
        Field[] fields1 = c.getFields();
        for(Field field : fields1){
            System.out.println("通过反射获到的公共字段名称:"+field.getName());
        }
        //获取类的所有字段(Field)
        Field[] fields2 =  c.getDeclaredFields();
        for(Field field : fields1){
            System.out.println("通过反射获到的所有字段名称:"+field.getName());
        }

        //下面的示例是通过反射来获取和设置字段的值。当然私有属性只需将 getField 换成 getDeclaredField
        Field f = null;
        try {
            f = c.getField("Test3");
            System.out.println("通过反射获到的单个字段名称:"+f.getName());

            Object value = f.get(testInfo);
            System.out.println("通过反射获到的单个字段的值:"+value);

            f.set(testInfo, "通过反射设置的名称");
            System.out.println("查看对象实际值:"+testInfo.Test3);

        } catch (NoSuchFieldException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }

六、反射操作方法

        使用反射可以获取和调用类的方法。

        以下是几种常用的反射方法操作方法:

        1、getDeclaredMethod(String name, Class<?>... parameterTypes): 获取指定名称和参数类型的类的方法(包括私有方法)。

        2、getMethod(String name, Class<?>... parameterTypes): 获取指定名称和参数类型的类的公共方法。

        3、getDeclaredMethods(): 获取类中声明的所有方法(包括私有方法)。

        4、invoke(Object obj, Object... args): 调用指定对象的方法。

测试代码:


        TestInfo testInfo = new TestInfo();
        testInfo.Test3 = "初始值";
        Class c = testInfo.getClass();

        //获取类的公共(Public)方法(Method)
        Method[] methods1 = c.getMethods();
        for(Method method : methods1){
            System.out.println("通过反射获到的公共方法名称:"+method.getName());
        }
        //获取类的所有方法(Method)
        Method[] methods2 =  c.getDeclaredMethods();
        for(Method method : methods2){
            System.out.println("通过反射获到的所有方法名称:"+method.getName());
        }

        //下面的示例是通过反射来获取和调用公共方法。当然私有方法只需将 getMethod 换成 getDeclaredMethod
        try {
            //1、获取无参方法(Method)
            Method m1 = c.getMethod("Test2");
            //2、获取有参方法(Method)
            Method m2 = c.getMethod("Test3", new Class[]{ String.class });
            System.out.println("通过反射获到的单个方法名称:"+m2.getName());

            //调用无参方法
            m1.invoke(testInfo);
            //调用有参方法
            Object value = m2.invoke(testInfo, "通过反射调用方法");
            System.out.println("通过反射调用方法获取返回:"+value);


        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

QIFU

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值