[java] Java 클래스에서 표준 이름, 단순 이름 및 클래스 이름의 차이점은 무엇입니까?

Java에서는 다음과 같은 차이점이 있습니다.

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

Javadoc을 여러 번 확인했지만 아직 잘 설명하지 못했습니다. 또한 테스트를 실행했는데 이러한 메소드가 호출되는 방식의 실제 의미를 반영하지 않았습니다.



답변

확실하지 않은 경우 먼저 테스트를 작성하십시오.

나는 이걸했다:

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

인쇄물:

int.class (프리미티브) :
    getName () : int
    getCanonicalName () : int
    getSimpleName () : int
    getTypeName () : int

String.class (일반 클래스) :
    getName () : java.lang.String
    getCanonicalName () : java.lang.String
    getSimpleName () : 문자열
    getTypeName () : java.lang.String

java.util.HashMap.SimpleEntry.class (중첩 된 클래스) :
    getName () : java.util.AbstractMap $ SimpleEntry
    getCanonicalName () : java.util.AbstractMap.SimpleEntry
    getSimpleName () : SimpleEntry
    getTypeName () : java.util.AbstractMap $ SimpleEntry

새로운 java.io.Serializable () {}. getClass () (익명 내부 클래스) :
    getName () : ClassNameTest $ 1
    getCanonicalName () : null
    getSimpleName () :
    getTypeName () : ClassNameTest $ 1

마지막 블록에는 getSimpleName빈 문자열이 반환되는 빈 항목이 있습니다 .

