В чем разница между каноническим именем, простым именем и именем класса в Java Class?




что такое объект в java (6)

В Java, в чем разница между ними:

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

Я проверил Javadoc несколько раз, и все же это никогда не объясняет это хорошо. Я также провел тест, и это не отражало никакого реального смысла в том, как называются эти методы.


Добавление локальных классов, lambdas и метода toString() для завершения предыдущих двух ответов. Кроме того, я добавляю массивы лямбда и массивы анонимных классов (которые на практике не имеют никакого смысла):

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 {}

Это полный вывод:

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;

Итак, вот правила. Во-первых, давайте начнем с примитивных типов и void :

  1. Если объект класса представляет примитивный тип или void , все четыре метода просто возвращают свое имя.

Теперь правила для getName() :

  1. Каждый класс или интерфейс, отличные от lambda и non-array (т.е. верхний уровень, вложенный, внутренний, локальный и анонимный) имеет имя (которое возвращается getName() ), которое является именем пакета, за которым следует точка (если есть представляет собой пакет), за которым следует имя его класса-файла, сгенерированного компилятором (без суффикса .class ). Если пакет отсутствует, это просто имя класса-файла. Если класс является внутренним, вложенным, локальным или анонимным классом, компилятор должен сгенерировать по крайней мере один $ в имени своего класса. Обратите внимание, что для анонимных классов имя класса заканчивается значком доллара, за которым следует число.
  2. Названия классов Lambda обычно непредсказуемы, и вы все равно не должны заботиться о них. Точно, их имя - это имя окружающего класса, за которым следует $$Lambda$ , за которым следует число, а затем косая черта, а затем другое число.
  3. Дескриптор класса примитивов - это Z для boolean , B для byte , S для short , C для char , I для int , J для long , F для float и D для double . Для классов и интерфейсов без массива дескриптор класса представляет собой L за которым следует getName() ; , Для классов массивов дескриптор класса [ сопровождается дескриптором класса типа компонента (который может быть сам по себе другим классом класса).
  4. Для классов getName() метод getName() возвращает дескриптор класса. Это правило, похоже, терпит неудачу только для классов массивов, у которых тип компонента является лямбдой (возможно, это ошибка), но, надеюсь, это не имеет значения, поскольку нет смысла даже в существовании классов массивов, тип компонента которых лямбда.

Теперь метод toString() :

  1. Если экземпляр класса представляет собой интерфейс (или аннотацию, являющийся специальным типом интерфейса), toString() возвращает "interface " + getName() . Если это примитив, он возвращает просто getName() . Если это что-то другое (тип класса, даже если он довольно странный), он возвращает "class " + getName() .

Метод getCanonicalName() :

  1. Для классов и интерфейсов верхнего уровня метод getCanonicalName() возвращает только то, что возвращает метод getName() .
  2. Метод getCanonicalName() возвращает значение null для анонимных или локальных классов и для классов массивов из них.
  3. Для внутренних и вложенных классов и интерфейсов метод getCanonicalName() возвращает то, что метод getName() заменит введенные компилятором знаки доллара точками.
  4. Для классов getCanonicalName() метод getCanonicalName() возвращает значение null если каноническое имя типа компонента равно null . В противном случае он возвращает каноническое имя типа компонента, за которым следует [] .

Метод getSimpleName() :

  1. Для верхних, вложенных, внутренних и локальных классов getSimpleName() возвращает имя класса, как указано в исходном файле.
  2. Для анонимных классов getSimpleName() возвращает пустую String .
  3. Для лямбда-классов getSimpleName() просто возвращает то, что getName() будет возвращать без имени пакета. Это не имеет большого смысла и выглядит как ошибка для меня, но нет смысла вызывать getSimpleName() для класса лямбда для начала.
  4. Для классов getSimpleName() метод getSimpleName() возвращает простое имя класса компонента, за которым следует [] . У этого есть смешной / странный побочный эффект, что классы массивов, тип компонента которых является анонимным классом, имеют просто [] как их простые имена.

Если вы не уверены в чем-либо, попробуйте сначала написать тест.

Я сделал это:

//primitive
System.out.println(int.class.getName());
System.out.println(int.class.getCanonicalName());
System.out.println(int.class.getSimpleName());

System.out.println();

//class
System.out.println(String.class.getName());
System.out.println(String.class.getCanonicalName());
System.out.println(String.class.getSimpleName());

System.out.println();

//inner class
System.out.println(HashMap.SimpleEntry.class.getName());
System.out.println(HashMap.SimpleEntry.class.getCanonicalName());
System.out.println(HashMap.SimpleEntry.class.getSimpleName());        

System.out.println();

//anonymous inner class
System.out.println(new Serializable(){}.getClass().getName());
System.out.println(new Serializable(){}.getClass().getCanonicalName());
System.out.println(new Serializable(){}.getClass().getSimpleName());

Печать:

int
int
int

java.lang.String
java.lang.String
String

java.util.AbstractMap$SimpleEntry
java.util.AbstractMap.SimpleEntry
SimpleEntry

ClassnameTest$1
null

В последнем блоке есть пустая строка, где getSimpleName возвращает пустую строку.

Результат:

  • имя - это имя, которое вы бы использовали для динамической загрузки класса, например, с вызовом Class.forName с Class.forName по умолчанию.
  • каноническое имя - это имя, которое будет использоваться в операторе импорта и однозначно идентифицирует класс. Может быть полезно во время операций toString или ведения журнала.
  • простое имя свободно идентифицирует класс, снова может быть полезно во время операций toString или ведения журнала, но не гарантировано быть уникальным.

В дополнение к наблюдениям Ника Холта я выполнил несколько случаев для типа данных Array :

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

Над надписями фрагмента кода:

[I
int[]
int[]

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

Меня тоже смущает широкий спектр различных схем именования, и я собирался спросить и ответить на мой собственный вопрос, когда я нашел здесь этот вопрос. Я думаю, что мои результаты соответствуют этому достаточно хорошо и дополняют то, что уже здесь. В центре внимания находится документация по различным терминам и добавление некоторых связанных терминов, которые могут возникнуть в других местах.

Рассмотрим следующий пример:

package a.b;
class C {
  static class D extends C {
  }
  D d;
  D[] ds;
}
  • Простое имя D - это D Это просто та часть, которую вы написали при объявлении класса. У анонимных классов нет простого имени. Class.getSimpleName() возвращает это имя или пустую строку. Возможно, что простое имя содержит $ если вы пишете его так, так как $ является действительной частью идентификатора.

  • Согласно разделу 6.7 JLS , оба abCD и abCDDD будут полностью квалифицированными именами , но только abCD будет каноническим именем D Поэтому каждое каноническое имя является полностью квалифицированным именем, но конверсии не всегда верны. Class.getCanonicalName() вернет каноническое имя или null .

  • Class.getName() документирован для возврата двоичного имени , как указано в разделе 13.1 JLS . В этом случае он возвращает abC$D для D и [La.bC$D; для D[] .

  • Этот ответ показывает, что два класса, загруженные одним и тем же загрузчиком классов, могут иметь одно и то же каноническое имя, но разные двоичные имена . Ни одного имени недостаточно для надежного вывода другого: если у вас есть каноническое имя, вы не знаете, какие части имени являются пакетами и которые содержат классы. Если у вас есть двоичное имя, вы не знаете, какие $ были введены как разделители и которые были частью некоторого простого имени.

  • Анонимные классы и локальные классы не имеют полностью квалифицированных имен, но все еще имеют двоичное имя . То же самое относится к классам, вложенным внутри таких классов. Каждый класс имеет двоичное имя.

  • Запуск javap -v -private на a/b/C.class показывает, что байт-код относится к типу d как La/b/C$D; и массива ds как [La/b/C$D; , Они называются дескрипторами , и они указаны в разделе 4.3 JVMS .

  • Имя класса a/b/C$D используемое в обоих этих дескрипторах, является тем, что вы получаете путем замены . по / в двоичном имени. Спецификация JVM, по-видимому, называет это внутренней формой двоичного имени . Раздел 4.2.1 JVMS описывает это и утверждает, что отличие от двоичного имени было связано с историческими причинами.

  • Имя файла класса в одном из типичных загрузчиков классов, основанных на имени файла, - это то, что вы получаете, если вы интерпретируете / во внутренней форме двоичного имени как разделитель каталогов и добавляете к нему расширение имени .class . Он разрешен относительно пути класса, используемого рассматриваемым загрузчиком класса.


    public void printReflectionClassNames(){
    StringBuffer buffer = new StringBuffer();
    Class clazz= buffer.getClass();
    System.out.println("Reflection on String Buffer Class");
    System.out.println("Name: "+clazz.getName());
    System.out.println("Simple Name: "+clazz.getSimpleName());
    System.out.println("Canonical Name: "+clazz.getCanonicalName());
    System.out.println("Type Name: "+clazz.getTypeName());
}

outputs:
Reflection on String Buffer Class
Name: java.lang.StringBuffer
Simple Name: StringBuffer
Canonical Name: java.lang.StringBuffer
Type Name: java.lang.StringBuffer

это лучший документ, который я нашел, описывая getName (), getSimpleName (), getCanonicalName ()

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[]






java