Article Directory
1. Introduction to Class Loaders
package com.itheima01.loader;
import org.junit.Test;
import sun.net.spi.nameservice.dns.DNSNameService;
/*
* 0. 类 .java文件 -> .class文件 -> Class对象(内存中)
* 源码 编译后 运行时
* 当.class文件被加载进内存,JVM会在堆中创建一个Class对象
* 1. 类加载器 : 将.class文件加载进内存, 随之产生Class对象,看成输入流(读档)
*
* 2. 三种类加载器:加载不同包下的类,像前面讲的不同的输入流
* 1. 引导类加载器 bootstrap (用c写的)。核心包下的类(rt.jar)
* 2. 扩展类加载器 extension。ext包下
* 3. 应用/系统 类加载器 application。 第三方编写的类
* Class对象.getClassLoader(); // 可以获取加载这个Class对象的类加载器
*
* 补充:
* 1. 三种类加载器存在继承关系的
* 2. 不叫继承 (叫组合 composition,因为引导类加载器用C写的)
* 3. 为什么一般情况下, 一个.class文件的Class对象只会有一个?
* 类加载器: 双亲委派机制(这机制保证一个类只会被一个类加载器加载,双亲:父类的父类)
* 什么情况下, 一个.class文件的Class对象会有多个?
* 多个类加载器去加载同一个.class文件(程序员自定义类加载器,手动操作类加载器去加载)
*/
public class ClassLoaderDemo {
public static void main(String[] args) {
ClassLoader loader1 = ClassLoaderDemo.class.getClassLoader();
System.out.println(loader1); //sun.misc.Launcher$AppClassLoader@18b4aac2 //应用类加载器
//1111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111
Class clazz = DNSNameService.class; // Ext扩展包下
ClassLoader loader2 = clazz.getClassLoader();
System.out.println(loader2); //sun.misc.Launcher$ExtClassLoader@45ee12a7 //扩展类加载器
//11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111
ClassLoader loader3 = String.class.getClassLoader();
System.out.println(loader3); // null ,因为不是用java写的。 //引导类加载器
}
//11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111
@Test
public void method01(){
//三种类加载器的关系:继承关系,组合关系,因为跨语言了
ClassLoader loader1 = ClassLoaderDemo.class.getClassLoader();
System.out.println(loader1);//sun.misc.Launcher$AppClassLoader@18b4aac2 //应用
ClassLoader loader2 = loader1.getParent(); //获取父加载器
System.out.println(loader2);//sun.misc.Launcher$ExtClassLoader@452b3a41 //扩展
ClassLoader loader3 = loader2.getParent();
System.out.println(loader3);//null //引导
}
}
2. Agency rental case
package com.itheima02.proxy;
import java.io.BufferedReader;
import java.io.FileNotFoundException;
import java.io.FileReader;
/*
* 代理: 一个代理对象(中介) 拥有 被代理对象(房东) 的大部分权限, 控制外界(租客)对被代理对象的访问
* 1. 被代理类对象(房东)
* 2. 代理类对象(中介) 。代理对象 控制 外界对被代理类对象的 '直接'访问,找中介间接访问房东
*
* 模型:
* 1. 中介 拥有 房东 的 大部分权限:代理类对象 拥有 被代理类对象的大部分方法
* 解决: 不用继承,用接口
* 2. 中介要控制外界对房东的访问:中介要拥有房东的联系方式, 要有随时找得到房东
* 解决: 将房东作为中介的属性存在
*
* 问题1:
* 我们没有限制对房东的直接访问, 用代理模式意义何在?
* 意义在于, 我们可以在不修改房东类的情况下, 限制访问条件
* 1. 假设 有个类 属于 某个jar包, 这个类改不了的
* 2. 我们又想改这个类的某些方法的访问条件 -> 代理模式
*
* 问题2:
* 继承同样也是实现上述需求, 为什么要用代理模式? 代理模式的扩展性更强
* 继承: 只扩展了对FangDong类的访问限制
* 代理: 扩展了对Fd接口 所有实现类对象的 访问限制(如还代理其他房东)
* BufferedReader : 代理模式(又称装饰设计模式)
* new BufferedReader(new FileReader(a.txt)); //里面new FileReader(a.txt)是房东 //BufferedReader是中介,也可以代理除了 FileReader还有Reader其他实现类对象 (多态)
*/
public class ProxyDemo {
public static void main(String[] args) throws FileNotFoundException {
FangDong fangDong = new FangDong(); //使用的时候: 先创建被代理类对象
ZhongJie zh = new ZhongJie(fangDong); //接着: 创建代理类对象
zh.zufang(999); //钱不够, 房子不租给你 //访问中介,实际上通过中介访问房东
}
}
package com.itheima02.proxy;
class FdSon extends FangDong{
//继承:也没改FangDong类,但只扩展了对FangDong类的访问限制,只能单继承
@Override
public void zufang(int money) {
if(money > 1000){
super.zufang(money);
}
}
}
//11111111111111111111111111111111111111111111111111111111111111111111111111111
public class FangDong implements Fd{
public void zufang(int money){
System.out.println("房东出租房子: " + money);
}
public void maifang(int money){
System.out.println("房东卖掉房子:" + money);
}
public void buyfang(int money){
//这权限不给中介
System.out.println("房东买入房子作为投资 :" + money);
}
}
interface Fd{
//房东类中的方法抽取
void zufang(int money);
void maifang(int money);
}
//11111111111111111111111111111111111111111111111111111111111111111111111111111
class ZhongJie implements Fd{
//fd接口也可以其他类实现,所以ZhongJie类构造里传入其他类相当于多继承
Fd fd; //房东对象 定义并没有实例化向string num
public ZhongJie(Fd fd){
//fd实例化,当我们创建中介对象的时候,必须要指定他所代理的房东对象
this.fd = fd;
}
@Override
public void zufang(int money) {
if(money > 1000){
//租客找中介租房, 当钱>1000, 中介就会帮租客联系房东签合同
fd.zufang(money);
}else{
System.out.println(" 钱不够, 房子不租给你");
}
}
@Override
public void maifang(int money) {
}
}
//111111111111111111111111111111111111111111111111111111111111111111111111111111
class MyProxy implements Fd{
//JVM底层代理类,为了解释proxy.zufang(1001)调用h.invoke 。这行Fd接口已通过Proxy.newProxyInstance方法中第二个参数传入了。
InvocationHandler h;
public MyProxy(InvocationHandler h){
//h是实现类对象,不是接口
this.h = h;
}
@Override
public void zufang(int money) {
//动态变化内容, JVM不能写, 交给程序员如下
// Method method = this.getClass().getMethod("zufang",int.class); //方法的反射,区分zufang还是maifang
// Object[] param = {money};
// h.invoke(this,method,param);
}
@Override
public void maifang(int money) {
// h.invoke();
}
}
3. Dynamic proxy
newProxyInstance (three parameters).zufang() calls invoke (three parameters)
package com.itheima03.dynamic;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.Arrays;
/*
* 动态代理(比上面多一个反射而已): 动态: 代理类不是静态定义,是动态生成的
* 1. 静态定义: 把这个类写死了即上面class ZhongJie
* 2. 动态生成: 这个类(中介类)压根就没写,在运行时由JVM动态生成(肯定涉及反射)。前提: 必须有接口
*/
public class DynamicDemo {
//Dynamic反义词static
public static void main(String[] args) {
FangDong fd = new FangDong(); //先看上房东房子
/*
//没有中介类 //Proxy.newProxyInstance这个方法最终目的: 创建一个代理类对象!!!
1. 动态生成一个类就是代理类
2. 并且创建这个类的对象
* static Object newProxyInstance(ClassLoader loader,
Class<?>[] interfaces,
InvocationHandler h)
1. loader : 类加载器(加载中介类的),这个中介类不存在,需要JVM动态生成
一般是应用类加载器(第三方类)。 直接 被代理类 的类加载器
2. interfaces : 接口的Class对象(让中介类实现的)
就是 被代理类(房东类) 所实现的接口
class对象在生成期间需要一个类加载器(将字节码文件加载生成class对象),这个中介类需要实现接口从而拥有被代 理类方法,所以上面1,2。接下来需要重写接口里方法,所以下面3。
3.InvocationHandler h : 调用处理器 (InvocationHandler是一个接口)
invoke方法可以看成 代理/中介类 对接口所有方法 的重写
*/
ClassLoader loader = fd.getClass().getClassLoader(); //对应上面1
Class<?>[] interfaces = fd.getClass().getInterfaces(); //获取此类实现的所有接口 //对应上面2
// Class<?>[] interfaces = {Fd.class}; //Class<?>[] interfaces = {com.itheima03.dynamic.Fd.class}; //效果同上行 //实现接口后要重写方法,重写不了,因为中介类运行时才会存在,所以需要上面的3。
// System.out.println(loader); //sun..$APPClassLoader.. //应用类加载器
// System.out.println(Arrays.toString(interfaces)); //[interface.com.itheima03.dynamic.Fd] //就是FD接口
InvocationHandler h = new InvocationHandler() {
//h是InvocationHandler接口实现类对象,只有这样的h才能传入Proxy.newProxyInstance这个方法
/*
* 参数:
* 1. proxy : 当前代理类对象(几乎没用,因为下面有Fd proxy)
* 2. method: 代理类对象当前调用的方法
* 3. args: 代理类对象当前调用方法传入的参数列表
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println(method.getName());
System.out.println(Arrays.toString(args));
return null;
}
};
//1111111111111111111111111111111111111111111111111111111111111111111111111111111111111111
//Object obj = Proxy.newProxyInstance(loader, interfaces, h); //如上是Proxy.newProxyInstance的三个参数获取
Fd proxy = (Fd) Proxy.newProxyInstance(loader, interfaces, h); //这行=号后面相当于new MyProxy
//平时MyProxy proxy = new MyProxy(.) ,因为MyProxy是JVM随机命名,所以用Fd(这个Fd是FangDong类下面定义的)
proxy.zufang(1001); //调用h.invoke即本文件上面 ,父类引用h,子类new InvocationHandler()
//proxy.maifang(2001); //接口回调(callback)(接口形式的多态):相当于直接调用本文件上面invoke方法
}
}
package com.itheima03.dynamic;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
/*
* 动态代理 比之 普通代理: 好处在于 不用事先定义类, 代理类在运行时动态生成 (反射)
* 运用场景: 被代理对象(房东) 有 10000万个方法, 你只想修改其中一个
*/
public class Demo02 {
public static void main(String[] args) {
FangDong fd = new FangDong(); //先看上房东房子
ClassLoader loader = fd.getClass().getClassLoader();
Class<?>[] interfaces = fd.getClass().getInterfaces();
InvocationHandler h = new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
String name = method.getName();
if("zufang".equals(name)){
int money = (int) args[0]; // Object[] param = {money};
if(money > 1000){
fd.zufang(money);
}else{
System.out.println("钱不够...");
}
}/*else if("maifang".equals(name)){
}*/else{
method.invoke(fd,args); //中介类.其他方法,走这行,即其他9000个方法依然交给房东fd自己处理
}
return null;
}
};
//1111111111111111111111111111111111111111111111111111111111111111111111111111111
Fd proxy = (Fd) Proxy.newProxyInstance(loader,interfaces,h);
proxy.zufang(888);
// proxy.maifang(3000);
}
}
Station B/Zhihu/WeChat Official Account: Code Farm Programming Record