Java Sınıfında standart ad, basit ad ve sınıf adı arasındaki fark nedir?


973

Java'da, bunlar arasındaki fark nedir:

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

Javadoc birkaç kez kontrol ettim ve henüz bu asla iyi açıklar. Ayrıca bir test yaptım ve bu, bu yöntemlerin çağrılmasıyla ilgili gerçek bir anlam ifade etmedi.



218
Bence bu makul bir soru. Javadoc, üçü arasındaki farkı açıklamak için iyi bir iş çıkarmıyor.
Graham Borland

1
Bkz. - docs.oracle.com/javase/6/docs/api/java/lang/Class.html veya belki sadece bir test yazın.
Nick Holt

7
@GrahamBorland Javadoc, "Java Dil Spesifikasyonu tarafından tanımlandığı şekliyle" diyor - böylece bu belgeye bakabilirsiniz. Sadece tıklanabilir bir bağlantı olmadığı için, insanlar hala minimum çaba gösterebilir ve ilk arama motoru sonucuna tıklayabilirler.
vbence

66
@vbence: Çoğu insan bu tür önemsiz şeyler için JLS'ye bakmak yerine işleri halletmeyi tercih eder. Bu nedenle, bu ilk Google sonucudur :)
pathikrit

Yanıtlar:


1130

Bir şeyden emin değilseniz, önce bir test yazmayı deneyin.

Bunu ben yaptım:

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

Baskılar:

int.class (ilkel):
    getName (): int
    getCanonicalName (): int
    getSimpleName (): int
    getTypeName (): int

String.class (normal sınıf):
    getName (): java.lang.String
    getCanonicalName (): java.lang.String
    getSimpleName (): Dize
    getTypeName (): java.lang.String

java.util.HashMap.SimpleEntry.class (iç içe sınıf):
    getName (): java.util.AbstractMap $ SimpleEntry
    getCanonicalName (): java.util.AbstractMap.SimpleEntry
    getSimpleName (): SimpleEntry
    getTypeName (): java.util.AbstractMap $ SimpleEntry

yeni java.io.Serializable () {}. getClass () (anonim iç sınıf):
    getName (): ClassNameTest $ 1
    getCanonicalName (): null
    getSimpleName ():    
    getTypeName (): SınıfAdıTest $ 1

Son blokta getSimpleNameboş bir dize döndüren boş bir giriş var .

Buna bakılan sonuç:

  • adı dinamik, örneğin, bir çağrı ile sınıf yüklemek için kullandığımız addır Class.forNamevarsayılan ClassLoader. Belli bir kapsam dahilinde ClassLoader, tüm sınıfların benzersiz isimleri vardır.
  • kurallı ad bir ithalat açıklamada kullanılacak adıdır. toStringİşlemler sırasında veya günlüğe kaydetme sırasında faydalı olabilir . Ne zaman javacderleyici bir sınıf yolunda tam bir görünümü vardır, derleme zamanında tam sınıf ve paket isimleri çatıştığı ile bunun içinde kanonik isimlerin benzersizliği zorlar. Ancak JVM'ler bu tür ad çakışmaları kabul etmelidir ve bu nedenle kanonik adlar a içindeki sınıfları benzersiz olarak tanımlamaz ClassLoader. (Gez, bu alıcı için daha iyi bir isim olurdu getJavaName; ancak bu yöntem, JVM'nin yalnızca Java programlarını çalıştırmak için kullanıldığı bir zamandan başlıyor.)
  • Basit adı gevşek sırasında yararlı olabileceğini yine sınıfını tanımlayan toStringveya kayıt işlemleri ancak benzersiz olması garanti edilmez.
  • tür adı " türün adı için bilgilendirici bir dize" döndürür, "toString () gibidir: tamamen bilgilendiricidir ve sözleşme değeri yoktur" (sir4ur0n tarafından yazıldığı gibi)

5
Sizce ekstra ne gerekiyor?
Nick Holt

2
@AnupamSaini evet. Gerçek bir uygulamada böyle bir paket adına sahip olmak çılgınca olurdu.
Jayen

