黑马程序员_java学习日记num16

----------------java培训,android培训,期待与你交流-------------------------


一,默认的类加载器

1,BootStrap  :用于加载jre/lib/rt.jar
ExtClassLoader:用于加载jre/lib/ext/*.jar
AppClassLoader:classpath指定的目录


2,通过反射获得类加载器
ClassLoader loader = ClassLoaderTest1.class.getClassLoader();
while(loader!=null){
System.out.println(loader.getClass().getName());
loader = loader.getParent();
}

3,委托机制

 java有三个类加载器,那么怎么确定哪个类该哪个加载器加载呢!?

  1、首相确定从哪个类开始加载
  首先当前线程的类加载器去加载线程中的第一个类。
  如果类A中引用了类B,Java虚拟机将使用加载类A的类装载器来加载类B。
  还可以直接调用ClassLoader.loadClass()方法来指定某个类加载器去加载某个类。
  2、确定了起始加载类之后,类的加载得遵循一个规则,这个规则就是委托机制。
   每个 ClassLoader 实例都有一个相关的父类加载器。需要查找类或资源时,ClassLoader 实例会在试图亲自查找类或资源之前,将搜索类或资源的任务委托给其父类加载器。虚拟机的内置类加载器(称为 "bootstrap class loader")本身没有父类加载器,但是可以将它用作ClassLoader 实例的父类加载器。然后依次从最父类类加载器开始找,若某个类加载器找到了指定的类则加载工作就已经结束。若是所有祖宗类加载器没有加载到类,回到发起者类加载器,还加载不了,则抛ClassNotFoundException,不会再去找发起者类加载器的子类。这个就是委托机制,委托机制保证了一个.class文件在内存中不会存在第二个,便于管理
 
数组类的 Class 对象不是由类加载器创建的,而是由 Java 运行时根据需要自动创建。数组类的类加载器由Class.getClassLoader()返回,该加载器与其元素类型的类加载器是相同的;如果该元素类型是基本类型,则该数组类没有类加载器。

4,类加载器的过程
类加载器会首先代理给其它类加载器来尝试加载某个类。这就意味着真正完成类的加载工作的类加载器和启动这个加载过程的类加载器,有可能不是同一个。真正完成类的加载工作是通过调用 defineClass来实现的;而启动类的加载过程是通过调用 loadClass来实现的。前者称为一个类的定义加载器(defining loader),后者称为初始加载器(initiating loader)。在 Java 虚拟机判断两个类是否相同的时候,使用的是类的定义加载器。也就是说,哪个类加载器启动类的加载过程并不重要,重要的是最终定义这个类的加载器。两种类加载器的关联之处在于:一个类的定义加载器是它引用的其它类的初始加载器。方法 loadClass()抛出的是 java.lang.ClassNotFoundException异常;方法 defineClass()抛出的是java.lang.NoClassDefFoundError异常。

类加载器在成功加载某个类之后,会把得到的 java.lang.Class类的实例缓存起来。下次再请求加载该类的时候,类加载器会直接使用缓存的类的实例,而不会尝试再次加载。也就是说,对于一个类加载器实例来说,相同全名的类只加载一次,即 loadClass方法不会被重复调用。


5,自定义类加载器

类的加载过程从loadClass发起(初始加载器)而真正完成类的加载是通过调用defindClass(类定义加载器),在 Java 虚拟机判断两个类是否相同的时候,使用的是类的定义加载器。也就是说,哪个类加载器启动类的加载过程并不重要,重要的是最终定义这个类的加载器。

 

自定义类加载器时要继承一个系统类加载器,是为了继承类加载器的一些机制。类加载器开始加载文件是从调用classLoader方法开始,loadClass方法内部会自动的去调用findClass,当找到Class文件时会调用defineClass方法,此时完成类的加载过程。

这里我们只需要复写finddClass方法就可以完成自定义类加载器。总的来说复写findClass就是需要将class文件转换为字节数组传给defineClass,defineClass将字节数组转换为Class实例。

 

一个自定义类加载器的示例
public class FileSystemClassLoader extends ClassLoader { 

    private String rootDir; 

    public FileSystemClassLoader(String rootDir) { 
        this.rootDir = rootDir; 
    } 

    protected Class<?> findClass(String name) throws ClassNotFoundException { //覆盖findclass()方法
        byte[] classData = getClassData(name); 
        if (classData == null) { 
            throw new ClassNotFoundException(); 
        } 
        else { 
            return defineClass(name, classData, 0, classData.length); //调用defineClass()方法
        } 
    } 

    private byte[] getClassData(String className) { //定义一个方法读取字节码文件
        String path = classNameToPath(className); 
        try { 
            InputStream ins = new FileInputStream(path); 
            ByteArrayOutputStream baos = new ByteArrayOutputStream(); 
            int bufferSize = 4096; 
            byte[] buffer = new byte[bufferSize]; 
            int bytesNumRead = 0; 
            while ((bytesNumRead = ins.read(buffer)) != -1) { 
                baos.write(buffer, 0, bytesNumRead); 
            } 
            return baos.toByteArray(); 
        } catch (IOException e) { 
            e.printStackTrace(); 
        } 
        return null; 
    } 

    private String classNameToPath(String className) { 
        return rootDir + File.separatorChar 
                + className.replace('.', File.separatorChar) + ".class"; 
    } 
 }

二、代理

1,代理概述

代理模式是常用的java设计模式,他的特征是代理类与委托类有同样的接口,代理类主要负责为委托类预处理消息、过滤消息、把消息转发给委托类,以及事后处理消息等。代理类与委托类之间通常会存在关联关系,一个代理类的对象与一个委托类的对象关联,代理类的对象本身并不真正实现服务,而是通过调用委托类的对象的相关方法,来提供特定的服务。
按照代理的创建时期,代理类可以分为两种。
静态代理:由程序员创建或特定工具自动生成源代码,再对其编译。在程序运行前,代理类的.class文件就已经存在了。
动态代理:在程序运行时,运用反射机制动态创建而成。

2,动态代理

 

JVM可以在运行期动态生成出类的字节码,这种动态生成的类往往被用作代理类,即动态代理类。生成的动态类必须实现一个或多个接口,所以,生成的动态类只能用作具有相同接口的目标类的代理。如果没有则不能生成动态代理类。lCGLIB库可以动态生成一个类的子类,一个类的子类也可以用作该类的代理,所以,如果要为一个没有实现接口的类生成动态代理类,那么可以使用CGLIB库。

java提供了两个类用于创建动态代理:

InvocationHandler接口 提供了invoke(Object proxy, Method method, Object args)方法,用于调用对应接口中的方法。

Proxy类,用于动态创建委托类的的代理类。newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)。从这个方法中可以看出,每次调用的时候都有复写接口InvocationHandler中的方法。


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值