Java 的反射机制

本部分主要介绍:Java 的反射机制的原理及使用

先知知识点

  1. Java 的 接口 (interface)

    • 对外提供规则的,表现形式是实现接口,必须实现接口中的抽象方法
  2. Java 的 字节码文件 (.class)

    • Java 的 源文件(.java)通过编译得到字节码文件(.class)

    • Java 的虚拟机(JVM)运行的就是字节码文件,固又称 "运行文件"

  3. Java 的 类加载过程

  4. Java 的 Class 类

    • 此类是对 运行文件(.class) 的描述类
  5. 应用程序配置文件

    • 存储着程序中依据环境变化的 变量

    • 通过 IO 流与程序进行通信

  6. 反射机制,将 Java 的 "一切皆对象" 演绎得淋淋尽致

反射机制介绍

  1. 概念

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

      百度百科

  2. 应用场景

    • 程序如图

      应用场景.jpg
    • 如何扩展应用程序

      1. 实现应用程序对外的接口

      2. 如何可以在 "正在运行的程序" 获取实现接口的类( .class 文件),并对其调用呢?

      3. 应用程序都有配置文件,可将实现接口的类名称写入配置文件

      4. 应用程序实现反射机制,可调用实现接口类

  3. 实例

    • Tomcat 服务程序

      1. 提供了处理请求和应答的一般方式

      2. 而具体的如何处理请求和应答,需要根据实际生产环境决定

      3. 解决方法

        • 对外提供接口(serlet 接口)

        • Tomcat 服务具有配置文件(web.xml 配置文件)

      4. 如何将实现接口的类,应用到 Tomcat 服务中呢?

        • Tomcat 的反射机制 + 配置文件 + 接口 = Tomcat 动态获取实现类的信息

反射 "如何反射" ?

  1. 问题描述

    • 如何反射?如何获取运行文件(.class 文件)中的属性和方法?如何进行获取和赋值、如何进行调用呢?
  2. .class 文件

    • Demo.java 是 Java 的源文件。便于人理解

    • Demo.java + 编译器 = Demo.class

    • Demo.class 是 JVM 运行文件(二进制文件),称之为 Java 的运行文件

    • .class 文件包含的内容

      1. 属性

      2. 构造器

      3. 方法

      4. 等等

  3. Java 一切皆对象

    • Java 对运行文件(.class 文件)也有描述类,此类为 Class 类 。就好比对学生、教师进行抽象一个 Person 类的作用相同

    • Class 类中定义了运行文件(.class)中通用的东西

      1. 定义描述 .class 文件的 属性

      2. 定义描述 .class 文件的 构造函数

      3. 定义描述 .class 文件的 方法

      4. 定义描述 .class 文件的 类名(包名)等……

    • Class 类中也定义了如何获取这些东西的方法

      1. 获取 .class 文件中的属性,如同 Person 的 get 方法
  4. 获取类文件(.class)的 Class 对象的 4 中方法

    • Object 类中的 getClass() 方法,可以获取 Class 对象(对象的运行时类型)

      1. 要明确类(.class 文件) ,为类创建对象才可调用 getClass() 方法,需要使用到此类的 构造函数
    • 任何数据类型都具备静态属性 .class 可以获取 Class 对象

      1. 无需使用此类的构造函数,使用静态属性
    • 通过给定的类(.class)字符串名称,就可以获取 Class 对象。Class 类中的静态forName(String className)

      1. Class clazz = Class.forName(className)

      2. 参数 calssName 需要自定包名

    • 通过类加载器对象的方法的获取 Class 对象(通常用在自定义类加载器对象去加载指定路径下的类)

      1. 获取类加载器

        Class clazzA = A.class;
        ClassLoader loader = clazz.getClassLoader();
        
      2. 在获取某一特定的 Class类的对象

        Class clazzB = loader.loadClass("类全名");
        
      3. 注意类加载器有 4 个层次,使用较高层次的类加载器去加载类(通过类名)会报异常 NullPointerException具体原因不太清除

        Class c1 = int.class;
        ClassLoader loader = c1.getClassLoader(); // 类加载器层次较高
        Class c2 = loader.loadClass("java.lang.String"); // 报异常
        
  5. 刨析 .class 对应的类

    • ReflectDemo.Person person = new ReflectDemo.Person(); // ReflectDemo 为包名

      1. new 时,先根据类的名称,寻找类的字节码文件,并加载进内存

      2. 再根据字节码文件创建 Class 对象

      3. 最后创建字节码文件对应的 Person 类,创建 person 对象

    • Class clazz = Class.forName(ReflectDemo.Person);

      1. 先寻找对应的字节码文件(.class),并加载进内存

      2. 再依据字节码文件创建 Class 对象

      3. 并不会创建字节码文件对应的类对象

    • 如何根据 Class 对象 clazz 创建 Person 类对应的 person 对象呢?

      1. Class 类中的 newInstance() 方法,可以获取 person 对象。这里注意区分一下:编译时类型;运行时类型,使用 newInstance() 获取的对象编译类型为 Object,运行类型为 Person 类型

        • 代码
          Class<?> clazz = Class.forName("Day1.src.ReflectDemo.People");
          
          Object obj = clazz.newInstance(); // 使用 Object 类型接受
          System.out.println(obj.getClass()); // Class 对应的类为 People 类
          
      2. newInstance() 方法,实际是调用 Person 类的空参构造函数

        • 没有空参构造器,则会报 InstantationException 异常

        • 空参构造器私有化,则会报 IlleagalAcessException 异常

      3. 构造方法有参数,怎么创建 Person 对象呢?

        • newInstance() 方法不行!!!

        • 但是可以使用 Class 类中方法获取 Person 的字节码文件( .class)中的属性、方法、构造器啊!!!

    • 使用 Class 类中的方法来获取 Person 构造器,创建 person 对象

      说明:Java 的 java.lang.reflect 包,包含 Constructor、Field、Method 类

      1. 获取构造函数方法

        • getConstructors() ,获取所有公共的构造函数,返回 Constructor (构造器对象)对象数组

        • getDeclareConstructors() ,获取所有构造函数(各种权限的),返回 Constructor 对象数组

        • getConstructor() ,获取公共的构造函数,返回 Constructor 对象

        • getDeclareConstructor() ,获取构造函数(各种权限的),返回 Constructor 对象

      2. 获取指定构造函数

        • Constructor constructor = clazz.getConstructor(String.class, int.class);

        • Object obj = constructor.newInstace(className, age); ,返回 Object 对象

    • 使用 Class 类获取属性

      1. 获取属性的方法

        • getField() ,获取共有属性,返回 Feild 对象

        • getDeclareField() ,获取任何权限的属性,返回 Field 对象

      2. 使用 Field 对象,设置、获取属性值

        • field.get(Object) ,需要明确属性的具体对象

        • 私有属性当然是不可访问的,报 IllegalAccessException 异常。这里还是遵循 Java 的基本权限语法的

      3. 如何获取私有属性

        • Constructor构造器类、Field属性类、Method方法类 的父类 AccessibleObject

        • 它提供了将反射的对象标记为在使用时取消默认 Java 语言访问控制检查的能力。

        • 对于公共成员、默认(打包)访问成员、受保护成员和私有成员,在分别使用 Field、Method 或 Constructor 对象来设置或获取字段、调用方法,或者创建和初始化类的新实例的时候,会执行访问查。

        • 暴力访问 AccessileObject 的 setAccessible(boolean flag) 方法 ,true 为取消权限,再进行访问

          Field field = clazz.getDeclareField("name");
          field.setAccessible(true);
          String name = field.get(Object);
          
      4. 使用 Class 类获取方法

        • 获取方法的方法

          1. getMethod() ,获取共有方法,返回 Method 对象

          2. getDeclareMethod() ,获取任何权限的方法,返回 Method 对象

        • 使用 Method 对象,获取方法,并执行

          1. Method method = clazz.getMethod(方法名, 方法参数) ,方法没有参数方法参数位置为 null ,有参数 String.class

          2. 运行方法 method.invoke(对象,方法参数) 。静态方法不需要对象,对象位置为 null 表示调用静态方法。

        • 私有方法,可使用 暴力访问

      5. 使用 Class 类获取注解信息

        • 使用 Class、Method、Constructor、Field 的 getAnnotation(Class<A> annotationClass) 方法,分别获取对应的注释对象(存在向下转型)
        • 有待补充……
      6. 使用 Class 类获取泛型类的实参类型

        • Type 接口以及子类的的认识

        • 有待补充……

      7. 代码块问题

        • 这里需要理解类的初始化过程,以及 .class 字节码文件格式。在字节码文件中,没有所谓的代码块,所以无法获取代码块的相关信息。

        • 静态代码块会在 clinit 方法中

        • 非静态代码块会在 init 方法中

      8. 使用 Class 类获取其它内容

        • 包名(Package 类)

        • 类名

        • 直接父类

        • 接口数组(实现的接口不止一个)

        • 类的修饰符(Modifier 类,表示程序元素(如类、方法或字段)上的修饰符)。实现方法很有意思。

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

推荐阅读更多精彩内容

  • Java反射机制 1.如何创建Class的实例(重点) 1.1过程:源文件经过编译(javac.exe)以后...
    站在巴黎铁塔阅读 137评论 0 0
  • 一、Java 反射机制概述 1. 反射概述 Reflection (反射)是被视为 动态语言 (动态性是核心,底...
    与乐为乐阅读 125评论 0 0
  • 一、Java反射机制定义 在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够...
    熊本极客阅读 165评论 0 0
  • Java反射 1. 什么是反射(Reflection)? Java的反射机制是在运行状态,对于任意一个类,都能知道...
    真理的羽毛阅读 238评论 0 0
  • java的反射机制原理 一反射机制的概念:指在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法,对于任...
    Stefan02阅读 221评论 0 0