一、三种类加载方式
常见的三种类加载方式如下:
TransactionMain transactionMain =
new
TransactionMain();
Class
<?> aClass =
Class.forName
(TransactionMain.class.getName());
ClassLoader.getSystemClassLoader()
.loadClass
(TransactionMain.class.getName());
要回答上面这个问题,我们先来看看一个java.class文件从磁盘被加载到
内存
要经历那些步骤吧
1. 加载阶段就是将class文件从磁盘加载到内存中,并生成该class文件对应的class对象
2. 连接阶段包含三个小步骤:
- 验证: 确保Class文件的字节流中包含的信息符合Java虚拟机规范,进行文件格式,元数据,字节码和符号引用验证等
- 准备: 为类中的静态变量分配内存并设置为当前数据类型的零值,例如: static int a=10 ,但是这里只会赋予零值,即a=0
- 解析: 将常量池内的符号引用替换为直接引用
3. 初始化: 执行类构造器中的代码,这里类构造器client方法是编译器自动收集类中的所有静态变量的赋值动作和静态语句块中的语句合并而成的,在这里a会被赋值为10。
二、区别
- new关键字和Class.forName使用的类加载器是相同的,都是当前类加载器,即应用程序上下文加载器
- classLoader.loadClass()方法是由用户指定类加载器进行加载,如果需要在当前类路径以查询并加载类,只能采用该方式
还有一点: 前一种方式是静态加载,而后两种方式是动态加载
-
静态加载:
编译时加载相关的类,如果没有则报错,依赖性太强。
静态加载的时候如果在运行环境中找不到要初始化的类,抛出的是NoClassDefFoundError,它在JAVA的异常体系中是一个Error;
-
动态加载:
运行时加载需要的类,如果运行时不用该类,即使该类不存在,也不会报错,降低了依赖性。
动态态加载的时候如果在运行环境中找不到要初始化的类,抛出的是ClassNotFoundException,它在JAVA的异常体系中是一个checked异常。
举例:
package com.ftn.reflection.classload_;
import java.lang.reflect.Method;
import java.util.Scanner;
//类的动态加载与静态加载
public class ClassLoad01 {
public static void main(String[] args) throws Exception {
Scanner scanner = new Scanner(System.in);
if (scanner.hasNext()) {
String s = scanner.nextLine();
System.out.println(s);
switch (s) {
case “1”:
//
new()
使用的是
静态加载
,
当 编译 时没有发现 Dog类存在时,则会报错
,依赖性强
Dog dog =
new
Dog();
dog.cry();
break;
case “2”:
//而通过
反射机制
加载类是
动态加载
,
如果 运行 时没有使用到该类,
即使该类不存在,也不会报错
,降低了依赖性
Class<?> cls =
Class.forName
(“Person”);
Object o = cls.newInstance();
Method hi = cls.getMethod(“hi”);
hi.invoke(o);
break;
default:
System.out.println(“do nothing”);
}
}
}
}
class Dog{
public void cry(){}
}
从JVM的角度看,我们使用关键字new创建一个类的时候,这个类可以没有被加载。但是使用Class对象的newInstance()方法的时候,就必须保证:1.这个类已经加载;2.这个类已经连接了。
new关键字和newInstance()方法的区别:
newInstance:
弱类型,低效率,
只能调用无参构造
;
new:
强类型,相对高效,
能调用任何Public构造
。
三、Class.forName与ClassLoader.loadClass区别
Class的装载包括3个步骤:加载(loading),连接(link),初始化(initialize)。上面也已经讲过一遍了。
Class.forName(className)
实际上是调用
Class.forName(className,
true
, this.getClass().getClassLoader())
。
第二个参数,是指Class被loading后是不是必须
被初始化
。
ClassLoader.loadClass(className)
实际上调用的是
ClassLoader.loadClass(name,
false
)
。
第二个参数指Class是
否被link
。
Class.forName(className)装载的class
已经被初始化
,
而ClassLoader.loadClass(className)装载的class
还没有被link
。
例子:
一般情况下,这两个方法效果一样,都能装载Class。但如果程序依赖于Class是否被初始化,就必须用Class.forName(name)了。
例如,在JDBC编程中,常看到这样的用法:
Class.forName(“com.mysql.jdbc.Driver”)
如果换成了下面这样就不行了
getClass().getClassLoader().loadClass(“com.mysql.jdbc.Driver”)
因为Driver的静态代码块中需要在初始化的时候被执行,这样才可以向DriverManager去注册自己
static {
try {
DriverManager.registerDriver(new Driver());
} catch (SQLException var1) {
throw new RuntimeException("Can't register driver!");
}
}
转载自,原文有一处写错了(静态加载与动态加载那里),本文已修改:
三种类加载姿势_大忽悠爱忽悠的博客-CSDN博客_classloader加载类三种方式
另外参考: