排错:Java的ClassNotFoundException异常

Table of Contents

ClassNotFoundException异常是我们在日常开发中常遇错误之一。

Java库将二进制class文件集于JAR文件中,JVM运行时,CLASSPATH决定了如何寻找JAR文件。所以理解CLASSPATH和JAR是关键。

1 CLASSPATH

JVM如何找到JAR,在启动时就已定好。请看HotSpot源码相关实现:

// 文件:hotspot/src/share/tools/launcher/java.c

if ((s = getenv("CLASSPATH")) == 0) {
  s = ".";
 }
#ifndef JAVA_ARGS
SetClassPath(s); // 感兴趣的请阅读此函数的实现
#endif

2 JAR

实际上JAR只是一个压缩文件,可通过file命令查看:

➜  file clojure-1.8.0.jar
clojure-1.8.0.jar: Zip archive data, at least v1.0 to extract

Java代码编译成class文件后,打包在JAR文件中。JVM启动时根据CLASSPATH决定自身能找到哪些JAR文件。调用一个类时,就依赖它们。

3 类是如何调用的

static jclass
LoadClass(JNIEnv *env, char *name)
{
  char *buf = JLI_MemAlloc(strlen(name) + 1);
  char *s = buf, *t = name, c;
  jclass cls;
  jlong start, end;

  if (_launcher_debug)
    start = CounterGet();

  // 把“.”转换成“/”
  do {
    c = *t++;
    *s++ = (c == '.') ? '/' : c;
  } while (c != '\0');
  // 再根据转换后的路径寻找类
  cls = (*env)->FindClass(env, buf);
  JLI_MemFree(buf);

  if (_launcher_debug) {
    end   = CounterGet();
    printf("%ld micro seconds to load main class\n",
	   (long)(jint)Counter2Micros(end-start));
    printf("----_JAVA_LAUNCHER_DEBUG----\n");
  }

  return cls;
}

大致:假如要寻找“org.shellcodes.Hello”这个类,将其名替换成“org/shellcodes/Hello”,如果没有找到文件org/shellcodes/Hello.class,就触发ClassNotFoundException异常。

4 当出现ClassNotFoundException

检查CLASSPATH。如果是在打包后运行遇上,就用“jar tvf filename.jar”命令,看是否有相关的class文件。比如找不到org.shellcodes.Hello,就看压缩包中是否有org/shellcodes/Hello.class。