Гарантирует ли Java, что Object.getClass () == Object.getClass ()? - PullRequest
38 голосов
/ 18 сентября 2010

Я действительно имею в виду равенство идентичности здесь.

Например, всегда ли будет печататься следующее true ?

System.out.println("foo".getClass() == "fum".getClass());

Ответы [ 4 ]

42 голосов
/ 18 сентября 2010

Да, токены классов уникальны (для любого данного загрузчика классов, то есть).

Т.е. вы всегда получите ссылку на один и тот же физический объект в пределах одной и той же области загрузчика классов .Однако другой загрузчик классов будет загружать другой токен класса в сочетании с тем фактом, что одно и то же определение класса считается разным при загрузке двумя различными загрузчиками классов.

См. этот мой более ранний ответ для демонстрации этого.

15 голосов
/ 18 сентября 2010

Для двух экземпляров класса X,

x1.getClass() == x2.getClass()

только если

x1.getClass().getClassLoader() == x2.getClass().getClassLoader()

Примечание: Class.getClassLoader() может возвращать нуль, что подразумевает загрузчик ClassLoader.

8 голосов
/ 18 сентября 2010

Да.

Возвращенный объект Class - это объект, который заблокирован статическими синхронизированными методами представленного класса.

Если можно было вернуть несколько экземпляров, то

public static synchronized void doSomething() {..}

не будет поточно-ориентированным.

3 голосов
/ 09 марта 2016

Гарантируется для каждого загрузчика классов, как указано в спецификации JVM :

Сначала виртуальная машина Java определяет, записала ли она уже, что L является инициирующим загрузчиком класса или интерфейса, обозначенного N. Если это так, эта попытка создания недопустима, и при загрузке возникает ошибка LinkageError.

То есть, если загрузчик классов (L) пытается обойти кэширование Class экземпляров по умолчанию и заставить JVM загрузить определение byte[] более одного раза для того же имени класса (N), LinkageError будет быть брошенным JVM.

Например, реализовать загрузчик классов, который вызывает defineClass(...) каждый раз, когда вызывается loadClass(...) (в обход кэширования по умолчанию):

public class ClassloaderTest {

    private static final byte[] CLASS_DEF = readClassBytes();

    private static byte[] readClassBytes() {
        try {
            InputStream is = ClassloaderTest.class.getResourceAsStream("ClassloaderTest.class");
            ByteArrayOutputStream buffer = new ByteArrayOutputStream();
            int nRead;
            byte[] data = new byte[16384];
            while ((nRead = is.read(data, 0, data.length)) != -1) {
                buffer.write(data, 0, nRead);
            }
            buffer.flush();
            return buffer.toByteArray();
        } catch (IOException ex) {
            throw new AssertionError();
        }
    }

    private static ClassLoader createNonCachingClassloader() {
        return new ClassLoader() {
            @Override
            public Class<?> loadClass(String name) throws ClassNotFoundException {
                if (name.equals("classloader.ClassloaderTest")) {
                    return defineClass(name, CLASS_DEF, 0, CLASS_DEF.length);
                } else {
                    return getParent().loadClass(name);
                }
            }
        };
    }

    public static void main(String[] args) throws Exception {
        ClassLoader cl = createNonCachingClassloader();
        Class<?> cl1 = cl.loadClass("classloader.ClassloaderTest");
        Class<?> cl2 = cl.loadClass("classloader.ClassloaderTest");
        System.out.println(cl1==cl2);
    }
}

и вот что происходит:

Exception in thread "main" java.lang.LinkageError: loader (instance of  classloader/ClassloaderTest$1): attempted  duplicate class definition for name: "classloader/ClassloaderTest"
    at java.lang.ClassLoader.defineClass1(Native Method)
    at java.lang.ClassLoader.defineClass(ClassLoader.java:760)
    at java.lang.ClassLoader.defineClass(ClassLoader.java:642)
    at classloader.ClassloaderTest$1.loadClass(ClassloaderTest.java:53)
    at classloader.ClassloaderTest.main(ClassloaderTest.java:64)

Приветствия

...