一、反射的概述
JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制。
要想解剖一个类,必须先要获取到该类的字节码文件对象。而解剖使用的就是Class类中的方法.所以先要获取到每一个字节码文件对应的Class类型的对象.
以上的总结就是什么是反射
反射就是把java类中的各种成分映射成一个个的Java对象
例如:一个类有:成员变量、方法、构造方法、包等等信息,利用反射技术可以对一个类进行解剖,把个个组成部分映射成一个个对象。
(其实:一个类中这些成员方法、构造方法、在加入类中都有一个类来描述)
如图是类的正常加载过程:反射的原理在与class对象。
熟悉一下加载的时候:Class对象的由来是将class文件读入内存,并为之创建一个Class对象。
基本操作如下:
import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.lang.reflect.Method; public class ReflectTest { public static void main(String[] args) throws Exception { //获取Class对象的三种方式 //第一种方式获取Class对象 Student stu1 = new Student(); //这一new 产生一个Student对象,一个Class对象 Class stuClass = stu1.getClass(); System.out.println(stuClass.getName()); // 输出:com.gdut.reflect.Student //第二种方式获取Class对象 Class stuClass2 = Student.class; System.out.println(stuClass==stuClass2); // 输出:true //第三种方式获取Class对象 try { Class stuClass3 = Class.forName("com.gdut.reflect.Student");//注意此字符串必须是真实路径,就是带包名的类路径,包名.类名 System.out.println(stuClass3 == stuClass2);//输出:true } catch (ClassNotFoundException e) { e.printStackTrace(); } //通过反射获取构造方法并使用 //1.加载Class对象 Class clazz = Class.forName("com.gdut.reflect.Student"); System.out.println("**********所有公有构造方法*************"); Constructor[] consArrays = clazz.getConstructors(); //就是构造方式被public修饰的 for(Constructor c : consArrays) { System.out.println(c); } System.out.println("************所有的构造方法(包括:私有、受保护、默认、公有)***************"); consArrays = clazz.getDeclaredConstructors(); //就是所有的构造方法,不限制访问权限 for(Constructor c : consArrays){ System.out.println(c); } System.out.println("*****************获取公有、无参的构造方法*******************************"); Constructor con = clazz.getConstructor(null); //1>、因为是无参的构造方法所以类型是一个null,不写也可以:这里需要的是一个参数的类型,切记是类型 //2>、返回的是描述这个无参构造函数的类对象。 System.out.println("con = " + con); //调用构造方法 Object obj = con.newInstance(); System.out.println("obj = " + obj); System.out.println("******************获取私有构造方法,并调用*******************************"); con = clazz.getDeclaredConstructor(int.class); System.out.println(con); //调用构造方法 con.setAccessible(true);//暴力访问(忽略掉访问修饰符),没有这条,下面就会报错 obj = con.newInstance(11); // 获取成员变量并调用 System.out.println("************获取所有公有的字段********************"); Field[] fieldArray = clazz.getFields(); for(Field f : fieldArray){ System.out.println(f); } System.out.println("************获取所有的字段(包括私有、受保护、默认的)********************"); fieldArray = stuClass.getDeclaredFields(); for(Field f : fieldArray){ System.out.println(f); } System.out.println("*************获取公有字段**并调用***********************************"); Field f = clazz.getField("name"); System.out.println(f); //获取一个对象 obj = clazz.getConstructor().newInstance();//产生Student对象--》Student stu = new Student(); //为字段设置值 f.set(obj, "刘德华");//为Student对象中的name属性赋值--》stu.name = "刘德华" //验证 Student stu = (Student)obj; System.out.println("验证姓名:" + stu.name); System.out.println("**************获取私有字段****并调用********************************"); f = clazz.getDeclaredField("phoneNum"); System.out.println(f); f.setAccessible(true);//暴力反射,解除私有限定 f.set(obj, "18888889999"); System.out.println("验证电话:" + stu); System.out.println("***************获取所有的”公有“方法*******************"); Method[] methodArray = clazz.getMethods(); for(Method m : methodArray){ System.out.println(m); } System.out.println("***************获取所有的方法,包括私有的*******************"); methodArray = clazz.getDeclaredMethods(); for(Method m : methodArray){ System.out.println(m); } System.out.println("***************获取公有的show1()方法*******************"); Method m = clazz.getMethod("show1", String.class); System.out.println(m); //实例化一个Student对象 obj = clazz.getConstructor().newInstance(); m.invoke(obj, "刘德华"); System.out.println("***************获取私有的show4()方法******************"); m = clazz.getDeclaredMethod("show4", int.class); System.out.println(m); m.setAccessible(true);//解除私有限定 Object result = m.invoke(obj, 20);//需要两个参数,一个是要调用的对象(获取有反射),一个是实参 System.out.println("返回值:" + result); } } class Student { public String name; protected int age; char sex; private String phoneNum; Student(String str) { System.out.println("defalut (默认)的构造方法 string = " + str); } public Student() { System.out.println("public 调用了公有、无参构造方法执行了。。。"); } //有一个参数的构造方法 public Student(char sex){ System.out.println("public 有一个参数的构造函数 char" + sex); } public Student(String name ,int age){ System.out.println("public 有多个参数的构造方法 string:"+name+" int:"+ age); } protected Student(boolean n){ System.out.println("protected 受保护的构造方法 boolean = " + n); } private Student(int age){ System.out.println("private 私有的构造方法 int:"+ age); } public void show1(String s){ System.out.println("public 调用了:公有的,String参数的show1(): s = " + s); } protected void show2(){ System.out.println("protected 调用了:受保护的,无参的show2()"); } void show3(){ System.out.println("default 调用了:默认的,无参的show3()"); } private String show4(int age){ System.out.println("private 调用了,私有的,并且有返回值的,String参数的show4(): age = " + age); return "abcd"; } @Override public String toString() { return "Student [name=" + name + ", age=" + age + ", sex=" + sex + ", phoneNum=" + phoneNum + "]"; } }
反射方法的其它使用之---通过反射越过泛型检查
泛型用在编译期,编译过后泛型擦除(消失掉)。所以是可以通过反射越过泛型检查的
ArrayList list1 =newArrayList(); ArrayList<String> list2 =newArrayList<String>(); Class c1 = list1.getClass(); Class c2 = list2.getClass(); System.out.println(c1==c2);//结果为true,为什么??
结果分析:因为反射的操作都是编译之后的操作,也就是运行时的操作,c1==c2返回true,说明编译之后集合的泛型是去泛型化的。
那么我们就可以理解为,Java集合中的泛型,是用于防止错误类型元素输入的,比如在list2中我们add一个int,add(10)就会编译报错,那么这个泛型就可以只在编译阶段有效,通过了编译阶段,泛型就不存在了。可以验证,我们绕过编译,用反射动态的在list2中add一个int是可以成功的,只是这时因为list2中存储了多个不同类型的数据(String型,和int型),就不能用for-each来遍历了,会抛出类型转换错误异常ClassCastException
另一个例子:
import java.lang.reflect.Method; import java.util.ArrayList; /* * 通过反射越过泛型检查 * * 例如:有一个String泛型的集合,怎样能向这个集合中添加一个Integer类型的值? */ public class Demo { public static void main(String[] args) throws Exception{ ArrayList<String> strList = new ArrayList<>(); strList.add("aaa"); strList.add("bbb"); // strList.add(100); //获取ArrayList的Class对象,反向的调用add()方法,添加数据 Class listClass = strList.getClass(); //得到 strList 对象的字节码 对象 //获取add()方法 Method m = listClass.getMethod("add", Object.class); //调用add()方法 m.invoke(strList, 100); //不报错哦 //遍历集合 for(Object obj : strList){ System.out.println(obj); } } }
console:
aaa
bbb
100