反射的几种方式

一.概念:

  • 引入:
    • 万事万物皆对象,那你创建的类型,这个类型是不是对象?
    • 如果这个类型也是对象,那这个类型的类型是谁?Class
  • 概念:
    • JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制。
      要想解剖一个类,必须先要获取到该类的字节码文件对象。而解剖使用的就是Class类中的方法.所以先要获取到每一个字节码文件对应的Class类型的对象.

  • 通常情况下代码编写阶段到程序运行阶段,主要有以下几个状态 

 

  •  看完这段文字,就会清楚的知道为什么需要反射了!

说完通常情况了,就该说一下特殊情况了。假设有这么一个需求,要求编写一段代码,这个代码的作用是,对于给定的任意一个类,要求调用这个类的任意某个方法。

这个要怎么做呢?

和上面的不同点在于,在开发人员层面,编写上面示例代码时,开发者是明确知道Person类的,知道Person类的所有信息,也知道要调用Person类的某个具体的方法。但是现在这个需求,开发人员什么都不知道了,不知道到底要调用类的哪个方法了,甚至不知道到底要调用哪个类的方法,对于这个类的信息是一无所知,只知道在程序运行期间会给你一个类。

从编译层面来看,在编译阶段,是没有这个类的信息的,编译器也是对这个类一无所知的。这个类的信息是在运行阶段才会有的。

那么就没有办法完成了吗?办法当然是有的,就算是开发人员在开发时不知道这个类是啥,就算是编译器在编译阶段不知道这个类的信息,但是在程序的运行阶段,是可以想办法获取这个类的信息的。只要在程序的运行阶段,我们获取了这个类的所有信息,那么我们想干啥就干啥,想执行它的啥方法就可以执行它的啥方法了。这就是反射。换言之,反射就是在程序的运行期动态获取类的信息。

如下图所示,虽然在代码开发和编译阶段可能不知道某个类的详细信息,但是在运行阶段,当这个类被加载到JVM中了就会生成一个Class对象,我们通过这个Class对象,在运行期一样可以获得这个类对应的信息,这就是反射。

 

二,创建类的三种方式(抽取成了一个方法):

  private static void get_class() throws ClassNotFoundException {
        
		//1、获取对象的字节码
        //创建/得到Class类型的三种方式,第三种方式最合适
        //1.User.class
        Class c1 = User.class;
        //获取类型的名字
        String name = c1.getSimpleName();
        //全名称
        String name1 = c1.getName();
        System.out.println(name+","+name1);


        //2.通过user对象来获取
        User us = new User();
        Class c2 = us.getClass();
        System.out.println(c2.getName());


        //3.根据字符串来得到class类型,有可能会报找不到类异常,这种最合适,因为运行的时候可以动态来处理,可以用变量的形式
        // Class c3 = Class.forName("com.pro.domain.User");
        String xx = "com.pro.domain.User";
        Class c3 = Class.forName(xx);
        System.out.println(c3.getName());
    }

 三.操作类当中的属性和方法

 private static void get_method() throws Exception {
 //buffer线程安全,builder更灵活
        /*StringBuffer b1;
        StringBuilder b2;
        String b3;*/

        //getModifiers,如果User类是公共的public,值为1,不是为0
        /*System.out.println(c.getModifiers());*/
        Class c = Class.forName("com.pro.domain.User");
        StringBuffer bf = new StringBuffer();

        int modifiers = c.getModifiers();//获取User类的访问修饰符
        String cmod = Modifier.toString(modifiers);

        bf.append(cmod+" class "+c.getSimpleName()+"{\n");
        //----------------操作所有的属性-------------------
        Field[] fields = c.getDeclaredFields();//获取到类里面的所有字段
        for (Field field : fields) {
            //获取属性的访问修饰符
            String fmod = Modifier.toString(field.getModifiers());
            //获取属性的类型名称
            Class<?> ftype = field.getType();
            String ftypeName = ftype.getSimpleName();
            //属性名
            String fname = field.getName();

            bf.append("\t"+fmod+" "+ftypeName+" "+fname+";\n");
        }

        //----------------操作所有的方法-------------------

/*
 * 获取成员方法并调用:
 * 
 * 1.批量的:
 * 		public Method[] getMethods():获取所有"公有方法";(包含了父类的方法也包含Object类)
 * 		public Method[] getDeclaredMethods():获取所有的成员方法,包括私有的(不包括继承的)
 * 2.获取单个的:
 * 		public Method getMethod(String name,Class<?>... parameterTypes):
 * 					参数:
 * 						name : 方法名;
 * 						Class ... : 形参的Class类型对象
 * 		public Method getDeclaredMethod(String name,Class<?>... parameterTypes)
 * 
 * 	 调用方法:
 * 		Method --> public Object invoke(Object obj,Object... args):
 * 					参数说明:
 * 					obj : 要调用方法的对象;
 * 					args:调用方式时所传递的实参;
):
 */

        //getDeclaredMethods对访问修饰符有限定,通过这种方式,对私有方法也可以访问,不带declare就不能访问私有,需要配置才行
        Method[] methods = c.getDeclaredMethods();
        for (Method method : methods) {
            //方法的修饰符
            String mmod = Modifier.toString(method.getModifiers());
            //方法的返回类型名
            String mreturnTypeName = method.getReturnType().getSimpleName();
            //方法名
            String mname = method.getName();
            bf.append("\t"+mmod+" "+mreturnTypeName+" "+mname+"(");

            //-------------方法的参数--------------
            Class<?>[] parameterTypes = method.getParameterTypes();
            for (int i = 0; i < parameterTypes.length; i++) {
                Class<?> parameterType = parameterTypes[i];
                //判断是否是最后一个参数
                if(i==parameterTypes.length-1){
                    bf.append(parameterType.getSimpleName());
                }else{
                    bf.append(parameterType.getSimpleName()+",");
                }
            }

            bf.append("){\n");
            bf.append("\t}\n");
        }


        //-------------构造函数,写起来和普通方法一样,只是方法名不同------------
        Constructor cs = c.getDeclaredConstructor();
//        cs.

        bf.append("}");

        System.out.println(bf);

       
    }

 四.操作自定义方法

