本帖最后由 佟亚鹏 于 2012-9-16 12:53 编辑
楼主理解反射,首先要了解反射的基石Class这个类 Java类用来描述一类事物的共性,而Class类就是描述了一个Java类的相关信息的,Class 类的实例表示正在运行的 Java 应用程序中的类和接口。Class类描述的信息有,类的名字、类的访问属性、类锁属于的包名、字段名称的列表、方法名称的列表等。在程序的运行期间,一旦我们想生成那个类的一个对象,用于执行程序的Java 虚拟机首先就会检查那个类型的Class 对象是否已经载入。如果没有载入,JVM 就会查找同名的.class 文件,并将其载入,一旦那个类型的Class 对象进入内存,就以它为模版来创建那一类型的所有对象。
以java.lang.String这个类来说明,有两种方式可以获得一个Java类的字节码,就是它对应的Class对象 第一种方法是通过类中一个静态的字段class获得 String.class 第二种方法是通过Java类的基类Object所提供的getClass方法 new String().getClass() 下面的这段代码打印的结果是true,表明每个Java类的字节码在程序中只有一份,这也很容易理解,有一份就够了 public static void main(String[] args) { //通过上述的第一种方法拿到Class对象
Class c1 = String.class; //通过上述的第二种方法拿到Class对象
Class c2 = new String().getClass(); //比较着两份字节码是否相同
System.out.println(c1 == c2);
} Java 基本的 类型(boolean、byte、char、short、int、long、float 和 double)和关键字 void 也有自己的 Class 对象 ,这点Java api中Class类的描述中有提到,可以通过类型+.class获得,如int.class、void.class,这九个对象时Java预定义的Class实例对象。数组类型也有他们对象的Class对象,如:int[].class、long.class等。总的来说,只要源程序中出现的类型,都有各自的Class实例对象。 可以通过Class的静态方法forName获得一个Java对象,它的参数是一个Java类的完整名(包名+类名),如果找不到这个类会抛出ClassNotFound这个异常, public static void main(String[] args) {
try {
//拿到java.lang.String这个类的字节码
Class strCls = Class.forName("java.lang.String");
//通过这份字节码创建一个String的对象
String str = (String) strCls.newInstance();
//打印String对象str的长度
System.out.println(str.length());
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
} 上面这段代码演示了使用Class.ForName获得一个String对象,程序的打印结果为0,表示str不是一个空的引用,否则调用它的length方法就会抛出空指针异常,因此成功的拿到了一个String对象 Class类的常用方法有 public Annotation[] getAnnotations() 返回它所表示的类或接口上存在的所有注释的数组 public ClassLoader getClassLoader() 返回该类的类加载器 public Constructor<T> getConstructor(Class<?>... parameterTypes) 参数为构造函数的类型的Class,返回一个表示类的构造方法的 Constructor 对象 public Field getDeclaredField(String name) 返回一个 Field 对象,该对象反映此 Class 对象所表示的类或接口的指定已声明字段。name 参数是一个 String,它指定所需字段的简称 public Method[] getMethods() 返回一个包含某些 Method 对象的数组 public boolean isArray() 判定此 Class 对象是否表示一个数组类 public boolean isInterface() 判定指定的 Class 对象是否表示一个接口类型。 public boolean isPrimitive() 判定指定的 Class 对象是否表示一个基本类型 。。。。。反射技术就是围绕这这些方法进行的。 现在知道了Class类用来是描述Java类的,java类中的,构造方法、包、成员变量、所拥有的方法,Java也都提供了相应的描述对象,分别是Constructor、Package、Field、Method,需要反射字段就先获得这个字段,然后再某个对象上设值,修改值,等等,脑海中有这个过程反射就很容易了解了,给你几个例子 通过反射把某个对象身上的值改掉 - public static void main(String[] args) throws Exception {
- //new出测试的对象
- ReflectPoint p1 = new ReflectPoint(520,909);
- //获得ReflectPoint类的成员变量y
- Field field = ReflectPoint.class.getDeclaredField("x");
- //私有成员默认是不可访问的,要设置为可以访问
- field.setAccessible(true);
- //得到p1对象,字段y的值
- System.out.println("before: " + field.get(p1));
- //把p1对象的x值修改为0
- field.set(p1, 0);
- System.out.println("after: " + field.get(p1));
- }
复制代码 一般的方法调用和反射调用- public static void main(String[] args) throws Exception {
- String str = "黑马程序员训练营";
- Method charAt = Class.forName("java.lang.String").getMethod("charAt", int.class);
- //若要获得str的第二个字符,通常的方式为
- System.out.println(str.charAt(2));
- //通过反射的方式
- System.out.println(charAt.invoke(str, 2));
- }
复制代码 通过这个构造方法获得对象- public static void main(String[] args) throws Exception {
- //获得String类的以一个字符数组,构造出String对象的构造方法
- Constructor c1 = String.class.getConstructor(char[].class);
- char[] chars = {'黑','马','程','序','员','训','练','营'};
- //调用c1的newInstance方法,把chars传递过去,返回的是Object,把它强制转换为String类型
- String str1 = (String)c1.newInstance(chars);
- System.out.println(str1);
- }
复制代码 上面程序的打印结果是:黑马程序员训练营
|