3
Ancak bu çılgınca olur, kötü niyetli bir aktörün çalışmasına izin verecek bir tür varsayım. Birisi "ah, sınıfların asla küçük harflerle başlamayacağını biliyoruz, paketler asla başkentlerle başlamayacak" diyor. Verilen, sınıf yükleyicinize erişimi olan kötü niyetli bir aktör zaten korkunç şeyler yapabilir, bu yüzden muhtemelen kesinlikle korkunç bir varsayım değildir.
corsiKa

2
@PieterDeBie Nasıl yani? Bilmeniz gereken tek şey test etmek istediğiniz yöntem adıdır.
fool4jesus

20
Java 8 de getTypeName () ekledi ... bunun için güncellemek ister misiniz?
Theodore Murdock

90

toString()Önceki iki yanıtı tamamlamak için yerel sınıflar, lambdalar ve yöntem ekleme . Ayrıca, lambda dizileri ve anonim sınıfların dizilerini ekliyorum (pratikte herhangi bir anlam ifade etmiyor):

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

Bu tam çıktı:

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;

İşte kurallar. İlk olarak, ilkel tiplerle başlayalım ve void:

  1. Sınıf nesnesi ilkel bir türü temsil ediyorsa veya voiddört yöntemin tümü de adını döndürür.

