Quando as variáveis ​​estáticas são inicializadas?

87

Estou me perguntando quando as variáveis ​​estáticas são inicializadas com seus valores padrão. É correto que, quando uma classe é carregada, vars estáticos são criados (alocados) e, em seguida, inicializadores estáticos e inicializações em declarações são executados? Em que ponto os valores padrão são fornecidos? Isso leva ao problema da referência direta.

Além disso, se você puder explicar isso em referência à pergunta feita em Por que campos estáticos não são inicializados a tempo? e principalmente a resposta dada por Kevin Brock no mesmo site. Não consigo entender o terceiro ponto.

Ankit
fonte
2
Por favor, edite sua pergunta para incluir a citação a que está se referindo.
Oliver Charlesworth
1
Você leu as especificações da linguagem Java? É um documento bastante legível, deliberadamente. Se você leu isso, você deve entender o que está acontecendo. Se não, você pode fazer uma pergunta mais específica, no mínimo ...
Maarten Bodewes
Acho que este Q&A é uma cópia de stackoverflow.com/questions/3499214 .
Stephen C

Respostas:

72

Em Consulte Métodos de variável estática Java :

  • É uma variável que pertence à classe e não ao objeto (instância)
  • Variáveis ​​estáticas são inicializadas apenas uma vez, no início da execução. Essas variáveis ​​serão inicializadas primeiro, antes da inicialização de quaisquer variáveis ​​de instância
  • Uma única cópia a ser compartilhada por todas as instâncias da classe
  • Uma variável estática pode ser acessada diretamente pelo nome da classe e não precisa de nenhum objeto.

Variáveis ​​de instância e classe (estáticas) são inicializadas automaticamente com os valores padrão padrão se você falhar em inicializá-las propositalmente. Embora as variáveis ​​locais não sejam inicializadas automaticamente, você não pode compilar um programa que falha ao inicializar uma variável local ou atribuir um valor a essa variável local antes de ser usada.

O que o compilador realmente faz é produzir internamente uma única rotina de inicialização de classe que combina todos os inicializadores de variáveis ​​estáticas e todos os blocos inicializadores estáticos de código, na ordem em que aparecem na declaração de classe. Este único procedimento de inicialização é executado automaticamente, apenas uma vez, quando a classe é carregada pela primeira vez.

No caso de classes internas , eles não podem ter campos estáticos

Uma classe interna é uma classe aninhada que não é declarada explícita ou implicitamente static.

...

As classes internas não podem declarar inicializadores estáticos (§8.7) ou interfaces de membro ...

As classes internas não podem declarar membros estáticos, a menos que sejam variáveis ​​constantes ...

Consulte JLS 8.1.3 Classes internas e instâncias anexas

finalcampos em Java podem ser inicializados separadamente de seu local de declaração; entretanto, não pode ser aplicável a static finalcampos. Veja o exemplo abaixo.

final class Demo
{
    private final int x;
    private static final int z;  //must be initialized here.

    static 
    {
        z = 10;  //It can be initialized here.
    }

    public Demo(int x)
    {
        this.x=x;  //This is possible.
        //z=15; compiler-error - can not assign a value to a final variable z
    }
}

Isso ocorre porque há apenas uma cópia das staticvariáveis ​​associadas ao tipo, ao invés de uma associada a cada instância do tipo como com as variáveis ​​de instância e se tentarmos inicializar o ztipo static finaldentro do construtor, ele tentará reinicializar o static finalcampo de tipo zporque o construtor é executado em cada instanciação da classe que não deve ocorrer em finalcampos estáticos .

Leão
fonte
5
In case of static inner classes, they can not have static fieldsparece um erro de digitação. As classes internas não são estáticas.
Daniel Lubarov
Você deve usar entretanto em vez de Embora
Suraj Jain
Quando você inicia uma JVM e carrega uma classe pela primeira vez (isso é feito pelo carregador de classe quando a classe é referenciada pela primeira vez de qualquer forma), quaisquer blocos ou campos estáticos são 'carregados' na JVM e se tornam acessíveis.
nhoxbypass
1
Infelizmente, esta resposta contém algumas imprecisões factuais sobre quando a estática é inicializada. Consulte stackoverflow.com/a/3499322/139985 .
Stephen C
15

Vejo:

O último, em particular, fornece etapas de inicialização detalhadas que explicam quando as variáveis ​​estáticas são inicializadas e em que ordem (com a ressalva de que finalas variáveis ​​de classe e os campos de interface que são constantes de tempo de compilação são inicializados primeiro).

