深入理解java——反射

首先还是概念问题。
什么是反射?
能够分析类能力的程序称为反射。反射的机制很强大,反射机制可以用来:
<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][][]

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值