类加载和类的初始化

类加载

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
import java.io.*;

public class FileSystemClassLoader extends ClassLoader {
private String rootDir;

public FileSystemClassLoader(String rootDir) {
this.rootDir = rootDir;
}

protected Class findClass(String name) throws ClassNotFoundException {
byte[] classData = getClassData(name);
if (classData == null) {
throw new ClassNotFoundException();
}
else {
return defineClass(name, classData, 0, classData.length);
}
}

private byte[] getClassData(String className) {
String path = classNameToPath(className);
try {
InputStream ins = new FileInputStream(path);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
int bufferSize = 4096;
byte[] buffer = new byte[bufferSize];
int bytesNumRead = 0;
while ((bytesNumRead = ins.read(buffer)) != -1) {
baos.write(buffer, 0, bytesNumRead);
}
return baos.toByteArray();
} catch (IOException e) {
e.printStackTrace();
}
return null;
}

private String classNameToPath(String className) {
return rootDir + File.separatorChar
+ className.replace('.', File.separatorChar) + ".class";
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
/**
* Created by hero on 18/11/2017
*/
public final class GCTest {

static int t = 2;

static {
System.out.println("static block");
}

public static void fun(){
System.out.println("fun");
}

public static int re(){
int t = 0;
try{
//t = 2;
t = 5 / t;
return t;
}catch (Exception e){
t = 3;
return t;
}finally {
t = 4;
return t;
}
}


public static void main(String[] args) {
}


private static GCTest o = new GCTest();

private GCTest() {
System.out.println("constructor");
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

/**
* @author hero
*/
public class MyClassLoader extends ClassLoader{
@Override
protected Class<?> findClass(String name) throws ClassNotFoundException {

return super.findClass(name);
}

public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException {
FileSystemClassLoader loader = new FileSystemClassLoader("/Users/hero/workspace/pack/src");
Class c = loader.loadClass("GCTest");
//c.newInstance();
Method m = c.getDeclaredMethod("fun");
//m.invoke(null, null);
}

}

GCTest这个类中有静态变量、静态方法块、无参构造函数、静态方法,MyClassLoader是个测试类,用FileSystemClassLoader加载GCTest看一看类加载过程中有没有初始化静态变量、静态方法块。

类的初始化

可以看出,类加载并没有做静态变量、静态方法块的初始化工作。
只有当静态方法fun被调用,GCTest这个类中的静态字段、静态方法块才被赋值和执行。
这样看来,单例模式的实现方式private static final Single s = new Single()就不是利用类的加载机制来确保的,它其实是利用了static关键字。当多个线程同时调用getInstance()时,如果每个线程都发现s没有初始化,虚拟机可能有自己的对类进行初始化的机制来确保只对类的静态属性和静态方法块仅仅执行一次。如果是这样的话,对静态属性的初始化就类似于DoubleIf型单例模式的初始化了:

1
2
3
4
5
6
7
if(s == null){
synchronized(Single.class){
if(s == null){
s = new Single();
}
}
}

类加载机制到底是个什么东西?枚举型的单例模式是不是原理依托于加载机制呢?
枚举型的单例模式确实是依赖于类加载机制,并且是在类加载时进行的初始化,这比其它单例模式的实现高在它无法利用反射生成多个实例。
单例模式

能够对类进行初始化而不用调用这个类的静态方法,Class.forName(),这个经常出现在对数据库驱动的加载上。并且调用多次也只有第一次会打印出初始化信息,相信在多线程下调用也只会有一次初始化,这样就更让我确信虚拟机对类的初始化确实更像上面这段代码。