Was ist der Unterschied zwischen a.getClass () und A.class in Java?


101

Welche Vor- und Nachteile gibt es in Java im Zusammenhang mit der Wahl der Verwendung a.getClass()oder A.class? Entweder kann verwendet werden, wo immer a Class<?>erwartet wird, aber ich stelle mir vor, dass die Verwendung beider unter verschiedenen Umständen (genau wie bei Class.forName()und) Leistung oder andere subtile Vorteile hätte ClassLoader.loadClass().

Antworten:


163

Ich würde sie nicht in Bezug auf Vor- / Nachteile vergleichen, da sie unterschiedliche Zwecke haben und es selten eine "Wahl" zwischen den beiden gibt.

  • a.getClass()Gibt den Laufzeittyp von zurück a. Dh wenn Sie A a = new B();dann haben, a.getClass()wird die BKlasse zurückgeben.

  • A.classwertet die AKlasse statisch aus und wird für andere Zwecke verwendet, die häufig mit Reflexion zusammenhängen.

In Bezug auf die Leistung mag es einen messbaren Unterschied geben, aber ich werde nichts dazu sagen, da es letztendlich JVM- und / oder Compiler-abhängig ist.


Dieser Beitrag wurde bereits als ein Artikel neu geschrieben hier .


2
Wie wäre es A.class.getClass()?
user1870400

5
Das würde Ihnen das ClassObjekt der Klasse geben, das das Objekt darstellt, von dem A.classwiederum eine Instanz ist java.lang.Class.
Aioobe

Wie wäre es A.getClass().class?
Shikhar Mainalee

@ShikharMainalee, das macht keinen Sinn, wenn Aes sich um eine Klasse handelt. Es gibt keine statische getClassMethode für Klassen.
Aioobe

In welcher Beziehung steht dies zu dem Vorschlag , dass sie auch auf verschiedene Klassenlader verweisen. Dies könnte auch ein signifikanter Unterschied zwischen den beiden sein?
Jim

32

Sie unterscheiden sich tatsächlich darin, wo Sie sie verwenden können. A.classFunktioniert zur Kompilierungszeit, a.getClass()erfordert eine Instanz vom Typ Aund zur Laufzeit.

Es kann auch einen Leistungsunterschied geben. Während A.classder Compiler ihn auflösen kann, weil er den tatsächlichen Typ von kennt A, a.getClass()findet zur Laufzeit ein virtueller Methodenaufruf statt.

Als Referenz gibt ein Compiler, der auf Bytecode abzielt, normalerweise die folgenden Anweisungen aus für Integer.getClass():

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

und folgendes für Integer.class:

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

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

Ersteres würde typischerweise einen virtuellen Methodenversand beinhalten und daher vermutlich länger dauern, um ausgeführt zu werden. Das ist aber letztendlich JVM-abhängig.


1
[1] Technisch gesehen erwähnt die Java-Sprachspezifikation überhaupt keinen konstanten Pool ...
aioobe

@aioobe: Ich denke, Sie haben Recht, deshalb habe ich den von Sun JDK generierten Bytecode überprüft.
Tomasz Nurkiewicz

1
... was technisch gesehen auch keine maßgebliche Antwort liefert, da in der Java-Sprachspezifikation der Bytecode in Bezug auf die Semantik nicht einmal erwähnt wird.
Aioobe

@aioobe: Ich verstehe deinen Standpunkt. Ich habe JLS nie erwähnt, nur empirisch überprüft, wie es funktioniert, da ich nicht sicher war. Das OP fragt nach der Leistung und die Prüfung des Bytecodes schien eine gute Idee zu sein. Fühlen Sie sich frei, meinen Beitrag zu bearbeiten oder mehrdeutige Aussagen zu entfernen
Tomasz Nurkiewicz

1
Rollback, wenn es dir nicht gefällt
;-)

8

Schauen Sie sich die folgenden Beispiele an

a.getClass()!= A.classdh a ist keine Instanz von A, sondern einer anonymen Unterklasse von A.

a.getClass() erfordert eine Instanz vom Typ A.


4

Verwenden a.getClassSie diese Option, wenn Sie eine Instanz von Klasse / Typ haben und den genauen Typ davon erhalten möchten. while a.classwird verwendet, wenn Sie typeverfügbar sind und eine Instanz davon erstellen möchten.
Auch getClass()gibt Laufzeittyp der Instanz , während .classbei der Kompilierung ausgewertet wird.
In Anbetracht Leistung getClass()und .class, .classhat eine bessere Leistung als getClass() .
Beispiel:

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

}

Ausgabe :

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

1

Es gibt einen Unterschied, den ich hinzufügen möchte. Angenommen, Sie haben eine Klasse als Konstruktor, wie unten gezeigt, mit einer Superklasse, die ein Class-Objekt akzeptiert. Sie möchten, dass das Klassenobjekt der Unterklasse bei jeder Erstellung eines Unterklassenobjekts an die Superklasse übergeben wird. Der folgende Code wird nicht kompiliert, da Sie keine Instanzmethode in einem Konstruktor aufrufen können. In diesem Fall , wenn Sie ersetzen myObject.getClass()mit MyClass.class. Es wird perfekt laufen.

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

2
Instanz derselben Klasse als Instanzvariablen derselben Klasse haben .... Ihnen wird der Heap-Speicherplatz ausgehen, wenn Sie rekursiv Objekte erstellen.
Aniket Thakur

1

Interessanterweise scheinen die im obigen Beispiel erwähnten Leistungsunterschiede mit anderen Gründen in Zusammenhang zu stehen. Bei Verwendung von 3 verschiedenen Klassen ist die Leistung im Durchschnitt nahezu gleich:

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

Die Ausgabe wird ungefähr so ​​aussehen:

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

Wenn Sie die Reihenfolge der Anrufe ändern, wird dies sogar getClass()schneller.

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

Ausgabe:

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

"Verwenden von 3 verschiedenen Klassen". Im Abschnitt getClass () verwenden Sie jedoch nicht drei verschiedene Klassen. Dies sind alles Zeichenfolgen und daher gibt getClass für alle Instanzen java.lang.String zurück.
Kilian

1

p.getClass(), wobei the peine Instanz eines Objekts ist, gibt die Laufzeitklasse dieses Objekts zurück p. pkann kein Typ sein, der einen Fehler bei der Kompilierung verursacht, es sollte eine Instanz eines Objekts sein.

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

p.classist ein Ausdruck. Das .classwird als Klassensyntax bezeichnet. pist ein Typ. Dies kann der Name einer Klasse, einer Schnittstelle oder eines Arrays und sogar ein primitiver Typ sein. a.getClass() == B.class.

Wenn ein Typ verfügbar ist und eine Instanz vorhanden ist, können Sie mithilfe der getClassMethode den Namen des Typs abrufen. Verwenden Sie andernfalls die .classSyntax

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.