Java 反射

一、概述

Java的反射(reflection)机制是指在程序的运行状态中,可以构造任意一个类的对象,可以了解任意一个对象所属的类,可以了解任意一个类的成员变量和方法,可以调用任意一个对象的属性和方法。这种动态获取程序信息以及动态调用对象的功能称为Java语言的反射机制。

  1. Java反射机制提供的功能

在运行时判断任意一个对象所属的类
在运行时构造任意一个类的对象
在运行时判断任意一个类所具有的成员变量和方法
在运行时获取泛型信息
在运行时调用任意一个对象的成员变量和方法
在运行时处理注解
生成动态代理

  1. 反射相关API

java.lang.Class:代表一个类
java.lang.reflect.Method:代表类的方法
java.lang.reflect.Field:代表类的成员变量
java.lang.reflect.Constructor:代表类的构造器

二、反射的使用

使用反射前,先来介绍一下Class:

  1. Class本身也是一个类
  2. Class对象只能由系统建立对象
  3. 一个加载的类在jvm中只有一个Class实例
  4. 一个Class对象对应的是一个加载到jvm中的一个.class文件
  5. 每个类的实例都会记得自己是由那个Class实例所生成
  6. 通过Class可以完整的得到一个类中的所有被加载的结构
  7. Class是反射的根源,任何想动态加载,运行的类,必须先获得相应的Class对象。

所以要使用反射,我们需要先获取到类的Class。首先我们先创建一个User实体类,其中包含name,age,get/set方法。

public class User {
    private String name;
    private int age;
    
    public User(){}
    
    public User(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    @Override
    public String toString() {
        return "User{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}

第一步要获取User的Class,我们先看有如下几种方式:

        /*
        第一种方式:已知具体的类,通过类的class属性获取。
         */
        Class c1 = User.class;

        /*
        第二种方式:已知类的实例,通过该实例的getClass()方法获取。
         */
        User user = new User();
        Class c2 = user.getClass();


        /*
        第三种方式:已知类的全类名,通过Class的静态方法forName()获取。
         */
        Class c3 = Class.forName("com.mvpapp.reflection.User");

获得Class之后,我们就可以通过Class来获取类的信息了,例如获取类的属性,方法,构造器等。

  1. 获取类的属性
        //获得类的属性
        Field[] fields = c1.getFields(); //只能获取public的属性

        Field[] fields1 = c1.getDeclaredFields();//获取所有属性
        for (Field field:fields1){
            System.out.println(field);
        }

        输出:
        private java.lang.String com.mvpapp.reflection.User.name
        private int com.mvpapp.reflection.User.age
  1. 获取指定属性
        //获得指定的属性值
        //Field name = c1.getField("name"); //只能获取public属性
        Field name2 = c1.getDeclaredField("name");//获取所有属性
        System.out.println(name2);

        输出:
        private java.lang.String com.mvpapp.reflection.User.name
  1. 获取类的方法
        //获取类的方法
        Method[] methods = c1.getMethods();//获取本类及其父类的所有方法
        Method[] methods1 = c1.getDeclaredMethods();//获取本类的所有方法
        for (Method method : methods1){
            System.out.println(method);
        }

        输出:
        public java.lang.String com.mvpapp.reflection.User.toString()
        public java.lang.String com.mvpapp.reflection.User.getName()
        public void com.mvpapp.reflection.User.setName(java.lang.String)
        public void com.mvpapp.reflection.User.setAge(int)
        public int com.mvpapp.reflection.User.getAge()
  1. 获取指定的方法
        //获取指定方法
        Method get = c1.getMethod("getName",new Class[0]);
        Method set = c1.getMethod("setName", String.class);
        System.out.println(get);
        System.out.println(set);

        输出:
        public java.lang.String com.mvpapp.reflection.User.getName()
        public void com.mvpapp.reflection.User.setName(java.lang.String)
  1. 获取构造器
        //获取构造器
        Constructor[] constructors = c1.getConstructors();//获取public方法
        Constructor[] constructors1 = c1.getDeclaredConstructors();//获取全部方法

        for (Constructor constructor: constructors1){
            System.out.println(constructor);
        }

        输出:
        public com.mvpapp.reflection.User()
        public com.mvpapp.reflection.User(java.lang.String,int)
  1. 获取指定构造器
        Constructor constructor = c1.getDeclaredConstructor(String.class,int.class);
        System.out.println(constructor);

        输出:
        public com.mvpapp.reflection.User(java.lang.String,int)
  1. 通过反射创建对象
        //利用反射创建对象
        //1. 获得构造方法
        Constructor constructor1 = c1.getDeclaredConstructor(String.class,int.class);
        //2.通过构造方法创建对象
        User user1 = (User) constructor1.newInstance("张三",20);
        System.out.println(user1);

        输出:
        User{name='张三', age=20}
  1. 通过反射调用方法
        //通过反射调用方法
        //通过Class获得对象实例
        User user2 = (User) c1.newInstance();
        //获取set方法
        Method method = c1.getMethod("setName",String.class);
        //执行set方法
        method.invoke(user2,"李四");
        System.out.println(user2.getName());
        
        输出:
        李四
  1. 通过反射操作属性
                //通过反射操作属性
        User user3 = (User) c1.newInstance();
        Field name = c1.getDeclaredField("name");
        //因为name属性为private  所以要调用此方法更改可见性
        name.setAccessible(true);
        name.set(user3,"张三");
        System.out.println(user3.getName());
        
        输出:
        张三
  1. 通过反射获取泛型
class Test {
    //参数为map
    public void test1(Map<String,User> map){
    }

    //返回为List
    public List<String> test2(){
        return null;
    }
}

        //通过反射获取泛型
        1. 获取参数的泛型类型
        Class test = Test.class;
        //获取方法
        Method method1 = test.getDeclaredMethod("test1", Map.class);
        //获取泛型的参数类型
        Type[] type = method1.getGenericParameterTypes();
        for (Type t:type){
            System.out.println(t);
            //如果参数类型为参数化类型
            if (t instanceof ParameterizedType){
                //获取参数化类型的参数类型
                Type[] actualTypeArguments = ((ParameterizedType) t).getActualTypeArguments();
                for (Type actualTypeArgument: actualTypeArguments){
                    //打印
                    System.out.println(actualTypeArgument);
                }
            }
        }
        
        输出:
        java.util.Map<java.lang.String, com.mvpapp.reflection.User>
                class java.lang.String
                class com.mvpapp.reflection.User
        
        
        
        2. 获取返回值为泛型的类型
        //获取返回值为泛型
        //获取方法
        Method method2 = test.getDeclaredMethod("test2");
        //获取泛型的参数类型
        Type type2 = method2.getGenericReturnType();
        System.out.println(type2);
        //如果参数类型为参数化类型
        if (type2 instanceof ParameterizedType){
            //获取参数化类型的参数类型
            Type[] actualTypeArguments = ((ParameterizedType) type2).getActualTypeArguments();
            for (Type actualTypeArgument: actualTypeArguments){
                //打印
                System.out.println(actualTypeArgument);
            }
        }
        
        输出:
        java.util.List<java.lang.String>
                class java.lang.String
  1. 通过反射操作注解

在上一篇文章中Java 注解我们自定义了一个注解,这里我们就通过使用这个自定义的注解来进行示范。

自定义注解

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE,ElementType.METHOD})
public @interface MyAnnotation {
    int id() default 0;
    String type();
}

在类中声明注解

@MyAnnotation(id = 100,type = "测试")
class Test {
}

获取注解

        //通过反射获取注解
        Class c = Test.class;
        //获取注解
        Annotation[] annotations = c.getAnnotations();
        for (Annotation annotation:annotations){
            System.out.println(annotation);
        }
        //获取注解的值
        MyAnnotation annotation = (MyAnnotation) c.getAnnotation(MyAnnotation.class);
        System.out.println(annotation.id()+ " "+ annotation.type());
        
        输出:
        @com.mvpapp.annotation.MyAnnotation(id=100, type=测试)
                100 测试


三、总结

通过上面的反射用法,我们可以动态的获取类中的属性,方法以及调用或修改这些方法和属性,可见反射的功能是很强大的。
优点:可以实现动态创建对象和编译,可以提高java程序的灵活性,允许程序创建和控制任何类的对象。
缺点:对性能有影响,反射是一种解释操作,这种操作慢与直接执行相同的操作。
应用:在日常开发中,基本都是在一些复杂的逻辑上动态的获取或操作类中的对象或方法时,但是在开发框架时,比如Retrofit请求框架,就是大量的使用注解和反射的方式实现的。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,684评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,143评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,214评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,788评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,796评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,665评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,027评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,679评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 41,346评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,664评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,766评论 1 331
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,412评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,015评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,974评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,203评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,073评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,501评论 2 343

推荐阅读更多精彩内容

  • 一、概述 1、Java反射机制(Java-Reflect): 在运行状态中,对于任意一个类,都能够知道这个类中的所...
    年少懵懂丶流年梦阅读 4,378评论 0 5
  • ## 引言 ### java中创建对象有几种方式? #### 1.使用new关键字 #### 2.使用clone方...
    芋头888阅读 579评论 1 0
  • 前言 现在在我们构建自己或公司的项目中,或多或少都会依赖几个流行比较屌的第三方库,比如:Butter Knife、...
    戴定康阅读 3,927评论 0 17
  • 能够分析类能力的程序称为反射(reflective)。反射机制的功能非常强大,主要提供了如下功能: 对于任意一个类...
    codersm阅读 275评论 2 1
  • 1.定义 JAVA反射机制是在运行状态中,对于任意一个实体类,都能够知道这个类的所有属性和方法;对于任意一个对象,...
    dancer4code阅读 485评论 0 2