Neste artigo, o autor apresentará a você um recurso muito importante e interessante no Java, que é o boxe e o desbotamento automáticos, e interpretarão os princípios de boxe e unboxing automáticos do código -fonte. Ao mesmo tempo, esse recurso também deixa uma armadilha. Se os desenvolvedores não prestarem atenção, eles cairão facilmente nessa armadilha.
AutoBoxing
definição
Ao escrever programas Java, as pessoas geralmente definem um objeto inteiro das seguintes maneiras:
Número inteiro i = 100;
A partir do código acima, você pode saber que eu é uma referência do tipo inteiro e 100 é o tipo de dados básico em Java (tipo de dados primitivo). Esse método de passar diretamente um tipo de dados básico para a classe Wrapper correspondente é o boxe automático.
No JDK 1.5, o boxe automático foi introduzido pela primeira vez. Antes do JDK 1.5, se você deseja definir um objeto inteiro com um valor de 100, precisa fazer isso:
Inteiro i = novo número inteiro (100);
princípio
Vamos fazer um ponto de interrupção no código acima "Inteiro i = 100;" e siga -o.
Em seguida, podemos ver que o programa salta para o método ValueOf (int i) da classe Integer.
/** * Retorna uma instância <tt> inteira </tt> representando o valor especificado * <tt> int </tt>. * Se uma nova instância <Tt> inteira </tt> não for necessária, esse método * geralmente deve ser usado em preferência ao construtor * {@link #integer (int)}, pois esse método provavelmente produzirá * um desempenho significativamente melhor de espaço e tempo, em cache * valores solicitados frequentemente. * * @param i um <code> int </code> Valor. * @return a <tt> integer </tt> Instância representando <tt> i </tt>. * @since 1.5 */ public static integer valueof (int i) {if (i> = -128 && i <= integegercache.high) retorna integercache.cache [i + 128]; caso contrário, retorne novos inteiros (i); }Em outras palavras, a embalagem é JDK que ajuda você a concluir a chamada para o número inteiro.Valueof (100).
Unboxing
definição
Número inteiro inteiro100 = 100; int int100 = integer100;
A partir do código acima, você pode ver que o Integer100 é uma referência para digitar inteiro e o INT100 é um tipo de dados primitivo do tipo int. No entanto, podemos atribuir um objeto de tipo inteiro a uma variável de seu tipo de dados original correspondente. Isso é unboxing.
O Unboxing é o oposto da embalagem. O boxe é uma variável que atribui um tipo de dados primitivo à classe encapsulada correspondente. Unboxing significa atribuir uma variável de uma classe encapsulada a uma variável do tipo de dados original correspondente. Os nomes de embalagem e unboxing também são bastante apropriados.
princípio
Acredito que todo mundo adivinhou o que o JDK fez por nós durante o processo de unboxing. Vamos provar nossa conjectura por meio de experimentos.
Defina um ponto de interrupção na segunda linha do código acima, ou seja, defina um ponto de interrupção em "int int100 = integer100;" e siga -o.
Podemos ver que o programa salta para o método IntValue () integer.
/** * Retorna o valor desse <code> inteiro </code> como um * <code> int </code>. */ public int intvalue () {retornar value; }Ou seja, o JDK nos ajuda a concluir a chamada para o método intvalue (). Para o experimento acima, é chamar o método intvalue () do Integer100 e atribuir seu valor de retorno ao INT100.
Estendido
Experiência 1
Integer Integer400 = 400; int int400 = 400; System.out.println (Integer400 == int400);
Na terceira linha do código acima, o Integer400 e o INT400 executam o == RUN. E esses dois são tipos diferentes de variáveis. O Integer400 Unboxing ou Int400 são embalagens? Quais são os resultados da operação?
== Operação é determinar se os endereços de dois objetos são iguais ou se os valores dos dois tipos de dados básicos são iguais. Portanto, é fácil inferir que, se o Integer400 não for caixa, isso significa que os valores dos dois tipos básicos são comparados e o resultado em execução deve ser verdadeiro no momento; Se o INT400 estiver embalado, isso significa que os endereços dos dois objetos são iguais e o resultado em execução deve ser falso no momento. (Quanto ao motivo pelo qual o autor os atribui a 400, está relacionado às armadilhas que serão discutidas mais adiante).
Nosso resultado real é verdadeiro. Portanto, era Integer400 Unboxing. Os resultados do rastreamento de código provam isso.
Experiência 2
Número inteiro inteiro100 = 100; int int100 = 100; System.out.println (Integer100.equals (int100));
Na terceira linha do código acima, o parâmetro do método do integer100 é igual é INT100. Sabemos que os parâmetros do método igual são objetos, não o tipo de dados básico, então aqui deve ser embalado INT100. Os resultados do rastreamento de código provam isso.
De fato, se o tipo de parâmetro em um método for o tipo de dados original e o tipo de parâmetro passado for sua classe de encapsulamento, ele será automaticamente não caixa; Consequentemente, se o tipo de parâmetro em um método for o tipo de encapsulamento e o tipo de parâmetro passado for o seu tipo de dados original, ele será automaticamente encaixotado.
Experiência 3
Inteiro inteiro100 = 100; int Int100 = 100; long long200 = 200l; System.out.println (Integer100 + Int100); System.out.println (Long200 == (Integer100 + Int100)); System.out.println (Long200.Equals (intereger100 +100);
No primeiro experimento, aprendemos que, quando um tipo de dados básico executa == operação com a classe de encapsulamento, a classe de encapsulamento será desbote. E se +, -, *, /? Podemos saber neste experimento.
Se + operação, o tipo de dados subjacente será encaixotado, então:
• Na linha 4, o Integer100+Int100 receberá um objeto O do tipo inteiro e o valor 200, e executará o método tostring () desse objeto e saída "200";
• Na linha 5, o Integer100+INT100 receberá um objeto o do tipo inteiro e valor de 200. A operação == compara esse objeto com um objeto Long200. Obviamente, o falso será emitido;
• Na linha 6, o Integer100+Int100 receberá um objeto o do tipo inteiro e o valor de 200. O método igual de comprimento compara o Long200 com o O, porque ambos são classes encapsuladas de diferentes tipos, portanto a saída é falsa;
Se + operação, a classe de encapsulamento não será caixa, então:
• Na linha 4, o Integer100+Int100 receberá um tipo de dados básico B do tipo int e Valor 200 e, em seguida, a Caixa B para obter O, executar o método ToString () desse objeto e saída "200";
• Na linha 5, o Integer100+Int100 receberá um tipo de dados básico B1 do tipo int e Value 200. A operação == Unboxes Long200 para obter B2. Obviamente B1 == B2, e produz a verdadeira;
• Na linha 6, o Integer100+INT100 receberá um tipo de dados básico B do tipo Int e Value 200. As caixas de métodos iguais de Long B, mas o boxe resulta em objeto O do tipo inteiro, porque O e Long200 são objetos de diferentes tipos, a saída é falsa;
O resultado do programa em execução é:
200
verdadeiro
falso
Portanto, a segunda especulação está correta, ou seja, a classe de encapsulamento não será caixa durante a operação +.
armadilha
TRAP 1
Inteiro inteiro100 = nulo;
int int100 = integer100;
Essas duas linhas de código são completamente legais e podem ser completamente compiladas, mas, quando executadas, uma exceção de ponteiro nulo será lançada. Entre eles, o Integer100 é um objeto de tipo inteiro, que obviamente pode apontar para nulo. Mas, na segunda linha, o Integer100 será não caixa, ou seja, o método intvalue () será executado em um objeto nulo e, é claro, uma exceção de ponteiro nulo será lançada. Portanto, ao Unboxing, você deve prestar atenção especial se o objeto de classe encapsulado é nulo.
TRAP 2
Número inteiro i1 = 100;
Número inteiro i2 = 100;
Número inteiro i3 = 300;
Número inteiro i4 = 300;
System.out.println (i1 == i2);
System.out.println (i3 == i4);
Como i1, i2, i3 e i4 são todos tipos inteiros, achamos que os resultados em execução devem ser falsos. No entanto, o resultado real é "System.out.println (i1 == i2);" o que é verdadeiro, mas "System.out.println (i3 == i4);" o que é falso. Isso significa que as referências dos dois tipos inteiros, I1 e I2, apontam para o mesmo objeto, enquanto i3 e i4, apontam para diferentes objetos. Por que? Eles não são todos chamados de método inteiro.ValueOf (int i)?
Vamos dar uma olhada no método inteiro.ValueOf (int i) novamente.
/** * Retorna uma instância <tt> inteira </tt> representando o valor especificado * <tt> int </tt>. * Se uma nova instância <Tt> inteira </tt> não for necessária, esse método * geralmente deve ser usado em preferência ao construtor * {@link #integer (int)}, pois esse método provavelmente produzirá * um desempenho significativamente melhor de espaço e tempo, em cache * valores solicitados frequentemente. * * @param i um <code> int </code> Valor. * @return a <tt> integer </tt> Instância representando <tt> i </tt>. * @since 1.5 */ public static integer valueof (int i) {if (i> = -128 && i <= integegercache.high) retorna integercache.cache [i + 128]; caso contrário, retorne novos inteiros (i); }Podemos ver que quando i> =-128 e i <= integercache.high, o integgercache.cache [i + 128] é retornado diretamente. Entre eles, o Integercache é uma classe estática interna de inteiro, e seu código original é o seguinte:
classe estática privada integercache {estático final int alto; Cache inteiro final estático []; estático {final int low = -128; // Alto valor pode ser configurado pela propriedade int h = 127; if (integercacheHighPropValue! = null) {// use long.decode aqui para evitar a invocação de métodos que // requerem o cache de autoboxing inteiro para ser inicializado int i = long.Decode (IntegerCacheHighPropValue) .IntValue (); i = math.max (i, 127); // O tamanho máximo da matriz é inteiro.max_value h = math.min (i, integer.max_value - -low); } alta = h; cache = novo número inteiro [(alto - baixo) + 1]; int j = baixo; for (int k = 0; k <cache.length; k ++) cache [k] = novo número inteiro (j ++); } private integercache () {}}Podemos ver claramente que o IntegerCache possui um cache de variável estática, que é uma matriz com 256 elementos. O cache também é inicializado no Integercache, ou seja, o i-ésimo elemento é um objeto inteiro com um valor de I-128. -128 a 127 são os objetos inteiros mais usados, e essa abordagem também melhora muito o desempenho. É também por causa disso que "integeri1 = 100; inteiro i2 = 100;", i1 e i2 recebem o mesmo objeto.
Comparando o segundo experimento na extensão, aprendemos que, quando a classe de encapsulamento está em execução == com o tipo básico, a classe de encapsulamento será unida e o resultado do desbaste é comparado com o tipo básico; Enquanto as duas classes de encapsulamento estão em execução == com os outros objetos, eles comparam os endereços dos dois objetos, ou seja, para determinar se as duas referências apontam para o mesmo objeto.
O artigo acima discute brevemente o empacotamento e o Unboxing automáticos do Java e suas armadilhas é todo o conteúdo que compartilho com você. Espero que possa lhe dar uma referência e espero que você possa apoiar mais o wulin.com.