Java Internals: O Custo Cognitivo da Imutabilidade de Strings

Introdução
Você já fez code review e encontrou algo assim?
&lt;span class=<span class=<span class="hljs-string">"hljs-string"</span>>&quot;hljs-keyword&quot;</span>&gt;<span class=<span class="hljs-string">"hljs-keyword"</span>><span class="hljs-keyword">public</span></span>&lt;/span&gt; &lt;span class=<span class=<span class="hljs-string">"hljs-string"</span>>&quot;hljs-keyword&quot;</span>&gt;<span class=<span class="hljs-string">"hljs-keyword"</span>><span class="hljs-keyword">static</span></span>&lt;/span&gt; String &lt;span class=<span class=<span class="hljs-string">"hljs-string"</span>>&quot;hljs-title function_&quot;</span>&gt;stopIt&lt;/span&gt;&lt;span class=<span class=<span class="hljs-string">"hljs-string"</span>>&quot;hljs-params&quot;</span>&gt;(String str)&lt;/span&gt; {
str.replace(str, &lt;span class=<span class=<span class="hljs-string">"hljs-string"</span>>&quot;hljs-string&quot;</span>&gt;&amp;quot;stop&amp;quot;&lt;/span&gt;); &lt;span class=<span class=<span class="hljs-string">"hljs-string"</span>>&quot;hljs-comment&quot;</span>&gt;<span class=<span class="hljs-string">"hljs-comment"</span>><span class="hljs-comment">// parece certo...&lt;/span&gt;</span></span>
&lt;span class=<span class=<span class="hljs-string">"hljs-string"</span>>&quot;hljs-keyword&quot;</span>&gt;<span class=<span class="hljs-string">"hljs-keyword"</span>><span class="hljs-keyword">return</span></span>&lt;/span&gt; str; &lt;span class=<span class=<span class="hljs-string">"hljs-string"</span>>&quot;hljs-comment&quot;</span>&gt;<span class=<span class="hljs-string">"hljs-comment"</span>><span class="hljs-comment">// mas retorna &amp;quot;start&amp;quot;&lt;/span&gt;</span></span>
}
A primeira reação costuma ser: "como esse dev não sabia que String é imutável?"
A resposta honesta é: ele provavelmente sabia. O problema não é falta de conhecimento - é o que podemos chamar de custo cognitivo da semântica verbal.
O Problema: a Semântica dos Verbos
Verbos como replace, trim, toUpperCase e toLowerCase carregam uma carga semântica de ação direta. O cérebro os processa como modificadores - da mesma forma que list.add() ou map.put() realmente alteram o estado do objeto.
Essa é a armadilha. Não é que o desenvolvedor esqueceu a imutabilidade. É que a leitura fluida do código ativa um padrão cognitivo diferente da realidade da JVM.
Veja a diferença de comportamento com coleções mutáveis:
&lt;span class=<span class=<span class="hljs-string">"hljs-string"</span>>&quot;hljs-comment&quot;</span>&gt;<span class=<span class="hljs-string">"hljs-comment"</span>><span class="hljs-comment">// List é mutável - add() modifica o estado diretamente&lt;/span&gt;</span></span>
List&amp;lt;String&amp;gt; list = &lt;span class=<span class=<span class="hljs-string">"hljs-string"</span>>&quot;hljs-keyword&quot;</span>&gt;<span class=<span class="hljs-string">"hljs-keyword"</span>><span class="hljs-keyword">new</span></span>&lt;/span&gt; &lt;span class=<span class=<span class="hljs-string">"hljs-string"</span>>&quot;hljs-title class_&quot;</span>&gt;ArrayList&lt;/span&gt;&amp;lt;&amp;gt;();
list.add(&lt;span class=<span class=<span class="hljs-string">"hljs-string"</span>>&quot;hljs-string&quot;</span>&gt;&amp;quot;item&amp;quot;&lt;/span&gt;); &lt;span class=<span class=<span class="hljs-string">"hljs-string"</span>>&quot;hljs-comment&quot;</span>&gt;<span class=<span class="hljs-string">"hljs-comment"</span>><span class="hljs-comment">// funciona como esperado&lt;/span&gt;</span></span>
System.out.println(list); &lt;span class=<span class=<span class="hljs-string">"hljs-string"</span>>&quot;hljs-comment&quot;</span>&gt;<span class=<span class="hljs-string">"hljs-comment"</span>><span class="hljs-comment">// [item]&lt;/span&gt;</span></span>
&lt;span class=<span class=<span class="hljs-string">"hljs-string"</span>>&quot;hljs-comment&quot;</span>&gt;<span class=<span class="hljs-string">"hljs-comment"</span>><span class="hljs-comment">// String é imutável - replace() não modifica nada&lt;/span&gt;</span></span>
&lt;span class=<span class=<span class="hljs-string">"hljs-string"</span>>&quot;hljs-type&quot;</span>&gt;String&lt;/span&gt; &lt;span class=<span class=<span class="hljs-string">"hljs-string"</span>>&quot;hljs-variable&quot;</span>&gt;texto&lt;/span&gt; &lt;span class=<span class=<span class="hljs-string">"hljs-string"</span>>&quot;hljs-operator&quot;</span>&gt;=&lt;/span&gt; &lt;span class=<span class=<span class="hljs-string">"hljs-string"</span>>&quot;hljs-string&quot;</span>&gt;&amp;quot;start&amp;quot;&lt;/span&gt;;
texto.replace(&lt;span class=<span class=<span class="hljs-string">"hljs-string"</span>>&quot;hljs-string&quot;</span>&gt;&amp;quot;start&amp;quot;&lt;/span&gt;, &lt;span class=<span class=<span class="hljs-string">"hljs-string"</span>>&quot;hljs-string&quot;</span>&gt;&amp;quot;stop&amp;quot;&lt;/span&gt;); &lt;span class=<span class=<span class="hljs-string">"hljs-string"</span>>&quot;hljs-comment&quot;</span>&gt;<span class=<span class="hljs-string">"hljs-comment"</span>><span class="hljs-comment">// resultado descartado silenciosamente&lt;/span&gt;</span></span>
System.out.println(texto); &lt;span class=<span class=<span class="hljs-string">"hljs-string"</span>>&quot;hljs-comment&quot;</span>&gt;<span class=<span class="hljs-string">"hljs-comment"</span>><span class="hljs-comment">// &amp;quot;start&amp;quot;&lt;/span&gt;</span></span>
O compilador não avisa. O IDE pode (e deve) alertar, mas em projetos com muitos warnings suprimidos, esse erro passa despercebido facilmente.
O que Acontece na JVM
Quando uma String é criada, seu conteúdo é alocado no String Constant Pool (SCP) - uma área especial da heap gerenciada pela JVM. Uma vez lá, o conteúdo é imutável e fixo.
Quando você chama str.replace(str, "stop"), a JVM:
- Lê o conteúdo original de
strno SCP - Cria um novo objeto String com o valor
"stop" - Retorna a referência para esse novo objeto
- Descarta a referência se você não a capturar
O objeto original não é tocado. A variável str continua apontando para "start".
SCP (String Constant Pool)
┌──────────────┐ ┌──────────────┐
│ &amp;quot;start&amp;quot; │ │ &amp;quot;stop&amp;quot; │
│ (imutável) │ │ (nova inst.) │
└──────┬───────┘ └──────┬───────┘
│ │
str ← referência descartada se não capturada
A Solução: Captura Explícita da Nova Referência
Métodos de manipulação de String não são mutators (modificadores de estado). São fábricas de novos objetos. Para que a alteração persista, a nova referência precisa ser explicitamente capturada:
&lt;span class=<span class=<span class="hljs-string">"hljs-string"</span>>&quot;hljs-comment&quot;</span>&gt;<span class=<span class="hljs-string">"hljs-comment"</span>><span class="hljs-comment">// ✗ Errado - resultado descartado&lt;/span&gt;</span></span>
&lt;span class=<span class=<span class="hljs-string">"hljs-string"</span>>&quot;hljs-keyword&quot;</span>&gt;<span class=<span class="hljs-string">"hljs-keyword"</span>><span class="hljs-keyword">public</span></span>&lt;/span&gt; &lt;span class=<span class=<span class="hljs-string">"hljs-string"</span>>&quot;hljs-keyword&quot;</span>&gt;<span class=<span class="hljs-string">"hljs-keyword"</span>><span class="hljs-keyword">static</span></span>&lt;/span&gt; String &lt;span class=<span class=<span class="hljs-string">"hljs-string"</span>>&quot;hljs-title function_&quot;</span>&gt;stopIt&lt;/span&gt;&lt;span class=<span class=<span class="hljs-string">"hljs-string"</span>>&quot;hljs-params&quot;</span>&gt;(String str)&lt;/span&gt; {
str.replace(str, &lt;span class=<span class=<span class="hljs-string">"hljs-string"</span>>&quot;hljs-string&quot;</span>&gt;&amp;quot;stop&amp;quot;&lt;/span&gt;);
&lt;span class=<span class=<span class="hljs-string">"hljs-string"</span>>&quot;hljs-keyword&quot;</span>&gt;<span class=<span class="hljs-string">"hljs-keyword"</span>><span class="hljs-keyword">return</span></span>&lt;/span&gt; str; &lt;span class=<span class=<span class="hljs-string">"hljs-string"</span>>&quot;hljs-comment&quot;</span>&gt;<span class=<span class="hljs-string">"hljs-comment"</span>><span class="hljs-comment">// retorna &amp;quot;start&amp;quot;&lt;/span&gt;</span></span>
}
&lt;span class=<span class=<span class="hljs-string">"hljs-string"</span>>&quot;hljs-comment&quot;</span>&gt;<span class=<span class="hljs-string">"hljs-comment"</span>><span class="hljs-comment">// ✓ Correto - nova instância capturada&lt;/span&gt;</span></span>
&lt;span class=<span class=<span class="hljs-string">"hljs-string"</span>>&quot;hljs-keyword&quot;</span>&gt;<span class=<span class="hljs-string">"hljs-keyword"</span>><span class="hljs-keyword">public</span></span>&lt;/span&gt; &lt;span class=<span class=<span class="hljs-string">"hljs-string"</span>>&quot;hljs-keyword&quot;</span>&gt;<span class=<span class="hljs-string">"hljs-keyword"</span>><span class="hljs-keyword">static</span></span>&lt;/span&gt; String &lt;span class=<span class=<span class="hljs-string">"hljs-string"</span>>&quot;hljs-title function_&quot;</span>&gt;stopIt&lt;/span&gt;&lt;span class=<span class=<span class="hljs-string">"hljs-string"</span>>&quot;hljs-params&quot;</span>&gt;(String str)&lt;/span&gt; {
&lt;span class=<span class=<span class="hljs-string">"hljs-string"</span>>&quot;hljs-keyword&quot;</span>&gt;<span class=<span class="hljs-string">"hljs-keyword"</span>><span class="hljs-keyword">return</span></span>&lt;/span&gt; str.replace(str, &lt;span class=<span class=<span class="hljs-string">"hljs-string"</span>>&quot;hljs-string&quot;</span>&gt;&amp;quot;stop&amp;quot;&lt;/span&gt;); &lt;span class=<span class=<span class="hljs-string">"hljs-string"</span>>&quot;hljs-comment&quot;</span>&gt;<span class=<span class="hljs-string">"hljs-comment"</span>><span class="hljs-comment">// retorna &amp;quot;stop&amp;quot;&lt;/span&gt;</span></span>
}
O mesmo padrão se aplica a toda a API de String:
&lt;span class=<span class=<span class="hljs-string">"hljs-string"</span>>&quot;hljs-type&quot;</span>&gt;String&lt;/span&gt; &lt;span class=<span class=<span class="hljs-string">"hljs-string"</span>>&quot;hljs-variable&quot;</span>&gt;nome&lt;/span&gt; &lt;span class=<span class=<span class="hljs-string">"hljs-string"</span>>&quot;hljs-operator&quot;</span>&gt;=&lt;/span&gt; &lt;span class=<span class=<span class="hljs-string">"hljs-string"</span>>&quot;hljs-string&quot;</span>&gt;&amp;quot; guilherme &amp;quot;&lt;/span&gt;;
&lt;span class=<span class=<span class="hljs-string">"hljs-string"</span>>&quot;hljs-comment&quot;</span>&gt;<span class=<span class="hljs-string">"hljs-comment"</span>><span class="hljs-comment">// ✗ Errado&lt;/span&gt;</span></span>
nome.trim();
nome.toUpperCase();
&lt;span class=<span class=<span class="hljs-string">"hljs-string"</span>>&quot;hljs-comment&quot;</span>&gt;<span class=<span class="hljs-string">"hljs-comment"</span>><span class="hljs-comment">// ✓ Correto&lt;/span&gt;</span></span>
nome = nome.trim().toUpperCase();
System.out.println(nome); &lt;span class=<span class=<span class="hljs-string">"hljs-string"</span>>&quot;hljs-comment&quot;</span>&gt;<span class=<span class="hljs-string">"hljs-comment"</span>><span class="hljs-comment">// &amp;quot;GUILHERME&amp;quot;&lt;/span&gt;</span></span>
Por que a Imutabilidade Existe
A imutabilidade das Strings não é uma limitação - é uma decisão arquitetural deliberada que viabiliza otimizações críticas na JVM:
1. String Constant Pool e Compartilhamento de Referências
O SCP permite que literais idênticos compartilhem a mesma referência em memória:
&lt;span class=<span class=<span class="hljs-string">"hljs-string"</span>>&quot;hljs-type&quot;</span>&gt;String&lt;/span&gt; &lt;span class=<span class=<span class="hljs-string">"hljs-string"</span>>&quot;hljs-variable&quot;</span>&gt;a&lt;/span&gt; &lt;span class=<span class=<span class="hljs-string">"hljs-string"</span>>&quot;hljs-operator&quot;</span>&gt;=&lt;/span&gt; &lt;span class=<span class=<span class="hljs-string">"hljs-string"</span>>&quot;hljs-string&quot;</span>&gt;&amp;quot;Java&amp;quot;&lt;/span&gt;;
&lt;span class=<span class=<span class="hljs-string">"hljs-string"</span>>&quot;hljs-type&quot;</span>&gt;String&lt;/span&gt; &lt;span class=<span class=<span class="hljs-string">"hljs-string"</span>>&quot;hljs-variable&quot;</span>&gt;b&lt;/span&gt; &lt;span class=<span class=<span class="hljs-string">"hljs-string"</span>>&quot;hljs-operator&quot;</span>&gt;=&lt;/span&gt; &lt;span class=<span class=<span class="hljs-string">"hljs-string"</span>>&quot;hljs-string&quot;</span>&gt;&amp;quot;Java&amp;quot;&lt;/span&gt;;
System.out.println(a == b); &lt;span class=<span class=<span class="hljs-string">"hljs-string"</span>>&quot;hljs-comment&quot;</span>&gt;<span class=<span class="hljs-string">"hljs-comment"</span>><span class="hljs-comment">// true - mesma referência no SCP&lt;/span&gt;</span></span>
Se Strings fossem mutáveis, compartilhar referências seria perigoso: alterar a afetaria b.
2. Thread Safety Nativa
Strings são inerentemente thread-safe. Não há necessidade de synchronized, volatile ou locks para compartilhar uma String entre threads, porque nenhuma thread pode modificar seu conteúdo.
&lt;span class=<span class=<span class="hljs-string">"hljs-string"</span>>&quot;hljs-comment&quot;</span>&gt;<span class=<span class="hljs-string">"hljs-comment"</span>><span class="hljs-comment">// Seguro compartilhar entre threads sem qualquer sincronização&lt;/span&gt;</span></span>
&lt;span class=<span class=<span class="hljs-string">"hljs-string"</span>>&quot;hljs-type&quot;</span>&gt;String&lt;/span&gt; &lt;span class=<span class=<span class="hljs-string">"hljs-string"</span>>&quot;hljs-variable&quot;</span>&gt;endpoint&lt;/span&gt; &lt;span class=<span class=<span class="hljs-string">"hljs-string"</span>>&quot;hljs-operator&quot;</span>&gt;=&lt;/span&gt; &lt;span class=<span class=<span class="hljs-string">"hljs-string"</span>>&quot;hljs-string&quot;</span>&gt;&amp;quot;https:<span class=<span class="hljs-string">"hljs-comment"</span>><span class="hljs-comment">//getcaramelo.dev/v1/posts&amp;quot;&lt;/span&gt;;</span></span>
&lt;span class=<span class=<span class="hljs-string">"hljs-string"</span>>&quot;hljs-type&quot;</span>&gt;ExecutorService&lt;/span&gt; &lt;span class=<span class=<span class="hljs-string">"hljs-string"</span>>&quot;hljs-variable&quot;</span>&gt;pool&lt;/span&gt; &lt;span class=<span class=<span class="hljs-string">"hljs-string"</span>>&quot;hljs-operator&quot;</span>&gt;=&lt;/span&gt; Executors.newVirtualThreadPerTaskExecutor();
&lt;span class=<span class=<span class="hljs-string">"hljs-string"</span>>&quot;hljs-keyword&quot;</span>&gt;<span class=<span class="hljs-string">"hljs-keyword"</span>><span class="hljs-keyword">for</span></span>&lt;/span&gt; (&lt;span class=<span class=<span class="hljs-string">"hljs-string"</span>>&quot;hljs-type&quot;</span>&gt;<span class=<span class="hljs-string">"hljs-type"</span>><span class="hljs-type">int</span></span>&lt;/span&gt; &lt;span class=<span class=<span class="hljs-string">"hljs-string"</span>>&quot;hljs-variable&quot;</span>&gt;i&lt;/span&gt; &lt;span class=<span class=<span class="hljs-string">"hljs-string"</span>>&quot;hljs-operator&quot;</span>&gt;=&lt;/span&gt; &lt;span class=<span class=<span class="hljs-string">"hljs-string"</span>>&quot;hljs-number&quot;</span>&gt;<span class=<span class="hljs-string">"hljs-number"</span>><span class="hljs-number">0</span></span>&lt;/span&gt;; i &amp;lt; &lt;span class=<span class=<span class="hljs-string">"hljs-string"</span>>&quot;hljs-number&quot;</span>&gt;<span class=<span class="hljs-string">"hljs-number"</span>><span class="hljs-number">100</span></span>&lt;/span&gt;; i++) {
pool.submit(() -&amp;gt; fetch(endpoint)); &lt;span class=<span class=<span class="hljs-string">"hljs-string"</span>>&quot;hljs-comment&quot;</span>&gt;<span class=<span class="hljs-string">"hljs-comment"</span>><span class="hljs-comment">// endpoint é imutável, sem risco&lt;/span&gt;</span></span>
}
3. String Deduplication (G1GC)
O G1 Garbage Collector possui uma otimização chamada String Deduplication: ele identifica objetos String com conteúdo idêntico em diferentes regiões da heap e consolida suas referências para apontar ao mesmo char[] interno. Isso só é possível porque o conteúdo é imutável - a JVM tem a garantia de que ninguém vai alterá-lo por baixo dos panos.
4. HashCode Cacheado
O hashCode() de uma String é computado uma vez e armazenado internamente. Como o conteúdo nunca muda, o cache é sempre válido - o que torna operações com HashMap<String, ?> mais eficientes do que com objetos mutáveis equivalentes.
Quando Você Precisa de Mutabilidade: StringBuilder
Toda vez que você precisa construir uma String em múltiplos passos, o StringBuilder entra em cena - mas usá-lo em todos os casos é um exagero que pode custar em legibilidade sem ganho real de performance.
O Cenário Crítico: Concatenação em Loops
O uso do operador + dentro de um loop é o principal caso onde o StringBuilder deve ser preferido explicitamente. Cada iteração com + pode criar um novo objeto String intermediário na heap, gerando lixo que o GC precisará processar. Em volumes altos, isso resulta em complexidade de tempo O(n²):
&lt;span class=<span class=<span class="hljs-string">"hljs-string"</span>>&quot;hljs-comment&quot;</span>&gt;<span class=<span class="hljs-string">"hljs-comment"</span>><span class="hljs-comment">// ✗ Ineficiente - potencialmente O(n²)&lt;/span&gt;</span></span>
&lt;span class=<span class=<span class="hljs-string">"hljs-string"</span>>&quot;hljs-comment&quot;</span>&gt;<span class=<span class="hljs-string">"hljs-comment"</span>><span class="hljs-comment">// cada iteração cria um objeto String descartável&lt;/span&gt;</span></span>
&lt;span class=<span class=<span class="hljs-string">"hljs-string"</span>>&quot;hljs-type&quot;</span>&gt;String&lt;/span&gt; &lt;span class=<span class=<span class="hljs-string">"hljs-string"</span>>&quot;hljs-variable&quot;</span>&gt;resultado&lt;/span&gt; &lt;span class=<span class=<span class="hljs-string">"hljs-string"</span>>&quot;hljs-operator&quot;</span>&gt;=&lt;/span&gt; &lt;span class=<span class=<span class="hljs-string">"hljs-string"</span>>&quot;hljs-string&quot;</span>&gt;&amp;quot;&amp;quot;&lt;/span&gt;;
&lt;span class=<span class=<span class="hljs-string">"hljs-string"</span>>&quot;hljs-keyword&quot;</span>&gt;<span class=<span class="hljs-string">"hljs-keyword"</span>><span class="hljs-keyword">for</span></span>&lt;/span&gt; (String parte : partes) {
resultado += parte;
}
&lt;span class=<span class=<span class="hljs-string">"hljs-string"</span>>&quot;hljs-comment&quot;</span>&gt;<span class=<span class="hljs-string">"hljs-comment"</span>><span class="hljs-comment">// ✓ Eficiente - O(n)&lt;/span&gt;</span></span>
&lt;span class=<span class=<span class="hljs-string">"hljs-string"</span>>&quot;hljs-comment&quot;</span>&gt;<span class=<span class="hljs-string">"hljs-comment"</span>><span class="hljs-comment">// uma única instância de buffer, append in-place&lt;/span&gt;</span></span>
&lt;span class=<span class=<span class="hljs-string">"hljs-string"</span>>&quot;hljs-type&quot;</span>&gt;StringBuilder&lt;/span&gt; &lt;span class=<span class=<span class="hljs-string">"hljs-string"</span>>&quot;hljs-variable&quot;</span>&gt;sb&lt;/span&gt; &lt;span class=<span class=<span class="hljs-string">"hljs-string"</span>>&quot;hljs-operator&quot;</span>&gt;=&lt;/span&gt; &lt;span class=<span class=<span class="hljs-string">"hljs-string"</span>>&quot;hljs-keyword&quot;</span>&gt;<span class=<span class="hljs-string">"hljs-keyword"</span>><span class="hljs-keyword">new</span></span>&lt;/span&gt; &lt;span class=<span class=<span class="hljs-string">"hljs-string"</span>>&quot;hljs-title class_&quot;</span>&gt;StringBuilder&lt;/span&gt;();
&lt;span class=<span class=<span class="hljs-string">"hljs-string"</span>>&quot;hljs-keyword&quot;</span>&gt;<span class=<span class="hljs-string">"hljs-keyword"</span>><span class="hljs-keyword">for</span></span>&lt;/span&gt; (String parte : partes) {
sb.append(parte);
}
&lt;span class=<span class=<span class="hljs-string">"hljs-string"</span>>&quot;hljs-type&quot;</span>&gt;String&lt;/span&gt; &lt;span class=<span class=<span class="hljs-string">"hljs-string"</span>>&quot;hljs-variable&quot;</span>&gt;resultado&lt;/span&gt; &lt;span class=<span class=<span class="hljs-string">"hljs-string"</span>>&quot;hljs-operator&quot;</span>&gt;=&lt;/span&gt; sb.toString();
O mesmo vale para construção condicional ao longo de múltiplos passos de lógica:
&lt;span class=<span class=<span class="hljs-string">"hljs-string"</span>>&quot;hljs-comment&quot;</span>&gt;<span class=<span class="hljs-string">"hljs-comment"</span>><span class="hljs-comment">// ✓ StringBuilder faz sentido aqui - construção incremental e condicional&lt;/span&gt;</span></span>
&lt;span class=<span class=<span class="hljs-string">"hljs-string"</span>>&quot;hljs-type&quot;</span>&gt;StringBuilder&lt;/span&gt; &lt;span class=<span class=<span class="hljs-string">"hljs-string"</span>>&quot;hljs-variable&quot;</span>&gt;query&lt;/span&gt; &lt;span class=<span class=<span class="hljs-string">"hljs-string"</span>>&quot;hljs-operator&quot;</span>&gt;=&lt;/span&gt; &lt;span class=<span class=<span class="hljs-string">"hljs-string"</span>>&quot;hljs-keyword&quot;</span>&gt;<span class=<span class="hljs-string">"hljs-keyword"</span>><span class="hljs-keyword">new</span></span>&lt;/span&gt; &lt;span class=<span class=<span class="hljs-string">"hljs-string"</span>>&quot;hljs-title class_&quot;</span>&gt;StringBuilder&lt;/span&gt;(&lt;span class=<span class=<span class="hljs-string">"hljs-string"</span>>&quot;hljs-string&quot;</span>&gt;&amp;quot;SELECT * FROM users WHERE <span class=<span class="hljs-string">"hljs-number"</span>><span class="hljs-number">1</span></span>=<span class=<span class="hljs-string">"hljs-number"</span>><span class="hljs-number">1</span></span>&amp;quot;&lt;/span&gt;);
&lt;span class=<span class=<span class="hljs-string">"hljs-string"</span>>&quot;hljs-keyword&quot;</span>&gt;<span class=<span class="hljs-string">"hljs-keyword"</span>><span class="hljs-keyword">if</span></span>&lt;/span&gt; (filtroNome != &lt;span class=<span class=<span class="hljs-string">"hljs-string"</span>>&quot;hljs-literal&quot;</span>&gt;<span class=<span class="hljs-string">"hljs-literal"</span>><span class="hljs-literal">null</span></span>&lt;/span&gt;) query.append(&lt;span class=<span class=<span class="hljs-string">"hljs-string"</span>>&quot;hljs-string&quot;</span>&gt;&amp;quot; <span class=<span class="hljs-string">"hljs-type"</span>>AND</span> <span class=<span class="hljs-string">"hljs-variable"</span>>nome</span> <span class=<span class="hljs-string">"hljs-operator"</span>>=</span> &amp;#x27;&amp;quot;&lt;/span&gt;).append(filtroNome).append(&lt;span class=<span class=<span class="hljs-string">"hljs-string"</span>>&quot;hljs-string&quot;</span>&gt;&amp;quot;&amp;#x27;&amp;quot;&lt;/span&gt;);
&lt;span class=<span class=<span class="hljs-string">"hljs-string"</span>>&quot;hljs-keyword&quot;</span>&gt;<span class=<span class="hljs-string">"hljs-keyword"</span>><span class="hljs-keyword">if</span></span>&lt;/span&gt; (filtroAtivo != &lt;span class=<span class=<span class="hljs-string">"hljs-string"</span>>&quot;hljs-literal&quot;</span>&gt;<span class=<span class="hljs-string">"hljs-literal"</span>><span class="hljs-literal">null</span></span>&lt;/span&gt;) query.append(&lt;span class=<span class=<span class="hljs-string">"hljs-string"</span>>&quot;hljs-string&quot;</span>&gt;&amp;quot; <span class=<span class="hljs-string">"hljs-type"</span>>AND</span> <span class=<span class="hljs-string">"hljs-variable"</span>>ativo</span> <span class=<span class="hljs-string">"hljs-operator"</span>>=</span> &amp;quot;&lt;/span&gt;).append(filtroAtivo);
&lt;span class=<span class=<span class="hljs-string">"hljs-string"</span>>&quot;hljs-keyword&quot;</span>&gt;<span class=<span class="hljs-string">"hljs-keyword"</span>><span class="hljs-keyword">if</span></span>&lt;/span&gt; (limite &amp;gt; &lt;span class=<span class=<span class="hljs-string">"hljs-string"</span>>&quot;hljs-number&quot;</span>&gt;<span class=<span class="hljs-string">"hljs-number"</span>><span class="hljs-number">0</span></span>&lt;/span&gt;) query.append(&lt;span class=<span class=<span class="hljs-string">"hljs-string"</span>>&quot;hljs-string&quot;</span>&gt;&amp;quot; LIMIT &amp;quot;&lt;/span&gt;).append(limite);
&lt;span class=<span class=<span class="hljs-string">"hljs-string"</span>>&quot;hljs-keyword&quot;</span>&gt;<span class=<span class="hljs-string">"hljs-keyword"</span>><span class="hljs-keyword">return</span></span>&lt;/span&gt; query.toString();
Onde o + Vence: Concatenação Simples
Para concatenações diretas em uma única instrução, o compilador e a JVM já fazem o trabalho por você - e fazem bem.
No JDK 8, o compilador transformava internamente "A" + "B" + "C" em uma implementação de StringBuilder no bytecode. A partir do JDK 9, o JEP 280 introduziu a Indify String Concatenation: a instrução invokedynamic delega a operação à JVM de forma dinâmica, permitindo otimizações que nem aparecem no bytecode e que evoluem com as versões do Java sem recompilação.
Na prática, benchmarks em máquinas modernas mostram que a concatenação via + em casos simples pode ser mais rápida do que o uso explícito de StringBuilder:
&lt;span class=<span class=<span class="hljs-string">"hljs-string"</span>>&quot;hljs-comment&quot;</span>&gt;<span class=<span class="hljs-string">"hljs-comment"</span>><span class="hljs-comment">// ✓ Deixa o compilador/JVM otimizar - é a escolha certa aqui&lt;/span&gt;</span></span>
&lt;span class=<span class=<span class="hljs-string">"hljs-string"</span>>&quot;hljs-type&quot;</span>&gt;String&lt;/span&gt; &lt;span class=<span class=<span class="hljs-string">"hljs-string"</span>>&quot;hljs-variable&quot;</span>&gt;mensagem&lt;/span&gt; &lt;span class=<span class=<span class="hljs-string">"hljs-string"</span>>&quot;hljs-operator&quot;</span>&gt;=&lt;/span&gt; &lt;span class=<span class=<span class="hljs-string">"hljs-string"</span>>&quot;hljs-string&quot;</span>&gt;&amp;quot;Olá, &amp;quot;&lt;/span&gt; + nome + &lt;span class=<span class=<span class="hljs-string">"hljs-string"</span>>&quot;hljs-string&quot;</span>&gt;&amp;quot;! Bem-vindo ao &amp;quot;&lt;/span&gt; + plataforma + &lt;span class=<span class=<span class="hljs-string">"hljs-string"</span>>&quot;hljs-string&quot;</span>&gt;&amp;quot;.&amp;quot;&lt;/span&gt;;
&lt;span class=<span class=<span class="hljs-string">"hljs-string"</span>>&quot;hljs-comment&quot;</span>&gt;<span class=<span class="hljs-string">"hljs-comment"</span>><span class="hljs-comment">// ✗ Desnecessariamente verbose - sem ganho de performance&lt;/span&gt;</span></span>
&lt;span class=<span class=<span class="hljs-string">"hljs-string"</span>>&quot;hljs-type&quot;</span>&gt;String&lt;/span&gt; &lt;span class=<span class=<span class="hljs-string">"hljs-string"</span>>&quot;hljs-variable&quot;</span>&gt;mensagem&lt;/span&gt; &lt;span class=<span class=<span class="hljs-string">"hljs-string"</span>>&quot;hljs-operator&quot;</span>&gt;=&lt;/span&gt; &lt;span class=<span class=<span class="hljs-string">"hljs-string"</span>>&quot;hljs-keyword&quot;</span>&gt;<span class=<span class="hljs-string">"hljs-keyword"</span>><span class="hljs-keyword">new</span></span>&lt;/span&gt; &lt;span class=<span class=<span class="hljs-string">"hljs-string"</span>>&quot;hljs-title class_&quot;</span>&gt;StringBuilder&lt;/span&gt;()
.append(&lt;span class=<span class=<span class="hljs-string">"hljs-string"</span>>&quot;hljs-string&quot;</span>&gt;&amp;quot;Olá, &amp;quot;&lt;/span&gt;).append(nome)
.append(&lt;span class=<span class=<span class="hljs-string">"hljs-string"</span>>&quot;hljs-string&quot;</span>&gt;&amp;quot;! Bem-vindo ao &amp;quot;&lt;/span&gt;).append(plataforma).append(&lt;span class=<span class=<span class="hljs-string">"hljs-string"</span>>&quot;hljs-string&quot;</span>&gt;&amp;quot;.&amp;quot;&lt;/span&gt;)
.toString();
Alternativas Modernas para Legibilidade
Quando o objetivo é legibilidade na construção de texto dinâmico - XML, JSON, SQL, templates - existem alternativas mais expressivas que o StringBuilder:
Text Blocks (JDK 15+) para strings multilinhas fixas:
&lt;span class=<span class=<span class="hljs-string">"hljs-string"</span>>&quot;hljs-type&quot;</span>&gt;String&lt;/span&gt; &lt;span class=<span class=<span class="hljs-string">"hljs-string"</span>>&quot;hljs-variable&quot;</span>&gt;json&lt;/span&gt; &lt;span class=<span class=<span class="hljs-string">"hljs-string"</span>>&quot;hljs-operator&quot;</span>&gt;=&lt;/span&gt; &lt;span class=<span class=<span class="hljs-string">"hljs-string"</span>>&quot;hljs-string&quot;</span>&gt;&amp;quot;&amp;quot;&amp;quot;
{
&amp;quot;nome&amp;quot;: &amp;quot;%s&amp;quot;,
&amp;quot;ativo&amp;quot;: %b
}
&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;.formatted(nome, ativo);
String.formatted() como alternativa legível ao String.format() - mesmo resultado, sintaxe mais fluida:
&lt;span class=<span class=<span class="hljs-string">"hljs-string"</span>>&quot;hljs-comment&quot;</span>&gt;<span class=<span class="hljs-string">"hljs-comment"</span>><span class="hljs-comment">// String.format() - clássico&lt;/span&gt;</span></span>
&lt;span class=<span class=<span class="hljs-string">"hljs-string"</span>>&quot;hljs-type&quot;</span>&gt;String&lt;/span&gt; &lt;span class=<span class=<span class="hljs-string">"hljs-string"</span>>&quot;hljs-variable&quot;</span>&gt;msg&lt;/span&gt; &lt;span class=<span class=<span class="hljs-string">"hljs-string"</span>>&quot;hljs-operator&quot;</span>&gt;=&lt;/span&gt; String.format(&lt;span class=<span class=<span class="hljs-string">"hljs-string"</span>>&quot;hljs-string&quot;</span>&gt;&amp;quot;Usuário %s criado em %s&amp;quot;&lt;/span&gt;, nome, data);
&lt;span class=<span class=<span class="hljs-string">"hljs-string"</span>>&quot;hljs-comment&quot;</span>&gt;<span class=<span class="hljs-string">"hljs-comment"</span>><span class="hljs-comment">// String.formatted() - mais idiomático desde o Java 15&lt;/span&gt;</span></span>
&lt;span class=<span class=<span class="hljs-string">"hljs-string"</span>>&quot;hljs-type&quot;</span>&gt;String&lt;/span&gt; &lt;span class=<span class=<span class="hljs-string">"hljs-string"</span>>&quot;hljs-variable&quot;</span>&gt;msg&lt;/span&gt; &lt;span class=<span class=<span class="hljs-string">"hljs-string"</span>>&quot;hljs-operator&quot;</span>&gt;=&lt;/span&gt; &lt;span class=<span class=<span class="hljs-string">"hljs-string"</span>>&quot;hljs-string&quot;</span>&gt;&amp;quot;Usuário %s criado em %s&amp;quot;&lt;/span&gt;.formatted(nome, data);
⚠️ Evite
String.format()eString.formatted()dentro de loops de alta frequência. Ambos são consideravelmente mais lentos queStringBuilderou concatenação simples por envolverem parsing de formato a cada chamada.
Guia de Decisão Rápido
| Cenário | Use |
|---|---|
| Concatenar poucas variáveis em uma linha | + ou String.formatted() |
| Construir String dentro de loop | StringBuilder |
| Construção incremental com condicionais | StringBuilder |
| Template multiline com valores fixos | Text Block |
| Template multiline com interpolação | Text Block + .formatted() |
| Alta frequência em loop crítico | StringBuilder (evite format) |
Revisitando o Código Original
Com tudo isso em mente, o erro inicial fica mais claro:
&lt;span class=<span class=<span class="hljs-string">"hljs-string"</span>>&quot;hljs-comment&quot;</span>&gt;<span class=<span class="hljs-string">"hljs-comment"</span>><span class="hljs-comment">// ✗ Versão com o erro&lt;/span&gt;</span></span>
&lt;span class=<span class=<span class="hljs-string">"hljs-string"</span>>&quot;hljs-keyword&quot;</span>&gt;<span class=<span class="hljs-string">"hljs-keyword"</span>><span class="hljs-keyword">public</span></span>&lt;/span&gt; &lt;span class=<span class=<span class="hljs-string">"hljs-string"</span>>&quot;hljs-keyword&quot;</span>&gt;<span class=<span class="hljs-string">"hljs-keyword"</span>><span class="hljs-keyword">static</span></span>&lt;/span&gt; String &lt;span class=<span class=<span class="hljs-string">"hljs-string"</span>>&quot;hljs-title function_&quot;</span>&gt;stopIt&lt;/span&gt;&lt;span class=<span class=<span class="hljs-string">"hljs-string"</span>>&quot;hljs-params&quot;</span>&gt;(String str)&lt;/span&gt; {
str.replace(str, &lt;span class=<span class=<span class="hljs-string">"hljs-string"</span>>&quot;hljs-string&quot;</span>&gt;&amp;quot;stop&amp;quot;&lt;/span&gt;); &lt;span class=<span class=<span class="hljs-string">"hljs-string"</span>>&quot;hljs-comment&quot;</span>&gt;<span class=<span class="hljs-string">"hljs-comment"</span>><span class="hljs-comment">// cria &amp;quot;stop&amp;quot; na heap, referência perdida&lt;/span&gt;</span></span>
&lt;span class=<span class=<span class="hljs-string">"hljs-string"</span>>&quot;hljs-keyword&quot;</span>&gt;<span class=<span class="hljs-string">"hljs-keyword"</span>><span class="hljs-keyword">return</span></span>&lt;/span&gt; str; &lt;span class=<span class=<span class="hljs-string">"hljs-string"</span>>&quot;hljs-comment&quot;</span>&gt;<span class=<span class="hljs-string">"hljs-comment"</span>><span class="hljs-comment">// str ainda aponta para &amp;quot;start&amp;quot; no SCP&lt;/span&gt;</span></span>
}
&lt;span class=<span class=<span class="hljs-string">"hljs-string"</span>>&quot;hljs-comment&quot;</span>&gt;<span class=<span class="hljs-string">"hljs-comment"</span>><span class="hljs-comment">// ✓ Versão correta&lt;/span&gt;</span></span>
&lt;span class=<span class=<span class="hljs-string">"hljs-string"</span>>&quot;hljs-keyword&quot;</span>&gt;<span class=<span class="hljs-string">"hljs-keyword"</span>><span class="hljs-keyword">public</span></span>&lt;/span&gt; &lt;span class=<span class=<span class="hljs-string">"hljs-string"</span>>&quot;hljs-keyword&quot;</span>&gt;<span class=<span class="hljs-string">"hljs-keyword"</span>><span class="hljs-keyword">static</span></span>&lt;/span&gt; String &lt;span class=<span class=<span class="hljs-string">"hljs-string"</span>>&quot;hljs-title function_&quot;</span>&gt;stopIt&lt;/span&gt;&lt;span class=<span class=<span class="hljs-string">"hljs-string"</span>>&quot;hljs-params&quot;</span>&gt;(String str)&lt;/span&gt; {
&lt;span class=<span class=<span class="hljs-string">"hljs-string"</span>>&quot;hljs-keyword&quot;</span>&gt;<span class=<span class="hljs-string">"hljs-keyword"</span>><span class="hljs-keyword">return</span></span>&lt;/span&gt; str.replace(str, &lt;span class=<span class=<span class="hljs-string">"hljs-string"</span>>&quot;hljs-string&quot;</span>&gt;&amp;quot;stop&amp;quot;&lt;/span&gt;); &lt;span class=<span class=<span class="hljs-string">"hljs-string"</span>>&quot;hljs-comment&quot;</span>&gt;<span class=<span class="hljs-string">"hljs-comment"</span>><span class="hljs-comment">// captura e retorna a nova referência&lt;/span&gt;</span></span>
}
O erro não é conceitual - é de leitura. replace soa como uma operação in-place. Para quem lê rápido (e revisores de PR leem rápido), o olho passa e o cérebro valida. Saber disso é o que separa um bom code reviewer de um revisor médio.
Conclusão
Imutabilidade de Strings em Java não é um detalhe trivial de documentação. É uma decisão que impacta gerenciamento de memória, segurança em concorrência e performance do GC. O custo cognitivo existe - e conhecê-lo te torna mais atento tanto ao escrever quanto ao revisar código.
Na próxima vez que você ver um verbo como replace, trim ou toUpperCase em uma revisão, a pergunta certa é simples: o resultado foi capturado?
Este artigo faz parte da série Objects · Immutability · Switch · Pattern Matching do getcaramelo.dev.
Tem dúvidas ou quer aprofundar algum ponto? Me encontra no LinkedIn ou no GitHub.
Publicado por: Guilherme Gomes - 28/06/2026 15:10
Gostou do conteúdo? Me segue no @getcaramelo.dev
Caramelo.dev