反射机制

第10章 反射机制

JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的属性和方法对于任意一个对象,都能够调用到它的任意一个方法和属性。这种状态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制。

10.1 反射的基石—Class类

1、简介

Java类用于描述一类事物,该类事物有什么属性,没什么属性,至于这个属性的值是什么,则使用这个类的实例对象来确定的。Java程序中的各个java类属于同一类型事物,描述这类事物的Java类名就是Class。

2、获取字节码文件的方式

要想对字节码文件进行解剖,必须要有字节码文件对象如何获取字节码文件对象?以下为获取对象有三种方式的示例

publicclass GetClass {

      publicstaticvoid main(String[] args) throws Exception {

        String s = "fdjak";

       //方式一:Object类中的getClass方法的,想要用这种方式,必须明确具体的类并创建对象

        Class clazz1 = s.getClass();

       //方式二:任何数据类型都具备一个静态的属性.class来获取其对应的Class对象 相对简单,但

       //是还是要用到类中的静态成员。还是不够扩展

        Class clazz2 = String.class;

       /*

方式三:只要通过给定的类的字符串名称就可获取该类,更为扩展,可以用Class类中的方法

完成。该方法就是forName,返回字节码文件,如果内存中有该字节码文件就直接返回,如果

没有,先加载然后在返回这种方式只要有名称即可,更为方便,扩展性强。

        */

        Class clazz3 = Class.forName("java.lang.String");

        System.out.println(clazz1==clazz2);

        System.out.println(clazz1==clazz3);

        //String是一个类,不是基本类型

        System.out.println(clazz1.isPrimitive());

        System.out.println(int.class.isPrimitive());

         System.out.println(int.class ==Integer.class);

        //九种基本类型可以用(该类型的包装类.TYPE)来表示

        System.out.println(int.class ==Integer.TYPE);

        }

}

3、Class实例对象

⑴、预定义的 Class实例对象

九个预定义的Class实例对象,表示八个基本类型和 void。这些类对象由 Java虚拟机创建,与其表示的基本类型同名,即 booleanbytecharshortintlongfloatdouble

⑵、数组的Class实例对象

总之,只要是在源程序中出现的类型,都有各自的Class实例对象,例如int[] ,void

10.2 反射

反射就是把java类中的各种成分映射成相应的java类。一个类中的每一个成员都可以用相应的反射API类的一个实例对象来表示。

1、Constructor类

Constructor类代表某个类中的一个构造方法。当要获取指定名称对应类中的所体现的对象时,而该对象初始化下不使用空参构造方法该怎么办?既然是通过指定的构造方法进行对象的初始化,应该先获取到构造方法,通过字节码文件对象即可完成,该方法是:getConstructor(paramterTypes);

     privatestaticvoidcreateNewObject_2() throws Exception {

        //反射机制.Person p = new 反射机制.Person("xiaoqiang",10);

        Stringname = "反射机制.Person";

        Class<?>clazz = Class.forName(name);

        //获取到了指定的构造方法对象

        Constructor<?>constructor = clazz.getConstructor(String.class,int.class);

        //通过该构造器对象的newInstance方法进行对象的初始化

        Objectobj = constructor.newInstance("xiaoming",45);   

    }

     publicstaticvoidcreateNewObject_1() throws Exception {

        //早期:new的时候,先根据被new的类的名称寻找该类的字节码文件,并加载进内存并创建该字

//节码文件对象,并接着创建该字节文件的对应的Person对象。反射机制.Person p = new 反射

//制.Person();

        //现在:

        Stringname = "反射机制.Person";

        //找寻该名称类文件,加载进内存,并产生Class对象

        Classclazz = Class.forName(name);

        //如何产生该类对象呢?用newInstance()可以创建一个实例,并利用空参构造方法初始化该对象

        //当Person类中没有空参构造方法,就会报InstantiationException,如果空参构造方法被私有

//化,就会发生IllegalAccessException

        Objectobj = clazz.newInstance();      

        }

}

2、Field

Field类代表某个类中的一个成员变量

publicstaticvoidmain(String[] args) throws Exception {

           Class clazz = Class.forName("反射机制.Person");

          //getField方法访问私有的方法会发生NoSuchFieldException,因为他只能获取公有的字段,

          Field field = null;//clazz.getField("age");

          //getDeclaredField方法只获取本类的,但包含私有

          field = clazz.getDeclaredField("age");

          //对私有字段访问取消权限检查,暴力访问

          field.setAccessible(true);

          Object obj = clazz.newInstance();

          field.set(obj, 54);

          Object o = field.get(obj);

          System.out.println(o);         

    }

3、Method

//获取所有的方法

     privatestaticvoidgetMethod1() throws ClassNotFoundException {

        Classclazz = Class.forName("反射机制.Person");

           //获取的都是公有的方法

           Method[] method = clazz.getMethods();

           //获取的都是本类中的所有方法,包含私有

           method = clazz.getDeclaredMethods();

           for(Method method2 : method) {

            System.out.println(method2);

    }}

