黑马程序员-类的加载

来源:互联网 发布:新闻知乎 编辑:程序博客网 时间:2024/06/05 13:58
---------------------- ASP.Net+Android+IOS开发</a>、.Net培训、期待与您交流! ----------------------


Class类

Class类用于描述所有的Java类,每个类被加载器加载后都会生成一个相应的Class对象(也称为字节码)

获取字节码的三种方式
1)使用Class类的forName(String className)静态方法    //Class.forName("java.lang.String"),该方法会抛出一个ClassNotFoundException异常
2)调用某个类的class属性来获取该类对应的Class对象    //Person.class,将会返回Person类对应的字节码
3)调用某个对象的getClass()方法    //p.getClass(),返回p对象的字节码

/**获取String的字节码*/ class ClassDemo{    public static void main(String[] args) throws ClassNotFoundException    {        String str = "abc";        Class clazz1 = str.getClass();        Class clazz2 = String.class;        Class clazz3 = Class.forName("java.lang.String");         //因为是同一个字节码,所以打印两个true        System.out.println(clazz1 == clazz2);        System.out.println(clazz2 == clazz3);         System.out.println(clazz1.isPrimitive());    //false        System.out.println(int.class.isPrimitive());    //true        System.out.println(int.class == Integer.class);    //false        //Integer.TYPE表示的是int类的字节码        System.out.println(int.class == Integer.TYPE);    //true        System.out.println(int[].class.isArray());    //true    }}
Method:
String getName()    //获取类名
Package getPackage()    //获取包对象

Field getField(String name)    //返回此Class对象所表示的类的指定的public属性(Field)
Field[] getFields()    //返回此Class对象所表示的类的所有public属性(Field)
Field getDeclaredField(String name)    //返回此Class对象所表示的类的指定属性(Field),与属性的访问级别无关

Method getMethod(String name, Class<?>... parameterTypes)     //返回此Class对象所表示的类的指定public方法
Method getMethods()     //返回此Class对象所表示的类的所有public方法
Method getDeclaredMethod(String name, Class<?>... parameterTypes)     //返回此Class对象所表示的类的指定方法,与方法的访问级别无关

Constructor<T> getConstructor(Class<?>... parameterTypes)    //返回此Class对象所表示的类的指定的public构造器


类加载器

类加载器负责将.class文件(可能在磁盘上,也可能在网络上)加载到内存中,并为之生成对应的java.lang.Class对象

一旦一个类被载入JVM中,同一个类就不会被再次载入了

那怎样算同一个类呢?
在Java中,一个类用其全限定类名(binary name)作为标识
但在JVM中,一个类用其全限定类名和其类加载器作为其唯一标识

JVM的三个类加载器:

Bootstrap(根类加载器)非常特殊,它并不是java.lang.ClassLoader的子类,而是由JVM自身实现的(c++)

Extension ClassLoader(扩展类加载器),它负责加载jre的扩展目录(JAVA_HOME/jre/lib/ext或者由java.ext.dirs系统属性指定的目录)中的jar包

通过这种方式,我们就可以为Java扩展核心类以外的新功能

System ClassLoader(系统类加载器),它负责在JVM启动时,加载来自命令java中的-classpath选项或java.class.path系统属性,或CLASSPATH环境变量所指定的JAR包和类路径


类加载器的委托机制

JVM的类加载机制有如下三种:
1)全盘负责:当一个类加载器负责加载某个Class的时候,该Class所依赖的和引用的其他Class也将由该类加载器负责载入,除非显示使用另外一个类加载器来载入
2)父类委托:先让parent(父)类加载器试图加载该Class,只有在父类加载器无法加载该类时才尝试从自己的类路径中加载该类
3)缓存机制:缓存机制将保证所有被加载过的Class都会被缓存。当程序中需要使用某个Class时,类加载器先从缓存中搜寻该Class,只有当缓存中不存在该Class对象时,系统才会重读取该类对应的二进制数据,并将其转换成Class对象,并存入cache

注意:
类加载器之间的父子关系并不是类继承上的父子关系,这里的父子关系是类加载器实例之间的关系


自定义类加载器

