黑马程序员--自学笔记--反射

来源:互联网 发布:守望先锋性能数据vrm 编辑:程序博客网 时间:2024/06/05 06:44

反射

-------- android培训java培训、期待与您交流! ---------

一.概述

    Java反射机制是在运行状态中,对于任意一个类(class文件),都能够查询并操作这个类中的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制。
    总而言之,反射就是把Java类中的各种成分映射成相应的Java类。比如,一个Java类中用一个Class类的对象来表示,一个类中的组成部分可分为:成员变量,方法,构造方法,包等。这些成分都可以用一个个的Java类来表示。表示java类的Class类显然要提供一系列的方法,来获取其中的变量,方法,构造方法,修饰符,包等信息,这些信息用相应的类的实例对象来表示,它们分别是Field,Method,Contructor,Package等等。

二.Class类字节码文件

    用于描述字节码的类就是Class类。只要创建Class类对象,就可以提取出字节码文件中的各种内容包括:字段、构造函数、一般函数。反射就是依靠Class类来完成的。如果要想对一个类文件中的内容进行操作,就要先获取到该类的字节码文件对象。
    获取Class对象的三种方式:(代码演示)
① 第一种方式:
<strong><span style="font-family:KaiTi_GB2312;"><strong><span style="font-family:KaiTi_GB2312;"><span style="font-family:KaiTi_GB2312;"><strong><strong><span style="font-size:14px;">/* * 1.使用object类的getClass()方法; * (此种方式必须明确具体的类,并创建对象) */private static void getClassObject_1() {Person p1 = new Person() ;Class clazz = p1.getClass() ;System.out.println(clazz);}</span></strong></strong></span></span></strong></span></strong>
② 第二种方式:
<strong><span style="font-family:KaiTi_GB2312;"><strong><span style="font-family:KaiTi_GB2312;"><span style="font-family:KaiTi_GB2312;"><strong>/* * 2.通过利用任何数据所具备的一个静态属性.class来获取其对应的class对象 * (此种方式不够扩展,还是需要明确用到类中的静态成员) */private static void getClassObject_2() {Class clazz = Person.class ;System.out.println(clazz);}</strong></span></span></strong></span></strong>
③ 第三种方式:
<strong><span style="font-family:KaiTi_GB2312;"><strong><span style="font-family:KaiTi_GB2312;"><span style="font-family:KaiTi_GB2312;"><strong>/* * 3.使用Class类中的forname(String str)方法,通过传入类的名字来获取其对应的class对象 * (传入类名时,需将类的整个包名传入。该方法更为扩展) * */private static void getClassObject_3() throws ClassNotFoundException {String className = "cn.itzixue.bean.Person" ;Class clazz = Class.forName(className) ;    // 需要完整的类名System.out.println(clazz);}</strong></span></span></strong></span></strong>
    相比三种获取方式,第一种方式最不灵活,因为这种方式每次都需要具体的类和该类的对象,以及调用getClass方法;第二种方式虽然比第一种较为简单,不用创建对象,也不用调用getClass方法,但是还是要使用具体的类,和该类中的一个静态属性class完成。而第三种方式最简单,只要知道类的名称即可。这种方式不需要使用该类,也不需要去调用具体的属性和行为就可以获取到Class对象。因为第三种方式仅知道类名就可以获取到该类字节码对象的方式,所以更有利于扩展,开发中最常使用。
    编译时刻加载类是静态加载类,运行时刻加载类是动态加载类。其中,使用new创建对象是静态加载类,在编译时可就需要加载所有的可能使用到的类。而动态加载类是在运行时刻进行加载,反射利用最多的就是动态加载类。

三.获取Class中的构造函数

    获取构造函数一般使用的是Class类对象的getConstructor()方法来获取(该方法返回一个Constructor类对象)。如果构造方法是需要传入参数的,则在创建对象时要使用Constructor类对象的newInstance()方法;若是空参构造函数,则可以使用Class类中的newInstance()方法。
代码演示:
① 调用类的空参构造方法创建一个新的对象(传统方法):(注意:注释中包含重要知识点和注意点)
<strong><span style="font-family:KaiTi_GB2312;"><strong><span style="font-family:KaiTi_GB2312;"><span style="font-family:KaiTi_GB2312;"><strong>/* 通过new来实例化对象,在new的时候,先根据被new的类的名称找寻该类的字节码文件,并加载进内存,接着创建该字节码文件对象,并接着创建该字节码文件对应的Person对象。*/public static void creatNewObject_1() {Person p1 = new Person() ;}</strong></span></span></strong></span></strong>
② 调用类的空参构造方法创建一个新的对象(利用反射机制): (注意:注释中包含重要知识点和注意点)
<strong><span style="font-family:KaiTi_GB2312;"><strong><span style="font-family:KaiTi_GB2312;"><span style="font-family:KaiTi_GB2312;"><strong>/* 先利用Class类的fotName(String str)方法找寻所传入名称的类文件吗,并加载进内存,接着产生Class对象。通过利用Class对象的newInstance()方法来创建所传入类的对象。(该方法扩展性强)*/public static void creatNewObject_2() throws ClassNotFoundException, InstantiationException, IllegalAccessException {String className = "cn.itzixue.bean.Person" ;Class clazz = Class.forName(className) ;Object obj = clazz.newInstance() ;}</strong></span></span></strong></span></strong>
③调用类的带参构造方法创建一个新的对象(利用反射机制): (注意:注释中包含重要知识点和注意点)
<strong><span style="font-family:KaiTi_GB2312;"><strong><span style="font-family:KaiTi_GB2312;"><span style="font-family:KaiTi_GB2312;"><strong>/*  先通过Class类对象的getConstructor(Class<?>... parameterType)方法来获取制定大的公共构造方法。(该方法返回一个Constructor对象,若要获取包括私有在内的指定构造方法,使用getDeclaredConstructor(Class<?>... parameterType)方法)。再通过调用所获得的对象的newInstance()方法进行对象初始化。*/public static void creatNewObject_3() throws Exception {String className = "cn.itzixue.bean.Person" ;Class clazz = Class.forName(className) ;//获取指定的构造函数对象Constructor constructor = clazz.getConstructor(String.class,int.class) ;//通过该构造器对象初始化对象(结果返回的是Object对象)Object obj = constructor.newInstance("权权",21) ;}</strong></span></span></strong></span></strong>

    通过Class类对象的getConstructors()方法可以获取Class字节码文件中的所有公有的(包括父类)构造函数:getDeclaredConstructors()方法可以获取Class字节码文件中的所有(仅本类但包括私有的)构造函数。

四.获取Class中的字段

    获取class字节码文件中的字段一般使用的是Class类对象的getField()方法来获取(该方法返回一个Field类对象)。
Class类对象中操作字段的相关方法代码演示:
    ① (返回值类型:Field) getField(String str) ;    // 此方法只能获取本类或者父类中的公有的字段
    ② (返回值类型:Field) getDeclaredField(String str) ;    // 此方法只能获取该类中的字段,但是包括私有
Field类中
    ① (返回值类型:void) setAccessible(true) ;    // 通过此方法,可以让该私有字段进行取消权限检查的能力,以便于更轻松的访问(也称之为暴力访问)
    ② (返回值类型:void) set(Object obj, Object value) ;    // 将指定对象变量上此Field对象所表示的字段设置为指定的新值
    ③ (返回值类型:void) get(Object obj) ;    // 返回指定对象上Field表示的字段的值
代码演示:(注意:注重中包含重要知识点和注意点)
<strong><span style="font-family:KaiTi_GB2312;">public static void getFieldDemo() throws Exception {Class clazz = Class.forName("cn.itzixue.bean.Person") ;//通过Class类的对象的getField("字段名称")方法获取制定类中共有的字段(只能获取共有,可以获取父类)//通过Class类的对象的getDeclaredField("字段名称")方法获取制定类中共有的字段(只能获取本类,但是可以包含私有)Field field = clazz.getDeclaredField("age") ;/*通过获得的字段处理其对应的值例如:获取字段的值,可用字段对象的get(?)方法,所传入的?必须是该字段所在类的对象(如果在类中该字段被定义成私有的,则用该方法会抛出无效访问异常) 对私有字段的访问,可以通过其getAccessible(true)方法对其进行暴力访问(不建议!!!)*/Constructor constructor = clazz.getConstructor(String.class,int.class) ;Object obj = constructor.newInstance("权权",21) ;field.setAccessible(true) ;Object o = field.get(obj) ;System.out.println(o) ;//设置所获得的字段的值field.set(obj, 22);o = field.get(obj) ;System.out.println(o);<span style="font-size:14px;">}</span></span></strong>

    通过Class类对象的getField()方法可以获取Class字节码文件中的所有公有的(包括父类)字段;getDeclaredField()方法可以获取Class字节码文件中的所有(仅本类但包括私有的)字段。

五.获取Class中的方法

    获取class字节码文件中的方法一般可以通过调用getMethod(String name,Class<?>... parameterTypes)方法,该方法将方法名和其参数同时传入。(如果为空参数,则传入null即可。该方法返回一个Method类对象)。
获取class字节码文件中的方法代码演示:
    ① (返回值类型:Method[]) methods = Class.getMethods() ;    // 获取类(包括父类)中所有的公有方法
    ② (返回值类型:Method[]) methods= Class.getDeclaredMethods() ;    // 获取类中所有方法(包括私有但不包括父类)
    ③ (返回值类型:Method) method = Class.getMethod(String name, 参数类型.Class,…参数类型.Class) ;
    // 返回一个带参数的Method对象,它反映此Class对象所表示的类或接口的指定公共方法
对获取到的Method对象进行调用代码演示:
    (返回值类型:Object) invoke(Object obj,参数) ;    
    // 由obj对象进行方法调用,如果该方法是静态的,则invoke方法中的对象参数可以置为null
代码演示: (注意:注重中包含重要知识点和注意点)   
① 获取无参方法
<strong><span style="font-family:KaiTi_GB2312;"><span style="font-size: 14px;">public static void getMedthodDemo_1() throws Exception {Class clazz = Class.forName("cn.itzixue.bean.Person") ;//可以通过Class对象的getMethod()方法来获取类中所有公共方法(包括父类)。Method[] method_1 = clazz.getMethods() ;//该方法将返回一个Method数组,可通过foreach遍历for(Method m : method_1){System.out.println(m);}//可以通过Class对象的getDeclaredMethod()方法来获取类中包括私有在内的所有方法(不包括父类)。Method[] method_2 = clazz.getDeclaredMethods() ;//该方法将返回一个Method数组,可通过foreach遍历for(Method m : method_2){System.out.println(m);}</span><span style="font-size:14px;">}</span></span></strong>
② 获取有参方法并对其进行调用
<strong><span style="font-family:KaiTi_GB2312;"><span style="font-size:14px;">public static void getMedthodDemo_2() throws Exception {</span>Class clazz = Class.forName("cn.itzixue.bean.Person") ;/*获取类中指定的方法,可以通过调用getMethod(String name,Class<?>... parameterTypes)方法,该方法将方法名和其参数同时传入。(如果为空参数,则传入null即可)*/Method method_1 = clazz.getMethod("show", null) ;Method method_2 = clazz.getMethod("show", String.class) ;System.out.println(method_1);System.out.println(method_2);/*若想调用所取到的Method对象,可以使用Method对象的invoke(Object obj,Object... args)方法,该方法需同时传入该类的对象以及相应的参数。*/Constructor constructor = clazz.getConstructor(String.class,int.class) ;Object obj = constructor.newInstance("权权",24) ; method_1.invoke(obj, null) ;method_2.invoke(obj, "No1") ;}</span></strong>

六.总结

    反射机制的灵活性很大,使用反射机制,我们可以极大的提高程序的扩展性。同时,反射机制使用的是动态加载,动态编译的优点是最大限度发挥了java的灵活性,又体现了多态的应用,也可以很大程度上降低类之间的藕合。这种机制,在框架的编写上最常使用到。






0 0
原创粉丝点击