Problema JFrame com Singleton

2 respostas
J

É o seguinte galera , estou tentando implementar um jframe de login mas que abra apenas uma instancia dele, que quando ele ja estiver aberto o sistema envia uma mensagem de erro. Por isso estou tentando a implementaçao do padrao singleton , so que o codigo continua abrindo 2 aplicaçoes da mesma. o que posso estar errando? ou posso estar errando ao usar singleton??

Segue o codigo abaixo

package forms;

import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

import javax.swing.ImageIcon;
import javax.swing.JButton;

import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPasswordField;
import javax.swing.JTextField;
import javax.swing.SwingUtilities;
import javax.swing.UIManager;

public class loginFrame extends JFrame implements ActionListener {

	/**
	 * 
	 */
	private static final long serialVersionUID = 1L;

	private JButton jbtn_login, jbtn_cancelar;

	private JLabel jlb_usuario, jlb_senha;

	private JTextField jtxf_usuario;

	private JPasswordField jpsf_senha;

	private volatile static loginFrame uniqueInstance;;

	private loginFrame() {

		initComponents();
	}

	public static loginFrame getInstance() {

		synchronized (loginFrame.class) {
			if (uniqueInstance == null) {
				synchronized (loginFrame.class) {
					uniqueInstance = new loginFrame();
				}

			}
		}

		return uniqueInstance;
	}

	public void initComponents() {

		try {
			UIManager
					.setLookAndFeel("com.sun.java.swing.plaf.windows.WindowsLookAndFeel");
			SwingUtilities.updateComponentTreeUI(this);
		} catch (Exception e) {
			JOptionPane.showMessageDialog(this, "ERRO", "" + e,
					JOptionPane.ERROR_MESSAGE);
		}

		setTitle("...: ADM PACIENTES :... L O G I N");
		setIconImage(new ImageIcon("src/icons/paciente.gif").getImage());
		setSize(400, 200);
		setLayout(null);
		setDefaultCloseOperation(DO_NOTHING_ON_CLOSE);
		setLocationRelativeTo(null);
		setResizable(false);

		jbtn_login = new JButton("Login");
		jbtn_login.setSize(100, 30);
		jbtn_login.setLocation(70, 130);

		jbtn_login.setIcon(new ImageIcon("src/icons/login.png"));

		jbtn_login.addActionListener(this);

		add(jbtn_login);

		jbtn_cancelar = new JButton("Cancelar");
		jbtn_cancelar.setSize(100, 30);
		jbtn_cancelar.setLocation(230, 130);

		jbtn_cancelar.setIcon(new ImageIcon("src/icons/cancelar.png"));

		jbtn_cancelar.addActionListener(this);

		add(jbtn_cancelar);

		jlb_usuario = new JLabel("Usuário :");
		jlb_usuario.setSize(45, 25);
		jlb_usuario.setLocation(100, 30);

		add(jlb_usuario);

		jtxf_usuario = new JTextField("");
		jtxf_usuario.setSize(150, 20);
		jtxf_usuario.setLocation(150, 30);

		jtxf_usuario.setToolTipText("Digite o nome de usuário.");

		add(jtxf_usuario);

		jlb_senha = new JLabel("Senha :");
		jlb_senha.setSize(45, 25);
		jlb_senha.setLocation(100, 60);

		add(jlb_senha);

		jpsf_senha = new JPasswordField();
		jpsf_senha.setSize(150, 20);
		jpsf_senha.setLocation(150, 60);

		jpsf_senha.setToolTipText("Digite a senha.");

		add(jpsf_senha);

	}

	public void actionPerformed(ActionEvent e) {
		if (e.getSource() == jbtn_login) {
			new mainFrame().setVisible(true);
			dispose();
		}

		if (e.getSource() == jbtn_cancelar) {
			System.exit(0);
		}

	}

	public static void main(String[] args) {
		java.awt.EventQueue.invokeLater(new Runnable() {
			public void run() {

				loginFrame.getInstance().setVisible(true);
			}

		});
	}

}

Obrigado

2 Respostas

ViniGodoy

O Singleton impede você de abrir 2 JFrames dentro da mesma aplicação. Não duas vezes a aplicação.
Se você abrir duas vezes a aplicação, cada instância poderá ter seu próprio singleton.

Se você quer prevenir que a aplicação abra 2 vezes faça o seguinte:

  1. Crie um server socket, aberto numa porta exotérica qualquer (tipo 128912)
  2. Se o server não abrir, feche a aplicação.
ViniGodoy

Outra coisa. Tem um excesso de sincronização desnecessário no seu Singleton.

A forma mais correta e mais simples é inicializar o atributo diretamente, e getInstance() simplesmente retorna-lo:

private static loginFrame uniqueInstance;  

public static loginFrame getInstance() {     
   return uniqueInstance;  
}

A vantagem dessa forma é que ela dispensa inicialização e, como o mecanismo de carga de classes também tem lazyness, ela também só ocupará memória quando a classe for carregada.

Se você estiver numa VM diferente da que a Oracle/Sun implementa, e ter que implementar o lazy no braço, simplesmente sincronize o getInstance():
private volatile static loginFrame uniqueInstance;

   public static synchronized loginFrame getInstance() {  
      if (uniqueInstance == null) {  
         uniqueInstance = new loginFrame();  
      }  
      return uniqueInstance;  
   }

O que você fez não deixa seu código necessariamente errado, só mais rebuscado.

Se sua classe não tiver nenhum pai, faça o Singleton na forma de um enum, com um único elemento.

Finalmente, é válido lembrar que nomes de classe, segundo a convenção de código Java, começam por letra maiúscula (LoginFrame no lugar de loginFrame).

Criado 15 de março de 2011
Ultima resposta 16 de mar. de 2011
Respostas 2
Participantes 2