Oracle_One-Alura_Latam/008_java_oo/excepciones.md

12 KiB

Lanzar y controlar Excepciones

Pila de ejecución

Ejemplo visual del flujo de ejecución

public class Flujo {

    public static void main(String[] args) {
        System.out.println("Inicio Main");
        metodo1();
        System.out.println("Fin Main");
    }

    public static void metodo1(){
        System.out.println("Inicio Método1");
        metodo2();
        System.out.println("Fin Método1");
    }

    public static void metodo2(){
        System.out.println("Inicio Método2");
        for(int i =1; i<= 5; i++){
            System.out.println("i:"+i);
        }
        System.out.println("Fin Método2");
    }
}

Punto de interrupción breakpoint en el IDE

img

Excepciones

Induciendo error en metodo2(), clase Flujo.java

    public static void metodo2(){
        System.out.println("Inicio Método2");
        for(int i =1; i<= 5; i++){
            System.out.println("i:"+i);
        }
        System.out.println("Fin Método2");
    }
Exception in thread "main" java.lang.ArithmeticException: / by zero  <- Tipo de Excepción, donde/cuando
    at pila_ejecucion.Flujo.metodo2(Flujo.java:19)                   <- último elemento de la pila
    at pila_ejecucion.Flujo.metodo1(Flujo.java:12)                   <- segundo elemento de la pila
    at pila_ejecucion.Flujo.main(Flujo.java:7)                       <- primer elemento de la pila
  • Donde y Tipo de Excepción se produce
  • Rastro, traceback de los elementos en la pila de ejecución

Manejo de excepciones

    public static void metodo2(){
        System.out.println("Inicio Método2");
        for(int i =1; i<= 5; i++){
            try {
                //int num = 0;
                //int resultado = i/num;
                //System.out.println("Resultado: "+resultado);
                String resultado = null;
                System.out.println("Resultado: "+resultado.toString());
            } catch (ArithmeticException e) {
                System.out.println(e.getMessage());
                e.printStackTrace();
            } catch (NullPointerException e) {
                System.out.println(e.getMessage());
                e.printStackTrace();
            }
        }
        System.out.println("Fin Método2");
    }

Creación de excepciones

Clase Flujo2.java

Ejemplo excepción propia ArithmeticException

public class Flujo2 {

    public static void main(String[] args) {
        System.out.println("Inicio Main");
        metodo1();
        System.out.println("Fin Main");
    }

    public static void metodo1(){
        System.out.println("Inicio Método1");
        try {
            metodo2();
        } catch (ArithmeticException ae) {
            ae.printStackTrace();
        }
        System.out.println("Fin Método1");
    }

    public static void metodo2(){
        System.out.println("Inicio Método2");
        //ArithmeticException ae = new ArithmeticException();
        //throw ae;
        throw new ArithmeticException("Error Aritmético");
    }
}
Inicio Main
Inicio Método1
Inicio Método2
java.lang.ArithmeticException: Error aritmético
	at pila_ejecucion.Flujo2.metodo2(Flujo2.java:26)
	at pila_ejecucion.Flujo2.metodo1(Flujo2.java:14)
	at pila_ejecucion.Flujo2.main(Flujo2.java:7)
Fin Método1
Fin Main

Lanzando exception heredando de RuntimeException

    ...
    public static void metodo1() {
        System.out.println("Inicio Método1");
        try {
            metodo2();
        } catch (MiExepcion me) {
            me.printStackTrace();
        }
        System.out.println("Fin Método1");
    }

    public static void metodo2(){
        System.out.println("Inicio Método2");
        throw new MiExcepcion("Mi excepción");
    }
}
Inicio Main
Inicio Método1
Inicio Método2
pila_ejecucion.MiExcepcion: Mi excepción
	at pila_ejecucion.Flujo2.metodo2(Flujo2.java:27)
	at pila_ejecucion.Flujo2.metodo1(Flujo2.java:14)
	at pila_ejecucion.Flujo2.main(Flujo2.java:7)
Fin Método1
Fin Main

Lanzando exception heredando de Exception

    ...
    public static void metodo1(){
        System.out.println("Inicio Método1");
        try {
            metodo2();
        } catch (MiExcepcion2 me2) {
            me2.printStackTrace();
        }
        System.out.println("Fin Método1");
    }

    public static void metodo2() throws MiExcepcion2 {
        System.out.println("Inicio Método2");
        throw new MiExepcion2("Mi excepción 2");
    }
Inicio Método1
Inicio Método2
pila_ejecucion.MiExcepcion2: Mi excepcion 2
	at pila_ejecucion.Flujo2.metodo2(Flujo2.java:29)
	at pila_ejecucion.Flujo2.metodo1(Flujo2.java:14)
	at pila_ejecucion.Flujo2.main(Flujo2.java:7)
Fin Método2
Fin Método1
Fin Main
%%{init: {'theme':'dark'}}%%
classDiagram
Throwable --|> Exception
Exception --|> RuntimeException
Exception --|> MiExepcion2
Throwable --|> Error
RuntimeException --|> ArithmeticException
RuntimeException --|> NullPointerException
RuntimeException --|> MiExepcion
Error --|> StackOverFlow

Excepciones UN/CHECKED

  • UNCHECKED Excepciones no verificadas por el compilador.
  • CHECKED Excepciones verificadas, el compilador nos obliga a informar que se lanzara esta excepción, throws.

