JAVA反射机制 JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法;这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制。 Java反射机制主要提供了以下功能: 在运行时判断任意一个对象所属的类;在运行时构造任意一个类的对象;在运行时判断任意一个类所具有的成员变量和方法;在运行时调用任意一个对象的方法;生成动态代理。 1. 得到某个对象的属性 [java] public Object getProperty(Object owner, String fieldName) throws Exception { Class ownerClass = owner.getClass(); Field field = ownerClass.getField(fieldName); Object property = field.get(owner); return property; [/java] Class ownerClass = owner.getClass():得到该对象的Class。 Field field = ownerClass.getField(fieldName):通过Class得到类声明的属性。 Object property = field.get(owner):通过对象得到该属性的实例,如果这个属性是非公有的,这里会报IllegalAccessException。 2. 得到某个类的静态属性 [java] public Object getStaticProperty(String className, String fieldName) throws Exception { Class ownerClass = Class.forName(className); Field field = ownerClass.getField(fieldName); Object property = field.get(ownerClass); return property; } [/java] Class ownerClass = Class.forName(className) :首先得到这个类的Class。 Field field = ownerClass.getField(fieldName):和上面一样,通过Class得到类声明的属性。 Object property = field.get(ownerClass) :这里和上面有些不同,因为该属性是静态的,所以直接从类的Class里取。 3. 执行某对象的方法 [java] public Object invokeMethod(Object owner, String methodName, Object[] args) throws Exception { Class ownerClass = owner.getClass(); Class[] argsClass = new Class[args.length]; for (int i = 0, j = args.length; i < j; i++) { argsClass = args.getClass(); } Method method = ownerClass.getMethod(methodName, argsClass); return method.invoke(owner, args); } [/java] Class owner_class = owner.getClass() :首先还是必须得到这个对象的Class。 5~9行:配置参数的Class数组,作为寻找Method的条件。 Method method = ownerClass.getMethod(methodName, argsClass):通过Method名和参数的Class数组得到要执行的Method。 method.invoke(owner, args):执行该Method,invoke方法的参数是执行这个方法的对象,和参数数组。返回值是Object,也既是该方法的返回值。 4. 执行某个类的静态方法 [java] public Object invokeStaticMethod(String className, String methodName, Object[] args) throws Exception { Class ownerClass = Class.forName(className); Class[] argsClass = new Class[args.length]; for (int i = 0, j = args.length; i < j; i++) { argsClass = args.getClass(); } Method method = ownerClass.getMethod(methodName, argsClass); return method.invoke(null, args); [/java] 基本的原理和实例3相同,不同点是最后一行,invoke的一个参数是null,因为这是静态方法,不需要借助实例运行。 5. 新建实例 [java] public Object newInstance(String className, Object[] args) throws Exception { Class newoneClass = Class.forName(className); Class[] argsClass = new Class[args.length]; for (int i = 0, j = args.length; i < j; i++) { argsClass = args.getClass(); } Constructor cons = newoneClass.getConstructor(argsClass); return cons.newInstance(args); } [/java] 这里说的方法是执行带参数的构造函数来新建实例的方法。如果不需要参数,可以直接使用newoneClass.newInstance()来实现。 Class newoneClass = Class.forName(className):第一步,得到要构造的实例的Class。 第5~第9行:得到参数的Class数组。 Constructor cons = newoneClass.getConstructor(argsClass):得到构造子。 cons.newInstance(args):新建实例。 6. 判断是否为某个类的实例 [java] public boolean isInstance(Object obj, Class cls) { return cls.isInstance(obj); } public boolean isInstance(Object obj, Class cls) { return cls.isInstance(obj); } 7. 得到数组中的某个元素查看复制到剪切板打印 public Object getByArray(Object array, int index) { return Array.get(array,index); } public Object getByArray(Object array, int index) { return Array.get(array,index); } [/java] |
***********************************************
一。课程:检查类
1.获得Class对象
得到Class对象
Class c = mystery.getClass();
得到超类的Class对象
TextField t = new TextField();
Class c = t.getClass();
Class s = c.getSuperclass();
通过类名获得Class对象
String strg="java.awt.Button";
Class c = Class.forName(strg);
2.获得类名
获得全路径名
Button b = new Button();
Class c = b.getClass();
String s = c.getName(); //java.awt.Button
3.发现类修饰符
定义一个类需要以下的修饰符:public,abstract,或者final。
1). 调用Class对象的getModifiers方法麳返回修饰符的集合。
2). 通过调用isPublic,isAbstract和isFinal方法麳检查修饰符。
例子:
import java.lang.reflect.*;
import java.awt.*;
class SampleModifier {
public static void main(String[] args) {
String s = new String();
printModifiers(s);
}
public static void printModifiers(Object o) {
Class c = o.getClass();
int m = c.getModifiers();//m为17,是16和1的合,public为1,final为16。
if (Modifier.isPublic(m))
System.out.println("public");
if (Modifier.isAbstract(m))
System.out.println("abstract");
if (Modifier.isFinal(m))
System.out.println("final");
}
}
示例程序的输出:
public
final
4.寻找超类
可以调用getSuperclass方法。这个方法返回超类的Class对象,或者返回null如果这个类没有超类。为了指定所有的父类,可以迭代调用getSuperclass直到返回null为止。
以下这个程序查找Button类的父类的名字。
import java.lang.reflect.*;
import java.awt.*;
class SampleSuper {
public static void main(String[] args) {
Button b = new Button();
printSuperclasses(b);
}
static void printSuperclasses(Object o) {
Class subclass = o.getClass();
Class superclass = subclass.getSuperclass();
while (superclass != null) {
String className = superclass.getName();
System.out.println(className);
subclass = superclass;
superclass = subclass.getSuperclass();
}
}
}
示例程序的输出为:
java.awt.Component
java.lang.Object
5.指定被类实现的接口
以下程序打印RandomAccessFile实现的接口。
import java.lang.reflect.*;
import java.io.*;
class SampleInterface {
public static void main(String[] args) {
try {
RandomAccessFile r = new RandomAccessFile("myfile", "r");
printInterfaceNames(r);
} catch (IOException e) {
System.out.println(e);
}
}
static void printInterfaceNames(Object o) {
Class c = o.getClass();
Class[] theInterfaces = c.getInterfaces();//得到当前类所实现的接口的类对象数组
for (int i = 0; i < theInterfaces.length; i++) {
String interfaceName = theInterfaces[i].getName();
System.out.println(interfaceName);
}
}
}
打印出以下信息:
java.io.DataOutput
java.io.DataInput
6.检查接口
如果你不确定一个Class对象到底是接口还是类,调用isInterface方法。
例子:
import java.lang.reflect.*;
import java.util.*;
class SampleCheckInterface {
public static void main(String[] args) {
Class observer = Observer.class;
Class observable = Observable.class;
verifyInterface(observer);
verifyInterface(observable);
}
static void verifyInterface(Class c) {
String name = c.getName();
if (c.isInterface()) {
System.out.println(name + " is an interface.");
} else {
System.out.println(name + " is a class.");
}
}
}
输出如下:
java.util.Observer is an interface。
Java.util.Observable is a class。
7.指定类的域
getFields方法返回一个Field对象的数组,包含每一个可访问的public域。
一个共有的域可以被任何一个成员访问:
这个类
这个类的超类
实现这个接口的类
这个类实现的接口的接口
以下的程序打印名字和类型属于GridBagConstraints类。注意这个程序首先返回Field对象,然后调用getName合getType方法。
import java.lang.reflect.*;
import java.awt.*;
class SampleField {
public static void main(String[] args) {
GridBagConstraints g = new GridBagConstraints();
printFieldNames(g);
}
static void printFieldNames(Object o) {
Class c = o.getClass();
Field[] publicFields = c.getFields(); //获得这个类所有的public的属性对象数组
for (int i = 0; i < publicFields.length; i++) {
String fieldName = publicFields[i].getName(); //属性的名字
Class typeClass = publicFields[i].getType(); //属性的类型,注意这里返回的是Class对象
String fieldType = typeClass.getName(); //属性的类型的名字
System.out.println("Name: " + fieldName +
", Type: " + fieldType);
}
}
}
输出如下:
Name: RELATIVE, Type: int
Name: REMAINDER, Type: int
Name: NONE, Type: int
Name: BOTH, Type: int
Name: HORIZONTAL, Type: int
Name: VERTICAL, Type: int
8.发现类的构造函数
为了创建一个类的实例,你调用一个特殊的方法,这个方法叫做构造函数。象方法一样,构造函数可以被重载。
你可以获得有关类的构造函数的信息,通过调用getConstructors方法,这个方法返回一个Constructor对象的数组。
你可以使用Constructor类提供的方法麳决定构造函数的名字,修饰符的结合,参数类型,和抛出异常的集合。
以下示例程序打印每一个构造函数的参数类型。程序按以下步骤执行:
1. 它返回一个Constructor对象的数组通过调用getConstructors。
2. 在Constructor数组的每一个元素,它创建一个Class对象的数组通过调用getParameterTypes。Class对象在数组中显示构造函数的参数。
3. 程序调用getName麳获得类名。
import java.lang.reflect.*;
import java.awt.*;
class SampleConstructor {
public static void main(String[] args) {
Rectangle r = new Rectangle();
showConstructors(r);
}
static void showConstructors(Object o) {
Class c = o.getClass();
Constructor[] theConstructors = c.getConstructors();
for (int i = 0; i < theConstructors.length; i++) {
System.out.print("( ");
Class[] parameterTypes =theConstructors[i].getParameterTypes();
for (int k = 0; k < parameterTypes.length; k ++) {
String parameterString = parameterTypes[k].getName();
System.out.print(parameterString + " ");
}
System.out.println(")");
}
}
}
第一行输出没有参数出现因为特定的Constructor对象显示一个没有参数的构造函数。
( )
( int int )
( int int int int )
( java.awt.Dimension )
( java.awt.Point )
( java.awt.Point java.awt.Dimension )
( java.awt.Rectangle )
9.获得方法信息
为了发现有关类的方法,你需要返回相应的Method对象。
调用getMethods方法,返回包含Method对象的数组。
你可以使用Method对象麳获得方法的名字,返回类型,参数类型,修饰符集合,抛出异常的类型。
例子描述:
1. 返回一个Method对象的数组,通过调用getMethods方法。
2. 遍历每一个元素:
a) 返回方法的名字通过调用getName方法。
b) 获得返回类型通过调用getReturnType。
c) 创建一个Class对象的数组通过调用getParameterTypes方法。
3. Class对象的数组显示方法的参数。为了返回每一个参数的类名,程序调用getName方法。
import java.lang.reflect.*;
import java.awt.*;
class SampleMethod {
public static void main(String[] args) {
Polygon p = new Polygon();
showMethods(p);
}
static void showMethods(Object o) {
Class c = o.getClass();
Method[] theMethods = c.getMethods();
for (int i = 0; i < theMethods.length; i++) {
String methodString = theMethods[i].getName();
System.out.println("Name: " + methodString);
String returnString =
theMethods[i].getReturnType().getName();
System.out.println(" Return Type: " + returnString);
Class[] parameterTypes = theMethods[i].getParameterTypes();
System.out.print(" Parameter Types:");
for (int k = 0; k < parameterTypes.length; k ++) {
String parameterString = parameterTypes[k].getName();
System.out.print(" " + parameterString);
}
System.out.println();
}
}
}
输出如下:
Name: equals
Return Type: boolean
Parameter Types: java.lang.Object
Name: getClass
Return Type: java.lang.Class
Parameter Types:
Name: hashCode
Return Type: int
Parameter Types:
Name: intersects
Return Type: boolean
Parameter Types: double double double double
Name: intersects
Return Type: boolean
Parameter Types: java.awt.geom.Rectangle2D
Name: translate
Return Type: void
Parameter Types: int int
二。课程:操纵对象
1.创建对象
1)使用没有参数的构造函数
以下的示例程序创建Rectangle类的实例,通过使用没有参数的构造方法。
import java.lang.reflect.*;
import java.awt.*;
class SampleNoArg {
public static void main(String[] args) {
Rectangle r = (Rectangle) createObject("java.awt.Rectangle");
System.out.println(r.toString());
}
static Object createObject(String className) {
Object object = null;
try {
Class classDefinition = Class.forName(className);
object = classDefinition.newInstance();
} catch (InstantiationException e) {
System.out.println(e);
} catch (IllegalAccessException e) {
System.out.println(e);
} catch (ClassNotFoundException e) {
System.out.println(e);
}
return object;
}
}
输出:
java.awt.Rectangle[x=0,y=0,width=0,height=0]
2)使用有参数的构造函数
为了使用有参数的构造函数创建对象,你可以调用Constructor对象的newInstance方法,不是Class对象。
这个技术包含以下步骤:
1. 创建一个Class对象。
2. 创建一个Constructor对象通过调用Class对象的getConstructor。getConstructor方法有一个参数:一个Class对象的数组,这些是相应的构造函数的参数。
3. 创建一个对象通过调用Constructor对象的newInstance方法。newInstance方法有一个参数:一个对象数组,这个对象数组的元素是传入构造函数的参数值。
示例程序创建一个Rectangle通过构造函数接受两个整型参数。
Rectangle rectangle = new Rectangle(12, 34);
这个构造函数的参数是原始类型,但是这个参数值传给newInstance必须为对象。所以,每一个原始类型被封装成Integer对象。
源代码如下:
import java.lang.reflect.*;
import java.awt.*;
class SampleInstance {
public static void main(String[] args) {
Rectangle rectangle;
Class rectangleDefinition;
Class[] intArgsClass = new Class[] {int.class, int.class}; //这个类数组包含两个int的类对象
Integer height = new Integer(12);
Integer width = new Integer(34);
Object[] intArgs = new Object[] {height, width}; //这个对象数组包含两个Integer对象
Constructor intArgsConstructor;
try {
rectangleDefinition = Class.forName("java.awt.Rectangle"); //通过类名得到类对象
intArgsConstructor = rectangleDefinition.getConstructor(intArgsClass); //得到类的构造函数,并且这个构造函数带参数,参数是一个数组
rectangle = (Rectangle) createObject(intArgsConstructor, intArgs);
} catch (ClassNotFoundException e) {
System.out.println(e);
} catch (NoSuchMethodException e) {
System.out.println(e);
}
}
public static Object createObject(Constructor constructor, Object[] arguments) {
System.out.println ("Constructor: " + constructor.toString());
Object object = null;
try {
object = constructor.newInstance(arguments); //创建实例时传入真实值
System.out.println ("Object: " + object.toString());
return object;
} catch (InstantiationException e) {
System.out.println(e);
} catch (IllegalAccessException e) {
System.out.println(e);
} catch (IllegalArgumentException e) {
System.out.println(e);
} catch (InvocationTargetException e) {
System.out.println(e);
}
return object;
}
}
输出如下:
Constructor: public java.awt.Rectangle(int,int)
Object: java.awt.Rectangle[x=0,y=0,width=12,height=34]
2.获得域的值
1. 创建一个Class对象
2. 创建一个Field对象通过getField方法
3. 调用Field对象的get方法的其中一个。
Field类有特定的方法麳获得原始类型的值。
例如,getInt方法返回一个int值的内容,getFloat返回一个浮点数,等等。如果域存储一个对象,使用get方法麳返回一个对象。
以下是源代码:
import java.lang.reflect.*;
import java.awt.*;
class SampleGet {
public static void main(String[] args) {
Rectangle r = new Rectangle(100, 325);
printHeight(r);
}
static void printHeight(Rectangle r) {
Field heightField;
Integer heightValue;
Class c = r.getClass();
try {
heightField = c.getField("height"); //获得这个类的某个属性,这里是“height”属性
heightValue = (Integer) heightField.get(r); //这里传入了对象
System.out.println("Height: " + heightValue.toString());
} catch (NoSuchFieldException e) {
System.out.println(e);
} catch (SecurityException e) {
System.out.println(e);
} catch (IllegalAccessException e) {
System.out.println(e);
}
}
}
输出:
Height: 325
4.设置域的值
调用Field类的set方法。为了修改一个域的值,执行以下步骤:
1. 创建一个Class对象
2. 创建一个通过调用getField方法获得Field对象
3. 调用适当的Field对象的set方法。
Field类提供几个set方法。特定的方法,例如setBoolean和setInt,这些方法是为了修改原始类型。
如果你想要修改的域是一个对象,则要调用set方法。你可以调用set麳修改一个原始类型,但是你必须使用适当的包装对象。
示例程序修改width域。因为width是一个原始类型,int,传递给set的是一个Integer,是一个包装类对象。
import java.lang.reflect.*;
import java.awt.*;
class SampleSet {
public static void main(String[] args) {
Rectangle r = new Rectangle(100, 20);
System.out.println("original: " + r.toString());
modifyWidth(r, new Integer(300));
System.out.println("modified: " + r.toString());
}
static void modifyWidth(Rectangle r, Integer widthParam ) {
Field widthField;
Integer widthValue;
Class c = r.getClass();
try {
widthField = c.getField("width");
widthField.set(r, widthParam); //设置新值时需要指定对象和要修改的值
} catch (NoSuchFieldException e) {
System.out.println(e);
} catch (IllegalAccessException e) {
System.out.println(e);
}
}
}
输出:
original: java.awt.Rectangle[x=0,y=0,width=100,height=20]
modified: java.awt.Rectangle[x=0,y=0,width=300,height=20]
5.调用方法
1. 创建一个Class对象对应你想要调用方法的对象
2. 创建一个Method对象,通过调用getMethod方法。
getMethod方法有两个参数:一个包含方法名称的字符串,还有一个是Class对象的数组。数组中的每一个元素对应你要调用方法的一个参数
3. 通过调用invoke调用方法。Invoke方法有两个参数:一个参数值的数组和一个对象。
示例程序展示的是如何动态调用方法。
程序返回Method对象,这个对象对应的是String.concat方法,然后使用invoke麳链接两个字符串对象。
import java.lang.reflect.*;
class SampleInvoke {
public static void main(String[] args) {
String firstWord = "Hello ";
String secondWord = "everybody.";
String bothWords = append(firstWord, secondWord);
System.out.println(bothWords);
}
public static String append(String firstWord, String secondWord) {
String result = null;
Class c = String.class;
Class[] parameterTypes = new Class[] {String.class};
Method concatMethod;
Object[] arguments = new Object[] {secondWord};
try {
concatMethod = c.getMethod("concat", parameterTypes); //指定要调用哪个方法,第一个是方法名,第二个是方法的参数类型
result = (String) concatMethod.invoke(firstWord, arguments);//开始调用方法,第一个是指定调用哪个对象的方法,第二个是方法的真实参数。
} catch (NoSuchMethodException e) {
System.out.println(e);
} catch (IllegalAccessException e) {
System.out.println(e);
} catch (InvocationTargetException e) {
System.out.println(e);
}
return result;
}
}
输出:
Hello everybody.
三。课程:数组
1.指定数组
如果你不确定一个特定的对象是一个数组,你可以通过Class.isArray方法麳检查
示例程序执行以下几步:
1. 它返回Class对象。
2. 获得Field对象。
3. 对每一个Field对象,程序获得相应的Class对象通过调用getType方法。
4. 验证前面步骤返回的Class对象是数组,这个程序调用isArray方法。
以下是源代码:
import java.lang.reflect.*;
import java.awt.*;
class SampleArray {
public static void main(String[] args) {
KeyPad target = new KeyPad();
printArrayNames(target);
}
static void printArrayNames(Object target) {
Class targetClass = target.getClass();
Field[] publicFields = targetClass.getFields();
for (int i = 0; i < publicFields.length; i++) {
String fieldName = publicFields[i].getName();
Class typeClass = publicFields[i].getType();
String fieldType = typeClass.getName();
if (typeClass.isArray()) {
System.out.println("Name: " + fieldName +
", Type: " + fieldType);
}
}
}
}
class KeyPad {
public boolean alive;
public Button power;
public Button[] letters;
public int[] codes;
public TextField[] rows;
public boolean[] states;
}
输出如下:
Name: letters, Type: [Ljava.awt.Button;
Name: codes, Type: [I
Name: rows, Type: [Ljava.awt.TextField;
Name: states, Type: [Z
2.返回组件类型
例如,arrowKeys数组的组件类型在以下代码中是Button类型:
Button[] arrowKeys = new Button[4];
通过调用getComponentType方法返回数组元素的组件类型。
import java.lang.reflect.*;
import java.awt.*;
class SampleComponent {
public static void main(String[] args) {
int[] ints = new int[2];
Button[] buttons = new Button[6];
String[][] twoDim = new String[4][5];
printComponentType(ints);
printComponentType(buttons);
printComponentType(twoDim);
}
static void printComponentType(Object array) {
Class arrayClass = array.getClass();
String arrayName = arrayClass.getName();
Class componentClass = arrayClass.getComponentType();
String componentName = componentClass.getName();
System.out.println("Array: " + arrayName +
", Component: " + componentName);
}
}
输出:
Array: [I, Component: int
Array: [Ljava.awt.Button;, Component: java.awt.Button
Array: [[Ljava.lang.String;, Component: [Ljava.lang.String;
3.创建数组
以下示例程序使用newInstance方法麳创建一个数组的拷贝,这个数组大小是原来数组的两倍。newInstance方法接受新数组的长度和组件类型。
以下是源代码:
import java.lang.reflect.*;
class SampleCreateArray {
public static void main(String[] args) {
int[] originalArray = {55, 66};
int[] biggerArray = (int[]) doubleArray(originalArray);
System.out.println("originalArray:");
for (int k = 0; k < Array.getLength(originalArray); k++)
System.out.println(originalArray[k]);
System.out.println("biggerArray:");
for (int k = 0; k < Array.getLength(biggerArray); k++)
System.out.println(biggerArray[k]);
}
static Object doubleArray(Object source) {
int sourceLength = Array.getLength(source);
Class arrayClass = source.getClass();
Class componentClass = arrayClass.getComponentType();
Object result = Array.newInstance(componentClass, sourceLength * 2);
System.arraycopy(source, 0, result, 0, sourceLength);
return result;
}
}
输出:
originalArray:
55
66
biggerArray:
55
66
0
0
你也可以使用newInstance方法麳创建多维数组。在这个情况下,方法的参数是新数组维数的组件类型和int类型的数组。
以下程序展示如何使用newInstance麳创建多维数组:
import java.lang.reflect.*;
class SampleMultiArray {
public static void main(String[] args) {
// The oneDimA and oneDimB objects are one
// dimensional int arrays with 5 elements.
int[] dim1 = {5};
int[] oneDimA = (int[]) Array.newInstance(int.class, dim1);
int[] oneDimB = (int[]) Array.newInstance(int.class, 5);
// The twoDimStr object is a 5 X 10 array of String objects.
int[] dimStr = {5, 10};
String[][] twoDimStr =
(String[][]) Array.newInstance(String.class,dimStr);
// The twoDimA object is an array of 12 int arrays. The tail
// dimension is not defined. It is equivalent to the array
// created as follows:
// int[][] ints = new int[12][];
int[] dimA = {12};
int[][] twoDimA = (int[][]) Array.newInstance(int[].class, dimA);
}
}
4.获得和设置元素的值
使用Array类的set和get方法麳访问数组元素
除了get和set方法,Array类对于原始类型有特定的方法。例如,setInt的参数值是一个int,getBoolean返回的对象是一个boolean类型的包装类。
示例程序使用set和get方法麳拷贝一个数组的内容到另一个数组。
import java.lang.reflect.*;
class SampleGetArray {
public static void main(String[] args) {
int[] sourceInts = {12, 78};
int[] destInts = new int[2];
copyArray(sourceInts, destInts);
String[] sourceStrgs = {"Hello ", "there ", "everybody"};
String[] destStrgs = new String[3];
copyArray(sourceStrgs, destStrgs);
}
public static void copyArray(Object source, Object dest) {
for (int i = 0; i < Array.getLength(source); i++) {
Array.set(dest, i, Array.get(source, i));
System.out.println(Array.get(dest, i));
}
}
}
输出:
12
78
Hello
there
everybody
四。课程:类摘要
列出了反射API的组成,提供适当的API文档链接。
以下的表格总结反射API的组成。Class和Object类在java.lang包中。其它的类在java.lang.reflect包中。
类名
描述
Array
提供静态方法动态创建和访问数组。
Class
描绘或者反射类和接口。
Constructor
提供有关访问类的构造函数的信息。 允许你动态实例化类。
Field
提供有关动态访问类或者接口的域的信息。
Method
提供有关和访问类或者接口单一方法。允许你动态调用方法。
Modifier
提供静态方法和常量麳获得有关访问类和它的成员的操作符。
Object
提供getClass方法。