Não tenho certeza de qual é a sua pergunta específica sobre o ponto 3 (supondo que você se refira ao aninhado?). A sequência detalhada afirma que esta seria uma solicitação de inicialização recursiva, portanto, ela continuará a inicialização.

Dave Newton
fonte
11

Os campos estáticos são inicializados quando a classe é carregada pelo carregador de classes. Os valores padrão são atribuídos neste momento. Isso é feito na ordem em que aparecem no código-fonte.

Dave
fonte
10

A ordem de inicialização é:

  1. Blocos de inicialização estáticos
  2. Blocos de inicialização de instância
  3. Construtores

Os detalhes do processo são explicados no documento de especificação da JVM .

Óscar López
fonte
6

variável estática

  • É uma variável que pertence à classe e não ao objeto (instância)
  • Variáveis ​​estáticas são inicializadas apenas uma vez, no início da execução (quando o Classloader carrega a classe pela primeira vez).
  • Essas variáveis ​​serão inicializadas primeiro, antes da inicialização de quaisquer variáveis ​​de instância
  • Uma única cópia a ser compartilhada por todas as instâncias da classe
  • Uma variável estática pode ser acessada diretamente pelo nome da classe e não precisa de nenhum objeto
aleroot
fonte
4

Começando com o código da outra pergunta:

class MyClass {
  private static MyClass myClass = new MyClass();
  private static final Object obj = new Object();
  public MyClass() {
    System.out.println(obj); // will print null once
  }
}

Uma referência a esta classe iniciará a inicialização. Primeiro, a classe será marcada como inicializada. Em seguida, o primeiro campo estático será inicializado com uma nova instância de MyClass (). Observe que myClass recebe imediatamente uma referência a uma instância MyClass em branco . O espaço está lá, mas todos os valores são nulos. O construtor agora é executado e imprime obj, que é nulo.

Agora, de volta à inicialização da classe: objé feita uma referência a um novo objeto real e pronto.

Se isso foi acionado por uma instrução como: MyClass mc = new MyClass();espaço para uma nova instância MyClass é novamente alocado (e a referência colocada mc). O construtor é executado novamente e imprime novamente obj, que agora não é nulo.

O verdadeiro truque aqui é que, quando você usa new, as in WhatEverItIs weii = new WhatEverItIs( p1, p2 ); weiié imediatamente fornecida uma referência a um pouco de memória nula. A JVM irá então inicializar os valores e executar o construtor. Mas se você de alguma forma referenciar weii antes disso - referenciando-o a partir de outro thread ou ou referenciando a partir da inicialização da classe, por exemplo - você está olhando para uma instância de classe preenchida com valores nulos.

RalphChapin
fonte
1
Uma classe não é marcada como inicializada até que a inicialização seja concluída - fazer o contrário não faria sentido. Marcar como inicializado é quase a última etapa realizada. Consulte JLS 12.4.2 .
Dave Newton
@DaveNewton: Uma vez que algo faz referência à classe e começa a inicializá-la, todas as referências posteriores tratarão a classe como inicializada. Eles não tentarão inicializá-lo e não esperarão que seja inicializado. Portanto, os campos que parecem não ser nulos desde o início de um programa podem, na verdade, ser nulos por um tempo. Não entender isso é o que está causando toda a confusão. Acho que é mais simples dizer que uma classe não inicializada é "marcada" como inicializada na primeira referência, todas as outras referências a tratam como inicializada e é por isso que isso acontece.
RalphChapin
Uma correção ao comentário anterior: conforme descrito no JLS 12.4.2 de Dave Newton, a classe é bloqueada enquanto é inicializada. Outros threads irão esperar que a classe seja inicializada. Isso não afeta este caso, no entanto, que acontece em um segmento.
RalphChapin
4

A variável estática pode ser inicializada das seguintes três maneiras como a seguir escolha qualquer uma de sua preferência

  1. você pode inicializá-lo no momento da declaração
  2. ou você pode fazer criando um bloco estático, por exemplo:

    static {
            // whatever code is needed for initialization goes here
        }
    
  3. Existe uma alternativa aos blocos estáticos - você pode escrever um método estático privado

    class name {
        public static varType myVar = initializeVar();
    
        private static varType initializeVar() {
            // initialization code goes here
        }
    }
    
ajay verma
fonte