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.


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 .


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.


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.


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 .


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

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.


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
        }
    }
    
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.