Analise o boxing e unboxing em Java da perspectiva do bytecode

1. Introdução básica de desembalagem e embalagem

Boxing e unboxing são dois açúcares sintáticos úteis fornecidos em Java.

O boxing se refere à conversão automática de um tipo de dados básico em seu tipo de invólucro. Como a conversão de int em Integer.

Unboxing refere-se à conversão do tipo de invólucro no tipo de dados básico correspondente. Como a conversão de Integer em int.

O seguinte é um exemplo:

Integer num1 = 1000;
int num2 = num1;

Entre eles, num1 é um objeto do tipo Integer, e a operação de atribuição a ele aqui é um processo de boxing.

num2 é uma variável do tipo básico int, e atribuir um valor a ela com num1 é um processo de desembalagem.

2. Implementação de bytecode de unboxing e boxing

Vamos dar uma olhada em como o boxing e o unboxing são implementados da perspectiva do bytecode.

Então, como obter o bytecode do código Java? Na verdade, o arquivo de classe que obtemos após compilar o código-fonte Java com javac é seu arquivo bytecode.

Mas esse arquivo de classe existe na forma binária e não pode ser lido diretamente. Portanto, precisamos de outro comando javap para nos ajudar a analisar o arquivo de classe em um formato legível .

Salvamos o seguinte código no arquivo test.java (categoria, método principal, etc. são omitidos aqui):

Integer num1 = 1000;
int num2 = num1;

Uso do comando javap
Em seguida, use javac para compilar e, em seguida, use javap -v para descompilar. Com javap mais o parâmetro -v, você pode ver informações mais abrangentes.

javac test.java
javap -v test

Depois de usar o javap para descompilar , podemos obter os seguintes resultados:

         0: sipush        1000
         3: invokestatic  #2 // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
         6: astore_1
         7: aload_1
         8: invokevirtual #3// Method java/lang/Integer.intValue:()I
        11: istore_2

O resultado da descompilação contém 6 instruções de bytecode. Se você entender essas instruções, também entenderá o princípio de uma implementação comum de unboxing e boxing.

Como essas instruções correspondem a várias operações no quadro de pilha, primeiro revisamos o conceito de quadro de pilha.

Revisão da estrutura da pilha

Acredito que todos estejam familiarizados com a pilha da máquina virtual Java na área de dados do tempo de execução Java .

O processo de cada método Java, desde a chamada até a conclusão da execução, corresponde ao processo de enviar um frame de pilha para dentro e para fora da pilha de máquina virtual.

O processo de execução do método consiste em realizar várias operações no frame da pilha.

Aqui, focamos principalmente na pilha de operandos e na tabela de variáveis ​​locais no quadro de pilha.
Java stack frame

Análise de execução de instrução

Abaixo analisamos em detalhes o processo de execução das 6 instruções acima.

Código Java

Integer num1 = 1000;
int num2 = num1;

Bytecode correspondente

         0: sipush        1000
         3: invokestatic  #2 // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
         6: astore_1
         7: aload_1
         8: invokevirtual #3// Method java/lang/Integer.intValue:()I
        11: istore_2

A primeira frase: sipush 1000

Sipush é uma operação push, o que significa colocar o número do tipo curto no frame da pilha. O frame da pilha após a execução desta frase é o seguinte:

sipush 1000
Você pode ver que o número 1000 é colocado na pilha de operandos.

Segunda frase: invokestatic # 2

Esta frase é para chamar um método estático.

Então, qual método é especificamente chamado?

Você pode ver que há um parâmetro # 2 por trás. Este # 2 pode ser entendido como um índice do pool constante.

Além de gerar o bytecode correspondente ao código acima, usamos o comando javap -v. Outra parte importante é a piscina constante.

O pool constante dessa descompilação é mostrado na figura a seguir:
Insira a descrição da imagem aqui
Você pode ver a localização do índice nº 2 e ele armazena um descritor de método. Na verdade, você pode ver diretamente os seguintes comentários. Este método é o método Integer.valueOf.

Não vou falar mais sobre a piscina constante aqui, já que todos sabem disso por enquanto.

De volta ao nosso tópico. Agora sabemos que a instrução invokestatic # 2 chama o método Integer.valueOf, então quem é o parâmetro de entrada?

Lembra o que a última instrução fez?

Correto. Coloque o número 1000 no topo da pilha de operandos. O número 1000 no topo da pilha é na verdade o parâmetro de entrada de Integer.valueOf.

O valor de retorno de Integer.valueOf é um objeto Integer. Depois de executar esta instrução, 1000 é retirado da pilha e o objeto Integer resultante é colocado na pilha. Este objeto Integer é num1 em nosso código Java.

Neste momento, o frame da pilha é o seguinte: A
Insira a descrição da imagem aqui
terceira frase: astore_1

Esta frase significa retirar o elemento superior da pilha de operandos e colocá-lo na posição onde o subscrito da tabela de variável local é 1.

Os resultados após a execução são os seguintes:

Insira a descrição da imagem aqui

Quando esta frase é executada, Número inteiro num1 = 1000 no código Java, mesmo se a execução for concluída.

Conclusão do boxe

Os primeiros três bytecodes completam a operação de encaixotamento. Por meio da análise acima, sabemos que o boxing de inteiros é obtido chamando o método Integer.valueOf.

É semelhante para outros tipos de dados.

As últimas três sentenças do bytecode correspondem ao processo de int num2 = num1.

A quarta sentença: aload_1
significa empurrar o elemento cuja posição é 1 na tabela de variável local na pilha de operandos.

O resultado desta frase é o seguinte.

aload_1
A quinta frase: invokevirtual # 3

Semelhante à segunda sentença de invokestatic, esta sentença também é uma execução de chamada de método.

É apenas um método de exemplo chamado aqui.

Participe da situação de pool constante dada na análise da segunda sentença, o método chamado aqui é o método intValue de Integer.

O operando é o objeto Integer colocado na pilha na etapa anterior.

Os resultados após a execução são os seguintes:

Insira a descrição da imagem aqui

Sexta frase: istore_2

Armazene os elementos da pilha de operandos na segunda posição da tabela de variáveis ​​locais.

Insira a descrição da imagem aqui
Neste ponto, a execução está completa int num2 = num1

Finalmente, num1 e num2 são armazenados nas posições 1 e 2 da tabela de variáveis ​​locais.

Conclusão de desempacotamento

Através da análise dos três últimos bytecodes, sabemos que o unboxing do tipo Integer é implementado usando Integer.intValue.

O mesmo é verdadeiro para outros tipos de wrapper.

3. Resumo deste artigo

Este artigo apresenta o significado de boxing e unboxing. Em seguida, obteve a implementação de bytecode da montagem e desempacotamento por meio do comando javap. E analise esses códigos de byte frase por frase.

Finalmente, para o tipo inteiro, o boxing é usar o método Integer.valueOf, e o unboxing é usar o método Integer.intValue para chegar à conclusão.


Se este artigo for útil para você, preste atenção à minha conta pública WeChat original "Java Technology Station" para receber mais dos meus artigos o mais rápido possível
Insira a descrição da imagem aqui

Acho que você gosta

Origin blog.csdn.net/vxzhg/article/details/103830715
Recomendado
Clasificación