java反射机制初体验2

Constructor

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

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

示例代码:

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

 

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
 1 package reflect;
 2 
 3 import java.lang.reflect.Constructor;
 4 import java.lang.reflect.Field;
 5 import java.lang.reflect.Method;
 6 import java.util.Iterator;
 7 
 8 public class ReflectTest {
 9 
10     /**
11      * @author alan
12      * @param args
13      * @throws Exception
14      */
15     public static void main(String[] args) throws Exception {
16 
17 
18         /*
19          * Field类解析 Field 提供有关类或接口的单个字段的信息,以及对它的动态访问权限。反射的字段可能是一个类(静态)字段或实例字段。
20          * Array 允许在执行 get 或 set 访问操作期间进行扩展转换,但如果将发生收缩转换,则抛出一个
21          * IllegalArgumentException。
22          */ 
23           System.out.println("Field类解析:"); 
24           ReflectPoint reflectPoint=new ReflectPoint(1, 2);
25           
26           Field fieldX=reflectPoint.getClass().getField("x"); //x!=1对应到类字节码的变量,没有对应类对象身上。
27           System.out.println(fieldX.get(reflectPoint)); //在reflectPoint具体对象中的变量
28           
29           Field fieldY=reflectPoint.getClass().getDeclaredField("y"); //y不可见
30           fieldY.setAccessible(true); //暴力反射
31           System.out.println(fieldY.get(reflectPoint));
32           
33           System.out.println("替换成员变量中的字符实验");
34           replaceWord(reflectPoint); //替换成员变量中的字符。
35           System.out.println(reflectPoint);
36 
37         
38     }
39     public static void replaceWord(Object object) throws Exception {
40         Field[] fields = object.getClass().getFields();
41         for (Field field : fields) {
42             if (field.getType() == String.class) { // 只有一份字节码,所以使用“==”比较,而非equals
43                 String oldWord = (String) field.get(object);
44                 String newWord = oldWord.replace("w", "W");
45                 field.set(object, newWord);
46             }
47         }
48     }
49 }
50 
51     

 辅助ReflectPoint类代码:

View Code
 1 package reflect;
 2 
 3 public class ReflectPoint {
 4      public int x;
 5      private  int y;
 6      public String string="helloworld";
 7     
 8     public ReflectPoint(int x, int y) {
 9         super();
10         this.x = x;
11         this.y = y;
12     }
13     
14     public String  toString () {
15         return "helloworld has changed to "+string;
16     }
17 }

 

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
 1 package reflect;
 2 
 3 import java.lang.reflect.Constructor;
 4 import java.lang.reflect.Field;
 5 import java.lang.reflect.Method;
 6 import java.util.Iterator;
 7 
 8 public class ReflectTest {
 9 
10     /**
11      * @author alan
12      * @param args
13      * @throws Exception
14      */
15     public static void main(String[] args) throws Exception {
16         /*
17          * Method类解析
18          */
19         System.out.println("Method类解析:"); 
20         String string = "string";
21         System.out.println("通用方式:"+string.charAt(1));
22         Class class1 = string.getClass();
23         Method charAtMethod = String.class.getMethod("charAt", int.class);
24         System.out.println("反射方式:"+charAtMethod.invoke(string, 1));// 指定对象string调用由此 Method对象charAtMethod表示的底层方法charAt().        
25     }    
26 }

 

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

示例代码:

View Code
 1 package reflect;
 2 
 3 import java.lang.reflect.Constructor;
 4 import java.lang.reflect.Field;
 5 import java.lang.reflect.Method;
 6 import java.util.Iterator;
 7 
 8 public class ReflectTest {
 9 
10     /**
11      * @author alan
12      * @param args
13      * @throws Exception
14      */
15     public static void main(String[] args) throws Exception {
16 
17     //写一个程序,程序能够根据用户提供的类名,去执行该类中的main方法
18     System.out.println("写一个程序,程序能够根据用户提供的类名,去执行该类中的main方法:");
19     TestArgument.main(new String[]{"1","2","3"});        //普通方式:类的静态方法调用
20     //使用反射方式调用。源程序无法知道类的名称,
21     String startString=args[0];
22     Method mainMethod=Class.forName(startString).getMethod("main", String[].class);
23     //mainMethod.invoke(null, new String[]{"1","2","3"});    //出现错误Exception in thread "main" java.lang.IllegalArgumentException: wrong number of arguments
24     mainMethod.invoke(null, new Object[]{ new String[]{"1","2","3"}});    //invoke第一个参数为null表示为静态方法调用。
25     //mainMethod.invoke(null, (Object)new String[]{"1","2","3"});    
26     }
27 }
28 
29 class TestArgument{
30     public static void  main(String[] args) {
31         for (String string : args) {
32             System.out.println(string);
33         }
34         
35     }
36     
37 }

问题: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反射机制)