ClassNotFoundException和NoClassDeFoundError

今天又出现了NoClassDefFoundError这个错误,其实已经出现过很多次了,也知道了一般都是缺少相关Jar包之类的造成的,但是也没进行更深入的了解,今天我们就来稍微的探究一下这个错误到底怎么发生的,他和ClassNotFonudException这一常见异常又有什么区别呢。

我们知道这两个Java类都属于异常,那么我们首先来看看他们是checked exception还是unchecked exception。
我们看看ClassNotFonudException:
ClassNotFonudException->ReflectiveOperationException->Exception
很显然ClassNotFoundException是checkedException。
而NoClassDefFoundError
NoClassDefFoundError->LinkageError->Error
祖先是Error,不用说,是uncheckedException,而且是Error的子孙类,我们知道Error属于JVM等引发的不可挽救的错误,会直接让程序宕了。
我们再看看在Java API文档中是怎么定义他们的

ClassNotFonudException

当应用程序试图使用以下方法通过字符串名加载类时,但是没有找到具有指定名称的类的定义。抛出该异常:

  • Class 类中的 forName 方法。
    使用Classs.forName来加载类时,会进行初始化的工作,所以如果想仅仅是干干净净的加载类的话,最好还是用ClassLoader中的loadClass。当然如果你是使用
Class<?> forName(String name,
                               boolean initialize,
                               ClassLoader loader)

这个方法,且第二个参数为false时(参数见名知义),也不会进行初始化工作。

  • ClassLoader 类中的 findSystemClass 方法。
    此方法通过系统类加载器(参见 getSystemClassLoader())来加载该类
    protected final Class<?> findSystemClass(String name)
        throws ClassNotFoundException
    {
        ClassLoader system = getSystemClassLoader();
        if (system == null) {
            if (!checkName(name))
                throw new ClassNotFoundException(name);
            Class<?> cls = findBootstrapClass(name);
            if (cls == null) {
                throw new ClassNotFoundException(name);
            }
            return cls;
        }
        return system.loadClass(name);
    }

getSystemClassLoader:获取系统类加载器的方法,那么什么是系统加载器呢,是Bootstrap这个祖先类加载器吗?不是的,系统类加载器是系统的入口点所使用的ClassLoader。一般是App ClassLoader。
Boootstrap->extsion->app classLoader,这三级类加载器大家应该都清楚作用吧,不清楚的话得好好补补基础啦。

  • ClassLoader 类中的 loadClass 方法。
    loadClass,很干净的加载类的方法。说到loadClass就有必要说我之前看过的自定义ClassLoader了。我们知道Java字节码文件是很容易被反编译的,我们常见的jd-gui反编译工具,反编译class文件是不是很方便。所以当我们的Java代码有安全性要求的时候,我们可以通过自定义ClassLoader来加密我们的Java代码(虽然一般不会用到)。那么自定义ClassLoader主要需要做些什么呢。我们先看loadClass方法的源码(源码版本均为JDK8)
  public Class<?> loadClass(String name) throws ClassNotFoundException {
        return loadClass(name, false);
    }
   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;
        }
    }

其实如果你仔细看这个类的话,你会发现他给我们传递了很多信息。

  • 首先,他告诉了我们ClassLoader的树形结构,从祖先类加载器开始,依次往下查找是否能够加载该类。
  • 然后, 我们通读代码,发现,如果没有在JVM中的三个类加载成功加载的话,会执行 c = findClass(name);
    这句话很关键,这句代码给了我们重写的空间。
    实际上,我们实现我们自己定义的ClassLoader的话,只需要重写这个方法。然后我们需要在这个方法里调用defineClass方法,因为这个方法的作用是把字节码转化为Class对象(类加载的最终产品就是位于堆上的Class对象)。
    扯了这么多好像还是没有讲出现这个异常的原因,其实简单来说:
    ClassNotFoundException只可能出现在你的应用程序主动的装载类的过程中,可能是使用框架配置的初始化过程中,也有可能是程序中动态调用你认为已经写好的类中,但是很实际情况下,你在尝试调用一个实际上不存在的类,赶紧查一下是名称写错了,还是没有把他配置到正确的配置文件中吧。

扯的有点跑偏,最近喜欢把相关的东西结合在一起讲,搞得很多时候没有重点,很尴尬。好,对于ClassNotFountException的API文档就讲到这里,接下来看NoClassDeFoundError

NoClassDeFoundError

API文档是这样说的:
当 Java 虚拟机或 ClassLoader 实例试图在类的定义中加载(作为通常方法调用的一部分或者作为使用 new 表达式创建的新实例的一部分),但无法找到该类的定义时,抛出此异常。
当前执行的类被编译时,所搜索的类定义存在,但无法再找到该定义。

简单点的理解可以是这样:你如果编译了一个类B,在类A中调用,编译完成以后,你又删除掉B,运行A的时候那么就会出现这个错误(这就是缺少JAR包的情况),还有的情况可能是Classpath路径更改了也会造成这个问题。

发布了143 篇原创文章 · 获赞 17 · 访问量 10万+
展开阅读全文

没有更多推荐了,返回首页

©️2019 CSDN 皮肤主题: 大白 设计师: CSDN官方博客

分享到微信朋友圈

×

扫一扫,手机浏览