1136 Stimmen

Was ist der Unterschied zwischen kanonischem Namen, einfachem Namen und Klassennamen in Java Class?

Was ist in Java der Unterschied zwischen diesen beiden:

Object o1 = ....
o1.getClass().getSimpleName();
o1.getClass().getName();
o1.getClass().getCanonicalName();

Ich habe mir die Javadoc mehrfach angesehen, aber das erklärt es nicht gut. Ich habe auch einen Test durchgeführt, der keine wirkliche Bedeutung hinter der Art und Weise, wie diese Methoden aufgerufen werden, erkennen ließ.

1302voto

Nick Holt Punkte 32407

Wenn Sie unsicher sind, schreiben Sie zuerst einen Test.

Ich habe dies getan:

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

Drucke:

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

Es gibt einen leeren Eintrag im letzten Block, wo getSimpleName gibt eine leere Zeichenkette zurück.

Die Schlussfolgerung daraus ist:

  • el Name ist der Name, den Sie verwenden würden, um die Klasse dynamisch zu laden, z. B. mit einem Aufruf von Class.forName mit dem Standard ClassLoader . Im Rahmen eines bestimmten ClassLoader haben alle Klassen eindeutige Namen.
  • el kanonischer Name ist der Name, der in einer Importanweisung verwendet werden würde. Er könnte nützlich sein während toString oder Protokollierungsvorgänge. Wenn die javac Compiler einen vollständigen Überblick über den Klassenpfad hat, erzwingt er die Eindeutigkeit der kanonischen Namen innerhalb des Pfades, indem er voll qualifizierte Klassen- und Paketnamen zur Kompilierungszeit zusammenführt. JVMs müssen jedoch solche Namenskollisionen akzeptieren, so dass kanonische Namen Klassen innerhalb eines Klassenpfades nicht eindeutig identifizieren. ClassLoader . (Im Nachhinein betrachtet wäre ein besserer Name für diesen Getter gewesen getJavaName Diese Methode stammt jedoch aus einer Zeit, als die JVM ausschließlich zur Ausführung von Java-Programmen verwendet wurde).
  • el einfacher Name identifiziert lose die Klasse, was wiederum nützlich sein kann bei toString oder Protokollierungsvorgänge, aber es ist nicht garantiert, dass sie eindeutig sind.
  • el Typbezeichnung gibt "eine informative Zeichenfolge für den Namen dieses Typs" zurück, "Es ist wie toString : Es ist rein informativ und hat keinen Vertragswert". (geschrieben von sir4ur0n)

Sie können auch die Java Language Specification-Dokumentation für diese Arten technischer Java-API-Details verwenden:

Example 6.7-2. y Example 6.7-2. geht über Fully Qualified Names y Fully Qualified Names v. Canonical Name bzw.

120voto

Victor Stafusa Punkte 13306

Hinzufügen von lokalen Klassen, Lambdas und dem toString() Methode, um die beiden vorherigen Antworten zu vervollständigen. Außerdem füge ich Arrays von Lambdas und Arrays von anonymen Klassen hinzu (die in der Praxis allerdings keinen Sinn machen):

package com.example;

public final class TestClassNames {
    private static void showClass(Class<?> c) {
        System.out.println("getName():          " + c.getName());
        System.out.println("getCanonicalName(): " + c.getCanonicalName());
        System.out.println("getSimpleName():    " + c.getSimpleName());
        System.out.println("toString():         " + c.toString());
        System.out.println();
    }

    private static void x(Runnable r) {
        showClass(r.getClass());
        showClass(java.lang.reflect.Array.newInstance(r.getClass(), 1).getClass()); // Obtains an array class of a lambda base type.
    }

    public static class NestedClass {}

    public class InnerClass {}

    public static void main(String[] args) {
        class LocalClass {}
        showClass(void.class);
        showClass(int.class);
        showClass(String.class);
        showClass(Runnable.class);
        showClass(SomeEnum.class);
        showClass(SomeAnnotation.class);
        showClass(int[].class);
        showClass(String[].class);
        showClass(NestedClass.class);
        showClass(InnerClass.class);
        showClass(LocalClass.class);
        showClass(LocalClass[].class);
        Object anonymous = new java.io.Serializable() {};
        showClass(anonymous.getClass());
        showClass(java.lang.reflect.Array.newInstance(anonymous.getClass(), 1).getClass()); // Obtains an array class of an anonymous base type.
        x(() -> {});
    }
}

enum SomeEnum {
   BLUE, YELLOW, RED;
}

@interface SomeAnnotation {}

Dies ist die vollständige Ausgabe:

getName():          void
getCanonicalName(): void
getSimpleName():    void
toString():         void

getName():          int
getCanonicalName(): int
getSimpleName():    int
toString():         int

getName():          java.lang.String
getCanonicalName(): java.lang.String
getSimpleName():    String
toString():         class java.lang.String

getName():          java.lang.Runnable
getCanonicalName(): java.lang.Runnable
getSimpleName():    Runnable
toString():         interface java.lang.Runnable

getName():          com.example.SomeEnum
getCanonicalName(): com.example.SomeEnum
getSimpleName():    SomeEnum
toString():         class com.example.SomeEnum

getName():          com.example.SomeAnnotation
getCanonicalName(): com.example.SomeAnnotation
getSimpleName():    SomeAnnotation
toString():         interface com.example.SomeAnnotation

getName():          [I
getCanonicalName(): int[]
getSimpleName():    int[]
toString():         class [I

getName():          [Ljava.lang.String;
getCanonicalName(): java.lang.String[]
getSimpleName():    String[]
toString():         class [Ljava.lang.String;

getName():          com.example.TestClassNames$NestedClass
getCanonicalName(): com.example.TestClassNames.NestedClass
getSimpleName():    NestedClass
toString():         class com.example.TestClassNames$NestedClass

getName():          com.example.TestClassNames$InnerClass
getCanonicalName(): com.example.TestClassNames.InnerClass
getSimpleName():    InnerClass
toString():         class com.example.TestClassNames$InnerClass

getName():          com.example.TestClassNames$1LocalClass
getCanonicalName(): null
getSimpleName():    LocalClass
toString():         class com.example.TestClassNames$1LocalClass

getName():          [Lcom.example.TestClassNames$1LocalClass;
getCanonicalName(): null
getSimpleName():    LocalClass[]
toString():         class [Lcom.example.TestClassNames$1LocalClass;

getName():          com.example.TestClassNames$1
getCanonicalName(): null
getSimpleName():    
toString():         class com.example.TestClassNames$1

getName():          [Lcom.example.TestClassNames$1;
getCanonicalName(): null
getSimpleName():    []
toString():         class [Lcom.example.TestClassNames$1;

getName():          com.example.TestClassNames$$Lambda$1/1175962212
getCanonicalName(): com.example.TestClassNames$$Lambda$1/1175962212
getSimpleName():    TestClassNames$$Lambda$1/1175962212
toString():         class com.example.TestClassNames$$Lambda$1/1175962212

getName():          [Lcom.example.TestClassNames$$Lambda$1;
getCanonicalName(): com.example.TestClassNames$$Lambda$1/1175962212[]
getSimpleName():    TestClassNames$$Lambda$1/1175962212[]
toString():         class [Lcom.example.TestClassNames$$Lambda$1;

Also, hier sind die Regeln. Beginnen wir mit den primitiven Typen und void :

  1. Wenn das Klassenobjekt einen primitiven Typ repräsentiert oder void geben alle vier Methoden einfach ihren Namen zurück.

Nun die Regeln für die getName() Methode:

  1. Jede Nicht-Lambda- und Nicht-Array-Klasse oder -Schnittstelle (d. h. Top-Level, verschachtelt, inner, lokal und anonym) hat einen Namen (der von getName() ), d.h. der Paketname, gefolgt von einem Punkt (falls es ein Paket gibt), gefolgt von dem Namen der vom Compiler erzeugten Klassendatei (ohne das Suffix .class ). Wenn es kein Paket gibt, ist es einfach der Name der Klassendatei. Wenn die Klasse eine innere, verschachtelte, lokale oder anonyme Klasse ist, sollte der Compiler mindestens eine $ in seinem Klassendateinamen. Beachten Sie, dass bei anonymen Klassen der Klassenname mit einem Dollar-Zeichen, gefolgt von einer Zahl, endet.
  2. Lambda-Klassennamen sind im Allgemeinen unvorhersehbar und sollten Ihnen ohnehin egal sein. Genau, ihr Name ist der Name der einschließenden Klasse, gefolgt von $$Lambda$ , gefolgt von einer Zahl, gefolgt von einem Schrägstrich, gefolgt von einer weiteren Zahl.
  3. Die Klassendeskriptoren der Primitive sind Z para boolean , B para byte , S para short , C para char , I para int , J para long , F para float y D para double . Für Nicht-Array-Klassen und -Schnittstellen lautet der Klassendeskriptor L gefolgt von dem, was gegeben ist durch getName() gefolgt von ; . Bei Array-Klassen lautet der Klassen-Deskriptor [ gefolgt von dem Klassendeskriptor des Komponententyps (der selbst eine andere Array-Klasse sein kann).
  4. Bei Array-Klassen wird die getName() Methode gibt ihren Klassendeskriptor zurück. Diese Regel scheint nur bei Array-Klassen zu versagen, deren Komponententyp ein Lambda ist (was möglicherweise ein Fehler ist), aber das sollte hoffentlich ohnehin keine Rolle spielen, da es nicht einmal auf die Existenz von Array-Klassen ankommt, deren Komponententyp ein Lambda ist.

Nun, die toString() Methode:

  1. Wenn die Klasseninstanz eine Schnittstelle (oder eine Anmerkung, die eine spezielle Art von Schnittstelle ist) darstellt, wird die toString() gibt zurück. "interface " + getName() . Wenn es sich um ein Primitiv handelt, gibt es einfach getName() . Wenn es etwas anderes ist (ein Klassentyp, auch wenn es ein ziemlich seltsamer Typ ist), gibt es zurück "class " + getName() .

En getCanonicalName() Methode:

  1. Für Klassen und Schnittstellen der obersten Ebene wird die getCanonicalName() Methode gibt genau das zurück, was die getName() Methode zurück.
  2. En getCanonicalName() Methode gibt zurück null für anonyme oder lokale Klassen und für Array-Klassen dieser Klassen.
  3. Für innere und verschachtelte Klassen und Schnittstellen wird die getCanonicalName() Methode gibt zurück, was die getName() Methode würde die vom Compiler eingeführten Dollar-Zeichen durch Punkte ersetzen.
  4. Bei Array-Klassen wird die getCanonicalName() Methode gibt zurück null wenn der kanonische Name des Komponententyps null . Andernfalls gibt es den kanonischen Namen des Komponententyps zurück, gefolgt von [] .

En getSimpleName() Methode:

  1. Für Top-Level-, verschachtelte, innere und lokale Klassen wird die getSimpleName() gibt den Namen der Klasse zurück, wie er in der Quelldatei steht.
  2. Bei anonymen Klassen wird die getSimpleName() gibt eine leere String .
  3. Für Lambda-Klassen wird die getSimpleName() gibt nur das zurück, was die getName() würde ohne den Paketnamen zurückgegeben. Das macht nicht viel Sinn und sieht für mich wie ein Fehler aus, aber es macht keinen Sinn, die getSimpleName() auf eine Lambda-Klasse zu beginnen.
  4. Bei Array-Klassen wird die getSimpleName() Methode gibt den einfachen Namen der Komponentenklasse zurück, gefolgt von [] . Dies hat den lustigen/unheimlichen Nebeneffekt, dass Array-Klassen, deren Komponententyp eine anonyme Klasse ist, nur [] als ihre einfachen Namen.

83voto

gerardw Punkte 5059

Zusätzlich zu den Beobachtungen von Nick Holt habe ich ein paar Fälle für Array Datentyp:

//primitive Array
int demo[] = new int[5];
Class<? extends int[]> clzz = demo.getClass();
System.out.println(clzz.getName());
System.out.println(clzz.getCanonicalName());
System.out.println(clzz.getSimpleName());       

System.out.println();

//Object Array
Integer demo[] = new Integer[5]; 
Class<? extends Integer[]> clzz = demo.getClass();
System.out.println(clzz.getName());
System.out.println(clzz.getCanonicalName());
System.out.println(clzz.getSimpleName());

Das obige Codeschnipsel wird gedruckt:

[I
int[]
int[]

[Ljava.lang.Integer;
java.lang.Integer[]
Integer[]

22voto

MvG Punkte 54213

Ich war auch schon verwirrt von der Vielzahl der verschiedenen Namensschemata und wollte gerade meine eigene Frage dazu stellen und beantworten, als ich diese Frage hier fand. Ich denke, meine Ergebnisse passen gut dazu und ergänzen das, was hier bereits steht. Mein Schwerpunkt ist die Suche nach Dokumentation zu den verschiedenen Begriffen und fügen weitere verwandte Begriffe hinzu, die an anderen Stellen auftauchen könnten.

Betrachten Sie das folgende Beispiel:

package a.b;
class C {
  static class D extends C {
  }
  D d;
  D[] ds;
}
  • En einfacher Name de D es D . Das ist nur der Teil, den Sie bei der Deklaration der Klasse geschrieben haben. Anonyme Klassen haben keinen einfachen Namen. Class.getSimpleName() gibt diesen Namen oder die leere Zeichenkette zurück. Es ist möglich, dass der einfache Name ein $ wenn Sie es so schreiben, denn $ ist ein gültiger Teil eines Bezeichners im Sinne von JLS-Abschnitt 3.8 (auch wenn davon eher abgeraten wird).

  • Selon le JLS-Abschnitt 6.7 beide a.b.C.D y a.b.C.D.D.D wäre vollständig qualifizierte Namen sondern nur a.b.C.D wäre die kanonischer Name de D . Jeder kanonische Name ist also ein voll qualifizierter Name, aber das Gegenteil ist nicht immer der Fall. Class.getCanonicalName() gibt den kanonischen Namen zurück oder null .

  • Class.getName() ist dokumentiert, um die Binärname wie angegeben in JLS-Abschnitt 13.1 . In diesem Fall gibt sie zurück a.b.C$D para D y [La.b.C$D; para D[] .

  • Diese Antwort zeigt, dass es möglich ist, dass zwei Klassen, die von demselben Klassenlader geladen werden, die gleiche kanonischer Name aber unterschiedlich binäre Namen . Keiner der beiden Namen reicht aus, um zuverlässig auf den anderen zu schließen: Wenn Sie den kanonischen Namen haben, wissen Sie nicht, welche Teile des Namens Pakete sind und welche Klassen enthalten. Wenn Sie den binären Namen haben, wissen Sie nicht, welche $ wurden als Trennzeichen eingeführt und waren Teil eines einfachen Namens. (Die Klassendatei speichert den Binärnamen der Klasse selbst und seine umschließende Klasse die es der Laufzeitumgebung ermöglicht diese Unterscheidung treffen .)

  • Anonyme Klassen y lokale Klassen haben keine vollständig qualifizierte Namen haben aber trotzdem eine Binärname . Das Gleiche gilt für Klassen, die innerhalb solcher Klassen verschachtelt sind. Alle Klasse hat einen binären Namen.

  • Ejecutar javap -v -private オン a/b/C.class zeigt, dass sich der Bytecode auf den Typ von d como La/b/C$D; und die des Arrays ds como [La/b/C$D; . Diese werden als Deskriptoren und sie sind angegeben in JVMS Abschnitt 4.3 .

  • Der Klassenname a/b/C$D die in diesen beiden Deskriptoren verwendet werden, erhält man, wenn man die . von / im Binärnamen. Die JVM-Spezifikation nennt dies anscheinend die interne Form des Binärnamens . JVMS Abschnitt 4.2.1 beschrieben und erklärt, dass die Abweichung von der binären Bezeichnung aus historischen Gründen erfolgte.

  • En Dateiname einer Klasse in einem der typischen filenamenbasierten Klassenlader ist das, was man erhält, wenn man die / in der internen Form des Binärnamens als Verzeichnistrennzeichen und fügen Sie die Dateinamenerweiterung .class dazu. Sie wird relativ zum Klassenpfad aufgelöst, den der betreffende Klassenlader verwendet.

10voto

Kiran Punkte 131

Dies ist das beste Dokument, das ich gefunden habe, das getName(), getSimpleName(), getCanonicalName() beschreibt

https://javahowtodoit.wordpress.com/2014/09/09/java-lang-class-what-is-the-difference-between-class-getname-class-getcanonicalname-and-class-getsimplename/

// Primitive type
int.class.getName();          // -> int
int.class.getCanonicalName(); // -> int
int.class.getSimpleName();    // -> int

// Standard class
Integer.class.getName();          // -> java.lang.Integer
Integer.class.getCanonicalName(); // -> java.lang.Integer
Integer.class.getSimpleName();    // -> Integer

// Inner class
Map.Entry.class.getName();          // -> java.util.Map$Entry
Map.Entry.class.getCanonicalName(); // -> java.util.Map.Entry
Map.Entry.class.getSimpleName();    // -> Entry     

// Anonymous inner class
Class<?> anonymousInnerClass = new Cloneable() {}.getClass();
anonymousInnerClass.getName();          // -> somepackage.SomeClass$1
anonymousInnerClass.getCanonicalName(); // -> null
anonymousInnerClass.getSimpleName();    // -> // An empty string

// Array of primitives
Class<?> primitiveArrayClass = new int[0].getClass();
primitiveArrayClass.getName();          // -> [I
primitiveArrayClass.getCanonicalName(); // -> int[]
primitiveArrayClass.getSimpleName();    // -> int[]

// Array of objects
Class<?> objectArrayClass = new Integer[0].getClass();
objectArrayClass.getName();          // -> [Ljava.lang.Integer;
objectArrayClass.getCanonicalName(); // -> java.lang.Integer[]
objectArrayClass.getSimpleName();    // -> Integer[]

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X