zoukankan      html  css  js  c++  java
  • Java反射机制

    Java反射机制

    一、什么是反射?

    Java创建对象有四种方式,分别是:new、序列化、克隆、反射
    反射是一种间接操作对象的机制,通过反射机制可以获取对象的字节码信息,进而产生实例对象,反射机制的核心是在JVM运行过程中动态的加载类文件并通过newInstance方法生成对应的类对象,我们可以通过反射获取字节码中包含的类的所有信息,包括所有的属性、方法、构造器、注解信息等等。要注意的是:在相应类的源码中如果有带参构造器,千万千万不要忘了空构造的声明,否则反射时直接调用newInstance()方法会报错:NoSuchMethodException。

    二、反射的原理

    在代码中通过编写Class.forName("类的全限定路径")代码,当JVM运行到该行代码时会到磁盘中根据给定的路径加载对应的字节码文件,加载到JVM虚拟机中后,会生成对应的Class类对象,在调用相应的newInstance方法时再在Java内存中分配对应的对象空间完成对象的实例化,注意:在JVM中,一个类的Class对象只有一个,不论该类以多少种方式生成多少个对象

    Class<?> reflect1 = Class.forName("com.mango.test.TestReflect");
    Class<?> reflect2 = new TestReflect().getClass();
    Class<?> reflect3 = TestReflect.class;
    Object o = reflect1.newInstance();
    System.out.println(reflect1==reflect2);
    System.out.println(reflect2==reflect3);
    System.out.println(reflect3==o.getClass());
    
    //对应的运行结果
    true
    true
    true
    

    三、反射的优缺点

    优点

    使用反射,就可以实现类的动态加载,从而就可以通过配置文件的方式让代码在不同需求下加载不同的字节码文件,继而完成不同的功能,很大程度上解耦了类与类之间的关系

    缺点

    首先,反射创建对象相比于传统的使用new方式创建对象,效率上会慢很多,以下是通过反射的newInstance方式和传统的new方式分别创建1000个对象时的用时及效率比:

    Class<?> reflect = Class.forName("com.mango.test.TestReflect");
    long begin = System.nanoTime();
    for (int i = 0; i < 1000; i++) {
        reflect.newInstance();
    }
    long end = System.nanoTime();
    long reflectTime = end - begin;
    begin = System.nanoTime();
    for (int i = 0; i < 1000; i++) {
        new TestReflect();
    }
    end = System.nanoTime();
    long newTime = end - begin;
    System.out.println("反射方式使用的时间:" + reflectTime);
    System.out.println("new方式使用的时间:" + newTime);
    System.out.println("效率比:" + reflectTime / newTime);
    
    //对应的运行结果
    反射方式使用的时间:3963604   //单位:纳秒
    new方式使用的时间:100437
    效率比:39  //注意:该值非定值,当只创建100个对象时,为171,当创建到10000个对象时,为11
    

    其次,反射调用方法时可以通过忽略权限检查来提升一部分性能,但是这同时也破坏了代码的封装性,容易引发安全问题

    //TestMango中的main方法,不能在Mango类中直接测试,因为类中方法直接获取私有属性一点问题都没有
    Class<?> mangoReflect = Class.forName("com.mango.test.Mango");
    Mango mangoSF = (Mango) mangoReflect.newInstance();
    Field handsome = mangoReflect.getDeclaredField("handsome");
    //System.out.println(handsome.get(mangoSF));    //未关闭权限检查,会报错:IllegalAccessException
    //handsome.set(mangoSF,100);                    //未关闭权限检查,会报错:IllegalAccessException
    handsome.setAccessible(true);                   //关闭权限检查
    handsome.set(mangoSF,100);                      //私有属性可以直接操作
    System.out.println(handsome.get(mangoSF));      //私有属性也可以直接获取
    
    public class Mango {
        private int handsome=0;
    }
    

    四、反射机制常用的五个类

    Java.lang.Class;                //类
    Java.lang.reflect.Constructor;  //构造器
    Java.lang.reflect.Field;        //成员变量
    Java.lang.reflect.Method;       //方法
    Java.lang.reflect.Modifier;     //修饰符
    

    五、反射的基本使用

    使用到的Mango类的类结构

    @MyAnnotation("class")
    public class Mango implements Serializable {
        @MyAnnotation("field")
        public String name = "Mango";
        private int handsome = 0;
    
        public Mango() { }
    
        private Mango(int handsome){ this.handsome=handsome; }
    
        public Mango(String name, int handsome) {
            this.name = name;
            this.handsome = handsome;
        }
    
        public int getHandsome() { return handsome; }
    
        @MyAnnotation("method")
        private void setHandsome(int handsome) { this.handsome = handsome; }
    
        public void func1(Map<String,Mango> map, List<Mango> list){/* some code here */}
    
        public Map<Integer,Mango> func2(){/* some code here */ return null;}
    }
    

    获取class的三个方法

    第一种:Class.forName("类的全限定路径名");根据全限定路径获取
    第二种:实例对象.getClass();根据实例对象来获取
    第三种:类.class;根据类名获取,不过该方法十分鸡肋,既然已经知道是哪个类了,直接new还更快

    判断生成的对象是否为某个类的实例:instanceof

    Class<?> mangoReflect = Class.forName("com.mango.test.Mango");
    //未指定泛型的情况下,返回的是Object类型
    Object o = mangoReflect.newInstance();
    //如果返回的是true,则代表o是Mango类型的对象
    if(o instanceof Mango)
    {
        //直接做向下转型
        Mango mango=(Mango)o;
    }
    

    调用带参与不带参构造方法生成实例对象

    无参构造方式实例化对象

    Class<?> mangoReflect = Class.forName("com.mango.test.Mango");
    //获取无参构造
    Constructor<?> constructor = mangoReflect.getConstructor(null);
    Mango mango = (Mango) constructor.newInstance();
    System.out.println(mango.name+"	"+mango.getHandsome());    //输出:Mango  0
    

    带参构造方式实例化对象

    Class<?> mangoReflect = Class.forName("com.mango.test.Mango");
    //获取带参构造
    Constructor<?> constructor = mangoReflect.getConstructor(new Class[]{String.class, int.class});
    Mango mango = (Mango) constructor.newInstance("Mango_SF",100);
    System.out.println(mango.name+"	"+mango.getHandsome());    输出:Mango_SF 100
    

    常用的方法

    操作类

    Class<?> mangoReflect = Class.forName("com.mango.test.Mango");
    System.out.println("getConstructors         :"+mangoReflect.getConstructors().length);  //获取所有public修饰的构造器
    System.out.println("getDeclaredConstructor  :"+mangoReflect.getDeclaredConstructor(new Class[]{String.class, int.class}));//获取声明的指定构造器(即使是私有的)
    System.out.println("getDeclaredConstructors :"+mangoReflect.getDeclaredConstructors().length);//获取声明的所有构造器(包括私有)
    System.out.println("getFields               :"+mangoReflect.getFields().length);//获取所有public修饰的成员变量
    System.out.println("getDeclaredField        :"+mangoReflect.getDeclaredField("handsome"));//获取指定成员变量(即使是私有的)
    System.out.println("getDeclaredFields       :"+mangoReflect.getDeclaredFields().length);//获取声明的所有成员变量(包括私有)
    System.out.println("getMethods              :"+mangoReflect.getMethods().length);//获取所有public修饰的方法
    System.out.println("getDeclaredMethod       :"+mangoReflect.getDeclaredMethod("setHandsome", int.class));//获取声明的指定方法(即使是私有的)
    System.out.println("getDeclaredMethods      :"+mangoReflect.getDeclaredMethods().length);//获取声明的所有方法(包括私有)
    System.out.println("getModifiers            :"+mangoReflect.getModifiers());//获取类修饰符,以整数返回
    System.out.println("getName                 :"+mangoReflect.getName());//获取类的全路径名
    System.out.println("getPackage              :"+mangoReflect.getPackage());//获取包名
    System.out.println("getSimpleName           :"+mangoReflect.getSimpleName());//获取类名
    System.out.println("getInterfaces           :"+mangoReflect.getInterfaces().length);//获取类继承的所有接口
    System.out.println("isAnnotationPresent     :"+mangoReflect.isAnnotationPresent(MyAnnotation.class));//获取类上是否有指定注解
    System.out.println("getAnnotations          :"+mangoReflect.getAnnotations().length);//获取类上的所有注解
    
    getConstructors         :2
    getDeclaredConstructor  :public com.mango.test.Mango(java.lang.String,int)
    getDeclaredConstructors :3
    getFields               :1
    getDeclaredField        :private int com.mango.test.Mango.handsome
    getDeclaredFields       :2
    getMethods              :12 //包括父类的方法:wait wait wait equals toString hashCode getClass notify notifyAll
    getDeclaredMethod       :private void com.mango.test.Mango.setHandsome(int)
    getDeclaredMethods      :4
    getModifiers            :1
    getName                 :com.mango.test.Mango
    getPackage              :package com.mango.test
    getSimpleName           :Mango
    getInterfaces           :1
    isAnnotationPresent     :true
    getAnnotations          :1
    

    操作成员变量

    Class<?> mangoReflect = Class.forName("com.mango.test.Mango");
    Mango mangoSF = (Mango) mangoReflect.newInstance();
    Field handsome = mangoReflect.getDeclaredField("handsome");
    handsome.setAccessible(true);//忽略权限检查
    handsome.set(mangoSF,100);//设置mangoSF对象的handsome值为100
    handsome.get(mangoSF);//获取mangoSF对象的handsome值,该处返回100
    handsome.getAnnotations();//获取成员变量上的所有注解
    handsome.getAnnotation(MyAnnotation.class);//获取成员变量上的指定注解
    handsome.getName();//返回指定成员变量在字节码中的名称,该处返回handsome
    handsome.getType();//返回handsome成员变量的数据类型,该处返回int
    

    操作方法

    Class<?> mangoReflect = Class.forName("com.mango.test.Mango");
    Mango mangoSF = (Mango) mangoReflect.newInstance();
    Method setHandsome = mangoReflect.getDeclaredMethod("setHandsome", int.class);
    setHandsome.setAccessible(true);//忽略权限检查
    setHandsome.invoke(mangoSF, 100);//调用mangoSF对象的setHandsome方法,参数为100
    setHandsome.getParameterTypes();//获取setHandsome方法的所有的参数类型,返回值为Class[]类型数组
    setHandsome.getReturnType();//获取返回值类型,该处返回void
    setHandsome.getModifiers();//获取修饰符类型,该处返回2
    

    反射操作泛型

    //参数为泛型
    Class<?> mangoReflect = Class.forName("com.mango.test.Mango");
    Method method = mangoReflect.getMethod("func1", Map.class, List.class);
    Type[] types = method.getGenericParameterTypes();
    for (Type paramType : types) {
        System.out.println("当前参数:" + paramType);
        //ParameterizedType表示一种参数化的类型,例如Collection<String>
        if (paramType instanceof ParameterizedType) {
            Type[] genericTypes = ((ParameterizedType) paramType).getActualTypeArguments();
            for (Type genericType : genericTypes) {
                System.out.println("泛型类型:" + genericType);
            }
        }
    }
    
    //对应运行结果
    当前参数:java.util.Map<java.lang.String, com.mango.test.Mango>
    泛型类型:class java.lang.String
    泛型类型:class com.mango.test.Mango
    当前参数:java.util.List<com.mango.test.Mango>
    泛型类型:class com.mango.test.Mango
    
    //返回值为泛型
    Class<?> mangoReflect = Class.forName("com.mango.test.Mango");
    Method method = mangoReflect.getMethod("func2");
    Type returnType = method.getGenericReturnType();
    if (returnType instanceof ParameterizedType) {
        Type[] genericTypes = ((ParameterizedType) returnType).getActualTypeArguments();
        for (Type genericType : genericTypes) {
            System.out.println("返回值,泛型类型:" + genericType);
        }
    }
    
    //对应运行结果
    返回值,泛型类型:class java.lang.Integer
    返回值,泛型类型:class com.mango.test.Mango
    

    如果对你有帮助,点个赞,或者打个赏吧,嘿嘿
    整理不易,请尊重博主的劳动成果

  • 相关阅读:
    Linux中的阻塞机制
    Shellz中awk的简单用法
    实际项目开发过程中常用C语言函数的9大用法
    堆栈溢出一般是什么原因?
    哈夫曼算法原理
    7款易上手C语言编程软件推荐
    嵌入式系统分类介绍
    什么是字符串数组
    C语言中数组定义方式
    第三章课后习题P56解析
  • 原文地址:https://www.cnblogs.com/Mango-Tree/p/12750225.html
Copyright © 2011-2022 走看看