Java是否保证Object.getClass() == Object.getClass()?

40

我这里真的是指恒等相等。

例如,以下代码是否总是打印true

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

如果一个类(1)没有覆盖equals(Object)方法;并且(2)不是覆盖equals(Object)方法的类的子类,则此类使用在根Object类中定义的equals(Object)方法-该方法使用==身份运算符。 - emory
12
我认为你的评论有误。在这段代码中,“==”始终执行引用比较,操作符不能被重载以调用“equals”。另外,java.lang.Class是final的,所以你无法覆盖它的“equals”。 - polygenelubricants
4个回答

45

是的,类标记是唯一的(对于任何给定的类加载器而言)。

也就是说,您将始终获得对同一物理对象的引用,在同一类加载器领域内。然而,不同的类加载器将加载不同的类标记,这与相同的类定义被两个不同的类加载器加载时被视为不同有关。

请参见我的早期回答,以进行演示。


17

对于两个类 X 的实例:

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

仅当

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

注意:Class.getClassLoader()可能会返回null,这意味着引导类加载器。


表达得非常好。 - salezica
@McDowell,你最后一段话是错误的。ClassLoader.getSystemClassLoader不同于引导类加载器。如果.getClassLoader()返回null,则表示该类由引导类加载器加载。ClassLoader.getSystemClassLoader不会返回null。 - Pacerier

8

是的。

返回的Class对象是由所表示类的静态同步方法锁定的对象。

如果可以返回多个实例,则

public static synchronized void doSomething() {..}

不具备线程安全性。


4
另一个线索是Javadoc中说getClass返回“代表此对象的运行时类的_类_对象”而不是“_一个_类对象”。 - Stephen C

4
根据JVM规范,它是基于类加载器保证的:

首先,Java虚拟机会确定它是否已经记录了 L 是 N 所表示的类或接口的启动加载器的信息。如果已经记录过,则此创建尝试无效,加载将抛出LinkageError。

也就是说,如果一个类加载器(L)试图绕过默认的Class实例缓存,并使JVM为相同的类名(N)加载多次byte[]定义,则JVM会抛出LinkageError。
例如,实现一个调用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)

干杯


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接