Diferenças na pilha JAVA
Autor:Eve Cole
Data da Última Atualização:2009-11-30 17:08:19
-
1. Pilha e heap são locais usados por Java para armazenar dados em Ram. Ao contrário do C++, Java gerencia automaticamente a pilha e o heap, e os programadores não podem definir diretamente a pilha ou o heap.
2. A vantagem da pilha é que a velocidade de acesso é mais rápida que a do heap, perdendo apenas para os registros localizados diretamente na CPU. Mas a desvantagem é que o tamanho e a vida útil dos dados armazenados na pilha devem ser determinados e há falta de flexibilidade. Além disso, os dados da pilha podem ser compartilhados; consulte o ponto 3 para obter detalhes. A vantagem do heap é que ele pode alocar dinamicamente o tamanho da memória e o tempo de vida não precisa ser informado ao compilador com antecedência. O coletor de lixo do Java coletará automaticamente os dados que não são mais usados. Mas a desvantagem é que devido à necessidade de alocar memória dinamicamente em tempo de execução, a velocidade de acesso é lenta.
3. Existem dois tipos de dados em Java.
Um deles são os tipos primitivos, existem 8 tipos no total, nomeadamente int, short, long, byte, float, double, boolean, char (observe que não existe um tipo básico de string). Este tipo de definição é definido na forma int a = 3 long b = 255L e é chamada de variável automática. Vale ressaltar que variáveis automáticas armazenam valores literais, não instâncias de classes, ou seja, não são referências a classes. Não há classe aqui. Por exemplo, int a = 3 onde a é uma referência apontando para o tipo int, apontando para o valor literal 3. Os dados desses valores literais são conhecidos em tamanho e tempo de vida (esses valores literais são definidos fixamente em um determinado bloco de programa e os valores dos campos desaparecem após a saída do bloco de programa, por uma questão de velocidade). existem na pilha.
Além disso, a pilha possui uma característica especial muito importante, ou seja, os dados armazenados na pilha podem ser compartilhados. Suponha que também definimos:
intuma = 3;
int b = 3;
O compilador primeiro processa int a = 3; primeiro ele cria uma referência para a variável a na pilha e depois procura um endereço com um valor literal de 3. Se não o encontrar, ele abre um endereço para armazenar o literal. valor de 3 e, em seguida, a aponta para o endereço de 3. A seguir, int b = 3 é processado após a criação da variável de referência de b, como já existe um valor literal de 3 na pilha, b é apontado diretamente para o endereço de 3. Desta forma, existe uma situação em que a e b apontam para 3 ao mesmo tempo.
É importante notar que a referência deste valor literal é diferente da referência do objeto de classe. Suponha que as referências de dois objetos de classe apontem para o mesmo objeto ao mesmo tempo. Se uma variável de referência de objeto modificar o estado interno do objeto, a outra variável de referência de objeto refletirá imediatamente a mudança. Por outro lado, modificar o valor de uma referência literal não faz com que o valor de outra referência ao literal também mude. Como no exemplo acima, depois de definirmos os valores de a e b, definimos então a=4 então, b não será igual a 4, mas ainda assim igual a 3; Dentro do compilador, ao encontrar a = 4;, ele pesquisará se existe um valor literal 4 na pilha. Caso contrário, ele reabrirá um endereço para armazenar o valor 4, se já existir. , ele apontará diretamente para este endereço. Portanto, alterações no valor de a não afetarão o valor de b.
O outro são os dados da classe wrapper, como Integer, String, Double e outras classes que agrupam os tipos de dados básicos correspondentes. Todos esses tipos de dados existem no heap. Java usa a instrução new () para informar explicitamente ao compilador que será criado dinamicamente conforme necessário em tempo de execução, por isso é mais flexível, mas a desvantagem é que leva mais tempo. 4. String é um tipo de dado de wrapper especial. Ou seja, ele pode ser criado na forma de String str = new String( "abc" ); ou pode ser criado na forma de String str = "abc" ; visto Integer i = 3 expressão, porque classes e valores literais não podem ser usados alternadamente, exceto para String no JDK 5.0, porque o compilador realiza a conversão de Integer i = new Integer(3) no! fundo) . O primeiro é um processo padronizado de criação de classes, ou seja, em Java tudo é um objeto, e os objetos são instâncias de classes, todas criadas na forma de new(). Algumas classes em Java, como a classe DateFormat, podem retornar uma classe recém-criada por meio do método getInstance() da classe, o que parece violar esse princípio. Na verdade. Esta classe usa o padrão singleton para retornar uma instância da classe, mas esta instância é criada dentro da classe através de new(), e getInstance() esconde esse detalhe de fora. Então por que em String str = "abc" ;, a instância não é criada por meio de new (). Isso viola o princípio acima? Na verdade não.
5. Sobre o funcionamento interno de String str = "abc". Java converte internamente esta instrução nas seguintes etapas:
(1) Primeiro defina uma variável de referência de objeto chamada str para a classe String: String str;
(2) Pesquise na pilha para ver se existe um endereço com valor "abc". Caso contrário, abra um endereço com valor literal "abc", crie um novo objeto o da classe String e adicione o caracteres de o O valor da string aponta para este endereço, e o objeto referenciado o é registrado próximo a este endereço na pilha. Se já existir um endereço com o valor "abc", o objeto o é encontrado e o endereço de o é retornado.
(3) Aponte str para o endereço do objeto o.
É importante notar que geralmente os valores da string na classe String são armazenados diretamente. Mas como String str = "abc"; neste caso, o valor da string salva uma referência aos dados armazenados na pilha!
Para melhor ilustrar este problema, podemos verificá-lo através dos códigos a seguir.
Stringstr1 = "abc";
Stringstr2 = "abc";
System.out.println(str1==str2);
Observe que não usamos str1.equals(str2); porque isso irá comparar se os valores das duas strings são iguais. O sinal ==, de acordo com as instruções do JDK, só retorna verdadeiro quando ambas as referências apontam para o mesmo objeto. O que queremos ver aqui é se str1 e str2 apontam para o mesmo objeto.
Os resultados mostram que a JVM criou duas referências str1 e str2, mas criou apenas um objeto, e ambas as referências apontaram para este objeto.
Vamos dar um passo adiante e alterar o código acima para:
Stringstr1 = "abc";
Stringstr2 = "abc";
str1 = "bcd";
System.out.println(str1 + "," + str2);
System.out.println(str1==str2);
Isso significa que a mudança na atribuição resulta em uma mudança na referência do objeto de classe e str1 aponta para outro novo objeto! E str2 ainda aponta para o objeto original. No exemplo acima, quando alteramos o valor de str1 para “bcd”, a JVM descobriu que não havia endereço para armazenar este valor na pilha, então abriu este endereço e criou um novo objeto cujo valor da string aponta para este endereço .
Na verdade, a classe String foi projetada para ser imutável. Se quiser alterar seu valor, você pode, mas a JVM cria silenciosamente um novo objeto com base no novo valor em tempo de execução e, em seguida, retorna o endereço desse objeto para uma referência à classe original. Embora esse processo de criação seja totalmente automatizado, afinal leva mais tempo. Num ambiente sensível aos requisitos de tempo, terá certos efeitos adversos.
Modifique o código original novamente:
Stringstr1 = "abc";
Stringstr2 = "abc";
str1 = "bcd";
Stringstr3 =str1;
System.out.println(str3);
Stringstr4 = "bcd";
System.out.println(str1 == str4);
A referência ao objeto str3 aponta diretamente para o objeto apontado por str1 (observe que str3 não cria um novo objeto). Após str1 alterar seu valor, crie uma referência de String str4 e aponte para o novo objeto criado porque str1 modificou o valor. Verifica-se que desta vez o str4 não criou um novo objeto, realizando assim novamente o compartilhamento dos dados na pilha.
Vejamos o código a seguir novamente.
Stringstr1 = newString("abc");
Stringstr2 = "abc";
System.out.println(str1==str2);
Duas referências são criadas. Dois objetos são criados. As duas referências apontam para dois objetos diferentes, respectivamente.
Stringstr1 = "abc";
Stringstr2 = newString("abc");
System.out.println(str1==str2);
Duas referências são criadas. Dois objetos são criados. As duas referências apontam para dois objetos diferentes, respectivamente.
Os dois trechos de código acima ilustram que, desde que new () seja usado para criar um novo objeto, ele será criado no heap e seu valor de string será armazenado separadamente, mesmo que seja igual aos dados na pilha. , ele não será compartilhado com os dados na pilha.
6. O valor da classe wrapper do tipo de dados não pode ser modificado. Não apenas o valor da classe String não pode ser modificado, mas todas as classes wrapper de tipo de dados não podem alterar seus valores internos. 7. Conclusão e sugestões:
(1) Quando definimos uma classe usando um formato como String str = "abc";, sempre consideramos que criamos um objeto str da classe String. Preocupe-se com armadilhas! O objeto pode não ter sido criado! A única coisa certa é que será criada uma referência à classe String. Quanto a saber se esta referência aponta para um novo objeto, deve ser considerado de acordo com o contexto, a menos que você crie explicitamente um novo objeto através do método new(). Portanto, uma afirmação mais precisa é criarmos uma variável de referência str que aponta para um objeto da classe String. Essa variável de referência de objeto aponta para uma classe String com um valor "abc". Uma compreensão clara disso é muito útil para eliminar bugs difíceis de encontrar no programa.
(2) Usar String str = "abc" pode melhorar até certo ponto a velocidade de execução do programa, pois a JVM determinará automaticamente se é necessário criar um novo objeto com base na situação real dos dados na pilha. . Já para o código de String str = new String("abc");, novos objetos são sempre criados no heap, independentemente de seus valores de string serem iguais ou se for necessário criar novos objetos, aumentando assim a carga no programa. Essa ideia deveria ser a ideia do modo flyweight, mas não se sabe se a implementação interna do JDK aplica esse modo.
(3) Ao comparar se os valores na classe de embalagem são iguais, use o método equals(); ao testar se as referências de duas classes de embalagem apontam para o mesmo objeto, use ==.
(4) Devido à natureza imutável da classe String, quando a variável String precisa alterar frequentemente seu valor, você deve considerar o uso da classe StringBuffer para melhorar a eficiência do programa.