如果一个类被多次加载,那么它的static-members会多次初始化吗? 我该如何检查?
答案 0 :(得分:6)
如果涉及不同的类加载器,那么它们将是完全独立的类,具有单独的静态字段等 - 并且每个都将单独初始化。
(最简单的诊断方法就是在初始化时记录,当然......)
static {
// Log initialization
}
答案 1 :(得分:3)
静态块中的代码只执行一次:第一次创建该类的对象或第一次访问该类的静态成员时(即使您从未创建该类的对象)。这意味着当类加载器将类加载到内存时调用它。所以它的每类加载器。如果你有多个类加载器,每个都有自己的类副本,所以每个类加载器都会调用静态块。要测试这个,您可以尝试将Sysout放在静态块中,然后使用自定义类加载器加载它。在下面的示例中,静态块将执行两次。一个是系统类加载器,当我们运行静态main方法,然后是我们的自定义类加载器。
package sample;
import java.io.BufferedInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
public class Sample {
static {
System.out.println("Entered Static Block!!");
}
public static void main(String[] args) {
CustomClassLoader loader = new CustomClassLoader();
try {
Class<?> c = loader.findClass("sample.Sample");
Object o = c.newInstance();
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
}
class CustomClassLoader extends ClassLoader {
private Map<String, Class<?>> classes = new HashMap<String, Class<?>>();
@Override
public String toString() {
return CustomClassLoader.class.getName();
}
@Override
protected Class<?> findClass(String name) throws ClassNotFoundException {
if (classes.containsKey(name)) {
return classes.get(name);
}
byte[] classData;
try {
classData = loadClassData(name);
} catch (IOException e) {
throw new ClassNotFoundException("Class [" + name
+ "] could not be found", e);
}
Class<?> c = defineClass(name, classData, 0, classData.length);
resolveClass(c);
classes.put(name, c);
return c;
}
private byte[] loadClassData(String name) throws IOException {
BufferedInputStream in = new BufferedInputStream(
ClassLoader.getSystemResourceAsStream(name.replace(".", "/")
+ ".class"));
ByteArrayOutputStream out = new ByteArrayOutputStream();
int i;
while ((i = in.read()) != -1) {
out.write(i);
}
in.close();
byte[] classData = out.toByteArray();
out.close();
return classData;
}
}
答案 2 :(得分:1)
以下操作 not 导致加载类:
classLoader.getResource(className.replace('.', '/') + ".class")
以下导致类为loaded(即,解析.class文件,并创建Class<?>
对象):
Foo.class
以下导致类为initialized(即执行静态块):
如果从多个ClassLoader初始化类,则静态块确实会多次执行。例如,以下代码:
import java.net.URL;
import java.net.URLClassLoader;
public class InitializeClassMultipleTimes {
static class Foo {
static {
System.out.format(" %s initialized by %s%n", Foo.class.getSimpleName(), Foo.class.getClassLoader());
}
public static void foo() {}
}
private static Class<Foo> loadClass() {
System.out.println("Loading class.");
// Load the .class file. This will fail if the class file is gone or has
// the wrong file format.
return Foo.class;
}
private static void initializeClass(Class<?> innerClass) {
System.out.println("Initializing class");
try {
Class.forName(innerClass.getName(), true, innerClass.getClassLoader());
} catch (ClassNotFoundException e) {
throw new IllegalStateException(e);
}
}
public static void main(String... argv) throws ClassNotFoundException {
Class<Foo> fooClass = loadClass();
initializeClass(fooClass);
URLClassLoader myClassLoader = ((URLClassLoader) InitializeClassMultipleTimes.class.getClassLoader());
URL[] urls = myClassLoader.getURLs();
for (int i = 0; i < 2; i++) {
URLClassLoader newClassLoader = new URLClassLoader(urls, null); // parent=bootstrap
System.out.format("%nLoading class using another class loader%n", Foo.class.getSimpleName());
Class<?> fooClassAgain = Class.forName(fooClass.getName(), false, newClassLoader);
initializeClass(fooClassAgain);
}
}
}
产生以下输出。请注意,您也可以在strace -f -e file
下运行它以验证何时读取.class文件。
Loading class.
Initializing class
Foo initialized by sun.misc.Launcher$AppClassLoader@73d16e93
Loading class using another class loader
Initializing class
Foo initialized by java.net.URLClassLoader@15db9742
Loading class using another class loader
Initializing class
Foo initialized by java.net.URLClassLoader@7852e922