Java反射机制初体验(2)

  Constructor类

  Constructor 提供关于类的单个构造方法的信息以及对它的访问权限。

  Constructor 允许在将实参与带有底层构造方法的形参的 newInstance() 匹配时进行扩展转换,但是如果发生收缩转换,则抛出 IllegalArgumentException。

  示例代码:

  View Codepackage reflect;import java.lang.reflect.Constructor;import java.lang.reflect.Field;import java.lang.reflect.Method;import java.util.Iterator;public class ReflectTest { /** * @author alan * @param args * @throws Exception */ public static void main(String[] args) throws Exception { /* * Contructor类解析:类的构造方法没有顺序 Constructor 提供关于类的单个构造方法的信息以及对它的访问权限。 * Constructor 允许在将实参与带有底层构造方法的形参的 newInstance() 匹配时进行扩展转换, * 但是如果发生收缩转换,则抛出 IllegalArgumentException。 * * 反射比较占用时间,需要缓存。程序性能下降:查看Class源码 * */ System.out.println(“Contructor类解析”); Constructor constructor=String.class.getConstructor(StringBuffer.class); //getConstructor是可变参数,选择构造方法(含一个字符串参数),运行时执行 String string2=(String) constructor.newInstance(new StringBuffer(“string”));//通过参数来选择构造方法创建实例。(与构造方法同样类型的变量) System.out.println(string2);// 编译时无措:二进制代码 运行时出错:Exception in thread “main” java.lang.IllegalArgumentException: argument type mismatch //String string3=(String) constructor.newInstance(“string”);//参数不同,运行时出错 //System.out.println(string3); }}

  Class 的newInstance()方法解析。newInstance ()中调用的newInstance0()方法使用缓存机制来保存默认构造方法的实例。

  jdk源码解析:

  public T newInstance() throws InstantiationException, IllegalAccessException { if (System.getSecurityManager() != null) { checkMemberAccess(Member.PUBLIC, ClassLoader.getCallerClassLoader(), false); } return newInstance0(); }private T newInstance0() throws InstantiationException, IllegalAccessException { // NOTE: the following code may not be strictly correct under // the current Java memory model. // Constructor lookup if (cachedConstructor == null) { //缓存为空 if (this == Class.class) { throw new IllegalAccessException( “Can not call newInstance() on the Class for java.lang.Class” ); } try { Class<?>[] empty = {}; final Constructor<T> c = getConstructor0(empty, Member.DECLARED);//得到无参的构造方法构造实例对象。 // Disable accessibility checks on the constructor // since we have to do the security check here anyway // (the stack depth is wrong for the Constructor’s // security check to work) java.security.AccessController.doPrivileged( new java.security.PrivilegedAction<Void>() { public Void run() { c.setAccessible(true); return null; } }); cachedConstructor = c; //将c缓存起来,下次调用newInstance时,直接读取缓存并返回 } catch (NoSuchMethodException e) { throw new InstantiationException(getName()); } } Constructor<T> tmpConstructor = cachedConstructor; //调用缓存内的实例对象创建tmpConstructor

  Field类

  Field 提供有关类或接口的单个字段的信息,以及对它的动态访问权限。反射的字段可能是一个类(静态)字段或实例字段。

  Array 允许在执行 get 或 set 访问操作期间进行扩展转换,但如果将发生收缩转换,则抛出一个 IllegalArgumentException。

  示例代码:

  View Code package reflect;import java.lang.reflect.Constructor;import java.lang.reflect.Field;import java.lang.reflect.Method;import java.util.Iterator;public class ReflectTest { /** * @author alan * @param args * @throws Exception */ public static void main(String[] args) throws Exception { /* * Field类解析 Field 提供有关类或接口的单个字段的信息,以及对它的动态访问权限。反射的字段可能是一个类(静态)字段或实例字段。 * Array 允许在执行 get 或 set 访问操作期间进行扩展转换,但如果将发生收缩转换,则抛出一个 * IllegalArgumentException。 */ System.out.println(“Field类解析:”); ReflectPoint reflectPoint=new ReflectPoint(1, 2); Field fieldX=reflectPoint.getClass().getField(“x”); //x!=1对应到类字节码的变量,没有对应类对象身上。 System.out.println(fieldX.get(reflectPoint)); //在reflectPoint具体对象中的变量 Field fieldY=reflectPoint.getClass().getDeclaredField(“y”); //y不可见 fieldY.setAccessible(true); //暴力反射 System.out.println(fieldY.get(reflectPoint)); System.out.println(“替换成员变量中的字符实验”); replaceWord(reflectPoint); //替换成员变量中的字符。 System.out.println(reflectPoint); } public static void replaceWord(Object object) throws Exception { Field[] fields = object.getClass().getFields(); for (Field field : fields) { if (field.getType() == String.class) { // 只有一份字节码,所以使用“==”比较,而非equals String oldWord = (String) field.get(object); String newWord = oldWord.replace(“w”, “W”); field.set(object, newWord); } } }}

  辅助ReflectPoint类代码:

  View Code package reflect;public class ReflectPoint { public int x; private int y; public String string=”helloworld”; public ReflectPoint(int x, int y) { super(); this.x = x; this.y = y; } public String toString () { return “helloworld has changed to “+string; }}

  Method类

  Method 提供关于类或接口上单独某个方法(以及如何访问该方法)的信息。所反映的方法可能是类方法或实例方法(包括抽象方法)。

  Method 允许在匹配要调用的实参与底层方法的形参时进行扩展转换;但如果要进行收缩转换,则会抛出 IllegalArgumentException。

  1、得到类中的某个方法,如下示:

  Method chatAt=Class.forName(“java.lang.String”).getMethod(“charAt”,int.class);

  2、调用方法,如下示:

  通用方式:System.out.println(string.charAt(1));

  反射方式:System.out.println(charAt.invoke(string,1));如果传递给Method对象的invoke()方法的一个参数为null时,说明Method对象对应的是一个静态方法。

  示例代码:

  View Code package reflect;import java.lang.reflect.Constructor;import java.lang.reflect.Field;import java.lang.reflect.Method;import java.util.Iterator;public class ReflectTest { /** * @author alan * @param args * @throws Exception */ public static void main(String[] args) throws Exception { /* * Method类解析 */ System.out.println(“Method类解析:”); String string = “string”; System.out.println(“通用方式:”+string.charAt(1)); Class class1 = string.getClass(); Method charAtMethod = String.class.getMethod(“charAt”, int.class); System.out.println(“反射方式:”+charAtMethod.invoke(string, 1));// 指定对象string调用由此 Method对象charAtMethod表示的底层方法charAt(). } }

  3、写一个程序,程序能够根据用户提供的类名,去执行该类中的main方法。

  示例代码:

  View Code package reflect;import java.lang.reflect.Constructor;import java.lang.reflect.Field;import java.lang.reflect.Method;import java.util.Iterator;public class ReflectTest { /** * @author alan * @param args * @throws Exception */ public static void main(String[] args) throws Exception { //写一个程序,程序能够根据用户提供的类名,去执行该类中的main方法 System.out.println(“写一个程序,程序能够根据用户提供的类名,去执行该类中的main方法:”); TestArgument.main(new String[]{“1″,”2″,”3”}); //普通方式:类的静态方法调用 //使用反射方式调用。源程序无法知道类的名称, String startString=args[0]; Method mainMethod=Class.forName(startString).getMethod(“main”, String[].class); //mainMethod.invoke(null, new String[]{“1″,”2″,”3”}); //出现错误Exception in thread “main” java.lang.IllegalArgumentException: wrong number of arguments mainMethod.invoke(null, new Object[]{ new String[]{“1″,”2″,”3”}}); //invoke第一个参数为null表示为静态方法调用。 //mainMethod.invoke(null, (Object)new String[]{“1″,”2″,”3”}); }}class TestArgument{ public static void main(String[] args) { for (String string : args) { System.out.println(string); } } }

  问题:Exception in thread “main” java.lang.IllegalArgumentException: wrong number of arguments

  原因:jdk1.4语法中,数组中每个元素对应一个参数。为了兼容jdk1.4,javac会按jdk1.4语法处理。即把数组打散成为若干个单独的参数。

  解决方法:

  1、mainMethod.invoke(null, new Object[]{ new String[]{“1″,”2″,”3”}}); //再包装一层,拆后为实际所需的参数。此方法效率较低。

  2、mainMethod.invoke(null, (Object)new String[]{“1″,”2″,”3”}); //给的是对象,不是数组,不会拆。编译器会做特殊处理,编译时不把参数当作数组对待,也就不会将数组打散为若干个参数了。

  相关链接:

  Java反射机制初体验(1)

使你疲倦的不是前面的高山,而是你鞋里的一粒沙子。

Java反射机制初体验(2)

相关文章:

你感兴趣的文章:

标签云: