Java反射机制

小编 2026-06-18 阅读:1117 评论:0
关于动态语言 程序运行时,可以改变程序结构,变量类型,如Python,ruby,js等。 Java动态性 Java不是动态语言,但是我们可以反射,字节码操作来获得类似动态语言的特性。...
  • 关于动态语言
    程序运行时,可以改变程序结构,变量类型,如Python,ruby,js等。

  • Java动态性
    Java不是动态语言,但是我们可以反射,字节码操作来获得类似动态语言的特性。

  • 反射机制
    运行时加载,使用编译期间完全未知的类,程序在运行时可以动态加载一个只有名 称的类,对于任何一个已经加载的类,都可以知道这个类的所有属性和方法和注解等。

    Class clazz= Class.forName(\"reflect.User\");

加载完之后在堆内存中传家一个Class对象,这个Class对象包含了这个对象完整的 结构信息,通过这个 对象就像一面镜子称之为反射。
Class:运行时的一个Classes和接口interfaces,只会被加载一次。

  • 反射的用途
    通过了解了反射机制,那么可以利用反射来干什么呢?通过反射的概念我们可以知道,反射机制就是来加载一个类的属性和方法等,如下我们逐步加载一个类以及方法属性等。

  • 加载类Class
    定义一个User类在reflect文件下,下图是我们是结构。\"在这里插入图片描述\"
    User类:

package reflect;
import lombok.Data;
import java.util.List;
import java.util.Map;
/**
 * Description:
 *
 * @Author lht
 * @Date 2018/12/22 下午3:25
 **/
@Data
public class User {

    private String name;

    @AccountAnnotation(value = \"123123\")
    private String password;

    @SexAnnotation(value = SexAnnotation.Sex.FEMALE)
    private String sex;
    
    public void setName(String name){
        this.name=name;
    }
    public Map<String,User> generic(){
        System.out.println(\"return Map\");
        return null;
    }
    public void setMap(Map<String,User> map, List<User> list){
        System.out.println(\"参数是:Map<String,User> map, List<User> list\");
    }
}

我们开始对User类进行动态加载:

 /**
         * 获取Class
         */
        Class clazz= Class.forName(\"reflect.User\");
        User user=new User();
        Class uClazz=user.getClass();
       //只加载一次hashCode相等
        Class UClazz=User.class;
        System.out.println(uClazz.hashCode());
        System.out.println(UClazz.hashCode());
        System.out.println(uClazz.equals(UClazz));//true
        //获取Class的两种方式
        System.out.println(User.class);
        System.out.println(user.getClass());

        //获取Class名称
        System.out.println(clazz.getName());
        System.out.println(uClazz.getName());
        System.out.println(UClazz.getName());
        /**
         * 运行结果:
         * 1279149968
         * 1279149968
         * true
         * class reflect.User
         * class reflect.User
         * reflect.User
         * reflect.User
         * reflect.User
         *
         */

从运行结果可以看出Class只加载一次。两次加载的hashCode是相等的。

  • 加载属性和注解
    前面已经通过反射加载到了类,我们现在开始加载属性和注解,把属性和注解加载放在一起顺便也就加载了,同样利用上面的User类。
 调用方法
 //Class clazzs=Class.forName(\"reflect.User\");
 //testAnnotation(clazzs);
 
 public static void testAnnotation(Class c){
 	//获取类的所有注解
        Annotation[] annotations=c.getAnnotations();
        System.out.println(annotations.length);
        for (Annotation annotation:annotations){
            System.out.println(\"类的所有注解annotation:\"+annotation);
        }
        //加载类的所有属性
        Field[] fields=c.getDeclaredFields();
        //对每个属性进行循环
        for (Field each:fields){
            if (each.isAnnotationPresent(AccountAnnotation.class)){//判断该属性是否存在注解
            //获取该属性的注解
                AccountAnnotation value=each.getAnnotation(AccountAnnotation.class);
                //输出该注解的值
                System.out.println(value.value());

            }
            if (each.isAnnotationPresent(SexAnnotation.class)){
                SexAnnotation sex=each.getAnnotation(SexAnnotation.class);
                System.out.println(sex.value());
            }
        }
    }

打印结果:

        /**
         * 打印结果:
         * 0
         * 123123
         * FEMALE
         */

  • 加载方法和构造函数
    方法比较复杂不想属性,只有类型的变化,方法有返回值,有参数,参数可能有多个,并且参数可能有泛型,因此方法的加载相对而言比较复杂,但也很简单。
 public static void trueTest() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
        User user=new User();
        Class clazz=user.getClass();
        //加载自身声明的方法
        Method method=clazz.getDeclaredMethod(\"setName\", String.class);
        //加载自身方法,包括父类,接口实现的方法
        Methid m=clazz.getMethod(\"setName\",String.class\");
      //调用user中的方法,参数传为lihaitao
            method.invoke(user,\"lihaitao\");
    }

上面就是对普通方法的加载。就那么简单。下面是对带有参数,且存在泛型的获取。

  • 反射操作泛型

java采用泛型擦除机制来擦出泛型,Java中的泛型仅仅时个javac使用的,确保数据的安全性和泛型转换的安全检查,
一旦编译完成,所有和泛型有关的全部擦除。
java.lang.reflect.Type:java语言中所有类型的公共父接口
Type直接子接口
ParameterizedType,GenericArrayType,TypeVariable和WildcardType四种类型的接口。
ParameterizedType: 表示一种参数化的类型,即拥有泛型定义的类型,比如HibernateDao或者Collection
GenericArrayType: 表示一种元素类型是参数化类型或者类型变量的数组类型
TypeVariable: 是各种类型变量的公共父接口.如你定义了一个参数化类型 User, 则T便是类型变量。
WildcardType: 代表一种通配符类型表达式,比如?, ? extends Number, ? super Integer.
以上四种接口,是为了反射操作需要,不能被归一到Class中的类型,但又和原始类型相似。
下面我开始操作上面User类中带有参数的方法:

public static void testParamGeneric() throws NoSuchMethodException {
        Method method=User.class.getMethod(\"setMap\", Map.class, List.class);
        Type[] types=method.getGenericParameterTypes();
        for (Type type:types){
            System.out.println(\"type:\"+type);//参数类型
            if (type instanceof ParameterizedType){
                Type[] genericTypes=((ParameterizedType) type).getActualTypeArguments();
                for (Type t:genericTypes){
                    System.out.println(\"参数泛型:\"+t);
                }
            }
        }

    }

上面是对带有泛型参数的方法。打印结果:

打印结果:
type:java.util.Map<java.lang.String, reflect.User>
参数泛型:class java.lang.String
参数泛型:class reflect.User
type:java.util.List<reflect.User>
参数泛型:class reflect.User

下面对有返回值,且返回值泛型做获取:

public static void testReturnGeneric() throws NoSuchMethodException {
        Method method=User.class.getMethod(\"generic\",null);
        Type type=method.getGenericReturnType();
        if (type instanceof ParameterizedType){
            System.out.println(\"type: \"+type);
            Type[] types=((ParameterizedType) type).getActualTypeArguments();
            for (Type t:types){
                System.out.println(\"返回值参数泛型:\"+t);
            }
        }
    }

打印结果:

打印结果:
type: java.util.Map<java.lang.String, reflect.User
返回值参数泛型:class java.lang.String
返回值参数泛型:class reflect.User

上面就是对方法的一些通过加载类获取,以及对方法的参数和返回值,包括泛型的操作。下面是性能问题

  • 反射机制性能:setAccessible

启用和禁用访问安全检查的开关,设置为true,则指反射的对象在使用时应该取消java语言的访问检查,如果值为false,则指反射的对象应该实施java语言访问检查,并不是true就能访问,false就不能访问。

// 三个方法以及运行时长
        simple();//129
        trueTest();//918
        falseTest();//728
//普通方法
public static void simple(){
        User user=new User();

        Long start=System.currentTimeMillis();
        for (long i=0;i<100000000L;i++){
            user.setName(\"lihaitao\");
        }
        Long end=System.currentTimeMillis();
        System.out.println(\"普通调用耗时:\"+(end-start));
    }

//设置安全检查为true
    public static void trueTest() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
        User user=new User();
        Class clazz=user.getClass();

        Method method=clazz.getDeclaredMethod(\"setName\", String.class);
        method.setAccessible(true);
        Long start=System.currentTimeMillis();
        for (long i=0;i<100000000L;i++){
            method.invoke(user,\"lihaitao\");
        }
        Long end=System.currentTimeMillis();
        System.out.println(\"true调用耗时:\"+(end-start));

    }
//不设置安全检查即为false
    public static void falseTest() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
        User user=new User();
        Class clazz=user.getClass();


        Method method=clazz.getDeclaredMethod(\"setName\", String.class);
        method.setAccessible(false);
        Long start=System.currentTimeMillis();
        for (long i=0;i<100000000L;i++){
            method.invoke(user,\"lihaitao\");
        }
        Long end=System.currentTimeMillis();
        System.out.println(\"false调用耗时:\"+(end-start));

    }

打印结果:

普通调用耗时:129
true调用耗时:1031
false调用耗时:735

从打印结果可以看出,作了安全检查明显耗时增加,所以不必要的安全检查可以省略。
总结:通过加载一个Class,到获取属性,方法以及对方法参数返回值的加载,基本上一个类的核心信息加载完了,通过对反射机制的性能了解,可以对方法的调用设置安全检查和去掉安全检查来提高安全性和效率。

版权声明

本文仅代表作者观点,不代表百度立场。
本文系作者授权百度百家发表,未经许可,不得转载。

热门文章
  • 机房智能化温湿度解决方式之POE供电以太网温湿度传感器

    机房智能化温湿度解决方式之POE供电以太网温湿度传感器
    机房智能化温湿度解决方式之POE供电以太网温湿度传感器 北京盈创力和电子科技有限公司 智能型TCP网口温湿度记录仪 北京IP网络温湿度记录仪厂家,北京盈创力和 北京智能型TCP网口温湿度记录仪IP网络温湿度记录仪是一种新型的基于TCP/IP协议双绞线以太网标准温湿度采集模块,利用它可以实现现场温度值、相对湿度值的采集,同时利用其自身的RJ45通信接口可以方便地和机房监控主机或交换机集线器进行联网。 工作于-40℃~85℃工业级带...
  • Sequential Monte Carlo Methods (SMC) 序列蒙特卡洛/粒子滤波/Bootstrap Filtering

    Sequential Monte Carlo Methods (SMC) 序列蒙特卡洛/粒子滤波/Bootstrap Filtering
    Problem Statement 我们考虑一个具有马尔可夫性质、非线性、非高斯的状态空间模型(State Space Model):对于一个时间序列上的观测结果{yt,t∈N}\\{ y_t , t \\in N \\}{yt​,t∈N},我们认为每个观测结果yty_tyt​的生成依赖于一个无法直接观察的隐变量xt∈{xt,t∈N}x_t \\in \\{x_t , t \\in N \\}xt​∈{xt​,t∈N},即:p(...
  • HTTP状态保持的原理

    HTTP状态保持的原理
    a)在用户登录之后,浏览器返回响应的时候会在响应中添加上cookieb)浏览器接收到cookie之后会自动保存c)当用户再次请求同一服务器中的其他网页的时候,浏览器会自动带上之前保存的cookied)服务接收到请求之后可以请 request 对象中取到cookie 判断当前用户是否登录  Http是无状态的,就是连接时数据互通,关闭后...
  • Hive 系统函数及示例

    Hive 系统函数及示例
    查看所有系统函数 show functions; 函数分类 内置函数【系统函数】 数学函数: floor、round、ceil、cos、log2等 字符串函数: length、reverse、trim、lower、get_json_object、repeat等 收集函数: size 转换函数: cast 日期函数: year、month、datediff、date、date_add等 条件函数: coalesce、case…w...
  • CSRF的原理和防范措施

    CSRF的原理和防范措施
    a)攻击原理:i.用户C访问正常网站A时进行登录,浏览器保存A的cookieii.用户C再访问攻击网站B,网站B上有某个隐藏的链接或者图片标签会自动请求网站A的URL地址,例如表单提交,传指定的参数iii.而攻击网站B在访问网站A的时候,浏览器会自动带上网站A的cookieiv.所以网站A在接收到请求之后可判断当前用户是登录状态,所以...
标签列表