1. Introducción a los objetos



Documentos relacionados
Java Inicial (20 horas)

Modulo 1 El lenguaje Java

Curso de Java POO: Programación orientada a objetos

9. Objetos y clases Clases

En cualquier caso, tampoco es demasiado importante el significado de la "B", si es que lo tiene, lo interesante realmente es el algoritmo.

Tema 3: Herencia en C++ Programación Orientada a Objetos Curso 2008/2009 Begoña Moros Valle

Programación Orientada a Objetos con Java

Tema 6. Reutilización de código. Programación Programación - Tema 6: Reutilización de código

Introducción a la programación orientada a objetos

Centro de Capacitación en Informática

Notación UML para modelado Orientado a Objetos

ISTP CIDET COMPUTACION E INFORMATICA ARREGLOS EN JAVA

Universidad de Cantabria

CASO PRÁCTICO DISTRIBUCIÓN DE COSTES

Ejercicio 1. Desarrollar un pequeño juego para practicar mecanografía.

Correspondencias entre taxonomías XBRL y ontologías en OWL Unai Aguilera, Joseba Abaitua Universidad de Deusto, EmergiaTech

GUIA PROGRAMACIÓN ORIENTADA A OBJETOS

2.2.- Paradigmas de la POO

RESUMEN DE CONCEPTOS BASICOS DE PROGRAMACION JAVA

Capitulo V Administración de memoria

Curso de Doctorado: Tecnologías de Objetos

SISTEMAS OPERATIVOS AVANZADOS

3.2 Operaciones aritmético-lógicas en Pascal

2. Estructura de un programa en Java

LABORATORIO Nº 2 GUÍA PARA REALIZAR FORMULAS EN EXCEL

Objetivo de aprendizaje del tema

Herencia. 3.- Herencia. Declaración de una clase derivada en Delphi. Jerarquía de clases

Introducción. Ciclo de vida de los Sistemas de Información. Diseño Conceptual

POLIMORFISMO "una interfaz, múltiples métodos".

Introducción a la Programación Orientada a Objetos

GUÍA RÁPIDA DE TRABAJOS CON ARCHIVOS.

Tema: Sobrecarga de Operadores.

Operación 8 Claves para la ISO

Universidad Católica del Maule. Fundamentos de Computación Especificación de tipos de datos ESPECIFICACIÓN ALGEBRAICA DE TIPOS DE DATOS

19. Packages o paquetes

Programación Orientada a Objetos en Java

LEER Y ESCRIBIR ARCHIVOS O FICHEROS EN C. FOPEN, FCLOSE, MODOS DE ACCESO READ, WRITE Y APPEND (CU00536F)

MATERIAL 2 EXCEL 2007

Los números racionales

Instrucción IrA (GoTo). Saltos no naturales en el flujo normal de un programa. Pseudocódigo y diagramas de flujo. (CU00182A)

La ventana de Microsoft Excel

Figura 4.1 Clasificación de los lenguajes de bases de datos

Operación de Microsoft Word

Usuarios y Permisos. Capítulo 12

Tutorial de UML. Introducción: Objetivos: Audiencia: Contenidos:

Programación orientada a objetos

EDICIÓN Y FORMATO (II)

8. Sentencia return y métodos

INSTITUTO TECNOLOGICO de la laguna Programación Orientada a Objetos en C++

Módulo II - PowerPoint

Diagramas del UML. A continuación se describirán los diagramas más comunes del UML y los conceptos que representan: Diagrama de Clases

Manual de usuario para Android de la aplicación PORTAFIRMAS MÓVIL

Tema 2. El lenguaje de programación Java (Parte 1)

Conceptos. ELO329: Diseño y Programación Orientados a Objetos. ELO 329: Diseño y Programación Orientados a Objetos

Práctica 2: Simón dice

GESTIÓN DE LA DOCUMENTACIÓN

Descarga Automática. Manual de Usuario. Operador del Mercado Ibérico de Energía - Polo Español Alfonso XI, Madrid

Capítulo 6. Introducción a la POO

APUNTES DE WINDOWS. Windows y sus Elementos INSTITUTO DE CAPACITACIÓN PROFESIONAL. Elementos de Windows

Elementos léxicos del lenguaje de programación Java

WinHIPE: edición, compilación y ejecución de programas; y generación de animaciones web. Manual de usuario.

INTRODUCCIÓN A LOS SISTEMAS GESTORES DE BASE DE DATOS

INSTRUCCIÓN DE SERVICIO NOCIONES BÁSICAS PARA DIAGRAMAS DE FLUJO. MICROSOFT VISIO

Google Calendar. Google Calendar

Definición de clases: Herencia, polimorfismo, ligadura dinámica

5.1. Organizar los roles

TABLA DE DECISION. Consideremos la siguiente tabla, expresada en forma genérica, como ejemplo y establezcamos la manera en que debe leerse.

Curso de Python Inicial

Comercial Cartas de Fidelización

Repaso de las características más importantes de la programación Java y su adaptación a Android

Programación Orientada a Objetos en Java

Esta extensión está obsoleta a partir de PHP 5.5.0, y será eliminada en el futuro

INDICE DEL CURSO APRENDER PROGRAMACIÓN JAVA DESDE CERO. PROGRAMACIÓN ORIENTADA A OBJETOS (CU00601B)

Receta: Entorno de Movilidad

PROGRAMACIÓN ORIENTADA A OBJETOS

Tema 4. Gestión de entrada/salida

Para ingresar a la aplicación Microsoft PowerPoint 97, los pasos que se deben seguir pueden ser los siguientes:

2. Conceptos básicos Abstracción La abstracción como un proceso mental natural La abstracción en el desarrollo de software

Instructivo de Microsoft Excel 2003

GENERAR DOCUMENTOS HTML USANDO LENGUAJE PHP. EJERCICIO RESUELTO EJEMPLO SENCILLO. (CU00733B)

Hoja1!C4. Hoja1!$C$4. Fila

Manual de ayuda para crear y gestionar Tareas, como actividad evaluable

Estimado usuario. Tabla de Contenidos

Clases y Objetos. Informática II Ingeniería Electrónica

Práctica 3 mtp. metodoloxía e tecnoloxía da programación. Presentar detalles básicos sobre la sintaxis del lenguaje de programación Java.

Acuerdo de aprobación de la Normativa Básica de Correo Electrónico de la Universidad Miguel Hernández.

15. Parámetros o argumentos

EXAMEN FINAL Metodología y Programación Orientada a Objetos. Curso Cuatrimestre de otoño. 17 de Enero de 2011

Tutorial de Introducción a la Informática Tema 0 Windows. Windows. 1. Objetivos

FUNDAMENTOS DE PROGRAMACIÓN. SEPTIEMBRE 2005

GESTIÓN Y CONTROL DEL DESARROLLO E IMPLANTACIÓN DE APLICACIONES

11. Algunas clases estándar de Java (II)

Base de datos en la Enseñanza. Open Office

GUÍA TÉCNICA PARA LA DEFINICIÓN DE COMPROMISOS DE CALIDAD Y SUS INDICADORES

Los elementos que usualmente componen la identidad digital son:

Guía del usuario de DocuShare Agent

Base de datos en Excel

HP Backup and Recovery Manager

VARIABLES, CONSTANTES Y EXPRESIONES ASIGNACIÓN. TIPOS ELEMENTALES. PRECEDENCIA DE LOS ESTRUCTURAS DE CONTROL. CONDICIONAL E

Memoria compartida y semáforos r/w. La página del manual que podría servir para describir estas funciones es la siguiente:

Transcripción:

