Java 反射机制是 Java 语言中一种很重要的机制,可能在工作中用到的机会不多,但是在很多框架中都有用到这种机制。我们知道 Java 是一门静态语言,在程序编译时变量的数据类型都已经确定,那么在 Java 运行时环境中,对于任意一个类,我们能否知道这个类有哪些属性和方法?对于任意一个对象,能否调用它的任意一个方法?答案是肯定的。这种动态获取类的信息以及动态调用对象的方法的功能来自于 Java 的反射机制(Reflection)。
1、Java 反射机制提供的功能
主要提供了以下几个功能:
1)在运行时判断任意一个对象所属的类;
2)在运行时构造任意一个类的对象;
3)在运行时判断任意一个类所具有的成员变量和方法;
4)在运行时调用任意一个对象的方法。
反射让 Java 具有了动态的特性,这种机制允许程序在运行时透过 Reflection API 获取任意一个已知名称的类的内部信息,包括成员变量(fields)、方法(methods)、实现的接口(interfaces)、Java 语言修饰符(modifiers)以及它的父类(superclass)等等,并可在运行时改变成员变量的内容或调用方法。
2、Java Reflection API
在 JDK 中,提供了以下类来实现 Java 反射机制,这些类都位于 java.lang.reflect 包下:
Class 类:代表一个类(注意:Class 类位于 java.lang 包下);
Field 类:代表类的成员变量;
Method 类:代表类的方法;
Constructor 类:代表类的构造方法;
Array 类:提供了动态创建数组,以及访问数组的元素的静态方法。
通过 API 提供的这些类里的方法,我们可以动态获取想要的类的内部信息。
3、获取类的 Class 对象
Class 类的实例表示正在运行的 Java 程序中的类和接口,每一个类都有对应的 Class 对象,不管一个类生成了多少个对象,这些对象都对应内存里的同一个 Class 对象。Class 类没有 public 的构造方法,Class 对象是在加载类时由 Java 虚拟机自动构建的。
有以下几种方式来获取一个类的 Class 对象:
1)Class 类提供的静态方法:forName(String className),参数 className 表示所需类的完全限定名。
- 1 public class GetClassObject {
- 2
- 3 public static void main(String[] args) throws Exception {
- 4
- 5 Class classType = Class.forName("java.lang.String");
- 6
- 7 System.out.println(classType);//输出:class java.lang.String
- 8 }
- 9
- 10 }
2)运用. class 语法
- 1 public class GetClassObject {
- 2
- 3 public static void main(String[] args) throws Exception {
- 4
- 5 Class classType = String.class;
- 6
- 7 System.out.println(classType);//输出:class java.lang.String
- 8 }
- 9
- 10 }
3)Object 类提供的方法:getClass()
- 1 public class GetClassObject {
- 2
- 3 public static void main(String[] args) throws Exception {
- 4
- 5 Map map = new HashMap();
- 6 Class classType = map.getClass();
- 7
- 8 System.out.println(classType);//输出:class java.util.HashMap
- 9 }
- 10
- 11 }
4、获取类的 Field(成员变量)对象
类的每一个成员变量都对应一个 Field 对象,Class 类提供了以下方法来获取类的成员变量对应的 Field 对象:
1)Field getDeclaredField(String name):根据传入的变量名称返回此 Class 对象所表示的类或接口中声明的变量对应的 Field 对象。
2)Field[] getDeclaredFields():返回一个 Field 类型的数组,包含此 Class 对象所表示的类或接口中声明的所有变量的 Field 对象。
3)Field getField(String name):根据传入的变量名返回一个 Field 对象,注意与 getDeclaredField(String name) 不同的是,此方法返回的是 public 变量对应的 Field 对象。
4)Field[] getFields():返回一个 Field 类型的数组,注意与 Field[] getDeclaredFields() 方法不同的是,此方法返回的是所有 public 变量对应的 Field 对象。
代码示例:
- 1 public class GetFieldObject {
- 2
- 3 public static void main(String[] args) throws Exception {
- 4
- 5 //首先,获得String类的Class对象
- 6 Class classType = Class.forName("java.lang.String");
- 7
- 8 //获得String类中声明的所有成员变量的Field对象的数组
- 9 Field[] fields = classType.getDeclaredFields();
- 10 for(Field field : fields){
- 11 System.out.println(field);
- 12 }
- 13
- 14 System.out.println("---------------------------------------------------------------------");
- 15
- 16 //获得String类中声明的public成员变量的Field对象的数组
- 17 Field[] publicFields = classType.getFields();
- 18 for(Field field : publicFields){
- 19 System.out.println(field);
- 20 }
- 21
- 22 }
- 23
- 24 }
输出结果:
从结果输出可以看出 getDeclaredFields() 与 getFields() 的区别:getDeclaredFields() 返回的是所有属性的 Field 对象;而 getFields() 返回的是声明为 public 的属性的 Field 对象。
5、获取类的 Method 对象
类中的每一个方法都对应一个 Method 对象,Class 类提供了以下方法来获取类中的方法对应的 Method 对象:
1)Method getDeclaredMethod(String name, Class<?>... parameterTypes):返回一个 Method 对象,参数 name 表示方法名,可变参数 parameterTypes 是一个 Class 对象的数组,代表方法的参数的 Class 类型;
2)Method[] getDeclaredMethods():返回 Method 对象的一个数组,这些对象反映此 Class 对象所表示的类或接口声明的所有方法,包括公共、保护、默认访问和私有方法,但不包括继承的方法;
3)Method getMethod(String name, Class<?>... parameterTypes):返回一个 Method 对象,注意和此 Method 对象对应的方法是公共 (public) 方法;
4)Method[] getMethods():返回一个 Method 数组,这些对象反映此 Class 对象所表示的类或接口中声明的公共 (public) 方法(也包括父类或父接口中声明的 public 方法)。
代码示例:
- 1 public class GetMethodObject {
- 2
- 3 public static void main(String[] args) throws Exception {
- 4
- 5 //首先,获得类的Class对象
- 6 Class classType = Class.forName("java.lang.reflect.Proxy");
- 7
- 8 //获得类中声明的所有方法的Method对象的数组,不包括继承的父类的方法
- 9 Method[] methods = classType.getDeclaredMethods();
- 10 for(Method method : methods){
- 11 System.out.println(method);
- 12 }
- 13
- 14 System.out.println("----------------------------------------------------------------------");
- 15
- 16 //获得类中的public方法的Method对象的数组,也包括继承的父类的public方法
- 17 Method[] publicMethods = classType.getMethods();
- 18 for(Method method : publicMethods){
- 19 System.out.println(method);
- 20 }
- 21
- 22 }
- 23
- 24 }
输出结果:
6、用反射机制调用对象的方法
Java 反射机制可以在运行时动态调用类中的方法,Java Reflection API 提供了我们所需的方法来完成动态调用。要想调用类中的方法首先要创建一个对象,我们通过类的 Class 对象来创建它所代表的类的实例,通过 Class 对象我们还能获得类中声明的方法的 Method 对象,Method 类提供了 Invoke 方法来调用此 Method 对象所表示的方法。反射机制调用方法代码示例如下:
- 1 public class InvokeTester {
- 2
- 3 public static int add(int a, int b){
- 4 return a + b;
- 5 }
- 6
- 7 public static String echo(String str){
- 8 return "hello "+str;
- 9 }
- 10
- 11
- 12 public static void main(String[] args) throws Exception {
- 13 // InvokeTester invoke = new InvokeTester();
- 14 // System.out.println(invoke.add(1, 2));
- 15 // System.out.println(invoke.echo("tom"));
- 16
- 17
- 18 //用反射机制调用,首先获得类的Class对象
- 19 Class classType = InvokeTester.class;
- 20
- 21 //通过Class对象获得一个InvokeTester类的实例
- 22 Object invoke = classType.newInstance();
- 23
- 24 //获得add(int a, int b)方法的Method对象,getMethod方法的参数为方法名和方法参数类型的Class对象的数组
- 25 Method addMethod = classType.getMethod("add", int.class, int.class);
- 26
- 27 //通过Method类的invoke方法,调用invoke对象的add方法
- 28 Object result = addMethod.invoke(invoke, 1, 2);
- 29
- 30 System.out.println(result);
- 31
- 32 Method echoMethod = classType.getMethod("echo", String.class);
- 33
- 34 Object result2 = echoMethod.invoke(invoke, "Tom");
- 35
- 36 System.out.println(result2);
- 37
- 38 }
- 39 }
7、用反射机制调用类的私有方法
我们知道正常情况下一个类的私有方法只允许这个类本身来调用,但使用反射机制能打破这种访问限制,让其他的类也能调用这个类的私有的方法。这种场景在实际开发中很少用到,Java 也不提倡这种用法。代码示例如下:
- public class Private {
- //定义一个私有方法
- private String sayHello(String name){
- return "hello, "+name;
- }
- }
- public class PrivateTest {
- public static void main(String[] args) throws Exception {
- //调用Private类的私有方法
- Private p = new Private();
- Class classType = p.getClass();
- Method method = classType.getDeclaredMethod("sayHello", String.class);
- method.setAccessible(true);//取消Java访问检查,如果不设置此项则会报错
- String str = (String)method.invoke(p, "Tracy");
- System.out.println(str);//输出:hello, Tracy
- }
- }
Method、Field、Constructor 类有一个共同的父类 AccessibleObject 类,它提供了将反射的对象标记为在使用时取消默认 Java 语言访问控制检查的能力。在上面的代码中,我们在反射对象 Method 中设置 accessible 标志,它允许程序以某种通常禁止的方式来操作对象。
8、用反射机制操作类的私有变量
与前面调用类的私有方法类似,通过反射我们还能操作类的私有变量,代码示例如下:
- 1 public class Private2 {
- 2 //定义私有变量
- 3 private String name = "zhangsan";
- 4
- 5 public String getName(){
- 6 return name;
- 7 }
- 8 }
- 9
- 10
- 11 public class PrivateTest2 {
- 12
- 13 public static void main(String[] args) throws Exception {
- 14 //改变Private2类的私有变量的值
- 15 Private2 p = new Private2();
- 16
- 17 Class classType = p.getClass();
- 18
- 19 Field field = classType.getDeclaredField("name");
- 20
- 21 field.setAccessible(true);//取消默认java访问控制检查,Field类的父类AccessibleObject类提供的方法
- 22
- 23 field.set(p, "lisi");//Field类的set(Object obj, Object value)方法将指定对象上此Field对象表示的字段设置为指定的新值
- 24
- 25 System.out.println(p.getName());//输出:lisi
- 26
- 27 }
- 28
- 29 }
以上这些内容,我介绍了 Java 反射机制的中涉及的主要的几个类以及这些类的基本用法,这些类中还有很多的方法,大家可以通过查看 API 进行了解,用法都很简单。Java 反射机制在很多框架的底层实现中有用到,还有一种很重要的设计模式也用到了反射,那就是代理模式中的动态代理,了解了动态代理模式的思想对我们研究框架有很大帮助,我会在后面的博客中介绍这些内容,欢迎大家共同探讨。
来源: http://www.cnblogs.com/fangfuhai/p/6540105.html