O que é um método e por que ele importa
Métodos são blocos de código nomeados que executam uma tarefa específica. Eles ajudam a organizar o programa, reduzir repetição e tornar o código mais legível e testável. Em Java, um método pertence a uma classe e pode ser chamado para executar uma ação (por exemplo, validar um texto) ou produzir um resultado (por exemplo, calcular um valor).
Estrutura básica de um método
Um método é definido por modificadores (opcionais), tipo de retorno, nome, lista de parâmetros e corpo:
modificadores tipoRetorno nomeDoMetodo(tipo1 p1, tipo2 p2) { ... }Exemplo simples de método que retorna um valor:
static int soma(int a, int b) { return a + b;}Assinatura do método: o que identifica um método
A assinatura de um método em Java é composta por: nomeDoMetodo + tipos dos parâmetros (ordem incluída). A assinatura não inclui o tipo de retorno.
Isso é importante para entender sobrecarga: dois métodos podem ter o mesmo nome, desde que tenham listas de parâmetros diferentes.
- Ouça o áudio com a tela desligada
- Ganhe Certificado após a conclusão
- + de 5000 cursos para você explorar!
Baixar o aplicativo
static int soma(int a, int b) { return a + b; }static int soma(int a, int b, int c) { return a + b + c; }Parâmetros e argumentos: entrada de dados
Parâmetros são as variáveis declaradas na definição do método. Argumentos são os valores passados na chamada do método.
static int dobro(int x) { // x é parâmetro return x * 2;}public static void main(String[] args) { int r = dobro(21); // 21 é argumento}Passagem por valor (inclusive para referências)
Em Java, a passagem é sempre por valor. Para tipos primitivos, o valor é copiado. Para tipos por referência, o que é copiado é o valor da referência (o “endereço lógico”), não o objeto em si. Isso afeta como você projeta APIs: um método pode alterar o estado de um objeto recebido, mas não consegue “trocar” a referência do chamador para apontar para outro objeto.
Retorno: produzindo resultados com return
O tipo de retorno define o que o método entrega ao chamador. Se o método não deve produzir um valor, usa-se void.
Métodos com retorno
static double media(double a, double b) { return (a + b) / 2.0;}Regras práticas:
- Todo caminho de execução deve retornar um valor compatível com o tipo declarado.
returnencerra a execução do método imediatamente.
Método void
Métodos void são usados para executar ações (efeitos), como imprimir, registrar, preencher estruturas, etc.
static void imprimirLinha(String texto) { System.out.println(texto);}Mesmo em void, você pode usar return; para sair cedo:
static void validarEImprimir(String texto) { if (texto == null) return; System.out.println(texto.trim());}Escopo de variáveis: onde uma variável existe
Escopo é a região do código onde uma variável pode ser acessada. Em métodos, os casos mais comuns são:
- Parâmetros: visíveis dentro do método.
- Variáveis locais: declaradas dentro do método ou bloco (
{}), existem apenas ali. - Variáveis de bloco: declaradas dentro de
if,for, etc., não existem fora do bloco.
static int exemploEscopo(int x) { int y = x + 1; // y é local do método if (y > 10) { int z = y * 2; // z só existe dentro deste bloco return z; } // z não existe aqui return y;}Sombreamento (shadowing) e legibilidade
Evite declarar variáveis locais com o mesmo nome de parâmetros, pois isso confunde a leitura:
static int ruim(int valor) { int valor = 10; // não compila: variável já definida no escopo}Mesmo quando compila (por exemplo, campos vs locais), prefira nomes distintos para reduzir ambiguidades.
Organização: utilitários com métodos static
Uma forma comum de organizar funções reutilizáveis é criar classes utilitárias com métodos static. Isso é útil para validação, cálculos e formatação, especialmente em projetos pequenos.
Boas práticas para utilitários:
- Nome da classe no plural ou descritivo:
Validacoes,Calculos,Formatacao. - Construtor privado para evitar instanciação.
- Métodos pequenos e focados.
final class Validacoes { private Validacoes() {} static boolean isBlank(String s) { return s == null || s.trim().isEmpty(); }}Sobrecarga (overload): mesmo nome, parâmetros diferentes
Sobrecarga permite criar múltiplas versões de um método com o mesmo nome, variando a lista de parâmetros (quantidade e/ou tipos). Isso melhora a ergonomia da API, oferecendo atalhos e defaults.
Exemplo: validação com mensagens diferentes
final class Validacoes { private Validacoes() {} static void exigirNaoBlank(String s) { exigirNaoBlank(s, "Texto obrigatório"); } static void exigirNaoBlank(String s, String mensagem) { if (s == null || s.trim().isEmpty()) { throw new IllegalArgumentException(mensagem); } }}Note que o tipo de retorno não diferencia sobrecarga. Isto não é permitido:
// NÃO permitido: mesma assinatura (nome + tipos de parâmetros)static int f(int x) { return x; }static double f(int x) { return x; }Resolução de sobrecarga: atenção a tipos numéricos
Quando há várias opções, o compilador escolhe a mais específica. Misturar int, long, double pode gerar chamadas ambíguas ou conversões inesperadas. Uma regra prática: mantenha assinaturas claras e evite sobrecargas que diferem apenas por “pequenas” promoções numéricas, a menos que seja realmente necessário.
Varargs: parâmetros variáveis
varargs permite receber zero ou mais argumentos de um mesmo tipo, como um array “implícito”. A sintaxe usa ... e deve ser o último parâmetro.
static int soma(int... valores) { int total = 0; for (int v : valores) total += v; return total;}Chamadas possíveis:
int a = soma(); // 0int b = soma(1); // 1int c = soma(1, 2, 3); // 6Você também pode passar um array explicitamente:
int[] xs = {2, 4, 6};int r = soma(xs);Varargs e sobrecarga: cuidado com ambiguidades
Evite combinar varargs com sobrecargas muito parecidas, pois pode ficar confuso para o compilador e para quem lê. Prefira assinaturas bem distintas.
Noções de imutabilidade ao projetar APIs simples
Imutabilidade significa que um objeto não muda após ser criado. Em APIs simples, isso traz previsibilidade e reduz efeitos colaterais. Algumas práticas úteis:
- Preferir retornar novos valores em vez de modificar entradas.
- Evitar expor estruturas mutáveis diretamente (por exemplo, não retornar arrays internos sem cópia).
- Tratar Strings como imutáveis: métodos de formatação devem retornar uma nova
String.
Exemplo: em vez de “limpar” um texto recebido, retorne uma versão normalizada:
final class Formatacao { private Formatacao() {} static String normalizarEspacos(String s) { if (s == null) return null; String t = s.trim(); return t.replaceAll("\s+", " "); }}Esse estilo facilita testes e evita surpresas: o chamador decide o que fazer com o retorno.
Projeto prático: construir uma mini-biblioteca incremental
A seguir, você vai construir três classes utilitárias (Validacoes, Calculos, Formatacao) e chamá-las a partir do main. A ideia é evoluir aos poucos, adicionando métodos, sobrecargas e varargs.
Passo 1: criar Validacoes com métodos pequenos
Comece com validações comuns de entrada.
final class Validacoes { private Validacoes() {} static boolean isBlank(String s) { return s == null || s.trim().isEmpty(); } static void exigirNaoNull(Object obj, String nome) { if (obj == null) { throw new IllegalArgumentException(nome + " não pode ser null"); } } static void exigirNaoBlank(String s, String nome) { if (isBlank(s)) { throw new IllegalArgumentException(nome + " não pode ser vazio"); } }}Observe o uso de parâmetros para tornar a mensagem reutilizável e o retorno void para um método que “impõe regra” (ou passa, ou lança exceção).
Passo 2: adicionar sobrecarga para ergonomia
Agora crie uma versão mais simples que usa uma mensagem padrão.
final class Validacoes { private Validacoes() {} static boolean isBlank(String s) { return s == null || s.trim().isEmpty(); } static void exigirNaoBlank(String s) { exigirNaoBlank(s, "texto"); } static void exigirNaoBlank(String s, String nome) { if (isBlank(s)) { throw new IllegalArgumentException(nome + " não pode ser vazio"); } }}Repare que a sobrecarga “menor” delega para a “maior”, centralizando a regra em um único lugar.
Passo 3: criar Calculos com retorno e varargs
Inclua cálculos simples e demonstre varargs.
final class Calculos { private Calculos() {} static int max(int a, int b) { return (a >= b) ? a : b; } static double media(double... valores) { if (valores == null || valores.length == 0) { throw new IllegalArgumentException("Informe ao menos um valor"); } double soma = 0.0; for (double v : valores) soma += v; return soma / valores.length; } static int somar(int... valores) { int total = 0; for (int v : valores) total += v; return total; }}Aqui você vê: retorno (int, double), validação de entrada e varargs para uma API mais flexível.
Passo 4: criar Formatacao com foco em imutabilidade
Formatação deve retornar novas Strings, sem alterar o argumento original.
final class Formatacao { private Formatacao() {} static String capitalizar(String s) { if (s == null) return null; String t = s.trim(); if (t.isEmpty()) return t; String primeira = t.substring(0, 1).toUpperCase(); String resto = t.substring(1).toLowerCase(); return primeira + resto; } static String juntarComVirgula(String... itens) { if (itens == null || itens.length == 0) return ""; StringBuilder sb = new StringBuilder(); for (int i = 0; i < itens.length; i++) { if (i > 0) sb.append(", "); sb.append(itens[i]); } return sb.toString(); }}StringBuilder é útil para montar Strings de forma eficiente. O método retorna uma nova String pronta.
Passo 5: integrar tudo no main (chamadas e testes rápidos)
Crie uma classe de aplicação e chame os utilitários. Use entradas variadas para observar retorno, exceções e sobrecargas.
public class App { public static void main(String[] args) { String nome = " aNa "; Validacoes.exigirNaoBlank(nome, "nome"); String nomeFmt = Formatacao.capitalizar(nome); System.out.println("Nome formatado: " + nomeFmt); int total = Calculos.somar(10, 20, 30); System.out.println("Total: " + total); double m = Calculos.media(7.5, 8.0, 9.0); System.out.println("Média: " + m); String lista = Formatacao.juntarComVirgula("validação", "cálculos", "formatação"); System.out.println("Lista: " + lista); // Exemplo de sobrecarga com default: Validacoes.exigirNaoBlank("ok"); // Descomente para ver a exceção: // Validacoes.exigirNaoBlank(" ", "apelido"); }}Checklist de design ao criar métodos
| Decisão | Pergunta prática | Dica |
|---|---|---|
| Nome | O nome descreve ação/resultado? | Use verbos para ações (validar, exigir) e substantivos para resultados (media, max). |
| Retorno | Preciso devolver algo ao chamador? | Se sim, retorne o valor; se não, use void e seja explícito sobre efeitos. |
| Parâmetros | Quais dados são realmente necessários? | Evite muitos parâmetros; prefira sobrecargas ou agrupar em um objeto quando crescer. |
| Escopo | Essa variável precisa existir fora do bloco? | Declare o mais próximo possível do uso. |
| Sobrecarga | Ajuda a API ou confunde? | Use para defaults e conveniência; evite assinaturas ambíguas. |
| Imutabilidade | Meu método altera entradas? | Prefira retornar novos valores e documente quando houver mutação. |