java反射机制详解

来源:互联网 发布:2017双十一数据 编辑:程序博客网 时间:2024/05/18 11:10

Java反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意方法和属性;这种动态获取信息以及动态调用对象方法的功能称为java语言的反射机制。
在java中尤其是J2ee中经常会使用到反射,究其原因是反射机制有很多好处,动态的反射来创建对象和调用方法可以帮助我们减少每个类之间的耦合度,也就是解耦合。

不使用反射机制:直接在代码中new出自己需要的类,这种静态编译的方法会在编译的时候绑定对象,虽然这样运行起来比反射要快,但是如果一旦需要在项目中修改某个类,那么多则几百处代码都要修改一遍,而且这个类的方法和属性也有可能要涉及修改,麻烦不断,这绝对不是我们想要看到的。

使用反射机制:相比较静态编译反射机制是动态编译,在运行的时候确定类并且实例化、调用方法。反射发挥了java编程的灵活性,如果此时类需要修改,那么只需要修改最开始反射类的那一句话即可,或者如果你喜欢,甚至可以把有关反射的内容单独作为一个配置文件用xml写出来,如果需要修改,直接在配置文件中修改反射的类名即可。

反射是很复杂的一门学问,java中反射方法集中在java.lang.Class这个类和java.lang.reflect包中。java.lang.Class<T>是反射的基础,而且这个类中包括了很多我们所需的反射方法。其中参数T是由Class对象建模的类型,比如,如果是String.class那么这个T就应该是String,如果并不能确定被建模的类,那么就要Class<?>。 class类也是一个泛型类。

泛型类介绍传送门:http://blog.csdn.net/quinnnorris/article/details/54808172

(一) 获取类名

Date d = new Date(); Class<?> c1 = d.getClass();//获取运行时类名。是Object的方法,不是Class的方法Class<?> c2=int.class;Class<?> c3=Class.forName("java.util.Date");//返回与给定字符串相关联的Class对象名System.out.println(c1.getName());System.out.println(c2.getName());System.out.println(c3.getName());

结果:
java.util.Date
int
java.util.Date

(二) 获取所有父类和接口

getSuperclass() 返回表示此 Class 所表示的实体(类、接口、基本类型或 void)的超类的 Class。如果此 Class 表示 Object 类、一个接口、一个基本类型或 void,则返回 null。

String t1= ClassName.class.getSuperclass().getName();//获取父类名Type t2 = ClassName.class.getGenericSuperclass();//获取父类名,如果父类是泛型类,获取其类型参数Class<?> t3[] = ClassName.class.getInterfaces();//获取所有接口,存入数组System.out.println(t1);System.out.println(t2);if (ParameterizedType.class.isAssignableFrom(t2.getClass())) {    for (Type t : ((ParameterizedType) t).getActualTypeArguments())        System.out.print(t + " ");}for(Class<?> ts : t3)System.out.println(ts.getName());

代码中if语句使用来输出父类泛型的参数的,用空格隔开。if语句的判断条件中:ParameterizedType是Type的一个子类,代表泛型类型。isAssignableFrom是Class类的方法,判断两边的类是否相同。那么这句判断语句的意思是:获取t2的类,判断是不是泛型类。如果是则((ParameterizedType) t).getActualTypeArguments()将Type类型的t转换成它的一个具体子类,也就是泛型类型的子类ParameterizedType,getActualTypeArguments()这个方法的作用是获取所有泛型参数的类名。

(三) 创建对象

创建此 Class 对象所表示的类的一个新实例。如同用一个带有一个空参数列表的 new 表达式实例化该类。

ClassName cn = ClassName.class.newInstance(); 

(四) 获取构造器

获取所有的构造器,将其存入一个Constructor<?>数组中。

Constructor<?>[] cons = ClassName.class.getDeclaredConstructors();

由给出的参数和参数顺序获取特定构造器,将其存入一个Constructor<?>中。

Constructor<?> con = Class.forName(ClassName.class.getSuperclass().getName()).getDeclaredConstructor(int.class,String.class,Date.class);//获取ClassName类的父类的特定构造器方法

Constructor类

getDeclaringClass()方法:获取这个Constructor对象中构造器表示的那个类,即构造器所属的原来的类。

Class<?> thisClass = con.getDeclaringClass();//thisClass是构造器所属的类名

getParameterTypes()方法:获取这个Constructor对象中的所有形参类型的class对象,存入Class<?>[]数组中。

Class<?>[] paras = con.getParameterTypes();for(Class<?> pa : paras)    System.out.println(pa.getSimpleName());//输出所有形参类型

