Java:反射
初识Java反射机制:
从上面的描述可以看出Java的反射机制使得Java语言可以在运行时去认识在编译时并不了解的类/对象的信息,并且能够调用相应的方法或修改属性的值。Java反射机制的核心就是允许在运行时通过Java Reflection APIs来取得已知名字的class类的内部信息(包括其modifiers(如public, static等)、superclass(如Object)、interfaces(如Serializable),也包括fields和methods的所有信息),动态地生成此类,并调用其方法或修改其域(甚至是本身声明为private的域或方法)。这种反射机制为Java本身带来了动态性,是一个非常强大的工具,能够让代码变得更加灵活。
简单示例:
细说Java反射机制:
(1)Class对象
Class对象是Java反射的基础,它包含了与类相关的信息,实际上,Class对象是用来创建类的所有对象的。Class对象是java.lang.Class<T>这个类生成的对象,其中参数T表示用此Class对象建模的类的类型。例如,String.class的类型是 Class<String>;如果被建模的类的类型未知,则使用Class<?>,下图有所体现:
以下是Java API
的描述:
Class
类的实例表示正在运行的 Java应用程序中的类和接口。基本的Java类型(boolean
、byte
、char
、short
、int
、long
、float
和double
)和关键字void也
表示为Class
对象。所有具有相同元素类型和维数的数组都共享同一个Class
对象。
实际上,每个类都有一个Class对象。换言之,每当编写并且编译了一个新类,就会产生一个Class对象(更恰当的说,是被保存在一个同名的.class文件中)。如果我们想生成某个类的对象,运行这个程序的虚拟机(JVM)将使用类加载器检查这个类的Class对象是否已经加载。如果尚未加载,默认的类加载器就会根据类名查找.class文件,并将其载入,一旦某个类的Class对象被载入内存,它就被用来创建这个类的所有对象。
Class
没有公共构造方法,Class
对象是在加载类时由Java虚拟机以及通过调用类加载器中的defineClass
方法自动构造的。
获取Class对象有三种方式:
A、通过实例变量的getClass()方法。
示例:Class c = new String("abc").getClass();
B、通过Class类的静态方法——forName()来实现。
示例:Class c =Class.forName("className");
注:当使用Class.forName()方法时,你必须提供完整类名(即类名要包括包名)。例如,如果MyObject是位于包com.test下,那么类的完整类名是com.test.MyObject。如果运行时在类路径上找不到指定的类,Class.forName()方法会抛出一个ClassNotFoundException。
C、使用类字面常量或TYPE字段。
示例:Class c = MyObject.class;
注:类字面常量不仅可以应用于普通的类,也可以应用于接口、数组以及基本数据类型,这种方式不仅更简单,而且更安全,因为它在编译时就会受到检查,并且根除了对forName方法的调用,所以也更高效,建议使用“.class”的形式。
示例:Class c = Integer.TYPE;
注:TYPE字段是基本数据类型的包装类型的一个标准字段,它是一个引用,指向对应的基本数据类型的Class对象,附表如下,两边等价:
(2)获取类的名称:从Class对象中可以获取两个不同的类名。
A、完整类名(包括包名)可以使用getName()或getCanonicalName()方法获取
示例:
Class c = MyObject.class;
String className = c.getName();
B、如果想要获取不含包名的类名可以使用getSimpleName()
方法
示例:
Class c = MyObject.class;
String className = c.getSimpleName();
(3)获取类的修饰符:使用Class对象可以获取一个类的修饰符,类的修饰符即为关键字"public"、"private"、"static"等,修饰符被包装进一个int内
示例:
Class c= MyObject.class;
int modifiers = c.getModifiers();
每一个修饰符都是一个标志位,可以使用java.lang.reflect.Modifier
类
中的以下方法来检验修饰符:
Modifier.isAbstract(int modifiers)
Modifier.isFinal(int modifiers)
Modifier.isInterface(int modifiers)
Modifier.isNative(int modifiers)
Modifier.isPrivate(int modifiers)
Modifier.isProtected(int modifiers)
Modifier.isPublic(int modifiers)
Modifier.isStatic(int modifiers)
Modifier.isStrict(int modifiers)
Modifier.isSynchronized(int modifiers)
Modifier.isTransient(int modifiers)
Modifier.isVolatile(int modifiers)
(4)获取类实现的接口:一个类可以实现多个接口,通过给定的类可以获取这个类所实现的接口列表
示例:
Class c= MyObject.class;
Class[] interfaces = c.getInterfaces();
在Java反射机制中,接口也由Class对象表示。
注意:只有给定类声明实现的接口才会返回。例如,如果类A的父类B实现了一个接口C,但类A并没有声明它也实现了C,那么C不会被返回到数组中。即使类A实际上实现了接口C,因为它的父类B实现了C。
所以如果想得到一个给定的类所实现接口的完整列表,需要递归访问类和其超类。
(5)获取类的构造函数:使用Class对象可以获取类的构造函数
示例:
Class c= MyObject.class;
Constructor[] constructors = c.getConstructors();
Constructor
数组为类中每一个声明为
public的
构造函数保存一个
Constructor
实例
。
如果知道要访问的构造函数确切的参数类型,可以不获取构造函数数组,而是直接获取该构造函数
下述示例将返回给定类中接受一个字符串作为参数的公共构造函数:
Class c= MyObject.class;//MyObject有一个参数为字符串的公共构造函数
Constructor constructor = c.getConstructor(new Class[]{String.class});
如果没有匹配给定的构造函数参数,在这个例子当中是String.class
,会抛出 NoSuchMethodException
异常.
获取类的构造函数并实例化对象,这是通过Java类java.lang.reflect.Constructor来实现的。
此处给出一个完整示例:
package com.test; import java.lang.reflect.*; public class WW { public WW() {
} public WW(int a, int b) {
System.out.println("a="+a+" b="+b);
} public static void main(String args[]) {
try {
Class cls = Class.forName("com.test.WW");
Class partypes[] = new Class[2];
partypes[0] = Integer.TYPE;
partypes[1] = Integer.TYPE;
Constructor ct = cls.getConstructor(partypes); Object arglist[] = new Object[2];
arglist[0] = new Integer(37);
arglist[1] = new Integer(47);
Object retobj = ct.newInstance(arglist); } catch (Throwable e) {
System.err.println(e);
}
}
}
解说:上例根据指定的参数类型找到相应的构造函数来创建一个新的对象实例。Constructor.newInstance()
方法接收可变长度的参数,但是在通过构造函数创建对象时必须为每一个参数提供一个准确的参量。在这个例子中,构造函数接收两个整型作为参数,所以必须要提供两个整数。使用这种方法可以在程序运行时动态地创建对象,而不是在编译的时候创建对象,这一点在实际应用中很重要。
(6)获取类的字段(成员变量):使用Class对象可以获取类的字段(成员变量)
示例:
Class c= MyObject.class;
Field[] methods = c.getFields();
Field
数组为类中每一个声明为
public的
字段
保存一个
Field
实例。
此时某些读者可能会问类中声明为
private的
字段该如何获取,
其实某些细心的读者应该已经发现此处使用的一些方法与本文最开始的示例中使用的方法不太一样,后文会进一步揭开反射的神秘面纱
一旦获得一个 Field
实例,可以通过Field.getName()
方法获取它的字段名字
示例:
Field field = ... //获取 field 对象
String fieldName = field.getName();
如果知道要访问的字段名称,可以这样获取该字段:
Class c= MyObject.class
Field field = c.getField("FieldName");
如果不存在getField()方法中所给参数名字对应的字段,会抛出NoSuchFieldException
异常。
可以通过Field.getType()
方法确定一个字段的类型
(
String,int等),如下:
Field field = aClass.getField("someField");
Object fieldType = field.getType();
可以通过Field.get()
和 Field.set()
方法获取和设置
某个字段的值,如下:
Class c= MyObject.class
Field field = c.getField("FieldName");
MyObject object = new MyObject();//构造一个MyObject类型的对象实例
Object value = field.get(object);
field.set(objet, value);
传递给get和set方法的对象实例应该是拥有该字段的类的一个实例。在上述的例子中使用了MyObject的一个实例,若获取的字段为静态字段(public static),则给get和set方法传递的对象实例应该为"null",而不是上述示例中传递的object参数。
(7)获取类的成员方法:使用Class对象可以获取类的成员方法
Class c = MyObject.class;
Method[] methods = c.getMethods();
Method
数组将为类中每一个声明为
public
的方法保存一个
Method
实例。
如果知道要访问的方法的参数类型,可以不必获取方法数组,而是直接获取该方法
下述示例返回给定类中接受一个字符串作为参数的公共方法”doSomething”:
Class c= MyObject.class;
Method method = c.getMethod("doSomething", new Class[]{String.class});
如果没有方法匹配所给的方法名和参数,在这个例子中是String.class,
将抛出NoSuchMethodException
异常。
如果你想访问的方法没有参数,传递 null
作为参数类型数组,如下:
Class c= MyObject.class;
Method method = c.getMethod("doSomething", null);
使用Method对象可以获取方法的所有参数,如下:
Method method = ... //获取 method – 如上
Class[] parameterTypes = method.getParameterTypes();
使用Method对象可以获取方法的返回值类型,如下:
Method method = ... //获取 method – 如上
Class returnType = method.getReturnType();
动态执行方法
Method method = MyObject.class.getMethod("doSomething", String.class);
Object returnValue = method.invoke(null, "parameter-value1");
上述代码中invoke()方法的第一个参数是你想要调用方法的对象,如果该方法是静态的,使用"null",而不是一个对象实例。在这个例子中,如果doSomething(String.class)不是静态的,需要提供有效的MyObject的对象实例,而不是使用"null"作为参数;
Method.invoke(Object target, Object ...parameters)
方法接受可变长度的参数,但是在调用时必须为每一个参数提供一个准确的参量。在这个例子中,方法以字符串作为参数的,所以必须提供一个字符串。
(8)检测一个给定的类有哪些get和set方法
可以通过扫描一个类的所有方法并检查每个方法是否是get或set方法,如下代码段可用来找到类的get和set方法:
public static void printGettersSetters(Class c) {
Method[] methods = c.getMethods();
for (Method method : methods) {
if (isGetter(method)) {
System.out.println("getter: " + method);
}
if (isSetter(method)) {
System.out.println("setter: " + method);
}
}
} public static boolean isGetter(Method method) {
if (!method.getName().startsWith("get")) {
return false;
}
if (method.getParameterTypes().length != 0) {
return false;
}
if (void.class.equals(method.getReturnType())) {
return false;
}
return true;
} public static boolean isSetter(Method method) {
if (!method.getName().startsWith("set")) {
return false;
}
if (method.getParameterTypes().length != 1) {
return false;
}
return true;
}
(9)访问私有字段
此处解答前面留下的疑问: Class.getFields()
和Class.getField("FieldName")方法仅返回public字段,要想访问private字段需要调用 Class.getDeclaredFields()
或Class.getDeclaredField("FieldName")方法
示例:
public class Test {
private String name = null;
public Test(String name) {
this.name = name;
}
}
Test test= new Test ("cat");
Field privateField = Test.class. getDeclaredField("name");
privateField.setAccessible(true);
String fieldValue = (String) privateField.get(test);
System.out.println("fieldValue = " + fieldValue);
注意Test.class.getDeclaredField("name")
的使用,该方法仅仅返回指定类声明的字段,而不包括其任何父类中声明的字段。
注意上述代码中标色语句,通过调用Field.setAcessible(true)方法关闭了特定Field实例的访问检查,现在通过反射可以访问它,即使它是私有的或被保护的。编译器不允许普通代码访问该字段,因为仅适用于反射。
(10)访问私有方法
Class.getMethods()
和Class.getMethod(String name, Class[]parameterTypes)
方法仅返回public方法,想要访问私有方法需要调用Class.getDeclaredMethods()
或Class.getDeclaredMethod(String name,Class[] parameterTypes)
方法
示例:
public class Test { private void call(){
System.out.println("Hello");
}
}
Test test = new Test();
Method privateMethod = Test.class.getDeclaredMethod("call", null);
privateMethod.setAccessible(true);
privateMethod.invoke(test, null);
注意Test.class.getDeclaredMethod("call",null)
的使用,
该方法仅仅返回指定类声明的方法,而不包括其任何父类中声明的方法。
注意上述代码中标色语句,通过调用Method
.setAcessible(true)方法关闭了特定Method
实例的访问检查,现在通过反射可以访问它,即使它是私有的或被保护的。编译器不允许普通代码访问该方法,因为仅适用于反射。
(11)注解
使用Java反射可以在运行时访问Java类中的注解,那么首先要知道什么是注解?注解是Java5的一个新功能,可以在Java代码中插入注释或元数据,这些注解可以在编译时由预编译工具进行处理,也可以在运行时通过Java反射机制来处理。(若想了解Java注解的细节可查看笔者的另一篇文章,本文只关注在运行时通过Java反射获取注解信息。)
下面是一个类注解的示例:
注解
的定义:
package com.test; import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention; @Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface MyAnnotation {
public String name();
public String value();
}
注解的使用:
package com.test; @MyAnnotation(name="someName", value = "Hello World")
public class TheClass { }
解说:注解像接口那样定义,TheClass
类的上面有
@MyAnnotation
注解,在interface
前面的”@
”表明它是一个注解。一旦定义了某个注解就可以在代码中使用它,就像上面的例子那样。
在注解的定义中的两个指令@Retention(RetentionPolicy.RUNTIME)
和@Target(ElementType.TYPE)
表明注解是如何使用的
@Retention(RetentionPolicy.RUNTIME)
表明在运行时可以使用反射来访问这个注解,
如果没有设置这个指令,在运行时这个注解将不会被保存,因此通过反射访问不到。
@Target(ElementType.TYPE)
表明注解仅能用于类、接口或枚举声明等类型上,你也可以指定METHOD
或FIELD,
或者是@Target什么都不指定,这样它可以用在任何程序元素上。
获取类的注解:使用Class对象
例如:
package com.test; import java.lang.annotation.Annotation; public class WW { public static void main(String[] args){
Class c = TheClass.class;
Annotation[] annotations = c.getAnnotations();//此处获得类的所有注解
for(Annotation annotation : annotations){
if(annotation instanceof MyAnnotation){
MyAnnotation myAnnotation = (MyAnnotation) annotation;
System.out.println("name: " + myAnnotation.name());
System.out.println("value: " + myAnnotation.value());
}
} }
}
获取类的某个特定的注解(与上例相比仅测试代码不一样),如下:
package com.test; import java.lang.annotation.Annotation; public class WW { public static void main(String[] args){
Class aClass = TheClass.class;
Annotation annotation = aClass.getAnnotation(MyAnnotation.class);//此处获得类的某个指定的注解
if(annotation instanceof MyAnnotation){
MyAnnotation myAnnotation = (MyAnnotation) annotation;
System.out.println("name: " + myAnnotation.name());
System.out.println("value: " + myAnnotation.value());
}
}
}
获取方法的注解:使用Method对象
示例:
package com.test; import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention; @Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface MyAnnotation {
public String name();
public String value();
}
注:此处注解的定义与前述注解的定义有不一样的地方
package com.test; public class TheClass {
@MyAnnotation(name="someName",value = "Hello World")
public void doSomething(){}
}
package com.test; import java.lang.annotation.Annotation;
import java.lang.reflect.Method; public class WW { public static void main(String[] args){
Class c= TheClass.class;
Method method = null;
try {
method = c.getMethod("doSomething", null);
Annotation[] annotations = method.getDeclaredAnnotations();//此处获取指定方法的所有注解
for(Annotation annotation : annotations){
if(annotation instanceof MyAnnotation){
MyAnnotation myAnnotation = (MyAnnotation) annotation;
System.out.println("name: " + myAnnotation.name());
System.out.println("value: " + myAnnotation.value());
}
}
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (SecurityException e) {
e.printStackTrace();
} }
}
获取指定方法的某个特定的注解(与上例相比仅测试代码不一样):
package com.test; import java.lang.annotation.Annotation;
import java.lang.reflect.Method; public class WW { public static void main(String[] args){
Class c= TheClass.class;
Method method = null;
try {
method = c.getMethod("doSomething", null);
Annotation annotation = method.getAnnotation(MyAnnotation.class);//此处获取指定方法的某个特定的注解
if(annotation instanceof MyAnnotation){
MyAnnotation myAnnotation = (MyAnnotation) annotation;
System.out.println("name: " + myAnnotation.name());
System.out.println("value: " + myAnnotation.value());
}
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (SecurityException e) {
e.printStackTrace();
} }
}
获取方法参数的注解:使用Method对象
注解的定义:
package com.test; import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention; @Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.PARAMETER)//此处使用ElementType.PARAMETER
public @interface MyAnnotation {
public String name();
public String value();
}
注解的使用:
package com.test; public class TheClass {
public void doSomething(@MyAnnotation(name="aName", value="aValue")String parameter){}//注解位于方法参数前
}
测试:
package com.test; import java.lang.annotation.Annotation;
import java.lang.reflect.Method; public class WW { public static void main(String[] args){
Class c= TheClass.class;
Method method = null;
try {
method = c.getMethod("doSomething",new Class[]{String.class});
Annotation[][] parameterAnnotations = method.getParameterAnnotations();//返回二维的Annotation
数组,每个方法参数都有一个一维的Annotation
数组
Class[] parameterTypes = method.getParameterTypes();
int i=0;
for(Annotation[] annotations : parameterAnnotations){
Class parameterType = parameterTypes[i++];
for(Annotation annotation : annotations){
if(annotation instanceof MyAnnotation){
MyAnnotation myAnnotation = (MyAnnotation) annotation;
System.out.println("param: " + parameterType.getName());
System.out.println("name : " + myAnnotation.name());
System.out.println("value: " + myAnnotation.value());
}
}
}
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (SecurityException e) {
e.printStackTrace();
} }
}
获取成员变量的注解:使用Field对象
注解的定义:
package com.test; import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention; @Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface MyAnnotation {
public String name();
public String value();
}
注解的使用:
package com.test; public class TheClass {
@MyAnnotation(name="aName", value="aValue")
public String myField = null;
}
测试:
package com.test; import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.Method; public class WW { public static void main(String[] args){
Class c= TheClass.class;
Field field;
try {
field = c.getField("myField");
Annotation[] annotations = field.getDeclaredAnnotations();//此处获取字段的所有注解
for(Annotation annotation : annotations){
if(annotation instanceof MyAnnotation){
MyAnnotation myAnnotation = (MyAnnotation) annotation;
System.out.println("name: " + myAnnotation.name());
System.out.println("value: " + myAnnotation.value());
}
}
} catch (NoSuchFieldException e) {
e.printStackTrace();
} catch (SecurityException e) {
e.printStackTrace();
} }
}
获取指定字段的某个特定的注解(与上例相比仅测试代码不一样):
package com.test; import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.Method; public class WW { public static void main(String[] args){
Class c= TheClass.class;
Field field;
try {
field = c.getField("myField");
Annotation annotation = field.getAnnotation(MyAnnotation.class);
if(annotation instanceof MyAnnotation){
MyAnnotation myAnnotation = (MyAnnotation) annotation;
System.out.println("name: " + myAnnotation.name());
System.out.println("value: " + myAnnotation.value());
}
} catch (NoSuchFieldException e) {
e.printStackTrace();
} catch (SecurityException e) {
e.printStackTrace();
} }
}
(12)泛型
使用Java的泛型通常分为两种不同的情况:
(1)声明一个可参数化的类/接口。
(2)使用参数化的类。
当写一个类或接口时,可以指定它应该是可参数化的,就像java.util.List
接口那样,可以参数化java.util.List
来创建一个String列表而不是创建Object列表。(若想了解Java泛型的细节可查看笔者的另一篇文章,本文只关注在运行时通过Java反射获取泛型信息。)
一般来说,Java泛型信息在编译的时候被擦除了,所以在运行时访问不到任何泛型信息,但在极少数的情况下,在运行时是可以访问泛型信息的。这些情况实际上涵盖一些我们需要的Java泛型信息,如下所述这些情形:
泛型方法的返回值类型
如果你获取到一个java.lang.reflect.Method
对象,可以获取它的返回值类型信息,下面是一个示例,MyClass类有一个参数化返回值的方法:
public class MyClass {
protected List<String> stringList = ...;
public List<String> getStringList(){
return this.stringList;
}
}
在这个类中可以获取 getStringList()
方法的泛型返回值类型
。换句话说,你可以探测到getStringList()
返回的是
List<String>
而不仅是一个 List,
如下:
Method method = MyClass.class.getMethod("getStringList", null);
Type returnType = method.getGenericReturnType();
if(returnType instanceof ParameterizedType){
ParameterizedType type = (ParameterizedType) returnType;
Type[] typeArguments = type.getActualTypeArguments();
for(Type typeArgument : typeArguments){
Class typeArgClass = (Class) typeArgument;
System.out.println("typeArgClass = " + typeArgClass);
}
}
代码输出 "typeArgClass = class java.lang.String"。
Type[]
数组typeArguments
包含一项:一个代表java.lang.String类
的
Class
实例。
Class
实现了Type
接口。
方法的泛型参数
通过Java反射可以在运行时访问方法的泛型参数的类型,下面的示例中,MyClass类有一个使用参数化的List作为参数的方法:
public class MyClass {
protected List<String> stringList = ...; public void setStringList(List<String> list){
this.stringList = list;
}
}
可以访问方法参数的泛型类型,如下:
Method method = Myclass.class.getMethod("setStringList", List.class);
Type[] genericParameterTypes = method.getGenericParameterTypes();
for(Type genericParameterType : genericParameterTypes){
if(genericParameterType instanceof ParameterizedType){
ParameterizedType aType = (ParameterizedType) genericParameterType;
Type[] parameterArgTypes = aType.getActualTypeArguments();
for(Type parameterArgType : parameterArgTypes){
Class parameterArgClass = (Class) parameterArgType;
System.out.println("parameterArgClass = " + parameterArgClass);
}
}
}
代码输出"parameterArgType= class java.lang.String"。
泛型字段
可以访问public字段的泛型类型。下面是一个例子,MyClass类有一个实例变量stringList
.
public class MyClass {
public List<String> stringList = ...;
}
访问:
Field field = MyClass.class.getField("stringList");
Type genericFieldType = field.getGenericType();if(genericFieldType instanceof ParameterizedType){
ParameterizedType aType = (ParameterizedType) genericFieldType;
Type[] fieldArgTypes = aType.getActualTypeArguments();
for(Type fieldArgType : fieldArgTypes){
Class fieldArgClass = (Class) fieldArgType;
System.out.println("fieldArgClass = " + fieldArgClass);
}
}
代码将输出"fieldArgClass = class java.lang.String"。
(13)数组
创建数组是通过Java反射机制由java.lang.reflect.Array
类来完成的,如下:
int[] array = (int[]) Array.newInstance(int.class, 3);
上述代码创建了一个int数组,Array.newInstance()
方法的第一个参数告诉我们数组的元素类型,
第二个参数声明了数组需要为多少个元素分配空间。
访问数组的元素可以通过Array.get(...)
和Array.set(...)
方法实现,如下:
int[] array = (int[]) Array.newInstance(int.class, 3);
Array.set(array, 0, 123);
Array.set(array, 1, 456);
Array.set(array, 2, 789);
System.out.println("array[0] = " + Array.get(array, 0));
获取某类型数组(如String数组)的Class对象
Class c = String[].class;
除了上述方式外,还可以使用Class.forName()
方法
例如,可以通过如下方式获取int数组的Class对象:
Class c = Class.forName("[I");
解说:字母I在JVM中代表一个int类型,左边的 [
表示它是一个int数组的class,这对其它的基本类型也有效。
对于对象,需要使用一个稍微不同的记号:
Class c = Class.forName("[Ljava.lang.String;");
解说:在左边的” [L
”和右边的” ;
”之间的类名代表对象数组的类型。
另外一个需要注意的是不能使用Class.forName()
方法获取基本类型的
Class
对象,
下面两个例子都会抛出ClassNotFoundException
异常:
Class c1 = Class.forName("I");
Class c2 = Class.forName("int");
可以像下面这样获取基本数据类型和对象的Class对象(下面这个方法可以写为工具类):
public Class getClass(String className){
if("int" .equals(className)){
return int.class;
}
if("long".equals(className)){
return long.class;
}
...
return Class.forName(className);
}
一旦获得了某个类型的Class对象(如int.class),有一个简单的方法可以用来获取该类型数组(如int数组)的Class对象。解决方案是创建所需类型的空数组并从这个空数组获取Class对象,如下:
Class cl = getClass("ClassName");//此处调用上述getClass()方法
Class theClass = Array.newInstance(cl, 0).getClass();
这提供了一个统一的方法来获取任何类型的数组的Class对象。
总结:
使用反射时需遵循三个步骤:
第一步:获取你想要操作的那个类的Class对象;
第二步:调用Class对象的一些方法(如getDeclaredMethodsClass()等),以取得你想要操作的那个类中定义的方法或成员变量;
第三步:使用reflection API来操作上述步骤获取的信息。
实际应用:
前面讲的都是如何获取类的某些信息,此处我们来看一个有实际意义的案例:
package com.test; import java.lang.reflect.*; public class WW { public int add(int a, int b) {
return a+b;
} public static void main(String args[]) {
try {
Class cls = Class.forName("com.test.WW");//完整类名
Class partypes[] = new Class[2];
partypes[0] = Integer.TYPE;
partypes[1] = Integer.TYPE;
Method meth = cls.getMethod("add", partypes); WW methobj = new WW();
Object arglist[] = new Object[2];
arglist[0] = new Integer(37);
arglist[1] = new Integer(47);
Object retobj = meth.invoke(methobj, arglist); Integer retval = (Integer) retobj;
System.out.println(retval.intValue()); } catch (Throwable e) {
System.err.println(e);
}
}
}
输出:84
解说:上例中,getMethod用于查找一个具有两个整型参数且名为 add 的方法,找到该方法并创建了相应的Method对象之后,在某个指定的对象实例(上例中是methobj)中执行它。执行该方法的时候,需要提供一个参数列表,这在上例中是分别包装了整数37和47的两个Integer对象。执行方法的返回是一个Integer对象,它封装了返回值84。
结束语:
参考资料:
(1)http://blog.csdn.net/jackiehff/article/details/8509075
Java:反射的更多相关文章
- 第28章 java反射机制
java反射机制 1.类加载机制 1.1.jvm和类 运行Java程序:java 带有main方法的类名 之后java会启动jvm,并加载字节码(字节码就是一个类在内存空间的状态) 当调用java命令 ...
- Java反射机制
Java反射机制 一:什么事反射机制 简单地说,就是程序运行时能够通过反射的到类的所有信息,只需要获得类名,方法名,属性名. 二:为什么要用反射: 静态编译:在编译时确定类型,绑定对象,即通过 ...
- java反射(基础了解)
package cn.itcast_01; /** *Person类 */ public class Person { /** 姓名 */ private String name; ...
- java基础知识(十一)java反射机制(上)
java.lang.Class类详解 java Class类详解 一.class类 Class类是java语言定义的特定类的实现,在java中每个类都有一个相应的Class对象,以便java程序运行时 ...
- java基础知识(十一)java反射机制(下)
1.什么是反射机制? java反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法,对于任意一个对象都能够调用他的属性和方法,这种动态获取属性和方法的功能称为java的反射机制. ...
- java反射学习之二万能EXCEL导出
一.EXCEL导出的实现过程 假设有一个对象的集合,现在需要将此集合内的所有对象导出到EXCEL中,对象有N个属性:那么我们实现的方式是这样的: 循环这个集合,在循环集合中某个对象的所有属性,将这个对 ...
- java反射学习之一反射机制概述
一.反射机制背景概述 1.反射(reflection)是java被视为动态语言的一个关键性质 2.反射机制指的是程序在运行时能获取任何类的内部所有信息 二.反射机制实现功能概述 1.只要给定类的全名, ...
- java反射 之 反射基础
一.反射 反射:JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法:对于任意一个对象,都能够调用它的任意一个方法和属性:这种动态获取的信息以及动态调用对象的方法的功能称为 ...
- java反射 cglib asm相关资料
有篇文章对java反射的调用的效率做了测试,写的比较好.猛击下面地址 http://www.blogjava.net/stone2083/archive/2010/09/15/332065.html ...
- 超详细的java反射教程
看技术博客时,看到关于java反射的博文,写的非常好.猛击下面的地址,开始java反射之旅 中文翻译地址:http://ifeve.com/java-reflection/ 英文原版地址:http:/ ...
随机推荐
- SQL Server(四)——查询练习(45道习题)
题目:设有一数据库,包括四个表:学生表(Student).课程表(Course).成绩表(Score)以及教师信息表(Teacher). 四个表的结构分别如表1-1的表(一)~表(四)所示,数据如表1 ...
- python之装饰器
一.简单装饰器: #定义装饰器函数 def W1(main_func): def outer(): print("before") main_func() print(" ...
- spring listener监听器
1.Listener的定义与作用 监听器Listener就是在application,session,request三个对象创建.销毁或者往其中添加修改删除属性时自动执行代码的功能组件. Listen ...
- Linux服务器下nginx的安全配置
1.一些常识 linux下,要读取一个文件,首先需要具有对文件所在文件夹的执行权限,然后需要对文件的读取权限. php文件的执行不需要文件的执行权限,只需要nginx和php-fpm运行账户的读取权限 ...
- oracle 数据库导出数据
cmd导出数据: exp ZD_ZD_ZDWW/zdzd1402!@11.111.111.213/orcl file=c:\1234.dmp owner=ZD_ZD_ZDWW
- 查看Android支持的硬解码信息
通过/system/etc/media_codecs.xml可以确定当前设备支持哪些硬解码.通过/system/etc/media_profiles.xml可以知道设备支持的具体profile和lev ...
- node命令
- 父容器根据子容器高度自适应:设置父容器 height:100%;overflow:hidden;
父容器根据子容器高度自适应:设置父容器 height:100%;overflow:hidden;
- 解决Qt在openSUSE上编译出现“cannot find -lGL”错误
在openSUSE上编译QT5.4程序出现“cannot find -lGL”,就连example都无法通过编译.QT是在官网下的最新的安装包. 大体意思是,缺少qt运行时所需要的openGL库.决绝 ...
- Hadoop多硬盘配置时的注意事项
<!-- hdfs-site.xml --> <property> <name>dfs.datanode.fsdataset.volume.choosing.pol ...