Manual 2.pdf

Share Embed Donate


Short Description

Download Manual 2.pdf...

Description

Unidad 10. Polimorfismo    10.1 Introducción   

El polimorfismo nos permite escribir programas para procesar objetos que compartan la misma superclase en una  jerarquía de clases, como si todos fueran objetos de la superclase; esto puede simplificar la programación.  Con  el  polimorfismo,  podemos  diseñar  e  implementar  sistemas  que  puedan  extenderse  con  facilidad;  pueden  agregarse  nuevas  clases  con  sólo  modificar  un  poco  (o  nada)  las  porciones  generales  del  programa,  siempre  y  cuando  las  nuevas  clases  sean  parte  de  la  jerarquía  de  herencia  que  el  programa  procesa  en  forma  genérica.  Las  únicas  partes  de  un  programa  que  deben  alterarse  para  dar  cabida  a  las  nuevas  clases  son  las  que  requieren  un  conocimiento directo de las nuevas clases que el programador agregará a la jerarquía. 

10.3 Demostración del comportamiento polimórfico  

                                                       

Cuando el compilador encuentra una llamada a un método que se realiza a través de una variable, determina si el  método puede llamarse verificando el tipo de clase de la variable. Si esa clase contiene la declaración del método  apropiada  (o  hereda  una),  se  compila  la  llamada.  En  tiempo  de  ejecución,  el  tipo  del  objeto  al  cual  se  refiere  la  variable es el que determina el método que se utilizará. 

// Fig. 10.1: PruebaPolimorfismo.java  // Asignación de referencias a la superclase y la subclase, a variables de la superclase y la subclase.    public class PruebaPolimorfismo  {     public static void main( String args[] )   {          // asigna la referencia a la superclase a una variable de la superclase        EmpleadoPorComision3 empleadoPorComision = new EmpleadoPorComision3(            "Sue", "Jones", "222‐22‐2222", 10000, .06 );                              // asigna la referncia a la subclase a una variable de la subclase        EmpleadoBaseMasComision4 empleadoBaseMasComision =            new EmpleadoBaseMasComision4(            "Bob", "Lewis", "333‐33‐3333", 5000, .04, 300 );                   // invoca a toString en un objeto de la superclase, usando una variable de la superclase        System.out.printf( "%s %s:\n\n%s\n\n",            "Llamada a toString de EmpleadoPorComision3 con referencia de superclase ",           "a un objeto de la superclase", empleadoPorComision.toString() );          // invoca a toString en un objeto de la subclase, usando una variable de la subclase         System.out.printf( "%s %s:\n\n%s\n\n",            "Llamada a toString de EmpleadoBaseMasComision4 con referencia",           "de subclase a un objeto de la subclase",            empleadoBaseMasComision.toString() );          // invoca a toString en un objeto de la subclase, usando una variable de la superclase        EmpleadoPorComision3 empleadoPorComision2 =            empleadoBaseMasComision;         System.out.printf( "%s %s:\n\n%s\n",            "Llamada a toString de EmpleadoBaseMasComision4 con referencia de superclase",           "a un objeto de la subclase", empleadoPorComision2.toString() );       } // fin de main  } // fin de la clase PruebaPolimorfismo 

  Llamada a toString de EmpleadoPorComision3 con referencia de superclase a un objeto de la superclase:   empleado por comision: Sue Jones   numero de seguro social: 222-22-2222 ventas brutas: 10000.00   tarifa de comision: 0.06     Llamada a toString de EmpleadoBaseMasComision4 con referencia de subclase a un objeto de la subclase: sueldo base empleado por comision: Bob Lewis   con numero de seguro social: 333-33-3333   ventas brutas: 5000.00   tarifa de comision: 0.04   sueldo base: 300.00   Llamada a toString de EmpleadoBaseMasComision4 con referencia de superclase a un objeto de la subclase:   con sueldo base empleado por comision: Bob Lewis   numero de seguro social: 333-33-3333 ventas brutas: 5000.00   tarifa de comision: 0.04   sueldo base: 300.00    10.4 Clases y métodos abstractos   



  

   

 



En algunos casos es conveniente declarar clases para las cuales no tenemos la intención de crear instancias de objetos.  A dichas clases se les conoce como clases abstractas. Como se utilizan sólo como superclases en jerarquías de herencia,  nos referimos a ellas como superclases abstractas. Estas clases no pueden utilizarse para instanciar objetos, ya que están  incompletas.  El propósito principal de una clase abstracta es proporcionar una superclase apropiada, a partir de la cual puedan heredar  otras clases y, por ende, compartir un diseño común.  Las  clases  que  pueden  utilizarse  para  instanciar  objetos  se  llaman  clases  concretas.  Dichas  clases  proporcionan  implementaciones de cada método que declaran (algunas de las implementaciones pueden heredarse).  No  todas  las  jerarquías  de  herencia  contienen  clases  abstractas.  Sin  embargo,  a  menudo  los  programadores  escriben  código cliente que utiliza sólo tipos de superclases abstractas para reducir las dependencias del código cliente en un rango  de tipos de subclases específicas.  Algunas veces las clases abstractas constituyen varios niveles de la jerarquía.  Para hacer una clase abstracta, ésta se declara con la palabra clave abstract. Por lo general, una clase abstracta contiene  uno o más métodos abstractos.  Los métodos abstractos no proporcionan implementaciones.  Una  clase  que  contiene  métodos  abstractos  debe  declararse  como  clase  abstracta,  aun  si  esa  clase  contiene  métodos  concretos  (no  abstractos).  Cada  subclase  concreta  de  una  superclase  abstracta  también  debe  proporcionar  implementaciones concretas de los métodos abstractos de la superclase.  Los constructores y los métodos static no pueden declararse como abstract.  Aunque no podemos instanciar objetos de superclases abstractas, sí podemos usar superclases abstractas para declarar  variables  que  puedan  guardar  referencias  a  objetos  de  cualquier  clase  concreta  que  se  derive  de  esas  superclases  abstractas.  Por  lo  general,  los  programas  utilizan  dichas  variables  para  manipular  los  objetos  de  las  subclases  mediante  el  polimorfismo.  En especial, el polimorfismo es efectivo para implementar los sistemas de software en capas. 