isSynthetic()方法:测试这个构造器表示的类是不是合成类,一般情况下都不是。如果结果是true,那么说明这个类是在java虚拟机内部的某个类的构造器,我们也不用去分析了,只有极复杂的情况下才会为true。

System.out.println(con.isSynthetic());//一般情况下为false

如果一定想要去研究一下合成方法和合成类,这里有一篇关于内部类的文章介绍,里面涉及了很多合成的知识。

合成类传送门:http://blog.csdn.net/quinnnorris/article/details/54864491

toGenericString()方法:和toString方法差不多,都是返回完整的构造器字符串,但是如果有类型参数,他也会包含在内输出。

System.out.println(con.toGenericString());//输出: public Reflect.ClassName(int,java.lang.String,java.util.Date)

(五) 获取属性

获取所有的类的属性,将其存入一个Field[]数组中。

Field[] fies =  ClassName.class.getDeclaredFields();

由给出的参数和参数顺序获取特定构造器,将其存入一个Constructor<?>中。

Field  fie = ClassName.class.getDeclaredField("a");

Field类

setAccessible()方法:setAccessible方法并不是Field类中的方法,但是却极为常用,作用是修改安全检查,使得我们可以访问Field对象中的属性,即使他是private来修饰的。

fie.setAccessible(true);//fie中的属性即使为private,我们现在也可访问

get()和set()方法:get方法可以获取Field对象中的属性的值,参数为那个被获取的属性所属的类的一个实例。而set方法可以设置Field对象的属性的值,第一个参数为那个属性所属的那个类的一个实例,第二个参数为要修改为的值。

Field  fie = ClassName.class.getDeclaredField("a");//fie中的属性为ClassName类中名为a的属性System.out.println(fie.get(ClassName.class.newInstance()));//获取了fie中属性的默认值ClassName<String, Date> ca=null;//声明一个ClassName类对象cafie.set(ca=ClassA.class.newInstance(), 10);//设置ca中a的值为10System.out.println(fie.get(ca));//输出a的值,显然输出应该是10

getModifiers()和Modifier.toString()方法:Field类中有一个getModifiers方法以整数的形式返回Field对象中属性的修饰符,而Modifier类中的toString方法则可以将整数解码,返回字符串形式的修饰符。

System.out.println(Modifier.toString(fie.getModifiers()));

以下的方法和Constructor类中相同。
isSynthetic()方法
toGenericString()方法
getDeclaringClass()方法

(六) 获取方法

获取所有的方法,将其存入一个Method[]数组中。

Method[] mths = ClassName.class.getDeclaredMethods();

由给出的参数和参数顺序获取特定的方法,将其存入一个Method对象中。

Method mth =ClassName.class.getDeclaredMethod("setObject",Object.class);//如果参数是泛型参数则用Object.class,原因是虚拟机擦除System.out.println(mth.toGenericString());//输出://public void Reflect.ClassName.setObject(T)

Method类

getReturnType()方法:获取Method对象中方法的正式返回类型。

System.out.println(mth.getReturnType()); //输出: void

isBridge() 方法:如果这个方法是桥方法,则返回true。

System.out.println(mth.isBridge());//如果是桥方法返回true,不是桥方法,返回false

invoke(Object obj,Object…args)方法:第一个参数是从中调用方法的对象,后面的参数是用于方法调用的参数(可为数组)。如果底层方法是静态的,那么可以忽略指定的 obj 参数。该参数可以为 null。 如果底层方法所需的形参数为 0,则所提供的 args 数组长度可以为 0 或 null。 如果底层方法是静态的,并且尚未初始化声明此方法的类,则会将其初始化。 如果方法正常完成,则将该方法返回的值返回给调用者;如果该值为基本类型,则首先适当地将其包装在对象中。但是,如果该值的类型为一组基本类型,则数组元素不 被包装在对象中;换句话说,将返回基本类型的数组。如果底层方法返回类型为 void,则该调用返回 null。

Method mth =ClassName.class.getDeclaredMethod("setObject",Object.class);mth.invoke(ClassName.class.newInstance(),"a");//调用并执行了ClassName中的setObject方法Method mth2 =ClassName.class.getDeclaredMethod("getObject");System.out.println(mth2.invoke(ClassName.class.newInstance()));//调用了无参数的getObject方法,并且将返回值显示出来

以下的方法和Constructor类中相同。
getParameterTypes()方法
isSynthetic()方法
toGenericString()方法
getDeclaringClass()方法

(七) 总结

反射中的最基本的方法和操作都说了一遍,包括基本的Class类和Constructor、Field、Method这几个类,还要多多在实际操作中思考,不断地学习一些优雅简洁的写法、处理方式,来不断提高自己的水平。

1 0
原创粉丝点击