Tempo, Dinheiro e Padrões — java.time, BigDecimal e Expressões Regulares

Tempo, Dinheiro e Padrões — java.time, BigDecimal e Expressões Regulares

Datas, horas e fusos com java.time, dinheiro exato com BigDecimal e padrões de texto com expressões regulares em Java. Mostra por que double não serve para valores monetários, como escolher o arredondamento, parcelar uma compra sem perder centavos e extrair dados com grupos nomeados.
Java

• • 18 min de leitura

As três ferramentas da biblioteca para dados que parecem simples e não são: datas e horas, valores monetários, e formatos de texto.

Três pendências se acumularam ao longo do curso. Desde "Cada Valor em sua Caixa", a promessa de uma forma correta de lidar com dinheiro, já que double não serve. Desde "Operadores, Texto e Conversa com o Usuário", a necessidade de validar formatos de texto. E na aula anterior, a observação de que datas guardadas como texto são frágeis. Esta aula trata das três: o pacote java.time para datas, horas e fusos horários, a classe BigDecimal para aritmética decimal exata, e as expressões regulares para reconhecer e extrair padrões de texto. Com ela, termina a Fase 3.

Datas e horas: o pacote java.time

Lidar com tempo parece fácil até aparecerem os detalhes: meses com 28, 29, 30 ou 31 dias, anos bissextos, fusos horários, horário de verão, segundos que se acumulam. O Java tem uma API madura para isso desde o Java 8, o pacote java.time, baseado na biblioteca Joda-Time. Todas as classes dele são imutáveis, como defendido em "O que Fica Dentro, o que Fica Fora": operações como plusDays devolvem um objeto novo.

As classes principais, cada uma para um tipo de informação:

Classe Representa Exemplo
LocalDate uma data, sem hora nem fuso 2026-03-02
LocalTime uma hora do dia, sem data 08:30
LocalDateTime data e hora, sem fuso 2026-03-02T08:30
ZonedDateTime data e hora num fuso específico 2026-10-15T14:00-03:00[America/Sao_Paulo]
Instant um instante absoluto na linha do tempo, em UTC 2026-10-15T17:00:00Z
Period uma quantidade de anos, meses e dias 31 anos, 4 meses, 2 dias
Duration uma quantidade de horas, minutos e segundos 9 horas e 15 minutos

O "local" de LocalDate significa "sem fuso horário": é a data como aparece num calendário na parede, válida em qualquer lugar. Uma data de aniversário é um LocalDate. O momento exato de uma transação bancária é um Instant.

void main() {
    LocalDate inicio = LocalDate.of(2026, 3, 2);
    LocalDate fim = inicio.plusWeeks(52);
    IO.println(inicio + " -> " + fim + " (" + fim.getDayOfWeek() + ")");
    IO.println(ChronoUnit.DAYS.between(inicio, fim) + " dias");

    LocalDate nascimento = LocalDate.of(1995, 5, 23);
    LocalDate hoje = LocalDate.of(2026, 9, 25);
    Period idade = Period.between(nascimento, hoje);
    IO.println(idade.getYears() + " anos, " + idade.getMonths() + " meses e " + idade.getDays() + " dias");

    LocalTime abertura = LocalTime.of(8, 30);
    LocalTime fechamento = LocalTime.of(17, 45);
    Duration expediente = Duration.between(abertura, fechamento);
    IO.println(expediente + " = " + expediente.toHours() + "h" + expediente.toMinutesPart() + "min");
}
2026-03-02 -> 2027-03-01 (MONDAY)
364 dias
31 anos, 4 meses e 2 dias
PT9H15M = 9h15min

Os métodos têm nomes que se leem como frases: plusWeeks, minusDays, getDayOfWeek, isBefore, isLeapYear. O dia da semana é um enum, DayOfWeek, o mesmo mencionado em "Dados sem Cerimônia". ChronoUnit.DAYS.between conta os dias entre duas datas. A representação PT9H15M é o formato ISO 8601 para durações ("período de tempo: 9 horas e 15 minutos").

A aritmética de calendário tem regras que a API aplica corretamente:

IO.println(LocalDate.of(2026, 1, 31).plusMonths(1));   // 2026-02-28

31 de janeiro mais um mês não é 31 de fevereiro, que não existe. A API ajusta para o último dia válido do mês. Para ajustes do tipo "último dia do mês" ou "próxima segunda-feira", existem os TemporalAdjusters: hoje.with(TemporalAdjusters.lastDayOfMonth()).

Formatando e lendo datas

O toString das datas usa o formato ISO, ano-mês-dia, adequado para arquivos e trocas entre sistemas. Para exibir e ler no formato brasileiro, usa-se um DateTimeFormatter:

DateTimeFormatter br = DateTimeFormatter.ofPattern("dd/MM/yyyy");
LocalDate lida = LocalDate.parse("31/12/2026", br);
IO.println(lida.format(br) + " é " + lida.getDayOfWeek() + ", dia " + lida.getDayOfYear() + " do ano");
// 31/12/2026 é THURSDAY, dia 365 do ano

No padrão, dd é o dia, MM é o mês (com maiúsculas, porque mm são minutos), e yyyy é o ano. Um texto que não segue o padrão faz o parse lançar DateTimeParseException, que se trata como qualquer exceção de "Quando o Contrato se Quebra". Para textos em português, como nomes de meses e dias da semana, informa-se o idioma:

DateTimeFormatter.ofPattern("EEEE, d 'de' MMMM 'de' yyyy", Locale.of("pt", "BR"))
// quinta-feira, 15 de outubro de 2026

Fusos horários

Um compromisso marcado para as 14h em São Paulo acontece às 18h em Lisboa. O ZonedDateTime carrega o fuso, e converte entre fusos preservando o instante:

ZoneId saoPaulo = ZoneId.of("America/Sao_Paulo");
ZoneId lisboa = ZoneId.of("Europe/Lisbon");

ZonedDateTime reuniao = ZonedDateTime.of(2026, 10, 15, 14, 0, 0, 0, saoPaulo);
IO.println(reuniao);                                  // 2026-10-15T14:00-03:00[America/Sao_Paulo]
IO.println(reuniao.withZoneSameInstant(lisboa));      // 2026-10-15T18:00+01:00[Europe/Lisbon]
IO.println(reuniao.toInstant());                      // 2026-10-15T17:00:00Z

Os fusos são identificados por nomes de região, como America/Sao_Paulo, e não por deslocamentos fixos, como -03:00. O motivo é o horário de verão, em que o deslocamento de uma região muda ao longo do ano, com regras decididas por governos. O Brasil aboliu o horário de verão em 2019, mas muitos países o mantêm. Nos Estados Unidos, em 2026, os relógios adiantam uma hora na madrugada de 8 de março:

ZoneId ny = ZoneId.of("America/New_York");
ZonedDateTime antes = ZonedDateTime.of(2026, 3, 8, 1, 30, 0, 0, ny);
IO.println(antes + " + 1h = " + antes.plusHours(1));
// 2026-03-08T01:30-05:00[America/New_York] + 1h = 2026-03-08T03:30-04:00[America/New_York]

Uma hora depois de 1h30 é 3h30, porque 2h30 não existiu naquela noite. A JVM traz embutido um banco de dados com o histórico e as regras de fusos do mundo inteiro, atualizado junto com o Java. Uma regra prática para sistemas: armazene instantes em UTC, como Instant, e converta para o fuso do usuário só na exibição.

E o java.util.Date?

Você vai encontrar em código antigo as classes java.util.Date e java.util.Calendar, anteriores ao java.time. Elas têm defeitos de projeto conhecidos: são mutáveis, os meses começam em zero e os anos são contados a partir de 1900. new Date(126, 0, 1) é 1º de janeiro de 2026. E um Calendar aceita silenciosamente 30 de fevereiro, convertendo-o para 2 de março. Os métodos problemáticos estão marcados como obsoletos (deprecated), e o compilador avisa quando são usados. Em código novo, use sempre java.time. Para conversar com código antigo, existem métodos de conversão como date.toInstant().

Dinheiro: BigDecimal

Desde "Cada Valor em sua Caixa", a regra é não usar double para dinheiro. O motivo, em números:

double soma = 0;
BigDecimal somaExata = BigDecimal.ZERO;
for (int i = 0; i < 1000; i++) {
    soma += 0.10;
    somaExata = somaExata.add(new BigDecimal("0.10"));
}
IO.println(soma + " vs " + somaExata);
// 99.9999999999986 vs 100.00

