一、反射机制的定义 JAVA反射机制是在运行状态_中,对于任意一个类 (class文件),都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制。
动态获取类中信息,就是java反射,可以理解为对类的解剖。要想要对字节码文件进行解剖,必须要有字节码文件对象。 获取class对象有三种方式:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 //方式一,这种方式实际上一般不会用,因为要明确具体的类并创建对象,如果这样就直接可以调用类的方法//和变量了。 /* * 获取字节码对象的方式: * 1,Object类中的getClass()方法的。 * 想要用这种方式,必须要明确具体的类,并创建对象。 * 麻烦 . * */ public static void getClassObject_1(){ Person p = new Person(); Class clazz = p.getClass(); Person p1 = new Person(); Class clazz1 = p1.getClass(); //结果为true,说明clazz和clazz1是同一个字节码对象 System.out.println(clazz==clazz1); } /* * 方式二: * 2,任何数据类型都具备一个静态的属性.class来获取其对应的Class对象。 * 相对简单,但是还是要明确用到类中的静态成员。 * 还是不够扩展。 * */ public static void getClassObject_2() { Class clazz = Person.class; Class clazz1 = Person.class; //还是返回true System.out.println(clazz==clazz1); } /* * 方式三: * 只要通过给定的类的 字符串名称就可以获取该类,更为扩展。 * 可是用Class类中的方法完成。 * 该方法就是forName. * 这种方式只要有名称即可,更为方便,扩展性更强。 */ public static void getClassObject_3() throws ClassNotFoundException { String className = "bean.Person"; Class clazz = Class.forName(className); // 打印 "class bean.Person",Person类在bean文件夹下 System.out.println(clazz); }
以上三种方式中第三种方式最常见。接下来看看得到字节码文件后创建对象的方式:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 //Person类 class Person { private int age; private String name; public Person(String name,int age) { this.age = age; this.name = name; } public Person() { } public void show(){ System.out.println(name+"...show run..."+age); } private void privateMethod(){ System.out.println(" method run "); } public void paramMethod(String str,int num){ System.out.println("paramMethod run....."+str+":"+num); } public static void staticMethod(){ System.out.println(" static method run......"); } } //方式一 String name = "bean.Person"; //找寻该名称类文件,并加载进内存,并产生Class对象。 Class clazz = Class.forName(name); //如何产生该类的对象呢? Object obj = clazz.getDeclaredConstructor().newInstance(); //方式二 String name = "bean.Person"; //找寻该名称类文件,并加载进内存,并产生Class对象。 Class clazz = Class.forName(name); //获取到了指定的构造函数对 象。 Constructor constructor = clazz.getConstructor(String.class,int.class); //通过该构造器对象的newInstance方法进行对象的初始化。 Object obj = constructor.newInstance("小明",38);
获取字节码文件中的字段
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 Class clazz = Class.forName("bean.Person"); Field field = null;//clazz.getField("age");//只能获取公有的, field = clazz.getDeclaredField("age");//只获取本类,但包含私有。 //对私有字段的访问取消权限检查。暴力访问。 field.setAccessible(true); Object obj = clazz.getDeclaredConstructor().newInstance(); field.set(obj, 89); Object o = field.get(obj); //打印结果为89 System.out.println(o); // bean.Person p = new cn.itcast.bean.Person(); // p.age = 30; // 89
获取指定Class中的所有公共函数。
1 2 3 4 5 6 7 8 9 10 11 12 Class clazz = Class.forName("bean.Person"); Method[] methods = clazz.getMethods();//获取的都是公有的方法。 methods = clazz.getDeclaredMethods();//只获取本类中所有方法,包含私有。 for(Method method : methods){ System.out.println(method); } // public void bean.Person.show() public void bean.Person.paramMethod(java.lang.String,int) public static void bean.Person.staticMethod() private void bean.Person.privateMethod()
二、先来个小总结:
1,反射是一种具有动态交互能力的一种机制,为什么强调动态交互?因为一般情况下都是动态加载也就是在运行的时候加载,而不是在编译的时候,在需要的时候才进行加载获取,或者说你可以在任何时候加载一个不存在的类到内存,然后进行各种交互,或者获取一个没有公开的类的所有信息,开发者可以随时随意的利用反射的这种机制动态进行一些特殊的事情。1,反射的组成部分
1,java.lang.Class.java:类对象
2,java.lang.reflect.Constructor.java:类的构造函数对象
3,java.lang.reflect.Method.java:类的方法对象
4,java.lang.reflect.Field.java:类的属性对象
2,反射的作用 反射在java和Android中的应用:
1,需要访问隐藏属性或者调用方法改变原来的逻辑,这个在开发中极为常见。当系统没有开放接口出来,这个时候利用反射是一个有效的解决方法
2,自定义注解,注解就是在运行的时候利用反射机制来获取的
3,在开发中动态加载类,比如Android中著名的65536问题,模块化和插件化都离不开反射,离开了反射这些问题很难解决
3,反射的原理 每个java文件都会编译成一个.class文件,这些Class对象承载了这个类的所有信息,包括父类、接口、构造函数、方法、字段等等,这些class文件在程序运行的时候会被ClassLoader加载到虚拟机中。 当一个类被加载后,java虚拟机会在内存中自动产生一个Class对象,而我们一般情况下用new关键字来创建对象,本质上是一样的,只是这些底层原理对我们开发者透明,实际上有了class的引用,就相当于有了Method,Field,Constructors的一切信息,在java中有了对象的引用就有了一切,开发者就可以自由发挥了。反射的简单演示(一) String类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 public class Student { private int age;//年龄 private String name;//姓名 private String address;//地址 private static String sTest; public Student() { throw new IllegalAccessError("Access to default Constructor Error!"); } private Student(int age, String name, String address) { this.age = age; this.name = name; this.address = address; sTest = "测试反射"; } private int getAge() { return age; } private void setAge(int age) { this.age = age; } private String getName() { return name; } private void setName(String name) { this.name = name; } private String getAddress() { return address; } private void setAddress(String address) { this.address = address; } private static String getTest() { return sTest; } }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 public class StudentClient { public static void main(String[] args) throws Exception{ Class<?> clazz=Class.forName("ClassLoader.Student"); Constructor constructors=clazz.getDeclaredConstructor(int.class,String.class,String.class); constructors.setAccessible(true); //利用构造器生成对象 Object mStudent=constructors.newInstance(27,"小文","北京市海定区XX号"); System.out.println(mStudent.toString()); //获取隐藏的int属性 Field mAgeField=clazz.getDeclaredField("age"); mAgeField.setAccessible(true); int age= (int) mAgeField.get(mStudent); System.out.println("年龄为:"+age); //调用隐藏的方法 Method getAddressMethod=clazz.getDeclaredMethod("getAge"); getAddressMethod.setAccessible(true); int newage= (int) getAddressMethod.invoke(mStudent); System.out.println("年龄为:"+newage); //调用静态方法 Method getTestMethod=clazz.getDeclaredMethod("getTest"); getTestMethod.setAccessible(true); String result= (String) getTestMethod.invoke(null); System.out.println("调用静态方法:"+result); } } //运行结果 reflect.Student@2c13da15 年龄为:27 年龄为:27 调用静态方法:测试反射
反射的简单演示(二)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 package reflect; import java.lang.reflect.Field; class Student_Demo { public Student_Demo(){ } //**********字段*************// public String name; protected int age; char sex; private String phoneNum; @Override public String toString() { return "Student [name=" + name + ", age=" + age + ", sex=" + sex + ", phoneNum=" + phoneNum + "]"; } } public class Demo { public static void main(String[] args) throws Exception { //1.获取Class对象 Class stuClass = Class.forName("reflect.Student_Demo"); //2.获取字段 System.out.println("************获取所有公有的字段********************"); Field[] fieldArray = stuClass.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 = stuClass.getField("name"); System.out.println(f); //获取一个对象 Object obj = stuClass.getConstructor().newInstance();//产生Student对象--》Student stu = new Student(); //为字段设置值 f.set(obj, "刘德华");//为Student对象中的name属性赋值--》stu.name = "刘德华" //验证 Student_Demo stu = (Student_Demo)obj; System.out.println("验证姓名:" + stu.name); System.out.println("**************获取私有字段****并调用********************************"); f = stuClass.getDeclaredField("phoneNum"); System.out.println(f); f.setAccessible(true);//暴力反射,解除私有限定 f.set(obj, "18888889999"); System.out.println("验证电话:" + stu); } }
输出结果:
1 2 3 4 5 6 7 8 9 10 11 12 13 ************获取所有公有的字段******************** public java.lang.String reflect.Student_Demo.name ************获取所有的字段(包括私有、受保护、默认的)******************** public java.lang.String reflect.Student_Demo.name protected int reflect.Student_Demo.age char reflect.Student_Demo.sex private java.lang.String reflect.Student_Demo.phoneNum *************获取公有字段**并调用*********************************** public java.lang.String reflect.Student_Demo.name 验证姓名:刘德华 **************获取私有字段****并调用******************************** private java.lang.String reflect.Student_Demo.phoneNum 验证电话:Student [name=刘德华, age=0, sex= , phoneNum=18888889999]