Qual è la differenza tra a.getClass () e A.class in Java?


101

In Java quali vantaggi / svantaggi esistono riguardo alla scelta di utilizzare a.getClass()o A.class? Entrambi possono essere usati ovunque Class<?>ci si aspetti a, ma immagino che ci sarebbero prestazioni o altri sottili vantaggi nell'usarli entrambi in circostanze diverse (proprio come ci sono con Class.forName()e ClassLoader.loadClass().

Risposte:


163

Non li confronterei in termini di pro / contro poiché hanno scopi diversi e raramente c'è una "scelta" da fare tra i due.

  • a.getClass()restituisce il tipo di runtime di a. Vale a dire, se si dispone A a = new B();poi a.getClass()tornerà la Bclasse.

  • A.classvaluta staticamente la Aclasse e viene utilizzato per altri scopi spesso legati alla riflessione.

In termini di prestazioni, potrebbe esserci una differenza misurabile, ma non ne parlerò perché alla fine dipende da JVM e / o compilatore.


Questo post è stato riscritto come articolo qui .


2
Che ne dici A.class.getClass()?
user1870400

5
Questo ti darebbe l' Classoggetto della classe che rappresenta l' A.classoggetto che è di nuovo un'istanza di java.lang.Class.
aioobe

Che ne dici A.getClass().class?
Shikhar Mainalee

@ShikharMainalee, non ha davvero senso se Aè una classe. Non esiste un getClassmetodo statico sulle classi.
aioobe

in che modo questo si collega al suggerimento qui che indicano anche diversi caricatori di classi. Questa potrebbe anche essere una differenza significativa tra i due?
Jim

32

In realtà sono diversi per quanto riguarda dove puoi usarli. A.classfunziona in fase di compilazione mentre a.getClass()richiede un'istanza di tipo Ae funziona in fase di esecuzione.

Potrebbe esserci anche una differenza di prestazioni. Sebbene A.classpossa essere risolto dal compilatore perché conosce il tipo effettivo di A, a.getClass()è una chiamata al metodo virtuale in fase di esecuzione.

Per riferimento, un compilatore che ha come target bytecode in genere emette le seguenti istruzioni per Integer.getClass():

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

e quanto segue per Integer.class:

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

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

Il primo implicherebbe tipicamente l'invio di un metodo virtuale e quindi presumibilmente richiederebbe più tempo per l'esecuzione. Ciò alla fine dipende dalla JVM.


1
[1] tecnicamente la specifica del linguaggio Java non menziona affatto alcun pool costante ...
aioobe

@ aioobe: immagino tu abbia ragione, ecco perché ho controllato il bytecode generato da Sun JDK.
Tomasz Nurkiewicz

1
... che tecnicamente parlando non fornisce nemmeno una risposta autorevole, dal momento che la specifica del linguaggio Java non menziona nemmeno il bytecode quando si tratta di semantica.
aioobe

@ aioobe: ho capito il tuo punto. Non ho mai menzionato JLS, ho solo controllato empiricamente come funziona, perché non ero sicuro. L'OP chiede informazioni sulle prestazioni e l'esame del bytecode sembrava una buona idea. Sentiti libero di modificare il mio post o rimuovere dichiarazioni ambigue
Tomasz Nurkiewicz

1
torna indietro se non ti piace ;-)
aioobe

8

dai un'occhiata agli esempi di seguito

a.getClass()!= A.class, cioè a non è un'istanza di A ma di una sottoclasse anonima di A

a.getClass() richiede un'istanza di tipo A


4

Da utilizzare a.getClassquando si dispone di un'istanza di classe / tipo e si desidera ottenerne il tipo esatto. while a.classviene utilizzato quando ne hai a typedisposizione e vuoi crearne un'istanza. Restituisce
anche il getClass()tipo di istanza runtime mentre .classviene valutato in fase di compilazione.
Considerando le prestazioni di getClass()e .class, .classha prestazioni migliori rispetto a getClass() .
Esempio :

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

}

Produzione :

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

1

C'è una differenza che vorrei aggiungere. Supponiamo che tu abbia una classe a costruttore come mostrato di seguito con una super classe che accetta un oggetto Class. Si desidera che ogni volta che viene creato un oggetto di sottoclasse, l'oggetto di classe della sottoclasse venga passato alla superclasse. Il codice seguente non verrà compilato poiché non è possibile chiamare un metodo di istanza in un costruttore. In tal caso, se sostituisci myObject.getClass()con MyClass.class. Funzionerà perfettamente.

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

2
Avere istanze della stessa classe come variabili di istanza della stessa classe .... Finirai lo spazio di heap mentre crei oggetti in modo ricorsivo.
Aniket Thakur

1

È interessante notare che le differenze di prestazioni menzionate nell'esempio sopra sembrano essere correlate ad altri motivi. Utilizzando 3 classi diverse, in media le prestazioni saranno quasi le stesse:

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

L'output sarà qualcosa del tipo:

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

E cambiare l'ordine delle chiamate risulterà addirittura getClass()più veloce.

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

Produzione:

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

"Utilizzo di 3 classi diverse". Ma nella sezione getClass () non stai usando 3 classi differenti. Questi sono tutti String e quindi getClass restituirà java.lang.String per tutte le istanze.
Kilian

1

p.getClass(), dove pè un'istanza di un oggetto, restituisce la classe di runtime di questo oggetto p. pnon può essere un tipo che causerà un errore in fase di compilazione, dovrebbe essere un'istanza di un oggetto.

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

p.classè un'espressione. Si .classchiama sintassi della classe. pè un tipo. Può essere il nome di una classe, interfaccia o array e persino un tipo primitivo. a.getClass() == B.class.

Se è disponibile un tipo ed è presente un'istanza, è possibile utilizzare il getClassmetodo per ottenere il nome del tipo. Altrimenti, usa la .classsintassi

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.