Projeto Sistema de Biblioteca
Este é o projeto de orientação a objetos do guia. Em vez de um programa com um main enorme, você vai construir várias classes pequenas, cada uma responsável por uma coisa: o livro sabe se está disponível, o usuário sabe quantos livros pode levar, a biblioteca guarda tudo e aplica as regras. Quando alguma regra é quebrada, uma exceção com nome próprio avisa o que aconteceu.
- Classes e objetos, construtores e encapsulamento (atributos
private, métodos que protegem as regras). - Interfaces implementadas por records, e polimorfismo.
- HashMap para achar um livro pelo código.
- Records como "fichas" de dados (
Emprestimo). - Criar suas exceções, com herança entre elas, e throw e throws.
- Um programa com vários arquivos.
O que vamos construir
No fim, a pasta do projeto terá dez arquivos, um por classe, e o Main encena um dia na biblioteca: o Bruno (aluno) e a Carla (professora) tentam pegar livros, e as regras entram em ação.
$ ls Aluno.java LimiteDeEmprestimosException.java Professor.java BibliotecaException.java LivroIndisponivelException.java Usuario.java Biblioteca.java Livro.java Emprestimo.java Main.java $ java Main.java Bruno levou L1. Bruno levou L2. Não deu: Bruno já está no limite de 2 livro(s). « regra: aluno leva no máximo 2 Não deu: "Dom Casmurro" já está emprestado. « regra: um livro, um leitor Bruno devolveu "Dom Casmurro". Carla levou L1. Erro: Livro não encontrado: L9 --- Empréstimos (2) --- Vidas Secas -> Bruno Dom Casmurro -> Carla --- Acervo (4 livros) --- L1 | Dom Casmurro | Machado de Assis | emprestado L2 | Vidas Secas | Graciliano Ramos | emprestado L3 | O Cortiço | Aluísio Azevedo | disponível L4 | Iracema | José de Alencar | disponível
Com vários arquivos na pasta, java Main.java continua funcionando: desde o Java 22, o lançador compila sozinho as outras classes que o Main usa, se estiverem na mesma pasta. Com javac *.java você compila tudo de uma vez e vê os .class:
$ javac *.java $ ls Aluno.class Emprestimo.java Main.class Aluno.java LimiteDeEmprestimosException.class Main.java Biblioteca.class LimiteDeEmprestimosException.java Professor.class BibliotecaException.class Livro.class Professor.java BibliotecaException.java LivroIndisponivelException.class Usuario.class Biblioteca.java LivroIndisponivelException.java Usuario.java Emprestimo.class Livro.java
- O livro (classe,
private, getters,toString). - Os usuários (interface
Usuario, recordsAlunoeProfessor). - O acervo (classe
BibliotecacomHashMap). - Os empréstimos (record
Emprestimo, regras de negócio). - Exceções próprias (
LivroIndisponivelExceptione companhia).
Antes de começar
Este projeto pede o Nível 2 quase inteiro: classes, encapsulamento, interfaces, records e exceções, além de HashMap. No seu computador, cada classe pública vai num arquivo com o mesmo nome:
$ mkdir -p java/biblioteca $ cd java/biblioteca
Para as missões caberem numa caixa, as classes estão juntas num único Main.java, uma depois da outra. O Java permite isso, com uma regra: só a classe Main pode ser public; as outras ficam sem o public na frente de class, interface ou record. O comportamento é o mesmo. No seu computador, prefira um arquivo por classe, como nas listagens desta página.
Etapa 1 · O livro
Começamos pela peça mais simples. Um livro tem código, título, autor e uma situação: disponível ou emprestado. A regra de ouro do encapsulamento: ninguém de fora mexe direto nos dados do livro. Os atributos são private, e quem quiser mudar a situação tem que pedir ao livro, pelos métodos emprestar() e devolver().
public class Livro {
private final String codigo;
private final String titulo;
private final String autor;
private boolean disponivel;
public Livro(String codigo, String titulo, String autor) {
this.codigo = codigo;
this.titulo = titulo;
this.autor = autor;
this.disponivel = true;
}
public String getCodigo() {
return codigo;
}
public String getTitulo() {
return titulo;
}
public String getAutor() {
return autor;
}
public boolean isDisponivel() {
return disponivel;
}
public void emprestar() {
disponivel = false;
}
public void devolver() {
disponivel = true;
}
@Override
public String toString() {
String situacao = disponivel ? "disponível" : "emprestado";
return codigo + " | " + titulo + " | " + autor + " | " + situacao;
}
}
| Parte | O que é |
|---|---|
private final String codigo; | Atributo privado (só a própria classe enxerga) e final (definido uma vez, no construtor, e nunca mais muda). Código, título e autor de um livro não mudam. |
private boolean disponivel; | Privado, mas não final: muda a cada empréstimo, só que apenas pelos métodos da classe. |
public Livro(…) | O construtor. this.codigo = codigo copia o parâmetro para o atributo (this). Todo livro nasce disponível. |
getCodigo(), isDisponivel() | Os getters: deixam ler sem deixar mudar. Para boolean, o costume é isAlgo() em vez de getAlgo(). |
Não há setDisponivel(…) | De propósito. Na etapa 5, emprestar() vai recusar um livro já emprestado; com um setter, qualquer um passaria por cima dessa regra. |
@Override public String toString() | Diz como o livro vira texto no println (sobrescrita). O ? : é o operador ternário. |
Um Main para experimentar:
public class Main {
public static void main(String[] args) {
Livro livro = new Livro("L1", "Dom Casmurro", "Machado de Assis");
System.out.println(livro);
livro.emprestar();
System.out.println(livro);
System.out.println("Disponível? " + livro.isDisponivel());
livro.devolver();
System.out.println(livro);
}
}
$ java Main.java L1 | Dom Casmurro | Machado de Assis | disponível L1 | Dom Casmurro | Machado de Assis | emprestado Disponível? false L1 | Dom Casmurro | Machado de Assis | disponível
Este código tem um erro de propósito.
Livro livro = new Livro("L1", "Dom Casmurro", "Machado de Assis");
livro.disponivel = false;
$ java Main.java
Main.java:4: error: disponivel has private access in Livro
livro.disponivel = false;
^
1 error
error: compilation failed
"disponivel tem acesso privado em Livro." É o encapsulamento funcionando: o compilador não deixa o Main pular as regras do livro. Use livro.emprestar().
Missão (etapa 1): a classe Livro está sem os métodos emprestar() e devolver() (rode e veja o cannot find symbol). Escreva os dois: um põe disponivel em false, o outro em true.
public class Main {
public static void main(String[] args) {
Livro livro = new Livro("L1", "Dom Casmurro", "Machado de Assis");
System.out.println(livro);
livro.emprestar();
System.out.println(livro);
System.out.println("Disponível? " + livro.isDisponivel());
livro.devolver();
System.out.println(livro);
}
}
class Livro {
private final String codigo;
private final String titulo;
private final String autor;
private boolean disponivel;
public Livro(String codigo, String titulo, String autor) {
this.codigo = codigo;
this.titulo = titulo;
this.autor = autor;
this.disponivel = true;
}
public String getCodigo() {
return codigo;
}
public String getTitulo() {
return titulo;
}
public String getAutor() {
return autor;
}
public boolean isDisponivel() {
return disponivel;
}
// escreva aqui os métodos emprestar() e devolver()
@Override
public String toString() {
String situacao = disponivel ? "disponível" : "emprestado";
return codigo + " | " + titulo + " | " + autor + " | " + situacao;
}
}
public class Main {
public static void main(String[] args) {
Livro livro = new Livro("L1", "Dom Casmurro", "Machado de Assis");
System.out.println(livro);
livro.emprestar();
System.out.println(livro);
System.out.println("Disponível? " + livro.isDisponivel());
livro.devolver();
System.out.println(livro);
}
}
class Livro {
private final String codigo;
private final String titulo;
private final String autor;
private boolean disponivel;
public Livro(String codigo, String titulo, String autor) {
this.codigo = codigo;
this.titulo = titulo;
this.autor = autor;
this.disponivel = true;
}
public String getCodigo() {
return codigo;
}
public String getTitulo() {
return titulo;
}
public String getAutor() {
return autor;
}
public boolean isDisponivel() {
return disponivel;
}
public void emprestar() {
disponivel = false;
}
public void devolver() {
disponivel = true;
}
@Override
public String toString() {
String situacao = disponivel ? "disponível" : "emprestado";
return codigo + " | " + titulo + " | " + autor + " | " + situacao;
}
}
Etapa 2 · Os usuários
A biblioteca tem dois tipos de usuário: alunos, que levam até 2 livros, e professores, que levam até 5. Os dois têm nome e um limite, mas o limite é diferente. Isso pede uma interface: um "contrato" que diz o que todo usuário sabe responder, deixando cada tipo decidir como.
public interface Usuario {
String nome();
int limiteDeLivros();
}
Cada tipo de usuário é um record que implementa a interface:
public record Aluno(String nome) implements Usuario {
@Override
public int limiteDeLivros() {
return 2;
}
}
public record Professor(String nome) implements Usuario {
@Override
public int limiteDeLivros() {
return 5;
}
}
- public interface Usuario
- O contrato: todo
Usuariotem os métodosnome()elimiteDeLivros(). Uma interface só declara; não tem o corpo dos métodos. - record Aluno(String nome)
- Um record com um componente,
nome. O record já ganha o métodonome(), que é exatamente o que a interface pede. - implements Usuario
- "Cumpre o contrato
Usuario." Se faltar um método da interface, o código não compila. - @Override public int limiteDeLivros()
- O método que faltava.
@Overrideavisa ao compilador que este método vem da interface; os métodos de interface são semprepublic.
import java.util.List;
public class Main {
public static void main(String[] args) {
Usuario bruno = new Aluno("Bruno");
Usuario carla = new Professor("Carla");
List<Usuario> usuarios = List.of(bruno, carla);
for (Usuario u : usuarios) {
System.out.println(u.nome() + " pode levar até " + u.limiteDeLivros() + " livro(s). " + u);
}
}
}
$ java Main.java Bruno pode levar até 2 livro(s). Aluno[nome=Bruno] Carla pode levar até 5 livro(s). Professor[nome=Carla]
Aqui está o polimorfismo ("muitas formas"): as variáveis bruno e carla são do tipo Usuario, e o laço trata os dois do mesmo jeito. Mas u.limiteDeLivros() dá 2 para um e 5 para o outro, porque cada objeto responde do seu jeito. A biblioteca nunca vai precisar de um if (u é aluno). Se um dia surgir o tipo "Funcionário", basta criar um record novo; nada mais muda.
Este código tem um erro de propósito.
$ java Main.java
Main.java:3: error: Usuario is abstract; cannot be instantiated
Usuario alguem = new Usuario();
^
1 error
error: compilation failed
"Usuario é abstrato; não pode ser instanciado." Uma interface é só o contrato, não dá para criar um objeto dela. Crie um Aluno ou um Professor e guarde numa variável do tipo Usuario.
extends?
Daria para fazer com uma classe abstrata Usuario e as subclasses Aluno extends Usuario e Professor extends Usuario (veja Classes Abstratas). Escolhemos interface + records porque aqui os usuários só carregam dados e uma regra: não há código para herdar. A herança aparece na etapa 5, entre as exceções.
Missão (etapa 2): o Main já usa new Professor("Carla"), mas o record Professor não existe. Crie-o, implementando Usuario, com limite de 5 livros.
import java.util.List;
public class Main {
public static void main(String[] args) {
Usuario bruno = new Aluno("Bruno");
Usuario carla = new Professor("Carla");
List<Usuario> usuarios = List.of(bruno, carla);
for (Usuario u : usuarios) {
System.out.println(u.nome() + " pode levar até " + u.limiteDeLivros() + " livro(s). " + u);
}
}
}
record Aluno(String nome) implements Usuario {
@Override
public int limiteDeLivros() {
return 2;
}
}
class Livro {
private final String codigo;
private final String titulo;
private final String autor;
private boolean disponivel;
public Livro(String codigo, String titulo, String autor) {
this.codigo = codigo;
this.titulo = titulo;
this.autor = autor;
this.disponivel = true;
}
public String getCodigo() {
return codigo;
}
public String getTitulo() {
return titulo;
}
public String getAutor() {
return autor;
}
public boolean isDisponivel() {
return disponivel;
}
public void emprestar() {
disponivel = false;
}
public void devolver() {
disponivel = true;
}
@Override
public String toString() {
String situacao = disponivel ? "disponível" : "emprestado";
return codigo + " | " + titulo + " | " + autor + " | " + situacao;
}
}
interface Usuario {
String nome();
int limiteDeLivros();
}
import java.util.List;
public class Main {
public static void main(String[] args) {
Usuario bruno = new Aluno("Bruno");
Usuario carla = new Professor("Carla");
List<Usuario> usuarios = List.of(bruno, carla);
for (Usuario u : usuarios) {
System.out.println(u.nome() + " pode levar até " + u.limiteDeLivros() + " livro(s). " + u);
}
}
}
record Aluno(String nome) implements Usuario {
@Override
public int limiteDeLivros() {
return 2;
}
}
class Livro {
private final String codigo;
private final String titulo;
private final String autor;
private boolean disponivel;
public Livro(String codigo, String titulo, String autor) {
this.codigo = codigo;
this.titulo = titulo;
this.autor = autor;
this.disponivel = true;
}
public String getCodigo() {
return codigo;
}
public String getTitulo() {
return titulo;
}
public String getAutor() {
return autor;
}
public boolean isDisponivel() {
return disponivel;
}
public void emprestar() {
disponivel = false;
}
public void devolver() {
disponivel = true;
}
@Override
public String toString() {
String situacao = disponivel ? "disponível" : "emprestado";
return codigo + " | " + titulo + " | " + autor + " | " + situacao;
}
}
record Professor(String nome) implements Usuario {
@Override
public int limiteDeLivros() {
return 5;
}
}
interface Usuario {
String nome();
int limiteDeLivros();
}
Etapa 3 · O acervo
Agora a classe que junta tudo: Biblioteca. Ela guarda os livros num HashMap, um "dicionário" em que cada chave (o código, "L1") leva a um valor (o livro). Achar um livro pelo código é instantâneo, sem percorrer lista nenhuma.
import java.util.HashMap;
import java.util.Map;
public class Biblioteca {
private final Map<String, Livro> livros = new HashMap<>();
public void cadastrar(Livro livro) {
livros.put(livro.getCodigo(), livro);
}
public Livro buscar(String codigo) {
Livro livro = livros.get(codigo);
if (livro == null) {
throw new IllegalArgumentException("Livro não encontrado: " + codigo);
}
return livro;
}
public void listarAcervo() {
System.out.println("--- Acervo (" + livros.size() + " livros) ---");
for (Livro livro : livros.values()) {
System.out.println(livro);
}
}
}
| Trecho | O que faz |
|---|---|
Map<String, Livro> livros | Um mapa com chave String e valor Livro. É private: só a biblioteca mexe no acervo. |
livros.put(livro.getCodigo(), livro) | Guarda o livro com o código como chave. Outro livro com o mesmo código substituiria o antigo. |
livros.get(codigo) | Devolve o livro daquela chave, ou null ("nada") se a chave não existe. |
throw new IllegalArgumentException(…) | Código inexistente é erro de quem chamou: avisamos na hora, com uma mensagem clara. |
livros.values() | Todos os livros do mapa, para o for-each. |
public class Main {
public static void main(String[] args) {
Biblioteca bib = new Biblioteca();
bib.cadastrar(new Livro("L1", "Dom Casmurro", "Machado de Assis"));
bib.cadastrar(new Livro("L2", "Vidas Secas", "Graciliano Ramos"));
bib.cadastrar(new Livro("L3", "O Cortiço", "Aluísio Azevedo"));
bib.cadastrar(new Livro("L4", "Iracema", "José de Alencar"));
bib.listarAcervo();
System.out.println("Busca L2: " + bib.buscar("L2").getTitulo());
System.out.println("Busca L9: " + bib.buscar("L9").getTitulo());
}
}
$ java Main.java --- Acervo (4 livros) --- L1 | Dom Casmurro | Machado de Assis | disponível L2 | Vidas Secas | Graciliano Ramos | disponível L3 | O Cortiço | Aluísio Azevedo | disponível L4 | Iracema | José de Alencar | disponível Busca L2: Vidas Secas Exception in thread "main" java.lang.IllegalArgumentException: Livro não encontrado: L9 at Biblioteca.buscar(Biblioteca.java:14) at Main.main(Main.java:11)
A busca por L9 parou o programa, e isso é bom: a mensagem diz exatamente o que houve. Na etapa 5, o Main vai capturar esse erro.
Os livros apareceram em ordem (L1, L2, L3, L4) por coincidência das chaves curtas. Um HashMap guarda as chaves na ordem que for melhor para ele. Se a ordem importa, use um TreeMap (ordem das chaves) ou um LinkedHashMap (ordem de inserção); veja TreeMap e TreeSet.
null
Se o buscar fosse só return livros.get(codigo);, o código L9 devolveria null, e o erro estouraria longe dali, no Main:
$ java Main.java --- Acervo (4 livros) --- L1 | Dom Casmurro | Machado de Assis | disponível L2 | Vidas Secas | Graciliano Ramos | disponível L3 | O Cortiço | Aluísio Azevedo | disponível L4 | Iracema | José de Alencar | disponível Busca L2: Vidas Secas Exception in thread "main" java.lang.NullPointerException: Cannot invoke "Livro.getTitulo()" because the return value of "Biblioteca.buscar(String)" is null at Main.main(Main.java:11)
"Não dá para chamar getTitulo() porque o valor devolvido por buscar é null." O Java até ajuda a achar a origem, mas a mensagem fala de null, não de "livro não encontrado". Conferir no lugar certo e lançar uma exceção com mensagem clara economiza horas de depuração.
Missão (etapa 3): rode: o programa termina com NullPointerException. Conserte o método buscar para lançar IllegalArgumentException com a mensagem Livro não encontrado: L9 quando o código não existir.
import java.util.HashMap;
import java.util.Map;
public class Main {
public static void main(String[] args) {
Biblioteca bib = new Biblioteca();
bib.cadastrar(new Livro("L1", "Dom Casmurro", "Machado de Assis"));
bib.cadastrar(new Livro("L2", "Vidas Secas", "Graciliano Ramos"));
bib.cadastrar(new Livro("L3", "O Cortiço", "Aluísio Azevedo"));
bib.cadastrar(new Livro("L4", "Iracema", "José de Alencar"));
bib.listarAcervo();
System.out.println("Busca L2: " + bib.buscar("L2").getTitulo());
System.out.println("Busca L9: " + bib.buscar("L9").getTitulo());
}
}
record Aluno(String nome) implements Usuario {
@Override
public int limiteDeLivros() {
return 2;
}
}
class Biblioteca {
private final Map<String, Livro> livros = new HashMap<>();
public void cadastrar(Livro livro) {
livros.put(livro.getCodigo(), livro);
}
public Livro buscar(String codigo) {
Livro livro = livros.get(codigo);
// se não achou, lance IllegalArgumentException
return livro;
}
public void listarAcervo() {
System.out.println("--- Acervo (" + livros.size() + " livros) ---");
for (Livro livro : livros.values()) {
System.out.println(livro);
}
}
}
class Livro {
private final String codigo;
private final String titulo;
private final String autor;
private boolean disponivel;
public Livro(String codigo, String titulo, String autor) {
this.codigo = codigo;
this.titulo = titulo;
this.autor = autor;
this.disponivel = true;
}
public String getCodigo() {
return codigo;
}
public String getTitulo() {
return titulo;
}
public String getAutor() {
return autor;
}
public boolean isDisponivel() {
return disponivel;
}
public void emprestar() {
disponivel = false;
}
public void devolver() {
disponivel = true;
}
@Override
public String toString() {
String situacao = disponivel ? "disponível" : "emprestado";
return codigo + " | " + titulo + " | " + autor + " | " + situacao;
}
}
record Professor(String nome) implements Usuario {
@Override
public int limiteDeLivros() {
return 5;
}
}
interface Usuario {
String nome();
int limiteDeLivros();
}
import java.util.HashMap;
import java.util.Map;
public class Main {
public static void main(String[] args) {
Biblioteca bib = new Biblioteca();
bib.cadastrar(new Livro("L1", "Dom Casmurro", "Machado de Assis"));
bib.cadastrar(new Livro("L2", "Vidas Secas", "Graciliano Ramos"));
bib.cadastrar(new Livro("L3", "O Cortiço", "Aluísio Azevedo"));
bib.cadastrar(new Livro("L4", "Iracema", "José de Alencar"));
bib.listarAcervo();
System.out.println("Busca L2: " + bib.buscar("L2").getTitulo());
System.out.println("Busca L9: " + bib.buscar("L9").getTitulo());
}
}
record Aluno(String nome) implements Usuario {
@Override
public int limiteDeLivros() {
return 2;
}
}
class Biblioteca {
private final Map<String, Livro> livros = new HashMap<>();
public void cadastrar(Livro livro) {
livros.put(livro.getCodigo(), livro);
}
public Livro buscar(String codigo) {
Livro livro = livros.get(codigo);
if (livro == null) {
throw new IllegalArgumentException("Livro não encontrado: " + codigo);
}
return livro;
}
public void listarAcervo() {
System.out.println("--- Acervo (" + livros.size() + " livros) ---");
for (Livro livro : livros.values()) {
System.out.println(livro);
}
}
}
class Livro {
private final String codigo;
private final String titulo;
private final String autor;
private boolean disponivel;
public Livro(String codigo, String titulo, String autor) {
this.codigo = codigo;
this.titulo = titulo;
this.autor = autor;
this.disponivel = true;
}
public String getCodigo() {
return codigo;
}
public String getTitulo() {
return titulo;
}
public String getAutor() {
return autor;
}
public boolean isDisponivel() {
return disponivel;
}
public void emprestar() {
disponivel = false;
}
public void devolver() {
disponivel = true;
}
@Override
public String toString() {
String situacao = disponivel ? "disponível" : "emprestado";
return codigo + " | " + titulo + " | " + autor + " | " + situacao;
}
}
record Professor(String nome) implements Usuario {
@Override
public int limiteDeLivros() {
return 5;
}
}
interface Usuario {
String nome();
int limiteDeLivros();
}
Etapa 4 · Os empréstimos
Um empréstimo liga um livro a um usuário. Ele não tem comportamento, só dados: é um record.
public record Emprestimo(Livro livro, Usuario usuario) {
}
A Biblioteca ganha uma lista de empréstimos e três métodos novos. Nesta etapa, quando uma regra impede o empréstimo, o método mostra o motivo e devolve false:
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class Biblioteca {
private final Map<String, Livro> livros = new HashMap<>();
private final List<Emprestimo> emprestimos = new ArrayList<>();
public void cadastrar(Livro livro) {
livros.put(livro.getCodigo(), livro);
}
public Livro buscar(String codigo) {
Livro livro = livros.get(codigo);
if (livro == null) {
throw new IllegalArgumentException("Livro não encontrado: " + codigo);
}
return livro;
}
public int quantosCom(Usuario usuario) {
int total = 0;
for (Emprestimo e : emprestimos) {
if (e.usuario().equals(usuario)) {
total++;
}
}
return total;
}
public boolean emprestar(String codigo, Usuario usuario) {
Livro livro = buscar(codigo);
if (!livro.isDisponivel()) {
System.out.println("Não deu: \"" + livro.getTitulo() + "\" já está emprestado.");
return false;
}
if (quantosCom(usuario) >= usuario.limiteDeLivros()) {
System.out.println("Não deu: " + usuario.nome() + " já está no limite de " + usuario.limiteDeLivros() + " livro(s).");
return false;
}
livro.emprestar();
emprestimos.add(new Emprestimo(livro, usuario));
System.out.println(usuario.nome() + " levou \"" + livro.getTitulo() + "\".");
return true;
}
public void devolver(String codigo) {
Livro livro = buscar(codigo);
for (Emprestimo e : emprestimos) {
if (e.livro() == livro) {
emprestimos.remove(e);
livro.devolver();
System.out.println(e.usuario().nome() + " devolveu \"" + livro.getTitulo() + "\".");
return;
}
}
System.out.println("\"" + livro.getTitulo() + "\" não estava emprestado.");
}
public void listarAcervo() {
System.out.println("--- Acervo (" + livros.size() + " livros) ---");
for (Livro livro : livros.values()) {
System.out.println(livro);
}
}
public void listarEmprestimos() {
System.out.println("--- Empréstimos (" + emprestimos.size() + ") ---");
for (Emprestimo e : emprestimos) {
System.out.println(e.livro().getTitulo() + " -> " + e.usuario().nome());
}
}
}
| Método | O que faz |
|---|---|
quantosCom(usuario) | Conta os empréstimos daquele usuário. e.usuario().equals(usuario) funciona porque records comparam pelo conteúdo: dois Aluno("Bruno") são iguais. |
emprestar(codigo, usuario) | Confere as duas regras (livro disponível, usuário abaixo do limite). Se passar, marca o livro e registra o empréstimo. |
devolver(codigo) | Procura o empréstimo daquele livro, remove da lista e devolve o livro. e.livro() == livro compara se é o mesmo objeto. O return logo depois do remove evita a ConcurrentModificationException (veja o projeto da agenda). |
listarEmprestimos() | Mostra quem está com o quê. |
public class Main {
public static void main(String[] args) {
Biblioteca bib = new Biblioteca();
bib.cadastrar(new Livro("L1", "Dom Casmurro", "Machado de Assis"));
bib.cadastrar(new Livro("L2", "Vidas Secas", "Graciliano Ramos"));
bib.cadastrar(new Livro("L3", "O Cortiço", "Aluísio Azevedo"));
bib.cadastrar(new Livro("L4", "Iracema", "José de Alencar"));
Usuario bruno = new Aluno("Bruno");
Usuario carla = new Professor("Carla");
bib.emprestar("L1", bruno);
bib.emprestar("L2", bruno);
bib.emprestar("L3", bruno);
bib.emprestar("L1", carla);
bib.devolver("L1");
bib.emprestar("L1", carla);
bib.listarEmprestimos();
bib.listarAcervo();
}
}
$ java Main.java Bruno levou "Dom Casmurro". Bruno levou "Vidas Secas". Não deu: Bruno já está no limite de 2 livro(s). Não deu: "Dom Casmurro" já está emprestado. Bruno devolveu "Dom Casmurro". Carla levou "Dom Casmurro". --- Empréstimos (2) --- Vidas Secas -> Bruno Dom Casmurro -> Carla --- Acervo (4 livros) --- L1 | Dom Casmurro | Machado de Assis | emprestado L2 | Vidas Secas | Graciliano Ramos | emprestado L3 | O Cortiço | Aluísio Azevedo | disponível L4 | Iracema | José de Alencar | disponível
Funciona, mas tem um defeito de projeto: a Biblioteca está escrevendo na tela. E se um dia a biblioteca for usada num site, ou num teste automático? Quem chamou emprestar deveria decidir o que fazer com a falha. E o boolean diz que falhou, mas não por quê. É o trabalho das exceções, na próxima etapa.
Missão (etapa 4): o emprestar confere se o livro está disponível, mas esqueceu o limite: o Bruno leva três livros. Acrescente a regra usando quantosCom(usuario) e usuario.limiteDeLivros(), mostrando Não deu: Bruno já está no limite de 2 livro(s).
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class Main {
public static void main(String[] args) {
Biblioteca bib = new Biblioteca();
bib.cadastrar(new Livro("L1", "Dom Casmurro", "Machado de Assis"));
bib.cadastrar(new Livro("L2", "Vidas Secas", "Graciliano Ramos"));
bib.cadastrar(new Livro("L3", "O Cortiço", "Aluísio Azevedo"));
bib.cadastrar(new Livro("L4", "Iracema", "José de Alencar"));
Usuario bruno = new Aluno("Bruno");
Usuario carla = new Professor("Carla");
bib.emprestar("L1", bruno);
bib.emprestar("L2", bruno);
bib.emprestar("L3", bruno);
bib.emprestar("L1", carla);
bib.devolver("L1");
bib.emprestar("L1", carla);
bib.listarEmprestimos();
bib.listarAcervo();
}
}
record Aluno(String nome) implements Usuario {
@Override
public int limiteDeLivros() {
return 2;
}
}
class Biblioteca {
private final Map<String, Livro> livros = new HashMap<>();
private final List<Emprestimo> emprestimos = new ArrayList<>();
public void cadastrar(Livro livro) {
livros.put(livro.getCodigo(), livro);
}
public Livro buscar(String codigo) {
Livro livro = livros.get(codigo);
if (livro == null) {
throw new IllegalArgumentException("Livro não encontrado: " + codigo);
}
return livro;
}
public int quantosCom(Usuario usuario) {
int total = 0;
for (Emprestimo e : emprestimos) {
if (e.usuario().equals(usuario)) {
total++;
}
}
return total;
}
public boolean emprestar(String codigo, Usuario usuario) {
Livro livro = buscar(codigo);
if (!livro.isDisponivel()) {
System.out.println("Não deu: \"" + livro.getTitulo() + "\" já está emprestado.");
return false;
}
// confira o limite do usuário (use quantosCom)
livro.emprestar();
emprestimos.add(new Emprestimo(livro, usuario));
System.out.println(usuario.nome() + " levou \"" + livro.getTitulo() + "\".");
return true;
}
public void devolver(String codigo) {
Livro livro = buscar(codigo);
for (Emprestimo e : emprestimos) {
if (e.livro() == livro) {
emprestimos.remove(e);
livro.devolver();
System.out.println(e.usuario().nome() + " devolveu \"" + livro.getTitulo() + "\".");
return;
}
}
System.out.println("\"" + livro.getTitulo() + "\" não estava emprestado.");
}
public void listarAcervo() {
System.out.println("--- Acervo (" + livros.size() + " livros) ---");
for (Livro livro : livros.values()) {
System.out.println(livro);
}
}
public void listarEmprestimos() {
System.out.println("--- Empréstimos (" + emprestimos.size() + ") ---");
for (Emprestimo e : emprestimos) {
System.out.println(e.livro().getTitulo() + " -> " + e.usuario().nome());
}
}
}
record Emprestimo(Livro livro, Usuario usuario) {
}
class Livro {
private final String codigo;
private final String titulo;
private final String autor;
private boolean disponivel;
public Livro(String codigo, String titulo, String autor) {
this.codigo = codigo;
this.titulo = titulo;
this.autor = autor;
this.disponivel = true;
}
public String getCodigo() {
return codigo;
}
public String getTitulo() {
return titulo;
}
public String getAutor() {
return autor;
}
public boolean isDisponivel() {
return disponivel;
}
public void emprestar() {
disponivel = false;
}
public void devolver() {
disponivel = true;
}
@Override
public String toString() {
String situacao = disponivel ? "disponível" : "emprestado";
return codigo + " | " + titulo + " | " + autor + " | " + situacao;
}
}
record Professor(String nome) implements Usuario {
@Override
public int limiteDeLivros() {
return 5;
}
}
interface Usuario {
String nome();
int limiteDeLivros();
}
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class Main {
public static void main(String[] args) {
Biblioteca bib = new Biblioteca();
bib.cadastrar(new Livro("L1", "Dom Casmurro", "Machado de Assis"));
bib.cadastrar(new Livro("L2", "Vidas Secas", "Graciliano Ramos"));
bib.cadastrar(new Livro("L3", "O Cortiço", "Aluísio Azevedo"));
bib.cadastrar(new Livro("L4", "Iracema", "José de Alencar"));
Usuario bruno = new Aluno("Bruno");
Usuario carla = new Professor("Carla");
bib.emprestar("L1", bruno);
bib.emprestar("L2", bruno);
bib.emprestar("L3", bruno);
bib.emprestar("L1", carla);
bib.devolver("L1");
bib.emprestar("L1", carla);
bib.listarEmprestimos();
bib.listarAcervo();
}
}
record Aluno(String nome) implements Usuario {
@Override
public int limiteDeLivros() {
return 2;
}
}
class Biblioteca {
private final Map<String, Livro> livros = new HashMap<>();
private final List<Emprestimo> emprestimos = new ArrayList<>();
public void cadastrar(Livro livro) {
livros.put(livro.getCodigo(), livro);
}
public Livro buscar(String codigo) {
Livro livro = livros.get(codigo);
if (livro == null) {
throw new IllegalArgumentException("Livro não encontrado: " + codigo);
}
return livro;
}
public int quantosCom(Usuario usuario) {
int total = 0;
for (Emprestimo e : emprestimos) {
if (e.usuario().equals(usuario)) {
total++;
}
}
return total;
}
public boolean emprestar(String codigo, Usuario usuario) {
Livro livro = buscar(codigo);
if (!livro.isDisponivel()) {
System.out.println("Não deu: \"" + livro.getTitulo() + "\" já está emprestado.");
return false;
}
if (quantosCom(usuario) >= usuario.limiteDeLivros()) {
System.out.println("Não deu: " + usuario.nome() + " já está no limite de " + usuario.limiteDeLivros() + " livro(s).");
return false;
}
livro.emprestar();
emprestimos.add(new Emprestimo(livro, usuario));
System.out.println(usuario.nome() + " levou \"" + livro.getTitulo() + "\".");
return true;
}
public void devolver(String codigo) {
Livro livro = buscar(codigo);
for (Emprestimo e : emprestimos) {
if (e.livro() == livro) {
emprestimos.remove(e);
livro.devolver();
System.out.println(e.usuario().nome() + " devolveu \"" + livro.getTitulo() + "\".");
return;
}
}
System.out.println("\"" + livro.getTitulo() + "\" não estava emprestado.");
}
public void listarAcervo() {
System.out.println("--- Acervo (" + livros.size() + " livros) ---");
for (Livro livro : livros.values()) {
System.out.println(livro);
}
}
public void listarEmprestimos() {
System.out.println("--- Empréstimos (" + emprestimos.size() + ") ---");
for (Emprestimo e : emprestimos) {
System.out.println(e.livro().getTitulo() + " -> " + e.usuario().nome());
}
}
}
record Emprestimo(Livro livro, Usuario usuario) {
}
class Livro {
private final String codigo;
private final String titulo;
private final String autor;
private boolean disponivel;
public Livro(String codigo, String titulo, String autor) {
this.codigo = codigo;
this.titulo = titulo;
this.autor = autor;
this.disponivel = true;
}
public String getCodigo() {
return codigo;
}
public String getTitulo() {
return titulo;
}
public String getAutor() {
return autor;
}
public boolean isDisponivel() {
return disponivel;
}
public void emprestar() {
disponivel = false;
}
public void devolver() {
disponivel = true;
}
@Override
public String toString() {
String situacao = disponivel ? "disponível" : "emprestado";
return codigo + " | " + titulo + " | " + autor + " | " + situacao;
}
}
record Professor(String nome) implements Usuario {
@Override
public int limiteDeLivros() {
return 5;
}
}
interface Usuario {
String nome();
int limiteDeLivros();
}
Etapa 5 · Exceções próprias
Vamos dar nome a cada problema. Uma exceção própria é só uma classe que estende Exception. Criamos uma "mãe", BibliotecaException, e duas "filhas", uma para cada regra:
public class BibliotecaException extends Exception {
public BibliotecaException(String mensagem) {
super(mensagem);
}
}
public class LivroIndisponivelException extends BibliotecaException {
public LivroIndisponivelException(Livro livro) {
super("\"" + livro.getTitulo() + "\" já está emprestado.");
}
}
public class LimiteDeEmprestimosException extends BibliotecaException {
public LimiteDeEmprestimosException(Usuario usuario) {
super(usuario.nome() + " já está no limite de " + usuario.limiteDeLivros() + " livro(s).");
}
}
- extends Exception
- A
BibliotecaExceptioné uma exceção checked: quem chama um método que a lança é obrigado a tratar ou repassar (checked × unchecked). - super(mensagem)
- Passa a mensagem para o construtor de
Exception; depois,e.getMessage()devolve esse texto (super). - extends BibliotecaException
- Herança: as duas filhas são também uma
BibliotecaException. Um únicocatch (BibliotecaException e)pega as duas. - LivroIndisponivelException(Livro livro)
- O construtor recebe o livro e monta a mensagem sozinho. Quem lança só escreve
throw new LivroIndisponivelException(this).
O Livro passa a defender a própria regra: emprestar um livro já emprestado lança a exceção.
public void emprestar() throws LivroIndisponivelException {
if (!disponivel) {
throw new LivroIndisponivelException(this);
}
disponivel = false;
}
E o emprestar da Biblioteca fica mais curto: nada de println, nada de boolean.
public void emprestar(String codigo, Usuario usuario) throws BibliotecaException {
Livro livro = buscar(codigo);
if (quantosCom(usuario) >= usuario.limiteDeLivros()) {
throw new LimiteDeEmprestimosException(usuario);
}
livro.emprestar();
emprestimos.add(new Emprestimo(livro, usuario));
}
| Palavra | Onde | Significado |
|---|---|---|
throw | dentro do método | "Lançar": cria e dispara a exceção agora. O método para nessa linha. |
throws | na assinatura | "Lança": avisa que o método pode lançar essa exceção. throws BibliotecaException cobre as duas filhas. |
livro.emprestar(); sem try | na Biblioteca | A LivroIndisponivelException do livro "atravessa" o método da biblioteca e chega a quem chamou. Isso é permitido porque ela é uma BibliotecaException, que está no throws. |
Quem decide o que fazer com a falha agora é o Main:
public class Main {
public static void main(String[] args) {
Biblioteca bib = new Biblioteca();
bib.cadastrar(new Livro("L1", "Dom Casmurro", "Machado de Assis"));
bib.cadastrar(new Livro("L2", "Vidas Secas", "Graciliano Ramos"));
bib.cadastrar(new Livro("L3", "O Cortiço", "Aluísio Azevedo"));
bib.cadastrar(new Livro("L4", "Iracema", "José de Alencar"));
Usuario bruno = new Aluno("Bruno");
Usuario carla = new Professor("Carla");
tentarEmprestar(bib, "L1", bruno);
tentarEmprestar(bib, "L2", bruno);
tentarEmprestar(bib, "L3", bruno);
tentarEmprestar(bib, "L1", carla);
bib.devolver("L1");
tentarEmprestar(bib, "L1", carla);
tentarEmprestar(bib, "L9", carla);
bib.listarEmprestimos();
bib.listarAcervo();
}
static void tentarEmprestar(Biblioteca bib, String codigo, Usuario usuario) {
try {
bib.emprestar(codigo, usuario);
System.out.println(usuario.nome() + " levou " + codigo + ".");
} catch (BibliotecaException e) {
System.out.println("Não deu: " + e.getMessage());
} catch (IllegalArgumentException e) {
System.out.println("Erro: " + e.getMessage());
}
}
}
O tentarEmprestar tem dois catch: o primeiro pega as regras da biblioteca (as duas filhas), o segundo pega o código inexistente (IllegalArgumentException, da etapa 3). A saída é a do começo da página:
$ java Main.java Bruno levou L1. Bruno levou L2. Não deu: Bruno já está no limite de 2 livro(s). Não deu: "Dom Casmurro" já está emprestado. Bruno devolveu "Dom Casmurro". Carla levou L1. Erro: Livro não encontrado: L9 --- Empréstimos (2) --- Vidas Secas -> Bruno Dom Casmurro -> Carla --- Acervo (4 livros) --- L1 | Dom Casmurro | Machado de Assis | emprestado L2 | Vidas Secas | Graciliano Ramos | emprestado L3 | O Cortiço | Aluísio Azevedo | disponível L4 | Iracema | José de Alencar | disponível
throws
Se o emprestar da Biblioteca não tiver throws BibliotecaException:
Este código tem um erro de propósito.
$ javac *.java
Biblioteca.java:35: error: unreported exception LimiteDeEmprestimosException; must be caught or declared to be thrown
throw new LimiteDeEmprestimosException(usuario);
^
Biblioteca.java:37: error: unreported exception LivroIndisponivelException; must be caught or declared to be thrown
livro.emprestar();
^
2 errors
O compilador encontrou duas exceções checked "sem dono": uma lançada ali mesmo (linha 35) e outra que pode vir do livro.emprestar() (linha 37). Ou o método trata com try/catch, ou declara com throws. Aqui, a biblioteca não sabe o que fazer com a falha, então declara e deixa o Main decidir.
Missão (etapa 5): está faltando a classe LimiteDeEmprestimosException (rode e veja o cannot find symbol). Crie-a estendendo BibliotecaException, com um construtor que recebe o Usuario e monta a mensagem Bruno já está no limite de 2 livro(s).
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class Main {
public static void main(String[] args) {
Biblioteca bib = new Biblioteca();
bib.cadastrar(new Livro("L1", "Dom Casmurro", "Machado de Assis"));
bib.cadastrar(new Livro("L2", "Vidas Secas", "Graciliano Ramos"));
bib.cadastrar(new Livro("L3", "O Cortiço", "Aluísio Azevedo"));
bib.cadastrar(new Livro("L4", "Iracema", "José de Alencar"));
Usuario bruno = new Aluno("Bruno");
Usuario carla = new Professor("Carla");
tentarEmprestar(bib, "L1", bruno);
tentarEmprestar(bib, "L2", bruno);
tentarEmprestar(bib, "L3", bruno);
tentarEmprestar(bib, "L1", carla);
bib.devolver("L1");
tentarEmprestar(bib, "L1", carla);
tentarEmprestar(bib, "L9", carla);
bib.listarEmprestimos();
bib.listarAcervo();
}
static void tentarEmprestar(Biblioteca bib, String codigo, Usuario usuario) {
try {
bib.emprestar(codigo, usuario);
System.out.println(usuario.nome() + " levou " + codigo + ".");
} catch (BibliotecaException e) {
System.out.println("Não deu: " + e.getMessage());
} catch (IllegalArgumentException e) {
System.out.println("Erro: " + e.getMessage());
}
}
}
record Aluno(String nome) implements Usuario {
@Override
public int limiteDeLivros() {
return 2;
}
}
class Biblioteca {
private final Map<String, Livro> livros = new HashMap<>();
private final List<Emprestimo> emprestimos = new ArrayList<>();
public void cadastrar(Livro livro) {
livros.put(livro.getCodigo(), livro);
}
public Livro buscar(String codigo) {
Livro livro = livros.get(codigo);
if (livro == null) {
throw new IllegalArgumentException("Livro não encontrado: " + codigo);
}
return livro;
}
public int quantosCom(Usuario usuario) {
int total = 0;
for (Emprestimo e : emprestimos) {
if (e.usuario().equals(usuario)) {
total++;
}
}
return total;
}
public void emprestar(String codigo, Usuario usuario) throws BibliotecaException {
Livro livro = buscar(codigo);
if (quantosCom(usuario) >= usuario.limiteDeLivros()) {
throw new LimiteDeEmprestimosException(usuario);
}
livro.emprestar();
emprestimos.add(new Emprestimo(livro, usuario));
}
public void devolver(String codigo) {
Livro livro = buscar(codigo);
for (Emprestimo e : emprestimos) {
if (e.livro() == livro) {
emprestimos.remove(e);
livro.devolver();
System.out.println(e.usuario().nome() + " devolveu \"" + livro.getTitulo() + "\".");
return;
}
}
System.out.println("\"" + livro.getTitulo() + "\" não estava emprestado.");
}
public void listarAcervo() {
System.out.println("--- Acervo (" + livros.size() + " livros) ---");
for (Livro livro : livros.values()) {
System.out.println(livro);
}
}
public void listarEmprestimos() {
System.out.println("--- Empréstimos (" + emprestimos.size() + ") ---");
for (Emprestimo e : emprestimos) {
System.out.println(e.livro().getTitulo() + " -> " + e.usuario().nome());
}
}
}
class BibliotecaException extends Exception {
public BibliotecaException(String mensagem) {
super(mensagem);
}
}
record Emprestimo(Livro livro, Usuario usuario) {
}
class Livro {
private final String codigo;
private final String titulo;
private final String autor;
private boolean disponivel;
public Livro(String codigo, String titulo, String autor) {
this.codigo = codigo;
this.titulo = titulo;
this.autor = autor;
this.disponivel = true;
}
public String getCodigo() {
return codigo;
}
public String getTitulo() {
return titulo;
}
public String getAutor() {
return autor;
}
public boolean isDisponivel() {
return disponivel;
}
public void emprestar() throws LivroIndisponivelException {
if (!disponivel) {
throw new LivroIndisponivelException(this);
}
disponivel = false;
}
public void devolver() {
disponivel = true;
}
@Override
public String toString() {
String situacao = disponivel ? "disponível" : "emprestado";
return codigo + " | " + titulo + " | " + autor + " | " + situacao;
}
}
class LivroIndisponivelException extends BibliotecaException {
public LivroIndisponivelException(Livro livro) {
super("\"" + livro.getTitulo() + "\" já está emprestado.");
}
}
record Professor(String nome) implements Usuario {
@Override
public int limiteDeLivros() {
return 5;
}
}
interface Usuario {
String nome();
int limiteDeLivros();
}
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class Main {
public static void main(String[] args) {
Biblioteca bib = new Biblioteca();
bib.cadastrar(new Livro("L1", "Dom Casmurro", "Machado de Assis"));
bib.cadastrar(new Livro("L2", "Vidas Secas", "Graciliano Ramos"));
bib.cadastrar(new Livro("L3", "O Cortiço", "Aluísio Azevedo"));
bib.cadastrar(new Livro("L4", "Iracema", "José de Alencar"));
Usuario bruno = new Aluno("Bruno");
Usuario carla = new Professor("Carla");
tentarEmprestar(bib, "L1", bruno);
tentarEmprestar(bib, "L2", bruno);
tentarEmprestar(bib, "L3", bruno);
tentarEmprestar(bib, "L1", carla);
bib.devolver("L1");
tentarEmprestar(bib, "L1", carla);
tentarEmprestar(bib, "L9", carla);
bib.listarEmprestimos();
bib.listarAcervo();
}
static void tentarEmprestar(Biblioteca bib, String codigo, Usuario usuario) {
try {
bib.emprestar(codigo, usuario);
System.out.println(usuario.nome() + " levou " + codigo + ".");
} catch (BibliotecaException e) {
System.out.println("Não deu: " + e.getMessage());
} catch (IllegalArgumentException e) {
System.out.println("Erro: " + e.getMessage());
}
}
}
record Aluno(String nome) implements Usuario {
@Override
public int limiteDeLivros() {
return 2;
}
}
class Biblioteca {
private final Map<String, Livro> livros = new HashMap<>();
private final List<Emprestimo> emprestimos = new ArrayList<>();
public void cadastrar(Livro livro) {
livros.put(livro.getCodigo(), livro);
}
public Livro buscar(String codigo) {
Livro livro = livros.get(codigo);
if (livro == null) {
throw new IllegalArgumentException("Livro não encontrado: " + codigo);
}
return livro;
}
public int quantosCom(Usuario usuario) {
int total = 0;
for (Emprestimo e : emprestimos) {
if (e.usuario().equals(usuario)) {
total++;
}
}
return total;
}
public void emprestar(String codigo, Usuario usuario) throws BibliotecaException {
Livro livro = buscar(codigo);
if (quantosCom(usuario) >= usuario.limiteDeLivros()) {
throw new LimiteDeEmprestimosException(usuario);
}
livro.emprestar();
emprestimos.add(new Emprestimo(livro, usuario));
}
public void devolver(String codigo) {
Livro livro = buscar(codigo);
for (Emprestimo e : emprestimos) {
if (e.livro() == livro) {
emprestimos.remove(e);
livro.devolver();
System.out.println(e.usuario().nome() + " devolveu \"" + livro.getTitulo() + "\".");
return;
}
}
System.out.println("\"" + livro.getTitulo() + "\" não estava emprestado.");
}
public void listarAcervo() {
System.out.println("--- Acervo (" + livros.size() + " livros) ---");
for (Livro livro : livros.values()) {
System.out.println(livro);
}
}
public void listarEmprestimos() {
System.out.println("--- Empréstimos (" + emprestimos.size() + ") ---");
for (Emprestimo e : emprestimos) {
System.out.println(e.livro().getTitulo() + " -> " + e.usuario().nome());
}
}
}
class BibliotecaException extends Exception {
public BibliotecaException(String mensagem) {
super(mensagem);
}
}
record Emprestimo(Livro livro, Usuario usuario) {
}
class LimiteDeEmprestimosException extends BibliotecaException {
public LimiteDeEmprestimosException(Usuario usuario) {
super(usuario.nome() + " já está no limite de " + usuario.limiteDeLivros() + " livro(s).");
}
}
class Livro {
private final String codigo;
private final String titulo;
private final String autor;
private boolean disponivel;
public Livro(String codigo, String titulo, String autor) {
this.codigo = codigo;
this.titulo = titulo;
this.autor = autor;
this.disponivel = true;
}
public String getCodigo() {
return codigo;
}
public String getTitulo() {
return titulo;
}
public String getAutor() {
return autor;
}
public boolean isDisponivel() {
return disponivel;
}
public void emprestar() throws LivroIndisponivelException {
if (!disponivel) {
throw new LivroIndisponivelException(this);
}
disponivel = false;
}
public void devolver() {
disponivel = true;
}
@Override
public String toString() {
String situacao = disponivel ? "disponível" : "emprestado";
return codigo + " | " + titulo + " | " + autor + " | " + situacao;
}
}
class LivroIndisponivelException extends BibliotecaException {
public LivroIndisponivelException(Livro livro) {
super("\"" + livro.getTitulo() + "\" já está emprestado.");
}
}
record Professor(String nome) implements Usuario {
@Override
public int limiteDeLivros() {
return 5;
}
}
interface Usuario {
String nome();
int limiteDeLivros();
}
O código completo
Os arquivos que ainda não apareceram inteiros nesta página, na versão final:
public class Livro {
private final String codigo;
private final String titulo;
private final String autor;
private boolean disponivel;
public Livro(String codigo, String titulo, String autor) {
this.codigo = codigo;
this.titulo = titulo;
this.autor = autor;
this.disponivel = true;
}
public String getCodigo() {
return codigo;
}
public String getTitulo() {
return titulo;
}
public String getAutor() {
return autor;
}
public boolean isDisponivel() {
return disponivel;
}
public void emprestar() throws LivroIndisponivelException {
if (!disponivel) {
throw new LivroIndisponivelException(this);
}
disponivel = false;
}
public void devolver() {
disponivel = true;
}
@Override
public String toString() {
String situacao = disponivel ? "disponível" : "emprestado";
return codigo + " | " + titulo + " | " + autor + " | " + situacao;
}
}
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class Biblioteca {
private final Map<String, Livro> livros = new HashMap<>();
private final List<Emprestimo> emprestimos = new ArrayList<>();
public void cadastrar(Livro livro) {
livros.put(livro.getCodigo(), livro);
}
public Livro buscar(String codigo) {
Livro livro = livros.get(codigo);
if (livro == null) {
throw new IllegalArgumentException("Livro não encontrado: " + codigo);
}
return livro;
}
public int quantosCom(Usuario usuario) {
int total = 0;
for (Emprestimo e : emprestimos) {
if (e.usuario().equals(usuario)) {
total++;
}
}
return total;
}
public void emprestar(String codigo, Usuario usuario) throws BibliotecaException {
Livro livro = buscar(codigo);
if (quantosCom(usuario) >= usuario.limiteDeLivros()) {
throw new LimiteDeEmprestimosException(usuario);
}
livro.emprestar();
emprestimos.add(new Emprestimo(livro, usuario));
}
public void devolver(String codigo) {
Livro livro = buscar(codigo);
for (Emprestimo e : emprestimos) {
if (e.livro() == livro) {
emprestimos.remove(e);
livro.devolver();
System.out.println(e.usuario().nome() + " devolveu \"" + livro.getTitulo() + "\".");
return;
}
}
System.out.println("\"" + livro.getTitulo() + "\" não estava emprestado.");
}
public void listarAcervo() {
System.out.println("--- Acervo (" + livros.size() + " livros) ---");
for (Livro livro : livros.values()) {
System.out.println(livro);
}
}
public void listarEmprestimos() {
System.out.println("--- Empréstimos (" + emprestimos.size() + ") ---");
for (Emprestimo e : emprestimos) {
System.out.println(e.livro().getTitulo() + " -> " + e.usuario().nome());
}
}
}
Os outros oito estão completos nas etapas: Usuario, Aluno e Professor (etapa 2), Emprestimo (etapa 4), as três exceções e o Main (etapa 5).
Desafios extras
- Devolver o que não foi emprestado. Troque o
printlndodevolverpor uma exceção nova,LivroNaoEmprestadoException. - Livros de um autor. Um método
livrosDe(String autor)que devolve umaList<Livro>. - Acervo em ordem. Troque o
HashMappor umTreeMape veja a listagem sair sempre ordenada pelo código. - Novo tipo de usuário. Crie
Visitante, que só pode levar 1 livro. Repare que nenhuma outra classe precisa mudar. - Prazo de devolução. Guarde a data no
Emprestimo(LocalDate, veja Datas e Horas) e um prazo por tipo de usuário.
São três passos: a classe LivroNaoEmprestadoException extends BibliotecaException com a mensagem "Iracema" não está emprestado.; no devolver, throw new LivroNaoEmprestadoException(livro) no lugar do println e throws LivroNaoEmprestadoException na assinatura; no Main, um try/catch em volta do bib.devolver(…). Teste com bib.devolver("L4"): a saída deve ter Não deu: "Iracema" não está emprestado.
Exercícios
1. Por que Livro não tem um método setDisponivel(boolean)?
2. O que livros.get("L9") devolve se não existe a chave "L9" no HashMap?
3. Por que um único catch (BibliotecaException e) pega as duas exceções da biblioteca?
4. Qual a diferença entre throw e throws?
Complete a declaração do record que implementa a interface:
Resumo
- Cada classe tem uma responsabilidade: o
Livrocuida da própria situação, aBibliotecaaplica as regras, oMainconversa com o mundo. - Atributos
private+ métodos com regras = encapsulamento; o compilador barra quem tenta pular. - Uma interface define o contrato; records podem implementá-la. Variáveis do tipo da interface dão polimorfismo.
HashMapacha pela chave;getdevolvenullquando não acha, então confira.- Exceções próprias dão nome aos problemas; herança entre elas permite tratar um grupo com um
catch. java Main.javacompila sozinho as outras classes da pasta (Java 22+);javac *.javacompila tudo.
Próximo projeto: Conta Bancária com Testes, em que o computador confere o seu código por você.