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
다음을 수행하십시오.
- 클래스 객체가 기본 유형 또는를 나타내는 경우
void
네 가지 메소드 모두 이름을 반환합니다.
이제 getName()
메소드 의 규칙 :
- 모든 람다 및 비 배열 클래스 또는 인터페이스 (예 : 최상위, 중첩, 내부, 로컬 및 익명)는
getName()
패키지 이름 뒤에 점 (패키지가있는 경우) 인 이름 (에 의해 반환 됨 )을 갖습니다. ) 다음에 컴파일러에서 생성 한 클래스 파일 이름 (접미사없이)이 붙습니다.class
. 패키지가 없으면 단순히 클래스 파일의 이름입니다. 클래스가 내부, 중첩, 로컬 또는 익명 클래스 인 경우 컴파일러는$
클래스 파일 이름으로 하나 이상을 생성해야합니다 . 익명 클래스의 경우 클래스 이름은 달러 기호와 숫자로 끝납니다. - Lambda 클래스 이름은 일반적으로 예측할 수 없으므로 어쨌든 신경 쓰지 않아야합니다. 정확하게 그들의 이름은 다음에 오는 클래스의 이름입니다.
$$Lambda$
, 뒤에, 숫자, 슬래시, 다른 숫자가옵니다. - 프리미티브의 클래스 기술자는
Z
위해boolean
,B
위해byte
,S
위해short
,C
위해char
,I
위해int
,J
위해long
,F
위해float
및D
위해double
. 비 배열 클래스 및 인터페이스의 경우 클래스 디스크립터L
다음에getName()
뒤에 오는 것이 표시됩니다.;
. 배열 클래스의 경우 클래스 설명자[
뒤에 구성 요소 유형의 클래스 설명자 (자체 배열 클래스 일 수 있음)가옵니다. - 배열 클래스의 경우
getName()
메소드는 클래스 디스크립터를 리턴합니다. 이 규칙은 구성 요소 유형이 람다 (버그 일 가능성이있는) 인 배열 클래스에만 실패하는 것으로 보이지만, 구성 요소 유형이 람다 인 배열 클래스가 존재하더라도 아무 의미가 없기 때문에 이것은 중요하지 않습니다.
이제 toString()
방법 :
- 클래스 인스턴스가 인터페이스 (또는 특수한 유형의 인터페이스 인 주석)를 나타내는 경우를
toString()
반환합니다"interface " + getName()
. 프리미티브 인 경우 간단히 반환합니다getName()
. 그것이 다른 것이라면 (클래스 타입이라면 꽤 이상한 것이더라도) 반환합니다"class " + getName()
.
그만큼 getCanonicalName()
방법 :
- 최상위 클래스 및 인터페이스의 경우
getCanonicalName()
메소드는 메소드가 리턴하는 내용 만getName()
리턴합니다. - 그만큼
getCanonicalName()
메소드는null
익명 또는 로컬 클래스 및 해당 클래스의 배열 클래스에 대해 리턴 합니다. - 내부 및 중첩 클래스 및 인터페이스의 경우이
getCanonicalName()
메소드는getName()
컴파일러가 도입 한 달러 기호를 점으로 대체하는 . - 배열 클래스의 경우, 구성 요소 유형의 표준 이름이 인 경우
getCanonicalName()
메소드가 리턴null
합니다null
. 그렇지 않으면, 구성 요소 유형의 정식 이름 뒤에을 리턴합니다[]
.
getSimpleName()
방법 :
- 최상위, 중첩, 내부 및 로컬 클래스의 경우
getSimpleName()
경우 소스 파일에 작성된 클래스의 이름을 반환합니다. - 익명 클래스의
getSimpleName()
경우 빈을 반환합니다.String
. - 람다 클래스의 경우 패키지 이름없이 반환
getSimpleName()
되는getName()
것을 반환합니다. 이것은별로 이해가되지 않으며 나에게 버그처럼 보이지만 전화하는 데는 아무런 의미가 없습니다.getSimpleName()
람다 클래스를 필요 . - 배열 클래스의
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$D
D
[La.b.C$D;
D[]
-
이 대답 은 동일한 클래스 로더에 의해로드 된 두 클래스가 동일한 표준 이름 이지만 별개의 이진 이름 을 가질 수 있음을 보여줍니다 . 어떤 이름도 다른 이름을 확실하게 추론하기에는 충분하지 않습니다. 정식 이름이 있으면 이름의 어느 부분이 패키지이고 어떤 부분이 클래스를 포함하는지 알 수 없습니다. 이진 이름이 있으면
$
구분자로 소개 된 것과 간단한 이름의 일부를 알 수 없습니다 . (클래스 파일 을 저장 바이너리 이름 의 클래스 자체 와 그 둘러싸는 클래스 런타임이 할 수 있습니다, 이 구별을 .) -
익명 클래스 와 로컬 클래스 에는 정규화 된 이름이 없지만 이진 이름이 있습니다. 이러한 클래스 안에 중첩 된 클래스도 마찬가지입니다. 모든 클래스에는 이진 이름이 있습니다.
-
실행
javap -v -private
에a/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 ()을 설명하는 가장 좋은 문서입니다.
// 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