Série de perguntas para entrevista nº 8: Fale sobre a diferença entre String, StringBuffer e StringBuilder?

"Java Interview Question Series": uma coluna de longo conhecimento e muito interessante. Pesquisando em profundidade, analisando o código-fonte, resumindo os princípios, combinando imagens e textos, criando uma série de artigos sobre a conta oficial e elevando o nível de entrevistas ou não Bem-vindo para continuar a prestar atenção ao [Programa Nova Visão]. Artigo 8.

Além da distribuição de memória e comparação de igualdade, as perguntas mais comuns da entrevista sobre strings são as diferenças entre StringBuffer e StringBuilder.

Se você responder: A classe String é imutável, StringBuffer e StringBuilder são classes mutáveis, StringBuffer é thread-safe, StringBuilder não é thread-safe.

Em termos do resumo acima, parece que sei um pouco. Este artigo o levará a uma compreensão abrangente das três diferenças e da implementação subjacente.

Concatenação de string

Muitos artigos sobre String foram descritos em detalhes nos artigos anteriores, e sua imutabilidade também é causada pela geração de uma nova string na memória sempre que a operação "+" é passada.

String a = "hello ";
String b = "world!";
String ab = a + b;

Para o código acima, o diagrama de distribuição de memória é o seguinte:

imagem

Entre eles, a e b estão no pool de constantes da string quando são inicializados, e o objeto emendado ab está no heap. Pode ser visto intuitivamente que um objeto String é gerado recentemente após o splicing. Se emendado várias vezes, vários objetos intermediários serão gerados.

A conclusão acima foi estabelecida antes do Java 8. No Java 8, o JDK otimizou o splicing do sinal "+". O método de splicing escrito acima será otimizado para processamento com base no método append de StringBuilder.

stack=2, locals=4, args_size=1
     0: ldc           #2                  // String hello
     2: astore_1
     3: ldc           #3                  // String world!
     5: astore_2
     6: new           #4                  // class java/lang/StringBuilder
     9: dup
    10: invokespecial #5                  // Method java/lang/StringBuilder."<init>":()V
    13: aload_1
    14: invokevirtual #6                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
    17: aload_2
    18: invokevirtual #6                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
    21: invokevirtual #7                  // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
    24: astore_3
    25: return

O acima é o resultado da descompilação do bytecode através do comando javap -verbose Obviamente, você pode ver a criação de StringBuilder e a chamada do método append.

Neste ponto, se você responder de uma forma geral: concatenar strings por meio do sinal de mais criará vários objetos String, portanto, o desempenho é pior do que StringBuilder, o que está errado. Porque o efeito da junção do sinal de mais é essencialmente o mesmo que StringBuilder após ser processado pelo compilador.

Se você usar o seguinte texto em seu código:

StringBuilder sb = new StringBuilder("hello ");
sb.append("world!");
System.out.println(sb.toString());

O plug-in do compilador até sugere que você use String em seu lugar.

Comparação de StringBuffer e StringBuilder

Os códigos principais de StringBuffer e StringBuilder são basicamente os mesmos e muitos códigos são comuns. Ambas as classes herdam da classe abstrata AbstractStringBuilder.

Vamos dar uma olhada nas diferenças entre o método de construção e o método append, um por um. Primeiro, olhe para o método de construção de StringBuilder:

public StringBuilder(String str) {
    super(str.length() + 16);
    append(str);
}

O super método é o método de construção do AbstractStringBuilder chamado. O mesmo é verdade no método de construção do StringBuffer correspondente:

public StringBuffer(String str) {
    super(str.length() + 16);
    append(str);
}

Do método de construção, StringBuffer e StringBuilder são os mesmos. Vamos dar uma olhada no método append. StringBuilder é implementado da seguinte maneira:

@Override
public StringBuilder append(String str) {
    super.append(str);
    return this;
}

O método correspondente a StringBuffer é o seguinte:

@Override
public synchronized StringBuffer append(String str) {
    toStringCache = null;
    super.append(str);
    return this;
}

