反射:就是将java类中的各成分映射成相应的java类:Field, Constructor ,Method ,Array。 运行期间去操作类!每个类的Class对象在运行期来获取类的内部信息:成员变量,方法甚至private的成员,和其父类,接口,构造任意类的对象,调用该对象方法 反射应用:将给定的对象改变其属性值,或改变特定配置文件的信息 java.lang.reflect包下:5个类:1Class<?> 2Field 3 Constructor 4 Method 5Array jdk1.5以上的版本都支持可变参数,本应该接受数组的参数,可以用可变参数去传! 1Class类:所有的java类或接口也是属于同一类事物,也应当属于一个类,这个类用Class类描述。只是描述一个类的结构:属性,方法,不管其值。 一个类或接口不论生成多少对象,都只对应内存中一个Class对象! 当首次用到一个类名或接口名时,先将这个类或借口的java二进制字节码从硬盘加载到内存,class loader的defineClass()方法被JVM调用时,JVM会产生该类的Class对象就是代表二进制码。然后才能去创建对象。当用到很多类时,会有很多字节码,每个字节码就是一个Class对象,就是1Student.class代表Student的字节码,2Student类的对象s也可以获得其类的字节码:s.getClass();3Class.forName(“类全名”)。都是得到类的字节码。 内存中只保留一份类的字节码: .class文件不是字节码,而是但类加载器把它加载到内存后才会变成字节码!!! 1如果类已经被加载,直接从内存缓存中读 2类从未被加载,加载类的字节码,并放入缓存!! 1获取类或借口的Class对象三种方式: 接口也有Class对象,获取方式:接口名.class 1利用Class类的静态方法forName(“类的全名”)获得类的Class对象 Class<?> class=Class<?>.forName(“java.lang.String”); 返回了String类的Class对象 2 Class<?>class=Test.class; 利用类名去获得 3 Class<?>class=对象.getClass();利用对象去获得,Object的getClass方法,返回对象真正的Class对象!(不用先强制转化) 九个预定义Class对象 8个基本数据类型+void类型 都有Class对象 Class c=void.class; Class c=boolean.class; Class c=int.class; Class c=char.class; 包装类用TYPE属性获取: Integer.TYPE 获取Integer的Class对象 数组类型:相同类型,相同维度的数组的Class对象相同 int[].class 获得一个对象的类所实现的接口的Class数组 o.getClass().getInterfaces(); 2Class类定义了方法用来判断字节码是什么类型: s.isPrimitive();是否为基本数据类型 s.isArray();是否为数组类型 3Class对象有getName方法,获得类的全名!如果是数组的Class对象的化,getName打印出:【X [加个字母,表示不同类型的数组,查文档getName 4获取类的构造方法,动态获取类的实例: 1//获取类所有构造方法 Constaructor[]c=String.class.getConstructors(); 2////获取类特定构造方法,参数是StringBuffer类型,1.5之前是getConstructor参数需要传递Class数组,1.5之后是可变参数,传几个都行,不用数组了!! Constaructor c=String.class.getConstructor (StringBuffer.class); 3//调用类的无参构造方法: 1.5之前 Constructor constructor=class.getConstructor(new Class[]{}); 1.5之后 Constructor constructor=class.getConstructor(); 4//利用Constructor对象的newInstance()方法,创建对象,方法传递的参数一定要Constructor对象匹配! 1//快速创建无参构造方法对象: //利用Class对象的newInstance方法,调用无参构造方法 Object o=class.newInstance(); 2//创建无参构造方法对象 1.5之前 用Object数组包装调用构造方法时传递的参数。 Object o=constructor.newInstance(new Object[]{}); 1.5之后 Object o=constructor.newInstance(); 3//生成带参数的构造方法对象。 1.5之前 Object o=constructor.newInstance(new Object[]{“efew”,16});传递真实参数 1.5之后 Object o=constructor.newInstance( “efew”,16);传递真实参数 4获取构造方法的参数类型: Class[]classes= constructor.getParameterTypes(); for(Class c:classes){ //参数类型的类名 c.getName(); } 5由Class对象获取类的方法: 1//getMethod只返回public方法 //第一参数方法名 第二是方法参数 Method m=String.class.getMethod(“charAt”,int.class); 方法参数可以是Class数组,或可变参数 2//获取所有方法,包括私有! Method[]methods=class.getDeclaredMethod(); 返回String类的方法数组,包括private for(Method m:methods){print{m};} 遍历方法 3调用特定对象的方法 有自定义类 class Test{ 有两个方法: int add(int p1,int p2){return p1+p2;} String static void echo(String m){return m;}} 调用某个特定对象的方法,利用反射调用某个对象的方法,虽然上面获得了Test对象,但一般不用对象直接调用方法,而是用反射。 1获得方法对象 Method m= class.getMthod(“add”, int.class,int.class); Method m2= class.getMthod(“echo”, String.class); 2利用Method对象的invoke(对象,Object数组参数或可变参数)方法来调用对象的方法,invoke方法返回Object类型,需要强制转化: Integer i =(Integer)m.invoke(o, 4,5); 第一个参数是调用哪个对象的方法,第二个参数是向对象方法传递的参数,result是返回值。其真正类型是add方法的返回值,这里是Integer,需要强制转换! 4调用类的静态方法:调用类的静态echo方法 Method m2= class.getMthod(“echo”, String.class); Integer i =(Integer)m.invoke(null, 4,5); 5调用一个类的静态main方法 invoke(Object o, Object...args) 如果传递一个Object数组,会将其拆开,除非只传一个Object对象。 main没有实现可变参数,只能接受一个String[]args数组参数 如果直接invoke(Object o, new String[]{“as”,”d”}),会将new String[]{“as”,”d”}当成一个Object数组,然后将它拆开成多个Object参数再传递给main方法,会出错。 应当:invoke(Object o, new Object[]{new String[]{“as”,”d”}}) 将String数组作为Object数组的第一个参数,拆开后就只有一个String数组参数!! 或者(Object)new String[]{“as”,”d”} 作为一个Object对象 class Student{ int age; String name; public static void main(String[] arg){print(age+name+arg[0]);} } class Test{ main(String[] arg){ //第一个参数传入类全名 Class class=Class.forName(arg[0]); //获取该类main方法 Method mainMethod=class.getMethod(main,String[].class); //调用main方法 mainMethod.invoke(null, new Object[]{new String[]{“as”,”d”}}); }} 6 获得方法的参数类型 Class[]classes= method.getParameterTypes(); for(Class c:classes){ //参数类型的类名 c.getName(); } 6利用反射获取对象的属性 class Student{ int age; private String name; Student(int age){ this.age=age;} } Student s=new Student(); s.setAge(10); s.setName(“sss“); 1//获取Class对象一个属性,指定属性名 Field f= Student.class.getField(“age”); getField只能获得public属性 //获取Class对象一个私有属性 Field f2= Student.class.getDeclaredField(“name”); 2//f是类上的属性,不存在值!可以获取:该属性在某一对象上的值,返回Object类型,需要强制转化!! inti=(Integer)f.get(s); 获取age属性在s对象上的值 //f2是私有属性不能直接用get取,要先f2.setAccessible(true); f2.setAccessible(true); String s=(String)f2.get(s); 3//返回Class对象的所有属性数组 利用反射获取对象的属性,并调用该属性的set方法,利用Field的getType方法返回属性的Class对象: //获取所有属性包括私有,getFields()只获取公共属性!! Field[] fields =class.getDeclaredFields(); for(Field f:fields){String name=f.getName(); 获取属性名 //获得该属性的SetXX方法名 String firstLetter=name.subString(0,1).toUpperCase(); 返回属性名大写的首字母。 String setMethodName=“set”+firstLetter+name.subString(1); 获得属性的set方法名 //动态调用SetXX方法 // Field对象的getType方法返回该field属性的Class对象: Method method=class.getMethod(setMethodName,new Class[]{field.getType()}) method.invoke(o,new Object[]{field.getType().newInstance()}) } 4设定属性在某对象的值: f.set(s,13); 设置s对象的值 5将一个对象的所有String属性的值中的”b”改为“a“ class Student{ int age; private String name=”bsds”; } //封装到方法 change(Obj o){ Field[] fields =o.getClass().getField(); for(Field f:fields){ //不用equals,因为一个类的字节码只有一份,比较类的字节码都用== !!!! if(f.getType()==String.class){ String s=(String)f.get(o); 替换 f.set(o,s); }}} |
欢迎光临 黑马程序员技术交流社区 (http://bbs.itheima.com/) | 黑马程序员IT技术论坛 X3.2 |