Práctica 4: Herencia Objetivos: Como se implementa la herencia en Java (superclase - clase descendiente). Utilizar super. Redefinición de métodos Clase Object Clase System Clases abstractas 1. Herencia. Cuando una clase hereda de otra, la primera (descendiente) automáticamente hereda las características (variables miembro) y el comportamiento (métodos) de la segunda (superclase). Debe tenerse presente que la herencia es siempre aditiva, es decir, una clase no puede heredar de otra y obtener menos de lo que esta última (la superclase) tiene. En Java, la herencia se gestiona mediante la palabra clave extends. Cuando una clase hereda de otra, se considera que la clase descendiente se deriva de la superclase. Ejemplo: public class Mamifero{ String nombre, colorojos; private int edad; public Mamifero(String n, String c, int e) { nombre = n; colordeojos = c; edad = e; Obsérvese que Mamifero presenta las características comunes de los mamíferos. public class Perro extends Mamifero{ String raza; public Perro(String n, String c, int e, String r) { // super(n,c,e) nombre = n; colorojos = c; edad = e; raza=r; public class Hombre extends Mamifero{ boolean casado; public Hombre(String n, String c, int e, boolean c) { // super(n,c,e) nombre = n; Página 1 de 7
colorojos = c; edad = e; casado = c; Una vez que Perro se deriva de Mamifero, dispone de las variables miembro y los métodos de esta última. De hecho, incluso Mamifero se obtiene por herencia de otra clase. Todas las clases de Java se derivan, en último término, de la clase Object; por tanto, si se declara una clase que no es una derivación de otra, implícitamente se deriva de la clase Object. 2. Utilización de super Hay ocasiones en las que se desea crear una superclase que mantenga los detalles de su implementación para sí, es decir, que mantenga privados sus datos miembros. En ese caso no habría manera de que una subclase pudiese acceder directamente o inicializar estas variables en su declaración. Java proporciona una solución a este problema. Cuando una subclase necesita referirse a su superclase inmediata, lo puede hacer utilizando la palabra super. Super se puede utilizar de dos formas: La primera para llamar al constructor de la superclase. La segunda para acceder a un miembro de la superclase que ha sido ocultado por un miembro de la subclase. Dado que, a menudo, interesará llamar explícitamente al constructor de la superclase, Java cuenta con una palabra clave que facilita esa operación. super (lista de parámetros)llama al constructor de la superclase al que se le hayan proporcionado los parámetros adecuados. Si se utiliza super, tiene que ser la primera sentencia ejecutada dentro del constructor de la subclase. Ejemplo: class A{ int i; A(int a){ i=a; class B extends A{ int j; B(int a, int b){ super(a); j=b; La segunda forma de utilizar super es según el formato siguiente: super.miembro Esta segunda forma de super se utiliza cuando los nombres de miembro de la subclase ocultan los miembros que tienen el mismo nombre en la superclase. Ejemplo: class A{ int i; class B extends A{ int i; // esta i oculta la i de A Página 2 de 7
B(int a, int b){ super.i=a; //i de A i=b; // i de B 3. Redefinición de métodos En una jerarquía de clases, cuando un método de una subclase tiene el mismo nombre y tipo que un método de su superclase, entonces se dice que el método de la subclase redefine o sobrescribe al método de la superclase. Cuando se llama a un método redefinido dentro de una subclase, siempre se refiere a la versión del método definida por la subclase. La versión del método definida por la superclase está oculta. 4. Clase Object Object es la superclase de las restantes clases de Java. Esto significa simplemente que todas las clases de Java se derivan de Object. Esta clase contiene varios métodos importantes; entre ellos, clone, equals y tostring. Cuando un objeto emplea el método clone, simplemente obtiene una copia de sí mismo. Para conseguirlo, se asigna memoria para la copia clónica y posteriormente se copia el contenido del objeto original al objeto clonado. Por ejemplo, supongamos que se desea obtener una copia de la clase Document que contenga las propiedades text (texto) y author(autor). Para crear una instancia de la clase Document que contenga ambas propiedades y los valores asociados al objeto, es necesario emplear el método clone. El código siguiente muestra cómo se llevaría a cabo esta operación. Document document1 = new Document ("doctext.txt", "Juan García") Document document2 = document1.clone(); El método equals compara las propiedades de dos objetos del mismo tipo para determinar si los objetos son iguales. El valor booleano devuelto depende del objeto que ha llamado al método y del objeto que se le ha pasado como parámetro. Por ejemplo, si un objeto llama a equals y le pasa como parámetro un objeto completamente idéntico a él, equals devuelve true (verdadero). El método tostring devuelve una cadena que representa el valor del objeto. Para que este método devuelva información correcta sobre los distintos tipos de objetos, debe redefinirse en la clase de cada objeto. Ejemplo. Partiendo de la clase Punto (Practica 3), definir una nueva clase Píxel, como un punto al que se le ha añadido color. La clase tiene: Un constructor que recibe como parámetro los valores iniciales de las coordenadas x e y del punto y su color. Un método que devuelve el color de un píxel Redefine el método tostring. Página 3 de 7
public class Pixel extends Punto{ private String color; public Pixel(double xinicial, double yinicial, String colorinicial){ super(xinicial, yinicial); color=colorinicial; public String getcolor(){ return color; public String tostring(){ String s=super.tostring(); return s+" "+"color="+color; 5. Clase System La clase System permite acceder a los recursos del sistema independientes de la plataforma. Se declara como final, por lo que no es posible obtener subclases de ella. También declara sus métodos y variables como static (estático). De esta forma se consigue que esté disponible sin instanciarla. Los métodos de la clase System admiten varias aplicaciones. Una característica importante es la posibilidad de obtener la hora actual del sistema mediante el método currenttimemillis. También pueden recuperarse y modificarse recursos del sistema mediante los métodos Get y Set Properties. El aspecto más útil de la clase System son las variables que declara. Estas variables se emplean para interactuar con el sistema. Entre ellas, se encuentran in, out, err. La variable in representa el flujo de entrada estándar del sistema, mientras que out representa el flujo de salida estándar. err es el flujo de errores estándar. Los flujos se tratan en la siguiente sección dedicada al paquete de E/S. 5.1 Paquete I/O (E/S) El paquete java.io permite leer y escribir datos en distintos dispositivos. Las clases contenidas en él pueden dividirse en : clases InputStream (flujo de entrada), clases OutputStream (flujo de salida), clases File (archivo) y clase StreamTokenizer. En esta asignatura tan solo se hace referencia a los flujos de entrada y salida. 5.2 Clases InputStream (flujo de entrada) Los flujos de entrada se emplean para leer datos de fuentes de entrada (por ejemplo, un archivo, una cadena, la memoria, etcétera). Esta definición comprende varias clases; entre ellas: InputStream, BufferedInputStream, DataInputStream, FileInputStream y StringBufferInputStream. El método básico para leer datos mediante una clase de flujo de entrada es siempre el mismo: (1) crear una instancia de una clase de flujo de entrada y, seguidamente, (2) indicarle dónde debe leer los datos. Estas clases leen flujos continuos de bytes. Si en un momento determinado no existen datos disponibles, la clase de flujo de entrada se bloquea (espera hasta que existan datos disponibles). Además de las clases InputStream, el paquete I/O incluye las correspondientes clases Reader (lectoras) para todas ellas (excepto para DataInputStream). Son las siguientes: Reader, BufferedReader, FileReader y StringReader. Las clases lectoras son idénticas a las de flujo de entrada, con la diferencia de que leen caracteres Unicode en lugar de bytes. Página 4 de 7
InputStream es una clase abstracta de la cual se derivan las restantes clases de flujo de entrada. Proporciona la interfaz básica para la lectura de flujos de bytes. En la siguiente tabla figuran algunos de los métodos de InputStream y los parámetros que aceptan. Todos estos métodos devuelven valores de tipo int, con la excepción de close. Método Acepta read () read (byte b[]) read (byte b[], int off, int len) available () skip (long) close () El primer método, abstract int read, lee un byte del flujo de entrada y lo devuelve como entero (puede convertirse el tipo devuelto a char). Cuando se llega al final del flujo, devuelve -1. El segundo método, int read(byte b[]), lee varios bytes y los almacena en la matriz que emplea como parámetro. Devuelve el número de bytes leídos o -1 cuando se llega al final del flujo. El último método read (lectura), int read(byte b[], int off, int len), permite a los desarrolladores establecer el número máximo de bytes que deben leerse y dirigirlos al lugar de la matriz donde deben almacenarse. El método int available devuelve el número de bytes de entrada que pueden leerse sin que se produzca un bloqueo. El método skip excluye del flujo el número de bytes que se especifique. Por último, el método close se emplea para crear el flujo de entrada. Normalmente, este método se llama automáticamente, si bien resulta más seguro hacerlo de forma manual. En la siguiente tabla figuran algunos de los métodos de InputStreamReader y los parámetros que aceptan. void close() void mark(int readaheadlimit) boolean marksupported() int read() int read(char[] cbuf, int off, int len) String readline() boolean ready() void reset() long skip(long n) En el siguiente ejemplo se muestra cómo solicitar un dato por teclado. public class Entrada{ /* Se crea una instancia de la clase de flujo de entrada (BufferReader), la instancia se denomina leer. Y se indica donde se deben leer los datos, en este caso representa la entrada estándar (System.in) */ Página 5 de 7
static BufferedReader leer = new BufferedReader (new InputStreamReader(System.in)); public static String pidecadena (String mensaje) { // Se visualiza el contenido de la cadena mensaje en pantalla System.out.print (mensaje); // El método readline de la clase InputStreamReader, lee cadenas. Return leer.readline(); System.out es un miembro estático de System y representa al dispositivo de salida estándar. Con objeto de enviar la salida al dispositivo estándar, se ha llamado al método print. El objeto System.out es del tipo PrintStream, que se examina en el apartado dedicado a las clases de flujo de salida. 5.3 Clases OutputStream (flujo de salida) Las clases de flujo de salida son complementarias de las clases de flujo de entrada. Sirven para dirigir flujos de datos a los distintos dispositivos de salida. Las principales clases de este tipo que existen en Java son: OutputStream, PrintStream, BufferedOutputStream, DataOutputStream y FileOutputStream. Para dirigir un flujo de datos a un dispositivo de salida, se crea un flujo de salida y se dirigen los datos a una fuente de salida concreta. Como cabría esperar, también existen clases writer (escritoras). Existe una clase escritora correspondiente para todas las clases de flujo de salida, con excepción de DataOutputStream. Dado que la clase OutputStream es la complementaria de InputStream, define los siguientes métodos. Método Acepta write (int) write (byte b[]) write (byte b[], int off, int len) flush () close () El método flush sirve para vaciar el flujo de salida (es decir, para que los datos almacenados en búferes se dirijan al dispositivo de salida). La clase PrintStream está diseñada fundamentalmente para dirigir los datos como texto al dispositivo de salida. Dispone de dos constructores: PrintStream(OutputStream) y PrintStream(OutputStream, boolean autoflush). Existe una diferencia entre ellos. Con el primero, el objeto PrintStream vacía los datos en búfer cuando se cumplen las condiciones especificadas; con el segundo, los datos se vacían cuando se encuentra un carácter de salto de línea (if autoflush es true). Estos son algunos de los métodos definidos por PrintStream. Método Acepta checkerror () Página 6 de 7
print (Object obj) print (Strings) println () println (Object obj) Los métodos print y println se sobrecargan para distintos tipos de datos. El método checkerror vacía el flujo y devuelve false si se detecta un error. 6. Clases Abstractas Es posible declarar abstracto un método de una clase, lo que significa que el método carece de implementación en esa clase; no obstante, las clases derivadas de ésta han de disponer de implementación. Cuando una clase contiene un método abstracto, ella también ha de declararse abstracta. Se deduce de esto que una clase que contenga uno o varios métodos abstractos (y que, por tanto, sea una clase abstracta) no puede instanciarse, es decir, no pueden crearse objetos de dichas clases directamente con el operador new. Lo que si se puede crear, es una referencia de una clase abstracta. No se pueden declarar constructores abstractos, ni métodos abstractos estáticos. A continuación, se propone el siguiente ejercicio. Se desea almacenar en un Vector diferentes figuras geométricas (Círculo y Cuadrado). 1.- Crea las clases Círculo y Cuadrado con sus correspondientes atributos y un método que calcule el área de dicha figura. 2.- Crea una clase que tenga un atributo de tipo Vector. 2.1- Crea un método que almacene un número determinado de figuras en un Vector. Almacena en las posiciones pares del vector los círculos y en las impares los cuadrados. 2.2.- Crea un método que visualice el área de las diferentes figuras almacenadas en el vector. 3.- Crea el método main que solicite por teclado el número de figuras a almacenar. Página 7 de 7