初识java类的加载机制

为什么要设计双亲委派?

答:1.避免类被重复加载,当父类加载过了就没必要让父类再加载一遍了,保证了类加载的唯一性

2.沙箱安全机制,即自己写的String类等与java中的类名重名的类不会被加载,防止了核心API库被随意篡改

public class String{ 
    public static void main(String[] args) {
        System.out.println("你好");
    }
}

这是会报错的:

原因是在这个类加载的时候,app没有加载过问ext,ext也没有加载过问boo,boo也没有加载过去库中找,在它的库中找到了,但是并没有main,所有抛异常,即便是我们自己定义一个类加载器,取消掉双亲委派(将ClassLoader.java中的

protected Class<?> loadClass(String name, boolean resolve)
    throws ClassNotFoundException
{
    synchronized (getClassLoadingLock(name)) {
        // First, check if the class has already been loaded
        Class<?> c = findLoadedClass(name);
        if (c == null) {
            long t0 = System.nanoTime();
            try {
                if (parent != null) {
                    c = parent.loadClass(name, false);
                } else {
                    c = findBootstrapClassOrNull(name);
                }
            } catch (ClassNotFoundException e) {
                // ClassNotFoundException thrown if class not found
                // from the non-null parent class loader
            }

            if (c == null) {
                // If still not found, then invoke findClass in order
                // to find the class.
                long t1 = System.nanoTime();
                c = findClass(name);

                // this is the defining class loader; record the stats
                sun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0);
                sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);
                sun.misc.PerfCounter.getFindClasses().increment();
            }
        }
        if (resolve) {
            resolveClass(c);
        }
        return c;
    }
}

重写) 重写过程为将try catch代码删掉,就是不让它向上找了,此时任然报错,原因还是沙箱安全机制,那把String换成其他类可以吗(比如自己写一个User类)?答:仍然不可以,原因是这个类肯定是object的子类,由于子类就会用super去调用object的构造方法,又是沙箱是的不能加载自己写的类,解决办法就是,做一个判断,java中的类你自己加载,自定义的类,交由app来加载(但是我们重写的String,仍然不可以)

3.双亲委派防止了顶层(Bootstrap Class Loader)类加载器中的类不会被底层(Application Class Loader)类加载器中的类所覆盖(比如在Bootstrap Class Loader中定义了object这个类,如果没有双亲委派机制,底层的类加载器将顶层的类加载器覆盖的话,则底层就无法应用这个类)

关于Bootstrap ClassLoader/ExtClassLoader/APPClassLoader的几点理解:

1.  用户自定义的类是由 应用(系统)类加载器AppClassLoader加载

2.  在”父亲委托机制”中,扩展类加载器ExtClassLoader是AppClassLoader的父亲,并不是继承关系,而是ExtClassLoader加载了AppClassLoader

3.  AppClassLoader 和 ExtClassLoader 都继承于 URLClassLoader加载器.

4. Bootstrap ClassLoader是用C/C++语言写的,这就是在我们调用时打印为空的原因

我们自己写类加载时怎么去调用启动类/扩展类/应用类加载器

ClassLoader systemClassLoader = ClassLoader.getSystemClassLoader();//用于获得应用类加载器
ClassLoader extClassLoader = systemClassLoader.getParent();//用于获得扩展类加载器

ClassLoader bootstrapClassLoader = extClassLoader.getParent();//用于获得启动类加载器(根类加载器) -- 加载基础类库

java中的4中类加载器:

  • BootstrapClassLoader 最顶层的加载类,主要加载核心类库(c++实现)
  • ExtentionClassLoader 扩展的类加载器
  • AppClassLoader也称为SystemAppClass 加载当前应用的classpath的所有类。
  • 自定义类加载器。

什么是沙箱?(沙箱安全机制)

Java安全模型的核心就是Java沙箱(sandbox),什么是沙箱?沙箱是一个限制程序运行的环境。沙箱机制就是将 Java 代码限定在虚拟机(JVM)特定的运行范围中,并且严格限制代码对本地系统资源访问,通过这样的措施来保证对代码的有效隔离,防止对本地系统造成破坏。沙箱主要限制系统资源访问,那系统资源包括什么?——CPU、内存、文件系统、网络。不同级别的沙箱对这些资源访问的限制也可以不一样。

什么是双亲委派机制?

  AppClassLoader加载到类后,先检查自己有没有加载过,如果加载过直接返回,如果没有加载过就去问它的父加载器ExtClassLoader(并不是其父类),ExtClassLoader就会检查自己有没有加载过,如果有加载过就直接返回,没有加载过就去问它的父加载器Bootstrap ClassLoader(并不是其父加载器,它是C++语言写的),Bootstrap ClassLoader就会检查自己有没有加载过,加载过就返回,没有加载过就去核心类库(rt.jar/charsets.jar等)中找,没有的话就派ExtClassLoader去jre/lib/ext目录中的JAR类包中找,没有的话就加载ClassPath路径类路径下的class字节码文件,主要加载你自己写的类,这样就将java文件加载到了虚拟机(整个过程就是问上级,上级查看自己有没有,有就返回,没有就问它的上级,它的上级查看自己有没有加载过,有就返回,没有就去库里找,找不到让下级去库里找,找不到就让下级加载)

一些代码演示:

上面的测试中没有打破双亲委派机制(即加载类的时候还是会向上找,那这个例子就是来打破双亲委派机制的),打破的方法就是在main方法中复制ClassLoader的代码如下:

protected Class<?> loadClass(String name, boolean resolve)

        throws ClassNotFoundException

    {

        synchronized (getClassLoadingLock(name)) {

            // First, check if the class has already been loaded

            Class<?> c = findLoadedClass(name);

            if (c == null) {

                long t0 = System.nanoTime();

                try {

                    if (parent != null) {

                        c = parent.loadClass(name, false);

                    } else {

                        c = findBootstrapClassOrNull(name);

                    }

                } catch (ClassNotFoundException e) {

                    // ClassNotFoundException thrown if class not found

                    // from the non-null parent class loader

                }//但是要删掉try{}catch{}中的代码

                if (c == null) {

                    // If still not found, then invoke findClass in order

                    // to find the class.

                    long t1 = System.nanoTime();

                    //c = findClass(name);

                    // this is the defining class loader; record the stats

                    sun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0);

                    sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);

                    sun.misc.PerfCounter.getFindClasses().increment();

                }

            }

            if (resolve) {

                resolveClass(c);

            }

            return c;

        }

}

将这段代码复制到main方法之前就可以实现打破双亲委派机制了(但是加载不成功原因是我们用的是java.lang.String,由于java的沙箱安全机制,是进制访问的,所以实验不成功,因为java.lang是java内部的包,怎么可能让别人随便访问呢)(再问:现在已经不是双亲委派就不会往上找了,那不就应该找当前路径下的要加载的那个类吗,虽然只是部分重名,但是它们所处的路径是不同的,当前是D:test下的文件而,java中的java.lang肯定不在这里,所以当前这个类被加载的时候不应该被扫到而报异常啊,原因是我们这里的路径并不是D:test/java/lang/ String而是,单独定义的并不是与D:test混在一起定义的,在找String的时候并不是按照这个路径找的而是按照这个路径找的,所以它就直接去找java中的java.lang.String了)即便这里不是java中的类比如自定义一个User类,它也是加载不到的原因是它这里有一个构造方法中的super(),是先加载父类的构造方法,而顶层父类时object,当然又是java的类了,所以仍然打破不了,那应该怎么办呢?在加载的时候去判断一下就可以了如果是java内部的类让它自己加载,我们的类用我们自己定义的类去加载即可

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值