它是一种 “运行时检视类型信息”、“修改属性”、“调用方法”的一种机制,
JDK 中关于反射的相关类型都在 “java.lang.reflect” 包下,并不需要额外的第三方包来完成反射。
在 java 中,用来表示运行时类型信息的对应类就是 Class 类,而由此 Class 类创建出来的实例就是传说中的 “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对象后,可以干的各种坏事如下:
1 //获取某个类的Class对象 2 Class clazz=...; 3 4 //通过反射获取此类的简称 5 String simpleName = clazz.getSimpleName(); 6 7 //通过反射花去此类的全称 8 String fullQualifiedName = clazz.getName();
通过 Class对象的 getModifiers() 方法获取此类的修饰符,但是此方法返回的是一个整数,此整数的含义即代表是什么修饰符,咱还需靠 Modifier类的一系列方法来分析,例如:
//获取到代表类修饰符的整数(此时我们不知道这个整数啥意思) int modifier = clazz.getModifiers(); //判断是否是public boolean isPublic = Modifier.isPublic(modifier); //判断是否是Abstract boolean isAbs = Modifier.isAbstract(modifier);
.
.
.
//通过反射获取包 Package pkg = clazz.getPackage(); //通过包对象获取包信息 String pkgName = pkg.getName();
靠 getSuperClass()方法实现,若当前 Class对象代表的是 Object类型、接口类型、void类型、基本类型,则此方法返回null值
//通过反射获取父类对象 Class<?> superClazz = clazz.getSuperClass(); //靠父类对象来获取父类的简称 String superClassName = superClazz.getSimpleName();
通过 getInterfaces() 方法实现
//省去获取 Class对象 的步骤 Class<?>[] personInterfaces = clazz.getInterfaces();
分下面几种情况:
第一种情况:当前的 Class对象代表的是一个类
此方法得到此类声明实现的所有接口信息,并不包含其父类所实现的接口,返回的数组中,按声明顺序排序;若没有,则返回长度为0的数组。
第二种情况:当前的 Class对象代表的是一个接口
此方法返回的是,此接口extends(继承)的所有接口信息,数组中顺序按照继承的接口顺序排序;若没有,则返回长度为0的数组。
第三种情况:当前的 Class对象代表的是void或基本类型
此方法返回长度为0的数组。
第四种情况:当前的 Class对象代表的是数组类型
此方法返回的是Cloneable 和 Seializable。
(1)、获取所有构造函数
//通过反射获取所有构造函数 Constructor<?>[] constructors = clazz.getConstructors();
//根据参数类型指定获取某个构造函数,若没有对应的构造函数则会抛出异常 Constructor<?>[] cons1 = clazz.getConstructor(String.class); Constructor<?>[] cons2 = clazz.getConstructor(Integer.class); //有了构造函数之后,就可以创建实例了(上文提到的没有无参构造时的情况) cons1.newInstance("hello"); cons2.newInstance(2);
字段分为该类自己声明(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);
方法也分为自身的和继承过来的。下面是获取方法:
//获取自身声明的方法 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);
我们得到 Class对象后,可通过此Class对象创建代表类的实例。
Class<Person> clazz = ....;
Person p = clazz.newInstance();
注意:此方法要求类中必须有一个“无参构造函数”,如若没有,则此方法会抛出异常。
那么,没有无参构造函数我们怎么获得实例化对象呢?肯定是天无绝人之路,我们可以靠反射,先获取Constructor对象,也就是构造函数,再通过 Constructor对象来创建实例。
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
原文:https://www.cnblogs.com/wfg934dbk/p/12495642.html