首页 > 编程语言 > 详细

java 的反射机制

时间:2020-03-15 12:44:23      阅读:56      评论:0      收藏:0      [点我收藏+]

绪论

首先,什么是反射机制?

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

 

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

  1. 运行时实例化类的对象。(例如:实例化一个 User 类,此 User 类的名字容器事先是不知道的)
  2. 运行时调用方法。(例如:调用 User 对象的 a 方法)
  3. 运行时修改属性值。(例如:修改 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 = new User();
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<User> 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 void doSth(String name){
   sout(name+"doSth");        
}

//则下面是通过反射调用其方法
Class<User> 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<Person> clazz = ....;

Person p = clazz.newInstance();

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

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

 

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

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

User user = new User();

//通过类实例来得到 Class对象
Class<User> 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

java 的反射机制

原文:https://www.cnblogs.com/wfg934dbk/p/12495642.html

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!