No obtuve una comprensión completa de los constructores estáticos en Java. Si está permitido, ¿por qué está permitido? ¿En qué escenarios lo usarías? ¿Qué propósito tendría? ¿Alguien puede darme un ejemplo simple por favor?
No obtuve una comprensión completa de los constructores estáticos en Java. Si está permitido, ¿por qué está permitido? ¿En qué escenarios lo usarías? ¿Qué propósito tendría? ¿Alguien puede darme un ejemplo simple por favor?
Respuestas:
Estrictamente hablando, Java no tiene constructores estáticos porque un constructor, por definición, no puede ser estático. A lo que se refiere se denomina "bloque de inicialización estático". Un constructor implica que estás construyendo un objeto. No puede tener un constructor para una clase porque una clase no es una instancia de sí misma. Es simplemente una clase. Lo que "construye" la clase se llama el compilador (o la máquina virtual, dependiendo de lo que se entiende por "construcciones"), y si entras a construir código dentro de otro código, estás entrando en la generación de código, que es Una bestia completamente diferente.
Dejando a un lado toda la selección, se usa un bloque de inicialización estático para inicializar campos estáticos complejos (o de nivel de clase) para una clase. Por lo general, se usan para inicializar cosas que no se pueden inicializar en una línea o requieren que algún otro objeto (que puede o no estar en la clase en la que se implementa el bloque estático) se inicialice primero.
Básicamente, uno podría usarlos para decirle a la clase "Hey, establezca la variable A en este valor PRIMERO, luego, una vez hecho, use el valor de A para inicializar B". Como Java requiere que la inicialización de campo estándar se realice dentro de un constructor o método, o mediante la llamada de un constructor o método (a menos que sea un literal), estos pueden ser un método conveniente para inicializar objetos complejos y estáticos.
Los bloques de inicialización estática no son necesarios con demasiada frecuencia, y generalmente deben evitarse a menos que tengan un uso real . No me malinterpreten, tienen su lugar en Java, pero al igual que muchas otras cosas (como las declaraciones break, return, switch y goto) se pueden usar fácilmente en exceso, lo que reduce su legibilidad y la facilidad de mantenimiento del código -base en la que se usan.
Un breve ejemplo de un bloque de inicialización estática que se utilizaría sería el siguiente (según la excelente explicación de los bloques de inicialización estática que se encuentran aquí ):
Código:
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");
}
}
Salida:
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
Algunas instancias enumeran cuándo los bloques estáticos pueden ser útiles:
Algunas razones para NO usar bloques estáticos (en otras situaciones):
thispalabra clave ya que no hay instancia.Debo tener en cuenta: Si bien algunos lenguajes (como C #) pueden tener sintaxis para "constructores" que son estáticos, esos "constructores" funcionan de la misma manera que los bloques de inicialización estática en Java, y son vistos por muchos (incluido yo mismo) como nombres incorrectos en el lenguaje, dado el concepto básico de un constructor OOP .
se usa para inicializar campos que son más difíciles que simplemente asignarlo:
public class Example{
public final static Map<String, String> preFilledField;
static{
Map<String, String> tmp = new HashMap<>();
//fill map
preFilledField = Collections.unmodifiableMap(tmp);
}
}
no es posible llenar un mapa en la inicialización (a menos que use el hack de subclase anónimo), por lo que esta es la mejor manera de garantizar que se complete antes del primer uso
También puede hacer esto para detectar excepciones marcadas al inicializar
La respuesta de Gurgadurgen es probablemente lo que estás buscando, pero solo agregaré un par de otros puntos que a veces se descuidan cuando alguien quiere un "constructor estático".
Si desea un método estático que cree una instancia de su clase, puede crear un método estático que simplemente invoque al constructor de la clase.
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() ) ;
}
}
Esto produce la salida:
stuff
more stuff
even more stuff
La otra razón para crear un método estático para construir una instancia es el caso en el que desea asegurarse de que exista exactamente una instancia de su clase en un momento dado; Esto se llama un singleton . Por convención, dicha clase proporcionaría un método estático llamado getInstance()para obtener la única instancia que se trata como 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() ) ;
}
}
Esto produce lo siguiente.
stuff
stuff
other stuff
changed stuff
Al almacenar una referencia al singleton y luego modificarlo, la siguiente llamada a getInstance()obtiene ese singleton modificado.
Es tentador usar singletons como una forma de comenzar a crear variables globales para su aplicación. En algunos contextos esto puede ser útil, pero también puede meterte en problemas. En particular, me encontré con un error interesante al desarrollar una aplicación de Android, donde las instancias de singleton podrían perderse. Saltar de una actividad a otra a veces puede llevar a la JVM a usar un nuevo "cargador de clases" que no sabrá sobre el singleton que estaba almacenado estáticamente por un cargador de clases anterior.
Si bien entiendo que hay muchos que ven la noción de un "constructor estático" como un nombre inapropiado, no creo que ese sea el caso. El problema está en el proceso de construir ambas clases y sus objetos de instancia . Se ha dicho en otros hilos que la construcción de la clase es el trabajo del compilador. Incluso en Java, esto es solo cierto a medias.
El compilador construye un andamio para cada definición de clase. El andamio contiene metadatos sobre la clase y qué instancias deberían tener en ellos al momento de la construcción. Si una clase define un campo al que se le asigna un valor primitivo constante, el compilador incluye ese valor en el andamio. Para cualquier otro tipo de valor asignado, el compilador genera una rutina de inicialización que debe ejecutarse 1 vez, antes de la creación de la primera instancia de clase, que actualiza el andamio con los valores adecuados. Esta actualización no puede ser realizada por el compilador.
Dado que esta actualización única del andamio es a menudo un requisito previo crítico para el funcionamiento adecuado de los constructores de instancias, también es razonable llamarlo un tipo de constructor. Es por eso que en los lenguajes OO comunes que admiten el concepto, se llama un constructor estático. El concepto detrás de los bloques de inicialización estática en Java es poco más que un cambio semántico para mantenerse en línea con la noción de que un programador de Java debe ser independiente del sistema y la implementación.
Como han dicho otras respuestas, puede escribir métodos estáticos que construyan un objeto. Esto evita la falta de constructores con nombre en Java (y muchos otros lenguajes. Delphi admite constructores con nombre). Puede jugar con los tipos y el orden de los parámetros, pero esto puede conducir a un código poco claro y frágil.
Por ejemplo, podríamos inventar un escenario en el que su objeto se pueda construir a partir de una cadena XML o una cadena JSON. Podrías escribir métodos como:
static MyObject createFromXml(String xml);
static MyObject createFromJson(String json);
He usado esto muy ocasionalmente como una alternativa a un constructor sin parámetros con métodos de inicialización con nombre:
MyObject myObject = new MyObject();
myObject.loadXml(xml).
Podrías considerar un método de creación estático como la implementación del patrón de construcción dentro de tu clase.
Puede ver la sección "estática" como un constructor de nivel de clase para inicializar las propiedades de clase (estática en Java). Lo mismo que el constructor "normal" que se usa para inicializar las propiedades de nivel de instancia.