If a class is loaded multiple times, are its static members initialized multiple times?

If a class is loaded multiple times, are its static members initialized multiple times? How can I check this?

+3


source to share


3 answers


If different classloaders are involved, they will be completely separate classes, with separate static fields, etc. - and each will be initialized separately.

(The easiest way to diagnose this is to just register when you initialize, of course ...)



static {
    // Log initialization
}

      

+5


source


The code inside a static block is executed only once: the first time you create an object of this class, or the first time you access a static member of this class (even if you never make an object of this class). This means that it is called when the classloader loads the class into memory. Thus, for each loader class. If you have multiple classloaders, each will have its own copy of the classes, so static blocks will be called by each classloader. To test this, you can install Sysout in a static block and load it using a custom classloader. The example below will execute the static block twice. One is the system class loader when we run the static main method and then our custom class loader.



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;
    }
}

      

+3


source


The following actions do not cause the class to be loaded:

  • By referring to a static final primitive field that is known at compile time.
  • classLoader.getResource(className.replace('.', '/') + ".class")

Below is the class that will be loaded (i.e. parsed the .class file as well Class<?>

):

  • Any line of code that refers to a class symbolically, eg. Foo.class

  • Class.forName (String, false, ClassLoader)
  • ClassLoader.loadClass (String)
  • Loading a subclass or array of a class, or initializing a class whose code or method signatures refer to the class.

Below is the class initialized (i.e. static blocks are executed):

  • Building a new instance
  • Calling a static method
  • Get or set a static field that is not a compile-time constant.
  • Class.forName (String, true, ClassLoader) and Class.forName (String)

If you initialize a class with multiple ClassLoaders, the static blocks are executed multiple times. For example, the following code:

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);
        }
    }
}

      

outputs the following output. Note that you can also run it under strace -f -e file

to check that the .class files are read.

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

      

+1


source







All Articles