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.
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:
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:
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
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:
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.
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:
Sexta frase: istore_2
Armazene os elementos da pilha de operandos na segunda posição da tabela de variáveis locais.
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