写在前面
上一部分中,我们对 Java 中的 集合框架和具体的集合类进行了一定的了解(作为 java 中常用的一些数据结构还是是比较重要的)。
在这门课程的最后一章,我们来看一下同样也是很重要的 Java 反射 & 代理
8.2 Java 反射机制(Reflection)
Java 在运行时,需要识别每一个对象的类型。而对于一个对象类型的识别有两种方法:
- RTTI (Run Time Type Identification)
- 反射机制 (Reflection)
RTTI
这里对于 RTTI,作者只是简单介绍了一下。作者给了一个例子,很多时候存储对象的时候,代码中可能只是存的父类(基类),在真正使用时候才会确定类型转换,进行检查(有点向上转型,向下转型的意思)。
比如 数组中存的都是 父类 Shape,而真正使用的时候才是具体的 Circle、Square 类
Java 反射机制的定义
运行状态中,对于任意一个类,都能知道这个类的所有属性和方法;对任意一个对象,都能调用它的任意方法和属性。 这种 动态获取信息 & 动态调用对象方法 的功能叫做 Java语言的反射机制。
有点拗口,总结一下就是,动态获取对象的信息和方法。
反射的实现
要用一个类,就要将字节码 .class 加载到 JVM 中,生成一个 Class 对象(注意,java 中有一个类叫做 Class类),这个 Class 对象就保存了这个类的一切信息(这里叫做“类的Class对象”)。
而反射机制的实现,就是要获取这个 Class 对象, 通过 Class 对象去访问类、对象的数据、方法等。
比如对于一个 AirPods 类,想要获取 AirPods 类的 Class 对象,就要使用
Class ap = Class.ForName("AirPods")
然后,就可以通过这个 AirPods类的 Class 对象,来获取AirPods类的方法、数据等(需要使用 Method类 & Field类)。
8.3 Java的静态代理
代理模式
某些情况下,客户不能直接引用另外一个对象,通过代理对象起到桥梁作用。就跟你买房子的中介差不多。
代理模式中的3中角色
- 真实角色:就是你最终想要通过代理访问的,最终的角色
- 代理角色:就是那个中介。中介内部有对真是角色对象的引用;同时中介提供跟真实角色相同的接口(因为你要代替、模仿真实角色);最后,代理还可以执行一定的附加操作(相当于对真实对象做了一个封装)
- 抽象角色:为真实对象和代理对象提供一个共同的接口,一般是抽象类或者接口(个人理解这个东西,就是真是角色和代理角色之间的纽带)
然后作者给出了一段代码例子,用来说明代理实现的原理。
代码例子
静态代理
代理类 在 程序运行前就已经存在。
- 优点:没有
- 缺点:还是代码静态性的缺点,如果真实对象的方法多了,那么每一种方法都需要代理类来实现;真实对象代码变化时候,代理类也要变化。
8.4 动态代理
代理类 在程序运行时才被创建。
实现动态代理,需要使用 Proxy 类 、InvocationHandler、Method 等类
这里有点复杂,暂时先不看了。
- 优点:接口中声明的方法,都被放到 调用处理器中一个集中的方法来处理 (InnovationHandler.invoke)
- 缺点:只支持 interface 代理(即目标对象实现了接口)。
8.5 JVM 类加载原理
类加载器种类
1. JVM 自带的默认加载器
- 根类加载器:bootstrap,由 C++ 编写,java 程序无法获得
- 扩展类加载器 :Java编写
- 系统类、应用类加载器:Java编写
2. 用户自定义类加载器
ClassLoader 的子类,用户定制类的加载方式。
类的加载方式:
- 本地编译好的 .class 中
- 网络加载:URLClassLoader加载 url 指定的类
- 从文件加载:jar/ zip 等
- java源代码动态编译成为 .class
类的加载步骤
这里和之前JVM中看到的5步,并不冲突
- 加载
- 连接:包括验证、准备、解析
- 类的初始化
加载 Class 的过程
第1步:检查这个 Class 是否载入过
第2步:如果 parent classloader 不存在,到4步
第3步:从 parent classloader 载入,成功到8,失败到5
第4步:请 JVM 从 bootstrap classloader 载入
第5步:寻找 .class 文件(从 跟 classloader 相关的路径中),失败到7
第6步:从文件中加载 Class,到8
第7步:抛出 ClassNotFoundException
第8步:返回 Class
网友评论