java中反射机制的含义_Java中的反射机制

本文深入解析Java反射机制,介绍Class实例的获取方式、反射机制的应用场景,如获取类信息、访问字段、调用方法和构造方法,以及获取继承关系。

java中除了基本类型以外都是class,interface也是广义上的class(例如:String,Object,Runnable,Exception),class的本质就是数据类型,我们把一个对象的实例赋值给一种 数据类型变量时,我们必须严格按照数据类型来赋值。没有继承关系的数据类型是无法赋值的。所以Java定义了一种强类型的数据关系,并且编译器会检查数据类型,不符合数据类型要求的就会报错

class和interface的数据类型是Class,JVM在加载class时,每加载一个class,JVM就为其创建一个Class类型的实例并关联起来。JVM持有的每一个Class实例都指向一个数据类型,例如:

而一个Class实例会包含该class的完整信息,例如类名,包等:

简而言之,Class就是JVM为每个class创建对应的Class实例,并在实例中保存该class的所有信息。那么,如果我们获取到了某个Class实例,我们就可以获取到该实例对应的class的所有信息,这种通过Class实例获取class信息的方法称为反射。

如何获取一个class的Class实例呢,有三种方法,

方法一:类名.class

方法二:对实例变量使用.getClass()方法

方法三:使用Class的静态方法forName()

在JVM中,一个Class实例对应唯一的class,因此我们可以用==来比较两个Class实例,那么这和instanceof的差别是什么呢?

instanceof不仅匹配当前类型,还会匹配当前类型的子类:

而使用==时,会精确地判断当前类型

由此可见,通常我们应该使用instanceof来判断数据类型,因为面向抽象编程时我们并不关心具体的子类型,只有在需要精确判断class时,才需要用==。

那么反射机制的意义是什么呢?

1.当获得某个Object实例时,我们可以获取该Object的class信息。

那么我们在Class实例中可以获取哪些class信息呢?从Class实例获取class信息的方法有: getName() getSimpleName() getPackage()等

此外,从Class实例还可以判断class的类型:

isInterface() 是否为interface

isEnum() 是否为枚举

isArray() 是否为数组

isPrimitive()是不是基本类型

前面我们说了基本类型变量本身并不是class,但是JVM的内部会为其创建int.class这几种基本类型对应的Class实例。

我们还可以通过Class的newInstance()来创建class实例:

这里的局限性在于我们只能使用不带参数的public默认构造方法,带参数的构造方法是不能这样调用的。

我们知道,JVM在加载class时是动态加载的,利用这个特性,可以在运行期根据条件不同加载不同的实现类。这样,就算某个包不存在,我们也可以通过条件判断来控制加载。例如:

2.访问字段:

我们先来看一下java中的属性与字段的区别

java中的Java中的属性(property),通常可以理解为get和set方法,而字段(field),通常叫做“类成员”,或 “类成员变量”,有时也叫“域”,理解为“数据成员”,用来承载数据的。

例如:

这里的属性a由get和set组成

这里的b没有get和set,是字段

这里的c由字段c和get/set组成

区别开属性与字段是为了更好的实现数据安全,比如当我们想给一个类的属性赋值或者其他类用到了,就需要将这个字段设置为public,然而这样可以对字段进行任意的读写操作,非常不利于数据安全。于是就加上了属性,简单说属性实现了字段的封装,属性有get、set 方法来控制字段,该字段的属性只有set方法没有get方法,就只可以对该方法进行赋值操作,没有读操作,反之亦然。就是对字段的操作通过属性来控制。

而通过Class实例,我们可以获取字段field信息:

getField(name):获取某个public的field(包括父类)

getDeclaredField(name):获取当前类的某个field(不包括父类)

getFields():获取所有public的field(包括父类)

getDeclaredFields():获取当前类的所有field(不包括父类)

在Field对象中包含一个field的所有信息,可以用以下方法获取:

getName() 名称

getType() 字段定义类型

getModifiers() 修饰符类型,返回的是1,2,....

通过field对象还可以获得和设置field的值:

get(Object obj)

set(Object, Object)

对于静态字段,我们在使用get和set时不需要传入实例,传入null即可

当我们需要访问private字段时,可以使用以下方法:

但是此方法有失败的可能,如果JVM定义了SecurityManager,那么会有一个规则,阻止我们对该Field设置accessible,抛出异常,例如,把规则应用于所有java和javax开头的package的类,通常我们编写的类以及第三方的类是没有这个限制的。

3.调用方法

Method对象中封装了方法的所有信息

通过Class实例可以获取到method

getMethod(name, Class...):获取某个public的method(包括父类)

getDeclaredMethod(name, Class...):获取当前类的某个method(不包括父类)

getMethods():获取所有public的method(包括父类)

getDeclaredMethods():获取当前类的所有method(不包括父类)

这里的Class参数是方法参数,是可变参数

与Field对象类似,Method对象包含了一个method的所有信息

getName()

getReturnType() 返回类型

getParameterTypes() 参数类型

getModifiers()

我们获取到method对象时,可以直接在某个实例上调用这个方法

调用无参数Method: Object invoke(Object obj)

调用含参数Method: Object invoke(Object obj, Object... args)

同样的,对于private方法,我们同样可以使用setAccessible(true),但是也同样受到SecurityManager的制约。

另外,对于从父类获取到的方法,当我们传入一个子类的实例时,实际调用的是子类覆写的方法,这样保证了多态的正确性。

4.构造方法的调用

我们前面说到,使用Class.newInstance()可以调用public无参数的默认构造方法,但是对于没有定义无参数构造方法的Class,不能直接使用newInstance。对于带参数的构造方法,我们要使用Constructor对象,Constructor对象包含了一个构造方法的所有信息,可以用于创建实例。

getConstructor(Class...):获取某个public的Constructor

getDeclaredConstructor(Class...):获取某个Constructor

getConstructors():获取所有public的Constructor

getDeclaredConstructors():获取所有Constructor

对于非public构造方法,我们可以同样可以通过设置setAccessible(true)来访问。

5.获取继承关系:

获取父类的Class:Class getSuperclass()

tip:Object的父类是null ,interface的父类是null

获取当前类直接实现的interface:Class[] getInterfaces()

返回的数组中不包括间接实现的interface ,没有interface的class返回空数组, 而对于interface,返回的是继承的interface

我们还可以使用bool isAssignableFrom(Class)判断一个向上转型是否成立:

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符  | 博主筛选后可见
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值