Por que a classe é declarada daquele jeito, o que cada palavra do main significa, e como formatar números, textos e datas sem cair nas armadilhas de sempre.

⌨️ Antes de tudo: deixe o editor trabalhar por você

Ninguém digita public static void main(String[] args) por extenso. Os editores têm atalhos de expansão — você escreve uma abreviação, aperta Tab, e o bloco completo aparece.

Uma confusão comum

Se você viu sout e psvm em algum tutorial, saiba que esses são atalhos do IntelliJ IDEA. No VS Code, com o Extension Pack for Java, os equivalentes canônicos são outros:

O que você querVS CodeIntelliJ
System.out.println();sysoutsout
System.err.println();syserrserr
Método main completomainpsvm
Laço for indexadoforifori
Construtor da classector

Vale testar os dois na sua instalação: algumas versões da extensão Java aceitam os apelidos do IntelliJ também. O que importa é descobrir qual funciona no seu ambiente e usar sempre.

Atalhos de teclado que valem mais que os snippets

AtalhoO que faz
Ctrl + SpaceAutocompletar — a base de tudo
Ctrl + .Correção rápida: adiciona o import que falta
Shift + Alt + OOrganiza os imports, remove os não usados
Shift + Alt + FFormata o arquivo inteiro
Ctrl + /Comenta ou descomenta a linha
F5Executa com depurador
Ctrl + F5Executa direto, sem depurador

O Ctrl + . é o que mais economiza tempo no começo: você escreve LocalDate sem ter importado nada, o editor sublinha, você aperta e ele resolve.

🏗️ A anatomia de uma classe

class Lesson1Print {
    public static void main(String[] args) {
        System.out.println("Hello Java");
    }
}

Parece muita cerimônia para imprimir uma frase. Cada pedaço tem uma razão.

O nome da classe não é livre

Em Java, a convenção é PascalCase: cada palavra começa em maiúscula, sem underscore.

class lesson1_print   // funciona, mas destoa de todo código Java
class Lesson1Print    // convenção
class PrintBasico     // ainda mais descritivo

Não é frescura. O compilador aceita lesson1_print, mas quem ler seu código vai estranhar — em Java, snake_case é sinal de quem veio de outra linguagem e não ajustou o hábito.

Há também uma regra rígida: se a classe for public, o arquivo precisa ter exatamente o mesmo nome. public class Lesson1Print mora obrigatoriamente em Lesson1Print.java. Sem o public, a regra afrouxa — mas mantenha o hábito.

public static void main(String[] args), palavra por palavra

Essa linha é o ponto de entrada: é ela que a JVM procura ao rodar o programa.

Troque qualquer uma dessas palavras e o programa compila, mas não roda: a JVM procura por essa assinatura exata.

🖨️ Três formas de imprimir

System.out.print("sem quebrar linha");
System.out.println("quebra a linha no fim");
System.out.printf("com formatação: %d%n", 42);

🔗 Concatenar ou formatar?

Com uma variável, concatenar é natural:

String nome = "Matheus";
System.out.println(nome + ", hello Java");

Com três, vira sopa de aspas e sinais de mais:

// difícil de ler e fácil de errar
System.out.println("Nome: " + nome + " | Idade: " + idade + " | Altura: " + altura + " m");

// o formato fica visível, separado dos dados
System.out.printf("Nome: %s | Idade: %d | Altura: %.2f m%n", nome, idade, altura);

A vantagem do printf não é economizar caracteres — é separar o molde do conteúdo. Você bate o olho e entende o layout da saída sem precisar decifrar onde uma variável termina e o texto recomeça.

📐 Os especificadores de formato

EspecificadorServe paraExemploSaída
%sTexto (qualquer objeto)%s com "Ana"Ana
%dInteiro%d com 4242
%fDecimal%f com 1.51,500000
%.2fDecimal com 2 casas%.2f com 19.9876519,99
%05dInteiro com zeros à esquerda%05d com 4200042
%-10sTexto alinhado à esquerda em 10 colunas%-10s| com "Ana"Ana       |
%,dInteiro com separador de milhar%,d com 12345671.234.567
%bBooleano%b com truetrue
%%Um sinal de porcentagem literal50%%50%
%nQuebra de linha

%n e \n não são a mesma coisa

Ambos quebram a linha, mas:

