Mostrar Mensajes

Esta sección te permite ver todos los posts escritos por este usuario. Ten en cuenta que sólo puedes ver los posts escritos en zonas a las que tienes acceso en este momento.


Mensajes - Kabuto

Páginas: 1 ... 3 4 5 6 7 [8] 9 10 11 12 13 ... 50
141
Para eso, tienes dos opciones.

1. La más fácil y por tanto la más recomendable.
Añadir como atributo de Cliente un double donde acumular los importes de sus compras.

Citar
public class Cliente {
   
   private int id;
   private String nombres;
   private String apellidos;
   private String email;
   private int telefono;
   private StringBuilder facturacion; //Simula un histórico de ventas´
   private double totalFacturado;

   public Cliente (int id, String nombres, String apellidos, String email, int telefono) {
      this.id = id;
      this.nombres = nombres;
      this.apellidos = apellidos;
      this.email=email;
      this.telefono = telefono;
      facturacion = new StringBuilder();
      totalFacturado = 0;
   }
Así luego, en su método facturar(), no solo le pasamos la línea para el StringBuilder, si no también el importe de la venta, que ha sido calculado en el main

Citar
   
   /*
    * Añade una nueva línea de facturación
    * al StringBuilder
    */
   public void facturar(String linea, double importe) {
      facturacion.append(linea);
      totalFacturado += importe;
   }

Ahora en el Main, además de pasarle la línea formateada para el StringBuilder, le pasaremos también el importe double para que lo sume a su nuevo atributo:

Citar
               //Y también en la facturación personal del comprador
               comprador.facturar(String.format("Codigo: %s Nombre: %s Unidades compradas: %d Importe: %.2f\n",
                     codigo, solicitado.getNombre(), unidades, facturar), facturar);

2- Un poco más compleja pero que no requiere añadir atributos ni cambiar nada en el main.
Hacer malabarismos con la clase String para detectar y extraer los caracteres que representan los importes, convertirlos a double y sumarlos.
El StringBuilder contiene una serie de líneas de cadenas, cada una con el importe al final de ella.
Pues habrá que separar estas líneas, detectar donde se encuentra el importe, extraerlo para poder convertirlo a double y poder hacer una suma con los importes de todas las líneas.
Todo este proceso lo haríamos en el método mostrarDetalles(), y solo si hay algo facturado.
Citar
   public void mostrarDetalles() {
      System.out.println("ID: " + id);
      System.out.println("Nombre: " + nombres +  " " + apellidos);
      System.out.println("E-Mail: " + email + " Telf: " + telefono);
      System.out.println("\tFACTURACION");
      System.out.println(facturacion.length()==0?"No hay historico de ventas":facturacion.toString());

      //Hacemos una suma del total facturado, si es que hay facturas
      if (facturacion.length() > 0) {
         double total = 0;

         //1-Creamos un arreglo donde cada elemento es una linea del StringBuilder
         String[] lineas = facturacion.toString().split("\n");
         //2-Recorremos cada linea con un bucle
         for (int in = 0; in < lineas.length; in++) {
            //3-Buscamos el último espacio en blanco de cada linea
            int pos = lineas[in].lastIndexOf(" ");
            //4-Tras ese último espacio en blanco, está el importe de la venta, lo extraemos
            String importe = lineas[in].substring(pos+1);
            //5-El importe está escrito con "coma decimal", pero el parseo solo funciona con "punto decimal".
            //Lo reemplazamos
            importe = importe.replace(',' , '.');
            //6-Parseamos a double y lo acumulamos en el total
            total += Double.parseDouble(importe);
         }

         //6-Finalizado el bucle, ya tenemos el total
         System.out.printf("\nEl total de los importes es: %.2f\n", total);
      }

   }

Como ves, serviría para hacer un examen sobre la clase String y el tratamiento de cadenas.  :P
Es enrevesado, pero funciona, si lo ejecutamos veremos que se realiza la suma de importes correctamente.
Citar
ELECTRODOMESTICOS ELECTROHOGAR
       MENU DE OPCIONES
1 = Listar Inventario
2 = Venta
3 = Facturacion
4 = Gestion Inventario
5 = Gestion Clientes
0 = Salir
Elija la opcion: 2

Indique el ID del Cliente
ID: 19034211
Cliente identificado:
[id= 19034211, nombre= Laura, apellidos= Juan, email= lajuanita@hotmail.com, telefono= 3451212]

Indique el CODIGO del electrodoméstico para realizar la venta.
CODIGO: LAV-0090

Detalles del producto:
Clase Lavadora
Marca: Whirlpool
Precio 109.9
Cantidad: 4
Capacidad de Lavado: 18Kg
Ciclos: 8
Velocidad: 1200

Unidades a facturar (MAX=4): 2

Importe a pagar: 246,18
Gracias por su compra

ELECTRODOMESTICOS ELECTROHOGAR
       MENU DE OPCIONES
1 = Listar Inventario
2 = Venta
3 = Facturacion
4 = Gestion Inventario
5 = Gestion Clientes
0 = Salir
Elija la opcion: 2

Indique el ID del Cliente
ID: 19034211
Cliente identificado:
[id= 19034211, nombre= Laura, apellidos= Juan, email= lajuanita@hotmail.com, telefono= 3451212]

Indique el CODIGO del electrodoméstico para realizar la venta.
CODIGO: COC-0350

Detalles del producto:
Clase Cocina
Marca: Miele
Precio: 109.9
Nº de Hornillas: 4
Con Horno
Sistema de Coccion: Induccion

Unidades a facturar (MAX=4): 1

Importe a pagar: 123,09
Gracias por su compra

ELECTRODOMESTICOS ELECTROHOGAR
       MENU DE OPCIONES
1 = Listar Inventario
2 = Venta
3 = Facturacion
4 = Gestion Inventario
5 = Gestion Clientes
0 = Salir
Elija la opcion: 5

      GESTION CLIENTES

1 == Añadir Cliente
2 == Eliminar Cliente
3 == Listar Clientes
0 == Regresar Menu Principal
Opcion: 3

      LISTADO DE CLIENTES

      ***********************
ID: 17183451
Nombre: Roberto Torres
E-Mail: roberto@hotmail.com Telf: 2134121
   FACTURACION
No hay historico de ventas
      ***********************
ID: 19034211
Nombre: Laura Juan
E-Mail: lajuanita@hotmail.com Telf: 3451212
   FACTURACION
Codigo: LAV-0090 Nombre: Renta Max Unidades compradas: 2 Importe: 246,18
Codigo: COC-0350 Nombre: Pyro Master Unidades compradas: 1 Importe: 123,09


El total de los importes es: 369,27

      ***********************

      GESTION CLIENTES

1 == Añadir Cliente
2 == Eliminar Cliente
3 == Listar Clientes
0 == Regresar Menu Principal
Opcion: 0

Regresando a menu principal...

ELECTRODOMESTICOS ELECTROHOGAR
       MENU DE OPCIONES
1 = Listar Inventario
2 = Venta
3 = Facturacion
4 = Gestion Inventario
5 = Gestion Clientes
0 = Salir
Elija la opcion: 0

