CodeGym /Blogue Java /Random-PT /Sequência de ações durante a criação do objeto
John Squirrels
Nível 41
San Francisco

Sequência de ações durante a criação do objeto

Publicado no grupo Random-PT
Oi! A lição de hoje será bastante... uh... multifacetada :) no sentido de que cobriremos uma ampla gama de tópicos, mas todos eles estarão relacionados ao processo de criação de objetos . Sequência de ações durante a criação do objeto - 1Vamos analisá-lo do começo ao fim: como os construtores são chamados, como e em que ordem os campos (incluindo campos estáticos) são inicializados etc. o material em construtores de classe base . Primeiro, vamos relembrar como um objeto é criado. Você lembra bem como é esse processo do ponto de vista de um desenvolvedor: ele cria uma classe, escreve newe está tudo pronto :) Aqui falaremos sobre o que acontece dentro do computador e da máquina Java quando escrevemos, por exemplo:

Cat cat = new Cat();
Já falamos sobre isso antes, mas só para garantir, vamos lembrá-lo:
  • Primeiro, a memória para armazenar o objeto é alocada.
  • Em seguida, a máquina Java cria uma referência ao objeto (no nosso caso, a referência é Cat cat).
  • Por fim, as variáveis ​​são inicializadas e o construtor é chamado (vamos ver esse processo com mais detalhes).
Além disso, da lição sobre o ciclo de vida do objeto , você provavelmente se lembra de que um objeto dura enquanto houver pelo menos uma referência a ele. Se não sobrar nenhum, o objeto se torna presa do coletor de lixo. Sequência de ações durante a criação do objeto - 2Esses dois primeiros pontos não devem levantar nenhuma questão especial. A alocação de memória é um processo simples e existem apenas dois resultados possíveis: ou há memória ou não há :) E criar um link não é incomum. Mas o terceiro ponto representa todo um conjunto de operações executadas em ordem estrita. Não sou fã de estudar para provas, mas você precisa entender bem esse processo e precisa memorizar essa sequência de operações. Quando falamos sobre o processo de criação de objetos nas lições anteriores, você ainda não sabia nada sobre herança, então explicar algumas coisas era problemático. Agora você sabe bastante e podemos finalmente considerar esta questão na íntegra :) Portanto, o terceiro ponto diz " Finalmente, as variáveis ​​são inicializadas e o construtor é chamado. " Mas em que ordem tudo isso acontece? Para um melhor entendimento, vamos criar duas classes super simples — uma parent e uma child:

public class Vehicle { 

   public static int vehicleCounter = 0; 

   private String description = "Vehicle"; 
   public Vehicle() { 
   } 

   public String getDescription() { 
       return description; 
   } 
} 

public class Truck extends Vehicle { 

   private static int truckCounter = 0; 

   private int yearOfManufacture; 
   private String model; 
   private int maxSpeed; 

   public Truck(int yearOfManufacture, String model, int maxSpeed) { 
       this.yearOfManufacture = yearOfManufacture; 
       this.model = model; 
       this.maxSpeed = maxSpeed; 

       Vehicle.vehicleCounter++; 
       truckCounter++; 
   } 
}
A Truckclasse é uma implementação de um caminhão com campos representando seu ano, modelo e velocidade máxima. Agora queremos criar um desses objetos:

public class Main { 

