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类加载器,模板/单例/工厂/代理的主要内容,如果未能解决你的问题,请参考以下文章

java 流程式代码适合啥设计模式

常用设计模式(单例,代理,工厂,观察者)

设计模式

设计模式

Java学习之动态代理

Java中23种设计模式(附代码样例)