Trabalhando com construtor estático em Java

11

Não entendi completamente os construtores estáticos em Java. Se é permitido, por que é permitido? Em quais cenários você o usaria? Que finalidade isso serviria? Alguém pode me dar um exemplo simples, por favor?

Kapeel45
fonte
2
Compartilhar sua pesquisa ajuda a todos. Conte-nos o que você tentou e por que ele não atendeu às suas necessidades. Isso demonstra que você dedicou um tempo para tentar ajudar a si mesmo, evita reiterar respostas óbvias e, acima de tudo, ajuda a obter uma resposta mais específica e relevante. Veja também How to Ask
gnat
3
Não existe um "construtor estático".
David Conrad

Respostas:

27

Estritamente falando, Java não possui construtores estáticos porque um construtor, por definição, não pode ser estático. O que você está se referindo é chamado de "bloco de inicialização estática". Um construtor implica que você está construindo um objeto. Você não pode ter construtor para uma classe porque uma classe não é uma instância em si. É simplesmente uma classe. O que "constrói" a classe é chamado de compilador (ou a máquina virtual, dependendo do significado de "construções") e, se você cria código dentro de outro código, entra na geração de código, o que é um animal completamente diferente.

Tirando todos os detalhes, um bloco de inicialização estático é usado para inicializar campos estáticos (ou em nível de classe) complexos para uma classe. Geralmente, eles são usados ​​para inicializar coisas que não podem ser inicializadas em uma linha ou exigem que outro objeto (que pode ou não estar na classe em que o bloco estático é implementado) seja inicializado primeiro.

Basicamente, pode-se usá-los para dizer à classe "Ei, defina a variável A com esse valor PRIMEIRO, depois que isso for feito, use o valor de A para inicializar B." Como o Java exige que a inicialização do campo padrão seja feita dentro de um construtor ou método, ou através da chamada de um construtor ou método (a menos que seja literal), esse pode ser um método conveniente para inicializar objetos estáticos complexos.

Blocos de inicialização estática não são necessários com muita frequência e geralmente devem ser evitados, a menos que tenham um uso real . Não me interpretem mal, eles têm seu lugar no Java, mas, como muitas outras coisas (como instruções break, return, switch e goto), eles podem ser facilmente superutilizados, o que reduz a legibilidade e a manutenção do código -base em que são usados.

Um breve exemplo de um bloco de inicialização estática sendo usado seria o seguinte (conforme excelente explicação dos blocos de inicialização estática encontrados aqui ):

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");
    }
}    

Resultado:

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

Algumas instâncias listam quando blocos estáticos podem ser úteis:

  • Se você estiver carregando drivers e outros itens no espaço para nome. Por exemplo, a classe Class possui um bloco estático no qual registra os nativos.
  • Se você precisar fazer cálculos para inicializar suas variáveis ​​estáticas, poderá declarar um bloco estático que será executado exatamente uma vez, quando a classe for carregada pela primeira vez.
  • Problemas relacionados à segurança ou tarefas relacionadas ao log

Algumas razões para NÃO usar blocos estáticos (em outras situações):

  • Há uma limitação da JVM de que um bloco inicializador estático não deve exceder 64K.
  • Você não pode lançar exceções verificadas.
  • Você não pode usar a thispalavra - chave, pois não há instância.
  • Você não deve tentar acessar super, pois não existe tal coisa para blocos estáticos.
  • Você não deve devolver nada deste bloco.
  • Blocos estáticos tornam o teste um pesadelo.

Devo observar: Embora algumas linguagens (como C #) possam ter sintaxe para "construtores" que são estáticos, esses "construtores" funcionam da mesma maneira que os blocos de inicialização estática funcionam em Java e são vistos por muitos (inclusive eu) como equívocos na linguagem, dado o conceito básico de um construtor OOP .

Gurgadurgen
fonte
o link sobre blocos estáticos não está mais funcionando
ASh 05/03
5

é usado para inicializar campos mais difíceis do que simplesmente atribuí-lo:

public class Example{

    public final static Map<String, String> preFilledField;

    static{
        Map<String, String> tmp = new HashMap<>();
        //fill map
        preFilledField = Collections.unmodifiableMap(tmp);
    }
}

não é possível preencher um mapa na inicialização (a menos que você use o hack da subclasse anônima), portanto, esta é a melhor maneira de garantir que ele seja preenchido antes do primeiro uso

Você também pode fazer isso para capturar exceções verificadas ao inicializar

catraca arrepiante
fonte
Esse foi um dos principais usos que eu já vi, mas com a inclusão dos métodos de fábrica estáticos para a estrutura de coleções no Java 9, esse caso de uso foi realmente preterido de certa forma.
Hangman4358
2

A resposta de Gurgadurgen é provavelmente o que você está procurando, mas vou adicionar alguns outros pontos que às vezes são negligenciados quando alguém quer um "construtor estático".

Se você deseja um método estático que cria uma instância da sua classe, pode criar um método estático que simplesmente chama o construtor da 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() ) ;
    }
}

