Qual é a diferença entre um bloco de código de inicialização estático e não estático

357

Minha pergunta é sobre um uso específico da palavra-chave estática. É possível usar a staticpalavra-chave para cobrir um bloco de código dentro de uma classe que não pertence a nenhuma função. Por exemplo, o seguinte código compila:

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

Se você remover a staticpalavra-chave, ela reclama porque a variável aé final. No entanto, é possível remover as palavras-chave finale as duas e staticcompilá-las.

É confuso para mim nos dois sentidos. Como devo ter uma seção de código que não pertence a nenhum método? Como é possível invocá-lo? Em geral, qual é o objetivo desse uso? Ou melhor, onde posso encontrar documentação sobre isso?

Szere Dyeri
fonte

Respostas:

403

O bloco de código com o modificador estático significa um inicializador de classe ; sem o modificador estático, o bloco de código é um inicializador de instância .

Os inicializadores de classe são executados na ordem em que são definidos (de cima para baixo, como simples inicializadores de variáveis) quando a classe é carregada (na verdade, quando é resolvida, mas isso é um detalhe técnico).

Os inicializadores de instância são executados na ordem definida quando a classe é instanciada, imediatamente antes da execução do código do construtor, imediatamente após a chamada do super construtor.

Se você remover statica partir int a, torna-se uma variável de instância, que não são capazes de acesso a partir da estática initializer bloco. Isso falhará ao compilar com o erro "a variável não estática não pode ser referenciada a partir de um contexto estático".

Se você também remover staticdo bloco inicializador, ele se tornará um inicializador de instância e, portanto, int aserá inicializado na construção.

Lawrence Dol
fonte
O inicializador estático é realmente chamado mais tarde, quando a classe é inicializada, depois de carregada e vinculada. Isso acontece quando você instancia um objeto de uma classe ou acessa uma variável ou método estático na classe. De fato, se você tiver uma classe com um inicializador estático e um método public static void staticMethod(){}, se executar TestStatic.class.getMethod("staticMethod");. O inicializador estático não será chamado. Mais informações aqui docs.oracle.com/javase/specs/jvms/se10/html/…
Totò
@ Totò: Sim, é isso que implica a resolução da classe (pelo menos eles costumavam se referir a ela como link + init como "resolução" quando). Não estou surpreso que você possa usar a reflexão para descobrir coisas sobre uma classe sem que ela resolva .
Lawrence Dol
166

Uff! o que é inicializador estático?

O inicializador estático é um static {}bloco de código dentro da classe java e é executado apenas uma vez antes que o construtor ou o método principal seja chamado.

ESTÁ BEM! Me diga mais...

  • é um bloco de código static { ... }dentro de qualquer classe java. e executado pela máquina virtual quando a classe é chamada.
  • Nenhuma returninstrução é suportada.
  • Nenhum argumento é suportado.
  • Não thisou supersão suportados.

Hmm, onde posso usá-lo?

Pode ser usado em qualquer lugar que você se sinta bem :) simples assim. Mas vejo na maioria das vezes que é usado ao fazer conexão com o banco de dados, API init, Log e etc.

Não basta latir! onde está o exemplo?

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

Resultado???

Inicializador estático interno.

maçã

laranja

Pera

Finalizar estático inicializador.

Método principal interno.

Espero que isto ajude!

Madan Sapkota
fonte
Graças à Madan! O bloco estático pode ser usado em vez afterPropertiesSet()de InitializingBean?
Alexander Suraphel
3
Sim você pode! O inicializador estático é chamado quando a classe é carregada pela jvm. Portanto, é realmente a primeira fase em que o código é executado. Se você tiver um construtor também, a ordem seria: inicializador estático, construtor, afterPropertiesSet
Martin Baumgartner
57

O staticbloco é um "inicializador estático".

É invocado automaticamente quando a classe é carregada, e não há outra maneira de invocá-la (nem mesmo através do Reflection).

Pessoalmente, eu só o usei ao escrever o código JNI:

class JNIGlue {
    static {
        System.loadLibrary("foo");
    }
}
Alnitak
fonte
6
Não, não há maneira explícita de invocá-lo, o inicializador de classe nunca é representado por uma Methodinstância, mas apenas invocado pela máquina virtual Java.
Rafael Winterhalter
46

