反射 reflection 非常有用!!各种框架实现的技术基础!就是将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);
}}}
|