4.3 Java之反射


反射初探

在这里插入图片描述

  • 反射机制(动态语言的关键)

    1. 编译时不指定类型,运行时动态获取类型从而进行相应操作(动态性)
    2. 通过反射动态创建对象
  • 对于数据库操作,通常书写一段通用性代码(固定好的模板),且将数据库中里每一条数据看作类的一个对象

  • 该通用性代码作用:获取数据库连接,然后返回一条数据对应的一个对象(如在程序中对应一个Customer类的对象)

  • 数据库中的对应Java程序中的数据模型(如Customer类)

  • 根据传入参数(表的类型,如Customer),动态创建对应类的对象(若对应为Customer表,则创建Customer对象)

  • 框架底层代码有大量应用反射——》学习反射的必要性

  • 编译时只是到字节码文件中,运行时把字节码文件对应的类加载到内存里(运行时加载,即运行时类) --------解释运行时类的由来


Class clazz = Person.class

对上述代码的理解

  • Person这个运行时类本身的字节码文件对应的类充当Class的实例
  • 任何一个类对于反射的Class对象来说是完全暴露的
  • clazz为栈空间的引用,堆空间实体为Person类本身,从而将该类各结构完全暴露

  • 调用属性操作
    • 首先通过反射(Class对象)获取对应Person类的结构信息(如属性)
    • f1是name属性引用
    • 关注当属性私有公有时不同的调用方法(实例中name共有,age私有)
  • getMethod方法的形参为Class类型,又考虑到display方法的形参为字符串,所以传入字符串类型对应的Class

反射概述


在这里插入图片描述
在这里插入图片描述

Class类

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

  • 返回类对象对应的运行时类(类型为Class)
  • 每个类对应一个字节码文件
  • 运行时加载到缓存区(非过河拆桥)
  • 只加载一次,所以Class类实例称为获取而不是创建(创建是可多次的,获取只创建一次)
  • 怼到类本身位置上
  • 编译异常要处理(如找不到类的属性,方法)

创建Class类的对象方法(4种)

在这里插入图片描述

  • 重点为(1)和(3)

在这里插入图片描述

在这里插入图片描述

  • getName为获取类的路径(如上图中className变量所示)
  • getName方法:以 String 的形式返回此 Class 对象所表示的实体(类、接口、数组类、基本类型或 void)名称

类的加载器

在这里插入图片描述

  • 共三种类加载器
  • 关注这三种类加载器对应的类

在这里插入图片描述

  • 字节码文件通过命令解释运行
  • 首先装载起来,然后校验和解释
  • 加载器:将字节码文件对应结构加载到内存中

类的加载过程

在这里插入图片描述

  • 类的加载包含三步:如上图
  • 类的初始化具体指对静态属性和方法初始化

实例

在这里插入图片描述

在这里插入图片描述

  • 由运行结果知,初始为系统类加载器,获取父类,得扩展类加载器,获取父类,应该得引导类加载器,但该加载器不能获取,返回null
  • 系统类加载器负责自定义的类
  • 无法获取最上面的引导类加载器(核心类库加载自动完成),试图获取则返回null
  • 获取加载器的作用在于显式加载某个类
    在这里插入图片描述
    在这里插入图片描述

应用:加载文件(两种方式)

  • 法一:获取当前包下创建的文件(要求文件在某个包中)
    • getResourceAsStream()方法功能:获取一个文件,即根据地址返回输入流
  • 法二:在当前工程目录下以流的形式获取一个文件,然后使用FileInputStream获取到(要求文件在当前工程目录)
    • pros.load(is):怼到输入流,然后可以读取信息

在这里插入图片描述
在这里插入图片描述

创建运行时类的对象

在这里插入图片描述

  • “==”判断是否指向同一个地址
  • 返回皆为true,说明每个类只加载了一次

实例

在这里插入图片描述

  • Class对象对应加载到内存中的一个类
  • 加载到内存中,则创建了对应的Class对象(以后一直在缓存中,供调用)
  • 不管直接创建还是通过反射,都是调用类结构中的构造器
  • 要求类保留空参构造器,注意权限问题(按照权限修饰符的范围来)

获取类的结构

获取类属性

获取类属性的所有结构

在这里插入图片描述

在这里插入图片描述

  • 父类包括间接父类
  • 一个是只能获取父类及其本身public属性,一个是本类的所有属性

在这里插入图片描述

在这里插入图片描述

  • 看作获取属性里面所有结构的实例
  • 不同修饰符对应不同int值(返回值类型为int),通过toString方法转化
  • default权限对应的字符串为空(int id前空缺)

获取类方法

在这里插入图片描述

在这里插入图片描述

  • 与类的属性用法相类比

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

  • 父类指直接父类及其间接父类
  • 方法的异常修饰指的是throws(只有抛出来的异常才可见),try/catch块不可见
  • 关注代码中异常判断语句(上图中的if语句),有异常才打印throws
  • 异常和参数获取都是数组返回(可能存在多个异常和方法参数)

获取构造器

在这里插入图片描述

在这里插入图片描述


在这里插入图片描述

  • 进一步获取构造器结构,可类比方法
  • 关注:方法签名抛出异常
获取父类及带泛型的父类

在这里插入图片描述

  • 此处父类为直接父类
  • 获取父类采用第一种方法(getSuperclass)是不带泛型的
获取父类泛型(重点关注)

在这里插入图片描述

  • 应用场景:数据库操作程序(获取数据库一条记录,则要创建一个对象,对象带泛型——》获取泛型),参考泛型章节的DAO类
  • Type是个接口,Class实现该接口,所以可以向下转型
  • 若带泛型,可强转(ParameterizedType是Type的子接口)
  • 首先获取带泛型的父类,然后将其强转为子接口,调用该子接口获取泛型的方法,最后强转为Class(Type——>Class)获取其泛型名(索引为0,是因为本例只有一个泛型)
获取实现的接口和所在包

在这里插入图片描述

  • 获取的接口不包括父接口,只是自身类实现的接口
获取注解

在这里插入图片描述
在这里插入图片描述

  • 只有声明为Runtime的注解才能被反射获取

反射调用类对象的属性

在这里插入图片描述
在这里插入图片描述在这里插入图片描述

  • 关注:只能获取public属性(default属性也获取不到)
  • 关注权限修饰符对反射调用类对象属性的影响(declared得到的属性要特别考虑权限问题,保险起见,配套使用)
  • 处理异常(找不到属性)
  • 使用反射,并不会打破封装性
  • 不在一个包中,即使是public属性,也要添加setAccessbile方法

反射调用方法

在这里插入图片描述

在这里插入图片描述在这里插入图片描述

  • 静态方法调用时不需要对象
  • 只能获取声明为public方法
  • invoke方法返回值即实际调用的方法返回值(没有返回值,返回null)

反射调用构造器

在这里插入图片描述
在这里插入图片描述

  • 没有空参——》调用其他构造器
  • 关注int.class(参数为int类型,同时要求传入Class类型参数)
  • int.class与Integer.class辨析
  • int.class = Integer.TYPE !=Integer.class
  • declared和setAccessible配套使用
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值