Jaka jest różnica między a.getClass () i A.class w Javie?


102

W Javie, jakie zalety / wady istnieją wokół wyboru użycia a.getClass()lub A.class? Każda z nich może być używana wszędzie tam, gdzie Class<?>jest oczekiwane, ale wyobrażam sobie, że użycie obu w różnych okolicznościach przyniosłoby wydajność lub inne subtelne korzyści (tak jak w przypadku Class.forName()i ClassLoader.loadClass().

Odpowiedzi:


163

Nie porównałbym ich pod względem zalet / wad, ponieważ mają różne cele i rzadko można dokonać „wyboru” między nimi.

  • a.getClass()zwraca typ wykonania z a. To znaczy, jeśli masz A a = new B();wtedy a.getClass()zwróci Bklasę.

  • A.classocenia Aklasę statycznie i jest używany do innych celów często związanych z refleksją.

Jeśli chodzi o wydajność, może istnieć wymierna różnica, ale nic o tym nie powiem, ponieważ ostatecznie jest to zależne od JVM i / lub kompilatora.


Ten post został przepisany jako artykuł tutaj .


2
A co powiesz A.class.getClass()?
user1870400

5
To dałoby Classobiekt klasy reprezentującej A.classobiekt, który ponownie jest instancją java.lang.Class.
aioobe

A co powiesz A.getClass().class?
Shikhar Mainalee

@ShikharMainalee, to nie ma sensu, jeśli Ajest to klasa. W getClassklasach nie ma metody statycznej .
aioobe

jak to się ma do sugestii tutaj , że one również wskazują na różne ładowarki klasy. Może to być również znacząca różnica między nimi?
Jim

32

W rzeczywistości różnią się pod względem miejsca, w którym można ich używać. A.classdziała w czasie kompilacji, podczas gdy a.getClass()wymaga wystąpienia typu Ai działa w czasie wykonywania.

Może również wystąpić różnica w wydajności. While A.classmoże zostać rozwiązany przez kompilator, ponieważ zna rzeczywisty typ A, a.getClass()jest wywołaniem metody wirtualnej w czasie wykonywania.

Dla porównania kompilator przeznaczony dla kodu bajtowego zazwyczaj emituje następujące instrukcje dotyczące Integer.getClass():

aload_1
invokevirtual   #3; //Method java/lang/Object.getClass:()Ljava/lang/Class;

oraz następujące dla Integer.class:

//const #3 = class  #16;    //  java/lang/Integer

ldc_w   #3; //class java/lang/Integer

Pierwsza z nich wymagałaby zazwyczaj wysyłania metody wirtualnej i dlatego prawdopodobnie wykonanie jej zajęłoby więcej czasu. To jednak ostatecznie zależy od JVM.


1
[1] technicznie specyfikacja języka Java nie wspomina w ogóle o żadnej stałej puli ...
aioobe,

@aioobe: Myślę, że masz rację, dlatego sprawdziłem kod bajtowy wygenerowany przez Sun JDK.
Tomasz Nurkiewicz

1
... co z technicznego punktu widzenia również nie dostarcza autorytatywnej odpowiedzi, ponieważ specyfikacja języka Java nie wspomina nawet o kodzie bajtowym, jeśli chodzi o semantykę.
aioobe

@aioobe: Rozumiem. Nigdy nie wspominałem o JLS, tylko empirycznie sprawdziłem, jak to działa, bo nie byłem pewien. Program operacyjny pyta o wydajność, a sprawdzenie kodu bajtowego wydawało się dobrym pomysłem. Zapraszam do edycji mojego posta lub usunięcia niejednoznacznych stwierdzeń
Tomasz Nurkiewicz

1
cofnij, jeśli ci się nie podoba ;-)
aioobe

8

spójrz na poniższe przykłady

a.getClass()!= A.class, tj. a nie jest instancją A, ale anonimowej podklasy A

a.getClass() wymaga wystąpienia typu A.


4

Użyj, a.getClassgdy masz instancję klasy / typu i chcesz uzyskać jej dokładny typ. while a.classjest używany, gdy masz typedostępne i chcesz utworzyć jego instancję. Zwraca
również getClass()typ instancji środowiska .classuruchomieniowego, podczas gdy jest oceniany w czasie kompilacji.
Biorąc pod uwagę wydajność getClass()i .class, .classma lepszą wydajność niż getClass() .
Przykład:

