Saltar al contenido

Gestión de ingresos y gastos.

En este ejercicio de programación orientada a objetos (POO), vamos a crear una aplicación de consola para la gestión de ingresos y gastos personales.

Esto lo conseguiremos a través de un menú de opciones en el que seleccionaremos la operación que deseemos realizar. Para desarrollar este programa, vamos a seguir el diagrama de clases que se nos muestra a continuación.

diagrama de clases para la gestión de ingresos y gastos
POO. Diagrama de clases. Gestión de gastos e ingresos.

Si nos damos cuenta, en el diagrama, aparecen diferentes símbolos delante de cada atributo y cada método que nos indica su visibilidad. Vamos a describir que significa cada símbolo.

  • El más (+): El atributo o método es público, (public).
  • El menos (-): Nos indica que el atributo o método es privado, (private).
  • La almoadilla (#): Quiere decir que la visibilidad del método o atributo es protegido, (protected).
  • Este símbolo (~): Hace referencia a la visibilidad del paquete.

Una vez que ya sabemos como funciona la visibilidad de los métodos y atributos de una clase dentro de un diagrama de clases en la POO, lo primero que tenemos que hacer es analizar y desglosar nuestro diagrama.

Desglosando el diagrama de clases. Parte 1. Herencia de ingresos y gastos.

Empezaremos explicando las tres primeras clases de las que se compone este ejercicio de gestión ingresos y gastos con POO en java.

herencia en la gestión de ingresos y gastos
diagrama de clases POO

Por un lado, en esta primera parte, vemos tres clases:

  • Una clase Dinero: Si nos fijamos, esta clase es una clase abstracta que contiene 2 atributos protegidos (protected) y 3 métodos públicos. Además debemos tener en cuenta que ninguno de los métodos nos dice que tenga que ser abstracto. Esto es algo que nos puede confundir, pero cuando trabajamos con clases abstractas, hay que tener en cuenta una serie de aspectos que puedes encontrar accediendo a nuestra guía de java.

¿Quieres aprender java desde cero?

Por otro lado, tenemos una clase Gasto y una clase Ingreso. Como podemos comprobar, estas clases no poseen ningún atributo, pero cada una de ellas, contiene un método constructor que recibe dos parámetros. Esto lo sabemos porque el nombre de los métodos de las clases Gasto e Ingreso, se llaman igual que sus clases.

En segundo lugar, cada uno de los métodos, entre paréntesis contiene dos variables con un tipo de dato. Esto nos indica que el método constructor es un método que recibe parámetros.

Por último, tanto la clase Ingreso, como la clase Gasto, posen un método toString. Este es un método getter, que devuelve una cadena de tipo String. Debes saber que el método toString es un método perteneciente a la clase Object. Por tanto, cuando queremos implementar este método en nuestras clases personalizadas, debemos indicar que es un método sobreescrito. Esto lo haremos con la palabra reservada en java @Override.

Herencia entre clases en la POO

Otro punto importante es que, tanto la clase Gasto, como la clase Ingreso, están unidas a la clase dinero a través de una flecha hacia arriba. Esto quiere decir, que la clase Ingreso y la clase Gasto, heredarán de la clase dinero. Es decir, también en este programa de java, aplicaremos el concepto de herencia.

En POO, cuando una clase, hereda de otra, en este caso, tanto la clase Ingreso como Gasto heredan de dinero, entones, las dos clases (Ingreso y gasto), tendrán sus métodos propios (en este caso los diferentes constructores y su método toString propio) y además, podrán hacer uso de los métodos getDinero, setDinero, getDescripcion y setDescripcion, ya que estos los heredará de la clase padre. En este caso, la clase padre de las clases Ingreso y Gasto, es la clase dinero.

Teniendo en cuenta esto, lo primero que vamos a hacer es crear las clases Dinero, Gasto e ingreso con sus métodos getter y setter.

Creación de la clase Dinero para la gestión de ingresos y gastos.

Según nuestro diagrama de clases, la clase Dinero, quedaría de la siguiente manera. Hay que tener en cuenta que esta clase es una clase abstracta

package gestioncuentas;

public abstract class Dinero {

    protected double dinero;
    protected String description;

    public void setDinero(double dinero) {
        this.dinero = dinero;
    }
    
    public void setDescription(String description) {
        this.description = description;
    }

    public double getDinero() {
        return dinero;
    }

    public String getDescription() {
        return description;
    }
}

Crear la clase Ingreso.

Como ya hablamos antes, la clase Ingreso hereda de la clase dinero. Por lo tanto haremos uso de la cláusula extends para indicar que esta clase hereda de Dinero.

package gestioncuentas;

public class Ingreso extends Dinero {

    public Ingreso(double ingreso, String description) {
        this.dinero=ingreso;
        this.description=description;
    }

    @Override
    public String toString() {
        return "Ingreso a su favor en concepto de " + this.description
                + ". Importe total: " + this.dinero ;
    } 
}

Creando la clase Gasto.

En este caso pasa como en el caso anterior, esta clase hereda de la clase Dinero, por lo tanto usaremos la cláusula extends para indicar que esta clase Gasto hereda también de dinero.

package gestioncuentas;

public class Gasto extends Dinero {

    public Gasto(double gasto, String description) {
        this.dinero=gasto;
        this.description=description;
    }

    @Override
    public String toString() {
        return "Gasto en concepto de " + this.description
                + ". Importe total: " + this.dinero ;
    }  
}

Desglose del diagrama de clases. Parte 2. Clases independientes de la gestión de ingresos y gastos.

En esta segunda parte de la explicación de nuestro diagrama de clases, veremos el resto de clases que forman parte de la aplicación de gestión de ingresos y gastos. Vamos a ver la siguiente imagen.

clases independientes sin herencia en la gestion de ingresos y gastos
Clases independientes del diagrama de clases

En este caso, tenemos tres clases independientes, las cuales no heredan ni de la clase Gasto ni de la clase ingreso. Las clases independientes serían:

  • La clase Usuario: Tiene sus atributos privados (características) y sus métodos públicos (lo que el usuario puede hacer).
  • Una clase Cuenta: Con sus características y métodos igual que la clase Usuario.
  • La clase GastoException: Esta clase no tiene ningún atributo, pero si un método constructor de tipo público. El motivo de tener que crear esta clase es porque el ejercicio, más adelante nos manda generar una excepción personalizada. Por otro lado, aunque el ejercicio no dice nada, esta clase heredará de la clase Exception.

Vamos a explicar cada una de las clases por separado.

Entendiendo la clase Usuario.

Esta clase será la encargada de gestionar un único usuario. Este se creará al inicio del programa leyendo datos por el teclado.

El DNI deberá tener un formato concreto. Está comprobación la realizará en una función setter y getter al mismo tiempo. Es decir, recibirá un DNI como parámetro y devolverá un booleano. True si es correcto y False si no lo es.

El formato correcto debe cumplir las siguientes condiciones:

  • Los primeros 8 caracteres sólo podrán ser numéricos.
  • El último caracteres deberá ser una letra entre la A y la Z.
  • El guion entre los números y la letra es opcional admitiendo ambas
  • posibilidades. Por ejemplo:
    • DNI: 78844112L
    • DNI: 78844112-L
  • Tendrá una función toString con la que devolver su contenido.

Composición de la clase usuario.

Para crear esta clase, el diagrama nos indica que debemos implementar los siguientes métodos:

  • Usuario(): Es el constructor de la clase.
  • String getNombre(): Es un método get, que devolverá el nombre del usuario, es decir, devolverá una cadena de texto (String).
  • setNombre(String nombre): Es un método set, que establece el nombre del usuario. Este método, recibe por parámetro o argumento una cadena de texto que llamaremos nombre y que el usuario introducirá por teclado.
  • int getEdad: Es un método getter que devolverá un número entero, en este caso, devolverá la edad del usuario.
  • setEdad(int edad): Es un método setter que establecerá la edad del usuario y que recibirá por parámetro un valor de tipo numérico llamado edad y que el usuario introducirá por teclado.
  • String getDNI(): Es un método getter que devolverá una cadena de texto con el DNI del usuario.
  • boolean setDNI(String dni): Este método es un setter, es decir, establece un valor al dni, recibiendo lo que el usuario introduce por teclado en formato de cadena de texto. Por otro lado, devolverá true si el formato es correcto y false si no lo es. Este es un método que podemos decir que funciona a la vez de set (establece el valor) y get (devuelve un resultado).
  • String toString: Este es un método que hereda de la clase object y que puede sobreescribirse en cualquier clase que creemos.

Programación de la clase Usuario.

package gestioncuentas;

public class Usuario {
    String nombre;
    String dni;
    int edad;

    // Metodo constructor para dar un estado inicial al objeto Usuario.
    public Usuario() { 
        this.nombre="";
        this.dni="";
        this.edad=0;
    }

    // Metodos setter (establecen valores)
    public void setNombre(String nombre) { // Establece el nombre
        this.nombre = nombre;
    }
    
    public void setEdad(int edad) { // Establece la edad
        this.edad = edad;
    }
    
    public void setDni(String dni) { // Establece el DNI
        this.dni = dni;
    }
    
    // Este metodo es getter (devuelve un valor) pero
    // también es un setter (establece un valor)
    // Este método comprueba si el DNI es correcto.
    public boolean setDNI(String dni){
        
        if(dni.matches("^[0-9]{8}[a-zA-Z]$")
                || dni.matches("^[0-9]{8}[-][a-zA-Z]$")){
            this.dni=dni;
            return true;
        }
        else{
            return false;
        }      
    }

    // Métodos getter (devuelven un valor).
    public String getNombre() { // Devuelve el nombre
        return nombre;
    }

    public String getDni() { // Devuelve el DNI
        return dni;
    }

    public int getEdad() { // Devuelve la edad
        return edad;
    }

    // Metodo sobreescrito toString
    @Override
    public String toString() {
        return "Nombre: " + this.nombre + ".\n" +
                "DNI: " + this.dni + ".\n" +
                "Edad: " + this.edad + ".";
    }
}

Desarrollando la clase Cuenta.

En esta clase, se gestionarán todos los movimientos de dinero tanto
ingresos como gastos.

  • Inicialmente (en el constructor) se recibirá el usuario que es dueño de la cuenta y el saldo inicial que será de 0€.
  • Al añadir un nuevo ingreso se sumará al saldo de la cuenta, sabiendo que esta variable (la variable saldo), mostrará nuestro dinero real. Además la función devolverá el saldo total que tenemos en la cuenta.
  • Al añadir un nuevo gasto se debe comprobar si se dispone de saldo suficiente.
    • En caso contrario, se deberá lanzar una nueva excepción del tipo GastoException() pero el programa no debe finalizar.
    • Si se dispone de saldo suficiente se restará el importe del gasto y se devolverá el saldo de la cuenta.
  • Las funciones getGastos y getIngresos nos devolverán todos los movimientos de uno u otro tipo.
  • Además esta clase, tendrá una función toString que devolverá el usuario y su saldo.

Composición de la clase cuenta.

Según el diagrama, la clase cuenta, contará con los siguientes métodos:

  • Cuenta(): Constructor de la clase, que según nuestro enunciado, recibirá por parámetro un objeto de tipo Usuario y además, establecerá el saldo inicial en cero euros.
  • double getSaldo(): Es un método getter que devolverá el saldo de la cuenta.
  • setSaldo(double saldo): Es un método setter que establecerá el saldo de la cuenta recibiendo por parámetro un valor de tipo decimal llamado saldo.
  • Usuario getUsuario(): Método getter que devolverá el usuario de la cuenta.
  • setUsuario(Usuario usuario): Es un setter que establecerá el usuario de la cuenta. Recibe por parámetro un objeto de tipo Usuario llamado usuario.
  • double addIngresos(String descripcion, double cantidad): Es un método getter, ya que devuelve un valor de tipo double (decimal) que se refiere al saldo, pero a la vez en un método setter, ya que en este método se sumará el saldo del ingreso al saldo de la cuenta. Recibe por parámetro dos argumentos, la descripción del movimiento que se realiza y el importe del mismo.
  • double addGastos(String descripcion, double cantidad): Es un método getter, ya que devuelve un valor de tipo double (decimal) que se refiere al saldo, pero a la vez en un método setter, ya que en este método se restará el saldo del gasto del saldo de la cuenta. Recibe por parámetro dos argumentos, la descripción del movimiento que se realiza y el importe del mismo.
  • List<Ingreso> getIngresos(): Es un método getter. Este método devuelve una lista de objetos tipo Ingreso (tanto su concepto como su importe).
  • List<Gasto> getGastos(): Es un método getter. Este método devuelve una lista de objetos tipo Gasto (tanto su concepto como su importe).
  • String toString(): Este es un método getter sobreescrito y que hereda de la clase object. Este método puede sobreescribirse siempre que queramos en nuestras propias clases.

Programación de la clase cuenta para la gestión de ingresos y gastos.

package gestioncuentas;

import java.util.*;

public class Cuenta {

    private double saldo; // Saldo de la cuenta
    private Usuario usuarioCuenta; // Usuario propietario de la cuenta
    private List<Gasto> gastos; // Lista de gastos
    private List<Ingreso>ingresos; // Lista de ingresos

    // Contructor del objeto cuenta. Le da un estado iniciar a la cuenta.
    public Cuenta(Usuario usuarioCuenta) {
        this.usuarioCuenta=usuarioCuenta;
        this.saldo=0;
        this.gastos=new ArrayList<Gasto>();
        this.ingresos=new ArrayList<Ingreso>();
    }

    // Establece el usuario de la cuenta
    public void setUsuarioCuenta(Usuario usuarioCuenta) {
        this.usuarioCuenta = usuarioCuenta;
    }

    // Establece el saldo de la cuenta.
    public void setSaldo(double saldo) {
        this.saldo = saldo;
    }
    
    // Devuelve el saldo de la cuenta
    public double getSaldo() {
        return saldo;
    }
    
    // Devuelve el usuario de la cuenta
    public Usuario getUsuarioCuenta() {
        return usuarioCuenta;
    }
    
    // Devuelve la lista de gastos
    public List<Gasto> getGastos() {
        return gastos;
    }
    
    // Devuelve la lista de ingresos
    public List<Ingreso> getIngresos() {
        return ingresos;
    }
    
    // Agrega un ingreso a la lista.
    public double addIngresos(String description, double cantidad){
        
        // Se crea el ingreso y se pasa el importe y la descripcion.
        Ingreso nuevoIngreso = new Ingreso(cantidad,description);
        // Se agrega el ingreso a la lista
        this.ingresos.add(nuevoIngreso);
        // Se suma el saldo
        this.saldo=this.saldo+cantidad;
        // se devuelve el saldo
        return saldo;
    }
    
    // Agrega un gasto a la lista
    public double addGastos(String description, double cantidad){
        
        try{ // Intentamos
            // Restar el saldo
            this.saldo = this.saldo-cantidad;
            
            if(this.getSaldo()<0){ // Si el saldo es menor que cero
                // Lanzamos la excepción personalizada
                throw new GastoException();
            }
            // Al lanzar la excepción se ejecuta el catch.
        }catch(GastoException e){
            // Devolvemos un -1
            return -1;
        }
        // Si no ejecuta el catch ejecutará lo siguiente
        // Creamos un nuevo gasto
        Gasto nuevoGasto=new Gasto(cantidad,description);
        // Lo agregamos a la lista de gastos
        gastos.add(nuevoGasto);
        // Devolvemos el saldo
        return saldo;
    }
    
    @Override
    public String toString(){
        return "Hola " + this.usuarioCuenta.getNombre() + ", el saldo de tu "
                + "cuenta es " + this.saldo;
    }
}

Implementando nuestra propia excepción. Clase GastoException().

Esta clase nos servirá para implementar nuestra propia excepción. Como bien se nos indica en el diagrama de clases, contendrá un constructor vacío.

Código de la clase GastoException.

package gestioncuentas;

public class GastoException extends Exception {

    // Declaramos una variable para almacenar el mensaje de error
    private String error="";
    public GastoException() {
        // Construimos la excepción.
        this.error="No se puede agregar el gasto ya que el saldo es menor que "
                + "el importe del gasto, o su saldo es cero euros.";
    }
    
    // Sobreescribimos el metodo getMessage
    @Override
    public String getMessage(){
        return error;
    }    
}

Creación del usuario y sus datos.

Primero, crearemos una clase llamada Principal, que contendrá el método main, encargado de arrancar el programa. El funcionamiento de la aplicación seguirá los siguientes pasos:

Lo primero será crear un usuario cuando arranca la aplicación. Como ya sabemos, en el momento que creamos una clase en java, ya podemos crear objetos de esa clase. Anteriormente, ya habíamos creado la clase usuario con sus propiedades y lo que el objeto puede hacer.

Ahora vamos a crear un nuevo objeto usuario y pedir sus datos por teclado a través de un método llamado pedirDatosUsuario(). Recordar que también se debe importar el paqute java.util para poder utilizar la clase Scanner.

Variables del método de la creación del usuario.

package gestioncuentas;

import java.util.Scanner;

public class GestionCuentas {

    // ------------VARIABLE PARA LEER DATOS INTRODUCIDOS -------------------//
    // Creamos una variable Scanner para leer datos por teclado
    private static final Scanner lectura = new Scanner(System.in);
    
    //********************************************************************//
    // --------------VARIABLES PARA LA CLASE USUARIO -------------------//
    /* Creamos una variable privada llamada nuevoUsuario.
    Esta variable almacenará objetos de tipo Usuario.
    Para ello usaremos la palabra reservada new */
    private static Usuario nuevoUsuario = new Usuario();
    
    // Declaramos una variable para almacenar el nombre del usuario
    private static String nombreUsuario="";
    
    // Declaramos una variable para almacenar la edad del usuario
    private static String edadUsu="";
    
    /* Declaramos una variable para almacenar la edad del usuario
    en un formato numérico*/
    private static byte edadUsuario=-1;
    
    // Declaramos una variable para almacenar el dni del usuario
    private static String dniUsuario="";
    
    /* Declaramos una variable para validar que el usuario creado es correcto
    La iniciamos en falso porque a estas alturas del programa todavia no
    se han pedido datos de usuario*/
    private static boolean usuarioCreado=false;

Programando el método de creación del usuario.

Este método pedirá los datos de usuario por teclado y si son correctos la varieble usuarioCreado le asignaremos true. En caso contrario la variable será false.

    // METODO QUE PIDE LOS DATOS PARA CREAR UN USURIO
    private static void pedirDatosUsuario(){
        usuarioCreado=false;
        // Pedimos el nombre de usuario y repetimos la operación mientras que
        // el nombre del usuario tenga una cadena vacía.
        do{
            System.out.println("Introduce el nombre del usuario");
            nombreUsuario=lectura.nextLine().toUpperCase();
        }while(nombreUsuario.isEmpty());
        nuevoUsuario.setNombre(nombreUsuario);
        
        /* Pedimos la edad de usuario y repetimos la operación mientras que
        la edad del usuario (edadUsu) sea una cadena vacía, o mientras la
        edad del usuario en formato numérico sea mayor que cero*/
        do{
            System.out.println("Introduce la edad del usuario");
            edadUsu=lectura.nextLine();
            // Intentamos pasar la edad del usuario a formato numérico
            try{
                edadUsuario = Byte.parseByte(edadUsu);
                nuevoUsuario.setEdad(edadUsuario);
            }catch(NumberFormatException e){
                System.out.println("La edad debe numérica y mayor que cero");
            }
        }while(edadUsu.isEmpty() || edadUsuario<=0);
        
        
        /* Pedimos el DNI del usuario y repetimos la operación mientras que
        la variable dniUsuario tenga una cadena vacía o siempre que el
        metodo setDNI de tipo booleano devuelva falso*/
        do{
            System.out.println("Introduce el DNI del usuario");
            dniUsuario=lectura.nextLine().toUpperCase();
            
            /* Si el valor es correcto se establece el valor del DNI
            a la variable dni de la clase (objeto) Usuario*/
            if(nuevoUsuario.setDNI(dniUsuario)){
                nuevoUsuario.setDNI(dniUsuario);
            }
            else{
                System.out.println("El DNI introducido no es correcto."
                        + "Vuelva a introducir el DNI");
            }
        }while(dniUsuario.isEmpty() || nuevoUsuario.setDNI(dniUsuario)==false);
        
        // Si todo está correcto, validamos el usuario
        usuarioCreado=true;
    }

Una vez que se ha creado el usuario, se debe crear una cuenta pasándole el usuario creado por parámetro para que pueda agregar sus ingresos y gastos. (Esto lo haremos en el método main que mostraremos más adelante).

Por el momento, vamos a imaginar que tenemos la cuenta creada. Entonces si tuviéramos la cuenta ya creada, debemos mostrar un mensaje por pantalla con las operaciones que el usuario puede realizar. Vamos a ver como hacerlo.

Programando el menú de opciones para la gestión de ingresos y gastos.

Lo primero que debemos hacer es crear una variable de tipo texto que almacenará lo que el usuario introduce por teclado y que llamaremos opcion. A continuación, se creará una variable llamada numeroOpcion de tipo entero. Esta variable almacenará la opción que el usuario selecciona.

Variables para la creación del menú de opciones.

//********************************************************************//
    // ------------VARIABLE PARA LEER OPCIONES DEL MENÚ -------------------//
    /* Declaramos una variable de tipo String que almacenará la opción
    que elija el usuario en formato texto y le asignaremos un valor vacío
    ya que, si no lo hacemos así la variable no tendrá ningún valor,
    ni siquiera cero. */ 
    private static String opcion = "";
    
    /* Declaramos una variable de tipo byte para almacenar en formato numérico
    el valor que el usuario está introduciendo en formato texto*/
    private static byte numeroOpcion= -1;

A continuación crearemos un método llamado mostrarMenu(). Este método, mostrará el menú y pedirá por teclado una opción al usuario hasta que introduzca una opción correcta. Esto lo hará una vez que la función sea llamada desde el método main.

Método para la creación e interacción con el menú de opciones.

// METODO QUE MUESTRA EL MENÚ DE OPCIONES, PIDE UNA OPCIÓN AL USUARIO
    // E INTENTA PASAR LA OPCION INTRODUCIDA EN TEXTO A FORMATO NUMÉRICO.
    private static void mostrarMenu(){
        
        do{
        System.out.println("1. Introducir un nuevo gasto");
        System.out.println("2. Introducir un nuevo ingreso");
        System.out.println("3. Mostrar gastos");
        System.out.println("4. Mostrar ingresos");
        System.out.println("5. Mostrar saldo de la cuenta");
        System.out.println("0. Salir");
        
        try{
        
            // Informamos al usuario que elija una opción.
            System.out.print("Introduce una opción del menú: ");

            // Leemos lo que el usuario ha introducido por teclado
            // y lo guardamos en la variable opción
            opcion = lectura.nextLine();
            
            /* Passamos el valor de tipo String a tipo byte. Esto lo hacemos
            para asegurarnos de que el usuario ha introducido un número
            y no un texto */
            numeroOpcion = Byte.parseByte(opcion);
        }
        catch(NumberFormatException e){
            System.out.println(e.getMessage());
            System.out.println("La opción elegida debe ser un número "
                    + "entre 0 y 5");
        }
        }while(numeroOpcion<0 && numeroOpcion>5);
    }

Una vez ya se ha creado el usuario y la cuenta, se mostrará el menú. Ahora crearemos un método para cada una de las opciones. El código estará comentado para una mejor comprensión.

Tanto para agregar gastos como ingresos, necesitamos declarar una serie de variables para almacenar los conceptos y las descripciones de los movimientos.

Estás variables almacenarán lo que el usuario introduce. Después a través de los métodos correspondientes de la clase cuenta (que gestiona todos los movimientos), haremos funcionar la aplicación.

Variables de la clase cuenta para gestionar gastos e ingresos.

// --------------VARIABLES PARA LA CLASE CUENTA -------------------//
    
    /* Declaramos una variable que va a almacenar un objeto de tipo Cuenta
    en una variable llamada nuevaCuenta. De momento le asignamos un valor null
    */
    private static Cuenta nuevaCuenta = null;
    
    /* Declaramos una variable de tipo texto (String)
    donde almacenaremos el importe del gasto o ingreso*/
    private static String importe="";
    
    /* Declaramos una variable de tipo double (decimal)
    donde almacenaremos el importe del gasto o ingreso
    convertido en formato numérico decimal tipo double*/
    private static double importeTotal=0;
    
    /* Declaramos una variable de tipo texto (String)
    donde almacenaremos el concepto del gasto o ingreso*/
    private static String descripcion="";

Implementación del método para agregar gastos.

// METODO QUE NOS PERMITIRÁ INTRODUCIR GASTOS EN LA CUENTA
    private static void introducirGastos(){
        
          // Reiniciamos las variables por si tuvieran algún valor.
        importe="";
        descripcion="";
        importeTotal=0;
        
         // Pedimos la descripción del gasto
        do{
            System.out.print("Introduce el concepto del gasto: ");
            descripcion = lectura.nextLine();
            
        }while(descripcion.isEmpty());
        
        // Pedimos la el importe del gasto
        do{
            System.out.print("Introduce el importe del gasto: ");
            importe = lectura.nextLine();
            
            try{ // Intentamos pasar el valor ingresado a formato numérico
                importeTotal=Double.parseDouble(importe);
            }catch(NumberFormatException e){
                System.out.println("El importe del ingreso debe ser numérico +"
                        + e.getMessage());
            } 
        }while(importe.isEmpty());
            
        // Si el saldo de la cuenta es cero, o es menor al importe del gasto
        if(nuevaCuenta.getSaldo()<importeTotal || nuevaCuenta.getSaldo()==0){
           
            // Por lo tanto, informamos al usuario que debe realizar un ingreso
            // antes de registrar el gasto.
            System.out.println("Debes realizar primero un ingreso para "
                    + "registrar un gasto");
        }
        else{ // En caso contrario
            
          // Agregamos el gasto a la cuenta
            nuevaCuenta.addGastos(descripcion, importeTotal);

            // Informamos al usuario que se ha registrado el gasto.
            System.out.println("Gasto registrado correctamente.");  
        }
    }

Creación del método para agregar los ingresos.

 /* METODO QUE NOS PERMITIRÁ INTRODUCIR INGRESOS EN LA CUELTA*/
    private static void introducirIngresos(){
        
        // Reiniciamos las variables por si tuvieran algún valor.
        importe="";
        descripcion="";
        importeTotal=0;
        
        // Pedimos la descripción del ingreso
        do{
            System.out.print("Introduce el concepto del ingreso: ");
            descripcion = lectura.nextLine();
            
        }while(descripcion.isEmpty());
        
        // Pedimos la el importe del ingreso
        do{
            System.out.print("Introduce el importe del ingreso: ");
            importe = lectura.nextLine();
            
            try{ //Intentamos pasar el valor ingresado a formato numérico
                importeTotal=Double.parseDouble(importe);
            }catch(NumberFormatException e){
                System.out.println("El importe del ingreso debe ser numérico +"
                        + e.getMessage());
            }
        }while(importe.isEmpty());
        
        // Agregamos el ingreso a la cuenta
        nuevaCuenta.addIngresos(descripcion, importeTotal);
        
        // Informamos al usuario
        System.out.println("Ingreso registrado correctamente");
    }

Mostrar los gastos a través del método mostrarGastos()

private static void mostrarGastos(){
        
        // Si la lista de gastos no está vacia
       if(!nuevaCuenta.getGastos().isEmpty()){
            // Recorremos la lista de gastos
           for(int x=0;x<nuevaCuenta.getGastos().size();x++){
               System.out.println(nuevaCuenta.getGastos().get(x).toString());
           }
       }
       else{ // En caso de que la lista esté vacía informamos al usuario
           System.out.println("No existen gastos en esta cuenta.");
       }
    }

Implementación del método para mostrar los ingresos.

// METODO QUE NOS PERMITE MOSTRAR UNA LISTA DE INGRESOS DE LA CUENTA
    private static void mostrarIngresos(){
        
        // Si la lista de ingresos no está vacia
       if(!nuevaCuenta.getIngresos().isEmpty()){
            // Recorremos la lista de gastos
           for(int x=0;x<nuevaCuenta.getIngresos().size();x++){
               System.out.println(nuevaCuenta.getIngresos().get(x).toString());
           }
       }
       else{ // En caso de que la lista este vacía, informamos al usuario
           System.out.println("No existen gastos en esta cuenta.");
       }
    }

Codificación del método main.

Debemos tener en cuenta que todos los métodos anteriores de mostrar ingresos y gastos y listar ambos movimientos, se encuentran en la clase principal, (la que contiene el método main). Una vez aclarado, veamos el código fuente del método para iniciar la aplicación en java.

   public static void main(String[] args) {
        
       /* Pedimos los datos para crear un usuario como mínimo una vez y hasta
       que el usuario se haya creado correctamente*/
        do{
           pedirDatosUsuario();
       }while(usuarioCreado=false);
        
        /* Si sale del bucle ya sabemos que el usuario está creado, entonces
        Mostramos los datos del usuario */
        System.out.println("Datos del nuevo usuario:");
        System.out.println(nuevoUsuario.toString());
        
        // Y creamos una nueva cuenta
        nuevaCuenta=new Cuenta(nuevoUsuario);
        
        /* Mostramos el menú como mínimo una vez.
        Tener en cuenta que la primera vez que se muestra el menú la variable
        numeroOpcion tiene un valor de -1 y el menú se mostrará mientras que
        la variable numeroOpcion sea diferente de cero.*/
        do{ 
            
            mostrarMenu();

            /* Creamos una estructura switch a través de la cual sabremos
            que opción se debe ejecutar en cada momento. */
            switch(numeroOpcion){

                case 0: // Eligiendo un cero, saldrá del programa
                    System.out.println("Programa finalizado."
                            + "Gracias por utilizar la aplicación.");
                    break;

                case 1: // Eligiendo un uno se registra un nuevo gasto
                    introducirGastos();
                    break;

                case 2: // Eligiendo un dos se registra un nuevo ingreso
                    introducirIngresos();
                    break;

                case 3: // Eligiendo un tres se muestran los gastos
                    mostrarGastos();
                    break;

                case 4: // Eligiendo un cuatro se muestran los ingresos
                        mostrarIngresos();
                    break;

                case 5: // Elige un cinco se muestra el saldo de la cuenta
                    System.out.println(nuevaCuenta.toString());
                    break;

                default: // Con cualquier otro número, muestra un mensaje

                    System.out.println("Introduce el valor correcto");
                    break;
            }

        // Esto lo hará miestras el valor de la variable
        // numeroOpcion sea diferente de cero
        }while(numeroOpcion !=0);
        
        // Cerramos el objeto lectura, ya que si sale del bucle do - while
        // quiere decir que el programa finalizó.
        lectura.close();
    }

DESCARGAR PROYECTO