40

I really do mean identity-equality here.

For example, will the following always print true?

System.out.println("foo".getClass() == "fum".getClass());
SurvivalMachine
  • 7,946
  • 15
  • 57
  • 87
Mackenzie
  • 1,897
  • 1
  • 19
  • 25
  • If a class (1) does not override the equals(Object) method; and (2) is not a subclass of a class that overrides the equals(Object) method then this class uses the equals(Object) method defined in the root Object class - which uses the == identity operator. – emory Sep 17 '10 at 21:50
  • 12
    @emory: I think your comment is wrong. The `==` in this snippet always performs reference comparison, and the operator can not be overloaded to invoke `equals` instead. Also, `java.lang.Class` is `final` so you can't override its `equals`. – polygenelubricants Sep 17 '10 at 23:47

4 Answers4

45

Yes, class tokens are unique (for any given classloader, that is).

I.e. you will always get a reference to the same physical object within the same classloader realm. However, a different classloader will load a different class token, in conjunction with the fact that the same class definition is deemed different when loaded by two distinct classloaders.

See this earlier answer of mine for a demonstration of this.

Community
  • 1
  • 1
Péter Török
  • 114,404
  • 31
  • 268
  • 329
16

For two instances of class X,

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

only if

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

Note: Class.getClassLoader() may return null which implies the bootstrap ClassLoader.

McDowell
  • 107,573
  • 31
  • 204
  • 267
  • Excellent way of putting it – salezica Jul 15 '14 at 03:17
  • @McDowell, Your last paragraph is wrong. `ClassLoader.getSystemClassLoader` is not the same as the bootstrap classloader. If `.getClassLoader()` returns null, it means that the class is loaded by the bootstrap classloader. `ClassLoader.getSystemClassLoader` will not return null. – Pacerier Aug 29 '14 at 04:31
8

Yes.

The returned Class object is the object that is locked by static synchronized methods of the represented class.

If it was possible to return multiple instances, then

public static synchronized void doSomething() {..}

would not be thread-safe.

Bozho
  • 588,226
  • 146
  • 1,060
  • 1,140
  • 4
    Another clue is that the javadoc says that `getClass` returns "_The_ Class object that represents the runtime class of this object" ... not "_A_ Class object ...". – Stephen C Sep 18 '10 at 02:25
4

It is guaranteed per classloader, as stated in the JVM specification:

First, the Java Virtual Machine determines whether it has already recorded that L is an initiating loader of a class or interface denoted by N. If so, this creation attempt is invalid and loading throws a LinkageError.

That is, if a class loader (L) tries to bypass default Class instances caching, and make the JVM load the byte[] definition more than once for the same class name (N), a LinkageError will be thrown by the JVM.

For example, implement a class loader that calls defineClass(...) each time loadClass(...) is invoked (bypassing default caching):

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);
    }
}

and this is what happens:

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)

Cheers

idelvall
  • 1,536
  • 15
  • 25