Quelle est la différence entre un bloc de code d'initialisation statique et non statique

357

Ma question concerne une utilisation particulière du mot-clé statique. Il est possible d'utiliser des staticmots clés pour couvrir un bloc de code dans une classe qui n'appartient à aucune fonction. Par exemple, le code suivant se compile:

public class Test {
    private static final int a;    
    static {
        a = 5;
        doSomething(a);
    }
    private static int doSomething(int x) {
        return (x+5);
    }
}

Si vous supprimez le staticmot clé, il se plaint car la variable aest final. Cependant , il est possible d'enlever les deux finalet staticmots - clés et qu'il compile.

C'est déroutant pour moi dans les deux sens. Comment suis-je censé avoir une section de code qui n'appartient à aucune méthode? Comment est-il possible de l'invoquer? En général, quel est le but de cette utilisation? Ou mieux, où puis-je trouver de la documentation à ce sujet?

Szere Dyeri
la source

Réponses:

403

Le bloc de code avec le modificateur statique signifie un initialiseur de classe ; sans le modificateur statique, le bloc de code est un initialiseur d' instance .

Les initialiseurs de classe sont exécutés dans l'ordre où ils sont définis (de haut en bas, tout comme les simples initialiseurs de variables) lorsque la classe est chargée (en fait, lorsqu'elle est résolue, mais c'est une technicité).

Les initialiseurs d'instance sont exécutés dans l'ordre défini lorsque la classe est instanciée, immédiatement avant l'exécution du code constructeur, immédiatement après l'invocation du super constructeur.

Si vous supprimez staticde int a, il devient une variable d'instance à laquelle vous ne pouvez pas accéder à partir du bloc d'initialisation statique. Cela ne pourra pas être compilé avec l'erreur "la variable non statique ne peut pas être référencée à partir d'un contexte statique".

Si vous supprimez également staticdu bloc d'initialisation, il devient alors un initialiseur d'instance et int aest donc initialisé lors de la construction.

Lawrence Dol
la source
L'initialiseur statique est en fait appelé plus tard, lorsque la classe est initialisée, après avoir été chargée et liée. Cela se produit lorsque vous instanciez un objet d'une classe ou accédez à une variable ou une méthode statique sur la classe. En fait, si vous avez une classe avec un initialiseur statique et une méthode public static void staticMethod(){}, si vous exécutez TestStatic.class.getMethod("staticMethod");. L'initialiseur statique ne sera pas appelé. Plus d'informations ici docs.oracle.com/javase/specs/jvms/se10/html/…
Totò
@ Totò: Oui, c'est ce que la résolution de la classe implique (au moins, ils l'appelaient link + init comme "résolution" dans le passé). Je ne suis pas surpris que vous puissiez utiliser la réflexion pour découvrir des choses sur une classe sans la résoudre.
Lawrence Dol
166

Uff! qu'est-ce qu'un initialiseur statique?

L'initialiseur statique est un static {}bloc de code à l'intérieur de la classe java, et ne s'exécute qu'une seule fois avant l'appel du constructeur ou de la méthode principale.

D'ACCORD! Dis m'en plus...

  • est un bloc de code static { ... }dans n'importe quelle classe java. et exécuté par une machine virtuelle lorsque la classe est appelée.
  • Aucune returndéclaration n'est prise en charge.
  • Aucun argument n'est pris en charge.
  • Non thisou superpris en charge.

Hmm, où puis-je l'utiliser?

Peut être utilisé partout où vous vous sentez bien :) aussi simple que cela. Mais je vois la plupart du temps qu'il est utilisé lors de la connexion à la base de données, du lancement de l'API, de la journalisation, etc.

Ne vous contentez pas d'aboyer! où est l'exemple?

package com.example.learnjava;

import java.util.ArrayList;

public class Fruit {

    static {
        System.out.println("Inside Static Initializer.");

        // fruits array
        ArrayList<String> fruits = new ArrayList<>();
        fruits.add("Apple");
        fruits.add("Orange");
        fruits.add("Pear");

        // print fruits
        for (String fruit : fruits) {
            System.out.println(fruit);
        }
        System.out.println("End Static Initializer.\n");
    }

    public static void main(String[] args) {
        System.out.println("Inside Main Method.");
    }
}

Production???

À l'intérieur de l'initialiseur statique.