      FIN DE PROGRAMA

142
te iba a preguntar el porque utilizaste esto en el codigo de factuar
Citar
acturacion.append(String.format("# ID. Cliente: %d Codigo: %s Nombre: %s Unidades vendidas: %d Importe: %.2f\n",
                     id, codigo, solicitado.getNombre(), unidades, facturar));
               totalFacturado += facturar;
               //Y también en la facturación personal del comprador
               comprador.facturar(String.format("Codigo: %s Nombre: %s Unidades compradas: %d Importe: %.2f\n",
                     codigo, solicitado.getNombre(), unidades, facturar));

hablo de los %d y %s eso no sabia el porque
Esos  %d y %s son caracteres comodines que se pueden usar construir cadenas con String.format()  o también con System.out.printf().

Esos comodines se sustituyen por las variables que hay separadas con comas, a continuación de la cadena que vamos a imprimir. Es una forma más rápida y cómoda de concatenar varias cadenas con muchas variables.
Un %s indica que será sustituido por un String.
Un %d indica que será sustituido por un valor entero del sistema decimal (el que usamos habitualmente.
Un %f indica que se sustituye por un valor en coma flotante, ya sea un float o un double.

Esos comodines se sustituyen según el orden de las variables que vienen a continuación.
Aquí relaciono con colores cada comodín y la variable que le corresponde:
Citar
comprador.facturar(String.format("Codigo: %s Nombre: %s Unidades compradas: %d Importe: %.2f\n",
                     codigo, solicitado.getNombre(), unidades, facturar));

No solo es sustituir, sirve sobre todo para dar formato
Si digo %.2f, estoy pidiendo que el float/double que lo va a sustituir, se muestre con solo dos decimales.

Si digo %20s, estoy diciendo que esa cadena ha de ocupar mínimo 20 caracteres. Si la cadena fuera más corta, el resto de caracteres se rellenará con espacios en blanco hasta sumar 20.

Esto son solo algunos ejemplos. Tienes muchos más por aquí.


Esta opción:
Citar
                 
                  break;
               case 4:

                  //Aqui seria la suma de todas las ventas totales es decir de cliente por cliente porque si un cliente lleva dos cosas debe incluirse en una solo instancia ha alguna forma ?
                 
                 
                  break;

Ya está hecha, cada venta se acumula en el atributo totalFacturado
Citar
public class Main {

      private static GestorClientes gestorCl = new GestorClientes();
      private static Inventario inventario = new Inventario();
      private static Scanner teclado = new Scanner(System.in);
      private static StringBuilder facturacion = new StringBuilder();
      private static double totalFacturado = 0;

Citar
                            //Dejamos constancia en la facturacion
                             
                            facturacion.append(String.format(" # ID. Cliente:%d \nCodigo: %s \nNombre: %s \nUnidades vendidas: %d \nImporte: %.2f\n",
                                  id, comprador.getNombres(), solicitado.getNombre(), unidades, facturar));
                            totalFacturado += facturar;
                            //Y también en la facturación personal del comprador


Sobre eliminar una venta ya hecha....
Olvídalo, al no poder usar ArrayList o bases de datos, nos vamos a encontrar con muchos límites.
Lo lógico sería tener una clase Venta, con algún identificador único para cada venta y poder añadir/eliminar de un ArrayList.

No tenemos nada de eso, las ventas las metemos en un StringBuilder para conseguir algo que se parezca mínimamente a un registro.
Se puede intentar complicarlo todo mucho más...., no se..., quizás haciendo que cada venta registrada, es decir, cada línea del StringBuilder comience por un número correlativo: el 1, el 2, el 3, el 4....
Y elegir que número de línea se quiere eliminar, y probar a ver si algún método de la clase StringBuilder o de la clase String permite eliminarla sin que afecte al resto de la cadena.

Aunque eso sería la menor de las complicaciones, porque luego habría que analizar esa línea que estamos eliminando para:
- identificar al cliente que hizo la compra y modificar su facturación personal.
- identificar el electrodoméstico comprado y corregir sus existencias, pues si se elimina la venta, las unidades vendidas vuelven a estar disponibles.
- identificar el importe que se pagó y restarlo del atributo totalFacturado.

Todo esto puede resultar más fácil con una clase Venta que pudiéramos guardar en ArrayList.
Pero mediante cadenas String..., el código se va a complicar muy innecesariamente..., cuando supongo que probablemente la intención principal del ejercicio, o una de las intenciones principales, es habituarse a manejar arreglos. Y no calentarse el cerebro en inventar formulas para poder usar cadenas String como si fueran registros.


Sobre simular un carrito de manera que en una venta el usuario pueda elegir varios productos para comprar a la vez.
De nuevo la cosa se puede complicar innecesariamente.
Habría que hacer un bucle que se repita hasta que el usuario diga si ha terminado de elegir productos.
En cada elección, pues quizás se pueda registrar esas elecciones como ventas, pero en una facturación "temporal", es decir, un StringBuilder que vaya creciendo con cada elección.
Lo mismo con el total facturado, usar una variable temporal para calcular el total de este proceso.

Cuando por fin diga que ha terminado de elegir productos, ahora sí lo que tenga ese StringBuilder temporal lo transferimos al StringBuilder de facturación de la empresa.
Y el importe total de esa venta, al atributo totalFacturado.
Y habrá que actualizar la facturación de ese cliente también con toda esa información.

Insisto en que al estar limitados con arreglos, también nos limita para muchos procesos. Y buscar la forma de saltar esos límites, puede ser más complicado de lo recomendable.

143
Lo primero, añadir como atributo el gestor de clientes
Citar
public class Main {
   
   private static GestorClientes gestorCl = new GestorClientes();
   private static Inventario inventario = new Inventario();
   private static Scanner teclado = new Scanner(System.in);
   private static StringBuilder facturacion = new StringBuilder();
   private static double totalFacturado = 0;
Ampliamos menú principal con la nueva funcionalidad:
Citar
   public static void main(String[] args) {
      
      int opcion = 0;
      
      do {
         System.out.println("\nELECTRODOMESTICOS ELECTROHOGAR");
         System.out.println("       MENU DE OPCIONES");
         System.out.println("1 = Listar Inventario");
         System.out.println("2 = Venta");
         System.out.println("3 = Facturacion");
         System.out.println("4 = Gestion Inventario");
         System.out.println("5 = Gestion Clientes");
         System.out.println("0 = Salir");
         System.out.print("Elija la opcion: ");
         opcion = Integer.parseInt(teclado.nextLine());
         
         switch(opcion) {
         case 1:
            listar();
            break;
         case 2:
            venta();
            break;
         case 3:
            facturacion();
            break;
         case 4:
            gestion();
            break;
         case 5:
            gestionClientes();
            break;

         case 0:
            System.out.println("\n\t\tFIN DE PROGRAMA");
            break;
         default:
            System.out.println("Opcion equivocada");
         }
      } while(opcion != 0);

Y tenemos que escribir el nuevo método gestionClientes().
Aquí ofreceremos un submenú de opciones: Añadir, eliminar y listar clientes
Código: [Seleccionar]

private static void gestionClientes() {
int opcion = 0;
do {
System.out.println("\n\t\tGESTION CLIENTES\n");
System.out.println("1 == Añadir Cliente");
System.out.println("2 == Eliminar Cliente");
System.out.println("3 == Listar Clientes");
System.out.println("0 == Regresar Menu Principal");
System.out.print("Opcion: ");
opcion = Integer.parseInt(teclado.nextLine());

switch(opcion) {
case 1:
System.out.println("\t\tNUEVO CLIENTE");
if (gestorCl.buscaPosicionLibrecl() == -1)
System.out.println("No hay espacio para añadir un nuevo Cliente");
else {
System.out.print("ID: ");
int nuevoId = Integer.parseInt(teclado.nextLine());
System.out.print("Nombre: ");
String nombre = teclado.nextLine();
System.out.print("Apellidos: ");
String apellidos = teclado.nextLine();
System.out.print("E-Mail: ");
String eMail = teclado.nextLine();
System.out.print("Telefono: ");
int telef = Integer.parseInt(teclado.nextLine());

if (gestorCl.agregarCliente(new Cliente(nuevoId, nombre, apellidos, eMail, telef)))
System.out.println("Nuevo Cliente registrado");
else
System.out.println("No se pudo registrar Cliente.\n"
+ "Puede que no haya espacio libre");
}
break;
case 2:
System.out.println("\nIndique ID del Cliente a eliminar.");
System.out.print("ID: ");
int id = Integer.parseInt(teclado.nextLine());
if (gestorCl.eliminarCliente(id))
System.out.println("\nCliente eliminado.");
else
System.out.println("\nNo existe Cliente con ID: " + id);
break;
case 3:
gestorCl.listarTodos();
break;
case 0:
System.out.println("\nRegresando a menu principal...");
break;
default:
System.out.println("Opcion equivocada");
}
}while(opcion != 0);
}

¿Y con esto ya queda implementa la entidad Cliente en nuestro programa?
Casi, pero no.
Falta modificar el proceso de venta de un electrodoméstico, de manera que ahora intervenga esta entidad.
Habrá que pedir el ID del cliente que va a hacer la compra, dejar constancia de dicho cliente en la "facturación general" de la empresa, y también en la facturación personal de este cliente.
Estos cambios los hacemos en el método venta().
Y nos va a quedar bastante gordito de código, pero es lo que hay...  ::)

Citar
   
   private static void venta() {
      //Pedimos Cliente que va a comprar
      System.out.println("\nIndique el ID del Cliente");
      System.out.print("ID: ");
      int id = Integer.parseInt(teclado.nextLine());
      //Comprobamos si existe
      Cliente comprador = gestorCl.getPorID(id);
      if (comprador == null) //No existe, aquí termina el proceso de venta
         System.out.println("No hay Cliente con el ID: " + id);
      else { //Si existe, continuamos con la venta
         System.out.println("Cliente identificado:\n" + comprador);

         //Pedimos Electrodomestico que se va a comprar
         System.out.println("\nIndique el CODIGO del electrodoméstico para realizar la venta.");
         System.out.print("CODIGO: ");
         String codigo = teclado.nextLine();
         //Comprobamos si existe
         Electrodomestico solicitado = inventario.getPorCodigo(codigo);
         if (solicitado == null) //No existe, aqui termina el proceso venta
            System.out.println("No hay electrodoméstico con el CODIGO: " + codigo);
         else { //Sí existe, seguimos adelante
            System.out.println("\nDetalles del producto:");
            solicitado.mostrarDetalles();
            //Pedimos cantidad de unidades deseadas, controlamos que nos de un valor válido.
            //Aunque primero comprobamos si hay stock disponible
            if (solicitado.getCantidad() <= 0)
               System.out.println("\nProducto agotado");
            else {
               int unidades = 0;
               do {
                  System.out.print("\nUnidades a facturar (MAX="+ solicitado.getCantidad() +"): ");
                  unidades = Integer.parseInt(teclado.nextLine());
               }while(unidades > solicitado.getCantidad());
               //Calculamos importe a facturar, actualizamos stock producto e informamos en pantalla
               double facturar = solicitado.calcularPrecioFinal(unidades);
               solicitado.setCantidad(solicitado.getCantidad() - unidades);
               System.out.printf("\nImporte a pagar: %.2f\n", facturar);
               System.out.println("Gracias por su compra");
               //Dejamos constancia en la facturacion
               facturacion.append(String.format("# ID. Cliente: %d Codigo: %s Nombre: %s Unidades vendidas: %d Importe: %.2f\n",
                     id, codigo, solicitado.getNombre(), unidades, facturar));
               totalFacturado += facturar;
               //Y también en la facturación personal del comprador
               comprador.facturar(String.format("Codigo: %s Nombre: %s Unidades compradas: %d Importe: %.2f\n",
                     codigo, solicitado.getNombre(), unidades, facturar));

            }
         }
      }
   }

Parece que ya está,¿no? Que emoción, vamos a probarlo.
Si intentamos una venta, nos vamos a llevar un chasco... :(
Citar
ELECTRODOMESTICOS ELECTROHOGAR
       MENU DE OPCIONES
1 = Listar Inventario
2 = Venta
3 = Facturacion
4 = Gestion Inventario
5 = Gestion Clientes
0 = Salir
Elija la opcion: 2

Indique el ID del Cliente
ID: 19034211
Exception in thread "main" java.lang.NullPointerException
   at electrodomesticos.GestorClientes.getPorID(GestorClientes.java:26)
   at electrodomesticos.Main.venta(Main.java:73)
   at electrodomesticos.Main.main(Main.java:34)

¿Qué ha pasado?
Pues que como todo buen programa que se precie, tiene sus bugs....
Hay un detalle que hemos pasado por alto en la gestión de los arreglos. Al principio no había problema porque escribimos el programa pensando en un arreglo que siempre iba a tener 10 electrodomésticos.
Pero luego añadimos la funcionalidad de poder eliminar electrodomésticos, así como ahora clientes.

Esto significa que ahora en nuestros arreglos vamos a tener posiciones con valor null. Entonces, a la hora de buscar elementos en ellos, o listar su contenido, tenemos que evitar las posiciones null. Ya que si intentamos preguntar cuál es el ID del cliente a una posición null, va a ocurrir esa excepción que nos ha salido en pantalla.

Toca corregir todos los métodos que hagan recorridos de los arreglos, excepto los métodos que buscan posiciones libres, ya que esos precisamente se dedican a buscar posiciones null y no van dar ningún problema.

Además, ahora hay que contemplar la posibilidad de que los arreglos sean completamente null y no tengamos ni clientes ni electrodomésticos para mostrar en pantalla.
Esto también deberíamos controlarlo para informar en pantalla correctamente.

En la clase GestorClientes, estos métodos se han de modificar para no actuar sobre posiciones null en el arreglo:
Citar
   
   public void listarTodos() {
      System.out.println("\n\t\tLISTADO DE CLIENTES");
      System.out.println("\n\t\t***********************");
      
      boolean arregloVacio = true;
      for (int i = 0; i < clientes.length; i++) {
         if (clientes[i ] != null) {
            arregloVacio = false;
            clientes[i ].mostrarDetalles();
            System.out.println("\t\t***********************");
         }
      }
      //Comprobamos si el arreglo resulta estar vacio
      if (arregloVacio)
         System.out.println("\nNo hay Clientes registrados");

   }
   
   public Cliente getPorID(int id) {
      
      for (int i = 0; i < clientes.length; i++) {
         if (clientes[i ] != null) {
            if (clientes[ i].getId() == id)
               return clientes[i ]; //Cliente encontrado, lo retornamos
         }
      }
      //Si bucle FOR termina sin retornar nada, es que no hay cliente con ese ID
      return null;
   }
   
   public boolean eliminarCliente(int id) {
      //Buscamos por codigo, si lo encontramos, asignamos valor null a la posicion.
      for (int i = 0; i < clientes.length; i++) {
         if (clientes[i ] != null) {
            if (clientes[i ].getId() == id) {
               clientes[i ] = null; //Cliente eliminado
               return true; //Informamos que se ha eliminado con exito
            }
         }
      }
      //Bucle FOR no ha retornado TRUE, no se ha encontrado cliente buscado
      return false;
   }

Y en la clase Inventario, con cuatro los métodos que se han de modificar:
Citar
   public void listarTodos() {
      System.out.println("\n\t\tLISTADO DE ELECTRODOMESTICOS");
      System.out.println("\n\t\t***********************");

      boolean arregloVacio = true;
      for (int i = 0; i < productos.length; i++) {
         if (productos[i ] != null) {
            arregloVacio = false;

            productos[i ].mostrarDetalles();
            System.out.println("\t\t***********************");
         }
      }
      //Comprobamos si el arreglo resulta estar vacio
      if (arregloVacio)
         System.out.println("\nNo hay Electrodomesticos registrados");

   }

   public void listarPorClase(String clase) {
      boolean encontrado = false; //Para controlar si se han encontrado productos de la clase solicitada
      System.out.println("\n\t\tLISTADO DE " + clase.toUpperCase());
      System.out.println("\n\t\t***********************");

      for (int i = 0; i < productos.length; i++) {
         if (productos[i ] != null) {
            if (productos[i ].getClass().toString().contains(clase)) {
               encontrado = true; //Se ha encontrado al menos un coincidente
               productos[i ].mostrarDetalles();
               System.out.println("\t\t***********************");
            }
         }
      }

      //Si no se encontró ningún producto de la clase solicitada, informamos
      if (!encontrado)
         System.out.println("No hay electrodomesticos de esta clase");
   }

   public Electrodomestico getPorCodigo(String codigo) {

      for (int i = 0; i < productos.length; i++) {
         if (productos[i ] != null) {
            if (productos[i ].getCodigo().equals(codigo))
               return productos[i ]; //Producto encontrado, lo retornamos
         }
      }
      //Si bucle FOR termina sin retornar nada, es que no hay producto con ese codigo
      return null;
   }

   public boolean eliminarElectrodomestico(String codigo) {
      //Buscamos por codigo, si lo encontramos, asignamos valor null a la posicion.
      for (int i = 0; i < productos.length; i++) {
         if (productos[i ] != null) {
            if (productos[i ].getCodigo().equals(codigo)) {
               productos[i ] = null; //Producto eliminado
               return true; //Informamos que se ha eliminado con exito
            }
         }
      }
      //Bucle FOR no ha retornado TRUE, no se ha encontrado el producto buscado
      return false;
   }

Ok, ahora sí.
Podemos mostrar listados aunque hayan posiciones null en los arreglos, y si hacemos una venta la facturación general registra el id del cliente y el cliente a su vez registra la venta en su facturación personal:
Citar
ELECTRODOMESTICOS ELECTROHOGAR
       MENU DE OPCIONES
1 = Listar Inventario
2 = Venta
3 = Facturacion
4 = Gestion Inventario
5 = Gestion Clientes
0 = Salir
Elija la opcion: 5

      GESTION CLIENTES

1 == Añadir Cliente
2 == Eliminar Cliente
3 == Listar Clientes
0 == Regresar Menu Principal
Opcion: 3

      LISTADO DE CLIENTES

      ***********************
ID: 17183451
Nombre: Roberto Torres
E-Mail: roberto@hotmail.com Telf: 2134121
   FACTURACION
No hay historico de ventas
      ***********************
ID: 19034211
Nombre: Laura Juan
E-Mail: lajuanita@hotmail.com Telf: 3451212
   FACTURACION
No hay historico de ventas
      ***********************

      GESTION CLIENTES

1 == Añadir Cliente
2 == Eliminar Cliente
3 == Listar Clientes
0 == Regresar Menu Principal
Opcion: 0

Regresando a menu principal...

ELECTRODOMESTICOS ELECTROHOGAR
       MENU DE OPCIONES
1 = Listar Inventario
2 = Venta
3 = Facturacion
4 = Gestion Inventario
5 = Gestion Clientes
0 = Salir
Elija la opcion: 2

Indique el ID del Cliente
ID: 19034211
Cliente identificado:
[id= 19034211, nombre= Laura, apellidos= Juan, email= lajuanita@hotmail.com, telefono= 3451212]

Indique el CODIGO del electrodoméstico para realizar la venta.
CODIGO: TLV-2338

Detalles del producto:
Clase Televisor
Marca: LG
Precio: 799.9
Cantidad: 17
Pulgadas: 34
Resolucion: 3840x2160
Tipo Pantalla: LED

Unidades a facturar (MAX=17): 3

Importe a pagar: 2687,66
Gracias por su compra

ELECTRODOMESTICOS ELECTROHOGAR
       MENU DE OPCIONES
1 = Listar Inventario
2 = Venta
3 = Facturacion
4 = Gestion Inventario
5 = Gestion Clientes
0 = Salir
Elija la opcion: 3

      REGISTRO DE FACTURACION

# ID. Cliente: 19034211 Codigo: TLV-2338 Nombre: Pentax Advance Unidades vendidas: 3 Importe: 2687,66

Total facturado: 2687,66

ELECTRODOMESTICOS ELECTROHOGAR
       MENU DE OPCIONES
1 = Listar Inventario
2 = Venta
3 = Facturacion
4 = Gestion Inventario
5 = Gestion Clientes
0 = Salir
Elija la opcion: 5

      GESTION CLIENTES

1 == Añadir Cliente
2 == Eliminar Cliente
3 == Listar Clientes
0 == Regresar Menu Principal
Opcion: 3

      LISTADO DE CLIENTES

      ***********************
ID: 17183451
Nombre: Roberto Torres
E-Mail: roberto@hotmail.com Telf: 2134121
   FACTURACION
No hay historico de ventas
      ***********************
ID: 19034211
Nombre: Laura Juan
E-Mail: lajuanita@hotmail.com Telf: 3451212
   FACTURACION
Codigo: TLV-2338 Nombre: Pentax Advance Unidades compradas: 3 Importe: 2687,66

      ***********************

      GESTION CLIENTES

1 == Añadir Cliente
2 == Eliminar Cliente
3 == Listar Clientes
0 == Regresar Menu Principal
Opcion: 0

Regresando a menu principal...

ELECTRODOMESTICOS ELECTROHOGAR
       MENU DE OPCIONES
1 = Listar Inventario
2 = Venta
3 = Facturacion
4 = Gestion Inventario
5 = Gestion Clientes
0 = Salir
Elija la opcion: 0

      FIN DE PROGRAMA

Y con esto parece que ya está.
Puede que probándolo a fondo aparezca algún otro bug. Si ocurriese, coméntalo aquí.
Un saludo.

144
Veamos.
La clase yo la llamaría Cliente, en singular. Porque esa clase va a representar la información de un cliente concreto. Si fuera a representar una colección de clientes, entonces si la pondría en plural.

Luego, el atributo "String pago", parece que quieres guardar la forma de pago del cliente.
Pero no tengo claro que sea necesario. Un cliente hoy puede pagar en efectivo, pero mañana con tarjeta, y al otro con transferencia bancaria...
Además no creo que sea un atributo asociable a una entidad Cliente, yo lo asociaría más bien a una entidad Factura..., es decir, que indique cómo se ha pagado una determinada factura.
Yo no usaría ese atributo en la clase Cliente, aunque por supuesto tú puedes dejarlo. Quizás se le ves un sentido que yo no.

Para anotar la facturación de un Cliente, debemos usar un StringBuilder, que nos da objetos dinámicos que permiten agregar cadenas de texto para luego convertirlas en un String normal.
La clase String es estática e inmutable. Se le puede añadir nuevas cadenas de texto, pero internamente lo que ocurre es que se crea un String nuevo cada vez que añadimos texto y los String "viejos" quedan en memoria sin referencia, son basura que ocupan recursos inútilmente.

Por eso es más óptimo usar como atributo un StringBuilder al que podemos llamar "facturacion", ya que contendrá lo que facturemos a este Cliente

Este atributo, en principio, no debería recibir valor por constructor, ya que en el momento de crear un nuevo Cliente, no va a tener ninguna facturación. El constructor lo que hará será inicializarlo y que luego pueda ir recibiendo "líneas de facturación".
Estas "líneas de facturación" las tendrá que recibir mediante un método, al que podemos llamar facturar().

Este atributo, no lo incluiría en el método toString(). Puede llegar a ser muy extenso si al cliente le facturamos muchas ventas. Además el método toString() no está pensado para mostrar TODA la información de un objeto, si no más bien mostrar los datos que consideremos más representativos.
En cambio, si podemos incluirlo en un método mostrarDetalles()
Código: [Seleccionar]
public class Cliente {

private int id;
private String nombres;
private String apellidos;
private String email;
private int telefono;
private StringBuilder facturacion; //Simula un histórico de ventas

public Cliente (int id, String nombres, String apellidos, String email, int telefono) {
this.id = id;
this.nombres = nombres;
this.apellidos = apellidos;
this.email=email;
this.telefono = telefono;
facturacion = new StringBuilder();
}

@Override
public String toString() {
return "[id= " + id + ", nombre= " + nombres + ", apellidos= " + apellidos + ", email= " + email
+ ", telefono= " + telefono + "]";
}

public int getId() {
return id;
}

public void setId(int id) {
this.id = id;
}

public String getNombres() {
return nombres;
}

public void setNombres(String nombres) {
this.nombres = nombres;
}

public String getApellidos() {
return apellidos;
}

public void setApellidos(String apellidos) {
this.apellidos = apellidos;
}

public String getEmail() {
return email;
}

public void setEmail(String email) {
this.email = email;
}

public int getTelefono() {
return telefono;
}

public void setTelefono(int telefono) {
this.telefono = telefono;
}

/*
* Retorna un String con las líneas de facturación
* contenidas en el StringBuilder
*/
public String getFacturacion() {
return facturacion.toString();
}

/*
* Añade una nueva línea de facturación
* al StringBuilder
*/
public void facturar(String linea) {
facturacion.append(linea);
}

public void mostrarDetalles() {
System.out.println("ID: " + id);
System.out.println("Nombre: " + nombres +  " " + apellidos);
System.out.println("E-Mail: " + email + " Telf: " + telefono);
System.out.println("\tFACTURACION");
System.out.println(facturacion.length()==0?"No hay historico de ventas":facturacion.toString());
}
}


Sobre la clase GestorClientes, veo que has puesto un método y el arreglo como static.
En esta clase, no vamos a necesitar ningún elemento que sea static.

Aquí el arreglo si lo podemos llamar "clientes" en plural, porque va a contener cierta cantidad de objetos Cliente.
Por lo demás no requiere más explicación, solo resta hacer un calco de los métodos que le pusimos a la clase Inventario, adaptándolo a la clase Cliente.
Por ejemplo, para listar clientes ya no preguntaremos de que clase, porque solo hay una.

Código: [Seleccionar]
public class GestorClientes {

private Cliente[] clientes;

public GestorClientes() {
clientes = new Cliente[10];
clientes[0] = new Cliente(17183451, "Roberto", "Torres", "roberto@hotmail.com", 2134121);
clientes[5] = new Cliente(19034211, "Laura", "Juan", "lajuanita@hotmail.com", 3451212);
}

public void listarTodos() {
System.out.println("\n\t\tLISTADO DE CLIENTES");
System.out.println("\n\t\t***********************");

for (int i = 0; i < clientes.length; i++) {
clientes[i].mostrarDetalles();
System.out.println("\t\t***********************");
}
}

public Cliente getPorID(int id) {

for (int i = 0; i < clientes.length; i++) {
if (clientes[i].getId() == id)
return clientes[i]; //Cliente encontrado, lo retornamos
}
//Si bucle FOR termina sin retornar nada, es que no hay cliente con ese ID
return null;
}

public boolean eliminarCliente(int id) {
//Buscamos por codigo, si lo encontramos, asignamos valor null a la posicion.
for (int i = 0; i < clientes.length; i++) {
if (clientes[i].getId() == id) {
clientes[i] = null; //Cliente eliminado
return true; //Informamos que se ha eliminado con exito
}
}
//Bucle FOR no ha retornado TRUE, no se ha encontrado cliente buscado
return false;
}


public int buscaPosicionLibrecl() {
for (int i = 0; i < clientes.length; i++) {
if (clientes[i] == null) //Esta posicion esta libre
return i; //Retornamos la posicion
}

return -1; //Significa que no hay posiciones libres
}

public boolean agregarCliente(Cliente nuevo) {
int libre = buscaPosicionLibrecl();

if (libre == -1)
return false; //No hay espacio para añadir nada
else {
clientes[libre] = nuevo; //Añadimos
return true; //Informamos que todo OK
}
}
}

Vale, pues toca integrar esto en la clase Main.
Lo vemos en el siguiente mensaje.

145
increíble muchísimas gracias si yo modifico para submenus eso si puedo, quería preguntarte por la observación de precio final como lo implemento porque al pegarlo en la parte precio final me da un error
Sobre el precio.
Yo lo que haría sería eliminar los métodos calcularSubtotal() y calcularIva().
Y escribiría el método calcularPrecioFinal() en la clase Electrodomestico de esta manera:
Código: [Seleccionar]
public double calcularPrecioFinal(int cantidad) {
return precio * cantidad * 1.12;
}

Por tanto deja de ser un método abstracto.
El único método que si interesa que se abstracto es mostrarDetalles(), porque este si va a ser distinto según cada clase hija.
Aunque de este método, eliminaría la línea que muestra el "valor a pagar por el electrodoméstico", ya que lo que mostraba era el precio de comprar TODAS las unidades existentes.

Y ya luego, en la clase Main donde hacemos la venta, ahora podemos calcular el precio final de esa venta llamando al nuevo método que hemos escrito:
Citar
            //Calculamos importe a facturar, actualizamos stock producto e informamos en pantalla
            double facturar = solicitado.calcularPrecioFinal(unidades);
            solicitado.setCantidad(solicitado.getCantidad() - unidades);
            System.out.printf("\nImporte a pagar: %.2f\n", facturar);
            System.out.println("Gracias por su compra");



hola me olvide decir que cual era la manera de hacer una funcion que agregue producto y elimine producto y como incluyo los datos de una persona me toca hacer una clase persona para eso ?  si una persona quiere comprar mas de 1 producto osea una cocina, una refrigerador y asi se puede integrar eso al programa?

Para hacer todo eso bien hecho, habría que usar ArrayList o una base de datos, es decir, colecciones dinámicas que puedan crecer o reducir su tamaño.
Como solo nos permiten usar arreglos, que no son los adecuados para estos menesteres, pues se puede intentar hacer algo parecido, algo que simule esas funcionalidades.

Añadir o eliminar productos. Como ya dijimos, estamos limitados a los elementos con los que inicializamos el arreglo.
Hemos puesto 10 y ese es nuestro límite.
Podemos poner 20, ó 100,  ó 1000.... eso nos da más margen, pero seguiremos teniendo un límite concreto.

Sin embargo, podemos hacer algo que parezca que añdimos/eliminamos productos. En el arreglo, para eliminar un producto podemos asignar valor null a la posición que ocupa y así tendríamos una posición libre para añadir otro producto.

Vamos a la clase Inventario, la que gestiona el arreglo, y la dotamos con estos tres métodos:
Código: [Seleccionar]

public boolean eliminarElectrodomestico(String codigo) {
//Buscamos por codigo, si lo encontramos, asignamos valor null a la posicion.
for (int i = 0; i < productos.length; i++) {
if (productos[i].getCodigo().equals(codigo)) {
productos[i] = null; //Producto eliminado
return true; //Informamos que se ha eliminado con exito
}
}
//Bucle FOR no ha retornado TRUE, no se ha encontrado el producto buscado
return false;
}

public int buscaPosicionLibre() {
for (int i = 0; i < productos.length; i++) {
if (productos[i] == null) //Esta posicion esta libre
return i; //Retornamos la posicion
}

return -1; //Significa que no hay posiciones libres
}

public boolean agregarElectrodomestico(Electrodomestico nuevo) {
int libre = buscaPosicionLibre();

if (libre == -1)
return false; //No hay espacio para añadir nada
else {
productos[libre] = nuevo; //Añadimos
return true; //Informamos que todo OK
}
}
No requieren demasiada explicaciones creo.
El primero elimina un producto según el código indicado.
El segundo busca una posición libre en arreglo y la retorna. Si no la hay, retorna -1.
El tercero añade un nuevo Electrodomestico al arreglo, si es que hay espacio libre.

Y ahora vamos al Main y ampliamos el menú con una nueva opción:
Citar
   public static void main(String[] args) {
      
      int opcion = 0;
      
      do {
         System.out.println("\nELECTRODOMESTICOS ELECTROHOGAR");
         System.out.println("       MENU DE OPCIONES");
         System.out.println("1 = Listar Inventario");
         System.out.println("2 = Venta");
         System.out.println("3 = Facturacion");
         System.out.println("4 = Gestion Inventario");
         System.out.println("0 = Salir");
         System.out.print("Elija la opcion: ");
         opcion = Integer.parseInt(teclado.nextLine());
         
         switch(opcion) {
         case 1:
            listar();
            break;
         case 2:
            venta();
            break;
         case 3:
            facturacion();
            break;
         case 4:
            gestion();
            break;

         case 0:
            System.out.println("\n\t\tFIN DE PROGRAMA");
            break;
         default:
            System.out.println("Opcion equivocada");
         }
      } while(opcion != 0);

   }

Y tenemos que escribir un nuevo método para llevar a cabo esta gestión.
Aquí mostraremos un submenú para que el usuario elija entre añadir un nuevo Electrodoméstico o eliminar uno existente.
Citar
   
   private static void gestion() {
      int opcion = 0;
      do {
         System.out.println("\n\t\tGESTION INVENTARIO\n");
         System.out.println("1 == Añadir Electrodomestico");
         System.out.println("2 == Eliminar Electrodomestico");
         System.out.println("0 == Regresar Menu Principal");
         System.out.print("Opcion: ");
         opcion = Integer.parseInt(teclado.nextLine());
         
         switch(opcion) {
         case 1:
            System.out.println("\nIndique la CLASE de Electrodomestico para añadir");
            System.out.print("CLASE: ");
            nuevoElectrodomestico(teclado.nextLine());
            break;
         case 2:
            System.out.println("\nIndique CODIGO del Electrodoméstico a eliminar.");
            System.out.print("CODIGO: ");
            String codigo = teclado.nextLine();
            if (inventario.eliminarElectrodomestico(codigo))
               System.out.println("\nElectrodomestico eliminado.");
            else
               System.out.println("\nNo existe electrodomestico con CODIGO: " + codigo);
            break;
         case 0:
            System.out.println("\nRegresando a menu principal...");
            break;
         default:
            System.out.println("Opcion equivocada");
         }
      }while(opcion != 0);
   }

He marcado en azul la invocación a otro método.
El proceso de crear un nuevo electrodoméstico requiere bastante código, porque hay que pedir muchos datos. Pero es que encima esos datos son distintos según la clase de electrodoméstico que vamos a registrar. Y hay cuatro diferentes....
Por eso mejor escribir todo esto en otro método independiente.

De nuevo, vamos a dar por hecho que el usuario nos va a dar una clase correcta, aunque vamos a ser "tolerantes" con las mayúsculas y minúsculas.
El método lo que hará será pedir primero los datos que son comunes a TODOS los electrodomésticos.
Y luego, según la clase indicada, pedirá los datos específicos de cada tipo de electrodoméstico.

Una vez recopilados esos datos, añadiremos el producto al inventario.
Pero, antes de hacer todo esto, comprobaremos primero si hay espacio libre, porque si no lo hay..., pues para que vamos a pedir ningún dato...
Código: [Seleccionar]

private static void nuevoElectrodomestico(String clase) {

//Comprobamos si hay espacio libre
if (inventario.buscaPosicionLibre() == -1)
System.out.println("\nNo hay espacio para añadir un nuevo Electrodomestico");
else {
Electrodomestico nuevo = null;
//Pedimos datos comunes
System.out.print("\nNombre: ");
String nombre = teclado.nextLine();
System.out.print("Codigo: ");
String codigo = teclado.nextLine();
System.out.print("Marca: ");
String marca = teclado.nextLine();
System.out.print("Cantidad: ");
int cantidad = Integer.parseInt(teclado.nextLine());
System.out.print("Precio: ");
double precio = Double.parseDouble(teclado.nextLine());

//Pedimos datos específicos, según la clase indicada
switch(clase.toLowerCase()) {
case "cocina":
System.out.print("Nº de Hornillas: ");
int hornillas = Integer.parseInt(teclado.nextLine());
System.out.print("Tiene Horno(Si/No): ");
boolean tieneHorno = teclado.nextLine().equalsIgnoreCase("si");
System.out.print("Sistema de Coccion: ");
String coccion = teclado.nextLine();
nuevo = new Cocina(nombre, codigo, marca, cantidad, precio, hornillas, tieneHorno, coccion);
break;
case "refrigeradora":
System.out.print("Pies: ");
double pies = Double.parseDouble(teclado.nextLine());
System.out.print("Nivel Congelamiento: ");
int nivel = Integer.parseInt(teclado.nextLine());
nuevo = new Refrigeradora(nombre, codigo, marca, cantidad, precio, pies, nivel);
break;
case "televisor":
System.out.print("Pulgadas: ");
int pulgadas = Integer.parseInt(teclado.nextLine());
System.out.print("Resolucion: ");
String resolucion = teclado.nextLine();
System.out.print("Tipo Pantalla: ");
String pantalla = teclado.nextLine();
nuevo = new Televisor(nombre, codigo, marca, cantidad, precio, pulgadas, resolucion, pantalla);
break;
case "lavadora":
System.out.print("Velocidad: ");
int velocidad = Integer.parseInt(teclado.nextLine());
System.out.print("Capacidad de Lavado: ");
String capacidad = teclado.nextLine();
System.out.print("Ciclos: ");
int ciclos = Integer.parseInt(teclado.nextLine());
nuevo = new Lavadora(nombre, codigo, marca, cantidad, precio, velocidad, capacidad, ciclos);
break;
}
//Nuevo electrodomestico construido, lo añadimos
if (nuevo == null)
System.out.println("\nERROR. No existen Electrodomesticos de la clase " + clase);
else if (inventario.agregarElectrodomestico(nuevo))
System.out.println("\nNuevo Electrodomestico registrado");
else
System.out.println("\nNo se pudo registrar Electrodomestico."
+ " Puede que no haya espacio libre en el Inventario.");
}

}


Y listo, si lo ponemos a prueba, vemos que al comenzar el programa no deja añadir nada porque no hay espacio libre.
Pero si eliminamos algo, ya podemos añadir nuevo electrodoméstico, y comprobarlo en el listado:
Citar
ELECTRODOMESTICOS ELECTROHOGAR
       MENU DE OPCIONES
1 = Listar Inventario
2 = Venta
3 = Facturacion
4 = Gestion Inventario
0 = Salir
Elija la opcion: 4

      GESTION INVENTARIO

1 == Añadir Electrodomestico
2 == Eliminar Electrodomestico
0 == Regresar Menu Principal
Opcion: 1

Indique la CLASE de Electrodomestico para añadir
CLASE: Lavadora

No hay espacio para añadir un nuevo Electrodomestico

      GESTION INVENTARIO

1 == Añadir Electrodomestico
2 == Eliminar Electrodomestico
0 == Regresar Menu Principal
Opcion: 2

Indique CODIGO del Electrodoméstico a eliminar.
CODIGO: LAV-0115

Electrodomestico eliminado.

      GESTION INVENTARIO

1 == Añadir Electrodomestico
2 == Eliminar Electrodomestico
0 == Regresar Menu Principal
Opcion: 1

Indique la CLASE de Electrodomestico para añadir
CLASE: lavadora

Nombre: Lavatronic 2000
Codigo: LAV-1010
Marca: Zanussi
Cantidad: 4
Precio: 119.90
Velocidad: 950
Capacidad de Lavado: 12Kg
Ciclos: 3

Nuevo Electrodomestico registrado

      GESTION INVENTARIO

1 == Añadir Electrodomestico
2 == Eliminar Electrodomestico
0 == Regresar Menu Principal
Opcion: 0

Regresando a menu principal...

ELECTRODOMESTICOS ELECTROHOGAR
       MENU DE OPCIONES
1 = Listar Inventario
2 = Venta
3 = Facturacion
4 = Gestion Inventario
0 = Salir
Elija la opcion: 1

Indique la clase que quiere listar.
Pulse ENTER con el campo vacio para listar todas las clases.
Clase: Lavadora

      LISTADO DE LAVADORA

      ***********************
Clase Lavadora
Marca: Whirlpool
Precio 109.9
Cantidad: 4
Capacidad de Lavado: 18Kg
Ciclos: 8
Velocidad: 1200
      ***********************
Clase Lavadora
Marca: Zanussi
Precio 119.9
Cantidad: 4
Capacidad de Lavado: 12Kg
Ciclos: 3
Velocidad: 950
      ***********************

ELECTRODOMESTICOS ELECTROHOGAR
       MENU DE OPCIONES
1 = Listar Inventario
2 = Venta
3 = Facturacion
4 = Gestion Inventario
0 = Salir
Elija la opcion: 0

      FIN DE PROGRAMA

Sobre lo de incluir una persona.
A ver, si quieres guardar un registro de "clientes", pues sí, necesitarás una clase Persona y una clase similar a Inventario que gestione un arreglo de tipo Persona.

Pero si solo quieres reflejar su nombre y algún dato más en la facturación, pues bastaría con pedirlos en el momento de la venta e incluirlos en la línea que se añade al StringBuilder en cada venta.

Hombre, la primera opción es más interesante.
Una clase que podría llamarse Agenda y gestione un arreglo de clase Persona.
Que haga lo mismo que Inventario: añadir personas, eliminar, listar todas las personas, listar por DNI/cédula, .....

Incluso, la clase Persona que escribas, podría incluir como atributo otro StringBuilder como el que usamos en la clase Main.
Así cada Persona tendría su propio registro de lo que se le factura y por tanto tendrías la posibilidad de mostrar TODA la facturación (la registrada en el Main) o solo la facturación de un determinado cliente(la registrada en clase Persona)

Todo esto supone hacer crecer más el código con más menús, más clases, más líneas en el proceso de venta (pedir un cliente, comprobar que existe, registrarle lo que se le factura...)

Si tienes tiempo y ganas, adelante... ;D

146
Bueno, pues lo siguiente es pensar en la opción de Facturacion.
Aquí deberíamos mostrar un informe de las ventas que se han realizado durante el día.
Lo ideal sería usar un ArrayList, porque no hay forma de saber cuántas ventas se van a hacer en cada sesión y por tanto usar un arreglo sería un fastidio porque hay que darle un tamaño fijo.
Pero como nos han vetado el uso de ArrayList  :P, hay que pensar otra alternativa.

Lo que podemos hacer es construir un String con el detalle de cada venta. Tras vender un producto, añadiremos una línea nueva a ese String.
Además podemos utilizar un acumulador de tipo double para sumar el total facturado.
Para construir el String es mejor usar la clase StringBuilder.

Así que añadimos estos dos nuevos atributos a la clase Main
Citar
public class Main {
   
   private static Inventario inventario = new Inventario();
   private static Scanner teclado = new Scanner(System.in);
   private static StringBuilder facturacion = new StringBuilder();
   private static double totalFacturado = 0;

Y vamos a hacer unos cambios en el método venta(), pues ahora queremos que cada venta realizada quede reflejada en la facturación:
Citar
   private static void venta() {
      System.out.println("Indique el CODIGO del electrodoméstico para realizar la venta.");
      System.out.print("CODIGO: ");
      String codigo = teclado.nextLine();
      
      Electrodomestico solicitado = inventario.getPorCodigo(codigo);
      if (solicitado == null)
         System.out.println("No hay electrodoméstico con el CODIGO: " + codigo);
      else {
         System.out.println("\nDetalles del producto:");
         solicitado.mostrarDetalles();
         //Pedimos cantidad de unidades deseadas, controlamos que nos de un valor válido.
         //Aunque primero comprobamos si hay stock disponible
         if (solicitado.getCantidad() <= 0)
            System.out.println("\nProducto agotado");
         else {
            int unidades = 0;
            do {
               System.out.print("\nUnidades a facturar (MAX="+ solicitado.getCantidad() +"): ");
               unidades = Integer.parseInt(teclado.nextLine());
            }while(unidades > solicitado.getCantidad());
            //Calculamos importe a facturar, actualizamos stock producto e informamos en pantalla
            double facturar = solicitado.getPrecio() * unidades * 1.12;
            solicitado.setCantidad(solicitado.getCantidad() - unidades);
            System.out.printf("\nImporte a pagar: %.2f\n", facturar);
            System.out.println("Gracias por su compra");
            //Dejamos constancia en la facturacion
            facturacion.append(String.format("# Codigo: %s Nombre: %s Unidades vendidas: %d Importe: %.2f\n",
                  codigo, solicitado.getNombre(), unidades, facturar));
            totalFacturado += facturar;

         }
      }
   }


Y con estos cambios, ya podemos escribir el método facturacion(), que básicamente lo único que tiene que hacer es mostrar lo que hemos ido registrando en el StringBuilder.

Código: [Seleccionar]

private static void facturacion() {
System.out.println("\n\t\tREGISTRO DE FACTURACION\n");
if (totalFacturado == 0)
System.out.println("No se han registrado ventas.");
else {
System.out.println(facturacion.toString());
System.out.printf("Total facturado: %.2f\n", totalFacturado);
}
}

Probémoslo. Hagamos unas cuantas ventas y a ver cómo queda registrado:
Citar
ELECTRODOMESTICOS ELECTROHOGAR
       MENU DE OPCIONES
1 = Listar Inventario
2 = Venta
3 = Facturacion
0 = Salir
Elija la opcion: 2
Indique el CODIGO del electrodoméstico para realizar la venta.
CODIGO: COC-0100

Detalles del producto:
Clase Cocina
Marca: Balay
Precio: 89.9
Nº de Hornillas: 2
Con Horno
Sistema de Coccion: Gas
Valor a pagar por la cocina:604.1280000000002

Unidades a facturar (MAX=6): 3

Importe a pagar: 302,06
Gracias por su compra

ELECTRODOMESTICOS ELECTROHOGAR
       MENU DE OPCIONES
1 = Listar Inventario
2 = Venta
3 = Facturacion
0 = Salir
Elija la opcion: 2
Indique el CODIGO del electrodoméstico para realizar la venta.
CODIGO: COC-0350

Detalles del producto:
Clase Cocina
Marca: Miele
Precio: 109.9
Nº de Hornillas: 4
Con Horno
Sistema de Coccion: Induccion
Valor a pagar por la cocina:492.35200000000003

Unidades a facturar (MAX=4): 1

Importe a pagar: 123,09
Gracias por su compra

ELECTRODOMESTICOS ELECTROHOGAR
       MENU DE OPCIONES
1 = Listar Inventario
2 = Venta
3 = Facturacion
0 = Salir
Elija la opcion: 2
Indique el CODIGO del electrodoméstico para realizar la venta.
CODIGO: TLV-2338

Detalles del producto:
Clase Televisor
Marca: LG
Precio: 799.9
Cantidad: 17
Pulgadas: 34
Resolucion: 3840x2160
Tipo Pantalla: LED
Valor a pagar por el Televisor:15230.096

Unidades a facturar (MAX=17): 4

Importe a pagar: 3583,55
Gracias por su compra

ELECTRODOMESTICOS ELECTROHOGAR
       MENU DE OPCIONES
1 = Listar Inventario
2 = Venta
3 = Facturacion
0 = Salir
Elija la opcion: 2
Indique el CODIGO del electrodoméstico para realizar la venta.
CODIGO: REF-2909

Detalles del producto:
Clase Refrigeradora
Marca: FAF
Precio: 139.9
Cantidad: 5
Pies: 22.0
Nivel Congelamiento: -25
Valor a pagar por la refrigeradora:783.44

Unidades a facturar (MAX=5): 1

Importe a pagar: 156,69
Gracias por su compra

ELECTRODOMESTICOS ELECTROHOGAR
       MENU DE OPCIONES
1 = Listar Inventario
2 = Venta
3 = Facturacion
0 = Salir
Elija la opcion: 3

      REGISTRO DE FACTURACION

# Codigo: COC-0100 Nombre: Fogo Prime Unidades vendidas: 3 Importe: 302,06
# Codigo: COC-0350 Nombre: Pyro Master Unidades vendidas: 1 Importe: 123,09
# Codigo: TLV-2338 Nombre: Pentax Advance Unidades vendidas: 4 Importe: 3583,55
# Codigo: REF-2909 Nombre: Cooler Veritas Unidades vendidas: 1 Importe: 156,69

Total facturado: 4165,39

ELECTRODOMESTICOS ELECTROHOGAR
       MENU DE OPCIONES
1 = Listar Inventario
2 = Venta
3 = Facturacion
0 = Salir
Elija la opcion: 0

      FIN DE PROGRAMA

Bueno, no es muy elegante pero funciona. Ahora ya si se quiere, se puede modificar para que quede mejor.


El caso es que con todo esto, pues ya hemos hecho algo parecido a un programa de gestión de ventas usando un arreglo.

Hay cosas a revisar. Como esto que comenté anteriormente:
Citar
Bien, yo ahora tengo ciertas reservas con los métodos heredados:
calcularPrecioFinal(), calcularSubtotal() y calcularIva().

Estos métodos hacen unos cálculos que SOLO dependen de los atributos de la superclase Electrodomestico, no influyen los atributos que puedan tener las clases hijas. Por tanto, no habría sido necesario declararlos como abstractos para que los sobrescriban las clases hijas.
Podrían haberse escrito directamente en la clase madre, ya que solo dependen de esta para sus cálculos.

Luego, el subtotal y el precio que calculan estos métodos, lo hacen sobre las cantidades existentes de cada electrodoméstico.
No nos van a decir el precio por comprar UNA cocina, si no que nos dirán el precio a pagar por comprar TODAS las cocinas que haya en la tienda.

Quizás el método calcularPrecioFinal() convenga reescribirlo para que reciba como argumento las unidades que se quieren vender y haga el cálculo sobre estas unidades, y no sobre el total disponible en el inventario.
Así ese método haría lo que yo he hice en esta línea, cuando hacemos una venta y calculamos el importe a pagar:
Citar
            //Calculamos importe a facturar, actualizamos stock producto e informamos en pantalla
            double facturar = solicitado.getPrecio() * unidades * 1.12;
            solicitado.setCantidad(solicitado.getCantidad() - unidades);
            System.out.printf("\nImporte a pagar: %.2f\n", facturar);

Pero bueno, eso ya lo puedes ir valorando tú mismo y si tienes más dudas o quieres añadir más funciones al programa y aún no sabes como proceder, pues consultalo por aquí.

Saludos.

147
Veamos.
Yo ahora haría dos clases más, una que gestione un arreglo de Electrodomesticos y la clase Main principal que ponga en marcha el programa.

La clase que gestione el arreglo, es decir, la que controla los productos que hay disponibles en tienda la podríamos llamar Inventario(cualquier otro nombre será igual de válido)

Tendrá como atributo el arreglo de electrodomésticos y los métodos que podamos necesitar para hacer distintas gestiones.
Esos método ya los iremos viendo sobre la marcha, de momento, podemos comenzar la clase con el arreglo iniciado para 10 elementos y en el constructor iniciamos esos elementos con distintos productos para tener ya cosas disponibles en el inventario.

Código: [Seleccionar]
public class Inventario {

private Electrodomestico[] productos;

public Inventario() {
//Inicializamos el arreglo con capacidad para 10 electrodomesticos
productos = new Electrodomestico[10];
//Añadimos electrodomesticos al inventario
productos[0] = new Cocina("Fogo Prime","COC-0100", "Balay", 6, 89.90, 2, true, "Gas");
productos[1] = new Televisor("Visio Suprem", "TLV-1221", "Sharp", 12, 899.90, 65, "3840x2160", "IPS");
productos[2] = new Refrigeradora("Ultra Frost", "REF-3810", "Balay", 2, 129.90, 18, -18);
productos[3] = new Lavadora("Renta Max", "LAV-0090", "Whirlpool", 4, 109.90, 1200, "18Kg", 8);
productos[4] = new Cocina("Pyro Master","COC-0350", "Miele", 4, 109.90, 4, true, "Induccion");
productos[5] = new Televisor("Pentax Basic", "TLV-2334", "LG", 23, 399.90, 34, "2560x1440", "VA");
productos[6] = new Refrigeradora("Cooler Veritas", "REF-2909", "FAF", 5, 139.90, 22, -25);
productos[7] = new Lavadora("Petit Eco", "LAV-0115", "Corbero", 10, 79.90, 800, "12Kg", 5);
productos[8] = new Cocina("Fuoco Chef","COC-3200", "Schmidt", 12, 69.90, 2, false, "Gas");
productos[9] = new Televisor("Pentax Advance", "TLV-2338", "LG", 17, 799.90, 34, "3840x2160", "LED");
}

}

Insisto en el detalle importante de que, al tratarse de un arreglo, no podemos añadir o eliminar elementos. Siempre serán 10.
Esos elementos se podrían ir sustituyendo por otros, pero el tamaño del arreglo no cambiará.

Vamos a comenzar también la clase Main, así iremos desarrollando estas dos clases  al mismo tiempo.

La clase principal tendrá como atributo un objeto Inventario y un Scanner.
Mostraremos un menú en pantalla y según la opción que elija el usuario, al inventario le iremos pidiendo distintas gestiones.

Comenzamos así:
Código: [Seleccionar]
public class Main {

private static Inventario inventario = new Inventario();
private static Scanner teclado = new Scanner(System.in);

public static void main(String[] args) {

int opcion = 0;

do {
System.out.println("\nELECTRODOMESTICOS ELECTROHOGAR");
System.out.println("       MENU DE OPCIONES");
System.out.println("1 = Listar Inventario");
System.out.println("2 = Venta");
System.out.println("3 = Facturacion");
System.out.println("0 = Salir");
System.out.print("Elija la opcion: ");
opcion = Integer.parseInt(teclado.nextLine());

switch(opcion) {
case 1:
listar();
break;
case 2:
venta();
break;
case 3:
facturacion();
break;
case 0:
System.out.println("\n\t\tFIN DE PROGRAMA");
break;
default:
System.out.println("Opcion equivocada");
}
} while(opcion != 0);

}

private static void listar() {
//TODO código para listado
}

private static void venta() {
//TODO código para ventas
}

private static void facturacion() {
//TODO código para facturacion
}
}
Fíjate que en el switch no ejecutamos código directamente, si no que llamaremos a distintos métodos, aún no escritos, que estarán separados del método main(), así este queda más legible al no sobrecargarlo con código.

La primera opción que he puesto es "Listar Inventario", es decir, mostrar en pantalla los productos que hay inventariados.
Podemos hacer que el usuario disponga de dos tipos de listado: un "listado total" donde se muestre todo, y otro donde el usuario indique que clase de electrodoméstico quiere ver: solo Lavadoras, o solo Televisores, etc..

Este podría ser el código del método listar():
Citar
   
   private static void listar() {
      System.out.println("\nIndique la clase que quiere listar.");
      System.out.println("Pulse ENTER con el campo vacio para listar todas las clases.");
      System.out.print("Clase: ");
      String clase = teclado.nextLine();
      
      if (clase.isBlank())
         inventario.listarTodos();
      else
         inventario.listarPorClase(clase);
   }

En azul marco las dos peticiones que le vamos a hacer a la clase Inventario, a la cuál ahora tenemos que añadirle dos métodos que cumplan con esas tareas.
Un método listará en pantalla todo lo que encuentre en el arreglo de electrodomésticos.
El otro método, hará un filtrado y solo mostrara los electrodomésticos que coincidan con la clase que hay indicado el usuario.
Estos son los dos métodos que le añadimos a la clase Inventario:
Código: [Seleccionar]

public void listarTodos() {
System.out.println("\n\t\tLISTADO DE ELECTRODOMESTICOS");
System.out.println("\n\t\t***********************");

for (int i = 0; i < productos.length; i++) {
productos[i].mostrarDetalles();
System.out.println("\t\t***********************");
}
}

public void listarPorClase(String clase) {
boolean encontrado = false; //Para controlar si se han encontrado productos de la clase solicitada
System.out.println("\n\t\tLISTADO DE " + clase.toUpperCase());
System.out.println("\n\t\t***********************");

for (int i = 0; i < productos.length; i++) {
if (productos[i].getClass().toString().contains(clase)) {
encontrado = true; //Se ha encontrado al menos un coincidente
productos[i].mostrarDetalles();
System.out.println("\t\t***********************");
}
}

//Si no se encontró ningún producto de la clase solicitada, informamos
if (!encontrado)
System.out.println("No hay electrodomesticos de esta clase");
}

Fíjate que para saber la clase de un elemento, llamamos al método getClass(). Este método devuelve en realidad otro objeto complejo, pero si a continuación llamamos a su método toString(), ya tenemos una cadena con el nombre de la clase que podemos utilizar para saber si contiene el nombre de la clase que el usuario ha indicado por teclado.
Para no complicar más el código, vamos a dar por hecho que el usuario va a escribir los nombres de clases correctamente, es decir, con la primera letra en mayúscula.
Si hacemos una prueba de ejecución y pedimos mostrar la clase "Televisor", funciona correctamente:
Citar
ELECTRODOMESTICOS ELECTROHOGAR
       MENU DE OPCIONES
1 = Listar Inventario
2 = Venta
3 = Facturacion
0 = Salir
Elija la opcion: 1

Indique la clase que quiere listar.
Pulse ENTER con el campo vacio para listar todas las clases.
Clase: Televisor

      LISTADO DE TELEVISOR

      ***********************
Clase Televisor
Marca: Sharp
Precio: 899.9
Cantidad: 12
Pulgadas: 65
Resolucion: 3840x2160
Tipo Pantalla: IPS
Valor a pagar por el Televisor:12094.655999999999
      ***********************
Clase Televisor
Marca: LG
Precio: 399.9
Cantidad: 23
Pulgadas: 34
Resolucion: 2560x1440
Tipo Pantalla: VA
Valor a pagar por el Televisor:10301.423999999999
      ***********************
Clase Televisor
Marca: LG
Precio: 799.9
Cantidad: 17
Pulgadas: 34
Resolucion: 3840x2160
Tipo Pantalla: LED
Valor a pagar por el Televisor:15230.096
      ***********************

ELECTRODOMESTICOS ELECTROHOGAR
       MENU DE OPCIONES
1 = Listar Inventario
2 = Venta
3 = Facturacion
0 = Salir
Elija la opcion: 0

      FIN DE PROGRAMA

Pero si escribimos mal el nombre, no encontrará la clase que pedimos:
Citar
ELECTRODOMESTICOS ELECTROHOGAR
       MENU DE OPCIONES
1 = Listar Inventario
2 = Venta
3 = Facturacion
0 = Salir
Elija la opcion: 1

Indique la clase que quiere listar.
Pulse ENTER con el campo vacio para listar todas las clases.
Clase: televisor

      LISTADO DE TELEVISOR

      ***********************
No hay electrodomesticos de esta clase

ELECTRODOMESTICOS ELECTROHOGAR
       MENU DE OPCIONES
1 = Listar Inventario
2 = Venta
3 = Facturacion
0 = Salir
Elija la opcion: 0

      FIN DE PROGRAMA
Como digo, con más código podríamos hacer el programa más "tolerante" a errores por parte del usuario, pero por ahora no lo vamos a complicar más.

Pensemos ahora en cómo hacer una venta.
Puesto que ya hay una opción para listar el inventario, incluso filtrando por el tipo de electrodoméstico, vamos a dar por sentado de que el usuario ya está en disposición de indicarnos el código de producto exacto del que quiere hacer una venta.
No será necesario mostrar un submenú para que elija qué tipo de electrodoméstico quiere.
Luego más adelante, si tu quieres hacerlo porque que queda más vistoso o lo que sea, pues ya lo modificarás.
Pero por ahora hagamos que todo funcione de la forma más simple posible.

Así que pedimos un código de producto al usuario. ¿Qué hacemos con ese código?
Pues le pediremos al Inventario que nos retorne una referencia al electrodoméstico que tenga ese código.
Con esa referencia, podemos mostrar en pantalla los detalles y preguntar cuántas unidades quiere vender.
A continuación calculamos el importe de la venta, según la cantidad indicada.
Restamos las unidades vendidas para que conste en el inventario y mostramos en pantalla el importe a facturar.

Pues este podría ser el código del método venta() de la clase Main:
Citar
   
   private static void venta() {
      System.out.println("Indique el CODIGO del electrodoméstico para realizar la venta.");
      System.out.print("CODIGO: ");
      String codigo = teclado.nextLine();
      
      Electrodomestico solicitado = inventario.getPorCodigo(codigo);
      if (solicitado == null)
         System.out.println("No hay electrodoméstico con el CODIGO: " + codigo);
      else {
         System.out.println("\nDetalles del producto:");
         solicitado.mostrarDetalles();
         //Pedimos cantidad de unidades deseadas, controlamos que nos de un valor válido.
         //Aunque primero comprobamos si hay stock disponible
         if (solicitado.getCantidad() <= 0)
            System.out.println("\nProducto agotado");
         else {
            int unidades = 0;
            do {
               System.out.print("\nUnidades a facturar (MAX="+ solicitado.getCantidad() +"): ");
               unidades = Integer.parseInt(teclado.nextLine());
            }while(unidades > solicitado.getCantidad());
            //Calculamos importe a facturar, actualizamos stock producto e informamos en pantalla
            double facturar = solicitado.getPrecio() * unidades * 1.12;
            solicitado.setCantidad(solicitado.getCantidad() - unidades);
            System.out.printf("\nImporte a pagar: %.2f\n", facturar);
            System.out.println("Gracias por su compra");
         }
      }
   }

En azul marco la petición que le hacemos al Inventario. Será un método que reciba un String con el código del producto solicitado y si lo encuentra, lo retornará.
Si no lo encuentra, retornará valor null.

Este sería el método que hay que añadirle a la clase Inventario:
Código: [Seleccionar]

public Electrodomestico getPorCodigo(String codigo) {

for (int i = 0; i < productos.length; i++) {
if (productos[i].getCodigo().equals(codigo))
return productos[i]; //Producto encontrado, lo retornamos
}
//Si bucle FOR termina sin retornar nada, es que no hay producto con ese codigo
return null;
}

Si hacemos una prueba de venta, podemos ver que solo deja facturar una cantidad menor o igual a lo que haya disponible.
Y que tras la venta, la disponibilidad disminuye. Y si se agota el producto, pues no se puede facturar nada.
Citar
ELECTRODOMESTICOS ELECTROHOGAR
       MENU DE OPCIONES
1 = Listar Inventario
2 = Venta
3 = Facturacion
0 = Salir
Elija la opcion: 2
Indique el CODIGO del electrodoméstico para realizar la venta.
CODIGO: LAV-0115

Detalles del producto:
Clase Lavadora
Marca: Corbero
Precio 79.9
Cantidad: 10
Capacidad de Lavado: 12Kg
Ciclos: 5
Velocidad: 800
Valor a pagar por la Lavadora:894.88

Unidades a facturar (MAX=10): 23

Unidades a facturar (MAX=10): 10

Importe a pagar: 894,88
Gracias por su compra

ELECTRODOMESTICOS ELECTROHOGAR
       MENU DE OPCIONES
1 = Listar Inventario
2 = Venta
3 = Facturacion
0 = Salir
Elija la opcion: 2
Indique el CODIGO del electrodoméstico para realizar la venta.
CODIGO: LAV-0115

Detalles del producto:
Clase Lavadora
Marca: Corbero
Precio 79.9
Cantidad: 0
Capacidad de Lavado: 12Kg
Ciclos: 5
Velocidad: 800
Valor a pagar por la Lavadora:0.0

Producto agotado

ELECTRODOMESTICOS ELECTROHOGAR
       MENU DE OPCIONES
1 = Listar Inventario
2 = Venta
3 = Facturacion
0 = Salir
Elija la opcion: 0

      FIN DE PROGRAMA

Continúo en el mensaje siguiente.

148
Vale, voy a intentar reescribir tu programa clase a clase, corrigiendo algunas cosas que no son correctas o que pueden resultar en una mala práctica.
Por ejemplo, los nombres de las clases deberían comenzar con mayúscula y los nombres de variables/atributos en minúscula.

Comencemos por la clase Electrodomestico. Como he dicho, nombre de clase en mayúscula y atributos en minúscula.
También conviene evitar nombres de atributos largos siempre que sea posible. En lugar de usa "nombreDelProducto", puede bastar con usar simplemente "nombre". Como es un atributo, ya se sobre entiende que es el nombre del electrodoméstico.

Luego, el constructor que no recibe parámetros, en lugar de dejarlo vacío, es mejor inicializar los atributos con unos valores por defecto. Aunque sean cadenas vacías para los String y valor 0 para los atributos numéricos.

Y ya que en las clases hijas vas a incluirles el método toString(), yo también se la incluiría a esta clase madre. Para que luego las clases hijas puedan mostrar la información completa, la que heredan de su madre y la suya propia.

Código: [Seleccionar]
public abstract class Electrodomestico {

private String nombre;
private String codigo;
private String marca;
private int cantidad;
private double precio;

public Electrodomestico() {
nombre = "";
codigo = "";
marca = "";
cantidad = 0;
precio = 0;
}

public Electrodomestico(String nombre, String codigo, String marca, int cantidad, double precio) {
this.nombre = nombre;
this.codigo = codigo;
this.marca = marca;
this.cantidad = cantidad;
this.precio = precio;
}

public String getNombre() {
return nombre;
}

public void setNombre(String nombre) {
this.nombre = nombre;
}

public String getCodigo() {
return codigo;
}

public void setCodigo(String codigo) {
this.codigo = codigo;
}

public String getMarca() {
return marca;
}

public void setMarca(String marca) {
this.marca = marca;
}

public int getCantidad() {
return cantidad;
}

public void setCantidad(int cantidad) {
this.cantidad = cantidad;
}

public double getPrecio() {
return precio;
}

public void setPrecio(double precio) {
this.precio = precio;
}

public abstract double calcularPrecioFinal();
public abstract double calcularSubtotal();
public abstract double calcularIva();
public abstract void mostrarDetalles();

@Override
public String toString() {
return "[nombre=" + nombre + ", codigo=" + codigo + ", marca=" + marca + ", cantidad="
+ cantidad + ", precio=" + precio + "]";
}

}


La clase Cocina.
Veo que le has puesto un atributo para "código". Pero esta clase ya tiene ese atributo porque lo está heredando de Electrodomestico, así que ponérselo también a Cocina es redundante. Mejor no ponerlo.

También hay que inicializar los atributos en su constructor sin parámetros, además de llamar al constructor de su clase madre con la instrucción super()

En su método toString() también incluiremos lo que retorna el toString de su "superclase".

Bien, yo ahora tengo ciertas reservas con los métodos heredados:
calcularPrecioFinal(), calcularSubtotal() y calcularIva().

Estos métodos hacen unos cálculos que SOLO dependen de los atributos de la superclase Electrodomestico, no influyen los atributos que puedan tener las clases hijas. Por tanto, no habría sido necesario declararlos como abstractos para que los sobrescriban las clases hijas.
Podrían haberse escrito directamente en la clase madre, ya que solo dependen de esta para sus cálculos.

Luego, el subtotal y el precio que calculan estos métodos, lo hacen sobre las cantidades existentes de cada electrodoméstico.
No nos van a decir el precio por comprar UNA cocina, si no que nos dirán el precio a pagar por comprar TODAS las cocinas que haya en la tienda.

Voy a dejar estos métodos tal y como tú los has planteado y seguir avanzando con el ejercicio. Pero cuando el programa esté terminado, quizás quieras revisar si son esos los cálculos que te interesan hacer.

Código: [Seleccionar]
public class Cocina extends Electrodomestico {

private int hornillas;
private boolean tieneHorno;
private String sistemaDeCoccion;

public Cocina() {
super();
hornillas = 0;
tieneHorno = false;
sistemaDeCoccion = "";
}

public Cocina(String nombre, String codigo, String marca, int cantidad, double precio,
int hornillas, boolean tieneHorno, String sistemaDeCoccion) {
super(nombre, codigo, marca, cantidad, precio);
this.hornillas = hornillas;
this.tieneHorno = tieneHorno;
this.sistemaDeCoccion = sistemaDeCoccion;
}

public int getHornillas() {
return hornillas;
}

public void setHornillas(int hornillas) {
this.hornillas = hornillas;
}

public boolean tieneHorno() {
return tieneHorno;
}

public void setHorno(boolean tieneHorno) {
this.tieneHorno = tieneHorno;
}

public String getSistemaDeCoccion() {
return sistemaDeCoccion;
}

public void setSistemaDeCoccion(String sistemaDeCoccion) {
this.sistemaDeCoccion = sistemaDeCoccion;
}

@Override
public double calcularPrecioFinal() {
return calcularSubtotal() + calcularIva();
}

@Override
public double calcularSubtotal() {
return getPrecio() * getCantidad();
}

@Override
public double calcularIva() {
return calcularSubtotal() * 0.12;
}

@Override
public void mostrarDetalles() {
System.out.println("Clase Cocina");
    System.out.println("Marca: " + getMarca());
    System.out.println("Precio: " + getPrecio());
    System.out.println("Nº de Hornillas: " + hornillas);
    System.out.println(tieneHorno?"Con Horno":"Sin Horno");
    System.out.println("Sistema de Coccion: " + sistemaDeCoccion);
    System.out.println("Valor a pagar por la cocina:" + calcularPrecioFinal());
}

@Override
public String toString() {
return "Cocina [" + super.toString() + " hornillas=" + hornillas +
", horno=" + tieneHorno + ", sistemaDeCoccion=" + sistemaDeCoccion + "]";
}

}

Estos mismos cambios que hemos hecho a Cocina, los aplicamos al resto de clases hijas:
Refrigeradora
Código: [Seleccionar]
public class Refrigeradora extends Electrodomestico {

private double pies;
private int nivelDeCongelamiento;

public Refrigeradora() {
super();
pies = 0;
nivelDeCongelamiento = 0;
}

public Refrigeradora(String nombre, String codigo, String marca, int cantidad, double precio,
double pies, int nivelDeCongelamiento) {
super(nombre, codigo, marca, cantidad, precio);
this.pies = pies;
this.nivelDeCongelamiento = nivelDeCongelamiento;
}

public double getPies() {
return pies;
}

public void setPies(double pies) {
this.pies = pies;
}

public int getNivelDeCongelamiento() {
return nivelDeCongelamiento;
}

public void setNivelDeCongelamiento(int nivelDeCongelamiento) {
this.nivelDeCongelamiento = nivelDeCongelamiento;
}

@Override
public double calcularPrecioFinal() {
return calcularSubtotal() + calcularIva();
}

@Override
public double calcularSubtotal() {
return getPrecio() * getCantidad();
}

@Override
public double calcularIva() {
return calcularSubtotal() * 0.12;
}

@Override
public void mostrarDetalles() {
System.out.println("Clase Refrigeradora");
System.out.println("Marca: " + getMarca());
    System.out.println("Precio: " + getPrecio());
System.out.println("Cantidad: " + getCantidad());
System.out.println("Pies: " + pies);
System.out.println("Nivel Congelamiento: " + nivelDeCongelamiento);
System.out.println("Valor a pagar por la refrigeradora:" + calcularPrecioFinal());

}

@Override
public String toString() {
return "Refrigeradora [" + super.toString() + " pies=" +
pies + ", nivelDeCongelamiento=" + nivelDeCongelamiento + "]";
}

}

Televisor
Código: [Seleccionar]
public class Televisor extends Electrodomestico {

private int pulgadas;
private String resolucion;
private String tipoPantalla;

public Televisor() {
super();
pulgadas = 0;
resolucion = "";
tipoPantalla = "";
}

public Televisor(String nombre, String codigo, String marca, int cantidad, double precio,
int pulgadas, String resolucion, String tipoPantalla) {
super(nombre, codigo, marca, cantidad, precio);
this.pulgadas = pulgadas;
this.resolucion = resolucion;
this.tipoPantalla = tipoPantalla;
}

public int getPulgadas() {
return pulgadas;
}

public void setPulgadas(int pulgadas) {
this.pulgadas = pulgadas;
}

public String getResolucion() {
return resolucion;
}

public void setResolucion(String resolucion) {
this.resolucion = resolucion;
}

public String getTipoPantalla() {
return tipoPantalla;
}

public void setTipoPantalla(String tipoPantalla) {
this.tipoPantalla = tipoPantalla;
}

@Override
public double calcularPrecioFinal() {
return calcularSubtotal() + calcularIva();
}

@Override
public double calcularSubtotal() {
return getPrecio() * getCantidad();
}

@Override
public double calcularIva() {
return calcularSubtotal() * 0.12;
}

@Override
public void mostrarDetalles() {
System.out.println("Clase Televisor");
System.out.println("Marca: " + getMarca());
System.out.println("Precio: " + getPrecio());
System.out.println("Cantidad: " + getCantidad());
System.out.println("Pulgadas: " + pulgadas);
System.out.println("Resolucion: " + resolucion);
System.out.println("Tipo Pantalla: " + tipoPantalla);

System.out.println("Valor a pagar por el Televisor:" + calcularPrecioFinal());
}

@Override
public String toString() {
return "Televisor [" + super.toString() + "pulgadas=" + pulgadas +
", resolucion=" + resolucion + ", tipoPantalla=" + tipoPantalla
+ "]";
}

}

Lavadora
Código: [Seleccionar]
public class Lavadora extends Electrodomestico {

private int velocidad;
private String capacidadDeLavado;
private int ciclos;

public Lavadora() {
super();
velocidad = 0;
capacidadDeLavado = "";
ciclos = 0;
}

public Lavadora(String nombre, String codigo, String marca, int cantidad, double precio,
int velocidad, String capacidadDeLavado, int ciclos) {
super(nombre, codigo, marca, cantidad, precio);
this.velocidad = velocidad;
this.capacidadDeLavado = capacidadDeLavado;
this.ciclos = ciclos;
}

public int getVelocidad() {
return velocidad;
}

public void setVelocidad(int velocidad) {
this.velocidad = velocidad;
}

public String getCapacidadDeLavado() {
return capacidadDeLavado;
}

public void setCapacidadDeLavado(String capacidadDeLavado) {
this.capacidadDeLavado = capacidadDeLavado;
}

public int getCiclos() {
return ciclos;
}

public void setCiclos(int ciclos) {
this.ciclos = ciclos;
}

@Override
public double calcularPrecioFinal() {
return calcularSubtotal() + calcularIva();
}

@Override
public double calcularSubtotal() {
return getPrecio() * getCantidad();
}

@Override
public double calcularIva() {
return calcularSubtotal() * 0.12;
}

@Override
public void mostrarDetalles() {
System.out.println("Clase Lavadora");
System.out.println("Marca: " + getMarca());
System.out.println("Precio " + getPrecio());
System.out.println("Cantidad: " + getCantidad());
System.out.println("Capacidad de Lavado: " + capacidadDeLavado);
System.out.println("Ciclos: " + ciclos);
System.out.println("Velocidad: " + velocidad);

System.out.println("Valor a pagar por la Lavadora:" + calcularPrecioFinal());
}

@Override
public String toString() {
return "Lavadora [" + super.toString() + "velocidad=" + velocidad +
", capacidadDeLavado=" + capacidadDeLavado + ", ciclos=" + ciclos
+ "]";
}

}


Vale, tenemos modeladas las entidades principales.
Hasta aquí tengo claro lo que has hecho.

Pero a partir de aquí, ya me pierdo en tus planteamientos. Has puesto una clase llamada Producto y luego ya la clase Main.
Pero veo que en el código se mencionan una clase Inventario, una clase GestorCliente... que no las has publicado, y de hecho no se si realmente las tienes escritas o se han colado por copiar/pegar de otros códigos..., no sé...

Así que a partir de aquí voy a seguir según lo haría yo....¡¡pero ahora mismo no dispongo de más tiempo!!  :o

Luego cuando pueda, retomaré este ejercicio y veremos cómo podemos hacer la gestión de electrodomésticos con un arreglo.

Saludos.

149
¿Y cuál sería la función que ha de cumplir el arreglo?
Nos será más fácil ayudarte si nos indicas cuál es el enunciado del ejercicio o explicas que tiene que hacer el programa.

La principal diferencia entre un ArrayList y un arreglo "primitivo", es que este último va a tener un tamaño predeterminado.
Un ArrayList es dinámico, puede aumentar su tamaño cada vez que añadimos un elemento.

Un arreglo no. al crearlo hay que decirle la cantidad exacta de elementos que va a contener.

Si el programa va a funcionar con una cantidad de electrodomésticos concreta, no debería haber problema en usar un arreglo.

Pero insisto, indícanos el enunciado del ejercicio y podremos ayudarte mejor.

Saludos.

150
No, no doy clases de Java.
Ni estoy capacitado para una tarea tan responsable ni tendría tiempo para ello.
Yo ni siquiera me dedico a la programación, mi trabajo no tiene nada que ver con ella.

Yo soy solo un aprendiz, por hobby, y que a veces rasco algo de tiempo libre para ir aprendiendo más cosas nuevas, o bien para ayudar a otros en lo que pueda.

Gracias por tu reconocimiento.
Un saludo.

151
El diagrama de flujo para el proceso de facturación podría ser algo así:



Sobre el código Java que solicita,mmmmh, aunque no lo parezca, puede ser bastante extenso.

Para empezar, pide una clase Cliente, que podría ser como esta:
Código: [Seleccionar]
public final class Cliente {

private String id;
private String nombre;
private String telefono;
private String domicilio;

public Cliente(String id, String nombre, String telefono, String domicilio) {
this.id = id;
this.nombre = nombre;
this.telefono = telefono;
this.domicilio = domicilio;
}

public String getId() {
return id;
}

public void setId(String id) {
this.id = id;
}

public String getNombre() {
return nombre;
}

public void setNombre(String nombre) {
this.nombre = nombre;
}

public String getTelefono() {
return telefono;
}

public void setTelefono(String telefono) {
this.telefono = telefono;
}

public String getDomicilio() {
return domicilio;
}

public void setDomicilio(String domicilio) {
this.domicilio = domicilio;
}

@Override
public String toString() {
return String.format("ID: %s\t\t\t\t Nombre: %s\nTelef: %s\t\t\t Domicilio: %s",
id, nombre, telefono, domicilio);
}

}

Luego pide una clase Factura, donde se debe asociar un Cliente con una relación de artículos que se le facturan, así como sus cantidades e importes.
Además luego cada Factura ha de mostrarse en pantalla indicando la relación de artículos, la suma total de las cantidades y la suma total de los importes.
Así que hay que pensar en como relacionamos todos estos datos de forma que luego fácilmente se puedan mostrar y hacer esos cálculos.
No se pide una clase Artículo, así que a priori podemos usar un String para este dato.
Precio de cada artículo un double y las cantidades un int, vale, ¿Pero como coleccionamos esos datos para que cada Factura tenga varios artículos, cada uno con un precio y una cantidad facturada?

Podrían usarse tres arrays primitivos, pero como no sabemos de antemano cuántos artículos va a tener una factura, no nos sirve.
Tres ArrayLists nos solucionarían el problema, pero creo que para optimizar aún más el proceso, podemos crear una clase (aunque no la pida el enunciado) que relacione directamente esos tres datos juntos en un único objeto.
Podría llamarse LineaFactura, porque representaría las distintas líneas de productos facturados que tendrá una factura.
Código: [Seleccionar]
public final class LineaFactura {

private String producto;
private double precio;
private int cantidad;

public LineaFactura(String producto, double precio, int cantidad) {
this.producto = producto;
this.precio = precio;
this.cantidad = cantidad;
}

//Para esta clase solo nos interesan tener dos getters
public int getCantidad() {
return cantidad;
}

public double getImporteLinea() {
return precio * cantidad;
}

@Override
public String toString() {
return String.format("%20s\t\t%5.2f\t%6d\t\t%5.2f", producto, precio, cantidad, getImporteLinea());
}

}

Gracias a esto, la clase Factura ya no va a requerir de tres ArrayList, bastará con solo uno que almacene las distintas líneas de facturación
Esta clase Factura va a requerir de un objeto Cliente y también de una fecha, la cuál podemos pedirla por teclado o usar la clase LocalDate y automáticamente coger la fecha actual del sistema.
Podemos añadirle también un atributo ID, un número que identifique una factura. No tiene porque ser un "dato real", podemos generar números al azar, ya que es simplemente una simulación.
Factura tendrá un método que se encargará de mostrar en pantalla todas las "líneas de factura", datos del cliente, fecha y calcular los totales.
Código: [Seleccionar]
import java.time.LocalDate;
import java.util.ArrayList;

public final class Factura {

private int numFactura;
private Cliente cliente;
private LocalDate fecha;
private ArrayList<LineaFactura> facturado;

public Factura(Cliente cliente) {
numFactura = (int) (Math.random() * 10000);//Numero de factura lo generamos al azar
this.cliente = cliente;
fecha = LocalDate.now();
facturado = new ArrayList<LineaFactura>();
}

public Cliente getCliente() {
return cliente;
}

public void agregarLinea(LineaFactura linea) {
facturado.add(linea);
}

public void imprimirFactura() {

/*
* Recorreremos la lista de productos facturados para
* mostrar los datos en pantalla de cada linea.
* De paso, iremos sumando el total de artículos
* y los importes de cada línea para mostrar al final
*/
int productosTotales = 0;
double importeTotal = 0d;

System.out.println("\n\t\t****** FACTURA #" + numFactura + " *****");
System.out.println("\n\tFecha: " + fecha.toString());
System.out.println("\n\t\tDatos del Cliente");
System.out.println("\t\t----- --- -------");
System.out.println(cliente);
System.out.println("\n\t\t\tProductos Facturados");
System.out.println("\t\t\t--------- ----------\n");

System.out.println(" LINEA\t\tPRODUCTO\t\tPRECIO\tCANTIDAD\tTOTAL LINEA");
System.out.println(" -----\t\t--------\t\t------\t--------\t-----------");
for (int i = 0; i < facturado.size(); i++) {
System.out.println("> #" + (i+1) + facturado.get(i));
productosTotales += facturado.get(i).getCantidad();
importeTotal += facturado.get(i).getImporteLinea();
}

System.out.println("\nTotal Articulos: " + productosTotales);
System.out.printf("Importa a facturar: %.2f",  importeTotal);
System.out.println("\n\n\t\t****** FIN DE FACTURA *****");
}

/*
* Indica si esta factura carece de líneas.
* Será útil para decidir si esta factura
* debemos guardarla en el sistema o descartarla
*/
public boolean facturaEnBlanco() {
return facturado.isEmpty();
}

}

Bien, tenemos una clase Cliente y una clase Factura, que se apoya en una clase secundaria llamada LineaFactura para organizar los datos de los artículos que se facturan.

Se podría comenzar con el programa principal, pero si nos fijamos en el enunciado nos dice que este va a tener "3 módulos": Cliente, Facturación y Finanzas
Es decir, el programa se va a dividir en tres partes: una para la gestión de clientes, otra para facturar artículos y otra que no estoy seguro de su cometido, pero supongo será para  cosas como mostrar las facturas registradas en el sistema.

Esto implica mucho código, pues son tres subprogramas dentro de uno solo.
Para repartir el código en lugar de concentrarlo todo en una única clase principal, podemos crear más clases de apoyo que se encarguen de tareas específicas.

Por ejemplo, podemos hacer una clase que se encargue de la gestión de los Clientes.
Esta clase tendrá un ArrayList de Cliente y tendrá métodos para registar un cliente nuevo, para mostrar los clientes registrados, para buscar un cliente en concreto...
Código: [Seleccionar]
import java.util.ArrayList;
import java.util.Scanner;

public final class GestorClientes {

public ArrayList<Cliente> clientes;
private Scanner teclado;

public GestorClientes() {
teclado = new Scanner(System.in);
clientes = new ArrayList<Cliente>();
//Añadimos unos clientes predefinidos para comenzar el programa con algun dato cargado.
//El ID se autogenera según el tamaño del ArrayList
clientes.add(new Cliente("ID00" + clientes.size(), "Pablo Segura", "656901234", "C/Paloma nº12"));
clientes.add(new Cliente("ID00" + clientes.size(), "Laura Juan", "690579012", "C/Carretas nº23"));
clientes.add(new Cliente("ID00" + clientes.size(), "Jorge Morales", "677119854", "C/Girasol nº56"));
clientes.add(new Cliente("ID00" + clientes.size(), "Carmen Robledo", "609718131", "C/Real nº3"));
}

public Cliente registrarCliente() {
System.out.println("\n\t\tREGISTAR CLIENTE");
System.out.print("Nombre: ");
String nombre = teclado.nextLine();
System.out.print("Telefono: ");
String telef = teclado.nextLine();
System.out.print("Domicilio: ");
String domi = teclado.nextLine();
Cliente nuevo = new Cliente("ID00" + clientes.size(), nombre, telef, domi);
clientes.add(nuevo);
System.out.println("\n\t\tCLIENTE REGISTRADO");
return nuevo;
}

public void listarClientes() {
System.out.println("\n\t\tLISTADO CLIENTES");
for (Cliente cl: clientes)
System.out.println(cl + "\n");
}

public Cliente buscarPorId() {
System.out.print("\nIntroduzca ID cliente: ");
String id = teclado.nextLine();
//Buscamos Cliente
for (Cliente cl: clientes)
if (cl.getId().equals(id))
return cl; //Encontrado

//Si bucle FOR termina sin retornar nada, es que no existe este cliente
return null;
}

public Cliente buscarPorNombre() {
System.out.print("\nIntroduzca nombre completo de cliente: ");
String nombre = teclado.nextLine();
//Buscamos Cliente
for (Cliente cl: clientes)
if (cl.getNombre().equals(nombre))
return cl; //Encontrado

//Si bucle FOR termina sin retornar nada, es que no existe este cliente
return null;
}

}

Y lo mismo con las Finanzas, una clase gestora con un ArrayList donde se almacenarán Facturas y se encargue de mostrar listados, ya sea de todas las facturas como también facturas de clientes concretos.
Código: [Seleccionar]
import java.util.ArrayList;

public final class GestorFinanzas {

private ArrayList<Factura> facturas = new ArrayList<Factura>();

public void agregarFactura(Factura fc) {
facturas.add(fc);
}

public void listarFacturas() {
System.out.println("\n\t\tLISTADO GENERAL FACTURAS");
if (facturas.isEmpty())
System.out.println("No hay FACTURAS que mostrar");
else
for (Factura fc: facturas)
fc.imprimirFactura();
System.out.println("\n\t\tFIN DE LISTADO");
}

public void listarPorID(String id) {
boolean encontrado = false;
System.out.println("\n\tLISTADO FACTURAS CLIENTE: " + id);
for (Factura fc: facturas)
if (fc.getCliente().getId().equals(id)) {
encontrado = true;
fc.imprimirFactura();
}

if (!encontrado)
System.out.println("No hay facturas de este cliente");
}

public void listarPorNombre(String nombre) {
boolean encontrado = false;
System.out.println("\n\tLISTADO FACTURAS CLIENTE: " + nombre);
for (Factura fc: facturas)
if (fc.getCliente().getNombre().equals(nombre)) {
encontrado = true;
fc.imprimirFactura();
}

if (!encontrado)
System.out.println("No hay facturas de este cliente");
}

}

Esta clase, sin embargo, no puede encargarse de crear facturas porque no tiene acceso a la lista de clientes que están en GestorClientes.
Puede registrar Facturas ya creadas, pero no crearlas por ella misma.

Así que la clase GestorClientes hará las principales tareas del "módulo Cliente".
La clase GestorFinanzas hará las del "módulo Finazas".

¿Y quién se encarga del "módulo Facturación"?
Pues esta tarea va a recaer en el propio programa principal, ya que es el que va a tener acceso tanto a Clientes como al registro de Facturas (mediante las clases gestoras correspondientes).
Total, el "módulo Facturación" es simplemente pedir datos para crear una Factura, realmente no necesita una clase específica para eso.

Así que a continuación pongo la clase Main principal.
Es muy extensa pero porque hay muchos menús.
Está el menú principal para escoger "módulo", y cada "módulo" tiene su propio submenú.

Todo está repartido en distintos métodos, así que en realidad el método main() principal es minúsculo.
Esto ayuda a entender mejor el código. Si uno quiere centrarse en leer y querer comprender un "módulo" en concreto, solo ha de irse a leer el método correspondiente a ese "módulo", en lugar de intentar vislumbrarlo en 200 líneas de código si se hubiera escrito todo junto.
Código: [Seleccionar]
import java.util.Scanner;

public final class Main {

private static Scanner teclado = new Scanner(System.in);
private static GestorClientes clientes = new GestorClientes();
private static GestorFinanzas finanzas = new GestorFinanzas();

public static void main(String[] args) {

int opcion = 0;
do {
System.out.println("\n\t\tMENU SISTEMA <<SEASHELL>>");
System.out.println("[1] -- Modulo Clientes");
System.out.println("[2] -- Modulo Facturacion");
System.out.println("[3] -- Modulo Finanzas");
System.out.println("[4] -- TERMINAR PROGRAMA");
opcion = Integer.parseInt(teclado.nextLine());

switch(opcion) {
case 1:
moduloClientes();
break;
case 2:
moduloFacturacion();
break;
case 3:
moduloFinanzas();
break;
case 4:
System.out.println("\n\t\tFIN DE PROGRAMA");
break;
default:
System.out.println("Opcion equivocada");
}

}while (opcion != 4);

}

private static void moduloClientes() {
int opcion = 0;
do {
System.out.println("\n\n\t\tMODULO CLIENTES");
System.out.println("[1] -- Registrar Cliente");
System.out.println("[2] -- Listar todos los Clientes");
System.out.println("[3] -- Buscar Cliente por ID");
System.out.println("[4] -- Buscar Cliente por Nombre");
System.out.println("[5] -- REGRESAR");
opcion = Integer.parseInt(teclado.nextLine());

switch(opcion) {
case 1:
clientes.registrarCliente();
break;
case 2:
clientes.listarClientes();
break;
case 3:
Cliente buscaID = clientes.buscarPorId();
if (buscaID == null)
System.out.println("No hay Cliente con ese ID");
else
System.out.println(buscaID);
break;
case 4:
Cliente buscaNom = clientes.buscarPorNombre();
if (buscaNom == null)
System.out.println("No hay Cliente con ese Nombre");
else
System.out.println(buscaNom);
break;
case 5:
System.out.println("\n\tREGRESANDO A MENU PRINCIPAL\n");
break;
default:
System.out.println("Opcion equivocada");
}

}while (opcion != 5);
}

private static void moduloFacturacion() {
Cliente cliente = null;
int opcion = 0;
do {
System.out.println("\n\n\t\tMODULO FACTURACION");
System.out.println("[1] -- Identificar Cliente por ID");
System.out.println("[2] -- Identificar Cliente por Nombre");
System.out.println("[3] -- Registrar Nuevo Cliente");
System.out.println("[4] -- REGRESAR");
opcion = Integer.parseInt(teclado.nextLine());

switch(opcion) {
case 1:
cliente = clientes.buscarPorId();
if (cliente == null)
System.out.println("No hay Cliente con ese ID");
else
nuevaFactura(cliente);
break;
case 2:
cliente = clientes.buscarPorNombre();
if (cliente == null)
System.out.println("No hay Cliente con ese Nombre");
else
nuevaFactura(cliente);
break;
case 3:
cliente = clientes.registrarCliente();
nuevaFactura(cliente);
break;
case 4:
System.out.println("\n\tREGRESANDO A MENU PRINCIPAL\n");
break;
default:
System.out.println("Opcion equivocada");
}

}while (opcion != 4);
}

private static void moduloFinanzas() {
int opcion = 0;
do {
System.out.println("\n\n\t\tMODULO FINANZAS");
System.out.println("[1] -- Listar Facturas por ID");
System.out.println("[2] -- Listar Facturas por Nombre");
System.out.println("[3] -- Listar todas las Facturas");
System.out.println("[4] -- REGRESAR");
opcion = Integer.parseInt(teclado.nextLine());

switch(opcion) {
case 1:
Cliente buscaID = clientes.buscarPorId();
if (buscaID == null)
System.out.println("No hay Cliente con ese ID");
else
finanzas.listarPorID(buscaID.getId());
break;
case 2:
Cliente buscaNombre = clientes.buscarPorNombre();
if (buscaNombre == null)
System.out.println("No hay Cliente con ese Nombre");
else
finanzas.listarPorNombre(buscaNombre.getNombre());
break;
case 3:
finanzas.listarFacturas();
break;
case 4:
System.out.println("\n\tREGRESANDO A MENU PRINCIPAL\n");
break;
default:
System.out.println("Opcion equivocada");
}

}while (opcion != 4);
}

private static void nuevaFactura(Cliente cliente) {

Factura nueva = new Factura(cliente);
String articulo = "";
do {
System.out.println("\nIntroduzca nuevo ARTICULO o pulse ENTER dejando el campo vacio para terminar.");
System.out.print("Nombre Articulo: ");
articulo = teclado.nextLine();
if (!articulo.isBlank()) {
//Pedimos datos para crear una LineaFactura
System.out.print("PVP del Artículo: ");
double pvp = Double.parseDouble(teclado.nextLine());
System.out.print("Cantidad unidades: ");
int cantidad = Integer.parseInt(teclado.nextLine());

nueva.agregarLinea(new LineaFactura(articulo, pvp, cantidad));
}

}while(!articulo.isBlank());

//Antes de guardar esta factura, comprobamos si tiene articulos registrados
if (!nueva.facturaEnBlanco()) {
finanzas.agregarFactura(nueva);
nueva.imprimirFactura();
System.out.println("\n\t\tFACTURA REGISTRADA");
}
}
}


He dado una explicación general de la lógica que se ha aplicado, sin entrar en cosas concretas porque entonces la explicación sería muy extensa y poco práctica.

Pero podéis preguntar cualquier duda que surja, cualquier cosa que no se entienda, instrucciones que sean desconocidas.., lo que sea ...

Un saludo.

152
A ver, es un ejercicio un poco puñetero.

Para empezar, los datos del txt no están separados por UN espacio, si no por TRES espacios.
Y esto es necesario porque algunos nombres de ciudades se componen de varias palabras separadas a su vez por UN espacio:
Citar
Los Teques   3   Azucar   1.5   1
Entonces si se usara solo UN espacio para separar, habría confusiones para distinguir que es un dato de venta del producto, y que es un nombre compuesto de ciudad.
Así que para evitarlo han usado TRES espacios para separar datos.

En el archivo parece que hay una línea vacía intencionada. Esto habrá que tenerlo en cuenta, pues no todas las líneas que componen el archivo proporcionan datos de venta.

Si usamos como referencia los TRES espacios en blanco para separar datos, las líneas nos van a dar 5 datos: Zona, ID, Producto, Precio y Unidades.

Todas, excepto una, pues no tiene ningún valor para el campo "Unidades"
Esta línea solo da 4 datos:
Citar
Isla de Margarita   5   Pollo   1.18   

Supongo que NO es un error, pues el enunciado nos dice que validemos el campo "Unidad" comprobando que sea un valor entero.
Pues para comprobar esto, habrá que tener en cuenta no solo que ese campo pueda ser una cadena, o un valor decimal..., si no que además puede que ni siquiera exista ese dato.


Teniendo en cuenta todo esto, creo que lo más cómodo sería hacer una única lectura en disco del archivo txt y componer una matriz con TODOS los datos.

Luego, el resto de tareas que pide, las haría trabajando sobre esa matriz y no volviendo a leer otra vez desde disco.

Así que tenemos que componer una matriz con los datos del archivo.
Para ello, primero vamos a necesitar saber cuántas líneas tiene ese archivo, ya que no podemos componer una matriz si no sabemos sus dimensiones.

Luego, sabemos que cada línea nos ha de dar 5 datos, pero ya hemos visto que alguna solo tiene 4. Esto habrá que controlarlo y cuando solo nos den 4 datos, habrá que añadir manualmente ese 5º dato que falta, que será el de "Cantidad" y hay que darle valor 1

Cuando sí tengamos los 5 datos, habrá que comprobar si el 5º dato representa un valor entero, de lo contrario, de nuevo tendremos que asignar manualmente el valor 1.
Comprobar si una cadena representa un entero es fácil. Basta con coger esa cadena e intentar parsearla a Integer.
Si sale bien, es que obviamente es un entero.
Si falla, se producirá una excepción y esto ya nos indica que NO es un entero.

Y ojo, porque alguna línea del archivo está vacía, no hay dato ninguno.

Bueno, para comenzar te propongo este código.
No realiza todas las tareas, solo lee el archivo y compone la matriz con los datos que contiene, teniendo en cuenta la validación de "Cantidad" y que puede haber alguna línea vacía en el archivo.
Código: [Seleccionar]
import java.io.BufferedReader;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.Arrays;

public class MatrizProductos {

public static void main(String[] args) {
//Crearemos una matriz con TODOS los datos completos del archivo de texto.
String[][] productos = null;

try {
//Para ello, necesitamos saber cuántas líneas tiene el archivo
int lineas = (int) Files.lines(Paths.get("productos.txt")).count();
//Ahora podemos declarar las dimensiones de la matriz
productos = new String[lineas][5]; //5 datos por cada línea

//Procedemos a leer el archivo para transferir la información a la matriz
BufferedReader lector = new BufferedReader(new FileReader("productos.txt"));
for (int i = 0; i < lineas; i++) {
String linea = lector.readLine();

if (linea.isEmpty()) //El txt puede contener líneas "vacías"
productos[i] = null; //En ese caso añadimos valor null a la matriz
else {
/*
* Descomponemos los 5 datos de la línea en un array.
* Algunas lineas pueden contener solo 4 datos porque omiten
* el dato "Cantidad".
* A nosotros solo nos sirve tener un array de 5 datos, en caso de no tenerlos
* tendremos que componerlo añadiendo valor 1 al dato "Cantidad" que nos falta
*/
String[] datos;
if (linea.split("   ").length < 5) {
//Esta línea solo nos va a dar 4 datos, hemos componer un array de 5
String[] lineaIncompleta = linea.split("   ");
datos = new String[5];
for (int j = 0; j < 4; j++) //Añadimos los 4 datos que tenemos
datos[j] = lineaIncompleta[j];
//Y ahora el 5º datos que nos falta
datos[4] = "1";
}
else //Esta línea sí que nos proporciona los 5 datos.
datos = linea.split("   ");

//Comprobamos si el dato de "Cantidad" es un valor entero
if (!esEntero(datos[4]))
datos[4] = "1"; //Si no es entero, se asigna valor 1

//Ya podemos agregar datos validados a la matriz
productos[i] = datos;
}
}
//Lectura de archivo finalizada, la matriz está completada
lector.close();
} catch (FileNotFoundException e) {
System.out.println("ERROR: Archivo TXT no encontrado");
} catch (IOException e) {
System.out.println("ERROR: No se puede acceder al archivo TXT");
}

//Impresión de prueba para ver si hemos compuesto la matriz de productos
for (int i = 0; i < productos.length; i++)
System.out.println(Arrays.toString(productos[i]));
}

/**
* Método que comprueba si un dato String es un entero.
* @param dato String que queremos comprobar
* @return TRUE si la cadena representa un entero, FALSE en caso contrario.
*/
private static boolean esEntero(String dato) {
/*
* Para comprobar si es un entero, podemos intentar
* un parseo a Integer. Si esto produce una excepción,
* es que la cadena no representa un número entero.
*/
try {
Integer.parseInt(dato);
//Si la línea anterior no genera excepción, es que sí es un entero
return true;
}
catch(NumberFormatException nfe) {
//Excepción capturada, no es un entero
return false;
}
}

}

Si lo ejecutamos, veremos que sí hemos logrado componer una matriz con TODOS los datos (incluida la línea vacía que es un valor null)

Citar
[Guanare, 7, Pescado, 2, 7]
[Guanare, 7, Pescado, 2, 1]
[Caracas, 1, Arroz, 2.5, 7]
[Maracay, 2, Frijol, 1.25, 1]
[Maturin, 6, Carne de Res, 3.5, 1]
[Caracas, 1, Arroz, 2.5, 1]
[Barquisimeto, 4, Aceite, 2.75, 1]
[Los Teques, 3, Azucar, 1.5, 1]
[Maturin, 6, Carne de Res, 3.5, 1]
[Merida, 8, Leche, 2.75, 1]
[Maracay, 2, Frijol, 1.25, 7]
[Guanare, 7, Pescado, 2, 1]
[Guanare, 7, Pescado, 2, 7]
[Maracay, 2, Frijol, 1.25, 1]
null
[Caracas, 1, Arroz, 2.5, 1]
[Valencia, 11, Naranja, 0.25, 1]
[Los Teques, 3, Azucar, 1.5, 1]
[Merida, 8, Leche, 2.75, 1]
[Barquisimeto, 4, Aceite, 2.75, 7]
[Guanare, 7, Pescado, 2, 7]
[Guanare, 7, Pescado, 2, 1]
[Isla de Margarita, 5, Pollo, 1.18, 1]
[Maturin, 6, Carne de Res, 3.5, 1]
[Maracay, 2, Frijol, 1.25, 1]
[Los Teques, 3, Azucar, 1.5, 1]
[Los Teques, 3, Azucar, 1.5, 1]
[Maturin, 6, Carne de Res, 3.5, 1]
[Merida, 8, Leche, 2.75, 1]
[Maracay, 2, Frijol, 1.25, 7]

Vale, pues a partir de aquí, el resto de tareas que pide podemos hacerla trabajando sobre esta matriz y así ya nos olvidamos del archivo de texto.

De lo que pide, ya habríamos cumplido con estas:
Citar
Una sentencia de iteración que permita leer todos los artículos
comprados.

Valide que el valor de la variable (cantidad) sea de número entero.
Si el valor de esta variable es «nulo o cadena», utilizar el número 1 en
su lugar

Así que nos faltarían estas:
Citar
Crear una matriz que permita almacenar el ID_Producto, precio y
unidad vendida.

Una sentencia condicional que evalúe las ventas de la region de
Maracay, Maturin. Hacer un total para el resto de las zonas, tomando en cuenta ek yuso de variables diferentes para cada resultados.

Una sentencia condicional que evalúa cuántas libras de pollo se compró.


No se si se espera que demos algún enfoque concreto al programa.
A mi lo que se me ocurre es escribir tres métodos separados para cada una de esas tareas.
Que reciban la matriz como argumento y hagan lo que se pide.

El programa final podría quedar de la siguiente manera.
No he hecho una presentación de datos "bonita" en pantalla, si luego quieres mostrar las matrices más elegantes, con cabecera de datos y tal..., ya es cosa tuya.
Código: [Seleccionar]
public class MatrizProductos {

public static void main(String[] args) {
//Crearemos una matriz con TODOS los datos completos del archivo de texto.
String[][] productos = null;

try {
//Para ello, necesitamos saber cuántas líneas tiene el archivo
int lineas = (int) Files.lines(Paths.get("productos.txt")).count();
//Ahora podemos declarar las dimensiones de la matriz
productos = new String[lineas][5]; //5 datos por cada línea

//Procedemos a leer el archivo para transferir la información a la matriz
BufferedReader lector = new BufferedReader(new FileReader("productos.txt"));
for (int i = 0; i < lineas; i++) {
String linea = lector.readLine();

if (linea.isEmpty()) //El txt puede contener líneas "vacías"
productos[i] = null; //En ese caso añadimos valor null a la matriz
else {
/*
* Descomponemos los 5 datos de la línea en un array.
* Algunas lineas pueden contener solo 4 datos porque omiten
* el dato "Cantidad".
* A nosotros solo nos sirve tener un array de 5 datos, en caso de no tenerlos
* tendremos que componerlo añadiendo valor 1 al dato "Cantidad" que nos falta
*/
String[] datos;
if (linea.split("   ").length < 5) {
//Esta línea solo nos va a dar 4 datos, hemos componer un array de 5
String[] lineaIncompleta = linea.split("   ");
datos = new String[5];
for (int j = 0; j < 4; j++) //Añadimos los 4 datos que tenemos
datos[j] = lineaIncompleta[j];
//Y ahora el 5º datos que nos falta
datos[4] = "1";
}
else //Esta línea sí que nos proporciona los 5 datos.
datos = linea.split("   ");

//Comprobamos si el dato de "Cantidad" es un valor entero
if (!esEntero(datos[4]))
datos[4] = "1"; //Si no es entero, se asigna valor 1

//Ya podemos agregar datos validados a la matriz
productos[i] = datos;
}
}
//Lectura de archivo finalizada, la matriz está completada
lector.close();
} catch (FileNotFoundException e) {
System.out.println("ERROR: Archivo TXT no encontrado");
} catch (IOException e) {
System.out.println("ERROR: No se puede acceder al archivo TXT");
}

System.out.println("\t\tMATRIZ COMPLETA");
System.out.println("\t\t------ --------");
for (int i = 0; i < productos.length; i++)
if (productos[i] != null) //Omitimos líneas vacías
System.out.println(Arrays.toString(productos[i]));

System.out.println("\n\n\t\tMATRIZ CON 3 DATOS");
System.out.println("\t\t------ --- - -----");
String[][] matriz3 = matriz3Datos(productos);
for (int i = 0; i < matriz3.length; i++)
if (matriz3[i] != null) //Omitimos líneas vacías
System.out.println(Arrays.toString(matriz3[i]));

System.out.println("\n\n\t\tVENTAS POR REGIONES");
System.out.println("\t\t------ --- --------");
evaluarRegiones(productos);

System.out.println("\n\n\t\tTOTAL DE POLLO VENDIDO");
System.out.println("\t\t----- -- ----- -------");
System.out.println("Peso total: " + librasDePollo(productos) + " lb");

System.out.println("\n\n\t\tFIN DE PROGRAMA");
}

/**
* Método que comprueba si un dato String es un entero.
* @param dato String que queremos comprobar
* @return TRUE si la cadena representa un entero, FALSE en caso contrario.
*/
private static boolean esEntero(String dato) {
/*
* Para comprobar si es un entero, podemos intentar
* un parseo a Integer. Si esto produce una excepción,
* es que la cadena no representa un número entero.
*/
try {
Integer.parseInt(dato);
//Si la línea anterior no genera excepción, es que sí es un entero
return true;
}
catch(NumberFormatException nfe) {
//Excepción capturada, no es un entero
return false;
}
}

/**
* Crea una matriz con solo 3 datos de los 5 originales.
* Los datos son: ID_Producto, precio y unidad vendida.
* @param matrizPrincipal Matriz con los datos originales.
* @return Matriz con una selección de solo 3 datos.
*/
private static String[][] matriz3Datos(String[][] matrizPrincipal) {

String[][] matriz = new String[matrizPrincipal.length][3];
//Recorremos las matrices, seleccionando los datos deseados
for (int i = 0; i < matriz.length; i++) {
//Ojo con las líneas null
if (matrizPrincipal[i] == null)
matriz[i] = null;
else {
matriz[i][0] = matrizPrincipal[i][1]; //ID_Producto
matriz[i][1] = matrizPrincipal[i][3]; //Precio
matriz[i][2] = matrizPrincipal[i][4]; //Unidades vendidas
}
}

return matriz;
}

/**
* Evalúa las ventas según 3 regiones:
* Maracay, Maturin, Resto zonas.
* @param matrizPrincipal Matriz con los datos originales.
*/
private static void evaluarRegiones(String[][] matrizPrincipal) {
double maracay = 0, maturin = 0, resto = 0;
for (int i = 0; i < matrizPrincipal.length; i++) {
if (matrizPrincipal[i] != null) {
//Parseamos precio a double
double precio = Double.parseDouble(matrizPrincipal[i][3]);
//Unidades vendidas
int unidades = Integer.parseInt(matrizPrincipal[i][4]);
//Calculamos venta
double venta = precio * unidades;

//Evaluamos región para saber donde sumamos esta venta
switch(matrizPrincipal[i][0]) {
case "Maracay":
maracay += venta;
break;
case "Maturin":
maturin += venta;
break;
default:
resto += venta;
}
}
}

//Presentación de datos
System.out.println("Ventas totales de Maracay: " + maracay);
System.out.println("Ventas totales de Maturin: " + maturin);
System.out.println("Ventas totales del resto de zonas: " + resto);
}

/**
* Calcula las libras de pollo totales vendidas
* @param matrizPrincipal Matriz con los datos originales.
* @return Peso total en libras vendidas
*/
private static int librasDePollo(String[][] matrizPrincipal) {
int total = 0;
for (int i = 0; i < matrizPrincipal.length; i++)
if (matrizPrincipal[i] != null)
total += Integer.parseInt(matrizPrincipal[i][4]);

return total;
}

}

Y con esto creo que se cumple lo que se pide.
Pregunta si algo no entiendes, si faltaría algo por hacer, lo que sea...
No se si hay que guardar alguna matriz en disco,...ya nos dirás algo.

Un saludo.

153
El problema está en que intentas guardar un valor que originalmente es un double, en una variable int.

Esto supone perder información, ya que un double es un número real, es decir, tiene una parte entera y otra decimal.
Pero un int solo es un entero y no puede albergar parte decimal.

Es decir, si yo el valor double 5.25 lo guardo en un int, se me quedaría en 5.
El 0.25 restante desaparece, o sea, que he perdido información.

¿Entonces es imposible guardar un double en un int?
No, no es imposible. Lo que pasa es que Java, por seguridad, no nos lo va a permitir, a no ser que hagamos lo que se llama un casting.
Un casting es una forma de decirle a Java que somos conscientes de la perdida de información que puede suponer este cambio de tipo, y que nos da igual.

Esto es muy fácil de hacer, basta con envolver con paréntesis el valor o expresión que origina el valor double y anteponer también entre paréntesis, el tipo de dato al que queremos transformar:

Citar
   public MultiplicadorDieces(){}
   
    public int multiplicarPorDieces(double a, int b){
        int resultado = 1;
        for(int i=1;i<=b; i++){
            resultado=resultado*10;
        }
        resultado = (int) (a * resultado);
        return  resultado;
    }

Y ya está, con esto Java ya no se quejará.

Sin embargo, en este ejercicio, ¿realmente queremos que el resultado sea un entero?

Habrá ocasiones en las que si nos convenga, o no nos importe, que un double acabe siendo un int.

Pero no en este caso, porque esa pérdida de información nos va a dar resultados matemáticos incorrectos.

Si probamos a multiplicar 2.55 por 2 dieces (o sea, por 100):
Código: [Seleccionar]
    public static void main(String[] args) {
   
    MultiplicadorDieces mult = new MultiplicadorDieces();
   
    System.out.println("Resultado:" + mult.multiplicarPorDieces(2.55, 2));
   
    }

El resultado debería ser 255. Pero nuestro método nos va a dar un resultado erróneo:
Código: [Seleccionar]
Resultado:254

He aquí el problema de convertir doubles a enteros. Como dijimos, en el proceso se pierde información y por eso Java no lo permite hacer a no ser que el programador lo pida expresamente con un casting

El caso es, que para nuestro ejercicio, resultado no puede ser un int.
Si estamos operando con un double, lo ideal es que el resultado final también sea un double (salvo en casos especiales como dije antes)
Así que el tipo de resultado, y el tipo que retorna el método, ha de cambiar a double:

Citar
   public MultiplicadorDieces(){}
   
    public double multiplicarPorDieces(double a, int b){
        double resultado = 1;
        for(int i=1;i<=b; i++){
            resultado=resultado*10;
        }
        resultado = a * resultado;
        return  resultado;
    }

Si lo probamos ahora, el resultado se acerca un poco más al correcto.
¡¡Pero sigue sin ser correcto del todo!! :o
Código: [Seleccionar]
Resultado:254.99999999999997
¡¡Aaghh!! Por algún sitio se han esfumado 0.00000000000003 décimas.
No es mucho, pero no deja de ser un resultado incorrecto.

Sinceramente, no se a que es debido. Pero desde luego no es por un error de programación por nuestra parte.
Será por alguna peculiaridad a nivel interno, ya sea de Java o de la propia CPU, cuando tratan con números de tan alta precisión.

¿Qué podemos hacer entonces?
Aunque no es un error nuestro, podemos probar a simplificar un poco el código.
Tu código no está mal, pero primero acumulas una multiplicación de varios "dieces" y luego lo multiplicas por el valor inicial.
Insisto, no está mal. Pero es un paso que podemos ahorrarnos.

Podemos inicializar la variable resultado directamente con el valor inicial.
Y en el bucle, por cada repetición, le aplicamos directamente una multiplicación por 10.

Así operamos directamente sobre el valor inicial, en lugar de acumular previamente unos "dieces multiplicados."

Citar
   public MultiplicadorDieces(){}
   
    public double multiplicarPorDieces(double a, int b){
        double resultado = a;
       
        for(int i=1;i<=b; i++){
            resultado=resultado*10;
        }
       
        return  resultado;
    }

Esto ahora, sí nos da un resultado correcto:
Código: [Seleccionar]
Resultado:255.0
Insisto en que tu lógica estaba bien y seguramente si solo trabajásemos con int habría funcionado.
Pero por algún motivo, al aplicarla con doubles, ocurre un pequeño baile de décimas y no se por qué....  :(

Cambiando de tema, este es un buen ejercicio para aplicar "Recursividad" en la solución.
La Recursividad es una forma bastante óptima de resolver determinados cálculos, pero puede ser algo difícil de comprender su proceso lógico.

Una función recursiva (o método recursivo) lo que hace es que los cómputos de sus valores se van realizando en sucesivas llamadas a sí misma.
Es decir, un método "normal" hace él todos los cómputos y retorna el valor computado.

Un método "recursivo" lo que hace es computar solo una parte y a continuación se llama a sí mismo para pasarse de nuevo esos valores y continuar con los cómputos.
Este proceso de llamarse a sí mismo una y otra vez (recursividad) finaliza cuando se ha cumplido una determinada condición y es entonces cuando se obtiene el resultado final.

Sí, no es fácil de explicar, y aún menos de entender.
Así que no te preocupes si ahora todo esto te suena a chino.

Pero aún así te voy a mostrar como podría resolverse de forma recursiva. Aunque no lo entiendas(que quizás sí lo entiendas, en realidad no hace falta ser un genio), guarda este ejemplo como referencia para el futuro.
En algún momento se te pedirá aplicar recursividad y tener algún ejemplo a mano viene bien.

En tu clase he puesto los dos métodos, el "tradicional" y el "recursivo"
Citar
   public MultiplicadorDieces(){}
   
    public double multiplicarPorDieces(double a, int b){
        double resultado = a;
       
        for(int i=1;i<=b; i++){
            resultado=resultado*10;
        }
       
        return  resultado;
    }
   
    public double multiplicarPorDiecesRecursivo(double a, int b) {
       if (b == 0) //Si el multiplicador de dieces ya llegó a 0
          return a; //Retornamos el valor y termina el proceso recursivo
       else { //Si el multiplicador de dieces aún no es 0
          a = a * 10; // Multiplicamos un 10
          b = b - 1; //Decrementamos el multiplicador
          return multiplicarPorDiecesRecursivo(a, b); //Llamada recursiva con los valores actuales

       }
    }

Lo que hace es comprobar el valor del "multiplicador de dieces"
Si no es 0, significa que hay que multiplicar al menos un 10 al valor original.
Pues se multiplica ese 10, se reduce el valor del "multiplicador de dieces" y se pasan estos valores a una llamada recursiva a ese mismo método.

Tras una o varias llamadas recursivas, el "multiplicador de dieces" alcanzará el valor 0.
Eso significa que se han multiplicado todos los "dieces" necesarios, así que ya podemos retornar el resultado final y poner fin a las llamadas recursivas.

Si probamos ambos métodos en un programa:
Código: [Seleccionar]
   
    public static void main(String[] args) {
   
    MultiplicadorDieces mult = new MultiplicadorDieces();
   
    System.out.println("Resultado:" + mult.multiplicarPorDieces(2.55, 2));
    System.out.println("Resultado Recursivo:" + mult.multiplicarPorDiecesRecursivo(2.55, 2));
   
    }

Ambos darán el mismo resultado en pantalla:
Código: [Seleccionar]
Resultado:255.0
Resultado Recursivo:255.0

La ventaja de la recursividad es que permite simplificar el código y reducir las operaciones necesarias.
Quizás en este ejercicio no se perciba tanta diferencia entre un método y otro, pero en ejercicios más complejos la diferencia si puede ser mayor.

Además lo he escrito de forma un poco más extensa para que se entienda mejor la lógica que se está siguiendo y poder comentar cada línea para explicarlo.
Si aplicásemos los distintos operadores que ofrece Java para comprimir el código, el método recursivo podría quedar así:
Código: [Seleccionar]
    public double multiplicarPorDiecesRecursivo(double a, int b) {
    if (b == 0)
    return a;
    else
    return multiplicarPorDiecesRecursivo(a*=10, --b);
    }

Y si usamos el "condicional ternario", todo queda resuelto en una sola línea:
Código: [Seleccionar]
    public double multiplicarPorDiecesRecursivo(double a, int b) {
   
    return (b == 0)? a: multiplicarPorDiecesRecursivo(a*=10, --b);

    }


Pero insisto en que ahora no es necesario, ni prudente, que te calientes el cerebro intentando asimilar todo esto.
Tan solo guarda este ejercicio a mano para poder revisitarlo en el futuro.

Un saludo.

154
La clase Coche la has declarado dentro de la clase Funciones2

Bien, eso no es problema. Pero, si quieres que el método estático main() tenga acceso a la clase Coche, lo has de declarar también como estático.

Es decir, declárala así:

Citar
static class Coche {
    public int puertas = 0;
    public void agregarPuertas() {
        this.puertas++;
    }
}


En Java hay tres ámbitos: Local, Global y Estático(este se combina con los otros dos)

Según el ámbito donde se encuentre una variable (o un método, o una declaración de clase..) será visible o no para otros elementos.

El método main() se encuentra en un ámbito "global" porque está declarado directamente dentro de la clase principal Funciones2.
Tu clase Coche también es de ámbito "global" por el mismo motivo.

Sin embargo, el main() además de "global", es "estático" (obligatoriamente).
Esto implica que no puede hacer referencia (no puede ver) a otros elementos si no son estáticos como él.
Esto es lo que significa el error que te sale en pantalla.

Así que no te queda otra que declarar la clase Coche como estática, para que se encuentren en el mismo ámbito.

Si hubieras escrito la clase Coche en otro archivo .java distinto, es decir, que ya no pertenezca a Funciones2, entonces no habría surgido este problema porque ya se trataría de una clase independiente.

Pero al ser un miembro de Funciones2, igual que lo es main(), entonces sí que hay que igualar sus ámbitos.

Esto de los ámbitos "estáticos" y "no estáticos", al principio resulta confuso y algo difícil entender las implicaciones.
Así que ahora mismo, tampoco te preocupes demasiado por esto. Ya lo irás aprendiendo sobre la marcha.

Solo recuerda que habrá ocasiones en las que tendrás que declarar las cosas como static para que el main() pueda verlas.

Un saludo.

155
Más cosas a comentar ahora que veo a fondo tu código.

Mucho ojo con el orden en que ponemos los parámetros para los constructores.
Para la clase SalonCasa, has puesto que primero recibe el String de tipoSalon y luego el int de numero televisores:

Código: [Seleccionar]
    public SalonCasa(String valorTipoSalon, int valorNumeroDeTelevisores){
        tipoSalon=valorTipoSalon;
        numeroDeTelevisores=valorNumeroDeTelevisores;}

Vale, eso está muy bien, pero luego cuando quieras construir un objeto de esta clase, recuerda que los parámetros ha de recibirlo en ese mismo orden.

Porque en la clase Casa, en el constructor donde construyes un SalonCasa, se los estás pasando invertidos. Y Java también se quejará por esto:
Citar
    public Casa(double valorSuperficie, String valorDireccion, SalonCasa objetoSalon, CocinaCasa objetoCocina){
        superficie=valorSuperficie;
        direccion=valorDireccion;
        salon=new SalonCasa(objetoSalon.getNumeroDeTelevisores(), objetoSalon.getTipoSalon());
        cocina=new CocinaCasa(objetoCocina.getEsIndependiente(), objetoCocina.getNumeroDeFuegos());}

Hay que dárselos en el orden correcto:
Citar
    public Casa(double valorSuperficie, String valorDireccion, SalonCasa objetoSalon, CocinaCasa objetoCocina){
        superficie=valorSuperficie;
        direccion=valorDireccion;
        salon=new SalonCasa(objetoSalon.getTipoSalon(), objetoSalon.getNumeroDeTelevisores());
        cocina=new CocinaCasa(objetoCocina.getEsIndependiente(), objetoCocina.getNumeroDeFuegos());}


Sigamos hablando de este constructor, porque has hecho algo que es muy interesante de comentar.
La forma en que tu constructor inicializa los objetos salon y cocina:
Citar
    public Casa(double valorSuperficie, String valorDireccion, SalonCasa objetoSalon, CocinaCasa objetoCocina){
        superficie=valorSuperficie;
        direccion=valorDireccion;
        salon=new SalonCasa(objetoSalon.getTipoSalon(), objetoSalon.getNumeroDeTelevisores());
        cocina=new CocinaCasa(objetoCocina.getEsIndependiente(), objetoCocina.getNumeroDeFuegos());
}

Haciéndolo de esa manera, has de tener en cuenta que estás creando NUEVOS objetos a partir de los que has recibido entre paréntesis.
Es decir, van a tener los mismos valores para los atributos, pero no serán IGUALES. Serán objetos distintos.
Esto no es necesariamente un problema. Habrá ocasiones en que precisamente nos interese hacerlo así, pero normalmente NO.

Cuando un constructor recibe objetos entre paréntesis, normalmente se asignan esos mismos objetos a los atributos, sin crear unos nuevos a partir de ellos:
Citar
    public Casa(double valorSuperficie, String valorDireccion, SalonCasa objetoSalon, CocinaCasa objetoCocina){
        superficie=valorSuperficie;
        direccion=valorDireccion;
        salon=objetoSalon;
        cocina=objetoCocina;}

¿Ves? Entre paréntesis hemos recibido unas "referencias" a unos objetos, y esas mismas referencias las asignamos a los atributos.
Y ya está, con esto es suficiente.

Pero tú lo que has hecho ha sido coger los "valores" de esas referencias, y usarlos para crear objetos nuevos

En otros lenguajes como C/C++, hay de hecho instrucciones específicas para indicar si un parámetro queremos pasarlo por "referencia" o por "valor".
En Java no las hay, simplificaron este aspecto.
Los datos primitivos (int, double, char...) se pasan por "valor" y los objetos de clase se pasan por "referencia".

Lo que tú has hecho ha sido una forma artificial de pasar por "valor" algo que Java solo permite pasar por "referencia"

¿Esto es incorrecto?
No, para nada. Pero implica unas diferencias que debemos tener en cuenta.

Al pasar por referencia un objeto externo y asignarlo a los atributos de otro objetos (que es lo habitual), esos objetos quedan conectados.
Es decir, para construir una Casa, primero he tenido que construir un SalonCasa.
Son objetos distintos, pero Casa incluye en su interior un SalonCasa.
Entonces, si pasamos por referencia, los cambios que ocurran en el objeto externo SalonCasa también quedarán reflejados en el atributo interno del objeto Casa

Esto se verá mucho mejor con un ejemplo.
Mira, aquí estoy creando un salon y una cocina.
Luego creo una casa y a su constructor les paso ese salon y cocina.

Y le pido a casa que muestre los valores de su nuevo salon y cocina (por cierto, a tu clase Casa le faltan setters y getters para los atributos de Salon y Cocina)

Después de mostrarlos, modifico los valores del salon y de la cocina.

Y vuelvo a pedir a  casa que muestre de nuevo sus valores de salon y cocina.
Código: [Seleccionar]
public class Prueba {

public static void main(String[] args) {

SalonCasa miSalon = new SalonCasa("Gigante", 10);
CocinaCasa miCocina = new CocinaCasa(true, 100);

Casa miCasa = new Casa(300, "Calle Paraiso", miSalon, miCocina);

System.out.println("Televisores: " + miCasa.getSalon().getNumeroDeTelevisores());
System.out.println("Fuegos: " + miCasa.getCocina().getNumeroDeFuegos());

//Cambiamos valores de miSalon y miCocina
miSalon.setNumeroDeTelevisores(3);
miCocina.setnumeroDeFuegos(4);
System.out.println("\n--Valores de salon y cocina cambiados --\n");

System.out.println("Televisores: " + miCasa.getSalon().getNumeroDeTelevisores());
System.out.println("Fuegos: " + miCasa.getCocina().getNumeroDeFuegos());
}

}

Resultado en pantalla:
Citar
Televisores: 10
Fuegos: 100

--Valores de salon y cocina cambiados --

Televisores: 3
Fuegos: 4

Fíjate que casa muestra el cambio de valores, a pesar de que en el objeto casa no hemos hecho cambios.
Los cambios los hemos hecho en los objetos salon y cocina, pero como casa tiene "referencias" a esos objetos, pues casa se verá afectada por esos cambios.


Ahora vamos a hacer exactamente el mismo ejercicio, pero usando el constructor que tú habías escrito.
Es decir, no vamos a pasar por "referencia", si no que de forma artificial vamos pasar por "valor".
¿Cuál será el resultado en pantalla?
Citar
Televisores: 10
Fuegos: 100

--Valores de salon y cocina cambiados --

Televisores: 10
Fuegos: 100

 :o ¡Oh!
El resultado es distinto. Esta vez los cambios que hemos hecho en cocina y salon, no han afectado al objeto casa.
Y esto es porque en tu constructor no asignabas la "referencia" recibida entre paréntesis, si no que construías objetos totalmente nuevos con los "valores" de esas "referencias".

Y vuelvo a preguntar, ¿esto es un problema?
Podría serlo.

Imagina que tenemos un objeto Alumno, que representa a "Juanito" y que vive en la calle "Las Flores".

Y tenemos una clase llamada Curso, donde tenemos insertados a 25 alumnos, entre ellos a "Juanito"

Supongamos que ahora "Juanito" se muda de domicilio y ahora vive en la calle "Las Cortes"

Si cambiamos este dato en el objeto que representa a "Juanito", y resulta que al objeto Curso lo habíamos pasado por "valor" y no por "referencia", tendremos el problema de que el "Juanito" que está dentro de Curso tiene el domicilio antiguo a diferencia del objeto "Juanito" que es externo a Curso.

Esto es un problema, en un sistema informático no podemos consentir esta discrepancia de datos.
Es que además "Juanito" puede estar insertado en múltiples objetos además de Curso. Puede estar insertado en un objeto que agrupe a los alumnos que hacen actividades extraescolares, otro objeto que agrupe alumnos que han solicitado beca, etc...

Cada vez que un alumno cambie de domicilio, no podemos ir rastreando todos los objetos donde ande insertado para cambiarle a todos ese dato.

Por eso es mejor pasar los objetos por "referencia", porque así haciendo un único cambio, este se verá reflejado en todos demás objetos donde pueda estar insertado.

Habrá ocasiones donde sí vamos a querer hacer una copia del "valor" un objeto para que no compartan "referencia" y sean objetos independientes.
Pero no es lo habitual.


Disculpa si me he extendido demasiado con explicaciones.
Pero no quería decirte simplemente "así no se hace, hazlo asá..."
Quería explicarte el motivo y es que además este es un tema que no es fácil de entender al principio.

El asunto de pasar datos por "valor o por "referencia" da para mucha literatura. Así que no te preocupes si aún no te ha quedado claro. Tampoco es algo que deba preocuparte mucho ahora mismo.

Simplemente recuerda que cuando recibas objetos en un constructor, salvo casos muy concretos, lo que vamos a hacer es asignar directamente esas referencias a los atributos.
Citar
public Casa(double valorSuperficie, String valorDireccion, SalonCasa objetoSalon, CocinaCasa objetoCocina){
        superficie=valorSuperficie;
        direccion=valorDireccion;
        salon=objetoSalon;
        cocina=objetoCocina;
}

156
Citar
    public casa1(double valorSuperficie, String valorDireccion, SalonCasa1 objetoSalon, CocinaCasa1 objetoCocina){
        superficie=valorSuperficie;
        direccion=valorDireccion;
        salon=new SalonCasa1(objetoSalon.getNumeroDeTelevisores(), objetoSalon.getTipoSalon());
        cocina=new CocinaCasa1(objetoCocina.getEsIndependiente(), objetoCocina.getNumeroDeFuegos());}


El error se debe a que esa 'c' la has puesto en minúscula. Al ponerla así, no coincide con el nombre de la clase Casa1 y entonces Java no lo considera un constructor, si no un método cualquiera y se queja porque no se le ha puesto ningún tipo de retorno (void, int, String, etc...) ya que un método siempre ha de tener un retorno.

Cambia esa 'c' por una mayúscula y solucionado.

Por cierto, el enunciado pide que el constructor por defecto de SalonCasa, inicialice el tipo de salón como "desconocido".


Código: [Seleccionar]
public SalonCasa1(){
    numeroDeTelevisores=0;
    tipoSalon="Desconocido";}

Inicializarlo con cadena vacía está bien y además suele ser lo habitual.
Pero en este caso nos piden hacerlo de manera distinta.

157
Aprender a programar desde cero / Re: Ejercicios de Java
« en: 04 de Agosto 2022, 18:20 »
Lo haces ver muy facil pero de verdad no lo es.
Sí que lo es.
Pero claro, se necesita tiempo para aprender y familiarizarse con la programación en general, y en este caso, con Java en particular.

Estas cosas no se aprenden en dos semanas, ni en cuatro..., ni en seis meses...
Podemos memorizar las instrucciones y utilizarlas aún sin entender del todo cómo y por qué funcionan.
Pero pasado un tiempo, es cuando empezamos a asimilar ese conocimiento, a comprenderlo. Y es entonces cuando nos damos cuenta de que es la cosa más simple y sencilla del mundo.

Por cierto, si nuestro "predicado" fuera un poco más sencillo, si podríamos haberlo creado con una expresión lambda sin tener que escribir una clase completa.

Suponiendo que el filtro fuera un único objeto Salud, no un array, podríamos haberlo hecho así:
Citar
public class Filtro {

   public static void main(String[] args) {
      Salud[] arrayRecords = new Salud[]{
            new Salud("Sangre", "Colesterol"),
            new Salud("Termometro", "Temperatura"),
            new Salud("Oximetro", "Oxigeno"),
            new Salud("Tensiometro", "Presion Arterial"),
            new Salud("Glucosa", "Azucar en la Sangre"),

      };
      
      Salud filtroUnico = new Salud("Tensiometro", "Alcohol");

      Arrays.stream(arrayRecords).filter(a -> a.equals(filtroUnico)).forEach(System.out::println);

      
   }
}


Pero en nuestro caso, sí necesitamos un array con varios filtros con los que comparar, y para ello se necesita que intervenga un bucle.

Aunque, ahora que lo pienso... :o también podríamos haberlo hecho así:

Citar
public class Filtro {

   public static Salud[] arrayFiltro;

   public static void main(String[] args) {
      Salud[] arrayRecords = new Salud[]{
            new Salud("Sangre", "Colesterol"),
            new Salud("Termometro", "Temperatura"),
            new Salud("Oximetro", "Oxigeno"),
            new Salud("Tensiometro", "Presion Arterial"),
            new Salud("Glucosa", "Azucar en la Sangre"),

      };

      arrayFiltro = new Salud[]{
            new Salud("Gasa", "Suturas"),
            new Salud("Tensiometro", "Alcohol")
      };
      
      for (Salud filtro: arrayFiltro)
         Arrays.stream(arrayRecords).filter(a -> a.equals(filtro)).forEach(System.out::println);

      
   }
}
De esta forma tampoco necesitamos crear una clase Predicate.
Basta con poner un bucle que recorrar el array de filtros, y cada filtro de ese array se lo pasamos a la expresión lambda..
Y solucionado  ;D

Esto es lo que más me gusta de la programación. Que hay muchas formas de conseguir un mismo objetivo, y a poco que te pares a pensar, vas encontrando maneras más sencillas y con menos código de hacer lo mismo.

158
Aprender a programar desde cero / Re: Ejercicios de Java
« en: 03 de Agosto 2022, 18:05 »
Hola.
Esta linea no puede funcionar así:
Código: [Seleccionar]
ArrayList<String> filtrados = !Arrays.stream(arrayRecords).filter(arrayFiltro);
Dentro del método filter() no puedes pasarle un array primitivo y esperar a que la clase Arrays sepa qué tiene que hacer con él.

El método filter() lo que espera recibir es un "predicado", es decir, un objeto de la clase Predicate que le diga cuáles son la reglas de filtrado.

Y además el resultado de todo ese proceso, sería un Stream<Salud> no un ArrayList<String>

Vamos por partes.

Recuperamos la clase Salud a la que le escribimos un método equals() y un método toString().
Ambos van a resultar muy útiles a la hora de crear un Stream filtrando con un "predicado" y hacer que se muestre en pantalla.
Código: [Seleccionar]
public class Salud {

public final String item;
public final String item2;

public Salud(String item, String item2){
this.item = item;
this.item2 = item2;
}

@Override
public String toString() {
return String.format("Items del objeto: %s -- %s", item, item2);
}

@Override
public boolean equals(Object objeto) {

if (objeto instanceof Salud) { //Si objeto recibido es de clase Salud...
//Lo instanciamos como Salud
Salud otroObjeto = (Salud) objeto;
//Si al menos uno de sus items coinciden, se considerarán iguales
if (this.item.equals(otroObjeto.item) || this.item.equals(otroObjeto.item2)
|| this.item2.equals(otroObjeto.item) || this.item2.equals(otroObjeto.item2))
return true;
else
return false; //Ningún item coincide
}
else
return false; //Objeto recibido no es clase Salud
}
}

Ahora vamos a crear un "predicado", o sea una clase que implemente la interfaz Predicate
Es en esta clase donde vamos a indicar el tipo de dato que se ha de filtrar (clase Salud) y cuál es la regla de filtrado:
Código: [Seleccionar]
import java.util.function.Predicate;

public class FiltroPredicado implements Predicate<Salud> {

private Salud[] filtros; //Este será el array de los objetos de salud que hacen de filtro

public FiltroPredicado(Salud[] arrayDeFiltros) {
filtros = arrayDeFiltros;
}

@Override
public boolean test(Salud objetoSalud) {
//Con bucle comprobamos si el objeto salud coincide con algún filtro
for (Salud filtro: filtros)
if (filtro.equals(objetoSalud))
return true;
//Bucle FOR no ha devuelto TRUE, así que NO coincide con ningún filtro
return false;
}

}
La explico.
Tiene como atributo un array de objetos Salud que serán los que harán de filtros.
Este array se va a recibir por el constructor.

Luego hay un método llamado test(). Se ha de llamar así porque es un método heredado al implementar la interfaz Predicate (una interfaz es algo parecido a una clase). Esta interfaz la escribió el Sr. Java (el Sr. Java es un ente abstracto inventado por mí xD ) y decidió que ese método se ha de llamar test(), así que es lo que hay...

Cuando le pasemos este predicado a Arrays.stream().filter(), aquí filter() se va a fijar en qué dice test() sobre cómo hay que filtrar los objetos Salud.
Y ahí estamos diciendo que los objetos Salud del Stream, han de ser comparados con los objetos del array de filtros.
Esta comparación va a funcionar según lo que hemos escrito en el método equals() de la propia clase Salud

Como puedes ver, todo está conectado. El filtrado se hace según lo que diga el predicado y el predicado se apoya en lo que le dicen los propios objetos salud acerca de cómo se comparan entre ellos mismos.


Vale, tenemos la clase Salud y una clase que implementa Predicate.
¿Cómo hacemos ahora para filtrar y mostrar el resultado en pantalla?

Pues nos va a bastar con una sola línea, un poco larga eso sí:
Citar
import java.util.Arrays;

public class Filtro {

   public static Salud[] arrayFiltro;

   public static void main(String[] args) {
      Salud[] arrayRecords = new Salud[]{
            new Salud("Sangre", "Colesterol"),
            new Salud("Termometro", "Temperatura"),
            new Salud("Oximetro", "Oxigeno"),
            new Salud("Tensiometro", "Presion Arterial"),
            new Salud("Glucosa", "Azucar en la Sangre"),

      };

      arrayFiltro = new Salud[]{
            new Salud("Gasa", "Suturas"),
            new Salud("Tensiometro", "Alcohol")
      };

      Arrays.stream(arrayRecords).filter(new FiltroPredicado(arrayFiltro)).forEach(System.out::println);
      
   }
}

Fíjate que bonito el programa principal: declaramos un array, luego otro y después con una sola línea hacemos un filtrado y se muestra el resultado en pantalla.
Y fin.

Pero claro, previamente hemos tenido que escribir una clase Predicate y a nuestra clase Salud hemos tenido que dotarla de los métodos equals() y toString()
Sin este trabajo previo, esa línea tan bonita no serviría para nada.

Pruébalo y comenta si algo no se entiende.

159
El método apilar() ha de recibir un valor entre paréntesis.
Así está declarado:
Código: [Seleccionar]
public void apilar(int valor){
Pero tú al llamarlo no le estás dando ningún valor:
Código: [Seleccionar]
case 3:
    pila.apilar();
    break;

Es decir, le tienes que indicar el valor que quieres apilar.
Y se supone que tenemos solicitar al usuario dicho valor, algo como esto:
Código: [Seleccionar]
case 3:
    System.out.print("Indique valor que quiere apilar: ");
    pila.apilar(teclado.nextInt());
    break;

En cuanto al método retirar(), el fallo principal es que no has separado con ; la instrucción System.out de la llamada al método:
Código: [Seleccionar]
case 4:
    System.out.println("Ingrese el elemento a eliminar") pila.retirar();
    break;
Falta el ; y mejor si separamos en dos líneas para mayor legibilidad:
Código: [Seleccionar]
case 4:
    System.out.println("Ingrese el elemento a eliminar");
    pila.retirar();
    break;
Ahora no hay fallos de sintaxis, pero sí de lógica.

En el case 4, lanzas mensaje para que el usuario ingrese elemento a eliminar, pero no recoges por teclado ese elemento, así que el usuario no tiene posibilidad de teclear nada.
Pero es que además, el método retirar() no está preparado para recibir ningún argumento, así que aunque lo recogieras, no puedes dárselo al método.
El método retirar() que has escrito, lo que hace es retirar el primer nodo, no puedes indicarle un valor concreto para que lo retire:
Código: [Seleccionar]
    //Elimina el elemento que se encuentra en el tope de la pila
    public void retirar(){
        if(!esVacio()){
            //Asigna como primer nodo al siguiente de la pila
            inicio= inicio.getSiguiente();
            //Decremento de la variable del contador del tamaño de la pila
            tamanio--;
           
        }
    }

Así que tienes que escribir un método nuevo que sí pueda recibir el valor que se ha de eliminar.
Lo que puedes hacer es sobrecargar el método retirar() para que existan dos versiones:
- la que no recibe argumento y retira el primer nodo, que es la que ya tienes escrita
- la que si recibe argumento y retira el nodo que coincida con ese argumento.

Para sobrecargar un método, simplemente has de volver a declararlo, indicando ahora el argumento que ha de recibir
Citar
    //Elimina el elemento que se encuentra en el tope de la pila
    public void retirar(){
        if(!esVacio()){
            //Asigna como primer nodo al siguiente de la pila
            inicio= inicio.getSiguiente();
            //Decremento de la variable del contador del tamaño de la pila
            tamanio--;
           
        }
    }
   
    //Sobrecarga del método retirar(), para que reciba un valor concreto para retirar
    public void retirar(int valor) {
       
    }

Ahora intenta escribir el código necesario para retirar el nodo que coincida con ese valor.

Cuando lo tengas, ya en el case 4 podrás pedir un valor al usuario y proporcionárselo al método sobrecargado:
Código: [Seleccionar]
case 4:
    System.out.println("Ingrese el elemento a eliminar");
    pila.retirar(teclado.nextInt());
    break;

160
Aprender a programar desde cero / Re: Ejercicios de Java
« en: 29 de Julio 2022, 19:32 »
Perfecto  ;)


Sobre la interpretación que yo hice del segundo ejercicio, pues eso, una interpretación.
La tuya también puede ser válida.
Tú conserva ambas soluciones, porque al margen de la interpretación dada, ambas han servido para ver cosas interesantes.
En la mía se puede ver manejo de cadenas String con split() y contains().
En la tuya hemos visto como hacer un método equals() para poder comparar objetos de clase.
Todo eso es conocimento valioso.

Sobre las expresiones lambda, son estupendas sí, pero cuando se está aprendiendo no son ideales porque no se ve de forma clara la lógica que se está aplicando.
Así que primero aprender a desarrollar lógicas y ya luego en el futuro, aprender a simplificar código con las expresiones lambda.
Para ser sincero, yo rara vez las uso.

Páginas: 1 ... 3 4 5 6 7 [8] 9 10 11 12 13 ... 50

Sobre la educación, sólo puedo decir que es el tema más importante en el que nosotros, como pueblo, debemos involucrarnos.

Abraham Lincoln (1808-1865) Presidente estadounidense.

aprenderaprogramar.com: Desde 2006 comprometidos con la didáctica y divulgación de la programación

Preguntas y respuestas

¿Cómo establecer o cambiar la imagen asociada (avatar) de usuario?
  1. Inicia sesión con tu nombre de usuario y contraseña.
  2. Pulsa en perfil --> perfil del foro
  3. Elige la imagen personalizada que quieras usar. Puedes escogerla de una galería de imágenes o subirla desde tu ordenador.
  4. En la parte final de la página pulsa el botón "cambiar perfil".