Mil moedas de dez centavos dão cem reais, e não 99,9999999999986. O BigDecimal representa números decimais exatamente, como um inteiro de tamanho arbitrário mais uma escala (o número de casas decimais): 100.00 é o inteiro 10000 com escala 2. Ele é imutável, e as operações são métodos que devolvem valores novos: add, subtract, multiply e divide.

Crie a partir de texto

A armadilha mais comum está na criação:

IO.println(new BigDecimal(0.1));     // 0.1000000000000000055511151231257827021181583404541015625
IO.println(new BigDecimal("0.1"));   // 0.1

O construtor que recebe um double converte exatamente o valor binário que o double guarda, e esse valor não é 0,1. O erro do ponto flutuante entra no BigDecimal antes de qualquer conta. Crie sempre a partir de um String, ou com BigDecimal.valueOf(0.1), que converte usando a representação textual do double e produz 0.1.

Arredondamento

Multiplicações aumentam a escala: 19,90 × 0,85 dá 16.9150, com quatro casas. Para voltar a centavos, setScale define a escala e exige que você escolha como arredondar:

BigDecimal preco = new BigDecimal("19.90");
BigDecimal comDesconto = preco.multiply(new BigDecimal("0.85"));
IO.println(comDesconto + " -> " + comDesconto.setScale(2, RoundingMode.HALF_EVEN));
// 16.9150 -> 16.92

Os modos mais usados:

  • HALF_UP: o arredondamento escolar, em que 5 sobe. 2.345 vira 2.35.
  • HALF_EVEN: o arredondamento bancário, em que 5 vai para o vizinho par. 2.345 vira 2.34, e 2.355 vira 2.36. Arredondar sempre para cima introduz um viés que, em milhões de operações, se acumula. Com o vizinho par, metade dos casos sobe e metade desce. É o padrão em contabilidade e finanças.
  • DOWN: trunca, descartando as casas extras.

A divisão tem uma exigência própria. Uma divisão sem resultado decimal finito, como 100 ÷ 3, lança exceção se você não disser quantas casas quer:

new BigDecimal("100").divide(new BigDecimal("3"));
// ArithmeticException: Non-terminating decimal expansion; no exact representable decimal result.

new BigDecimal("100").divide(new BigDecimal("3"), 2, RoundingMode.HALF_EVEN);   // 33.33

É o BigDecimal recusando-se a arredondar sem ordem expressa. Ele prefere um erro a uma imprecisão escondida.

equals e compareTo discordam

A pendência de "Ordem e Critério":

BigDecimal x = new BigDecimal("2.0");
BigDecimal y = new BigDecimal("2.00");
IO.println(x.equals(y));              // false
IO.println(x.compareTo(y) == 0);      // true

O equals do BigDecimal compara valor e escala, e 2.0 tem escala 1, enquanto 2.00 tem escala 2. O compareTo compara só o valor numérico. A consequência aparece nas coleções: um HashSet com os dois guarda dois elementos, e um TreeSet guarda um só. Compare valores monetários com compareTo, nunca com equals. Se precisar usá-los como chaves ou em conjuntos, normalize a escala antes, com setScale ou stripTrailingZeros.

Para inteiros maiores que um long, existe o BigInteger, com precisão ilimitada: o fatorial de 30, que estoura o long (o problema do fatorial(21) de "Dividir para Resolver"), é 265252859812191058636308480000000. Criptografia e matemática de precisão usam BigInteger.

Parcelando uma compra

Um exemplo que junta datas e dinheiro: dividir R$ 1.000,00 em três parcelas mensais. A divisão exata dá 333,333..., e três parcelas de 333,33 somam 999,99. Um centavo some. A solução profissional é calcular a parcela-base truncada e colocar a diferença numa das parcelas:

record Parcela(int numero, LocalDate vencimento, BigDecimal valor) { }

List<Parcela> parcelar(BigDecimal total, int quantidade, LocalDate primeiroVencimento) {
    BigDecimal base = total.divide(BigDecimal.valueOf(quantidade), 2, RoundingMode.DOWN);
    BigDecimal resto = total.subtract(base.multiply(BigDecimal.valueOf(quantidade)));

    List<Parcela> parcelas = new ArrayList<>();
    for (int i = 0; i < quantidade; i++) {
        BigDecimal valor = (i == 0) ? base.add(resto) : base;
        parcelas.add(new Parcela(i + 1, primeiroVencimento.plusMonths(i), valor));
    }
    return parcelas;
}
List<Parcela> parcelas = parcelar(new BigDecimal("1000.00"), 3, LocalDate.of(2026, 1, 31));
1ª parcela: 31/01/2026  R$ 333.34
2ª parcela: 28/02/2026  R$ 333.33
3ª parcela: 31/03/2026  R$ 333.33
Soma das parcelas: R$ 1000.00 (confere? true)

A soma confere ao centavo, e a data de fevereiro foi ajustada para o último dia do mês pelo plusMonths. Repare que o vencimento de março voltou a ser dia 31, porque cada parcela é calculada a partir do primeiro vencimento, e não da parcela anterior. Esse é o tipo de detalhe que um sistema financeiro precisa decidir de forma explícita.

Padrões de texto: expressões regulares

Um CEP tem cinco dígitos, um hífen e três dígitos. Verificar isso com charAt e laços seria longo e cheio de erros. Uma expressão regular (regex) descreve o padrão de forma compacta: \d{5}-\d{3}. Os elementos mais usados:

Elemento Significa Exemplo
\d um dígito \d\d casa com "42"
\w letra, dígito ou _
\s espaço em branco (espaço, tab, quebra)
. qualquer caractere
[abc], [a-z] um caractere do conjunto [A-Z] é uma maiúscula
[^0-9] um caractere fora do conjunto
{n}, {n,m} exatamente n vezes, de n a m vezes \d{5}
+, *, ? uma ou mais, zero ou mais, zero ou uma vez \s+
(...) um grupo
| alternativa sim|não

Em Java, as expressões são escritas dentro de String, e a barra invertida precisa ser duplicada: o \d da regex vira "\\d" no código.

static final Pattern CEP = Pattern.compile("\\d{5}-\\d{3}");

for (String cep : new String[] {"50030-230", "50030230", "5003-0230", "50030-2300"}) {
    IO.println(cep + " -> " + CEP.matcher(cep).matches());
}
50030-230 -> true
50030230 -> false
5003-0230 -> false
50030-2300 -> false

Pattern.compile transforma a expressão numa estrutura pronta para uso. Compilar é caro, e por isso o padrão fica numa constante static final, compilado uma única vez. matcher(texto).matches() testa se o texto inteiro segue o padrão.

Encontrando e extraindo

find procura o padrão dentro de um texto, e os grupos capturam partes do que foi encontrado. Com grupos nomeados, (?<nome>...), a extração fica legível:

static final Pattern DATA = Pattern.compile("(?<dia>\\d{2})/(?<mes>\\d{2})/(?<ano>\\d{4})");

String texto = "Prova em 15/10/2026, entrega em 30/11/2026 e recuperação em 07/12/2026.";
Matcher m = DATA.matcher(texto);
while (m.find()) {
    IO.println("achei " + m.group() + " na posição " + m.start()
               + ": dia " + m.group("dia") + ", mês " + m.group("mes") + ", ano " + m.group("ano"));
}
achei 15/10/2026 na posição 9: dia 15, mês 10, ano 2026
achei 30/11/2026 na posição 32: dia 30, mês 11, ano 2026
achei 07/12/2026 na posição 60: dia 07, mês 12, ano 2026

Os métodos de String também aceitam expressões regulares:

IO.println(texto.replaceAll("(\\d{2})/(\\d{2})/(\\d{4})", "$3-$2-$1"));
// Prova em 2026-10-15, entrega em 2026-11-30 e recuperação em 2026-12-07.

IO.println(Arrays.toString("maçã,  banana ;uva;;pera".split("\\s*[,;]\\s*")));
// [maçã, banana, uva, , pera]

IO.println("  muitos    espaços   aqui ".strip().replaceAll("\\s+", " "));
// muitos espaços aqui

IO.println("R$ 1.234,56".replaceAll("[^0-9]", ""));
// 123456

No replaceAll, $1, $2 e $3 se referem aos grupos capturados, o que permite reordenar as partes. O split da aula anterior também recebe uma regex, e isso tem uma consequência traiçoeira: "a.b.c".split(".") devolve um array vazio, porque o ponto casa com qualquer caractere, e todo o texto vira separador. Para dividir por um ponto literal, escape-o com "\\." ou use Pattern.quote(".").

Com moderação

