viernes, 30 de septiembre de 2011

17.- Interfaces


Una interfaz es una especie de plantilla para la construcción de clases. Normalmente una interfaz se compone de un conjunto de declaraciones de cabeceras de métodos (sin implementar, de forma similar a un método abstracto) que especifican un protocolo de comportamiento para una o varias clases. Además, una clase puede implementar una o varias interfaces: en ese caso, la clase debe proporcionar la declaración y definición de todos los métodos de cada una de las interfaces o bien declararse como clase abstract. Por otro lado, una interfaz puede emplearse también para declarar constantes que luego puedan ser utilizadas por otras clases.

Una interfaz puede parecer similar a una clase abstracta, pero existen una serie de diferencias entre una interfaz y una clase abstracta:

  • Todos los métodos de una interfaz se declaran implícitamente como abstractos y públicos. 
  • Una clase abstracta no puede implementar los métodos declarados como abstractos, una interfaz no puede implementar ningún método (ya que todos son abstractos). 
  • Una interfaz no declara variables de instancia.
  • Una clase puede implementar varias interfaces, pero sólo puede tener una clase ascendiente
    directa.
  •  Una clase abstracta pertenece a una jerarquía de clases mientras que una interfaz no pertenece a una jerarquía de clases. En consecuencia, clases sin relación de herencia pueden implementar la misma interfaz.
Declaración de una interfaz


La declaración de una interfaz es similar a una clase, aunque emplea la palabra reservada interface en lugar de class y no incluye ni la declaración de variables de instancia ni la implementación del cuerpo de los métodos (sólo las cabeceras). La sintaxis de declaración de una interfaz es la siguiente:

                    public interface IdentificadorInterfaz {
                      // Cuerpo de la interfaz . . .
                      }

Una interfaz declarada como public debe ser definida en un archivo con el mismo nombre de la interfaz y con extensión .java. Las cabeceras de los métodos declarados en el cuerpo de la interfaz se separan entre sí por caracteres de punto y coma y todos son declarados implícitamente como public y abstract (se pueden omitir). Por su parte, todas las constantes incluidas en una interfaz se declaran implícitamente como public, static y final (también se pueden omitir) y es necesario inicializarlas en la misma sentencia de declaración.

Por ejemplo, la interfaz Modificacion declara la cabecera de un único método:


/**
* Declaracion de la interfaz Modificacion
*/
        public interface Modificacion {
          void incremento(int a);
         }
que se almacena en el archivo fuente Modificacion.java y que, al compilarse:

$>javac Modificacion.java

genera un archivo Modificacion.class. Al no corresponder a una clase que implementa un método main, este archivo no puede ejecutarse con el intérprete de Java.

Segundo ejemplo: la interfaz constantes declara dos constantes reales con el siguiente

código fuente:
/**
* Declaracion de la interfaz Constantes
*/
                  public interface Constantes {
                       double valorMaximo = 10000000.0;
                       double valorMinimo = -0.01;
                   }

que se almacena en el archivo fuente Constantes.java y que, al compilarse, genera un archivo Constantes.class

Tercer ejemplo: la interfaz Numerico declara una constante real y dos cabeceras de
métodos con el siguiente código fuente:
/**
* Declaracion de la interfaz Numerico
*/

            public interface Numerico {
               double EPSILON = 0.000001;
               void establecePrecision(float p);
               void estableceMaximo(float m);
                 }

que se almacena en el archivo fuente Numerico.java y que, al compilarse, genera un archivo
Numerico.class.


 Implementación de una interfaz en una clase

Para declarar una clase que implemente una interfaz es necesario utilizar la palabra reservada implements en la cabecera de declaración de la clase. Las cabeceras de los métodos (identificador y número y tipo de parámetros) deben aparecer en la clase tal y como aparecen en la interfaz implementada. Por ejemplo, la clase Acumulador implementa la interfaz Modificacion y por lo tanto debe declarar un método incremento:

/**
* Declaracion de la clase Acumulador
*/
                   public class Acumulador implements Modificacion {
                           private int valor;
                          public Acumulador (int i) {
                         valor = i;
        }

                 public int daValor () {
                        return valor;
                 }

                 public void incremento (int a) {
                         valor += a;
                   }
  }

Esta cabecera con la palabra implements... implica la obligación de la clase Acumulador de definir el método incremento declarado en la interfaz Modificacion. El siguiente código muestra un ejemplo de uso de la clase Acumulador.

/**
* Demostracion de la clase Acumulador
*/

                    public class PruebaAcumulador {
                          public static void main (String [] args) {
                          Acumulador p = new Acumulador(25);
                          p.incremento(12);
                          System.out.println(p.daValor());
                      }
    }

La compilación y posterior ejecución del código anterior origina la siguiente salida por pantalla:
$>javac PruebaAcumulador.java
$>java PruebaAcumulador
37

La clase Acumulador tendría también la posibilidad de utilizar directamente las constantes declaradas en la interfaz si las hubiera.
Para poder emplear una constante declarada en una interfaz, las clases que no implementen esa interfaz deben anteponer el identificador de la interfaz al de la constante.


Jerarquía entre interfaces

La jerarquía entre interfaces permite la herencia simple y múltiple. Es decir, tanto la declaración de una clase, como la de una interfaz pueden incluir la implementación de otras interfaces. Los identificadores de las interfaces se separan por comas. Por ejemplo, la interfaz Una implementa otras dos interfaces: Dos y Tres.

             public interface Una implements Dos, Tres {
                   // Cuerpo de la interfaz . . .
                 }

