JAVA 类加载

在这里插入图片描述

一:类加载

类加载通常需要三个步骤:将class文件加载到内存中,并建立class对象是类加载的最终状态。
1.加载:由类加载器进行加载,jvm提供类加载器,也可以通过继承classLoader实现自定义的类加载器。
2.连接:生成对应的class对象后就进入连接状态把类的二进制数据加载到jre中。
(1)验证:当前字节码文件是否符合虚拟机要求(魔数(用来标记字节码文件的类型),版本号…);
(2)准备:给类变量在方法区中开辟内存,赋类型的初始值;
(3)解析:解析阶段是虚拟机将常量池内的符号引用替换为直接引用的过程。
3.初始化:正常的类初始化,执行顺序。

二:类加载机制

Java 虚拟机一般使用 Java 类的流程:首先将开发者编写的 Java 源代码(.java 文件)编译生成 Java 字节码文件 (.class 文件),然后类加载器会读取字节码文件,并转换成 java.lang.Class 对象。有了该 Class 对象后,Java 虚拟机 可以利用反射方法创建其真正的对象了。 Java 提供的类加载器绝大多数都继承自 ClassLoader 类,它们被用来加载不 同来源的字节码文件。

三:类加载器

class对象的唯一标识:(类名,包名,加载器名)
JDK 提供的三种类加载器,分别是 BootstrapClassLoader(启动类加载器),ExtClassLoader(扩展类加 载器),AppClassLoader(应用类加载器)。每一种类加载器都有其指定的类加载路径。
(1) BootstrapClassLoader(启动类加载器又称为根加载器/引导类加载器)主要加载 JAVA_HOME/jre/lib 里的 jar 包,该目录下的所有 jar 包都是运行 JVM 时所必需的 jar 包。注意:类加载器其实自身也是一个 Java 类,因此, 自身类加载器需要被其他类加载器进行加载后方可使用,显然必须有一个类加载器的顶级父类(也就是 Bootstrap ClassLoader,该类加载器是由 C 语言代码进行开发的)是其他类加载器的父类。关键点在于,如果一个类的类加载器 是 BootstrapClassLoader,那么该类的 getClassLoader()方法返回 null。
(2) ExtClassLoader 主要加载 Java 核心扩展类,即 JAVA_HOME/jre/ext 目录下的 jar 文件。
(3) AppClassLoader 主要加载的是开发者在应用程序中编写的类,即 CLASSPATH 路径下所有的 jar 文件。
这三种类加载器之间的关系:
在这里插入图片描述
双亲委派模型:
在这里插入图片描述
双亲委派模型工作过程:
(1)当前类加载器从自己已经加载的类中查询是否此类已经加载,如果已经加载则返回原来已经加载的类。 (2)如果没有找到,就去委托父类加载器去加载。父类加载器也会采用同样的策略,查看自己已经加载过的 类中是否包含这个类,有就返回,没有就委托其父类去加载,直到委托到启动类加载器为止。因为如果父类加载器 为空了,就代表使用启动类加载器作为父加载器去加载该类。(也就是看到的 String 类加载器为 null)
(3)如果启动类加载器加载失败,就会使用扩展类加载器来尝试加载,继续失败则会使用 AppClassLoader 来 加载,继续失败就会抛出一个异常 ClassNotFoundException。
双亲委派模型的优点:
(1)安全性,避免用户自己编写的类动态替换 Java 的一些核心类。
(2)避免类的重复加载。

四:自定义类加载器

如果用户想加载一个特定目录下的类时,就要用到用户自定义的类加载器。除了启动类加载器之外,所有的类 加载器都是 ClassLoader 的子类。
自定义类加载器MyClassLoader要继承自ClassLoader类,有三个方法较为关键:loadClass 方法、findClass 方法和 defineClass 方法。 。
(1)loadClass:负责以双亲委托方式去加载类 ;
(2)loadClass:负责以双亲委托方式去加载类 ;
(3)defineClass:负责从 class 字节码中加载 Class 对象,然后 Class 对象通过反射机制生成对象。
例:

** 
     * 一、ClassLoader加载类的顺序 
     *  1.调用 findLoadedClass(String) 来检查是否已经加载类。 
     *  2.在父类加载器上调用 loadClass 方法。如果父类加载器为 null,则使用虚拟机的内置类加载器。 
     *  3.调用 findClass(String) 方法查找类。 
     * 二、实现自己的类加载器 
     *  1.获取类的class文件的字节数组 
     *  2.将字节数组转换为Class类的实例 
     * @author lei 2011-9-1 
     */  
    public class ClassLoaderTest {  
        public static void main(String[] args) throws InstantiationException, IllegalAccessException, ClassNotFoundException {  
            //新建一个类加载器  
            MyClassLoader cl = new MyClassLoader("myClassLoader");  
            //加载类,得到Class对象  
            Class<?> clazz = cl.loadClass("classloader.Animal");  
            //得到类的实例  
            Animal animal=(Animal) clazz.newInstance();  
            animal.say();  
        }  
    }  
    class Animal{  
        public void say(){  
            System.out.println("hello world!");  
        }  
    }  
    class MyClassLoader extends ClassLoader {  
        //类加载器的名称  
        private String name;  
        //类存放的路径  
        private String path = "E:\\workspace\\Algorithm\\src";  
        MyClassLoader(String name) {  
            this.name = name;  
        }  
        MyClassLoader(ClassLoader parent, String name) {  
            super(parent);  
            this.name = name;  
        }  
        /** 
         * 重写findClass方法 
         */  
        @Override  
        public Class<?> findClass(String name) {  
            byte[] data = loadClassData(name);  
            return this.defineClass(name, data, 0, data.length);  
        }  
        public byte[] loadClassData(String name) {  
            try {  
                name = name.replace(".", "//");  
                FileInputStream is = new FileInputStream(new File(path + name + ".class"));  
                ByteArrayOutputStream baos = new ByteArrayOutputStream();  
                int b = 0;  
                while ((b = is.read()) != -1) {  
                    baos.write(b);  
                }  
                return baos.toByteArray();  
            } catch (Exception e) {  
                e.printStackTrace();  
            }  
            return null;  
        }  
    }  

自定义类加载器代码实现:

class MyClassLoader extends ClassLoader{
    @Override 
    protected Class<?> findClass(String name) throws ClassNotFoundException {
        // TODO Auto-generated method stub 
         String Dir="D:";//用户自定义路径进行拼接 
         String classPath=Dir+File.separatorChar+name.replace('.', File.separatorChar)+".class";
        File file = new File(classPath);
        byte[] buff = null;
        InputStream in;
        try {
            in = new FileInputStream(file);
            buff = new byte[in.available()];
            in.read(buff);
            in.close();
            Class<?> c = defineClass(name, buff, 0, buff.length);
            return c;
        } catch (FileNotFoundException e) {
            // TODO Auto-generated catch block 
             e.printStackTrace(); 
             } catch (IOException e) { 
            // TODO Auto-generated catch block 
             e.printStackTrace(); 
             } 
             return super.findClass(classPath);
        }
    }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值