1. Introducción a los objetos 1.1 Todo objeto tiene una interfaz. La creación de tipos abstractos de datos (clases) es un concepto fundamental en la POO. Es posible la creación de variables de un tipo (objeto o instancia) y manipularlas (mensajes). Los miembros de cada clase comparten algunos rasgos comunes. Siempre que aparezca la palabra clave tipo (type) puede sustituirse por la clase (class) y viceversa. Las peticiones que se puedan hacer a un objeto se encuentran definidas en su interfaz, que viene determinada por el tipo de objeto. La interfaz establece qué peticiones pueden hacerse a un objeto en particular. 1.2 La implementación oculta. La creación de clases debe ser de forma que únicamente se exponga lo necesario, ocultando el resto. Este enfoque permite la modificación de la parte oculta sin preocuparse de las implicaciones de la modificación. Otro motivo es evitar las manipulaciones indebidas de los métodos de las clases. Java usa tres palabras clave para establecer los límites en una clase, estos modificadores de acceso determinan quién puede usar las definiciones a las que preceden: public: Las definiciones están disponibles para todo el mundo. private: Sólo el creador del tipo puede acceder a esas definiciones. Si se intenta acceder se obtiene un error en tiempo de compilación. protected: Actúa como privada con la diferencia de que una clase heredada tiene acceso a miembros protegidos pero no a los privados. Cuando no se especifica el modo tiene un acceso por defecto, se suele denominar acceso amistoso (friendly) ya que las clases pueden acceder a los miembros amigos de otras clases que estén en el mismo paquete (package), fuera del paquete, estos miembros amigos se convierten en private. 1.3 Reutilizar la implementación La manera más simple de reutilizar una clase es usar un objeto de esta clase, también es posible ubicar un objeto de esa clase dentro de otra clase, creación de un objeto miembro. La nueva clase puede construirse a partir de un número indefinido de otros objetos, de igual o distinto tipo, en cualquier combinación necesaria para lograr la funcionalidad deseada dentro de la nueva clase. Este concepto es la composición (agregación), que se representa mediante la relación esparte-de. La composición conlleva una gran carga de flexibilidad. Los objetos miembros de la nueva clase suelen ser privados. Esto permite cambiar los miembros en tiempo de ejecución, para así cambiar de manera dinámica el comportamiento de un programa. La herencia no es tan flexible, ya que el compilador debe emplazar restricciones de tiempo de compilación en las clases creadas por herencia. Es recomendable intentar el uso de la composición (simple y sencilla) que aplicar herencia. 1.4 Herencia: Reutilizar la interfaz. La herencia expresa la semejanza entre tipos haciendo uso del concepto de tipos base y tipos derivados. Un tipo base contiene todas las características y comportamientos que comparten los tipos que de él se derivan. El uso de la herencia permite construir una jerarquía de tipos que expresa el problema en términos de los propios tipos. De esta forma el modelo principal lo constituye la jerarquía de tipos, de manera que se puede ir directamente de la descripción del sistema en el mundo real a la descripción del sistema en código. Al heredar a partir de un tipo existente, se crea un nuevo tipo. Este nuevo tipo contiene no sólo los miembros del tipo existente (aunque los datos private estén ocultos e inaccesibles) sino que duplica la interfaz de la clase base. Hay dos formas de diferenciar el comportamiento de la clase derivada de la clase base original. El primero es añadir nuevas funciones. La segunda (y más importante) es variar el comportamiento de una función ya existente en la clase base. A esto se le llama redefinición (anulación o superposición) de la función. Para ello simplemente se crea una nueva definición de la función dentro de la clase derivada. 1.4.1 La relación es-un frente a la relación es-como-un El principio de sustitución dice que la filosofía a seguir entre clases bases y derivadas es sólo superponer las funciones de la clase base (un círculo es un polígono), es lo que se conoce como relación es-un, en esta relación es posible sustituir un objeto de la clase derivada por otro de la clase base. J.M. Godoy, F. Gómez y E. Rubio. 2002-2003 página 1

Existen casos en los que es necesario añadir nuevos elementos a la interfaz del tipo derivado, extendiendo así la interfaz y cuando un nuevo tipo, en este caso la sustitución por el tipo base no es perfecto y esta relación se define escomo-un. 1.5 Objetos intercambiables con polimorfismo Se puede desear tratar un objeto como su tipo base, con lo que el código no dependerá de tipos específicos. En los compiladores no-oo una llamada a una función crea una ligadura temprana, es decir, genera una llamada a una función con nombre específico y el montador resuelve esta llamada a la dirección absoluta del código a ejecutar. En POO, el programa no puede determinar la dirección del código hasta tiempo de ejecución, utilizándose otro esquema, el de ligadura tardía, al enviar un mensaje a un objeto, no se determina el código invocado hasta el tiempo de ejecución. El compilador se asegura de que la función exista y hace la comprobación de tipos de los argumentos y del valor de retorno, pero se desconoce el código exacto a ejecutar. El proceso de tratar un tipo derivado como si fuera el tipo base se le llama conversión de tipos (moldeado) hacia arriba (casting), es decir, moverse hacia arriba por el diagrama de herencias. 1.5.1 Clases base abstractas e interfaces Puede ser deseable que la clase base sólo presente una interfaz para sus clases derivadas, es decir, que no se creen objetos de ella, sino sólo de las derivadas. Esto se consigue convirtiendo esa clase en abstracta con la palabra clave abstrac. Si se intenta construir un objeto de una clase abstracta el compilador lo evita. También se puede utilizar la palabra clave abstrac para describir un método que aún no ha podido ser implementando, un método abstracto sólo puede existir dentro de una clase abstracta. Cuando se hereda la clase, debe implementarse el método o de lo contrario la clase heredada se convierte en abstracta. La creación de métodos abstractos permiten poner un método en una interfaz sin verse forzado a proporcionar un fragmento de código para ese método. La palabra clave interface toma el concepto de clase abstracta, evitando totalmente las definiciones de funciones, es una herramienta muy útil al proporcionar la separación perfecta entre interfaz e implementación. 1.6 Localización de objetos y longevidad Un factor importante en la POO es la manera de crear y destruir objetos. Java utiliza el enfoque de crear objetos dinámicamente en el montículo (heap). En este enfoque, no es necesario conocer hasta el tiempo de ejecución el número de objetos, longevidad o tipo. Si se necesita un nuevo objeto simplemente se construye en el montículo. Como el almacenamiento se gestiona dinámicamente, en tiempo de ejecución, la cantidad de tiempo necesaria para asignar espacio de almacenamiento en el montículo es mayor que el necesario en la pila. Sin embargo la flexibilidad es esencial para resolver en general los problemas de programación. Cada vez que se desea crear un objeto se usa la palabra clave new para construir una instancia dinámica de ese objeto. El aspecto de la longevidad se resuelve, en el caso de la pila, por el compilador que determina cuánto dura cada objeto y puede destruirlo cuando no es necesario. En el enfoque del montículo el compilador no tiene conocimiento de su duración, lo proporciona un recolector de basura que descubre automáticamente cuándo se ha dejado de utilizar un objeto, y puede ser destruido. Esto disminuye la cantidad de código y ofrece un nivel superior de seguridad. 1.6.1 Colecciones e iteradores. En un problema concreto se pueden desconocer el número y duración de objetos para solucionarlo, y en consecuencia cómo almacenarlos. En la POO se soluciona creando otro tipo de objetos que tiene referencias a otros objetos. Además este nuevo objeto, llamado contenedor, se expandirá a sí mismo cuando sea necesario para albergar todo lo que se coloque en su interior. Todos los contenedores tienen alguna manera de introducir y extraer cosas, pero la extracción puede ser problemática porque una función de selección única suele ser restrictiva. La solución es un iterador, que es un objeto cuyo trabajo es seleccionar los elementos de dentro de un contenedor y presentárselos al usuario. Como clase también proporciona un cierto nivel de abstracción, que puede ser usada para separar los detalles del contenedor del código al que éste esté accediendo. El contenedor se abstrae a través del iterador hasta convertirse en una secuencia, que se puede recorrer con el iterador, sin preocuparse de la estructura subyacente (Arraylist, LinkedList, Stack...). Java2 cuenta con un iterador denominado Iterator (en Java 1.0 y 1.1 Enumeration). Página 2 J.M. Godoy, F. Gómez y E. Rubio. 2002-2003

1.6.2 Jerarquía de raíz única. En Java todas las clases heredan, en última instancia, de una única clase base, Object. De esta forma todos los objetos tienen una interfaz común, por lo que en última instancia son del mismo tipo. Puede garantizarse que todos los objetos de una jerarquía de raíz única tienen cierta funcionalidad y junto a la creación de todos los objetos en el montículo simplifica el paso de argumentos. Además simplifica la implementación del recolector de basura ya que su soporte se puede instalar en la clase base y podrá enviar los mensajes apropiados a todos los objetos del sistema. Dado que está garantizado que en tiempo de ejecución la información de tipos está en todos los objetos, jamás será imposible encontrar un objeto cuyo tipo no pueda ser determinado, esto es importante en operaciones a nivel de sistema y por la flexibilidad que proporciona al programar. 1.6.3 Bibliotecas de colecciones Para utilizar un contenedor, basta con añadirle referencias a objetos y luego preguntar por ellas. Dado que el contenedor sólo guarda objetos de tipo Object, al añadirle una referencia se hace un moldeado hacia arriba a Object perdiendo la identidad. Al recuperarlo, se obtiene una referencia a Object y no una referencia al tipo introducido. Para recuperar la interfaz del objeto introducido en el contenedor se debe hacer un moldeado hacia abajo, a un tipo erróneo, se mostrará un error en tiempo de ejecución, exception. Para solucionar el moldeado hacia abajo y las comprobaciones en tiempo de ejecución, se crean los tipos parametrizados, que son clases que el compilador puede adaptar automáticamente para que trabajen con tipos determinados. Dado que C++ no tiene jerarquía de raíz única es la solución que adopta (template). Java no los tienen ya que logra lo mismo explotando la unicidad de raíz de su jerarquía, aunque lo hace de manera complicada. 1.7 Multihilo A veces, es necesario hacer uso de las interrupciones para el manejo de tareas críticas en el tiempo, pero hay una gran cantidad de problemas en los que simplemente se intenta dividir un problema en fragmentos de código que pueden ser ejecutados por separado, de manera que se logra un menor tiempo de respuesta para todo el programa en general. Estos fragmentos de código que pueden ser ejecutados por separado, se denominan hilos y el concepto general multihilo. Los hilos son una herramienta para facilitar la planificación de un monoprocesador. Si el sistema operativo soporta múltiples procesadores, es posible asignar cada hilo a un procesador distinto de manera que los hilos se ejecuten verdaderamente en paralelo. Uno de los aspectos más destacables es que el programa no tiene de ocuparse de ello. Para los recursos compartidos por varios hilos se pueden establecer bloqueos a los accesos. El hilo de Java está incluido en el propio lenguaje y se soporta a nivel de objeto. El bloqueo de memoria de cualquier objeto se logra mediante la palabra clave synchronized. Otros tipos de recursos se deben bloquear por el programador creando un objeto que represente el bloqueo que todos los hilos deben comprobar antes de acceder al recurso. J.M. Godoy, F. Gómez y E. Rubio. 2002-2003 página 3

2. Todo es un objeto 2.1 Los objetos se manipulan mediante referencias En Java, todo se trata como un objeto, utilizando una única sintaxis consistente, que se utiliza en todas partes. Aunque se trata todo como un objeto, el identificador que se manipula es una referencia a un objeto. Es la referencia, la que actúa sobre el objeto. La referencia puede existir por sí misma sin necesidad de que exista un objeto. De esta forma, si se desea tener una palabra o frase, se crea una referencia String : String s; Esta sentencia sólo crea la referencia, no el objeto. Si se decide enviar un mensaje en este momento, se obtendrá un error en tiempo de ejecución porque s no se encuentra vinculado a nada. Una práctica más segura es iniciar la referencia en el mismo momento de su creación: String s = abcd Cuando se crea una nueva referencia, debe conectarse con un objeto. Java utiliza la palabra reservada new, para crear un objeto nuevo. String s = new String ( abcd ) 2.2 Donde reside el almacenamiento? Hay seis lugares diferentes donde almacenar información: 1. Registros. Elemento de almacenamiento más rápido. Número limitado de ellos. Los asigna el compilador en función de sus necesidades. No se tiene control directo sobre ellos. 2. La pila. Reside en la memoria RAM, tiene soporte directo del procesador a través del puntero de pila, que se mueve hacia abajo para crear más memoria y hacia arriba para liberarla. Este método es una manera rápida y eficiente de asignar espacio. El compilador debe conocer, mientras está creando el programa, el tamaño exacto y la vida de todos los datos almacenados en la pila. El uso de la pila pone limitaciones de flexibilidad a los programas. 3. El Montículo. Espacio de memoria de propósito general, en el que residen los objetos Java. En el montículo, a diferencia de la pila, el compilador no necesita conocer cuanto espacio de almacenamiento se necesita asignar. Proporciona gran flexibilidad. 4. Almacenamiento estático: (con una ubicación/posición fija pero en RAM). Contiene datos que están disponibles durante todo el tiempo que se esté ejecutando un programa. Se utiliza static para especificar que un elemento particular de un objeto sea estático, pero los objetos nunca se sitúan en el espacio de almacenamiento estático. 5. Almacenamiento constante. Los valores constantes se suelen ubicar directamente en el código del programa. En ocasiones las constantes suelen ser acordonadas por si mismas, de forma que puedan ser ubicadas ocasionalmente en ROM. 6. Almacenamiento no-ram. Los dos ejemplos principales son: objeto de flujo de datos (stream), que se convierte en flujo o corrientes de bits, generalmente para ser enviados a otra máquina y los objetos persistentes, que son ubicados en disco. Java proporciona soporte para persistencia ligera. Página 4 J.M. Godoy, F. Gómez y E. Rubio. 2002-2003

2.3 Los tipos primitivos Los tipos primitivos tienen un tratamiento especial, no es eficiente crear un objeto de este tipo con new ya que lo coloca en el montículo. Para estos tipos se crea una variable automática que no es una referencia. La variable guarda el valor y se coloca en la pila para que sea más eficiente. Java determina el tamaño de cada tipo primitivo, que no varía de una plataforma a otra: Tipo primitivo Tamaño Máximo Mínimo Tipo envoltura boolean - - - Boolean char 16 bits Unicode 0 Unicode 2 16-1 Character byte 8 bits -128 127 Byte short 16 bits - 2 15 +2 15-1 Short int 32 bits - 2 31 +2 31-1 Integer long 64 bits - 2 63 +2 63-1 Long float 32 bits IEEE754 IEEE754 Float double 64 bits IEEE754 IEEE754 Double void - - - Void Todos los tipos numéricos tienen signo. El tamaño de boolean no está explícitamente definido; sólo se especifica que debe ser capaz de tomar los valores True o False. Los tipos de datos primitivos tienen también clases envoltura, de forma que si se desea crear un objeto no primitivo en el montículo para representar ese tipo primitivo, se hace uso del envoltorio asociado. Por ejemplo: char c = 'x'; Character C = new Character(c); 2.4 Números de alta precisión Java incluye dos clases para llevar a cabo aritmética de alta precisión: BigInteger y BigDecimal. Aunque estos tipos vienen a encajar en la misma categoría de las clases envoltorio, ninguna de ellas tiene un tipo primitivo. Ambas clases tienen métodos que proporcionan operaciones análogas que se llevan a cabo con tipos primitivos (int y float), utilizando llamadas a métodos en lugar de operadores. Son más lentas que las primitivas. 2.5 Arrays en Java Java garantiza que los arrays estarán siempre inicializados y que no se podrá acceder más allá de su rango. La comprobación de rangos se resuelve con una pequeña sobrecarga de memoria en cada array, además de verificar el índice en tiempo de ejecución. Cuando se crea un array de objetos, se está creando realmente un array de referencias a los objetos, y cada una de estas se inicializa con un valor especial representado por la palabra clave null. Debe asignarse un objeto a cada referencia antes de usarla y si se intenta hacer uso de una referencia que aún vale null, se informará de que ha ocurrido un problema en tiempo de ejecución. J.M. Godoy, F. Gómez y E. Rubio. 2002-2003 página 5

