Java Concatenação

Concatenar é juntar textos, um atrás do outro: "Olá, " + "Ana" = "Olá, Ana". Você vai fazer isso o tempo todo para montar mensagens. Parece simples, e é, com uma pegadinha famosa: quando números entram na conta, o + às vezes soma e às vezes junta. Nesta aula você vai entender exatamente quando acontece cada coisa.

Você vai aprender
  • Como juntar textos com + e com +=.
  • O método concat e quando ele é diferente do +.
  • O que acontece quando você junta texto com números, char e boolean, e por que "1" + 2 + 3 dá 123 mas 1 + 2 + "3" dá 33.
  • Por que concatenar dentro de um laço pode ficar lento.
  • Como juntar uma lista de textos com String.join.

A ideia: emendar pedaços

Pense em vagões de trem: cada pedaço de texto é um vagão, e o + é o engate que prende um no outro. O trem resultante é um texto novo, com todos os vagões em ordem. Os vagões originais não mudam (lembre que Strings são imutáveis): a concatenação sempre cria uma String nova.


O operador +

Entre dois textos, o + não soma: ele emenda. Você já usou isso em System.out.println:

public class Main {
    public static void main(String[] args) {
        String nome = "Ana";
        String sobrenome = "Souza";
        System.out.println("Olá, " + nome + "!");
        System.out.println(nome + sobrenome);
        System.out.println(nome + " " + sobrenome);
        String completo = nome + " " + sobrenome;
        System.out.println(completo.length());
    }
}
$ java Main.java
Olá, Ana!
AnaSouza      « o + não coloca espaço sozinho
Ana Souza     « o espaço foi um pedaço a mais: " "
9
"Olá, " + nome + "!"
Três pedaços: um literal, uma variável e outro literal. Repare no espaço dentro das aspas depois da vírgula: é ele que separa "Olá," de "Ana".
nome + sobrenome
Emenda sem nada no meio: AnaSouza. O Java nunca adivinha espaços.
String completo = …
O resultado é uma String como qualquer outra: pode ser guardado, medido, comparado.
Onde vai o espaço?

O erro mais comum de quem começa é a frase sair grudada: Olá,Ana ou Ana tem30anos. Os espaços precisam estar dentro das aspas: "Ana tem " + idade + " anos". Espaços fora das aspas (nome + sobrenome) são só formatação do código e não aparecem na saída.

Quebrar uma frase comprida no código

Quando o texto é longo demais para uma linha do editor, quebre-o em pedaços ligados por +. O javac junta tudo:

public class Main {
    public static void main(String[] args) {
        String mensagem = "Esta é uma frase bem comprida que não cabe "
                + "numa linha só do editor, então foi quebrada "
                + "em três pedaços.";
        System.out.println(mensagem);
    }
}
$ java Main.java
Esta é uma frase bem comprida que não cabe numa linha só do editor, então foi quebrada em três pedaços.

Um literal entre aspas não pode ser quebrado no meio com Enter; por isso cada linha fecha as aspas e a próxima começa com +. Para textos de várias linhas de verdade, existe uma forma melhor: os Text Blocks.


Acrescentar no fim: +=

Você viu em Operadores de Atribuição que x += 5 é um atalho para x = x + 5. Com Strings funciona igual: frase += "texto" quer dizer frase = frase + "texto", ou seja, "cria uma String nova com o texto antigo mais o pedaço, e guarda de volta em frase".

public class Main {
    public static void main(String[] args) {
        String frase = "Eu gosto de";
        frase = frase + " maçã";
        frase += ", banana";
        frase += " e uva";
        System.out.println(frase);
        int idade = 30;
        String texto = "Idade: " + idade;
        String soNumero = "" + idade;
        String comValueOf = String.valueOf(idade);
        System.out.println(texto);
        System.out.println(soNumero.length() + " " + comValueOf.length());
    }
}
$ java Main.java
Eu gosto de maçã, banana e uva
Idade: 30
2 2      « "30" virou um texto de 2 caracteres, pelos dois caminhos

As duas últimas variáveis mostram dois jeitos de transformar um número em texto: "" + idade (juntar com a String vazia, um truque muito usado) e String.valueOf(idade) ("valor de", a forma mais explícita). Os dois dão "30".


O método concat

Toda String tem o método concat, que faz a mesma emenda: a.concat(b) é parecido com a + b.

public class Main {
    public static void main(String[] args) {
        String nome = "Ana";
        String completo = nome.concat(" Souza");
        System.out.println(completo);
        System.out.println("Bom".concat(" ").concat("dia"));
        String nada = null;
        System.out.println("Valor: " + nada);
        System.out.println("Valor: ".concat(nada));
    }
}
$ java Main.java
Ana Souza
Bom dia
Valor: null
Exception in thread "main" java.lang.NullPointerException: Cannot invoke "String.isEmpty()" because "str" is null
	at java.base/java.lang.String.concat(String.java:2961)
	at Main.main(Main.java:9)

As duas primeiras linhas funcionam como o +. A diferença aparece com null: o + escreve a palavra null e segue em frente; o concat derruba o programa. A mensagem fala de "str" porque esse é o nome do parâmetro dentro do método concat, na biblioteca do Java (a linha at java.base/java.lang.String.concat); a sua linha é a de baixo, Main.java:9.

+concat
Aceita números, char, boolean…Sim, transforma em textoNão: só String (erro de compilação)
Com nullEscreve nullNullPointerException
UsoO dia a diaRaro

Na prática, quase todo mundo usa o +. É bom conhecer o concat para entender código de outras pessoas.


Texto + número: soma ou emenda?

Aqui está a parte que mais confunde. O + tem dois trabalhos em Java:

  • Se os dois lados são números, ele soma: 2 + 3 é 5.
  • Se pelo menos um lado é uma String, ele emenda, transformando o outro lado em texto: "2" + 3 é "23".

E a regra que decide tudo: o Java avalia o + da esquerda para a direita, um par de cada vez (veja Precedência). A cada passo, olha os dois lados daquele + e decide se soma ou emenda. Veja:

public class Main {
    public static void main(String[] args) {
        System.out.println("1" + 2 + 3);
        System.out.println(1 + 2 + "3");
        System.out.println(1 + 2 + "3" + 4 + 5);
        System.out.println("Soma: " + 2 + 3);
        System.out.println("Soma: " + (2 + 3));
        System.out.println("Dobro: " + 2 * 3);
        System.out.println('A' + 'B');
        System.out.println("" + 'A' + 'B');
        System.out.println('A' + "B");
        System.out.println("Peso: " + 1.5 + " kg");
        System.out.println("Aprovada? " + true);
    }
}
$ java Main.java
123
33
3345
Soma: 23
Soma: 5
Dobro: 6
131
AB
AB
Peso: 1.5 kg
Aprovada? true

Vamos passo a passo, como o Java faz:

ExpressãoPasso 1Passo 2Resultado
"1" + 2 + 3"1" + 2 → texto + número → "12""12" + 3 → "123"123
1 + 2 + "3"1 + 2 → dois números → soma 33 + "3" → "33"33
1 + 2 + "3" + 4 + 51 + 2 → 33 + "3" → "33", depois "334", "3345"3345
"Soma: " + 2 + 3"Soma: " + 2 → "Soma: 2""Soma: 2" + 3 → "Soma: 23"Soma: 23 (errado!)
"Soma: " + (2 + 3)Parênteses primeiro: 2 + 3 → 5"Soma: " + 5Soma: 5
"Dobro: " + 2 * 3* vem antes de +: 2 * 3 → 6"Dobro: " + 6Dobro: 6

Moral: quando quiser fazer uma conta dentro de uma frase, coloque a conta entre parênteses. *, / e % já vêm antes do +, mas os parênteses deixam claro para quem lê.

E o char?

Lembra que um char é, por dentro, um número (veja char e boolean)? Por isso 'A' + 'B' não dá "AB": são dois "números" (65 e 66), e o + soma: 131. Basta uma String na conta para virar emenda: "" + 'A' + 'B' e 'A' + "B" dão AB. Atenção às aspas: 'A' (simples) é char; "B" (duplas) é String.

Outros tipos

Qualquer valor pode ser emendado num texto: double vira 1.5 (com ponto, sempre; a vírgula do português só aparece com format e printf), boolean vira true/false, null vira null.

Missão: o programa deveria mostrar Soma: 5, mas mostra Soma: 23. Rode, veja o problema e corrija com parênteses.

public class Main {
    public static void main(String[] args) {
        int a = 2;
        int b = 3;
        System.out.println("Soma: " + a + b);
    }
}
public class Main {
    public static void main(String[] args) {
        int a = 2;
        int b = 3;
        System.out.println("Soma: " + (a + b));
    }
}

Brinque no JShell: antes de apertar Enter, tente adivinhar o resultado de "a" + 1 + 1, 1 + 1 + "a", 'a' + 1 e "" + 'a' + 1. Depois confira.


Concatenar dentro de um laço

Um laço repete um trecho de código várias vezes; você vai estudá-los em while e for. Aqui, o for repete a linha linha += i + " "; cinco vezes, com i valendo 1, 2, 3, 4 e 5:

