Aplicação completa integrando os conceitos do curso
Nesta aula vamos construir um Sistema de Controle de Estoque — uma das aplicações mais comuns no mundo empresarial. O programa gerencia produtos com nome, preço e quantidade, e avisa automaticamente quando algum item está com estoque baixo.
Este projeto usa uma abordagem diferente da aula anterior: ao invés de usar uma classe separada para o produto, os dados são armazenados em três ArrayLists paralelos — uma decisão de design que reforça o conceito de vetores paralelos visto na Aula 47.
Como rodar no VSCode: Crie um arquivo chamado Estoque.java, cole o código completo, abra o terminal integrado (Ctrl + `) e execute com javac Estoque.java seguido de java Estoque.
| Conceito | Onde aparece no projeto | Aulas relacionadas |
|---|---|---|
| ArrayLists paralelos | nomes, precos e quantidades compartilham o mesmo índice para representar um produto |
Aulas 47, 57–59 |
| Scanner | Lê todas as entradas do usuário | Aula 17 |
| Métodos personalizados | adicionarProduto(), entradaEstoque(), saidaEstoque(), relatorio(), alertaBaixoEstoque() |
Aulas 34, 48 |
| Switch-case | Direciona as opções do menu | Aula 45 |
| do-while | Mantém o menu ativo até o usuário sair | Aula 42 |
| For tradicional com índice | Necessário para acessar os três ArrayLists pelo mesmo índice i |
Aula 41 |
| Operador ternário | Define o status do produto (OK / BAIXO / ESGOTADO) em uma linha | Aula 46 |
| printf e especificadores | Formata a tabela do relatório com colunas alinhadas | Aula 21 |
| Constantes | ESTOQUE_MINIMO define o limite de alerta como valor fixo |
Aula 18 |
| Métodos de String | .equalsIgnoreCase() compara nomes sem diferenciar maiúsculas |
Aula 35 |
Neste projeto, cada produto é representado pela mesma posição em três listas diferentes. O índice i conecta as informações:
| Lista | Pos. 0 | Pos. 1 | Pos. 2 | Pos. 3 |
|---|---|---|---|---|
| nomes.get(i) | Arroz | Feijão | Café | Açúcar |
| precos.get(i) | 8.90 | 12.50 | 22.00 | 5.70 |
| quantidades.get(i) | 50 | 3 | 0 | 20 |
Atenção: A grande responsabilidade com ArrayLists paralelos é manter a sincronia. Ao adicionar um produto, os três valores devem ser adicionados juntos — e ao remover, os três devem ser removidos pelo mesmo índice. Se um for adicionado sem os outros, todo o sistema desalinha.
import java.util.ArrayList;
import java.util.Scanner;
public class Estoque {
// Constante: quantidade mínima aceitável em estoque
static final int ESTOQUE_MINIMO = 5;
// Três ArrayLists paralelos: mesmo índice = mesmo produto
static ArrayList<String> nomes = new ArrayList<>();
static ArrayList<Double> precos = new ArrayList<>();
static ArrayList<Integer> quantidades = new ArrayList<>();
static Scanner sc = new Scanner(System.in);
// ── MÉTODO PRINCIPAL ────────────────────────────────────────────────────
public static void main(String[] args) {
// Produtos iniciais do estoque
adicionarProduto("Arroz", 8.90, 50);
adicionarProduto("Feijão", 12.50, 3);
adicionarProduto("Café", 22.00, 0);
adicionarProduto("Açúcar", 5.70, 20);
adicionarProduto("Óleo", 9.80, 4);
int opcao;
do {
exibirMenu();
opcao = Integer.parseInt(sc.nextLine().trim());
switch (opcao) {
case 1: menuAdicionarProduto(); break;
case 2: menuEntrada(); break;
case 3: menuSaida(); break;
case 4: relatorio(); break;
case 5: alertaBaixoEstoque(); break;
case 0:
System.out.println("\nSistema encerrado.");
break;
default:
System.out.println("Opção inválida.");
}
} while (opcao != 0);
sc.close();
}
// ── MENU ────────────────────────────────────────────────────────────────
static void exibirMenu() {
System.out.println("\n╔══════════════════════════════════╗");
System.out.println("║ CONTROLE DE ESTOQUE ║");
System.out.println("╠══════════════════════════════════╣");
System.out.println("║ 1. Cadastrar novo produto ║");
System.out.println("║ 2. Entrada de estoque ║");
System.out.println("║ 3. Saída de estoque ║");
System.out.println("║ 4. Relatório geral ║");
System.out.println("║ 5. Alertas de estoque baixo ║");
System.out.println("║ 0. Sair ║");
System.out.println("╚══════════════════════════════════╝");
System.out.print("Escolha uma opção: ");
}
// ── ADICIONAR PRODUTO ────────────────────────────────────────────────────
static void adicionarProduto(String nome, double preco, int qtd) {
// Verifica se produto já existe (ignora maiúsculas/minúsculas)
for (String n : nomes) {
if (n.equalsIgnoreCase(nome)) {
System.out.println("Produto já cadastrado: " + nome);
return;
}
}
// Adiciona nas três listas ao mesmo tempo — mantém sincronia
nomes.add(nome);
precos.add(preco);
quantidades.add(qtd);
}
static void menuAdicionarProduto() {
System.out.print("Nome do produto : "); String nome = sc.nextLine();
System.out.print("Preço (R$) : "); double preco = Double.parseDouble(sc.nextLine().replace(",", "."));
System.out.print("Qtd inicial : "); int qtd = Integer.parseInt(sc.nextLine().trim());
adicionarProduto(nome, preco, qtd);
System.out.println("✓ Produto cadastrado!");
}
// ── BUSCAR ÍNDICE DO PRODUTO ─────────────────────────────────────────────
static int buscarIndice(String nome) {
for (int i = 0; i < nomes.size(); i++) {
if (nomes.get(i).equalsIgnoreCase(nome)) return i;
}
return -1; // -1 indica "não encontrado"
}
// ── ENTRADA DE ESTOQUE ───────────────────────────────────────────────────
static void menuEntrada() {
System.out.print("Nome do produto : "); String nome = sc.nextLine();
int idx = buscarIndice(nome);
if (idx == -1) {
System.out.println("Produto não encontrado.");
return;
}
System.out.print("Quantidade a adicionar: ");
int qtd = Integer.parseInt(sc.nextLine().trim());
if (qtd <= 0) {
System.out.println("A quantidade deve ser positiva.");
return;
}
int novaQtd = quantidades.get(idx) + qtd;
quantidades.set(idx, novaQtd);
System.out.println("✓ Entrada registrada! Novo estoque de "
+ nomes.get(idx) + ": " + novaQtd + " unidades.");
}
// ── SAÍDA DE ESTOQUE ─────────────────────────────────────────────────────
static void menuSaida() {
System.out.print("Nome do produto : "); String nome = sc.nextLine();
int idx = buscarIndice(nome);
if (idx == -1) {
System.out.println("Produto não encontrado.");
return;
}
System.out.print("Quantidade a retirar: ");
int qtd = Integer.parseInt(sc.nextLine().trim());
if (qtd <= 0) {
System.out.println("A quantidade deve ser positiva.");
return;
}
int atual = quantidades.get(idx);
if (qtd > atual) {
System.out.println("Estoque insuficiente! Disponível: " + atual);
return;
}
quantidades.set(idx, atual - qtd);
System.out.println("✓ Saída registrada! Estoque restante de "
+ nomes.get(idx) + ": " + (atual - qtd) + " unidades.");
}
// ── RELATÓRIO GERAL ──────────────────────────────────────────────────────
static void relatorio() {
System.out.println("\n══════════════════════════════════════════════════════");
System.out.printf("%-15s %10s %12s %12s%n", "PRODUTO", "PREÇO", "QTDE", "STATUS");
System.out.println("──────────────────────────────────────────────────────");
double valorTotal = 0;
// For tradicional: precisamos do índice i para acessar as três listas juntas
for (int i = 0; i < nomes.size(); i++) {
String nome = nomes.get(i);
double preco = precos.get(i);
int qtd = quantidades.get(i);
double valorEmEstoque = preco * qtd;
valorTotal += valorEmEstoque;
// Operador ternário aninhado para definir o status
String status = qtd == 0 ? "⚠ ESGOTADO"
: qtd < ESTOQUE_MINIMO ? "⚠ BAIXO"
: "✓ OK";
System.out.printf("%-15s R$%8.2f %8d un %s%n",
nome, preco, qtd, status);
}
System.out.println("──────────────────────────────────────────────────────");
System.out.printf("%-15s %10s %12s%n", "TOTAL EM ESTOQUE:", "", "");
System.out.printf("Valor total: R$ %.2f%n", valorTotal);
System.out.printf("Produtos cadastrados: %d%n", nomes.size());
}
// ── ALERTA DE ESTOQUE BAIXO ──────────────────────────────────────────────
static void alertaBaixoEstoque() {
System.out.println("\n⚠ PRODUTOS COM ESTOQUE BAIXO OU ESGOTADO:");
System.out.println("(limite mínimo: " + ESTOQUE_MINIMO + " unidades)");
System.out.println("─────────────────────────────────────────");
boolean encontrou = false;
for (int i = 0; i < nomes.size(); i++) {
int qtd = quantidades.get(i);
if (qtd < ESTOQUE_MINIMO) {
String aviso = qtd == 0 ? "ESGOTADO" : "apenas " + qtd + " unidades";
System.out.println("• " + nomes.get(i) + " — " + aviso);
encontrou = true;
}
}
if (!encontrou) {
System.out.println("Todos os produtos estão com estoque adequado.");
}
}
}
Escolha uma opção: 4
══════════════════════════════════════════════════════
PRODUTO PREÇO QTDE STATUS
──────────────────────────────────────────────────────
Arroz R$ 8.90 50 un ✓ OK
Feijão R$ 12.50 3 un ⚠ BAIXO
Café R$ 22.00 0 un ⚠ ESGOTADO
Açúcar R$ 5.70 20 un ✓ OK
Óleo R$ 9.80 4 un ⚠ BAIXO
──────────────────────────────────────────────────────
Valor total: R$ 659.90
Produtos cadastrados: 5
Escolha uma opção: 5
⚠ PRODUTOS COM ESTOQUE BAIXO OU ESGOTADO:
(limite mínimo: 5 unidades)
─────────────────────────────────────────
• Feijão — apenas 3 unidades
• Café — ESGOTADO
• Óleo — apenas 4 unidades
Dica de extensão: Tente adicionar uma opção "6. Buscar produto por nome" que exiba os dados de um único produto. Você já tem o método buscarIndice() — basta imprimir os dados do índice retornado com printf.
Neste projeto usamos três listas paralelas. Na Aula 63, usamos uma classe Livro. Ambas as abordagens funcionam — a classe é mais segura para projetos grandes (os dados ficam agrupados), enquanto listas paralelas são mais simples para projetos menores. Em Java profissional, prefere-se sempre a classe.
Ao declarar static final int ESTOQUE_MINIMO = 5, criamos um valor que nunca muda e tem um nome significativo. Se o limite mudar para 10, basta alterar em um único lugar — e não em cada if espalhado pelo código.
A listagem e a busca usam for (int i = 0; i < nomes.size(); i++) porque precisamos do mesmo índice i para acessar nomes.get(i), precos.get(i) e quantidades.get(i) simultaneamente. O for-each não fornece o índice.
O status usa dois ternários encadeados: primeiro verifica se está zerado, depois se está abaixo do mínimo, e só então confirma como OK. Isso substitui um bloco if-else if-else inteiro em uma única linha expressiva.
✅ Validação de entrada: O projeto verifica se a quantidade é positiva, se o estoque é suficiente para a saída e se o produto já existe antes de cadastrar. Essas verificações evitam que o programa trave ou produza dados incoerentes — uma prática fundamental em sistemas reais.
Teste sua compreensão do projeto Estoque:
Reforce o conteúdo desta aula com mais dois exercícios práticos: