2、反射技术

注解

一、前期概要

1、 什么是反射

Java 反射机制在程序运行时,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性。这种 动态的获取信息 以及 动态调用对象的方法 的功能称为 java 的反射机制

反射中的反的理解:在使用的之前,提前不知道需要使用什么类型的对象。只是在调用的时候,才知道要调用的对象类型。这种反其道而行的就是反射中反的理解。

程序执行分为编译器和运行期,编译时刻加载一个类就称为静态加载类,运行时刻加载类称为动态加载类,

核心思想 让你在写代码的时候可以更加灵活,降低耦合,提高代码的自适应能力。

反射框架提供如下常用的核心功能:

1.在运行时判断任意对象所属的类;

2.在运行时构造任意一个类的对象;

3.在运行时判断任意一个类所具有的成员变量和方法(通过反射甚至可以调用private方法);

4.在运行时调用任意一个对象的方法;

2、反射的主要用途

通用框架,很多框架都是配置化的(比如Spring通过xml配置Bean), 为了保证框架的通用性,可能需要根据不同的配置文件加载不同的对象或者类,调用不同的方法,这个时候就需要反射,运行时动态加载需要加载的对象。

3、缺点

  • 性能不佳 - 由于java反射动态解析类型,因此涉及扫描类路径以查找要加载的类的处理,从而导致性能降低。
  • 安全限制 - Reflection需要运行时权限,这些权限可能不适用于在安全管理器下运行的系统。由于安全管理器,这可能导致应用程序在运行时失败。
  • 安全问题 - 使用反射,我们可以访问我们不应该访问的部分代码,例如,我们可以访问类的私有字段并更改它的值。这可能是严重的安全威胁,并导致您的应用程序出现异常行为。
  • 高维护 - 反射代码很难理解和调试,在编译时也无法找到代码的任何问题,因为这些类可能不可用,使其不太灵活且难以维护。
  1. Class 对象
  2. 类名
  3. 修饰符
  4. 包信息
  5. 父类
  6. 实现的接口
  7. 构造器
  8. 方法
  9. 变量
  10. 注解

二、获得 Class 对象

在运行期间,一个类,只有一个Class对象产生

1、类的静态方法(常用) :

  1. 说明
    获取指定的类完整的路径相关联接口的Class对象。
  2. 方法
    // 掌握
    public static Class<?> forName(String className)
     // 了解
    public static Class<?> forName(String className, boolean initialize,ClassLoader loader)
    
  3. 区别
    第一个默认进行初始化操作,
    第二个可以指定是否进行初始化操作。当initialize=false 不进行初始化操作,即不会执行静态代码块。
  4. 举个栗子
    Class clazz = Class.forName("com.wener.reflect.Xxx")
    // 或者
    Class clazz = Class.forName("com.wener.reflect.Xxx",initialize,this.getClass().getClassLoader)
    

2、使用 .class 静态语法。

  1. 说明
    任何数据类型(包括基本数据类型)都有一个“静态”的class属性
  2. 方法
    Class<?> cls = 类型.class;
    
  3. 举个栗子
    Class<String> cls = String.class;
    System.out.println(cls.toString());
    

3、使用类对象的 getClass()

  1. 说明
    通过对象的实例来返回Class对象
  2. 方法
    Class<?> cls = instance.getClass()
    
  3. 举个栗子
    public class User {
    }
    User user = new User();
    Class<? extends User> clz = user.getClass();
    

4、总结

  1. 常用的是类的静态方法,
  2. getClass()的话一般在继承的时候用的比较多一点,比如Android里的注解框架
  3. .class 静态语法: 需要导入类的包,依赖太强,不导包就抛编译错误

三、创建实例

1.4、注意

  1. cls.newInstance()方法返回的是一个泛型T,我们要强转成自定义类
  2. cls.newInstance()默认返回的是类的无参数构造对象
  3. 被反射机制加载的类必须有无参数构造方法,否者运行会抛出异常

四、属性操作

1、说明

类的成员变量也是一个对象,它是java.lang.reflect.Field的一个对象,所以我们通过java.lang.reflect.Field里面封装的方法来获取这些信息并且操作这些属性

2、获取单个成员字段

  1. 方法
// 通过字段名,返回一个具体的具有public属性的成员变量(包括父类的)
Field getField(String name)
// 通过字段名所有已声明的所有成员变量(私有的 默认的 共有的),但不能得到其父类的成员变量
Field getDeclaredField(String name)
  1. 举个栗子
    public class User {
        private String name;
        public String detail;
    }
    public static void main(String[] args) {
      try {
        Class<?> cls = Class.forName("com.wener.reflect.demo1.User");
        /**
         * 获取类的指定名称公开的属性
         */
        Field detail = cls.getField("detail");
        System.out.println(detail);
        /**
         * 获取类的指定名称的的属性(包括私有的属性)
         */
        Field name = cls.getDeclaredField("name");
        System.out.println(name);
      } catch (ClassNotFoundException | NoSuchFieldException e) {
        e.printStackTrace();
      }
    }
    

3、获取所有成员字段

  1. 方法
    // 获取所有的”公有字段”
    Field[] getFields()
    // 获取所有字段(私有、受保护、默认、公有)
    Field[] getDeclaredFields()
    
  2. 举个栗子
    public class TestReflectUserField {
        public static void main(String[] args) {
            try {
                Class<?> cls = Class.forName("com.wener.reflect.demo1.User");
                Field[] fields = cls.getFields();
                for (Field field : fields) {
                    System.out.println("类型: " + field.getType() + "方法名:  " + field.getName());
                }
                Field[] declaredFields = cls.getDeclaredFields();
                for (Field declaredField : declaredFields) {
                    System.out.println("类型: " + declaredField.getType() + "方法名:  " + declaredField.getName());
                }
            } catch (ClassNotFoundException | NoSuchFieldException | InstantiationException | IllegalAccessException e) {
                e.printStackTrace();
            }
        }
    }
    

4、字段赋值

  1. 方法
    // 将指定对象参数上的此Field对象表示的字段设置为指定的新值
    field.set(Object obj,Object value)
    
  2. 参数说明
    • Object obj: 字段所在的类的实例对象
    • Object value : 新值
  3. 注意
    // 如果要给私有变量赋值必须取消权限的访问控制
     field.setAccessible(true);
    
  4. 举个栗子
        public static void main(String[] args) {
            try {
               // 1 实例化Class对象
                Class<?> cls = Class.forName("com.wener.reflect.demo1.User");
                /**
                 * 获取类的指定名称公有的属性
                 */
                Field detail = cls.getField("detail");
                System.out.println(detail);
                /**
                 * 获取类的所有的属性(包括私有 默认的 公有的)
                 */
                Field name = cls.getDeclaredField("name");
                // 2.创建对象
                Object o = cls.newInstance();
                 // 3 通过字段的set方法设置
                name.set(o, "娇娇");
                System.out.println(o.toString());
            } catch (ClassNotFoundException | NoSuchFieldException | InstantiationException | IllegalAccessException e) {
                e.printStackTrace();
            }
        }
    

五、方法操作

1、获取单个方法

  1. 方法
    // 方法返回一个特定的方法,其中第一个参数为方法名称,后面的参数为方法的参数对应Class的对象
    public Method getMethod(String name, Class<?>... parameterTypes)
    
  2. 举个栗子
    public class User {
        private String name = "木木";
        public String detail = "hello";
        private int age;
        private BigDecimal balance;
        public void increment() {
            this.age++;
            System.out.println(age);
        }
        public BigDecimal getBalance() {
            return balance;
        }
        public void setBalance(BigDecimal balance) {
            this.balance = balance;
        }
        private void say(int num) {
            System.out.println(num + "号技师");
        }
        @Override
        public String toString() {
            return "User{" +
                    "name='" + name + '\'' +
                    ", detail='" + detail + '\'' +
                    '}';
        }
    }
    public class TestReflectUser {
        public static void main(String[] args) {
            reflectMethod();
        }
        public static void reflectMethod() {
            try {
                // 1.实例化class对象
                Class<?> cls = Class.forName("com.wener.reflect.demo1.User");
                // 2.实例化User对象
                Object o = cls.newInstance();
                // 3.获取increment方法
                Method method = cls.getMethod("increment");
                // 有参数无返回值
                Method setBalance = cls.getMethod("setBalance", BigDecimal.class);
                // 有返回值值无参数
                Method methodGet = cls.getMethod("getBalance");
                 // 获取私有的方法
                Method say = cls.getDeclaredMethod("say", int.class);
            } catch (ClassNotFoundException | NoSuchMethodException | InstantiationException | IllegalAccessException | InvocationTargetException e) {
                e.printStackTrace();
            }
        }
    }
    

2、获取所有的方法

  1. 方法
    // 返回类或接口声明的所有方法,包括公共、保护、默认(包)访问和私有方法,但不包括继承的方法。
    public Method[] getDeclaredMethods() throws SecurityException
    
    // 返回某个类的所有公用(public)方法,包括其继承类的公用方法。
    public Method[] getMethods() throws SecurityException
    
  2. 举个栗子
    public class TestReflectUser {
        public static void main(String[] args) {
            reflectMethod();
        }
        public static void reflectMethod() {
            try {
                // 1.实例化class对象
                Class<?> cls = Class.forName("com.wener.reflect.demo1.User");
                // 2.实例化User对象
                Object o = cls.newInstance();
                // 获取所有的共有的方法(包括父类的方法)
                Method[] methods = cls.getMethods();
                for (Method method1 : methods) {
                    System.out.println(method1.getName());
                }
                // 获取所有的方法(包括私有的,共有的,默认的)
                Method[] declaredMethods = cls.getDeclaredMethods();
                for (Method declaredMethod : declaredMethods) {
                    System.out.println(declaredMethod.getName());
                }
            } catch (ClassNotFoundException | NoSuchMethodException | InstantiationException | IllegalAccessException | InvocationTargetException e) {
                e.printStackTrace();
            }
        }
    }
    

