看完这篇文章,你将学会:
- 反射的概念
- 反射的功能及应用
- 如何应用反射开发简单的框架
一、先来一个小例子
我们先来看一个小问题:
如何打印一个已有的Double类的全部信息?
package www.reflect;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.Scanner;
/**
* This program use reflection to print all features of a class
*/
public class reflectionTest {
public static void main(String[] args) throws ClassNotFoundException {
String name;
if(args.length>0)
name = args[0];
else{
Scanner in = new Scanner(System.in);
System.out.println("Enter class name:");
name = in.next();
}
Class cls = Class.forName(name);
Class superClass = cls.getSuperclass();
String modifiers = Modifier.toString(cls.getModifiers());//获得当前类的标识符
if(modifiers.length()>0){
System.out.print(modifiers+" ");
}
System.out.print("class "+name);
if(superClass!=null && superClass!=Object.class){
System.out.print(" extends "+superClass.getName());
}
System.out.print("\n{\n");
printConstructor(cls);
System.out.println();
printMethods(cls);
System.out.println();
printField(cls);
System.out.println("}");
}
// print all constructors of a class
public static void printConstructor(Class cls){
Constructor[] constructors = cls.getConstructors();
for(Constructor c:constructors) {
String name = c.getName();
System.out.print(" ");
String modifiers = Modifier.toString(c.getModifiers());
if (modifiers.length() > 0) {
System.out.print(modifiers + " ");
}
System.out.print(name + "(");
//print parameter types
Class[] parameterType = c.getParameterTypes();
for(int j = 0;j<parameterType.length;j++){
if(j>0){
System.out.print(",");
}
}
System.out.println(");");
}
}
//print all methods of a class
public static void printMethods(Class cls){
Method[] methods = cls.getDeclaredMethods();
for(Method m:methods){
Class retType = m.getReturnType();
String name = m.getName();
System.out.print(" ");
//print modofiers,return type and method
String modifiers = Modifier.toString(m.getModifiers());
if(modifiers.length()>0){
System.out.print(modifiers+" ");
System.out.print(retType.getName()+" "+name+"(");
Class[] paramTypes = m.getParameterTypes();
for(int j = 0;j<paramTypes.length;j++){
if(j>0){
System.out.print(",");
}
}
System.out.println(");");
}
}
}
//print all fields of a class
public static void printField(Class cls){
Field[] fields = cls.getDeclaredFields();
for(Field f:fields){
Class type = f.getType();
String name = f.getName();
System.out.print(" ");
String modifiers = Modifier.toString(f.getModifiers());
if(modifiers.length()>0){
System.out.print(modifiers+" ");
}
System.out.println(type.getName()+" "+name+";");
}
}
}
以下是程序的输出:
可见,这个程序输出了Double类所有的构造器,方法,属性。
后面将讲解反射如何实现这种功能。
Enter class name:
public final class java.lang.Double extends java.lang.Number
{
public java.lang.Double();
public java.lang.Double();
public boolean equals();
public static java.lang.String toString();
public java.lang.String toString();
public int hashCode();
public static int hashCode();
public static double min(,);
public static double max(,);
public static native long doubleToRawLongBits();
public static long doubleToLongBits();
public static native double longBitsToDouble();
public volatile int compareTo();
public int compareTo();
public byte byteValue();
public short shortValue();
public int intValue();
public long longValue();
public float floatValue();
public double doubleValue();
public static java.lang.Double valueOf();
public static java.lang.Double valueOf();
public static java.lang.String toHexString();
public static int compare(,);
public static boolean isNaN();
public boolean isNaN();
public static boolean isFinite();
public static boolean isInfinite();
public boolean isInfinite();
public static double sum(,);
public static double parseDouble();
public static final double POSITIVE_INFINITY;
public static final double NEGATIVE_INFINITY;
public static final double NaN;
public static final double MAX_VALUE;
public static final double MIN_NORMAL;
public static final double MIN_VALUE;
public static final int MAX_EXPONENT;
public static final int MIN_EXPONENT;
public static final int SIZE;
public static final int BYTES;
public static final java.lang.Class TYPE;
private final double value;
private static final long serialVersionUID;
}
Process finished with exit code 0
下面我们将解决一下问题:
1.什么是反射
2.反射的主要用途
2.如何通过反射获取类信息(类名,构造器,方法,属性)
二、什么是反射
反射 (Reflection) 是 Java 的特征之一,它允许运行中的 Java 程序获取自身的信息,并且可以操作类或对象的内部属性。
反射的核心是 JVM 在运行时才动态加载类或调用方法/访问属性,它不需要事先(写代码的时候或编译期)知道运行对象是谁。
Java 反射主要提供以下功能:
- 在运行时判断任意一个对象所属的类;
- 在运行时构造任意一个类的对象;
- 在运行时判断任意一个类所具有的成员变量和方法(通过反射甚至可以调用private方法);
- 在运行时调用任意一个对象的方法
三、反射的主要用途
举一个很常见的例子,当我们在使用IDEA时,输入一个对象或类并且想调用它的属性或方法时,输入点号,编译器就会自动列出它的属性或方法,这里就会用到反射。
反射最重要的用途就是开发各种通用框架。 很多框架都是配置化的,为了保证框架的通用性,它们可能需要根据配置文件加载不同的对象或类,调用不同的方法,这个时候就必须用到反射,运行时动态加载需要加载的对象。
对与框架开发人员来说,反射虽小但作用非常大,它是各种容器实现的核心。而对于一般的开发者来说,不深入框架开发则用反射用的就会少一点。
四、反射的基本运用
在程序运行期间,Java运行时系统始终为所有的对象维护一个被称为运行时的类型标识。这个信息跟踪着每个对象所属的类。虚拟机利用运行时类型信息选择相应的方法执行。可以通过一个专门的类访问这些信息,保存这些信息的类被称为Class类。
1.如何获得一个Class类型的实例
以 java.util.Date 为例简单说明以下三种方法的使用
(1)Object类的getClass()方法
package www.reflect;
public class Test {
public static void main(String[] args) {
Date date = new Date();
Class cls = date.getClass();
System.out.println(cls.getName());//java.util.Date
}
}
(2)Class类的静态方法forName()
public class Test {
public static void main(String[] args) throws ClassNotFoundException {
Date date = new Date();
String className = "java.util.Date";
Class cls = Class.forName(className);
}
}
这个方法只有在className是类名或接口名时才能够执行。否则,forName方法将抛出一个checkException异常。无论何时使用这个方法,都应该提供一个异常处理。
(3)T.class
这里的T表示任意的Java类型。一个Class对象实际上表示的是一个类型,而这个类型未必一定是一种类。
例如:int不是类,但int.class是一个Class类型的对象。
public class Test {
public static void main(String[] args) {
Class cls = Date.class;
System.out.println(cls.getName());//java.util.Date
Class cls2 = int.class;
System.out.println(cls2.getName());//int
Class cls3 = Double[].class;
System.out.println(cls3.getName());//[Ljava.lang.Double;
}
}
2.判断是否为某个类的实例
一般地,我们用 instanceof 关键字来判断是否为某个类的实例。同时我们也可以借助反射中 Class 对象的 isInstance() 方法来判断是否为某个类的实例,它是一个 native 方法:
public native boolean isInstance(Object obj);
3.创建实例
- 通过反射类生成对象主要有两种方式:
1.使用Class对象的的newInstance()方法来创建Class对象对应类的实例
Class<?> c = String.class;
Object str = c.newInstance();
2.先通过Class对象获取指定的Constructor对象,在调用Constructor对象的newInstance()方法来创建实例。这种方法可以用指定的构造器构造类的实例
//获取String所对应的Class对象
Class<?> c = String.class;
//获取String类带一个String参数的构造器
Constructor constructor = c.getConstructor(String.class);
//根据构造器创建实例
Object obj = constructor.newInstance("23333");
System.out.println(obj);
- 现在发现除了关键字new之外,对于对象的实例化模式有了第二种做法,通过反射进行。
4.获取方法(Method类)
获取某个Class对象的方法集合,主要有以下几个方法:
(1)getDeclaredMethods() 方法返回类或接口声明的所有方法,包括公共,保护,默认和私有方法,但不包括继承的方法。
public Method[] getDeclaredMethods() throws SecurityException
(2)getMethods()方法返回某个类的所有public 方法,包括其继承类的公用方法。
public Method[] getMethods() throws SecurityException
(3)getMethod()方法返回一个特定的方法,其中第一个参数为方法名称,后面的参数化为方法的参数对应Class的对象。
public Method getMethod(String name, Class<?>... parameterTypes)
5.获取构造器信息(Constructor类)
获取类构造器的用法与上述获取方法的用法类似。主要是通过Class类的getConstructor方法得到Constructor类的一个实例,而Constructor类有一个newInstance方法可以创建一个对象实例:
public T newInstance(Object ... initargs)
- 注意:
Class类通过反射实例化类对象的时候,只能够调用类中的无参构造。如果现在类中没有无参构造则无法使用Class类调用,只能够通过明确的构造调用实例化处理。
6.获取类的字段信息(Field类)
(1) 父类中
(父类中)-取得类中全部属性:
public Field[] getFields() throws SecurityException
(父类中)-取得类中指定名称属性:
public Field getField(String name) throws
NoSuchFieldException, SecurityException
(2)本类中
取得类中全部属性
public Field[] getDeclaredFields() throws SecurityException
取得类中指定名称属性
public Method getDeclaredMethod(String name, Class<?parameterTypes) throws NoSuchMethodException, SecurityException
7.反射调用普通方法
类中普通方法的反射调用你在开发之中一定会使用到,并且使用好了可以节省大量的重复编码。在Class类中有如
下两种取得类中普通方法的函数:
- 取得全部普通方法
public Method[] getMethods() throws SecurityException
- 取得指定普通方法
public Method getMethod(String name, Class<?>... parameterTypes)
当我们从类中获取了一个方法后,我们就可以用 invoke() 方法来调用这个方法。invoke 方法的原型为:
public Object invoke(Object obj, Object... args)throws IllegalAccessException,
IllegalArgumentException,InvocationTargetException
来看一个实例:
package www.reflect;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
public class test1 {
public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException {
Class<?> klass = methodClass.class;
//创建methodClass的实例
Object obj = klass.newInstance();
//获取methodClass类的add方法
Method method = klass.getMethod("add",int.class,int.class);
//调用method对应的方法 => add(1,4)
Object result = method.invoke(obj,1,4);
System.out.println(result);//5
}
}
class methodClass{
public static int a = 1;
public int add(int x, int y){
return x+y;
}
public int sub(int x,int y){
return x-y;
}
}
4.反射调用类中属性
在Class类中提供有两组取得属性的方法:
第一组(父类中)-取得类中全部属性: Field[] getFields()
第一组(父类中)-取得类中指定名称属性: Field getField(String name)
第二组(本类中)-取得类中全部属性: Field[] getDeclaredFields()
第二组(本类中)-取得类中指定名称属性 : Method getDeclaredMethod(String name, Class<?>… parameterTypes)
现在有了这些方法介绍,是不是更好理解文章开头的那段代码怎么实现获取类信息的功能了呢。