AFF Education Construa seu Futuro

Aula 64 - Projeto Final: Sistema de Controle de Estoque

Aplicação completa integrando os conceitos do curso

Sobre este Projeto

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.

Conceitos Aplicados Neste Projeto

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

Design: ArrayLists Paralelos

Neste projeto, cada produto é representado pela mesma posição em três listas diferentes. O índice i conecta as informações:

Lista Pos. 0Pos. 1Pos. 2Pos. 3
nomes.get(i) ArrozFeijãoCaféAçúcar
precos.get(i) 8.9012.5022.005.70
quantidades.get(i) 503020

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.

Código Completo — Estoque.java

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.

Revisão dos Conceitos-Chave

📋 ArrayLists paralelos vs Classe

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.

🔢 Constante ESTOQUE_MINIMO

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.

🔁 For tradicional obrigatório

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.

🔀 Operador ternário aninhado

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.

Exercícios

Teste sua compreensão do projeto Estoque:

1. Por que o método adicionarProduto() adiciona nas três listas ao mesmo tempo?

2. O que o método buscarIndice() retorna quando o produto NÃO é encontrado?

3. Qual é o valor total em estoque do Arroz, considerando preço R$8,90 e 50 unidades?

4. Por que o relatório usa for (int i = 0; i < nomes.size(); i++) e não for-each?

5. Qual a vantagem de declarar ESTOQUE_MINIMO como constante (final)?

6. O que acontece se o usuário tentar registrar uma saída maior que o estoque disponível?

7. No operador ternário aninhado do relatório, qual status é exibido quando qtd = 0?

String status = qtd == 0 ? "⚠ ESGOTADO"
              : qtd < ESTOQUE_MINIMO ? "⚠ BAIXO"
              : "✓ OK";

Exercícios de Aplicação AFF Education

Reforce o conteúdo desta aula com mais dois exercícios práticos:

1. Num controle de estoque, para somar a quantidade total de itens de uma lista de produtos, usa-se:

2. Para localizar rapidamente um produto pelo seu código único, a estrutura ideal é: