Formatação de Strings em Java
Formate strings Java com String.format e System.out.printf usando especificadores como %s, %d, %f e %n.
A concatenação (+) funciona bem para strings curtas e simples. No momento em que você quiser fixar um número em um número específico de casas decimais, alinhar colunas com preenchimento, incorporar uma data com layout consistente ou, de outro modo, formatar valores em vez de simplesmente colá-los, você recorre à API no estilo printf que o Java tomou emprestado do C.
Existem três pontos de entrada intimamente relacionados e você verá todos eles em código real:
String.format(fmt, args...)— retorna umaStringformatada."...".formatted(args...)— forma de instância adicionada no Java 15. Resultado idêntico, mais amigável para encadeamento.System.out.printf(fmt, args...)— imprime diretamente em umPrintStream(ou qualquerFormatter).
Todos os três compartilham a mesma sintaxe de especificadores de formato. Aprenda uma vez.
Especificadores de formato
Um especificador tem a forma %[flags][width][.precision]conversion. A letra de conversão é o único elemento obrigatório. O restante ajusta largura, alinhamento, preenchimento e precisão.
String s = String.format("%-10s | %5d | %8.2f", "apples", 42, 3.14159);
// "apples | 42 | 3.14"As conversões mais úteis:
| Conversão | Argumento | Significado |
|---|---|---|
%s | qualquer | toString() do valor |
%d | inteiro | inteiro decimal |
%f | ponto flutuante | decimal de ponto fixo |
%e | ponto flutuante | notação científica |
%g | ponto flutuante | o mais curto entre %e e %f |
%x, %o | inteiro | hexadecimal / octal |
%c | caractere | caractere único |
%b | qualquer | true/false (null → "false") |
%n | nenhum | separador de linha adequado à plataforma |
%% | nenhum | um % literal |
%t... | data/hora | toda uma família — %tF, %tT, etc. |
A largura preenche a saída com pelo menos N caracteres; a precisão significa "dígitos decimais" para floats e "máximo de caracteres" para strings.
Flags: alinhamento, sinal, preenchimento com zeros e agrupamento
Um conjunto de flags se encaixa entre % e a largura:
-— alinha à esquerda dentro da largura. O padrão é alinhar à direita.0— preenche com zeros até a largura (somente para conversões numéricas).+— sempre exibe sinal em números (+42,-7).(espaço) — exibe um espaço à esquerda para números positivos, como+, mas com um espaço em branco.,— agrupa dígitos usando o separador de milhares da localidade.(— envolve números negativos em parênteses, estilo contábil.
String.format("%08d", 42); // "00000042"
String.format("%,d", 1234567); // "1,234,567"
String.format("%+.2f", 3.14159); // "+3.14"
String.format("%-10s|", "hi"); // "hi |"Largura e precisão
A largura é a largura mínima do campo — se o valor formatado for mais largo, nada é truncado.
A precisão tem significados diferentes para conversões distintas:
%.3f— três dígitos após a vírgula decimal.%.10s— trunca a string para no máximo 10 caracteres.%.4e— quatro dígitos de precisão de mantissa.
Combinar largura e precisão é comum quando você quer que colunas se alinhem e números sejam arredondados:
String.format("%10.4f", Math.PI); // " 3.1416"
String.format("%-10.4s", "abcdef"); // "abcd "%n vs \n
%n emite o separador de linha adequado à plataforma: "\n" no Unix, "\r\n" no Windows. \n é sempre exatamente um byte. Para arquivos e saídas de protocolo onde a terminação de linha importa com precisão, prefira \n e escolha-a conscientemente. Para saída de console que deve parecer correta em qualquer SO em que a JVM esteja rodando, %n é a escolha mais segura.
Índices de argumento: reutilização e reordenação
Um especificador da forma %N$... se refere ao N-ésimo argumento (baseado em 1). Útil quando um valor aparece mais de uma vez em um modelo, ou quando a ordem natural de leitura difere da ordem dos argumentos:
String.format("%1$s, %1$s, %1$s!", "go"); // "go, go, go!"
String.format("%2$s before %1$s", "lunch", "tea"); // "tea before lunch"Esta é a ferramenta certa ao localizar modelos — idiomas diferentes colocam substantivos em posições diferentes, e um tradutor pode reorganizar os marcadores de posição sem tocar no local da chamada.
A localidade importa para números e datas
A formatação de números respeita a localidade padrão da JVM, a menos que você a substitua. Em en-US você obtém 3.14; em de-DE você obtém 3,14; em fr-FR os milhares são agrupados com um espaço não separável. Para saída voltada a humanos, isso geralmente é o que você quer. Para formatos de dados — JSON, CSV, arquivos de log, qualquer coisa legível por máquina — é um desastre aguardando uma implantação em Frankfurt.
Sempre passe uma localidade explicitamente para saída legível por máquina:
String json = String.format(Locale.ROOT, "{\"price\": %.2f}", 19.95);
// "{\"price\": 19.95}" — always, regardless of JVM localeLocale.ROOT significa "sem formatação específica de localidade" — ponto como decimal, sem agrupamento. Locale.US é a outra escolha comum para o mesmo propósito. O perigo está em não passar uma localidade e presumir.
Formatação de data/hora
%t é uma meta-conversão: a letra que a segue escolhe o campo. O mesmo argumento Date, Calendar, Long (millis) ou java.time.temporal.TemporalAccessor pode ser formatado de várias maneiras:
LocalDateTime now = LocalDateTime.of(2026, 5, 29, 14, 30, 15);
String.format("%tF", now); // "2026-05-29" — ISO date
String.format("%tT", now); // "14:30:15" — 24-hour time
String.format("%tA", now); // "Friday" — locale-dependent
String.format("%1$tF %1$tT", now); // "2026-05-29 14:30:15"Para qualquer coisa além de formatação rápida, a API java.time.format.DateTimeFormatter é mais flexível e consciente da localidade — mas %tF e seus afins continuam sendo úteis em linhas de log.
Armadilhas comuns
- Conversão errada para o tipo.
String.format("%d", 3.14)lançaIllegalFormatConversionExceptionem tempo de execução —%dquer inteiro,%fquer ponto flutuante. O compilador não consegue verificar isso. - Argumentos ausentes. Esquecer um argumento de marcador lança
MissingFormatArgumentException. - Decimal dependente de localidade em saída de máquina. Abordado acima.
%semnull. Produz"null". Aceitável para logs, embaraçoso em saída voltada ao usuário.- Usar
+para números formatados."Price: " + 19.95dá"Price: 19.95", mas"Price: " + 0.1 + 0.2dá"Price: 0.10.2", não"Price: 0.3"— concatenação, não adição.
Um exemplo prático
Um pequeno formatador de resumo de pedidos que exercita largura, precisão, alinhamento, agrupamento, localidade e a conversão de data %t. A saída é um relatório organizado de duas colunas — o tipo de coisa que você construiria manualmente com auxiliares padLeft.
Dois pontos a observar: o %<tT na linha "Placed" reutiliza o argumento anterior (< é o flag de referência anterior), evitando um segundo placedAt redundante. E a linha JSON usa Locale.ROOT — o mesmo código em uma JVM alemã ainda emite 1339.43, não 1339,43, que é exatamente o que um analisador JSON espera.
O que vem a seguir
Construir strings é metade do trabalho. Compará-las — igualdade, ordenação, dobramento de maiúsculas/minúsculas, a diferença entre == e equals — é a outra metade e uma fonte frequente de bugs sutis. Continue para Comparação de Strings em Java.