首先还是概念问题。
什么是反射?
能够分析类能力的程序称为反射。反射的机制很强大,反射机制可以用来:
<1>在运行时分析类的能力
<2>在运行时查看对象,比如,编写一个toString方法供所有类使用
<3>实现通用的数组操作代码
<4>利用Method对象
Java反射框架主要提供以下功能:
1.在运行时判断任意一个对象所属的类;
2.在运行时构造任意一个类的对象;
3.在运行时判断任意一个类所具有的成员变量和方法(通过反射甚至可以调用private方法);
4.在运行时调用任意一个对象的方法
重点:是运行时而不是编译时
Class类
在程序运行期间,java运行时系统始终为所有对象维护一个被称为运行时的类型标识。这个信息跟踪着每个对象所属的类。虚拟机利用运行时类型信息选择相应的方法执行。
然而,可以通过专门的java类访问这些信息。保存这些信息的类被称为Class。
1.1获得class类对象的方法:
<1>Object类中的getClass()方法将会返回Class类型的实例。
如:Class c1 = e.getClass();
<2>可以调用静态方法forName获得类名对应的Class类
如:
String className = "java.util.Random";
Class c1 = Class.forName(className);
如果类名保存在字符串中,并可在运行中改变,就可以使用这个方法,这个方法只有在className是类名或接口名时才能执行。否则将抛出一个已检查异常。
<3>如果T是任意的java类型(或void关键字),T.class将代表匹配的类对象。
如:
Class c1 = Random.class;
请注意,一个Class对象实际上表示的是一个类型,而这个类型未必是一个类,比如,int不是类,但int.class是一个Class类型的对象。
虚拟机为每个类型管理一个Class对象,因此,可以用==实现两个类对象的比较操作。如:if(e.getClass == Employee.class);
可以用来动态地创建一个类的实例。如:e.getClass().newInstance();
创建了一个与e 具有相同类型的实例。newInstance方法调用默认的构造器(无参)初始化新创建的对象。如果这个类没有默认的构造器,就会抛出一个异常。
利用反射分析类的能力
简要地介绍一下反射机制最重要的内容——检查类的结构。
在java.lang.reflect 包中有三个类Field、Method和Constructor分别用于描述类的域、方法和构造器。这三个类都有一个getClass的方法,用来返回项目的名称。Field类有一个getType方法,用来返回描述域所属类型的Class对象。Method 和Constructor类有能够报告参数类型的方法,Method类还有一个可以报告返回类型的方法。这三个类还有一个叫做getModifiers的方法,它将返回一个整型数值,用不同的位开关描述public和static这样的修饰符使用状况。另外,还可以利用java.lang.reflect包中的Modifier类的静态方法分析getModifiers返回的整型数值。比如,可以使用Modifier类中的isPublic、isPrivate、或isFinal类的相应方法,并对返回的整型数值进行分析,另外还可以利用Modifiers.toString方法将修饰符打印出来。
Class类中的getFileds、getMethods和getConstructors 方法将分别返回类提供的public域、方法和构造器数组,其中包括超类的公有成员。Class类的getDeclareFields、getDeclareMethods和getDeclaredConstructors方法将分别返回类中声明的全部域、方法和构造器,包括私有和受保护成员,但不包括超类的成员。
下面给出一个例子:
package reflection;
import java.util.*;
import java.lang.reflect.*;
public class ReflectionTest {
public static void main(String[] args) {
// TODO Auto-generated method stub
String name;
if(args.length > 0) name = args[0];
else{
Scanner in = new Scanner(System.in);
System.out.println("Enter class name (e.g. java.util.Date):");
name = in.next();
}
try{
Class cl = Class.forName(name);
Class supercl = cl.getSuperclass();
String modifiers = Modifier.toString(cl.getModifiers());
if(modifiers.length() > 0) System.out.print(modifiers + " ");
System.out.print("class " + name);
if(supercl != null && supercl != Object.class) System.out.print(" extends " + supercl.getName());
System.out.println("\n{\n");
printConstructors(cl);
System.out.println();
printMethods(cl);
System.out.println();
printFields(cl);
System.out.println("}");
}
catch(ClassNotFoundException e){
e.printStackTrace();
}
System.exit(0);
}
private static void printFields(Class cl) {
// TODO Auto-generated method stub
Field[] fields = cl.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 + ";");
}
}
private static void printMethods(Class cl) {
// TODO Auto-generated method stub
Method[] methods = cl.getDeclaredMethods();
for(Method m : methods){
Class retType = m.getReturnType();
String name = m.getName();
System.out.print(" ");
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.print(paramTypes[j].getName());
}
System.out.println(");");
}
}
private static void printConstructors(Class cl) {
// TODO Auto-generated method stub
Constructor[] constructors = cl.getDeclaredConstructors();
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 + "(");
Class[] paramTypes = c.getParameterTypes();
for(int j = 0; j < paramTypes.length; j++)
{
if(j > 0) System.out.print(", ");
System.out.print(paramTypes[j].getName());
}
System.out.println(");");
}
}
}
运行结果,输入java.lang.Double:
public final class java.lang.Double extends java.lang.Number
{
public java.lang.Double(double);
public java.lang.Double(java.lang.String);
public boolean equals(java.lang.Object);
public static java.lang.String toString(double);
public java.lang.String toString();
public int hashCode();
public static native long doubleToRawLongBits(double);
public static long doubleToLongBits(double);
public static native double longBitsToDouble(long);
public int compareTo(java.lang.Double);
public volatile int compareTo(java.lang.Object);
public byte byteValue();
public short shortValue();
public int intValue();
public long longValue();
public float floatValue();
public double doubleValue();
public static java.lang.Double valueOf(double);
public static java.lang.Double valueOf(java.lang.String);
public static java.lang.String toHexString(double);
public static int compare(double, double);
public static boolean isNaN(double);
public boolean isNaN();
public static boolean isInfinite(double);
public boolean isInfinite();
public static double parseDouble(java.lang.String);
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 java.lang.Class TYPE;
private final double value;
private static final long serialVersionUID;
}
这个程序可以分析java解释器能够加载的任何类,而不仅仅是编译程序时可以使用的类。
在运行时使用反射分析对象
通过上面我们知道如何查看任意对象的数据域名称和类型:
1.获得对应Class对象
2.通过Class对象查询getDeclaredFields。
在编写程序时,如果知道想要查看的域名和类型,查看指定的域是一件简单的事情。而利用反射机制可以查看编译时还不清楚的对象域。
查看对象域的关键方法是Field类中的get方法。如果f是一个Field类型的对象,obj是某个包含f域的类的对象,f.get(obj)将会返回一个对象,其值为obj域的当前值。如:
Employee h = new Employee("harry hacker");
Class cl = h.getClass();
Field f = cl.getDeclaredField("name");
Object v = f.get(h);
当然鉴于java安全机制这段代码会抛出一个IllegalAccessException异常。
反射机制的默认行为受限于java的访问控制。但是,如果一个java程序没有受到安全管理器的控制,就可以覆盖访问控制。为达这个目的,需要调用Field、Method或Constructor对象的setAccessible方法。如:f.setAccessible(true);
这样上面的代码就不会报错。
setAccessible方法是AccessibleObject类中的一个方法,它是Field。Method和Constructor类的公共超类。这个特性是为了调试、持久存储和相似机制提供的。
当然,可以获得就可以设置。调用f.set(obj,value)可以将obj对象的f域设置成新值。
如下例显示了如何编写一个可供任意类使用的通用toString方法
package objectAnalyzer;
import java.util.ArrayList;
public class objectAnalyzerTest {
public static void main(String[] args) {
// TODO Auto-generated method stub
ArrayList<Integer> squares = new ArrayList<>();
for(int i = 1; i <= 5; i++)
squares.add(i * i);
System.out.println(new ObjectAnalyzer().toString(squares));
}
}
package objectAnalyzer;
import java.lang.reflect.AccessibleObject;
import java.lang.reflect.Array;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
public class ObjectAnalyzer {
private ArrayList<Object> visited = new ArrayList<>();
public String toString(Object obj)
{
if(obj == null) return "null";
if(visited.contains(obj)) return "...";
visited.add(obj);
Class cl = obj.getClass();
if(cl == String.class) return (String) obj;
if(cl.isArray())
{
String r = cl.getComponentType() + "[]{";
for(int i = 0; i < Array.getLength(obj); i++)
{
if(i > 0) r += ",";
Object val = Array.get(obj, i);
if(cl.getComponentType().isPrimitive()) r += val;
else r += toString(val);
}
return r + "}";
}
String r = cl.getName();
do{
r += "[";
Field[] fields = cl.getDeclaredFields();
AccessibleObject.setAccessible(fields, true);
for(Field f : fields)
{
if(!Modifier.isStatic(f.getModifiers()))
{
if(!r.endsWith("[")) r += ",";
r += f.getName() + "=";
try{
Class t = f.getType();
Object val = f.get(obj);
if(t.isPrimitive()) r += val;//此方法主要用来判断Class是否为基础类型
else r += toString(val);
}
catch(Exception e)
{
e.printStackTrace();
}
}
}
r += "]";
cl = cl.getSuperclass();
}
while(cl != null);
return r;
}
}
运行结果:
java.util.ArrayList[elementData=class java.lang.Object[]{java.lang.Integer[value=1][][],java.lang.Integer[value=4][][],java.lang.Integer[value=9][][],java.lang.Integer[value=16][][],java.lang.Integer[value=25][][],null,null,null,null,null},size=5][modCount=5][][]