许多java对象在运行时表现出两种状态:编译时类型和运行时类型。例如:Person p = new Student();
为了解决这个问题,需要知道该对象运行时类型的方法。
第一种做法是假设在编译时和运行时完全知道类型的真实信息,这就可以用instanceof进行判断,然后利用强制类型转换将其转换成其运行时类型的变量。
第二种做法是编译时完全不知道这个对象可能属于哪些类,这时就必须要利用反射。
要获得Class对象,通常用下面三种方式:
1、Class类的静态方法,Class.forName()
2、类的class属性,Person.class
3、通过对象的getClass()方法。这个方法是java.lang.Object类中的方法。
1、2都是通过类来获取的,一般情况下用第二种比较好,直接调用属性。但是当程序只能获取到字符串如”java.lang.String”时,就只能用第一种了。
获得某个类对应的Class对象后,就可以调用Class对象的方法来获得该对象和该类的真实信息了,如类包含的构造器、方法、成员变量、Annotation、内部类、接口、继承的父类、修饰符、所在包、类名等等。
使用反射来生成对象有两种方式:
1,使用Class对象的newInstance()方法来创建该Class对象对应类的实例,这种方式要求该对象的对应类有默认构造器。
2,先使用Class对象获取指定的Construtor对象,再调用Construtor对象的newInstance()方法来创建该Class对象对应类的实例。
使用反射调用方法:
获得类对应的Class对象后,可以通过该Class对象的getMethods()方法或者getMethod()方法来获取全部方法或者指定方法,返回Method数组或者对象。
在Method中包含一个invoke()方法:public Object invoke(Object obj, Object... args),其中obj是执行该方法的主调,后面的args是传入的实参。
使用反射访问成员变量:
getField(String name)获取指定名称的public成员变量
getDeclaredField(String name)获取指定名称的成员变量,与访问权限无关。
Field提供了两组方法来读取或者设置成员变量值:
getXxx(Object obj),获取成员变量值,这里Xxx对应8种基本类型,如果该成员变量是引用类型,则取消get后面的Xxx。
setXxx(Object obj),设置成员变量值,这里Xxx对应8种基本类型,如果该成员变量是引用类型,则取消set后面的Xxx。
使用反射操作数组:
newInstance(Class<?> componentType, int... dimensions)创建一个指定元素类型,指定维度的新数组。
getXxx(Object array, int index) 这里Xxx对应8种基本类型,如果该成员变量是引用类型,则取消get后面的Xxx。
setFloat(Object array, int index, Xxx f) 这里Xxx对应8种基本类型,如果该成员变量是引用类型,则取消set后面的Xxx。
注意这些方法都是java.lang.reflect.Array的静态方法
如果在程序中为一个或多个接口动态生成实现类或创建实例,都可以使用Proxy生成动态代理。
使用泛型可以避免强制类型转换,下面代码在编译时不会发生错误,但是在运行时抛出异常:
public static Object getInstance(String clsName)
{
try
{
// 创建指定类对应的Class对象
Class cls = Class.forName(clsName);
// 返回使用该Class对象所创建的实例
return cls.newInstance();
}
catch(Exception e)
{
e.printStackTrace();
return null;
}
}
但是如果改成下面这样,就不会发生了:
public class CrazyitObjectFactory2
{
public static <T> T getInstance(Class<T> cls)
{
try
{
return cls.newInstance();
}
catch(Exception e)
{
e.printStackTrace();
return null;
}
}
public static void main(String[] args)
{
// 获取实例后无须类型转换
Date d = CrazyitObjectFactory2.getInstance(Date.class);
JFrame f = CrazyitObjectFactory2.getInstance(JFrame.class);
}
}
使用反射来获取泛型信息:
public class GenericTest
{
private Map<String , Integer> score;
public static void main(String[] args)
throws Exception
{
Class<GenericTest> clazz = GenericTest.class;
Field f = clazz.getDeclaredField("score");
// 直接使用getType()取出的类型只对普通类型的成员变量有效
Class<?> a = f.getType();
// 下面将看到仅输出java.util.Map
System.out.println("score的类型是:" + a);
// 获得成员变量f的泛型类型
Type gType = f.getGenericType();
// 如果gType类型是ParameterizedType对象
if(gType instanceof ParameterizedType)
{
// 强制类型转换
ParameterizedType pType = (ParameterizedType)gType;
// 获取没有泛型信息的原始类型
Type rType = pType.getRawType();
System.out.println("原始类型是:" + rType);
// 取得泛型参数的类型
Type[] tArgs = pType.getActualTypeArguments();
System.out.println("泛型信息是:");
for (int i = 0; i < tArgs.length; i++)
{
System.out.println("第" + i + "个泛型类型是:" + tArgs[i]);
}
}
else
{
System.out.println("获取泛型类型出错!");
}
}
}