Constructor类
Constructor 提供关于类的单个构造方法的信息以及对它的访问权限。
Constructor 允许在将实参与带有底层构造方法的形参的 newInstance() 匹配时进行扩展转换,但是如果发生收缩转换,则抛出 IllegalArgumentException。
示例代码:
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。
示例代码:
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类代码:
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对象对应的是一个静态方法。
示例代码:
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方法。
示例代码:
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"}); //给的是对象,不是数组,不会拆。编译器会做特殊处理,编译时不把参数当作数组对待,也就不会将数组打散为若干个参数了。