Isto é diretamente de http://www.programcreek.com/2011/10/java-class-instance-initializers/

1. Ordem de Execução

Veja a classe a seguir, você sabe qual delas é executada primeiro?

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

Resultado:

inicializador estático chamado

inicializador de instância chamado

construtor chamado

inicializador de instância chamado

construtor chamado

2. Como o inicializador de instância Java funciona?

O inicializador de instância acima contém uma instrução println. Para entender como ele funciona, podemos tratá-lo como uma declaração de atribuição de variável, por exemplo b = 0,. Isso pode tornar mais óbvio o entendimento.

Ao invés de

int b = 0você poderia escrever

int b;
b = 0;

Portanto, inicializadores de instância e inicializadores de variável de instância são praticamente os mesmos.

3. Quando os inicializadores de instância são úteis?

O uso de inicializadores de instância é raro, mas ainda pode ser uma alternativa útil para inicializadores de variável de instância se:

  1. O código do inicializador deve lidar com exceções
  2. Execute cálculos que não podem ser expressos com um inicializador de variável de instância.

Obviamente, esse código pode ser escrito em construtores. Mas se uma classe tivesse vários construtores, você teria que repetir o código em cada construtor.

Com um inicializador de instância, você pode escrever o código apenas uma vez e ele será executado independentemente do construtor usado para criar o objeto. (Acho que esse é apenas um conceito e não é usado com frequência.)

Outro caso em que os inicializadores de instância são úteis são as classes internas anônimas, que não podem declarar nenhum construtor. (Este será um bom lugar para colocar uma função de registro?)

Graças a Derhein.

Observe também que as classes anônimas que implementam interfaces [1] não têm construtores. Portanto, são necessários inicializadores de instância para executar qualquer tipo de expressão no momento da construção.

Alexei Fando
fonte
12

"final" garante que uma variável deve ser inicializada antes do final do código do inicializador de objetos. Da mesma forma "estático final" garante que uma variável será inicializada no final do código de inicialização da classe. Omitir o "estático" do seu código de inicialização o transforma em código de inicialização do objeto; portanto, sua variável não satisfaz mais suas garantias.

DJClayworth
fonte
8

Você não gravará código em um bloco estático que precise ser chamado em qualquer lugar do seu programa. Se o objetivo do código for invocado, você deverá colocá-lo em um método

Você pode escrever blocos inicializadores estáticos para inicializar variáveis ​​estáticas quando a classe é carregada, mas esse código pode ser mais complexo.

Um bloco inicializador estático se parece com um método sem nome, sem argumentos e sem tipo de retorno. Como você nunca o chama, não precisa de um nome. O único momento em que é chamado é quando a máquina virtual carrega a classe.

Vincent Ramdhanie
fonte
6

quando um desenvolvedor usa um bloco inicializador, o Java Compiler copia o inicializador em cada construtor da classe atual.

Exemplo:

o seguinte código:

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
    }
}

é equivalente a:

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
    }
}

Espero que meu exemplo seja entendido pelos desenvolvedores.

cardman
fonte
4

O bloco de código estático pode ser usado para instanciar ou inicializar variáveis ​​de classe (ao contrário de variáveis ​​de objeto). Portanto, declarar estática "a" significa que é apenas um compartilhado por todos os objetos de Teste, e o bloco de código estático inicializa "a" apenas uma vez, quando a classe Test é carregada pela primeira vez, não importa quantos objetos de Teste sejam criados.

Paul Tomblin
fonte
Como acompanhamento, se eu não criar uma instância do objeto, mas chamar uma função estática pública. Isso implica que é garantido que este bloco seja executado antes desta chamada de função pública? Obrigado.
Szere Dyeri 02/12/08
Se você chamar uma função estática pública da classe, a classe precisará ser carregada primeiro; portanto, sim, o inicializador estático será executado primeiro.
Paul Tomblin 02/12/08
A menos que tenha sido a inicialização da classe que (indiretamente) chame o código que está tentando usá-lo. IFYSWIM. Dependências circulares e tudo isso.
Tom Hawtin - defina 2/08/08
11
@ Tom está certo - é possível escrever algo em que um inicializador estático chama um método estático antes que outro inicializador estático seja chamado, mas minha mente recua com o pensamento, então nunca o considerei.
Paul Tomblin 02/12/08