Гарантирует ли Java, что Object.getClass() == Object.getClass()?
Я действительно имею в виду равенство идентичности здесь.
Например, будет ли следующее всегда выводить true?
System.out.println("foo".getClass() == "fum".getClass());
4 ответа
Да, токены классов уникальны (то есть для любого данного загрузчика классов).
Т.е. вы всегда будете получать ссылку на один и тот же физический объект в одной и той же области загрузчика классов. Однако другой загрузчик классов будет загружать другой токен класса в сочетании с тем фактом, что одно и то же определение класса считается различным при загрузке двумя различными загрузчиками классов.
Смотрите этот мой предыдущий ответ для демонстрации этого.
Для двух экземпляров класса X
,
x1.getClass() == x2.getClass()
только если
x1.getClass().getClassLoader() == x2.getClass().getClassLoader()
Замечания: Class.getClassLoader()
может возвратить нуль, который подразумевает загрузчик ClassLoader.
Да.
Возвращенный объект Class - это объект, который заблокирован статическими синхронизированными методами представленного класса.
Если было возможно вернуть несколько экземпляров, то
public static synchronized void doSomething() {..}
не будет потокобезопасным.
Это гарантировано для загрузчика классов, как указано в спецификации 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)
ура