public class XxTest {
    public static void main(String[] args) throws Exception {
        //拿到类,有三种方式
        Class c = Class.forName("com.pro.domain.User");
        //newInstance创建类是这个类必须已经加载过且已经连接(Class.forName(“A”)这个过程)
        // new创建类是则不需要这个类加载过
        //获取c的对象
        Object o = c.newInstance();
        //通过反射获取方法名,第一个参数是方法名,后面的参数是方法参数
        Method say = c.getDeclaredMethod("say", String.class, int .class);
        //这个say方法是o对象的
        //第一个参数为实例对象,第二个参数为实参。总结:通过反射获取方法名,然后invoke方法注入方法对象和实参
        Object result = say.invoke(o, "pkb", 18);
        System.out.println(result);
    }
}

五.反射再理解:

  • 用途
    • 通过前面的叙述,就算是开发人员在开发代码时可以不知道某些类的相关信息,编译器在编译阶段也可以不知道类的相关信息,但是通过在运行期动态获取类的相关信息,依然可以完成许多事情。许多的Java框架比如Spring、mybatis等等,都是基于反射实现的。

      总之,最重要的就是要理解一点,对于java类,通常情况下是开发人员自己编写的java文件通过编译得到对应的class文件,或者直接引用别人的jar包得到对应的class文件,在这些方式里面,程序在运行之前就已经获取到类对应的信息了,就无需用到反射。

      而反射的作用场景就在于,在程序运行之前,是获取不到对应的class文件的。这些class文件有可能是程序运行中从其他地方获取到的,有可能是动态生成的。在这种情况下,就需要通过反射在运行期来动态获取对应类的相关信息。

  • 理解:
    • 在Java语言当中,一切都是类与对象。

      比如说有三辆具体的车,每一辆车都有各自的属性(比如颜色)和相同方法(比如前进),我们可以抽象出来车这样一个类,三辆车中的每一辆都是车这个类的一个对象(实例)。

      在生活中,既可以有车这个类,还可以有人这个类,也可以有树这个类,总之,我们可以有无穷多个类。那么其实我们可以站在更高的一个角度来看待问题,把各个类都抽象出来形成一个新的类别,这个类别名字就叫作“类”(Class),就像我们可以把几辆车抽象出来形成的一个类叫“车”一样。车这个类是抽象出来的新的“类”的一个实例,人这个类也是抽象出来的新的这个“类”的一个实例。

      为了形象化一点,我们可以这样假想以便于理解:对于每一个类,比如车这个类Car,会有对应的Car.class,我们具有无数个类也就具有无数个class文件。想象一下其实每个class文件都是一个对象,一个叫做“类”的这个类的对象。

      总而言之:车对应的类型是Car,可以有无数辆具体的车,但只会有一个Car类别;类对应的类型是Class类,可以有无数个具体的类(对应无数个class文件),但只会有一个Class类别。
       

       
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值