   public static void main(String[] args) throws IOException { 

       Truck truck = new Truck(2017, "Scania S 500 4x2", 220); 
   } 
}
Para a máquina Java, o processo ficará assim:
  1. A primeira coisa que acontece é que as variáveis ​​estáticas da Vehicleclasse são inicializadas . Sim, eu disse a Vehicleclasse, não Truck. As variáveis ​​estáticas são inicializadas antes que os construtores sejam chamados, e isso começa na classe pai. Vamos tentar verificar isso. Definimos o vehicleCountercampo na Vehicleclasse igual a 10 e tentamos exibi-lo nos construtores Vehiclee Truck.

    
    public class Vehicle { 
    
       public static int vehicleCounter = 10; 
       private String description = "Vehicle"; 
    
       public Vehicle() { 
           System.out.println(vehicleCounter); 
       } 
    
       public String getDescription() { 
           return description; 
       } 
    } 
    
    public class Truck extends Vehicle { 
    
       private static int truckCount = 0;
    
       private int yearOfManufacture; 
       private String model; 
       private int maxSpeed; 
    
       public Truck(int yearOfManufacture, String model, int maxSpeed) { 
           System.out.println(vehicleCounter); 
           this.yearOfManufacture = yearOfManufacture; 
           this.model = model; 
           this.maxSpeed = maxSpeed; 
    
           Vehicle.vehicleCounter++; 
           truckCount++; 
       } 
    }
    

    Colocamos deliberadamente a instrução println bem no início do Truckconstrutor para garantir que os campos do caminhão ainda não tenham sido inicializados quando vehicleCounterfor exibido.

    E aqui está o resultado:

    
    10 
    10
    
  2. Depois que as variáveis ​​estáticas da classe pai são inicializadas, as variáveis ​​estáticas da classe filha são inicializadas. No nosso caso, este é o truckCountercampo da Truckclasse.

    Vamos fazer outro experimento onde tentaremos exibir o valor de truckCounterdentro do Truckconstrutor antes que os outros campos sejam inicializados:

    
    public class Truck extends Vehicle { 
    
       private static int truckCounter = 10; 
    
       private int yearOfManufacture; 
       private String model; 
       private int maxSpeed; 
    
       public Truck(int yearOfManufacture, String model, int maxSpeed) { 
           System.out.println(truckCounter); 
           this.yearOfManufacture = yearOfManufacture; 
           this.model = model; 
           this.maxSpeed = maxSpeed; 
    
           Vehicle.vehicleCounter++; 
           truckCounter++; 
       } 
    }
    

    Como você pode ver, o valor 10 já foi atribuído à nossa variável estática quando o Truckconstrutor começou.

  3. Ainda não é a hora dos construtores! A inicialização da variável continua. As variáveis ​​não estáticas da classe pai são inicializadas em terceiro lugar. Como você pode ver, a herança complica significativamente o processo de criação de um objeto, mas não há nada que você possa fazer a respeito: você só precisa memorizar algumas coisas na programação :)

    Como experiência, podemos atribuir algum valor inicial à descriptionvariável na Vehicleclasse e depois alterá-lo no construtor.

    
    public class Vehicle { 
    
       public static int vehicleCounter = 10; 
    
       private String description = "Initial value of the description field"; 
    
       public Vehicle() { 
           System.out.println(description); 
           description = "Vehicle"; 
           System.out.println(description); 
       } 
    
       public String getDescription() { 
           return description; 
       } 
    }
    

    Vamos executar nosso main()método que cria um caminhão:

    
    public class Main { 
    
       public static void main(String[] args) throws IOException { 
    
           Truck truck = new Truck(2017, "Scania S 500 4x2", 220); 
       } 
    }
    

    Obtemos o seguinte resultado:

    
    Initial value of the description field 
    Vehicle
    

    Isso prova que, quando o Vehicleconstrutor começa, o descriptioncampo já recebeu um valor.

  4. Finalmente, é a vez dos construtores! Mais precisamente, é hora do construtor da classe base. Ele é invocado na quarta etapa do processo de criação do objeto.

    Isso também é bastante fácil de verificar. Vamos tentar enviar duas linhas para o console: uma dentro do Vehicleconstrutor da classe base, a segunda dentro do Truckconstrutor. Precisamos estar convencidos de que a linha interna Vehicleé exibida primeiro:

    
    public Vehicle() { 
    
       System.out.println("Hello from the Vehicle constructor!"); 
    } 
    
    public Truck(int yearOfManufacture, String model, int maxSpeed) { 
    
       System.out.println("Hello from the Truck constructor!"); 
       this.yearOfManufacture = yearOfManufacture; 
       this.model = model; 
       this.maxSpeed = maxSpeed; 
    
       Vehicle.vehicleCounter++; 
       truckCounter++; 
    }
    

    Vamos executar nosso main()método e ver o resultado:

    
    Hello from the Vehicle constructor! 
    Hello from the Truck constructor! 
    

    Excelente. Isso significa que não estamos enganados :) Vamos em frente.

  5. Agora é hora de inicializar os campos não estáticos da classe filha, ou seja, nossa Truckclasse. Os campos imediatamente dentro da classe que está sendo instanciada não são inicializados até a quinta etapa! Surpreendente, mas é verdade :) Mais uma vez, faremos uma verificação simples — assim como na classe pai: atribuiremos algum valor inicial à maxSpeedvariável e no Truckconstrutor verificaremos se o valor foi atribuído antes do início do construtor:

    
    public class Truck extends Vehicle { 
    
       private static int truckCounter = 10; 
    
       private int yearOfManufacture; 
       private String model; 
       private int maxSpeed = 150; 
    
       public Truck(int yearOfManufacture, String model, int maxSpeed) { 
    
           System.out.println("Initial value of maxSpeed = " + this.maxSpeed); 
           this.yearOfManufacture = yearOfManufacture; 
           this.model = model; 
           this.maxSpeed = maxSpeed; 
    
           Vehicle.vehicleCounter++; 
           truckCounter++; 
       } 
    }
    

    Saída do console:

    
    Initial value of maxSpeed = 150
    

    Como você pode ver,  quando o Truck construtor inicia, maxSpeed já é igual a 150!

  6. O construtor da Truckclasse filha é chamado.

    E somente neste ponto, por último, será chamado o construtor da classe que estamos instanciando!

    Somente na sexta etapa serão atribuídos aos campos os valores que passamos como argumentos para nosso caminhão.

    Como você pode ver, "construir" um caminhão, ou seja, o processo de criação do objeto, não é simples. Mas parece que dividimos em partes menores :)

Sequência de ações durante a criação do objeto - 3 Por que é tão importante entender bem esse processo? Imagine como os resultados da criação de um objeto comum poderiam ser inesperados se você não soubesse exatamente o que estava acontecendo "sob o capô" :) Agora é hora de voltar ao curso e concluir algumas tarefas! Boa sorte e até breve! :)
Comentários
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION