Escreva uma classe SerieLimitada, que encapsula um valor inteiro sequencial como os usados em notas e séries de gravuras. Esta classe deve permite que um programa crie um número limitado de instâncias dela, cada uma numerada com um valor sequencial. O número total de instâncias é controlado pelo campo máximoDeInstâncias, declarado como static final e o de instâncias já criadas é controlado pelo campo contador declarado como static. Escreva também um método main que cria algumas instâncias da classe para demonstrar o seu funcionamento.

Passo 1

O enunciado pede uma classe chamada SerieLimitada que gere um número sequencial para cada instância criada, com um limite máximo de instâncias permitido. Os pontos que o enunciado fornece e que precisamos respeitar são:
  • Deve existir um campo chamado maximoDeInstancias declarado como static final que controla o número total máximo de instâncias que a classe permite.
  • Deve existir um campo chamado contador declarado como static que conta quantas instâncias já foram criadas.
  • A cada nova instância válida deve ser atribuído um valor sequencial inteiro (por exemplo, 1, 2, 3, …).
  • Deve haver um main que cria algumas instâncias para demonstrar o funcionamento.
    Observações importantes que devemos considerar (mas sem resolver ainda): quando o limite for atingido, o construtor precisa impedir a criação de mais instâncias — podemos sinalizar isso lançando uma exceção ou usando outra estratégia; e os campos static significam que são compartilhados por todas as instâncias da classe.

Passo 2

Vamos decidir a abordagem prática (sem ainda mostrar o código linha a linha):
  • Usaremos public static final int maximoDeInstancias como a constante que fixa o limite. Escolherei um valor padrão (por exemplo, 5) no código de exemplo, e explicarei como alterar.
  • Usaremos private static int contador para contar quantas instâncias já foram criadas. Ele começa em 0.
  • Cada objeto terá um campo de instância private final int numero que guarda o número sequencial atribuído àquele objeto.
  • No construtor da classe, verificamos se contador já atingiu maximoDeInstancias. Se sim, lançamos uma IllegalStateException com uma mensagem clara. Caso contrário, incrementamos contador e atribuímos o valor a numero.
  • Mantemos métodos de acesso (getters) para obter o numero e o contador (este último como static) para fins de demonstração.
  • O main tentará criar maximoDeInstancias + 1 objetos para mostrar o comportamento quando o limite for ultrapassado; envolveremos cada criação em um try/catch para capturar a exceção e mostrar uma mensagem amigável.
Motivo da escolha: usar o próprio construtor para validar o limite é a forma mais direta e simples (não exige factory methods) e atende ao enunciado que falou em campos static e static final. Lançar exceção é uma maneira clara de falhar quando passado o limite — em prova isso é aceitável e demonstra controle de estado.

Passo 3

Aula rápida e prática sobre os conceitos usados — nada complicado, só o essencial pra você lembrar na prova:
  • Campos static: são compartilhados por todas as instâncias da classe. Se eu tenho static int contador, quando eu faço contador++ dentro do construtor, esse mesmo contador é visto por todas as instâncias. Serve para contagem global.
  • static final: é uma constante de classe. static final int maximoDeInstancias = 5; significa que maximoDeInstancias é um valor fixo e visível sem criar instância (acessível como SerieLimitada.maximoDeInstancias).
  • final em campo de instância: private final int numero; significa que cada objeto terá um numero imutável (atribuído uma vez no construtor).
  • Lançar exceção quando não for possível criar: usar throw new IllegalStateException("...") interrompe a criação do objeto e informa o motivo. Em main a gente captura essa exceção com try/catch para demonstrar.
  • Encapsulamento: manter os campos privados e expor getters ajuda a controlar acesso e seguir boas práticas (útil em provas também).
    Exemplo mental simples: pense que cada vez que constrói um objeto você pega a próxima etiqueta numerada (1,2,3…). Quando as etiquetas acabam (maximoDeInstancias), você não pode numerar mais.

Passo 4

Explicação passo a passo do construtor e das operações que vamos codificar (aqui mostro a lógica que você poderia escrever na prova):
  1. No início do construtor, verificamos if (contador >= maximoDeInstancias):
    • Se verdadeiro, fazemos throw new IllegalStateException("Limite de instâncias (" + maximoDeInstancias + ") atingido");.
    • Isso garante que, ao tentar criar a (maximo+1)-ésima instância, a criação falha.
  2. Se não atingiu o limite, incrementamos contador com contador++.
  3. Atribuímos this.numero = contador; — assim o primeiro objeto recebe 1, o segundo 2, e assim por diante.
  4. Mantemos getNumero() para retornar o número da instância e getContador() (estático) para retornar quantas instâncias já existem (útil no main para demonstrar).
  5. No main, usamos um laço que tenta criar maximoDeInstancias + 1 objetos. Cada tentativa é envolvida num try e, em catch (IllegalStateException e), imprimimos uma mensagem explicando que o limite foi alcançado.

Passo 5

Dicas práticas para a prova e variações que podem aparecer:
  • Se na prova pedirem que o limite seja configurável em tempo de execução, então maximoDeInstancias não poderia ser final. Como o enunciado exige static final, assumimos que é uma constante conhecida em tempo de compilação.
  • Se quiser tornar a criação thread-safe, seria preciso sincronizar a região que testa e incrementa contador. Ex.: sincronizar no bloco synchronized(SerieLimitada.class) { ... }. Aqui vou manter simples, pois a maioria das questões de prova não exige concorrência.
  • Ao testar localmente, você verá que as primeiras maximoDeInstancias criações funcionam e a tentativa seguinte gera a exceção. Isso demonstra que o contador e o limite estão funcionando como desejado.

Passo 6

Agora a implementação final (vou colocá-la no campo de resposta). Nesta implementação:
  • maximoDeInstancias e contador têm os nomes exigidos pelo enunciado.
  • O construtor faz a validação e atribuição do número sequencial.
  • O main demonstra a criação de maximoDeInstancias + 1 objetos e captura a exceção quando o limite é excedido.
  • Você pode alterar o valor de maximoDeInstancias para testar com outros limites.

Resposta

public class SerieLimitada {
public static final int maximoDeInstancias = 5;
private static int contador = 0;
private final int numero;
public SerieLimitada() {
    if (contador >= maximoDeInstancias) {
        throw new IllegalStateException("Limite de instâncias (" + maximoDeInstancias + ") atingido");
    }
    contador++;
    this.numero = contador;
}

public int getNumero() {
    return numero;
}

public static int getContador() {
    return contador;
}

@Override
public String toString() {
    return "SerieLimitada#" + numero;
}

public static void main(String[] args) {
    System.out.println("Tentando criar " + (maximoDeInstancias + 1) + " instâncias:");
    for (int i = 0; i < maximoDeInstancias + 1; i++) {
        try {
            SerieLimitada s = new SerieLimitada();
            System.out.println("Criada: " + s + " (numero=" + s.getNumero() + ", contador=" + SerieLimitada.getContador() + ")");
        } catch (IllegalStateException e) {
            System.out.println("Falha ao criar instância " + (i + 1) + ": " + e.getMessage());
        }
    }
}
}

Exercícios relacionados