黑马程序员_java高新-类加载器及代理

时间:2021-10-08 11:32:27

----------------------android培训java培训、期待与您交流! ----------------------

一. 类加载器

1.类加载器基础知识

类加载器的作用就是获取.class里面的内容等等

java虚拟机中的三个类加载器:BootStrap,ExtClassLoader,AppClassLoader

类加载器也是java类,因为其他是java类的类加载器本身也是要被类加载器加载的,只有BootStrap不是java类,它嵌套在java虚拟机的内核中。

2.类加载器之间的父子关系和管辖范围

3.类加载器的委托机制

黑马程序员_java高新-类加载器及代理

当Java虚拟机要加载一个类时,到底派出哪个类加载器去加载呢?
        首先当前线程的类加载器去加载线程中的第一个类。
       如果类A中引用了类B,Java虚拟机将使用加载类A的类装载器来加载类B。
        还可以直接调用ClassLoader.loadClass()方法来指定某个类加载器去加载某个类。
        每个类加载器加载类时,又先委托给其上级类加载器。
当所有祖宗类加载器没有加载到类,回到发起者类加载器,还加载不了,则抛ClassNotFoundException,不是再去找发起者类加载器的儿子,因为没有getChild方法,即使有,那有多个儿子,找哪一个呢?
        对着类加载器的层次结构图和委托加载原理,解释先前将ClassLoaderTest输出成jre/lib/ext目录下的itcast.jar包中后,运行结果为ExtClassLoader的原因。

加载类的方式:

一、用当前线程的类加载器加载了线程中的第一个类,然后这个类引用到的其他类,那么那个类也用这个线程的这个类加载器进行加载。

二、可以直接调用ClassLoader.loadClass()方法来指定某个类加载器加载类。

三、每个类加载器加载类的时候,会先委托给上级类加载器,会从BootStrap开始查看这个类是否已经加载过,如果父类加载器没有加载过这个类,那么才会顺着继承关系逐级下来进行查看。

可以写一个java.lang.String类通过自己写的类加载器加载,但是这个类加载器就不是依托与虚拟机加载类了。因为如果依托于java虚拟机加载的话,java虚拟机中的类加载机制是委托机制,即最后会从BootStrap开始逐级查找要进行加载的类,如果在父类中先找到了这个类,java虚拟机就会加载这个类,那么我们写的这个类就根本不可能加载到。

数组类的 Class对象不是由类加载器创建的,而是由Java运行时根据需要自动创建。数组类的类加载器由Class.getClassLoader()返回,该加载器与其元素类型的类加载器是相同的;如果该元素类型是基本类型,则该数组类没有类加载器。

4. 自定义类加载器

自定义的类加载器必须继承ClassLoader

模板方法设计模式:loadClass:通过findClass方法按照委托机制查找对应的类,在通过父类加载器检查所请求的类后,如果找到了类,那么就立刻调用defineClass把找到的字节码转换为这个类的实例。如果经过父类的加载器检查所请求的类却没有找到,反而在自定义的类加载器中找到了这个类,那么就会通过自定义的类加载器中复写过的findClass来对类的字节码进行在操作,然后调用defineClass把找到的字节码转换为这个类的实例。

A. loadClass方法,不能覆盖

Class<?> loadClass(String name) 使用指定的类名来加载类。

protected Class<?> loadClass(Stringname,booleanresolve) 使用指定的二进制名称来加载类。

B. findClass方法,需要覆盖这个方法。该方法按照委托机制查找对应的类,在通过父类加载器检查所请求的类后,此方法将被loadClass方法调用

protected Class<?> findClass(Stringname)使用指定的二进制名称查找类。

例子:

package com.base.chapter4;

class Base{
    public static int a = 10;
    public int b = 20;
    static
    {
        System.out.println("Static Init Base " + a);
        //System.out.println("Null Init " + b);
    }
    public Base()
    {
        System.out.println("Init Base " + this.b);
    }
}
class SuperClass extends Base{
//静态变量、静态块执行顺序,按书写先后顺序
    public static int a1 = getSuperStaticNumber();
    public int b1 = getSuperInstanceNumber();
    public SuperClass()
    {
        System.out.println("Init SuperClass" + this.b1);
    }
    static
    {
        System.out.println("Static Init SuperClass" + a1);
    }
    public static int getSuperStaticNumber()
    {
        System.out.println("Static member init");
        return 100;
    }
    public int getSuperInstanceNumber()
    {
        System.out.println("Instance member init");
        return 200;
    }
}
public class Sub extends SuperClass{
    public static int a2 = getStaticNumber();
    public int b2 = getInstanceNumber();
    public Sub()
    {
        System.out.println("Init SubClass " + this.b2);
    }
    public static int getStaticNumber()
    {
        System.out.println("Static member init Sub");
        return 1000;
    }
    public int getInstanceNumber()
    {
        System.out.println("Instance member init Sub");
        return 2000;
    }
    static
    {
        System.out.println("Static Init " + a2);
    }
  
