1,什么是反射
反射机制是java语言提供的一种基础功能,它能够赋予成语在运行时进行自省的能力。通过反射我们可以直接操作类或者对象,例如:可以通过反射去获取某个对象的类的定义,属性,方法,还可以修改类的定义。
2,反射中的包
2.1反射包
java中专门的反射包java.lang.reflect,反射包下包含了很多类,具体可以参考jdk,这里我们用到的有Field类的成员变量,Method方法,Constructor构造器。
2.2AccessibleObject
关于反射有一点需要特意注意一下,就是反射包提供的AccessibleObject
类中的setAccessible
方法。
他的子类很多也重写了这个方法,这里的所谓的setAccessible顾名思义,就是指的是成员变量前面的用于修饰的public、protected、private,这个方法也就意味着我们可以在运行时通过反射去修改类的成员变量的访问限制。
setAccessible的应用场景非常广泛,各种框架:开发、测试、依赖注入。例如在数据库O/R Mapping框架中,我们在加载或者持久化数据的时候,框架通常会利用反射做这个事情,而不需要开发者自己去实现。
还有个典型的应用场景,就是绕过API的访问控制。我们在开发的过程中,有时候,可能需要调用内部的API去做一些事情,比如,自定义的高性能的NIO框架需要显示的释放DirectBuffer,使用反射绕开限制是一种常见的办法。
2.2Modifier
反射包中还有一个重要的类Modifier,该类是静态类,其中的方法也是静态方法。
Modifier.Class类中getModifiers()函数返回一个用于描述类,构造器,方法和域的修饰符的整形数值。、
调用Modifier.toString()方法将整型数值转变成字符串,也是就我们熟悉的public,private,static,final等修饰符。
2.3代码实例
/**
* 创建一个user类
* 可不在与main方法同一个类中创建
*/
class User{
//定一个成员变量:姓名
private String name;
//无参构造
public User(){}
//有参构造
public User(String name) {
this.name = name;
}
//get方法
public String getName() {
return name;
}
//set方法
public void setName(String name) {
this.name = name;
}
}
/**
* 测试Class类的函数和反射库中的函数
*/
public class TestReflectController {
public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException, NoSuchFieldException {
//获得User类的Class对象
Class<?> cc = User.class;
//下面这个是通过new出来的对象,并通过set给成员变量赋值
User user1 = new User();
user1.setName("hello");
//下面这个是通过构造器的方式
//1.有参构造器
//先得到有参构造器的信息,再根据构造器的信息,由newInstance()函数创建一个User对象
Constructor<?> constructor = cc.getConstructor(String.class);
//这个constructor.newInstance方法就是反射包下java.lang.reflect.Constructor类下的newInstance方法
User user2 = (User) constructor.newInstance("world");
System.out.println(user1.getName());//hello
System.out.println(user2.getName());//world
//2.无参构造器
Constructor<?> constructor1 = cc.getConstructor();
//无参构造不能够像1一样赋值
//User user3 = (User) constructor1.newInstance("熊本");
User user3 = (User) constructor1.newInstance();
user3.setName("熊本");
System.out.println(user3.getName());//熊本
//由无参构造器创建对象时,可不必获得构造器,直接由Class对象调用newInstance()方法。
Class<?> cc2 = User.class;
User user4 = (User) cc2.newInstance();
user4.setName("同学");
System.out.println(user4.getName());//同学
//3.下面2个输出语句可看出cc保存类信息,输出的是“class + 类名”。cc.newInstance()是具体类的对象。
System.out.println(cc);//class com.newframe.controllers.api.User
System.out.println(cc.newInstance());//com.newframe.controllers.api.User@3a03464
//4.AccessibleObject、Field类
//首先得到有参构造函数的信息,然后根据构造函数实例化一个对象。
//由getDeclaredField()函数得到类里面的私有成员变量,访问私有成员变量要用setAccessible()函数设置访问权限。
//Field类对象得到成员变量后还可以设置该变量的值,使用set()方法。
Constructor<?> constructor2 = cc.getConstructor(String.class);
User user5 = (User) constructor2.newInstance("改变前:100");
Field field = cc.getDeclaredField("name");
field.setAccessible(true);
field.set(user5,"改变后:50");
System.out.println(user5.getName());//改变后:50
//5.Method.invoke
//首先根据获得的构造函数信息实例化一个对象
//然后由函数名获得类中的公有函数,getMethod("函数名")
//invoke()方法执行由getMethod()获得的函数,这里获得的函数是getter()
//对于获得的有参函数,invoke(对象)里只添加对象名。
Constructor<?> constructor3 = cc.getConstructor(String.class);
User user6 = (User) constructor3.newInstance("你好世界");
//getName是User实体类中的方法
System.out.println(cc.getMethod("getName").invoke(user6));//你好世界
//对于获得到的无参函数,在调用getMethod()函数时,要在getMethod()中指定被获得函数的"函数名"和"参数类型"
//并且在执行该函数(即调用invoke()函数时),要指定对象和参数类型的具体实例。
User user7 = (User) cc.newInstance();
Method method = cc.getMethod("setName", String.class);
method.invoke(user7,"你好哇,这个世界");
System.out.println(user7.getName());//你好哇,这个世界
//6.Modifier类
//Class类中getModifiers()函数返回一个用于描述类,构造器,方法和域的修饰符的整形数值。、
//调用Modifier.toString()方法将整型数值转变成字符串,也是就我们熟悉的public,private,static,final等修饰符。
System.out.println(Modifier.toString(cc.getModifiers()));//无
System.out.println(Modifier.toString(constructor.getModifiers()));//public
System.out.println(Modifier.toString(field.getModifiers()));//private
System.out.println(Modifier.toString(method.getModifiers()));//public
//同时,Modifier类还有一些判断修饰符是不是某一类型的方法。
System.out.println(Modifier.isPublic(cc.getModifiers()));
System.out.println(Modifier.isPublic(constructor.getModifiers()));
}
}
3,数组的反射
public class TestReflectArray {
public static void main(String[] args) throws ClassNotFoundException {
//新建4个不同的数组
//一维数组
int[] a = new int[3];
int[] b = new int[4];
//二维数组
int[][] c = new int[3][4];
//String类型的数组
String[] d = new String[3];
//可以对比各个数组所属的类是否相同
System.out.println(a.getClass() == b.getClass());//true
//System.out.println(a.getClass() == c.getClass());不同类型的数组不同比较
System.out.println(a.length == c.length);//但是可以比较长度,true
System.out.println(a.getClass().getName());//[I
System.out.println(d.getClass().getName());//[Ljava.lang.String;
System.out.println(a.getClass().getSuperclass().getName());//java.lang.Object
//利用反射生成一个数组
int[] e = (int[]) Array.newInstance(int.class,3);
//0为数组索引下标,1为数字值
Array.set(e,0,1);
Array.set(e,1,2);
Array.set(e,2,3);
System.out.println(Array.get(e,0));
System.out.println(Array.get(e, 1));
System.out.println(Array.get(e, 2));
//利用反射获取数组类型常用的一些方式
Class<?> cc = String[].class;
Class<?> cc1 = Class.forName("[I");
Class<?> cc2 = Class.forName("[Ljava.lang.String;");
System.out.println(cc);
System.out.println(cc1);
System.out.println(cc2);
//通过已有的对象获取数组的类型
Class<?> cc3 = e.getClass();
System.out.println(cc3);
//根据类型信息获取数组内成员的类型
Class<?> cc4 = cc3.getComponentType();
System.out.println(cc4);//int
}
}