Expressões regulares são poderosas e fáceis de exagerar. Três conselhos:

  • Valide o formato com regex e o significado com código. \d{2}/\d{2}/\d{4} aceita "99/99/9999". Para saber se é uma data válida, faça o parse com DateTimeFormatter. Uma regex que tenta validar dias por mês, com anos bissextos, fica ilegível.
  • Desconfie de regex para formatos complexos. A especificação de endereços de e-mail é tão complicada que nenhuma regex prática a cobre inteira. Para e-mail, uma verificação simples, como "tem um @ e um ponto depois dele", mais um e-mail de confirmação, funciona melhor.
  • Cuidado com o desempenho. Algumas expressões mal escritas, com repetições aninhadas como (a+)+, podem levar um tempo exponencial em certas entradas, e já derrubaram sites inteiros. Se a regex for aplicada a textos vindos de usuários, mantenha-a simples.

Com esta aula termina a terceira fase. Você aprendeu a escrever código genérico, e entendeu o preço que ele carrega desde 2004. Usou as coleções do Collections Framework, sabendo o custo de cada operação e as regras de ordenação por trás delas. Aprendeu a tratar erros com exceções, a trabalhar com arquivos sem vazar recursos, e a representar corretamente tempo, dinheiro e padrões de texto. É o repertório da biblioteca padrão que aparece em praticamente todo programa Java.

Mas o estilo dos programas ainda é o mesmo da Fase 1: laços que percorrem coleções, variáveis acumuladoras, condições dentro dos laços. O relatório de notas da aula anterior tinha três laços para agrupar, somar e escrever, e os comparadores de "Ordem e Critério" precisavam de seis linhas de classe anônima para dizer "compare pela idade". Esse estilo muda quando um comportamento pode ser passado como um valor, tão facilmente quanto um número ou um texto: as classes anônimas viram uma linha, e os laços de busca, filtragem e agrupamento viram descrições do resultado desejado.

Fontes e leituras recomendadas

Exercícios

Exercício 1

Escreva um programa que, a partir de uma data fixa (25/09/2026), mostre quantos dias faltam para o fim do ano, qual é o último dia do mês e qual é a data da próxima segunda-feira.

Ver resposta

✓ Resposta:

void main() {
    LocalDate hoje = LocalDate.of(2026, 9, 25);
    long faltam = ChronoUnit.DAYS.between(hoje, LocalDate.of(2026, 12, 31));
    LocalDate ultimoDoMes = hoje.with(TemporalAdjusters.lastDayOfMonth());
    LocalDate proximaSegunda = hoje.with(TemporalAdjusters.next(DayOfWeek.MONDAY));
    IO.println(faltam + " dias; " + ultimoDoMes + "; " + proximaSegunda);
}

A saída é "97 dias; 2026-09-30; 2026-09-28". ChronoUnit.DAYS.between conta os dias do intervalo, sem incluir o último dia. Se "faltam" devesse incluir o próprio 31 de dezembro, seriam 98. Esse tipo de decisão de fronteira é uma fonte clássica de erros por um, e deve ser tomada de forma explícita. Os TemporalAdjusters expressam ajustes de calendário sem nenhuma conta. Num programa real, a data de referência viria de LocalDate.now(), mas usar uma data fixa torna o resultado reproduzível, algo que também importa em testes.

Exercício 2

Explique a diferença entre new BigDecimal(0.1), new BigDecimal("0.1") e BigDecimal.valueOf(0.1), e diga qual usar ao receber um valor que o usuário digitou.

Ver resposta

✓ Resposta: new BigDecimal(0.1) converte exatamente o valor binário guardado no double, que é aproximadamente 0,1000000000000000055511..., e traz a imprecisão do ponto flutuante para dentro do BigDecimal. new BigDecimal("0.1") interpreta o texto e produz exatamente 0,1. BigDecimal.valueOf(0.1) converte o double usando Double.toString, que produz o texto "0.1", e por isso também dá 0,1. Mas ele ainda depende de o valor ter passado por um double, o que pode ter perdido precisão antes. Para um valor digitado pelo usuário, o certo é criar o BigDecimal diretamente do texto lido, com new BigDecimal(texto), sem nunca passar por double. Se o usuário digitar com vírgula, a vírgula precisa ser trocada por ponto antes, ou o texto precisa ser interpretado com um formatador que conheça o idioma.

Exercício 3

Calcule quanto R$ 1.000,00 rendem em 12 meses a 1% ao mês, com juros compostos, usando BigDecimal. Compare o resultado arredondando a cada mês com o resultado arredondando só no final. Qual é o correto?

Ver resposta

✓ Resposta:

BigDecimal saldo = new BigDecimal("1000.00");
BigDecimal taxa = new BigDecimal("0.01");
for (int mes = 1; mes <= 12; mes++) {
    saldo = saldo.add(saldo.multiply(taxa)).setScale(2, RoundingMode.HALF_EVEN);
}
IO.println(saldo);   // 1126.84

BigDecimal semArredondar = new BigDecimal("1000.00").multiply(BigDecimal.ONE.add(taxa).pow(12));
IO.println(semArredondar.setScale(2, RoundingMode.HALF_EVEN));   // 1126.83

Os resultados diferem em um centavo: 1126,84 arredondando todo mês e 1126,83 arredondando só no fim (o valor exato é 1126,825030...). Nenhum dos dois está "errado". Eles modelam situações diferentes. Se o dinheiro fica numa conta que credita os juros mensalmente, cada crédito é um valor real em centavos, e o saldo do mês seguinte rende sobre esse valor arredondado. Então a primeira versão é a que reproduz o extrato. Se é um cálculo teórico de rendimento, a segunda é mais precisa. O exercício mostra que quando arredondar é uma regra de negócio, e não um detalhe técnico, e que o BigDecimal torna essa decisão explícita em vez de escondê-la num double.

Exercício 4

Escreva uma expressão regular que aceite placas de veículo nos dois formatos usados no Brasil, o antigo (três letras, hífen opcional e quatro dígitos, como "ABC-1234") e o do Mercosul (três letras, um dígito, uma letra e dois dígitos, como "ABC1D23"). Teste com casos válidos e inválidos.

Ver resposta

✓ Resposta:

static final Pattern PLACA = Pattern.compile("[A-Z]{3}\\d[A-Z]\\d{2}|[A-Z]{3}-?\\d{4}");

A alternativa | combina os dois formatos. O primeiro lado é o do Mercosul, e o segundo, o antigo, com o hífen opcional (-?). Com matches, "ABC1D23", "ABC-1234" e "ABC1234" são aceitas, e "AB1C234" (duas letras só) e "abc1d23" (minúsculas) são recusadas. Se a entrada vier digitada pelo usuário, é razoável normalizá-la antes, com strip().toUpperCase(), em vez de complicar a regex para aceitar minúsculas e espaços. É a separação entre limpar a entrada e validar o formato. Uma expressão mais permissiva, como [A-Z]{3}-?\d[A-Z0-9]\d{2}, também aceitaria as duas, mas deixaria passar combinações inválidas como "ABC-1D23", com hífen no formato Mercosul.

Exercício 5

Um sistema guarda preços num HashSet<BigDecimal> para detectar preços repetidos, e um preço de 2.0 não é detectado como repetido de 2.00. Explique o motivo e proponha duas correções.

Ver resposta

✓ Resposta: O HashSet usa equals e hashCode, e o equals do BigDecimal considera a escala: 2.0 (escala 1) e 2.00 (escala 2) são diferentes, e têm hashCode diferentes. Para o conjunto, são dois preços distintos. Primeira correção: normalizar a escala antes de inserir, com preco.setScale(2, RoundingMode.HALF_EVEN), o que é natural para valores monetários, que sempre têm duas casas. Com todos os preços na mesma escala, o equals passa a funcionar como esperado. Segunda correção: usar um TreeSet<BigDecimal>, que compara com compareTo, e para o qual 2.0 e 2.00 são equivalentes. A primeira é geralmente melhor, porque corrige a causa (valores monetários com escalas inconsistentes) em vez de contornar o sintoma, e evita que o mesmo problema reapareça em outros lugares, como em chaves de mapas ou comparações com equals.

Comentários

Mais em Java

O Programa que Escolhe e Repete — if, switch e Laços
O Programa que Escolhe e Repete — if, switch e Laços

O if e a condição que precisa ser boolean, o switch clássico com fall-through…

Um Novo Território — Java nos Jogos, o Game Loop e o Caso Minecraft
Um Novo Território — Java nos Jogos, o Game Loop e o Caso Minecraft

Nesta aula você conhece a história do Java nos jogos, do applet de navegador…

Dividir para Resolver — Métodos, Parâmetros e Recursão
Dividir para Resolver — Métodos, Parâmetros e Recursão

Como escrever métodos em Java: tipo de retorno, parâmetros, return, passagem…