public class Main {
    public static void main(String[] args) {
        String linha = "";
        for (int i = 1; i <= 5; i++) {
            linha += i + " ";
        }
        System.out.println(linha);
    }
}
$ java Main.java
1 2 3 4 5 

Funciona, e para poucas repetições está ótimo. O problema aparece quando são milhares. Como Strings são imutáveis, cada += cria uma String nova e copia tudo o que já existia. Na repetição número 10.000, o Java copia 10.000 caracteres só para acrescentar um. É como reescrever o caderno inteiro à mão cada vez que você quer acrescentar uma palavra no fim.

A solução é o StringBuilder ("construtor de Strings"), um "rascunho" que pode ser alterado, e que você vai estudar em StringBuilder. Só para ver a diferença, este programa junta 20.000 letras x dos dois jeitos e mede o tempo com System.nanoTime() (o relógio do computador, em bilionésimos de segundo):

public class Main {
    public static void main(String[] args) {
        int n = 20000;

        long inicio = System.nanoTime();
        String texto = "";
        for (int i = 0; i < n; i++) {
            texto += "x";
        }
        long tempoMais = (System.nanoTime() - inicio) / 1_000_000;

        inicio = System.nanoTime();
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < n; i++) {
            sb.append("x");
        }
        String texto2 = sb.toString();
        long tempoSb = (System.nanoTime() - inicio) / 1_000_000;

        System.out.println(texto.length() + " caracteres com +=: " + tempoMais + " ms");
        System.out.println(texto2.length() + " caracteres com StringBuilder: " + tempoSb + " ms");
    }
}
$ java Main.java
20000 caracteres com +=: 113 ms
20000 caracteres com StringBuilder: 2 ms

O resultado é o mesmo (20.000 caracteres), mas o += levou dezenas de vezes mais tempo. Os números mudam a cada execução e de computador para computador; a diferença, não. E ela cresce quanto maior o n, porque cada repetição do += copia um texto cada vez mais comprido.

Não precisa ter medo do +

Fora de laços, use + à vontade: numa linha como "Olá, " + nome + "!", o próprio Java já monta o texto de forma eficiente. A regra é só esta: dentro de um laço que repete muitas vezes, use StringBuilder.


Juntar uma lista: String.join

Quando você tem vários textos e quer juntá-los com um separador no meio (vírgula, espaço, barra), o String.join é mais limpo que uma fila de +. Ele cuida de colocar o separador só entre os itens, nunca sobrando no fim.

public class Main {
    public static void main(String[] args) {
        System.out.println(String.join(", ", "maçã", "banana", "uva"));
        String nome = "Ana";
        int idade = 30;
        System.out.println(String.join(" ", "Nome:", nome, "| Idade:", String.valueOf(idade)));
        System.out.println("Nome: %s | Idade: %d".formatted(nome, idade));
    }
}
$ java Main.java
maçã, banana, uva
Nome: Ana | Idade: 30
Nome: Ana | Idade: 30
String.join(", ", …)
O primeiro argumento é o separador; os outros são os itens. Todos têm de ser Strings: por isso o número idade passou por String.valueOf.
"Nome: %s | Idade: %d".formatted(nome, idade)
Uma terceira forma: um modelo com lugares marcados (%s para texto, %d para número inteiro) que são preenchidos em ordem. Para frases com muitos valores, costuma ser a mais legível. Ela tem aula própria: Formatar Texto.

Qual usar?

SituaçãoUseExemplo
Montar uma frase com poucas partes+"Olá, " + nome + "!"
Acrescentar um pedaço a uma variável, poucas vezes+=frase += " e uva";
Conta dentro da frase+ com parênteses"Total: " + (a + b)
Vários itens com separadorString.joinString.join(", ", itens)
Frase com muitos valores e formataçãoformatted / String.format"%s tem %d anos".formatted(nome, idade)
Juntar dentro de um laço longoStringBuilderveja StringBuilder
Número para textoString.valueOf ou "" + nString.valueOf(30)

Quando dá errado

A conta saiu grudada: Soma: 23

O que aconteceu: o + trabalha da esquerda para a direita; depois de encontrar o texto, todos os + seguintes emendam.

Como resolver: parênteses em volta da conta: "Soma: " + (a + b).

error: ')' or ',' expected (esqueceu o +)

Este código tem um erro de propósito.

public class Main {
    public static void main(String[] args) {
        String nome = "Ana";
        System.out.println("Olá, " nome);
    }
}
$ javac Main.java
Main.java:4: error: ')' or ',' expected
        System.out.println("Olá, " nome);
                                  ^
Main.java:4: error: not a statement
        System.out.println("Olá, " nome);
                                   ^
