Je n'ai pas eu une compréhension complète des constructeurs statiques en Java. Si c'est permis, pourquoi est-il permis? Dans quels scénarios l'utiliseriez-vous? À quoi cela servirait-il? Quelqu'un peut-il me donner un exemple simple s'il vous plaît?
Je n'ai pas eu une compréhension complète des constructeurs statiques en Java. Si c'est permis, pourquoi est-il permis? Dans quels scénarios l'utiliseriez-vous? À quoi cela servirait-il? Quelqu'un peut-il me donner un exemple simple s'il vous plaît?
Réponses:
À proprement parler, Java n'a pas de constructeurs statiques car un constructeur, par définition, ne peut pas être statique. Ce à quoi vous faites référence est appelé un "bloc d'initialisation statique". Un constructeur implique que vous construisez un objet. Vous ne pouvez pas avoir de constructeur pour une classe car une classe n'est pas une instance d'elle-même. C'est simplement une classe. La chose qui "construit" la classe s'appelle le compilateur (ou la machine virtuelle, selon ce que l'on entend par "constructions"), et si vous entrez dans la construction de code dans un autre code, vous entrez dans la génération de code, qui est une bête entièrement différente.
Mis à part toute sélection, un bloc d'initialisation statique est utilisé pour initialiser des champs statiques complexes (ou au niveau de la classe) pour une classe. Généralement, ceux-ci sont utilisés pour initialiser des choses qui ne peuvent pas être initialisées sur une seule ligne, ou nécessitent qu'un autre objet (qui peut ou non appartenir à la classe dans laquelle le bloc statique est implémenté) soit initialisé en premier.
Fondamentalement, on pourrait les utiliser pour dire à la classe "Hé, définissez la variable A à cette valeur en premier, puis, une fois cela fait, utilisez la valeur de A pour initialiser B." Étant donné que Java requiert que l'initialisation de champ standard soit effectuée dans un constructeur ou une méthode, ou via l'appel d'un constructeur ou d'une méthode (sauf s'il s'agit d'un littéral), ceux-ci peuvent être une méthode pratique pour initialiser des objets statiques complexes.
Les blocs d'initialisation statiques ne sont pas trop souvent nécessaires et doivent généralement être évités, sauf s'ils ont une utilisation réelle . Ne vous méprenez pas, ils ont leur place en Java, mais comme beaucoup d'autres choses (telles que les instructions break, return, switch et goto), ils peuvent être facilement surutilisés, ce qui réduit leur lisibilité et la maintenabilité du code -base dans laquelle ils sont utilisés.
Un bref exemple d'un bloc d'initialisation statique utilisé serait le suivant (selon l' excellente explication des blocs d'initialisation statiques trouvée ici ):
Code:
public class StaticExample{
static {
System.out.println("This is first static block");
}
public StaticExample(){
System.out.println("This is constructor");
}
public static String staticString = "Static Variable";
static {
System.out.println("This is second static block and "
+ staticString);
}
public static void main(String[] args){
StaticExample statEx = new StaticExample();
StaticExample.staticMethod2();
}
static {
staticMethod();
System.out.println("This is third static block");
}
public static void staticMethod() {
System.out.println("This is static method");
}
public static void staticMethod2() {
System.out.println("This is static method2");
}
}
Production:
This is first static block
This is second static block and Static Variable
This is static method
This is third static block
This is constructor
This is static method2
Certaines instances qu'ils répertorient lorsque les blocs statiques peuvent être utiles:
Quelques raisons de NE PAS utiliser de blocs statiques (dans d'autres situations):
this
mot clé car il n'y a pas d'instance.Je dois noter: alors que certains langages (tels que C #) peuvent avoir une syntaxe pour les "constructeurs" qui sont statiques, ces "constructeurs" fonctionnent de la même manière que les blocs d'initialisation statiques en Java, et sont considérés par beaucoup (moi y compris) comme les mauvais noms dans le langage, étant donné le concept de base d'un constructeur de POO .
il est utilisé pour initialiser des champs qui est plus difficile que de simplement l'affecter:
public class Example{
public final static Map<String, String> preFilledField;
static{
Map<String, String> tmp = new HashMap<>();
//fill map
preFilledField = Collections.unmodifiableMap(tmp);
}
}
il n'est pas possible de remplir une carte lors de l'initialisation (sauf si vous utilisez le hack de sous-classe anonyme), c'est donc le meilleur moyen de garantir qu'il est rempli avant la première utilisation
Vous pouvez également le faire pour intercepter les exceptions vérifiées lors de l'initialisation
La réponse de Gurgadurgen est probablement ce que vous cherchez, mais j'ajouterai simplement quelques autres points qui sont parfois négligés lorsque quelqu'un veut un "constructeur statique".
Si vous voulez une méthode statique qui crée une instance de votre classe, vous pouvez créer une méthode statique qui appelle simplement le constructeur de la classe.
public class Example
{
/** Static method to create an instance. */
public static Example build()
{ return new Example() ; }
/** A field of each instance. */
private String stuff ;
/** The class's actual constructor. */
public Example()
{ stuff = new String() ; }
public String getStuff()
{ return this.stuff ; }
/**
* Mutator for "stuff" property. By convention this returns "void"
* but you might want to return the object itself, to support the
* sort of chained invocations that are becoming trendy now. You'll
* see the stylistic benefits of chaining later in this example.
*/
public Example setStuff( String newStuff )
{
this.stuff = newStuff ;
return this ;
}
}
public class ExampleTest
{
public static void main( String[] args )
{
// The usual instance model.
Example first = new Example() ;
System.out.println( first.setStuff("stuff").getStuff() ) ;
// Using your static method to construct an instance:
Example second = Example.build() ;
System.out.println( second.setStuff("more stuff").getStuff() ) ;
// Chaining all the invocations at once:
System.out.println( Example.build().setStuff("even more stuff").getStuff() ) ;
}
}
Cela produit la sortie:
stuff
more stuff
even more stuff
L'autre raison de créer une méthode statique pour construire une instance est le cas lorsque vous voulez vous assurer qu'il existe exactement une instance de votre classe à un moment donné; c'est ce qu'on appelle un singleton . Par convention, une telle classe fournirait une méthode statique appelée getInstance()
pour obtenir la seule et unique instance traitée comme un "singleton".
public class SingletonExample extends Example
{
// Note: This extends my previous example, which has a "stuff"
// property, and a trivial constructor.
/** The singleton instance, statically initialized as null. */
private static SingletonExample singleton = null ;
/**
* The static accessor for the singleton. If no instance exists,
* then it will be created; otherwise, the one that already exists
* will be returned.
*/
public static SingletonExample getInstance()
{
if( singleton == null )
singleton = new SingletonExample() ;
return singleton ;
}
}
public class SingletonExampleTest
{
public static void main( String[] args )
{
System.out.println( SingletonExample.getInstance().setStuff("stuff").getStuff() ) ;
// You could still create instances of this class normally if you want to.
SingletonExample otherstuff = new SingletonExample() ;
otherstuff.setStuff("other stuff") ;
// But watch what happens to this.
System.out.println( SingletonExample.getInstance().getStuff() ) ;
System.out.println( otherstuff.getStuff() ) ;
// Now we show what happens when you start modifying the singleton.
SingletonExample theoneandonly = SingletonExample.getInstance() ;
theoneandonly.setStuff("changed stuff") ;
System.out.println( SingletonExample.getInstance().getStuff() ) ;
}
}
Cela produit ce qui suit.
stuff
stuff
other stuff
changed stuff
En stockant une référence au singleton, puis en le modifiant, l'appel suivant à getInstance()
obtient ce singleton modifié.
Il est tentant d'utiliser des singletons comme moyen de commencer à créer des variables globales pour votre application. Dans certains contextes, cela peut être utile, mais cela peut également vous causer des ennuis. En particulier, j'ai rencontré un bogue intéressant lors du développement d'une application Android, où des instances singleton pouvaient se perdre. Passer d'une activité à une autre peut parfois conduire la JVM à utiliser un nouveau "chargeur de classe" qui ne connaîtra pas le singleton qui a été stocké statiquement par un chargeur de classe précédent.
Bien que je comprenne que beaucoup considèrent la notion de "constructeur statique" comme un terme impropre, je ne pense pas que ce soit le cas. Le problème est en train de construire les deux classes et leurs objets d'instance . Il a été dit dans d'autres threads que la construction de la classe est le travail du compilateur. Même en Java, ce n'est qu'à moitié vrai.
Le compilateur construit un échafaudage pour chaque définition de classe. L'échafaudage contient des métadonnées sur la classe et les instances qui devraient y figurer au moment de la construction. Si une classe définit un champ auquel est affectée une valeur primitive constante, cette valeur est incluse dans l'échafaudage par le compilateur. Pour tout autre type de valeur affecté, le compilateur génère une routine d'initialisation qui doit être exécutée 1 fois, avant la création de la première instance de classe, qui met à jour l'échafaudage avec les valeurs appropriées. Cette mise à jour ne peut pas être effectuée par le compilateur.
Étant donné que cette mise à jour ponctuelle de l'échafaudage est souvent une condition préalable essentielle au bon fonctionnement des constructeurs d'instances, il est également raisonnable de l'appeler un type de constructeur. C'est pourquoi dans les langages OO courants qui prennent en charge le concept, il est appelé un constructeur statique. Le concept derrière les blocs d'initialisation statiques en Java n'est guère plus qu'un changement sémantique pour rester en ligne avec l'idée qu'un programmeur Java devrait être à la fois indépendant du système et de l'implémentation.
Comme d'autres réponses l'ont dit, vous pouvez écrire des méthodes statiques qui construisent un objet. Cela permet de contourner le manque de constructeurs nommés en Java (et dans de nombreux autres langages. Delphi prend en charge les constructeurs nommés). Vous pouvez jouer avec les types et l'ordre des paramètres, mais cela peut conduire à un code peu clair et fragile.
Par exemple, nous pourrions inventer un scénario dans lequel votre objet peut être construit à partir d'une chaîne XML ou d'une chaîne JSON. Vous pouvez écrire des méthodes comme:
static MyObject createFromXml(String xml);
static MyObject createFromJson(String json);
Je l'ai utilisé très occasionnellement comme alternative à un constructeur sans paramètre avec des méthodes d'initialisation nommées:
MyObject myObject = new MyObject();
myObject.loadXml(xml).
Vous pouvez considérer une méthode de création statique comme implémentant le modèle de générateur dans votre classe.
Vous pouvez afficher la section "statique" comme un constructeur de niveau classe pour initialiser les propriétés de classe (statique en java). Identique au constructeur "normal" utilisé pour initialiser les propriétés de niveau instance.