PROGRAMACION CON VISUAL AGE FOR JAVA Introducción al Lenguaje Java Sun Microsystems, líder en servidores para Internet, unos de cuyos lemas desde hace mucho tiempo es “the network is the computer” (la red es la computadora, quiere dar a entender que el verdadero ordenador es la red en su conjunto y no cada máquina individual). Es quien ha desarrollado el lenguaje Java, en un intento de resolver simultáneamente todos los problemas que se plantean a los desarrolladores de software por la proliferación de arquitecturas incompatibles, tanto entre las diferentes máquinas como entre los diversos sistemas operativos de ventanas que funcionaban sobre una misma máquina, añadiendo la dificultad de crear aplicaciones distribuidas en una red como Internet. He podido leer más de cinco versiones distintas sobre el origen, concepción y desarrollo de Java, desde la que dice que éste fue un proyecto que rebotó durante mucho tiempo por distintos departamentos de Sun sin que nadie le prestara atención, hasta que finalmente encontró su nicho de mercado en la aldea global que es Internet; hasta la más difundida, que justifica a Java como lenguaje de pequeños electrodomésticos. Hace algunos años, Sun Microsystems decidió intentar introducirse en el mercado de la electrónica de consumo y desarrollar programas para pequeños dispositivos electrónicos. Tras unos comienzos dudosos, Sun decidió crear una filial denominada FirstPerson Inc. Para dar margen de maniobra al equipo responsable del proyecto. El mercado inicialmente previsto para los programas de FirstPerson eran equipos domésticos: microondas, tostadoras y fundamentalmente, televisión interactiva. Este mercado, dada la falta de pericia de los usuarios para el manejo de estos dispositivos, requería unas interfaces mucho más cómodas e intuitivas que los sistemas de ventanas que proliferaban en el momento. Otros requisitos importantes a tener en cuenta era la fiabilidad del código y la facilidad de desarrollo, James Gosgling, el miembro del equipo con más experiencia en lenguajes de programación, decidió que las ventajas aportadas por la eficiencia de C++ no compensaban el gran coste de pruebas y depuración. Gosgling había trabajado en su tiempo libre en un lenguaje de programación que él había llamado Oak, el cual, aún partiendo de la sintaxis de C++, intentaba remediar las deficiencias que iba observando. Los lenguajes al uso, como C o C++, deben ser compilados para un chip, y si se cambia el chip, todo el software debe compilarse de nuevo. Esto encarece mucho los desarrollos y el problema es especialmente acusado en el campo de la electrónica de consumo. La aparición de un chip más barato y generalmente más eficiente conduce inmediatamente a los fabricantes a incluirlo en las nuevas series de sus cadenas de producción, por pequeña que sea la diferencia en precio ya que, multiplicada por la tirada masiva de los aparatos, supone un ahorro considerable. Por tanto, Gosgling decidió mejorar las características de Oak y utilizarlo. El primer proyecto en que se aplicó este lenguaje recibió el nombre de proyecto Green y consistía en un sistema de control completo de los aparatos electrónicos y el entorno de un 1 hogar. Para ello se construyó un ordenador experimental denominado *7 (Star Seven). El sistema presentaba una interfaz basada en la representación de la casa de forma animada y el control se llevaba a cabo mediante una pantalla sensible al tacto. En el sistema aparecía Duke, la actual mascota de Java. Posteriormente se aplicó a otro proyecto denominado VOD (Video on Demand) en el que se empleaba como interfaz para la televisión interactiva. Ninguno de estos proyectos se convirtió nunca en un sistema comercial, pero fueron desarrollados enteramente en un Java primitivo y fueron como su bautismo de fuego. Una vez que en Sun se dieron cuenta que a corto plazo la televisión interactiva no iba a ser un gran éxito, urgieron a FirstPerson a desarrollar con rapidez nuevas estrategias que produjeran beneficio. No lo consiguieron y FirstPerson cerró en la primavera en la primavera de 1994. Pese a lo que parecía ya un olvido definitivo, Bill Joy, cofundador de Sun y uno de los desarrolladores principales del Unix de Berkeley, juzgó que Internet podría llegar a ser el campo de juego adecuado para disputar a Microsoft su primacía casi absoluta en el terreno del software y vio en Oak el instrumento idóneo para llevar a cabo estos planes. Tras un cambio de nombre y modificaciones de diseño, el lenguaje Java fue presentado en sociedad en Agosto de 1995. Lo mejor será hacer caso omiso de las historias que pretenden dar carta de naturaleza a la clarividencia industrial de sus protagonistas; porque la cuestión es si independientemente de su origen y entorno comercial, Java ofrece soluciones a nuestras expectativas. Porque vamos a desechar la penicilina aunque haya sido su origen fruto de la casualidad. Características de Java Es simple. Es Orientado a Objetos. Es distribuido. Es robusto. Es de arquitectura neutral. Es seguro. Es portable. Es interpretado. Es multithreaded. Es dinámico. Es Simple: Java reduce en un 50% los errores más comunes de programación: - No presenta aritmética de punteros. - No existen referencias. - No se definen tipos (struct, unio, typedef) - No se usan macros. - No hay necesidad de liberar memoria. Es Orientado a Objetos Java implementa la tecnología de OOP de C++. - Encapsulación. - Herencia. - Polimorfismo. 2 Es Distribuido - Java tiene capacidades de interconexión TCP/IP. Tiene librerías para acceder e interactuar con los protocolos http y ftp. Es Robusto - Comprobación de punteros. Comprobación de límites de arrays. Excepciones. Verificación de byte-codes. Es de Arquitectura Neutral Para establecer como parte integral de la red, el compilador Java compila su código a un fichero objeto de formato independiente de la arquitectura de la máquina en que se ejecutará. Cualquier máquina que tenga el sistema de ejecución (run-time) puede ejecutar ese código objeto, sin importar en modo alguno la máquina en que ha sido generado. Actualmente existen sistemas run-time para Solares, SunOs, Windows 95, 98, Windows NT, Linux, Iris, Aix, Mac, Apple y probablemente haya grupos de desarrollo trabajando en el porting a otras plataformas. El código fuente Java se “compila” a un código de bytes de alto nivel independiente de la máquina. Este código (byte-codes) está diseñado para ejecutarse en una máquina hipotética que es implementada por un sistema run-time, que sí es dependiente de la máquina. Además, habrá APIs de Java que también entren en contacto directo con el hardware y serán dependientes de la máquina, como ejemplo de este tipo de APIs podemos citar: - Java 2D: gráficos 2D y manipulación de imágenes. - Java Media Framework: Elementos críticos en el tiempo: audio, video… - Java Animation: Animación de objetos 2D - Java Telephony: Integración con telefonía. - Java Share: Interacción entre aplicaciones multiusuario. - Java 3D: Gráficos 3D y su manipulación. Es Seguro Seguridad por parte del lenguaje: - No se hace uso de punteros. - Casting sólo para datos numéricos. Seguridad por parte del compilador: - Se genera archivo de byte-codes. - Tipos de parámetros y argumentos conocidos. - Acceso conocido: public, private o protected. Es Portable - Tiene arquitectura independiente del sistema. Usa enteros reales de 32 bits en complemento a 2. Tiene un sistema abstracto de ventanas conocido como AWT (Abstract Window Toolkit). Es Interpretado - El archivo bytecodes es muy cercano a el lenguaje de máquina. Pero necesita de un run-time para ser ejecutado. 3 - Existe un run-time específico para cada sistema operativo. Es Multithreaded - Java permite muchas actividades simultáneas en un programa. Los Threads son pequeños procesos o piezas independientes de un proceso que se ejecutan independientemente a al aplicación. Es Dinámico Java se beneficia todo lo posible de la tecnología orientada a objetos. Java no intenta conectar todos los módulos que comprenden una aplicación hasta el tiempo de ejecución. Las librerías nuevas o actualizadas no paralizarán las aplicaciones actuales (siempre que mantengan el API anterior). Java también simplifica el uso de protocolos nuevos o actualizados. Si su sistema ejecuta una aplicación Java sobre la red y encuentra una pieza de la aplicación que no sabe manejar, tal como se ha explicado en párrafos anteriores, Java es capaz de traer automáticamente cualquiera de esas piezas que el sistema necesita para funcionar. Java, para evitar que los módulos de byte-codes o los objetos o nuevas clases, haya que estar trayéndolos de la red cada vez que se necesiten, implementa las opciones de persistencia, para que no se eliminen cuando se la limpie la caché de la máquina. 4 CONCEPTOS Y SINTAXIS DE JAVA Definición de una Clase La primera línea en negrilla en el listado siguiente comienza un bloque de definición de clase /** * The HelloWorldApp class implements an application that * simply displays "Hello World!" to the standard output. */ class HelloWorldApp { public static void main(String[] args) { System.out.println("Hello World!"); //Display the string. } } Una clase--el bloque de construcción básico de un lenguaje orientado a objetos tal como Java- es una plantilla que describe los datos y el comportamiento asociados con las instancias de esa clase. Cuando se instancia una clase se crea un objeto que se parezca y luzca como otras instancias de la misma clase. Los datos asociados con una claseo un objeto son almacenados en variables; el comportamiento asociado con una clase u objeto es implemtado mediante métodos. Los métodos son similares a las funciones o procedimientos en lenguajes procedurales como C. La receta de estofado de cordero de Julia es un ejemplo del mundo real de lo que es una clase. Su interpretación del estofado de cordero es una instancia de la receta, y la mía es otra muy distinta. (mientras ambos estofados de cordero podrian parecer lo mismo, me imagino que ellos "huelen y saben" diferente.) Un ejemplo más tradicional del mundo de la programación es una clase que representa un rectángulo. Esta clase podría contener variables que representan los puntos de originen del rectángulo, su ancho y altura. La clase podría también contener un método que calcule el área del rectángulo. Una instancia del rectángulo podría contener la información para un rectángulo específico, como las dimensiones del piso del salón de clases, o las dimensiones de esta página. En el lenguaje Java, la forma más simple de la definición de una clase es: class Nombre { ... } La palabra clave class empieza la definición de la clase llamada nombre. Las variables y métodos de la clase se incluyen dentro de llaves que empiezan justo al final de la definición de la clase. La aplicación "Hello World" no tiene variables y tiene un método simple llamado main. El Método Main La primera línea en negrilla en el listado siguiente comienza la definición del método main: /** * The HelloWorldApp class implements an application that * simply displays "Hello World!" to the standard output. */ 5 class HelloWorldApp { public static void main(String[] args) { System.out.println("Hello World!"); //Display the string. } } Toda aplicación Java debe contener un método main cuya estructura es como sigue: public static void main(String[] args) Esta estructura presenta los siguientes modificadores: public indica que el método main puede ser llamado por cualquier objeto. static indica que el método main es un método de clase. void indica que el método main no retorna valor. Cómo se invoca al Método main Cuando el interprete de Java ejecuta una aplicación (siendo invocado por encima de la clase de control de la aplicación), empieza invocando al método main de la clase. El método main a su vez llama a todos los métodos requeridos para ejecutar una aplicación. Si se trata de invocar el intérprete de Java en una clase que no contiene el método main, el intérprete se niega a ejecutar el programa y muestra un mensaje similar al siguiente: In class NoMain: void main(String argv[]) is not defined Argumentos del método Main Como se puede apreciar en el siguiente fragmento de código, el método main acepta un único argumento: un arreglo de elementos del tipo String. public static void main(String[] args) Este array es el mecanismo por el cual el sistema en tiempo de ejecución pasa información a una aplicación. Cada String en el arreglo se llama argumento de línea de comando. Los argumentos de línea de comando permiten interferir la operación de una aplicación sin necesidad de recompilar. Por ejemplo, un programa de ordenamiento podría permitir al usuario especificar que la data a ordenarse en orden descendiente con la siguiente línea de comando: -descending Uso de Clases y Objetos La aplicación "Hello World" es el programa de Java más simple que realmente ‘haga algo’. Ya que es un programa tan simple, no necesita definir otra clase más que HelloWorldApp. Sin embargo, la mayoría de programas que escribirá serán más complejos y será necesario escribir otras clases y código de Java de soporte. La "Hello World" application does use another class--the System class--that is part of the API (application programming interface) provided with the Java environment. The System class provides system-independent access to system-dependent functionality. 6 El código en negrita en el siguiente listado ilustra el empleo de una variable de clase de la clase System, y un método instanciado. /** * The HelloWorldApp class implements an application that * simply displays "Hello World!" to the standard output. */ class HelloWorldApp { public static void main(String[] args) { System.out.println("Hello World!"); //Display the string. } } La Anatomía de un Applet de Java Veamos como funciona un applet de Java. import java.applet.Applet; import java.awt.Graphics; public class HelloWorld extends Applet { public void paint(Graphics g) { g.drawString("Hello world!", 50, 25); } } Definición de una Subclase de Applet La primera línea en negrita del siguiente listado inicia un bloque que define la clase HelloWorld. import java.applet.Applet; import java.awt.Graphics; public class HelloWorld extends Applet { public void paint(Graphics g) { g.drawString("Hello world!", 50, 25); } } La palabra clave extends indica que HelloWorld es una subclase de la clase Applet. Desde la clase Applet, los applets heredan una gran cantidad de funcionalidad. Quizás lo más importante es la habilidad de responder a los requerimientos del browser. Por ejemplo, cuando un browser que soporta Java carga una página que contiene un applet, el browser envía un requerimiento al applet, le indica al applet que se inicialice y que empiece a ejecutarse. Un applet no está restringido para definir solo una clase. Detrás de la subclase necesaria del Applet, un applet puede definir otras clases propias. Cuando un applet intenta hacer uso de una clase, la aplicación que está ejecutando el applet primero busca la clase en forma local. Si la clase no se encuentra disponible localmente, se descarga desde la ubicación desde donde la subclase del Applet se originó. 7 Implementación de los Métodos de un Applet import java.applet.Applet; import java.awt.Graphics; public class HelloWorld extends Applet { public void paint(Graphics g) { g.drawString("Hello world!", 50, 25); } } Todo applet debe implementar uno o más de los métodos init, start, o paint. Lección: Conceptos de Programación Orientada a Objetos ¿Qué es un Objeto? Los objetos son clave para entender la tecnología orientada a objetos. Si ves a tu alrededor verás varios ejemplos de objetos del mundo real: tu perro, tu escritorio, tu juego de televisión, tu bicicleta, etc... Todos estos objetos comparten dos características: Todos ellos presentan un estado y comportamiento. Por ejemplo, los perros presentan un estado (nombre, color, raza, hambre) y comportamiento (ladrar, traer cosas, y perseguir su cola). La bicicletas tienen estado (velocidad actual, cadena del pedal actual, dos ruedas, número de cambios) y comportamiento (frenar, acelerar, bajar la velocidad, hacer cambios). Los objetos de software se modelan como objetos del mundo real en el sentido de que también presentan estado y comportamiento. Un objeto de software mantiene su estado en una o más variables. una variable es un elemento de dato llamado por un identificador. Un objeto de software implementa su comportamiento a través de los métodos. Un método es una función, una sub-rutina, asociada con un objeto. Definición: Un objeto es un ‘manojo’ de software de variables y de métodos relacionados. Se puede representar los objetos del mundo real mediante objetos de software. Se puede representar perros del mundo real como objetos de software en programas de animación o una bicicleta del mundo real como un objeto de software en un programa que controle una bicicleta de ejercicios eléctricos. También se puede usar objetos de software para modelar conceptos abstractos. Por ejemplo, un evento es un objeto usado en sistemas de ventanas GUI para representar la acción de un usuario presionando el botón del mouse o una tecla. La siguiente ilustración es una representación visual común de un objeto de software: Todo aquello que el objeto de software conozca (estado) y pueda hacer (comportamiento) es expresado mediante variables y métodos dentro de un objeto. Un objeto de software que 8 modele una bicicleta del mundo real podrían tener variables que indiquen el estado actual de la bicicleta: su velocidad es 10 m/h, su ritmo de pedaleo es 90 r/m, y su cambio actual es el quinto. Estas variables formalmente se conocen como variables de instancia porque contienen el estado para un objeto de la bicicleta en particular, y en la terminología orientada a objetos, un objeto en particular se llama instancia. La siguiente figura ilustra una bicicleta como un objeto de software. Además de sus variables, la bicicleta de software podría también tener métodos de frenado, cambiar el ritmo del pedal, y mover los cambios. (La bicicleta no tendría un método para cambiar la velocidad de la bicicleta, ya que la velocidad de la bicicleta es tan solo un efecto secundario del manejo de los cambios, que tan rápido pedalee el ciclista, si están puestos los frenos, y que tan inclinada esté la pista.) Estos métodos se conocen formalmente como métodos de instancia porque inspeccionan o cambian el estado de una instancia de bicicleta en particular. Los diagramas de objetos muestran que las variables de objetos conforman el centro, o el núcleo, del objeto. Los métodos se encuentran alrededor y ocultan el núcleo del objeto de otros objetos del programa. Empaquetar las variables del objeto dentro de la custodia protectiva de sus métodos se llama encapsulación. Esta imagen conceptual de un objeto-un núcleo de variables empaquetadas dentro de una membrana protectora de métodos es una representación ideal de un objeto y es el ideal que los diseñadores de sistemas orientados a objetos buscan. Sin embargo, esto no es todo. Frecuentemente, por razones prácticas, un objeto podría desear exponer alguna de sus variables u ocultar alguno de sus métodos. En el lenguaje de programación Java, un objeto puede especificar uno de los cuatro niveles de acceso para cada una de sus variables y métodos. El nivel de acceso determina que otros objetos y clases pueden acceder a determinada variable o método. Las variables y métodos relacionados a la encapsulación en un software ordenado es una idea poderosa que provee dos beneficios principales a los desarrolladores de software: •Modularidad: El código fuente para un objeto se puede escribir y mantener independientemente del código fuente para otros objetos. Además, un objeto puede ser pasado fácilmente alrededor del sistema. Le puedes dar tu bicicleta a otra persona, y ésta aún funcionará. •Ocultar Información: Un objeto tiene una interfase pública que otros objetos pueden usar para comunicarse. El objeto puede mantener información privada y los métodos se pueden cambiar en cualquier momento sin afectar los otros objetos que dependen de él. No necesitas comprender el mecanismo del los engranajes de tu bicicleta para usarla. Qué es un Mensaje? Un solo objeto generalmente no es muy útil. Por el contrario, un objeto usualmente aparece como componente de un programa más grande o una aplicación que contiene muchos otros objetos. Mediante la interacción de estos objetos, los programadores alcanzan una gran funcionalidad y un comportamiento más complejo. Tú bicicleta estacionada en un garaje es tan solo una aleación de titanio y caucho; por si misma, la bicicleta es incapaz de realizar una actividad. La bicicleta es útil solo cuando otro objeto (tú) interactúa con ésta (pedal). 9 Los objetos de software interactúan y se comunican entre ellos enviándose mensajes el uno al otro. Cuando un objeto A desea que un objeto B ejecute uno de los métodos de B, el objeto A le envía un mensaje a B. Algunas veces, el objeto receptor necesita más información de tal forma que sepa qué hacer exactamente; por ejemplo, cuando deseas mover los cambios en tu bicicleta, necesitas indicar que cambio deseas mover. Esta información es pasada con el mensaje en forma de parámetro. La siguiente figura muestra los tres componentes que comprenden un mensaje: 1. El objeto al cual se dirige el mensaje (YourBicycle) (TuBicicleta) 2. El nombre del método a ejecutarse (changeGears) (moverCambios) 3. Cualquier parámetro que necesite el método (lowerGear) (cambioMenor) Estos tres componentes son información suficiente para el objeto receptor para ejecutar el método deseado. Ninguna otra información o contexto es requerido. Los mensajes proveen dos beneficios importantes. •El comportamiento de un objeto se expresa a través de sus métodos, de tal forma (fuera del acceso a variables directamente) el pasado de mensajes soporta todas las interacciones posibles entre objetos. •Los objetos no necesitan estar en el mismo proceso o inclusive en la misma máquina para enviar y recibir mensajes de ida y vuelta entre ellos. ¿Qué es una Clase? En el mundo real, frecuentemente te encontrarás objetos del mismo tipo. Por ejemplo, tu bicicleta es tan una sólo una de las tantas bicicletas que existen en el mundo. Usando la tecnología orientada a objetos, decimos que su bicicleta es una instancia de una clase de objetos conocida como bicicletas. Las bicicletas tiene estado (cambio actual, ritmo actual, dos ruedas) y comportamiento (mover los cambios, frenar) en común. Sin embargo, cada estado de la bicicleta es independiente y puede ser diferente de otras bicicletas. Cuando se construyen las bicicletas, los fabricantes aprovechan el hecho de que las bicicletas compartan características, al construir varias bicicletas a partir del mismo diseño. Sería muy ineficiente producir un nuevo diseño para cada bicicleta producida individualmente. En el software orientado a objetos, también es posible tener objetos del mismo tipo que comparten características: rectángulos, empleados registros, video clips, etc. Como los productores de bicicleta, usted puede aprovechar el hecho de que los objetos del mismo tipo 10 son similares y puede crear plantillas para dichos objetos. Una plantilla de software para objetos se denomina clase. Definición: Una clase es una plantilla, o prototipo, que define las variables y métodos comunes a todos los objetos de un cierto tipo. La clase para el ejemplo de la bicicleta declara las variables de clase necesarias para contener la velocidad actual, el ritmo actual, etc. Para cada objeto de bicicleta. La clase también declara y provee implementaciones para los métodos de instancia que permiten al ciclista mover los cambios, frenar, y cambiar el ritmo del pedal, como se muestra en la siguiente figura. currentSpeed=velocidadActual currentCadence=ritmoActualGear Implementation=implementación de cambio currentGear=cambio actual Después de creada la clase bicicleta, se puede crear cualquier cantidad de objetos bicicleta a partir de la clase. Cuando crea una instancia de la clase, el sistema asigna el espacio en memoria para el objeto y todas sus variables de instancia. Cada instancia obtiene su propia copia de todas las variables de instancia definidas en la clase. Mi bicicleta Tu bicicleta Además de las variables de instancia, las clases pueden definir variables de clase. Una variable de clase contiene información que es compartida por todas las instancias de clase. Por ejemplo, supongamos que todas las bicicletas tienen su número de cambios. En este caso, la defición de una variable de instancia para contener el número de cambios es ineficiente; cada instancia tiene su propia instancia de variable, pero el valor será el mismo para cada instancia. En tales situaciones, se define una variable de clase que contiene el número de cambios. Todas las instancias comparten esta variable. Si un objeto cambia la variable, los cambios repercuten en todos los objetos del mismo tipo. Una clase también puede declarar métodos de clases. Se puede invocar un método de clase directamente de la clase, por el contrario se debe invocar métodos de instancia en una instancia en particular. 11 Clase Instancia de una Clase ¿Qué es una interfase? En castellano, una interfaz es un dispositivo de un sistema el cual es usado por entidades no relacionadas para poder interactuar entre ellas. De acuerdo a esta definición, un control remoto es una interfaz entre una persona y un televisor, el idioma castellano es una interfaz entre dos personas, y el protocolo de comportamiento impuesto a los militares es la interfaz entre las personas del mismo rango. En el lenguaje de programación Java, una interfaz es un dispositivo que los objetos sin relación usan para interactuar entre ellos. El concepto de interfaz en Java es probablemente más parecido a la definición de un protocolo (un acuerdo de comportamiento). En realidad, otros lenguajes orientados a objetos manejan la funcionalidad de las interfaces, pero llaman a sus interfaces protocolos. La clase bicicleta y su jerarquía de clase definen lo que puede o no puede hacer una bicicleta en términos de su "biciclitedad." Pero las bicicletas interactúan con el mundo en otros términos. Por ejemplo, una bicicleta en un almacén podría ser administrada por un programa de inventarios. A un programa de inventarios no le importa que tipo de elementos administre mientras cada elemento le provea de cierta información, como el precio y el número de serie. En vez de forzar relaciones de clases entre elementos sin relación, el programa de inventario establece un protocolo de comunicación. Este protocolo se presenta en la forma de un conjunto de métodos definidos y constantes contenidas dentro de la interfaz. La interfaz de inventario define, pero no implementa, métodos que establecen y obtienen el precio para el consumidor, asignar un número de serie, etc... Para que la bicicleta funcione en el programa de inventario, debe estar de acuerdo a un protocolo mediante la implementación de una interfaz. Cuando una clase implementa una interfaz, la clase acepta implementar todos los métodos definidos en la interfaz. Por lo tanto, la clase bicicleta proveerá las implementaciones para los métodos que establecen y obtienen el precio para el consumidor, asignar un código de barra, etc... Una interfaz se usa para definir un protocolo de comportamiento que puede ser implementado por cualquier clase en cualquier parte de la jerarquía de la clase. Las Interfaces son útiles porque permite: Capturar las similitudes entre las clases no relacionadas sin forzar de forma artificial una relación entre clases. Declarar métodos que una o más clases suponen implementar. Revelar una interfaz de programación de un objeto sin revelar su clase. Variables Un objeto almacena su estado en variables. 12 Definición: Una variable es un elemento de datos llamados por un identificador. public class MaxVariablesDemo { public static void main(String args[]) { // enteros byte largestByte = short largestShort int largestInteger long largestLong = Byte.MAX_VALUE; = Short.MAX_VALUE; = Integer.MAX_VALUE; Long.MAX_VALUE; // números reales float largestFloat = Float.MAX_VALUE; double largestDouble = Double.MAX_VALUE; // other primitive types char aChar = 'S'; boolean aBoolean = true; // mostrar en consola System.out.println("Máximo " + largestByte); System.out.println("Máximo largestShort); System.out.println("Máximo largestInteger); System.out.println("Máximo largestLong); valor de byte es 127 valor de short es " + valor de integer es " + valor de long es " + System.out.println("Máximo valor de float es " + largestFloat); System.out.println("Máximo valor de double es " + largestDouble); if (Character.isUpperCase(aChar)) { System.out.println("El carácter " + aChar + " está en mayúscula."); } else { System.out.println("El carácter " + aChar + " está en minúscula."); } System.out.println("El valor de aBoolean es " + aBoolean); } } El resultado de este programa es: Máximo Máximo Máximo Máximo Máximo Máximo valor valor valor valor valor valor de de de de de de byte es 127 short es 32767 integer es 2147483647 long es 9223372036854775807 float es 3.40282e+38 double es 1.79769e+308 13 El caracter S está en minúscula. El valor de aBoolean es true Tipos de Datos Cada variable debe tener un tipo de datos. Un tipo de datos de variable determina los valores que la variable puede contener y las operaciones que pueden ser ejecutados en ellos. Por ejemplo, en el programa MaxVariablesDemo, la declaración int largestInteger declara que largestInteger tiene un tipo de dato entero (int). Los enteros pueden contener solo valores integrales (positivos y negativos). Se puede ejecutar operaciones aritméticas, como suma, en variables enteras. El lenguaje de programación Java presenta dos categorías de tipos de datos: primitiva y referencial. Una variable del tipo primitivo contiene un único valor del tamaño y formato apropiado para su tipo: un número, un caracter, o un valor boolean. Por ejemplo, un valor entero es de 32 bits de datos en un formato conocido como complemento dual, el valor de un char es de 16 bits de datos en formato de caracter Unicode, etc... nombreDeVariable La siguiente tabla lista, por tipo de dato, todos los tipos de datos primitivos soportados en Java, sus tamaños y formatos, y una descripción breve de cada uno. El programa MaxVariablesDemo declara una variable de cada tipo primitivo. Tipos de Datos Primitivos Tipo de Dato Descripción Tamaño/Formato (enteros) byte Entero de longitud Byte Complemento dual de 8-bit short Entero Short Complemento dual de 16-bit int Entero Complemento dual de 32-bit long Entero Long Complemento dual de 64-bit (números reales) float Punto flotante de precisión Single 32-bit IEEE 754 double Punto flotante de precisión Double 64-bit IEEE 754 (other types) char Un caracter simple boolean Un valor boolean (true o false) true o false Caracter de 16-bit Unicode Puede poner un valor literal primitivo directamente en su código. Por ejemplo, si necesita asignar el valor 4 a una variable entera puede escribir lo siguiente: int anInt = 4; 14 El dígito 4 is un valor entero literal. Ejemplos de Valores Literales y sus Tipos de Datos Literal Tipo de Dato 178 Int 8864L Long 37.266 Double 37.266D Double 87.363F Float 26.77e3 Double 'c' Char true Boolean false Boolean Generalmente, una serie de dígitos sin punto decimal se escribe como entero. Se puede especificar un entero long colocando una 'L' o 'l' después del número. La 'L' se usa más para evitar confundirse con '1'. Una serie de dígitos con puntos decimales es de tipo double. Se puede especificar un float colocando una'f' o 'F' después del número. Un valor de caracter literal es un único caracter Unicode que va entre comillas. Los dos literales Boolean son simplemente true y false. Arrays, clases, e interfaces son de tipo referencial. El valor de una variable de tipo referencial, a diferencia de un tipo de dato primitivo, es una referencia a (una dirección de) un valor o conjunto de valores representados por la variable. Una referencia se llama puntero, o una dirección en memoria en otros lenguajes. El lenguaje de programación Java no soporta el uso explícito de direcciones como otros lenguajes. En vez de ello se usa el nombre de la variable. un objeto o un arreglo Variables Finales Se puede declarar una variable en cualquier alcance como final. El valor de una variable de tipo final no se puede cambiar después de que ha sido inicializada. Tales variables son como las constantes en otros lenguajes de programación. 15 Para declarar una variable final, se emplea la palabra clave final en la declaración de la variable antes del tipo: final int aFinalVar = 0; ó final int blankfinal; . . . blankfinal = 0; Una variable local que ha sido declarada pero no ha sido inicializada es llamada final en blanco. Una vez que una variable final ha sido inicializada, en un intento posterior no se le puede asignar un valor a blankfinal ya que resultaría en un error al compilar el programa. Resumen de Operadores Resumen de Operadores Aritméticos El operador + se usa para concatenar cadenas, los demás operadores sólo pueden ser usados con datos numéricos. Operator Use Description + op1 + op2 Suma op1 y op2 - op1 - op2 Resta op2 de op1 * op1 * op2 Multiplica op1 por op2 / op1 / op2 Divide op1 por op2 % op1 % op2 Calcula el residuo de dividir op1 entre op2 Los siguientes operadores incrementan o decrementan un número en uno. Operador Uso Descripción ++ op++ Incrementa op en 1; evalúa el valor de op antes de incrementarse ++ ++op Incrementa op en 1; evalúa el valor de op después de incrementarse -- op-- Decrementa op en 1; evalúa el valor de op antes de decrementarse -- --op Decrementa op en 1; evalúa el valor de op después de decrementarse Aquí otros operadores aritméticos del lenguaje de programación Java. Operador Uso + Descripción +op Promueve op a int si es un byte, short, o char 16 - -op Niega aritméticamente op Resumen de Operadores Relationales y Conditional Use estos operadores relacionales para determinar la relación entre dos valores. Operador Retorna true si Uso > op1 > op2 >= op1 >= op2 op1 es mayor o igual que op2 < op1 < op2 <= op1 <= op2 op1 es menor o igual que op2 == op1 == op2 op1 y op2 son iguales != op1 != op2 op1 y op2 no son iguales op1 es mayor que op2 op1 es menor que op2 Operadores para formar decisiones múltiples. Operador Retorna true si Uso && op1 && op2 op1 y op2 son true, evalúa condicionalmente op2 || op1 || op2 o op1 o op2 es true, evalúa condicionalmente op2 ! ! op op es false & op1 & op2 op1 y op2 son true, siempre evalúa op1 y op2 | op1 | op2 o op1 o op2 es true, siempre evalúa op1 y op2 ^ op1 ^ op2 si op1 y op2 son diferentes--si uno o el otro de los operandos es true pero no ambos. Resumen de Operadores Lógicos Estos operadores realizan funciones lógicas en sus operandos. Operador Uso Operación & op1 & op2 bitwise and | op1 | op2 bitwise or ^ op1 ^ op2 bitwise xor 17 ~ ~op2 bitwise complemento Resumen de Operadores de Asignación Un operador de asignación básico se ve como sigue y asigna el valor de op2 a op1. op1 = op2; Además de la operación de asignación básica, el lenguaje de programación Java define estos operadores cortos de asignación que ejecutan una operación y una asignación usando un operador. Operador Uso Equivalente a += op1 += op2 op1 = op1 + op2 -= op1 -= op2 op1 = op1 – op2 *= op1 *= op2 op1 = op1 * op2 /= op1 /= op2 op1 = op1 / op2 %= op1 %= op2 op1 = op1 % op2 &= op1 &= op2 op1 = op1 & op2 |= op1 |= op2 op1 = op1 | op2 ^= op1 ^= op2 op1 = op1 ^ op2 <<= op1 <<= op2 op1 = op1 << op2 >>= op1 >>= op2 op1 = op1 >> op2 >>>= op1 >>>= op2 op1 = op1 >>> op2 Resumen de Otros Operadores El lenguaje de programación Java también soporta los siguientes operadores. Operador Uso Descripción ?: op1 ? op2 : op3 Si op1 es true, retorna op2. De lo contrario, returna op3. [] Tipo [] Declara un array de longitud desconocida, que contiene elementos tipo. [] Tipo[ op1 ] Crea un array con op1 elementos. Debe ser usado con el operador new. [] op1[ op2 ] Accede el elemento en el índice op2 dentro del array op1. Los Índices empiezan en 0 y se extienden a través de la longitud del array menos uno. 18 . op1.op2 Es una referencia de op2 miembro de op1. () op1(params) Declara o llama el método llamado op1 con los parámetros especificados. La lista de parámetros puede estar vacía. La lista es separada mediante comas. (tipo) (tipo) op1 Convierte op1 a un determinado tipo. Se lanzará una excepción si el tipo de op1 no es compatible con tipo. new new op1 Crea un nuevo objeto o array. op1 es ya sea una llamada al constructor, o una especificación de array. op1 instanceof instanceof op2 Retorna true si op1 es una instancia de op2 Expresiones, Sentencias, y Bloques La siguiente tabla muestra la precedencia asignada a los operadores. Los operadores en esta tabla son listados en orden de precedencia: mientras más arriba aparezca el operador en la tabla, más alta es su precedencia. Los operadores con precedencia más alta son evaluados antes que los operadores con una precedencia relativamente más alta. Los operadores en la misma línea tienen igual precedencia. postfix operators [] . (params) expr++ expr-- unary operators ++expr –expr +expr -expr ~ ! Creation or cast new (type)expr multiplicative * / % Additive + - Shift << >> >>> relational < > <= >= instanceof Equality == != bitwise AND & bitwise exclusive OR ^ bitwise inclusive OR | logical AND && logical OR || conditional ? : assignment = += -= *= /= %= &= ^= |= <<= >>= >>>= 19 Cuando los operadores de igual precedencia aparecen en la misma expresión, una regla debe evaluar cual se debe evaluar primero. Todos los operadores binary a excepción de los operadores de asignación se evalúan de izquierda a derecha. Los operadores de asignación son evaluados de derecha a izquierda. Sentencias Las sentencias son más o menos equivalentes a las oraciones en los lenguajes naturales. Una sentencia conforma una unidad completa de ejecución. Los siguientes tipos de expresiones pueden ser convertidas en sentencias finalizando la expresión con un punto y coma (;): Expresiones de Asignación Cualquier uso de ++ o -Llamada a métodos Expresiones de creación de objetos Estos tipos de sentencias son llamadas sentencias de expresión. Aquí hay algunos ejemplos de sentencias de expresión: aValue = 8933.234; //sentencia de asignación aValue++; //sentencia de incremento System.out.println(aValue); //sentencia de llamada a método Integer integerObject = new Integer(4); //sentencia de creación de //objetos Además de estos tipos de sentencia de expresión, hay otros dos tipos de sentencias. Una sentencia de declaración declara una variable. Ya hemos visto varios ejemplos de sentencias de declaración. double aValue = 8933.234; declaración // sentencia de Una sentencia de flujo de control regula el orden en que las sentencias se ejecutan. la sentencia for, loop e if son ejemplos de sentencias de flujo de control. Resumen de Sentencias de flujo de control Bucles La sentencia while se utiliza para buscar en forma contínua dentro de un bloque de sentencias mientras una expresión boolean sea verdadera. The expression al comienzo del bucle. while (boolean expresión) { statement(s) } La sentencia do-while utiliza para buscar en forma contínua dentro de un bloque de sentencias mientras una expresión boolean sea verdadera. Condicionales 20 Para controlar el flujo de un programa, el lenguaje de programación Java presenta tres constructores, una sentencia if-else flexible, una sentencia switch, sentencia de manejo de excepciones, y sentencias de agrupamiento. Utilice switch para tomar decisiones múltiples basadas en un único valor entero. Lo siguiente es la sentencia if más simple cuyo único bloque de sentencia se ejecuta si la expresión boolean es true: if (expresión boolean) { sentencia (s) } if-else: if (expresión boolean) { sentencia (s) } else { sentencia (s) } sentencias if compuestas: if (boolean expression) { sentencia (s) } else if (boolean expression) { sentencia (s) } else if (boolean expression) { sentencia (s) } else { sentencia (s) } La sentencia switch evalúa una expresión entera y ejecuta la sentencia case apropiada. switch (expresión entera) { case expresión entera: sentencia(s) break; ... default: sentencia (s) break; } Sentencias de Manipulación de Excepciones Se utiliza las sentencias try, catch, y finally para manipular las excepciones. try { sentencia (s) } catch (nombre tipoexcepción) { sentencia (s) } catch (nombre tipoexcepción) { sentencia (s) } finally { sentencia (s) 21 } Sentencias Árboles Algunas sentencias árboles cambian el flujo de control en un programa a una sentencia etiquetada. Se etiqueta una sentencia ubicando un identificador legal (la etiqueta) seguida de dos puntos (:) antes de la sentencia: nombreSentencia: algunaSentenciaJava; Utilice la forma sin etiqueta de la sentencia break para finalizar la sentencia switch, for, while, o do-while más al interior. break; Utilice la forma etiquetada de la sentencia break para terminar una sentencia switch, for, while, or do-while que se encuentra al exterior con la etiqueta dada: break label; Una sentencia continue finaliza la iteración actual del bucle más interno y evalúa la expresión boolean que controla el bucle. continue; Utilice la forma etiquetada de la sentencia continue finaliza la iteración actual del bucle con la etiqueta dada: continue label; Utilice return para finalizar el método actual. return; Puede retornar un valor al solicitante de un método, empleando la forma de return que obtiene un valor. return value; Declaración de Clases El lado izquierdo del siguiente diagrama muestra los posibles componentes de una declaración de clase en el orden en que deberían o deben aparecer en su declaración de clase. El lado derecho describe sus propósitos. Los componentes requeridos son las palabras claves de la clase y el nombre de la clase y se muestran en negrita. Todos los otros componentes son iguales, y cada uno aparece en una línea por si mismos (por lo tanto "extends Super" es un único componente). Las palabras en cursiva indican un identificador como el nombre de una clase o interfase. Si no se declara explícitamente los elementos opcionales, el compilador Java asume por defecto una subclase de Objeto: no público, no abstracto, no final y que no implementa interfaces. public abstract final class NombreDeClase extends Super implements Clase públicamente accesible. Clase que no puede ser inicializada. Clase que no puede generar subclases. Nombre de la Clase. Superclase de la clase. Interfaces implementadas por la clase 22 Interfaces { CuerpoDeLaClase } La siguiente lista provee detalles sobre cada componente de declaración de clase. public Por defecto, una clase se puede usar sólo por otras clases en el mismo paquete. El modificador public declara que la clase se puede usar por cualquier otra clase sin importer en que paquete se encuentre. abstract Declara que la clase no se puede inicializar. final Declara que la clase no puede generar subclases. class NombreDeLaClase La palabra clave class le indica al compilador que es una declaración de clase y que el nombre de la clase NombreDeLaClase. extends Super La cláusula extends identifica Super como la superclase de la clase, por lo tanto se inserta la clase dentro de la jerarquía de la clase. implements Interfaces Para declarar que una clase implementa una o más interfaces, se utiliza la palabra clave implements seguida de una coma para enumerar las otras interfaces implementadas por la clase. Se puede especificar que pueden crear otras instancias de objetos de su clase utilizando un especificador de acceso en la declaración del constructor: private Ninguna otra clase puede instanciar su clase. Su clase podría contener métodos de clase públicos (algunas veces llamados métodos de construcción), y tales métodos pueden construir un objeto y devolverlo, pero ninguna otra clase lo puede hacer. protected Sólo las subclases de la clase y las clases en el mismo paquete pueden crear instancias de ésta. public Cualquier clase puede crear una instancia de su clase. acceso a paquetes sin especificador Sólo las clases dentro del mismo paquete de su clase pueden construir una instancia de ésta. Los constructores proveen una forma de iniciar objetos. Declaración de las Variables Miembro La Clase Pila usa la siguiente línea de código para definir su única variable miembro: private Vector items; 23 Esto declara una variable miembro y no otro tipo de variable (como una variable local) debido a que la declaración aparece dentro del cuerpo de la clase pero fuera de cualquiera de los métodos o constructores. La variable método declarada se llama items, y su tipo de dato es Vector. También, la palabra clave private identifica a items como un miembro privado. Esto significa que solo el código dentro de la clase Pila lo puede acceder. En el ejemplo anterior, items es una variable miembro relativamente simple, pero las declaraciones pueden ser más complejas. Se puede especificar no solamente el tipo, el nombre, y el nivel de acceso, sino también otros atributos, que incluye ya sea si la variable es una clase o una variable de instancia y si es una constante. Cada componente de una declaración de una variable miembro se define de la siguiente forma: accessLevel Le permite controlar qué otras clases tienen acceso a las variables miembro usando uno de los cuatro niveles de acceso: public, protected, package, y private. El control de acceso a los métodos es de la misma forma. static Indica que es una variable de clase en vez de una variable de instancia. También se usa static para declarar métodos de clase. final Indica que el valor de este miembro no puede ser cambiado. La siguiente declaración de variable define una constante llamada AVOGADRO, cuyo valor es el número de Avogadro (6.022 * 1023) y no puede ser cambiado: final double AVOGADRO = 6.022e23; Ocurrirá un error de compilación si un programa intenta cambiar una variable final. Por convención, el nombre de los valores constantes se escribe en mayúsculas. transient El marcador transient no está completamente especificado en la Especificación del Lenguaje Java pero se usa en la serialización de objetos para marcar las variables miembro que no deberían ser serializadas. volatile La palabra clave volatile se usa para prevenir que el compilador realice ciertas optimizaciones en un miembro. Esta es una característica avanzada de Java, usada por unos cuantos programadores. tipo Como otras variables, una variable miembro debe tener un tipo. Se puede usar nombres de tipo primitivos como int, float, o boolean. O se pueden usar tipos referenciales, como un array, object, or nombres de interfaces. nombre Un nombre de variable miembro puede ser cualquier identificador de Java legal y, por convención, empieza con minúscula. No se puede declarar más de una variable miembro con el mismo nombre en la misma clase, pero una subclase puede ocultar una variable miembro del mismo nombre en su superclase. Adicionalmente, una variable miembro y un método pueden tener el mismo nombre. Por ejemplo, el siguiente código es correcto: public class Pila { private Vector items; 24 // un método con el mismo nombre de una variable miembro public Vector items() { . . . } } Implementación de Métodos Esta figura muestra el código para el método push de la clase Pila. Este método agrega un objeto, el que está como argumento, al comienzo de la pila y luego lo retorna. Declaración del método Cuerpo del método Al igual que una clase, un método tiene dos partes importantes: declaración del método y el cuerpo del método. La declaración del método define todos los atributos del método, como el nivel de acceso, tipo de retorno, nombre, y argumentos, como se muestra aquí: Nivel de Acceso Tipo de Retorno Nombre de Método Argumentos El cuerpo del método es donde toda la acción toma lugar. Contiene las instrucciones Java que implementan el método. Implementación de Métodos Esta figura muestra el código para el método push de la clase Pila. Este método agrega un objeto, el que está como argumento, al comienzo de la pila y luego lo retorna. Declaración del método Cuerpo del método 25 Al igual que una clase, un método tiene dos partes importantes: declaración del método y el cuerpo del método. La declaración del método define todos los atributos del método, como el nivel de acceso, tipo de retorno, nombre, y argumentos, como se muestra aquí: Nivel de Acceso Tipo de Retorno Nombre de Método Argumentos El cuerpo del método es donde toda la acción toma lugar. Contiene las instrucciones Java que implementan el método. Detalles de la declaración de un método La declaración de un método provee bastante información sobre el método para el compilado, al sistema de ejecución, y a otras clases y objetos. No solamente se incluye el nombre del método, sino información como el tipo de retorno del método, el número y el tipo de argumentos requeridos por el método, y qué otras clases y objetos pueden llamar el método. La mayoría de los métodos pueden ser declarados implícitamente. Los únicos elementos requeridos para una declaración de métodos son el nombre del método, su tipo de retorno, y un par de paréntesis( ). A continuación los elementos de una declaración de métodos. accessLevel static abstract final native synchronized ejecutarse returnType nombreDeMétodo (lista de parámetros) throws exceptions Nivel de Acceso para este método. Este es un método de clase. Este método no está implementado Un método que no puede ser ignorado Método implementado en otro lenguaje Método que necesita monitorearse para El tipo de retorno y el nombre del método Lista de Argumentos Las excepciones lanzadas por este método Cada elemento de una declaración de un método se define como sigue: accessLevel Como con la variable miembro, tú controlas que otras clases tienen acceso a un determinado método usando uno de los cuatro niveles de acceso: public, protected, package, y private. static Al igual que las variables miembro, static declara este método como un método de clase en lugar de un método de instancia. 26 abstract Un método abstracto no tiene implementación y debe ser un miembro de una clase abstracta. final Un método final no puede ser pasado por alto por las subclases. native Si se tiene una librería significativa de funciones escritos en otro lenguaje como C, puede que desee conservarlos y utilizar dichas funciones desde Java. Los métodos implementados en un lenguaje distinto a Java son llamados métodos nativos y son declarados como tales empleando la palabra clave native. synchronized Los threads que se ejecutan al mismo tiempo frecuentemente invocan métodos que operan sobre la misma data. Estos métodos deben ser declarados synchronized para asegurarse de que los threads accesan a la información en forma segura. returnType Java requiere que un método declare el tipo de dato del valor que retorna. Si tu método no retorna valor, emplee la palabra clave void para el tipo de retorno. nombreDeMétodo El nombre de un método puede ser cualquier identificador legal de Java. (Lista de Parámetros) Se pasa información a un método a través de sus argumentos. [throws exceptions] Si su método lanza cualquier excepción evaluada, su declaración del método debe indicar el tipo de tales excepciones. Retornar un Valor de un Método Se declara el tipo de retorno de un método en su declaración de métodos. Dentro del cuerpo del método, se utiliza el operador return para devolver un valor. Todo método que no sea declarado como void debe incluir una sentencia de retorno. La clase Pila declara el método isEmpty, el cual retorna un boolean: public boolean isEmpty() { if (items.size() == 0) return true; else return false; } Los tipos de datos del valor de retorno deben coincidir con el tipo de retorno del método; no se puede retornar un tipo de datos String de un método declarado para retornar un entero. El método isEmpty retorna ya sea un valor Boolean, true or false, dependiendo del resultado de la evaluación. Se genera un error de compilación si se trata de escribir un método en la que el valor de retorno no coincide con el tipo de retorno. 27 El método isEmpty retorna un tipo primitivo. Los métodos también pueden retornar un tipo referencial. Por ejemplo, La clase Pila declara el método pop() que retorna un tipo de referencia Object: public synchronized Object pop() { int len = items.size(); Object obj = null; if (len == 0) throw new EmptyStackException(); obj = items.elementAt(len - 1); items.removeElementAt(len - 1); return obj; } Nombre de los Métodos Java soporta la sobrecarga de nombres para métodos de tal forma que múltiples métodos puedan compartir el mismo nombre. Por ejemplo, supongamos que tenemos una clase que puede suministrar varios tipos de datos (strings, enteros, etc) para su área de dibujo. Necesita escribir un método que sepa cómo suministrar cada tipo de datos. En otros lenguajes, se tiene que pensar en algún otro nombre para cada método, por ejemplo, drawString, drawInteger, drawFloat, etc.. En Java, se puede usar el mismo nombre para todos los métodos draw pero que pasen un tipo diferente de parámetro para cada método. De tal forma que en su clase, se pueda declarar tres métodos draw, cada uno tomando un tipo diferente de parámetro: class DataRenderer { void draw(String s) { . . . } void draw(int i) { . . . } void draw(float f) { . . . } } Los métodos con el mismo nombre se diferencian por el número y tipo de argumentos pasados al método. en el ejemplo, draw(String s) y draw(int i) son métodos distintos y únicos porque requieren tipos diferentes de argumentos. No se puede declarar más de un método con el mismo nombre y tipo de argumentos porque el compilador no los puede diferenciar, draw(String s) y draw(String t) serían idénticos lo que generaría un error de compilación. Pasar información a un Método Cuando se escribe un método, se declara el número y tipo de argumentos requeridos por tal método. Por ejemplo, el siguiente método calcula los pagos mensuales de un préstamo basado en la cantidad del préstamo, la tasa de interés, el número de periodos, y el valor futuro del 28 préstamo(asumamos que el valor futuro del préstamo es cero porque al final del préstamo, se terminó de pagar): double calcularPago(double cantPrest, double tasa, double valorFuturo, int numPeriodos) { double I, parcial1, denominador, respuesta; I = tasa / 100.0; parcial1 = Math.pow((1 + I), (0.0 - numPeriodos)); denominador = (1 - parcial1) / I; respuesta = ((-1 * cantPrest) / denominador) - ((valorFuturo * parcial1) / denominador); return respuesta; } Este método tiene cuatro argumentos: la cantidad prestada, la tasa de interés, el valor futuro y el número de períodos. Los tres primeros son números de punto flotante double, y el cuarto es un entero. Como en este método, el conjunto de argumentos para cualquier método es una lista separada por comas de declaraciones de variables donde cada declaración de variable viene a ser el nombre de la variable acompañada de su tipo: tipo nombre Como se puede ver en el cuerpo del método calcularPago, simplemente se usa el nombre del argumento para referirse al valor del argumento. Tipos de Argumentos En Java, se puede pasar un argumento de cualquier tipo de dato de Jaca a un método. Esto incluye tipos de datos primitivos como doubles, floats y enteros como se vio en el método calcularPago, y tipos de datos referenciales como objetos y arrays. Aquí tenemos el ejemplo de un método que acepta un arreglo como argumento. En este ejemplo, el método crea un nuevo objeto Polygon y lo inicializa a partir de una lista de Points (Point es una clase que representa una coordenada x, y): static Polygon polygonFrom(Point[] listOfPoints) { . . . } A diferencia de otros lenguajes, no se puede pasar métodos dentro de otros métodos de Java. pero se puede pasar un objeto a un método y luego invocar los métodos del objeto. El Cuerpo de un Método En el siguiente ejemplo, los cuerpos de los métodos para los métodos estaVacio y pop son mostrados en negrita: class Pila { static final int PILA_VACIA = -1; Object[] elementosdepila; 29 int elementosuperior = PILA_VACIA; . . . boolean estaVacio() { if (elementosuperior == PILA_VACIA) return true; else return false; } Object pop() { if (elementosuperior == PILA_VACIA) return null; else { return elementosdepila [elementosuperior --]; } } } Junto con los elementos regulares de Java, puede usar this en el cuerpo del método para referirse a los métodos en el objeto actual. El objeto actual es el objeto cuyo método es llamado. También puede usar super para referirse a los miembros de la superclase que el objeto actual a ocultado o sustituido. Además, el cuerpo de un método podría contener las declaraciones para las variables que son locales para dicho método. this Típicamente, dentro del cuerpo de un objeto se puede hacer referencia directamente a las variables miembro de un objeto. Sin embargo, es necesario evitar la ambigüedad con respecto al nombre de una variable miembro si uno de los argumentos para el método tiene el mismo nombre. Por ejemplo, el siguiente constructor para la clase HSBColor inicializa algunas de las variables miembro de un objeto de acuerdo a los argumentos que son pasados al constructor. Cada argumento para el constructor tiene el mismo nombre que la variable miembro de un objeto cuyo valor inicial es contenida por el argumento. class HSBColor { int tono, nitidez, brillo; HSBColor (int tono, int nitidez, int brillo) { this.tono = tono; this.nitidez = nitidez; this.brillo = brillo; } Debe usar la palabra clave this en este constructor porque es necesario evitar la ambigüedad entre el argumento tono y la variable miembro hue (lo mismo con los otros argumentos). Al escribir tono = tono; no tiene sentido. Los nombres de los argumentos se anticipan y ocultan las variables miembro con el mismo nombre. Entonces para referirse a una variable miembro se debe hacer a través del objeto actual empleando la palabra clave this para referirse al objeto actual explícitamente. Algunos programadores prefieren siempre usar la palabra clave this cuando se refieren a una variable miembro del objeto cuyo método está siendo referenciado. Al hacer esto se tiene un código más explícito y se reduce errores al escribir nombres repetidos. 30 También se puede usar la palabra clave this para invocar uno de los métodos actuales de un objeto. Esto es sólo necesario si existe alguna ambigüedad en el nombre del método y es frecuentemente usado con el propósito de hacer el código más claro. super Si un método oculta una de las variables miembro de su superclase, un método se puede referir a las variables ocultas a través de la palabra clave super. De manera similar, si un método pasa por encima de los métodos de su superclase, un método puede invocar al método sobrepasado a través del uso de la palabra clave super. Considere esta clase: class UnaClaseTonta { boolean unaVariable; void unMetodo() { unaVariable = true; } } y su subclase la cual oculta unaVariable y pasa por encima a unMetodo: class UnaClaseMasTonta extends UnaClaseTonta { boolean unaVariable; void unMetodo() { unaVariable = false; super.unMethod(); System.out.println(unaVariable); System.out.println(super.unaVariable); } } Primero unMethod establece unaVariable (declarada en UnaClaseMasTonta que oculta la variable declarada en UnaClaseTonta) a false. Luego unMetodo invocó su método sobrepasado con la siguiente sentencia: super.unMethod(); Esto establece la versión oculta de unaVariable (declarada en UnaClaseTonta) a true. Luego unMetodo visualiza ambas versiones de unaVariable la cual tiene diferentes valores: false true Variables Locales Dentro del cuerpo del método se puede declarar más variables para usarse dentro de dicho método. Estas variables son llamadas variables locales y viven sólo cuando se tiene control sobre dicho método. Este método declara una variable local i la cual es usada para recorrer los elementos de su argumento array. 31 Object encontrarObjetoEnArreglo(Object o, Object[] arregloDeObjetos) { int i; // variable local for (i = 0; i < arregloDeObjetos.length; i++) { if (arregloDeObjetos [i] == o) return o; } return null; } Después de que este método retorna, i ya no existe. Control de Acceso a los Miembros de Una Clase Uno de los beneficios de las clases es que las clases pueden proteger su variable miembro y métodos del acceso de otros métodos. Por qué es importante esto? Bien, considere lo siguiente. Esta escribiendo una clase que representa una consulta a una base de datos que contiene todo tipo de información secreta, digamos el registro de empleados o declaraciones de ingresos de su compañía. Cierta información y consultas contenidas en la clase, aquellas soportadas por los métodos y variables públicamente accesibles en su objeto consulta, están bien para el consumo de cualquier otro objeto en un sistema. Otras consultas contenidas en la clase se encuentran ahí simplemente para el uso personal de la clase. Dichas consultas soportan las operaciones de la clase pero no deberían ser usados por objetos de otro tipo—usted tiene información secreta que proteger. Le gustaría ser capaz de proteger dichas variables personales y métodos a nivel del lenguaje y deshabilitar el acceso a objetos de otro tipo. En Java, se puede emplear identificadores de acceso tanto para proteger las variables de una clase y sus métodos cuando son declarados. El lenguaje Java cuatro niveles de acceso diferentes para los métodos y variables miembros: private, protected, public, y, si no se especifica, package. El siguiente cuadro muestra el nivel de acceso permitido por cada identificador. Specificador clase subclase paquete Mundo private X protected X X X public X X X package X X X La primera columna indica si la propia clase tiene acceso al miembro definido por el especificador de acceso. Como puede ver, una clase siempre tiene acceso a sus propios miembros. La segunda columna indica si las subclases de la clase (sin importar en que paquete se encuentren) tienen acceso al miembro. La tercera columna indica si las clases en el mismo paquete de la clase (sin importar el paquete al que pertenecen) tienen acceso al miembro. La cuarta columna indica si todas las clases tienen acceso al miembro. Veamos cada nivel de acceso en detalle. 32 Private El nivel de acceso más restrictivo es private. Un miembro privado es sólo accedido por la clase en donde es definida. Use este acceso para declarar miembros que sólo deberían ser usados por la clase. Esto incluye las variables que contengan información que si son accedidas desde afuera podrían poner al objeto en un estado inconsistente, o los métodos que, si son invocados desde afuera, podrían amenazar el estado del objeto o el programa en el que se está ejecutando. Los miembros privados son como tus más preciados secretos. Para declarar a un miembro como privado, se utiliza la palabra clave private en su declaración. La siguiente clase contiene una variable de miembro privado y in método también privado: class Alpha { private int yosoyprivate; private void metodoPrivado() { System.out.println("metodoPrivado"); } } Los objetos del tipo Alpha pueden inspeccionar y modificar la variable yosoyprivate y pueden invocar metodoPrivado, pero los objetos de otro tipo no lo pueden hacer. Por ejemplo, la clase Beta definida aquí: class Beta { void metodoDeAcceso() { Alpha a = new Alpha(); a.yosoyprivate = 10; a.metodoPrivado(); } } // ilegal // ilegal no puede acceder la variable yosoyprivate o invocar metodoPrivado en un objeto de tipo Alpha porque Beta no es de tipo Alpha. Cuando una de sus clases intenta acceder una variable miembro a la cual no tiene acceso, el compilador muestra un mensaje de error y se niega a compilar el programa: Beta.java:9: Variable yosoyprivate in class Alpha not accessible from class Beta. a.yosoyprivate = 10; // ilegal ^ 1 error Además, si su programa intente acceder un método al cual no tiene acceso, verá un error de compilación: Beta.java:12: No method matching metodoPrivado() found in class Alpha. a.metodoPrivado(); // ilegal 1 error Los nuevos programadores de Java se podrían preguntar si un objeto Alpha puede acceder los miembros privados de otro objeto Alpha. Esto se ilustra en el siguiente objeto. 33 Supongamos que la clase Alpha contiene un método de instancia que compara el objeto actual Alpha (this) con otro objeto basado en sus variables yosoyprivate: class Alpha { private int yosoyprivate; boolean esIgualA(Alpha otroAlpha) { if (this.yosoyprivate == otroAlpha.yosoyprivate) return true; else return false; } } Esto es perfectamente legal. Los objetos del mismo tipo tienen acceso entre sus miembros privados. Esto es posible porque las restricciones de acceso se aplican al nivel de tipo o de clase(todas las instancias de una clase) en vez de hacerlo al nivel de objeto(esta instancia en particular de una clase). Note: this es una palabra clave de Java que se refiere al objeto actual. Protected El siguiente especificador de nivel de acceso es protected, que permite a la misma clase, subclases (con la advertencia referida anteriormente), y todas las clases en el mismo paquete acceder a los miembros. Utilice el nivel de acceso protected cuando es apropiado para las subclases de una clase que tengan acceso a otro miembro, excepto la clase sin relación. Los miembros protected son como los secretos de familia—no te importa si toda la familia lo sabe, e inclusive algunos amigos de confianza pero si no querrá que algún otro extraño se entere. Para declarar a un miembro como protected emplee la palabra clave protected. Primero, veamos cómo el especificador protected afecta el acceso de las clases en el mismo paquete. Considere esta versión de la clase Alpha la cual es ahora declarada dentro de un paquete llamado Greek y tiene una variable miembro protected y un método protected declarado en la misma clase: package Greek; public class Alpha { protected int yosoyprotected; protected void metodoProtected() { System.out.println("metodoProtected "); } } Ahora, suponga que la clase Gamma fue también declarada para ser miembro del paquete Greek (y no es una subclase de Alpha). La clase Gamma puede acceder en forma legal a la variable miembro yosoyprotected del objeto Alpha y puede legalmente invocar su metodoProtected: package Greek; class Gamma { 34 void metodoDeAcceso() { Alpha a = new Alpha(); a.yosoyprotected = 10; a.metodoProtected(); } // legal // legal } Public El especificador de acceso public. Cualquier clase, en cualquier paquete, tienen acceso a los miembros públicos de la clase. Declare los miembros como públicos sólo si dicho acceso no genere resultados indeseados si es utilizado desde afuera. Aquí no hay secretos personales o familiares; para efectos personales no interesa si los demás se enteran de todo. para declarar a un miembro como público, usa la palabra clave public. Por ejemplo, package Greek; public class Alpha { public int yosoypublic; public void metodoPublico() { System.out.println("metodoPublico"); } } Rescribamos nuestra clase Beta una vez más pero esta vez en un paquete diferente al de Alpha y asegurémonos de que no estén relacionado a (no una subclase) Alpha: package Roman; import Greek.*; class Beta { void metodoDeAcceso() { Alpha a = new Alpha(); a.yosoypublic = 10; a.metodoPublico(); } } // legal // legal Como se puede apreciar en el fragmento de código anterior, Beta puede modificar e inspeccionar legalmente la variable yosoypublico en la clase Alpha y puede invocar legalmente al método metodoPublico. Package El nivel de acceso package se obtiene si no se especifica explícitamente un acceso para un miembro de cualquiera de los otros niveles. Este nivel de acceso permite a las clases en el mismo paquete de su clase acceder a los miembros. Este nivel de acceso asume que las clases en el mismo paquete son amigos de confianza. Este nivel de confianza es como aquel que se da con alguno amigo muy cercano que incluso no se lo confiarías a tu familia. 35 Por ejemplo, esta versión de la clase Alpha declara una variable miembro simple de accesopackage y un método simple de acceso package. Alpha vive en el paquete Greek: package Greek; class Alpha { int yosoypackage; void metodoPackage() { System.out.println("metodoPackage"); } } La clase Alpha tiene acceso tanto a yosoypackage y a packageMethod. Además, todas las clases declaradas dentro del mismo paquete de Alpha también tienen acceso a yosoypackage y a packageMethod. Suponga que tanto Alpha y Beta fueron declarados como parte del paquete Greek: package Greek; class Beta { void metodoDeAcceso() { Alpha a = new Alpha(); a.yosoypackage = 10; // legal a.metodoPackage(); // legal } } Beta puede acceder legalmente a yosoypackage y a metodoPackage como se muestra. Instancia y Miembros de Clase Cuando declara una variable miembro como unFloat en MiClase: class MiClase { float unFloat; } usted declara una variable de instancia. Cada vez que crea una instancia de una clase, el sistema en tiempo de ejecución crea una copia de cada una de las variables de instancia de una clase para la instancia. Se puede acceder a las variables de instancia del objeto desde un objeto. Las variables de instancia son lo opuesto a las variables de clase (las cuales se declaran usando el modificador static). El sistema en tiempo de ejecución asigna las variables de clase, una sola vez por clase, sin importar el número de instancias creadas de dicha clase. El sistema asigna memoria para las variables de clase la primera vez que encuentra la clase. Todas las clases comparten la misma copia de las variables de clase de la clase. Usted puede acceder a las variables de clase a través de una instancia o a través de la misma clase. Sucede lo mismo con los métodos: Sus clases pueden tener métodos de instancia y métodos de clase. los métodos de instancia operan en las variables de instancia del objeto actual pero también tienen acceso a las variables de clase. Los métodos de clase, por otra parte, no pueden 36 acceder a las variables de instancia declaradas dentro de la clase(al menos que creen un nuevo objeto y accedan a ellos a través del objeto).Además, los métodos de clase pueden ser invocados en la clase, no necesita una instancia para llamar un método de clase. Por defecto, al menos que se especifique lo contrario, un miembro declarado dentro de una clase es un miembro de instancia. La clase definida abajo tiene una variable de instancia—un entero llamado x—y dos métodos de instancia--x y establecerX-que permite a otros objetos establecer y evaluar el resultado de x: class UnEnteroLlamadoX { int x; public int x() { return x; } public void establecerX(int newX) { x = newX; } } Cada vez que instancia un nuevo objeto a partir de una clase, se obtiene una nueva copia de cada una de las variables de instancia de la clase. Estas copias son asociadas con el nuevo objeto. Por consiguiente, cada vez que instancia un nuevo objeto UnEnteroLlamadoX a partir de la clase, se obtiene una nueva copia de x que es asociada con el nuevo objeto UnEnteroLLamadoX. Todas las instancias de una clase comparten la misma implementación de un método de instancia; todas las instancias de UnEnteroLLamadoX comparten la misma implementación de x y establecerX. Note que ambos métodos, x y establecerX, se refieren a la variable de instancia del objeto x por el nombre. "Pero", usted se preguntará, "si todas las instancias de UnEnteroLlamadoX que comparten la misma implementación de x y establecerX no es ambiguo?" La respuesta es "no." Dentro de un método de instancia, el nombre de una variable de instancia se refiere a la variable de instancia del objeto actual, asumiendo que la variable de instancia no es ocultada por un parámetro de método. Entonces, dentro de x y establecerX, x es equivalente a this.x. Los objetos fuera de UnEnteroLlamadoX que deseen acceder a x deben hacerlo a través de una instancia en particular de UnEnteroLlamadoX. Supongamos que este fragmento de código estaba en otro método de un objeto. Se crea dos objetos diferentes del tipo UnEnteroLlamadoX, establece sus valores x a diferentes valores, luego los muestra: . . . UnEnteroLlamadoX miX = new UnEnteroLlamadoX (); UnEnteroLlamadoX otraX = new UnEnteroLlamadoX (); myX.establecerX(1); anotherX.x = 2; System.out.println("miX.x = " + miX.x()); System.out.println("otraX.x = " + otraX.x()); . . . Note que el código usado establecerX para establecer el valor x para miX pero justo asignó un valor a otraX.x directamente. De cualquier forma, el código está manipulando dos copias diferentes de x: aquella contenida en el objeto miX y aquella contenida en el objeto otraX. El resultado producido por este fragmento de código es: 37 miX.x = 1 otraX.x = 2 muestra que cada instancia de la clase UnEnteroLlamadoX tiene su propia copia de la variable de instancia x cada x posee un valor diferente. Se puede, cuando se declara una variable miembro, especificar que la variable es una variable de clase en vez de una variable de clase. De manera similar, se puede especificar que un método es un método de clase en vez de ser un método de instancia. El sistema crea una única copia de una variable de clase la primera vez que encuentra la clase en donde la variable es definida. Todas las instancias de dicha clase comparten la misma copia de la variable de clase. Los métodos de clase sólo pueden operar en las variables de clase—los métodos no pueden acceder las variables de instancias definida en la clase. Para especificar que una variable miembro es una variable de clase, use la palabra clave static. Por ejemplo, cambiemos la clase UnEnteroLlamadoX de tal forma que su variable x sea ahora una variable de clase: class UnaVariableLlamadaX { static int x; public int x() { return x; } public void establecerX(int newX) { x = newX; } } Ahora exactamente el mismo fragmento de código que crea dos instancias de UnEnteroLlamadoX, establece sus valores x, y luego los visualiza produce este, diferentes, resultado. miX.x = 2 otraX.x = 2 El resultado es diferente porque x es ahora una variable de clase de tal forma que sólo existe una copia de la variable y es compartida por todas las instancias de UnEnteroLlamadoX, incluyendo miX y otraX. Cuando usted invoca establecerX en ya sea una instancia, usted cambia el valor de x para todas las instancias de UnEnteroLlamadoX. Usted emplea las variables de clase para los elementos que necesita sólo una copia y aquellos que deban ser accesibles por todos los objetos que heredan de la clase en donde es declarada la variable. Por ejemplo, las variables de clase son frecuentemente usadas con final para definir constantes; esto es más eficiente en términos de memoria que las variables de instancia final ya que las constantes no pueden cambiar, entonces sólo necesita una copia). De manera similar, cuando se declara un método, puede especificar dicho método para que sea un método de clase en vez de un método de instancia. Los métodos de clase sólo pueden operar en las variables de clase y no pueden acceder las variables de clase definidas en la clase. Para especificar que un método es un método de clase, use la palabra clave static en la declaración del método. Cambiemos la clase UnEnteroLlamadoX de tal forma que su 38 variable miembro x es de nuevo una variable de instancia, y sus dos métodos son ahora métodos de clase: class UnEnteroLlamadoX { int x; static public int x() { return x; } static public void establecerX(int newX) { x = newX; } } Cuando intente compilar esta versión de UnEnteroLlamadoX, el compilador lanza el siguiente error: UnEnteroLlamadoX.java:4: Can't make a static reference to nonstatic variable x in class AnIntegerNamedX. return x; ^ Esto se debe a que los métodos de clase no pueden acceder a las variables de instancia al menos que primero el método creara una instancia de UnEnteroLlamadoX y acceda a la variable a través de él. Arreglemos la clase UnEnteroLlamadoX haciendo de su variable x una variable de clase: class UnEnteroLlamadoX { static int x; static public int x() { return x; } static public void establecerX(int newX) { x = newX; } } Ahora la clase sí compilará y el mismo fragmento de código que crea dos instancias de UnEnteroLlamadoX, establece sus valores x, y luego imprime los valores de x produce este resultado: miX.x = 2 otraX.x = 2 De nuevo, al cambiar x a través de miX también lo cambia para todas las instancias de UnEnteroLlamadoX. Otra diferencia entre los miembros de instancia y los miembros de clase es que los miembros de clase son accesibles desde la misma clase. No hay necesidad de instanciar una clase para acceder a sus miembros de clase. Rescribamos el fragmento de código para acceder a x y a establecerX directamente desde la clase UnEnteroLlamadoX: . . . 39 UnEnteroLlamadoX.establecerX(1); System.out.println("UnEnteroLlamadoX.x = " + UnEnteroLlamadoX.x()); . . . Note que ya no necesita crear miX y otraX. Puede establecer y recuperar x directamente de la clase UnEnteroLlamadoX. Esto no se puede hacer con los miembros de instancia, sólo puede invocar los métodos de instancia a partir de un objeto y poder sólo acceder a las variables de instancia a partir de un objeto. Puede acceder a los métodos y a las variables de clase ya sea a partir de una instancia de una clase o a partir de la misma clase. Inicialización de una Instancia y Miembros de una Clase Se puede usar los iniciadores static y los iniciadores de instancia para proveer de valores iniciales para la clase y miembros de instancia cuando son declarados en una clase: class CamaYDesayuno { static final int MAX_CAPACIDAD = 10; boolean lleno = false; } Esto funciona bien para los miembros de tipos de datos primitivos. Algunas veces, inclusive funciona cuando se crea arrays u objetos. Pero esta forma de inicialización tiene limitaciones, como sigue: 1. Los iniciadores sólo pueden realizar inicializaciones que puedan ser expresadas en una sentencia de asignación. 2. Los iniciadores no pueden llamar cualquier método que pueda lanzar una excepción conocida. 3. Si el iniciador llama a un método que lanza una excepción en tiempo de ejecución, luego no puede efectuar la recuperación de errores. Si tiene alguna inicialización que realizar que no se puede hacer en un iniciador debido a alguna de sus limitaciones, es necesario colocar el código de inicialización en otra parte. Para inicializar los miembros de una clase, coloque el código de inicialización en un bloque de inicialización static. Para inicializar a los miembros de instancia, coloque el código de inicialización en el constructor. Empleo de los Bloques de Inicialización Static Este es un ejemplo de un bloque de inicialización static: 40 El grupo de recursos errorStrings debe ser inicializado en un bloque de inicialización static. Esto se debe a que la recuperación de errores debe ser ejecutada si el grupo no puede ser encontrado. Además, errorStrings es un miembro de clase y no tiene sentido para éste ser inicializado en un constructor. Como se muestra en el ejemplo anterior, un bloque de inicialización static comienza con la palabra clave static y es un bloque normal de código de Java encerrado entre llaves{}. Una clase puede contener cualquier número de bloque de inicialización static que aparecen en cualquier parte del cuerpo de la clase. El sistema en tiempo de ejecución garantiza que los bloques de inicialización static y los iniciadores static sean llamados en el orden (izquierda a derecha, de arriba a abajo) en que aparecen en el código fuente. Inicialización de los Miembros de Instancia Si desea inicializar una variable de instancia y no lo puede hacer en la declaración de variable por las razones mencionadas anteriormente, entonces coloque la inicialización en el constructor(s) para la clase. Suponga que el grupo errorStrings en el ejemplo anterior es una variable de instancia en vez de una variable de clase. Entonces usará el siguiente código para inicializarlo: import java.util.ResourceBundle; class Errors { GrupoDeRecursos errorStrings; Errors() { try { errorStrings = ResourceBundle. getBundle("ErrorStrings"); } catch (java.util.MissingResourceException e) { // error recovery code here } } } El código que inicializa errorStrings está ahora en un constructor para la clase. Algunas veces una clase contiene varios constructores y cada constructor permite al solicitante proveer los valores iniciales para las diferentes variables de instancia del nuevo objeto. Por ejemplo, java.awt.Rectangle presenta estos tres constructores: Rectangle(); Rectangle(int ancho, int alto); Rectangle(int x, int y, int ancho, int alto); El constructor sin argumentos no permite para nada al solicitante proveer los valores iniciales, y los otros dos constructores permiten al solicitante establecer los valores iniciales ya sea para el tamaño o para el origen y tamaño. Aún más, todas las variables de instancia, el origen y el tamaño, para Rectangle deben ser inicializadas. En este caso, las clases frecuentemente tienen un constructor que realiza todo el trabajo. Los otros constructores llaman a este constructor y le provee los valores de sus parámetros o con valores por defecto. Por ejemplo, aquí están las posibles implementaciones de los tres constructores de Rectangle mostrados anteriormente (asumamos que x, y, ancho, y alto son los nombres de la variable de instancia a ser inicializados): 41 Rectangle() { this(0,0,0,0); } Rectangle(int ancho, int alto) { this(0,0,ancho,alto); } Rectangle(int x, int y, int ancho, int alto) { this.x = x; this.y = y; this.ancho = ancho; this.alto = alto; } El lenguaje Java soporta bloques de inicialización de instancias, que se puede usar en vez de lo mostrado anteriormente. Sin embargo, se usan con el propósito de ser implementados con clases anónimas, las cuales no pueden declarar constructores. Usar constructores es mejor por las siguientes razones: Todo el código de inicialización está en un solo lugar, por lo tanto hace que el código sea más fácil de mantener y leer. Los valores por defecto son manejados explícitamente. Los constructores son ampliamente entendidos por la comunidad Java, incluyendo a los programadores relativamente nuevos, mientras los iniciadores de instancia no lo son y podrían causar confusión a terceros al leer su código. 42 HERRAMIENTAS DE PROGRAMACIÓN Y OBJETOS BÁSICOS Conociendo el Entorno de Trabajo de Visual Age for Java : Visual Age for Java – WorkBench Cómo Empezar: Barra de Herramientas: Proyectos Paquetes Clases Applets 1. Creando un Proyecto: 1. Adicione un nuevo Proyecto (Add New or Existing Project to Workspace). 2. Ponga como nombre del proyecto (Clase01). 2. Creando un Paquete: 43 2. Adicione un Package (Add New or Existing Package to Workspace). Ponga como nombre su apellido y nombre (Romero_Angel). 3. Para este ejemplo agregue un Applet (Create Applet). Póngale como nombre Hola. Quitar el check de Compose the class visually y clic en Finish. Finalmente tendrá las siguiente estructura. Haga clic en el hombrecito que esta corriendo para ejecutar su applet Obtendrá lo siguiente: 44 Applet ejecutado Agreguémosle un nuevo método a nuestro applet seleccionando el ícono de la barra de herramientas, a continuación aparecerá la siguiente pantalla en donde colocará el nombre de su método con sus respectivo argumentos si el caso lo amerita. Nuestro método se llamara pintarFondo y recibirá como argumentos un color definido por el argumento (java.awt.Color c). Luego invocamos nuestro método como se indica en la figura: 45 En este ejemplo la invocación de nuestro método pintarFondo() se hace dentro del método init() el cual es el método de arranque de todo applet. A continuación el resultado: CONVERSIÓN DE DATOS - De cadenas a valores: Double.valueOf(str).doubleValue(); Integer.valueOf(str).intValue(); Nota: str es la cadena de dígitos a convertir - De valores a cadenas: String.valueOf(valorReal); String.valueOf(valorEntero); 46 PROGRAMACIÓN DE CLASES Vamos a desarrolar la clase ConversionesBasicas por lo tanto, le recomiendo crear el paquete Ejemplo02 y estando sobre él crear la clase según los gráficos siguientes: Una vez creada su clases debe crear métodos para esta clase: Creemos el método double aReal(String s), para ello utilicemos el asistente de creación de métodos. Poner el siguiente código para este método: 47 public double aReal(String s) { return Double.valueOf(s).doubleValue(); } Asimismo, crear los siguientes métodos para esta clase: public String aCadena(double d) { return String.valueOf(d); } public String aCadena(int i) { return String.valueOf(i); } Herencia Ahora debemos crear la clase OperacionesBasicas extendida de la clase ConversionesBasicas, esto hará que los métodos de ConversionesBasicas serán heredados a la nueva clase OperacionesBasicas. En esta nueva clase crear los métodos sumar, restar, multiplicar y dividir. Como sigue: public class OperacionesBasicas extendí ConversionesBasicas { public String sumar(String s1, String s2) { return aCadena(aReal(s1)+aReal(s2)) 48 } public String restar(String s1, String s2) { return aCadena(aReal(s1)-aReal(s2)) } public String multiplicar(String s1, String s2) { return aCadena(aReal(s1)*aReal(s2)) } public String dividir(String s1, String s2) { return aCadena(aReal(s1)/aReal(s2)) } } USANDO CLASES EN MODO VISUAL Tenemos las clases OpereacionesBasicas extendida de la clase ConversionesBasicas. En la clase OperacionesBasicas tenemos cuatro métodos propios (sumar, restar, multiplicar y dividir) más los métodos de la clase ConversionesBasicas por la extensión. Ahora vamos a desarrollar un applet que utilice la clase OperacionesBasicas. Para eso tiene que seguir los siguientes pasos: 49 1. Debe crear ahora el applet Caculadora (Check en Compose the class visually): Agregue los siguientes controles al área de composición visual. 2. Ahora debe agregar la clase OperacionesBasicas al applet Calculadora: 50 Clic en Browse, conforme digita Oper…, sale su clase escogida (Póngale de nombre Básico) Agregue el Bean fuera del área de composición visual: 3. Debe conectar el botón con el método sumar: a. Clic derecho sobre el botón (+). b. Connect. c. ActionPerformed. d. Luego clic en la instancia Basico. e. Connectable Features. f. Escoja el método sumar. 51 El color verde punteado indica que falta parámetros. 4. Debe dar parámetros al método sumar: a. Clic derecho sobre la caja de texto que representa a valor 1. b. Connect. c. Text. d. Clic en la raya verde. e. Escoger el parámetro s1. 5. Hacer lo mismo con la caja de texto que representa a Valor 2, y escoger el parámetro s2: 6. El método sumar retorna un dato. Debe ponerlo en la caja de texto que representa al resultado: a. Clic derecho en raya verde. b. Connect. c. NormalResult. d. Clic sobre la caja de texto que representa al resultado. e. Escoger Text. 7. Ejecute finalmente para probar su aplicación. 52 EL ABSTRACT WINDOW TOOLKIT DE JAVA AWT es una biblioteca de clases de Java para el desarrollo de Interfases de Usuarios Gráficas. La estructura básica del AWT se basa en Componentes y Contenedores. Se incluye a nuestra aplicación, con la siguiente sentencia: import java.awt.*; Estructura del AWT La estructura de la versión actual del AWT podemos resumirla en los puntos que exponemos a continuación: Los contenedores tienen Componentes, que son los controles básicos. No se usan posiciones fijas de los Componentes, sino que están situados a través de una disposición controlada (layouts). El común denominador de más bajo nivel se acerca al teclado, ratón y manejo de eventos. Alto nivel de abstracción respecto al entorno de ventanas en que se ejecute la .aplicación (no hay áreas cliente, ni llamadas a X, ni hWnds, etc.). La arquitectura de la aplicación es dependiente del entorno de ventanas, en vez de tener un tamaño fijo. Es bastante dependiente de la máquina en que se ejecuta la aplicación (no puede asumir que un diálogo tendrá el mismo tamaño en cada máquina). Relación de Componentes Básicos de AWT - Botones. Etiquetas. Listas. Campos de Texto. Áreas de Texto. Barras de desplazamiento. Canvas. Una interfase gráfica está construida en base a elementos gráficos básicos, los Componentes listados anteriormente. Los componentes permiten al usuario interactuar con la aplicación y proporcionar información desde el programa al usuario sobre el estado del programa. En el AWT, todos los Componentes de la interfase de usuario son instancias de la clase Component a uno de sus subtipos. Los componentes no se encuentran aislados, sino agrupados dentro de los Contenendores. Los Contenedores contienen y organizan la situación de los Componentes; además, los Contenedores son en si mismos Componentes y como tales pueden ser utilizados dentro de otros Contenedores. También contienen el código necesario para el control de eventos, cambiar la forma del cursor o modificar el ícono de la aplicación. En el AWT, todos los Contenedores son instancias de la clase Container o uno de sus subtipos. 53 BOTONES implements ActionListener actionPerformed(ActionEvent e) //Constructor public Button(); public Button(String label); //Methods public String getLabel(); //Retorna el caption del botón. Public void setLabel(String label); //Establece el caption del botón. Ejemplo En el siguiente ejemplo mostraremos 3 botones (Uno, Dos, Tres), cuando el usuario da clic en uno de ellos, ese botón desaparece, pero los otros están visibles: import java.awt.*; import java.awt..event.*; public class Boton01 extends java.applet.Applet implements ActionListener { Button b1,b2,b3 ; Public void init() { b1=new Button("Uno") ; b1.addActionListener(this); add(b1); b2=new Button("Dos") ; b2.addActionListener(this); add(b2); b3=new Button("Tres") ; b3.addActionListener(this); add(b3); } public void actionPerformed(ActionEvent e) { if(e.getSource().equals(b1)) { b1.setVisible(false);´ b2.setVisible(true);´ b3.setVisible(true);´ } if(e.getSource().equals(b2)) { b2.setVisible(false);´ b1.setVisible(true);´ b3.setVisible(true);´ } 54 if(e.getSource().equals(b3)) { b3.setVisible(false);´ b1.setVisible(true);´ b2.setVisible(true);´ } } } ETIQUETAS //Constructos public Label(); public Label(String label); public Label(String label, int aligment); //aligment puede ser Label.LEFT, Label.CENTER o Label.RIGHT //Methods public int getAlignment(); //Devuelve Label.LEFT, Label.CENTER o Label.RIGHT public String getText(); //Devuelve el Caption del Label public void setAlignment(int alignment); //Establece la justificación con Label.LEFT, Label.CENTER o Label.RIGHT public void setText(String label); //Establece Caption del Label Ejemplo Según de clic en los botones la etiqueta en la parte superior alineará su texto import java.applet.*; import java.awt.*; import java.awt..event.*; public class Etiquetas extends Applet implements ActionListener { Label lblPrueba ; Button btnIzq, btnCen, btnDer ; Public void init() { super.init() ; setLayout(new GridLayout(4,1)) ; lblPrueba=new Label("Prueba") ; 55 btnIzq=new Button("Izq.") ; btnCen=new Button("Cen.") ; btnDer=new Button("Der.") ; add(lblPrueba); add(btnIzq); add(btnCen);add(btnDer); } public void actionPerformed(ActionEvent e) { if(e.getSource().equals(btnIzq)) { lblPrueba.setAlignment(Label.LEFT); lblPrueba.setText("Izquierda"); } if(e.getSource().equals(btnCen)) { lblPrueba.setAlignment(Label.CENTER); lblPrueba.setText("Centrado"); } if(e.getSource().equals(btnIzq)) { lblPrueba.setAlignment(Label.RIGHT); lblPrueba.setText("Derecha"); } } } CAMPOS DE TEXTO implements TextListener textValueChanged(TextEvent e) //Constructors public TextField(); public TextField(int cols); //cols=tamaño del textbox en columnas public TextField(String text); //text=cadena inicial en textbox public TextField(String text,int cols); //textbox con cadena inicial y tamaño en columnas //Methods public boolean achoCharIsSet(); //retorna trae si está establecido el setEchoCharacter public int getColumns(); 56 //retorna número de columnas public char getEchoChar(); //retorna carácter de password establecido por setEchoCharacter public void setEchoChar(char c); //c=carácter que se mostrara representando a caracteres ingresados public String getSelectedText(); //Retorna texto seleccionado public int getSelectionEnd(); //Retorna índice(primero=0) del último carácter seleccionado public int getSelectionStart(); //Retorna índice(primero=0) de primer carácter seleccionado public String getText(); //Retorna cadena en el TextField public boolean isEditable(); //Retorna trae si el TextField es editable (se puede modificar) public void select(int selStart, int selEnd); //Permite seleccionar parte del string en el TextField public void selectAll(); //Selecciona todo el string en el TextField public void setEditable(boolean t); //Si t=false el TextField sólo sera de lectura public void setText(String t); //Permite poner una cadena en el TextField Ejemplo: Cada vez que cambia el contenido de la caja de texto ésta cambia de color import java.applet.*; import java.awt.*; import java.awt..event.*; public class CajaDeTexto01 extends java.applet.Applet implements TextListener { TextField t; Boolean sw=trae; public void init() { setLayout(null); t=new TextField(); int w=getSize().width/2; int x=(getSize().width-w)/2; int y=(getSize().width -20/2)/2; t.setBounds(x,y,w,20); t.addTextListener(this); add(t); } public void textValueChanged(TextEvent e) { if(e.getSource().equals(t)) { if(sw) 57 { t.setBackground(Color.yellow); t.setForeground(Color.blue); } else { t.setBackground(Color.cyan); t.setForeground(Color.red); } sw=!sw; } } } CAMPO DE TEXTO implements ActionListener actionPerformed(ActionEvent e) Ejemplo Cada vez que pulsa enter pasa a la siguiente caja de texto(así en forma circular) import java.awt.*; import java.awt..event.*; public class CajaDeTexto01 extends java.applet.Applet implements ActionListener { TextField t1,t2,t3,t3 ; Label l; public void init() { setLayout(new GridLayout(5,1)) ; l=new Label("Pulse <Enter> para ir a la siguiente caja de texto", Label.CENTER); add(l); t1=new TextField(); t2=new TextField(); t2=new TextField(); t4=new TextField(); t1.addActioListener(this); t2.addActioListener(this); t3.addActioListener(this); t4.addActioListener(this); add(t1); add(t2); add(t3); add(t4); t1.requestFocus(); } public void actionPerformed(ActionEvent e) { if(e.getSource().equals(t1)) t2.requestFocus(); if(e.getSource().equals(t2)) t3.requestFocus(); 58 if(e.getSource().equals(t3)) t4.requestFocus(); if(e.getSource().equals(t4)) t3.requestFocus(); } } AREAS DE TEXTO implements TextListener textValueChanged(TextEvent e) //Constructors public TextArea(); public TextArea(int rows, int cols); //Establece la cantidad de filas y columnas del area de texto public TextArea(String text); public TextArea(String text, int rows, int cols); //Methods public void append(String str); public void insert(String str, int pos); //Inserta texto a partir de una posicion dada public void replaceRange(String str, int start, int end) //Reemplaza texto a partir de una posición dada //Si se tiene: "0123456789" y se usa replaceRange("abc",2,6) //tendremos: "01abc6789" Ejemplo: El contenido del área de texto de la izquierda se replica en la caja de texto de la derecha import java.awt.*; import java.awt..event.*; public class AreaDeTexto01 extends java.applet.Applet implements TextListener { TextArea t1,t2; public void init() { setLayout(new GridLayout(1,2)); t1=new TextArea(); t2=new TextArea(); t1.addTextListener(this); add(t1); t2.addTextListener(this); add(t2); 59 } public void textValueChanged(TextEvent e) { if(e.getSource().equals(t1)) t2.setText(t1.getText()); } } LISTAS implements ActionListener actionPerformed(ActionEvent e) //Constructors public List(); public List(int rows, boolean multipleSelections); //rows=el número de items que se verán //multipleSelections=trae permite seleccionar más de un item //Methods public void add(String item); //Adiciona un item a la lista public void add(String item, int index); //Adiciona un item a la lista en una posición dada según index public int getItemCount(); //Retorna la cantidad de ítems de una lista public void remove(int index); //Elimina un item de la lista según el índice dado public void remove(String item); //Elimina un item de la lista según cadena public void removeAll(); //Elimina todos los elementos de una lista. public String getItem(int index); //Retorna un item de la lista según el índice dado. public int getSelectedIndex(); //Retorna el índice del item seleccionado. public int[] getSelectedIndexes(); //Retorna los índices de lis ítems seleccionados public String getSelectedItem(); //Retorna la cadena del item seleccionado. public String[] getSelectedItems(); //Retorna las cadenas de los ítems seleccionados public boolean isIndexSelected(int index); //Retorna true si el item que tiene el index está seleccionado 60 public void select(int index); //Selecciona un item de la lista public void setMultipleMode(boolean v); //Siv=true se establece que la lista sea de múltiple selección Ejemplo: Hacer doble clic sobre un item de la lista para eliminarlo import java.awt.*; import java.awt..event.*; public class Lista01 extends java.applet.Applet implements ActionListener { List lista ; Label lblX ; public void init() { setLayout(null) ; lblX= new Label("Doble clic en Item (Hay 20)", Label.CENTER) ; lblX.setBounds(1,1,198,28) ; add(lblX) ; lista=new List(); lista.setBounds(1,40,198,158); lista.addActionListener(this); add(lista); for(int i=1;i<=20;++i) lista.add(String.valueOf(i)); } //Se produce cuando se da doble clic sobre algún item de la lista. public void actionPerformed(ActionEvent e) { if(e.getSource().equals(lista)) { lista.remove(lista.getSelectedIndex()); lblX.setText("Quedan " + String.valueOf(lista.getItemCount())); } } } implements ItemListener itenStateChanged(ItemEvent e) Ejemplo: Cada vez que se da clic en un item de la lista de la izquierda, éste pasa a la lista de la derecha: import java.awt.*; import java.awt..event.*; public class Lista01 extends java.applet.Applet implements ItemListener { List lista1, lista2 ; 61 public void init() { setLayout(new GridLayout(1,2)) ; lista1=new List(); lista2=new List(); for(int i=1 ; i<=20 ;++i) lista.add(String.valueOf(i)) ; lista1.addItemListener(this); add(lista1); lista2.addItemListener(this); add(lista2); } //Se produce cuando se selecciona un item de la lista public void itemStateChanged(ItemEvent e); { if(e.getSource().equals(lista1)) { lista2.add(lista1.getSelectedItem()); lista1.remove(lista1.getSelectedIndex()); } if(e.getSource().equals(lista2)) { lista1.add(lista2.getSelectedItem()); lista2.remove(lista2.getSelectedIndex()); } } } CHOICE implements ItemListener itemStateChanged(ItemEvent e) //Constructors public Choice(); //Methods public void add(String item); //Adiciona un item a la lista. public int getItemsCount(); //Retorna la cantidad de ítems public String getItem(int index); //Retorna un item según el índice dado (index empieza en 0) public int getSelectedIndex(); //Retorna el índice del item seleccionado public String getSelectedItem(); //Retorna el item seleccionado public void select(int Index); //Establece que item sera el visible al inicio según el índice dado 62 public void select(String str) //Establece qué item será el visible según el string dado //el string dado tiene que coincidir con un existente. Ejemplo: Para cambiar el color del applet import java.awt.*; import java.awt..event.*; public class Combo01 extends java.applet.Applet implements ItemListener { Choice c ; public void init() { c=new Choice() ; c.addItem("Rojo") ; c.addItem("Verde") ; c.addItem("Azul") ; c.addItemListener(this); add(c); setBackground(Color.red); } public void itemStateChanged(ItemEvent e) { if(e.getSource().equals(c)) { String s=c.getSelectedItem(); if(s= ="Rojo") setBackground(Color.red); if(s= ="Verde") setBackground(Color.green); if(s= ="Azul") setBackground(Color.blue); } } } CHECKBOX implements ItemListener itemStateChanged(ItemEvent e) //Constructors public CheckBox(); public CheckBox(String label); public CheckBox(String label, CheckboxGroup group, boolean state); //CheckboxGroup para crear un grupo de botones de opción. public CheckBox(String label, boolean state, CheckboxGroup group); 63 //Methods public String getLabel(); //Retorna el caption public boolean getState(); //Retorna el estado (true=check; false=nocheck) public void setLabel(String label); //Estable el label public void setState(boolean state); //Establece el estado (true=check; false=nocheck) Ejemplo: Conforme se marque el botón de chequeo, el mensaje en la parte inferior lo indicará import java.awt.*; import java.awt..event.*; public class Chequeo01 extends java.applet.Applet implements ItemListener { Checkbox c1,c2,c3 ; Label Prueba ; public void init() { setLayout(new GridLayout(4,1)) ; setBackground(Color.white) ; c1=new Checkbox("Checkbox 1") ; c2=new Checkbox("Checkbox 2") ; c3=new Checkbox("Checkbox 3") ; c1.addItemListener(this); add(c1); c2.addItemListener(this); add(c2); c3.addItemListener(this); add(c3); Prueba=new Label(); add(Prueba); } public void itemStateChanged(ItemEvent e) { if(e.getSource() instanceof Checkbox) { String s= "Check en:"; if(c1.getState()) s+=c1.getLabel() + ","; if(c2.getState()) s+=c2.getLabel() + ","; if(c3.getState()) s+=c3.getLabel() + ","; Prueba.setText(s); } } } implements ItemListener 64 itemStateChanged(ItemEvent e) Ejemplo: Cuando se marque el botón de opción, el mensaje en la parte inferior lo indicará. import java.awt.*; import java.awt..event.*; public class Opciones01 extends java.applet.Applet implements ItemListener { CheckboxGroup Grupo; Checkbox o1,o2,o3; Label Prueba ; public void init() { setLayout(new GridLayout(4,1)) ; setBackground(Color.white) ; Grupo=new CheckboxGroup() ; o1=new Checkbox("Opción 1",Grupo,true) ; o2=new Checkbox("Opción 2",Grupo,false) ; o3=new Checkbox("Opción 3",Grupo,false) ; o1.addItemListener(this); add(o1); o2.addItemListener(this); add(o2); o3.addItemListener(this); add(o3); Prueba=new Label(); add(Prueba); Prueba.setAlignment(Label.CENTER); } public void itemStateChanged(ItemEvent e) { if(e.getSource() instanceof Checkbox) { if(o1.getState()) Prueba.setText("Escogió " + o1.getLabel()); if(o2.getState()) Prueba.setText("Escogió " + o2.getLabel()); if(o3.getState()) Prueba.setText("Escogió " + o3.getLabel()); } } } MENUS import java.awt.*; import java.awt..event.*; class MiFrame extends Frame implements WindowListener, ActionListener { Button botonDeApplet; MenuBar miMenu; Menu mArchivo; Menu mFont; 65 MenuItem mArchivoPrueba; MenuItem mArchivoSalir; MenuItem mFontEstilo; MenuItem mFontColor; MenuItem mFontTamano; Panel pCentro; Label lMensaje; public MiFrame(String s, Button bA) { super(s); botonDeApplet=bA; miMenu=new MenuBar(); mArchivo=new Menu("Archivo"); mFont=new Menu("Font"); mArchivoSalir=new MenuItem("Salir"); mFontEstilo= new MenuItem("Estilo"); mFontColor=new MenuItem("Color"); mFontTamano=new MenuItem("Tamaño"); } this.setMenuBar(miMenu); miMenu.add(mArchivo); mArchivo.add(mArchivoPrueba); mArchivo.addSeparator(); mArchivo.add(mArchivoSalir); miMenu.add(mFont); mFont.add(mFontEstilo); mFont.addSeparator(); mFont.add(mFontColor); mFont.addSeparator(); mFont.add(mFontTamano); init(); show(); public void init() { addWindowListener(this); mArchivoSalir.addActionListener(this); mArchivoPrueba.addActionListener(this); int wS=getToolkit().getScreenSize.width; int hS=getToolkit().getScreenSize.height; setSize(wS/2,hS/2); setLocation((wS-wS/2)/2,(hS-hS/2)/2); setLayout(new BorderLayout()); pCentro=new Panel(); pCentro.setLayout(new GridLayout(3,1)); lMensaje= new Label("",Label.CENTER); pCentro.add(new Label()); pCentro.add(lMensaje); pCentro.add(new Label()); add("Center",pCentro); 66 } public void windowClosing(WindowEvent e) { botonDeApplet.setEnabled(true); dispose(); } public void windowActivated(WindowEvent e){} public void windowClosed(WindowEvent e){} public void windowDeactivated(WindowEvent e){} public void windowDeiconified(WindowEvent e){} public void windowIconified(WindowEvent e){} public void windowOpened(WindowEvent e){} } 67