//获取单个无参方法方法

  privatestaticvoid getMethod2() throws Exception {

        Class<?>clazz = Class.forName("反射机制.Person");

        //获取空参数一般方法

        Methodmethod = clazz.getMethod("show",null);

        //运行方法需要对象

        Objectobj = clazz.newInstance();

        Constructor<?>con = clazz.getConstructor(String.class,int.class);

        Objectobj1 = con.newInstance("ciao",365);

        method.invoke(obj1,null);    

    }

//获取单个有参方法方法的方式

privatestaticvoidgetMethod3() throws Exception {

        Classclazz = Class.forName("反射机制.Person");

        Methodmethod = clazz.getMethod("paramMethod",String.class,int.class);

        Objectobj = clazz.newInstance();

        method.invoke(obj,"xiaocqiang",65 );

     }

注意:jdk1.4和jdk1.5的invoke方法的区别

在jdk1.4没有可变参数,因此当有多个参数时,要将这些参数封装成一个数组,然后传递给invoke方法,数组中的每个元素分别对应被调用方法中的一个参数。

     publicvoid test() throws Exception {

        Stringstr1 = "adfawe";

//jdk1.5的invoke方法的形式

        System.out.println(methodCharAt.invoke(str1, 1));

        //jdk1.4的invoke方法的传参形式

        System.out.println(methodCharAt.invoke(str1, new Object[]{2}));

        //如果传递给Method对象的invoke()方法的第一个参数为null,这意味着Method对象对应的是一个静态方法

        System.out.println(methodCharAt.invoke(null, 1));

    }

10.3 反射的应用

10.3.1 用反射方式执行某个类中的main方法

⑴、缘由:之所以要用反射方式执行某个类中的main方法,是因为在程序中的某个类在运行到某处需要去调用其他类的main方法时,如果此程序并不知道此main方法所属类的名称,而只是在程序中接受某一代表此main方法所属类的名称的参数,那么这时候就不能通过“类名.main(String[] args);"这样的方式来完成调用,而需要运用Java的反射机制了,需要编写相关的反射代码来完成对其他类的main方法的调用。

⑵、问题:启动Java程序的main方法的参数是一个字符串数组,即public static void main(String[] args),通过反射方式来调用这个main方法时,如何为invoke方法设置参数呢?

按jdk 1.5的语法,由于使用的是可变参数(Object类型),设置的数组参数会被作为一个参数进行传递,而按jdk 1.4的语法,此处应设置一个Object数组,数组中的每个元素对应所调用方法的一个参数。

当把一个字符串数组作为参数传递给invoke方式时,编译器会兼容jdk 1.4的语法,即按照1.4的语法进行处理,即把字符串数组打散成为若干个单独的参数,这样就会产生参数个数不匹配的异常。

解决办法:

mainMethod.invoke(null,newObject[](new String[]{“xxxx”}))打包成一个

mainMethod.invoke(null,(Object)(newString[]{“xxxx”}))采用上述强制向上转型后,可以是编译器按照正确的方法进行参数处理,即将整个字符串参数作为整体传递给目标main方法。

10.3.2、数组的反射

1、每一个具有相同维数和元素类型的数组属于同一个类型,即具有相同的Class实例对象。

class ArrayReflect {

     publicstaticvoid main(String[] args) {

        int[] a1 = newint[2];

        int[] a2 = newint[4];

        System.out.println(a1.getClass() == a2.getClass());

     }

}

2、代表数组的Class实例对象的个体Superclass()方法返回的父类为Object类对应的Class

class ArrayReflect {

     publicstaticvoid main(String[] args) {

        int[] a1 = newint[]{1,2,3};

        System.out.println(a1.getClass().getName());

        System.out.println(a1.getClass().getSuperclass().getName());

     }

}

3、基本类型的一维数组可以被当做Object类型使用,但不能当做Object[]类型使用,非基本类型即可当做Object类型使用,又可以当做Object[]类型使用。

class ArrayReflect {

    publicstaticvoid main(String[] args) {

       int[] a1 = newint[]{1,2,3};

       int[][] a3 = newint[2][3];

       String[] a4 = new String[]{"a","v","c"};       

       Object aobj1 = a1;

       Object aobj2 = a4;    

      // Object[]aobj3 = a1;报错:a1里面存的是int型数据,而不是Object

       Object[] aobj4 = a3;

       Object[] aobj5 = a4;

    }

}

4、Arrays.asList()方法处理int[]和String[]时的差异

class ArrayReflect {

    publicstaticvoid main(String[] args) {

       int[] a1 = newint[]{1,2,3};

       String[] a4 = new String[]{"a","r","c"};   

       System.out.println( Arrays.asList(a4));

       System.out.println( Arrays.asList(a1));

    }

}

打印结果:[a, r, c] [[I@55e55f]之所以如此也和3的原理差不多

5、数组的反射

      如何使用反射的方式来获取、设置数组中的元素,或者获取数组的长度?

privatestaticvoidprintObject(Object obj) {

      Class clazz = obj.getClass();

      if(clazz.isArray()) {

         int len = Array.getLength(obj);

         for(int i= 0; i<len; i++){

//get()方法返回指定数组对象中索引组件的值。

            System.out.println(Array.get(obj, i));

         }

      }else {

           System.out.println(obj);

      }

}

怎么获得数组的类型?结论是:没有办法获得数组的类型。

反射的作用——实现框架

框架与框架要解决的核心问题

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值