Curso Java Experto
Clase 3
Los usuarios esperan que los programas se comporten de forma sensata cuando se producen errores. Si no es posible terminar una operación por causa de error el programa debería:
La jerarquía se divide en dos ramas: Error y Exception
Regla general: "Si es una RuntimeException, es culpa tuya".
Ejemplos de causas de RuntimeException:
Ejemplos de causas de excepciones que no heredan de RuntimeException:
El compilador verifica que nosotros proporcionemos manejadores de excepciones para todas las excepciones comprobadas.
Cuáles excepciones debemos manejar?
Para capturar una excepción, se configura un bloque del tipo try/catch. La forma más sencilla del bloque try es la siguiente:
try
{
...
}
catch (TipoDeExcepcion e)
{
// manejador para este tipo de excepcion
}Si cualquier parte del código situado dentro del bloque try lanza una excepción de la clase especificada en el catch entonces:
Si no hay parte del código situado dentro del bloque try que lance una excepción, entonces el programa salta la cláusula catch.
Si alguna parte del código lanza una excepción de tipo distinto a la mencionada en el catch este método finaliza inmediatamente.
public void read(String nombrefichero)
{
try
{
InputStream in = new FileInputStream(nombrefichero);
int b:
while ((b = in.read()) !+ -1)
{
//procesar entrada
}
}
catch(IOException e)
{
e.printStackTrace();
}
}Ejemplo
Con frecuencia, la mejor opción consiste en no hacer nada y limitarse a pasar la excepción a quien hiciera la llamada. Si se produce este error que se ocupe quien hizo la llamada al método.
public void read(String nombrefichero) throws IOException
{
InputStream in = new FileInputStream(nombrefichero);
int b:
while ((b = in.read()) !+ -1)
{
//procesar entrada
}
}Debemos capturar las excepciones que debemos manejar y notificar la existencia de aquellas que no sepamos manejar.
Cuando se notifica la existencia de una excepción es preciso añadir el throws.
if(!s.empty()) s.pop();try
{
s.pop();
}
catch(EmptyStackException e)
{
}Las excepciones solo deben utilizarse para situaciones excepcionales.
OutputStream out;
Stack s;
for(i=0; i<100;i++)
{
try
{
n=s.pop();
}
catch (EmptyStackException s)
{
//la pila esta vacía
}
try
{
out.writeInt(n);
}
catch(IOException e)
{
//hay un problema al escribir el archivo
}
}OutputStream out;
Stack s;
try
{
for(i=0; i<100;i++)
{
n=s.pop();
out.writeInt(n);
}
}
catch (EmptyStackException s)
{
//la pila esta vacía
}
catch(IOException e)
{
//hay un problema al escribir el archivo
}public Image cargarImagen(String s)
{
try
{
//código que amenaza con lanzar excepciones comprobadas
}
catch(Exception e)
{
}//y arreglado ?????
}Los métodos de nivel superior suelen estar mejor equipados para notificar al usuario la existencia de errores o para abandonar órdenes que no han tenido éxito.
public void readStuff(String nombreFichero) throws IOException //esto es válido
{
InputStream in = new FileInputStream(nombreFichero);
...
}De forma general muchos desarrolladores se enfrentan en la situación de implementar una interfaz que sólo tiene uno o dos métodos, y que dichos métodos poseen un par de lineas de código. Esta situación es muy frecuente cuando trabajamos con AWT y Swing, donde los componentes necesitan manejar un determinado evento.
Para esto las clases Anónimas son muy útiles ya que nos permiten crear instancias que son usadas una sola vez. Normalmente no puedes instanciar directamente una interfaz, por ejemplo, no puedes llamar al constructor de la interfaz Runnable de la forma:
Runnable runner = new Runnable(); //Error
Pero java nos permite crear una instancia de una clase anónima local que pueda implementar la interfaz, lo siguiente si se puede hacer :
Runnable runner = new Runnable() {
public void run() {
for (int i=0; i<10000000; i++) {
contarOvejas();
}
}
};
objetoFuenteDeEventos.addEventListener(objetoOyenteDeEventos):
ActionListener oyente = ...;
JButton boton = new JButtton("OK");
boton.addActionListener(oyente);Ahora el oyente recibirá una notificación siempre que se produzca un "Evento acción". Para los botones un evento acción sería un click.
class MiOyente implements ActionListener {
public void actionPerformed(ActionEvent evento) {
//reacción ante un click
}
}JButton(Icon icon)
//Creates a button with an icon.
JButton(String text)
//Creates a button with text.
JButton(String text, Icon icon)
//Creates a button with initial text and an icon.JButton botonAmarillo = new JButton("Amarillo";
JButton botonAzul = new JButton(new ImageIcon("blue-ball.gif");class LaminaBotones extends JPanel {
public LaminaBotones(){
JButton botonAmarillo = new JButton("Amarillo");
JButton botonAzul = new JButton("Azul");
JButton botonRojo = new JButton("Rojo");
add(botonAmarillo);
add(botonAzul);
add(botonRojo);
}
}
public void actionPerformed(ActionEvent e)
Invoked when an action occurs.
class AccionColor implements ActionListener{
private Color colorDeFondo;
public AccionColor(Color c){
colorDeFondo = c;
}
@Override
public void actionPerformed(ActionEvent e) {
//se especifica el color de fondo de la lamina
...
}
}AccionColor accionAmarillo = new AccionColor(Color.YELLOW);
AccionColor accionAzul = new AccionColor(Color.BLUE);
AccionColor accionRojo = new AccionColor(Color.RED);
botonAmarillo.addActionListener(accionAmarillo);
botonAzul.addActionListener(accionAzul);
botonRojo.addActionListener(accionRojo);Hacer que AccionColor sea una clase interna de la clase LaminaBotones. Entonces sus métodos podrán acceder a la lámina externa automáticamente.
class LaminaBotones extends JPanel {
class AccionColor implements ActionListener{
private Color colorDeFondo;
public AccionColor(Color c){
colorDeFondo = c;
}
@Override
public void actionPerformed(ActionEvent e) {
//se especifica el color de fondo de la lamina
setBackground(colorDeFondo);
}
}
}Utilizar método auxiliar
void hacerBoton(String nombre, Color colorDeFondo) {
JButton boton = new JButton(nombre);
add(boton);
AccionColor accion = new AccionColor(colorDeFondo);
boton.addActionListener(accion);
}Entonces el constructor de LaminaBotones quedaría
public LaminaBotonesS1() {
hacerBoton("Amarillo", Color.YELLOW);
hacerBoton("Azul", Color.BLUE);
hacerBoton("Rojo", Color.RED);
}Observar que la clase AccionColor se usa en un solo lugar, por lo tanto podemos convertirla en una clase anónima.
void hacerBoton(String nombre, Color colorDeFondo) {
//Creamos boton
JButton boton = new JButton(nombre);
//Añadimos botón a lámina
add(boton);
//Añadimos listener
boton.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
// se especifica el color de fondo de la lamina
setBackground(colorDeFondo);
}
});
}botonCargar.addActionListener(
(ActionListener) EventHandler.create(ActionListener.class, marco, "cargarDatos"));botonCargar.addActionListener(new
ActionListener() {
public void actionPerformed(ActionEvent evento) {
marco.cargarDatos();
}
});Tenemos la libertad de hacer que cualquier objeto de una clase que implemente la interfaz ActionListener se el oyente de un botón.
class LaminaBotonesSCI extends JPanel implements ActionListener {
private JButton botonAmarillo;
private JButton botonAzul;
private JButton botonRojo;
public LaminaBotonesSCI() {
botonAmarillo = new JButton("Amarillo");
botonAzul = new JButton("Azul");
botonRojo = new JButton("Rojo");
botonAmarillo.addActionListener(this);
botonAzul.addActionListener(this);
botonRojo.addActionListener(this);
add(botonAmarillo);
add(botonAzul);
add(botonRojo);
}
@Override
public void actionPerformed(ActionEvent e) {
// averiguo origen del evento
Object origen = e.getSource();
// se especifica el color de fondo de la lamina
if (origen == botonAmarillo)
setBackground(Color.YELLOW);
else if (origen == botonAzul)
setBackground(Color.BLUE);
else if (origen == botonRojo)
setBackground(Color.RED);
}
}WindowListener oyente = ...;
marco.addListener(oyente);
void
|
windowActivated(WindowEvent e)
Invoked when the Window is set to be the active Window.
|
void
|
windowClosed(WindowEvent e)
Invoked when a window has been closed as the result of calling dispose on the window.
|
void
|
windowClosing(WindowEvent e)
Invoked when the user attempts to close the window from the window's system menu.
|
void
|
windowDeactivated(WindowEvent e)
Invoked when a Window is no longer the active Window.
|
void
|
windowDeiconified(WindowEvent e)
Invoked when a window is changed from a minimized to a normal state.
|
void
|
windowIconified(WindowEvent e)
Invoked when a window is changed from a normal to a minimized state.
|
void
|
windowOpened(WindowEvent e)
Invoked the first time a window is made visible.
|
Todas las interfaces de oyente de AWT que tienen más de un método se ofrecen junto a una clase adaptador asociada que implementa todos los métodos de la interfaz pero no hace nada con ellos. Entonces si queremos implementar solo un método en nuestro oyente:
class Terminador extends WindowAdapter {
public void windowClosing(WindowEvent e) {
System.exit(0);
}
}
class Terminador implements WindowListener {
@Override
public void windowOpened(WindowEvent e) {}
@Override
public void windowClosing(WindowEvent e) { System.exit(0);}
@Override
public void windowClosed(WindowEvent e) {}
@Override
public void windowIconified(WindowEvent e) {}
@Override
public void windowDeiconified(WindowEvent e) {}
@Override
public void windowActivated(WindowEvent e) {}
@Override
public void windowDeactivated(WindowEvent e) {}
}En vez de crear una clase Terminador utilizar clase anónima.
marco.addWindowListener(new WindowAdapter() {
@Override
public void windowClosing(WindowEvent e) {
System.exit(0);
};
});Este código hace lo siguiente:
Otro oyente que trabaja sobre ventanas.
Métodos:
void WindowStateChanged(WindowEvent evento)
se invoca después de que la ventana de ha maximizado, minimizado o devuelto a su tamaño normal.
Proporcionan el estado nuevo y viejo de una ventana en los eventos de cambio de estado de ventana. El entero proporcionado es uno de los valores siguientes:
Uso frecuente
Expresan lo que esta haciendo el usuario, por ejemplo hacer click en un botón. Por lo tanto ActionEvent es un evento semántico.
Aquellos que hacen posible los eventos semánticos. En el caso de un click, esto es una pulsación del ratón, una serie de movimientos del ratón o bien podría ser una pulsación de una tecla.
Eventos
Clases de Uso Frecuente
| void |
keyPressed(KeyEvent e)
Invoked when a key has been pressed. |
| void |
keyReleased(KeyEvent e)
Invoked when a key has been released. |
| void |
keyTyped(KeyEvent e)
Invoked when a key has been typed. |
Refiere al carácter A, B, C, etc. No a las teclas en sí.
Método keyTyped notifica carácter.
Se indican con el prefijo VK_, por ejemplo VK_A o VK_MAYUS.
Se corresponden con teclas del teclado por ejemplo VK_A corresponde a la tecla A.
No se hace distinción entre mayúsculas y minúsculas.
Métodos keyPressed y keyReleased notifican teclas.
Con los métodos keyPressed y keyReleased es preciso verificar los códigos de letras. Las constates están definidas en la clase KeyEvent.
public void keyPressed(KeyEvent evento)
{
int codigoDeTecla = evento.getKeyCode();
...
}Con el método keyTyped llamamos al método getKeyChar para obtener el carácter real que se ha pulsado.
Para comprobar el estado de las teclas MAYUS, CONTROL, ALT podemos usar los métodos isShiftDown, isControlDown, isAltDown.
public void keyPressed(KeyEvent evento)
{
int codigoDeTecla = evento.getKeyCode();
if(codigoDeTecla == KeyEvent.VK_RIGHT && evento.isShiftDown()) {
...
}
}Una acción es un objeto que encapsula:
void actionPerformed(ActionEvent evento)
void setEnabled(boolean b)
boolean isEnabled()
void putValue(String clave, Object valor)
Object getValue(String clave)
void addPropertyChangeListener(PropertyChangeListener oyente)
void removePropertyChangeListener(PropertyChangeListener oyente)
Modifier and TypeField and Description
| static String |
ACCELERATOR_KEY
The key used for storing a KeyStroke to be used as the accelerator for the action. |
| static String |
ACTION_COMMAND_KEY
The key used to determine the command String for the ActionEvent that will be created when an Action is going to be notified as the result of residing in a Keymap associated with aJComponent. |
| static String |
DEFAULT
Not currently used. |
| static String |
DISPLAYED_MNEMONIC_INDEX_KEY
The key used for storing an Integer that corresponds to the index in the text (identified by theNAME property) that the decoration for a mnemonic should be rendered at. |
| static String |
LARGE_ICON_KEY
The key used for storing an Icon. |
| static String |
LONG_DESCRIPTION
The key used for storing a longer String description for the action, could be used for context-sensitive help. |
| static String |
MNEMONIC_KEY
The key used for storing an Integer that corresponds to one of the KeyEvent key codes. |
| static String |
NAME
The key used for storing the String name for the action, used for a menu or button. |
| static String |
SELECTED_KEY
The key used for storing a Boolean that corresponds to the selected state. |
| static String |
SHORT_DESCRIPTION
The key used for storing a short String description for the action, used for tooltip text. |
| static String |
SMALL_ICON
The key used for storing a small Icon, such as ImageIcon. |
Construimos un objeto tipo Action que pueda ejecutar ordenes de cambio de color. Almacenaremos el nombre de la orden, un ícono y el color deseado.
class AccionColor extends AbstractAction {
public AccionColor(String nombre, Icon icon, Color c) {
putValue(Action.NAME, nombre);
putValue(Action.SMALL_ICON, icon);
putValue(Action.SHORT_DESCRIPTION, "Poner la lamina de color: " + nombre.toLowerCase());
putValue("color", c);
}
@Override
public void actionPerformed(ActionEvent e) {
// obtengo color
Color colorDeFondo = (Color) getValue("color");
// se especifica el color de fondo de la lamina
setBackground(colorDeFondo);
}
}
Creamos tres objetos de esta clase de la siguiente forma:
Action actionAmarillo = new AccionColor("Amarillo", new ImageIcon("src/circuloAmarillo.png"), Color.YELLOW);
Action actionAzul = new AccionColor("Azul", new ImageIcon("src/circuloAzul.png"), Color.BLUE);
Action actionRojo = new AccionColor("Rojo", new ImageIcon("src/circuloRojo.png"), Color.RED);Asociamos estas acciones utilizando el constructor de tipo JButton que admite un objeto tipo Action. Este constructor lee el nombre y el icono a partir de la acción y fija esa acción como oyente.
JButton botonAmarillo = new JButton(actionAmarillo);
JButton botonAzul = new JButton(actionAzul);
JButton botonRojo = new JButton(actionRojo);Los atajos de teclado se le entregan al objeto que tiene el foco. Hay 3 botones en el ejemplo. Solución utilización de mapas de entrada.
| Indicador | Se invoca la acción |
|---|---|
| WHEN_FOCUSED | Cuando este componente tiene el foco del teclado |
| WHEN_ANCESTOR_OF_FOCUSED_COMPONENT | Cuando este componente contiene el componente que tiene el foco del teclado. |
| WHEN_IN_FOCUSED_WINDOW | Cuando este componente está alojado en la misma ventana que el componente que tiene el componente que tiene el foco del teclado. |
Obteniendo mapa
InputMap mapEnt = lamina.getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT);Asociando los atajos de teclado
Primero es necesario generar objetos de la clase KeyStroke. Esta es una clase de conveniencia que encapsula la descripción de una tecla. Utilizamos el método estático getKeyStroke.
KeyStroke teclaCtrlB = KeyStroke.getKeyStroke("ctrl B");Creamos una clave para el objeto acción y añadimos la pareja combinación de teclas y clave de acción al mapa de entrada.
mapEnt.put(KeyStroke.getKeyStroke("ctrl Y"), "lamina.yellow");
mapEnt.put(KeyStroke.getKeyStroke("ctrl B"), "lamina.blue");
mapEnt.put(KeyStroke.getKeyStroke("ctrl R"), "lamina.red");Conseguimos el mapa de acción del componente de nivel superior (lámina) y añadimos la pareja de la clave de acción creada y el objeto de acción a este mapa.
ActionMap mapaAccion = getActionMap();
mapaAccion.put("lamina.yellow", actionAmarillo);
mapaAccion.put("lamina.blue", actionAzul);
mapaAccion.put("lamina.red", actionRojo);