Şimdi getName()yöntemin kuralları :

  1. Her lambda olmayan ve dizi olmayan sınıf veya arabirimin (yani, üst düzey, iç içe, iç, yerel ve anonim), getName()paket adından sonra bir nokta (bir paket varsa ) olan bir adı (döndürülen ) vardır. ), ardından derleyici tarafından oluşturulan sınıf dosyasının adını (sonek olmadan .class) takip eder. Paket yoksa, sınıf dosyasının adıdır. Sınıf iç, iç içe, yerel veya anonim bir sınıfsa, derleyici $sınıf dosyası adında en az bir tane oluşturmalıdır . Anonim sınıflar için sınıf adının dolar işareti ve ardından bir sayı ile biteceğini unutmayın.
  2. Lambda sınıfı isimleri genellikle tahmin edilemez ve yine de onları önemsememelisiniz. Tam olarak, isimleri ekteki sınıfın adıdır, ardından$$Lambda$ , ardından bir sayı, ardından bir eğik çizgi ve ardından başka bir sayıdır.
  3. İlkel sınıf tanımlayıcısı vardır Ziçin boolean, Biçin byte, Siçin short, Ciçin char, Iiçin int, Jiçin long, Fiçin floatve Diçin double. Dizi olmayan sınıflar ve arayüzler için, sınıf tanımlayıcısının Lardından verilenleri getName()takip eder ;. Dizi sınıfları için, sınıf tanımlayıcısını, [bileşen türünün sınıf tanımlayıcısı (kendisi de başka bir dizi sınıfı olabilir) takip eder.
  4. Dizi sınıfları için getName()yöntem, sınıf tanımlayıcısını döndürür. Bu kural yalnızca bileşen türü bir lambda olan (muhtemelen bir hata olan) dizi sınıfları için başarısız gibi görünüyor, ancak umarım bu önemli olmamalıdır, çünkü bileşen türü bir lambda olan dizi sınıflarının varlığında bile bir nokta yoktur.

Şimdi, toString()yöntem:

  1. Sınıf örneği bir arabirimi (veya özel bir arabirim türü olan ek açıklama) temsil ediyorsa, toString()döndürür "interface " + getName(). Bir ilkel ise, basitçe geri döner getName(). Başka bir şeyse (oldukça garip olsa bile bir sınıf türü), geri döner "class " + getName().

getCanonicalName()yöntem:

  1. Üst düzey sınıflar ve arabirimler için, getCanonicalName()yöntem yalnızca getName()yöntemin döndürdüğünü döndürür.
  2. getCanonicalName()yöntemi döndürürnull anonim veya yerel sınıfları için ve bu dizisi sınıfları için.
  3. İç ve iç içe sınıflar ve arabirimler için, getCanonicalName()yöntem, getName()yöntemin derleyici tarafından sunulan dolar işaretlerini noktalarla değiştireceği yöntemi döndürür .
  4. Dizi sınıfları için, bileşen türünün kurallı adı ise getCanonicalName()yöntem döndürülür . Aksi takdirde, bileşen türünün kurallı adını ve arkasından döner .nullnull[]

getSimpleName()yöntem:

  1. Üst düzey, iç içe, iç ve yerel sınıflar için getSimpleName() kaynak dosyada yazıldığı gibi sınıfın adını döndürür.
  2. Anonim sınıflar için getSimpleName()boş döndürürString .
  3. Lambda sınıfları için getSimpleName()sadece getName()paket adı olmadan ne döndüreceğini döndürür . Bu çok mantıklı değil ve benim için bir hata gibi görünüyor, ama aramanın bir anlamı yokgetSimpleName() başlamak için bir lambda sınıfını bir .
  4. Dizi sınıfları için getSimpleName()yöntem, bileşen sınıfının basit adını ve arkasından döner []. Bu, bileşen türü anonim bir sınıf olan dizi sınıflarının []basit adları gibi komik / garip yan etkiye sahiptir .

2
… replacing the dollar-signs by dots: Yalnızca sınırlayıcı olarak verilen dolar işaretleri değiştirilir. Basit bir ismin parçası olarak dolarlarınız olabilir ve bunlar yerinde kalacaktır.
MvG

Oh hayır! Sınıf adının bir parçası olarak! Bir sınıf transformatörü geliştiriyorum ve sınıf ve paket adı arasında '/' güvenli bir sınırlayıcı olacağını düşündüm: /
José Roberto Araújo Júnior

81

Nick Holt'un gözlemlerine ek olarak, Arrayveri türü için birkaç örnek çalıştırdım :

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

Kod pasajının üstündeki baskılar:

[I
int[]
int[]

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

28
Yukarıdaki cevaba bir düzenleme önermem daha iyi olmaz.
LoKi

17

Çok çeşitli farklı adlandırma şemaları ile kafam karıştı ve bu soruyu burada bulduğumda kendi sorumu sormak ve cevaplamak üzereydim. Bulgularımın yeterince iyi olduğunu düşünüyorum ve burada olanları tamamlıyor. Odak noktam, çeşitli terimlerle ilgili belgeler aramak ve başka yerlerde ortaya çıkacak daha alakalı terimler eklemek.

Aşağıdaki örneği düşünün:

package a.b;
class C {
  static class D extends C {
  }
  D d;
  D[] ds;
}
  • Basit adı arasında Dolduğunu D. Bu sadece sınıfı ilan ederken yazdığınız bölüm. Anonim sınıfların basit bir adı yoktur. Class.getSimpleName()bu adı veya boş dizeyi döndürür. JLS bölüm 3.8'e göre bir tanımlayıcının geçerli bir parçası olduğu için (biraz cesaret kırılmış olsa bile) basit adın $bunu böyle yazarsanız içermesi mümkündür .$

  • Göre JLS bölüm 6.7 , hem a.b.C.Dve a.b.C.D.D.Dolacağını tam isimleri , fakat sadece a.b.C.Dolurdu kurallı ad arasında D. Bu yüzden her kanonik isim tam olarak nitelenmiş bir isimdir, ancak tersi her zaman doğru değildir. Class.getCanonicalName()standart adı döndürür veya null.

  • Class.getName()JLS bölüm 13.1'de belirtildiği gibi ikili adı döndürmek üzere belgelenmiştir . Bu durumda döndürür için ve için .a.b.C$DD[La.b.C$D;D[]

  • Bu cevap , aynı sınıf yükleyici tarafından yüklenen iki sınıfın aynı kanonik ada sahip fakat farklı ikili adlara sahip olabileceğini göstermektedir . Her iki ad da diğerini güvenilir bir şekilde çıkarmak için yeterli değildir: kanonik adınız varsa, adın hangi bölümlerinin paketler ve hangilerinin sınıf içerdiğini bilmezsiniz. İkili adınız varsa, hangilerinin $ayırıcılar olarak tanıtıldığını ve hangilerinin basit adın bir parçası olduğunu bilmiyorsunuz . (Sınıf dosyası , sınıfın kendisinin ve çevreleyen sınıfının ikili adını depolar ; bu da çalışma zamanının bu ayrımı yapmasını sağlar .)

  • Anonim sınıfların ve yerel sınıfların tam nitelikli adları yoktur ancak yine de ikili bir adı vardır . Aynı şey, bu tür sınıfların içinde yuvalanmış sınıflar için de geçerlidir. Her sınıfın bir ikili adı vardır.

  • Çalıştırma javap -v -privateile a/b/C.classgösterir bayt kodu tipine değinmektedir olduğu dşekilde La/b/C$D;ve dizinin bu dsşekilde [La/b/C$D;. Bunlara tanımlayıcılar denir ve JVMS bölüm 4.3'te belirtilir .

  • Sınıf adı a/b/C$Dbu tariflerin hem kullanılan Eğer değiştirerek elde ediyoruz .tarafından /ikili adına. JVM spesifik olarak bunu ikili adın dahili formu olarak adlandırıyor . JVMS bölüm 4.2.1 bunu açıklar ve ikili addan farkın tarihsel nedenlerle olduğunu belirtir.

  • Dosya adı tipik dosya tabanlı sınıf yükleyici birinde bir sınıfın sen yorumlamak eğer ne olsun /bir dizin ayırıcı olarak ikili ismin iç formda ve dosya adı uzantısını eklemek .classbuna. Söz konusu sınıf yükleyicisi tarafından kullanılan sınıf yoluna göre çözümlenir.


3
JLS'ye atıfta bulunan ve uygun terminolojileri kullanan tek cevap olduğu için bu kabul edilen cevap olmalıdır.
John

10

Bu getName (), getSimpleName (), getCanonicalName () açıklayan bulduğum en iyi belge

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

3

Bunu not etmek ilginçtir getCanonicalName()ve getSimpleName()yükseltebilirInternalError sınıf adı şekil bozukluğuna zaman. Bu, Java dışındaki bazı JVM dillerinde, örneğin Scala için olur.

Aşağıdakileri düşünün (Java 8'de 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

Bu, karma dil ortamları veya bayt kodunu dinamik olarak yükleyen ortamlar, örneğin uygulama sunucuları ve diğer platform yazılımları için bir sorun olabilir.


1
    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

1
Yöntemin içindeki ilk iki satırClass<StringBuffer> clazz = StringBuffer.class
ThePyroEagle

1

getName () - bu Class nesnesi tarafından temsil edilen varlığın adını (sınıf, arabirim, dizi sınıfı, ilkel tür veya void) bir String olarak döndürür.

getCanonicalName () - Java Dil Belirtimi tarafından tanımlanan temel sınıfın standart adını döndürür.

getSimpleName () - temel sınıfın basit adını, yani kaynak kodunda verilen adı döndürür.

package com.practice;

public class ClassName {
public static void main(String[] args) {

  ClassName c = new ClassName();
  Class cls = c.getClass();

  // returns the canonical name of the underlying class if it exists
  System.out.println("Class = " + cls.getCanonicalName());    //Class = com.practice.ClassName
  System.out.println("Class = " + cls.getName());             //Class = com.practice.ClassName
  System.out.println("Class = " + cls.getSimpleName());       //Class = ClassName
  System.out.println("Class = " + Map.Entry.class.getName());             // -> Class = java.util.Map$Entry
  System.out.println("Class = " + Map.Entry.class.getCanonicalName());    // -> Class = java.util.Map.Entry
  System.out.println("Class = " + Map.Entry.class.getSimpleName());       // -> Class = Entry 
  }
}

Bir fark, anonim bir sınıf kullanırsanız, sınıfın adını kullanarak sınıf adını almaya çalışırken boş bir değer elde edebilmenizgetCanonicalName()

Başka bir gerçek, yöntemin iç sınıflar için getName()olan getCanonicalName()yöntemden farklı davranmasıdır .getName()çevreleyen sınıf kanonik adı ile iç sınıf basit adı arasındaki ayırıcı olarak bir dolar kullanır.

Java'da bir sınıf adı alma hakkında daha fazla bilgi için .

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.