public class PerfomanceClass {

    public static void main(String[] args) {
        // TODO Auto-generated method stub

        long time=System.nanoTime();
        Class class1="String".getClass();
        class1="String".getClass();
        class1="String".getClass();
        class1="String".getClass();

        System.out.println("time (getClass()) :"+(System.nanoTime()-time)+" ns");     


        long time2=System.nanoTime();
        Class class2=String.class;
        class2=String.class;
        class2=String.class;
        class2=String.class;

        System.out.println("time (.class):"+(System.nanoTime()-time2)+" ns");
    }

}

Wynik :

time (getClass()) : 79410 ns
time (.class)     : 8032 ns

1

Jest jedna różnica, którą chciałbym dodać. Powiedzmy, że masz klasę konstruktora, jak pokazano poniżej, z superklasą, która przyjmuje obiekt Class. Chcesz, aby za każdym razem, gdy tworzony był obiekt podklasy, obiekt klasy podklasy był przekazywany do superklasy. Poniższy kod nie zostanie skompilowany, ponieważ nie można wywołać metody instancji w konstruktorze. W takim przypadku, jeśli zastąpi myObject.getClass()się MyClass.class. Będzie działać idealnie.

Class MyClass
{
    private MyClass myObject = new MyClass();
    public MyClass()
    {
        super(myObject.getClass()); //error line compile time error
    }
}

2
Mając instancję tej samej klasy, co zmienne instancji tej samej klasy… Podczas rekurencyjnego tworzenia obiektów zabraknie miejsca na stosie.
Aniket Thakur,

1

Co ciekawe, różnice w działaniu, o których mowa w powyższym przykładzie, wydają się mieć inne przyczyny. Używając 3 różnych klas, średnia wydajność będzie prawie taka sama:

import java.util.LinkedHashMap;
public class PerfomanceClass {

public static void main(String[] args) {

    long time = System.nanoTime();
    Class class1 = "String".getClass();
    Class class11 = "Integer".getClass();
    Class class111 = "LinkedHashMap".getClass();

    System.out.println("time (getClass()) :" + (System.nanoTime() - time) + " ns");

    long time2 = System.nanoTime();
    Class class2 = String.class;
    Class class22 = Integer.class;
    Class class222 = LinkedHashMap.class;

    System.out.println("time (.class):" + (System.nanoTime() - time2) + " ns");
} }

Wynik będzie podobny do:

time (getClass()) :23506 ns 
time (.class):23838 ns

A zmiana kolejności połączeń spowoduje nawet getClass()szybsze działanie.

import java.util.LinkedHashMap;

public class PerfomanceClass {

public static void main(String[] args) {
    long time2 = System.nanoTime();
    Class class2 = LinkedHashMap.class;

    System.out.println("time (.class):" + (System.nanoTime() - time2) + " ns");

    long time = System.nanoTime();
    Class class1 = "LinkedHashMap".getClass();

    System.out.println("time (getClass()) :" + (System.nanoTime() - time) + " ns");
}}

Wynik:

time (.class):33108 ns
time (getClass()) :6622 ns

„Korzystanie z 3 różnych klas”. Ale w sekcji getClass () nie używasz 3 różnych klas. To wszystko jest typu String i dlatego getClass zwróci java.lang.String dla wszystkich instancji.
Kilian,

1

p.getClass(), gdzie pjest instancją obiektu, zwraca klasę środowiska wykonawczego tego obiektu p. pnie może być typem, który spowoduje błąd w czasie kompilacji, powinien to być instancja obiektu.

// B extends A
A a = new B();
System.out.println(a.getClass());
//output: class B

p.classjest wyrażeniem. .classNazywa składnia klasa. pjest typem. Może to być nazwa klasy, interfejsu lub tablicy, a nawet typ prymitywny. a.getClass() == B.class.

Jeśli typ jest dostępny i istnieje instancja, można użyć getClassmetody, aby uzyskać nazwę typu. W przeciwnym razie użyj .classskładni

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.