Eu estava ocupado com a implementação lógica do projeto durante a semana e tinha algum tempo no sábado, então tirei da estante a versão grossa em inglês de Thinking In Java e li sobre a emenda de objetos string. Faça uma tradução com referência a este livro, acrescente suas próprias idéias e escreva este artigo para registrá-las.
Objeto String imutável
Em Java, os objetos String são imutáveis. No código, você pode criar vários aliases para um objeto String. Mas todos esses apelidos se referem à mesma coisa.
Por exemplo, s1 e s2 são aliases do objeto "droidyue.com" e os aliases armazenam referências aos objetos reais. Então s1 = s2
Copie o código do código da seguinte forma:
String s1 = "droidyue.com";
Sequência s2 = s1;
System.out.println("s1 e s2 tem a mesma referência =" + (s1 == s2));
O único operador sobrecarregado em Java
Em Java, o único operador sobrecarregado está relacionado à concatenação de strings. +,+=. Além disso, os designers Java não permitem a sobrecarga de outros operadores.
Análise de emenda
Existe realmente um custo de desempenho?
Depois de compreender os dois pontos acima, você pode ter este pensamento: como os objetos Sting são imutáveis, a emenda de múltiplas (três ou mais) strings produzirá inevitavelmente objetos String intermediários redundantes.
Copie o código do código da seguinte forma:
String nome_do_usuário = "Andy";
String idade = "24";
String job = "Desenvolvedor";
String info = userName + idade + trabalho;
Para obter as informações acima, userName e age serão unidos para gerar um objeto String temporário t1, o conteúdo é Andy24 e, em seguida, t1 e job serão unidos para gerar o objeto de informação final que precisamos. intermediário t1 é gerado, e t1 é criado Depois, se não houver reciclagem ativa, inevitavelmente ocupará uma certa quantidade de espaço. Se for uma emenda de muitas strings (assumindo centenas delas, principalmente em chamadas para toString de objetos), o custo será ainda maior e o desempenho será bastante reduzido.
Processamento de otimização do compilador
Existe realmente um custo de desempenho acima? Não há otimização de processamento especial para concatenação de strings tão comumente usada? Essa otimização é executada quando o compilador compila .java em bytecode.
Se um programa Java quiser ser executado, ele precisará passar por dois períodos: tempo de compilação e tempo de execução. Durante a compilação, o compilador Java (Compilador) converte o arquivo java em bytecode. Em tempo de execução, a Java Virtual Machine (JVM) executa o bytecode gerado em tempo de compilação. Durante esses dois períodos, o Java alcançou a chamada compilação em um só lugar e execução em qualquer lugar.
Vamos experimentar quais otimizações foram feitas durante a compilação e podemos criar um trecho de código que pode prejudicar o desempenho.
Copie o código do código da seguinte forma:
Concatenação de classe pública {
public static void main(String[] args) {
String nome_do_usuário = "Andy";
String idade = "24";
String job = "Desenvolvedor";
String info = userName + idade + trabalho;
System.out.println(info);
}
}
Compile Concatenation.java. getConcatenation.class
Copie o código do código da seguinte forma:
javacConcatenation.java
Em seguida, usamos javap para descompilar o arquivo Concatenation.class compilado. javap -c Concatenação. Se o comando javap não for encontrado, considere adicionar o diretório onde o javap está localizado à variável de ambiente ou usar o caminho completo para javap.
Copie o código do código da seguinte forma:
17:22:04-androidyue~/workspace_adt/strings/src$ javap -c Concatenação
Compilado de "Concatenation.java"
Concatenação de classe pública {
concatenação pública();
Código:
0: aload_0
1: invocaespecial #1 // Método java/lang/Object."<init>":()V
4: retorno
public static void main(java.lang.String[]);
Código:
0: ldc #2 // String Andy
2: uma loja_1
3: ldc #3 // Sequência 24
5: uma loja_2
6: ldc #4 // Desenvolvedor de String
8: uma loja_3
9: novo #5 // classe java/lang/StringBuilder
12: dup
13: invocaespecial #6 // Método java/lang/StringBuilder."<init>":()V
16: aload_1
17: invocavirtual #7 // Método java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
20: aload_2
21: invocavirtual #7 // Método java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
24: carga_3
25: invocavirtual #7 // Método java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
28: invocarvirtual #8 // Método java/lang/StringBuilder.toString:()Ljava/lang/String;
31: loja 4
33: getstatic #9 // Campo java/lang/System.out:Ljava/io/PrintStream;
36: carga 4
38: invocarvirtual #10 // Método java/io/PrintStream.println:(Ljava/lang/String;)V
41: retorno
}
Entre eles, ldc, astore, etc. estão instruções de bytecode Java, semelhantes às instruções de montagem. Os comentários a seguir usam conteúdo relacionado ao Java para explicação. Podemos ver que existem muitos StringBuilders acima, mas não os chamamos explicitamente no código Java. Esta é a otimização feita pelo compilador Java. Quando o compilador Java encontrar a emenda de string, ele criará um objeto StringBuilder e o seguinte. Na verdade, a emenda chama o método anexar do objeto StringBuilder. Desta forma, não haverá problemas com os quais nos preocupamos acima.
Otimização do compilador sozinha?
Já que o compilador fez a otimização para nós, é suficiente confiar apenas na otimização do compilador. Claro que não?
Abaixo, vemos um trecho de código não otimizado com desempenho inferior
Copie o código do código da seguinte forma:
public void implicitUseStringBuilder(String[] valores) {
Resultado da string = "";
for (int i = 0; i < valores.comprimento; i ++) {
resultado += valores[i];
}
System.out.println(resultado);
}
Use javac para compilar e javap para visualizar
Copie o código do código da seguinte forma:
public void implicitUseStringBuilder(java.lang.String[]);
Código:
0: ldc #11 // String
2: uma loja_2
3: íconest_0
4: istore_3
5: carregar_3
6: aload_1
7: comprimento da matriz
8: if_icmpge 38
11: novo #5 //classe java/lang/StringBuilder
14: dup
15: invocaespecial #6 // Método java/lang/StringBuilder."<init>":()V
18: carga_2
19: invocavirtual #7 // Método java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
22: aload_1
23: carregar_3
24: aaload
25: invocavirtual #7 // Método java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
28: invocavirtual #8 // Método java/lang/StringBuilder.toString:()Ljava/lang/String;
31: uma loja_2
32: inc 3, 1
35: vá para 5
38: getstatic #9 // Campo java/lang/System.out:Ljava/io/PrintStream;
41: aload_2
42: invocarvirtual #10 // Método java/io/PrintStream.println:(Ljava/lang/String;)V
45: retorno
Entre eles, 8: if_icmpge 38 e 35: goto 5 formam um loop. 8: if_icmpge 38 significa que se a comparação de inteiros da pilha de operandos JVM for maior ou igual a (o resultado oposto de i <values.length), pule para a linha 38 (System.out). 35: goto 5 significa pular diretamente para a linha 5.
Mas uma coisa muito importante aqui é que a criação de objetos StringBuilder ocorre entre loops, o que significa quantos objetos StringBuilder serão criados em tantos loops, o que obviamente não é bom. Código nu de baixo nível.
Um pouco de otimização pode melhorar instantaneamente seu desempenho.
Copie o código do código da seguinte forma:
public void explicitUseStringBuider(String[] valores) {
Resultado StringBuilder = new StringBuilder();
for (int i = 0; i < valores.comprimento; i ++) {
resultado.append(valores[i]);
}
}
Informações compiladas correspondentes
Copie o código do código da seguinte forma:
public void explicitUseStringBuider(java.lang.String[]);
Código:
0: novo #5 //classe java/lang/StringBuilder
3: idiota
4: invocaespecial #6 // Método java/lang/StringBuilder."<init>":()V
7: uma loja_2
8: íconest_0
9: istore_3
10: carregar_3
11: aload_1
12: comprimento da matriz
13: if_icmpge 30
16: carga_2
17: aload_1
18: carregar_3
19: aaload
20: invocavirtual #7 // Método java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
23: pop
24: inc 3, 1
27: vá para 10
30: retorno
Como pode ser visto acima, 13: if_icmpge 30 e 27: goto 10 formam um loop e 0: new #5 está fora do loop, portanto, StringBuilder não será criado várias vezes.
Em geral, precisamos tentar evitar a criação implícita ou explícita de StringBuilder no corpo do loop. Portanto, aqueles que entendem como o código é compilado e como é executado internamente podem escrever código de nível superior.
Se houver algum erro no artigo acima, critique e corrija-o.