JVM中除BootStrap之外的所有类加载器都是ClassLoader的子类实例。可以通过继承ClassLoader并重写findClass(String)方法来实现自定义类加载器

ClassLoader类

Method:
static ClassLoader getSystemClassLoader()    //静态方法,用于返回系统类加载器
ClassLoader getParent()    //获取该类加载器的父类加载器

protected Class<?> loadClass(String binaryName)    //该方法为ClassLoader的入口点,根据指定的binary name来加载类,返回对应的Class对象
protected Class<?> findLoadedClass(String binaryName)    //如果此Java虚拟机已经装载了名为name的类,则直接返回对应的Class实例,否则,返回null。该方法是Java类加载里的缓存机制的体现
protected Class<?> findClass(String binaryName)    //根据二进制名称来查找类
protected final Class<?> defineClass(String binaryName, byte[] b, int off, int len)    //从字节数组b中读取数据,指定开始位置和长度,返回一个Class实例。defineClass管理JVM的许多复杂的实现,它负责将字节码分析成运行时数据结构,并校验有效性等


自定义类加载器如何加载类:

使用自定义类加载器继承ClassLoader,通过入口方法loadClass(String binaryName)加载类,loadClass(String binaryName)方法的执行步骤如下:
使用findLoadedClass(String binaryName)方法来检查是否已经加载类,如果已存在则返回
调用父类加载器的loadClass(String binaryName)方法
调用findClass(String binaryName)方法查找类

import java.io.*;/**自定义类加载器MyClassLoader,重写findClass方法。调用自定义类加载器的loadClass方法的执行流程如下:1. 查看缓存中是否存在2. 调用父类加载器的loadClass方法3. 如果以上两步骤都没有加载类,最后才执行重写的findClass方法MyClassLoader类加载器在加载类的过程中,会对类解密,同时可以独立使用加密、解密功能(main方法)*/class MyClassLoader extends ClassLoader{    //MyClassLoader类加载器搜索class文件的路径只能是"r:/myclassPath/"    private String classPath = "r:/myClassPath/";    @Override    protected Class<?> findClass(String name)    {        Class clazz = null;        String fullName = classPath + name + ".class";        try        {            byte[] arr = getBytes(fullName);            clazz = defineClass(name, arr, 0, arr.length);        }        catch(Exception e)        {            e.printStackTrace();        }        return clazz;    }    //把class文件存入字节数组,存入的过程中对字节加密    private static byte[] getBytes(String fullName) throws Exception    {        FileInputStream fis = new FileInputStream(fullName);        ByteArrayOutputStream baos = new ByteArrayOutputStream();        int buf = 0;        while((buf = fis.read()) != -1)        {            //解密(或者加密)            baos.write(buf ^ 0xFF);        }        fis.close();        return baos.toByteArray();    }    //可以独立的使用加密、解密功能    public static void main(String[] args) throws Exception    {        //args[0]表示需要加密的文件        String srcFullName = args[0];        //加密后的文件存放路径        String destPath = args[1];        //加密后文件的全路径(包括文件名)        String destFullName = destPath + File.separator + srcFullName.substring(srcFullName.lastIndexOf(File.separator) + 1);        byte[] arr = getBytes(srcFullName);        FileOutputStream fos = new FileOutputStream(destFullName);        for(int i = 0; i < arr.length; i++)        {            fos.write(arr[i]);        }        fos.close();    }}
/**Hello类一定要public修饰*/public class Hello{    public static void show()    {        System.out.println("hello");    }}
import java.lang.reflect.Method;/**测试自定义类加载器因为MyClassLoader加载器的加载路径是"r:/myClassPath/",所以提供加密过的Hello.class文件到此路径下在使用MyClassLoader加载器加载类的过程中,会进行解密*/class MyClassLoaderTest{    public static void main(String[] args) throws Exception    {            Class clazz = new MyClassLoader().loadClass("Hello");        System.out.println("类加载器:" + clazz.getClassLoader());        Method showMethod = clazz.getMethod("show");        showMethod.invoke(null);    }}




---------------------- ASP.Net+Android+IOS开发</a>、.Net培训、期待与您交流! ----------------------