10.5 Ejemplo práctico: sistema de nómina utilizando polimorfismo  

 

 

Al incluir un método abstracto en una superclase, obligamos a cada subclase directa de la superclase a sobrescribir ese  método abstracto para que pueda convertirse en una clase concreta. Esto permite al diseñador de la jerarquía de clases  demandar que cada subclase concreta proporcione una implementación apropiada del método.  La mayoría de las llamadas a los métodos se resuelven en tiempo de ejecución, con base en el tipo del objeto que se está  manipulando. Este proceso se conoce como vinculación dinámica o vinculación postergada.  Una referencia a la superclase puede utilizarse para invocar sólo a métodos de la superclase (y la superclase puede invocar  versiones sobrescritas de éstos en la subclase). 

El operador instanceof se puede utilizar para determinar si el tipo de un objeto específico tiene la relación “es un”  con un tipo específico.  Todos los objetos en Java conocen su propia clase y pueden acceder a esta información a través del método 

   

getClass,  que  todas  las  clases  heredan  de  la  clase  Object.  El  método  getClass  devuelve  un  objeto  de  tipo  Class  (del  paquete java.lang), el cual contiene información acerca del tipo del objeto, incluyendo el nombre de su clase.  La relación “es un” se aplica sólo entre la subclase y sus superclases, no viceversa.  No está permitido tratar de invocar a los métodos que sólo pertenecen a la subclase, en una referencia a la superclase.  Aunque  el  método  que  se  llame  en  realidad  depende  del  tipo  del  objeto  en  tiempo  de  ejecución,  podemos  usar  una  variable para invocar sólo a los métodos que sean miembros del tipo de esa variable, que el compilador verifica. 

  // Fig. 10.4: Empleado.java   La superclase abstracta Empleado.   

public abstract class Empleado  {     private String primerNombre;     private String apellidoPaterno;     private String numeroSeguroSocial;   

   // constructor con tres argumentos     public Empleado( String nombre, String apellido, String nss )  {        primerNombre = nombre;        apellidoPaterno = apellido;        numeroSeguroSocial = nss;     } // fin del constructor de Empleado con tres argumentos   

   // establece el primer nombre     public void establecerPrimerNombre( String nombre )  {        primerNombre = nombre;     } // fin del método establecerPrimerNombre   

   // devuelve el primer nombre     public String obtenerPrimerNombre()  {        return primerNombre;     } // fin del método obtenerPrimerNombre   

   // establece el apellido paterno     public void establecerApellidoPaterno( String apellido ) {        apellidoPaterno = apellido;     } // fin del método establecerApellidoPaterno   

   // devuelve el apellido paterno     public String obtenerApellidoPaterno() {        return apellidoPaterno;     } // fin del método obtenerApellidoPaterno   

   // establece el número de seguro social     public void establecerNumeroSeguroSocial( String nss ) {        numeroSeguroSocial = nss; // debe validar     } // fin del método establecerNumeroSeguroSocial   

   // devuelve el número de seguro social     public String obtenerNumeroSeguroSocial()  {        return numeroSeguroSocial;     } // fin del método obtenerNumeroSeguroSocial   

   // devuelve representación String de un objeto Empleado     public String toString() {        return String.format( "%s %s\nnumero de seguro social: %s",            obtenerPrimerNombre(), obtenerApellidoPaterno(), obtenerNumeroSeguroSocial() );     } // fin del método toString   

   // método abstracto sobrescrito por las subclases     public abstract double ingresos(); // aquí no hay implementación   

} // fin de la clase abstracta Empleado 

// Fig. 10.5: EmpleadoAsalariado.java  La clase EmpleadoAsalariado extiende a Empleado.   

public class EmpleadoAsalariado extends Empleado  {     private double salarioSemanal;   

   // constructor de cuatro argumentos     public EmpleadoAsalariado( String nombre, String apellido, String nss,  double salario ) {        super( nombre, apellido, nss ); // los pasa al constructor de Empleado        establecerSalarioSemanal( salario ); // valida y almacena el salario     } // fin del constructor de EmpleadoAsalariado con cuatro argumentos   

   // establece el salario     public void establecerSalarioSemanal( double salario ) {        salarioSemanal = salario  0), primer nombre, apellido paterno y saldo. ? 100 Bob Jones 24.98 Escriba el numero de cuenta (> 0), primer nombre, apellido paterno y saldo. ? 200 Steve Doe -345.67 Escriba el numero de cuenta (> 0), primer nombre, apellido paterno y saldo. ? 300 Pam White 0.00 Escriba el numero de cuenta (> 0), primer nombre, apellido paterno y saldo. ? 400 Sam Stone -42.16 Escriba el numero de cuenta (> 0), primer nombre, apellido paterno y saldo. ? 500 Sue Rich 224.62 Escriba el numero de cuenta (> 0), primer nombre, apellido paterno y saldo. ? ^Z 

 

14.5.2 Cómo leer datos de un archivo de texto de acceso secuencial      // Fig. 14.11: LeerArchivoTexto.java    Este programa lee un archivo de texto y muestra cada registro.  import java.io.File;    import java.io.FileNotFoundException;    import java.lang.IllegalStateException;    import java.util.NoSuchElementException;    import java.util.Scanner;        import com.deitel.jhtp7.cap14.RegistroCuenta;      public class LeerArchivoTexto {       private Scanner entrada;           // permite al usuario abrir el archivo       public void abrirArchivo()  {          try {           entrada = new Scanner( new File( "clientes.txt" ) );          } // fin de try          catch ( FileNotFoundException fileNotFoundException ) {             System.err.println( "Error al abrir el archivo." );             System.exit( 1 );          } // fin de catch       } // fin del método abrirArchivo         // lee registro del archivo       public void leerRegistros() {          // objeto que se va a escribir en la pantalla          RegistroCuenta registro = new RegistroCuenta();              System.out.printf( "%‐9s%‐15s%‐18s%10s\n", "Cuenta", "Primer nombre", "Apellido paterno", "Saldo" );            try // lee registros del archivo, usando el objeto Scanner          {             while ( entrada.hasNext() ) {                registro.establecerCuenta( entrada.nextInt() ); // lee el número de cuenta                registro.establecerPrimerNombre( entrada.next() ); // lee el primer nombre                registro.establecerApellidoPaterno( entrada.next() ); // lee el apellido paterno                registro.establecerSaldo( entrada.nextDouble() ); // lee el saldo                  // muestra el contenido del registro                System.out.printf( "%‐9d%‐15s%‐18s%10.2f\n", 

                                 

               registro.obtenerCuenta(), registro.obtenerPrimerNombre(),                 registro.obtenerApellidoPaterno(), registro.obtenerSaldo() );           } // fin de while        } // fin de try        catch ( NoSuchElementException elementException ) {           System.err.println( "El archivo no esta bien formado." );           entrada.close();           System.exit( 1 );        } // fin de catch        catch ( IllegalStateException stateException ) {           System.err.println( "Error al leer del archivo." );           System.exit( 1 );        } // fin de catch     } // fin del método leerRegistros   

   public void cerrarArchivo() {       // cierra el archivo y termina la aplicación        if ( entrada != null )           entrada.close(); // cierra el archivo   

   } // fin del método cerrarArchivo  } // fin de la clase LeerArchivoTexto 

         

// Fig. 14.12: PruebaLeerArchivoTexto.java        Este programa prueba la clase LeerArchivoTexto.    public class PruebaLeerArchivoTexto {     public static void main( String args[] ) {        LeerArchivoTexto aplicacion = new LeerArchivoTexto();            aplicacion.abrirArchivo();          aplicacion.leerRegistros();          aplicacion.cerrarArchivo();       } // fin de main    } // fin de la clase PruebaLeerArchivoTexto 

               

Cuenta   Primer nombre   100   Bob     200   Steve     300   Pam     400   Sam     500   Sue    

Apellido paterno   Jones       Doe       White       Stone       Rich      

Saldo 24.98  ‐345.67  0.00  ‐42.16  224.62 

 

14.5.3 Ejemplo práctico: un programa de solicitud de crédito 

                       

                       

// Fig. 14.13: OpcionMenu.java    Define un tipo enum para las opciones del programa de consulta de crédito.    public enum OpcionMenu {     // declara el contenido del tipo enum     SALDO_CERO( 1 ),     SALDO_CREDITO( 2 ),     SALDO_DEBITO( 3 ),     FIN( 4 );   

   private final int valor; // opción actual del menú   

   OpcionMenu( int valorOpcion ) {        valor = valorOpcion;     } // fin del constructor del tipo enum OpcionMenu       public int obtenerValor()  {        return valor;     } // fin del método obtenerValor  } // fin del tipo enum OpcionMenu  // Fig. 14.14: ConsultaCredito.java     Este programa lee un archivo secuencialmente y muestra su contenido con base en el tipo    //de cuenta que solicita el usuario   (saldo con crédito, saldo con débito o saldo de cero).  import java.io.File;  import java.io.FileNotFoundException;  import java.lang.IllegalStateException;  import java.util.NoSuchElementException;  import java.util.Scanner;   

