java 检视_java 的反射机制

绪论

首先,什么是反射机制?

它是一种 “运行时检视类型信息”、“修改属性”、“调用方法”的一种机制,

其次,常用的运用场景有哪些?

运行时实例化类的对象。(例如:实例化一个 User 类,此 User 类的名字容器事先是不知道的)

运行时调用方法。(例如:调用 User 对象的 a 方法)

运行时修改属性值。(例如:修改 User 对象的 name 属性)

最后,温馨提示

JDK 中关于反射的相关类型都在 “java.lang.reflect” 包下,并不需要额外的第三方包来完成反射。

第一章:获取 Class对象

1、啥是 Class对象?

在 java 中,用来表示运行时类型信息的对应类就是 Class 类,而由此 Class 类创建出来的实例就是传说中的 “Class 对象”。

2、咋获取 Class 对象?

有三种方法,咱细品:

第一种方法:Class.forName("类的全称")

此方法适合,只知道类的字符表示(人话:类的全称)的情况。若类还没加载此类,则加载;若类已经加载了此类,则返回已加载的类的 “Class 对象” 给你。

//like this

1 Class user = Class.forName("com.User");

第二种方法:类的 Class字段

此方法适合,容器事先就知道该类的情况。

1 //比如:

2 Class user1 = User.class;

4

5 //也可以这样:

6 Class integerCls = Integer.class;7

8 //对于8个基本类型而言,想获取其 Class对象的信息,

除了用 class字段的方式外,还可以用其对应的包装类型的 TYPE字段来获取,像这样:

9 Class intCls = int.class;10 Class intCls1 = Integer.TYPE;

第三种方法:对象的 getClass() 方法

此方法适合,已有该类的 “Class 对象”的情况。

1 //like this

2 User user = newUser();3

4 Class userCls = user.getClass();

//就是直接利用已知的类的对象来获取该类的 Class对象信息

第二章:Class对象的基本操作

咱有了 Class对象后,可以干的各种坏事如下:

1、获取名字

1 //获取某个类的Class对象

2 Class clazz=...;3

4 //通过反射获取此类的简称

5 String simpleName =clazz.getSimpleName();6

7 //通过反射花去此类的全称

8 String fullQualifiedName = clazz.getName();

2、获取修饰符

通过 Class对象的 getModifiers() 方法获取此类的修饰符,但是此方法返回的是一个整数,此整数的含义即代表是什么修饰符,咱还需靠 Modifier类的一系列方法来分析,例如:

//获取到代表类修饰符的整数(此时我们不知道这个整数啥意思)

int modifier =clazz.getModifiers();//判断是否是public

boolean isPublic =Modifier.isPublic(modifier);//判断是否是Abstract

boolean isAbs = Modifier.isAbstract(modifier);

.

.

.

3、获取所在包的信息

//通过反射获取包

Package pkg =clazz.getPackage();//通过包对象获取包信息

String pkgName = pkg.getName();

4、获取父类信息

靠 getSuperClass()方法实现,若当前 Class对象代表的是 Object类型、接口类型、void类型、基本类型,则此方法返回null值

//通过反射获取父类对象

Class> superClazz =clazz.getSuperClass();//靠父类对象来获取父类的简称

String superClassName = superClazz.getSimpleName();

5、获取实现的接口

通过 getInterfaces() 方法实现

//省去获取 Class对象 的步骤

Class>[] personInterfaces = clazz.getInterfaces();

分下面几种情况:

第一种情况:当前的 Class对象代表的是一个类

此方法得到此类声明实现的所有接口信息,并不包含其父类所实现的接口,返回的数组中,按声明顺序排序;若没有,则返回长度为0的数组。

第二种情况:当前的 Class对象代表的是一个接口

此方法返回的是,此接口extends(继承)的所有接口信息,数组中顺序按照继承的接口顺序排序;若没有,则返回长度为0的数组。

第三种情况:当前的 Class对象代表的是void或基本类型

此方法返回长度为0的数组。

第四种情况:当前的 Class对象代表的是数组类型

此方法返回的是Cloneable 和 Seializable。

6、构造函数

(1)、获取所有构造函数

//通过反射获取所有构造函数

Constructor>[] constructors = clazz.getConstructors();

(2)、获取某个构造函数

//根据参数类型指定获取某个构造函数,若没有对应的构造函数则会抛出异常

Constructor>[] cons1 = clazz.getConstructor(String.class);

Constructor>[] cons2 = clazz.getConstructor(Integer.class);//有了构造函数之后,就可以创建实例了(上文提到的没有无参构造时的情况)

cons1.newInstance("hello");

cons2.newInstance(2);

7、字段

字段分为该类自己声明(Declare)的和从父类继承过来的。