Isso produz a saída:

stuff
more stuff
even more stuff

O outro motivo para criar um método estático para construir uma instância é o caso em que você deseja garantir que exista exatamente uma instância da sua classe a qualquer momento; isso é chamado de singleton . Por convenção, essa classe forneceria um método estático chamado getInstance()para obter a única instância que é tratada como um "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() ) ;
    }
}

Isso produz o seguinte.

stuff
stuff
other stuff
changed stuff

Armazenando uma referência ao singleton e modificando-o, a próxima chamada para getInstance()obtém esse singleton modificado.

É tentador usar singletons como uma maneira de começar a criar variáveis ​​globais para seu aplicativo. Em alguns contextos, isso pode ser útil, mas também pode causar problemas. Em particular, deparei-me com um bug interessante ao desenvolver um aplicativo Android, onde as instâncias singleton poderiam se perder. Ir de uma atividade para outra às vezes pode levar a JVM a usar um novo "carregador de classes" que não saberá sobre o singleton que foi armazenado estaticamente por um carregador de classes anterior.

zerobandwidth
fonte
0

Embora eu entenda que há muitos que veem a noção de um "construtor estático" como um nome impróprio, não acredito que seja esse o caso. O problema está no processo de construção de ambas as classes e seus objetos de instância . Já foi afirmado em outros threads que a construção da classe é o trabalho do compilador. Mesmo em Java, isso é apenas meia verdade.

O compilador constrói um andaime para cada definição de classe. O andaime contém metadados sobre a classe e quais instâncias devem ter neles no momento da construção. Se uma classe define um campo ao qual é atribuído um valor primitivo constante, esse valor é incluído no andaime pelo compilador. Para qualquer outro tipo de valor atribuído, o compilador gera uma rotina de inicialização que deve ser executada 1 vez, antes da criação da instância da primeira classe, que atualiza o andaime com os valores adequados. Esta atualização não pode ser feita pelo compilador.

Como essa atualização única para o andaime é frequentemente um pré-requisito crítico para o bom funcionamento dos construtores de instância, também é razoável chamá-lo de tipo de construtor. É por isso que nas linguagens OO comuns que suportam o conceito, é chamado de construtor estático. O conceito por trás dos blocos de inicialização estática em Java é pouco mais do que uma mudança semântica para manter a noção de que um programador Java deve ser independente de sistema e de implementação.

Arkain
fonte
0

Como outras respostas disseram, você pode escrever métodos estáticos que constroem um objeto. Isso contorna a falta de construtores nomeados em Java (e muitas outras linguagens. O Delphi suporta construtores nomeados). Você pode jogar com os tipos e a ordem dos parâmetros, mas isso pode levar a um código pouco claro e frágil.

Por exemplo, poderíamos inventar um cenário em que seu objeto possa ser construído a partir de uma string XML ou JSON. Você pode escrever métodos como:

static MyObject createFromXml(String xml);
static MyObject createFromJson(String json);

Eu usei isso muito ocasionalmente como uma alternativa a um construtor sem parâmetros com métodos de inicialização nomeados:

MyObject myObject = new MyObject();
myObject.loadXml(xml). 

Você pode considerar um método de criação estático como implementando o padrão do construtor dentro de sua classe.

kiwiron
fonte
Enquanto leio a pergunta, trata-se de inicializadores estáticos, não de métodos estáticos normais.
CodesInChaos
-2

Você pode visualizar a seção "estática" como um construtor de nível de classe usado para inicializar propriedades da classe (estática em java). O mesmo que o construtor "normal" é usado para inicializar propriedades no nível da instância.

AlfredoCasado
fonte