Подтвердить что ты не робот

Несколько экземпляров статических переменных

Я экспериментирую с использованием разных загрузчиков классов для загрузки определенного класса и вижу, могут ли статические переменные в этом классе иметь разные экземпляры.

В принципе, я пытаюсь написать код для того, что Стивен C упомянул в этом ответе.

Вот мои классы:

CustomClassLoader.java

class CustomClassLoader extends ClassLoader
{
    public Class loadClass(String classname)  throws ClassNotFoundException {
        return super.loadClass(classname, true);
    }
}

Test.java(который содержит драйвер)

class Test {
        public static void main(String[] args) throws Exception {
                CustomClassLoader c1 = new CustomClassLoader();
                CustomClassLoader  c2 = new CustomClassLoader();
                Class m1, m2;

                m1 = c1.loadClass("A");
                m2 = c2.loadClass("A");

                m1.getField("b").set(null, 10);

                System.out.println(m1.getField("b").get(null));
                System.out.println(m2.getField("b").get(null));
        }

}

A.java(который содержит статическую переменную)

class A {
        public static int b = 5;
}

Когда я запускаю класс Test, я получаю следующий вывод:

$ java Test
10
10

Я ожидал, что результат будет равен 10 и 5. Как я могу заставить код создать два экземпляра моей статической переменной?

Примечание. Я делаю это только для экспериментов и обучения, но мне было бы интересно узнать, может ли быть какое-то реальное применение этого.

4b9b3361

Ответ 1

Похоже, что класс "A" загружается загрузчиком родительского класса, а не вашим CustomClassLoader (потому что вы вызываете super.loadClass).

Следующая непроверенная поправка должна позволить вам определить класс "А" с помощью собственного загрузчика классов (при делегировании всего остального родительскому загрузчику).

Извиняюсь за ужасное место, где я предполагаю, что единственный inputStream.read() будет читать все! Но вы можете надеяться увидеть, что я имею в виду.

    public Class loadClass(String classname)  throws ClassNotFoundException {
    if (classname.equals("A")) {
        InputStream is = getResourceAsStream("A.class");
        byte[] bodge = new byte[8192];  // Should read until EOF
        try {
            int len = is.read(bodge);
            return defineClass("A", bodge, 0, len);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    return super.loadClass(classname, true);
}

Вероятно, в итоге вы получите ClasscastExceptions или что-то подобное...

Ответ 2

Ваша проблема в том, что new CustomClassLoader() создает загрузчик классов, который будет пытаться делегировать классы загрузки системному загрузчику классов - и это будет одинаково для обоих экземпляров. Ваш CustomClassLoader также не способен загружать сами классы. Попробуйте использовать URLClassLoader и передать null в качестве родителя.

Что касается приложений реального мира: это важно для Java Web-контейнеров и серверов приложений, позволяя различным приложениям полностью изолироваться друг от друга, даже если они могут использовать многие из тех же классов.

Ответ 3

Если вы посмотрите на источник ClassLoader или даже javadocs, вы обнаружите, что по умолчанию ClassLoader делегирует стандартную систему ClassLoader, которая по сути является общей для экземпляров.

Ответ 4

У меня была та же проблема (интеграционные тесты), и я попробовал ее с помощью подхода @Michael Borgwardt. Вот пример кода:

URLClassLoader classLoader1 = new URLClassLoader(new URL[]{new URL("file:///path/to/jar/my-classes.jar")}, null);
URLClassLoader classLoader2 = new URLClassLoader(new URL[]{new URL("file:///path/to/jar/my-classes.jar")}, null);

// Load with classLoader1
Class<?> myClass1 = classLoader1.loadClass("MyClass");
Constructor<?> constructor1 = myClass1.getConstructor();
Object instance1 = constructor1.newInstance();

// Load with classLoader2
Class<?> myClass2 = classLoader2.loadClass("MyClass");
Constructor<?> constructor2 = myClass2.getConstructor();
Object instance2 = constructor2.newInstance();

// Load with system classloader
MyClass myClass = new MyClass();

// ...