java——反射机制
import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; public class Reflect { /* * 反射 java的反射就是将java类中的每一个成分映射成为一个相应的类。表示java类的Class类需要提供一系列方法来获得其中的变量Field, * 方法Method,构造方法Constructor,修饰符Modifier,包Package等信息。这些信息就是用相应的实例对象来表示。 * 举例来说:System类中有的方法如System.exit,System.getProperties()等,它们都属于Method这个类型 * 而每个方法对应的是methodobj1或者methodobj2等等,即是这个类的一个对象(即Method的对象对应的是一个具体的方法)。 */ /* * 一个类的所有成员都可以使用反射API类的一个实例对象来表示。通过调用Class类的方法获得这些实例对象后,就要对这些对象进行使用--->学习反射的要点 */ /* * Constructor类 Constructor类代表一个类中的一个构造方法 得到一个类中所有的构造方法:Constructor[] * cst=Class * .forName("类名").getConstructors(),而要获得某一个构造方法使用getConstructor()方法。 * 一个类里面会有多个构造方法,可以根据参数的类型和个数获得需要的方法(构造方法是没有顺序的)。例如要得到 String类中String(byte[] * b,int offset, int length, String charSetName){}这个构造方法,就可以这样做: Constructor * ctr1=String.class.getConstructor(byte[].class, int.class, int.class, * String.class);因为 * JDK1.5以上的版本具有可变参数的新特性,所以可以接受多于原始参数个数的参数。但是参数的个数是不一定的,如何指定需要的参数的个数呢 * ?通过可变参数args来指定。 * 得到的ctr1是一个Constructor类的对象,而这种类的对象所具有的的方法可以从Constructor的说明文档中了解 * ,然后根据需要调用。例如getDeclaringClass * (),getModifier(),getParameterTypes(),newInstance(Object... * initargs)等。其中newInstance方法,可以利用这个构造器对象所代表的构造器类型来产生这个构造器申明类的一个实例,并对其 * 进行初始化(使用可变参数实现) */ public static void main(String[] args) throws NoSuchMethodException, SecurityException, InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException { // 普通实现方式:先new一个StringBuffer,作为参数传递给String的这个构造方法。再利用这个构造方法new一个String对象。 String str = new String(new StringBuffer("abc")); /* * 利用反射实现:首先将String的某种构造方法映射为一个构造器类对象,然后利用这个构造器类对象产生String实例对象。该构造器类对象可以多次使用 * 。 */ Constructor<String> ctr1 = String.class.getConstructor(StringBuffer.class); String str1 = (String) ctr1.newInstance(new StringBuffer("abc")); System.out.println(str1.charAt(2)); String.class.newInstance(); // 下面两句执行时报错。但编译时不提示错误。 String str2 = (String) ctr1.newInstance("abc"); System.out.println(str2.charAt(2)); /* * 第一个StringBuffer的引用是为了指定要产生哪种构造器对象, * 第二个StringBuffer是在使用已经产生的构造器实例化对象时传递这样一个参数 * 。注意newInstance方法返回的是Object,所以需要进行强制类型转换。 * * 编译过程和执行过程的区别: 1,编译器只看变量的定义,不看代码的执行。对于Constructor * ctr1=String.class.getConstructor(StringBuffer.class); String * str1=(String)ctr1.newInstance(new * StringBuffer("abc"));之所以要使用强制类型转换就是因为 * 在编译阶段,在第一句中等号右边的部分是不执行的,编译器不知道我们生成的构造器对象时String类型的 * 。在第二句中编译器检查语法时知道newInstance方法返回的是一个Object类型的对象 * ,要赋给String类型的变量就必须要进行强制类型转换。 2,对于String * str2=(String)ctr1.newInstance("abc");编译时并不会报错,但是在执行时就会出错,因为需要 * 传递一个StringBuffer类型的参数,但却传递了一个String。 */ /* 总结一下,利用反射创建对象步骤:a,获得构造方法(需要参数的类型)b,调用构造方法(传递同样类型的对象作为参数) */ /* * Class类的newInstance()方法:通过查阅java中Class的源文件可以知道,在这一方法内部实际是将默认的 * 即不带参数的构造方法加载到缓存中,当需要利用这种构造方法时,直接利用缓存的构造器对象来实例化该类 * 的对象。这样可以避免应用反射的繁琐步骤(class--->constructor--->new object),提高程序性能。 */ } }
import java.lang.reflect.Array; import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.util.Arrays; public class Reflect2 { public static void main(String[] args) throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException, NoSuchMethodException, InvocationTargetException, ClassNotFoundException { // TODO Auto-generated method stub /*------------------------------------------------------字段反射--------------------------------------------------------------------------------*/ ReflectPoint pt1 = new ReflectPoint(3, 5); ReflectPoint pt2 = new ReflectPoint(4, 6); ReflectPoint pt3 = new ReflectPoint(7, 10); Field fieldY = pt1.getClass().getField("y"); /* * 利用反射得到一个字段。此处的fieldY代表的是pt1这个对象的字节码文件中的变量,而不是这个对象中的变量 * 也就是说此处fieldY的值并不是5.也就是说,pt1,pt2,pt3对应的是同一个fieldY字段对象,但这个字段对象 * 的值在三个对象中是不一样的,分别为5,6,10. */ // 要取出fieldY在每个对象中对应的值: System.out.println(fieldY.get(pt1)); // Field // fieldX1=pt1.getclass().getField("x");//报错,getField()只能得到对外部类可见的那些字段。 // x是类私有的成员变量,对外部类是不可见的。 Field fieldX2 = pt1.getClass().getDeclaredField("x");// getDeclaredField()用于得到所有申明的字段。 // System.out.println(fieldX2.get(pt1));//报错,仍然没有权限 // 暴力反射 fieldX2.setAccessible(true); System.out.println("\t -- > \t " + fieldX2.get(pt1)); changeStringValue(pt1); System.out.println(pt1); /*--------------------------------------------------成员方法的反射-------------------------------------------------------------------------*/ // 成员方法的反射 : /* * Method类:代表的就是类中的方法,即字节码中的方法,和对象没有关系,属于类本身,方法的调用必须通过对象来进行 方法的调用方式: * 普通方式:str.charAt(1)------直接通过对象调用 反射方式: */ // 成员方法的反射 String str1 = "ball"; Method methodCharAt = String.class.getMethod("charAt", int.class); // 第二个参数为方法参数的类型 System.out.println(methodCharAt.invoke(str1, 1));// 在str1这个对象上,调用methodCharAt这个方法对象所代表的 // 方法,并给调用过程传递方法的参数1. /* * invoke方法是methodCharAt这个方法对象的方法,但其含义是在某个指定的对象上调用前面调用invoke方法的方法对象所代表的方法 * 。 */ /* * invoke: Parameters: obj - 方法从哪个对象上面调用 args - 调用方法时的参数 Returns: * obj对象利用传递的参数,调用方法之后的结果 关于参数: * 在一个特定的对象上调用这个Method对象所代表的方法。单个参数自动被解析为原始类型 * ,而多个参数时,原始类型和引用类型的阐述会在必要时由方法调用本身来识别和支配。 关于方法类型: * 如果方法是静态的,参数对象可以被忽略。另外当方法是静态的,申明这个方法的类如果没有已经被初始化,那么就会 被初始化。 * 如果方法是实例方法,可以按照在The Java Language Specification中描述的动态方法查找的方式进行调用,尤其 * 是可以通过靶标对象在运行时的类型来对方法进行重写。 关于返回值: * 如果方法使用完成,其返回值会赋给调用invoke的对象,如果返回值是基本数据类型,它首先会被包装在一个相应的对象 * 中。但是如果返回值是引用类型的,如数组,数组中的元素不会被包装在对象中,换言之,就会直接返回一个含有基本数据 * 类型元素的数组。如果返回值是void,那么这个调用过程返回null。 * * e.g System.out.println(methodCharAt.invoke(null, * 1));表示方法是静态的。当需要调用静态方法是,就可将Object写成 null。原理就是静态方法的调用不需要对象。 */ // 专家模式:把变量设为私有的,变量在谁身上,那么利用这些变量的方法就在谁身上。 System.out.println(methodCharAt.invoke(str1, new Object[] { 2 })); /* * 如果不利用上面的可变参数的方式,即JDK1.5以前的版本,无论参数有多少个,是什么类型,都可以参数列表放到一个对象数组中, * 根据需要指定参数的个数和内容,如例子中对象数组中有一个对象,即Integer对象,数值为2。 */ // 例如new Object[]{new String("abc"), 1, new // int[]{}},单个对象元素分别为String,int和int数组。它们都是对象,其中 // '1'在JDK1.5之后可以被自动封装成一个Integer对象。 /*----------------------------------------------------------------------------------------------------------------------------------*/ /*----------------------------------用反射的方式执行某个类中的main方法-----------------------------------------------------------*/ // 用反射的方式执行某个类中的main方法:首先定义一个TestArguments类,见下面。 // 普通调用方式: TestArguments.main(new String[] { "111", "222", "333" }); // 反射方式调用: /* * 为什么使用反射方法调用: * 首先,在写程序时不需要知道要执行的类的名称,那么在执行程序时可以通过将类名作为参数传给程序,就可以调用这个类的main方法。 */ String startingClassName = args[0]; Method mainMethod = Class.forName(startingClassName).getMethod("main", String[].class); mainMethod.invoke(null, new Object[] { new String[] { "````abc", "````123" } }); /* * 如果将new String[]{"abc","123"}直接传递给invoke,这个String数组会被“打开”,而其中的每个元素都会 * 被当成一个对象,作为参数传给mainMethod所对应的方法。而main方法只接受一个String[]类型的数组作为参数 * 如果采用这种方法,相当于将两个参数传给了main方法。所以可以先将这个String[]类型的数组包裹为一个对象 * 当将它传给invoke时,在被解包后,就成了将一个String数组传递给main方法,那样就合理了 */ // 另外一种方法: mainMethod.invoke(null, (Object) new String[] { "----abc", "----123" }); /* 这种方法也可以,相当于将字符数组转成一个对象,将这个对象传给main方法。可以理解为不让程序将数组拆包 */ /*----------------------------------------------------------------------------------------------------------------------------------*/ /*---------------------------------------------------数组的反射------------------------------------------------------------------------------*/ // 数组的反射:Every array belongs to a class that is reflected as a Class // object that is shared // by all arrays with the same element type and number of // dimensions.维数和类型一致的数组 // 对应的字节码是同一份。 int[] a1 = new int[] { 1, 2, 3 }; int[] a2 = new int[4]; int[][] a3 = new int[2][3]; String[] a4 = new String[] { "a", "b", "c" }; // System.out.println(a1.getClass()==a3.getClass());//通不过编译器 // System.out.println(a1.getClass()==a4.getClass());//通不过编译器 System.out.println(a1.getClass() == a2.getClass());// true System.out.println(a1.getClass().getName());// [I [表示数组,I表示int // 查看getName的说明文档 System.out.println(a1.getClass().getSuperclass().getName());// java.lang.Object,即父类是Object System.out.println(a4.getClass().getSuperclass().getName()); System.out.println(a3.getClass().getName());// [[I Object aObj1 = a1; Object aObj2 = a3; Object aObj3 = a4; // Object[] aObj4=a1;//基本类型的一维数组中的每个元素不是Object; Object[] aObj5 = a3;// a3是一个二维数组,即数组的数组。因为每一个数组可以作为一个Object,所以Object数组里面可以是数组。 Object[] aObj6 = a4; // 如何打印出一个数组里的元素? System.out.println(a1);// [I@590e130c---表示这是一个一维数组,后面跟的是其hashcode值 System.out.println(a4);// [Ljava.lang.String;@2b04a681---表示这是一个一维字符串类型数组,后面也是其hashcode值 System.out.println(aObj1); // [I@2b04a681 System.out.println(aObj3); // [Ljava.lang.String;@3e4f7537 System.out.println(aObj6); // [Ljava.lang.String;@3e4f7537 // Arrays这个类中有对数组进行操作的静态方法 /* * asList()方法:public static List (T...a)返回一个list对象.其参数是可变参数,在老版本中是对象数组。 * This method acts as bridge between array-based and collection-based * API, in combination with Collection.toArray(). The returned list id * serializable and implements RandomAccess. */ System.out.println("Arrays.asList\t\t\t\t " + Arrays.asList(a1));// [[I@2b04a681] System.out.println(Arrays.asList(a4));// [a, b, c] /* * 在JDK1.4版本中,会将a1中的每个元素当成一个对象,及传给asList的参数个数即int[]数组中元素个数,然后转化 * 成List。而int数组中每个元素时不可以作为对象的。所以打印出的是上面的结果,在JDK1.5即以后的版本中,会 * 将int[]数组整体作为一个对象传递给asList(可变参数的性质),即作为一个参数, */ /*----------------------------------------------------------------------------------------------------------------------------------*/ Object obj = null; printObject(obj); } /*-------------------------------------使用java.lang.reflect.Array这个类进行数组反射---------------------------------------------*/ // 数组的反射:使用java.lang.reflect.Array这个类进行数组反射。 public static void printObject(Object obj) { // 首先要确定传递进来的对象时一个对象还是一堆对象,如果是一个,直接打印这个对象,如果是一堆,就逐个打印 Class<? extends Object> cls = obj.getClass(); if (cls.isArray()) { int len = Array.getLength(cls); for (int i = 0; i < len; i++) { System.out.println(Array.get(obj, i)); } } else { System.out.println(obj); } } // 如果对象是数组类型,那么如何知道其中元素的类型? // Object[] a=new Object[]{"abcd",12};在此处,如何知道a的某个String类型还是int类型?------不知道! // 只能通过a[0].getClass().getName()来获得具体每个元素的类型,而不能通过a来得知。 // 如果需要自己开发框架,对这些知识一定要清楚。 /*----------------------------------------------------------------------------------------------------------------------------------*/ /*---------------------------------------------------成员变量反射的综合案例----------------------------------------------------------*/ // 成员变量反射的综合案例:一定要理解和掌握,自己可以写出这个程序就掌握了反射。 /* 练习:将任意一个对象中的所有的String类型的成员变量所对应的字符串内容中的“b”改成“a”。 */ public static void changeStringValue(Object obj) throws IllegalArgumentException, IllegalAccessException { Field[] fields = obj.getClass().getFields(); for (Field field : fields) { // if (field.getType().equals(String.class)){} /* * equals() Indicates whether some other object is "equal to" this * one. Parameters: obj - the reference object with which to * compare. Returns: true if this object is the same as the obj * argument; false otherwise. */ /* * 为什么最好不用equals而是用等号来比较: * 如果field为String,那么field.getType()获得的字节码和String.class对应的是同一份字节码,虽然 * 用equals也可以,但语义不准确。因此使用等号比较比较合理。------对字节码的比较应该用等号比 */ /* * The getType() method returns a Class Object that identifies the * declared type for the field represented by this Field object. */ if (field.getType() == String.class) { String oldValue = (String) field.get(obj); String newValue = oldValue.replace('b', 'a'); field.set(obj, newValue); } } } /*----------------------------------------------------------------------------------------------------------------------------------*/ } /*--------------------------------------------用反射的方式执行某个类中的main方法----------------------------------------------*/ // 用反射的方式执行某个类中的main方法。 /* 题目:写一个程序,这个程序可以根据用户提供的类名来调用类中的main方法。 */ // 首先定义一个类 class TestArguments { public static void main(String[] args) { for (String arg : args) { System.out.println(arg); } } }
参考链接:
http://blog.csdn.net/hfsnail86/article/details/9966769 黑马程序员:java中的Class类和反射(一)
http://blog.csdn.net/hfsnail86/article/details/9967047 黑马程序员:java中的Class类和反射(二)
http://blog.csdn.net/ljphhj/article/details/12858767 一个例子让你了解Java反射机制
浙公网安备 33010602011771号