Las clases que implementan la interfaz Una también lo hacen con Dos y Tres.
Otro ejemplo: pueden construirse dos interfaces, Constantes y Variaciones, y una clase, Factura, que las implementa:

         // Declaracion de la interfaz Constantes
            public interface Constantes {
                   double valorMaximo = 10000000.0;
                   double valorMinimo = -0.01;
       }

         // Declaracion de la interfaz Variaciones
          public interface Variaciones {
                        void asignaValor(double x);
                        void rebaja(double t);
        }

          // Declaracion de la clase Factura
            public class Factura implements Constantes, Variaciones {
                      private double totalSinIVA;
                      public final static double IVA = 0.16;

          public double sinIVA() {
              return totalSinIVA;
          }

          public double conIVA() {
                return totalSinIVA * (1+IVA);
            }

        public void asignaValor(double x) {
                 if (valorMinimo<x) totalSinIVA=x;
                 else totalSinIVA=0;
            }

            public void rebaja(double t) {
            totalSinIVA *= (1-t/100);
}
                 public static void main (String [] args) {
                          factura a = new factura();
                          a.asignaValor(250.0);
                          System.out.println("El precio sin IVA es: " + a.sinIVA());
                          System.out.println("El precio con IVA es: " + a.conIVA());
                          System.out.println("Rebajado durante el mes de mayo un 20%");
                          a.rebaja(20);
                          System.out.println("Rebajado sin IVA es: " + a.sinIVA());
                          System.out.println("Rebajado con IVA es: " + a.conIVA());
                        }
  }

Si una interfaz implementa otra, incluye todas sus constantes y declaraciones de métodos, aunque puede  redefinir tanto constantes como métodos.

Nota: Es peligroso modificar una interfaz ya que las clases dependientes dejan de funcionar hasta que éstas implementen los nuevos métodos.

Una clase puede simultáneamente descender de otra clase e implementar una o varias interfaces. En este caso la seccion implements se coloca a continuación de extends en la cabecera de declaración de la clase. 

Por ejemplo:
                 public class ClaseDescendiente extends ClaseAscendiente implements Interfaz {
                     ...
                 }

Utilización de una interfaz como un tipo de dato

Al declarar una interfaz, se declara un nuevo tipo de referencia. Pueden emplearse identificadores de interfaz en cualquier lugar donde se pueda utilizar el identificador de un tipo de dato (o de una clase). El objetivo es garantizar la sustituibilidad por cualquier instancia de una clase que la implemente. Por ejemplo, puede emplearse como tipo de un parámetro de un método:

      public class Calculos {
      public void asignacion(Variaciones x); {
         . . .
               }
    }

Sólo una instancia de una clase que implemente la interfaz puede asignarse al parámetro cuyo tipo corresponde al identificador de la interfaz. Esta facultad se puede aprovechar dentro la propia
interfaz. 

Por ejemplo:

            public interface Comparable {
                 // La instancia que llama a esMayor (this) y el parametro otra
                 // deben ser de la misma clase o de clases que implementen esta interfaz
                // La funcion devuelve 1, 0, -1 si this es mayor, igual o menor que otra

        public int esMayor(Comparable otra);
     }

En algún caso puede ser útil declarar una interfaz vacía como, por ejemplo:
public interface Marcador {
                }

Esta declaración es totalmente válida ya que no es obligatorio incluir dentro de una interfaz la declaración de una constante o la cabecera de un método. La utilidad de estas interfaces reside en la posibilidad de ser empleadas como tipos de dato para especificar clases sin necesidad de obligar a éstas a implementar algún método en concreto. Una interfaz no es una clase pero se considera un tipo en Java y puede ser utilizado como tal.

16.- Tipos de Metodos

Un método es una abstracción de una operación que puede hacer o realizarse con un objeto. Una
clase puede declarar cualquier número de métodos que lleven a cabo operaciones de lo más variado con
los objetos. En esta sección los métodos se clasifican en dos grupos: los métodos de instancia y los
métodos de clase. Además se cierra el capítulo con los métodos de clase o estáticos de la clase Math.

Métodos de instancia

Las clases pueden incluir en su declaración muchos métodos o no declarar ninguno. Los métodos pueden clasificarse en métodos de instancia y métodos de clase. 
Los métodos de instancia operan sobre las variables de instancia de los objetos pero también tienen acceso a las variables de clase. La sintaxis de llamada a un método de instancia es:

            idReferencia.idMetodo(parametros);    // Llamada tipica a un metodo de instancia

Todas las instancias de una clase comparten la misma implementación para un método de instancia. La instancia que hace la llamada al método es siempre un parámetro o argumento implícito. Dentro de un método de instancia, el identificador de una variable de instancia hace referencia al atributo de la instancia concreta que hace la llamada al método (suponiendo que el identificador del atributo no ha sido ocultado por el de un parámetro).

En el ejemplo anterior en la declaración de la clase CuentaBancaria, los métodos saldo y transferencia son métodos de instancia.

                 public double saldo() {
                    return saldo;
                   }

                 public void transferencia( CuentaBancaria origen ) {
                   saldo += origen.saldo;
                   origen.saldo=0;
                 }

Ejemplos de llamada a estos métodos dentro de PruebaCuentaBancaria:

                       CuentaBancaria c1 = new CuentaBancaria();
                       CuentaBancaria c2 = new CuentaBancaria(20.0);
                       c1.transferencia(c2);
                       System.out.println("Cuenta con: " + c1.saldo() + " euros");

Métodos de clase

En principio, los métodos de clase no operan sobre las variables de instancia de los objetos.
Los métodos de clase pueden trabajar con las variables de clase pero no pueden acceder a las variables de instancia declaradas dentro de la clase, a no ser que se crea una nueva instancia y se acceda a las variables de instancia a través del nuevo objeto. Los métodos de clase también pueden ser llamados precediendolos con el identificador de la clase, sin necesidad de utilizar el de una instancia.

            IdClase.idMetodo(parametros); // Llamada tipica a un metodo de clase

La palabra static determina la declaración de un método de clase. Por defecto, si no se indica la palabra static, el método declarado se considera un método de instancia.
En el ejemplo anterior en la declaración de la clase CuentaBancaria, el método incCuentas es un método de clase.

                 public static void incCuentas () {
                     totalCuentas++;
                   }

Un ejemplo de llamada a este método dentro de PruebaCuentaBancaria sería:

                  CuentaBancaria.incCuentas();

Las diferencias entre los métodos de instancia y los de clase se resumen en la Tabla


Los métodos de clase o estáticos se pueden considerar equivalentes a las rutinas (globales) de otros lenguajes de programación como Pascal o C. Como ejemplos típicos de métodos estáticos pueden indicarse los métodos de Java correspondientes a las funciones matemáticas sin, cos, exp, pow... de la clase java.lang.Math (Tabla 14.2). Las llamadas a estos métodos se realizan anteponiendo el identificador de la clase Math al identificador del método: Math.sin(angulo)…

miércoles, 14 de septiembre de 2011

15.- Devolución de Valores y Sobrecarga de Métodos

Devolución de Valores


Los métodos pueden devolver valores básicos (int, short, double, etc.), Strings, arrays e incluso objetos.
En todos los casos es el comando return el que realiza esta labor. En el caso de arrays y objetos, devuelve una referencia a ese array u objeto. 
Ejemplo:

     class FabricaArrays {
                public int[] obtenArray(){
                int array[]= {1,2,3,4,5};
                return array;
                   }
}

      public class returnArray {
                public static void main(String[] args) {
                FabricaArrays fab=new FabricaArrays();
                int nuevoArray[]=fab.obtenArray();
                   }
}

Sobrecarga de Métodos

Una propiedad de la POO es el polimorfismo. Java posee esa propiedad ya que admite 
sobrecargar los métodos. Esto significa crear distintas variantes del mismo método.

Ejemplo:

      class Matemáticas{
               public double suma(double x, double y) {
               return x+y;
               }

         public double suma(double x, double y, double z){
               return x+y+z;
         }

          public double suma(double[] array){
               double total =0;
               for(int i=0; i<array.length;i++){
               total+=array[i];
         }
               return total;
}



La clase matemáticas posee tres versiones del método suma. una versión que suma dos números double, otra que suma tres y la última que suma todos los miembros de un array de doubles. Desde el código se puede utilizar cualquiera de las tres versiones según convenga.

14.- Argumentos por valor y por referencia

En todos los lenguajes éste es un tema muy importante. Los argumentos son los datos
que recibe un método y que necesita para funcionar. Ejemplo:


      public class Matemáticas {
                    public double factorial(int n){
                    double resultado;
                    for (resultado=n;n>1;n--) resultado*=n;
                    return resultado;
             }
                   ...
     public static void main(String args[]){
                   Matemáticas m1= new Matemáticas();
                   double x=m1.factorial(25);//Llamada al método
             }

En el ejemplo anterior, el valor 25 es un argumento requerido por el método factorial para que éste devuelva el resultado (que será el factorial de 25). En el código del método factorial, este valor 25 es copiado a la variable n, que es la encargada de almacenar y utilizar este valor.

Se dice que los argumentos son por valor, si la función recibe una copia de esos datos, es decir la variable que se pasa como argumento no estará afectada por el código.

Ejemplo:




     class prueba {
                    public void metodo1(int entero){
                    entero=18;
                    ...
                }
                    ...

    public static void main(String args[]){
                    int x = 24;
                    prueba miPrueba = new prueba();
                    miPrueba.metodo1(x);
                    System.out.println(x);           //Escribe 24, no 18
       }


Este es un ejemplo de paso de parámetros por valor. La variable x se pasa como argumento o parámetro para el método metodo1, allí la variable entero recibe una copia del valor de x en la variable entero, y a esa copia se le asigna el valor 18. Sin embargo la variable x no está afectada por esta asignación.
Sin embargo en este otro caso:


     class prueba {
                    public void metodo1(int[] entero){
                       entero[0]=18;
                    ...
                    }
                    ...
     public static void main(String args[]){
                    int x[]={24,24};
                    prueba miPrueba = new prueba();
                    miPrueba.metodo1(x);
                    System.out.println(x[0]); //Escribe 18, no 24


Aquí sí que la variable x está afectada por la asignación entero[0]=18. La razón es porque en este caso el método no recibe el valor de esta variable, sino la referencia, es decir la dirección física de esta variable. entero no es una replica de x, es la propia x llamada de otra forma.
Los tipos básicos (int, double, char, boolean, float, short y byte) se pasan por valor. También se pasan por valor las variables String. Los objetos y arrays se pasan por referencia.

13.- Creación de clases

Definir atributos de la clase (variables, propiedades o datos de la clases)



Cuando se definen los datos de una determinada clase, se debe indicar el tipo de propiedad que es (String, int, double, int[][],...) y el especificador de acceso (public, private,...). El especificador indica en qué partes del código ese dato será visible.

Ejemplo:
                    class Persona {
                                  public String nombre;    //Se puede acceder desde cualquier clase
                                  private int contraseña;   //Sólo se puede acceder desde la clase Persona
                                  protected String dirección;    //Acceden a esta propiedad esta clase y sus 
                                                                              //descendientes

Por lo general las propiedades de una clase suelen ser privadas o protegidas, a no ser que se trate de un valor constante, en cuyo caso se declararán como públicos.
Las variables locales de una clase pueden ser inicializadas.

             class auto{
                              public nRuedas=4;

Ilustración 8, La clase persona en UML. El signo + significa public,
el signo # protected y el signo - private




Definir métodos de clase (operaciones o funciones de clase)

Un método es una llamada a una operación de un determinado objeto. Al realizar esta llamada (también se le llama enviar un mensaje), el control del programa pasa a ese método y lo mantendrá hasta que el método finalice o se haga uso de return. Para que un método pueda trabajar, normalmente hay que pasarle unos datos en forma de argumentos o parámetros, cada uno de los cuales se separa por comas.

Ejemplos de llamadas:

                                balón.botar();                 //sin argumentos
                                miCoche.acelerar(10);
                                ficha.comer(posición15);  //posición 15 es una variable que se pasa como argumento
                                partida.empezarPartida(“18:15”,colores);

Los métodos de la clase se definen dentro de ésta. Hay que indicar un modificador de acceso (public, private, protected o ninguno, al igual que ocurre con las variables y con la propia clase) y un tipo de datos, que indica qué tipo de valores devuelve el método.
Esto último se debe a que los métodos son funciones que pueden devolver un determinado valor (un entero, un texto, un valor lógico,...) mediante el comando return. Si el método no devuelve ningún valor, entonces se utiliza el tipo void que significa que no devuelve valores (en ese caso el método no tendrá instrucción return).
El último detalle a tener en cuenta es que los métodos casi siempre necesitan datos para realizar la operación, estos datos van entre paréntesis y se les llama argumentos. Al definir el método hay que indicar que argumentos se necesitan y de qué tipo son.

Ejemplo:

      public class vehiculo {
                            /** Función principal */
                                int ruedas;
                                private double velocidad=0;
                                 String nombre;
                            /** Aumenta la velocidad*/
                            public void acelerar(double cantidad) {
                               velocidad += cantidad;
                            }
                            ** Disminuye la velocidad*/
                            public void frenar(double cantidad) {
                               velocidad -= cantidad;
                            }
                            /** Devuelve la velocidad*/
                            public double obtenerVelocidad(){
                               return velocidad;
                            }
                            public static void main(String args[]){
                                  vehiculo miCoche = new vehiculo();
                                  miCoche.acelerar(12);
                                  miCoche.frenar(5);
                                  System.out.println(miCoche.obtenerVelocidad());
                            } // Da 7.0


En la clase anterior, los métodos acelerar y frenar son de tipo void por eso no tienen sentencia return. Sin embargo el método obtenerVelocidad es de tipo double por lo que su resultado es devuelto por la sentencia return y puede ser escrito en pantalla.

Ilustración 9, Versión UML de la clase
Coche







12 .- Especificadores de Acceso

Modificadores (public, protected y private)

Se trata de una palabra que antecede a la declaración de una clase, método o propiedad de clase. Hay tres posibilidades: public, protected y private. Una cuarta posibilidad es no utilizar ninguna de estas tres palabras; entonces se dice que se ha utilizado el modificador por defecto (friendly).
Los especificadores determinan el alcance de la visibilidad del elemento al que se refieren. Referidos por ejemplo a un método, pueden hacer que el método sea visible sólo para la clase que lo utiliza (private), para éstas y las heredadas (protected), para todas las clases del mismo paquete (friendly) o para cualquier clase del tipo que sea (public).

En la siguiente tabla se puede observar la visibilidad de cada especificador:




Modificadores static y final.

Un atributo de una clase se puede modificar con la palabra reservada static, con ello indicamos que el atributo no pertenece a las instancias de la clase si no a la propia clase. Que quiere decir esto?, pues que no existe una copia de ese atributo en cada uno de los objetos de la clase, si no que existe una unica copia que es compartida por todos los objetos de la clase. Por ello, a los atributos
static se les llama atributos de la clase. Una consecuencia de lo anterior es que para acceder a los atributos static de una clase no necesitamos crear una instancia de la clase, podemos acceder a
ellos a través del nombre de la clase.

De igual modo, podemos modificar los métodos de una clase con la palabra reserva static. A estos métodos se les llaman métodos de la clase, y, al igual que con los atributos static, podemos usarlos a través del nombre de la clase, sin necesidad de crear ninguna instancia de la clase. Pero existe una restricción, los métodos estáticos de una clase solo pueden acceder a atributos estáticos u otros
métodos estáticos de la clase, pero nunca a atributos o métodos que no lo sean. Ves porque? Que ocurrir a si desde un método estático y usando el nombre de la clase intentases acceder a un atributo de instancia de la clase? 
En el siguiente ejemplo de código hemos añadido un contador para saber el numero de instancias de la clase Persona que se han creado:

 package tipos ;

 public class Persona implements Contacto {
             private String nombre ;
         private String apellidos ;
         private String telefono ;
         private static nInstancias ;

 publ ic Persona ( ) {
         super ( ) ;
         iniciaAtributos( ) ;
 }

 public static int getNInstancias( ) {
         return  nInstancias ;
 }

Fjate que el metodo getNInstancias() que accede al atributo nInstancias es estático. En el siguiente ejemplo de código se est a utilizando este método estático a través del nombre de la clase y a través de una instancia concreta:



 publ ic f inal class Principal {
       private Pr i n c i p a l ( ) {
       super ( ) ;
 }

  private void e j e cu t a ( ) {
    Persona unaPersona = new Persona ( ) ;

 // Accedemos al método a través de la clase

 System.out.p r i n t l n ( " Numero de Personas Creadas : " + Persona.getNInstancias ( ) ) ;

 Persona otraPersona = new Persona ( " James " , " Bond " , " 555 123 456 " ) ;

 // Accedemos al método a través de una instancia concreta

Cuando un atributo de una clase los modificamos en su definición con la palabra reservada final, estamos indicando que ese atributo no puede cambiar de valor, por ejemplo:

 private f inal String autor = " Oscar " ;

Una vez definido, este atributo no puede cambiar de valor, si lo intentásemos cambiar el compilador nos dará un error.
Muchas veces los modificadores static y final se utilizan en combinación para definir constantes, como en el siguiente ejemplo:

 publ ic clas s Constantes {
 public static final double PI = 3 . 1 4 1 5 9 2 ;
 . . .
 g

De este modo, la constante es accesible desde cualquier otra clase (al ser public) y podemos leerla a través del nombre de la clase de este modo Constantes.PI, pero si por descuido intentamos modificarla, el compilador de Java nos dar a un error.




11.- Objetos

Objetos 

Se les llama instancias de clase. Son un elemento en sí de la clase (en el ejemplo del parchís, una ficha en concreto). Un objeto se crea utilizando el llamado constructor de la clase. El constructor es el método que permite iniciar el objeto.

Datos miembro (propiedades o atributos)

Para poder acceder a los atributos de un objeto, se utiliza esta sintaxis:

                           objeto.atributo
Por ejemplo:
                           Noria.radio;

Métodos

Los métodos se utilizan de la misma forma que los atributos, excepto porque los métodos poseen siempre paréntesis, dentro de los cuales pueden ir valores necesarios para la ejecución del método (parámetros):

                         objeto.método(argumentosDelMétodo)

Los métodos siempre tienen paréntesis (es la diferencia con las propiedades) y dentro de los paréntesis se colocan los argumentos del método. Que son los datos que necesita el método para funcionar. 
Por ejemplo:
                         MiNoria.gira(5);

Lo cual podría hacer que la Noria avance a 5 Km/h.

Herencia

En la POO tiene mucha importancia este concepto, la herencia es el mecanismo que permite crear clases basadas en otras existentes. Se dice que esas clases descienden de las primeras. Así por ejemplo, se podría crear una clase llamada vehículo cuyos métodos serían mover, parar, acelerar y frenar. Y después se podría crear una clase coche basada en la anterior que tendría esos mismos métodos (les heredaría) y además
añadiría algunos propios, por ejemplo abrirCapó o cambiarRueda.

Creación de objetos de la clase

Una vez definida la clase, se pueden utilizar objetos de la clase. Normalmente consta de dos pasos. Su declaración, y su creación. La declaración consiste en indicar que se va a utilizar un objeto de una clase determinada. Y se hace igual que cuando se declara una variable simple. 
Por ejemplo:
                            Noria noriaDePalencia;

Eso declara el objeto noriaDePalencia como objeto de tipo Noria; se supone que previamente se ha definido la clase Noria.

Para poder utilizar un objeto, hay que crearle de verdad. Eso consiste en utilizar el operador new.

 Por ejemplo:
                         noriaDePalencia = new Noria();

Al hacer esta operación el objeto reserva la memoria que necesita y se inicializa el objeto mediante su constructor. Más adelante veremos como definir el constructor.


lunes, 12 de septiembre de 2011

10.- Programación Orientada a Objetos

Se ha comentado anteriormente en este manual que Java es un lenguaje totalmente orientado a objetos. De hecho siempre hemos definido una clase pública con un método main que permite que se pueda visualizar en la pantalla el programa Java.
La gracia de la POO es que se hace que los problemas sean más sencillos, al permitir dividir el problema. Está división se hace en objetos, de forma que cada objeto funcione de forma totalmente independiente. Un objeto es un elemento del programa que posee sus propios datos y su propio funcionamiento.
Es decir un objeto está formado por datos (propiedades) y funciones que es capaz de realizar el objeto (métodos).
Antes de poder utilizar un objeto, se debe definir su clase. La clase es la definición de un tipo de objeto. Al definir una clase lo que se hace es indicar como funciona un determinado tipo de objetos. Luego, a partir de la clase, podremos crear objetos de esa clase.
Por ejemplo, si quisiéramos crear el juego del parchís en Java, una clase sería la casilla, otra las fichas, otra el dado, etc., etc. En el caso de la casilla, se definiría la clase para indicar su funcionamiento y sus propiedades, y luego se crearía tantos objetos casilla como casillas tenga el juego.
Lo mismo ocurriría con las fichas, la clase ficha definiría las propiedades de la ficha (color y posición por ejemplo) y su funcionamiento mediante sus métodos (por ejemplo un método sería mover, otro llegar a la meta, etc., etc., ), luego se crearían tantos objetos ficha, como fichas tenga el juego.

Propiedades de la POO

  • Encapsulamiento. El  mecanismo  de  herencia  permite  definir  nuevas  clases  partiendo  de  otras  ya existentes.  Las  clases  que  derivan  de  otras  heredan  automáticamente  todo  su
    comportamiento, pero además pueden introducir características particulares propias que
    las diferencian.
  • Ocultación. Hay una zona oculta al definir la clases (zona privada) que sólo es utilizada por esa clases y por alguna clase relacionada. Hay una zona pública (llamada también interfaz de la clase) que puede ser utilizada por cualquier parte del código.
  • Polimorfismo. Cada método de una clase puede tener varias definiciones distintas. En el caso del parchís: partida.empezar(4) empieza una partida para cuatro jugadores, partida.empezar(rojo, azul) empieza una partida de dos jugadores para los colores rojo y azul; estas son dos formas distintas de emplear el método empezar, que es polimórfico.
  • Herencia. Una clase puede heredar propiedades de otra.
Introducción al Concepto de Objeto

Un objeto es cualquier entidad representable en un programa informático, bien sea real (ordenador) o bien sea un concepto (transferencia). Un objeto en un sistema posee: una identidad, un estado y un comportamiento.
El estado marca las condiciones de existencia del objeto dentro del programa. Lógicamente este estado puede cambiar. Un coche puede estar parado, en marcha, estropeado, funcionando, sin gasolina, etc.
El comportamiento determina como responde el objeto ante peticiones de otros objetos. Por ejemplo un objeto conductor puede lanzar el mensaje arrancar a un coche.  El comportamiento determina qué es lo que hará el objeto. 
La identidad determina que cada objeto es único aunque tengan el mismo valor.
No existen dos objetos iguales. Lo que sí existe es dos referencias al mismo objeto.
Los objetos se manejan por referencias, existirá una referencia a un objeto. De modo que esa referencia permitirá cambiar los atributos del objeto. Incluso puede haber varias referencias al mismo objeto, de modo que si una referencia cambia el estado del objeto, el resto (lógicamente) mostrarán esos cambios.
Los objetos por valor son los que no usan referencias y usan copias de valores concretos. En Java estos objetos son los tipos simples: int, char, byte, short, long, float, double y boolean. El resto son todos objetos (incluidos los arrays y Strings).

Clases

Las clases son las plantillas para hacer objetos. Una clase sirve para definir una serie de objetos con propiedades (atributos), comportamientos (operaciones o métodos), y semántica comunes. Hay que pensar en una clase como un molde. A través de las clases se obtienen los objetos en sí.
Es decir antes de poder utilizar un objeto se debe definir la clase a la que pertenece, esa definición incluye:
  • Sus atributos. Es decir, los datos miembros de esa clase. Los datos pueden ser públicos (accesibles desde otra clase) o privados (sólo accesibles por código de su propia clase. También se las llama campos.
  • Sus métodos. Las funciones miembro de la clase. Son las acciones (u operaciones) que puede realizar la clase.
  • Código de inicialización. Para crear una clase normalmente hace falta realizar operaciones previas (es lo que se conoce como el constructor de la clase).
  • Otras clases. Dentro de una clase se pueden definir otras clases (clases internas, son consideradas como asociaciones dentro de UML).

El formato general para crear una clase en Java es:

                  [acceso] class nombreDeClase {
                   [acceso] [static] tipo atributo1;
                   [acceso] [static] tipo atributo2;
                   [acceso] [static] tipo atributo3;
                   ...
                   [access] [static] tipo método1(listaDeArgumentos) {
                   ...código del método...
                        }
                       ...
                   }

La palabra opcional static sirve para hacer que el método o la propiedad a la que precede se pueda utilizar de manera genérica (más adelante se hablará de clases genéricas), los métodos o propiedades así definidos se llaman atributos de clase y métodos de clase respectivamente. Su uso se verá más adelante. 
Ejemplo;


                   class Noria {
                        double radio;
                        void girar(int velocidad){
                       ...//definición del método
                     }
                        void parar(){...
                   }




Objetos

Se les llama instancias de clase. Son un elemento en sí de la clase (en el ejemplo del parchís, una ficha en concreto). Un objeto se crea utilizando el llamado constructor de la clase. El constructor es el método que permite iniciar el objeto. 

datos miembro (propiedades o atributos)

Para poder acceder a los atributos de un objeto, se utiliza esta sintaxis: 
          objeto.atributo

Por ejemplo:
          Noria.radio;

Métodos

Los métodos se utilizan de la misma forma que los atributos, excepto porque los métodos poseen siempre paréntesis, dentro de los cuales pueden ir valore snecesarios para la ejecución del método (parámetros):

              objeto.método(argumentosDelMétodo)

Los métodos siempre tienen paréntesis (es la diferencia con las propiedades) y dentro de los paréntesis se colocan los argumentos del método. Que son los datos que necesita el método para funcionar.
 Por ejemplo:

              MiNoria.gira(5);

Lo cual podría hacer que la Noria avance a 5 Km/h.

Herencia

En la POO tiene mucha importancia este concepto, la herencia es el mecanismo que permite crear clases basadas en otras existentes. Se dice que esas clases descienden de las primeras. Así por ejemplo, se podría crear una clase llamada vehículo cuyos métodos serían mover, parar, acelerar y frenar. Y después se podría crear una clase coche basada en la anterior que tendría esos mismos métodos (les heredaría) y además
añadiría algunos propios, por ejemplo abrirCapó o cambiarRueda.

Creación de Objetos de la Clase

Una vez definida la clase, se pueden utilizar objetos de la clase. Normalmente consta de dos pasos. Su declaración, y su creación. La declaración consiste en indicar que se va a utilizar un objeto de una clase determinada. Y se hace igual que cuando se declara una 
variable simple. Por ejemplo:

                     Noria noriaDePalencia;

Eso declara el objeto noriaDePalencia como objeto de tipo Noria; se supone que previamente se ha definido la clase Noria.


Para poder utilizar un objeto, hay que crearle de verdad. Eso consiste en utilizar el operador new. Por ejemplo:
                    noriaDePalencia = new Noria();

Al hacer esta operación el objeto reserva la memoria que necesita y se inicializa el objeto mediante su constructor. Más adelante veremos como definir el constructor.




miércoles, 7 de septiembre de 2011

9.- Clase String

Introducción


Para Java las cadenas de texto son objetos especiales. Los textos deben manejarse creando objetos de tipo String.
Ejemplo:

                          String texto1 = “¡Prueba de texto!”;

Las cadenas pueden ocupar varias líneas utilizando el operador de concatenación “+”.

                         String texto2 =”Este es un texto que ocupa “ “varias líneas, no obstante se puede “+
                         “perfectamente encadenar”;
                      
También se pueden crear objetos String sin utilizar constantes entrecomilladas, usando otros constructores:

                         char[] palabra = {‘P’,’a’,’l’,’b’,’r’,’a’};//Array de char
                         String cadena = new String(palabra);
                         byte[] datos = {97,98,99};
                         String codificada = new String (datos, “8859_1”);

En el último ejemplo la cadena codificada se crea desde un array de tipo byte que contiene números que serán interpretados como códigos Unicode. Al asignar, el valor 8859_1 indica la tabla de códigos a utilizar.
comparación entre objetos String.

Los objetos String no pueden compararse directamente con los operadores de comparación. En su lugar se deben utilizar estas expresiones:

   cadena1.equals(cadena2). El resultado es true si la cadena1 es igual a la cadena2. Ambas cadenas son variables de tipo String.

   cadena1.equalsIgnoreCase(cadena2). Como la anterior, pero en este caso no se
tienen en cuenta mayúsculas y minúsculas.

   s1.compareTo(s2). Compara ambas cadenas, considerando el orden alfabético.
Si la primera cadena es mayor en orden alfabético que la segunda devuelve 1, si son iguales devuelve 0 y si es la segunda la mayor devuelve -1. Hay que tener en cuenta que el orden no es el del alfabeto español, sino que usa la tabla ASCII, en esa tabla la letra ñ es mucho mayor que la o.

    s1.compareToIgnoreCase(s2). Igual que la anterior, sólo que además ignora
las mayúsculas (disponible desde Java 1.2)


String.valueOf


Este método pertenece no sólo a la clase String, sino a otras y siempre es un método que convierte valores de una clase a otra. En el caso de los objetos String, permite convertir valores que no son de cadena a forma de cadena.
Ejemplos:
                        String numero = String.valueOf(1234);
                        String fecha = String.valueOf(new Date());

En el ejemplo se observa que este método pertenece a la clase String directamente, no hay que utilizar el nombre del objeto creado (como se verá más adelante, es un método estático).

Métodos de las variables de las cadenas.

Son métodos que poseen las propias variables de cadena. Para utilizarlos basta con
poner el nombre del método y sus parámetros después del nombre de la variable String.

Es decir: variableString.método(argumentos)

length


Permite devolver la longitud de una cadena (el número de caracteres de la cadena):

                        String texto1=”Prueba”;
                        System.out.println(texto1.length());//Escribe 6

Concatenar cadenas

Se puede hacer de dos formas, utilizando el método concat o con el operador +.
Ejemplo:

                        String s1=”Buenos ”, s2=”días”, s3, s4;
                        s3 = s1 + s2;
                        s4 = s1.concat(s2);

charAt

Devuelve un carácter de la cadena. El carácter a devolver se indica por su posición (el primer carácter es la posición 0) Si la posición es negativa o sobrepasa el tamaño de la cadena, ocurre un error de ejecución, una excepción tipo IndexOutOfBounds- Exception.
Ejemplo:
                        String s1=”Prueba”;
                        char c1=s1.charAt(2); //c1 valdrá ‘u’

substring

Da como resultado una porción del texto de la cadena. La porción se toma desde una posición inicial hasta una posición final (sin incluir esa posición final). Si las posiciones indicadas no son válidas ocurre una excepción de tipo IndexOutOfBounds- Exception. Se empieza a contar desde la posición 0.
Ejemplo:
                       String s1=”Buenos días”;
                       String s2=s1.substring(7,10); //s2 = día


indexOf


Devuelve la primera posición en la que aparece un determinado texto en la cadena. En el caso de que la cadena buscada no se encuentre, devuelve -1. El texto a buscar puede ser char o String.
Ejemplo:

                       String s1=”Quería decirte que quiero que te vayas”;
                       System.out.println(s1.indexOf(“que”)); //Da 15

Se puede buscar desde una determinada posición. En el ejemplo anterior:

                       System.out.println(s1.indexOf(“que”,16)); //Ahora da 26

lastIndexOf

Devuelve la última posición en la que aparece un determinado texto en la cadena. Es casi idéntica a la  anterior, sólo que busca desde el final.
Ejemplo:

                       String s1=”Quería decirte que quiero que te vayas”;
                       System.out.println(s1.lastIndexOf(“que”); //Da 26

También permite comenzar a buscar desde una determinada posición.

endsWith


Devuelve true si la cadena termina con un determinado texto.
Ejemplo:
                       String s1=”Quería decirte que quiero que te vayas”;
                       System.out.println(s1.endsWith(“vayas”);     //Da true
startsWith


Devuelve true si la cadena empieza con un determinado texto.

replace


Cambia todas las apariciones de un carácter por otro en el texto que se indique y lo
almacena como resultado. El texto original no se cambia, por lo que hay que asignar el
resultado de replace a un String para almacenar el texto cambiado:

                       String s1=”Mariposa”;
                       System.out.println(s1.replace(‘a’,’e’));    //Da Meripose
                       System.out.println(s1);     //Sigue valiendo Mariposa

replaceAll

Modifica en un texto cada entrada de una cadena por otra y devuelve el resultado. El
primer parámetro es el texto que se busca (que puede ser una expresión regular), el
segundo parámetro es el texto con el que se reemplaza el buscado. La cadena original no
se modifica.


                       String s1=”Cazar armadillos”;
                       System.out.println(s1.replace(“ar”,”er”));     //Da Cazer ermedillos
                       System.out.println(s1);    //Sigue valiendo Cazar armadilos

8.- Arrays y Cadenas

Unidimensionales


Un array es una colección de valores de un mismo tipo engrosados en la misma variable.
De forma que se puede acceder a cada valor independientemente. Para Java además un array es un objeto que tiene propiedades que se pueden manipular. 

Los arrays solucionan problemas concernientes al manejo de muchas variables que se refieren a datos similares. Por ejemplo si tuviéramos la necesidad de almacenar las notas de una clase con 18 alumnos, necesitaríamos 18 variables, con la tremenda lentitud de manejo que supone eso. Solamente calcular la nota media requeriría una tremenda línea de código.
Almacenar las notas supondría al menos 18 líneas de código.
Gracias a los arrays se puede crear un conjunto de variables con el mismo nombre. La diferencia será que un número (índice del array) distinguirá a cada variable. En el caso de las notas, se puede crear un array llamado notas, que representa a todas las notas de la clase. Para poner la nota del primer alumno se usaría notas[0], el
segundo sería notas[1], etc. (los corchetes permiten especificar el índice en concreto del
array).

La declaración de un array unidimensional se hace con esta sintaxis.

           tipo nombre[];

Ejemplo:
                  double cuentas[]; //Declara un array que almacenará valores
                                             // doubles



Declara un array de tipo double. Esta declaración indica para qué servirá el array, pero no reserva espacio en la RAM al no saberse todavía el tamaño del mismo. Tras la declaración del array, se tiene que iniciar. Eso lo realiza el operador new, que es el que realmente crea el array indicando un tamaño. Cuando se usa new es cuando se reserva el espacio necesario en memoria. Un array no inicializado es un array null. 

Ejemplo:

           

                    int notas[];                           //sería válido también int[] notas;
                    notas = new int[3];             //indica que el array constará de tres valores de tipo int
                                                           //También se puede hacer todo a la vez  int notas[]=new int[3];



En el ejemplo anterior se crea un array de tres enteros (con los tipos básicos se crea en memoria el array y se inicializan los valores, los números se inician a 0).


Los valores del array se asignan utilizando el índice del mismo entre corchetes:

                         notas[2]=8;

También se pueden asignar valores al array en la propia declaración:

                    int notas[] = {8, 7, 9};
                    int notas2[]= new int[] {8,7,9};//Equivalente a la anterior

Esto declara e inicializa un array de tres elementos. En el ejemplo lo que significa es que

                    notas[0] vale 8, notas[1] vale 7 y notas[2] vale 9.

En Java (como en otros lenguajes) el primer elemento de un array es el cero. El primer elemento del array notas, es notas[0]. Se pueden declarar arrays a cualquier tipo de datos (enteros, booleanos, doubles, ... e incluso objetos).

La ventaja de usar arrays (volviendo al caso de las notas) es que gracias a un simple
bucle for se puede rellenar o leer fácilmente todos los elementos de un array:

       //Calcular la media de las 18 notas
                    suma=0;
                    for (int i=0;i<=17;i++){
                    suma+=nota[i];
                      }
                    media=suma/18;

A un array se le puede inicializar las veces que haga falta:

                    int notas[] = new notas[16];
...
                    notas = new notas[25];

Pero hay que tener en cuenta que el segundo new hace que se pierda el contenido anterior. Realmente un array es una referencia a valores que se almacenan en memoria mediante el operador new, si el operador new se utiliza en la misma referencia, el anterior contenido se queda sin referencia y, por lo tanto se pierde.

Un array se puede asignar a otro array (si son del mismo tipo):

                           int notas[];
                           int ejemplo[]=new int[18];
                           notas=ejemplo;

En el último punto, notas equivale a ejemplo. Esta asignación provoca que cualquier cambio en notas también cambie el array ejemplos. Es decir esta asignación anterior, no copia los valores del array, sino que notas y ejemplo son referencias al mismo array.

Ejemplo:
                           int notas[]={3,3,3};
                           int ejemplo[]=notas;
                           ejemplo= notas;


Arrays Multidimensionales

Los arrays además pueden tener varias dimensiones. Entonces se habla de arrays de arrays (arrays que contienen arrays) 
Ejemplo:
               int notas[][];

notas es un array que contiene arrays de enteros

              notas = new int[3][12];         //notas está compuesto por 3 arrays
                                                          //de 12 enteros cada uno
              notas[0][0]=9;//el primer valor es 0

Puede haber más dimensiones incluso (notas[3][2][7]). Los arrays multidimensionales se pueden inicializar de forma más creativa incluso.
 Ejemplo:
                    int notas[][] = new int[5][];    //Hay 5 arrays de enteros
                    notas[0] = new int[100];     //El primer array es de 100 enteros
                    notas[1] = new int[230];     //El segundo de 230
                    notas[2] = new int[400];
                    notas[3] = new int[100];
                    notas[4] = new int[200];

Hay que tener en cuenta que en el ejemplo anterior, notas[0] es un array de 100 enteros. Mientras que notas, es un array de 5 arrays de enteros. Se pueden utilizar más de dos dimensiones si es necesario.

Longitud de un array

Los arrays poseen un método que permite determinar cuánto mide un array. Se trata de
length. Ejemplo (continuando del anterior):

             System.out.println(notas.length);      //Sale 5
             System.out.println(notas[2].length);      //Sale 400

La Clase Arrays

En el paquete java.utils se encuentra una clase estática llamada Arrays. Una clase estática permite ser utilizada como si fuera un objeto (como ocurre con Math). Esta clase posee métodos muy interesantes para utilizar sobre arrays.

Su uso es

Arrays.método(argumentos);


fill

Permite rellenar todo un array unidimensional con un determinado valor. Sus argumentos son el array a rellenar y el valor deseado:

                  int valores[]=new int[23];
                  Arrays.fill(valores,-1);//Todo el array vale -1

También permite decidir desde que índice hasta qué índice rellenamos:
Arrays.fill(valores,5,8,-1);//Del elemento 5 al 7 valdrán -1


equals

Compara dos arrays y devuelve true si son iguales. Se consideran iguales si son del
mismo tipo, tamaño y contienen los mismos valores.

sort

Permite ordenar un array en orden ascendente. Se pueden ordenar sólo una serie de
elementos desde un determinado punto hasta un determinado punto.

                   int x[]={4,5,2,3,7,8,2,3,9,5};
                  Arrays.sort(x);//Estará ordenado
                  Arrays.sort(x,2,5);//Ordena del 2º al 4º elemento

binarySearch

Permite buscar un elemento de forma ultrarrápida en un array ordenado (en un array
desordenado sus resultados son impredecibles). Devuelve el índice en el que está
colocado el elemento.
Ejemplo:
                    int x[]={1,2,3,4,5,6,7,8,9,10,11,12};
                    Arrays.sort(x);
                    System.out.println(Arrays.binarySearch(x,8));       //Da 7


toUpperCase

Devuelve la versión en mayúsculas de la cadena.

toLowerCase

Devuelve la versión en minúsculas de la cadena.

toCharArray

Obtiene un array de caracteres a partir de una cadena.