Pomme

Orange

Poire

Fin de l'initialisation statique.

À l'intérieur de la méthode principale.

J'espère que cela t'aides!

Madan Sapkota
la source
Merci Madan! Le bloc statique peut-il être utilisé à la place afterPropertiesSet()de InitializingBean?
Alexander Suraphel
3
Oui, vous pouvez! L'initialiseur statique est appelé lorsque la classe est chargée par le jvm. C'est donc vraiment la première phase où le code est exécuté. Si vous avez aussi un constructeur, l'ordre serait: initialiseur statique, constructeur, afterPropertiesSet
Martin Baumgartner
57

Le staticbloc est un "initialiseur statique".

Il est automatiquement invoqué lorsque la classe est chargée, et il n'y a pas d'autre moyen de l'invoquer (même pas via Reflection).

Je ne l'ai personnellement utilisé que lors de l'écriture de code JNI:

class JNIGlue {
    static {
        System.loadLibrary("foo");
    }
}
Alnitak
la source
6
Non, pas de manière explicite de l'invoquer, l'initialiseur de classe n'est jamais représenté par une Methodinstance mais uniquement invoqué par la machine virtuelle Java.
Rafael Winterhalter
46

Ceci est directement de http://www.programcreek.com/2011/10/java-class-instance-initializers/

1. Ordonnance d'exécution

Regardez la classe suivante, savez-vous laquelle est exécutée en premier?

public class Foo {

    //instance variable initializer
    String s = "abc";

    //constructor
    public Foo() {
        System.out.println("constructor called");
    }

    //static initializer
    static {
        System.out.println("static initializer called");
    }

    //instance initializer
    {
        System.out.println("instance initializer called");
    }

    public static void main(String[] args) {
        new Foo();
        new Foo();
    }
}

Production:

initialiseur statique appelé

initialiseur d'instance appelé

constructeur appelé

initialiseur d'instance appelé

constructeur appelé

2. Comment fonctionne l'initialiseur d'instance Java?

L'initialiseur d'instance ci-dessus contient une instruction println. Pour comprendre comment cela fonctionne, nous pouvons le traiter comme une déclaration d'affectation de variable, par exemple b = 0. Cela peut rendre la compréhension plus évidente.

Au lieu de

int b = 0, tu pourrais écrire

int b;
b = 0;

Par conséquent, les initialiseurs d'instance et les initialiseurs de variable d'instance sont à peu près les mêmes.

3. Quand les initialiseurs d'instance sont-ils utiles?

L'utilisation d'initialiseurs d'instance est rare, mais elle peut néanmoins être une alternative utile aux initialiseurs de variable d'instance si:

  1. Le code d'initialisation doit gérer les exceptions
  2. Effectuez des calculs qui ne peuvent pas être exprimés avec un initialiseur de variable d'instance.

Bien sûr, un tel code pourrait être écrit dans des constructeurs. Mais si une classe avait plusieurs constructeurs, vous devrez répéter le code dans chaque constructeur.

Avec un initialiseur d'instance, vous pouvez simplement écrire le code une fois, et il sera exécuté quel que soit le constructeur utilisé pour créer l'objet. (Je suppose que ce n'est qu'un concept et qu'il n'est pas souvent utilisé.)

Un autre cas dans lequel les initialiseurs d'instance sont utiles sont les classes internes anonymes, qui ne peuvent déclarer aucun constructeur du tout. (Est-ce que ce sera un bon endroit pour placer une fonction de journalisation?)

Merci à Derhein.

Notez également que les classes anonymes qui implémentent des interfaces [1] n'ont pas de constructeurs. Par conséquent, les initialiseurs d'instance sont nécessaires pour exécuter tout type d'expressions au moment de la construction.

Alexei Fando
la source
12

"final" garantit qu'une variable doit être initialisée avant la fin du code d'initialisation de l'objet. De même, "static final" garantit qu'une variable sera initialisée par le code d'initialisation de fin de classe. Omettre le "statique" de votre code d'initialisation le transforme en code d'initialisation d'objet; ainsi votre variable ne satisfait plus à ses garanties.

DJClayworth
la source
8

Vous n'écrirez pas de code dans un bloc statique qui doit être appelé n'importe où dans votre programme. Si le but du code doit être invoqué, vous devez le placer dans une méthode.

