Validar, gerar e formatar CPF em Java
Classe utilitária em Java 17 para validar, gerar e formatar CPF com o módulo 11, testes com JUnit 5 @ParameterizedTest e chamada à API com HttpClient.
Em Java, o lugar natural para validar CPF é uma classe utilitária final, sem estado, que pode ser chamada de um controller Spring, de um validador do Bean Validation ou de um teste. O código abaixo usa apenas o JDK e compila em Java 17. A lógica é idêntica à das ferramentas do site; o guia do algoritmo do módulo 11 mostra a conta completa.
Validar CPF em Java
import java.security.SecureRandom;
import java.util.regex.Pattern;
public final class Cpf {
private static final Pattern SEPARADORES = Pattern.compile("[.\\-\\s]");
private static final Pattern ONZE_DIGITOS = Pattern.compile("[0-9]{11}");
private static final SecureRandom RANDOM = new SecureRandom();
private Cpf() {}
static int calcularDigito(String digitos, int pesoInicial) {
int soma = 0;
for (int i = 0; i < digitos.length(); i++) {
soma += (digitos.charAt(i) - '0') * (pesoInicial - i);
}
int resto = soma % 11;
return resto < 2 ? 0 : 11 - resto;
}
public static boolean validar(String valor) {
if (valor == null) return false;
String cpf = SEPARADORES.matcher(valor).replaceAll("");
if (!ONZE_DIGITOS.matcher(cpf).matches()) return false;
if (cpf.chars().allMatch(c -> c == cpf.charAt(0))) return false;
int dv1 = calcularDigito(cpf.substring(0, 9), 10);
int dv2 = calcularDigito(cpf.substring(0, 10), 11);
return cpf.charAt(9) - '0' == dv1 && cpf.charAt(10) - '0' == dv2;
}
}Pontos que merecem atenção:
Patternpré-compilado.String.replaceAlleString.matchesrecompilam a expressão a cada chamada. Emvalidar, que roda a cada requisição, vale manter os padrões em constantes.[0-9]{11}commatches()exige que a string inteira seja de dígitos ASCII. Letras ou símbolos que sobraram da limpeza derrubam a validação.charAt(i) - '0'converte o caractere em número sem criar objetos, o que é mais barato queInteger.parseIntem um laço.- A checagem de repetidos vem antes da conta. 111.111.111-11 passa no módulo 11, mas sequências assim não são consideradas válidas.
Com 529.982.247-25, a primeira soma é 295 (resto 9, dígito 2) e a segunda é 347 (resto 6, dígito 5), e validar devolve true. Com 529.982.247-26, o último caractere vale 6, o dígito calculado é 5, e o resultado é false.
Para usar com Bean Validation, crie uma anotação @CpfValido com um ConstraintValidator<CpfValido, String> cujo isValid chama Cpf.validar. Lembre que null aqui é inválido; se o campo for opcional, trate null como válido no validador e use @NotNull quando ele for obrigatório.
Gerar CPF válido em Java
Os métodos a seguir ficam dentro da mesma classe Cpf. SecureRandom evita sequências previsíveis.
public static String gerar(boolean formatado) {
String base;
do {
StringBuilder sb = new StringBuilder(9);
for (int i = 0; i < 9; i++) sb.append(RANDOM.nextInt(10));
base = sb.toString();
} while (base.matches("(\\d)\\1{8}"));
int dv1 = calcularDigito(base, 10);
int dv2 = calcularDigito(base + dv1, 11);
String cpf = base + dv1 + dv2;
return formatado ? formatar(cpf) : cpf;
}O número gerado é válido pelo algoritmo e pode coincidir com um CPF real por acaso; use-o em testes e massas de dados fictícias. O guia de massa de teste e LGPD traz boas práticas para isso.
Formatar e limpar CPF em Java
public static String limpar(String valor) {
return valor == null ? "" : valor.replaceAll("[^0-9]", "");
}
public static String formatar(String valor) {
String d = limpar(valor);
if (d.length() != 11) {
throw new IllegalArgumentException("CPF precisa de 11 dígitos, recebeu " + d.length());
}
return d.substring(0, 3) + "." + d.substring(3, 6) + "." + d.substring(6, 9) + "-" + d.substring(9);
}Cpf.formatar("52998224725") devolve "529.982.247-25" e Cpf.limpar("529.982.247-25") devolve "52998224725". Persista a versão limpa em uma coluna CHAR(11), nunca em BIGINT, que descarta o zero à esquerda; o guia sobre validar CPF no banco de dados cobre o lado do PostgreSQL e do MySQL.
Testes
Com JUnit 5, @ParameterizedTest e @CsvSource rodam o mesmo método para cada linha. O JUnit converte "true" e "false" em boolean automaticamente e ignora os espaços depois da vírgula.
import static org.junit.jupiter.api.Assertions.assertEquals;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.CsvSource;
class CpfTest {
@ParameterizedTest(name = "{0} -> {1}")
@CsvSource({
"529.982.247-25, true",
"111.111.111-11, false",
"529.982.247-26, false",
})
void validaOsTresCasos(String cpf, boolean esperado) {
assertEquals(esperado, Cpf.validar(cpf));
}
}O artefato junit-jupiter-params precisa estar no classpath de teste; no Spring Boot ele já vem com spring-boot-starter-test. Para ampliar a tabela, veja os erros comuns ao validar CPF.
Usar a API em vez de reimplementar
A API do cpf.dev.br valida e gera CPFs por HTTP, sem chave, com limite de 60 requisições por minuto por IP. O HttpClient do Java 11 em diante resolve sem dependências.
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.time.Duration;
public class ApiCpf {
public static void main(String[] args) throws Exception {
HttpClient http = HttpClient.newBuilder().connectTimeout(Duration.ofSeconds(5)).build();
HttpRequest req = HttpRequest.newBuilder(
URI.create("https://www.cpf.dev.br/api/v1/cpf/validar/529.982.247-25")).GET().build();
HttpResponse<String> resp = http.send(req, HttpResponse.BodyHandlers.ofString());
System.out.println(resp.body());
// {"valido":true,"cpf":"52998224725","formatado":"529.982.247-25","uf":["ES","RJ"]}
HttpRequest lote = HttpRequest.newBuilder(
URI.create("https://www.cpf.dev.br/api/v1/cpf/gerar?quantidade=5&uf=SP&formatado=true")).GET().build();
System.out.println(http.send(lote, HttpResponse.BodyHandlers.ofString()).body());
}
}Para transformar o JSON em objeto, use Jackson ou Gson com um record de quatro campos. Em validação de entrada, a classe local é mais rápida e não depende de rede. Para conferir um número à mão, use o validador de CPF.