文章目录
反射初探
-
反射机制(动态语言的关键)
- 编译时不指定类型,运行时动态获取类型从而进行相应操作(动态性)
- 通过反射动态创建对象
-
对于数据库操作,通常书写一段通用性代码(固定好的模板),且将数据库中表里每一条数据看作类的一个对象
-
该通用性代码作用:获取数据库连接,然后返回一条数据对应的一个对象(如在程序中对应一个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配套使用