Vous pouvez écrire des blocs d'initialisation statiques pour initialiser des variables statiques lorsque la classe est chargée, mais ce code peut être plus complexe.

Un bloc d'initialisation statique ressemble à une méthode sans nom, sans argument et sans type de retour. Comme vous ne l'appelez jamais, il n'a pas besoin de nom. La seule fois où elle est appelée, c'est lorsque la machine virtuelle charge la classe.

Vincent Ramdhanie
la source
6

lorsqu'un développeur utilise un bloc d'initialisation, le compilateur Java copie l'initialiseur dans chaque constructeur de la classe actuelle.

Exemple:

le code suivant:

class MyClass {

    private int myField = 3;
    {
        myField = myField + 2;
        //myField is worth 5 for all instance
    }

    public MyClass() {
        myField = myField * 4;
        //myField is worth 20 for all instance initialized with this construtor
    }

    public MyClass(int _myParam) {
        if (_myParam > 0) {
            myField = myField * 4;
            //myField is worth 20 for all instance initialized with this construtor
            //if _myParam is greater than 0
        } else {
            myField = myField + 5;
            //myField is worth 10 for all instance initialized with this construtor
            //if _myParam is lower than 0 or if _myParam is worth 0
        }
    }

    public void setMyField(int _myField) {
        myField = _myField;
    }


    public int getMyField() {
        return myField;
    }
}

public class MainClass{

    public static void main(String[] args) {
        MyClass myFirstInstance_ = new MyClass();
        System.out.println(myFirstInstance_.getMyField());//20
        MyClass mySecondInstance_ = new MyClass(1);
        System.out.println(mySecondInstance_.getMyField());//20
        MyClass myThirdInstance_ = new MyClass(-1);
        System.out.println(myThirdInstance_.getMyField());//10
    }
}

est équivalent à:

class MyClass {

    private int myField = 3;

    public MyClass() {
        myField = myField + 2;
        myField = myField * 4;
        //myField is worth 20 for all instance initialized with this construtor
    }

    public MyClass(int _myParam) {
        myField = myField + 2;
        if (_myParam > 0) {
            myField = myField * 4;
            //myField is worth 20 for all instance initialized with this construtor
            //if _myParam is greater than 0
        } else {
            myField = myField + 5;
            //myField is worth 10 for all instance initialized with this construtor
            //if _myParam is lower than 0 or if _myParam is worth 0
        }
    }

    public void setMyField(int _myField) {
        myField = _myField;
    }


    public int getMyField() {
        return myField;
    }
}

public class MainClass{

    public static void main(String[] args) {
        MyClass myFirstInstance_ = new MyClass();
        System.out.println(myFirstInstance_.getMyField());//20
        MyClass mySecondInstance_ = new MyClass(1);
        System.out.println(mySecondInstance_.getMyField());//20
        MyClass myThirdInstance_ = new MyClass(-1);
        System.out.println(myThirdInstance_.getMyField());//10
    }
}

J'espère que mon exemple est compris par les développeurs.

cardman
la source
4

Le bloc de code statique peut être utilisé pour instancier ou initialiser des variables de classe (par opposition aux variables d'objet). Ainsi, déclarer "a" statique signifie qu'il n'est partagé que par tous les objets Test, et le bloc de code statique initialise "a" une seule fois, lorsque la classe Test est chargée pour la première fois, quel que soit le nombre d'objets Test créés.

Paul Tomblin
la source
À titre de suivi, si je ne crée pas d'instance de l'objet mais à la place j'appelle une fonction statique publique. Cela signifie-t-il que l'exécution de ce bloc est garantie avant cet appel de fonction publique? Merci.
Szere Dyeri
Si vous appelez une fonction statique publique de la classe, la classe doit être chargée en premier, donc oui, l'initialiseur statique s'exécutera en premier.
Paul Tomblin
A moins que ce ne soit l'initialisation de classe qui (indirectement) appelle le code qui essaie de l'utiliser. IFYSWIM. Dépendances circulaires et tout ça.
Tom Hawtin - tackline
1
@Tom a raison - il est possible d'écrire quelque chose où un initialiseur statique appelle une méthode statique avant d'appeler un autre initialiseur statique, mais mon esprit recule à cette pensée, donc je ne l'ai jamais envisagée.
Paul Tomblin