이 결과는 다음과 같습니다.

  • 이름은 동적으로, 예를 들어, 전화를 가진 클래스를로드하는 데 사용하는 거라고 이름입니다 Class.forName기본으로 ClassLoader. 특정 범위 내에서 ClassLoader모든 클래스는 고유 한 이름을 갖습니다.
  • 정식 이름은 import 문에 사용되는 이름입니다. toString또는 로깅 작업 중에 유용 할 수 있습니다 . 때 javac컴파일러가 클래스 경로의 완전한 뷰를 갖고,이 컴파일시에 정규화 된 클래스 및 패키지 이름이 충돌하여 내 정식 이름의 고유성을 적용합니다. 그러나 JVM은 이러한 이름 충돌을 허용해야하므로 표준 이름은에서 클래스를 고유하게 식별하지 않습니다 ClassLoader. (이 후자의 경우,이 게터의 이름은 더 좋았을 것입니다 getJavaName. 그러나이 방법은 JVM이 Java 프로그램을 실행하는 데만 사용 된 시점부터 시작됩니다.)
  • 간단한 이름은 느슨하게 동안 유용 할 수 있습니다 다시 클래스를 식별 toString또는 기록 작업을하지만, 고유하지 않을 수도 있습니다.
  • 형의 이름 반환, “그것은) toString (같은 : 그것은 순전히 정보의 어떠한 계약 금액이 없습니다” “이러한 유형의 이름에 대한 정보를 문자열”(sir4ur0n에 의해 작성된로)

답변

로컬 클래스, 람다 및 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. 모든 람다 및 비 배열 클래스 또는 인터페이스 (예 : 최상위, 중첩, 내부, 로컬 및 익명)는 getName()패키지 이름 뒤에 점 (패키지가있는 경우) 인 이름 (에 의해 반환 됨 )을 갖습니다. ) 다음에 컴파일러에서 생성 한 클래스 파일 이름 (접미사없이)이 붙습니다 .class. 패키지가 없으면 단순히 클래스 파일의 이름입니다. 클래스가 내부, 중첩, 로컬 또는 익명 클래스 인 경우 컴파일러는 $클래스 파일 이름으로 하나 이상을 생성해야합니다 . 익명 클래스의 경우 클래스 이름은 달러 기호와 숫자로 끝납니다.
  2. Lambda 클래스 이름은 일반적으로 예측할 수 없으므로 어쨌든 신경 쓰지 않아야합니다. 정확하게 그들의 이름은 다음에 오는 클래스의 이름입니다.$$Lambda$ , 뒤에, 숫자, 슬래시, 다른 숫자가옵니다.
  3. 프리미티브의 클래스 기술자는 Z위해 boolean, B위해 byte, S위해 short, C위해 char, I위해 int, J위해 long, F위해 floatD위해 double. 비 배열 클래스 및 인터페이스의 경우 클래스 디스크립터 L다음에 getName()뒤에 오는 것이 표시됩니다.; . 배열 클래스의 경우 클래스 설명자 [뒤에 구성 요소 유형의 클래스 설명자 (자체 배열 클래스 일 수 있음)가옵니다.
  4. 배열 클래스의 경우 getName() 메소드는 클래스 디스크립터를 리턴합니다. 이 규칙은 구성 요소 유형이 람다 (버그 일 가능성이있는) 인 배열 클래스에만 실패하는 것으로 보이지만, 구성 요소 유형이 람다 인 배열 클래스가 존재하더라도 아무 의미가 없기 때문에 이것은 중요하지 않습니다.

이제 toString()방법 :

  1. 클래스 인스턴스가 인터페이스 (또는 특수한 유형의 인터페이스 인 주석)를 나타내는 경우를 toString()반환합니다 "interface " + getName(). 프리미티브 인 경우 간단히 반환합니다 getName(). 그것이 다른 것이라면 (클래스 타입이라면 꽤 이상한 것이더라도) 반환합니다 "class " + getName().

그만큼 getCanonicalName()방법 :

  1. 최상위 클래스 및 인터페이스의 경우 getCanonicalName() 메소드는 메소드가 리턴하는 내용 만 getName()리턴합니다.
  2. 그만큼 getCanonicalName() 메소드는 null익명 또는 로컬 클래스 및 해당 클래스의 배열 클래스에 대해 리턴 합니다.
  3. 내부 및 중첩 클래스 및 인터페이스의 경우이 getCanonicalName()메소드는getName() 컴파일러가 도입 한 달러 기호를 점으로 대체하는 .
  4. 배열 클래스의 경우, 구성 요소 유형의 표준 이름이 인 경우 getCanonicalName()메소드가 리턴 null합니다 null. 그렇지 않으면, 구성 요소 유형의 정식 이름 뒤에을 리턴합니다 [].

getSimpleName()방법 :

  1. 최상위, 중첩, 내부 및 로컬 클래스의 경우 getSimpleName() 경우 소스 파일에 작성된 클래스의 이름을 반환합니다.
  2. 익명 클래스의 getSimpleName()경우 빈을 반환합니다.String .
  3. 람다 클래스의 경우 패키지 이름없이 반환 getSimpleName()되는 getName()것을 반환합니다. 이것은별로 이해가되지 않으며 나에게 버그처럼 보이지만 전화하는 데는 아무런 의미가 없습니다.getSimpleName() 람다 클래스를 필요 .
  4. 배열 클래스의 getSimpleName()경우이 메소드는 구성 요소 클래스의 단순 이름과 그 뒤에 오는 이름을 리턴합니다 []. 이것은 구성 요소 유형이 익명 클래스 인 배열 클래스가 []단순한 이름과 같은 재미있는 부작용을 가지고 있습니다.

답변

Nick Holt의 관찰 외에도 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()이 이름 또는 빈 문자열을 반환합니다. JLS 섹션 3.8에 따라 식별자의 유효한 부분 $이기 때문에 간단한 이름에 이와 같이 쓰면 if 가 포함될 수 있습니다 (추천하지 않더라도).$

  • 에 따르면 JLS 섹션 6.7 , 모두 a.b.C.D와는 a.b.C.D.D.D정규화 된 이름 ,하지만 a.b.C.D정식 이름D. 따라서 모든 정식 이름은 정규화 된 이름이지만 대화가 항상 사실은 아닙니다. Class.getCanonicalName()정식 이름 또는을 반환합니다 null.

  • Class.getName()JLS 섹션 13.1에 지정된대로 이진 이름 을 반환하도록 문서화되어 있습니다. 이 경우에는 반환 을 위해 및 위해 .a.b.C$DD[La.b.C$D;D[]

  • 이 대답 은 동일한 클래스 로더에 의해로드 된 두 클래스가 동일한 표준 이름 이지만 별개의 이진 이름 을 가질 수 있음을 보여줍니다 . 어떤 이름도 다른 이름을 확실하게 추론하기에는 충분하지 않습니다. 정식 이름이 있으면 이름의 어느 부분이 패키지이고 어떤 부분이 클래스를 포함하는지 알 수 없습니다. 이진 이름이 있으면 $구분자로 소개 된 것과 간단한 이름의 일부를 알 수 없습니다 . (클래스 파일 을 저장 바이너리 이름클래스 자체 와 그 둘러싸는 클래스 런타임이 할 수 있습니다, 이 구별을 .)

  • 익명 클래스로컬 클래스 에는 정규화 된 이름이 없지만 이진 이름이 있습니다. 이러한 클래스 안에 중첩 된 클래스도 마찬가지입니다. 모든 클래스에는 이진 이름이 있습니다.

  • 실행 javap -v -privatea/b/C.class프로그램은 바이트 코드의 유형을 의미하는 것이 d아니라 La/b/C$D;어레이의 ds[La/b/C$D;. 이를 디스크립터 라고 하며 JVMS 섹션 4.3에 지정되어 있습니다.

  • a/b/C$D두 가지 설명자 모두에 사용되는 클래스 이름 은 이진 이름 으로 대체 .하여 얻는 것 /입니다. JVM 스펙은 분명히 이것을 이진 이름내부 형식 이라고 부릅니다 . JVMS 섹션 4.2.1에서는이를 설명하고 이진 이름과의 차이는 역사적인 이유로 인한 것임을 설명합니다.

  • 일반적인 파일 이름 기반 클래스 로더 중 하나에있는 클래스 의 파일 이름/이진 이름의 내부 형식으로 디렉토리 구분 기호로 해석 하고 파일 이름 확장자 .class를 추가하면 얻을 수있는 것입니다. 문제의 클래스 로더가 사용하는 클래스 경로를 기준으로 해결되었습니다.


답변

이것은 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[]


답변

것을주의하는 것이 재미있다 getCanonicalName()getSimpleName()올릴 수 있습니다InternalError 클래스 이름이 올바르지 않은 경우에. 이는 Java 이외의 일부 JVM 언어 (예 : Scala)에서 발생합니다.

다음을 고려하십시오 (Java 8의 경우 Scala 2.11).

scala> case class C()
defined class C

scala> val c = C()
c: C = C()

scala> c.getClass.getSimpleName
java.lang.InternalError: Malformed class name
  at java.lang.Class.getSimpleName(Class.java:1330)
  ... 32 elided

scala> c.getClass.getCanonicalName
java.lang.InternalError: Malformed class name
  at java.lang.Class.getSimpleName(Class.java:1330)
  at java.lang.Class.getCanonicalName(Class.java:1399)
  ... 32 elided

scala> c.getClass.getName
res2: String = C

이는 혼합 언어 환경이나 바이트 코드를 동적으로로드하는 환경 (예 : 앱 서버 및 기타 플랫폼 소프트웨어)에 문제가 될 수 있습니다.


답변

    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