3、调用方法

  1. 方法
    Object invoke(Object obj, Object... args)
    
  2. 参数说明
    • obj - 从中调用底层方法的对象,必须是实例化的对象
    • args - 用于方法调用的参数,是个Object数组,因为参数有可能有多个
  3. 返回值
    使用参数 args 在 obj 上指派该对象所表示方法的结果
  4. 举个栗子
    public class User {
        private String name = "木木";
        public String detail = "hello";
        private int age;
        private BigDecimal balance;
        public void increment() {
            this.age++;
            System.out.println(age);
        }
        public BigDecimal getBalance() {
            return balance;
        }
        public void setBalance(BigDecimal balance) {
            this.balance = balance;
        }
        private void say(int num) {
            System.out.println(num + "号技师");
        }
        @Override
        public String toString() {
            return "User{" +
                    "name='" + name + '\'' +
                    ", detail='" + detail + '\'' +
                    '}';
        }
    }
    public class TestReflectUser {
        public static void main(String[] args) {
            reflectMethod();
        }
        public static void reflectMethod() {
            try {
                // 1.实例化class对象
                Class<?> cls = Class.forName("com.wener.reflect.demo1.User");
                // 2.实例化User对象
                Object o = cls.newInstance();
                // 3.获取set方法
                Method method = cls.getMethod("increment");
                // 4 执行方法
                method.invoke(o);
                // 有参数无返回值
                Method setBalance = cls.getMethod("setBalance", BigDecimal.class);
                Object methodSet = setBalance.invoke(o, new BigDecimal(100.00));
                System.out.println(methodSet);
                // 有返回值值无参数
                Method methodGet = cls.getMethod("getBalance");
                Object invoke = methodGet.invoke(o);
                System.out.println(invoke);
                 // 获取私有的方法
                Method say = cls.getDeclaredMethod("say", int.class);
                //  运行时取消访问权限检测机制
                say.setAccessible(true);
                 //  执行方法
                say.invoke(o, 1);
            } catch (ClassNotFoundException | NoSuchMethodException | InstantiationException | IllegalAccessException | InvocationTargetException e) {
                e.printStackTrace();
            }
        }
    }
    

4、其它API

返回值 方法 说明
String getName() 获取方法的名称
int getModifiers() 获取方法的修饰符
Class<?> getReturnType() Type getGenericReturnType 返回方法的返回值类型
Class<?>[] getParameterTypes() Type[] getGenericParameterTypes() 返回方法的参数(列表)
Class<?>[] getExceptionTypes() Type[] getGenericExceptionTypes() 返回方法的异常信息

六、综合案例

1、通过配置文件动态切换

  1. 说明
    反射非常强大,但是学习了之后,会不知道该如何使用,反而觉得还不如直接调用方法来的直接和方便。
    但等我们后面接触到一些框架之后才会有一些感触
  2. 测试类
    package com.wener.reflect.demo2;
    public class ReflectDemo1 {
        public void say() {
            System.out.println("ReflectDemo1");
        }
    }
    public class ReflectDemo2 {
        public void say() {
            System.out.println("ReflectDemo2");
        }
    }
    
  3. 配置文件reflect.properties
    class=ReflectDemo2.
    method=say
    
  4. 测试代码
     public static void main(String[] args) {
            //从spring.txt中获取类名称和方法名称
            File springConfigFile = new File("/Users/zhangwei/work/IdeaProjects/JavaExample/ReflectExample/src/reflect.properties");
            Properties properties = new Properties();
            try {
                properties.load(new FileInputStream(springConfigFile));
                String className = (String) properties.get("class");
                String methodName = (String) properties.get("method");
                //根据类名称获取类对象
                Class cls = Class.forName(className);
                //根据方法名称,获取方法对象
                Method m = cls.getMethod(methodName);
                //根据构造器,实例化出对象
                Object service = cls.newInstance();
                //调用对象的指定方法
                m.invoke(service);
            } catch (IOException | InvocationTargetException | NoSuchMethodException | IllegalAccessException | ClassNotFoundException | InstantiationException e) {
                e.printStackTrace();
            }
        }
    
  5. 优点
    当需要从调用第一个类的方法,切换到调用第二类的方法的时候,不需要修改一行代码
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,590评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 86,808评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,151评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,779评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,773评论 5 367
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,656评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,022评论 3 398
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,678评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 41,038评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,659评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,756评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,411评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,005评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,973评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,203评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,053评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,495评论 2 343

推荐阅读更多精彩内容