Obviamente, além da atribuição interna da variável toStringCache para null na implementação do método append de StringBuffer, a única diferença é que o método usa synchronized para processamento de sincronização.

toStringCache é usado para armazenar em cache a string gerada na última vez que o método toString foi chamado. Quando o conteúdo de StringBuffer muda, o valor alterado também muda.

Por meio da comparação do método append acima, podemos facilmente descobrir que StringBuffer é thread-safe e StringBuilder não é thread-safe. Obviamente, usar o synchronized para o processamento de sincronização reduzirá muito o desempenho.

Implementação de baixo nível de StringBuffer e StringBuilder

StringBuffer e StringBuilder chamam o método de construção da classe pai:

AbstractStringBuilder(int capacity) {
    value = new char[capacity];
}

Por meio desse método de construção, podemos ver que o atributo-chave que eles usam para processar as informações da string é o valor. Inicialize um array char [] cujo comprimento é o comprimento da string de entrada +16, que é o valor do valor, para armazenar a string real durante a inicialização.

Depois de chamar o método de construção da classe pai, o respectivo método append é chamado (consulte o código anterior), e o processamento principal nele chama o método append da classe pai:

public AbstractStringBuilder append(String str) {
    if (str == null)
        return appendNull();
    int len = str.length();
    ensureCapacityInternal(count + len);
    str.getChars(0, len, value, count);
    count += len;
    return this;
}

No código acima, o método str.getChars é usado para emendar a string str de entrada e preenchê-la após a matriz de valor original. E a contagem é usada para registrar o comprimento que foi usado no número do valor atual.

imagem

Portanto, quando a operação sincronizada não é usada para sincronização, onde ocorre a insegurança do thread? A contagem + = len no código acima não é uma operação atômica. Por exemplo, a contagem atual é 5, e os dois threads executam a operação ++ ao mesmo tempo, e os valores obtidos são ambos 5. Depois que a operação de adição é realizada, o valor é atribuído a count e os dois threads recebem o valor 6 em vez de 7. Neste momento, existe um problema de insegurança do thread.

Por que String deve ser projetada para ser imutável

O design de String para ser imutável em Java é o resultado da consideração abrangente de vários fatores, pelas seguintes razões:

1. A necessidade de um pool de constantes de string. Se a string for variável, a mudança de um objeto afetará outro objeto independente. Este também é um pré-requisito para a existência de um pool de constantes de string.

2. O código hash de objetos String em Java é freqüentemente usado, como em contêineres como HashMap. A string constante garante a exclusividade do código hash, que pode ser armazenado em cache e usado.

3. Segurança, para garantir que String permanece inalterada quando passada como um parâmetro para evitar riscos de segurança. Por exemplo, o nome de usuário do banco de dados, senha, caminho de acesso, etc. permanecem inalterados durante o processo de transmissão para evitar que o valor do objeto apontado pela string alterada seja alterado.

4. Como as variáveis ​​de string são imutáveis, elas podem ser compartilhadas e usadas em vários threads.

resumo

Todos nós conhecemos perguntas de teste mecânico simples, mas devemos aprender mais sobre os princípios de implementação subjacentes no processo de memorização das perguntas da entrevista, o que não só ajudará a entender o "porquê", mas também aprenderá conhecimentos e princípios mais relevantes.

Neste artigo, a explicação das etapas de copiar os dados internos de StringBuilder e StringBuffer e expandir o array é simplificada. Os interessados ​​podem continuar a realizar pesquisas aprofundadas no código-fonte.

Link original: " Série de perguntas para entrevista nº 8: Fale sobre a diferença entre String, StringBuffer, StringBuilder?


Nova Visão de Procedimento

A conta pública " Nova Visão do Programa ", uma plataforma que permite melhorar simultaneamente o seu soft power e hard technology, disponibilizando enormes quantidades de dados

Conta oficial do WeChat: nova visão do programa

Acho que você gosta

Origin blog.csdn.net/wo541075754/article/details/108604389
Recomendado
Clasificación