2.6 Ámbito Determina tanto la visibilidad como la vida de los nombres definidos dentro de éste ámbito. El ámbito se determina por la ubicación de las llaves {, por ejemplo: { int x=12; /* sólo x disponible */ { int q=96; /* tanto x como q están disponibles */ /* sólo x disponible */ /* q está fuera del ámbito o alcance */ Una variable definida dentro de un ámbito solamente está disponible hasta que finalice su ámbito. La visibilidad en Java está implementada en modo de anidamiento descendente. Si una variable se define en un determinado nivel, y posteriormente en un nivel inferior, el compilador comunicará que la variable está duplicada. { int x; { int x; 2.7 Ámbito de los objetos Los objetos no tienen la misma vida que los tipos primitivos. Cuando se crea un objeto con new, éste perdura hasta el final del ámbito, de manera que: { String s = new String( un string ); /* fin del ámbito */ La referencia s desaparece al final del ámbito, sin embargo, el objeto String al que apunta s sigue ocupando memoria. Java tiene un recolector de basura, que recorre todos los objetos que fueron creados con new y averigua cuales no serán referenciados más, liberando la memoria que estos ocupan. En Java no es necesario hacer ninguna reserva de memoria, simplemente se crean los objetos y cuando dejan de ser necesarios, desaparecen por sí mismos. Esto elimina el problema de programación denominado agujero de memoria, que se produce en otros lenguajes cuando no se destruyen explícitamente los objetos para liberar memoria. 2.8 Crear nuevos tipos de datos: clases Un nuevo tipo de objeto se define mediante la palabra clave class, que siempre va seguida del nombre del nuevo tipo, por ejemplo: class UnNombreDeTipo {/* Aquí va el cuerpo de la clase */ Esto introduce un nuevo tipo, siendo posible crear un objeto de este tipo haciendo uso de la palabra new, por ejemplo: UnNombreDeTipo u = new UnNombreDeTipo (); 2.9 Campos y métodos Ilegal Cuando se define una nueva clase, es posible poner dos tipos de elementos: datos miembros (denominados campos) funciones miembros (denominados métodos). Un dato miembro es un objeto de cualquier tipo con el que te puedes comunicar a través de su referencia. También puede ser algún tipo primitivo. Si es una referencia a un objeto, hay que reinicializar esa referencia para conectarla a algún objeto real (utilizando new) en una función especial denominada constructor. Si se trata de un tipo primitivo es posible inicializarla directamente en el momento de definir la clase. Cada objeto mantiene el espacio de almacenamiento necesario para todos sus datos miembro; estos no son compartidos con otros objetos: class SoloDatos { Página 6 J.M. Godoy, F. Gómez y E. Rubio. 2002-2003

int i; float f; boolean b; Es posible asignar valores a los datos miembro, pero primero es necesario hacer referencia al miembro del objeto. s.i = 47; s.f = 1.1f; f.b = False; También es posible que un objeto pueda contener otros datos que se quieran modificar. Para ello hay que seguir concatenando puntos por ejemplo: miavion.tanqueizquierdo.capacidad = 100; 2.10 Valores por defecto para los miembros primitivos Cuando un tipo de datos primitivo es un miembro de una clase, se garantiza que tenga un valor por defecto si no se inicializa: Tipo Primitivo boolean char false Valor por defecto u0000 (null) byte (byte) (0) short short (0) Tipo Primitivo int 0 long 0L float 0.0f double 0.0d Valor por defecto Esta garantía no se aplica a las variables locales -- aquellas que no sean campos de clases. Si dentro de una función se tiene: int x; entonces x tomará algún valor arbitrario. El compilador Java advierte como error de esta circunstancia. 2.11 Métodos, parámetros y valores de retorno Los métodos en Java determinan los mensajes que puede recibir un objeto. Las partes fundamentales de un método son su nombre, sus parámetros, el tipo de retorno y el cuerpo. Su forma básica se asemeja a la siguiente: tiporetorno nombremetodo ( /* lista de parámetros */ ) { /* cuerpo del método */ El tipo de retorno es el tipo de valor que surge del método tras ser invocado. La lista de parámetros indica los tipos y nombres de las informaciones que es necesario pasar en ese método. Cada método se identifica unívocamente mediante el nombre del método y la lista de parámetros. En Java, los métodos pueden crearse como parte de una clase. Es posible que un método pueda ser invocado sólo por un objeto, que llevará a cabo la llamada al método. Un método puede invocarse mediante la expresión completa de su ruta cualificada, o bien, asignando a una variable el valor de retorno de un objeto, en este caso, la declaración de la variable debe corresponderse con el valor que retorna el método. El acto de invocar a un método suele denominarse envío de un mensaje a un objeto. Por ejemplo en: int x = a.f(x); el mensaje es f() y el objeto es a. 2.12 Lista de parámetros La lista de parámetros de un método especifica la información que se le pasa. Esta información tiene forma de objeto y se debe especificar los tipos de objetos a pasar, y nombre a utilizar en cada uno. Debe tenerse en cuenta que realmente no se están manipulando directamente objetos, sino que se están pasando referencias. Un método puede devolver el tipo que se desee, pero si no se desea devolver nada, se debe indicar que el método devuelve void Cuando el tipo de retorno es void, se utiliza la palabra clave return sólo para salir del método, siendo innecesaria cuando se llega al final del mismo. Es posible salir de un método en cualquier punto, pero si se devuelve un valor de retorno distinto de void, el compilador obligará a devolver el tipo apropiado (mediante mensaje de error). J.M. Godoy, F. Gómez y E. Rubio. 2002-2003 página 7

2.13 Construcción de un programa en Java 2.13.1 Visibilidad de nombres Para producir un nombre no ambiguo para una biblioteca, el identificador utilizado se asemeja a los nombres de dominio de Internet, pero a la inversa, dado que es posible garantizar que estos sean únicos. Una vez que se da la vuelta al nombre de dominio, los nombres supuestamente representan directorios. Este mecanismo hace posible que todos sus ficheros residan automáticamente en sus propios espacios de nombres, y cada clase de un fichero debe tener un identificador único. 2.13.2 Utilización de otros componentes Cuando se desee utilizar una clase predefinida en un programa, el compilador debe saber donde encontrarla. Para ello, debe utilizarse la sentencia import. Esta palabra clave le dice al compilador que traiga un paquete, que es una biblioteca de clases. La mayoría de las veces se utilizan componentes de bibliotecas de Java estándar que vienen con el propio compilador, simplemente se hace: import java.util.arraylist; para indicar al compilador que se desea utilizar la clase ArrayList de Java. Sin embargo, util contiene bastantes clases y se podría desear utilizar varias de ellas sin tener que declararlas todas. Esto se logra sencillamente utilizando el * que hace las veces de comodín, así: import java.util.*; importaría la colección de clases que forman parte de la librería útil. 2.17.3 La palabra clave static Al crear una clase se está describiendo qué apariencia tienen sus objetos y cómo se comportan. No se tiene nada hasta crear un objeto de esa clase con new, momento en el que se crea el espacio de almacenamiento y los métodos pasan a estar disponibles. Hay dos situaciones en las que este enfoque no es suficiente. Una es cuando se desea tener sólo un fragmento de espacio de almacenamiento para una parte concreta de datos, independientemente de cuántos objetos se crean, o incluso aunque no se cree ninguno. La otra es cuando se necesita un método que no esté asociado a ningún objeto particular de una clase. Puesto que los métodos estáticos no precisan de la creación de ningún objeto, no pueden acceder directamente a miembros de métodos no estáticos sin referirse al objeto con nombre del que son propiedad. Para declarar un dato o un miembro a nivel de clase como estático, basta con colocar la palabra clave static antes de la definición, así: class PruebaEstatica { static int i = 47; Ahora, incluso si se construyen dos objetos de tipo PruebaEstatica, sólo habrá un espacio de almacenamiento para PruebaEstatica.i. Ambos objetos compartirán la misma i: PruebaEstatica st1 = new PruebaEstatica(); PruebaEstatica st2 = new PruebaEstatica(); En este momento, tanto st1.i como st2.i tienen el valor 47, puesto que se refieren al mismo espacio de memoria. Hay dos maneras de referirse a una variable estática. Es posible manejarlas a través de un objeto como en st2.i. También es posible referirse a ellas directamente a través de su nombre de clase, algo que no se puede hacer con miembros no estáticos. Ésta manera especial énfasis en la naturaleza estática de esa variable. PruebaEstatica.i ++; El operador ++ incrementa la variable. En ese momento tanto st1.i como st2.i valdrán 48. Algo similar se aplica a los métodos estáticos. Es posible referirse a ellos, bien a través de un objeto o bien con la sintaxis adicional NombreClase.metodo(). Un método estático se define de manera semejante: class FunEstatico { static void incr() {PruebaEstatica.i++ Página 8 J.M. Godoy, F. Gómez y E. Rubio. 2002-2003

Mientras que static al ser aplicado a un miembro de datos, cambia definitivamente la manera de crear los datos (uno por cada clase en lugar de uno por cada objeto no estático), al aplicarse a un método no es tan drástico. Un uso importante de static para los métodos es permitir invocar a un método sin tener que crear un objeto. Esto, es esencial en la definición del método main(), que es el punto de entrada para la ejecución de la aplicación. J.M. Godoy, F. Gómez y E. Rubio. 2002-2003 página 9

3 Controlar el flujo del programa. 3.1 Asignación La asignación de tipos primitivos es sencilla y directa, ya que el dato primitivo alberga el valor actual y no una referencia a un objeto. En la asignación de objetos, sin embargo, cuando se hace una asignación de un objeto a otro, se copia la referencia de un sitio a otro, es decir, si se hace C=D siendo ambos objetos, tanto C como D apuntarán al objeto que originalmente apuntaba D. Este fenómeno se llama uso de alias. El uso de alias también se produce en el paso de un objeto a un método, de forma que se pasa una referencia. 3.2 Operadores Relacionales(<,>,<=...), generan un resultado de tipo boolean, su uso para tipos primitivos es tal cual, pero para comparar los contenidos de dos objetos (referencias) se debe usar equals. Lógicos (AND &&, OR, NOT!), producen un valor lógico, pero sólo es posible aplicarlos a los valores boolean. La evaluación de operadores lógicos sufre cortocircuito, es decir, se evalúan solo hasta que es posible determinar sin ambigüedad la certeza o falsedad de toda expresión lógica. Operadores de bit. Permiten manipular bits individuales, los operadores son AND (&), OR( ), XOR(^), NOT(~); estos operadores son combinables con el de asignación (&=) a excepción del operador NOT. Los tipos boolean se pueden manipular con estos operadores, pero no se les puede aplicar el operador NOT. Operadores de desplazamiento. Estos operadores manipulan bits y sólo se pueden utilizar con tipos primitivos enteros. Al desplazar un char será convertido a int y el resultado será también un int. Sólo se desplazan los n-1 bits más significativos para evitar salirse de rango. Operador ternario if-else, tiene la forma exp-booleana? Valor_0 : valor_1 Si la expresión booleana es cierta se evaluará valor_0, en caso contrario valor_1. Conversión. Normalmente Java convierte los tipos cuando cree apropiado, y la conversión es segura exceptuando las conversiones reductoras, en las que se corre el riesgo de perder información. Página 10 J.M. Godoy, F. Gómez y E. Rubio. 2002-2003

4 Inicialización y limpieza 4.1 Inicialización garantizada con el constructor. En Java, el diseñador de cada clase puede garantizar que se inicialice cada objeto proporcionando un método especial llamado constructor. Si una clase tiene constructor, Java lo llama automáticamente al crear un objeto, quedando la inicialización garantizada. Para evitar conflictos el nombre del constructor es el mismo que el de la clase. Los constructores no tienen valor de retorno. El constructor por defecto es aquél que no tiene parámetros, y se utiliza para crear un objeto básico. Si se crea una clase sin constructores, el compilador siempre creará un constructor por defecto. Sin embargo si se define algún constructor (con o sin parámetros) el compilador no creará uno automáticamente. 4.2 Sobrecarga de métodos La sobrecarga de métodos es esencial para permitir que se use el mismo nombre de métodos con distintos tipos de parámetros y es esencial para los constructores. 4.2.1 Distinción de métodos sobrecargados. Orden de los parámetros Los tipos primitivos sino coinciden y se tiene un tipo de datos menor al parámetro del método, ese tipo se promociona (a excepción de char que se promociona a int). Si el parámetro es mayor se debe convertir explícitamente sino el compilador mostrará un mensaje de error. NO se pueden usar los tipos de valores de retorno para distinguir los métodos sobrecargados. 4.2.2 La palabra clave this. La palabra clave this, que sólo se puede usar dentro de un método, produce la referencia al objeto por el que se ha invocado al método. Si se invoca a un método de una clase dentro de un método de esa misma clase no es necesario usar this. Cuando se escriben varios constructores para una clase, hay veces en las que uno quisiera invocar a un constructor desde otro para evitar la duplicación de código. Esto se puede lograr utilizando this. Se puede invocar a un constructor utilizando this, pero no a dos. Además, la llamada al constructor debe ser la primera cosa que se haga o se obtendrá un mensaje de error del compilador. El compilador NO permite tampoco invocar a un constructor desde dentro de otro método que no sea un constructor. En los métodos estáticos no existe la posibilidad de usar this ya que no se puede invocar a métodos no estáticos desde dentro de métodos estáticos (al revés sí), y se puede invocar a un método estático de la propia clase sin objetos. 4.3 Limpieza. En Java existe el método finalize, que se puede definir en cada clase, y si el recolector de basura está preparado para liberar el espacio de almacenamiento utilizado por el objeto, primero invocará a finalize, y sólo recuperará la memoria del objeto durante la pasada del recolector de basura. Sin embargo finalize no es en sí un destructor, es decir, si se quiere hacer alguna cosa cuando finalice la vida del objeto se debe programar fuera de finalize, ya que puede que éste no llegue nunca a ejecutarse. La verdadera utilidad de finalize es la verificación de la condición de muerte de un objeto. 4.4 Inicialización de miembros Las variables definidas localmente en un método deben estar inicializadas, en caso contrario se obtendrá un error en tiempo de compilación. En el caso de atributos de tipo primitivo esto no es así y Java tiene una inicialización por defecto. Al definir una referencia a un objeto dentro de una clase sin inicializarla a un nuevo objeto, la referencia recibe el valor null. Si se define una clase y no se inicializa se obtiene una excepción. Es posible invocar a un método para proporcionar un valor de inicialización, pero si tiene parámetros no pueden ser de una clase que no esté inicializada, el compilador daría error. J.M. Godoy, F. Gómez y E. Rubio. 2002-2003 página 11

4.5 Inicialización de constructores Dentro de una clase el orden de inicialización lo determina el orden en que se definen las variables dentro de la clase. Las definiciones pueden estar dispersas dentro de las definiciones de los métodos, pero las variables se inicializan antes de invocar a ningún método, incluido el constructor. Los objetos estáticos se inicializan antes que los no estáticos aunque sólo se hace una vez, es decir en la primera llamada. Al aplicar la herencia se inicializan las clases bases antes que los constructores de las derivadas, y se llama a la clase base una vez por cada clase derivada. Página 12 J.M. Godoy, F. Gómez y E. Rubio. 2002-2003

5. Ocultar la implementación. 5.1 El paquete: la unidad de biblioteca. Un paquete es lo que se obtiene al utilizar la palabra clave import. Las importaciones proporcionan un mecanismo para gestionar los espacios de nombres. Existe la necesidad de controlar los espacios de nombres en Java, para tener la capacidad de crear un nombre completamente único sin importar las limitaciones de Internet. Al crear un fichero de código fuente en Java, se crea lo que comúnmente se denomina una unidad de compilación. Cada una de estas unidades tiene un nombre que acaba en.java, y dentro de la unidad de compilación sólo puede haber una única clase pública, sino el compilador dará error. El resto de clases de esa unidad de compilación quedan ocultas al exterior del paquete y son de apoyo para la clase pública principal. Al compilar un fichero.java, se obtiene un fichero de salida que tiene exactamente el mismo nombre con la extensión.class. Una biblioteca también es un conjunto de ficheros de clase. Cada fichero tiene una clase que es pública (aunque no es obligatorio), de forma que hay un componente por cada fichero. Si se desea que los componentes permanezcan unidos es necesario el uso de pakage al principio del archivo, en la primer línea que no sea un comentario, indicando que esa unidad de compilación es parte de un paquete. A la hora de buscar los archivos de clase el interprete de Java procede de la siguiente forma. En primer lugar encuentra la variable de entorno CLASSPATH, a partir de esta raíz, toma el nombre del paquete y reemplaza cada punto por una barra para generar un nombre relativo a la raíz CLASSPATH y busca los ficheros.class correspondientes. En caso de colisiones de nombres al utilizar import clase.*, el compilador se queja y se deberá especificar cual es la clase que se quiere usar. Es posible que exista alguna unidad de compilación sin ninguna clase pública, en este caso el archivo se puede llamar como se desee. 5.2 Modificadores de acceso en Java. Public: Las declaraciones del miembro estarán disponibles a todo el mundo Private: Nadie, puede tener acceso a ese miembro excepto a través de los métodos de esa clase. Protected: Como private pero dando acceso a los descendientes por herencia. Amistoso: Acceso por defecto, permite el acceso a las clases del mismo paquete. Si no hay un nombre de paquete explícito se usa el mismo directorio (paquete por defecto). Ejemplo de acceso a clase con constructor privado class sopa { private sopa () { // permite la creación a través de un método estático. public static sopa hacersopa () { return new sopa(); // Patrón devuelve una referencia private static sopa sp1=new sopa(); public static acceso () { return ps1; public void f() {.. public class Almuerzo { void prueba () { sopa priv_2 =sopa.hacersopa();// el uso de sopa priv_2 = new sopa(); dará error sopa.acceso().f(); J.M. Godoy, F. Gómez y E. Rubio. 2002-2003 página 13

6. Reutilizando clases 6.1 Herencia En Java siempre se está haciendo herencia cuando se crea una clase, ya que al menos se herede explícitamente de otra clase, se hereda implícitamente de la clase raíz estándar de Java object. Para heredar se usa la palabra clase extends seguida del nombre de la clase base. Al hacer esto se tienen automáticamente todos los datos miembro y métodos de la clase base. Class derivada extends ClaseBase {... Las clases derivadas y las clases base pueden tener un método main cada una, de hecho esto es interesante para poder realizar pruebas individuales de las clases, no importa que la clase no sea pública ya que el método main si lo es. Se debe de tener cuidado de hacer los métodos de la clase extendida públicos, sino la herencia sólo es posible en clases del mismo paquete (acceso amistoso). Es posible sobreescribir los métodos heredados, en este caso sí queremos referirnos al método original (el de la clase base) se utiliza la palabra clave super (super.metodo). También se pueden añadir nuevos métodos a la clase derivada. 6.1.1 Inicialización de la clase base. La herencia no es una simple copia de la interfaz de la clase base, cuando se crea un objeto de la clase derivada, éste contiene dentro de él un subobjeto de la clase base. Es esencial que este subobjeto de la clase base se inicialice correctamente, para ello Java inserta automáticamente llamadas al constructor de la clase base en el constructor de la clase derivada, de forma que se inicializan las clases bases antes que los constructores de la clase derivada puedan acceder a ellos. En el caso de que el constructor de la clase base tenga parámetros se debe escribir explícitamente la llamada al constructor de la clase base mediante super y la lista de parámetros apropiada. Además debe ser lo primero que se haga en el constructor de la derivada. Sino se hace el compilador envía un error. Dado que no puede aparecer nada antes de la llamada al constructor de la clase base no se podrán capturar excepciones en el constructor de la derivada, lo que a veces es un inconveniente. Si una clase en Java tiene un nombre de método sobrecargado varias veces, la redefinición de ese nombre de método en la clase derivada no esconde las versiones de la clase base, es decir, la sobrecarga funciona igual con independencia del nivel de herencia. 6.2 La palabra clave final. 6.2.1 Declaración de datos como final. Si el dato es de un tipo primitivo, se convierte en constante en tiempo de compilación y se le debe dar un valor en tiempo de la definición. Un campo que se defina como estático y final sólo tiene un espacio de almacenamiento que no se puede modificar. Sin embargo cuando declaramos como final cualquier objeto, en realidad se hace final la referencia a una constante, es decir, una vez que la referencia se inicializa a un objeto, ésta nunca se puede cambiar para que apunte a otro objeto, pero, sí se puede modificar el objeto en sí: public class DatosConstantes { DatosConstantes dc1 = new datosconstantes();... dc1.i1++; // ERROR i1 es constante final int i1=9; dc1.val2.i1++; // Referencia constante el objeto no public static final int VAL =99; dc1.val2=new Valor(); // ERROR referencia constante final valor Val2=new Valor();......... Si declaramos un dato como final y no se inicializa hasta el momento de su uso es lo que se conoce como constante blanca, en cualquier caso se debe inicializar antes de su uso y ésto lo garantiza el compilador. Es obligatorio hacer las asignaciones a constantes, bien en el momento de definición del campo o bien en el constructor, de forma que se garantice que el campo constante se inicializa siempre antes de ser usado. Java permite hacer parámetros constantes declarándolos como final en la lista de parámetros. Esto significa que dentro del método no se puede cambiar aquello a lo que apunta la referencia al parámetro. Página 14 J.M. Godoy, F. Gómez y E. Rubio. 2002-2003

6.2.2 Declaración de métodos como final Hay dos razones para justificar los métodos constates, evitar que las clases heredadas varíen su significado y por motivos de eficiencia. Cualquier método privado de una clase es implícitamente constante, no se puede acceder ni modificar (si se intenta se crea uno nuevo en realidad). 6.2.3 Clases constantes Al definir una clase como constante (final) se establece que no se puede heredar de esta clase bien sea por cuestiones de diseño, seguridad o eficiencia. Al definir una clase como constante todos sus métodos son implícitamente constantes ya que no se pueden modificar. 6.3 Carga de clases e inicialización. En general se puede asegurar que el código de las clases se carga en el momento de su primer uso, esto normalmente ocurre cuando se construye el primer objeto de esa clase, pero también se da una carga cuando se accede a un dato o método estático. En el momento del primer uso es también donde se da la inicialización estática. Todos los objetos estáticos y el bloque de código estático se inicializan en el orden textual en el momento de la carga. Los datos estáticos se inicializan solo una vez. Si se utiliza herencia la clase base se carga con independencia de si se crea un objeto de la clase derivada. Posteriormente se lleva a cabo la inicialización estática de la clase raíz y posteriormente la siguiente clase derivada. Tras cargar las clases necesarias se puede crear el objeto. Primero se pone a sus valores por defecto todos los datos primitivos y las referencias a objetos se ponen a null. Después se invoca al constructor de la clase base. J.M. Godoy, F. Gómez y E. Rubio. 2002-2003 página 15

7. Polimorfismo La conexión de una llamada a un método se denomina ligadura. Cuando se lleva a cabo la ligadura antes de ejecutar el programa, es decir, en tiempo de compilación, se denomina temprana. La correspondencia en tiempo de ejecución se llama ligadura tardía o dinámica. Cuando un lenguaje implementa la ligadura tardía, debe haber algún mecanismo para determinar el tipo de objeto en tiempo de ejecución e invocar al método adecuado. Toda ligadura de métodos en Java es tardía excepto en los métodos constantes. Esto permite asegurar el polimorfismo ya que se puede escribir el código que trata la clase base y saber que todas las clases derivadas funcionarán correctamente al usar el código. 7.1 Clases y métodos abstractos. Se crea una clase abstracta cuando se desea manipular un conjunto de clases a través de un interfaz común. Todos los métodos de clases derivadas que encajan en la declaración de la clase base se invocarán utilizando el mecanismo de ligadura dinámica (si los parámetros son diferentes se tiene sobrecarga). Toda clase que tenga uno o más métodos abstractos se considera abstracta. No es posible crear objetos de una clase abstracta, y si se hereda de ella se deben de proporcionar definiciones de los métodos que en la clase base eran abstractos. Si no se hace así la clase derivada será también abstracta y el compilador obligará a definirla como tal. Se puede tener una clase cuyos métodos sean no abstractos y esté definido como abstracto, eso tiene sentido si no se desea que se creen instancias de ellos. Abstract class claseabstracta class clasederivadadeabstracta...... public abstract void metodoabstracto(); public void metodoabstracto () public void metodo_no_abstracto() {...; {//Definición............ 7.2 Constructores y polimorfismo En el constructor de la clase derivada siempre se invoca a un constructor de la clase base, encadenando la jerarquía de herencias de forma que se invoca a un constructor de cada clase base, después se llama a los inicializadores de miembros en el orden de declaración y finalmente al cuerpo del constructor de la clase derivada. Al utilizar la herencia y utilizar finalize, se debe superponer éste método en la clase derivada si se quiere hacer alguna limpieza especial ya que se puede tener control sobre el orden de finalización. El orden más adecuado es finalizar primero la clase derivada y después la clase base. Sin embargo, cuando dentro de un constructor se invoca a un método de ligadura dinámica del objeto que se construye, se utiliza la definición superpuesta de ese método, el efecto puede ser inesperado y dar lugar a errores difíciles de detectar. Esto es así porque se llama a un método que podría manipular miembros que no han sido aún inicializados. 7.2.1 Conversión hacia abajo Dado que en la conversión hacia arriba se pierde información específica de los tipos, tiene sentido hacer una conversión hacia abajo si se quiere recuperar la información de tipos, es decir, moverse hacia abajo por la jerarquía. La conversión hacia arriba siempre es segura ya que la clase base no puede tener una interfaz mayor que la derivada, sin embargo la conversión hacia abajo no está tan claro. Para garantizarlo, Java, realiza una comprobación de todas las conversiones en tiempo de ejecución. Página 16 J.M. Godoy, F. Gómez y E. Rubio. 2002-2003

8 Interfaces y clases internas 8.1 Interfaces Mediante la palabra clave abstract, que permite crear uno o más métodos sin definición dentro de una clase, se proporciona parte de la interfaz sin proporcionar la implementación, que será creada por sus descendientes. La palabra clave interface produce una clase completamente abstracta, que no tiene ningún tipo de implementación. Una interfaz puede contener campos, pero éstos son implícitamente estáticos y constantes, proporcionando sólo la forma, pero no la implementación. Definición de interfaz Implementación tipo_acceso interfaz nombre { acceso class nombreclase implements nombre tipo_devuelto metodo1 (parámetro); {...... tipo_var1 = valor; Acceso public o no se usa. Las variables son implícitamente final y static Un método privado no es parte de la interfaz 8.2 Herencia múltiple Acceso public o no se usa Los métodos que implemente la interfaz deben ser public Dado que una interfaz no tiene implementación alguna (no tiene espacio de almacenamiento asociado) se pueden combinar varias interfaces. Sólo se puede heredar desde una clase no interfaz, el resto de elementos base deben ser interfaces. Al implementar múltiples interfaces pueden colisionar nombres de métodos. Al mezclar la sobrecarga, la implementación y la superposición, dado que las funciones sobrecargadas no pueden diferir sólo en el valor de retorno, podemos tener errores del compilador. Por ello se debe evitar la utilización de métodos con los mismos nombres en las interfaces. Una interfaz puede extender otra interfaz o bien puede combinar varias interfaces sin ningún tipo de problema. 8.2.1 Constantes de agrupamiento Cualquier campo que se ponga en un interfaz se convierte automáticamente en estático y constante, por ello la interface es una herramienta conveniente para la creación de grupos de valores constantes (tipo enumerado de C). 8.2.2 Inicialización de atributos Los atributos definidos en las interfaces son automáticamente estáticos y constantes. Estos no pueden se constantes blancas, pero pueden inicializarse con expresiones no constantes. Dado que los campos son estáticos, se inicializan cuando se carga la clase por primera vez, lo que ocurre cuando se accede a cualquiera de los atributos por primera vez. Los atributos aunque no son pare del interfaz se almacenan, sin embargo, en el área de almacenamiento estático de esa interfaz. Int num = (int)(mathrandom()*10);// La espresión no es constante pero sería válida en un interfaz 8.2.3 Interfaces anidadas Las interfaces anidadas, y las que no lo son, pueden tener visibilidad pública o amistosa y pueden implementarse como clases públicas, amistosas y privadas. La novedad de las interfaces anidadas es que pueden definirse privadas. 8.3 Clases internas Es posible colocar una definición de clase dentro de otra definición de clase, lo que se denomina clase interna. Esto permite agrupar clases que están relacionadas, además de controlar la visibilidad de una con la otra. Las clases internas tienen su razón de ser al comenzar a hacer una conversión hacia una clase base (en particular a una interfaz). Esto aumenta la ocultación de la implementación. 8.3.1 Ámbitos y clases internas en métodos Las clases internas pueden crearse dentro de un método o incluso en un ámbito arbitrario. Las razones son: Se implementa una interfaz de algún tipo, de forma que se puede crear y devolver una referencia. Para crear una clase que no esté públicamente disponible. J.M. Godoy, F. Gómez y E. Rubio. 2002-2003 página 17

8.3.2 Clases internas anónimas Tienen la forma public class clase1 { public claseinterna nombre() { return new claseinterna() {... ; Cómo se ve, combina la creación del valor de retorno con la definición de la clase que representa ese valor de retorno y es anónima (no tiene nombre). Este código crea un objeto de una clase anónima heredada de claseinterna, a la referencia que devuelve new se le hace una conversión hacia arriba automáticamente para convertirla en una referencia a claseinterna, es decir, se puede considerar una abreviación de: class miclaseinterna implements claseinterna {... return new miclaseinterna(); Si se define una clase anónima interna y se desea utilizar un objeto definido fuera de la clase interna anónima, el compilador exige que el objeto externo sea constante. Una clase interna tiene acceso automático a los miembros de la clase contenedora ya que mantiene una referencia al objeto particular de la clase contenedora que era responsable de crearla. 8.3.3 Clases internas estáticas Si no se necesita una conexión entre el objeto de la clase interna y el objeto de la clase externa, se puede hacer estática la clase interna. Esto es así ya que una clase interna estática quiere decir: 1. No se necesita un objeto de la clase externa para crear un objeto de la clase interna estática. 2. No se puede acceder a un objeto de una clase externa desde un objeto de la clase interna estática. 3. Las clases interna no estáticas no pueden tener campos, datos o clases internas estáticas. Las clases internas hacen referencia a la clase externa mediante: claseexterna.this. 8.3.4 Porque las clases internas? Cada clase interna puede heredar independientemente de una implementación. Por consiguiente, la clase interna no está limitada por el hecho de que la clase externa puede ya estar heredando de una implementación. Es decir, las clases internas permiten heredar de forma efectiva de más de un no interfaz. Además se tienen las siguientes características adicionales: 1. La clase interna tiene múltiples instancias, cada una con sus propia información de estado que es independiente de la información del objeto de la clase externa. 2. En una clase externa se pueden tener varias clases internas, cada una de las cuales implementan la misma interfaz o hereda de la misma clase de distinta forma. 3. El momento de la creación del objeto de la clase interna no está atado a la creación del objeto de la clase externa. 4. No hay relaciones es un dentro de la clase interna. 10 Manejo de errores con excepciones. 10.1 Excepciones básicas. Una condición excepcional es un problema que evita la continuación de un método o del alcance actual, debido a que no se dispone de la información necesaria para tratar el problema en el contexto actual. Todo lo que se puede hacer es salir del contexto actual y relegar el problema a un contexto superior. Esto es lo que ocurre cuando se lanza una excepción: Se crea el objeto excepción en el montículo con new. Se detiene el cauce normal de ejecución y se lanza la referencia al objeto excepción desde el contexto actual. Página 18 J.M. Godoy, F. Gómez y E. Rubio. 2002-2003

El mecanismo de gestión de excepciones busca el lugar apropiado donde continuar ejecutando el programa. Este lugar es el gestor de excepciones, cuyo trabajo es recuperar el problema de forma que el programa pueda o bien intentarlo de nuevo, o bien simplemente continuar. Como cualquier objeto en Java las excepciones tienen su constructor, en el caso de las excepciones estándar dos, uno por defecto y otro que toma un parámetro string de forma que pueda ubicar la información pertinente en la excepción. Tras crear el objeto excepción (new) se le da a throw la referencia resultante. Además se puede lanzar cualquier tipo de objeto Throwable que se desee. Habitualmente se lanzará una clase de excepción diferente para cada tipo de error. La información sobre cada error se representa tanto dentro del objeto excepción como implícitamente en el tipo de objeto excepción elegido. 10.2 Capturar una excepción. Si un método lanza una excepción, debe asumir que esa excepción será capturada y tratada. El método acaba en el momento del lanzamiento, para evitarlo, se puede establecer un bloque excepcional dentro del método para que capture la excepción: try { // Código que puede generar excepciones Toda excepción lanzada debe acabar en el manejador de excepciones, hay uno para cada tipo de excepción que se desee capturar. Los manejadores de excepción siguen inmediatamente al bloque try y se identifican por la palabra clave catch: try { // Código que puede generar excepciones catch (tipo1 id1) { // Manejo excepciones tipo 1 catch (tipo2 id2) { // Manejo excepciones tipo 2 catch (tipo3 id3) { // Manejo excepciones tipo 3... En este código sólo se ejecuta una sentencia catch (no pasa como en switch que le hace falta un break). No se está limitado a las excepciones estándar de Java, se pueden crear las propias excepciones para indicar un error especial, para ello se debe heredar de un tipo de excepción existente. 10.2.1 La especificación de excepciones La especificación de excepciones utiliza la palabra clave Throws seguida de todos los tipos de excepción potenciales. No se puede engañar sobre una especificación de excepciones, si un método provoca excepciones y no las maneja, el compilador lo detecta e indica que o bien se tratan o se colocan en la especificación de excepciones todas las excepciones que el método puede lanzar. Este sistema permite a Java asegurar la corrección de la excepción en tiempo de compilación. 10.2.2 Captura de cualquier excepción. La clase base de todas las excepciones es la clase Exception (si se usa en una lista debe ser la última), para conseguir información se puede llamar a los métodos de su tipo base Throwable. String getmessage() : Mensaje String getlocalizedmessage(): Mensaje ajustado al escenario particular String tostring: Descripción del objeto Throwable void printstacktrace (): Imprime el objeto y la traza de pila de llamadas void printstacktrace(printstream): Apunta a un flujo de datos void printstacktrace (PrintWriter) Throwable fillinstacktrace (): registra la información relativa al estado actual de la pila. J.M. Godoy, F. Gómez y E. Rubio. 2002-2003 página 19

10.2.3 Relanzar una excepción Si se desea se puede volver a lanzar una excepción que se acaba de capturar. Esto hace que la excepción vaya al contexto inmediatamente superior de manejadores de excepciones. Cualquier cláusula catch subsiguiente del mismo bloque try seguirá siendo ignorada. 10.3 Excepciones estándar de Java. Hay un grupo de tipos de excepciones que Java siempre lanza automáticamente, están agrupadas bajo una única clase denominada RuntimeException. Si no se capturan estas excepciones en tiempo de ejecución, se invoca a printstacktrace() para esa excepción y el programa finaliza su ejecución. 10.3.1 Restricciones Al aplicar la herencia a clases que tengan excepciones, el constructor de la clase derivada debe declarar cualquier excepción del constructor de la clase base en su especificación de excepciones. El constructor de la clase derivada no puede capturar las excepciones de la clase base. Justo porque existe una especificación de excepciones en una versión de la clase base de un método, no tiene porque existir en la versión de la clase derivada del mismo, esto es distinto a las normas generales de la herencia. Página 20 J.M. Godoy, F. Gómez y E. Rubio. 2002-2003