Main.java:4: error: ';' expected
        System.out.println("Olá, " nome);
                                       ^
3 errors

O que aconteceu: depois do texto "Olá, ", o javac esperava o fim dos parênteses ou uma vírgula, e encontrou nome. Os outros dois erros são consequência do primeiro.

Como resolver: coloque o + que falta: "Olá, " + nome.

error: incompatible types: int cannot be converted to String

Este código tem um erro de propósito.

public class Main {
    public static void main(String[] args) {
        String idade = 30;
        System.out.println(idade);
    }
}
$ javac Main.java
Main.java:3: error: incompatible types: int cannot be converted to String
        String idade = 30;
                       ^
1 error

O que aconteceu: o + transforma números em texto, mas a atribuição direta, não: 30 é um int e a variável é String.

Como resolver: String idade = "30";, String.valueOf(30) ou, se é um número, declare como int. O mesmo erro aparece com "Idade: ".concat(idade) quando idade é int, porque o concat só aceita String; nesse caso o javac ainda acrescenta a linha Note: Some messages have been simplified; recompile with -Xdiags:verbose to get full output ("algumas mensagens foram simplificadas").

error: bad operand types for binary operator '-'

Este código tem um erro de propósito.

public class Main {
    public static void main(String[] args) {
        System.out.println("Total: " + 5 - 2);
    }
}
$ javac Main.java
Main.java:3: error: bad operand types for binary operator '-'
        System.out.println("Total: " + 5 - 2);
                                         ^
  first type:  String
  second type: int
1 error

O que aconteceu: + e - têm a mesma prioridade e são feitos da esquerda para a direita. Primeiro "Total: " + 5 virou o texto "Total: 5"; depois o Java tentou "Total: 5" - 2, e não existe "texto menos número" ("tipos de operandos inválidos para o operador '-'": o primeiro é String, o segundo é int).

Como resolver: "Total: " + (5 - 2).

A frase saiu com null no meio

O que aconteceu: uma das variáveis valia null, e o + escreve a palavra null sem reclamar.

Como resolver: descubra por que a variável não recebeu valor (veja Strings, seção "String vazia × null").


Referência

FormaO que fazExemplo → resultado
a + bEmenda (se um dos lados é String)"Ana" + " Souza" → "Ana Souza"
s += xs = s + xs = "a"; s += "b"; → "ab"
a.concat(b)Emenda; só aceita String; quebra com null"Bom".concat(" dia") → "Bom dia"
"" + nNúmero para texto"" + 30 → "30"
String.valueOf(x)Qualquer valor para textoString.valueOf(1.5) → "1.5"
String.join(sep, …)Junta com separador entre os itensString.join("-", "a", "b") → "a-b"
"…%s…".formatted(…)Preenche um modelo"Oi, %s".formatted("Ana") → "Oi, Ana"

Exercícios

Missão final: usando as variáveis (sem escrever "Ana", "Souza", 30 ou 35 dentro de aspas), mostre exatamente:
Ana Souza tem 30 anos e daqui a 5 anos terá 35.
Cuidado com os espaços e com a conta do 35.

public class Main {
    public static void main(String[] args) {
        String nome = "Ana";
        String sobrenome = "Souza";
        int idade = 30;
        // escreva aqui
    }
}
public class Main {
    public static void main(String[] args) {
        String nome = "Ana";
        String sobrenome = "Souza";
        int idade = 30;
        System.out.println(nome + " " + sobrenome + " tem " + idade + " anos e daqui a 5 anos terá " + (idade + 5) + ".");
    }
}

1. O resultado

O que System.out.println("5" + 5 + 5); mostra?

2. E agora?

O que System.out.println(5 + 5 + "5"); mostra?

3. Juntar com vírgulas

Complete para mostrar pão, leite, café:

System.out.println(String.(", ", "pão", "leite", "café"));

4. O que System.out.println('x' + 'y'); mostra?

5. Você precisa juntar 50.000 linhas de um relatório dentro de um laço. O que usar?

6. Se String s = null;, o que System.out.println("Nome: " + s); faz?


Resumo

  • + entre textos emenda; os espaços têm de estar dentro das aspas.
  • += acrescenta no fim (cria uma String nova e guarda de volta).
  • concat faz o mesmo, mas só aceita String e quebra com null; o + é o usado no dia a dia.
  • O + trabalha da esquerda para a direita: dois números somam; com uma String no meio, emenda. "1" + 2 + 3 → 123; 1 + 2 + "3" → 33. Contas dentro de frases vão entre parênteses.
  • char + char soma os códigos; para juntar letras, comece com "".
  • Em laços longos, use StringBuilder; para listas com separador, String.join.