Sumario

  • Existe una gran jerarquía de clases que representan excepciones. Por ejemplo, ArithmeticException es hija de RuntimeException, que hereda de Exception, que a su vez es hija de la clase de excepciones más ancestral, Throwable. Conocer bien esta jerarquía significa saber cómo usar las excepciones en su aplicación.

  • Throwable es la clase que debe extenderse para poder lanzar un objeto en la pila (usando la palabra reservada throw)

  • Es en la clase Throwable donde tenemos casi todo el código relacionado con las excepciones, incluyendo getMessage() e printStackTrace(). El resto de la jerarquía tiene solo algunas sobrecargas de constructores para comunicar mensajes específicos.

  • La jerarquía que comenzó con la clase Throwable se divide en excepciones y errores. Las excepciones se utilizan en los códigos de aplicación. Los errores son utilizados exclusivamente por la máquina virtual.

  • Las clases que heredan de Error se utilizan para informar errores en la máquina virtual. Los desarrolladores de aplicaciones no deben crear errores que hereden de Error.

  • StackOverflowError es un error de máquina virtual para informar que la pila de ejecución no tiene más memoria.

  • Las excepciones se dividen en dos categorías amplias: las que el compilador comprueba obligatoriamente y las que no.

    • Los primeros se denominan checked y se crean por pertenecer a una jerarquía que no pasa RuntimeException.

    • Los segundos están unchecked y se crean como descendientes de RuntimeException.

Ejemplo de manejo de checked exception

Clase SaldoInsuficienteExcepction.java , proyecto bytebank

public class SaldoInsuficienteExcepction extends Exception {
    public SaldoInsuficienteExcepction(String mensaje) {
        super(mensaje);
    }
}

Clase Cuenta.java

    public void retirar(double valorRetiro) throws SaldoInsuficienteExcepction {
        if (this.saldo < valorRetiro) {
            throw new SaldoInsuficienteExcepction("Saldo insuficiente");
        }
        this.saldo -= valorRetiro;
    }

    public boolean transferir(double montoTransferencia, Cuenta cuenta) throws SaldoInsuficienteExcepction{
        if (this.saldo >= montoTransferencia) {
            try {
                this.retirar(montoTransferencia);
            } catch (SaldoInsuficienteExcepction e) {
                e.printStackTrace();
            }
            cuenta.depositar(montoTransferencia);
            return true;
        } else {
            throw new SaldoInsuficienteExcepction("Saldo insuficiente\nSaldo : $ "+
                                                  this.saldo+"\nRetiro: $ "+montoTransferencia);
        }
    }
}

Clase CuentaCorriente.java

    @Override
    public void retirar(double valorRetiro) throws SaldoInsuficienteExcepction {
        double comision = 0.2;
        System.out.println("Ejecutando retiro sobreescrito");
        super.retirar( valorRetiro + comision );
    }

    @Override
    public void depositar(double valorDeposito) {
        this.saldo += valorDeposito;
    }
}

Test TestCuentaExceptionSaldo.java

public class TestCuentaExceptionSaldo {
    public static void main(String[] args) {
        Cuenta ca = new CuentaAhorro(11, 22);
        System.out.println("Deposito 1000");
        ca.depositar(1000);
        try {
            System.out.println("Retirando 1000");
            ca.retirar(1000);
            System.out.println("Retiro OK");
            
            //System.out.println("Retirando 1");
            //ca.retirar(1);
            //System.out.println("Retiro OK");
            
            Cuenta cc = new CuentaCorriente(11, 33);
            cc.depositar(1000);
            cc.transferir(500, ca);
            cc.transferir(5000, ca);
            System.out.println(ca.getSaldo());
            System.out.println(cc.getSaldo());
        } catch (SaldoInsuficienteExcepction e) {
            e.printStackTrace();
        }
        System.out.println("Fin Programa");
    }
}

Salida del programa con mensaje personalizado del error Saldo: $499.8 y Retiro: $5000.0, incluyendo valores de variables

Deposito 1000
Retirando 1000
Retiro OK
Cuentas creadas: 2
Ejecutando retiro sobreescrito
bytebank_excep.SaldoInsuficienteExcepction: Saldo insuficiente
Saldo : $ 499.8
Retiro: $ 5000.0
    at bytebank_excep/bytebank_excep.Cuenta.transferir(Cuenta.java:41)
    at bytebank_excep/bytebank_excep.TestCuentaExceptionSaldo.main(TestCuentaExceptionSaldo.java:20)
Fin Programa

finally

Conexion.java

public class Conexion {

    public Conexion() {
        System.out.println("Abriendo conexion");
        //throw new IllegalStateException();
    }

    public void leerDatos() {
        System.out.println("Recibiendo datos");
        throw new IllegalStateException();
    }

    public void cerrar() {
        System.out.println("Cerrando conexion");
    }
}

TestConexion.java

public class TestConexion {
    public static void main(String[] args) {
        Conexion con = null;
        try {
            con = new Conexion();
            con.leerDatos();
        } catch (IllegalStateException e) {
            System.out.println("Ejecutando catch");
            e.printStackTrace();
        } finally {
            System.out.println("Ejecutando finally");
            if (con != null) {
                con.cerrar();
            }
        }
    }
}

try with resources

Similar a los context managers de Python. Se usan con recursos, donde la clase instanciada implementa la interfáz AutoCloseable, que requiere implementar un método close().

Conexion.java

public class Conexion implements AutoCloseable {

    public Conexion() {
        System.out.println("Abriendo conexion");
        //throw new IllegalStateException();
    }

    public void leerDatos() {
        System.out.println("Recibiendo datos");
        throw new IllegalStateException();
    }

    public void cerrar() {
        System.out.println("Cerrando conexion");
    }

    @Override
    public void close() throws Exception {
        cerrar();
    }
}

TestConexion.java

public class TestConexion {
    public static void main(String[] args) throws Exception {
        try (Conexion con = new Conexion()){
            con.leerDatos();
        } catch (IllegalStateException e) {
            System.out.println("Ejecutando catch");
            e.printStackTrace();
        }
    }
}