Trabajando con constructor estático en Java

11

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?

Kapeel45
fuente
2
Compartir su investigación ayuda a todos. Cuéntanos qué has probado y por qué no satisfizo tus necesidades. Esto demuestra que te has tomado el tiempo para intentar ayudarte a ti mismo, nos salva de reiterar respuestas obvias y, sobre todo, te ayuda a obtener una respuesta más específica y relevante. También vea Cómo preguntar
mosquito
3
No existe un "constructor estático".
David Conrad

Respuestas:

28

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:

  • Si está cargando controladores y otros elementos en el espacio de nombres. Por ejemplo, la clase Class tiene un bloque estático donde registra a los nativos.
  • Si necesita hacer un cálculo para inicializar sus variables estáticas, puede declarar un bloque estático que se ejecuta exactamente una vez, cuando la clase se carga por primera vez.
  • Problemas relacionados con la seguridad o tareas relacionadas con el registro

Algunas razones para NO usar bloques estáticos (en otras situaciones):

  • Hay una limitación de JVM de que un bloque inicializador estático no debe exceder los 64K.
  • No puede lanzar Excepciones marcadas.
  • No puede usar la thispalabra clave ya que no hay instancia.
  • No debe intentar acceder a super ya que no existe tal cosa para los bloques estáticos.
  • No debe devolver nada de este bloque.
  • Los bloques estáticos hacen que las pruebas sean una pesadilla.

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 .

Gurgadurgen
fuente
el enlace sobre bloques estáticos ya no funciona
ASh
5

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

monstruo de trinquete
fuente
Este fue uno de los principales usos que he visto, pero con la inclusión de los métodos de fábrica estáticos para el marco de colecciones en Java 9, este caso de uso realmente ha quedado obsoleto de alguna manera.
Hangman4358
2

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.

ancho de banda cero
fuente
0

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.

Arkain
fuente
0

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.

kiwiron
fuente
Mientras leo la pregunta, se trata de inicializadores estáticos, no de métodos estáticos normales.
CodesInChaos
-2

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.

AlfredoCasado
fuente