StackOverflowError getBounds(Native Method)

Boa tarde, em primeiro lugar sei que Swing é uma ferramenta antiga, mas quero saber como funciona. E acho que minha dúvida é mais de POO

Estou tentando fazer um JButton trocar o JPanel de um JFrame.
Tenho uma classe Handler que implementa a ActionListener e nela eu instancio um dos JPanels (acho que esse é o problema)
Estou recebendo o StackOverflowError, porque acho que a classe fica sendo instanciada várias vezes e que estou adicionando o pnlCad em um objeto diferente do original.

Qual seria uma solução pra isso? Sou iniciante e me perdi.

Tela:

import java.awt.Dimension;
import java.awt.HeadlessException;
import javax.swing.JFrame;

public class Tela extends JFrame {
    
    private PainelInicial pnlIni;
    private PainelCadastro pnlCad;
    private PainelDados pnlDados;
   //private Container containerTela;
    
    public Tela() throws HeadlessException {
        initTela();
       
        pnlIni = new PainelInicial();
        pnlCad = new PainelCadastro();
        pnlDados = new PainelDados();
        
        getContentPane().add(pnlIni);
        
        pack();
        setVisible(true);    
    }
    
  //  private Container getContPane(){
  //      containerTela = new Container();
  //      containerTela = this.getContentPane();
  //      return containerTela;
  //  }
    private void initTela(){
        setDefaultCloseOperation(EXIT_ON_CLOSE);
        setPreferredSize(new Dimension(500, 580));
        setResizable(false);
        setLocation(490, 140);   
   
        
    
    }
    
    public void setPainelInicial(){
        this.setTitle("Início");
        this.setContentPane(pnlIni);
    }
    
    public void setPainelCadastro(){
        this.setTitle("Cadastro");
        this.setContentPane(pnlCad);
    }
    
    public void setPainelDados(){
        this.setTitle("Página de "); //TODO pegar nome do user logado
        this.setContentPane(pnlDados);
    }
}

Handler:

package view;

import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;

public class ButtonHandler implements ActionListener {

    private Tela tela;
    private JButton btCad;
    private PainelCadastro pnlCad;
    
    public ButtonHandler(JButton btCad) {
        this.btCad = btCad;
        this.tela = new Tela();
        tela.setContentPane(pnlCad);
    }
    
    
    
    @Override
    public void actionPerformed(ActionEvent e) {
        if(e.getSource() == btCad){
            tela.setContentPane(pnlCad);
            tela.pack();
            tela.setVisible(true);
        }
    }
    
}

fica dificil de saber sem olhar a stack trace do problema.

run:
Exception in thread “main” java.lang.StackOverflowError
at sun.awt.Win32GraphicsConfig.getBounds(Native Method)
at sun.awt.Win32GraphicsConfig.getBounds(Win32GraphicsConfig.java:222)
at java.awt.Window.init(Window.java:505)
at java.awt.Window.(Window.java:537)
at java.awt.Frame.(Frame.java:420)
at java.awt.Frame.(Frame.java:385)
at javax.swing.JFrame.

stack.txt (52.2 KB)

	at view.Tela.<init>(Tela.java:14)
	at view.ButtonHandler.<init>(ButtonHandler.java:15)
	at view.PainelInicial.setCorpoNoroeste(PainelInicial.java:56)
	at view.PainelInicial.<init>(PainelInicial.java:37)
	at view.Tela.<init>(Tela.java:17)

No construtor de Tela, você instancia um PainelInicial, que chama o método setCorpoNoroeste, que instancia um ButtonHandler que vai instanciar novamente a classe Tela, sendo que ela ainda está em processo de instanciação.

Importante: parece óbvio, mas a instrução new tem esse nome pois ela cria uma nova instância, dê uma avaliada se você realmente que ter uma nova instância de Tela nessa classe ButtonHandler

Sim, esse é meu problema. O que eu não sei é como posso usar o mesmo objeto já instanciado na classe ButtonHandler.

Se você não possui uma classe controladora, o mais simples é criar uma classe que forneça suas telas instanciando-as uma única vez. Aí basta você usar esta classe para obter as instâncias das telas que precisa.

Exemplo de classe que fornece as telas:

public class Telas {

    public static Telas get() {
        return Holder.INSTANCE;
    }

    private static final class Holder {

        private static final Telas INSTANCE = new Telas();
    }

    private Tela1 tela1;

    private Telas() {}

    public Tela1 tela1() {
        if (tela1 == null) {
            tela1 = new Tela1();
        }
        return tela1;
    }
}

Exemplo de uso:

Tela1 minhaTela = Telas.get().tela1();

Era exatamente o que eu precisava.

Caso mais alguém caia aqui com o mesmo problema, é bom pesquisar sobre o padrão Singleton também.

Obrigada