Lazy loaded image
编程
📕Day21-反射和注解
字数 4009阅读时长 11 分钟
2019-2-1
2025-8-13
type
status
date
slug
summary
tags
category
icon
password

反射的概念

Reflection(反射)是被视为动态语言的关键,反射机制允许程序在执行期借助于Reflection API取得任何类的内部信息,并直接操作任意对象的内部属性及方法。
框架 = 反射 + 注解 + 设计模式

体会反射的动态性

反射机制可以提供的功能

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

相关API

  • java.lang.Class:反射的源头
  • java.lang.reflect.Method
  • java.lang.reflect.Field
  • java.lang.reflect.Constructor
notion image

Class类的理解

  1. 类的加载过程:
  1. Class的实例就对应着一个运行时类。
  1. 加载到内存中的运行时类,会缓存一定的时间。在此时间之内,我们可以通过不同的方式 来获取此运行时类。

类的加载过程

  1. 调用 javac.exe 命名将 .java 编译生成为 .class字节码文件!生成的字节码文件在硬盘里,没有被加载到内存中 在 IDEA里通过 build-->要编译的项目/模块/类
  1. 类在什么情况下会被加载:
    1. 使用类创建对象
    2. 使用反射操作类
    3. 调用类里的静态方法,访问静态属性。但如果访问的是类里的静态成员常量,这个类不会被初始化
    4. 子类初始化之前也会先初始化父类
    5. 运行main方法所在的类也会加载
3. 类是由 类加载器ClassLoader加载到内存中的
notion image

类加载器

类加载器作用是用来加载一个类,不同的类使用的类加载器有区别
  • JAVA内置核心库里的类,由 BootstrapClassLoader 来加载 String.java, System.java等文件编译以后生成的 .class 文件在 Java_HOME/JRE/lib/rt.jar包 JavaDevelopmentKit Java开发工具集 JavaRuntimeEnvironment Java运行环境
💡
运行一个代码,不一定需要 .java 文件,只需要有 .class字节码文件 和 JRE就能启动java程序。BootstrapClassLoader不是由Java语言写的,由C/C++写的,使用 Java代码获取不到
  • JAVA的扩展类库,由 ExtClassLoader 来加载 扩展类库位置: JAVA_HOME/JRE/lib/ext 文件夹下,所有的 .jar包
  • 程序员自己写的 .java文件编译成的 .class文件,由 AppClassLoader 来加载
同时还可以自定义类加载器
notion image

类加载器的作用

notion image

创建类的对象方式

  1. new + 构造器
  1. 看类中是否存在静态方法可以返回一个实例对象
  1. 反射

获取类加载器的方法

  1. 类对象.getClassLoader()
  1. ClassLoader.getSystemClassLoader()也能获取到一个 AppClassLoader

获取类对象的四种方式

  1. 类名.class 获取到类对象
  1. 调用 实例对象.getClass() 方法获取到类对象
  1. Class.forName(String className);
  1. 使用类加载器来加载一个类

用反射读取文件的注意事项

使用类加载器或者类对象读取文件的前提是: 文件必须要被编译到 out文件夹下 创建一个和 src 同级的文件夹 config,怎样将文件夹编译到 out目录里: 右键 config文件夹 --> mark directory as --> 选择 sourcesRoot或者resourcesRoot
  • 使用 类加载器 获取到IO流:(推荐使用) 从当前模块编译后所在的 out文件夹 <项目名>/out/production/Day21
  • 使用 类对象 获取到IO流: 参考使用类对象所在的包

使用反射获取构造方法

  • getConstructors(): 获取到所有被public修饰的构造方法
  • getConstructor(Class<T>... paramTypes): 根据参数的类型获取到指定的被public修饰的构造方法
  • getDeclaredConstructors(): 获取到所有的构造方法,包括被private/protected/缺省修饰的构造方法
  • getDeclaredConstructor(Class<T>...paramTypes): 根据参数的类型获取到指定的构造方法

用反射获取类里声明的方法

  1. getMethods(): 获取到所有被public修饰的方法,包括父类里声明的public方法
  1. getMethod(String methodName,Class<T>...paramTypes):根据方法名和参数获取到指定的方法
  1. getDeclaredMethods(): 获取到本类里所有的方法,包括被private/protected以及缺省修饰的方法
  1. getDeclaredMethod(String methodName,Class<T>...paramTypes)

通过反射获取成员变量

getFields() / getField(String fieldName) getDeclaredFields() / getDeclaredField(String fieldName)

创建运行时类的对象

newInstance():调用此方法,创建对应的运行时类的对象。内部调用了运行时类的空参的构造器。
要想此方法正常的创建运行时类的对象,要求:
  1. 运行时类必须提供空参的构造器
  1. 空参的构造器的访问权限得够。通常,设置为public。
在javabean中要求提供一个public的空参构造器。原因:
  1. 便于通过反射,创建运行时类的对象
  1. 便于子类继承此运行时类时,默认调用super()时,保证父类此构造器

获取运行时类的完整结构

我们可以通过反射,获取对应的运行时类中所有的属性、方法、构造器、父类、接口、父类的泛型、包、注解、异常等

获取运行时类的指定结构

代理模式

使用一个代理将对象包装起来, 然后用该代理对象取代原始对象。任何对原始对象的调用都要通过代理。代理对象决定是否以及何时将方法调用转到原始对象上。
  • 静态代理:
  • 静态代理的缺点
    • 代理类和目标对象的类都是在编译期间确定下来,不利于程序的扩展。
    • 每一个代理类只能为一个接口服务,这样一来程序开发中必然产生过多的代理。

动态代理

动态代理是指客户通过代理类来调用其它对象的方法,并且是在程序运行时根据需要动态创建目标类的代理对象。
  • 动态代理需要解决的两个主要问题
    • 如何根据加载到内存中的被代理类,动态的创建一个代理类及其对象。(通过Proxy.newProxyInstance()实现)
    • 当通过代理类的对象调用方法a时,如何动态的去调用被代理类中的同名方法a。 (通过InvocationHandler接口的实现类及其方法invoke())
 
上一篇
Day20-网络编程(socket通信)
下一篇
Day22-注解