Em texto que vai para o terminal, a diferença raramente aparece. Ela morde quando você grava arquivo: um .txt gerado com \n no Windows pode abrir todo em uma linha só no Bloco de Notas antigo. Dentro de printf, prefira %n.

🇧🇷 A armadilha da vírgula decimal

Este é o erro que mais pega brasileiro:

double preco = 19.98765;
System.out.printf("Preço: R$ %.2f%n", preco);

O que sai depende da configuração regional da máquina:

Saída Máquina em pt-BR:  Preço: R$ 19,99
Máquina em en-US:  Preço: R$ 19.99

O printf usa o locale padrão do sistema. Isso é ótimo quando você quer o formato local — e desastroso quando precisa de saída previsível, como ao gerar um CSV, um JSON ou um arquivo que outro sistema vai ler.

Para fixar o formato, informe o locale:

import java.util.Locale;

// sempre com ponto, independente da máquina
System.out.printf(Locale.US, "%.2f%n", preco);                 // 19.99

// sempre com vírgula
System.out.printf(new Locale("pt", "BR"), "%.2f%n", preco);    // 19,99
Regra prática: saída para humano pode usar o locale do sistema; saída para máquina deve ter locale fixo.

📅 Datas: o %tD mente para você

System.out.printf("Hoje é %tD%n", new Date());
// Saída: Hoje é 07/28/26

Isso não é a data errada — é a data no formato americano. O %tD equivale a %tm/%td/%ty: mês, dia, ano com dois dígitos. Para 28 de julho de 2026, sai 07/28/26.

Para o formato brasileiro, monte os pedaços:

System.out.printf("Hoje é %td/%tm/%tY%n", data, data, data);

Repetir o argumento três vezes incomoda. Existe atalho — o sinal < reaproveita o argumento anterior:

System.out.printf("Hoje é %td/%<tm/%<tY%n", data);
// Saída: Hoje é 28/07/2026

Os principais conversores de data:

ConversorSignificaExemplo
%tdDia com 2 dígitos28
%tmMês com 2 dígitos07
%tYAno com 4 dígitos2026
%tyAno com 2 dígitos26
%tHHora, 24h14
%tMMinuto05

E tem um problema maior que o formato

import java.util.Date;   // legado

java.util.Date é de 1996 e carrega decisões ruins: é mutável, mistura data com hora, e vários métodos estão marcados como obsoletos desde o Java 1.1.

Desde o Java 8 existe o pacote java.time, que resolve isso:

import java.time.LocalDate;
import java.time.format.DateTimeFormatter;

LocalDate hoje = LocalDate.now();
System.out.println(hoje.format(DateTimeFormatter.ofPattern("dd/MM/yyyy")));
// Saída: 28/07/2026

Além de mais legível, LocalDate é imutável — nenhum método altera o objeto, todos devolvem um novo. Isso elimina uma classe inteira de bugs em que uma data muda sem você perceber.

Você ainda vai encontrar Date em código antigo e precisa saber lê-lo. Mas em código novo, use java.time.

✅ O código completo, revisado

import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.Locale;

public class Lesson1Print {

    public static void main(String[] args) {

        System.out.println("Hello Java");

        String nome = "Matheus";
        System.out.println(nome + ", hello Java");

        // Decimal com 2 casas — locale fixo para saída previsível
        double preco = 19.98765;
        System.out.printf(Locale.US, "Preço: US$ %.2f%n", preco);       // 19.99
        System.out.printf("Preço: R$ %.2f%n", preco);                   // 19,99 em pt-BR

        // Inteiro com zeros à esquerda
        int numero = 42;
        System.out.printf("Número: %05d%n", numero);                    // 00042

        // Data no formato brasileiro — o < reaproveita o argumento
        LocalDate hoje = LocalDate.now();
        System.out.printf("Hoje é %td/%<tm/%<tY%n", hoje);              // 28/07/2026

        // Alternativa mais legível para datas
        System.out.println(hoje.format(DateTimeFormatter.ofPattern("dd/MM/yyyy")));

        // Várias variáveis: o molde fica separado dos dados
        int idade = 25;
        double altura = 1.82;
        System.out.printf("Nome: %s | Idade: %d | Altura: %.2f m%n", nome, idade, altura);
    }
}

📌 Recapitulando