2010-09-17 3 views
36

Я действительно имею в виду равенство идентичности здесь.Гарантирует ли Java, что Object.getClass() == Object.getClass()?

Например, всегда ли печатать true?

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

Если класс (1) не переопределяет метод equals (Object); и (2) не является подклассом класса, который переопределяет метод equals (Object), тогда этот класс использует метод equals (Object), определенный в корневом классе Object, который использует оператор идентификации ==. – emory

+12

@emory: Я думаю, что ваш комментарий неправильный. '==' в этом фрагменте всегда выполняет сравнение ссылок, и оператор не может быть перегружен, чтобы вызывать 'equals' вместо этого. Кроме того, 'java.lang.Class' является' final', поэтому вы не можете переопределить его 'equals'. – polygenelubricants

ответ

40

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

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

См. this earlier answer of mine для демонстрации этого.

8

Да.

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

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

public static synchronized void doSomething() {..} 

не будет потокобезопасным.

+2

Другая подсказка заключается в том, что javadoc говорит, что 'getClass' возвращает объект _The_ Class, который представляет класс среды выполнения этого объекта« ... не »_A_ Объект класса ...». –

14

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

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

только если

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

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

+0

Отличный способ поместить его – slezica

+0

@McDowell, ваш последний абзац неверен. 'ClassLoader.getSystemClassLoader' - это не то же самое, что и bootstrap classloader. Если '.getClassLoader()' возвращает значение null, это означает, что класс загружается загрузчиком классов bootstrap. 'ClassLoader.getSystemClassLoader' не возвращает null. – Pacerier

+0

@ Pacerier благодарит за это – McDowell

2

Гарантируется за загрузчику, как указано в JVM specification:

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

То есть, если загрузчик классов (L) пытается обойти по умолчанию кэширование Class экземпляров, и сделать JVM загрузите byte[] определение более чем один раз за имя того же класса (N), A 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) 

Приветствия

Смежные вопросы