In che modo il modificatore statico influisce su questo codice?


109

Ecco il mio codice:

class A {
    static A obj = new A();
    static int num1;
    static int num2=0;

    private A() {
        num1++;
        num2++;
    }
    public static A getInstance() {
        return obj;
    }
}

public class Main{
    public static void main(String[] arg) {
        A obj = A.getInstance();
        System.out.println(obj.num1);
        System.out.println(obj.num2);
    }
}

L'output è 1 0, ma non riesco a capire.

Qualcuno può spiegarmelo?


10
Bella domanda! Cosa dovremmo imparare da questo: non farlo! ;)
isnot2bad

Risposte:


116

In Java si svolgono due fasi: 1. Identificazione, 2. Esecuzione

  1. In fase di identificazione tutte le variabili statiche vengono rilevate e inizializzate con valori di default.

    Quindi ora i valori sono:
    A obj=null
    num1=0
    num2=0

  2. La seconda fase, l' esecuzione , inizia dall'alto verso il basso. In Java, l'esecuzione inizia dai primi membri statici.
    Ecco la tua prima variabile statica static A obj = new A();, quindi prima creerà l'oggetto di quella variabile e chiamerà il costruttore, da cui il valore di num1e num2diventa 1.
    E poi, di nuovo, static int num2=0;verrà eseguito, il che fa num2 = 0;.

Supponiamo ora che il tuo costruttore sia così:

 private A(){
    num1++;
    num2++;
    System.out.println(obj.toString());
 }

Questo lancerà un messaggio NullPointerExceptionche objnon ha ancora un riferimento class A.


11
Allungherò: sposta la linea static A obj = new A();sotto static int num2=0;e dovresti ottenere 1 e 1.
Thomas

2
Ciò che ancora mi confonde è il fatto che anche se num1 non ha un'inizializzazione esplicita, È (implicitamente) inizializzato con 0. Non dovrebbe esserci alcuna differenza tra l'inizializzazione esplicita e implicita ...
isnot2bad

@ isnot2bad "l'inizializzazione implicita" avviene come parte della dichiarazione. Dichiarazione accade prima assegnazione, non importa in che ordine li presentiamo in. A obj = new A(); int num1; int num2 = 0;Viene trasformato in questo: A obj; int num1; int num2; obj = new A(); num2 = 0;. Java lo fa quindi num1, num2sono definiti dal momento in cui raggiungi il new A()costruttore.
Hans Z

31

Il staticsignificato del modificatore quando applicato a una dichiarazione di variabile è che la variabile è una variabile di classe piuttosto che una variabile di istanza. In altre parole ... c'è solo una num1variabile e solo una num2variabile.

(A parte: una variabile statica è come una variabile globale in alcune altre lingue, tranne per il fatto che il suo nome non è visibile ovunque. Anche se viene dichiarato come a public static, il nome non qualificato è visibile solo se è dichiarato nella classe corrente o in una superclasse , o se viene importato utilizzando un'importazione statica. Questa è la distinzione. Un vero globale è visibile senza qualificazione ovunque.)

Così, quando si fa riferimento a obj.num1e obj.num2, in realtà si sta riferendo le variabili statiche le cui designazioni reali sono A.num1e A.num2. Allo stesso modo, quando il costruttore incrementa num1e num2, incrementa le stesse variabili (rispettivamente).

La ruga confusa nel tuo esempio è nell'inizializzazione della classe. Una classe viene inizializzata prima inizializzando per impostazione predefinita tutte le variabili statiche, quindi eseguendo gli inizializzatori statici dichiarati (e i blocchi di inizializzatori statici) nell'ordine in cui appaiono nella classe. In questo caso, hai questo:

static A obj = new A();
static int num1;
static int num2=0;

Succede così:

  1. Le statistiche iniziano con i loro valori iniziali di default; A.objè nulle A.num1/ A.num2sono zero.

  2. La prima dichiarazione ( A.obj) crea un'istanza di A(), e il costruttore per Aincrementi A.num1e A.num2. Quando la dichiarazione viene completata, A.num1e A.num2sono entrambi 1e si A.objriferisce Aall'istanza appena costruita .

  3. La seconda dichiarazione ( A.num1) non ha inizializzatore, quindi A.num1non cambia.

  4. La terza dichiarazione ( A.num2) ha un inizializzatore che assegna zero a A.num2.

Quindi, alla fine dell'inizializzazione della classe, A.num1è 1ed A.num2è 0... ed è ciò che mostrano le istruzioni di stampa.

Questo comportamento confuso è dovuto al fatto che stai creando un'istanza prima che l'inizializzazione statica sia stata completata e che il costruttore che stai utilizzando dipende e modifica una statica che deve ancora essere inizializzata. Questo è qualcosa che dovresti evitare di fare in codice reale.


16

1,0 è corretto.

Quando la classe viene caricata, tutti i dati statici vengono inizializzati o vengono dichiarati. Per impostazione predefinita, int è 0.

  • viene creata la prima A. num1 e num2 diventano 1 e 1
  • che static int num1;non fa niente
  • allora static int num2=0;scrive 0 in num2

9

È dovuto all'ordine degli inizializzatori statici. Le espressioni statiche nelle classi vengono valutate in ordine dall'alto verso il basso.

Il primo ad essere chiamato è il costruttore di A, che imposta num1e num2sia su 1:

static A obj = new A();

Poi,

static int num2=0;

viene chiamato e imposta nuovamente num2 = 0.

Ecco perché num1è 1 ed num2è 0.

Come nota a margine, un costruttore non dovrebbe modificare le variabili statiche, questo è un pessimo design. Prova invece un approccio diverso all'implementazione di un singleton in Java .


6

È possibile trovare una sezione in JLS: §12.4.2 .

Procedura di inizializzazione dettagliata:

9.Poi, esegui gli inizializzatori delle variabili di classe e gli inizializzatori statici della classe, o gli inizializzatori dei campi dell'interfaccia, in ordine testuale, come se fossero un blocco unico, tranne che le variabili di classe finali ei campi delle interfacce i cui valori sono compilati -Le costanti di tempo vengono inizializzate per prime

Quindi le tre variabili statiche verranno inizializzate una ad una in ordine testuale.

Così

static A obj = new A();
//num1 = 1, num2 = 1;
static int num1;
//this is initilized first, see below.
static int num2=0;
//num1 = 1, num2 = 0;

Se cambio l'ordine in:

static int num1;
static int num2=0;
static A obj = new A();

Il risultato sarà 1,1.

Notare che static int num1;non è un inizializzatore di variabili perché ( §8.3.2 ):

Se un dichiaratore di campo contiene un inizializzatore di variabile, allora ha la semantica di un assegnamento (§15.26) alla variabile dichiarata e: Se il dichiaratore è per una variabile di classe (cioè un campo statico), l'inizializzatore di variabile è valutata e l'assegnazione eseguita esattamente una volta, quando la classe viene inizializzata

E questa variabile di classe viene inizializzata quando viene creata la classe. Questo accade per primo ( §4.12.5 ).

Ogni variabile in un programma deve avere un valore prima che il suo valore venga utilizzato: Ogni variabile di classe, variabile di istanza o componente di array viene inizializzata con un valore predefinito quando viene creata (§15.9, §15.10): per il tipo byte, il valore predefinito è zero, cioè il valore di (byte) 0. Per il tipo short, il valore predefinito è zero, ovvero il valore di (short) 0. Per il tipo int, il valore predefinito è zero, ovvero 0. Per il tipo long, il valore predefinito è zero, ovvero 0L. Per il tipo float, il valore predefinito è zero positivo, ovvero 0,0f. Per il tipo double, il valore predefinito è zero positivo, ovvero 0,0d. Per il tipo char, il valore predefinito è il carattere null, ovvero "\ u0000". Per il tipo booleano, il valore predefinito è false. Per tutti i tipi di riferimento (§4.3), il valore predefinito è nullo.


2

Forse sarà utile pensarla in questo modo.

Le classi sono progetti per oggetti.

Gli oggetti possono avere variabili quando vengono istanziati.

Le classi possono anche avere variabili. Questi sono dichiarati statici. Quindi sono impostati sulla classe piuttosto che sulle istanze dell'oggetto.

Puoi avere solo uno di qualsiasi classe in un'applicazione, quindi è un po 'come l'archiviazione globale specifica per quella classe. Ovviamente è possibile accedere a queste variabili statiche e modificarle da qualsiasi punto dell'applicazione (assumendo che siano pubbliche).

Ecco un esempio di una classe "Dog" che utilizza una variabile statica per tenere traccia del numero di istanze che ha creato.

La classe "Dog" è la nuvola mentre le caselle arancioni sono istanze "Dog".

Classe di cani

leggi di più

Spero che questo ti aiuti!

Se hai voglia di una curiosità, questa idea è stata introdotta per la prima volta da Platone


1

La parola chiave statica viene utilizzata in java principalmente per la gestione della memoria. Possiamo applicare una parola chiave statica con variabili, metodi, blocchi e classi nidificate. La parola chiave static appartiene alla classe rispetto all'istanza della classe.Per una breve spiegazione sulla parola chiave static:

http://www.javatpoint.com/static-keyword-in-java


0

Molte delle risposte precedenti sono corrette. Ma proprio per illustrare cosa sta succedendo ho apportato alcune piccole modifiche di seguito.

Come accennato più volte sopra, ciò che sta accadendo è che un'istanza della classe A viene creata prima che la classe A sia completamente caricata. Quindi quello che è considerato il normale "comportamento" non viene osservato. Ciò non è molto dissimile dalla chiamata di metodi da un costruttore che può essere sovrascritto. In tal caso, le variabili di istanza potrebbero non essere in uno stato intuitivo. In questo esempio le variabili di classe non sono in uno stato intuitivo.

class A {
    static A obj = new A();
    static int num1;
    static int num2;
    static {
        System.out.println("Setting num2 to 0");
        num2 = 0;
    }

    private A() {
        System.out.println("Constructing singleton instance of A");
        num1++;
        num2++;
    }

    public static A getInstance() {
        return obj;
    }
}

public class Main {

    public static void main(String[] arg) {
        A obj = A.getInstance();
        System.out.println(obj.num1);
        System.out.println(obj.num2);
    }
}

L'output è

Constructing singleton instance of A
Setting num2 to 0
1
0

0

java non inizializza il valore di alcun membro di dati statico o non statico finché non viene chiamato ma lo crea.

quindi In questo modo, quando num1 e num2 verranno chiamati in main, verranno inizializzati con valori

num1 = 0 + 1; e

num2 = 0;

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.