反射机制简介

JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法;这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制。


Java反射机制主要提供了以下功能: 

  1. 在运行时,判断任意一个对象所属的类;
  2. 在运行时,构造任意一个类的对象;
  3. 在运行时,判断任意一个类所具有的成员变量和方法;
  4. 在运行时,调用任意一个对象的方法;生成动态代理。

 

  • 获取类对象

 类对象概念

所有的类,都存在一个类对象,这个类对象用于提供类本身的信息,比如有几种构造方法, 有多少属性,有哪些普通方法。


什么是类对象

在理解类对象之前,先说我们熟悉的对象之间的区别
garen和teemo都是Hero对象,他们的区别在于,各自有不同的名称,血量,伤害值

然后说说类之间的区别:
Hero和Item都是类,他们的区别在于有不同的方法,不同的属性

类对象,就是用于描述这种类,都有什么属性,什么方法的。


获取类对象

获取类对象有3种方式

  1. Class.forName
  2. Hero.class
  3. new Hero().getClass()


在一个JVM中,一种类,只会有一个类对象存在。所以以上三种方式取出来的类对象,都是一样的。
注: 

准确的讲是一个ClassLoader(类加载器)下,一种类,只会有一个类对象存在。

通常一个JVM下,只会有一个ClassLoader。


获取类对象的时候,会导致类属性被初始化

无论什么途径获取类对象,都会导致静态属性被初始化,而且只会执行一次

(除了直接使用 Class c = Hero.class 这种方式,这种方式不会导致静态属性被初始化)


  • 创建对象

创建一个对象(传统与反射方式对比)

与传统的通过new 来获取对象的方式不同 
反射机制,会先拿到Hero的“类对象”,然后通过类对象获取“构造器对象” 
再通过构造器对象创建一个对象 

package reflection;
import java.lang.reflect.Constructor;
import charactor.Hero;
public class TestReflection {
  
    public static void main(String[] args) {
        //传统的使用new的方式创建对象
        Hero h1 =new Hero();
        h1.name = "teemo";
        System.out.println(h1);
          
        try {
            //使用反射的方式创建对象
            String className = "charactor.Hero"; //包名.类名
            //类对象
            Class pClass=Class.forName(className);
            //构造器
            Constructor c= pClass.getConstructor();
            //通过构造器实例化
            Hero h2= (Hero) c.newInstance();
            h2.name="gareen";
            System.out.println(h2);
        } catch (Exception e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }
}

  • 访问属性

通过反射机制修改对象的属性

为了访问属性,把name修改为public。
对于private修饰的成员,需要使用setAccessible(true)才能访问和修改。

package charactor;
 
public class Hero {
    public String name;
    public float hp;

通过传统和反射方式修改属性的值对比:

package reflection;
 
import java.lang.reflect.Field;
 
import charactor.Hero;
  
public class TestReflection {
  
    public static void main(String[] args) {
            Hero h =new Hero();
            //使用传统方式修改name的值为garen
            h.name = "garen";
            try {
                //获取类Hero的名字叫做name的字段
                Field f1= h.getClass().getDeclaredField("name");

                //f1.setAccessible(true); //当字段private修饰时,需要这句话

                //修改这个字段的值
                f1.set(h, "teemo");
                //打印被修改后的值
                System.out.println(h.name);
                 
            } catch (Exception e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
    }
}

  • 调用方法

通过反射机制,调用一个对象的方法

首先为Hero的name属性,增加setter和getter
通过反射机制调用Hero的setName

package reflection;
 
import java.lang.reflect.Method;
 
import charactor.Hero;
 
public class TestReflection {
 
    public static void main(String[] args) {
        Hero h = new Hero();
 
        try {
            // 获取这个名字叫做setName,参数类型是String的方法
            Method m = h.getClass().getMethod("setName", String.class);
            // 对h对象,调用这个方法
            m.invoke(h, "盖伦");

            // 使用传统的方式,调用getName方法
            System.out.println(h.getName());
 
        } catch (Exception e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
 
    }
}

反射机制有什么用?

反射非常强大,但是学习了之后,会不知道该如何使用,反而觉得还不如直接调用方法来的直接和方便。 
通常来说,需要在学习了Spring 的依赖注入,反转控制之后,才会对反射有更好的理解。

参详:http://how2j.cn/k/reflection/reflection-usage/1111.html#nowhere


 

  • 反射机制中一些方法的区别于解析

getField() 和 getDeclaredField() 的区别(与下面获取方法类似)

这两个方法都是用于获取字段


getField() 只能获取public的,包括从父类继承来的字段。


getDeclaredField() 可以获取当前类所有的字段,包括private的,但是不能获取继承的字段。

(注: 这里只能获取到private的字段,但并不能访问该private字段的,除非加上setAccessible(true),才能访问和修改)

field.setAccessible(true)的用处是赋予反射对象超级权限,绕过语言权限检查


扩展:

从方法名称上也可以看出来,setAccessible(true),设置是否允许访问,而不是修改原来的访问权限修饰词。

需要注意setAccessible 并不是在Field中的,而是在AccessibleObject中

源码对AccessibleObject的解释:

意思是 AccessibleObject  类是 Field Method Constructor 类的基类。它提供反射对象绕过Java语言权限控制检查的权限。

当Fields Methods Constructors被用来set get 对象域,调用方法或者产生初始化对象实例的时候会践行权限检查(public default(package) protected private)。

将反射对象中的 accessible 标志位设置为 true,就意味着允许客户端拥有超级权限,比如Java对象序列化 或者 其他持久化机制等通常禁止的机制。

所以我们在accessible 标志位设置为true 的时候需要非常谨慎,这会带来一定的安全隐患


源码中对 field.setAccessible(true);  方法的解释。

意思就是改方式是用来设置获取权限的。

如果 accessible 标志被设置为true,那么反射对象在使用的时候,不会去检查Java语言权限控制(private之类的);

如果设置为false,反射对象在使用的时候,会检查Java语言权限控制。

需要注意的是,设置为true会引起安全隐患。


getMethods() 和 getDeclaredMethods() 的区别(与上面获取字段类似)

这两个方法都是获取类对象的方法

getMethod():获取当前类及所有继承的父类的public修饰的方法。仅包括public

getDeclaredMethod():获取当前类所有方法,包括public/private/protected/default修饰的方法,但是不能获取继承的方法。

我觉得一般使用 getMethod() 就可以满足开发需求,因为一般方法都是public修饰。


 

参考源于:

http://how2j.cn/k/reflection/reflection-class/108.html

https://www.cnblogs.com/cuglkb/p/8463039.html

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值