一、什么是反射机制
在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制。简单来说,就是Java对每一个类和类中的所有成员都进行了封装,这样每个类都有一个与之对应的Class对象,通过这个对象可以直接访问类中的所有成员。
二、反射机制的作用
① 在运行时判断任意一个对象所属的类
② 在运行时构造任意一个类的对象
③ 在运行时判断任意一个类所具有的成员变量和方法
④ 在运行时调用任意一个对象的方法;生成动态代理
三、反射机制的优点与缺点
首先要搞清楚为什么要用反射机制?直接创建对象不就可以了吗,这就涉及到了动态与静态的概念。
静态编译:在编译时确定类型,绑定对象,即通过。
动态编译:运行时确定类型,绑定对象。动态编译最大限度发挥了java的灵活性,体现了多态的应用,有以降低类之间的藕合性。
● 优点:可以实现动态创建对象和编译,体现出很大的灵活性(特别是在J2EE的开发中它的灵活性就表现的十分明显)。通过反射机制我们可以获得类的各种内容,进行了反编译。对于JAVA这种先编译再运行的语言来说,反射机制可以使代码更加灵活,更加容易实现面向对象。
比如,一个大型的软件,不可能一次就把把它设计的很完美,当这个程序编译后,发布了,当发现需要更新某些功能时,我们不可能要用户把以前的卸载,再重新安装新的版本,假如这样的话,这个软件肯定是没有多少人用的。采用静态的话,需要把整个程序重新编译一次才可以实现功能的更新,而采用反射机制的话,它就可以不用卸载,只需要在运行时才动态的创建和编译,就可以实现该功能。
● 缺点:对性能有影响。使用反射基本上是一种解释操作,我们可以告诉JVM,我们希望做什么并且它 满足我们的要求。这类操作总是慢于只直接执行相同的操作。
四、Java类反射中所必须的类
除了 Class 类之外,基本都在 java.lang.reflect 包中。它们分别是:Field、Constructor、Method、Class、Object,下面我将对这些类做一个简单的说明。
● Class类:类的实例表示正在运行的 Java 应用程序中的类和接口。枚举是一种类,注释是一种接口。每个数组属于被映射为 Class 对象的一个类,所有具有相同元素类型和维数的数组都共享该 Class 对象。
● Constructor类:提供关于类的单个构造方法的信息以及对它的访问权限。这个类和Field类不同,Field类封装了反射类的属性,而Constructor类则封装了反射类的构造方法。
● Field类:提供有关类或接口的属性的信息,以及对它的动态访问权限。反射的字段可能是一个类(静态)属性或实例属性,简单的理解可以把它看成一个封装反射类的属性的类。
● Method类:提供关于类或接口上单独某个方法的信息。所反映的方法可能是类方法或实例方法(包括抽象方法)。 这个类不难理解,它是用来封装反射类方法的一个类。
● Object类:每个类都使用 Object 作为超类。所有对象(包括数组)都实现这个类的方法。
五、获取字节码方式
在类加载的时候,jvm 会创建一个 class 对象;class 对象是反射机制中常用的,用来获取 class 对象的字节码文件
在 Java 中可以通过三种方法获取类的字节码 (Class) 对象:
// 获取字节码对象的三种方式 Class c = 对象.getClass();已经创建对象一般不需要进行反射 Class c = 类名.class;需要导入类的包,依赖性太强 Class c = Class.forName("package.className");使用字符串获取(含包名),一般开发中选择这种★ // 使用Class对象创建该对象实例: Object obj = c.newInstance();
六、获取类的构造函数
● Constructor [] getConstructors();获取public权限的所有构造器
● Constructor getConstructor(Class<?>...params);根据指定参数获得public权限的构造器
● Constructor [] getDeclaredConstructors();获取public权限的所有构造器
● Constructor getDeclaredConstructor(Class<?>...params);根据指定参数获得public权限和非public权限的构造器
Class c = Class.forName("bean.Person"); // 1、获取这个带有参数的构造器 Constructor<?> con = c.getConstructor(String.class,int.class); // 2、使用构造器来创建: Object obj = con.newInsatnce("LiSi",20);
七、获取类的字段
● Field [] getFields();获得类中所以public权限的方法;
● Field getField(String name);根据变量名得到相应的public权限的变量;
● Field [] getDeclaredFields();获得类中所有的public权限和非public的权限方法 ;
● Field getDeclaredField(String name);根据方法名获得public权限和非public权限的变量;
package reflect; import java.lang.reflect.Field; public class getFieldDemo { public static void main(String[] args) throws Exception { String className = "bean.Person"; Class<?> c = Class.forName(className); // 获取私有字段 Field nameField = c.getDeclaredField("name"); Field ageField = c.getDeclaredField("age"); // 使用字段(使用之前我们需要一个该类对象) Object obj = c.newInstance(); // 使用set()方法设置字段值 nameField.setAccessible(true); ageField.setAccessible(true);// 暴力访问 nameField.set(obj, "张三"); ageField.set(obj,20); // 打印查看效果 System.out.println("获取到的字段:"); System.out.println("name:"+nameField); System.out.println("age:"+ageField); System.out.println("字段设置的值:name="+nameField.get(obj)+",age="+ageField.get(obj)); } }
八、获取类的方法
● Method [] getMethod();获取所有的public方法,包括继承的
● Method getMethods(String name,Class [] params);根据方法名和参数获取方法;
● Method [] getDeclaredMethod();获取所有public权限和非public权限方法,不包含继承的
● Method getDeclaredMethods(String name,Class [] params);根据方法名和参数类型,获取public权限和非public权限的方法
package reflect; import java.lang.reflect.Method; public class getMethodDemo { public static void main(String[] args) throws Exception { String className = "bean.Person"; Class<?> c = Class.forName(className); //获取公共方法: Method[] pubMethods = c.getMethods(); //获取私有方法: Method[] priMethods = c.getDeclaredMethods(); //获取单个方法:按方法名和参数获取 //获取单个の静态方法:function1 Method staMethod = c.getMethod("function1",null); //获取单个の无参数方法:function2 Method nullMethod = c.getMethod("function2",null); //获取单个の有参数方法:function3 Method moreMethod = c.getMethod("function3",String.class,int.class); //获取单个の私有方法:function4 Method priMethod = c.getDeclaredMethod("function4",null); //打印查看效果 System.out.println("[Person类的公共方法及父类方法:]"); for(Method m:pubMethods){ System.out.println(m); } System.out.println("[Person类的私有方法:]"); for(Method m:priMethods){ System.out.println(m); } System.out.println("[按方法名和参数类型获取的方法4个方法:]"); System.out.println(staMethod); System.out.println(nullMethod); System.out.println(moreMethod); System.out.println(priMethod); } }
九、暴力访问 - setAccessible(boolean)
对私有字段的访问取消权限检查。暴力访问(私有的不建议使用)