java里生成对象有如下两种方式:
1: Object obj = new ClassName(); 直接new一个对象
2: Class clazz = Class.forName(ClassName);
Object obj = clazz.newInstance(); 通过class loader动态装载一个类,然后获取这个类的实例同样是生成对象,
同样是生成对象,
(一) 在编译期间
1: 在编译期间检查classpath, 如果没有类定义,编译没法通过。
2: 在编译期间是不会检查的,不过需要抛出或者自己catch ClassNotFoundException。
运行期间,如果1编译时依赖的类不在classpath中(导致classloader装载失败),此时抛出的异常就是NoClassDefFoundError。而如果2在运行期间需要装载的类不在classpath中,抛出的则是ClassNotFoundException。
(二) 在运行期间
如果 1 编译时依赖的类不在classpath中(导致classloader装载失败),此时 抛出的异常就是 NoClassDefFoundError。
如果 2 在运行期间需要装载的类不在classpath中,抛出的则是 ClassNotFoundException。
ClassNotFoundException
ClassNotFoundException这个错误,比较常见也好理解。
原因:就是找不到指定的class。
常见的场景就是:
1 调用class的forName方法时,找不到指定的类
2 ClassLoader 中的 findSystemClass() 方法时,找不到指定的类
3 ClassLoader 中的 loadClass() 方法时,找不到指定的类
开发者平时会有这样一种使用方法,类似JDBC加载驱动!
1 package test321; 2 3 public class test { 4 public static void main(String[] args) { 5 try { 6 Class.forName("test321.hello"); 7 } catch (ClassNotFoundException e) { 8 e.printStackTrace(); 9 } 10 } 11 }
此时,程序会到当前的目录中寻找指定位置test321.hello这个class。
并且这个类也是可以正常执行的。
但是,我们修改一下加载的类名,这样显然是找不到指定的类的。
1 package test321; 2 3 public class test { 4 public static void main(String[] args) { 5 try { 6 Class.forName("test321.hello1"); 7 } catch (ClassNotFoundException e) { 8 e.printStackTrace(); 9 } 10 } 11 }
此时就会报错!
报错! java.lang.ClassNotFoundException: test321.hello1 at java.net.URLClassLoader$1.run(URLClassLoader.java:366) at java.net.URLClassLoader$1.run(URLClassLoader.java:355) at java.security.AccessController.doPrivileged(Native Method) at java.net.URLClassLoader.findClass(URLClassLoader.java:354) at java.lang.ClassLoader.loadClass(ClassLoader.java:425) at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:308) at java.lang.ClassLoader.loadClass(ClassLoader.java:358) at java.lang.Class.forName0(Native Method) at java.lang.Class.forName(Class.java:190) at test321.test.main(test.java:6)
原因就是找不到指定的string对应的class文件。
NoClassDefFoundError
这个就比较奇葩了,查找其他的资料是说,通过了编译,但是使用的时候,比如new的时候会出错。
通过查找资料,搜集到如下的场景:
1 类依赖的class或者jar不存在
2 类文件存在,但是存在不同的域中
3 大小写问题,javac编译的时候是无视大小的,很有可能你编译出来的class文件就与想要的不一样!这个没有做验证。
针对上面的第二点,做了个关于包名的验证:
另一种情况就是由于你通过了编译,但是这个类是有包名的,因此在编译时需要指定classpath,在使用的时候需要加上包名才可以。
下面做了一个小例子!
在没有包名的情况下,我们看一下正常情况是什么样子的。
1 public class test { 2 public static void main(String[] args) { 3 System.out.println("test"); 4 } 5 }
而如果这个类中包含包名,那么按照上面的方法编译,使用时就会报错!
1 package ccc; 2 3 public class test { 4 public static void main(String[] args) { 5 System.out.println("test"); 6 } 7 }
很明显,报错信息中指出了包的信息!那么怎么办呢?
在编译时,加上【 -d . 】这样可以把当前的目录加入到classpath中。
在使用时,加上包名就可以了!
也就是说,这个含有包名的类,编译时,需要指定classpath的路径,并且使用的时候指定包名全路径,才可以。