基本概念
在Java运行时环境中,对于任意一个类,能否知道这个类有哪些属性和方法?对于任意一个对象,能否调用它的任意一个方法?
答案是肯定的。
这种动态获取类的信息以及动态调用对象的方法的功能来自于Java语言的反射(Reflection)机制。
Java反射机制主要提供了以下功能:
1.在运行时判断任意一个对象所属的类。
2.在运行时构造任意一个类的对象。
3.在运行时判断任意一个类所具有的成员变量和方法。
4.在运行时调用任意一个对象的方法。
Reflection是Java被视为动态(或准动态)语言的一个关键性质。
这个机制允许程序在运行时透过Reflection APIs取得任何一个已知名称的class的内部信息。
包括其modifiers(诸如public、static等)、 superclass(例如Object)、实现了的 interfaces (例如Serializable)、也包括其fields和methods的所有信息,并可于运行时改变fields内容或调用methods。
动态语言
动态语言的定义“程序运行时,允许改变程序结构或者变量类型,这种语言称为动态语言”。
从这个观点看,Perl,Python,Ruby是动态语言,C++,Java,C#不是动态语言。
尽管在这样的定义与分类下Java不是动态语言,它却有着一个非常突出的动态相关机制:Reflection。这个字的意思是:反射、映像、倒影,用在Java身上指的是我们可以于运行时加载、探知、使用编译期间完全未知的classes。
换句话说,Java程序可以加载一个运行时才得知名称的class,获悉其完整构造(但不包括methods定义),并生成其对象实体、或对其fields设值、或唤起其methods。
这种“看透”class的能力(the ability of the program to examine itself)被称为introspection(内省、内观、反省)。Reflection和introspection是常被并提的两个术语。
Java Reflection API简介
在JDK中,主要由以下类来实现Java反射机制,这些类(除了第一个)都位于java.lang.reflect包中
Class类:代表一个类,位于java.lang包下。
Field类:代表类的成员变量(成员变量也称为类的属性)。
Method类:代表类的方法。
Constructor类:代表类的构造方法。
Array类:提供了动态创建数组,以及访问数组的元素的静态方法。
Class对象
要想使用反射,首先需要获得待操作的类所对应的Class对象。
Java中,无论生成某个类的多少个对象,这些对象都会对应于同一个Class对象。
这个Class对象是由JVM生成的,通过它能够获悉整个类的结构。
常用的获取Class对象的3种方式:
1.使用Class类的静态方法。例如:
Class.forName("java.lang.String");
2.使用类的.class语法。如:
String.class;
3.使用对象的getClass()方法。如:
String str = "aa"; Class<?> classType1 = str.getClass();
getClass()方法定义在Object类中,不是静态方法,需要通过对象来调用,并且它声明为final,表明不能被子类所覆写。
直接print所获得的Class对象classType会输出:
class 完整类名
如果调用该Class对象的getName()方法,则输出完整类名,不加class。
例程1:获取方法
例程ReflexTest 类演示了Reflection API的基本作用,它读取命令行参数指定的类名,然后打印这个类所具有的方法信息。
package com.reflex; import java.lang.reflect.Field; import java.lang.reflect.Method; public class ReflexTest { public static void main(String[] args) { try { Class c = Class.forName("java.lang.Integer"); Field[] fs = c.getDeclaredFields(); for(Field f:fs){ System.out.println(f); } System.out.println("=================================================="); Method[] methods = c.getDeclaredMethods(); for(Method method:methods){ System.out.println(method); } } catch (ClassNotFoundException e) { // TODO Auto-generated catch block e.printStackTrace(); } } }
结果:
public static final int java.lang.Integer.MIN_VALUE
public static final int java.lang.Integer.MAX_VALUE
public static final java.lang.Class java.lang.Integer.TYPE
static final char[] java.lang.Integer.digits
static final char[] java.lang.Integer.DigitTens
static final char[] java.lang.Integer.DigitOnes
static final int[] java.lang.Integer.sizeTable
private final int java.lang.Integer.value
public static final int java.lang.Integer.SIZE
private static final long java.lang.Integer.serialVersionUID
static final boolean java.lang.Integer.$assertionsDisabled
==================================================
public int java.lang.Integer.hashCode()
public boolean java.lang.Integer.equals(java.lang.Object)
public static java.lang.String java.lang.Integer.toString(int,int)
public java.lang.String java.lang.Integer.toString()
public static java.lang.String java.lang.Integer.toString(int)
public static java.lang.String java.lang.Integer.toHexString(int)
public static int java.lang.Integer.compare(int,int)
public int java.lang.Integer.compareTo(java.lang.Object)
public int java.lang.Integer.compareTo(java.lang.Integer)
public static java.lang.Integer java.lang.Integer.decode(java.lang.String) throws java.lang.NumberFormatException
static void java.lang.Integer.getChars(int,int,char[])
public static java.lang.Integer java.lang.Integer.valueOf(int)
public static java.lang.Integer java.lang.Integer.valueOf(java.lang.String,int) throws java.lang.NumberFormatException
public static java.lang.Integer java.lang.Integer.valueOf(java.lang.String) throws java.lang.NumberFormatException
public long java.lang.Integer.longValue()
public int java.lang.Integer.intValue()
public static int java.lang.Integer.reverse(int)
static int java.lang.Integer.stringSize(int)
public static int java.lang.Integer.reverseBytes(int)
public byte java.lang.Integer.byteValue()
public double java.lang.Integer.doubleValue()
public float java.lang.Integer.floatValue()
public short java.lang.Integer.shortValue()
public static int java.lang.Integer.parseInt(java.lang.String) throws java.lang.NumberFormatException
public static int java.lang.Integer.parseInt(java.lang.String,int) throws java.lang.NumberFormatException
public static int java.lang.Integer.bitCount(int)
public static java.lang.Integer java.lang.Integer.getInteger(java.lang.String,int)
public static java.lang.Integer java.lang.Integer.getInteger(java.lang.String)
public static java.lang.Integer java.lang.Integer.getInteger(java.lang.String,java.lang.Integer)
public static int java.lang.Integer.highestOneBit(int)
public static int java.lang.Integer.lowestOneBit(int)
public static int java.lang.Integer.numberOfLeadingZeros(int)
public static int java.lang.Integer.numberOfTrailingZeros(int)
public static int java.lang.Integer.rotateLeft(int,int)
public static int java.lang.Integer.rotateRight(int,int)
public static int java.lang.Integer.signum(int)
public static java.lang.String java.lang.Integer.toBinaryString(int)
public static java.lang.String java.lang.Integer.toOctalString(int)
private static java.lang.String java.lang.Integer.toUnsignedString(int,int)
例程2:通过反射调用方法
通过反射调用方法。详情见代码及注释:
package com.reflex; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; public class InvokeTester { public int add(int param1, int param2) { return param1 + param2; } public String echo(String message) { return "Hello: " + message; } public static void main(String[] args) throws InstantiationException, IllegalAccessException, NoSuchMethodException, SecurityException, IllegalArgumentException, InvocationTargetException { // 以前的常规执行手段 InvokeTester tester = new InvokeTester(); System.out.println(tester.add(1, 2)); System.out.println(tester.echo("Tom")); System.out.println("---------------------------"); // 通过反射的方式 // 第一步,获取Class对象 // 前面用的方法是:Class.forName()方法获取 // 这里用第二种方法,类名.class Class<?> classType = InvokeTester.class; Object invokeTester = classType.newInstance(); System.out.println(invokeTester instanceof InvokeTester);// 输出true // 通过反射调用方法 // 首先需要获得与该方法对应的Method对象 // 第一个参数是方法名,第二个参数是这个方法所需要的参数的Class对象的数组 Method addMethd = classType.getMethod("add", new Class[] { int.class, int.class }); // 调用目标方法 Object addResult = addMethd.invoke(invokeTester,new Object[]{1,2}); System.out.println(addResult); //调用第二个echo方法 Method echoMethod = classType.getMethod("echo", new Class[] {String.class}); Object echoResult = echoMethod.invoke(invokeTester,new Object[]{"tom"}); System.out.println(echoResult); } }
生成对象
若想通过类的不带参数的构造方法来生成对象,我们有两种方式:
1.先获得Class对象,然后通过该Class对象的newInstance()方法直接生成即可:
2.先获得Class对象,然后通过该对象获得对应的Constructor对象,再通过该Constructor对象的newInstance()方法生成
(其中Customer是一个自定义的类,有一个无参数的构造方法,也有带参数的构造方法):
Class<?> classType = String.class; Object obj = classType.newInstance();
Class<?> classType = Customer.class; // 获得Constructor对象,此处获取第一个无参数的构造方法的 Constructor cons = classType.getConstructor(new Class[] {}); // 通过构造方法来生成一个对象 Object obj = cons.newInstance(new Object[] {});
若想通过类的带参数的构造方法生成对象,只能使用下面这一种方式:
(Customer为一个自定义的类,有无参数的构造方法,也有一个带参数的构造方法,传入字符串和整型)
Class<?> classType = Customer.class; Constructor cons2 = classType.getConstructor(new Class[] {String.class, int.class}); Object obj2 = cons2.newInstance(new Object[] {"ZhangSan",20});