import com.deitel.jhtp7.cap14.RegistroCuenta; 

  public class ConsultaCredito {   

     private OpcionMenu tipoCuenta;       private Scanner entrada;       private OpcionMenu opciones[] = { OpcionMenu.SALDO_CERO,        OpcionMenu.SALDO_CREDITO, OpcionMenu.SALDO_DEBITO,          OpcionMenu.FIN };    Figura 14.14 | Programa de consulta de crédito. (Parte 1 de 3). 

  // lee los registros del archivo y muestra sólo los registros del tipo apropiado       private void leerRegistros()  {          // objeto que se va a escribir en el archivo        RegistroCuenta registro = new RegistroCuenta();               try  {  // lee registros             // abre el archivo para leer desde el principio             entrada = new Scanner( new File( "clientes.txt" ) );               while ( entrada.hasNext() )  { // recibe los valores del archivo                registro.establecerCuenta( entrada.nextInt() ); // lee número de cuenta                registro.establecerPrimerNombre( entrada.next() ); // lee primer nombre                registro.establecerApellidoPaterno( entrada.next() ); // lee apellido paterno                registro.establecerSaldo( entrada.nextDouble() ); // lee saldo                  // si el tipo de cuenta es apropiado, muestra el registro                if ( debeMostrar( registro.obtenerSaldo() ) )                   System.out.printf( "%‐10d%‐12s%‐12s%10.2f\n", registro.obtenerCuenta(), registro.obtenerPrimerNombre(),                      registro.obtenerApellidoPaterno(), registro.obtenerSaldo() );             } // fin de while        } // fin de try          catch ( NoSuchElementException elementException )  {             System.err.println( "El archivo no esta bien formado." );             entrada.close();             System.exit( 1 );        } // fin de catch          catch ( IllegalStateException stateException )  {             System.err.println( "Error al leer del archivo." );             System.exit( 1 );          } // fin de catch          catch ( FileNotFoundException fileNotFoundException )  {           System.err.println( "No se puede encontrar el archivo." );             System.exit( 1 );          } // fin de catch          finally          {           if ( entrada != null )                entrada.close(); // cierra el objeto Scanner y el archivo          } // fin de finally       } // fin del método leerRegistros           // usa el tipo de registro para determinar si el registro debe mostrarse     private boolean debeMostrar( double saldo ) {          if ( ( tipoCuenta == OpcionMenu.SALDO_CREDITO )  && ( saldo  0 ) )           return true;              else if ( ( tipoCuenta == OpcionMenu.SALDO_CERO )  && ( saldo == 0 ) )             return true;              return false;     } // fin del método debeMostrar           // obtiene solicitud del usuario       private OpcionMenu obtenerSolicitud()  {          Scanner textoEnt = new Scanner( System.in );        int solicitud = 1;    Figura 14.14 | Programa de consulta de crédito. (Parte 2 de 3). 

        // muestra opciones de solicitud          System.out.printf( "\n%s\n%s\n%s\n%s\n%s\n",             "Escriba solicitud", " 1 ‐ Lista de cuentas con saldos de cero",             " 2 ‐ Lista de cuentas con saldos con credito",           " 3 ‐ Lista de cuentas con saldos con debito", " 4 ‐ Finalizar ejecucion" );             try // trata de recibir la opción del menú           {             do // recibe solicitud del usuario             {              System.out.print( "\n? " );                solicitud = textoEnt.nextInt();             } while ( ( solicitud  4 ) );          } // fin de try          catch ( NoSuchElementException elementException ) {           System.err.println( "Entrada invalida." );             System.exit( 1 );          } // fin de catch             return opciones[ solicitud ‐ 1 ]; // devuelve valor de enum para la opción       } // fin del método obtenerSolicitud         public void procesarSolicitudes() {          // obtiene la solicitud del usuario (saldo de cero, con crédito o con débito)          tipoCuenta = obtenerSolicitud();           while ( tipoCuenta != OpcionMenu.FIN ) {             switch ( tipoCuenta )  {                case SALDO_CERO:                   System.out.println( "\nCuentas con saldos de cero:\n" );                   break;                case SALDO_CREDITO:                 System.out.println( "\nCuentas con saldos con credito:\n" );                   break;                case SALDO_DEBITO:                   System.out.println( "\nCuentas con saldos con debito:\n" );                   break;           } // fin de switch                leerRegistros();             tipoCuenta = obtenerSolicitud();             } // fin de while     } // fin del método procesarSolicitudes    } // fin de la clase ConsultaCredito        // Fig. 14.15: PruebaConsultaCredito.java    // Este programa prueba la clase ConsultaCredito.       public class PruebaConsultaCredito {       public static void main( String args[] )  {          ConsultaCredito aplicacion = new ConsultaCredito();          aplicacion.procesarSolicitudes();     }     } // fin de main  } // fin de la clase PruebaConsultaCredito       

  Escriba solicitud   1 - Lista de cuentas con saldos de cero 2 - Lista de cuentas con saldos con credito   3 - Lista de cuentas con saldos con debito   4 - Finalizar ejecución   ?1   Cuentas con saldos de cero:   300 Pam White 0.00   Escriba solicitud   1 - Lista de cuentas con saldos de cero   2 - Lista de cuentas con saldos con credito 3 - Lista de cuentas con saldos con debito   4 - Finalizar ejecución   ?2   con saldos con credito:   Cuentas 200 Steve Doe -345.67   400 Sam Stone -42.16   Escriba solicitud   1 - Lista de cuentas con saldos de cero de cuentas con saldos con credito   23 -- Lista Lista de cuentas con saldos con debito   4 - Finalizar ejecución   ?3   Cuentas con saldos con debito:   100 Bob Jones 24.98 Sue Rich 224.62   500   ? 4    14.6 Serialización de objetos      

Java cuenta con un mecanismo llamado Serialización de objetos, el cual permite escribir o leer objetos completos  mediante un flujo.  Un objeto serializado es un objeto que se representa como una secuencia de bytes, e incluye los datos del objeto,  así como información acerca del tipo del objeto y los tipos de datos almacenados en el mismo.  Una vez que se escribe un objeto serializado en un archivo, se puede leer del archivo y deserializarse; es decir, se  puede utilizar la información de tipo y los bytes que representan al objeto para recrearlo en la memoria.  Las  clases  ObjectInputStream  y  ObjectOutputStream,  que  implementan  en  forma  respectiva  a  las  interfaces  ObjectInput y ObjectOutput, permiten leer o escribir objetos completos de/a un flujo (posiblemente un archivo).  Sólo  las  clases  que  implementan  a  la  interfaz  Serializable  pueden  serializarse  y  deserializarse  con  objetos  ObjectOutputStream y ObjectInputStream. 

  14.6.1 Creación de un archivo de acceso secuencial mediante el uso de la Serialización de objetos  // Fig. 14.17: RegistroCuentaSerializable.java    Una clase que representa un registro de información.  package com.deitel.jhtp7.cap14; // empaquetada para reutilizarla   

import java.io.Serializable;   

public class RegistroCuentaSerializable implements Serializable {     private int cuenta;     private String primerNombre;     private String apellidoPaterno;     private double saldo;   

  // el constructor sin argumentos llama al otro constructor con valores predeterminados     public RegistroCuentaSerializable()    {        this( 0, "", "", 0.0 );     } // fin del constructor de RegistroCuentaSerializable sin argumentos 

Figura 14.17 | La clase RegistroCuentaSerializable para los objetos serializables. (Parte 1 de 2).

                                                             

 

     // el constructor con cuatro argumentos inicializa un registro     public RegistroCuentaSerializable( int cta, String nombre, String apellido, double sal ) {        establecerCuenta( cta );        establecerPrimerNombre( nombre );        establecerApellidoPaterno( apellido );        establecerSaldo( sal );     } // fin del constructor de RegistroCuentaSerializable con cuatro argumentos   

   public void establecerCuenta( int cta ) {      // establece el número de cuenta        cuenta = cta;     } // fin del método establecerCuenta   

   public int obtenerCuenta()  {       // obtiene el número de cuenta        return cuenta;      } // fin del método obtenerCuenta          public void establecerPrimerNombre( String nombre ) {         // establece el primer nombre         primerNombre = nombre;     } // fin del método establecerPrimerNombre   

   public String obtenerPrimerNombre()  {     // obtiene el primer nombre        return primerNombre;      } // fin del método obtenerPrimerNombre   

   public void establecerApellidoPaterno( String apellido ) {      // establece el apellido paterno          apellidoPaterno = apellido;     } // fin del método establecerApellidoPaterno   

   public String obtenerApellidoPaterno()  {       // obtiene el apellido paterno        return apellidoPaterno;      } // fin del método obtenerApellidoPaterno   

   public void establecerSaldo( double sal )  {      // establece el saldo         saldo = sal;     } // fin del método establecerSaldo   

   public double obtenerSaldo()  {        // obtiene el saldo        return saldo;      } // fin del método obtenerSaldo  } // fin de la clase RegistroCuentaSerializable 

Figura 14.17 | La clase RegistroCuentaSerializable para los objetos serializables. (Parte 2 de 2). ‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐      // Fig. 14.18: CrearArchivoSecuencial.java Escritura de objetos en forma secuencial a un archivo, con la clase  ObjectOutputStream.    import java.io.FileOutputStream;    import java.io.IOException;    import java.io.ObjectOutputStream;    import java.util.NoSuchElementException;  import java.util.Scanner;        import com.deitel.jhtp7.cap14.RegistroCuentaSerializable;        public class CrearArchivoSecuencial {       private ObjectOutputStream salida; // envía los datos a un archivo    Figura 14.18 | Archivo secuencial creado mediante ObjectOutputStream. (Parte 1 de 2).         

                                                                                                   

   public void abrirArchivo()  {        // permite al usuario especificar el nombre del archivo        try  {   // abre el archivo           salida = new ObjectOutputStream( new FileOutputStream( "clientes.ser" ) );        } // fin de try        catch ( IOException ioException ) {           System.err.println( "Error al abrir el archivo." );        } // fin de catch     } // fin del método abrirArchivo   

   public void agregarRegistros() {          // agrega registros al archivo        RegistroCuentaSerializable registro; // objeto que se va a escribir al archivo        int numeroCuenta = 0; // número de cuenta para el objeto registro        String primerNombre; // primer nombre para el objeto registro        String apellidoPaterno; // apellido paterno para el objeto registro        double saldo; // saldo para el objeto registro   

      Scanner entrada = new Scanner( System.in );   

      System.out.printf( "%s\n%s\n%s\n%s\n\n",           "Para terminar de introducir datos, escriba el indicador de fin de archivo ", "Cuando se le pida que introduzca los datos.",   "En UNIX/Linux/Mac OS X escriba  d y oprima Intro", "En Windows escriba  z y oprima Intro" );   

      System.out.printf( "%s\n%s",  "Escriba el numero de cuenta (> 0), primer nombre, apellido y saldo.", "? " );   

      while ( entrada.hasNext() )  {  // itera hasta el indicador de fin de archivo           try {    // envía los valores al archivo              numeroCuenta = entrada.nextInt(); // lee el número de cuenta              primerNombre = entrada.next(); // lee el primer nombre              apellidoPaterno = entrada.next(); // lee el apellido paterno              saldo = entrada.nextDouble(); // lee el saldo                if ( numeroCuenta > 0 ) {                 // crea un registro nuevo                 registro = new RegistroCuentaSerializable( numeroCuenta, primerNombre, apellidoPaterno, saldo );                 salida.writeObject( registro ); // envía el registro como salida              } // fin de if              else {                 System.out.println( "El numero de cuenta debe ser mayor de 0." );              } // fin de else           } // fin de try           catch ( IOException ioException ) {              System.err.println( "Error al escribir en el archivo." );              return;           } // fin de catch           catch ( NoSuchElementException elementException ) {              System.err.println( "Entrada invalida. Intente de nuevo." );              entrada.nextLine(); // descarta la entrada para que el usuario intente de nuevo           } // fin de catch           System.out.printf( "%s %s\n%s", "Escriba el numero de cuenta (>0),",  "primer nombre, apellido y saldo.", "? " );        } // fin de while     } // fin del método agregarRegistros          public void cerrarArchivo() {   // cierra el archivo y termina la aplicación        try  { // cierra el archivo           if ( salida != null )              salida.close();        } // fin de try        catch ( IOException ioException ) {           System.err.println( "Error al cerrar el archivo." );           System.exit( 1 );        } // fin de catch     } // fin del método cerrarArchivo  } // fin de la clase CrearArchivoSecuencial 

                   

                       

 

 

// Fig. 14.19: PruebaCrearArchivoSecuencial.java  // Prueba de la clase CrearArchivoSecuencial.   

public class PruebaCrearArchivoSecuencial {     public static void main( String args[] ) {        CrearArchivoSecuencial aplicacion = new CrearArchivoSecuencial();   

      aplicacion.abrirArchivo();        aplicacion.agregarRegistros();        aplicacion.cerrarArchivo();   

   } // fin de main  } // fin de la clase PruebaCrearArchivoSecuencial  Para terminar de introducir datos, escriba el indicador de fin de archivo cuando se le pida que introduzca los datos. En UNIX/Linux/Mac OS X escriba d y oprima Intro En Windows escriba z y oprima Intro Escriba el numero de cuenta (> 0), primer nombre, apellido y saldo. ? 100 Bob Jones 24.98 Escriba el numero de cuenta (> 0), primer nombre, apellido y saldo. ? 200 Steve Doe -345.67 Escriba el numero de cuenta (> 0), primer nombre, apellido y saldo. ? 300 Pam White 0.00 Escriba el numero de cuenta (> 0), primer nombre, apellido y saldo. ? 400 Sam Stone -42.16 Escriba el numero de cuenta (> 0), primer nombre, apellido y saldo. ? 500 Sue Rich 224.62 Escriba el numero de cuenta (> 0), primer nombre, apellido y saldo. ? ^Z 

  14.6.2 Lectura y deserialización de datos de un archivo de acceso Secuencial  // Fig. 14.20: LeerArchivoSecuencial.java  // Este programa lee un archivo de objetos en forma secuencial   y muestra cada registro.  import java.io.EOFException;  import java.io.FileInputStream;  import java.io.IOException;  import java.io.ObjectInputStream;   

import com.deitel.jhtp7.cap14.RegistroCuentaSerializable;   

public class LeerArchivoSecuencial  {     private ObjectInputStream entrada;       // permite al usuario seleccionar el archivo a abrir     public void abrirArchivo() {        try  {  // abre el archivo           entrada = new ObjectInputStream( new FileInputStream( "clientes.ser" ) );        } // fin de try        catch ( IOException ioException ) {           System.err.println( "Error al abrir el archivo." );        } // fin de catch     } // fin del método abrirArchivo      // lee el registro del archivo     public void leerRegistros()  {        RegistroCuentaSerializable registro;        System.out.printf( "%‐10s%‐15s%‐15s%10s\n", "Cuenta", "Primer nombre", "Apellido paterno", "Saldo" ); 

Figura 14.20 | Lectura de un archivo secuencial, usando un objeto ObjectInputStream. (Parte 1 de 2). 

                                                 

      try  {  // recibe los valores del archivo           while ( true ) {              registro = ( RegistroCuentaSerializable ) entrada.readObject();                // muestra el contenido del registro              System.out.printf( "%‐10d%‐15s%‐15s%11.2f\n",                 registro.obtenerCuenta(), registro.obtenerPrimerNombre(), registro.obtenerApellidoPaterno(), registro.obtenerSaldo() );           } // fin de while        } // fin de try          catch ( EOFException endOfFileException )  {           return; // se llegó al fin del archivo        } // fin de catch        catch ( ClassNotFoundException classNotFoundException )  {           System.err.println( "No se pudo crear el objeto." );        } // fin de catch        catch ( IOException ioException ) {           System.err.println( "Error al leer del archivo." );        } // fin de catch     } // fin del método leerRegistros       // cierra el archivo y termina la aplicación     public void cerrarArchivo() {        try   {  // cierra el archivo y sale           if ( entrada != null )              entrada.close();           System.exit( 0 );        } // fin de try        catch ( IOException ioException ) {           System.err.println( "Error al cerrar el archivo." );             System.exit( 1 );          } // fin de catch     } // fin del método cerrarArchivo    } // fin de la clase LeerArchivoSecuencial 

  Figura 14.20 | Lectura de un archivo secuencial, usando un objeto ObjectInputStream. (Parte 2 de 2).      // Fig. 14.21: PruebaLeerArchivoSecuencial.java    // Este programa prueba la clase ReadSequentialFile.     public class PruebaLeerArchivoSecuencial {       public static void main( String args[] )   {          LeerArchivoSecuencial aplicacion = new LeerArchivoSecuencial();           aplicacion.abrirArchivo();          aplicacion.leerRegistros();          aplicacion.cerrarArchivo();        } // fin de main    } // fin de la clase PruebaLeerArchivoSecuencial   

 

 

14.7 Clases adicionales de java.io    



La  interfaz  ObjectOutput  contiene  el  método  writeObject,  el  cual  recibe  un  objeto  Object  que  implementa  a  la  interfaz Serializable como argumento y escribe su información en un objeto OutputStream.  La interfaz ObjectInput contiene el método readObject, que lee y devuelve una referencia a un objeto Object de un  objeto InputStream. Una vez que se ha leído un objeto, su referencia puede convertirse al tipo actual del objeto.  El  uso  de  búfer  es  una  técnica  para  mejorar  el  rendimiento  de  E/S.  Con  un  objeto  BufferedOutputStream,  cada  instrucción de salida no necesariamente produce una transferencia física real de datos al dispositivo de salida. En  vez de ello, cada operación de salida se dirige hacia una región en memoria llamada búfer, la cual es lo bastante  grande  como  para  contener  los  datos  de  muchas  operaciones  de  salida.  La  transferencia  actual  al  dispositivo  de  salida se realiza entonces en una sola operación de salida física extensa cada vez que se llena el búfer.  Con  un  objeto  BufferedInputStream,  muchos  trozos  “lógicos”  de  datos  de  un  archivo  se  leen  como  una  sola  operación de entrada física extensa y se colocan en un búfer de memoria. A medida que un programa solicita cada  nuevo  trozo de  datos,  se  obtiene  del  búfer.  Cuando  el  búfer  está  vacío,  se  lleva  a  cabo  la  siguiente  operación  de  entrada física real desde el dispositivo de entrada, para leer el nuevo grupo de trozos “lógicos” de datos. 

  14.8 Abrir archivos con JFileChooser  

La  clase  JFileChooser  se  utiliza  para  mostrar  un  cuadro  de  diálogo,  que  permite  a  los  usuarios  de  un  programa  seleccionar archivos con facilidad, mediante una GUI. 

    // Fig. 14.22: DemostracionFile.java        Demostración de la clase File.    import java.awt.BorderLayout;    import java.awt.event.ActionEvent;    import java.awt.event.ActionListener;    import java.io.File;    import javax.swing.JFileChooser;    import javax.swing.JFrame;  import javax.swing.JOptionPane;    import javax.swing.JScrollPane;    import javax.swing.JTextArea;    import javax.swing.JTextField;        public class DemostracionFile extends JFrame {       private JTextArea areaSalida; // se utiliza para salida     private JScrollPane panelDespl; // se utiliza para que la salida pueda desplazarse           // establece la GUI       public DemostracionFile()  {          super( "Prueba de la clase File" );            areaSalida = new JTextArea();              // agrega areaSalida a panelDespl          panelDespl = new JScrollPane( areaSalida );             add( panelDespl, BorderLayout.CENTER ); // agrega panelDespl a la GUI              setSize( 400, 400 ); // establece el tamaño de la GUI           setVisible( true ); // muestra la GUI          analizarRuta(); // crea y analiza un objeto File       } // fin del constructor de DemostracionFile           // permite al usuario especificar el nombre del archivo       private File obtenerArchivo()  {        // muestra el cuadro de diálogo de archivos, para que el usuario pueda elegir el archivo a abrir          JFileChooser selectorArchivos = new JFileChooser();          selectorArchivos.setFileSelectionMode(             JFileChooser.FILES_AND_DIRECTORIES );      Figura 14.22 | Demostración de JFileChooser. (Parte 1 de 2).

                                                                                                             

      int resultado = selectorArchivos.showOpenDialog( this );          // si el usuario hizo clic en el botón Cancelar en el cuadro de diálogo, regresa        if ( resultado == JFileChooser.CANCEL_OPTION )           System.exit( 1 );          File nombreArchivo = selectorArchivos.getSelectedFile(); // obtiene el archivo seleccionado          // muestra error si es inválido        if ( ( nombreArchivo == null ) || ( nombreArchivo.getName().equals( "" ) ) )   {           JOptionPane.showMessageDialog( this, "Nombre de archivo inválido",              "Nombre de archivo inválido", JOptionPane.ERROR_MESSAGE );           System.exit( 1 );        } // fin de if          return nombreArchivo;     } // fin del método obtenerArchivo       // muestra información acerca del archivo que especifica el usuario     public void analizarRuta()  {        // crea un objeto File basado en la entrada del usuario        File nombre = obtenerArchivo();          if ( nombre.exists() )    {   // si el nombre existe, muestra información sobre él           // muestra la información sobre el archivo (o directorio)           areaSalida.setText( String.format(  "%s%s\n%s\n%s\n%s\n%s%s\n%s%s\n%s%s\n%s%s\n%s%s",              nombre.getName(), " existe",              ( nombre.isFile() ? "es un archivo" : "no es un archivo" ),              ( nombre.isDirectory() ? "es un directorio" : "no es un directorio" ),              ( nombre.isAbsolute() ? "es una ruta absoluta" :  "no es una ruta absoluta" ), "Ultima modificacion: ",              nombre.lastModified(), "Tamanio: ", nombre.length(),               "Ruta: ", nombre.getPath(), "Ruta absoluta: ",              nombre.getAbsolutePath(), "Padre: ", nombre.getParent() ) );             if ( nombre.isDirectory() ) { // imprime el listado del directorio              String directorio[] = nombre.list();              areaSalida.append( "\n\nContenido del directorio:\n" );                   for ( String nombreDirectorio : directorio )                 areaSalida.append( nombreDirectorio + "\n" );           } // fin de else        } // fin de if exterior        else // no es archivo ni directorio, imprime mensaje de error        {           JOptionPane.showMessageDialog( this, nombre +              " no existe.", "ERROR", JOptionPane.ERROR_MESSAGE );        } // fin de else       } // fin del método analizarRuta  } // fin de la clase DemostracionFile 

// Fig. 14.23: PruebaDemostracionFile.java    Prueba de la clase DemostracionFile.  import javax.swing.JFrame;   

public class PruebaDemostracionFile  {     public static void main( String args[] )   {        DemostracionFile aplicacion = new DemostracionFile();        aplicacion.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE );   

   } // fin de main  } // fin de la clase PruebaDemostracionFile 

 

 

 

Unidad 15. Recursividad  15.1 Introducción   

Un método recursivo se llama a sí mismo en forma directa o indirecta a través de otro método.  Cuando se llama a un método recursivo para resolver un problema, en realidad el método es capaz de resolver sólo  el (los) caso(s) más simple(s), o caso(s) base. Si se llama con un caso base, el método devuelve un resultado. 

  15.2 Conceptos de recursividad  





 

Si se llama a un método recursivo con un problema más complejo que el caso base, por lo general, divide el  problema en dos piezas conceptuales: una pieza que el método sabe cómo resolver y otra pieza que no sabe cómo  resolver.  Para que la recursividad sea factible, la pieza que el método no sabe cómo resolver debe asemejarse al problema  original, pero debe ser una versión ligeramente más simple o pequeña del mismo. Como este nuevo problema se  parece al problema original, el método llama a una nueva copia de sí mismo para trabajar en el problema más  pequeño; a esto se le conoce como paso recursivo.  Para que la recursividad termine en un momento dado, cada vez que un método se llame a sí mismo con una  versión más simple del problema original, la secuencia de problemas cada vez más pequeños debe converger en un  caso base.  Cuando el método reconoce el caso base, devuelve un resultado a la copia anterior del método.  Una llamada recursiva puede ser una llamada a otro método, que a su vez realiza una llamada de vuelta al método  original. Dicho proceso sigue provocando una llamada recursiva al método original. A esto se le conoce como  llamada recursiva indirecta, o recursividad indirecta. 

  15.3 Ejemplo de uso de recursividad: factoriales  

La acción de omitir el caso base, o escribir el paso recursivo de manera incorrecta para que no converja en el caso  base, puede ocasionar una recursividad infinita, con lo cual se agota la memoria en cierto punto. Este error es  análogo al problema de un ciclo infinito en una solución iterativa (no recursiva). 

15.4 Ejemplo de uso de recursividad: serie de Fibonacci    

La serie de Fibonacci empieza con 0 y 1, y tiene la propiedad de que cada número subsiguiente de Fibonacci es la  suma de los dos anteriores.  La proporción de números de Fibonacci sucesivos converge en un valor constante de 1.618…, un número al que se le  denomina la proporción dorada, o media dorada.  Algunas soluciones recursivas, como la de Fibonacci (que realiza dos llamadas por cada paso recursivo), producen  una “explosión” de llamadas a métodos. 

// Fig. 15.3: CalculoFactorial.java    Método factorial recursivo.    public class CalculoFactorial {     // declaración recursiva del método factorial     public long factorial( long numero ) {        if ( numero  3 1 --> 2 3 --> 2 1 --> 3 2 --> 1 2 --> 3 1 --> 3 

15.8 Fractales o Un fractal es una figura geométrica que se genera a partir de un patrón que se repite en forma recursiva, un número infinito de veces. o Los fractales tienen una propiedad de auto-similitud: las subpartes son copias de tamaño reducido de toda la pieza.

Pág. 66  

15.9 “Vuelta atrás” recursiva (backtracking)  

Al uso de la recursividad para regresar a un punto de decisión anterior se le conoce como “vuelta atrás” recursiva. Si  un conjunto de llamadas recursivas no produce como resultado una solución al problema, el programa retrocede  hasta  el  punto  de  decisión  anterior  y  toma  una  decisión  distinta,  lo  cual  a  menudo  produce  otro  conjunto  de  llamadas recursivas. 

View more...

Comments

Copyright ©2017 KUPDF Inc.
SUPPORT KUPDF