Java31类加载器,模板/单例/工厂/代理
Posted 码农编程录
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Java31类加载器,模板/单例/工厂/代理相关的知识,希望对你有一定的参考价值。
文章目录
1.类加载器:.class.getClassLoader(),引扩应,双亲委派
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. 三种类加载器存在继承关系的【不叫继承 (叫组合 composition,因为引导类加载器用C写的)】
* 2. 为什么一般情况下, 一个.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 //应用类加载器
//11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111
Class clazz = DNSNameService.class; // Ext扩展包下
ClassLoader loader2 = clazz.getClassLoader();
System.out.println(loader2); //sun.misc.Launcher$ExtClassLoader@45ee12a7 //扩展类加载器
//111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111
ClassLoader loader3 = String.class.getClassLoader();
System.out.println(loader3); // null ,因为不是用java写的。 //引导类加载器
}
@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.代理租房案例:静态代理,中介可代理实现FD接口的多个房东
装饰者设计模式(IO流,可层层增加功能),迭代器设计模式(集合)。
代理更多的是强调对对象的访问控制
,比如说,访问A对象的查询功能时,访问B对象的更新功能时,访问C对象的删除功能时,都需要判断对象是否登陆,那么我需要将判断用户是否登陆的功能抽提出来,并对A对象、B对象和C对象进行代理,使访问它们时都需要去判断用户是否登陆,简单地说就是将某个控制访问权限应用到多个对象
上。使用的是代理对象(中介)
在自己的构造方法里面new的一个被代理的对象,不是调用者传入的。
而装饰器更多的强调给对象加强功能
,比如说要给只会唱歌的A对象添加跳舞功能,添加说唱功能等,简单地说就是将多个功能附加在一个对象
上。装饰是使用的调用者从外部传入的被装饰对象,调用者只想要你把他给你的对象
装饰(加强)一下。
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);
}
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) {
}
}
//111111111111111111111111111111111111111111111111111111111111111111111111111111111
//为了解释proxy.zufang(1001)调用h.invoke 。下行Fd接口已通过Proxy.newProxyInstance方法中第二个参数传入了。
class MyProxy implements Fd{ //JVM底层动态创建中介类,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.动态代理:newProxyInstance(三参).zufang()调用h中invoke(三参)
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(); //先看上房东房子
/*
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这个方法
/*
* invoke函数参数: 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;
}
};
//1111111111111111111111111111111111111111111111如上是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个方法,走这行,即其他9000个方法依然交给房东fd自己处理
}
return null;
}
};
//1111111111111111111111111111111111111111111111111111111111111111111111111111111
Fd proxy = (Fd) Proxy.newProxyInstance(loader,interfaces,h);
proxy.zufang(888);
// proxy.maifang(3000);
}
}
4.模板:类里有抽象方法必须抽象类
package com.atguigu.test02.abstract_;
// 编写一个类,包含一个方法,可以统计 你执行任意代码的运行时间。
public class TestTemplate {
public static void main(String[] args) {
MyCalTime my = new MyCalTime();
long time = my.getTime();
System.out.println("耗时:" + time + "毫秒");
}}
//111111111111111111111111111111111111111111111111111111111111111111111
abstract class CalTime{
public final long getTime(){//可以计算任意一段代码的运行时间 //这里加final的目的是不希望子类重写,改写我的算法的结构
long start = System.currentTimeMillis(); //(1)获取开始时系统时间
doWork(); //(2)执行xxxx
long end = System.currentTimeMillis(); //(3)获取结束时系统时间
return end - start; //(4)计算时间差
}
protected abstract void doWork(); //protected的目的,希望只是子类中进行访问和重写
}
//111111111111111111111111111111111111111111111111111111111111111111
class MyCalTime extends CalTime{
@Override
protected void doWork() { //重写抽象方法
long sum = 0;
for(int i=1; i<=100000; i++){
sum += i;
}
System.out.println("sum = " + sum);
}
}
5.单例:某个类只能有唯一的一个实例对象,多线程
package com.atguigu.test17;import org.junit.Test;
public class Test17 {
@Test
public void test1(){
SingleEnum s1 = SingleEnum.INSTANCE;
SingleEnum s2 = SingleEnum.INSTANCE;
System.out.println(s1 == s2); //true
}
@Test
public void test2(){
// SingleEnum.test();
//此时我并没有需要用到这个INSTANCE对象,但是它也创建出来SingleEnum对象,单例恶汉式
}
@Test
public void test3(){
SingleClass s1 = SingleClass.INSTANCE;
SingleClass s2 = SingleClass.INSTANCE;
System.out.println(s1==s2); //true,地址一样,只有一个对象。
}
@Test
public void test4(){
Single s1 = Single.getInstance();
Single s2 = Single.getInstance();
System.out.println(s1 == s2); //true
}
@Test
public void test5(){
LazyClass s1 = LazyClass.getInstance(); //getInstance()里必有new
LazyClass s2 = LazyClass.getInstance();
System.out.println(s2 == s1);
}
LazyClass s1;
LazyClass s2;
@Test
public void test6(){
//匿名的内部类,继承Thread类。=后面是子类,然后多态
Thread t1 = new Thread(){
public void run(){
s1 = LazyClass.getInstance();
}
};
Thread t2 = new Thread(){
public void run(){
s2 = LazyClass.getInstance();
}
};
t1.start();
t2.start();
try {
//这里用join的目的是,为了两个子线程都执行完,再执行主线程的System.out.println(s1);
t1.join();
t2.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(s1);
System.out.println(s2);
System.out.println(s1 == s2);
} }
//11111111111111111111饿汉式: 不管我们使用者是否需要这个对象,它都上来先给你创建好这个唯一的对象。
//形式一:对应test1()
enum SingleEnum{
INSTANCE; //单例,枚举只有一个
//public static void test(){ //用这个方法不用上面这个INSTANCE对象,对应test2() }
}
//形式二:对应test3()
class SingleClass{ //1.5之前老版的枚举 //用一个全局的静态的常量,来保存这个唯一的实例对象
public static final SingleClass INSTANCE = new SingleClass();
private SingleClass(){
}}
//形式三:对应test4()
class Single{
//用一个私有的静态的常量,来保存这个唯一的实例对象
private static final Single INSTANCE = new Single();
private Single(){
}
public static Single getInstance(){ //提供一个静态方法,来返回这个常量对象
return INSTANCE;
}}
//111111111111111111懒汉式: 延迟创建对象。当使用者来或者这个对象,要用到对象时,我再创建。
//形式一:对应test6()
class LazyClass{
private static LazyClass instance; //不加final,可以不用new出来 //加final必须new SingleClass()
private LazyClass(){
}
public static LazyClass getInstance(){
if(instanc以上是关于Java31类加载器,模板/单例/工厂/代理的主要内容,如果未能解决你的问题,请参考以下文章