If you’re unsure about something, try writing a test first.
I did this:
class ClassNameTest {
public static void main(final String... arguments) {
printNamesForClass(
int.class,
"int.class (primitive)");
printNamesForClass(
String.class,
"String.class (ordinary class)");
printNamesForClass(
java.util.HashMap.SimpleEntry.class,
"java.util.HashMap.SimpleEntry.class (nested class)");
printNamesForClass(
new java.io.Serializable(){}.getClass(),
"new java.io.Serializable(){}.getClass() (anonymous inner class)");
}
private static void printNamesForClass(final Class<?> clazz, final String label) {
System.out.println(label + ":");
System.out.println(" getName(): " + clazz.getName());
System.out.println(" getCanonicalName(): " + clazz.getCanonicalName());
System.out.println(" getSimpleName(): " + clazz.getSimpleName());
System.out.println(" getTypeName(): " + clazz.getTypeName()); // added in Java 8
System.out.println();
}
}
Prints:
int.class (primitive):
getName(): int
getCanonicalName(): int
getSimpleName(): int
getTypeName(): int
String.class (ordinary class):
getName(): java.lang.String
getCanonicalName(): java.lang.String
getSimpleName(): String
getTypeName(): java.lang.String
java.util.HashMap.SimpleEntry.class (nested class):
getName(): java.util.AbstractMap$SimpleEntry
getCanonicalName(): java.util.AbstractMap.SimpleEntry
getSimpleName(): SimpleEntry
getTypeName(): java.util.AbstractMap$SimpleEntry
new java.io.Serializable(){}.getClass() (anonymous inner class):
getName(): ClassNameTest$1
getCanonicalName(): null
getSimpleName():
getTypeName(): ClassNameTest$1
There’s an empty entry in the last block where getSimpleName
returns an empty string.
The upshot looking at this is:
- the name is the name that you’d use to dynamically load the class with, for example, a call to
Class.forName
with the defaultClassLoader
. Within the scope of a certainClassLoader
, all classes have unique names.- the canonical name is the name that would be used in an import statement. It might be useful during
toString
or logging operations. When thejavac
compiler has complete view of a classpath, it enforces uniqueness of canonical names within it by clashing fully qualified class and package names at compile time. However JVMs must accept such name clashes, and thus canonical names do not uniquely identify classes within aClassLoader
. (In hindsight, a better name for this getter would have beengetJavaName
; but this method dates from a time when the JVM was used solely to run Java programs.)- the simple name loosely identifies the class, again might be useful during
toString
or logging operations but is not guaranteed to be unique.- the type name returns “an informative string for the name of this type”, “It’s like
toString
: it’s purely informative and has no contract value”. (as written by sir4ur0n)
Also you can commonly reference the Java Language Specification documentation for these types technical Java API details:
- Here’s the Java 11 Specification on this subject matter: https://docs.oracle.com/javase/specs/jls/se11/html/jls-6.html#jls-6.7
Example 6.7-2.
andExample 6.7-2.
goes overFully Qualified Names
andFully Qualified Names v. Canonical Name
respectively