下面的返回数组的方法中,数组中元素是没有进行排序的,所以你的代码不能依赖反射中得到的字段顺序来编写逻辑。获取字段对象:

//通过反射获取自身和从父类继承过来的所有 public 修饰的字段

File[] filds =clazz.getFilelds();//通过反射获取自己声明的(不包含从父类继承过来的)

File[] filds =clazz.getDeclareFields();//通过反射得到具体名字的字段

File f = clazz.getDeclareFields("字段名");

注意:如果 Class对象代表的类型没有字段,或者代表的是数组类型/基本数据类型/void类型,都会返回一个长度为0的数组。

得到字段对象后,我们可以做相关操作:

Field a = clazz.getDclaredField("a");//当不可访问时,对字段的可访问性进行调整方可获得字段值

a.setAccessible(true);

sout(a.get(user));

a.setAccessible(false);

获取字段值,主要靠 get() 方法来完成,如若确定字段的类型,则可调用对应类型的方法(例如:调用getInt() 方法获取证书字段的值),注意:此种以get开头获取字段值的方法主要针对八个基本类型

众所周知,字段分为静态字段和实例字段,静态字段的调用不需要实例,如下:

//从头开始,获取Class对象

Class clazz = User.class;

User user=clazz.newInstance();//获取静态字段值 (假设name是一个public static字段)

Field name = clazz.getDeclaredField("name");//输出静态字段值,要传入null,此时不确定字段类型

sout(name.get(null));//获取实例字段值

Field sex = clazz.getDeclaredField("sex");//要传入该类的实例,此时不确定字段类型

sout(sex.get(user));

设置字段值

Field name = clazz.getDeclaredField("name");

name.setAccessible(true);

name.set(user,"张三");//设置字段值

sout(name.get(user));//输出字段值

name.setAccessible(false);

8、方法

方法也分为自身的和继承过来的。下面是获取方法:

//获取自身声明的方法

Method[] mehtods =clazz.getDeclareMethods();//获取所有的方法(不管是自身还是继承)

Method[] methods =clazz.getMethods();//由于方法有重载功能,所以我们有时需要制定哪一个方法

Method method = clazz.getDeclareMothods("doSth",String.class);//doSth 表示方法名;String.class 表示参数类型

对获取到的 Method对象,我们可以对它进行一些操作:

//获取可访问性

boolean access =method.isAccessible();//得到方法名字

String name =method.getName();//得到方法参数个数

int parameterCount =method.getParameterCount();//得到所有参数信息

Parameter[] ps = method.getParameters();

调用方法

//假设类中有如下的方法

public voiddoSth(String name){

sout(name+"doSth");

}//则下面是通过反射调用其方法

Class clazz = User.class;//获取Class对象

User user = clazz.newInstance();//通过反射获取类实例

Method m = clazz.getDeclaredMethod("doSth", String.class);//得到指定方法,指定的条件是方法名和参数类型//调用,若此时方法不能访问,则需调整可访问性(开完后要及时关闭)

m.setAccessible(true);

m.invoke(user,"hello");

m.setAccessible(false);

9、实例化对象

我们得到 Class对象后,可通过此Class对象创建代表类的实例。

Class clazz =....;

Person p= clazz.newInstance();

注意:此方法要求类中必须有一个“无参构造函数”,如若没有,则此方法会抛出异常。

那么,没有无参构造函数我们怎么获得实例化对象呢?肯定是天无绝人之路,我们可以靠反射,先获取Constructor对象,也就是构造函数,再通过 Constructor对象来创建实例。

10、判断实例是否是某个类型(可能表达的不好)

Class对象还有一个 isInstance()方法,作用等价于 instanceOf操作符,用法如下:

User user = newUser();//通过类实例来得到 Class对象

Class clazz = User.class;//下面的输出是 true,因为user实例的类型是User类型

Sout(clazz.isInstace(user))

第三章:数组与反射

数组是某个类型对象的一个合集,基于这个认识,数组的反射与普通的反射有点不同,例如:

//创建一个长度为10的字符串数组//首先,获取到String类的Class对象

Class clz = Class.forName("java.lang.String");//其次,创建字符串数组

Object arr = Array.newInstance(clz,10);//设置第三个位置的字符串值

Array.set(arr,3,"hello world!");

sout((String)Array.get(arr,3));//输出第三个位置的值

String[] arr2=(String[])arr;

sout(arr2[3]);//同样是输出第三个位置的值

其中,Array类是在 java.lang.reflection包下面的类型。

本文仅供参考使用,如有不足之处请指出,谢谢。

参考资料:https://www.oracle.com/technetwork/articles/java/ javareflection-1536171.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值