    public static void main(String args[])
    {
        new Sub();
    }
}

结果:

Static Init Base 10
Static member init
Static Init SuperClass100
Static member init Sub
Static Init 1000
Init Base 20
Instance member init
Init SuperClass200
Instance member init Sub
Init SubClass 2000

 

二。代理

AOP(Aspect oriented program) 面向方面的编程AOP的目标就是要使交叉业务模块化,可以采用将切面代码移动到原始方法的周围,这与直接在方法中编程切面代码的运行效果是一样的。

1.proxy类:

构造方法:

protected Proxy(InvocationHandler h)

使用其调用处理程序的指定值从子类(通常为动态代理类)构建新的Proxy实例。

方法:

static Object newProxyInstance(ClassLoaderloader,Class<?>[]interfaces, InvocationHandler h) 返回一个指定接口的代理类实例,该接口可以将方法调用指派到指定的调用处理程序。

static Class<?> getProxyClass(ClassLoaderloader,Class<?>...interfaces) 返回代理类的java.lang.Class对象,并向其提供类加载器和接口数组。

static InvocationHandler getInvocationHandler(Objectproxy)返回指定代理实例的调用处理程序。

static boolean isProxyClass(Class<?> cl) 当且仅当指定的类通过 getProxyClass方法或newProxyInstance方法动态生成为代理类时,返回true

2.InvocationHandler接口

Object invoke(Object proxy, Methodmethod,Object[]args) 在代理实例上处理方法调用并返回结果。这个方法的proxy就是InvocationHandler所属于的代理对象,method就是代理对象调用的方法,args就是代理对象调用的方法里面的参数。在invoke内部,menthod会调用invoke方法作用于目标对象身上。目标对象即target

3.创建动态代理类的方法

Collection接口的动态代理类为例

A.使用Proxy.getProxyClass(ClassLoaderloader,Class<?>...interfaces)方法来获取动态代理类的class,然后在调用这个class的构造方法Proxy(InvocationHandlerh)创建实例。【期间通过反射获取构造方法。】

Collection proxy= (Collection)Proxy.getProxyClass(Collection.class.getClassLoader,Collection.class).getConstrctor(InvocationHandler.class).newInstance(newInvocationHandler(){

public Object invoke(Object target,Methodmethod,ObjectparaListOfMethod[]){}

})

B.使用Proxy.newProxyInstance(ClassLoaderloader,Class<?>[]interfaces, InvocationHandler h) 方法来直接创建动态代理类的实例

Collection proxy= (Collection)Proxy.newProxyInstance(Collection.class.getClassLoader,Collection.class,newInvocationHandler(){

public Object invoke(Object target,Methodmethod,ObjectparaListOfMethod[]){}

})

怎样将目标类传入代理的Invoke方法中?

target和系统代码[即我们要在方法前或后面执行的代码]封装成对象,抽到InvocationHandler外面,之后通过参数的方法传递进去。getProxytarketadvice

InvocationHandler类里面只复写了Object里面的equalstoStringhashCode方法。没有复写getClass方法,所以想要获取代理类代理的目标的类名是需要自己复写getClass方法的。

代理的运作其实还是用到了反射的原理。

C. defineClass方法将一个byte数组转换为Class类的实例。这种新定义的类的实例可以使用Class.newInstance来创建

protected Class<?> defineClass(Stringname,byte[]b, int off, int len) 将一个byte数组转换为Class类的实例。

protected Class<?> defineClass(Stringname,byte[]b, int off, int len, ProtectionDomainprotectionDomain)使用可选的ProtectionDomain将一个byte数组转换为Class类的实例。

protected Class<?> defineClass(Stringname,ByteBufferb, ProtectionDomain protectionDomain) 使用可选的ProtectionDomainByteBuffer转换为Class类的实例。

D. ClassLoader

protected ClassLoader() 创建一个新的类加载器,将该加载器作为父类加载器。

protected ClassLoader(ClassLoader parent) 使用指定的、用于委托操作的父类加载器创建新的类加载器。

---------------------- android培训java培训、期待与您交流! ----------------------

详细请查看:http://edu.csdn.net/heima