Você está na página 1de 741

1 Explicando la Tecnología Java

Este capítulo es la introducción al lenguaje Java. A lo largo del mismo


describiremos los conceptos fundamentales del lenguaje de programación Java,
aprenderemos de los tres grupos de productos de la tecnología Java y
resumiremos sus etapas de ciclo de vida.
1.1 Objetivos

Una vez finalizado este capítulo, usted será capaz de:

• Describir los paradigmas de programación.


• Definir los conceptos de compilador e intérprete.
• Describir los conceptos fundamentales del lenguaje de programación
Java
• Listar los tres grupos de productos de la tecnología Java
• Resumir cada una de las siete etapas del ciclo de vida de un producto.

Este capítulo ofrece una visión general de la tecnología Java, de la


programación basada en la tecnología Java y del ciclo de vida del desarrollo de
una aplicación.

Discusión – Las siguientes preguntas son relevantes para comprender de


qué se trata la tecnología Java:

• ¿Cuál es su definición para las siguientes palabras?


• Seguridad
• Orientación a Objetos
• Independencia
• Dependencia
• Distribución
• ¿Cuáles son las etapas comprendidas en la construcción de alguna cosa,
como por ejemplo una casa o un mueble?

1.2 Recursos Adicionales

Recursos adicionales – Las siguientes referencias proporcionan información


adicional sobre los temas descritos en este capítulo:

• Java Technology: An Early History. (Online). Disponible en:


http://java.sun.com/features
/1999/05/birthday.html
Una historia del quinto aniversario del grupo y las personas que
participaron en el desarrollo de la tecnología Java.

• The Java Tutorial. (Online). Disponible en:


http://java.sun.com/docs
/books/tutorial/
Una guía práctica para programadores con cientos de ejemplos
completos y en funcionamiento.

1.3 Introducción a los Lenguajes de Programación

Un paradigma de programación es un modelo básico de diseño e


implementación de programas. Un modelo que permite desarrollar programas
conforme a ciertos principios o fundamentos específicos que se aceptan como
válidos. Es una colección de modelos conceptuales que juntos modelan el
proceso de diseño, orientan la forma de pensar y solucionar los problemas y,
por lo tanto, determinan la estructura final de un programa.

A los paradigmas se los podría clasificar de diversas maneras según los


criterios que se prioricen. Pero partiendo de los principios fundamentales de
cada paradigma en cuanto a las orientaciones sobre la forma para construir las
soluciones, podemos distinguir entre los procedurales y los declarativos.
1.3 Introducción a los Lenguajes de Programación
1.3.1 Paradigmas Procedurales

También llamados operacionales, la característica fundamental de estos


paradigmas es la secuencia computacional realizada etapa a etapa para
resolver el problema.

Los programas realizados con lenguajes procedurales deben incluir en su


codificación las instrucciones de control para determinar el flujo de la ejecución,
como decisiones, iteraciones y otras, conformando, de esta manera, diferentes
“algoritmos”.

Actúan modificando repetidamente la representación de sus datos. Hay una


estrecha relacion entre las variables y las posiciones de memoria del equipo.
Al ejecutar un programa los valores en las posiciones de memoria se actualizan
repetidamente, ya que las variables reciben multiples asignaciones, el valor
final de las variables es el resultado.

Su mayor dificultad reside en saber si el valor obtenido es el correcto, por lo


que se han desarrollado multitud de técnicas de depuración y verificación para
probar la corrección de los problemas desarrollados basándose en este tipo de
paradigmas.
En otras palabras, se basan en “cómo” lograr la solución.

Orientación a Objetos

El paradigma de objetos, o como se lo conoce generalmente, la Programación


Orientada a Objetos, se fundamenta en concebir a un sistema como un
conjunto de entidades que representan al mundo real, los “objetos”, que tienen
distribuida la funcionalidad e información necesaria y que cooperan entre sí
para el logro de un objetivo común.

Objetos + Mensaje = Programa

La Orientación a Objetos permite una representación más directa del modelo


de mundo real, reduciendo fuertemente la transformación radical normal desde
los requerimientos del sistema, definidos en términos del usuario, a las
especificaciones del sistema, definidas en términos del computador.

El lenguaje originario y paradigmático de la programación en objetos es


SMALLTALK . Actualmente de los existentes hoy en día el más conocido es
el JAVA.

Paradigma Imperativo

Su esencia es resolver un problema complejo mediante la ejecución repetitiva y


paso a paso de operaciones y cálculos sencillos con la asignación de los
valores calculados a posiciones de memoria.
La programación en este paradigma consiste en determinar qué datos son
requeridos para el cálculo, asociar a esos datos una dirección de memoria, y
efectuar, paso a paso, una secuencia de transformaciones en los datos
almacenados, de forma que el estado final represente el resultado correcto.

Algoritmo + Estructuras de Datos = Programa

1.3 Introducción a los Lenguajes de Programación


1.3.2 Paradigmas
Declarativos

Los paradigmas declarativos se basan en desarrollar programas especificando


o “declarando” un conjunto de proposiciones, condiciones, restricciones,
afirmaciones, ecuaciones o transformaciones que caracterizan al problema y
describen su solución.

A partir de esta información el sistema utiliza mecanismos internos de control


que evalúan y relacionan adecuadamente dichas especificaciones, de manera
de obtener la solución.

Estos paradigmas permiten utilizar variables para almacenar valores


intermedios, pero no para actualizar estados de información. Si bien sus
variables se relacionan con posiciones de memoria, no existe el concepto de
reasignación de un valor a una variable.

Como estos paradigmas especifican la solución sin indicar cómo construirla, en


principio, eliminan la necesidad de probar que el valor calculado es el valor
solución.

En otras palabras, se basan en “qué” es necesario especificar.

Paradigma Funcional

El paradigma funcional está basado en el modelo matemático de composición


funcional. En este modelo, el resultado de un cálculo es la entrada del siguiente,
y así sucesivamente hasta que una composición produce el resultado deseado.
Así, un programa es un conjunto de funciones que cooperan entre ellas para el
logro de un objetivo común.

Funciones + Estructura de Datos = Programa

El lenguaje más típico del paradigma funcional es el LISP y existen otros


lenguajes similares más modernos como GOFER y HASKELL.

Paradigma Lógico

El paradigma lógico tiene como característica principal la aplicación de las


reglas de la lógica para inferir conclusiones a partir de datos. Conociendo la
información y las condiciones del problema, la ejecución de un programa
consiste en la búsqueda de un objetivo dentro de las declaraciones realizadas.
Esta forma de tratamiento de la información permite pensar la existencia de
“programas inteligentes” que puedan responder, no por tener en la base de
datos todos los conocimientos, sino por poder inferirlos a través de la
deducción.

Lógica + Control = Programa

El PROLOG es el lenguaje emblemático del paradigma. Existen diferentes


versiones y variantes, pero todas basadas en las misma raíz.
1.4 Compiladores e Intérpretes

En 1946 se desarrolló el primer ordenador digital, en cuya construcción


participó John von Neumann (junto con John Presper Eckert y John W.
Mauchly), el ENIAC .

Durante el transcurso de la utilización de ENIAC observaron una serie


de limitaciones. Decidieron entonces definir todo un nuevo sistema lógico de
computación basado en las ideas de máquina universal de Turing y
se centraron en el diseño y la construcción de una computadora más poderosa
el EDVAC (Electronic Discrete Variable Arithmetic Computer) .

En un principio, estas máquinas ejecutaban instrucciones consistentes en


códigos numéricos que señalan a los circuitos de la máquina los estados
correspondientes a cada operación. Esta expresión mediante códigos
numéricos se llamó Lenguaje de Máquina, interpretado por un secuenciador
cableado o por un microprograma. Pero los códigos numéricos de las máquinas
son engorrosos. Pronto los primeros usuarios de estos ordenadores
descubrieron la ventaja de escribir sus programas mediante claves más fáciles
de recordar que esos códigos numéricos; al final, todas esas claves juntas se
traducían manualmente a Lenguaje de Máquina. Estas claves constituyen los
llamados lenguajes ensambladores, que se generalizaron en cuanto se dio el
paso decisivo de hacer que las propias máquinas realizaran el proceso
mecánico de la traducción. A este trabajo se le llama ensamblar el programa.
1.4 Compiladores e Intérpretes
1.4.1 Compiladores

Un compilador es un programa que lee un programa escrito en un lenguaje


fuente y lo traduce a un programa equivalente en otro lenguaje, el lenguaje
objeto. Como parte importante de este proceso de traducción, el compilador
informa al usuario de la presencia de errores en el programa fuente.
1.4 Compiladores e Intérpretes
1.4.2 Intérpretes

En informática, un intérprete es un programa capaz de analizar y ejecutar otros


programas, escritos en un lenguaje de alto nivel. Los intérpretes suelen
contraponerse a los compiladores, ya que mientras que los segundos se
encargan de traducir un programa desde su descripción en un lenguaje de
programación al código máquina del sistema destino, los primeros sólo realizan
la traducción a medida que sea necesario y normalmente, no guardan el
resultado de dicha traducción.

Los intérpretes, en lugar de producir un programa objeto como resultado de


una traducción, recorren el código fuente una línea cada vez. Para cada
sentencia que compone el código de entrada, se realiza una traducción, se
ejecuta dicha sentencia y se reinicia el proceso con la sentencia siguiente.
1.5 Conceptos Fundamentales del Lenguaje de Programación Java

El lenguaje de programación Java se originó en 1991 como parte de un


proyecto de investigación para desarrollar un lenguaje de programación,
llamado "Oak", que permitiera hacer un puente para disminuir la brecha de
comunicación entre varios tipos de dispositivos electrónicos, como por ejemplo
reproductores de video casettes (VCR) y televisores. Específicamente, un
equipo de desarrolladores de software altamente calificado (denominado el
equipo Verde) quería crear un lenguaje de programación que permitiera a los
dispositivos electrónicos con distintas unidades centrales de proceso (CPU)
compartir los mismos beneficios del software.

El concepto inicial falló después de varios intentos con las compañías


fabricantes de dispositivos electrónicos. El equipo Verde fue entonces
orientado a buscar otro mercado para el nuevo lenguaje de programación.
Afortunadamente, comenzó la popularidad del World Wide Web y el equipo
reconoció que el lenguaje Oak era perfecto para desarrollar componentes
multimedia para mejorar las páginas Web. Estas pequeñas aplicaciones,
denominadas applets, constituyeron el primer uso del lenguaje Oak y los
programadores usuarios de Internet adoptaron lo que se constituiría en el
lenguaje de programación Java.

El lenguaje de programación Java fue diseñado para ser:

• Orientado a objetos.
• Distribuido.
• Simple.
• Multihilo.
• Seguro.
• Independiente de la plataforma.

Historia de Sun y La Tecnología Java

La siguiente referencia ofrece información adicional sobre la historia de Sun y


Java:

Historia de Sun y La Tecnología Java

1.5 Conceptos Fundamentales del Lenguaje de Programación


Java
1.5.1 Orientado
a Objetos

El lenguaje de programación Java es un lenguaje de programación orientado a


objetos (POO ) porque uno de los principales objetivos del programador en
tecnologías Java es crear objetos, fragmentos de código autónomo, que
puedan interactuar con otros objetos para resolver un problema. La POO
comenzó con el lenguaje de programación SIMULA-67 en 1967 y condujo a la
creación de otros lenguajes de programación populares, como C++, en el que
el lenguaje de programación Java está basado.

La POO difiere de la programación procedural en que esta última hace


hincapié en la secuencia de los pasos requeridos para resolver un problema,
mientras que la POO hace hincapié en la creación y la interacción de los
objetos.
1.5 Conceptos Fundamentales del Lenguaje de Programación
Java
1.5.2 Distribuido

El lenguaje de programación Java es un lenguaje distribuido porque ofrece


soporte para tecnologías de redes distribuidas, tales como Remote Method
Invocation (RMI), Common Object Request Broker Architecture (CORBA), y
Universal Resource Locator (URL).

Adicionalmente, la capacidad de carga dinámica de clases de la tecnología


Java permite que fragmentos de código sean descargados a través Internet y
ejecutados sobre un computador personal.
1.5 Conceptos Fundamentales del Lenguaje de Programación
Java
1.5.3 Simple

El lenguaje de programación Java es simple debido a que sus diseñadores


quitaron algunas de las construcciones complejas y poco claras que se
encuentran en otros lenguajes de programación populares.

Por ejemplo, el lenguaje de programación Java no permite que los


programadores puedan manipular directamente punteros a ubicaciones de
memoria, lo que constituye una característica compleja de los lenguajes de
programación C y C++, que frecuentemente es mal usada.

En cambio, el lenguaje de programación Java sólo permite a los


programadores manipular objetos usando referencias a objetos. El lenguaje de
programación utiliza también una característica denominada "garbage collector"
para controlar y eliminar aquellos objetos que ya no están referenciados.

Otra característica que hace simple al lenguaje de programación Java es que


un tipo de datos boolean puede tener un valor true (verdadero) o false (falso), a
diferencia de otros lenguajes de programación donde el valor también puede
ser 1 o 0.

1.5 Conceptos Fundamentales del Lenguaje de Programación


Java
1.5.4 Multihilo
El lenguaje de programación Java ofrece facilidades de multihilo, esto es,
realizar varias tareas al mismo tiempo, tales como consultar una base de datos
y desplegar una interfaz de usuario. La tecnología multihilo posibilita que un
programa basado en la tecnología Java sea muy eficiente en el uso que hace
de los recursos del sistema.

1.5 Conceptos Fundamentales del Lenguaje de Programación


Java
1.5.5 Seguro

Los programas basados en la tecnología Java son seguros debido a que el


lenguaje de programación Java, junto al entorno en el cual estos programas
ejecutan, utilizan medidas de seguridad para proteger los programas de
ataques externos. Estas medidas incluyen:

• Prohibir el manejo de la memoria usando punteros


• Prohibir a los programas distribuidos, tales como los applets, leer y
escribir a un disco duro de un computador.
• Verificar que todos los programas basados en la tecnología Java
contienen código válido.
• Soportar firmas digitales. El código basado en la tecnología Java puede
ser "firmado" por una compañía o por una persona de forma tal que otra
persona que reciba el código pueda verificar la legitimidad del mismo.

1.5 Conceptos Fundamentales del Lenguaje de


Programación Java
1.5.6 Independiente
de la
Plataforma

Los programas escritos en otros lenguajes requieren generalmente numerosas


modificaciones para poder ejecutarse sobre más de un tipo de plataforma
(computacional). Se entiende por plataforma computacional una combinación
de una CPU y un sistema operativo. Esta dependencia de la plataforma se
debe a que muchos lenguajes requieren que se escriba código específico para
la plataforma subyacente.

Los lenguajes de programación populares como C y C++ requieren que el


programador compile y ensamble sus programas, obteniendo como resultado
un programa ejecutable único para una determinada plataforma. A diferencia
de C y C++, el lenguaje de programación Java es independiente de la
plataforma.

Programas dependientes de la plataforma


Un compilador es una aplicación que convierte un programa escrito por un
programador, en un código específico para una CPU. A este código se le
denomina código de máquina. Estos archivos específicos para cada plataforma
(archivos binarios) se combinan frecuentemente con otros archivos, tales como
bibliotecas que contienen código previamente escrito, usando un ensamblador
para crear un programa dependiente de la plataforma (al que se le denomina
ejecutable), y que puede ser ejecutado por un usuario final.

Programas independientes de la plataforma

Un programa basado en la tecnología Java puede ejecutar sobre varias


combinaciones de CPUs y sistemas operativos diferentes, tales como:

• Solaris OS sobre un chip SPARC®,


• MacOS sobre un chip Motorola y Microsoft Windows sobre un chip Intel,
generalmente con muy pocas o sin modificaciones

Al igual que los programas C y C++, los programas basados en la tecnología


Java también son compilados usando un compilador de la tecnología Java. Sin
embargo, el formato resultante de un programa basado en la tecnología Java
compilado es bytecode de la tecnología Java independiente de la plataforma,
en lugar de código de máquina específico
para una CPU. Luego que el bytecode es creado, es interpretado (ejecutado)
por un intérprete de bytecode denominado la máquina virtual (o MV).

Una máquina virtual es un programa para una plataforma específica que


entiende bytecode independiente de la plataforma y que puede ejecutarlo sobre
una plataforma particular.

Por esta razón, el lenguaje de programación Java es frecuentemente referido


como un lenguaje interpretado y los programas con tecnología Java son
denominados portables o ejecutables sobre cualquier plataforma. Existen
varios lenguajes interpretados, como por ejemplo Perl.

Para que los programas con tecnología Java sean independientes de la


plataforma, se requiere una máquina virtual, denominada la "Máquina Virtual
Java", sobre cada plataforma donde los programas serán ejecutados. La
Máquina Virtual Java es responsable de:

• interpretar el código basado en la tecnología Java,


• cargar las clases Java y
• ejecutar los programas basados en la tecnología Java.

Sin embargo, un programa basado de la tecnología Java necesita más que una
Máquina Virtual Java para poder ejecutar. Un programa basado en la
tecnología Java necesita también un conjunto de bibliotecas de clases Java
estándar para la plataforma. Las bibliotecas de clases Java son bibliotecas de
código previamente escrito que pueden ser combinadas con el código que
usted escriba para crear aplicaciones robustas.
Conjuntamente, el software de la JVM y las bibliotecas de clase Java, son
denominados el entorno de ejecución de Java (Java runtime environment JRE).
Los entornos de ejecución de Java están disponibles en Sun Microsystems
para muchas de las plataformas más comunes.

La capacidad de los programas basados en la tecnología Java para ejecutar


sobre todas las plataformas es central en el lema de Sun Microsystems
"Escribir una vez, ejecutar en cualquier lugar™" ("Write Once, Run
Anywhere™").
1.6 Grupos de Productos de la Tecnología Java

Sun Microsystems provee una línea completa de productos de tecnología Java


que van desde kits que crean programas basados en la tecnología Java hasta
entornos para emular dispositivos electrónicos y móviles (por ejemplo,
teléfonos móviles).

1.6 Grupos de Productos de la


Tecnología Java
1.6.1 Identificación de los Grupos de
Productos de la Tecnología Java

Las tecnologías Java, tales como la Máquina Virtual Java, están incluidas (en
diferentes formas) en tres grupos de productos, cada uno de los cuales fue
diseñado para satisfacer las necesidades de un mercado objetivo paricular:

• Java™ 2 Platform, Standard Edition (J2SE™) - Permite el desarrollo de


applets y aplicaciones que ejecutan en el navegador Web y sobre el
escritorio del computador, respectivamente. Por ejemplo, usted puede
utilizar el J2SE Software Developer s Kit (SDK) para crear un programa
procesador de palabras para un computador personal.
• Java™ 2 Platform, Enterprise Edition (J2EE™) - Permite crear grandes
aplicaciones empresariales distribuidas cliente-servidor. Por ejemplo, se
puede utilizar el J2EE SDK para crear una aplicación correspondiente al
sitio web de una compañía de ventas al por menor, implementando
sobre él una tienda virtual (eCommerce o Comercio Electrónico)
• Java™ 2 Platform, Micro Edition (J2ME™) - Permite crear aplicaciones
para dispositivos electrónicos con recursos restringidos. Por ejemplo,
usted puede utilizar J2ME SDK para crear un juego que ejecute en un
teléfono móvil.

Entre otras tecnologías Java, cada edición incluye un kit de desarrollo de


software (SDK) que permite a los programadores crear, compilar y ejecutar sus
programas basados en la tecnología Java sobre una plataforma particular.
1.6 Grupos de Productos de la Tecnología
Java
1.6.2 Selección del Grupo de
Productos de la Tecnología
Java Correctos

Mientras que muchos programadores Java se especializan en el desarrollo de


aplicaciones para un mercado objetivo particular, los programadores
generalmente comienzan sus carreras creando aplicaciones y applets para
computadores personales.

Por lo tanto, el kit J2SE SDK es el grupo de productos más usado por los
programadores mientras aprenden el lenguaje de programación Java™.
1.6 Grupos de Productos de la Tecnología
Java
1.6.3 Uso de los componentes
del SDK de la Plataforma
Java 2, Standard Edition

Sun Microsystems ha desarrollado una versión del SDK para la Plataforma


Java 2, Standard Edition para:

• el sistema operativo Solaris OS sobre el chip SPARC (32 bits),


• el sistema operativo Microsoft Windows sobre el chip Intel (32 bits),
• el sistema operativo Linux sobre el chip Intel y
• el sistema operativo Solaris OS sobre el chip SPARC (64©\bits).

El SDK para la Plataforma Java 2, Standard Edition incluye:

• El entorno de ejecución de Java:


-Una Máquina Virtual Java para la plataforma que usted seleccione.
-Las bibliotecas de clases Java para la plataforma que usted seleccione.
• Un compilador Java
• Documentación de la biblioteca de clases Java (API) (como un archivo
para ser descargado en forma independiente).
• Utilidades adicionales, tales como programas para crear archivos JAR
(Java archive) y para depurar programas basados en la tecnología Java.
• Ejemplos de programas basados en la tecnología Java.

Demostración - Su profesor realizará demostraciones de cuatro tipos de


aplicaciones basadas en la tecnología Java. Estas aplicaciones son:

• Una aplicación con tecnología J2SE SDK


• Un applet con tecnología J2SE SDK
• Una aplicación con tecnología J2EE SDK
• Una aplicación con tencología J2ME SDK

Durante la demostración, usted debería poner especial atención a:

• Cómo se ejecutan el applet y la aplicación (si la ejecución se realiza a


través del ingreso de comandos, haciendo clic sobre un ícono, etc.)
• Desde dónde se ejecutan el applet o la aplicación (si es dentro de un
navegador Web, un dispositivo de consumidor, etc.)
1.7 Etapas del Ciclo de vida de un Producto

El ciclo de vida de un producto (CVP) representa un conjunto de etapas


aceptado por la industria, que usted debería seguir cuando desarrolle un nuevo
producto. Hay siete etapas en el CVP. Estas etapas son:

1. Análisis
2. Diseño
3. Desarrollo
4. Prueba
5. Implementación
6. Mantenimiento
7. Fin de Vida
1.7 Etapas del Ciclo de vida de un
Producto
1.7.1 Etapa de Análisis

El análisis es el proceso de investigación de un problema que se quiere


resolver con un producto. Entre otras tareas, el análisis consiste en:

• Definir claramente el problema que se quiere resolver, el nicho de


Mercado al que está dirigido el producto o el sistema que desea crear. El
límite de un problema es también conocido como el alcance del proyecto.
• Identificar los sub-componentes claves que componen el producto total.
1.7 Etapas del Ciclo de vida de un Producto
1.7.2 Etapa de Diseño

El diseño es el proceso que consiste en aplicar los resultados obtenidos


durante la etapa de análisis. La primera tarea durante la etapa de diseño
consiste en desarrollar blueprints o especificaciones para los productos y
componentes del sistema.
1.7 Etapas del Ciclo de vida de un Producto
1.7.3 Etapa de Desarrollo

El Desarrollo consiste en la creación de los componentes propiamente dichos,


a partir de las maquetas o blueprints creados durante la etapa de diseño
1.7 Etapas del Ciclo de vida de un Producto
1.7.4 Etapa de Prueba

La Prueba consiste en el aseguramiento de que los componentes individuales,


o el producto en su totalidad, satisfacen los requerimientos de la especificación
creada durante la etapa de diseño.
La Prueba es realizada generalmente por un equipo integrado por personas
que no intervinieron en el desarrollo del producto. Este equipo asegura que el
producto está probado, sin ninguna intromisión por parte del desarrollador
1.7 Etapas del Ciclo de vida de un Producto
1.7.5 Etapa de
Implementación

La Implementación consiste en hacer que el producto quede disponible al


cliente.
1.7 Etapas del Ciclo de vida de un Producto
1.7.6 Etapa de Mantenimiento

El Mantenimiento consiste en la reparación de los problemas que puedan surgir


con el producto y liberar el producto como una nueva versión o revisión

1.7 Etapas del Ciclo de vida de un Producto


1.7.7 Etapa Fin de Vida

Mientras que el CVP no tiene una etapa separada para el comienzo de un


concepto o proyecto, tiene una etapa para el fin del proyecto. Esta etapa de fin
de vida consiste en realizar todas las tareas necesarias para asegurar que los
clientes y empleados están concientes de que el producto ya no será vendido,
que ya no se ofrecerán servicios a los clientes y que está disponible un nuevo
producto.
1.7 Etapas del Ciclo de vida de un Producto
1.7.8 Por qué se debería seguir el
CVP

El CVP es una parte importante del desarrollo de un producto dado que ayuda
a asegurar que los productos serán creados y entregados de forma tal que:

• el tiempo de salida al mercado sea reducido,


• el producto tenga alta calidad, y
• el retorno de la inversión sea maximizado

Los desarrolladores que no siguen el CVP frecuentemente encuentran


problemas con sus productos, ya que suelen ser costosos de reparar y
hubiesen podido ser evitados
1.7 Etapas del Ciclo de vida de un Producto

Ahora le proponemos que resuelva el siguiente ejercicio para poner a prueba


sus conocimientos.

¡Éxitos!
1.8 Preguntas de Repaso

Ahora le proponemos que resuelva el siguiente ejercicio para poner a prueba


sus conocimientos.

Defina por Verdadero o Falso las siguientes afirmaciones según corresponda.


¡Éxitos!
2 El Análisis de un Problema y el Diseño de una Solución

En el transcurso de éste capítulo aprenderemos a analizar un problema usando


el análisis orientado a objetos, también diseñaremos las clases a partir de las
cuales crearemos objetos

2.1 Repaso del Capítulo Anterior

Los principales temas del capítulo anterior son:

Conceptos Fundamentales del Lenguaje de Programación Java

El lenguaje de programación Java fue diseñado para ser:

• Orientado a objetos.
• Distribuido.
• Simple.
• Multihilo.
• Seguro.
• Independiente de la plataforma.

Grupos de Productos de la Tecnología Java

Las tecnologías Java, tales como la Máquina Virtual Java, están incluidas (en
diferentes formas) en tres grupos de productos:

Java™ 2 Platform, Standard Edition (J2SE™)


Permite el desarrollo de applets y aplicaciones que ejecutan en el navegador
Web y sobre el escritorio del computador, respectivamente.

Java™ 2 Platform, Enterprise Edition (J2EE™)


Permite crear grandes aplicaciones empresariales distribuidas cliente-servidor.

Java™ 2 Platform, Micro Edition (J2ME™)


Permite crear aplicaciones para dispositivos electrónicos con recursos
restringidos.

Etapas del Ciclo de vida de un Producto

El ciclo de vida de un producto (CVP) representa un conjunto de etapas:

1. Análisis
2. Diseño

3. Desarrollo

4. Prueba

5. Implementación

6. Mantenimiento

7. Fin de Vida

2.3 Objetivos

Una vez finalizado este capítulo, usted debería ser capaz de:

• Analizar un problema usando análisis orientado a objetos.


• Diseñar las clases a partir de las cuales los objetos serán creados.

Este capítulo describe cómo analizar una situación y cómo desarrollar un


diseño para una aplicación.

Discusión - Las siguientes preguntas son relevantes para entender qué es el


Análisis y Diseño Orientado a Objetos (OOAD):

• ¿Cómo decidir qué componentes son necesarios para lo que usted debe
construir, como por ejemplo una casa o un mueble?
• ¿Qué es una taxonomía?
• ¿Cómo se relacionan los elementos en una taxonomía?
• ¿Cuál es la diferencia entre atributos y valores?
2.4 Recursos Adicionales

Recursos adicionales – Las siguientes referencias proveen información


adicional sobre los temas descriptos en este capítulo:

• Larman, Craig. Applying UML and Patterns - An Introduction to Object


Oriented Analysis and Design. Prentice-Hall, 1998. Este libro presenta el
proceso de OOAD en detalle y sin excesiva complejidad.
• Eckel, Bruce. Thinking in Java. Prentice-Hall, 2000.
• Fowler, Martin and Kendall Scott. UML Distilled. Addison Wesley
Longman, Inc., 1997.
• The Java Tutorial. [Online]. Disponible en:

http://java.sun.com/docs/books/
tutorial/java/concepts/object.html

http://java.sun.com/docs/books/
tutorial/java/concepts/class.html

2.5 El Análisis de un Problema Usando Análisis Orientado a Objetos


Caso de Estudio – El siguiente caso de estudio se presenta para ayudar a
ilustrar los principios de OOAD.

DirectClothing, Inc. vende camisas por catálogo. El negocio está creciendo 30


por ciento por año y por lo tanto, la empresa necesita un nuevo sistema de
ingreso de órdenes de compra. Usted ha sido contratado por DirectClothing
para diseñar este nuevo sistema.

DirectClothing produce un catálogo de prendas de vestir cada seis meses y lo


envía por correo a sus suscriptores. Cada camisa en el catálogo tiene un
identificador de ítem (ID), uno o más colores (cada uno de ellos con un código
de color diferente), uno o más tamaños, una descripción y un precio.

DirectClothing acepta cheques y la mayoría de las tarjetas de crédito.


Para ingresar una orden de compra, los clientes pueden llamar telefónicamente
a DirectClothing desde un representante de servicio al cliente (RSC) para
enviar las órdenes directamente o pueden enviar un formulario de orden de
compra por correo o por fax a DirectClothing.

Las órdenes de compra que son enviadas por correo o por fax son ingresadas
por un RSC.

A DirectClothing le gustaría dar la opción a los clientes de ingresar sus órdenes


de compra en forma online a través de Internet. El precio de los ítem
disponibles para ventas online es fijado de acuerdo al catálogo vigente.

Cuando la orden de compra es ingresada en el sistema, se verifica


la disponibilidad de cada ítem (disponibilidad real). Si uno o más ítem no están
disponibles en ese momento (en el almacén de DirectClothing), entonces la
orden de compra es colocada en estado "Pendiente" hasta que los ítem
solicitados ingresen en el almacén.

Después que todos los ítem solicitados estén disponibles, se verifica el pago y
entonces la orden es enviada al almacén para armar el paquete
correspondiente y enviarlo a la dirección del cliente.

Si la orden se recibe por teléfono, el RSC informa al cliente el ID de la orden de


compra, el cual puede ser utilizado para realizar el seguimiento de la orden a
través del proceso. El RSC también le proporciona al cliente su número de
extensión del número telefónico.
2.5 El Análisis de un Problema Usando Análisis
Orientado a Objetos
2.5.1 Identificación del
Dominio del Problema

Dado que el lenguaje de programación Java es un lenguaje orientado a objetos,


uno de los principales objetivos del programador Java es crear objetos para
construir un sistema o, más específicamente, para resolver un problema.

Al alcance del problema a resolver se le denomina "dominio del problema".

La mayoría de los proyectos comienzan definiendo el dominio del problema


mediante el relevamiento de los requerimientos del cliente y escribiendo una
declaración de alcance que describa brevemente lo que el desarrollador debe
resolver.

Por ejemplo, una declaración de alcance para el proyecto DirectClothing podría


ser: "Crear un sistema que permita a las personas que ingresan órdenes de
compra, entrar y aceptar pagos para una orden."

Después que se ha determinado el alcance del proyecto, se puede comenzar a


identificar los objetos que interactuarán para resolver el problema.

2.5 El Análisis de un Problema Usando Análisis


Orientado a Objetos
2.5.2 Identificación de
Objetos

Para validar los objetos en un dominio del problema se debe, en primer lugar,
identificar las propiedades de todos los objetos:

• Los objetos pueden ser físicos o conceptuales - Una cuenta de un


cliente es un ejemplo de un objeto conceptual dado que no es algo
tangible. Un cajero automático es algo que mucha gente puede tocar
cada día y constituye un ejemplo de un objeto físico.
• Los objetos tienen atributos (características), tales como tamaño,
nombre, forma etc. Por ejemplo, un objeto podría tener un atributo color.
El valor de todos los atributos de un objeto se denomina frecuentemente
el estado actual (o corriente) del objeto. Por ejemplo, un objeto puede
tener un atributo color con el valor "rojo" y un atributo tamaño con el
valor "grande".
• Los objetos tienen operaciones (las acciones que los objetos pueden
realizar), tales como asignar un valor, desplegar una pantalla o
incrementar la velocidad. Las peraciones generalmente afectan a los
atributos del objeto. Al conjunto de las operaciones que un objeto realiza
se conoce con el nombre de comportamiento del objeto.

Por ejemplo, un objeto puede tener una operación que permita, a otros objetos,
cambiar su atributo color de un estado a otro (por ejemplo, de rojo a azul).
Su capacidad para reconocer los objetos de su realidad, le ayudará a definir
mejor los objetos cuando aborde un problema usando análisis orientado a
objetos.
Por ejemplo, una puerta puede ser un objeto en el dominio del problema
"construir una casa". Una puerta tiene al menos un atributo que tiene un valor
(abierta o cerrada) y una operación, como por ejemplo "cerrar la puerta" o "abrir
la puerta", que permite cambiar el estado de la misma.
2.5 El Análisis de un Problema Usando
Análisis Orientado a Objetos
2.5.3 Criterios adicionales para el
Reconocimiento de Objetos

Utilice los siguientes criterios para verificar si algo debería ser considerado un
objeto en el dominio del problema:

• Relevancia para el dominio del problema.


• Existencia en forma independiente.

Para determinar si el objeto es relevante para el dominio del problema,


pregúntese a sí mismo lo siguiente:

• ¿Existe el objeto dentro de los límites del dominio del problema?


• ¿Se requiere el objeto para completar la solución del problema?
• ¿Es el objeto requerido como parte de la interacción entre el usuario y la
solución?

Existencia en Forma Independiente

Para que un ítem sea un objeto y no un atributo de otro objeto, este debe existir
en forma independiente en el contexto del dominio del problema. Los objetos
pueden estar conectados y aún así tener existencia independiente. En el caso
de estudio de DirectClothing, un cliente y una orden de compra están
conectados pero son independientes entre sí, y por lo tanto, ambos podrían ser
objetos.
Durante la evaluación de los objetos potenciales, pregúntese si el objeto
necesita existir en forma independiente, en lugar de ser un atributo de otro
objeto.
2.5 El Análisis de un Problema Usando
Análisis Orientado a Objetos
2.5.4 Identificación de Atributos
y Operaciones de Objetos

Después de identificar los objetos, se deben identificar sus atributos y sus


operaciones.
Como se describió anteriormente, los atributos definen el estado de un objeto.
Los atributos pueden ser:

• datos, como la identificación de un cliente y la identificación de la orden


para un objeto Orden,
• otros objetos, un cliente podria tener como atributo un objeto Orden de
Compra, en lugar de sólo su identificación.

Como fue mencionado anteriormente, las operaciones establecen el


comportamiento que usualmente modifica el estado de un atributo. Por ejemplo,
una orden puede ser impresa, puede tener un ítem agregado o eliminado, etc..
(El cliente o RSC debería iniciar estas acciones en la vida real, pero las
operaciones pertenecen al objeto Orden.)

Atributos que refieren a Otros Objetos

Un atributo puede ser una referencia a otro objeto. Por ejemplo, el objeto
Cliente puede tener un atributo que sea un objeto Orden. Esta asociación
podría, o no, ser necesaria dependiendo del problema que se está tratando de
resolver.
2.5 El Análisis de un Problema Usando Análisis Orientado a Objetos
2.5.5 Solución del Caso de Estudio

A continuación se presentan varias figuras que contienen una lista parcial de


los objetos (incluyendo la mayoría de sus atributos y operaciones) para el caso
de estudio DirectClothing, Inc.
2.6 Ejercicio 1: Análisis del Dominio de un Problema

El objetivo de este ejercicio es listar los objetos, atributos y operaciones en el


dominio del problema ejemplo

2.6 Ejercicio 1: Análisis del Dominio de un


Problema
2.6.1 Preparación

Lea el siguiente caso de estudio y luego modele el sistema seleccionando los


objetos, y sus atributos y operaciones

2.6 Ejercicio 1: Análisis del Dominio de un Problema


2.6.2 Tarea - Realizar el Análisis

Su tarea es realizar un análisis orientado a objetos para una aplicación basada


en la tecnología Java que realice el seguimiento de los resultados de los
partidos de fútbol. El problema debe monitorizar:

• La lista de jugadores de cada equipo


• La cantidad de goles de cada jugador
• Los partidos jugados durante una temporada, incluyendo los equipos
que jugaron entre sí, y el resultado final del partido.

La aplicación debe permitir generar resultados estadísticos por equipos,


jugadores y temporadas.

Para completar el análisis, siga los siguientes pasos:

1. Cree una lista de los objetos potenciales.


2. Aplique las reglas vistas en este módulo para decidir si todos sus objetos
son válidos.
3. Cree una lista de atributos y operaciones para cada uno de sus objetos.

2.7 Ejercicio 1: Resumen

Discusión – Tómese unos minutos para discutir qué experiencias, asuntos y


descubrimientos ha realizado durante este ejercicio.

• Experiencias
• Interpretaciones
• Conclusiones
• Aplicaciones
2.8 Diseño de las Clases

La identificación de los objetos ayuda en el diseño de las clases y en el


prototipo de cada uno de los objetos en un sistema. Por ejemplo, los
fabricantes de ventanas frecuentemente crean un único prototipo para cada
uno de los estilos de ventanas que construyen. Estos prototipos definen un
rango de colores y unos estilos que pueden ser elegidos cuando se compra
una de sus ventanas.

Dichos prototipos constituyen la base para la construcción de cualquier


cantidad de ventanas con cualquier cantidad de combinaciones de color y estilo.
En términos de diseño orientado a objetos, cada objeto (ventana) creado
usando la clase (el prototipo genérico) es llamado una instancia de una clase.
Más específicamente, cada objeto creado a partir de una clase puede tener un
estado específico (valores) para cada uno de sus atributos, pero tendrá los
mismos atributos y operaciones.

Los términos clases y objetos son usados frecuentemente en el campo de la


Biología. Por ejemplo, un biólogo marino que estudia criaturas marinas se
encarga de categorizar estas criaturas en una familia, o clase, de criaturas
marinas. En términos de OOAD, cada animal (como por ejemplo una ballena
azul) en una familia (por ejemplo ballenas) puede ser considerado un instancia
del objeto de la clase ballena.

Referido al caso de estudio DirectClothing:

• Una clase está relacionada a la definición que se realizó del objeto. Las
clases son categorías descriptivas, plantillas o blueprints. Una Camisa
puede ser una clase que defina todas las camisas que tienen una
identificación, un tamaño, un código de color, una descripción y un
precio.
• Los objetos son instancias únicas de las clases. La camisa polo, azul,
grande, que cuesta $29.99 y que tiene la identificación 62467-B es una
instancia de la clase Camisa. También lo es la camisa verde, pequeña,
que tiene el mismo precio y tiene la identificación 66889-C, asi como la
camisa estampada a $ 39.99 y con ID 09988-A. Eventualmente se
pueden tener dos objetos Camisa en la memoria con exactamente los
mismos valores en los atributos.

En el lenguaje de programación Java, los atributos son representados usando


variables, y las operaciones son representadas usando métodos. Las variables
constituyen el mecanismo del lenguaje de programación Java para almacenar
datos. Los métodos constituyen el mecanismo del lenguaje de programación
Java para ejecutar una operación.
2.8 Diseño de las Clases
2.8.1 El Modelado de Clases

La primera fase de la etapa de diseño consiste en la organización visual, o


modelado, de un programa y sus clases. Cada clase en un diseño debería
estar modelada de forma tal que se incluya su nombre en la parte superior de
un rectángulo, seguido por la lista de las variables de atributos (incluyendo el
rango de los posibles valores) y la lista de los métodos. La siguiente figura
ilustra el modelado de una clase.

Los nombres de las variables y de los métodos están escritos de forma tal que
comienzan con una letra en minúscula y las siguientes palabras en el nombre
comienzan con mayúscula. Por ejemplo, el nombre para la operación "calcular
el precio total" podría ser calcPrecioTotal() (o calcTotalPrice() en inglés). Los
paréntesis al final indican que se trata de un método
2.9 Ejercicio 2: Diseñar una Solución

El objetivo de este ejercicio es modelar un objeto utilizando la notación similar a


UML que se mostró en este módulo

2.9 Ejercicio 2: Diseñar una Solución


2.9.1 Tarea - Realizar un Diseño

Su tarea consiste en diseñar las clases para el sistema de seguimiento de


resultados de partidos de fútbol presentado anteriormente. Recuerde:

• Utilice nombres de clases, atributos, variables y métodos como los


descritos en este módulo.
• Identifique un rango de valores válidos para cada atributo (en aquellos
casos que el rango sea conocido).
• Utilice los paréntesis para identificar los métodos
2.10 Ejercicio 2: Resumen

Discusión – Tómese unos minutos para discutir qué experiencias, asuntos y


descubrimientos ha realizado durante este ejercicio.

• Experiencias
• Interpretaciones
• Conclusiones
• Aplicaciones

2.11 Preguntas de Repaso

Ahora le proponemos que resuelva el siguiente ejercicio para poner a prueba


sus conocimientos.

Defina por Verdadero o Falso las siguientes afirmaciones según corresponda.


¡Éxitos!
3 Desarrollo y Prueba de un Programa basado en la Tecnología Java

A lo largo de este capítulo identificaremos los cuatro componentes de una clase


en el lenguaje de programación Java. Usaremos el método main en una clase
de prueba y compilaremos y ejecutaremos un programa

3.1 Repaso del Capítulo Anterior

Los principales temas del capitulo anterior son:

Análisis de un Problema Usando Análisis Orientado a Objetos

Identificación del Dominio del Problema

Al alcance del problema a resolver se le denomina "dominio del problema".La


mayoría de los proyectos comienzan definiendo el dominio del problema
mediante el relevamiento de los requerimientos del cliente y escribiendo una
declaración de alcance que describa brevemente lo que el desarrollador debe
resolver.

Identificación de Objetos

Identificar las propiedades de todos los objetos:

• Los objetos pueden ser físicos o conceptuales.


• Los objetos tienen atributos (características).
• Los objetos tienen operaciones (las acciones que los objetos pueden
realizar).

Criterios para verificar si algo debería ser considerado un objeto en el


dominio del problema:

• Relevancia para el dominio del problema.


• Existencia en forma independiente.

Diseño de las Clases

• Una clase está relacionada a la definición que se realizó del objeto. Las
clases son categorías descriptivas, plantillas o blueprints. Una Camisa
puede ser una clase que defina todas las camisas que tienen una
identificación, un tamaño, un código de color, una descripción y un
precio.
• Los objetos son instancias únicas de las clases. La camisa polo, azul,
grande, que cuesta $29.99 y que tiene la identificación 62467-B es una
instancia de la clase Camisa.
Las variables constituyen el mecanismo del lenguaje de programación Java
para almacenar datos. Los métodos constituyen el mecanismo del lenguaje de
programación Java para ejecutar una operación.

Cada clase en un diseño debería estar modelada de forma tal que se incluya su
nombre en la parte superior de un rectángulo, seguido por la lista de las
variables de atributos (incluyendo el rango de los posibles valores) y la lista de
los métodos.

Los nombres de las variables y de los métodos están escritos de forma tal que
comienzan con una letra en minúscula y las siguientes palabras en el nombre
comienzan con mayúscula

3.3 Objetivos

Una vez completado este capítulo, usted debería ser capaz de:

• Identificar los cuatro componentes de una clase en el lenguaje de


programación Java.
• Usar el método main en una clase de prueba para ejecutar desde la
línea de comando un programa basado en la tecnología Java.
• Compilar y ejecutar un programa basado en la tecnología Java

Este capítulo ofrece una visión general de los componentes de una clase.
También describe cómo compilar y ejecutar un programa que consiste en
múltiple clases.

Discusión – La siguiente pregunta es relevante para comprender de qué trata


el desarrollo y prueba de clases:

¿Cómo prueba usted algo que ha construido, como por ejemplo una casa o un
mueble?
3.4 Recursos Adicionales

Recursos adicionales – Las siguientes referencias ofrecen información


adicional sobre los temas descritos en este capítulo:

• Farrell, Joyce. Java Programming: Comprehensive. 1999.


Se trata de un excelente libro para no programadores. Explica conceptos
que son pasados por alto en libros más avanzados.
• The Java Tutorial. [Online]. Disponible en:

http://java.sun.com/docs/books
/tutorial/getStarted/
cupojava/win32.html
3.5 ¿Qué es la Tecnología Java?

La tecnología Java es:

• Un lenguaje de programación.
• Un entorno de desarrollo.
• Un entorno de aplicación.
• Un entorno de despliegue.

La sintaxis del lenguaje de programación Java es similar a la sintaxis del


lenguaje C++. Usted puede usar el lenguaje de programación Java para crear
todo tipo de aplicaciones que podrían ser desarrolladas usando cualquier
lenguaje de programación convencional.

Como entorno de desarrollo, la tecnología Java le provee una amplia gama de


herramientas: un compilador, un intérprete, un generador de documentación,
una herramienta de paquetes de archivos, entre otras.

El lenguaje de programación Java se menciona generalmente en el contexto de


la World Wide Web (web) y de los navegadores que son capaces de ejecutar
programas llamados applets.

Los applets son programas escritos en el lenguaje de programación Java que


residen en servidores web. Son descargados por los navegadores al sistema
del cliente, el que posteriormente los ejecuta. Son usualmente pequeños en
tamaño para minimizar el tiempo de descarga y son invocados desde una
página web Hypertext Markup Language (HTML).

Las aplicaciones de tecnología Java son programas de escritorio que no


requieren un navegador web para ejecutarse. Típicamente, son programas de
propósito general que se ejecutan en cualquier máquina donde esté instalado
el JRE (Java runtime enviroment).

Existen dos posibles entornos de despliegue:

1. El JRE. Este es proporcionado con el Java 2 Software Development Kit


(Java 2 SDK), que contiene el conjunto de archivos class para todos los
paquetes de la tecnología Java e incluye las clases básicas del lenguaje,
componentes de GUI y colecciones de API avanzadas entre otros.
2. El que está en su navegador web. La mayoría de los navegadores
comerciales brindan un intérprete y un entorno de ejecución de la
tecnología Java.

Historia de Sun y La Tecnología Java

La siguiente referencia ofrece información adicional sobre la historia de Sun y


Java:

Historia de Sun y La Tecnología Java


3.6 Objetivos Primarios de la Tecnología Java

La tecnología Java ofrece:

1. Un lenguaje en el que es fácil programar porque:


• Elimina los escollos de otros lenguajes, como por ejemplo la aritmética
de punteros y la administración de memoria que afectan la robustez del
código.
• Está orientado a objetos para ayudarlo a visualizar el programa en
términos de la vida real.
• Permite que el código sea fácilmente entendible.
2. Un entorno interpretado que brinda:
• Velocidad de desarrollo. (Reduce el ciclo de compilación-ensamblado-
carga-prueba.)
• Portabilidad del código. (Le permite escribir un código que puede ser
ejecutado en múltiples sistemas operativos sobre cualquier JVM
certificada.)
• Posibilidad de que los programas ejecuten más de un hilo de actividad.
• Un medio para que los programas puedan cambiar dinámicamente
durante el tiempo de ejecución, permitiéndoles descargar módulos de
código.
• Un medio para garantizar la seguridad verificando los módulos de
código cargados

La arquitectura de tecnología Java usa las siguientes características para


satisfacer los objetivos enunciados previamente:

• La JVM.
• Garbage collection
• El JRE.
• Una Interfaz de la herramienta JVM

3.6 Objetivos Primarios de la Tecnología


Java
3.6.1 La Máquina Virtual de Java

La Especificación de la Máquina Virtual de Java define la JVM como:

Una máquina imaginaria que está implementada mediante software y cuya


emulación se realiza sobre una máquina real. El código para la JVM está
almacenado en archivos .class, cada uno de los cuales contiene código para, a
lo sumo, una clase pública.

La Especificación de la Máquina Virtual de Java provee las especificaciones de


la plataforma de hardware para las que hay que compilar el código de la
tecnología Java. Esta especificación le permite al software Java ser
independiente de la plataforma porque la compilación es realizada para una
máquina genérica, conocida como la Máquina Virtual de Java (JVM).Usted
puede emular esta máquina genérica en software, para ejecutar sobre varios
sistemas de ordenadores existentes o implementarlo en hardware.

El compilador toma el código fuente de la aplicación Java y genera bytecode. El


bytecode es un conjunto de instrucciones de código máquina para la JVM.
Cada intérprete de la tecnología Java, independientemente de si es una
herramienta de desarrollo de tecnología Java o si es una navegador web que
puede ejecutar applets, tiene una implementación de
la JVM.

La especificación de la JVM provee definiciones concretas para la


implementación de lo siguiente:

1. Un conjunto de instrucciones (equivalente a una Unidad Central de


Procesos [CPU]).
2. Un conjunto de registros.
3. El formato de los archivos class.
4. Un stack (o pila) en tiempo de ejecución.
5. Un heap que incluye collected garbage (resultante de la recolección).
6. Un área de memoria.
7. Un mecanismo de reportes de errores fatales.
8. Un cronómetro de alta precisión.

El formato del código de la JVM consiste en un conjunto de bytecode


compactos y eficientes. Los programas representados por bytecode en la JVM
deben mantener una disciplina de tipo apropiada. La mayoría de los chequeos
de tipo se realizan en tiempo de compilación. Cualquier intérprete de la
tecnología Java debe estar apto para ejecutar cualquier programa con archivos
class que se correspondan al formato de los archivos especificados en la
Especificación de la Máquina Virtual de Java.

El diseño de la JVM permite la creación de implementaciones para múltiples


entornos operativos. Por ejemplo, Sun Microsystems provee implementaciones
de la JVM para los entornos operativos Solaris OS, Linux y Microsoft Windows.

3.6 Objetivos Primarios de la Tecnología


Java
3.6.2 Garbage Collector

Muchos lenguajes de programación admiten que la memoria sea asignada


dinámicamente en tiempo de ejecución. El proceso de asignar la memoria varía
según la sintaxis del lenguaje, pero siempre involucra devolver un puntero a la
dirección de comienzo de un bloque de la memoria. Después que la memoria
asignada ya no se requiere (el puntero que
refería a la memoria está fuera de alcance), el programa o entorno de ejecución
debe desasociar la memoria.
En C, C++ y en otros lenguajes, usted es el responsable de desasociar la
memoria, ejercicio que resulta difícil a veces, porque no siempre se sabe
cuándo la memoria debe ser liberada. Los programas que no liberan la
memoria pueden eventualmente fallar cuando no hay memoria libre en el
sistema para asignar. Se dice que estos programas tienen memory leaks.

El lenguaje de programación Java desliga al programador de la responsabilidad


de desasignar la memoria. Incluye un hilo a nivel del sistema que realiza el
seguimiento de cada asignación de memoria. Durante los ciclos ociosos de la
JVM, el hilo correspondiente al garbage collector verifica los sectores de
memoria que pueden ser liberados y los libera.

El proceso de garbage collection ocurre automáticamente durante el tiempo de


vida del programa de tecnología Java. Se elimina, por lo tanto, la necesidad de
desasociar memoria y se evitan los memory leaks. Sin embargo, los esquemas
de garbage collection pueden variar considerablemente a través de las distintas
implementaciones de la JVM
3.6 Objetivos Primarios de la Tecnología
Java
3.6.3 El Entorno de Tiempo de
Ejecución de Java

La Figura muestra el JRE y cómo se implementa la seguridad del código.

La compilación de los archivos fuente escritos en el lenguaje Java debe ser


entendida como la conversión de estos a un conjunto de bytecode. Los
bytecode se almacenan en archivos .class.

En tiempo de ejecución, los bytecode que constituyen un programa de software


Java son cargados, verificados y ejecutados en un intérprete. En el caso de los
applets, se pueden descargar los bytecode y luego estos son interpretados por
la JVM provista por el navegador. El intérprete tiene dos funciones: ejecutar los
bytecode y realizar las llamadas apropiadas al hardware subyacente.

En algunos entornos de ejecución de la tecnología Java, un segmento de los


bytecode verificados es compilado al código de máquina nativo y ejecutado
directamente en la plataforma de hardware. Esto habilita al código del software
Java a ejecutar casi a la velocidad de C o C++. La única diferencia es una
pequeña demora en el tiempo de la carga del
archivo, para permitirle al código ser compilado al código de máquina nativo
(vea figura ).
3.6 Objetivos Primarios de la Tecnología
Java
3.6.4 Las Tareas de la JVM

La siguiente sección brinda mayores detalles acerca de las tres tareas


principales realizadas por la JVM:

• Carga del código - Realizada por el cargador de clases.


• Verificación del código - Realizada por el verificador de bytecode.
• Ejecución del código - Realizada por el intérprete de tiempo de ejecución

3.6 Objetivos Primarios de la Tecnología


Java
3.6.5 El Cargador de Clases

El cargador de clases carga todas las clases necesarias para la ejecución de


un programa. Agrega seguridad mediante la separación del espacio de
nombres de las clases del sistema local de archivos de aquellas importadas
desde otros orígenes en la red. Esto impide la ejecución de cualquier aplicación
del tipo "Caballo de Troya", dado que se cargan las clases locales antes que
cualquier otra.

Luego de que todas las clases fueron cargadas, se determina el plan de


asignación de la memoria para el archivo ejecutable. En este punto, se asignan
las direcciones de memoria específicas a referencias simbólicas y se crea la
tabla de búsqueda. Como la planificación de asignación de memoria ocurre en
tiempo de ejecución, el intérprete de la tecnología Java agrega protección
contra el acceso no autorizado a las áreas restringidas del código.

3.6 Objetivos Primarios de la Tecnología


Java
3.6.6 El Verificador del Bytecode

El código del software Java realiza varias validaciones antes de ejecutarse en


su máquina. La JVM pasa el código a través de un verificador de bytecode.
Este valida el formato de los fragmentos de código y verifica que no contengan
código ilegal. El código ilegal es aquel que falsifica punteros, quebranta los
derechos de acceso a objetos o intenta cambiar los tipos de los objetos.

Proceso de verificación

El verificador de bytecode realiza cuatro pasadas sobre el código de un


programa. Esto asegura que el código se adhiera a la especificación de la JVM
y no quebrante la integridad del sistema. Si el verificador completa las cuatro
pasadas sin devolver un mensaje de error, asegura que:

• Las clases se apegan al formato de los archivos class de la


especificación de la JVM.
• No hay quebrantamiento a las restricciones de acceso.
• El código no causa operaciones incorrectas sobre la pila, tales como
agregar elementos sobre una pila completa (overflow) o tratar de quitar
un elemento de la pila vacía (underflow).
• Los tipos de los parámetros para todas las operaciones son correctos.
• No ocurren conversiones de datos ilegales, tales como enteros a
referencias de objetos.

3.7 Identificación de los Componentes de una Clase

Las clases son los blueprints que se crean para definir los objetos de un
programa. Por ejemplo, la siguiente figura ilustra algunos de los objetos que se
necesitarían para el programa de ingreso de órdenes de compra para
DirectClothing, Inc.

Una aplicación de escritorio generalmente contiene un objeto que es el punto


de comienzo del programa. A este objeto, frecuentemente se le denomina el
objeto controlador, objeto principal u objeto de prueba. En la figura anterior, un
objeto OrderEntry podría interactuar con uno o más objetos Window, objetos
Customer u objetos Order durante la ejecución del programa.

Cada objeto en la figura es una instancia de una clase o blueprint. Por ejemplo,
todos los objetos Window son instancias de la clase Window. Algunas clases,
como la clase Window (usada para crear ventanas de interfaz gráfica de
usuario [GUI]) son clases de propósito general y son provistas como parte de la
API de la tecnología Java. Otras clases, como por ejemplo Shirt, son
específicas de este programa y deben ser creadas por el programador. En este
curso se describe cómo usar las clases ya existentes y cómo crear y usar
clases propias.

3.7 Identificación de los Componentes de una


Clase
3.7.1 La Estructura de las
Clases

Las clases están compuestas por código basado en la tecnología Java


necesario para instanciar objetos, como por ejemplo los objetos de la clase
Shirt. En este curso se divide el código en un archivo de una clase Java en
cuatro secciones separadas:

• La declaración de la clase.
• La declaración e inicialización de las variables (opcional).
• Ls métodos.
• Los comentarios.

El código del programa para una clase está contenido en un archivo de texto
que debe seguir cierta estructura

2 public class Shirt {

4 public int shirtID = 0; // Default ID for the shirt

5 public String description = "-description required-";//default

7 // Los códigos de los colores son R=Red, B=Blue, =Green, U=Unset

8 public char colorCode = 'U';

10 public double price = 0.0; // Precio por defecto

11 // para todas las camisas

12 public int quantityInStock = 0; // Cantidad por defecto


13 // para todas las camisas

14 // This method displays the values for an item

15 public void displayShirtInformation() {

16

17 System.out.println("Shirt ID: " + shirtID);

18 System.out.println("Shirt description:" + description);

19 System.out.println("Color Code: " + colorCode);

20 System.out.println("Shirt price: " + price);

21 System.out.println("Quantity in stock: " + quantityInStock);

22

23 } // fin del método display

24 } // fin de la clase

25
3.7 Identificación de los Componentes de una
Clase
3.7.2 Declaración de una
Clase

Se debe declarar una clase por cada clase incluida en el diseño


correspondiente al dominio del problema. Para cada clase del diseño, se
debería escribir una declaración de clase. La sintaxis para declarar una clase
es:

[modificadores] class identificador_clase

• Los [modificadores] de la clase determinan la accesibilidad que otras


clases tendrán sobre ella. Los modificadores se presentan en detalle
más adelante en este curso. Los [modificadores] son opcionales en la
declaración de la clase (lo cual es indicado por los corchetes) y puede
tener los valores public, abstract o final. Por ahora, utilice el modificador
public.
• La palabra clave class indica al compilador que el bloque de código es
una clase. Las palabras clave son palabras que están reservadas para
ciertas construcciones del lenguaje de programación Java.
• El identificador_clase es el nombre que se le dará a la clase. Las reglas
para nombrar una clase son las siguientes:

• Los nombres de clase deberían ser sustantivos, con letras mayúsculas


y minúsculas pero con la primera letra de cada palabra en mayúscula.
Por ejemplo: MiClase.
• Los nombres de clase deberían contener palabras completas. Se debe
evitar usar acrónimos y abreviaturas (a menos que la abreviatura sea
más utilizada o conocida que la forma larga, como por ejemplo JVM o
UML).

La declaración de clase (Línea 1) del código de la clase Shirt en el ejemplo


anterior, tiene el modificador de clase public, seguido de la palabra clave class,
y a continuación el nombre de clase (Shirt).

Requerimientos para sus Archivo Fuente

En este curso, usted desarrollará sus clases de forma tal que el código de los
programas basados en la tecnología Java que escriba para cada clase se
almacene en su propio archivo de texto o archivo con el código fuente. En el
lenguaje de programación Java, los nombres de los archivos con el código
fuente deben:

• coincidir con el nombre de la clase pública incluida en dicho archivo y


• tener extensión .java.

Por ejemplo, la clase Shirt debe estar almacenada en un archivo llamado


Shirt.java.

La definición de clase es seguida por una llave izquierda ({) indicando el


comienzo del cuerpo de la clase (cuerpo_clase), las variables atributo y los
métodos que componen la clase. Las llaves {…} alrededor del cuerpo_clase
definen dónde comienza y dónde finaliza la clase, respectivamente.
3.7 Identificación de los Componentes de
una Clase
3.7.3 Declaración de Variables y
Asignaciones

La declaración de variables atributo y el bloque de asignaciones siguen a la


primera llave izquierda ({). Generalmente, después es esta llave se inicializan
todas las variables atributo de la clase.
El código de la clase Shirt en el ejemplo, contiene cinco declaraciones de
variables atributo:

• una para el identificador shirtID (línea 3),


• una para una descripción (description) (línea 4),
• una para el código de color (colorCode) (línea 7),
• una para el precio (price) (línea 9) y
• una para la cantidad en stock (quantityInStock) (línea 11).
3.7 Identificación de los Componentes de una Clase
3.7.4 Comentarios
Usted debería incluir comentarios en cada clase que escriba, para hacer más
fácil la comprensión de lo que hace el programa. Comentar un programa es
particularmente importante en programas grandes o desarrollados por grandes
equipos donde muchos programadores necesitan leer el código. Los
comentarios ayudan al mantenimiento de un programa cuando nuevos
programadores necesitan determinar lo que hace el código.

Estructura de los Comentarios

Se pueden utilizar dos estilos principales de comentarios:

• Comentarios de una única línea - La marca " //" indica al compilador que
ignore todos los caracteres desde esta marca hasta el fin de la línea
donde se encuentra. Por ejemplo, las líneas 3, 4 y 6 de la clase Shirt
contienen comentarios de una única línea.

public int shirtID = 0; // Default ID for the shirt


public double price = 0.0; // Default price for all shirts

// The color codes are R=Red, B=Blue, G=Green

Muchos programadores también construyen sus programas usando


comentarios de una única línea para comentar la primera y la última línea de
cada clase o método. Por ejemplo, la clase Shirt contiene un comentario de fin
de línea para indicar el fin del método display. (línea 22)

} // end of display method

En programas grandes, puede ser dificultoso encontrar la llave derecha que


cierra una clase. Incluir un comentario en cada llave derecha de una
estructura hace el código más legible y permite reparar errores más fácilmente.

• Comentarios tradicionales - La combinación de los caracteres /* indica al


compilador que ignore todo el texto hasta la marca de finalización del
comentario (*/). Esta acción es realizada por el compilador, incluso a
través de varias líneas. Los programadores utilizan frecuentemente los
comentarios tradicionales para grandes bloques de código.

/*********************
* Sección de Declaración de Variables Atributo
*********************/
3.7 Identificación de los Componentes de una
Clase
3.7.5 Los métodos

Los métodos de una clase se escriben a continuación de la declaración de las


variables atributos. La sintaxis para los métodos es:

[modificadores] tipo_retorno identificador_método


([argumentos]) {
bloque_código_método
}

donde:

• Los [modificadores] representan palabras clave únicas de la tecnología


Java que modifican la forma en que los métodos son accedidos. Los
modificadores son opcionales (lo cual es indicado en la sintaxis por su
inclusión entre corchetes).
• El tipo_retorno indica el tipo del valor (si existe) que el método devuelve.
Si el método devuelve un valor, el tipo del mismo debe ser declarado.
Los valores devueltos por un método pueden ser usados por el método
que lo llama. Cualquier método puede devolver a lo sumo un valor. Si el
método no devuelve ningún valor, se utiliza la palabra void en el lugar de
tipo_retorno.
• El identificador_método es el nombre del método.
• Los [argumentos] representan una lista de variables cuyos valores son
pasados al método para ser usados por él. Los argumentos son
opcionales (indicado con su inclusión entre corchetes) dado que no se
requiere que el método acepte argumentos. Notar que los paréntesis
curvos no son opcionales. Un método que no acepta argumentos se
declara incluyendo solamente el paréntesis curvo que abre y el que
cierra.
• El bloque_código_método es la secuencia de sentencias que el método
realiza. Una gran variedad de tareas pueden ser realizadas en el bloque
de código, o cuerpo, del método.

La clase Shirt contiene un método, el método displayShirtInformation (líneas 14


a 22), el cual despliega los valores de los atributos de una camisa.

public void displayShirtInformation() {


System.out.println("Shirt ID: " + shirtID);
System.out.println("Shirt description:" + description);
System.out.println("Color Code: " + colorCode);
System.out.println("Shirt price: " + price);
System.out.println("Quantity in stock: " + quantityInStock);
} // end of display method

3.8 Una Aplicación Simple Java


Como cualquier otro lenguaje de programación, el lenguaje de programación
Java se utiliza para crear aplicaciones. En el Código y el Código se
muestra una aplicación Java que despliega un mensaje de bienvenida
3.8 Una Aplicación Simple Java
3.8.1 La Aplicación TestGreeting

Las Líneas 1–3 en el programa son líneas comentadas. (Figura )

La Línea 4 declara el nombre de la clase como TestGreeting. Cuando se


compila el archivo fuente, se crea un archivo nombreClase.class, donde
nombreClase es el indicado en el archivo fuente. Si no se especifica un
directorio destino para que use el compilador, este archivo de clase estará en el
mismo directorio que el archivo fuente. En este caso, el compilador crea un
archivo llamado TestGreeting.class. Este contiene el código compilado para la
clase pública TestGreeting. (Figura )

En la línea 5 el programa comienza a ejecutarse (Figura ) El intérprete de la


tecnología Java debe encontrar esto definido exactamente en esta forma o no
ejecutará el programa.
Otros lenguajes de programación, como C y C++, también usan la declaración
del main() como el punto inicial para la ejecución. Describiremos brevemente
aquí las partes que componen esta declaración. Los detalles se explicarán a
medida que el curso avance. Si al programa se le pasan argumentos desde la
línea de comandos, estos son trasladados al método main() en un arreglo de
String, denominado args. En este ejemplo, no se utiliza ningún argumento.

A continuación se describen cada uno de los elementos de la línea 5:


• public - Indica que cualquiera puede acceder al método main(),
incluyendo el intérprete de tecnología Java.
• static - Esta palabra clave le indica al compilador que el método main()
es usable dentro del contexto de la clase TestGreeting. No es necesario
crear una instancia de la clase para ejecutar sus métodos estaticos.
• void - Esta palabra clave indica que el método main() no devuelve
ningún valor. Esto es importante porque el lenguaje de programación
Java realiza cuidadosos chequeos de tipos para confirmar que los
métodos llamados devuelven los tipos con que fueron declarados.
• String args[] - Este método declara un sólo parámetro para el método
main (de nombre args) y este es del tipo arreglo de String. Cuando se
llama a este método, el parámetro args contiene los argumentos escritos
en la línea de comandos a continuación del nombre de la clase, por
ejemplo: java TestGreeting args[0] args[1]…

La línea 6 ilustra cómo crear un objeto, referenciado por la variable hello. La


sintaxis new Greeting le indica al intérprete de la tecnología Java que debe
construir un nuevo objeto de la clase Greeting. (Figura )

La línea 7 demuestra la llamada a un método de un objeto. Esta llamada le


indica al objeto hello que salude (greet) al mundo. La implementación de este
método se muestra en las líneas 3-5 del archivo Greeting.java. (Figura )

En las líneas 8-9 del programa, las dos llaves cierran el método main() y la
clase TestGreeting, respectivamente. (Figura )
3.8 Una Aplicación Simple Java
3.8.2 La Clase Greeting

La línea 1 declara la clase Greeting. (Figura )

Las líneas 2-4 demuestran la declaración de un método-declarado public-


accesible al programa TestGreeting. El mismo no devuelve un valor, por lo
tanto, se utiliza void como el tipo de retorno. El método greet envía una cadena
de caracteres al flujo de salida estándar. El método println() se usa para escribir
este mensaje al flujo de salida estándar. (Figura )

La línea 5 cierra la declaración de la clase Greeting. (Figura )


3.8 Una Aplicación Simple Java
3.8.3 Compilación y Ejecución del Programa
Test Greeting

Después de creado el archivo fuente TestGreeting.Java, compílelo con la


siguiente línea:

javac TestGreeting.java

Si el compilador no retorna ningún mensaje, el nuevo archivo


TestGreeting.class es guardado en el mismo directorio que el archivo fuente, a
menos que se especifique otro. El archivo Greeting.java ha sido compilado
generando el archivo Greeting.class. Esto se realiza automáticamente por el
compilador debido a que la clase TestGreeting usa la clase Greeting.

Para ejecutar su aplicación TestGreeting, use el intérprete de la tecnología


Java. Los ejecutables para las herramientas de la tecnología Java (javac, java,
javadoc y demás) están ubicados en el directorio bin.

java TestGreeting
3.8 Una Aplicación Simple Java
3.8.4 Errores de Compilación

Las siguientes secciones describen errores que puede encontrar cuando


compila el código.

Errores en tiempo de compilación

Los siguientes son errores comunes vistos en tiempo de compilación, con


ejemplos de mensajes del compilador o que pueden producirse en tiempo de
ejecución. Estos mensajes pueden variar dependiendo qué versión del Java 2
SDK esté usando.

• javac: Command not found

La variable PATH no está configurada apropiadamente, de forma tal que


incluya al compilador javac. El compilador javac está ubicado en el
directorio bin, bajo el directorio en el que esté instalado el Java
Development Kit (JDK™).
• Greeting.java:4:cannot resolve symbol
symbol : method printl (java.lang.String)
location: class java.io. PrintStream
System. out.printl("hi");
^
El método denominado println está escrito incorrectamente.
• Nombre de la clase y del archivo.
Si el archivo Java contiene una clase pública, esta debe tener el mismo
nombre de archivo que la clase. Por ejemplo, la definición de la clase en
el ejemplo es:

public class TestGreeting

El nombre del archivo fuente debe ser TestGreeting .java. Si nombra al


archivo TestGreet .java, luego debería obtener el mensaje de error:

TestGreet.java:4: Public class TestGreeting must be defined in a file


called "TestGreeting.java".
• Cantidad de clases
En cada archivo fuente se debería declarar una única clase pública no
estática, al nivel más alto. Si tiene más de una clase pública, obtendrá el
mismo mensaje que el obtenido en el punto anterior para cada una de
las clases públicas incluidas en el archivo y que no tienen el mismo
nombre que el archivo.

Errores en tiempo de ejecución

Algunos de los errores generados cuando escribe java TestGreeting son:

• Can't find class TestGreeting

Generalmente, esto significa que el nombre de la clase especificada en


la línea de comandos fue escrito de forma diferente al
nombrearchivo.class. El lenguaje de programación Java es sensible a
mayúsculas y minúsculas. Por ejemplo,

public class TestGreet {

crea el archivo TestGreet.class, el cual no es el nombre de la clase


(TestGreeting.class) que el compilador espera.
• Exception in thread "main" java.lang. NoSuchMethodError: main
Esto significa que la clase indicada al intérprete para ejecutar no
contiene un método main estático. También se podría producir esta
situación, cuando existe el método main, pero no se declaró como
estático o se escribieron incorrectamente sus parámetros. Por ejemplo,

public static void main(String args) {

En este ejemplo, args es un String simple, en vez de un arreglo de


Strings.

public static void main() {

En este ejemplo, quien escribió el código olvidó incluir la lista de


parámetros.
La figura ilustra cómo los programas de la tecnología Java pueden ser
compilados y luego ejecutados en la JVM. Hay varias implementaciones de la
JVM en diferentes plataformas de hardware y de sistema

3.9 Creación y Uso de una Clase de Prueba

La mayoría de las clases que se crearán en este curso no pueden ser usadas
(ejecutadas y probadas) por sí mismas. Sin embargo, se puede crear una clase
que cree un objeto de dicha clase y ejecutar esta nueva clase para realizar la
prueba.

Durante este curso, usted utilizará una clase principal, o de prueba, para probar
cada una de sus clases. El siguiente código es un ejemplo de una clase de
prueba para la clase Shirt.

A cada clase de prueba en este curso se le debería dar un nombre de forma tal
que pueda ser reconocida como una clase de prueba para una clase particular.
Específicamente, cada nombre de clase de prueba debería consistir del
nombre de la clase que se desea probar, seguido por la palabra "Test". Por
ejemplo, el nombre de la clase de prueba para la clase Shirt se debería llamar
"ShirtTest".

Las clases de prueba realizan dos tareas diferentes:


• Ofrecer un punto de comienzo, llamado el método main, para los
programas.
• Crear un objeto de la clase y probar sus métodos.

3.9 Creación y Uso de una Clase de


Prueba
3.9.1 El Método main

El método main es un método especial que la Máquina Virtual de Java


reconoce como el punto de comienzo de cualquier programa basado en la
tecnología Java que se ejecuta desde la línea de comando o desde un "prompt".
Cualquier programa que se desee ejecutar desde la línea de comando o desde
un prompt debe tener un método main.

La sintaxis para el método main es:

public static void main (String args[])

El método main tiene la misma sintaxis que todos los métodos, la cual se
describió anteriormente. Específicamente:

• El método main contiene dos modificadores requeridos: public y static


• El método main no devuelve ningún valor, por lo que el tipo de retorno
es void.
• El método main tiene como identificador de método (nombre) a "main"
• El método main acepta cero o más objetos de tipo String (String args[]).
Esta sintaxis le permite ingresar los valores a ser usados por su
programa desde la línea de comandos durante la ejecución.
3.9 Creación y Uso de una Clase de
Prueba
3.9.2 Compilación y Ejecución
(Prueba) de un Programa

Usted ha visto una clase básica de nombre Shirt basada en la tecnología Java
y una clase de prueba con nombre ShirtTest. Estas dos clases conjuntamente,
constituyen su primer programa basado en la tecnología Java. A continuación
usted compilará y ejecutará (probará) su programa.
3.9 Creación y Uso de una Clase de
Prueba
3.9.3 Compilar un Programa

La compilación convierte los archivos con código fuente de las clases en


bytecode, para que pueda ser ejecutado por una Máquina Virtual Java.
Recuerde las reglas para el nombramiento de los archivos fuente Java. Si un
archivo fuente contiene una clase pública, el archivo fuente debe tener el
mismo nombre de la clase pública y extensión .java. Por ejemplo, la clase Shirt
debe estar almacenada en un archivo con nombre Shirt.java.
Para compilar los archivos de código fuente Shirt y ShirtTest, usted podría:

1. Ir al directorio donde están almacenados los archivos con el código


fuente.
2. Ingresar el siguiente comando para cada archivo .java que quiera
compilar:

javac filename

Por ejemplo:

javac Shirt.java

Luego de que la compilación haya finalizado, y asumiendo que no se


produjeron errores de compilación, en ese directorio deberá haber un nuevo
archivo con el nombre classname.class por cada archivo con código fuente que
se haya compilado. Si se compiló una clase que referencia otros objetos, las
clases correspondientes a estos objetos también serán compiladas (si no
habían sido compiladas previamente).

Por ejemplo, si se compila la clase ShirtTest.java (la cual referencia un objeto


Shirt), se compilará también la clase Shirt.java y se generarán los archivos
ShirtTest.class y Shirt.class

3.9 Creación y Uso de una Clase de Prueba


3.9.4 Ejecutar (Probar) un
Programa

Después de compilar exitosamente los archivos con código fuente, se los


puede ejecutar y probar usando la Máquina Virtual Java. Para ejecutar y probar
el programa, se deben seguir los siguientes pasos:

1. Vaya al directorio donde están almacenados los archivos de las clases


2. Ingrese el siguiente comando para el archivo de la clase que contiene el
método main:
java nombreClase

Por ejemplo:
java ShirtTest

Este comando ejecuta la clase ShirtTest. Como se mencionó previamente, la


clase ShirtTest crea una instancia del objeto Shirt, usando la clase Shirt. Todos
los objetos Shirt tienen un método, el método display, el cual despliega los
valores de las variables atributo, por ejemplo:

Shirt ID: 0
Shirt description:-description required-
Color Code: U
Shirt price: 0.0
Quantity in stock: 0

Sugerencias para la Depuración

La mayoría de la veces, se tendrá al menos un error en el código. El término


empleado para referir a la reparación de errores (o bugs) es depuración. Las
siguientes son algunas sugerencias para llevar a cabo la depuración de un
programa:

• Los mensajes de error indican el número de línea donde ocurre el error.


Sin embargo, este número no necesariamente corresponde a la línea
donde se encuentra el error, por lo que se deberían verificar las líneas
de código anteriores y posteriores al número de línea que es desplegado.
• Asegúrese que existe un signo de punto y coma al final de cada
sentencia.
• Asegúrese que hay una cantidad par de llaves: una llave derecha por
cada llave izquierda.
• Asegúrese que utilizó en el código un modelo de sangrías como el visto
en el este curso. De todas formas, los programas ejecutarán sin importar
el tipo de sangría utilizado, pero lo harán más legible y fácil de depurar.
También le posibilitará evitar errores mientras esté escribiéndolos.

3.10 Laboratorios

Laboratorio 1 - Comenzando a Trabajar

3.10 Laboratorios
3.10.1 Objetivos

Una vez completado este laboratorio, debería estar capacitado para:

• Diagnosticar errores simples de compilación y errores que se producen


durante el tiempo de ejecución.
• Crear una clase de prueba para una clase existente
3.10 Laboratorios
3.10.2 Preparación del Laboratorio

Usted debe tener una ventana de comando abierta además de la de


configuración en la que está trabajando.

Inicie un editor de texto. Es posible usar cualquier editor de texto disponible en


su máquina. Su ambiente de laboratorio incluye Jedit, un editor de texto de
código abierto con resaltado de sintaxis. Inícielo con el comando:

jedit &

Si este no está disponible, ejecute este comando:

java -jar /opt/jedit/jedit.jar &

Pregúntele a su instructor a qué otros editores y herramientas tiene acceso

3.10 Laboratorios
3.10.3 Ejercicio 1: Exploración de Errores en un Programa
Java(tm)

Ejercicio 1 - Exploración de Errores en un Programa Java™

3.10 Laboratorios
3.10.4 Ejercicio 2: Creación de un Programa de Prueba (Nivel
1)

Ejercicio 2 - Creación de un Programa de Prueba (Nivel 1)

3.10 Laboratorios
3.10.5 Ejercicio 2: Creación de un Programa de Prueba (Nivel
2)

Ejercicio 2 - Creación de un Programa de Prueba (Nivel 2)

3.10 Laboratorios
3.10.6 Ejercicio 2: Creación de un Programa de Prueba (Nivel
3)

Ejercicio 2 - Creación de un Programa de Programa de Prueba (Nivel 3)

3.10 Laboratorios
3.10.7 Resumen del Ejercicio
Discusión – Dedique unos pocos minutos para discutir las experiencias, temas
o descubrimientos que tuvo durante el ejercicio de laboratorio.

• Experiencias
• Interpretaciones
• Conclusiones
• Aplicaciones

3.11 Ejercicio: Escribir, Compilar y Probar un Programa Básico

El objetivo de este ejercicio es familiarizarse con los pasos necesarios para


escribir (o modificar), compilar y probar (ejecutar) sus programas basados en la
tecnología Java

3.11 Ejercicio: Escribir,


Compilar y Probar un
Programa Básico
3.11.1 Tarea - Compilar y Ejecutar un Programa
basado en la Tecnología Java

En esta tarea, usted compilará y ejecutará su primer programa basado en la


tecnología Java. Siga los siguientes pasos para compilar y ejecutar el programa
ShirtTest:

1. Vaya al directorio getstarted.


2. Abra un editor de texto e ingrese el código, siguiendo la sintaxis de la
tecnología Java, para la clase Shirt presentada anteriormente en este
módulo (el código fuente del programa).
3. Guarde y cierre el archivo.
4. Abra un editor de texto e ingrese el código fuente de la clase ShirtTest
mostrado anteriormente en este módulo.
5. Guarde y cierre el archivo.
6. Abra una ventana de comandos.
7. Navegue hasta el directorio donde se encuentran los
archivos correspondientes a este módulo.
8. Ingrese el siguiente comando para compilar el programa y generar un
archivo bytecode ejecutable.

javac Shirt.java

De esta forma, se creará el archivo Shirt.class.


9. Ingrese el siguiente comando para compilar el programa y generar un
archivo bytecode ejecutable.

javac ShirtTest.java
El archivo creado será ShirtTest.class.
10. Cuando reaparezca el símbolo de prompt, ingrese el siguiente comando
para ejecutar el programa:

java ShirtTest
11. Abra nuevamente el archivo Shirt.java.
12. Cambie el valor de la variable price a 14.99 y el shirtID a 112244.
13. Guarde y cierre el archivo.
14. Compile nuevamente el archivo Shirt.java.

javac Shirt.java
15. Ejecute nuevamente el archivo ShirtTest.class.

java ShirtTest

3.11 Ejercicio: Escribir,


Compilar y Probar un
Programa Básico
3.11.2 Tarea - Crear, Compilar y Ejecutar Otro
Programa basado en la Tecnología Java

En esta tarea, usted creará, compilará y ejecutará su segundo programa


basado en la tecnología Java. Siga los siguientes pasos para crear una clase
de nombre Quotation para desplegar un texto:

1. Vaya al directorio getstarted.


2. Abra un editor de texto e ingrese la siguiente clase:

public class Quotation {


String quote = "Welcome to Sun!";
public void display() {
System.out.println(quote);
}
}
3. Guarde el archivo con el nombre Quotation.java.
4. Desde una ventana de comandos, navegue hasta el directorio donde su
programa está almacenado.
5. Ingrese el siguiente comando en la ventana de comandos para compilar
el programa:

javac QuotationTest.java
6. Si ocurre un error, abra el archivo y verifique que ha copiado el
programa exactamente.
7. Si no ocurre ningún mensaje de error, ingrese el siguiente comando en
la ventana de comandos para ejecutar el programa:

java QuotationTest
8. Abra nuevamente el archivo Quotation.java y cambie el texto "Welcome
to Sun!" por el que prefiera. Asegúrese que sólo cambió el texto y que
las comillas no se modificaron.
9. Compile y ejecute el programa nuevamente.

3.12 Resumen del Ejercicio

Discusión - Tómese unos minutos para discutir qué experiencias, temas y


descubrimientos se realizaron durante este ejercicio.

• Experiencias
• Interpretaciones
• Conclusiones
• Aplicaciones

3.13 Preguntas de Repaso

Ahora le proponemos que resuelva el siguiente ejercicio para poner a prueba


sus conocimientos.

Defina por Verdadero o Falso las siguientes afirmaciones según corresponda.


¡Éxitos!
4 Declaración, Inicialización y Uso de Variables

En éste capítulo identificaremos los usos de las variables, listaremos los tipos
de datos del lenguaje Java. Aprenderemos acerca de la promoción y
conversión de tipos de datos.

4.1 Repaso del Capítulo Anterior

Los principales temas del capítulo anterior son:

Identificación de los Componentes de una Clase

Una aplicación de escritorio generalmente contiene un objeto que es el punto


de comienzo del programa. A este objeto, frecuentemente se le denomina el
objeto controlador, objeto principal u objeto de prueba.

La Estructura de las Clases

Se divide el código de una clase Java en cuatro secciones separadas:

• La declaración de la clase.
• La declaración e inicialización de las variables (opcional).
• Los métodos.
• Los comentarios.

Creación y Uso de una Clase de Prueba

El método main es un método especial que la Máquina Virtual de Java


reconoce como el punto de comienzo de cualquier programa basado en la
tecnología Java que se ejecuta desde la línea de comando o desde un "prompt".

La sintaxis para el método main es:

public static void main (String args[])

El método main tiene la misma sintaxis que todos los métodos, la cual se
describió anteriormente. Específicamente:

• Contiene dos modificadores requeridos: public y static.


• No devuelve ningún valor, por lo que el tipo de retorno es void.
• Tiene como identificador de método (nombre) a "main".
· Acepta cero o más objetos de tipo String (String args[]). Esta sintaxis le
permite ingresar los valores a ser usados por su programa desde la línea de
comandos durante la ejecución.

Compilar un Programa

1. Ir al directorio donde están almacenados los archivos con el código fuente.

2. Ingresar el siguiente comando para cada archivo .java que quiera compilar:

javac filename

Por ejemplo:

javac Shirt.java

Ejecutar (Probar) un Programa:

1. Vaya al directorio donde están almacenados los archivos de las clases

2. Ingrese el siguiente comando para el archivo de la clase que contiene el


método main:

java nombreClase

Por ejemplo:

java ShirtTest

Objetivos Primarios de la Tecnología Java

La Máquina Virtual de Java

La Especificación de la Máquina Virtual de Java define la JVM como:

"Una máquina imaginaria que está implementada mediante software y cuya


emulación se realiza sobre una máquina real. El código para la JVM está
almacenado en archivos .class, cada uno de los cuales contiene código para, a
lo sumo, una clase pública."

Garbage Collector

Durante los ciclos ociosos de la JVM, el hilo correspondiente al garbage


collector verifica los sectores de memoria que pueden ser liberados y los
libera. El proceso de garbage collection ocurre automáticamente durante el
tiempo de vida del programa de tecnología Java.

El Entorno de Tiempo de Ejecución de Java


La compilación de los archivos fuente escritos en el lenguaje Java debe ser
entendida como la conversión de estos a un conjunto de bytecode. Los
bytecode se almacenan en archivos .class. En tiempo de ejecución, los
bytecode que constituyen un programa de software Java son cargados,
verificados y ejecutados en un intérprete. En el caso de los applets, se pueden
descargar los bytecode y luego estos son interpretados por la JVM provista por
el navegador. El intérprete tiene dos funciones: ejecutar los bytecode y realizar
las llamadas apropiadas al hardware subyacente.

Las Tareas de la JVM

• Carga del código - Realizada por el cargador de clases.


• Verificación del código - Realizada por el verificador de bytecode.
• Ejecución del código - Realizada por el intérprete de tiempo de ejecución.

El Cargador de Clases

El cargador de clases carga todas las clases necesarias para la ejecución de


un programa.

Luego de que todas las clases fueron cargadas, se determina el plan de


asignación de la memoria para el archivo ejecutable.

El Verificador del Bytecode

La JVM pasa el código a través de un verificador de bytecode. Este valida el


formato de los fragmentos de código y verifica que no contengan código ilegal.

4.3 Objetivos

Una vez completado este capítulo, usted debería ser capaz de:

• Identificar los usos de las variables y definir la sintaxis para la


declaración de una variable.
• Listar los ocho tipos de datos primitivos del lenguaje de programación
Java.
• Declarar, inicializar y usar variables y constantes de acuerdo a la guía y
los estándares de codificación para el lenguaje de programación Java.
• Modificar valores de variables usando operadores.
• Utilizar promoción y conversión de tipos de datos.

Este capítulo describe cómo crear variables usando tipos de datos primitivos y
cómo modificar los valores de las variables usando operadores, promoción y
conversión de tipos de datos.
Discusión – Las siguientes preguntas son relevantes para comprender lo que
respecta a variables:

• Una variable refiere a algo que puede cambiar. Las variables pueden
contener un valor o un conjunto de valores. ¿Dónde ha visto variables
con anterioridad?
• ¿Qué tipos de datos piensa que las variables pueden contener?

4.4 Recursos Adicionales

Recursos adicionales – Las siguientes referencias pueden ofrecer detalles


adicionales sobre los temas discutidos en este capítulo:

• Farrell, Joyce. Java Programming: Comprehensive. 1999.


Se trata de un excelente libro para no programadores. Explica
conceptos que son pasados por alto en libros más avanzados.
• The Java Tutorial. [Online]. Disponible en:

http://java.sun.com/docs/books
/tutorial/java/nutsandbolts/
variables.html

http://java.sun.com/docs/books
/tutorial/java/nutsandbolts/
datatypes.html

4.5 Identificación del Uso de las Variables y la Sintaxis

Las variables se utilizan para almacenar y recuperar datos en un programa. El


código en el siguiente ejemplo muestra una clase Shirt que declara varias
variables.

Las variables atributo (variables declaradas fuera de un método y sin la palabra


clave static) también se denominan variables miembros o variables de instancia
(como por ejemplo price, shirtID y colorCode en la clase Shirt). Cuando un
objeto es instanciado a partir una clase, estas variables contienen datos
específicos de una instancia de la clase
particular.

Cuando un objeto es instanciado desde una clase, estas variables son


denominadas variables de instancia, porque pueden contener datos específicos
para un objeto que sea una instancia particular de una clase. Por ejemplo, una
instancia de la clase Shirt podría tener asignado el valor 7 a la variable atributo
quantityInStock, mientras que otra
instancia de la clase Shirt podría tener el valor 100 asignado a la variable
atributo quantityInStock.

Los programas pueden tener también variables definidas dentro de los métodos.
Estas variables se denominan variables locales porque están disponibles
localmente dentro del método en el cual fueron declaradas. No hay variables
locales declaradas en la clase Shirt. Sin embargo, si se usaran variables
locales, deberían ser declaradas en el método displayShirtInformation.

1
2 public class Shirt {
3
4 public int shirtID = 0; // Default ID for the shirt
5 public String description = "-description required-";//default
7 // Los códigos de los colores son R=Red, B=Blue, =Green, U=Unset
8 public char colorCode = 'U';
10 public double price = 0.0; // Precio por defecto
11 // para todas las camisas
12 public int quantityInStock = 0; // Cantidad por defecto
13 // para todas las camisas
14 // This method displays the values for an item
15 public void displayShirtInformation() {
16
17 System.out.println("Shirt ID: " + shirtID);
18 System.out.println("Shirt description:" + description);
19 System.out.println("Color Code: " + colorCode);
20 System.out.println("Shirt price: " + price);
21 System.out.println("Quantity in stock: " + quantityInStock);
22
23 } // fin del método display
24 } // fin de la clase
25

4.5 Identificación del Uso de las Variables y


la Sintaxis
4.5.1 Uso de Variables

Las variables son usadas extensamente en el lenguaje de programación Java


para realizar tareas tales como:

• Almacenar datos correspondientes a los atributos para un objeto


instancia particular (como se ha vista con las variables price y ID).
• Asignar el valor de una variable a otra.
• Representar valores en una expresión matemática.
• Desplegar los valores en la pantalla. Por ejemplo, la clase Shirt usa las
variables price y ID para desplegar el valor del precio y el ID de una
camisa (shirt).

System.out.println("Shirt price: " + price);


System.out.println("Shirt ID: " + shirtID);
• Almacenar referencias a otros objetos.

Este curso muestra varios usos de variables en las clases de tecnología Java.

4.5 Identificación del Uso de las Variables y la


Sintaxis
4.5.2 Declaración e
Inicialización de Variable

La declaración e inicialización de las variables atributo siguen la misma sintaxis


general. La sintaxis para la declaración e inicialización de variables atributo es:

[modificadores] tipo identificador [=valor];

Las variables locales pueden ser declaradas e inicializadas en forma separada


(en líneas de código diferente) o en una única línea de código. La sintaxis para
la declaración de variables dentro de un método es:

tipo identificador;

La sintaxis para la inicialización de variables dentro de un método es:

identificador = valor;

La sintaxis para la declaración e inicialización de una variable dentro de un


método es:

tipo identificador [= valor];

donde:

• Los [modificadores] representan varias palabras clave especiales de la


tecnología Java, como public y private, que modifican el acceso que otro
código tiene para una variable atributo. Los modificadores son
opcionales (lo cual se indica en la sintaxis por los corchetes). Por ahora,
todas las variables serán creadas con el modificador public.
• El tipo representa al tipo de información o datos cuyos valores puede
contener la variable. Algunas variables pueden contener caracteres,
otras número y otras valores booleanos. Todas las variables deben
pertenecer a un tipo de datos.
• El identificador es el nombre que se le da a la variable.
• El valor es el valor que se le quiere asignar a la variable. Este es
opcional dado que no se necesita asignar un valor a una variable en el
momento de su declaración.
4.6 Las Variables, las Declaraciones y las Asignaciones

El programa en la figura ilustra cómo declarar y asignar valores a los tipos de


variables int, float, boolean, char y String

1 public class Assign {

2 public static void main (String args []) {

3 //declara las variables enteras

4 int x, y;

5 //declara y asigna un punto flotante

6 float x = 3.414f;

7 //declara y asigna un double

8 double w = 3.1415;
9 //declara y asigna un Boolean

10 boolean truth = true;

11 //declara una variable caracter

12 char c;

13 //declara una variable String

14 String str;

15 //declara y asigna una variable String

16 String str1 = "bye";

17 //asigna un valor a la variable char

18 c = 'A';

19 //asigna un valor a la variable String

20 str = "Hi out there!";

21 //asigna valores a las variables int

22 x = 6;

23 y = 1000;

25 }

La figura es un ejemplo de asignaciones ilegales

y = 3.1415926;
// 3.1415926 no es un entero (int)
// Necesita ser convertido y el decimal será truncado.

w = 175,000;
// El símbolo de la coma (,) no puede aparecer.

truth = 1;
// Este es un error común hecho por ex programadores C /
C++

z = 3.14156;
// No se puede asignar un valor double a un float. Esto
requiere casteo.

4.7 Los Tipos Básicos del Lenguaje de Programación Java

El lenguaje de programación Java tiene muchos tipos de datos predefinidos.


Iintegran dos categorías amplias: tipos de clase y tipos primitivos. Los tipos
primitivos son valores simples, no son objetos. Los tipos de clase se usan para
tipos más complejos, incluyendo todos los declarados por el programador. Se
utilizan para crear objetos

4.7 Los Tipos Básicos del Lenguaje de Programación


Java
4.7.1 Los Tipos
Primitivos

El lenguaje de programación Java define ocho tipos primitivos de datos, que


pueden ser considerados en cuatro categorías:

• Lógica - boolean
• Texto - char
• Enteros - byte, short, int y long
• Punto flotante - double y float

4.7 Los Tipos Básicos del Lenguaje de Programación


Java
4.7.2 Lógica - boolean

Los valores lógicos son representados usando el tipo boolean, que toma uno
de dos valores: true (verdadero) o false (falso). Estos pueden usarse para
representar cualquiera de dos estados, como, por ejemplo, encendido y
apagado o sí y no. El tipo boolean tiene dos valores literales: true y false. El
siguiente código es un ejemplo de la declaración e inicialización de una variable
de tipo boolean.

// declara la variable truth como boolean y


// le asigna el valor true
boolean truth = true;
4.7 Los Tipos Básicos del Lenguaje de Programación
Java
4.7.3 Texto - char

Los caracteres individuales son representados usando el tipo char. Un char


representa un carácter Unicode de 16-bits sin signo y debe estar encerrarado
entre comillas simples (' ').

Por Ejemplo:

• 'a' La letra a
• '\t' Una tabulación
• '\u????' Un carácter específico Unicode, ????, es reemplazado con
exactamente cuatro dígitos hexadecimales.Por ejemplo, '\u03A6' es la
letra Griega phi.

4.7 Los Tipos Básicos del Lenguaje de Programación


Java
4.7.4 Texto - String

El tipo String no es un tipo primitivo, sino una clase. Se puede utilizar para
representar cadenas de caracteres. Los caracteres son Unicode y la notación
con barra inversa - mostrada previamente para el tipo char - también puede
utilizarse en Strings. A diferencia de C y C++, las cadenas de caracteres no
terminan con \0. Un literal de tipo String debe estar encerrado entre comillas
dobles:

"El veloz murciélago hindú comía feliz cardillo y kiwi."

Algunos ejemplos de la declaración e inicialización de variables de tipos char y


String son:

// declara e inicializa una variable de tipo char


char ch = 'A';

// declara dos variables del tipo char


char ch1, ch2;

// declara dos variables String y las inicializa


String greeting = "Good Morning !! \n";
String errorMessage = "Record Not Found !";

// declara dos variables String


String str1, str2;

4.7 Los Tipos Básicos del Lenguaje de Programación


Java
4.7.5 Enteros - byte,
short, int, and
long

Hay cuatro tipos enteros en el lenguaje de programación Java. Cada uno se


declara usando una de las palabras claves byte, short, int o long. Los literales
de tipos enteros se pueden representar usando formas decimales, octales o
hexadecimales como las siguientes:

2 Esta es la forma decimal para el entero 2


077 El 0 al principio indica un valor octal
0xBAAC El 0x al principio indica un valor hexadecimal

Todos los tipos numéricos en el lenguaje de programación Java representan


números con signo.

Los literales enteros son de tipo int, a menos que estén seguidos por la letra L,
que indica un valor long. En el lenguaje de programación Java, se puede
utilizar tanto una L mayúscula o minúscula. Una l minúscula no es
recomendable dado que resulta difícil distinguirla del dígito 1.

Las versiones long de los literales mostrados previamente son:


2L La L indica que el valor decimal 2 es representado como un valor long
077L El 0 al principio indica un valor octal.
0xBAACL El prefijo 0x indica un valor hexadecimal.

El tamaño y el rango para los cuatro tipos enteros se muestran en la Tabla .


La representación del rango está definida por la especificación del lenguaje de
programación Java como complemento a 2 y es independiente de la plataforma.
4.8 Los Tipos de Referencias Java

Como habrá visto, hay ocho tipos primitivos Java: boolean, char, byte, short, int,
long, float y double. Todos los otros tipos se refieren a objetos en lugar de tipos
primitivos. Las variables que refieren a objetos son variables de referencia.

Se puede definir una clase MyDate como en la figura Un Ejemplo de uso de


la clase MyDate (Figura )

La variable today es una variable de referencia que mantiene un objeto MyDate.


4.9 Tipos de Datos Primitivos

Muchos de los valores en los programas de tecnología Java están


almacenados como tipos de datos primitivos.
Hay ocho tipos de datos primitivos provistos por el lenguaje de programación
Java.

• Tipos enteros: byte, short, int y long


• Tipos Punto Flotante: float y double
• Tipo Texto: char
• Tipo Lógico: boolean

4.9 Tipos de Datos Primitivos


4.9.1 Tipos Primitivos Enteros

En el lenguaje de programación Java hay cuatro tipos de datos primitivos


enteros, identificados por las palabras clave byte, short, int y long. Los
conjuntos de datos de estos tipos de datos son conjuntos de números sin parte
decimal.
Si se necesita almacenar la edad de una persona, por ejemplo, se podría
utilizar una variable de tipo byte, dado que el tipo de datos byte puede aceptar
valores en este rango.

Cuando especifique un valor entero para un tipo long, coloque una L a la


derecha del valor para explicitar que es un valor de tipo long. El compilador
asume que los literales enteros son de tipo int a menos que se utilice una L
como se explicó, y en cuyo caso indica que pertenece al tipo long.
La clase Shirt contiene dos atributos de tipo int para almcenar los valores del
identificador y la cantidad en stock, y se utilizan valores literales para
proporcionar un valor inicial 0 (cero) por defecto para cada uno.

public int shirtID = 0; // Default ID for the shirt


public int quantityInStock = 0; // Default quantity for all shirts
4.9 Tipos de Datos
Primitivos
4.9.2 Tipos Primitivos de Punto Flotante

En el lenguaje de programación Java hay dos tipos de datos para los números
de punto flotante: float y double. El conjunto de valores corresponde a números
con valores a la derecha del punto decimal, tales como 12.24 y 3.14159.

Los literales de punto flotante, por defecto pertenecen al tipo de datos double.
Para explicitar que se trata de un literal perteneciente al tipo de datos float, se
debe colocar una F al final del valor numérico.
La clase Shirt muestra el uso de un valor literal double especificado en el valor
por defecto para la variable atributo price:

public double price = 0.0; // Default price for all shirts


4.9 Tipos de Datos Primitivos
4.9.3 El Punto Flotante - float y double

Se puede declarar una variable de punto flotante usando las palabras claves
float o double. La siguiente lista contiene ejemplos de números de punto
flotante. Un literal numérico es un punto flotante si:

• incluye un punto decimal o bien una parte exponente (la letra E o e), o
• está seguido por la letra F o f (float) o la letra D o d (double).

Algunos literales de ejemplo de punto flotante incluyen

3.14 Un valor simple de un punto flotante (a double)


6.02E23 Un valor de punto flotante largo
2.718F Un valor de tamaño simple float
123.4E+306D Un valor double largo con una D redundante

Los literales de punto flotante son double por defecto. Un literal de float se
puede declarar anexándole una F o una f a su valor.

El formato de un número de punto flotante está definido por The Java


Language Specification para ser IEEE (Institute of Electrical and Electronics
Engineers) 754, usando los tamaños mostrados en la Tabla . Este formato es
independiente de la plataforma.
4.9 Tipos de Datos Primitivos
4.9.4 Tipos Primitivos de Texto

Otro tipo de datos utilizado para almacenar caracteres, tales como una 'a', es el
tipo char, en el cual cada valor tiene un tamaño de 16 bits.
La clase Shirt muestra el uso de un valor literal de texto para especificar el
valor por defecto para colorCode:

public char colorCode = 'U';

Cuando se asigna un valor literal a una variable char, como por ejemplo t, se
debe encerrar entre el símbolo comilla simple. Un ejemplo de valor literal de
tipo char es 't'.

Si no se utilizaran las comillas simples, el compilador no podría determinar si se


trata de un valor literal de tipo char o un identificador (el nombre de una
variable por ejemplo).

Los valores del tipo char son representados en el computador como una
secuencia de bits que corresponden a un carácter. La correspondencia entre
cada una de estas secuencias y el carácter que representa queda definida por
el conjunto de caracteres que cada lenguaje de programación utiliza.

La clase Shirt contiene una variable de tipo primitivo de texto, char, para
almacenar el valor por defecto del color.

public char colorCode = 'U';


4.9 Tipos de Datos Primitivos
4.9.5 Tipo Primitivo Lógico

Los programas de computadores frecuentemente necesitan tomar decisiones.


El resultado de una decisión es un valor verdadero o falso (true o false), y
puede ser almacenado en variables de tipo boolean. En una variable de tipo
boolean solamente se puede almacenar:

• Los literales true y false del lenguaje de programación Java.


• Los resultados de una expresión que evalúe a true o false. Por ejemplo,
si la variable answer es igual a 42, entonces la expresión "if (answer <
42)" evaluará al resultado false.

4.9 Tipos de Datos


Primitivos
4.9.6 Selección de un Tipo de Datos

Una práctica común para los programadores experimentados es usar los tipos
int, long y double para las variables numéricas a menos que existan
restricciones que exijan restringir el uso de memoria (por ejemplo, dispositivos
como teléfonos móviles) o asegurar velocidad de ejecución
4.10 Declaración de Variables y Asignación de Valores a una Variable

Las variables deben ser declaradas antes de que sean usadas. Las siguientes
secciones describen cómo declarar y asignar valores a las variables.

4.10 Declaración de Variables y Asignación de


Valores a una Variable
4.10.1 Nombre de una Variable

Al igual que para los métodos y las clases, cada variable en un programa debe
ser identificada por un nombre o identificador. Recuerde que el objetivo de las
variables es actuar como mecanismos para almacenar y recuperar valores. Por
lo tanto, los identificadores o nombres de las variables deben ser simples y
descriptivos.

Por ejemplo, si se desea almacenar el valor de la IDentificación de un ítem en


una variable, dicha variable podría tener como nombre: myID, itemID,
itemNumber o cualquier otro que permita clarificar a usted mismo o cualquier
otro programador para qué se utiliza dicha variable.

Reglas y Guía para Asignar Nombres a los Identificadores de Variables

Utilice las siguientes reglas para asignar identificadores a las variables:

• Los identificadores de variables deben comenzar con una letra en


mayúscula o minúscula, un guión bajo (_) o un signo de pesos ($). Los
siguientes caracteres pueden ser cualquiera de los anteriores o un dígito.
• Los identificadores de variables no pueden contener signos de
puntuación, tales como espacios en blanco o guiones.
• Las palabras clave de la tecnología Java, no pueden ser utilizadas como
identificadores. Estas palabras se listan en la tabla .

La siguiente es una guía que puede ser tomada para asignar identificadores a
las variables:
• Comenzar cada nombre de variable con una letra minúscula y la primera
letra de cada palabra en el nombre de la variable en mayúscula, como
por ejemplo, miVariable.
• Elegir nombres que sean mnemotécnicos y que indiquen a un lector
casual la intención de uso de la variable.
4.10 Declaración de Variables y Asignación de Valores a una Variable
4.10.2 Los Identificadores

En el lenguaje de programación Java, un identificador es un nombre dado a


una variable, a una clase o a un método. Los identificadores comienzan con
una letra, un guión bajo o un signo de pesos ($). Los siguientes caracteres
pueden tener dígitos. Los identificadores son case-sensitive (sensibles a
mayúsculas y minúsculas) y no tienen un largo máximo.
Los siguientes son identificadores válidos:

• identificador
• nombreUsuario
• nombre_usuario
• _sys_var1
• $change

Los archivos fuente de la tecnología Java son textos escritos en Unicode 16-bit
en lugar de American Code for Information Interchange (ASCII) de 8-bit. Por lo
tanto, la definición de letra es mucho más amplia que tan sólo a-z y A-Z.

Los identificadores pueden usar caracteres que no sean ASCII, pero preste
atención a las siguientes advertencias:

• Unicode puede soportar diferentes caracteres que lucen iguales.


• Los nombres de las clases deben estar solamente en caracteres ASCII
porque la mayoría de los sistemas de archivos no soportan los
caracteres Unicode.

Un identificador no puede ser una palabra clave, pero puede contenerla como
parte de su nombre. Por ejemplo, thisOne es un identificador válido, pero this
no lo es porque this es una palabra clave de la tecnología Java.
4.10 Declaración de Variables y Asignación de Valores
a una Variable
4.10.3 Asignación de un
Valor a una
Variable

Se puede asignar un valor a una variable al momento de ser declarada o


asignar un valor luego de la declaración. Para asignar un valor a una variable
se debe agregar el símbolo "=" a continuación de la declaración seguido por el
valor a ser asignado. Por ejemplo, a la variable atributo price en la clase Shirt
se le podría asignar el valor 12.99 en el momento
de la declaración como sigue:

double price = 12.99;

Un ejemplo de una declaración de una variable de tipo boolean y su asignación


es:

boolean isOpen = false;

El operador = asigna el valor del lado derecho al ítem del lado izquierdo, y debe
ser leído como "se asigna a". En el ejemplo anterior, se leería "12.99 se asigna
a price". Los operadores, como el operador de asignación (=) se describen más
adelante en este curso.

Declaración e Inicialización de Varias Variables en Una Sola Línea de


Código

Se pueden declarar una o más variables en la misma línea de código, pero


solamente si tienen asociado el mismo tipo de datos. La sintaxis para esto es la
siguiente:

tipo identificador = valor[, identificador = valor];

Por lo tanto, si la clase Shirt tuviese dos variables atributo, como por ejemplo
price (precio minoritsta) y wholesalePrice (mayorista), podrían ser declaradas e
inicializadas como sigue:

double price = 0.0, wholesalePrice = 0.0;

Otras Maneras para Declarar Variables y Asignar Valores a las Variables

Se pueden asignar valores a las variables usando diferentes modalidades:

• Asignar valores literales directamente a la variable (como fue descrito


anteriormente en este módulo)

int ID = 0;
float pi = 3.14F;
char myChar = 'G';
boolean isOpen = false;
• Asignar el valor de una variable a otra variable:

int ID = 0;
int saleID = ID;

La primera línea de código crea una variable entera ID y le asigna el


valor numérico 0. La segunda línea de código crea otra variable entera
de nombre saleID y le asigna el valor que tiene asignado la variable ID
en ese momento (es decir, 0 también). Si posteriormente en el código
del programa se cambia el valor de la variable ID, el valor de la variable
saleID no cambia automáticamente. Es decir, a pesar de que con la
segunda asignación ambas variables tienen el mismo valor, pueden ser
cambiadas en forma independiente durante el programa.
• Asignar el resultado de una expresión a una variable entera, punto
flotante o boolean. Para esto, se evalúa el resultado de la expresión a la
derecha del operador de asignación y dicho resultado es asignado a la
variable cuyo nombre se declara.

float numberOrdered = 908.5F;


float casePrice = 19.99F;
float price = (casePrice * numberOrdered);
int hour = 12;
boolean isOpen = (hour > 8);
• Asignar a una variable el resultado devuelto por la llamada a un
método. Esto es descrito más adelante en este curso.

4.10 Declaración de Variables y Asignación de Valores a una


Variable
4.10.4 Constantes

En este módulo se ha estudiado el concepto de variable. Las variables tienen


asignados valores que pueden ser cambiados en el programa. En esta sección,
se analizará el uso de constantes para representar valores que no pueden ser
cambiados en el programa.

Por ejemplo, como parte del programa del catálogo de prendas de vestir, es
necesario utilizar un impuesto a las ventas. Para ello, se podría crear una
variable en la clase como sigue:

double salesTax = 6.25;

Sin embargo, este valor puede ser potencialmente cambiado en el programa,


cuando el valor debería ser definido y permanecer constante durante la
ejecución de la aplicación. Por lo tanto, dicho valor debería estar almacenado
en un lugar que no pueda ser cambiado. Para esto, se transforma dicha
variable en una constante, utilizando la palabra clave final.

De esta forma, se le indica al compilador que el valor de dicha variable no


puede ser cambiado luego de ser inicializado. También, por convención, los
identificadores de las constantes se escriben con todas las letras en
mayúsculas, utilizando guiones bajos para separar las palabras en el nombre.
De esta forma, se puede determinar fácilmente que se trata de una constante:

final double SALES_TAX = 6.25;

Cualquier persona que necesite acceder al impuesto puede utilizar la constante


SALES_TAX y no necesita conocer su valor o preocuparse por cambios
accidentales.

Si cualquier persona intentara cambiar el valor de una constante después que


se le ha asignado un valor, el compilador podría emitir un error. Si se modifica
el código para asignar un valor diferente a

la constante, entonces es necesario recompilar el programa.

Si el impuesto a las ventas se incrementa, sólo se necesitará cambiar el valor


de la constante en el único lugar del programa donde fue definida. Todos los
usos de la constante adoptarán el cambio automáticamente. Las constantes
también son convenientes para valores extremadamente precisos, como por
ejemplo pi (3.14159).

Guía para Nombrar Constantes

A las constantes se le deben asociar nombres de forma tal que puedan ser
fácilmente identificables. Generalmente, los nombres de las constantes se
escriben en mayúscula separando las palabras en el nombre por un guión bajo
(_).

4.10 Declaración de Variables y


Asignación de Valores a una
Variable
4.10.5 Almacenamiento de Variables
Primitivas y Constantes en la
Memoria

Los valores literales, las variables y constantes creadas, y los valores


asignados son almacenados en la memoria del computador.

Los objetos, con sus variables atributos y métodos, generalmente se


almacenan en la memoria heap. La memoria heap consiste en segmentos de
memoria que son asignados dinámicamente y usados para almacenar objetos
mientras estos son necesitados por el programa.

Las otras variables son generalmente almacenadas en la memoria de pila. La


memoria de pila almacena los ítem que sólo se utilizan por periodos cortos de
tiempo (menores al tiempo de vida de un objeto), tales como las variables
declaradas dentro de un método.
4.11 Las Palabras Clave del Lenguaje de Programación Java

Las palabras clave tienen un significado especial para el compilador de la


tecnología Java. Estas identifican un tipo de datos o el nombre del constructor
del programa. La Figura lista la palabras clave del lenguaje de rogramación
Java.

Notas importantes acerca de las palabras clave:

• Los literales true, false y null van escritos en minúsculas y no en


mayúsculas como en el lenguaje C++. En sentido estricto, estos no son
palabras clave, sino literales; sin embargo, esta distinción es académica.
• No hay un operador sizeof; no es posible el acceso directo a la memoria
por lo tanto esta información carecería de valor.
• Las palabras clave goto y const no se usan en el lenguaje de
programación Java.
4.12 Ejercicio 1: Uso de Variables de Tipo Primitivo en un Programa

El objetivo de este ejercicio es crear una clase que contenga varias


declaraciones de variable

4.12 Ejercicio 1: Uso de Variables de Tipo


Primitivo en un Programa
4.12.1 Tarea - Crear una Clase con
Variables

En esta tarea, usted compilará y ejecutará una clase Customer con varias
variables. Siga los siguientes pasos para crear la clase Customer:

1. Vaya al directorio data_types.


2. Abra un editor de texto, y cree un archivo en ese directorio con el
nombre Customer.java
3. En el archivo Customer.java, escriba una clase con el nombre Customer
que cree y asigne valores por defecto a variables para la siguiente
información (cuando sea apropiado utilice constantes)
- Un ID de Cliente
- El estado de un cliente: ‘N’ para Nuevo y ‘O’ para uno ya existente.
- Total de compras en el año.
4. Cree en la clase un método con el nombre displayCustomerInfo que usa
el método System.out.println para desplegar en la pantalla el valor de
cada una de las variables con una etiqueta que identifique el valor a
desplegar (como por ejemplo: "Total de ventas al año:").
5. Compile y ejecute el programa usando la clase provista en el archivo
CustomerTest.
6. Verifique la salida del programa para asegurarse que contiene los
valores asignados por usted.
4.13 Ejercicio 1: Resumen

Discusión – Tómese unos minutos para discutir qué experiencias, temas y


descubrimientos ha realizado durante este ejercicio.

• Experiencias
• Interpretaciones
• Conclusiones
• Aplicaciones

4.14 Uso de Operadores Aritméticos para Modificar Valores

Los programas realizan muchos cálculos matemáticos, tanto simples como


complejos. Los operadores aritméticos permiten especificar cómo los valores
de las variables deberían ser combinados o evaluados
4.14 Uso de Operadores Aritméticos
para Modificar Valores
4.14.1 Operadores Matemáticos
Estándar

Los operadores matemáticos estándar, frecuentemente llamados operadores


binarios, usados en el lenguaje de programación Java se muestran en el
gráfico.
4.14 Uso de Operadores
Aritméticos para Modificar
Valores
4.14.2 Operadores de Incremento y
Decremento (++ y - -)

Una operación común en los programas es agregar o sustraer 1 al valor de una


variable. Esto puede realizarse utilizando el operador de adición como sigue:

age = age + 1;

Sin embargo, dado que las operaciones de incremento y de decremento en 1 al


valor de una variable son operaciones muy comunes, se ofrece un operador
unario específico para cada una de ellas: el operador ++ para el incremento y el
operador -- para el decremento. Estos operadores pueden utilizarse antes (pre-
incremento y pre-decremento) o después (post-incremento y post-decremento)
de la variable. El ejemplo anterior podría escribirse entonces como:

age++; or ++age;

Utilice estos operadores en una expresión de forma cuidadosa. Con la forma


prefija, la operación (incremento o decremento) se realiza antes de cualquier
cálculo o asignación en la que interviene. Con la forma postfija, la operación se
realiza después de cualquier cálculo o asignación en la que interviene, por lo
que no se utiliza el valor modificado. El gráfico muestra los operadores de
incremento y decremento.

El siguiente ejemplo muestra el uso básico de los operadores de incremento y


decremento:

int count=15;
int a, b, c, d;
System.out.println(a + ", " + b + ", " + c + ", " + d);

El resultado de este código es:

15, 16, 17, 17

4.14 Uso de Operadores Aritméticos para Modificar


Valores
4.14.3 Precedencia de
Operadores

En una expresión matemática compleja en la que intervienen varios operadores,


¿cómo se determina cuál es la primera operación que debe realizarse?

Reglas de Precedencia
A efectos de que las operaciones matemáticas sean consistentes, el lenguaje
de programación Java sigue las reglas matemáticas estándar para la
precedencia de operadores. Los operadores son procesados en el siguiente
orden.

1. Los operadores dentro de un par de paréntesis.


2. Los operadores de incremento y decremento.
3. Los operadores de multiplicación y división, evaluados de izquierda a
derecha.
4. Los operadores de adición y sustracción, evaluados de izquierda a
derecha.

Si en una expresión ocurren operadores matemáticos con la misma


precedencia, los operadores son evaluados de izquierda a derecha.

Ejemplo de Necesidad para las Reglas de Precedencia

El siguiente ejemplo demuestra la necesidad de la precedencia de operadores:

c = 25 - 5 * 4 / 2 - 10 + 4;

En este ejemplo, no queda claro cuál fue la intención del programador. El


resultado puede ser evaluado como sigue:

• Cuando se evalúa la expresión estrictamente de izquierda a derecha, el


resultado es 34:

c = 25 - 5 * 4 / 2 - 10 + 4;
• El valor que se obtiene como resultado al aplicar las reglas de
precedencia, y que se indican con el uso de paréntesis a continuación,
es 9:

c = 25 - ((5 * 4) / 2)) - 10 + 4;

Uso de Paréntesis

La expresión se evaluará automáticamente siguiendo las reglas de precedencia.


Sin embargo se puede hacer uso de paréntesis para realizar los cálculos en
otro orden, como se muestra a continuación:

c = (((25 - 5) * 4) / (2 - 10)) + 4;
c = ((20 * 4) / (2 - 10)) + 4;
c = (80 / (2 - 10)) + 4;
c = (80 / -8) + 4;
c = -10 + 4;
c = -6;

4.15 Uso de Promoción y Conversión de Tipos


La asignación de una variable o una expresión a otra variable puede resultar en
un error debido a que no concuerdan el tipo de datos del cálculo y la memoria
asignada a la variable destino. Específicamente, el compilador podría detectar
que en la asignación se perderá precisión y entonces no permitirá la
compilación, o que el resultado será incorrecto.

Para reparar este problema, los tipos de las variables tienen que ser
promovidos a un tipo con tamaño mayor, o bien, convertidos (casting) a un tipo
con tamaño menor.

Por ejemplo, considere las siguientes asignaciones:

int num1 = 53; // 32 bits de memoria para almacenar el valor


int num2 = 47; // 32 bits de memoria para almacenar el valor
byte num3; // 8 bits de memoria reservados
num3 = (num1 + num2); // causa un error de compilación

Este código podría funcionar correctamente, debido a que los valores del tipo
byte están incluidos en el del tipo int. Sin embargo, el compilador no puede
resolver esta asignación y por lo tanto enviará el mensaje de error "possible
loss of precision" dado que el tamaño de un valor de tipo byte es menor que el
de un valor de tipo int.

Para solucionar este problema, se puede convertir el tipo del lado derecho para
que concuerde con el tipo del lado izquierdo o se puede declarar la variable del
lado izquierdo (num3) de un tipo mayor, como por ejemplo int.

Este problema es solucionado cambiando el tipo de num3 a int:

int num1 = 53;


int num2 = 47;
int num3;
num3 = (num1 + num2);
4.15 Uso de Promoción y Conversión de Tipos
4.15.1 Promoción

En algunas circunstancias, el compilador realiza el cambio del tipo de una


variable a uno mayor. Esta acción es conocida como promoción y en algunos
casos se relaiza en forma automática por el compilador. Este tipo de
promociones incluye:

• La asignación de un valor de un tipo (a la derecha del signo de =) a una


variable de un tipo mayor (a la izquierda del signo de =).
• La asignación de un valor de un tipo entero a una variable de tipo punto
flotante (dado que no se perderán la parte decimal).

El siguiente ejemplo contiene un literal (de tipo int) que será promovido
automáticamente a otro tipo (long) antes de que el valor (6) sea asignado a la
variable.

long big = 6;

La promoción se realiza en forma automática dado que el valor 6 es de tipo int,


y el tipo long es mayor que el tipo int. temporal.
4.15 Uso de Promoción y Conversión de Tipos
4.15.2 Conversión de Tipos

La conversión de tipos permite cambiar el tipo de un valor a uno menor, como


por ejemplo convertir un valor de tipo long a un valor de tipo int. Esto puede ser
requerido para convertir valores para poder invocar a un método
apropiadamente, asignar valores a una variable de un tipo de datos menor, etc.

Para realizar la conversión de tipos se coloca el tipo destino entre paréntesis


delante de la expresión a convertir. La sintaxis para la conversión del tipo de
una expresión es:

identificador = (tipo_destino) expresión

donde:

• El identificador es el nombre de la variable a la que se desea asignar.


• El valor es el valor que se desea asignar al identificador.
• El tipo_destino es el tipo al que se desea convertir el valor. Observar que
el tipo_destino se debe incluir entre paréntesis.

Por ejemplo, considere las siguientes asignaciones:

int num1 = 53; // 32 bits de memoria para alojar el valor


int num2 = 47; // 32 bits de memoria para alojar el valor
byte num3; // 8 bits de memoria reservados
num3 = (num1 + num2); // causa un error de compilación

El error de compilación puede ser solucionado convirtiendo el resultado a un


tipo byte:

int num1 = 53; // 32 bits de memoria para alojar el valor


int num2 = 47; // 32 bits de memoria para alojar el valor
byte num3; // 8 bits de memoria reservada
num3 = (byte)(num1 + num2); // no hay pérdida de datos

Otro posible problema es el siguiente:

int myInt;
long myLong = 99L;
myInt = (int) (myLong);// No hay pérdida de datos. Solo ceros.
// Un número mayor podría
// obtenerse como resultado
long myLong = 123987654321L;
myInt = (int) (myLong); // El número es is truncado

Si se convierte un valor de tipo float o double con parte decimal, a untipo entero
como por ejemplo int, la parte decimal se perderá. Sin embargo, esta
modalidad de conversión de tipos es útil en algunos casos como modalidad de
truncar un número punto flotante a la parte entera del mismo
4.15 Uso de Promoción y Conversión de Tipos
4.15.3 Supuestos realizados por el Compilador para los Tipos de Datos
Enteros y Punto Flotante

El compilador de la tecnología Java realiza ciertos supuestos cuando evalúa


expresiones. Es importante entender estos supuestos para realizar
apropiadamente la conversión de tipos u otras actividades.

Tipos de Datos Enteros y Operaciones.

Cuando se utilizan valores de tipos de datos primitivos en una expresión con


ciertos operadores (*, /, -, +, %), los valores se convierten automáticamente a
valores int (o mayores si es necesario) y a continuación se realiza la operación.
Esta conversión puede causar un error de overflow o pérdida de precisión. En
el siguiente ejemplo, ocurre un error debido a que dos de los tres operandos
son promovidos de tipo short a tipo int antes de realizar la adición.

short a, b, c;
a=1;
b=2;
c = a + b ; //error de compilaci¨®n

En la última línea, los valores de a y b son promovidos al tipo int y a


continuación se realiza la adición. El resultado de la adición es un valor de tipo
int. El operador de asignación intentará asignar un valor de tipo int a una
variable de tipo short ( c ), que es una variable de tipo menor. Por lo tanto, esta
asignación es ilegal y causará un error durante la
compilación.
El código funcionará correctamente si se realiza alguno de los siguientes
cambios:

• Declarar c como de tipo int (int c;)


• Convertir el tipo del resultado de (a+b) en la asignación (c=(short)(a+b)))

Tipos de Datos de Punto Flotante y Asignación

Así como int es el tipo por defecto de los tipos enteros, los valores asignados a
variables de tipo punto flotante por defecto son double, a menos que se
explicite que es de tipo float.
Por ejemplo, la siguiente línea de código producirá un error durante la
compilación, dado que 27.9 por defecto es un valor de tipo double y se lo trata
de asignar a una variable de tipo float.

float float1 = 27.9;//error de compilación

Los siguientes cambios podrían realizarse para que el código funcione


correctamente:
• Agregar una F a continuación del literal 27.9 (27.9F) para indicar que se
trata de un literal del tipo float.
• Convertir el literal 27.9 a float (float float1 = (float) 27.9;)

4.16 El Punto y Coma, los Bloques y los Espacios en Blanco

En el lenguaje de programación Java, una sentencia consiste en una o más


líneas de código y finaliza con el símbolo de punto y coma(;).

Por ejemplo,

totales = a + b + c + d + e + f;

es lo mismo que

totales = a + b + c
+ d + e + f;

Un bloque, algunas veces llamado "sentencia compuesta", es un grupo de


sentencias delimitadas por las llaves que abren y que cierran ({ }). Tiene
funciones útiles que se describen en el Capítulo 4, "Las Expresiones y el
Control de Flujo". Por ahora, sólo piense en un bloque como un grupo de
sentencias que se tratan en conjunto.

A continuación se presentan otros ejemplos de sentencias de bloques:

// Una sentencia de bloque


{
x = y + 1;
y = x + 1;
}

// un bloque contiene una definición de una clase


public class MyDate{
private int day;
private int month;
private int year;
}

// una sentencia de bloque puede estar anidada dentro de otra sentencia

while ( i < large ) {


a = a + i;
// bloque anidado
if ( a == max ) {
b = b + a;
a = 0;
}
I = I +1;
}

Puede haber espacios en blanco entre los elementos del código fuente. Se
permite cualquier cantidad. Puede usar espacios en blanco, incluyendo
separadores, tabuladores o nuevas líneas, para mejorar la claridad y el aspecto
visual de su código fuente. Compare:

{int x;x=23*54;}

con:

int x;

x = 23 * 54;

4.17 Ejercicio 2: Uso de Operadores y Conversión de Tipos


El objetivo de este ejercicio es practicar el uso de operadores y la conversión
de tipos.

4.17 Ejercicio 2: Uso de


Operadores y Conversión
de Tipos
4.17.1 Tarea - Calcular Edades Usando
Operadores

En esta tarea, se utilizan operadores para calcular la edad en minutos y en


milisegundos. Siga los siguientes pasos para crear una clase Person que
calcule varias edades.

1. Vaya al directorio data_types .


2. Escriba la clase Person de este módulo.
3. Agregue el siguiente código a la clase Person
a. Agregue una línea para calcular y desplegar su edad en minutos
b. Agregar una línea para calcular y desplegar su edad en milisegundos
(la milésima parte de un segundo: 1/1000)
c. Pruebe el programa usando la clase PersonTest con las edades 1, 24
y 100.

4.17 Ejercicio 2: Uso de


Operadores y Conversión
de Tipos
4.17.2 Tarea - Uso de Conversión de Tipos
para Prevenir Pérdida de Datos

En esta tarea utilizará conversión de tipos para asegurar que no ocurre pérdida
de datos en su programa. Siga los siguientes pasos para crear una clase Order:

1. Escriba una clase Order con dos variables de tipo int itemQuantity e
itemPrice y un método con el nombre calculateTotal.
2. Escriba un método calculateTotal que multiplique los dos valores enteros
y despliegue el resultado.
3. Pruebe el programa usando la clase OrderTest y los siguientes valores:
a. Un valor int de un dígito
b. Un valor int de cinco dígitos
c. Un valor int de nueve dígitos

Asegúrese que el resultado producido por su programa es el correcto (por


ejemplo, realizando los mismos cálculos en forma manual o utilizando una
máquina calculadora)
4.17 Ejercicio 2: Uso de Operadores y Conversión de
Tipos
4.17.3 Tarea - Crear un
Programa de
Temperatura

En esta tarea, escribirá un programa de nombre Temperature que contiene una


temperatura en Fahrenheit y una llamada a un método calculateCelsius. Siga
los siguientes pasos para crear una clase Temperature:

1. Escriba un método calculateCelsius que convierta un valor Fahrenheit a


un valor Celsius y despliegue dicho valor.
2. Use la siguiente información para convertir valores Farenheit a valores
Celsius:
- Para convertir valores Fahrenheit a Celsius, reste 32 al valor
Fahrenheit, multiplique por 5 y divida entre 9.
- Asegúrese que el resultado obtenido es correcto (por ejemplo
realizando los cálculos en forma manual o con una máquina calculadora).
3. Pruebe el programa usando la clase TemperatureTest.

4.18 Ejercicio 2: Resumen

Discusión – Tómese unos minutos para discutir las experiencias, temas y


descubrimientos realizados durante estos ejercicios.

• Experiencias
• Interpretaciones
• Conclusiones
• Aplicaciones

4.19 Preguntas de Repaso

Ahora le proponemos que resuelva el siguiente ejercicio para poner a prueba


sus conocimientos.

Defina por Verdadero o Falso las siguientes afirmaciones según corresponda.


¡Éxitos!
5 Creación y Uso de Objetos

A lo largo de este capítulo aprenderemos a declarar, instanciar e inicializar


variables. Compararemos el almacenamiento de las variables de referencia y
de tipos primitivos. Aprenderemos acerca de la especificación y uso de las
bibliotecas de Clases.

5.1 Repaso del Capítulo Anterior

Los principales temas del capítulo anterior son:

Identificación del Uso de las Variables y la Sintaxis

Las variables se utilizan para almacenar y recuperar datos en un programa.


Las variables atributo (variables declaradas fuera de un método y sin la palabra
clave static) también se denominan variables miembros o variables de instancia.
Cuando un objeto es instanciado a partir una clase, estas variables contienen
datos específicos de una instancia de la clase particular.
Los programas pueden tener también variables definidas dentro de los métodos.
Estas variables se denominan variables locales.

Las variables realizan tareas tales como:

• Almacenar datos correspondientes a los atributos para un objeto


instancia particular (como se ha vista con las variables price y ID).
• Asignar el valor de una variable a otra.
• Representar valores en una expresión matemática.
• Desplegar los valores en la pantalla.

Declaración e Inicialización de Variables

La declaración e inicialización de las variables atributo siguen la misma sintaxis


general. La sintaxis para la declaración e inicialización de variables atributo es:

[modificadores] tipo identificador [=valor];

Tipos de Datos Primitivos

• Tipos enteros: byte, short, int y long


• Tipos Punto Flotante: float y double
• Tipo Texto: char
• Tipo Lógico: boolean

Para explicitar que se trata de un literal perteneciente al tipo de datos float, se


debe colocar una F al final del valor numérico.

Declaración de Variables y Asignación de Valores a una Variable

Las variables deben ser declaradas antes de que sean usadas.

Nombre de una Variable

Reglas y Guía para Asignar Nombres a los Identificadores de Variables:

• Los identificadores de variables deben comenzar con una letra en


mayúscula o minúscula, un guión bajo (_) o un signo de pesos ($). Los
siguientes caracteres pueden ser cualquiera de los anteriores o un dígito.
• Los identificadores de variables no pueden contener signos de
puntuación, tales como espacios en blanco o guiones.
• Las palabras clave de la tecnología Java, no pueden ser utilizadas como
identificadores. Estas palabras se listan en la tabla.
• Comenzar cada nombre de variable con una letra minúscula y la primera
letra de cada palabra en el nombre de la variable en mayúscula, como
por ejemplo, miVariable.
• Elegir nombres que sean mnemotécnicos y que indiquen a un lector
casual la intención de uso de la variable.

Asignación de un Valor a una Variable


Se puede asignar un valor a una variable al momento de ser declarada o
asignar un valor luego de la declaración. Para asignar un valor a una variable
se debe agregar el símbolo "=" a continuación de la declaración seguido por el
valor a ser asignado.

double price = 12.99;

boolean isOpen = false;

double price = 0.0, wholesalePrice = 0.0;

El operador = asigna el valor del lado derecho al ítem del lado izquierdo, y debe
ser leído como "se asigna a".

Asignar el valor de una variable a otra variable:

int ID = 0;
int saleID = ID;

Constantes

En esta sección, se analizará el uso de constantes para representar valores


que no pueden ser cambiados en el programa.
Por ejemplo, como parte del programa del catálogo de prendas de vestir, es
necesario utilizar un impuesto a las ventas. Para ello, se podría crear una
variable en la clase como sigue:

double salesTax = 6.25;

Sin embargo, este valor puede ser potencialmente cambiado en el programa,


cuando el valor debería ser definido y permanecer constante durante la
ejecución de la aplicación. Por lo tanto, dicho valor debería estar almacenado
en un lugar que no pueda ser cambiado. Para esto, se transforma dicha
variable en una constante, utilizando la palabra clave final.

final double SALES_TAX = 6.25;

A las constantes se le deben asociar nombres de forma tal que puedan ser
fácilmente identificables. Generalmente, los nombres de las constantes se
escriben en mayúscula separando las palabras en el nombre por un guión bajo
(_).

Uso de Operadores Aritméticos para Modificar Valores


Los operadores matemáticos estándar, frecuentemente llamados operadores
binarios, usados en el lenguaje de programación Java son: Adicion,
Sustracción, Division y Modulo.

Operadores de Incremento y Decremento (++ y --)

Dado que las operaciones de incremento y de decremento en 1 al valor de una


variable son operaciones muy comunes, se ofrece un operador unario
específico para cada una de ellas: el operador ++ para el incremento y el
operador -- para el decremento.

age++; or ++age;

Precedencia de Operadores

Reglas de Precedencia:

1. Los operadores dentro de un par de paréntesis.

2. Los operadores de incremento y decremento.

3. Los operadores de multiplicación y división, evaluados de izquierda a


derecha.

4. Los operadores de adición y sustracción, evaluados de izquierda a


derecha.

Si en una expresión ocurren operadores matemáticos con la misma


precedencia, los operadores son evaluados de izquierda a derecha. Sin
embargo se puede hacer uso de paréntesis para realizar los cálculos en otro
orden.

Uso de Promoción y Conversión de Tipos

Específicamente, el compilador podría detectar que en la asignación se perderá


precisión y entonces no permitirá la compilación, o que el resultado será
incorrecto.
Para reparar este problema, los tipos de las variables tienen que ser
promovidos a un tipo con tamaño mayor, o bien, convertidos (casting) a un tipo
con tamaño menor.

En algunas circunstancias, el compilador realiza el cambio del tipo de una


variable a uno mayor. Esta acción es conocida como promoción y en algunos
casos se relaiza en forma automática por el compilador.
La conversión de tipos permite cambiar el tipo de un valor a uno menor, como
por ejemplo convertir un valor de tipo long a un valor de tipo int.
Para realizar la conversión de tipos se coloca el tipo destino entre paréntesis
delante de la expresión a convertir. La sintaxis para la conversión del tipo de
una expresión es:

identificador = (tipo_destino) expresión

Las Palabras Clave del Lenguaje de Programación Java

Las palabras clave tienen un significado especial para el compilador de la


tecnología Java.

5.3 Objetivos

Una vez completado este capítulo, usted estará en condiciones de:

• Declarar, instanciar e inicializar variables de referencia a objetos.


• Comparar el almacenamiento de las variables de referencias a objetos y
las variables de tipos primitivos.
• Usar una clase (la clase String) incluida en el Java SDK.
• Usar la especificación de la biblioteca de clases J2SE para aprender
acerca de las clases de esta API.

Este capítulo describe cómo se utilizan los objetos en un programa y explica


cómo organizar un programa usando la sintaxis específica de clases y de
métodos.

Discusión – Las siguientes preguntas son relevantes para comprender cómo


se crean y usan los objetos:

• ¿Qué significa crear una instancia de un modelo de una casa?


• ¿Cómo se refiere a las distintas casas que están ubicadas en una
misma calle?
• Cuando un constructor de casas construye una casa, ¿realiza él mismo
todos los componentes de la casa (incluyendo las ventanas, las puertas,
los gabinetes, etc.)?

5.4 Recursos Adicionales


Recursos adicionales – La siguiente referencia ofrece información adicional
sobre los temas descritos en este capítulo:

• The Java Tutorial. [Online]. Disponible en:

http://java.sun.com/docs/books/
tutorial/java/javaOO/
objectcreation.html

http://java.sun.com/docs/books/
tutorial/java/javaOO/
usingobject.html

• The Code Conventions


for the Java Programming
Language. [Online].
Disponible en:

http://java.sun.com/docs/
codeconv/html/
CodeConvTOC.doc.html

5.5 Declaración de Referencias a Objetos, Instanciación de Objetos e


Inicialización de Referencias

Las variables que referencian a objetos contienen una dirección de memoria


que indica la ubicación del objeto en la memoria. Una carta puede asociarse a
una variable de referencia dado que tiene una dirección que "apunta" a un
edificio (objeto) particular. La figura muestra varias direcciones apuntando a
distintas casas.

En esta sección, usted creará clases e instanciará objetos dentro de dichas


clases. La figura muestra una clase ShirtTest que crea una variable, que
referencia a un objeto (myShirt), e inicializa la variable de forma tal que apunte
a una instancia de la clase Shirt (líneas 5 y 6). Luego de que la variable de
referencia fue creada e inicializada puede ser utilizada para invocar al método
displayShirtInformation (Línea 8) del objeto Shirt.

La declaración e inicialización de una variable de referencia es muy similar a la


declaración e inicialización de una variable de tipo primitivo. La única diferencia
es que se debe crear un objeto de la clase a la que apunta la variable de
referencia, antes de que se pueda inicializar dicho objeto.
Para declarar, instanciar e inicializar el objeto al que referencia la variable, se
deben seguir los siguientes pasos:

1. Declarar una referencia a un objeto, mediante la especificación de su


identificador y el tipo del objeto al que la referencia apunta (la clase del
objeto).
2. Crear un objeto instancia utilizando la palabra clave new.
3. Inicializar la variable que referencia al objeto asignándole el objeto
creado en el punto anterior.
5.5 Declaración de Referencias a Objetos,
Instanciación de Objetos e
Inicialización de Referencias
5.5.1 Declaración de Variables
que Referencian Objetos

Para declarar una variable que referencia a un objeto se determina la clase del
objeto que desea crear y luego se selecciona el nombre que se querrá utilizar
para referenciar al objeto. La sintaxis para la declaración de variables que
referencian a objetos es:

NombreClase identificador;

donde:

• El NombreClase es el nombre de la clase o del tipo del objeto al que se


referirá.
• El identificador es el nombre asignado a la variable del tipo
NombreClase.

Por ejemplo, la clase ShirtTest crea una variable que referencia a un objeto en
la Línea 5, cuyo nombre es myShirt. Por defecto, las referencias a objetos que
son variables miembros de una clase se inicializan con el valor null. Dado que
myShirt es una variable local declarada en la Línea 5, no está inicializada. Al
igual que con todas las variables, se debería crear un nombre que identifique el
propósito de la misma, siguiendo las reglas para asignar nombres a
identificadores.

5.5 Declaración de Referencias a Objetos, Instanciación de


Objetos e Inicialización de Referencias
5.5.2 Instanciación
de un Objeto

Después de declarar la referencia al objeto, se debería crear el objeto al que la


variable referencia. La sintaxis para instanciar un objeto es:

new NombreClase();

donde:

• La palabra clave new crea un objeto instancia de la clase


• NombreClase es el nombre de la clase o del tipo del objeto a ser creado.

Por ejemplo, la clase ShirtTest crea un objeto de tipo Shirt en la Línea 6 usando
la palabra clave new.
5.5 Declaración de Referencias a Objetos,
Instanciación de Objetos e Inicialización de
Referencias
5.5.3 La Construcción y la
Inicialización de
Objetos

En secciones anteriores, se ha visto cómo ejecutar una llamada a new Xyz()


para asignar espacio para un nuevo objeto. A continuación, se mostrará que
algunas veces se pueden colocar argumentos entre los paréntesis, como, por
ejemplo, en new MyDate(22, 7, 1964). De esta forma, el uso de la palabra clave
new logra que:

1. Se asigne e inicialice el espacio para el nuevo objeto con la forma cero o


null. En el lenguaje de programación Java, esta fase es indivisible para
asegurar que no se pueda tener un objeto con valores aleatorios en él.
2. Se realice cualquier inicialización explícita.
3. Se ejecute un constructor, que es un método especial. Los argumentos
encerrados entre los paréntesis, en la llamada al new, son pasados al
constructor (en este caso, (22, 7, 1964)).
4. La operación new devuelve una referencia al nuevo objeto en la
memoria heap. Esta referencia se almacena en la variable de referencia.

5.5 Declaración de Referencias a Objetos,


Instanciación de Objetos e
Inicialización de Referencias
5.5.4 Inicialización de Variables que
Referencian Objetos

El paso final en la creación de una variable que referencia un objeto es su


inicialización, asignándole el objeto recién creado. Al igual que con la
asignación e inicialización de variables de tipos primitivos, esto se realiza con el
signo de asignación (=). La sintaxis para la inicialización de un objeto a una
variable que le referencia es la siguiente:

identificador = new NombreClase();

Por ejemplo, la clase ShirtTest asigna el objeto creado a la variable referencia


myShirt. Se puede decir que "La referencia a un objeto myShirt apunta al objeto
Shirt".

myShirt = new Shirt();

Una vez que se haya creado un objeto instancia válido, se le puede utilizar para
manipular sus datos y llamar a sus métodos.

Creación de Variables que Referencian Objetos usando una o dos Líneas


de Código

En la clase ShirtTest del ejemplo, la declaración de la referencia al objeto, la


creación del nuevo objeto Shirt y la inicialización tienen lugar en más de una
línea de código. Por ejemplo:

Shirt myShirt;
myShirt = new Shirt();

Sin embargo, también es posible crear una referencia a un objeto, crear un


objeto y realizar la asignación del objeto a la referencia en la misma línea de
código. La siguiente línea de código es equivalente a las dos líneas del ejemplo
anterior:

Shirt myShirt = new Shirt();

5.5 Declaración de Referencias a Objetos,


Instanciación de Objetos e
Inicialización de Referencias
5.5.5 Usar una Variable que
Referencia a un Objeto para
Manejar Datos

Se debe utilizar el operador punto (.) aplicado a una referencia a un objeto para
manejar los valores de las variables miembro o para invocar los métodos de un
objeto específico. Por ejemplo, para cambiar el valor de la variable colorCode
del nuevo objeto al valor "G" (por green), se debe agregar la siguiente línea de
código a la clase ShirtTest:

myShirt.colorCode = 'G';

A continuación se muestra una nueva clase ShirtTestTwo que crea dos


referencias a objetos, myShirt y yourShirt, las cuales refieren a dos objetos
Shirt diferentes (Líneas 5 y 6).
En el ejemplo, la declaración, la instanciación y la inicialización para cada
referencia es realizada en una única línea de código. La declaración se ubica
en el lado izquierdo del signo de igual, mientras que la instanciación para cada
referencia es realizada en el lado derecho del signo de igual. El signo de igual
asigna el objeto instanciado a la referencia declarada. Después de que se
crearon las referencias a los objetos, cada objeto puede ser tratado en forma
independiente. Específicamente, al objeto referenciado por la variable myShirt
se le asigna el valor "R" (red) en el atributo colorCode (Línea 11), mientras que
al objeto referenciado por la variable yourShirt se le asigna el valor "G" (green)
al atributo colorCode (Linea 12). Usted podría escribir líneas de código para
crear cualquier cantidad de referencias a distintos objetos Shirt dependiendo de
cuánta memoria dispone su computador.

1
2 public class ShirtTestTwo {
3
4 public static void main (String args[]) {
5
6 Shirt myShirt = new Shirt();
7 Shirt yourShirt = new Shirt();
8
9 myShirt.displayShirtInformation();
10 yourShirt.displayShirtInformation();
11
12 myShirt.colorCode='R';
13 yourShirt.colorCode='G';
14
15 myShirt.displayShirtInformation();
16 yourShirt.displayShirtInformation();
17
18 }
19 }
20
5.5 Declaración de Referencias a
Objetos, Instanciación de Objetos
e Inicialización de Referencias
5.5.6 Almacenamiento de las Variables
de Referencias a Objetos en
Memoria

Mientras que las variables primitivas almacenan valores, las variables que
referencian objetos almacenan la ubicación (dirección de memoria) de objetos
en la memoria.
5.5 Declaración de Referencias a Objetos, Instanciación de Objetos e
Inicialización de Referencias
5.5.7 Asignación de una Referencia desde una Variable a Otra

¿Qué cree Usted que sucede si asigna una variable que referencia a un objeto
a otra variable referencia? (como por ejemplo en el siguiente código)

Shirt myShirt = new Shirt();


Shirt yourShirt = new Shirt();
myShirt = yourShirt;

Las variables referencia myShirt y yourShirt contienen la misma dirección


(ambas referencian al mismo objeto). Al igual que con las variables primitivas,
el valor del ítem en el lado derecho es asignado al ítem del lado izquierdo. En
el ejemplo anterior, la dirección en la variable referencia yourShirt (0x99f311
por ejemplo), es asignada a la variable myShirt. Ambas variables referencian
ahora al mismo objeto. Sin embargo, el objeto al que antes referenciaba la
variable myShirt todavía existe.

A menos que otra variable de referencia estuviese referenciando al segundo


objeto de tipo Shirt, este será recolectado por el Garbage Collector.
5.5 Declaración de Referencias a
Objetos, Instanciación de Objetos
e Inicialización de Referencias
5.5.8 La Ubicación y la Distribución de
la Memoria

En el cuerpo de un método, la siguiente declaración reserva memoria


únicamente por la referencia mostrada en la Figura . El uso de la palabra
clave new en el ejemplo de la figura implica la reserva e inicialización de
memoria.
5.5 Declaración de Referencias a Objetos,
Instanciación de Objetos e Inicialización de
Referencias
5.5.9 La Inicialización
Explícita de los
Atributos

Si se colocan expresiones de asignación simple en la declaración de los


miembros, se pueden inicializar explícitamente durante la construcción del
objeto.

En la clase MyDate de este ejemplo, se declara explícitamente la inicialización


de los tres atributos, como se muestra en la Figura

5.5 Declaración de Referencias a Objetos,


Instanciación de Objetos e Inicialización de
Referencias
5.5.10 La Ejecución del
Constructor
La fase final de la inicialización de un nuevo objeto es la llamada al constructor.
La inicialización por defecto de un objeto puede ser sobrescrita en un
constructor. También se pueden realizar cálculos y operaciones en él y se le
pueden pasar argumentos a su proceso de construcción. De esta forma, el
código que se requiere para la construcción del nuevo objeto puede controlar el
objeto que crea.

En el ejemplo se llama al constructor, como se muestra en la figura

5.5 Declaración de Referencias a Objetos,


Instanciación de Objetos e Inicialización
de Referencias
5.5.11 La Asignación a una
Variable

La asignación de la variable establece la relación entre la variable de referencia


(my_birth en este ejemplo) y el objeto creado, como se muestra en la figura
5.6 La Asignación de Referencias

En el lenguaje de programación Java, una variable declarada con un tipo de


clase es una variable de referencia, dado que alude a un tipo no primitivo. Esto
tiene consecuencias relativas a la asignación. Por ejemplo, en el siguiente
fragmento de código:

int x = 7;
int y = x;
MyDate s = new MyDate(22, 7, 1964);
MyDate t = s;

se crean cuatro variables: dos de tipos primitivos (int) y dos de referencia de


tipo MyDate. El valor de x es 7 y este valor es copiado en y. Las variables x e y
son variables independientes y los cambios que se realicen a una de ellas no
afectarán a la otra.

Sin embargo, para las variables s y t, sólo existe un objeto MyDate y este
contiene la fecha 22 de Julio de 1964. Las variables s y t se refieren a este
único objeto, como se muestra en la Figura
Con una reasignación de la variable t, se crea el nuevo objeto MyDate (para el
22 de Diciembre de 1964) y t se refiere a este último, como se muestra en la
Figura . Dicha situación se representa como:

t = new MyDate(22, 12, 1964); // //reasigna la variable


5.7 El Pasaje por Valor

El lenguaje de programación Java pasa los argumentos sólo por valor. Esto
significa que no es posible cambiar el valor del argumento del método que
llama dentro del método llamado.

Sin embargo, cuando una instancia de objeto se pasa como argumento a un


método, el valor del argumento no es el objeto en sí mismo, sino una referencia
a él. De esta forma, se puede cambiar el contenido del objeto en el método
llamado, pero no la referencia a él.

Para muchas personas, esto puede interpretarse como pasaje por referencia.
En efecto, desde el punto de vista del comportamiento, tiene mucho en común
con el pasaje por referencia. Sin embargo, hay dos razones por las que esto es
incorrecto:

• Primero, la capacidad para cambiar lo que se pase a un método sólo se


aplica a objetos y no a valores primitivos.
• Segundo, el valor actual asociado a la variable del tipo del objeto es la
referencia al objeto, y no, el objeto en sí mismo. Esta es una distinción
importante y, si es claramente comprendida, se acepta que el lenguaje
de programación Java pasa los argumentos por valor.
El siguiente código de la figura ilustra un ejemplo de este punto.

Dicho código produce la siguiente salida:

java PassTest

Int value is: 11


MyDate: 22-7-1964
MyDate: 4-7-1964

El método changeObjectAttr no cambia el objeto MyDate. Sin embargo, el


método changeObjectAttr cambia el atributo date del objeto MyDate.

1 public class PassTest {


2
3 // Methods to change the current values
4 public static void changeInt(int value) {
5 value = 55;
6 }
7 public static void changeObjectRef(MyDate ref) {
8 ref = new MyDate(1, 1, 2000);
9 }
10 public static void changeObjectAttr(MyDate ref) {
11 ref.setDay(4);
12 }
13
14 public static void main(String args[]) {
15 MyDate date;
16 int val;
17
18 // Assign the int
19 val = 11;
20 // Try to change it
21 changeInt(val);
22 // What is the current value?
23 System.out.println("Int value is: " + val);
24
25 // Assign the date
26 date = new MyDate(22, 7, 1964);
27 // Try to change it
28 changeObjectRef(date);
29 // What is the current value?
30 System.out.println("MyDate: " + date);
31
32 // Now change the day attribute
33 // through the object reference
34 changeObjectAttr(date);
35 // What is the current value?
36 System.out.println("MyDate: " + date);
37 }
38 }

5.8 La Referencia this

La palabra clave this tiene dos usos:

• La resolución de ambigüedades entre los parámetros y las instancias de


las variables.
• El pasaje del objeto actual como un parámetro a otro método.

El Código de la Figura brinda una definición de clase que demuestra estos


usos. La clase MyDate declara variables de instancia en las Líneas 2-4. A uno
de los parámetros en uno de los constructores (Lineas 6-10) se le da también el
nombre day. Por lo tanto, en este contexto, la palabra clave this resuelve la
ambigüedad (Línea 7).

El método addDays crea un nuevo objeto MyDate (Línea 18). En la llamada a


este constructor, el método usa la palabra reservada this, como un argumento
para referenciar el objeto actual

1 public class MyDate {


2 private int day = 1;
3 private int month = 1;
4 private int year = 2000;
5
6 public MyDate(int day, int month, int year) {
7 this.day = day;
8 this.month = month;
9 this.year = year;
10 }
11 public MyDate(MyDate date) {
12 this.day = date.day;
13 this.month = date.month;
14 this.year = date.year;
15 }
16
17 public MyDate addDays(int moreDays) {
18 MyDate newDate = new MyDate(this);
19 newDate.day = newDate.day + moreDays;
20 // Not Yet Implemented: wrap around code...
21 return newDate;
22 }
23 public String toString() {
24 return "" + day + "-" + month + "-" + year;
25 }
26 }
27

5.9 Las Convenciones para la Codificación en el Lenguaje de


Programación Java

A continuación se describen las convenciones para la codificación en el


lenguaje de programación Java:

• Paquetes - Sus nombres deben ser sustantivos en minúscula. package


shipping.objects
• Clases - Sus nombres deben ser sustantivos en mayúsculas
y minúsculas, con la primera letra de cada palabra en mayúscula. class
AccountBook
• Interfaces - Sus nombres deben escribirse en mayúsculas y minúsculas,
de la misma forma que los nombres de las clases. interface Account
• Métodos - Sus nombres deben ser verbos, escritos en mayúsculas y
minúsculas, con la primera letra en minúscula. Dentro del nombre de
cada método, se utilizan las mayúsculas para separar las palabras. Se
recomienda utilizar en forma limitada el uso del guión bajo.
balanceAccount()
• Variables - Sus nombres pueden escribirse en mayúsculas y minúsculas,
pero la primera letra debe ser minúscula. Las palabras en el nombre se
separan por mayúsculas. Se recomienda utilizar en forma limitada el uso
de los guiones bajos y evitar usar el signo de pesos ($) ya que este
carácter tiene un significado especial para las clases internas.
currentCustomer
Los nombres de las variables tienen que ser significativos e indicar al
lector casual la intención de su uso. Se sugiere evitar los de un solo
carácter, excepto para variables temporales (por ejemplo i, j y k son
usadas como variables de control en iteraciones).
• Constantes - Los nombres de las constantes primitivas deben estar
escritos en mayúsculas, con las palabras separadas por guiones bajos.
En los de las constantes de objetos pueden usarse mayúsculas y
minúsculas.

HEAD_COUNT
MAXIMUM_SIZE
• Estructuras de control - Se deben utilizar las llaves ({ }) alrededor de
todas las sentencias, incluso en las sentencias simples que son parte de
una estructura de control, tales como en una sentencia if-else o en una
sentencia for.

if ( condition ) {
statement1;
} else {
statement2;
}
• Espaciado - Se debe escribir sólo una sentencia simple por línea y
utilizar sangrías de doble o cuádruple espaciado para hacer el código
legible. El número de espacios puede variar dependiendo de qué
estándares de código se utilicen.
• Comentarios - Los comentarios se utilizan para explicar los segmentos
de código que no resultan obvios. Se usa el delimitador de comentarios
// para comentarios normales y los delimitadores /* ….*/ para comentar
grandes secciones de código. El comentario de documentación /** ... */
se utiliza para proporcionar una entrada al javadoc para que este genere
documentación HTML para el código.

// Un comentario de una sola línea.


/* Un comentario que continúa pasada una línea y toma
el espacio de múltiples líneas. */
/** Un comentario para documentación.
* @see Otra clase para más información.
*/

5.10 Laboratorios

Laboratorio 3 - Los Identificadores, las Palabras Clave y los Tipos


5.10 Laboratorios
5.10.1 Objetivos

Una vez completado este laboratorio, usted debería poder:

• Explorar las asignaciones de variables de referencias.


• Usar una variable de referencia para codificar una asociación de objetos.

5.10 Laboratorios
5.10.2 Ejercicio 1: Investigación Acerca de la Asignación de
Referencias

Ejercicio 1 - Investigación Acerca de la Asignación de Referencias

5.10 Laboratorios
5.10.3 Ejercicio 2: Creación de Cuentas de Cliente (Nivel 1)

Ejercicio 2 - Creación de Cuentas de Cliente (Nivel 1)

5.10 Laboratorios
5.10.4 Ejercicio 2: Creación de Cuentas de Cliente (Nivel 2)

Ejercicio 2 - Creación de Cuentas de Cliente (Nivel 2)

5.10 Laboratorios
5.10.5 Ejercicio 2: Creación de Cuentas de Cliente (Nivel
3)

Ejercicio 2 - Creación de Cuentas de Cliente (Nivel 3)

5.10 Laboratorios
5.10.6 Resumen del Ejercicio
Discusión – Dedique unos pocos minutos para discutir las experiencias, temas
o descubrimientos que tuvo durante el ejercicio de laboratorio.

• Experiencias
• Interpretaciones
• Conclusiones
• Aplicaciones

5.11 Ejercicio 1: Usar ObjectTool para Crear y Manejar Objetos

El objetivo de este ejercicio es familiarizarse con la herramienta ObjectTool,


para crear instancias de clases y manejar estas instancias de distintas formas.
ObjectTool es una herramienta educativa para simular el comportamiento de
los objetos en la memoria. ObjectTool permite escribir líneas particulares de la
sintaxis del lenguaje de programación Java y observar las salidas
correspondientes, sin tener que pasar por la compilación e interpretación en un
entorno de tecnología Java.

5.11 Ejercicio 1: Usar ObjectTool para Crear y Manejar Objetos


5.11.1 Tarea - Instanciar Objetos con ObjectTool

En esta tarea, usted ejecutará ObjectTool y creará un objeto Shirt:

1. Desde la línea de comando, ejecute ObjectTool, escribiendo:

java OT <Intro>

Se abrirán dos ventanas ObjectTool. La primera ventana se denomina el


intérprete ObjectTool. Usted puede ingresar líneas de código en esta
ventana.La segunda ventana es denominada "Display Controller
Window". Los objetos que usted cree aparecerán en esta ventana.
2. En el campo Code Entry en el intérprete de ObjectTool escriba lo
siguiente:

int myVariable; <Intro>

Después de presionar la tecla Intro, se desplegará la línea de sintaxis en


el panel "Code Entry History". El panel "Code Entry History" despliega
todas las líneas de sintaxis que usted ha ingresado sin obtener un
mensaje de error. Si usted comete un error, debería también recibir un
mensaje de error en el panel "Code Entry History". En ese caso, intente
ingresar la sintaxis nuevamente. También, después de presionar la tecla
Intro, se declarará una variable de nombre myVariable y se ubicará en la
tabla de variables locales. Esta tabla simula el comportamiento de todas
las variables que usted cree y que típicamente deberían aparecer en la
memoria de pila.
3. En el campo Code Entry, escriba lo siguiente:

myVariable = 10; <Intro>

Después de presionar la tecla Intro, el valor 10 es asignado a la variable


myVariable.
4. Seleccione la opción "Click the Show Binary Value Using Int" del menú
para desplegar el valor de la variable myVariable como un entero binario.
Dado que los enteros ocupan 32 bits de memoria, usted visualizará el
valor 10 en binario.
5. Seleccionando nuevamente la opción "Show Binary Value Using Int" del
menú, el valor de la variable myVariable se volverá a desplegar como un
literal entero.
6. Seleccione el objeto "TShirt" creado previamente en "Display Controller
Window", desde el menú "Show Precreated Objects".
"Display Controller Window" simula cómo debería aparecer un objeto en
la memoria heap y cómo el objeto es una instancia de la clase.

5.11 Ejercicio 1: Usar ObjectTool para


Crear y Manejar Objetos
5.11.2 Tarea - Inicialización de
Objetos

En esta tarea, usted creará e inicializará objetos instancia de la clase Shirt.


Para crear e inicializar objetos instancia, siga los siguientes pasos:

1. Vaya al directorio object_structure.


2. Ejecute ObjectTool.
3. Use el campo Code Entry de ObjectTool para crear dos variables que
referencian a objetos de tipo Shirt: myShirt y yourShirt. Cada variable
que referencia a un objeto y su tipo es colocada en la lista de "Local
Variables"
4. Cree una tercera variable que referencie a objetos, de nombre 9Shirt.
Usted obtendrá un error, dado que 9Shirt no es un nombre de variable
válido.
5. Inicialice ambos objetos usando la palabra clave new, de forma tal que
refieran a objetos Shirt. El valor de cada variable se completará con una
dirección.
6. Seleccione la variable para cada referencia a objetos. La ventana
"Display Controller" desplegará los objetos.

5.11 Ejercicio 1: Usar ObjectTool para Crear y


Manejar Objetos
5.11.3 Tarea - Manejar Datos de
los Objetos

Para manejar cada objeto, siga los siguientes pasos:

1. Usando el campo "Code Entry", cambie el valor de la variable colorCode


del objeto myShirt a "G" para indicar el color verde.
2. Usando el campo "Code Entry", cambie el valor de la variable colorCode
del objeto yourShirt a "R" para indicar el color rojo. Los objetos en
"Display Controller" tienen diferentes códigos de color (diferentes valores
para la misma variable de instancia).
3. Para verificar que cada uno de los objetos tiene un código de color
diferente, use el campo "Code Entry" para ejecutar el método
displayShirtInformation de cada objeto.
La salida del método displayShirtInformation aparecerá en la ventana
donde usted ejecutó ObjectTool.

5.11 Ejercicio 1: Usar ObjectTool


para Crear y Manejar Objetos
5.11.4 Tarea - Asignar una Referencia Desde
un Objeto a Otro

Para asignar el valor de una referencia a un objeto a otra referencia a objeto,


siga los siguientes pasos:

1. Asigne una referencia a un objeto a otra referencia a un objeto, mediante


la asignación de myShirt a yourShirt. Las direcciones para myShirt y
yourShirt en la columna Value tienen el mismo valor.
2. Observe los dos objetos Shirt en "Display Controller", y visualizará que
los objetos aún existen.
3. Cambie los valores de las variables en ambos objetos. ¿Existe otra
manera de manejar ambos objetos?
5.12 Ejercicio 1: Resumen

Discusión – Tómese unos minutos para discutir las experiencias, temas y


descubrimientos que ha realizado durante este ejercicio.

• Experiencias
• Interpretaciones
• Conclusiones
• Aplicaciones

5.13 Ejercicio 2: Crear una Clase de Prueba

El objetivo de este ejercicio es crear su propia clase de prueba.


5.13 Ejercicio 2: Crear una Clase de Prueba
5.13.1 Tarea - Desarrollar una Clase
de Prueba de Otra Clase

En esta tarea, usted creará una clase de prueba para la clase Customer. La
clase Customer tiene siete variables: customerID, name, address,
phoneNumber, emailAddress, status y totalPurchases. La clase Customer tiene
un método para desplegar información de los clientes, cuyo nombre es
displayCustomerInfo. Siga los siguientes pasos para crear su clase de prueba:

1. Cree una clase de prueba que tenga un método main.


2. En el método main:
a. Cree una variable referencia a un objeto que refiere a una instancia de
la clase Customer.
b. Asigne un valor entero al campo customerID del cliente.
c. Asigne el estado del cliente a "O" (old).
d. Asigne el valor 100.00 a la variable totalPurchases
e. Invoque al método displayCustomerInfo de la clase Customer.
3. Compile y ejecute el programa usando el archivo con el código de la
clase Customer provisto.

5.14 Ejercicio 2: Resumen

Discusión - Tómese unos minutos para discutir las experiencias, temas y


descubrimientos realizados durante ese ejercicio.

• Experiencias
• Interpretaciones
• Conclusiones
• Aplicaciones

5.15 Uso de la Clase String

La clase String es una de las tantas clases incluidas en las bibliotecas de


clases de java. La clase String ofrece facilidades para manejar secuencias de
caracteres. Esta es una clase utilizada frecuentemente en los programas. Por
lo tanto, es importante comprender algunas de las características de las
cadenas de caracteres en el lenguaje de Programación Java

5.15 Uso de la Clase String


5.15.1 Creación de un Objeto String con la Palabra Clave new

Hay varias formas de crear e inicializar un objeto String. Una de ellas consiste
en utilizar la palabra clave new para crear un objeto String, y al mismo tiempo
definir la cadena de caracteres con que será inicializado el objeto.

String myName = new String("Fred Smith");

La creación de un objeto String usando la palabra clave new crea dos objetos
String en la memoria y coloca la representación de los caracteres del literal
String en la memoria heap reservada para literales, cuyo nombre es literal pool

5.15 Uso de la Clase String


5.15.2 Creación de un Objeto String Sin la palabra
clave new

El tipo de datos String es el único que permite construir objetos sin usar a
palabra clave new. Por ejemplo:

String myName = "Fred Smith";

Esta sintaxis es muy similar a la declaración e inicialización de una variable


para un tipo de datos primitivo.
La creación de un objeto String sin usar la palabra clave new, crea un objeto
String y coloca la representación de la cadena de caracteres en el literal pool
(si esa cadena de caracteres ya no existe en el literal pool). Para evitar la
innecesaria duplicación de los objetos String, se sugiere utilizar la creación de
los objetos String sin la palabra clave new.

5.15 Uso de la Clase String


5.15.3 Almacenamiento de los Objetos String en
la Memoria

El almacenamiento de las variables referencia a String funciona de la misma


manera que para otras referencias a objetos: se tiene una variable con nombre
(por ejemplo myName) conteniendo una referencia a un lugar de la memoria
donde un objeto String está ubicado. El objeto String tiene un valor (una
secuencia de caracteres) que es la que se proporcionó durante la inicialización.
La siguiente figura ilustra como aparece en la memoria una variable que
referencia a un objeto String.

String myString = "Sammy Summary";

La dirección contenida en la variable referencia myString refiere a un objeto


String en memoria. El objeto String tiene un atributo llamado [C value, que
contiene una dirección que refiere a la secuencia de caracteres
5.15 Uso de la Clase String
5.15.4 Uso de Variables Referencia para Objetos
String

Las variables de tipo String en los programas, pueden usarse como se usan las
variables de tipos primitivos. El ejemplo muestra una clase que establece el
nombre y trabajo de una persona.

Esta sintaxis le permitirá aprender acerca de la clase String sin tener que
realizar invocaciones a métodos especiales. Típicamente, no se querrá que las
instancias contengan datos específicos. Un valor más apropiado para la
variable name es "-nombre requerido-". Un valor más apropiado para la
variable job es "-job requerido-".

2 public class PersonTwo {

4 public String name = "Jonathan";


5 public String job = "Ice Cream Taster";

7 public void display(){

8 System.out.println("My name is " + name + ", I am a " + job);

9}

10 } // end of class

11

5.16 Ejercicio 3: Uso de la Clase String

El objetivo de este ejercicio es crear e inicializar objetos String y desplegar su


contenido
5.16 Ejercicio 3: Uso de la Clase String
5.16.1 Tarea - Crear y Manejar Objetos
String

En esta tarea, usted escribirá una clase de nombre PersonTwo, que crea e
inicializa dos variables String y despliega sus valores. Siga los siguientes pasos
para crear su clase:

1. Vaya al directorio object_structure.


2. Cree una clase de nombrePersonTwo que cree e inicialice dos variables.
a. La primera variable almacena la cadena de caracteres vacía.
a. La segunda variable almacena el nombre de la persona que usted
prefiera
3. Cree un método displayQuote para desplegar los dos valores en la
pantalla.
4. Compile su clase
5. Ejecute su clase usando la clase PersonTwoTest que se le provee.

5.17 Ejercicio 3: Resumen

Discusión – Tómese unos minutos para discutir las experiencias, temas y


descubrimientos realizados durante este ejercicio.

• Experiencias
• Interpretaciones
• Conclusiones
• Aplicaciones

5.18 Ejercicio 4: Examinar Objetos String Con ObjectTool

El objetivo de este ejercicio es usar la herramienta ObjectTool para crear


instancias de la clase String y examinar y modificar variables String

5.18 Ejercicio 4: Examinar Objetos


String Con ObjectTool
5.18.1 Tarea - Visualizar el Contenido de
un Objeto String

En esta tarea, usted utilizará la herramienta ObjectTool para visualizar el


contenido de un objeto String.
1. Ejecute ObjectTool digitando java OT en la línea de comandos.
2. Escriba lo siguiente en el campo Code Entry:

String s = "Hello";

Se crea un objeto de tipo String.


3. Haga doble clic en la dirección en la columna valor de "Local Variables".
Se creará un objeto de tipo String en la ventana "Display Controller
Window" El primer atributo en el objeto String es una referencia (C value)
a una serie de valores char para cada letra en la cadena de caracteres.
4. Haga doble clic sobre la dirección del atributo C value. Un objeto de tipo
Array aparecerá, conteniendo cada carácter en la cadena de caracteres.

5.19 Ejercicio 4: Resumen

Discusión – Tómese unos minutos para discutir las experiencias, temas y


descubrimientos realizados durante este ejercicio.

• Experiencias
• Interpretaciones
• Conclusiones
• Aplicaciones

5.20 Investigación de las Bibliotecas de Clases Java

Todos los SDKs de la tecnología Java contienen una serie de clases pre-
escritas para ser utilizadas en los programas. Estas bibliotecas de clases de
tecnología Java están documentadas en la especificación de bibliotecas de
clases para la versión del SDK que se esté utilizando. La especificación de la
biblioteca de clases es un conjunto de páginas web HTML (Hypertext Markup
Language) que se pueden cargar en un navegador web.

5.20 Investigación de las Bibliotecas de Clases


Java
5.20.1 Especificación de la
Biblioteca de Clases
Java

Una especificación de bibliotecas de clases Java es un documento muy


detallado que describe las clases en la API. Cada API incluye documentación
que describe el uso de clases, las variables y los métodos en los programas.
Cuando se está buscando cómo realizar ciertas tareas, la especificación es la
mayor fuente de información acerca de las clases pre-desarrolladas en las
bibliotecas de clase Java.
Para navegar online a través de la especificación de la J2SE API, vaya al sitio
Web:
http://java.sun.com/j2se/
version/docs/api/index.html

donde la versión es el número de versión del J2SE SDK que está usando. Por
ejemplo:
http://java.sun.com/j2se/
5.0/docs/api/index.html

Para descargar la documentación, siga las instrucciones en el Apéndice A,


"¿Cómo Continuar?"
5.21 Uso de la Especificación de la Biblioteca de Clases Java para
aprender acerca de un Método

Una clase que se ha utilizado a través de este curso es la clase System.


Específicamente, se utilizó el método println para desplegar la salida de los
programas:

System.out.println(datos_a_desplegar);

El método System.out.println es un método pre-escrito en el lenguaje de


programación Java. Siga los siguientes pasos para encontrar más información
acerca de este método utilizando la J2SE API:

1. Utilizando su navegador Web, ubique la primera página de la


documentación de la J2SE API.
2. Haga clic sobre la clase System en la lista de clases. Note que la clase
System tiene una variable denominada out, la cual es una variable que
referencia a un objeto PrintStream. De hecho, el método println es un
método dentro de la clase PrintStream.
3. Haga clic sobre el link correspondiente a la variable out para ver más
detalles sobre esta variable.
4. Haga clic sobre el enlace de la clase PrintStream al inicio de la
explicación de la variable out.
5. Desplácese hacia abajo hasta ubicar el método println en la sección
"Method Summary". Existen varios métodos println. La clase PrintStream
tiene otros métodos para desplegar una salida en la pantalla.
Por ejemplo, la clase PrintStream tiene un método print. La
documentación explica que la diferencia entre el método print y el
método println es que este último termina la línea, y por lo tanto el
siguiente texto será desplegado en una nueva línea.

Por ejemplo:

System.out.print("Carpe diem");
System.out.println("Seize the day");

Despliega esto:

Carpe diem Seize the day

Por lo tanto, dependiendo de lo que se quiera que el programa haga, se


debería usar el método print en lugar del método println.

5.22 Ejercicio 5: Uso de la Especificación de la Biblioteca de Clases


El objetivo de este ejercicio es que se familiarice con la especificación de la
J2SE API.

5.22 Ejercicio 5: Uso de la Especificación de la Biblioteca de Clases


5.22.1 Tarea - Examinar la Especificación de la Biblioteca de Clases
J2SE ™

Para ver la especificación de la Java API 5.0:

1. Vaya a la siguiente URL:


http://java.sun.com/j2se/5.0/docs/
2. En ese sitio, busque la especificación de la API.
3. Usando la especificación API, realice las siguientes actividades:
a. Busque la clase Math bajo el paquete java.lang. ¿Cuántos métodos
tiene esta clase?
b. ¿A qué clase refiere cada clase al inicio de la página?
Sugerencia: ¿Qué clase es la superclase de todas las clases?

5.23 Ejercicio 5: Resumen


Discusión – Tómese unos minutos para discutir las experiencias, temas y
descubrimientos ha realizado durante el ejercicio:

• Experiencias
• Interpretaciones
• Conclusiones
• Aplicaciones

5.24 Preguntas de Repaso

Ahora le proponemos que resuelva el siguiente ejercicio para poner a prueba


sus conocimientos.

Defina por Verdadero o Falso las siguientes afirmaciones según corresponda.


¡Éxitos!
6 Uso de Operadores y Construcciones de Decisión

En éste capítulo veremos como identificar los operadores relacionales y dos


condicionales. Aprenderemos algunas contrucciones y sentencias anidadas y
encadenadas

6.1 Repaso del Capítulo Anterior

Los principales temas del capítulo anterior son:

Declaración de Referencias a Objetos,


Instanciación de Objetos e
Inicialización de Referencias

Las variables que referencian a objetos contienen una dirección de memoria


que indica la ubicación del objeto en la memoria.

Para declarar, instanciar e inicializar el objeto al que referencia la variable, se


deben seguir los siguientes pasos:

1. Declarar una referencia a un objeto, mediante la especificación de su


identificador y el tipo del objeto al que la referencia apunta (la clase del
objeto).

2. Crear un objeto instancia utilizando la palabra clave new.

3. Inicializar la variable que referencia al objeto asignándole el objeto


creado en el punto anterior.

Instanciación de un Objeto

La sintaxis para instanciar un objeto es:

new NombreClase();

Inicialización de Variables que Referencian Objetos

La sintaxis para la inicialización de un objeto a una variable que le referencia es


la siguiente:
identificador = new NombreClase();

Sin embargo, también es posible crear una referencia a un objeto, crear un


objeto y realizar la asignación del objeto a la referencia en la misma línea de
código.

Shirt myShirt = new Shirt();

La Asignación de Referencias

En el lenguaje de programación Java, una variable declarada con un tipo de


clase es una variable de referencia, dado que alude a un tipo no primitivo. Esto
tiene consecuencias relativas a la asignación. Por ejemplo, en el siguiente
fragmento de código:

int x = 7;
int y = x;
MyDate s = new MyDate(22, 7, 1964);
MyDate t = s;

El Pasaje por Valor

El lenguaje de programación Java pasa los argumentos sólo por valor. Esto
significa que no es posible cambiar el valor del argumento del método que
llama dentro del método llamado

El operador punto (.)

Se debe utilizar el operador punto (.) aplicado a una referencia a un objeto para
manejar los valores de las variables miembro o para invocar los métodos de un
objeto específico. Ejemplo:

myShirt.colorCode = 'G';

Asignación de una Referencia desde una Variable a Otra

Shirt myShirt = new Shirt();


Shirt yourShirt = new Shirt();
myShirt = yourShirt;

Uso de la Clase String


La clase String es una de las tantas clases incluidas en las bibliotecas de
clases de Java.

Creación de un Objeto String:

Utilizando la palabra clave new para crear un objeto String:

String myName = new String("Fred Smith");

Creación de un Objeto String sin la palabra clave new:

String myName = "Fred Smith";

Almacenamiento de los Objetos String en la Memoria

String myString = "Sammy Summary";

La dirección contenida en la variable referencia myString refiere a un objeto


String en memoria. El objeto String tiene un atributo llamado [C value, que
contiene una dirección que refiere a la secuencia de caracteres.

Las Convenciones para la Codificación en el Lenguaje de Programación


Java

Paquetes
Clases
Interfaces
Métodos
Variables
Constantes
Estructuras de control
Espaciado
Comentarios

6.3 Objetivos

Una vez completado este capítulo, usted debería estar en condiciones de:

• Identificar los operadores relacionales y condicionales


• Crear construcciones if e if/else
• Usar la construcción switch

Este capítulo describe cómo usar operadores en expresiones dentro del código
y cómo usar estas expresiones en construcciones para la toma de decisiones.

Discusión – Las siguientes preguntas son relevantes para comprender de qué


tratan los operadores y las construcciones de decisión.
• Cuándo debe tomar una decisión que tiene varias opciones, ¿cómo
finalmente elige uno de todos los caminos?
• Por ejemplo, ¿cuáles son todas las cosas que pasan por su mente en el
momento de hacer una compra?

6.4 Recursos Adicionales

Recursos adicionales – Las siguientes referencias ofrecen información


adicional sobre los temas descritos en este capítulo:

• The Java Tutorial. [Online]. Disponible en:

http://java.sun.com/docs/books/
tutorial/java/nutsandbolts/if.html

http://java.sun.com/docs/books/
tutorial/java/nutsandbolts/switch.html

6.5 Las Variables

Esta sección describe cómo se declaran las variables, así como su alcance y
su inicialización en el lenguaje de programación Java.

6.5 Las Variables


6.5.1 Las Variables y su Alcance

Se han visto dos maneras para describir variables: como variables del tipo
primitivo o como variables del tipo de referencia.
También se han visto dos lugares donde declarar variables: dentro de un
método o fuera de él, pero dentro de la definición de una clase.

Las variables definidas dentro de un método se llaman variables locales.


También se les denomina variables automáticas, temporales o de stack. Estas
variables se deben inicializar explícitamente antes de su primer uso. Los
parámetros de métodos o de constructores también son variables locales, pero
son inicializadas por el código que llama al método.

Las variables definidas fuera de un método se crean cuando se construye el


objeto usando la llamada new Xxx().

Hay dos tipos posibles de estas variables:

• El primero es una variable de clase, declarada usando la palabra


reservada static. Las variables etiquetadas static se crean cuando se
carga la clase y su existencia continúa durante el tiempo en el que la
clase esté cargada.
• El segundo es una variable de instancia, declarada sin la palabra
reservada static. Las variables de instancia continúan su existencia
mientras el objeto exista. También se les denomina variables miembro,
porque son miembros de los objetos creados desde las clases.

Las variables static serán descritas, con más detalle, más adelante en este
curso. Las variables miembro y las variables de clase se inicializan
automáticamente cuando se crean.

Las variables que corresponden a parámetros de los métodos definen


argumentos pasados en la llamada al método. Cada vez que se llama al
método, se crea una nueva variable parámetro y dura hasta que la ejecución
sale del método.

Las variables locales se crean cuando la ejecución entra en el método y se


destruyen cuando sale de él. Por esto, las variables locales algunas veces se
denominan variables temporales o automáticas. Las variables definidas dentro
de una función miembro son locales a esa función. Por lo tanto, se puede usar
el mismo nombre de variable en varias funciones miembro para referirse a
variables diferentes.El Código de la Figura ilustra esto:
6.5 Las Variables
6.5.2 La Inicialización de una Variable

Cada variable en un programa Java debe ser inicializada antes de que se use.
Para las variables del stack, al compilador le resulta fácil determinar si en el
código se le asigna un valor antes de intentar usarla: la referencia this y los
parámetros del método deben tener valores asignados cuando la ejecución del
método comienza. Las variables locales que están definidas dentro del código
de cada método se verifican buscando sólo en el código del método. Por lo
tanto, el compilador exige que el código del método no pueda leer el valor de
una variable antes de que se le haya asignado uno.

Sin embargo, no resulta tan fácil una verificación para los atributos de los
objetos en el heap.

¿Cómo puede el compilador saber si un cliente de nuestra clase llama a un


método setter en un objeto antes de que otro cliente quiera llamar a un método
getter en él?

La única manera razonable de manejar este problema es exigir la asignación


de un valor inicial en el constructor. Si el programador no le provee un valor por
defecto, el compilador le asignará el valor por defecto. La Tabla en la Figura
lista los valores por defecto para las variables primitivas y las de referencia..
6.6 El Principio de la Inicialización antes del Uso

Mientras que las variables definidas fuera de un método se inicializan


automáticamente, las locales deben ser inicializadas manualmente antes de su
uso. El compilador señalará un error si determina una condición en la que una
variable puede llegar a ser usada antes de ser inicializada
6.6 El Principio de la Inicialización antes del
Uso
6.6.1 La Concatenación de String
con el signo de +

El operador + realiza una concatenación de objetos String, produciendo un


nuevo String.

String salutation = "Dr. ";


String name = "Pete" + " " + "Seymour";
String title = salutation + " " + name;

El resultado de la última línea es:

Dr. Pete Seymour

Si cualquiera de los argumentos del operador + es un objeto String, los otros


argumentos se convierten a un objeto String. Todos los objetos pueden ser
convertidos a un objeto String automáticamente, aunque el resultado puede ser
más bien incomprensible (críptico). El objeto que no es String se convierte en
un equivalente de String usando la función miembro toString().

6.6 El Principio de la Inicialización antes del Uso


6.6.2 La Conversión
La conversión (casting) significa asignar un valor de un tipo a una variable de
otro tipo. Si los dos tipos son compatibles, el software Java realiza la
conversión automáticamente.
Por ejemplo, un valor int puede siempre ser asignado a una variable long.

Cuando se pueda perder información en una asignación, el compilador


requerirá que se confirme la asignación con una conversión. Por ejemplo, se
puede comprimir un valor long en una variable int como se muestra en la Figura

El tipo deseado se escribe entre paréntesis y se añade como prefijo a la


expresión cuyo tipo debe ser modificado. Aunque puede no ser necesario, se
aconseja incluir entre paréntesis la expresión completa a ser convertida. De
otra manera, la precedencia de la operación de conversión puede causar
problemas.

6.6 El Principio de la Inicialización antes del


Uso
6.6.3 La Promoción y la
Conversión de
Expresiones
Las variables pueden ser promovidas automáticamente a una forma mayor
(como un int a un long), cuando no hay pérdida de información. (Ver Figura )

En general, se puede pensar que una expresión tiene una asignación


compatible, si el tipo de la variable es, por lo menos, tan grande (en cuanto al
valor máximo que admite) como el tipo de la expresión. Para los operadores
binarios, tales como el operador +, cuando los dos operandos son de tipos
numéricos primitivos, el tipo del resultado es determinado como el mayor tipo
resultante entre los tipos de los operandos e int.

Por lo tanto, todas las operaciones binarias sobre tipos numéricos resultan en,
por lo menos, un resultado int y, posiblemente, en uno mayor si alguno de los
operandos presentes en la expresión es de tipo long, float o double. Esto puede
resultar en una situación de overflow o pérdida de precisión cuando el resultado
se asigna a una variable.

Por ejemplo, el siguiente fragmento de código:

short a, b, c;
a = 1;
b = 2;
c = a + b;

causa un error porque promociona cada short a un int antes de operar sobre él.
Sin embargo, si c es declarada como un int o se hace una conversión:

c = (short)(a + b);

el código funciona correctamente


6.7 Uso de Operadores Relacionales y Condicionales

Una de las tareas que los programas realizan frecuentemente es evaluar una
condición y dependiendo del resultado, ejecutar bloques de código diferente o
ramas de código. Por ejemplo, el programa podría tener que verificar si el valor
de una variable es igual al valor de otra variable, y si eso se satisface, entonces
realizar algo.

La figura ilustra el tipo de decisión que las personas realizan diariamente.

Además de los operadores aritméticos, tales como el de adición (+) y el de


incremento (++), el lenguaje de programación Java ofrece varios operadores
relacionales, tales como < y > para las comparaciones por menor y mayor y el
operador && para la operación lógica AND. Estos operadores son utilizados
cuando se desea que el programa ejecute
distintas ramas de código dependiendo de diferentes condiciones, tales como
la verificación de si los valores de dos variables son iguales.
6.7 Uso de Operadores Relacionales y
Condicionales
6.7.1 Ejemplo del Ascensor

Adicionalmente a los ejemplos que son parte del caso de estudio DirectClothing,
Inc., este curso utiliza varias variaciones de la clase Elevator (Ascensor) para
mostrar conceptos del lenguaje de programación Java. El código ilustra una de
dichas variaciones.
Un ascensor tiene muchas funciones y para este curso son:

• Las puertas del ascensor se pueden abrir (el método openDoor en las
líneas 9 a 13)
• Las puertas del ascensor se pueden cerrar (el método closeDoor en las
líneas 15 a 19)
• El ascensor puede subir un piso (el método goUp entre las líneas 21 a
25)
• El ascensor puede bajar un piso (el método goDown entre las líneas 27
a 31)

Durante este módulo y los siguientes, se podrán ver varias variaciones de la


clase Elevator.

Una clase de prueba, similar a la siguiente, ejecuta el ascensor a través de


algunas pruebas

1
2 public class Elevator {
3
4 public boolean doorOpen=false; // Doors are
closed by default
5 public int currentFloor = 1; // All elevators
start on first floor
6 public final int TOP_FLOOR = 10;
7 public final int MIN_FLOORS = 1;
8
9 public void openDoor() {
10 System.out.println("Opening door.");
11 doorOpen = true;
12 System.out.println("Door is open.");
13 }
14
15 public void closeDoor() {
16 System.out.println("Closing door.");
17 doorOpen = false;
18 System.out.println("Door is closed.");
19 }
20
21 public void goUp() {
22 System.out.println("Going up one floor.");
23 currentFloor++;
24 System.out.println("Floor: " + currentFloor);
25 }
26
27 public void goDown() {
28 System.out.println("Going down one floor.");
29 currentFloor--;
30 System.out.println("Floor: " + currentFloor);
31 }
32
33
34 }
35

1
2 public class ElevatorTest {
3
4 public static void main(String args[]) {
5
6 Elevator myElevator = new Elevator();
7
8 myElevator.openDoor();
9 myElevator.closeDoor();
10 myElevator.goDown();
11 myElevator.goUp();
12 myElevator.goUp();
13 myElevator.goUp();
14 myElevator.openDoor();
15 myElevator.closeDoor();
16 myElevator.goDown();
17 myElevator.openDoor();
18 myElevator.goDown();
19 myElevator.openDoor();
20 }
21 }
22

6.7 Uso de Operadores Relacionales y


Condicionales
6.7.2 Operadores
Relacionales

Los operadores relacionales comparan dos valores y determinan la relación


entre ellos.

La tabla lista las distintas condiciones que pueden ser verificadas usando
operadores relacionales.
El resultado de la operación realizada por los operadores relacionales es un
valor de tipo boolean. Los valores booleanos son true y false. En todos los
ejemplos de la tabla, el resultado es el valor true del tipo bolean

6.8 Los Operadores

Esta sección describe los operadores en el lenguaje de programación Java.

6.8 Los Operadores


6.8.1 La Procedencia de Operadores

Los operadores del lenguaje de programación Java son similares en estilo y


funciones a los de C y C++. La Tabla del a Figura lista los operadores en
orden de precedencia (I a D significa asociativo de izquierda-a-derecha, D a I
significa asociativo de derecha-a-izquierda).
6.8 Los
Operadores
6.8.2 Verificación de Igualdad entre Cadenas de Caracteres

Si se utiliza el operador == para comparar referencias a objetos String, se


verifica la igualdad de las direcciones de memoria y no de los contenidos de las
cadenas de caracteres.

Si se desea verificar la igualdad de las cadenas de caracteres, como por


ejemplo, si "Fred Smith" es igual a "Joseph Smith", se debe usar el método
equals de la clase String. La siguiente clase contiene dos nombres de
empleados (employee) y un método que compara los nombres.
6.8 Los Operadores
6.8.3 Los Operadores Lógicos

La mayoría de los operadores de la tecnología Java fueron tomados de otros


lenguajes y se comportan tal como se espera. Los operadores lógicos y
relacionales devuelven un valor de tipo booleano como resultado. El valor 0 no
se interpreta automáticamente como false y los valores distintos de cero
tampoco se interpretan automáticamente como true.

int i = 1;
if (i) // Genera un error de compilación
if (i != 0) // Correcto

Los operadores booleanos soportados son !, &, ^ y | para las operaciones


algebraicas booleanas NOT, AND, XOR y OR, respectivamente. Cada uno de
estos devuelve un resultado boolean. Los operadores && y || son los
equivalentes de corto circuito de los operadores & y |.

Los Operadores Lógicos de Corto Circuito

Los operadores && (definidos como AND) y || (definido como OR) realizan
expresiones lógicas de corto circuito.Considere este ejemplo:

MyDate d = reservation.getDepartureDate();
if ((d != null) && (d.day > 31)) {
// Hace algo con d
}

La expresión boolean que forma el argumento de la sentencia if () es legal y


completamente segura. La segunda sub-expresión se saltea cuando la primera
es false. Esto se debe a que el resultado de la expresión en su totalidad es
false cuando la primera sub-expresión da como resultado false (sin importar el
resultado que dé la evaluación de la segunda sub-expresión).

De forma similar, si se utiliza el operador || y la primera expresión devuelve true,


la segunda sub-expresión no se evalúa porque se sabe que el resultado final
de la evaluación de la expresión será true (verdadero).

6.8 Los Operadores


6.8.4 Los Operadores Lógicos a Nivel de Bits

Las operaciones a nivel de bits, incluyendo operaciones lógicas y de


desplazamiento, realizan operaciones de bajo nivel directamente sobre la
representación binaria de los enteros. Estas operaciones no se utilizan con
frecuencia en sistemas del tipo empresarial, pero pueden ser críticas en
sistemas gráficos, científicos o de control. La habilidad para operar
directamente sobre binario podría ahorrar grandes cantidades de memoria,
permitir realizar complicados cálculos de forma eficiente y simplificar
significativamente algunas operaciones sobre colecciones de bits, tales como
lectura o escrita en los puertos I/O paralelos.

El lenguaje de programación Java soporta operaciones a nivel de bits sobre


tipos de datos enteros. Estas son representadas con los operadores ~, &, ^ y |
para las operaciones a nivel de bits de NOT (complemento a nivel de bits),
AND a nivel de bits, XOR a nivel de bits y OR a nivel de bits, respectivamente).
6.8 Los
Operadores
6.8.5 Los Operadores de Desplazamiento a la Derecha

El lenguaje de programación Java provee dos operadores de desplazamiento a


la derecha. El operador >> realiza un desplazamiento aritmético, o de signo, a
la derecha. Su resultado es el primer operando dividido entre 2 elevado al
número especificado por el segundo operando.
Por Ejemplo:

128 >> 1 devuelve 128/21 = 64


256 >> 4 devuelve 256/24 = 16
-256 >> 4 devuelve -256/24 = -16

El resultado del operador >> en el bit del signo se copia durante el


desplazamiento.

El operador lógico de desplazamiento a la derecha, o sin signo, >>>, trabaja


sobre el patrón de bits más que en el significado aritmético del valor y siempre
ubica ceros en los bits más significativos.
1010 ... >> 2 devuelve 111010 ...
1010 ... >>> 2 devuelve 001010 ...

6.8 Los Operadores


6.8.6 El Operador de Desplazamiento a la Izquierda

El operador << realiza un desplazamiento a la izquierda. El resultado de este


desplazamiento es que el que resulta de multiplicar el primer operando por dos
elevado al número especificado por el segundo operando. Por ejemplo:

128 << 1 devuelve 128*21 = 256


16 << 2 devuelve 16*22 = 64

Los tres operadores de desplazamiento reducen su operando del lado derecho


a módulo 32 si el operando del lado izquierdo es un int y a módulo 64 si el
operando del lado izquierdo es un long. Por lo tanto, para un entero x de tipo int,
x>>>32 da como resultado al propio x y no 0 como podría esperarse.

Los operadores de desplazamiento se permiten sólo sobre tipos enteros. El


operador de desplazamiento a la derecha >>> es efectivo en valores int y long.
Si se usa sobre un valor short o byte, el valor es promovido, con extensión de
signo, a un int antes de que el operador >>> sea aplicado. Debido a esto, es
que, usualmente, el desplazamiento sin signo se convierte en un
desplazamiento con signo.

6.8 Los Operadores


6.8.7 Ejemplos de Operadores de Desplazamiento

La Figura muestra los patrones de bits de un número positivo y de un


número negativo, y los patrones resultantes de las tres operaciones de
desplazamiento: >>, >>> y <<.
6.8 Los Operadores
6.8.8 Operadores
Condicionales

En algunos casos, se requerirá tomar una decisión basada en más de una


condición. Bajo estas circunstancias, se pueden utilizar los operadores
condicionales para evaluar una condición compleja.
La tabla lista los operadores condicionales utilizados comúnmente en el
lenguaje de programación Java. Todos los ejemplos en la tabla dan como
resultado el valor booleano false

6.9 Creación de Construcciones if e if/else

Una sentencia if, o una construcción if, ejecuta un bloque de código si una
expresión es evaluada a true.

6.9 Creación de Construcciones if e


if/else
6.9.1 La Construcción if

Existen algunas variaciones de la construcción if. Sin embargo, la más simple


es:
if (expresion_booleana){
bloque_código;
} // fin de la construcción if
// el programa continúa aquí.

donde:

• expresión_booleana es un combinación de operadores relacionales,


operadores condicionales y valores, que tiene como resultado un valor
true o false.
• bloque_código representa las líneas de código que serán ejecutadas si
el resultado de evaluar la expresión_booleana es true.

En primer lugar, se evalúa la expresión expresión_booleana. Si el resultado de


dicha evaluación es true, se ejecuta el bloque_código. Si el resultado de la
evaluación no es true, el programa continúa con la sentencia siguiente a la
llave que cierra el bloque de código de la construcción if.

La clase ElevatorTest prueba un objeto Elevator invocando sus métodos. Uno


de los primeros métodos invocados es el método goDown.

La llamada al método goDown antes de que el ascensor haya subido, da como


resultado un problema porque el ascensor comienza en el primer piso y no hay
pisos más bajos (la constante MIN_FLOORS indica que el primer piso es el
piso más bajo). Cuando se ejecuta, la clase ElevatorTest despliega:

Opening door.
Door is open.
Closing door.
Door is closed.
Going down one floor.
Floor: 0 <----this is an error in logic
Going up one floor.
Floor: 1
Going up one floor.
Floor: 2

Dos sentencias if pueden resolver este problema. El siguiente método goDown


contiene dos construcciones if para determinar si el ascensor debería bajar o
desplegar un mensaje de error.

2 public class IfElevator {


6 public final int TOP_FLOOR = 10;
7 public final int MIN_FLOORS = 1;
9 public void openDoor() {
11 doorOpen = true;
15 public void closeDoor() {
17 doorOpen = false;
20
21 public void goUp() {

Si el valor de la variable currentFloor es igual al de la constante MIN_FLOORS,


se despliega un mensaje de error y el ascensor no desciende (Líneas 29 a 31).
Si el valor de la variable currentFloor es mayor que el de la constante
MIN_FLOORS, el ascensor desciende (Líneas 33 a 37).
Al ejecutar la clase IfElevator se despliega:

Opening door.
Door is open.
Closing door.
Door is closed.
Cannot Go Down <-- la lógica del ascensor previene el
problema
Going up one floor.
Floor: 2
Going up one floor.
Floor: 3
......

1
2 public class IfElevator {
3
4 public boolean doorOpen=false; // Doors are
closed by default
5 public int currentFloor = 1; // All elevators
start on first floor
6 public final int TOP_FLOOR = 10;
7 public final int MIN_FLOORS = 1;
8
9 public void openDoor() {
10 System.out.println("Opening door.");
11 doorOpen = true;
12 System.out.println("Door is open.");
13 }
14
15 public void closeDoor() {
16 System.out.println("Closing door.");
17 doorOpen = false;
18 System.out.println("Door is closed.");
19 }
20
21 public void goUp() {
22 System.out.println("Going up one floor.");
23 currentFloor++;
24 System.out.println( gFloor: g+currentFloor);
25 }
26
27 public void goDown() {
28
29 if (currentFloor == MIN_FLOORS) {
30 System.out.println( gCannot go down h);
31 }
32
33 if (currentFloor > MIN_FLOORS) {
34 System.out.println( gGoing down one floor. h);
35 currentFloor--;
36 System.out.println( gFloor: g+currentFloor);
37 }
38 }
39
40
41 }
42

6.9 Creación de Construcciones if e


if/else
6.9.2 Sentencias if Anidadas

Algunas veces, se necesitará ejecutar una sentencia if como parte de otra


sentencia if. El código en el siguiente ejemplo ilustra cómo usar sentencias if
anidadas para validar los valores de dos variables.
Si el valor de la variable currentFloor es igual al valor de la constante
MIN_FLOORS, se despliega un mensaje de error y el ascensor no desciende
(Líneas 29 a 31). Si el valor de la variable currentFloor es mayor que el valor de
la constante MIN_FLOORS y las puertas están cerradas, entonces el ascensor
desciende (Líneas 33 a 41).

1
2 public class NestedIfElevator {
3
4 public boolean doorOpen=false; // Doors are
closed by default
5 public int currentFloor = 1; // All elevators
start on first floor
6 public final int TOP_FLOOR = 10;
7 public final int MIN_FLOORS = 1;
8
9 public void openDoor() {
10 System.out.println("Opening door.");
11 doorOpen = true;
12 System.out.println("Door is open.");
13 }
14
15 public void closeDoor() {
16 System.out.println("Closing door.");
17 doorOpen = false;
18 System.out.println("Door is closed.");
19 }
20
21 public void goUp() {
22 System.out.println("Going up one floor.");
23 currentFloor++;
24 System.out.println("Floor: " + currentFloor);
25 }
26
27 public void goDown() {
28
29 if (currentFloor == MIN_FLOORS) {
30 System.out.println("Cannot Go down");
31 }
32
33 if (currentFloor > MIN_FLOORS) {
34
35 if (!doorOpen) {
36
37 System.out.println("Going down one floor.");
38 currentFloor--;
39 System.out.println("Floor: " + currentFloor);
40 }
41 }
42 }
43
44
45 }
46
47
48

6.9 Creación de Construcciones if e


if/else
6.9.3 La construcción if/else

Frecuentemente, se necesita ejecutar un bloque de código si la expresión es


evaluada a true y otro bloque de código diferente si es evaluada a false. Se
puede utilizar la construcción if para ejecutar un bloque de código si la
expresión es evaluada a true, con una construcción else que solo se ejecuta si
la expresión es evaluada a false. La sintaxis de la construcción if/else es:

if (expresión_booleana) {
bloque_código1;
} // fin de la construcción if
else {
bloque_código2;
} // fin de la construcción else
// el programa continúa aquí

donde:

• expresión_booleana es una combinación de operadores relacionales,


operadores lógicos y valores, que da como resultado un valor true o
false.
• código_bloque1 representa las líneas de código que serán ejecutadas si
la expresión es evaluada a true.
• código_bloque2 representa las líneas de código que serán ejecutadas si
la expresión es evaluada a false.

Se puede utilizar una sentencia if/else para solucionar el problema de que el


ascensor se dirija a un piso no válido. El siguiente método goDown contiene
una construcción if/else que determina si el ascensor debe descender o
desplegar un error.

Si el valor de la variable currentFloor es igual al de la constante MIN_FLOORS,


se despliega un mensaje de error y el ascensor no desciende (Líneas 29 a 31).
En caso contrario (else), se asume que el valor de currentFloor es mayor que el
de la constante MIN_FLOORS y el ascensor desciende (Líneas 33 a 37). Al
ejecutar la clase IfElseElevator se
despliega:

Opening door.
Door is open.
Closing door.
Door is closed.
Cannot Go Down <-- la lógica del ascensor previene el
problema
Going up one floor.
Floor: 2
Going up one floor.
Floor: 3
......

1
2 public class IfElseElevator {
3
4 public boolean doorOpen=false; // Doors are closed by default
5 public int currentFloor = 1; // All elevators start on first floor
6 public final int TOP_FLOOR = 10;
7 public final int MIN_FLOORS = 1;
8
9 public void openDoor() {
10 System.out.println("Opening door.");
11 doorOpen = true;
12 System.out.println("Door is open.");
13 }
14
15 public void closeDoor() {
16 System.out.println("Closing door.");
17 doorOpen = false;
18 System.out.println("Door is closed.");
19 }
20
21 public void goUp() {
22 System.out.println("Going up one floor.");
23 currentFloor++;
24 System.out.println("Floor: " + currentFloor);
25 }
26
27 public void goDown() {
28
29 if (currentFloor == MIN_FLOORS) {
30 System.out.println(“Cannot go down”);
31 }
32
33 else {
34 System.out.println(“Going down one floor.”);
35 currentFloor--;
36 System.out.println(“Floor: “+currentFloor);
37 }
38 }
39
40
41 }
42
43
44

6.9 Creación de Construcciones if e if/else


6.9.4 Encadenamiento de Construcciones if/else

Se pueden encadenar varias construcciones if y else para realizar acciones


diferentes según distintas expresiones.
La sintaxis para una construcción if/else encadenada es:

if (expresión_booleana) {
bloque_código1;
} // fin de la construcción if
else if (expresión_booleana){
bloque_código2;
} // fin de la construcción else
else {
bloque_código3;
}
// el programa continúa aquí

donde:

• expresión_booleana es una combinación de operadores relacionales,


operadores condicionales y valores, que da como resultado el valor true
o false.
• bloque_código1 representa las líneas de código que se ejecutarán si la
expresión es evaluada a true.
• bloque_código2 representa las líneas de código que se ejecutarán si la
expresión es evaluada a false, y la segunda condición es evaluada a
true.
• bloque_código3 representa las líneas de código que se ejecutarán si la
expresión en el segundo if es evaluada a false.

A continuación se presenta la clase IfElseDate que contiene varias


construcciones if/else encadenadas que determinan la cantidad de días que
hay un mes.

El método calculateNumDays encadena tres sentencias if/else para determinar


la cantidad de días de un mes. Aunque este código es sintácticamente correcto,
el código con sentencias if/else encadenadas puede resultar confuso y debería
ser evitado.

1
2 public class IfElseDate {
3
4 public int month = 10;
5
6 public void calculateNumDays() {
7
8 if ( month == 1 || month == 3 || month == 5 || month == 7 ||
9 month == 8 || month == 10 || month == 12) {
10
11 System.out.println("There are 31 days in that month.");
12 }
13
14 else if (month == 2) {
15 System.out.println("There are 28 days in that month.");
16 }
17
18 else if (month == 4||month == 6 || month == 9 || month == 11) {
19 System.out.println("There are 30 days in that month.");
20 }
21
22 else {
23 System.out.println("Invalid month.");
24 }
25 }
26 }
27
28

20
21 public void goUp() {
22 System.out.println("Going up one floor.");
23 currentFloor++;
24 System.out.println("Floor: " + currentFloor);
25 }
26
27 public void goDown() {
28 System.out.println("Going down one floor.");
29 currentFloor--;
30 System.out.println("Floor: " + currentFloor);
31 }
32
33
34 }
35

6.9 Creación de Construcciones if e


if/else
6.9.5 Las Sentencias de Bifurcación

Las sentencias condicionales permiten la ejecución selectiva de secciones de


un programa, de acuerdo con el valor de algunas expresiones. El lenguaje de
programación Java soporta las sentencias if y switch para las bifurcaciones de
dos direcciones y las de múltiples direcciones, respectivamente.

6.9 Creación de Construcciones if e


if/else
6.9.6 Las Sentencias Simples if, else

Las sintaxis básica para una sentencia if es:

if ( <expresión_boolean> )
<sentencia_o_bloque>

Por ejemplo:

if ( x < 10 )
System.out.println("Are you finished yet?");
Sin embargo, es recomendable que ubique todas las sentencias dentro de un
bloque. Por ejemplo:

if ( x < 10 ) {
System.out.println("Are you finished yet?");
}

6.9 Creación de Construcciones if e


if/else
6.9.7 Las Sentencias Complejas if,
else

Si se requiere una cláusula else, entonces debe usar la sentencia if-else:

if ( <expresión_boolean > )
<sentencia_o_bloque>
else
<sentencia_o_bloque>

Por ejemplo:

if ( x < 10 ) {
System.out.println("Are you finished yet?");
} else {
System.out.println("Keep working...");
}

Si se requiere una serie de validaciones condicionales, entonces puede


encadenar una secuencia de sentencias if-else-if:

if ( <expresión_boolean> )
<sentencia_o_bloque>
else if ( <expresión_boolean> )
<sentencia_o_bloque>

Se muestra un Ejemplo en la Figura

El lenguaje de programación Java difiere de C y C++ porque una sentencia if()


toma una expresión booleana y no un valor numérico. No se puede convertir
tipos numéricos a tipos booleanos. Si tiene:

if (x) // x is int

use:
if (x != 0)

La parte entera de la sentencia else es opcional y puede ser omitida si ninguna


acción fuera realizada cuando la condición evaluada resulte falsa.
int count = getCount(); // un método definido en la clase
if (count < 0) {
System.out.println("Error: count value is negative.");
} else if (count > getMaxCount()) {
System.out.println("Error: count value is too big.");
} else {
System.out.println("There will be " + count +
" people for lunch today.");
}

6.9 Creación de Construcciones if e


if/else
6.9.8 Ejercicios if y operadores
logicos

Ahora le proponemos que resuelva los siguientes ejercicios para poner a


prueba sus conocimientos.

Ejercicios if y operadores logicos

Escribir el código que dado el día y el mes de nacimiento de una persona se


determine el signo zodiacal de la misma.

Escribir el código que dado un número entero representando un día de la


semana, retorne por pantalla el nombre del día de la semana. Si el valor
ingresado no es válido, debe imprimir un mensaje de advertencia como "Dia no
válido".

6.10 Ejercicio 1: Usar las Construcciones if e if/else

El objetivo de este ejercicio es crear clases que usen las construcciones if/else.

6.10 Ejercicio 1: Usar las Construcciones if e


if/else
6.10.1 Tarea - Escribir una
Clase que use las
Sentencias if/else
En esta tarea, usted escribirá una clase de nombre DateTwo que usa las
sentencias if/else para desplegar el día de la semana basado en el valor de una
variable. Siga los siguientes pasos para escribir su clase:

1. Vaya al directorio opsdec.


2. Cree una clase de nombre DateTwo con una variable que contenga un
valor entre 1 y 7 donde:
• El número 1 representa Lunes (comienzo de la semana)
• El número 7 representa Domingo (fin de la semana)
3. En la clase DateTwo, cree un método displayDay que use
construcciones if/else para inspeccionar el valor del día de la semana. El
método displayDay debería desplegar un mensaje de error si se trata de
utilizar un número no válido.
4. Compile y ejecute su programa usando el archivo de clase provisto
DateTwoTest.
5. Repita el paso 4 varias veces usando diferentes valores para el día de la
semana.

6.10 Ejercicio 1: Usar las Construcciones if e


if/else
6.10.2 Tarea - Escribir Otra Clase
que use Sentencias if/else

En esta tarea, escribirá una clase de nombre Clock que usa sentencias if/else
para desplegar la parte del día dependiendo de la hora. Utilice la Tabla como
guía.

Siga las siguientes instrucciones para escribir su clase:

1. Vaya al directorio opsdec


2. Cree una clase de nombre Clock con un variable de nombre currentTime,
que contenga la hora del día.
3. En la clase Clock, cree un método displayPartOfDay que use las
construcciones if/else para desplegar la parte del día asociada con la
variable currentTime. Por ejemplo, si el valor de la variable currentTime
es igual a 1504, el programa deberá desplegar "Afternoon".
4. Compile y ejecute su programa usando el archivo con la clase ClockTest.
5. Repita varias veces el paso 4, usando valores diferentes para la hora del
día
6.11 Ejercicio 1: Resumen

Discusión – Tómese unos minutos para discutir las experiencias, temas y


descubrimientos realizados durante estos ejercicios.

• Experiencias
• Interpretaciones
• Conclusiones
• Aplicaciones

6.12 Uso de la Construcción switch

Otra palabra clase utilizada en la toma de decisiones es la palabra clave switch.


La construcción switch ayuda a evitar confusiones, dado que simplifica la
organización de varias ramas de código que pueden ser ejecutadas.
La clase IfElseDate del ejemplo podría ser reescrita utilizando la construcción
switch. La sintaxis de la construcción switch es:

switch (variable) {
case valor_literal:
bloque_código;
[break;]
case otro_valor_literal:
bloque_código;
[break;]
[default:]
bloque_código;
}

donde:

• La palabra clave switch indica una sentencia switch.


• La variable es la variable a la que se le desea verificar el valor.
• La palabra clave case indica el valor a ser verificado. La combinación de
la palabra clave case y el valor_literal se denomina una "etiqueta case".
• El valor_literal es cualquier valor válido que la variable podría contener.
Usted puede tener una etiqueta case por cada valor que quiera verificar.
Los valores literales no pueden ser variables, expresiones o llamadas a
métodos. Los valores literales pueden ser constantes (variables final
como MAX_NUMBER definidas en algún otro
lugar), literales (como 'A' o 10) o ambos.
• La sentencia break es una palabra clave opcional que causa que el flujo
de código termine con la ejecución del switch. Sin una sentencia break,
todas las sentencias bloque_código que siguen a la sentencia case
aceptada son ejecutadas (hasta que una sentencia break o el fin de la
construcción switch se alcancen)
• La palabra clave default indica que bloque_código deberá ser ejecutado
si no se satisface ninguna de las etiquetas case. La etiqueta case default
es similar a la construcción else de la sentencia if/else. La etiqueta case
default es opcional.

El método calculateNumDays en la clase SwitchDate usa una sentencia switch


para realizar acciones diferentes dependiendo del valor de la variable month
(Línea 8). Si la variable month es igual a 1, 3, 5, 7, 8 o 10 el código salta a la
etiqueta case apropiada y ejecuta la línea 16.

1
2 public class SwitchDate {
3
4 public int month = 10;
5
6 public void calculateNumDays() {
7
8 switch(month) {
9 case 1:
10 case 3:
11 case 5:
12 case 7:
13 case 8:
14 case 10:
15 case 12:
16 System.out.println("There are 31 days in that month.");
17 break;
18 case 2:
19 System.out.println("There are 28 days in that month.");
20 break;
21 case 4:
22 case 6:
23 case 9:
24 case 11:
25 System.out.println("There are 30 days in that month.");
26 break;
27 default:
28 System.out.println("Invalid month.");
29 break;
30 }
31 }
32 }
33
34

6.12 Uso de la Construcción


switch
6.12.1 Cuándo utilizar Construcciones
switch

La construcción switch se puede utilizar solamente para verificación de


igualdad, y no para verificar si los valores son mayores o menores a un valor.
Tampoco se puede utilizar para realizar verificaciones de más de un valor y
solamente se puede utilizar con expresiones de tipos de datos enteros.
La construcción switch puede ser considerada para:

• Verificación de igualdad
• Verificación de un valor simple
• Verificación del valor de un tipo int, short, byte o char.

6.13 Ejercicio 2: Uso de la Construcción switch

El objetivo de este ejercicio es practicar, usando la construcción switch, en


programas de toma de decisiones.

6.13 Ejercicio 2: Uso de la Construcción


switch
6.13.1 Tarea - Escribir una Clase
que Use una Sentencia
switch
En esta tarea, usted escribirá una clase de nombre DateThree que usa
sentencias switch para desplegar el día de la semana basado en el valor de
una variable. Siga los siguientes pasos para escribir la clase:

1. Vaya al directorio opsdec.


2. Cree una clase de nombre DateThree con una variable que contenga un
valor entre 1 y 7 donde:
• El número 1 representa Lunes (comienzo de la semana).
• El número 7 representa Domingo (fin de la semana).
3. En la clase DateThree, cree un método displayDay que use una
construcción switch para inspeccionar el valor de la cantidad de días y
despliegue el día de la semana correspondiente. El método displayDay
debería también desplegar un mensaje de error si se utiliza un número
no válido.
4. Compile y ejecute su programa usando el archivo DateThreeTest
provisto.
5. Repita varias veces el paso 4 usando diferentes valores para los días de
la semana.

6.14 Ejercicio 2: Resumen

Discusión – Tómese unos minutos para discutir las experiencias, asuntos y


descubrimientos que haya realizado durante este módulo.

• Experiencias
• Interpretaciones
• Conclusiones
• Aplicaciones

6.15 Ejercicios de switch

Ahora le proponemos que resuelva los siguientes ejercicios para poner a


prueba sus conocimientos.

Ejercicios de switch

Escribir el código que dado un número entero representando un mes del año,
retorne el número de días del mes o cero si el mes es inválido (no se tendrán
en cuenta los años bisiestos).

Escribir el código que dado un caracter (A, B, C, D o E) representando la


calificación en formato americano, retorne las siguientes leyendas según
corresponda:
A APROBADO - Excelente
B: APROBADO - Muy Bien
C: APROBADO - Bien
D: DESAPROBADO - Regular
E: DESAPROBADO - Mal

6.16 Ejercicio de Repaso

Ahora le proponemos que resuelva el siguiente ejercicio para poner a prueba


sus conocimientos.

¡Èxitos!!

6.17 Preguntas de Repaso

Ahora le proponemos que resuelva el siguiente ejercicio para poner a prueba


sus conocimientos.
Defina por Verdadero o Falso las siguientes afirmaciones según corresponda.
¡Éxitos!
7 Uso de las Construcciones de Iteración
En el transcurso de este capítulo aprenderemos acerca de las contrucciones
iterativas que controlan la repetición de un conjuto de sentencias.

7.1 Repaso del Capítulo Anterior

Los principales temas del capítulo anterior son:

Uso de Operadores Relacionales y Condicionales

Una de las tareas que los programas realizan frecuentemente es evaluar una
condición y dependiendo del resultado, ejecutar bloques de código diferente o
ramas de código.
El lenguaje de programación Java ofrece varios operadores relacionales, tales
como < y > para las comparaciones por menor y mayor y el operador && para
la operación lógica AND.

Operadores Relacionales

Los operadores relacionales comparan dos valores y determinan la relación


entre ellos.
El resultado de la operación realizada por los operadores relacionales es un
valor de tipo boolean.
Los operadores relacionales son:

Es igual a ==
No es igual a !=
Es menor que <
Es menor que o igual <=
Es mayor que >
Es mayor que o igual >=

Verificación de Igualdad entre Cadenas de Caracteres

Si se utiliza el operador == para comparar referencias a objetos String, se


verifica la igualdad de las direcciones de memoria y no de los contenidos de las
cadenas de caracteres.
Si se desea verificar la igualdad de las cadenas de caracteres, se debe usar el
método equals de la clase String.

Operadores Condicionales

En algunos casos, se requerirá tomar una decisión basada en más de una


condición. Bajo estas circunstancias, se pueden utilizar los operadores
condicionales para evaluar una condición compleja.
AND &&
OR ||
NOT !

Los Operadores de Desplazamiento

Derecha

El operador >> realiza un desplazamiento aritmético, o de signo, a la derecha.


Su resultado es el primer operando dividido entre 2 elevado al número
especificado por el segundo operando.
El operador lógico de desplazamiento a la derecha, o sin signo, >>>, trabaja
sobre el patrón de bits más que en el significado aritmético del valor y siempre
ubica ceros en los bits más significativos.

Izquierda

El operador << realiza un desplazamiento a la izquierda. El resultado de este


desplazamiento es que el que resulta de multiplicar el primer operando por dos
elevado al número especificado por el segundo operando.

Creación de Construcciones if e if/else

Una sentencia if, o una construcción if, ejecuta un bloque de código si una
expresión es evaluada a true.

if (expresion_booleana){bloque_código;}

La construcción if/else

Una construcción else solo se ejecuta si la expresión es evaluada a false.

if (expresión_booleana) {bloque_código1;} // fin de la construcción if


else {bloque_código2;} // fin de la construcción else

Uso de la Construcción switch

La construcción switch ayuda a evitar confusiones, dado que simplifica la


organización de varias ramas de código que pueden ser ejecutadas.
La sintaxis de la construcción switch es:

switch (variable) {
case valor_literal:
bloque_código;
[break;]
case otro_valor_literal:
bloque_código;
[break;]
[default:]
bloque_código;
}

La construcción switch se puede utilizar solamente para verificación de


igualdad, y no para verificar si los valores son mayores o menores a un valor.

7.3 Objetivos

Una vez completado este capítulo, usted debería ser capaz de:

• Crear iteraciones while


• Desarrollar iteraciones for
• Crear iteraciones do/while

Este capítulo presenta distintos tipos de construcciones iterativas que controlan


la repetición de un conjunto de sentencias del lenguaje de programación Java.

Discusión – Las siguientes preguntas son relevantes para comprender los


temas relacionados a iteraciones:

¿Cuáles son algunas situaciones donde usted querría continuar realizando una
acción, hasta que se dé cierta condición?

7.4 Recursos Adicionales

Recursos adicionales – Las siguientes referencias ofrecen información


adicional sobre los temas descritos en este capítulo:

• The Java Tutorial. [Online]. Disponible en:

http://java.sun.com/docs/books/
tutorial/java/nutsandbolts/while.html

http://java.sun.com/docs/books/
tutorial/java/nutsandbolts/for.html

http://java.sun.com/docs/books/
tutorial/java/nutsandbolts/branch.html

7.5 Creación de Bucles while


Un bucle while itera un bloque de código mientras una condición tiene el valor
true. La sintaxis para un bucle while es:

while (expresión_booleana) {
bloque_código;
} // fin de la construcción while
// el programa continúa aquí

Todos los bucles tienen los siguientes componentes:

• La expresion_booleana es una expresión que evalúa a true o false, y es


evaluada antes de cada iteración del bucle.
• El bloque_código representa las líneas de código que se ejecutan si la
expresión_booleana es evaluada a true.

El bucle while es un bucle iterativo del tipo cero a muchas veces ya que la
expresión booleana que forma parte del bucle es procesada antes de ejecutar
el cuerpo del bucle y si el resultado de la evaluación es false, entonces el
cuerpo no es ejecutado. Por ejemplo:

int i=0;
while (i<5) {
System.out.println("Hello Thomas");
++i
}

En el ejemplo anterior, la frase "Hello Thomas" se despliega cinco veces.

El código en el gráfico muestra un método setFloor que contiene un bucle while


que permite a un ascensor desplazarse hacia arriba o hacia abajo.
El método setFloor de la clase Elevator usa un bucle while para determinar si el
ascensor está ubicado en el piso seleccionado (Líneas 34 a 51). Si el valor de
la variable currentFloor no es igual al valor de la variable desiredFloor,
entonces el ascensor continúa moviéndose hacia el piso elegido (Líneas 45 y
48).

1
2 public class WhileElevator {
3
4 public boolean doorOpen=false;
5 public int currentFloor = 1;
6
7 public final int TOP_FLOOR = 5;
8 public final int BOTTOM_FLOOR = 1;
9
10 public void openDoor() {
11 System.out.println("Opening door.");
12 doorOpen = true;
13 System.out.println("Door is open.");
14 }
15
16 public void closeDoor() {
17 System.out.println("Closing door.");
18 doorOpen = false;
19 System.out.println("Door is closed.");
20 }
21
22 public void goUp() {
23 System.out.println("Going up one floor.");
24 currentFloor++;
25 System.out.println("Floor: " + currentFloor);
26 }
27
28 public void goDown() {
29 System.out.println("Going down one floor.");
30 currentFloor--;
31 System.out.println("Floor: " + currentFloor);
32 }
33
34 public void setFloor() {
35
36 // Normally you would pass the desiredFloor as an
argument to the
37 // setFloor method. However, because you have not
learned how to
38 // do this yet, desiredFloor is set to a specific
number (5)
39 // below.
40
41 int desiredFloor = 5;
42 while (currentFloor != desiredFloor) {
43
44 if (currentFloor < desiredFloor) {
45 goUp();
46 }
47 else {
48 goDown();
49 }
50 }
51 }
52 }
53
54
55

7.5 Creación de Bucles


while
7.5.1 Bucles while anidados
Considere qué es necesario para dibujar un rectángulo como el que se muestra
a continuación, de a un caracter por vez:

@@@@@@@@@@
@@@@@@@@@@
@@@@@@@@@@

Se podría utilizar un bucle while para dibujar una fila del rectángulo y colocar
ese bucle dentro de otro bucle para dibujar las tres filas. El segundo bucle es
un bucle anidado.

El código en el gráfico despliega un rectángulo de símbolos '@'. Dicho


rectángulo tiene 10 columnas de ancho y 3 filas de largo (3 filas compuestas
cada una de ellas por 10 símbolos '@'). Cada fila es desplegada por un bucle
interno. El bucle exterior ejecuta el código anterior tres veces.

El bucle interior despliega una fila de símbolos '@' hasta que el valor de la
variable width (ancho) sea alcanzado (Líneas 15 a 18). El primer bucle (externo)
verifica si el valor de la variable height (alto) ha sido alcanzado (línea 12). Si el
alto no ha sido alcanzado, se crea una fila adicional usando el bucle interior. En
caso contrario, el rectángulo se habrá completado.

1
2 public class WhileRectangle {
3
4 public int height = 3;
5 public int width = 10;
6
7 public void displayRectangle() {
8
9 int colCount = 0;
10 int rowCount = 0;
11
12 while (rowCount < height) {
13 colCount=0;
14
15 while (colCount < width) {
16 System.out.print("@");
17 colCount++;
18 }
19
20 System.out.println();
21 rowCount++;
22 }
23 }
24 }
25
26
27
28
7.5 Creación de Bucles
while
7.5.2 El Bucle while

La sintaxis del bucle while es:

while ( <test_expr> )
<sentencia_o_bloque>

Por ejemplo:

int i = 0;
while ( i < 10 ) {
System.out.println(i + " squared is " + (i*i));
i++;
}

Asegúrese de que la variable de control del bucle está inicializada


apropiadamente antes de que comience la ejecución del cuerpo del bucle.
Debe actualizar el control de la variable apropiadamente para prevenir un bucle
infinito.

7.5 Creación de Bucles


while
7.5.3 El Bucle do/while

La sintaxis para el bucle do/while es:

do
<sentencia_o_bloque>
while ( <test_expr> );

Por ejemplo:

int i = 0;
do {
System.out.println(i + " squared is " + (i*i));
i++;
} while ( i < 10 );

Así como lo hace con el bucle while, asegúrese de que la variable de control
del bucle está inicializada correctamente, actualizada en el cuerpo del bucle y
verificada. Use el bucle for en los casos en los que el bucle será ejecutado un
número de veces predeterminado. Use los bucles do y while en casos en los
que no está determinado de antemano.

7.5 Creación de Bucles while


7.5.4 Codificación de un bucle do/while

El bucle do/while es un bucle del tipo una a muchas iteraciones: la condición


está ubicada debajo del bucle y es procesada después del cuerpo. El cuerpo
del bucle es por lo tanto, procesado al menos una vez. Si usted quiere que la
sentencia o sentencias en el cuerpo sean procesadas al menos una vez, utilice
el bucle do/while en lugar de los bucles while o for. La sintaxis para el bucle
do/while es:

do {
bloque_código;
}
while (expresión_booleana);// El punto y coma es obligatorio.

• bloque_código representa las líneas de código que serán ejecutadas


más de una vez si la expresión_booleana es evaluada a true.
• La expresión_booleana es una expresión que evalúa a true o false. La
expresión_booleana es procesada después de cada iteración del bucle.

El código en el gráfico muestra un método setFloor que contiene un bucle


do/while para asistir al ascensor a desplazarse hacia arriba o hacia abajo.

El método setFloor de la clase Elevator usa un bucle do/while para determinar


si el ascensor está en el piso seleccionado (líneas 43 a 51). Si el valor de la
variable currentFloor no es igual al valor de la variable desiredFloor, entonces
el ascensor continúa su movimiento hacia arriba (Línea 45) o hacia abajo
(Línea 48).
1
2 public class DoWhileElevator {
3
4 public boolean doorOpen=false;
5 public int currentFloor = 1;
6
7 public final int TOP_FLOOR = 5;
8 public final int BOTTOM_FLOOR = 1;
9
10 public void openDoor() {
11 System.out.println("Opening door.");
12 doorOpen = true;
13 System.out.println("Door is open.");
14 }
15
16 public void closeDoor() {
17 System.out.println("Closing door.");
18 doorOpen = false;
19 System.out.println("Door is closed.");
20 }
21
22 public void goUp() {
23 System.out.println("Going up one floor.");
24 currentFloor++;
25 System.out.println("Floor: " + currentFloor);
26 }
27
28 public void goDown() {
29 System.out.println("Going down one floor.");
30 currentFloor--;
31 System.out.println("Floor: " + currentFloor);
32 }
33
34 public void setFloor() {
35
36 // Normally you would pass the desiredFloor as an
argument to the
37 // setFloor method. However, because you have not
learned how to
38 // do this yet, desiredFloor is set to a specific
number (5)
39 // below.
40
41 int desiredFloor=5;
42
43 do {
44 if (currentFloor < desiredFloor) {
45 goUp();
46 }
47 else if (currentFloor > desiredFloor) {
48 goDown();
49 }
50 }
51 while (currentFloor!=desiredFloor);
52 }
53
54
55 }
56
57
58

7.5 Creación de Bucles while


7.5.5 Bucles do/while Anidados

El gráfico despliega un rectángulo de símbolos '@', con 10 columnas y 3 filas (3


filas de 10 símbolos '@' cada una). Cada fila se despliega en el bucle interior.
El bucle exterior despliega el código tres veces.

El segundo bucle (el bucle interno) despliega una fila de símbolos '@' hasta
que el valor de la variable width es alcanzado (líneas 15 a 19). Al menos uno
de los símbolos '@' es desplegado antes de verificar el valor de la variable
width. El bucle exterior verifica si el valor de la variable height ha sido
alcanzado (Línea 24). Si el valor de la variable height no
fue alcanzado, se crea y agrega una nueva fila usando el bucle interior. En
caso contrario, el rectángulo está completado. Al menos una fila es creada
antes de que el valor de la variable height sea alcanzado.

1
2 public class DoWhileRectangle {
3
4 public int height = 3;
5 public int width = 10;
6
7 public void displayRectangle() {
8
9 int rowCount = 0;
10 int colCount = 0;
11
12 do {
13 colCount = 0;
14
15 do {
16 System.out.print("@");
17 colCount++;
18 }
19 while (colCount < width);
20
21 System.out.println();
22 rowCount++;
23 }
24 while (rowCount < height);
25 }
26 }
27
28

7.6 Ejercicio 1: Uso del bucle while

El objetivo de este ejercicio es escribir clases que usen el bucle while.

7.6 Ejercicio 1: Uso del bucle


while
7.6.1 Tarea - Escribir una Clase que Use el
Bucle while

En esta tarea, usted escribirá una clase que cuenta desde 1 hasta algún
número. Siga los siguientes pasos para escribir la clase.

1. Vaya al directorio loops.


2. Escriba una clase de nombre Counter que contenga un método llamado
displayCount que:
• Cuenta, en una variable count, desde uno (1) hasta MAX_COUNT,
donde MAX_COUNT es una variable que está declarada e inicializada
con cualquier número, usando un bucle while.
• Despliega el valor de la variable count.
3. Compile su programa
4. Use la clase CounterTest.class para probar su programa

7.6 Ejercicio 1: Uso del bucle


while
7.6.2 Tarea - Escribir Otra Clase que Usa el
Bucle while

En esta tarea, usted escribirá una clase Sequence que despliega una
secuencia comenzando con los números 1 y 1. Los números sucesivos en la
secuencia son la suma de los dos números anteriores. Por ejemplo: 1 1 2 3 5 8
13. Siga estos pasos para crear la clase:

1. Escriba una clase llamada Sequence que contenga un método llamado


displaySequence
2. El método debería contener un bucle while que:
• Realice una operación para calcular un número en la secuencia.
• Desplegar los 10 primeros números en la secuencia. El resultado
debería ser el siguiente:
1
1
2
3
5
8
13
21
34
55
3. Compile su programa.
4. Use la clase SequenceTest.class para probar su programa.

7.7 Ejercicio 1: Resumen

Discusión – Tómese unos minutos para discutir las experiencias, temas y


descubrimientos realizados durante este ejercicio.

• Experiencias
• Interpretaciones
• Conclusiones
• Aplicaciones

7.8 Ejercicio 2: Uso del bucle do/while

El objetivo de este ejercicio es escribir clases que usan los bucles do/while.

7.8 Ejercicio 2: Uso del bucle


do/while
7.8.1 Tarea - Escribir una Clase Usando
el bucle do/while

En esta tarea, usted escribirá una clase que cuente desde 1 hasta algún
número. Siga los siguientes pasos para escribir su clase:

1. Vaya al directorio loops.


2. Escriba una clase de nombre CounterThree que contenga un método de
nombre displayCount que:
• Cuente, en la variable count, desde 1 hasta el valor de la constante
MAX_COUNT, donde la constante MAX_COUNT es una variable que
usted debe declarar e inicializar usando el bucle do/while.
• Despliegue el valor de la variable count.
3. Compile su programa
4. Use la clase CounterThreeTest.class para verificar su programa

7.9 Ejercicio 2: Resumen

Discusión – Tómese unos minutos para discutir las experiencias, los temas y
los descubrimientos que ha realizado durante estos ejercicios.

• Experiencias
• Interpretaciones
• Conclusiones
• Aplicaciones

7.9 Ejercicio 2:
Resumen
7.9.1 Ejercicios de while y do/while

Ahora le proponemos que resuelva los siguientes ejercicios para poner a


prueba sus conocimientos.
Ejercicios de while y do/while

Escribir el código para verificar si un número es primo o no. Para ello se


suministrará un número entero y el programa verificará si es efectivamente
primo o no, en caso de serlo deberá informarlo con la leyenda "Número Primo".

Escribir el código que imprima por pantalla los numeros impares del 0 al 100.
Además deberá comprobar si ese número impar es multiplo de 3, en caso de
serlo, deberá informarlo con la leyenda "Es multiplo de 3".

7.10 Desarrollo de una Iteración for

El bucle for permite a los programas ejecutar una secuencia de sentencias una
cantidad predeterminada de veces. El bucle for opera de la misma manera que
el bucle while, incluyendo el hecho que es un bucle de cero a muchas veces,
pero que tiene una estructurada centrada en contar a través de un rango de
valores. La sintaxis para el bucle for es:

for (inicializador[,inicializador]; expresión_booleana;


modificador[,modificador]) {
bloque_código;
}

donde:

• La sección inicializador[,inicializador] contiene sentencias que inicializan


variables (tales como los contadores del bucle) usados a través del
bucle. Esta sección de código es procesada solamente una vez, antes
que cualquier parte del bucle. Si se utilizan múltiples variables se deben
separar sus declaraciones por comas.
• La expresión_booleana es una expresión cuyo resultado es true o false.
Esta expresión es procesada antes de cada iteración del bucle.
• La sección modificador[,modificador] contiene las variables (contadores
del bucle) que son modificadas (incrementadas o decrementadas). Esta
sección es procesada después del cuerpo pero antes de la re-
verificación de la expresión_booleana.
• El bloque_código representa las líneas de código que serán ejecutadas
si la expresión booleana es evaluada a true.

El bucle for es también un bucle iterativo de cero a muchas veces. La expresión


que forma parte del for es procesada antes de la ejecución del cuerpo del bucle,
y si la primera vez es false, el cuerpo del for nunca es procesado. Por ejemplo,

for (int i=0; i<5; i++) {


System.out.println("Hello Thomas");
}

En el ejemplo previo, la frase "Hello Thomas" se despliega cinco veces. El


código en el gráfico muestra un método setFloor que contiene un bucle for.

En este ejemplo, el valor de la variable currentFloor es comparado con el valor


de la variable desiredFloor para determinar si el ascensor debe ascender o
descender (Línea 43). Dos bucles for invocan al método goDown o goUp la
cantidad de veces que sean necesarias para hacer el valor de la variable
desiredFloor igual al valor de la variable currentFloor (Líneas 40 a 46 y 50 a 52).

1
2 public class ForElevator {
3
4 public boolean doorOpen=false;
5 public int currentFloor = 1;
6
7 public final int TOP_FLOOR = 5;
8 public final int BOTTOM_FLOOR = 1;
9
10 public void openDoor() {
11 System.out.println("Opening door.");
12 doorOpen = true;
13 System.out.println("Door is open.");
14 }
15
16 public void closeDoor() {
17 System.out.println("Closing door.");
18 doorOpen = false;
19 System.out.println("Door is closed.");
20 }
21
22 public void goUp() {
23 System.out.println("Going up one floor.");
24 currentFloor++;
25 System.out.println("Floor: " + currentFloor);
26 }
27
28 public void goDown() {
29 System.out.println("Going down one floor.");
30 currentFloor--;
31 System.out.println("Floor: " + currentFloor);
32 }
33
34 public void setFloor() {
35
36 // Normally you would pass the desiredFloor as an
argument to the
37 // setFloor method. However, because you have not
learned how to
38 // do this yet, desiredFloor is set to a specific
number (5)
39 // below.
40
41 int desiredFloor=5;
42
43 if (currentFloor > desiredFloor) {
44 for (int down=currentFloor; down != desiredFloor; --down) {
45 goDown();
46 }
47 }
48
49 else {
50 for (int up=currenFloor; up!=desiredFloor; ++up) {
51 goUp();
52 }
53 }
54 }
55
56
57 }
58
59
60

7.10 Desarrollo de una Iteración


for
7.10.1 Bucles for Anidados

El código en el gráfico despliega un rectángulo de símbolos '@' de 10 columnas


de ancho por 3 filas de alto (tres filas compuestas cada una de ellas por 10
símbolos '@'). Cada fila es desplegada por un bucle interno. El bucle exterior
despliega el código tres veces.
El segundo bucle (el interior) despliega una fila de símbolos '@' hasta que el
valor de la variable width sea alcanzado (Líneas 10 a 12). El primer bucle
verifica si se ha alcanzado el valor de la variable height (Línea 9). En caso
contrario, se agrega una fila usando el bucle interno

1
2 public class ForRectangle {
3
4 public int height = 3;
5 public int width = 10;
6
7 public void displayRectangle() {
8
9 for (int rowCount = 0; rowCount < height;
rowCount++) {
10 for (int colCount = 0; colCount < width;
colCount++) {
11 System.out.print("@");
12 }
13 System.out.println();
14 }
15 }
16 }
17
18

7.10 Desarrollo de una Iteración for


7.10.2 El Bucle for

La sintaxis del bucle for es:

for ( <expr_inicial>; <expr_verific>; <expr_modif> )


<sentencia_o_bloque>

Por ejemplo:

for ( int i = 0; i < 10; i++ )


System.out.println(i + " squared is " + (i*i));

Sin embargo, es recomendable que ubique todas las cláusulas del bucle en un
bloque. Por ejemplo:

for ( int i = 0; i < 10; i++ ) {


System.out.println(i + " squared is " + (i*i));
}

En el ejemplo anterior, dentro del bloque for se declara y define la variable int i.
Se accede a la variable i sólo dentro del alcance de este bloque for en
particular.
7.10 Desarrollo de una Iteración for
7.10.3 Los Flujos de Control de Bucles Especiales

Puede usar las siguientes sentencias para mejorar el control de las sentencias
de bucles:

• break [<label>];
Use la sentencia break para salir anticipadamente de las sentencias
switch, de las sentencias de bucle y de los bloques etiquetados.
• continue [<label>];
Use la sentencia continue para saltar al final del cuerpo de la iteración y
luego devolverle el control a la sentencia de control del bucle.
• <label> : <sentencia>
Use la sentencia label para identificar cualquier sentencia válida para la
cual el control debe ser transferido. Con respecto a la sentencia break
etiquetada, la etiqueta puede identificar a cualquier sentencia. Con
respecto a la sentencia continue etiquetada, la etiqueta debe identificar a
una construcción de bucle.

La Sentencia break

La Figura muestra un ejemplo de un bucle con una sentencia break sin


etiqueta

La Sentencia continue
La Figura muestra un ejemplo de un bucle con una sentencia continue sin
etiqueta

El Uso de las Sentencias break con Etiquetas

La Figura muestra un ejemplo de una sentencia break con etiqueta

El Uso de la Sentencia continue con Etiquetas

La Figura muestra un ejemplo de una sentencia continue con etiqueta


7.11 Ejercicio 3: Uso del bucle for

El objetivo de este ejercicio es escribir clases que usen el bucle for.

7.11 Ejercicio 3: Uso del bucle for


7.11.1 Tarea - Desarrollar una Clase Usando
el bucle for

En esta tarea, usted escribirá una clase que cuente desde 1 hasta cualquier
número. Siga los siguientes pasos para escribir la clase:

1. Vaya al directorio loops


2. Escriba una clase de nombre CounterTwo que contenga un método de
nombre displayCount que:
• Cuente desde 1 hasta el valor de la constante MAX_COUNT, donde la
constante MAX_COUNT es una variable que debe declarar e inicializar
con cualquier número, usando el bucle for.
• Despliegue el valor del contador
3. Compile su programa
4. Use la clase CounterTwoTest.class para probar su programa
7.11 Ejercicio 3: Uso del bucle
for
7.11.2 Tarea - Desarrollar Otra Clase Usando el
bucle for

En esta tarea, usted escribirá una clase de nombre Sequence que despliega
una secuencia de números comenzando con los números 1 y 1. Los siguientes
números en la secuencia son la suma de los dos números anteriores, por
ejemplo, 1 1 2 3 5 8 13, etc.. Siga los siguientes pasos para escribir su clase:

1. Escriba una clase de nombre SequenceTwo que contenga un método de


nombre displaySequence. El método displaySequence debería contener
un bucle for que:
• Realice una operación que calcule un número en la secuencia.
• Despliegue los 10 primeros números en la secuencia. El resultado
debería ser similar al siguiente:

1
1
2
3
5
8
13
21
34
55
2. Compile su programa
3. Use la clase SequenceTwoTest.class para probar su programa

7.12 Ejercicio 3: Resumen

Discusión – Tómese unos minutos para discutir las experiencias, temas y


descubrimientos que ha realizado durante estos ejercicios.

• Experiencias
• Interpretaciones
• Conclusiones
• Aplicaciones

7.13 Comparación de las construcciones de bucle


Los bucles for, while y do/while operan en forma similar. Sin embargo, en una
situación dada, una construcción de bucle puede resultar mejor que otra. Siga
las siguientes pautas para determinar cuál construcción de bucle se debería
usar:

• Use el bucle while para iterar indefinidamente a través de sentencias y


ejecutar las sentencias cero o más veces.
• Use el bucle for para ejecutar las sentencias una cantidad predefinida de
veces. El bucle for es más compacto y fácil de leer que el bucle while,
dado que está diseñado para contar a través de un rango discreto de
valores.

7.13 Comparación de las construcciones de bucle


7.13.1 Las Sentencias de Bucle

Las sentencias de bucle permiten ejecutar bloques de sentencias


repetidamente. El lenguaje de programación Java soporta tres tipos de
construcciones de bucle: for, while y do. Los bucles for y while verifican la
condición del bucle antes de ejecutar el cuerpo de la misma. El bucle do
verifica la condición luego de ejecutar el cuerpo. Esto implica que los bucles for
y while pueden no ejecutar el cuerpo ni una vez. En cambio, el bucle do lo hace
por lo menos una vez.

7.14 Laboratorios

Laboratorio 4 - Las Expresiones y el Control de Flujo

7.14 Laboratorios
7.14.1 Objetivos

Una vez completado este laboratorio, usted debería ser capaz de:

• Usar un bucle simple for.


• Usar sentencias condicionales en la lógica de negocios.
• (Opcional) Usar bucles anidados para implementar una operación de
búsqueda de una cadena de caracteres

7.14 Laboratorios
7.14.2 Ejercicio 1: El Uso de Bucles y Sentencias de Bifurcación

Ejercicio 1 - El Uso de Bucles y Sentencias de Bifuracación


7.14 Laboratorios
7.14.3 Ejercicio 2: El Uso de Sentencias Condicionales en la
Clase Account (Nivel 1)

Ejercicio 2 - El Uso de Sentencias Condicionales en la Clase Account (Nivel 1)

7.14 Laboratorios
7.14.4 Ejercicio 2: El Uso de Sentencias Condicionales en la Clase
Account (Nivel 2)

Ejercicio 2 - El Uso de Sentencias Condicionales en la Clase Account (Nivel 2)

7.14 Laboratorios
7.14.5 Ejercicio 2: El Uso de Sentencias Condicionales en la
Clase Account (Nivel 3)

Ejercicio 2 - El Uso de Sentencias Condicionales en la Clase Account (Nivel 3)

7.14 Laboratorios
7.14.6 Ejercicio 3: El Uso de Bucles Anidados (Avanzado)

Ejercicio 3 - El Uso de Bucles Anidados (Avanzado)

7.14 Laboratorios
7.14.7 Resumen del Ejercicio

Discusión – Dedique unos minutos para discutir qué experiencias, temas o


descubrimientos realizó durante el ejercicio de laboratorio.

• Experiencias
• Interpretaciones
• Conclusiones
• Aplicaciones

7.15 Preguntas de Repaso

Ahora le proponemos que resuelva el siguiente ejercicio para poner a prueba


sus conocimientos.

Defina por Verdadero o Falso las siguientes afirmaciones según corresponda.


¡Éxitos!
8 Desarrollo y Uso de Métodos

En este capítulo describiremos la creación y el uso de métodos para combinar


la lógica del programa y cumplir una tarea particular. Definiremos los tipos de
métodos y describiremos sus ventajas. Aprenderemos también como usar
métodos sobrecargados.

8.1 Repaso del Capítulo Anterior

Los principales temas del capítulo anterior son:

Creación de Bucles while

Un bucle while itera un bloque de código mientras una condición tiene el valor
true.
La sintaxis para un bucle while es:

while (expresión_booleana) {bloque_código;}

Se podría utilizar un bucle while para dibujar una fila del rectángulo y colocar
ese bucle dentro de otro bucle para dibujar las tres filas. El segundo bucle es
un bucle anidado.
Desarrollo de una Iteración for

El bucle for permite a los programas ejecutar una secuencia de sentencias una
cantidad predeterminada de veces.
El bucle for opera de la misma manera que el bucle while, incluyendo el hecho
que es un bucle de cero a muchas veces, pero que tiene una estructurada
centrada en contar a través de un rango de valores. La sintaxis para el bucle for
es:

for (inicializador[,inicializador]; expresión_booleana;modificador[,modificador])


{bloque_código;}

La expresión que forma parte del for es procesada antes de la ejecución del
cuerpo del bucle, y si la primera vez es false, el cuerpo del for nunca es
procesado.

Codificación de un Bucle do/while

El bucle do/while es un bucle del tipo una a muchas iteraciones: la condición


está ubicada debajo del bucle y es procesada después del cuerpo. El cuerpo
del bucle es por lo tanto, procesado al menos una vez. Si usted quiere que la
sentencia o sentencias en el cuerpo sean procesadas al menos una vez, utilice
el bucle do/while en lugar de los bucles while o for. La sintaxis para el bucle
do/while es:

do {bloque_código;}
while (expresión_booleana);

Comparación de las construcciones de bucle

Los bucles for, while y do/while operan en forma similar. Sin embargo, en una
situación dada, una construcción de bucle puede resultar mejor que otra. Siga
las siguientes pautas para determinar cuál construcción de bucle se debería
usar:

• Use el bucle while para iterar indefinidamente a través de sentencias y


ejecutar las sentencias cero o más veces.
• Use el bucle for para ejecutar las sentencias una cantidad predefinida de
veces. El bucle for es más compacto y fácil de leer que el bucle while, dado que
está diseñado para contar a través de un rango discreto de valores.

8.3 Objetivos

Una vez completado este capítulo, usted será capaz de:

• Describir las ventajas de los métodos y definir métodos "trabajadores" y


llamadores.
• Declarar e invocar un método.
• Comparar métodos de objetos y métodos estáticos.
• Usar métodos sobrecargados.

Este capítulo describe cómo crear y usar métodos para combinar la lógica del
programa y cumplir una tarea particular.

Discusión – La siguiente pregunta es relevante para entender de qué tratan


los métodos.

¿Cómo estructura o implementa las operaciones realizadas sobre un objeto?

8.4 Recursos Adicionales

Recursos adicionales – Las siguientes referencias ofrecen información


adicional sobre los temas descritos en este capítulo:

• The Java Tutorial. [Online]. Disponible en:

http://java.sun.com/docs/books/
tutorial/java/javaOO/methods.html

http://java.sun.com/docs/books/
tutorial/java/javaOO/arguments.html

http://java.sun.com/docs/books/
tutorial/java/javaOO/returnvalue.html

8.5 Creación e Invocación de Métodos

La mayoría del código que se escribe para una clase está contenido en uno o
más métodos. Los métodos permiten dividir el trabajo que realiza su programa
en tareas o comportamientos lógicos separados. Por ejemplo, usted podría
querer desarrollar un programa que calcule y despliegue un valor total. Todo el
código para realizar estas tareas puede ser incluido en un solo método. Sin
embargo, agrupar las tareas puede crear mejores programas orientados a
objetos. En otras palabras, dividir las tareas en segmentos de forma tal que
cada método pueda ser usado en forma independiente. Por lo tanto, usted
podría invocar el método que calcula el total en una clase, pero que no
despliegue el resultado de los cálculos. La sintaxis de las declaraciones de los
métodos es la siguiente:

[modificadores] tipo_retorno identificador_método ([argumentos]){


bloque_código_método
}

• Los [modificadores] representan varias palabras clave de la tecnología


Java que modifican la forma en que los métodos son usados. Los
modificadores son opcionales (lo que se indica con los corchetes)
• El tipo_retorno es el tipo del valor devuelto por el método, el que puede
ser usado en cualquier lugar en el programa. Los métodos pueden
devolver únicamente un ítem (valor literal, referencia a objeto, etc.). Si el
método no devolviera nada, entonces se debe especificar la la palabra
clave void como tipo de retorno.
• El identificador_método es el nombre del método.
• Los ([agumentos]) representan una lista de variables cuyos valores son
pasados al método para ser usados por este. Los argumentos son
opcionales (indicado por los corchetes). Muchos métodos no aceptan
argumentos.
• El bloque_código_método es la secuencia de sentencias que el método
realiza. Una gran variedad de tareas pueden tener lugar en el
bloque de código o el cuerpo de un método.
8.5 Creación e Invocación de
Métodos
8.5.1 Forma Básica de un Método

La forma básica de un método no tiene argumentos y no retorna nada. El


método display de la clase Shirt es un método básico.

public void displayShirtInformation() {


System.out.println("Shirt ID: " + shirtID);
System.out.println("Shirt description:" + description);
System.out.println("Color Code: " + colorCode);
System.out.println("Shirt price: " + price);
System.out.println("Quantity in stock: " + quantityInStock);
} // fin del método displayShirtInformation

Este método despliega varias líneas de información, tales como el valor de las
variables itemId y price.

8.5 Creación e Invocación de


Métodos
8.5.2 Invocación de un Método desde
una Clase Diferente

Para invocar o ejecutar un método declarado en una clase diferente, se puede


usar el operador punto (.) con un variable de referencia a un objeto, de la
misma manera que se hace para acceder a una variable pública de un objeto.
El código en el gráfico muestra la clase ShirtTest.
En este ejemplo, una variable de referencia a un objeto con nombre myShirt es
declarada e inicializada con un objeto Shirt en la línea 7. La variable de
referencia al objeto myShirt invoca el método displayShirtInformation en el
objeto Shirt (Línea 9).

Métodos Llamadores y Trabajadores

En el ejemplo la clase ShirtTest llama al método displayShirtInformation desde


otro método (el método main). Por lo tanto, el método main es denominado el
método llamador porque realiza la invocación o llamada a otro método para que
realice el trabajo. Por el contrario, el método displayShirtInformation es
denominado el método trabajador porque realiza algún trabajo para el método
principal.

Cuando un método llamador llama a un método trabajador, el método llamador


detiene su ejecución hasta que el método trabajador finalice. Cuando el método
trabajador se completa, el flujo del programa se dice que "regresa" al punto
después de la invocación al método en el método llamador (regresa a la Línea
10 en la clase ShirtTest.java
8.5 Creación e Invocación de
Métodos
8.5.3 Invocación a un Método en la Misma
Clase

Llamar a un método en la misma clase es fácil. Simplemente se incluyen el


nombre del método trabajador y sus argumentos (si existen).
A el gráfico muestra un ejemplo de código de una clase con un método que
llama otro método de la misma clase.
El método setFloor invoca a los métodos goDown y goUp (Líneas 37 y 40). Los
tres métodos pertenecen a la clase Elevador

1
2 public class Elevator {
3
4 public boolean doorOpen=false;
5 public int currentFloor = 1;
6
7 public final int TOP_FLOOR = 5;
8 public final int BOTTOM_FLOOR = 1;
9
10 public void openDoor() {
11 System.out.println("Opening door.");
12 doorOpen = true;
13 System.out.println("Door is open.");
14 }
15
16 public void closeDoor() {
17 System.out.println("Closing door.");
18 doorOpen = false;
19 System.out.println("Door is closed.");
20 }
21
22 public void goUp() {
23 System.out.println("Going up one floor.");
24 currentFloor++;
25 System.out.println("Floor: " + currentFloor);
26 }
27
28 public void goDown() {
29 System.out.println("Going down one floor.");
30 currentFloor--;
31 System.out.println("Floor: " + currentFloor);
32 }
33
34 public void setFloor(int desiredFloor) {
35 while (currentFloor != desiredFloor){
36 if (currentFloor < desiredFloor){
37 goUp();
38 }
39 else {
40 goDown();
41 }
42 }
43 }
44
45 public int getFloor() {
46 return currentFloor();
47 }
48
49 public boolean checkDoorStatus() {
50 return doorOpen;
51 }
52 }
53
54

8.5 Creación e Invocación de Métodos


8.5.4 Pautas para la Invocación de Métodos

Las pautas para la creación e invocación de métodos son:


• No hay límite en cuanto a la cantidad de llamadas a métodos que un
método llamador puede realizar.
• El método llamador y el método trabajador pueden estar en la misma
clase o en diferentes clases.
• La forma en que se invoca a un método trabajador es diferente,
dependiendo si está en la misma clase o en una clase diferente a la
del método llamador.
• Se puede invocar a los métodos en cualquier orden. Los métodos no
necesitan ser completados en el orden en que son listados en la clase
donde están declarados (la clase que contiene a los métodos
trabajadores)

8.6 Pasaje de Argumentos y Devolución de Valores

Como fue mencionado anteriormente, los métodos pueden ser invocados por
un método llamador con una lista de argumentos (las variables o los valores a
ser usados por el método trabajador). Adicionalmente, los métodos pueden
devolver un valor al método llamador, el que puede ser usando en el mismo.

8.6 Pasaje de Argumentos y Devolución de


Valores
8.6.1 Declaración de
Métodos Con
Argumentos
Muchos de los métodos que usted declara o usa desde la biblioteca de clases
de la tecnología Java aceptan argumentos. Por ejemplo, el método setFloor de
la clase Elevator acepta un valor desiredFloor, el cual es usado en el cuerpo
del método.

public void setFloor(int desiredFloor) {


while (currentFloor != desiredFloor) {
if (currentFloor < desiredFloor) {
goUp();
}
else {
goDown();
}
}
}
public void multiply(int numberOne, int numberTwo)

El Método main

Anteriormente se utilizó el método main en las clases de prueba, el que debe


ser escrito de forma tal que acepte argumentos: una o más (un array de)
referencias a objetos String.
public static void main (String args[])

¿Por qué el método debe aceptar estos argumentos? El lenguaje de


programación requiere que el método main sea escrito de formal tal que acepte
valores cuando es ejecutado desde la línea de comando. Por ejemplo, si desea
pasar un precio de 12.00 y un color R a la clase ShirtTest, ejecute la clase
usando la Máquina Virtual Java con los argumentos adicionales:

java ShirtTest 12.99 R

Para que sean usables en el programa, los valores numéricos pasados al


método main deben ser convertidos desde cadena de caracteres a un tipo
primitivo
8.6 Pasaje de Argumentos y Devolución de
Valores
8.6.2 Invocación de Métodos
con Argumentos

Para pasar argumentos desde un método a otro, estos se incluyen entre los
paréntesis en la llamada al método. Los argumentos pasados pueden ser
valores literales o variables. Al igual que en la asignación de valores literales a
las variables, se debe utilizar comillas para los literales de tipo String, una F al
final de un valor numérico para indicar que es un literal float, etc.
Al llamar a un método, se deben listar los argumentos en el mismo orden en
que dichos argumentos están declarados en el método trabajador y se deben
pasar todos los argumentos requeridos. El compilador verifica si el tipo, orden y
cantidad de argumentos pasados concuerda con el tipo, el orden y la cantidad
de parámetros que acepta el método llamado.
1
2 public class ElevatorTest {
3
4 public static void main(String args[]) {
5
6 Elevator myElevator = new Elevator();
7
8 myElevator.openDoor();
9 myElevator.closeDoor();
10 myElevator.goUp();
11 myElevator.goUp();
12 myElevator.goUp();
13 myElevator.openDoor();
14 myElevator.closeDoor();
15 myElevator.goDown();
16 myElevator.openDoor();
17 myElevator.closeDoor();
18 myElevator.goDown();
19
20 myElevator.setFloor(myElevator.TOP_FLOOR);
21
22 myElevator.openDoor();
23 }
24 }
25
8.6 Pasaje de Argumentos y Devolución de
Valores
8.6.3 Declaración de Métodos
que Devuelven Valores

La mayoría de las declaraciones de métodos que se han visto en el curso no


devuelven un valor (indicado por la palabra void en la declaración el método).
Sin embargo, muchos de los métodos que se crearán devolverán un valor y
muchos de los métodos en las bibliotecas de clase Java también devuelven
valores.
Para declarar un método que devuelve un valor, se debe incluir el tipo del valor
que devuelve delante del identificador del método en la declaración del mismo.
El siguiente ejemplo muestra un método que acepta dos valores de tipo int y
devuelve un valor de tipo int:

public int sum(int numberOne, int numberTwo)

8.6 Pasaje de Argumentos y Devolución de


Valores
8.6.4 Devolución de un
Valor

Para devolver un valor desde un método, se utiliza la palabra clave return. Por
ejemplo, el siguiente código devuelve el valor que tiene actualmente la variable
result.
public int sum(int numberOne, int numberTwo) {
int result= numberOne + numberTwo;
return result;
}

El método getFloor en la clase Elevator devuelve el valor que tiene asignado la


variable currentFloor.

public int getFloor() {


return currentFloor;
}

8.6 Pasaje de Argumentos y Devolución


de Valores
8.6.5 Recepción de Valores
Devueltos

Si se invoca un método que devuelve un valor, como en el caso del método


getFloor, se puede utilizar el valor devuelto en el método llamador.
El código en el gráfico contiene una clase de prueba que crea una instancia de
la clase Elevator e invoca su método getFloor, esperando el valor de tipo int
devuelto.
La variable curFloor está declarada en la Línea 20 y es usada para recibir el
valor de tipo int devuelto por el método getFloor en la clase Elevator.

1
2 public class ElevatorTestTwo {
3
4 public static void main(String args[]) {
5
6 Elevator myElevator = new Elevator();
7
8 myElevator.openDoor();
9 myElevator.closeDoor();
10 myElevator.goUp();
11 myElevator.goUp();
12 myElevator.goUp();
13 myElevator.openDoor();
14 myElevator.closeDoor();
15 myElevator.goDown();
16 myElevator.openDoor();
17 myElevator.closeDoor();
18 myElevator.goDown();
19
20 int curFloor = myElevator.getFloor();
21 System.out.println("Current Floor: " + curFloor);
22
23 myElevator.setFloor(curFloor+1);
24
25 myElevator.openDoor();
26 }
27 }
28
29

8.6 Pasaje de Argumentos y Devolución de


Valores
8.6.6 Ventajas del Uso de
Métodos

Los métodos son el mecanismo por el cual los objetos interactúan. En un


programa de tecnología Java, es común tener varios objetos que interactúan
invocando los métodos en forma mutua. A continuación se describen algunas
ventajas del uso de métodos en los programas:

• Los métodos hacen que los programas sean más legibles y fáciles de
mantener. Por ejemplo, es más fácil saber lo que un programa hace si el
código está dividido en varios métodos diferentes con nombres que
describan el comportamiento de los mismos.
• Los métodos hacen que el desarrollo y el mantenimiento sean más
rápido. Por ejemplo, usted puede elegir crear y probar un método por
vez para asegurar que el programa, en su totalidad, funcione bien
cuando sea finalizado.
• Los métodos son centrales para el software reusable. Por ejemplo, las
bibliotecas de la tecnología Java tienen muchas clases con métodos que
pueden ser usados una y otra vez en los programas. También se
pueden crear métodos para que otros programadores puedan usar.
• Los métodos permiten separar los objetos para comunicar y distribuir el
trabajo realizado por el programa.

Un método en un objeto puede invocar un método en otro objeto. El objeto


puede pasar al método información y recibir un valor devuelto.

8.7 Ejercicio 1: Uso de Argumentos y Devolución de Valores

El objetivo de este ejercicio es escribir una clase con un método que invoca a
un método trabajador en otra clase

8.7 Ejercicio 1: Uso de Argumentos


y Devolución de Valores
8.7.1 Tarea - Escribir una Clase que Usa
Argumentos y Devuelve Valores
En esta tarea, usted escribirá una clase de prueba que agrega múltiples objetos
Shirt a un objeto Order y despliega la cantidad total actual, en dólares, para la
orden. Siga los siguientes pasos para escribir su clase:

1. Vaya al directorio methods.


2. Escriba una clase OrderTest que contenga un método main.
3. En el método main
a. Cree referencias a objetos de tipo Order y de tipo Shirt. La clase
Order (Order.java) y la clase Shirt (Shirt.java) son provistas en el
directorio de ejercicios para este módulo. Usted debería visualizar estos
archivos y familiarizarse con ellos.
b. Asigne el valor 14.00 al precio del objeto Shirt.
c. Invoque al método addShirt para agregar una camisa (shirt) a la orden
(order). La documentación para el método addShirt es la siguiente:
public double addShirt (Shirt s) Agrega una camisa a la lista de camisas
en una orden.
Parámetros:
s - Una referencia al objeto Shirt.
Devuelve: La cantidad total actual para la orden.
d. Despliega la cantidad devuelta. Por ejemplo: Total amount for the
order is: 14.00
4. Cree objetos Shirt adicionales y agréguelos a la orden. ¿Se incrementó
la cantidad total apropiadamente?
5. Compile y ejecute el programa.

8.7 Ejercicio 1: Uso de Argumentos y Devolución de


Valores
8.7.2 Ejercicio 1:
Resumen

Discusión - Tómese unos minutos para discutir las experiencias, temas y


descubrimientos realizados durante este ejercicio.

• Experiencias
• Interpretaciones
• Conclusiones
• Aplicaciones

8.7 Ejercicio 1: Uso de Argumentos y Devolución de


Valores
8.7.3 Ejercicios de
Métodos

Ahora le proponemos que resuelva los siguiente ejercicios para poner a prueba
sus conocimientos.
Ejercicios de Métodos

Escribir un método que reciba un número entero representando un mes del año
y retorne el nombre del mes especificado. Si el mes es inválido que muestre la
leyenda "Mes Inválido". El método no debe implementar System.out, la salida
por pantalla es gestionada por el método llamador.

Escribir un método que reciba un caracter representando una letra del


abecedario y retorne el nombre de la letra correspondiente. Si el caracter no
corresponde a una letra muestre la leyenda "Caracter no válido". El método no
debe implementar System.out, la salida por pantalla es gestionada por el
método llamador.

8.8 Creación de Métodos y Variables static

Anteriormente, se vio cómo acceder a métodos y variables mediante la


creación de un objeto de la clase a la que el método o la variable pertenecen, e
invocar al método o acceder a la variable (si es una variable public). Los
métodos y las variables son únicos para cada instancia y se les denomina
métodos de la instancia y variables de la instancia.
También se han usado métodos que no requieren instanciación de objetos,
tales como el método main. Estos métodos se denominan métodos de clase o
métodos estáticos y pueden ser invocados sin crear un objeto previamente.
Similarmente, el lenguaje de programación Java permite crear variables
estáticas o variables de clase, las cuales pueden ser usadas sin crear un objeto.

8.8 Creación de Métodos y Variables


static
8.8.1 Declaración de Métodos static

Los métodos estáticos se declaran usando la palabra clave static. A


continuación se presenta la declaración del método getProperties() en la clase
System en la API de Java.

static Properties getProperties()

8.8 Creación de Métodos y Variables


static
8.8.2 Invocación de Métodos static

Dado que los métodos de clase o estáticos no son parte de cada objeto , no se
necesita una referencia a un objeto para invocarlos. La sintaxis para invocar un
método static es:

Classname.method();
A continuación, se presenta un ejemplo con un método que podría ser
agregado a la clase Shirt para convertir tallas de camisa numéricas a tallas del
tipo Small, Medium o Large. El método es estático porque:

• No utiliza directamente ningún atributo de la clase Shirt.


• Se podría querer invocar al método aún cuando no se tuviese un objeto
Shirt.

public static char convertShirtSize(int numericalSize){


if (numericalSize < 10) {
return 'S';
}
else if (numericalSize < 14) {
return 'M';
}
else if (numericalSize < 18) {
return 'L';
}
else {
return 'X';
}
}

El método convertShirtSize acepta una talla numérica, determina el tamaño en


letra correspondiente (S,M,L o X) y devuelve la talla en letra. Para acceder al
método estático convertShirtSize de la clase Shirt, se procede como sigue:

char size = Shirt.convertShirtSize(16);


8.8 Creación de Métodos y Variables
static
8.8.3 Declaración de Variables static

Se puede usar la palabra clave static para declarar que sólo una copia de la
variable en la memoria se asocia con una clase, en lugar de una copia por cada
objeto instancia. Por ejemplo:

static double salesTax = 8.25;

8.8 Creación de Métodos y Variables


static
8.8.4 Acceso a las Variables static

Al igual que con los métodos estáticos, se debe usar el nombre de la clase para
acceder a las variables estáticas. La sintaxis para acceder a una variable static
es:

Classname.variable;

Por ejemplo, para acceder al valor de la variable static PI en la clase Math


declarada como:

double myPI;

se procede como:

myPI = Math.PI;

8.8 Creación de Métodos y Variables


static
8.8.5 Métodos y Variables Estáticos
en la API de Java

Algunas bibliotecas de clases Java, tales como la clase System, contienen sólo
métodos y variables estáticas. La clase System contiene métodos útiles para
manejar tareas específicas del sistema operativo (no operan sobre un objeto
instancia). Por ejemplo, el método getProperties de la clase System
proporciona información acerca de la computadora que se está usando.
Hay varias clases en la API de Java que son clases de utilidades. Estas clases
contienen métodos static que son útiles para objetos de todos los tipos.
Algunos ejemplos de estas clases y métodos son:

• La clase Math.
Esta clase contiene métodos y variables para realizar operaciones
numéricas básicas, tales como exponenciación, logaritmo, raíz cuadrada
y funciones trigonométricas.
• La clase System.
Esta clase contiene métodos y variables para realizar funciones a nivel
del sistema, tales como recuperar información de las variables de
entorno del sistema operativo.

8.8 Creación de Métodos y Variables


static
8.8.6 Cuándo Declarar un Método o
Una Variable static

A continuación se presentan algunas sugerencias para declarar un método o


una variable static. Se debería considerar declarar una variable o un método
static si:

• Realizar la operación sobre un objeto individual o asociar la variable con


un tipo de objeto específico no es importante.
• Acceder a la variable o al método antes de instanciar un objeto es
importante.
• El método o variable no pertenecen lógicamente a un objeto, y en
cambio pertenece a una clase de utilidades, tal como la clase Math,
incluida en la API de Java. La clase Math contiene varios métodos de
utilidades static que pueden ser usados en expresiones matemáticas.

8.9 Uso de Métodos Sobrecargados

En el lenguaje de programación Java, pueden haber varios métodos en una


clase que tienen el mismo nombre pero con argumentos diferentes (difieren en
la signatura o firma del método). Este concepto es denominado sobrecarga de
métodos. Así como usted puede distinguir entre dos estudiantes en la misma
clase con el nombre Jim, llamándolos "Jim, el de la camisa verde" y "Jim, el del
beeper", puede distinguir dos métodos por el nombre y sus argumentos.
Por ejemplo, se pueden crear varios métodos para sumar dos números, tales
como dos valores de tipo int, o dos valores de tipo float. Utilizando sobrecarga
de métodos, se pueden crear varios métodos con el mismo nombre, pero con
diferentes signaturas.

Por ejemplo, el código en la figura contiene varios métodos sum, cada uno
de los cuales acepta un conjunto de argumentos diferente.
El método sum (Líneas 4 a 9) acepta dos argumentos int y devuelve un valor int.
El segundo método sum (Líneas 11 a 16) acepta dos argumentos float y
devuelve un valor float. El tercer método sum (Líneas 18 a 23) acepta un valor
de tipo int y un valor de tipo float como argumentos y devuelve un valor de tipo
float. Para invocar a cualquiera de los métodos sum, el compilador compara la
signatura del método en la invocación del método con la signatura del método
en una clase.

Por ejemplo, el código de la figura presenta un método main que invoca


cada uno de los métodos sum de un método Calculator.

1
2 public class Calculator {
3
4 public int sum(int numberOne, int numberTwo){
5
6 System.out.println("Method One");
7
8 return numberOne + numberTwo;
9 }
10
11 public float sum(float numberOne, float numberTwo) {
12
13 System.out.println("Method Two");
14
15 return numberOne + numberTwo;
16 }
17
18 public float sum(int numberOne, float numberTwo) {
19
20 System.out.println("Method Three");
21
22 return numberOne + numberTwo;
23 }
24 }
25
26

1
2 public class CalculatorTest {
3
4 public static void main(String [] args) {
5
6 Calculator myCalculator = new Calculator();
7
8 int totalOne = myCalculator.sum(2,3);
9 System.out.println(totalOne);
10
11 float totalTwo = myCalculator.sum(15.99F,
12.85F);
12 System.out.println(totalTwo);
13
14 float totalThree = myCalculator.sum(2, 12.85F);
15 System.out.println(totalThree);
16 }
17 }
18

8.9 Uso de Métodos Sobrecargados


8.9.1 Sobrecarga de Métodos y la
API de Java

Muchos métodos en la API de Java están sobrecargados, incluyendo el método


System.out.println. La tabla en el gráfico muestra todas las variaciones del
método println.
Se pueden imprimir valores de tipo String, int, float, etc. usando el método
System.out.println, dado que está sobrecargado con argumentos diferentes
para diferentes tipos de datos

8.9 Uso de Métodos


Sobrecargados
8.9.2 Uso de la Sobrecarga de Métodos
Cuando se escribe un código, se debe tener en cuenta que se deben escribir
métodos sobrecargados si las acciones que lleva a cabo el método deben ser
realizadas sobre diferentes tipos de datos. También se puede utilizar
sobrecarga para crear varios métodos con el mismo nombre pero con diferente
cantidad de parámetros. Por ejemplo, se pueden crear tres métodos sum, cada
uno con una cantidad diferente de argumentos a sumar:

public int sum(int numberOne, int numberTwo)


public int sum(int numberOne, int numberTwo, int numberThree)
public int sum(int numberOne, int numberTwo,int numberThree, int numberFour)

La clase ShirtTwo tiene un método, setShirtInfo, el cual está sobrecargado dos


veces: una vez para aceptar un valor de color (Línea 18) y otra vez para
aceptar valores para color y quantity (Línea 25).

La clase ShirtTwoTest invoca al método sobrecargado setShirtInfo, llamándolo


tres veces:

1. El método setShirtInfo es invocado con un ID, una descripción y un


precio como argumentos (Línea 9).
2. El método setShirtInfo es invocado con un ID, una descripción, un precio
y un código de color como argumentos (Línea 10).
3. El método setShirtInfo es invocado con un ID, una descripción, un precio,
un código de color y una cantidad en stock como argumentos (Línea 11).

1
2 public class ShirtTwo {
3
4 public int shirtID = 0; // Default ID for the shirt
5 public String description = "-description required-"; // default
6
7 // The color codes are R=Red, B=Blue, G=Green, U=Unset
8 public char colorCode = 'U';
9 public double price = 0.0; // Default price for all items
10 public int quantityInStock = 0; // Default quantity for all items
11
12 public void setShirtInfo(int ID, String desc, double cost){
13 shirtID = ID;
14 description = desc;
15 price = cost;
16 }
17
18 public void setShirtInfo(int ID, String desc, double cost, char
color){
19 shirtID=ID;
20 description=desc;
21 price=cost;
22 colorCode=color;
23 }
24
25 public void setShirtInfo(int ID, String desc, double cost, char
color, int quantity){
26 shirtID = ID;
27 description = desc;
28 price = cost;
29 colorCode = color;
30 quantityInStock = quantity;
31 }
32
33 // This method displays the values for an item
34 public void display() {
35
36 System.out.println("Item ID: " + shirtID);
37 System.out.println("Item description:" + description);
38 System.out.println("Color Code: " + colorCode);
39 System.out.println("Item price: " + price);
40 System.out.println("Quantity in stock: " + quantityInStock);
41
42 } // end of display method
43 } // end of class
44
45
46

1
2 class ShirtTwoTest {
3
4 public static void main (String args[]) {
5 ShirtTwo shirtOne = new ShirtTwo();
6 ShirtTwo shirtTwo = new ShirtTwo();
7 ShirtTwo shirtThree = new ShirtTwo();
8
9 shirtOne.setShirtInfo(100, "Button Down", 12.99);
10 shirtTwo.setShirtInfo(101, "Long Sleeve Oxford", 27.99, 'G');
11 shirtThree.setShirtInfo(102, "Shirt Sleeve T-Shirt", 9.99, 'B', 50);
12
13 shirtOne.display();
14 shirtTwo.display();
15 shirtThree.display();
16 }
17 }
18

8.10 Ejercicio 2: Uso de Métodos Sobrecargados

El objetivo de este ejercicio es escribir un método que está sobrecargado varias


veces.
8.10 Ejercicio 2: Uso de Métodos
Sobrecargados
8.10.1 Tarea - Desarrollar una
Clase con un Método
Sobrecargado

En esta tarea, usted escribirá una clase con un método sobrecargado. Siga los
pasos que se describen a continuación:

1. Vaya al directorio methods.


2. Cree una clase de nombre Customer.java.
3. En la clase Customer.java, agregue un método sobrecargado de nombre
setCustomerInfo.

Dependiendo de cómo el método setCustomerInfo es llamado, realiza


una de las siguientes acciones:

• Asigna los valores pasados como argumentos a Id, nombre (name),


dirección (address) y número telefónico (phoneNumber) (esta es la
mínima información necesaria para crear un nuevo Customer)
• Asigna los valores pasados como argumentos a ID, nombre (name),
dirección (address), número telefónico (phoneNumber) y dirección de
correo electrónico (email) a un objeto Customer.
4. Cree una nueva clase de prueba de nombre CustomerTest.
5. En el método main:

a. Cree dos referencias a objetos Customer diferentes.


b. Use cada variación del método setCustomerInfo para proporcionar
información para cada objeto Customer
c. Despliegue el contenido de cada objeto Customer

8.11 Ejercicio 2: Resumen

Discusión – Tómese unos minutos para discutir acerca de las experiencias,


asuntos y descubrimientos realizados durante este ejercicio.

• Experiencias
• Interpretaciones
• Conclusiones
• Aplicaciones

8.12 Preguntas de Repaso


Ahora le proponemos que resuelva el siguiente ejercicio para poner a prueba
sus conocimientos.

Defina por Verdadero o Falso las siguientes afirmaciones según corresponda.


¡Éxitos!
9 Implementación de Encapsulación y Constructores

A lo largo de éste capítulo aprenderemos a utilizar la encapsulación para


proteger datos y crearemos contructores para inicializar objetos. Aprenderemos
como combinar los conceptos de programación orientada a objetos y el
lenguaje Java.

9.1 Repaso del Capítulo Anterior

Los principales temas del capítulo anterior son:

Creación e Invocación de Métodos

Los métodos permiten dividir el trabajo que realiza su programa en tareas o


comportamientos lógicos separados. Dividir las tareas en segmentos de forma
tal que cada método pueda ser usado en forma independiente.
La sintaxis de las declaraciones de los métodos es la siguiente:

[modificadores] tipo_retorno identificador_método ([argumentos])


{bloque_código_método}

Invocación de un Método desde una Clase Diferente

Para invocar o ejecutar un método declarado en una clase diferente, se puede


usar el operador punto (.) con un variable de referencia a un objeto.
Por lo tanto, el método main es denominado el método llamador porque realiza
la invocación o llamada a otro método para que realice el trabajo.

Invocación a un Método en la Misma Clase

Llamar a un método en la misma clase es fácil. Simplemente se incluyen el


nombre del método trabajador y sus argumentos (si existen).

Pasaje de Argumentos y Devolución de Valores

Declaración de Métodos Con Argumentos

El método main en las clases de prueba, el que debe ser escrito de forma tal
que acepte argumentos: una o más (un array de) referencias a objetos String.
public static void main (String args[]).

Invocación de Métodos con Argumentos

Para pasar argumentos desde un método a otro, estos se incluyen entre los
paréntesis en la llamada al método. Los argumentos pasados pueden ser
valores literales o variables.
Al llamar a un método, se deben listar los argumentos en el mismo orden en
que dichos argumentos están declarados en el método trabajador y se deben
pasar todos los argumentos requeridos.

Declaración de Métodos que Devuelven Valores

Para declarar un método que devuelve un valor, se debe incluir el tipo del valor
que devuelve delante del identificador del método en la declaración del mismo.
El siguiente ejemplo muestra un método que acepta dos valores de tipo int y
devuelve un valor de tipo int:

public int sum(int numberOne, int numberTwo)

Devolución de un Valor

Para devolver un valor desde un método, se utiliza la palabra clave return.

Creación de Métodos y Variables static

Los métodos y las variables son únicos para cada instancia y se les denomina
métodos de la instancia y variables de la instancia.
También se han usado métodos que no requieren instanciación de objetos,
tales como el método main. Estos métodos se denominan métodos de clase o
métodos estáticos y pueden ser invocados sin crear un objeto previamente.
Similarmente, el lenguaje de programación Java permite crear variables
estáticas o variables de clase, las cuales pueden ser usadas sin crear un objeto.
A continuación se presenta la declaración del método getProperties() en la
clase System en la API de Java.

static Properties getProperties()

La sintaxis para invocar un método static es:

Classname.method();

Se puede usar la palabra clave static para declarar que sólo una copia de la
variable en la memoria se asocia con una clase, en lugar de una copia por cada
objeto instancia. Por ejemplo:

static double salesTax = 8.25;

Uso de Métodos Sobrecargados

En el lenguaje de programación Java, pueden haber varios métodos en una


clase que tienen el mismo nombre pero con argumentos diferentes (difieren en
la signatura o firma del método).
Este concepto es denominado sobrecarga de métodos.
Usted puede distinguir dos métodos por el nombre y sus argumentos.

9.3 Objetivos
Una vez completado este capítulo, usted debería ser capaz de:

• Usar encapsulación para proteger datos.


• Crear constructores para inicializar objetos.

Este capítulo describe cómo se pueden combinar los conceptos de


programación Orientación a Objetos y del lenguaje Java presentados en este
curso para escribir programas orientados a objetos encapsulados usando buen
diseño e implementación Orientados a Objetos.

Discusión – Las siguientes preguntas son relevantes para comprender qué


significa encapsulación y qué es un constructor:

• Los ascensores antiguos, requerían que un usuario manejara una o más


poleas, cuerdas y ruedas para operar el ascensor. Los ascensores
modernos ocultan estos detalles, y solamente requieren que un usuario
presione unos pocos botones para operar el ascensor. ¿Cuáles son las
ventajas de los ascensores modernos sobre los antiguos?
• Muchos ascensores, tales como los ascensores de servicio en una
fábrica, requieren una clave para poder ser operados. Otros ascensores
requieren una clave para trasladarse a pisos particulares, tales como a
la azotea de un hotel. ¿Por qué estas claves son importantes?
• ¿Qué piensa cuando escucha las palabras private y public?

9.4 Recursos Adicionales

Recursos adicionales – Las siguientes referencias ofrecen información


adicional sobre los temas descritos en este capítulo:

• The Java Tutorial. [Online]. Disponible en:

http://java.sun.com/docs/books/
tutorial/java/javaOO/variables.html

http://java.sun.com/docs/books/
tutorial/java/javaOO/constructors.html

http://java.sun.com/docs/books/
tutorial/java/javaOO/arguments.html

9.5 Uso de Encapsulación

En programación orientada a objetos, el término encapsulación hace referencia


al ocultamiento de los datos en una clase (una "cápsula" segura) y a hacerlos
disponibles sólo a través de métodos. La encapsulación es importante porque
hace que otros programadores puedan usar la clase fácilmente y además
proteger algunos de sus datos para que no sean modificados en forma
inapropiada.

El gráfico ilustra el concepto de encapsulación mostrando la seguridad que


tiene una interfaz pública (una cerradura de combinación). Cuando esta interfaz
es usada correctamente se permite el acceso al contenido privado.

El primer paso en el aseguramiento de que las clases estén correctamente


encapsuladas es aplicar los modificadores de visibilidad apropiados a la clase,
variables de atributos y declaraciones de métodos.

9.5 Uso de Encapsulación


9.5.1 Modificadores de Visibilidad

Los atributos y los métodos pueden tener modificadores, tales como public, que
indican los niveles de acceso que otros objetos tendrán sobre el método o el
atributo. Los modificadores más usados comúnmente son public y private.

9.5 Uso de Encapsulación


9.5.2 El Modificador public
Todos los ejemplos vistos en este curso usan el modificador public. Este
modificador permite que la clase, los atributos y los métodos sean visibles a
cualquier objeto en el programa.
La figura ilustra un ascensor en un hotel con acceso libre a cualquier piso
del edificio, incluyendo las áreas de visitantes o invitados no registrados que no
sean bienvenidos.
Para hacer que un atributo o método sea público, se agrega el modificador
public delante de la variable atributo o del método.

public int currentFloor=1;


public void setFloor(int desiredFloor) {
...
}

Problemas Potenciales con Atributos Públicos

El código en la figura ilustra los problemas introducidos cuando todos lo


atributos en un programa son públicos. En el código, todos los atributos están
definidos con acceso public, lo cual permite que los valores puedan ser
cambiados sin ninguna verificación de errores.
El código en la figura ilustra cómo un programa puede ser escrito para
acceder directamente a los atributos de un objeto PublicElevator, y cómo esto
produce varios problemas.

Dado que la clase PublicElevator no usa encapsulación, la clase


PublicElevatorTest puede cambiar los valores de sus atributos libremente y en
varias formas no deseables. Por ejemplo, en la Línea 10, el valor del atributo
currentFloor es decrementado a 0, el cual podría no ser un valor válido para un
número de piso. También, en la Línea 15, al atributo currentFloor se le asigna
el valor 7, el cual, de acuerdo a la constante TOP_FLOOR, no es un número de
piso válido (hay sólo cinco pisos). Estas son sólo dos formas en que la clase
PublicElevatorTest puede modificar un objeto de tipo PublicElevator y causar
problemas en el programa
2 public class PublicElevator {

4 public boolean doorOpen=false;

5 public int currentFloor = 1;

6 public int weight =0;

8 public final int CAPACITY=1000;

9 public final int TOP_FLOOR = 5;

10 public final int BOTTOM_FLOOR = 1;

11 }

12

2 public class PublicElevatorTest {


3

4 public static void main(String args[]) {

6 PublicElevator pubElevator = new PublicElevator();

8 pubElevator.doorOpen = true; //passengers get on

9 pubElevator.doorOpen = false; //doors close

10 //go down to floor 0 (below bottom of building)

11 pubElevator.currentFloor--;

12 pubElevator.currentFloor++;

13

14 //jump to floor 7 (only 5 floors in building)

15 pubElevator.currentFloor = 7;

16 pubElevator.doorOpen = true; //passengers get on/off

17 pubElevator.doorOpen = false;

18 pubElevator.currentFloor = 1; //go to the first floor

19 pubElevator.doorOpen = true; //passengers get on/off

20 pubElevator.currentFloor++; //elevator moves with door open

21 pubElevator.doorOpen = false;

22 pubElevator.currentFloor--;

23 pubElevator.currentFloor--;

24 }

25 }

26

27
9.5 Uso de Encapsulación
9.5.3 El Modificador private

El modificador private posibilita que los objetos de una clase dada, sus
atributos y las operaciones no sean accesibles desde otros objetos.
La figura ilustra un ascensor que tiene acceso privado a ciertos pisos.
Dando acceso privado a algunos pisos, las personas y las propiedades sobre
estos pisos, quedan protegidos contra el acceso de invitados o visitantes no
registrados.
Para hacer que un atributo o método sea privado, se coloca el modificador
private delante del nombre de la variable o del método.

private int currentFloor=1;

private void calculateCapacity() {


...
}

El código en la figura ilustra cómo encapsular los datos en el ascensor del


ejemplo anterior, a efectos de asegurar que los datos no sean modificados en
forma inapropiada.
La clase PrivateElevator1 contiene sólo variables atributo privadas.
En el código en la figura , se intenta acceder a las variables privadas de un
objeto instancia de la clase PrivateElevator1 a través de una referencia a dicho
objeto.

El código en el ejemplo anterior no compila correctamente, debido a que el


método main en la clase PrivateElevator1Test intenta cambiar el valor de un
atributo privado en la clase PrivateElevator1 (Líneas 24 a 29).
La clase PrivateElevator1 no resulta muy útil, dado que no hay forma de
modificar los valores de sus atributos privados. En un programa ideal, la
mayoría de los atributos de una clase deberían ser privados. Los atributos
privados no pueden ser visualizados o modificados directamente desde fuera
de la clase y por lo tanto, sólo pueden ser vistos o modificados por los métodos
de la propia clase. Estos métodos deberían contener código y lógica de
negocio para asegurar que no se asignarán valores no apropiados a una
variable para un atributo.

2 public class PrivateElevator1 {

4 private boolean doorOpen=false;


5 private int currentFloor = 1;

6 private int weight =0;

8 private final int CAPACITY=1000;

9 private final int TOP_FLOOR = 5;

10 private final int BOTTOM_FLOOR = 1;

11 }

12

2 public class PrivateElevator1Test {

4 public static void main(String args[]) {

6 PrivateElevator1 privElevator = new PrivateElevator1();

8 /**************************************************

9 * Las siguientes líneas de código no compilan*

10 * dado que se intenta acceder a variables con*

11 * acceso privado. *

12 **************************************************/

13

14 privElevator.doorOpen = true; //passengers get on

15 privElevator.doorOpen = false; //doors close

16 //go down to currentFloor 0 (below bottom of building)


17 privElevator.currentFloor--;

18 privElevator.currentFloor++;

19

20 //jump to currentFloor 7 (only 5 floors in building)

21 privElevator.currentFloor=7;

22 privElevator.doorOpen=true; //passengers get on/off

23 privElevator.doorOpen=false;

24 privElevator.currentFloor=1; //go the first floor

25 privElevator.doorOpen=true; //passenger get on/off

26 privElevator.currentFloor++; //elevator moves with door open

27 privElevator.doorOpen=false;

28 privElevator.currentFloor--;

29 privElevator.currentFloor--;

30 }

31 }

9.5 Uso de Encapsulación


9.5.4 Interfaz e Implementación

La figura ilustra la interfaz de un ascensor y dos implementaciones


independientes. Mientras que muchos ascensores alrededor del mundo pueden
tener la misma interfaz, sus implementaciones pueden ser diferentes.
Cuando las clases están encapsuladas, otros objetos interactúan sólo con unas
pocas partes (métodos) de cada clase. Por ejemplo, un programador puede
cambiar el bloque de código para un método print según lo necesite, pero si la
declaración del método print no cambia, entonces los lugares del código donde
se hace referencia a este método no necesitan ser cambiados.
9.5 Uso de Encapsulación
9.5.5 Métodos Get y Set

Si se hace que los atributos sean privados, ¿cómo pueden los otros objetos
acceder a ellos? Un objeto puede acceder a los atributos privados de un
segundo objeto si este segundo objeto tiene métodos públicos para cada una
de las operaciones que serán realizadas sobre los valores de un atributo. Por
ejemplo, es común proveer métodos públicos para asignar (set) y obtener (get)
los valores de cada atributo privado en una clase. El código de la figura
contiene una clase PrivateShirt1 con atributos privados y métodos públicos
para asignar y obtener el valor de la variable colorCode.

Mientras que este código es sintácticamente correcto, el método setColorCode


no contiene la lógica para asegurar que los valores correctos serán asignados.
La clase de prueba en el código de la figura ejemplifica una asignación
exitosa de un código de color no válido en un objeto PrivateShirt1.
La prueba en el código de la figura no sólo asigna un código de color válido
( R ) en la Línea 10 sino que también asigna un código de color no válido (Z) en
la línea 17. Esta clase puede asignar el código no válido porque la clase
PrivateShirt1 no está encapsulada propiamente para prevenirse contra código
no válido que desee asignarlo.

Los métodos pueden contener lógica de negocio adicional para validar la


acción a ser tomada. Por ejemplo, el método debería asegurar que el valor
pertenece a un rango correcto antes de ser asignado. En el código de la figura
se presenta otra versión de la clase PrivateShirt1. En este caso, antes de
asignar un valor, la clase asegura que el valor que recibe es válido.
El método setColorCode (Line 16) verifica que un código de color válido es
asignado usando una sentencia switch. Si se pasa al método setColorCode un
código de color no válido, se despliega un mensaje de error (Línea 25).

Después de que se ha escrito una clase encapsulando sus datos utilizando la


palabra clave private y los métodos getter y setter, se debe escribir una clase
que invoque los métodos getter y setter para acceder a los valores del objeto.

El código en la figura contiene una clase de prueba que invoca los métodos
getter y setter en la clase PrivateShirt2 encapsulada.
La clase de prueba en el código del ejemplo anterior no puede asignar un
código de color no válido (Z) en la Línea 16. Esta clase no puede asignar un
código no válido porque la clase PrivateShirt2Test está apropiadamente
encapsulada para prevenir que códigos no válidos sean asignados.

Figura 1:

2 public class PrivateShirt1 {

4 private int shirtID = 0; // Default ID for the shirt

5 private String description = "-description required-"; // default

7 // The color codes are R=Red, B=Blue, G=Green, U=Unset

8 private char colorCode = 'U';

9 private double price = 0.0; // Default price for all items

10 private int quantityInStock = 0; // Default quantity for all items

11

12 public char getColorCode() {

13 return colorCode;

14 }
15

16 public void setColorCode(char newCode) {

17 colorCode = newCode;

18 }

19

20 // Additional get and set methods for shirtID, description,

21 // price, and quantityInStock would follow

22

23 } // end of class

24

25

Figura 2:

2 public class PrivateShirt1Test {

4 public static void main (String args[]) {

6 PrivateShirt1 privShirt = new PrivateShirt1();

7 char colorCode;

9 // Set a valid colorCode

10 privShirt.setColorCode('R');

11 colorCode = privShirt.getColorCode();

12
13 // The PrivateShirtTest1 class can set a valid colorCode

14 System.out.println("Color Code: " + colorCode);

15

16 // Set an invalid color code

17 privShirt.setColorCode('Z');

18 colorCode = privShirt.getColorCode();

19

20 // The PrivateShirtTest1 class can set an invalid colorCode

21 System.out.println("Color Code: " + colorCode);

22 }

23 }

24

Figura 3:

2 public class PrivateShirt2Test {

4 public static void main (String args[]) {

5 PrivateShirt2 privShirt = new PrivateShirt2();

6 char colorCode;

8 // Set a valid colorCode

9 privShirt.setColorCode('R');

10 colorCode = privShirt.getColorCode();

11
12 // The PrivateShirtTest2 class can set a valid colorCode

13 System.out.println("Color Code: " + colorCode);

14

15 // Set an invalid color code

16 privShirt.setColorCode('Z');

17 colorCode = privShirt.getColorCode();

18

19 // The PrivateShirtTest2 class cannot set an invalid colorCode.

20 // Color code is still R

21 System.out.println("Color Code: " + colorCode);

22 }

23 }

24

Figura 4:

2 public class PrivateShirt2 {

4 private int shirtID = 0; // Default ID for the shirt

5 private String description = "-description required-"; // default

7 // The color codes are R=Red, B=Blue, G=Green, U=Unset

8 private char colorCode = 'U';

9 private double price = 0.0; // Default price for all items

10 private int quantityInStock = 0; // Default quantity for all items

11
12 public char getColorCode() {

13 return colorCode;

14 }

15

16 public void setColorCode(char newCode) {

17

18 switch (newCode) {

19 case 'R':

20 case 'G':

21 case 'B':

22 colorCode = newCode;

23 break;

24 default:

25 System.out.println("Invalid colorCode. Use R, G, or B");

26 }

27 }

28

29 // Additional get and set methods for shirtID, description,

30 // p1

2 public class PrivateShirt2 {

4 private int shirtID = 0; // Default ID for the shirt

5 private String description = "-description required-"; // default

7 // The color codes are R=Red, B=Blue, G=Green, U=Unset


8 private char colorCode = 'U';

9 private double price = 0.0; // Default price for all items

10 private int quantityInStock = 0; // Default quantity for all items

11

12 public char getColorCode() {

13 return colorCode;

14 }

15

16 public void setColorCode(char newCode) {

17

18 switch (newCode) {

19 case 'R':

20 case 'G':

21 case 'B':

22 colorCode = newCode;

23 break;

24 default:

25 System.out.println("Invalid colorCode. Use R, G, or B");

26 }

27 }

28

29 // Additional get and set methods for shirtID, description,

30 // p1

2 public class PrivateShirt2 {

3
4 private int shirtID = 0; // Default ID for the shirt

5 private String description = "-description required-"; // default

7 // The color codes are R=Red, B=Blue, G=Green, U=Unset

8 private char colorCode = 'U';

9 private double price = 0.0; // Default price for all items

10 private int quantityInStock = 0; // Default quantity for all items

11

12 public char getColorCode() {

13 return colorCode;

14 }

15

16 public void setColorCode(char newCode) {

17

18 switch (newCode) {

19 case 'R':

20 case 'G':

21 case 'B':

22 colorCode = newCode;

23 break;

24 default:

25 System.out.println("Invalid colorCode. Use R, G, or B");

26 }

27 }

28
29 // Additional get and set methods for shirtID, description,

30 // p1

2 public class PrivateShirt2 {

4 private int shirtID = 0; // Default ID for the shirt

5 private String description = "-description required-"; // default

7 // The color codes are R=Red, B=Blue, G=Green, U=Unset

8 private char colorCode = 'U';

9 private double price = 0.0; // Default price for all items

10 private int quantityInStock = 0; // Default quantity for all items

11

12 public char getColorCode() {

13 return colorCode;

14 }

15

16 public void setColorCode(char newCode) {

17

18 switch (newCode) {

19 case 'R':

20 case 'G':

21 case 'B':

22 colorCode = newCode;

23 break;

24 default:
25 System.out.println("Invalid colorCode. Use R, G, or B");

26 }

27 }

28

9.5 Uso de Encapsulación


9.5.6 La clase Elevator Encapsulada

El código en la figura ilustra cómo el programa Elevator previene problemas


en su uso. Contiene atributos privados y métodos públicos para acceder a los
valores de las variables atributo. Los métodos aseguran que a las variables
atributo no se le asignarán valores inapropiados.

El código en la figura ilustra cómo acceder las variables atributo en la clase


PrivateElevator2 usando métodos públicos.
Dado que la clase PrivateElevator2 no permite el manejo directo de los
atributos de la clase, la clase PrivateElevator2Test sólo puede invocar los
métodos para actuar sobre las variables atributo de la clase. Estos métodos
realizan validaciones que verifican que se usan los valores correctos antes de
completar la tarea, asegurando que el ascensor no haga nada inesperado.
Toda la lógica compleja en este programa está encapsulada en los métodos
públicos de la clase PrivateElevator2. El código en la clase de prueba, por lo
tanto, es fácil de leer y mantener. Este concepto es uno de los varios beneficios
de la encapsulación.

Ejemplo de Salida

La salida desde la clase PrivateElevator2 difiere cada vez que la clase es


ejecutada. A continuación se presenta un ejemplo de salida para la clase
Elevator2Test.

La clase Elevator2 no puede ser utilizada para realizar una tarea no válida,
dado que la clase Elevator2 está encapsulada apropiadamente. Por ejemplo, el
ascensor no puede moverse:

• hacia un piso no válido,


• cuando la capacidad excede 1000 libras, o
• cuando las puertas aún están abiertas.

Figura 1:

2 public class PrivateElevator2 {

4 private boolean doorOpen=false;

5 private int currentFloor=1;

6 private int weigth=0;

8 private final int CAPACITY=1000;

9 private final int TOP_FLOOR=5;

10 private final int BOTTOM_FLOOR=1;

11

12 public void openDoor() {

13 doorOpen=true;
14 }

15

16 public void closeDoor() {

17 calculateCapacity();

18

19 if (weight <=CAPACITY) {

20 doorOpen=false;

21 }

22 else {

23 System.out.println(“The elevator has exceeded capacity”);

24 System.out.println(“Doors will remain open until someone

exit”);

25 }

26 }

27

28 // En realidad, el ascensor debería tener sensores de peso

29 // para verificar el peso corriente, pero por simplicidad

30 // se utiliza un número aleatorio para representar el

31 // peso del ascensor

32

33 private void calculateCapacity() {

34 weight = (int) (Math.random() * 1500);

35 System.out.println("The weight is " + weight);

36 }

37
38 public void goUp() {

39 if (!doorOpen) {

40 if (currentFloor < TOP_FLOOR) {

41 currentFloor++;

42 System.out.println(currentFloor);

43 }

44 else {

45 System.out.println("Already on top floor.");

46 }

47 }

48 else {

49 System.out.println("Doors still open!");

50 }

51 }

52

53 public void goDown() {

54 if (!doorOpen) {

55 if (currentFloor > BOTTOM_FLOOR) {

56 currentFloor--;

57 System.out.println(currentFloor);

58 }

59 else {

60 System.out.println("Already on bottom floor.");

61 }

62 }
63 else {

64 System.out.println("Doors still open!");

65 }

66 }

67

68 public void setFloor(int desiredFloor) {

69 if ((desiredFloor >= BOTTOM_FLOOR) && (desiredFloor<=TOP_FLOOR))

70

71 while (currentFloor != desiredFloor) {

72 if (currentFloor < desiredFloor) {

73 goUp();

74 }

75

76 else {

77 goDown();

78 }

79 }

80 }

81 else {

82 System.out.println(“Invalid Floor”);

83 }

84 }

85

86 public int getFloor() {


87 return currentFloor;

88 }

89

90 public boolean getDoorStatus() {

91 return doorOpen;

92 }

93 }

Figura 2:

2 public class PrivateElevator2Test {

4 public static void main(String args[]) {

6 PrivateElevator2 privElevator = new PrivateElevator2();

8 privElevator.openDoor();

9 privElevator.closeDoor();

10 privElevator.goDown();

11 privElevator.goUp();

12 privElevator.goUp();

13 privElevator.openDoor();

14 privElevator.closeDoor();

15 privElevator.goDown();

16 privElevator.openDoor();
17 privElevator.goDown();

18 privElevator.closeDoor();

19 privElevator.goDown();

20 privElevator.goDown();

21

22 int curFloor = privElevator.getFloor();

23

24 if (curFloor != 5 && ! privElevator.getDoorStatus()) {

25 privElevator.setFloor(5);

26 }

27

28 privElevator.setFloor(10);

29 privElevator.openDoor();

30 }

31 }

32

Figura 3:

The weight is 453.

Already on bottom floor.

Doors still open!

Already on bottom floor.

The weight is 899

2
The weight is 974

9.6 Ejercicio 1: Escritura de Clases Encapsuladas

El objetivo de este ejercicio es escribir una clase que use la encapsulación para
ocultar datos a otros objetos.

9.6 Ejercicio 1: Escritura de Clases Encapsuladas


9.6.1 Tarea - Crear una Clase Encapsulada

En esta tarea, usted creará una clase encapsulada que contenga atributos
privados y los métodos getter y setter. Siga los siguientes pasos para crear una
clase:

1. Vaya al directorio object_oriented


2. Cree una clase de nombre DateOne que contenga tres atributos enteros:
day, month, y year.
• No agregue ningún método
• Haga los atributos públicos
3. Cree otra clase de nombre DateOneTest dentro de la clase
DateOneTest:
a. Cree un método main
b. Cree una variable que referencia a un objeto instancia de la clase
DateOne.
c. Use la variable de referencia para asignar los valores de los atributos
del objeto DateOne.
d. Despliegue los valores de las variables atributo para el objeto de la
clase DateOne creado.
4. Compile y ejecute su programa
5. Haga una copia del archivo DateOne.java, cuyo nombre sea
DateTwo.java (cambia el nombre de la clase de acuerdo a esto).
6. Defina como private los atributos en la clase DateTwo.
7. Haga una copia del archivo DateOneTest.java, cuyo nombre sea
DateTwoTest.java
8. Modifique las referencias a objetos de forma tal que refieran a objetos de
la clase DateTwo.
9. Compile y ejecute su programa. ¿Funciona correctamente?
10. Haga una copia del archivo DateTwo.java, cuyo nombre sea
DateThree.java.
11. En el archivo DateThree.java, agregue los métodos getter y setter
apropiados para cada atributo de la clase.
12. Agregue tres nuevos métodos al archivo DateThree.java:

• calcDaysInMonth - Usa el mes en el objeto DateThree y devuelve la


cantidad de días en ese mes.
• setDate - Tiene parámetros para cada atributo de la fecha, los verifica y
los asigna. Mientras realiza la verificación de la fecha, asegúrese que el
día es válido para ese mes particular. Por ejemplo, el 31 de Febrero
debería ser incorrecto. El método setDate debería tomar la fecha en este
orden: mm,dd,yyyy. Por ejemplo: 03,09,2001.
• displayDate - Despliega la fecha en el siguiente formato:
Today's Date is: 3/25/2001
13. Realice una copia del archivo DateTwoTest.java, cuyo nombre sea
DateThreeTest.java.
14. Modifique las referencias a los objetos en el archivo DateThreeTest.java.
15. Asigne valores válidos a las variables atributo day, month y year.
16. Invoque el método calcDaysInMonth y despliegue el resultado.
17. Invoque el método displayDate y despliegue la fecha.
18. Compile y ejecute su programa. ¿Ha obtenido el resultado que esperaba?
19. Asigne valores no válidos para las variables atributo day, month y year.
20. Compile y ejecute su programa. ¿Obtuvo los mensajes de error que
esperaba?

9.6 Ejercicio 1: Escritura de Clases


Encapsuladas
9.6.2 Tarea - Crear otra Clase
Encapsulada

En esta tarea, usted creará una clase encapsulada que contenga atributos
privates y métodos getter y setter. Siga los siguientes pasos para crear su clase.

1. Cree una clase de nombre Rectangle que represente un rectángulo


usando atributos privados width y height (ancho y alto respectivamente).
2. Cree los siguientes métodos públicos:
• El método getHeight devuelve el alto del rectángulo.
• El método getWidth devuelve el ancho del rectángulo.
• El método setHeight verifica que el alto sea mayor que cero y menor
que 30, y asigna el nuevo valor al alto.
• El método setWidth verifica que el ancho sea mayor que 0 y menor que
30 y asigna el nuevo valor al ancho.
• El método getArea devuelve el área (superficie) del rectángulo.
• El método getPerimeter devuelve el perímetro del rectángulo.
• El método draw dibuja el rectángulo usando el símbolo de asterisco.
3. Cree otra clase con el nombre RectangleTest que contenga un método
main para probar la clase Rectangle (invoque los métodos
correspondientes).
4. Prueba la clase Rectangle con valores para ancho y alto. ¿Ha obtenido
los resultados que esperaba?
5. Verifique la clase Rectangle con valores no válidos. ¿Ha obtenido los
resultados que esperaba?

9.6 Ejercicio 1: Escritura de Clases


Encapsuladas
9.6.3 Ejercicio 1: Resumen

Discusión – Tómese unos minutos para discutir acerca de las experiencias,


asuntos y descubrimientos realizados durante estos ejercicios.

• Experiencias
• Interpretaciones
• Conclusiones
• Aplicaciones

9.7 Descripción del Alcance de las Variables

Las variables declaradas dentro de un método, un constructor o un bloque de


código, no pueden ser usadas en otro lugar de la clase. El alcance de una
variable refiere a la extensión en que una variable puede ser usada dentro de
un programa. Por ejemplo, las variables atributo están declaradas al comienzo
de una clase, y por lo tanto, pueden ser usadas en toda la clase. Sin embargo,
una variable definida dentro de un método, sólo puede ser usada dentro de ese
método. Estas variables se denominan variables locales.
El gráfico muestra el alcance de una variable atributo de nombre age y una
variable local de nombre name.

Dado que la variable atributo age está definido fuera del método (Línea 5), la
variable age existe durante todo el tiempo de vida de un objeto basado en esta
clase (el alcance de la variable age es la clase entera).

Sin embargo, la variable name está declarada en el método displayName


(Línea 6), haciendo que la misma sólo exista durante el tiempo de vida del
método. Referenciar a la variable name dentro de otro método (como por
ejemplo el método getName (Líneas 15 a 18), causará un error de compilación.
Dado que las variables declaradas en un bucle o en las sentencias if, sólo son
válidas dentro del bucle o de la sentencia if, se deberá identificar el alcance de
la variable en todo momento, particularmente cuando se utilizan bucles y
sentencias if.

2 public class Person2 {


3

4 // begin scope of int age

5 private int age = 34;

7 public void displayName() {

9 // begin scope of String name

10 String name = "Peter Simmons";

11 System.out.println("My name is " + name + " and I am " + age );

12

13 } // end scope of String name

14

15 public String getName () {

16

17 return name; // this causes an error

18 }

19 } // end scope of int age

20

9.7 Descripción del Alcance de


las Variables
9.7.1 Cómo Aparecen en Memoria las
Variables Locales y las Variables de
Instancia

Las variables de atributo (o de instancia) se almacenan en un área de memoria


diferente a la utilizada para almacenar las variables locales. El gráfico ilustra
cómo se almacenan las variables locales y las variables de instancia.

public static void main (String args[]) {


int counter;
Shirt myShirt= new Shirt();
myShirt.shirtID=425566;
}

La variable shirtID es una variable de instancia que está contenida en un objeto


Shirt. Como se mencionó anteriormente, todos los atributos y métodos del
objeto se almacenan en la memoria heap. La variable counter es una variable
local declarada dentro de un método, constructor u otro bloque de código. La
variable counter está almacenada en la pila de la memoria dado que sólo se
necesita hasta que el método, el constructor u otro bloque de memoria que
contiene la variable counter se complete

1 package OrderEntry;

2 import OrderSupport.Person;

3 import OrderSupport.Address;

5 public class Customer extends Person {

6 private Address shipTo;

8 // public void initialize(Address addr) {

9 // homeAddress = addr;

10 // setShipTo(addr);

11 // }

12

13 Customer() {

14 setShipTo(homeAddress);

15 }

16

17 public Address getShipTo() {}

18 public void setShipTo(Address addr){}

19 }

9.8 Creación de Constructores


Los constructores son métodos con una estructura similar a la de los métodos y
son invocados automáticamente cuando se instancia un objeto de esa clase.
Los constructores son usados generalmente para inicializar valores en un
objeto. La sintaxis para un constructor es similar a la sintaxis de la declaración
de un método:

[modificadores] class NombreClase {


[modificadores] NombreConstructor([argumentos]) {
bloque_código
}
}

donde:

• Los [modificadores] representan varias palabras clave de la tecnología


Java que pueden modificar la forma en que los constructores son
accedidos. Los modificadores son opcionales.
• El NombreConstructor es el nombre de un método constructor. El
NombreConstructor debe ser igual al nombre en la declaración de la
clase.
• Los [argumentos] representan uno o más argumentos pasados al
constructor.
• El bloque_código representa una o más líneas de código opcionales
para el constructor.

El código en la figura ilustra una clase que contiene un constructor que


asigna un valor a una variable atributo.

La clase mostrada en el código de la figura tiene una llamada a un


constructor ConstructorShirt1 que acepta un valor char para inicializar el código
de color de ese objeto (Líneas 12 a 23). El constructor ConstructorShirt1
asegura que se haya pasado un código de valor válido antes que se asigne el
valor.

El código en la figura ilustra una clase que crea una variable que referencia
a un objeto ConstructorShirt1 e inicializa la variable atributo colorCode.

En ese ejemplo, se crea una variable de referencia a un objeto, cuyo nombre


es constShirt y se inicializa con un nuevo objeto ConstructorShirt1. Cuando se
crea el objeto ConstructorShirt1, el constructor del objeto ConstructorShirt1 es
llamado con un color de código (Línea 6).

Figura 1:

2 public class ConstructorShirt1 {

3
4 private int shirtID = 0; // Default ID for the shirt

5 private String description = "-description required-"; // default

7 // The color codes are R=Red, B=Blue, G=Green, U=Unset

8 private char colorCode = 'U';

9 private double price = 0.0; // Default price for all items

10 private int quantityInStock = 0; // Default quantity for all items

11

12 public ConstructorShirt1(char startingCode) {

13

14 switch (startingCode) {

15 case 'R':

16 case 'G':

17 case 'B':

18 colorCode = startingCode;

19 break;

20 default:

21 System.out.println("Invalid colorCode. Use R, G, or B");

22 }

23 }

24

25 public char getColorCode() {

26 return colorCode;

27 }

28 } // end of class
29

Figura 2:

2 public class ConstructorShirt1Test {

4 public static void main (String args[]) {

6 ConstructorShirt1 constShirt = new ConstructorShirt1('R');

7 char colorCode;

9 colorCode = constShirt.getColorCode();

10

11 System.out.println("Color Code: " + colorCode);

12 }

13 }

14
9.8 Creación de Constructores
9.8.1 Constructor por Defecto

Si el compilador del lenguaje Java encuentra una clase que no tiene un


constructor definido explícitamente (uno provisto por el programador), el
compilador Java inserta un constructor por defecto. El constructor por defecto
es creado solamente para cumplir los requerimientos del compilador. De hecho,
se ha utilizado el constructor por defecto durante este curso. Cuando se utiliza
la palabra clave new para instanciar un objeto cuya clase no contiene un
constructor definido explícitamente, la palabra clave new automáticamente
llama al constructor por defecto de la clase.

Shirt shirt1 = new Shirt();

El constructor Shirt() es el constructor por defecto insertado por el compilador,


para la clase Shirt. El constructor por defecto, sin embargo, nunca aparece en
el código corriente.
La declaración por parte del programador de su propio constructor (o sus
propios constructores) previene al compilador de insertar el constructor por
defecto en el código.
Dado que la clase ConstructorShirtOne ya tiene un constructor definido
explícitamente, la siguiente línea de código causará un error.

ConstructorShirt1 constShirt = new ConstructorShirt1();

Sin embargo, el programador puede crear su propio constructor sin argumentos,


agregando un constructor que no acepte argumentos en su clase. El código en
la figura muestra una clase que contiene un constructor con una declaración
similar al constructor por defecto.
En ese ejemplo, cuando el constructor por defecto (Línea 12) es invocado, la
variable atributo colorCode es inicializada con el valor 'R'.

2 public class DefaultShirt {

4 private int shirtID = 0; // Default ID for the shirt

5 private String description = "-description required-"; // default

7 // The color codes are R=Red, B=Blue, G=Green, U=Unset

8 private char colorCode = 'U';

9 private double price = 0.0; // Default price for all items

10 private int quantityInStock = 0; // Default quantity for all

items

11

12 public DefaultShirt() {

13 colorCode = 'R';

14 }

15

16 public char getColorCode() {

17 return colorCode;

18 }

19 } // end of class

20
9.8 Creación de Constructores
9.8.2 Constructores Sobrecargados

Al igual que los métodos, los constructores pueden ser sobrecargados. La


sobrecarga de constructores ofrece una variedad de formas en que los objetos
pueden ser creados e inicializados usando una sola clase.

En el código de la figura aparecen tres constructores.

La clase ConstructorShirt2 contiene tres constructores con declaraciones en las


Líneas 12, 16 y 29. El constructor ConstructorShirt2() no acepta argumentos e
inicializa la variable atributo colorCode con el valor 'R' (Líneas 12 a 14). El
constructor ConstructorShirt2(char startingCode) acepta un código de color
(Líneas 16 a 27). Finalmente, el constructor ConstructorShirt2(char
startingCode, int startingQuantity) acepta un código de color y una cantidad en
stock (Líneas 29 a 52).

El código en la figura crea tres objetos que usan los tres constructores en la
clase ConstructorShirt2.

La clase ConstructorShirt2Test crea tres objetos mientras invoca cada uno de


los tres constructores (Líneas 6 a 8).

Figura 1:

2 public class ConstructorShirt2 {

4 private int shirtID = 0; // Default ID for the shirt

5 private String description = "-description required-"; // default

7 // The color codes are R=Red, B=Blue, G=Green, U=Unset

8 private char colorCode = 'U';

9 private double price = 0.0; // Default price for all items

10 private int quantityInStock = 0; // Default quantity for all items

11

12 public ConstructorShirt2() {

13 colorCode = 'R';
14 }

15

16 public ConstructorShirt2 (char startingCode) {

17

18 switch (startingCode) {

19 case 'R':

20 case 'G':

21 case 'B':

22 colorCode = startingCode;

23 break;

24 default:

25 System.out.println("Invalid colorCode. Use R, G, or B");

26 }

27 }

28

29 public ConstructorShirt2 (char startingCode, int startingQuantity)

30 {

31

32 switch (startingCode) {

33 case 'R':

34 colorCode = startingCode;

35 break;

36 case 'G':

37

38 colorCode = startingCode;
39 break;

40 case 'B':

41 colorCode = startingCode;

42 break;

43 default:

44 System.out.println("Invalid colorCode. Use R, G, or

B");

45 }

46

47 if (startingQuantity > 0 && startingQuantity < 2000) {

48 quantityInStock = startingQuantity;

49 }

50

51 else {

52 System.out.println("Invalid quantity. Must be > 0 or <

2000");

53 }

54 }

55

56 public char getColorCode() {

57 return colorCode;58 }

59

60

61 public int getQuantityInStock() {

62 return quantityInStock;
63 }

64

65 } // end of class

66

Figura 2:

2 public class ConstructorShirt2Test {

4 public static void main (String args[]) {

6 ConstructorShirt2 constShirtFirst = new ConstructorShirt2();

7 ConstructorShirt2 constShirtSecond = new

ConstructorShirt2('G');

8 ConstructorShirt2 constShirtThird = new ConstructorShirt2('B',

1000);

10 char colorCode;

11 int quantity;

12

13 colorCode = constShirtFirst.getColorCode();

14 System.out.println("Object 1 Color Code: " + colorCode);

15

16 colorCode = constShirtSecond.getColorCode();

17 System.out.println("Object 2 Color Code: " + colorCode);

18
19 colorCode = constShirtThird.getColorCode();

20 quantity = constShirtThird.getQuantityInStock();

21 System.out.println("Object 3 Color Code: " + colorCode);

22 System.out.println("Object 3 Quantity on Hand: " + quantity);

23 }

24 }

25

9.9 Ejercicio 2: Uso de Constructores

El objetivo de este ejercicio es implementar los constructores de una clase.

9.9 Ejercicio 2: Uso de Constructores


9.9.1 Tarea - Crear y Usar Constructores

En esta tarea, usted creará una clase con constructores y creará objetos de la
clase usando sus constructores. Siga los siguientes pasos para crear la clase:

1. Vaya al directorio object_oriented.


2. Haga una copia del programa DateThree.java con el nombre
DateFour.java (cambie la clase de acuerdo a este nuevo nombre)
3. Agregue un constructor, sin argumentos, al archivo DateFour.java que
asigne el valor 1 al mes, el valor 1 al día y el valor 2002 al año.
4. Renombre el método setDate para que sea un constructor que acepte,
verifique y asigne los valores de mes, día y año.
5. Compile el archivo DateFour.java
6. Cree un programa DateFourTest.java que crea los objetos d1 y d2 de
tipo DateFour, donde d1 es creado usando el constructor sin argumentos
y d2 es creado usando el constructor con tres argumentos.
7. También, en la clase DateFourTest, invoque el método display para
desplegar el día, mes y año de los objetos d1 y d2.
8. Compile el archivo DateFourTest.java y verifique su código date.
La salida de la clase DateFourTest debería ser similar a la siguiente:
Today's Date is: 1/1/2001
Today's Date is: 3/9/1967

9.9 Ejercicio 2: Uso de


Constructores
9.9.2 Tarea- Crear Constructores para
Inicializar Objetos
En esta tarea, usted modificará una clase para usar constructores para
incializar objetos. Siga los siguientes pasos para crear sus clases:

1. Realice una copia del archivo Rectangle.java y asígnele el nombre


RectangleTwo.java.
2. Agregue los siguientes constructores a RectangleTwo.java:
• Un constructor sin argumentos que despliegue el mensaje "Default
rectangle created: width=25, height=10" y asigne el valor 25 a width y el
valor 10 a height.
• Un constructor que reciba dos enteros como argumentos (w y h) y
asigne a la variable atributo width el valor de w, y a la variable height el
valor de la variable h si y sólo si w y h tienen asignado un valor mayor
que 0 y menor que 30. Si w o h están fuera de rango, se debe desplegar
un mensaje de error apropiado. Adicionalmente, se debe desplegar un
mensaje indicando que el rectángulo fue creado con un ancho w y un
alto h.
3. Copie el archivo RectangleTest.java a un archivo con nombre
RectangleTwoTest.java
4. Compile el archivo RectangleTwo.java.
5. Modifique el programa RectangleTwoTest.java para que cree dos
rectángulos r1 y r2, tales que:
• r1 es creado con el constructor sin argumentos.
• r1 es dibujado inmediatamente después de ser creado.
• r2 es creado usando el constructor con argumentos.
• r2 es dibujado y el área es desplegada.
6. Recompile el archivo RectangleTwoTest.java y verifique el código del
rectángulo.
La salida de la clase RectangleTwoTest debería ser similar a la siguente:

Default rectangle created: width=25, height=10

This is the first rectangle:


*****************
*****************
*****************
*****************
*****************
*****************
*****************
*****************
*****************
*****************
Rectangle created: width=20, height=7
This is the second rectangle:
**************
**************
**************
**************
**************
**************
**************
The area is 140

9.10 Ejercicio 2: Resumen

Discusión – Tómese unos minutos para discutir sobre las experiencias,


asuntos y descubrimientos realizados durante este ejercicio.

• Experiencias
• Interpretaciones
• Conclusiones
• Aplicaciones

9.11 Preguntas de Repaso

Ahora le proponemos que resuelva el siguiente ejercicio para poner a prueba


sus conocimientos.

Defina por Verdadero o Falso las siguientes afirmaciones según corresponda.


¡Éxitos!
10 Los Arreglos

En éste capítulo Usted aprenderá a codificar arreglos de una dimensión,


asignar valores a un arreglo, pasar argumentos al método main y crear arreglos
de dos dimensiones

10.1 Repaso del Capítulo Anterior

Los principales temas del capítulo anterior son:

Uso de Encapsulación

En programación orientada a objetos, el término encapsulación hace referencia


al ocultamiento de los datos en una clase (una "cápsula" segura) y a hacerlos
disponibles sólo a través de métodos.
La encapsulación es importante porque hace que otros programadores puedan
usar la clase fácilmente y además proteger algunos de sus datos para que no
sean modificados en forma inapropiada.

Modificadores de Visibilidad

Indican los niveles de acceso que otros objetos tendrán sobre el método o el
atributo.

El Modificador public

Este modificador permite que la clase, los atributos y los métodos sean visibles
a cualquier objeto en el programa.
Para hacer que un atributo o método sea público, se agrega el modificador
public delante de la variable atributo o del método.

public int currentFloor=1;

public void setFloor(int desiredFloor) {


...
}

Si todos los atributos están definidos con acceso public, permite que los
valores puedan ser cambiados sin ninguna verificación de errores.

El Modificador private

El modificador private posibilita que los objetos de una clase dada, sus
atributos y las operaciones no sean accesibles desde otros objetos.
Para hacer que un atributo o método sea privado, se coloca el modificador
private delante del nombre de la variable o del método.
private int currentFloor=1;

private void calculateCapacity() {


...
}

En un programa ideal, la mayoría de los atributos de una clase deberían ser


privados. Los atributos privados no pueden ser visualizados o modificados
directamente desde fuera de la clase y por lo tanto, sólo pueden ser vistos o
modificados por los métodos de la propia clase.
Estos métodos deberían contener código y lógica de negocio para asegurar
que no se asignarán valores no apropiados a una variable para un atributo.

Interfaz e Implementación

Mientras que muchos ascensores alrededor del mundo pueden tener la misma
interfaz, sus implementaciones pueden ser diferentes.

Métodos get y set

Un objeto puede acceder a los atributos privados de un segundo objeto si este


segundo objeto tiene métodos públicos para cada una de las operaciones que
serán realizadas sobre los valores de un atributo. Por ejemplo, es común
proveer métodos públicos para asignar (set) y obtener (get) los valores de cada
atributo privado en una clase.
Los métodos pueden contener lógica de negocio adicional para validar la
acción a ser tomada.
Después de que se ha escrito una clase encapsulando sus datos utilizando la
palabra clave private y los métodos getter y setter, se debe escribir una clase
que invoque los métodos getter y setter para acceder a los valores del objeto.

Descripción del Alcance de las Variables

El alcance de una variable refiere a la extensión en que una variable puede ser
usada dentro de un programa.
Las variables atributo están declaradas al comienzo de una clase, y por lo tanto,
pueden ser usadas en toda la clase. Sin embargo, una variable definida dentro
de un método, sólo puede ser usada dentro de ese método. Estas variables se
denominan variables locales.

Una variable de instancia que está contenida en un objeto. Como se mencionó


anteriormente, todos los atributos y métodos del objeto se almacenan en la
memoria heap.
Las variable locales declarada dentro de un método, constructor u otro bloque
de código estan almacenadas en la pila de la memoria.

Constructores

Los constructores son métodos con una estructura similar a la de los métodos y
son invocados automáticamente cuando se instancia un objeto de esa clase.
La sintaxis para un constructor es similar a la sintaxis de la declaración de un
método:

[modificadores] class NombreClase {


[modificadores] NombreConstructor([argumentos]){
bloque_código
}
}

Constructor por Defecto

Si el compilador del lenguaje Java encuentra una clase que no tiene un


constructor definido explícitamente (uno provisto por el programador), el
compilador Java inserta un constructor por defecto.
Cuando se utiliza la palabra clave new para instanciar un objeto cuya clase no
contiene un constructor definido explícitamente, la palabra clave new
automáticamente llama al constructor por defecto de la clase.

Constructores Sobrecargados

La sobrecarga de constructores ofrece una variedad de formas en que los


objetos pueden ser creados e inicializados usando una sola clase.

10.3 Objetivos

Una vez completado este capítulo, usted debería ser capaz de:

• Codificar arreglos de una dimensión.


• Asignar valores a un arreglo usando el atributo length y un bucle.
• Pasar argumentos al método main para que sean usados en un
programa.
• Crear arreglos de dos dimensiones.

Este capítulo describe cómo usar arreglos que manejan múltiples valores en la
misma variable.

Discusión – Las siguientes preguntas son relevantes para comprender lo que


respecta al tema arreglos:

• ¿Cuál es el propósito de un arreglo?


• Un arreglo es una disposición ordenada de cosas, como por ejemplo una
lista ordenada. ¿Cuáles son las cosas en que las personas usan
arreglos en la vida diaria?
• Si un arreglo unidimensional es una lista de ítem, ¿qué es un arreglo
bidimensional?
• ¿Cómo hace usted para acceder a los ítem en un arreglo?
10.4 Recursos Adicionales

Recursos adicionales – Las siguientes referencias ofrecen información


adicional sobre los temas descritos en este capítulo:

• The Java Tutorial. [Online].


Disponible en:

http://java.sun.com/docs/books/
tutorial/java/nutsandbolts/
variables.html

http://java.sun.com/docs/books/
tutorial/java/nutsandbolts/
arrays.html

http://java.sun.com/docs/books/
tutorial/java/nutsandbolts/
variablesummary.html

10.5 La Declaración de Arreglos

Los arreglos se usan para agrupar objetos del mismo tipo. Permiten referenciar
un grupo de objetos con un nombre en común.

Usted puede declarar arreglos de cualquier tipo, sean tipos primitivos o clases.

char[] s;
Point[] p; // Donde Point es una clase

Cuando se declaran arreglos con los corchetes a la izquierda, estos se aplican


a todas las variables que se encuentren a su derecha.

En el lenguaje de programación Java, un arreglo es un objeto, incluso cuando


está compuesto de tipos primitivos. Al igual que en otros tipos de clases, la
declaración no crea al objeto en sí. En cambio, la declaración de un arreglo
crea una referencia que usted puede utilizar para referirse a él. La memoria
usada por los elementos del arreglo se ubica dinámicamente, ya sea por una
sentencia new o por una inicialización de un arreglo.

Puede declarar arreglos usando los corchetes después del nombre de la


variable.

char s[];
Point p[];
Esto es estándar para C, C++ y el lenguaje de programación Java. Esta forma
de declaración lidera frente a otras que pueden resultar difíciles de leer.

Resulta posible considerar una declaración teniendo el tipo en la


parte izquierda y el nombre de la variable a la derecha. Usted verá utilizados
ambos formatos, pero deberá decidirse por uno u otro para su propio uso. Las
declaraciones no especifican el tamaño actual del arreglo.

10.6 La Creación de Arreglos

Usted puede crear arreglos, como cualquier otro objeto, usando la palabra
reservada new. Por ejemplo, para crear un arreglo de tipo primitivo (char):

s = new char[26];

La primera línea crea un arreglo de 26 valores char. Luego de la creación, los


elementos del arreglo son inicializados con el valor por defecto ("\u0000" para
los caracteres). Debe llenar el arreglo para poder usarlo. (Figura )

Este código genera un arreglo en la memoria heap con las letras mayúsculas
del alfabeto inglés. La Figura muestra el arreglo en el heap.

El subíndice que indexa los elementos individuales del arreglo comienza en 0 y


debe ser mantenido en el dominio legal: mayor o igual que cero y menor que el
largo del arreglo. Cualquier intento de acceder a un elemento del arreglo fuera
de estos límites causa una excepción en tiempo de ejecución.
10.6 La Creación de Arreglos
10.6.1 Creación de un Arreglo Unidimensional

Imagine un programa en el que se desea almacenar las edades de 10


personas. Se pueden crear variables independientes para almacenar cada uno
de los 10 valores. Por ejemplo:

int ageOne = 27;


int ageTwo = 12;
int ageThree = 82;
int ageFour = 70;
int ageFive = 54;
int ageSix = 6;
int ageSeven = 1;
int ageEight = 30;
int ageNine = 34;
int ageTen = 42;

¿Cómo resolvería el caso en que haya que almacenar 1000 edades o 10000
edades? Notar que a medida que la cantidad de valores aumenta, el programa
se torna inmanejable.
El lenguaje de programación Java permite agrupar múltiples valores del mismo
tipo (listas) usando arreglos unidimensionales. Los arreglos son útiles cuando
se tienen datos relacionados (como por ejemplo las edades de varias
personas), pero no se quiere crear variables diferentes para almacenar cada
uno de los datos.
El gráfico ilustra varios arreglos unidimensionales.
Se pueden crear arreglos de distintos tipos, como por ejemplo: un arreglo de
tipos primitivos (como por ejemplo int), un arreglo de referencias a objetos
(como por ejemplo Shirt).
Cada parte del arreglo es un elemento. Si se declara un arreglo de 100 enteros,
entonces se dice que tiene 100 elementos. Se puede acceder a cada elemento
en el arreglo usando su ubicación o índice en el arreglo.

10.6 La Creación de Arreglos


10.6.2 Declaración de Arreglos Unidimensionales

Los arreglos son manejados por un objeto Array implícito (que no está
disponible en la API de Java, pero que está disponible para el código que se
escriba). Al igual que con otros objetos, se debe:

• declarar una referencia a un objeto arreglo,


• instanciar un objeto Array e
• inicializar el arreglo antes de usarlo.

La sintaxis para declarar un arreglo unidimensional es:

tipo [ ] identificador_arreglo;

donde:
• tipo representa al tipo de datos primitivo o al tipo del
objeto correspondiente a los valores que serán almacenados en el
arreglo.
• Los corchetes ([]) informan al compilador que se está declarando un
arreglo.
• identificador_arreglo es el nombre que se está asignando para referir al
arreglo.

El código en el siguiente ejemplo muestra la declaración de un arreglo de


nombre status cuyos elementos son de tipo char. También se declara un
arreglo con valores de tipo int y cuyo nombre es ages.

char [] status;
int [] ages;

El código en el siguiente ejemplo muestra la declaración de un arreglo de


referencias a objetos de la clase Shirt, cuyo nombre es shirts. También se
declara un arreglo de referencias a String, cuyo nombre es names.

Shirt [] shirts;
String [] names;

Cuando se declara un arreglo, el compilador y la Máquina Virtual Java no


conocen el tamaño del mismo, dado que sólo se declaró la variable referencia y
en ese momento aún no refiere a ningún objeto.
10.6 La Creación de
Arreglos
10.6.3 Instanciación de un Arreglo Unidimensional

Antes de que se pueda inicializar un arreglo, se debe instanciar el objeto con


un tamaño suficiente como para almacenar todos sus valores. La instanciación
de un arreglo consiste en definir su cantidad de elementos. La sintaxis utilizada
para instanciar un objeto Array es:

identificador_arreglo = new tipo [largo];

donde:

• identificador_arreglo es el nombre dado a la referencia al arreglo.


• tipo representa el tipo de datos primitivo o el tipo del objeto de los
valores a ser almacenados en el arreglo.
• largo representa el tamaño (cantidad de elementos) del arreglo.

El siguiente código muestra la creación de una instancia de un arreglo de


caracteres, cuyo nombre es status. También se muestra la instanciación de un
arreglo de elementos de tipo int, cuyo nombre es ages.

status = new char [20];


ages = new int [5];

El código a continuación muestra la instanciación de un objeto Array que


contiene referencias a String, cuyo nombre es names. También se muestra un
arreglo de referencias a objetos Shirt, cuyo nombre es shirts.

names = new String [7];


shirts = new Shirt [3];

Cuando se instancia un objeto Array, cada elemento de tipo primitivo es


inicializado con el valor cero del tipo especificado. En el caso del arreglo status,
que contiene valores de tipo char, cada valor es inicializado con el valor \u0000
(el carácter null en el conjunto de caracteres Unicode).
Para el arreglo de enteros de nombre ages, el valor inicial de cada elemento es
el valor entero 0. Para los arreglos de nombre names y shirts, las referencias a
los objetos son inicializadas con null.
10.6 La Creación de Arreglos
10.6.4 Inicialización de un Arreglo Unidimensional

Se puede completar el contenido de un arreglo después que ha sido creado. La


sintaxis para asignar valores a un arreglo es:

identificador_arreglo[índice] = valor;

donde:

• identificador_arreglo es el nombre que se le ha dado al arreglo.


• índice representa la ubicación en el arreglo donde el valor será ubicado.
• valor es el valor a ser asignado al elemento índice en el arreglo.

El siguiente código muestra cómo asignar los valores al arreglo ages:

ages[0] = 19;
ages[1] = 42;
ages[2] = 92;
ages[3] = 33;
ages[4] = 46;

El siguiente código muestra cómo asignar los valores al arreglo shirts:

shirts[0] = new Shirt();


shirts[1] = new Shirt('G');
shirts[2] = new Shirt('G', 1000);

Se utiliza la plabra clave new para crear los objetos Shirt. A cada posición del
arreglo se asigna la referencia al objeto creado.

10.6 La Creación de
Arreglos
10.6.5 Declaración, Instanciación e Inicialización
de Arreglos Unidimensionales

Si al momento de declarar un arreglo se conocen los valores que se desean


almacenar, se puede realizar su declaración, instanciación e inicialización en la
misma línea de código.
La sintaxis para esta declaración, instanciación e inicialización de un arreglo es:

tipo [] identificador_arreglo =
{lista _valores_o_expresiones _separados_por_comas};

donde:

• tipo representa el tipo de datos primitivo o el tipo del objeto para los
valores a ser almacenados en el arreglo.
• Los corchetes informan al compilador que se está declarando un arreglo.
• identificador_arreglo es el nombre que se le ha dado al arreglo.
• {lista _valores_o_expresiones _separados _por_comas} representa la
lista de valores que se desea almacenar en el arreglo o una lista de
expresiones cuyos resultados serán almacenados en el arreglo.

La siguiente sentencia combina la declaración, instanciación e inicialización del


ejemplo anterior para el arreglo ages:

int [] ages = {19, 42, 92, 33, 46};

Se pueden asignar los valores para cada objeto con una referencia que es
almacenada en un arreglo, usando el constructor para cada objeto. La siguiente
sentencia combina la declaración, instanciación e inicialización del ejemplo
anterior, en una sola sentencia:

Shirt [] shirts =
{ new Shirt(), new Shirt('G'), new Shirt('G',1000) };

No se puede declarar e inicializar un arreglo en líneas diferentes usando una


lista de valores separados por comas. El siguiente código ocasionará un error.

int [] ages;
ages = {19, 42, 92, 33, 46};

10.6 La Creación de Arreglos


10.6.6 Acceso a un Valor en un Arreglo

Cada elemento de un arreglo es accedido usando su índice. Para acceder a un


valor en un arreglo, se coloca el nombre del arreglo seguido por el número del
índice entre corchetes.
El código en el siguiente ejemplo, muestra cómo asignar un valor a un índice
particular en un arreglo:

status[0] = '3';
names[1] = "Fred Smith";
ages[1] = 19;
prices[2] = 9.99F;

El código siguiente muestra cómo recuperar el valor de un índice particular del


arreglo.

char s = status[0];
String name = names [1];
int age = ages[1];
double price = prices[2];

10.6 La Creación de
Arreglos
10.6.7 Almacenamiento de Arreglos
Unidimensionales en la Memoria
Los arreglos son objetos que se referencian por una variable de referencia a
objetos.
La figura compara cómo se almacenan en la memoria un arreglo con
valores de tipos primitivos (arreglo primitivo) en la memoria y un valor de un
tipo primitivo.
El valor de la variable size (un valor de tipo char) es L. El valor de sizes[] es
0x334009 y referencia a un objeto de tipo "array of char" con tres valores. El
valor de sizes[0] es el carácter S, el valor de sizes[1] es el character M y el
valor de sizes[2] es el carácter L.

10.6 La Creación de
Arreglos
10.6.8 Almacenamiento de Variables de Referencia
y Arreglos de Referencias en la Memoria

La siguiente figura ilustra cómo un arreglo de referencias a objetos es


almacenado en la memoria.
10.6 La Creación de
Arreglos
10.6.9 Ejercicio 1: Creación y Uso de un Arreglo
Unidimensional

El objetivo de este ejercicio es crear y usar arreglos

10.6 La Creación de Arreglos


10.6.10 Tarea - Crear una Clase Con un Arreglo
Unidimensional de Tipos Primitivos

En esta tarea, usted escribirá una clase de nombre VacationScale que


contendrá un arreglo de siete elementos, cada uno de los cuales contendrá una
cantidad de días de vacaciones correspondiente a la posición en el arreglo. La
cantidad de días de licencia que un empleado de DirectClothing, Inc. tiene, está
basado en la cantidad de años que hace
que el empleado trabaja para DirectClothing, Inc. En el gráfico se presenta la
escala de días de vacaciones para DirectClothing, Inc.
Siga los siguientes pasos para escribir su clase:

1. Vaya al directorio arrays.


2. Escriba una clase de nombre VacationScale que contenga un arreglo de
siete elementos (cada uno conteniendo la cantidad de días de
vacaciones relativa al índice en el arreglo). Por ejemplo, para el índice 0
debería contener el número 10 y para el índice 4 debería contener el
número 20.
3. Use un método de nombre setVacationScale para asignar cada uno de
los valores en el arreglo.
4. Use un método de nombre displayVacationDays que acepte un int
(yearOfService) y despliegue la cantidad de días de vacaciones que el
empleado recibe. Por ejemplo, si se pasa el valor 1 al método
displayVacationDays, entonces se deberá desplegar el número 15.
Sugerencia: Para recuperar un elemento de un arreglo, se puede utilizar
una variable entre los corchetes.
5. Compile y ejecute la clase usando la clase VacationScaleTest que se le
provee. La salida de su programa debería ser similar a:

Your vacation is: 15


Your vacation is: 20
Your vacation is: 25

10.6 La Creación de
Arreglos
10.6.11 Tarea - Crear una Clase con un Arreglo
Unidimensional de Objetos

En esta tarea, usted escribirá un programa que crea y asigna valores a un


arreglo de objetos Shirt.
1. Vaya al directorio arrays.
2. Escriba una clase de nombre ShirtArrayTest que:
• Cree tres objetos Shirt (y asigne sus valores usando un constructor). El
constructor para la clase Shirt es el siguiente:

public Shirt(int shirtID, String description, char colorcode,


double price, int quantityInStock)

• Ubique cada referencia al objeto Shirt en un arreglo.


• Use el arreglo para desplegar los valores del objeto Shirt usando el
metodo displayShirtInformation del objeto Shirt.
3. Compile y ejecute su clase usando la clase Shirt, que se le provee.
4.

10.6 La Creación de Arreglos


10.6.12 Ejercicio 1: Resumen

Discusión – Tómese unos minutos para discutir las experiencias, temas y


descubrimientos que haya realizado durante este ejercicio.

• Experiencias
• Interpretaciones
• Conclusiones
• Aplicaciones

10.7 La Creación de Arreglos de Referencia

Usted puede crear arreglos de objetos. Para esto, use la misma sintaxis:

p = new Point[10];

Esta línea crea un arreglo de 10 referencias del tipo Point. Sin embargo, no
crea 10 objetos del tipo Point. Estos se crean en otra etapa, como se muestra
en la Figura

Este código genera un arreglo en la memoria heap con cada elemento del
mismo, completado con una referencia a un objeto Point. La Figura muestra
el arreglo en el heap.
10.7 La Creación de Arreglos de Referencia
10.7.1 La Inicialización de Arreglos

Cuando se crea un arreglo, se inicializa cada elemento. En el caso del arreglos


de tipo char de la sección anterior, cada valor se inicializa con el carácter nulo
('\u0000').

En el caso del arreglo p, cada valor es inicializado con el valor null, indicando
que aún no refiere a un objeto Point. Después de la asignación p[0] = new
Point(), el primer elemento del arreglo se refiere a un objeto Point real.

El lenguaje de programación Java permite una forma más simple de crear


arreglos con valores iniciales.

String[] names = {
"Georgianna",
"Jen",
"Simon"
};

Este código es equivalente a:

String[] names;
names = new String[3];
names[0] = "Georgianna";
names[1] = "Jen";
names[2] = "Simon";

Puede usar esta forma abreviada para cualquier tipo, como por ejemplo:

MyDate[] dates = {
new MyDate(22, 7, 1964),
new MyDate(1, 1, 2000),
new MyDate(22, 12, 1964)
};
10.7 La Creación de Arreglos de Referencia
10.7.2 Los Arreglos Multidimensionales

El lenguaje de programación Java no provee arreglos multidimensionales como


lo hacen otros lenguajes. Dado que se puede declarar un arreglo que tenga
elementos de cualquier tipo de dato, se pueden crear arreglos de arreglos (y
arreglos de arreglos de arreglos y así sucesivamente). El siguiente ejemplo
muestra un arreglo de dos dimensiones.

int[][] twoDim = new int [4][];


twoDim[0] = new int[5];
twoDim[1] = new int[5];

El objeto que se crea en la primera llamada a new es un arreglo que contiene


cuatro elementos. Cada uno es una referencia nula a un elemento del tipo
arreglo de int. Se debe inicializar cada elemento separadamente, para que el
mismo apunte al arreglo que le corresponde.

Dada esta separación, puede crear arreglos de arreglos no rectangulares.


Puede inicializar los elementos de twoDim así:

twoDim[0] = new int[2];


twoDim[1] = new int[4];
twoDim[2] = new int[6];
twoDim[3] = new int[8];
Como este tipo de inicialización es tediosa y los arreglos de arreglos son la
forma más común, existe una forma corta de crear arreglos de dos
dimensiones. Por ejemplo, puede usar lo siguiente para crear un arreglo
compuesto de cuatro arreglos, de cinco enteros cada uno:

int[][] twoDim = new int[4][5];

10.7 La Creación de Arreglos de


Referencia
10.7.3 Los Límites del Arreglo

En el lenguaje de programación Java, todos los índices de los arreglos


comienzan en 0. El número de elementos en un arreglo es almacenado como
parte del objeto arreglo, en el atributo length. Si ocurre un acceso en tiempo de
ejecución fuera de los límites del arreglo, entonces habrá una excepción en
tiempo de ejecución.

Se utiliza el atributo length para iterar en un arreglo, como se muestra en el


Código de la Figura .

Si se usa el atributo length del arreglo, el mantenimiento del programa será


mucho más fácil porque no necesita conocer el número de elementos en el
arreglo en el momento de la compilación.
10.7 La Creación de Arreglos de
Referencia
10.7.4 El Uso de la Mejora de la
Iteración for

Iterar sobre un arreglo es una tarea muy común. La Java 2 Platform, Standard
Edition (J2SE™) versión 5.0 ha agregado una mejora en los bucles for para
hacer las iteraciones de arreglos más fáciles. El Código de la Figura
muestra esto.

Esta versión del bucle for puede ser leída como por cada elemento en la lista
list haga. El compilador es quien maneja el código de iteración.
10.7 La Creación de Arreglos de Referencia
10.7.5 La Redimensión de un Arreglo

Luego de ser creado, un arreglo no puede ser redimensionado. Sin embargo,


puede usar la misma variable de referencia para referir a un arreglo
completamente nuevo:

int[] myArray = new int[6];


myArray = new int[10];

En este caso, el primer arreglo se pierde a menos que otra referencia a él lo


esté reteniendo en alguna parte.

10.7 La Creación de Arreglos de Referencia


10.7.6 La Copia de un Arreglo

El lenguaje de programación Java provee un método especial en la clase


System, para copiar arreglos (arraycopy()). Por ejemplo, puede usar el método
arraycopy() como en la Figura
En este punto, el arreglo hold tiene el siguiente contenido: 1,2,3,4,5,6,4,3,2,1
10.8 Laboratorios

Laboratorio 5 - Los Arreglos

10.8 Laboratorios
10.8.1 Objetivos

Una vez completado este laboratorio, usted debería ser capaz de:

• Declarar, crear y manipular arreglos primitivos de una dimensión.


• Usar un arreglo para representar una relación de uno a muchos

10.8 Laboratorios
10.8.2 Ejercicio 1: El Uso de Arreglos Primitivos (Nivel
2)

Ejercicio 1 - El Uso de Arreglos Primitivos (Nivel 2)

10.8 Laboratorios
10.8.3 Ejercicio 1: El Uso de Arreglos Primitivos (Nivel 3)

Ejercicio 1 - El Uso de Arreglos Primitivos (Nivel 3)

10.8 Laboratorios
10.8.4 Ejercicio 2: El Uso de Arreglos para Representar Asociaciones
Uno a Muchos (Nivel 1)

Ejercicio 2 - El Uso de Arreglos para Representar Asociaciones Uno a Muchos


(Nivel 1

10.8 Laboratorios
10.8.5 Ejercicio 2: El Uso de Arreglos para Representar
Asociaciones Uno a Muchos (Nivel 2)

Ejercicio 2 - El Uso de Arreglos para Representar Asociaciones Uno a Muchos


(Nivel 2)

10.8 Laboratorios
10.8.6 Ejercicio 2: El Uso de Arreglos para Representar
Asociaciones Uno a Muchos (Nivel 3)

Ejercicio 2 - El Uso de Arreglos para Representar Asociaciones Uno a Muchos


(Nivel 3)
10.8 Laboratorios
10.8.7 Resumen del Ejercicio

Discusión – Dedique unos pocos minutos para discutir que experiencias,


temas o descubrimientos realizó durante el ejercicio de laboratorio.

• Experiencias
• Interpretaciones
• Conclusiones
• Aplicaciones

10.9 Ejercicios de Arreglos

Ahora le proponemos que resuelva los siguientes ejercicios para poner a


prueba sus conocimientos.

Ejercicios de Arreglos

Definir una clase conteniendo un método para cargar en un arreglo


unidimensional de 7 posiciones los días de la semana.

Definir una clase conteniendo un método para cargar en un arreglo


unidimensional los primeros 10 números enteros primos encontrados.

10.10 Ejercicio 2: Visualización de Arreglos Usando ObjectTool

El objetivo de este ejercicio es visualizar, usando ObjectTool, cómo se


organizan los arreglos en la memoria.

10.10 Ejercicio 2: Visualización de


Arreglos Usando ObjectTool
10.10.1 Tarea - Crear y Visualizar Arreglos Usando
ObjectTool

En esta tarea, usted utilizará la herramienta ObjectTool para crear un arreglo


de enteros y para descubrir cómo el arreglo se organiza en la memoria. Siga
los siguientes pasos para crear su arreglo:

1. Vaya al directorio arrays


2. Ejecute ObjectTool escribiendo java OT en la línea de comando.
3. Escriba lo siguiente en el campo Code Entry:

a. int intArray [];


b. intArray = new int[10];

. Un objeto de tipo Array of int con 10 elementos es creado.


4. Haga clic sobre la dirección en la columna con el valor de Local Value.
Se desplegará el Display Controller con un objeto de tipo Array of int.
5. Escriba lo siguiente en el campo Code Entry:

a. intArray[4] = 100;
b. intArray[2] = 50;

Los valores y los índices correspondientes en el arreglo intArray


cambiarán a 100 y 50 respectivamente.

10.10 Ejercicio 2: Visualización


de Arreglos Usando
ObjectTool
10.10.2 Tarea- Crear y Visualizar Otro Arreglo
Usando ObjectTool

En esta tarea, usará la herramienta ObjectTool para crear un arreglo de objetos


y para descubrir cómo se organiza el arreglo en la memoria. Siga los siguientes
pasos para crear su arreglo:

1. Ejecute ObjectTool digitando java OT en la línea de comando.


2. Escriba lo siguiente en el campo Code Entry:

Shirt shirtArray = new Shirt[5];

Un objeto de tipo Array de referencia a Shirt con cinco elementos es


creado.
3. Haga clic en la columa con el valor de Local Variable. Se desplegará el
Display Controller con un objeto de tipo Array de referencias a Shirt.
Note que cada índice en el arreglo tiene asignado el valor null, lo que
significa que la referencia al objeto no contiene una dirección de un
objeto Shirt existente.
4. Escriba lo siguiente en el campo Code Entry:

a. shirtArray[1] = new Shirt();


b. shirtArray[2] = new Shirt();

Los valores y los índices correspondientes en shirtArray ahora contienen


referencias a objetos Shirt
5. Haga clic sobre cualquiera de las nuevas referencias para visualizar el
objeto de tipo Shirt.

10.11 Ejercicio 2: Resumen

Discusión – Tómese unos minutos para discutir las experiencias, temas y


descubrimientos que ha realizado durante estos ejercicios.

• Experiencias
• Interpretaciones
• Conclusiones
• Aplicaciones

10.12 Asignar Valores a un Arreglo Usando el Atributo length y un


Bucle

Asignar valores a un arreglo es tedioso porque se debe escribir una línea de


código para asignar cada elemento en el arreglo. Sin embargo, se puede
utilizar un bucle para iterar a través de cada elemento en el arreglo para
asignar cada valor.

10.12 Asignar Valores a un Arreglo


Usando el Atributo length y un
Bucle
10.12.1 El Atributo length

Todos los objetos Array tienen una variable atributo length que contiene el largo
del arreglo.
Al largo de un arreglo también se le conoce como su límite. Los límites de un
arreglo, como por ejemplo ages[10], van desde ages[0] a ages[9], dado que
todos los arrays comienzan en el elemento con índice cero (0). La cantidad de
elementos en un arreglo es almacenada como parte del objeto arreglo. El largo
es usado por el software de la JVM para asegurar que cada acceso al arreglo
corresponde a un elemento válido del arreglo.
Si se intenta acceder en el código a un elemento del arreglo que no existe, tal
como ages[21] en un arreglo con largo 10, se recibirá un error.

10.12 Asignar Valores a un


Arreglo Usando el
Atributo length y un
Bucle
10.12.2 Asignación de Valores a un Arreglo
usando un Bucle

Se puede utilizar la variable atributo length en la clase Array en la parte


condicional de un bucle para iterar a través de un arreglo. El siguiente ejemplo
ilustra cómo iterar a través de un arreglo usando un bucle.

int [] myArray;
myArray = new int[100];

for (int count = 0; count < myArray.length; count++) {


myArray[count] = count;
}

En este ejemplo, cada índice en myArray es completado con un valor igual al


número del índice (count). Por ejemplo, myArray[10] es 10, myArray[99] es 99 y
así sucesivamente.

10.13 Ejercicio 3: Uso de Bucles y Arreglos

El objetivo de este ejercicio es crear e inicializar un arreglo usando un bucle.

10.13 Ejercicio 3: Uso de Bucles y


Arreglos
10.13.1 Tarea - Usar un Bucle para
Inicializar un Arreglo

En esta tarea, usted reescribirá el método setVacationScale en la clase


VacationScale para usar un bucle. Siga los siguientes pasos para reescribir su
método:

1. Vaya al directorio arrays.


2. Haga una copia del archivo Vacation Scale.java, con el nombre Vacation
Scale Two.java.
3. Edite el archivo VacationScaleTwo.java y cambie el nombre de la clase a
Vacation Scale Two.
4. Reescriba el método setVacation Scale para asignar cada uno de los
valores en el arreglo usando la variable atributo length y un bucle.
5. Compile y ejecute la clase usando la clase VacationScaleTwoTest que
se le prove.

10.14 Ejercicio 3: Resumen

Discusión – Tómese unos minutos para discutir las experiencias, temas y


descubrimientos que ha realizado durante estos ejercicios.

• Experiencias
• Interpretaciones
• Conclusiones
• Aplicaciones

10.15 Uso del Arreglo args en el Método main

Ya se ha presentado un ejemplo de un arreglo como un argumento del método


main. El arreglo args tiene una cantidad no especificada de objetos String:

public static void main (String args[]);

Cuando se pasan cadenas de caracteres a un programa desde la línea de


comandos, las cadenas de caracteres se colocan el arreglo args. Para usar
estas cadenas de caracteres, se las debe extraer desde el arreglo args y,
opcionalmente, convertirlas al tipo de datos apropiado.
El código de la figura ilustra el uso de cadenas de caracteres pasados al
método main en un programa. La clase ArgsTest extrae dos argumentos String
pasados desde la línea de comandos y los despliega.
10.15 Uso del Arreglo args en el
Método main
10.15.1 Conversión de Argumentos String
a Otros Tipos

El método main trata todo lo que se digita en la línea de comando como un


literal de tipo String. Si se desea usar la cadena de caracteres como la
representación de un número en una expresión, se debe convertir la cadena de
caracteres en su equivalente numérico. Cada tipo de datos tiene asociada una
clase con un método utilitario static para convertir
cadenas de caracteres a dicho tipo de datos (la clase Integer para el tipo int, la
clase Byte para el tipo byte, la clase Long para el tipo long, y así
sucesivamente). Por ejemplo, para convertir el primer argumento pasado al
método main a un tipo int:

int ID = Integer.parseInt(args[0]);

10.16 Ejercicio 4: Análisis del Arreglo args [ ]

El objetivo de este ejercicio es usar el arreglo args [ ] en el método main para


crear un juego para adivinar un número.

10.16 Ejercicio 4: Análisis del


Arreglo args [ ]
10.16.1 Tarea - Crear un Juego Usando el
Arreglo args [ ]

En esta tarea, usted escribirá un juego que acepte un argumento y despliegue


un mensaje asociado. Siga los siguientes pasos para escribir su juego:

1. Vaya al directorio arrays


2. Escriba una clase GuessingGame de forma tal que contenga un método
main que acepte un argumento que sea cualquier número entre 1 y 5 o
la palabra "help".
3. Si la clase GuessingGame recibe la palabra "help" como un argumento,
la clase GuessingGame despliega una lista de los argumentos válidos
para el juego (una pantalla de ayuda).
4. Si se ha ingresado alguno de los siguientes números: 1, 2, 3, 4 o 5,
entonces:

a. Genera un número al azar entre 1 y 5 usando el siguiente código


snippet:

randomNum =((int)( Math.random()*5)+1);

b. Compara el argumento con el número aleatorio.Si el valor del


argumento y el número aleatorio son iguales, despliega un mensaje al
usuario indicando que ha adivinado el número correcto. Si el valor del
argumento y el número aleatorio no son iguales, despliega el número
aleatoria y solicita al usuario que intente nuevamente.
5. Si se ingresa un número no válido (menor a 1 o mayor a 5), se despliega
un mensaje al usuario indicando que se ha ingresado un número
incorrecto.
6. Compile y ejecute su clase

10.17 Ejercicio 4: Resumen

Discusión – Tómese unos minutos para discutir las experiencias, asuntos y


descubrimientos que ha realizado durante estos ejercicios.

• Experiencias
• Interpretaciones
• Conclusiones
• Aplicaciones

10.18 Arreglos bidimensionales

Se pueden almacenar matrices de datos usando arreglos multi-dimensionales


(arreglos de arreglos, de arreglos, y así sucesivamente). Un arreglo
bidmensional es similar a una planilla con múltiples columnas (cada columna
representa un arreglo o lista de ítem) y múltiples filas.
La figura muestra un arreglo bidimensional.
10.18 Arreglos
bidimensionales
10.18.1 Declaración de un Arreglo Bidimensional

Los arreglos bidmensionales requiren un par de corchetes adicional. El proceso


para crear y usar arreglos bidmensionales es el mismo que para crear arreglos
unidimensionales. La sintaxis para declarar arreglos bidmensionales es:

tipo [][] identificador_arreglo;

donde:

• tipo representa el tipo de datos primitivos o el tipo del objeto para los
valores a ser almacenados en el arreglo.
• El par de corchetes[][] informa al compilador que se está declarando un
arreglo bidmensional.
• identificador_arreglo es el nombre que se le asignará al arreglo

El siguiente ejemplo declara un arreglo de arreglos para las ventas trimestrales


durante un periodo de cinco años.

int [][] yearlySales;


10.18 Arreglos
bidimensionales
10.18.2 Instanciación de un Arreglo Bidimensional

La sintaxis para la instanciación de un arreglo bidmensional es:

Identificador_arreglo = new tipo [cantidad_de_arreglos]


[largo];

donde:

• El identificador_arreglo es el nombre que se le ha asignado al arreglo


durante la declaración.
• cantidad_de_arreglos es la cantidad de arreglos dentro del arreglo.
• largo es el largo de cada arreglo en el arreglo.

El siguiente ejemplo instancia un arreglo de arreglos para la cantidad de ventas


trimestrales durante un periodo de 5 años.

// Instancia un arreglo bidmensional: 5 arreglos de cuatro elementos cada uno


yearlySales = new int[5][4];

Este arreglo bidimensional crea una matriz como la de la figura .


El arreglo yearlySales contiene cinco elementos de tipo arreglo int (cinco sub-
arreglos). Cada sub-arreglo tiene cuatro elementos, cada uno de los cuales
contiene las ventas por trimestre para un año dado
10.18 Arreglos
bidimensionales
10.18.3 Inicialización de un Arreglo
Bidimensional

Cuando se asignan valores en un arreglo bidmensional, se indica el número


correspondiente al índice en el arreglo usando un número para representar la
fila, seguido por un número para representar la columna. A continuación se
presentan algunos ejemplos de cómo asignar valores en el arreglo yearlySales:

yearlySales[0][0] = 1000;
yearlySales[0][1] = 1500;
yearlySales[0][2] = 1800;
yearlySales[1][0] = 1000;
yearlySales[2][0] = 1400;
yearlySales[3][3] = 2000;

El código anterior, genera el arreglo bidimensional de la figura .


10.19 Ejercicio 5: Creación y Uso de Arreglos Bidimensionales

El objetivo de este ejercicio es usar la herramienta ObjectTool para crear,


manejar y visualizar un arreglo bidimensional.

10.19 Ejercicio 5: Creación


y Uso de Arreglos
Bidimensionales
10.19.1 Tarea - Crear una Clase con un Arreglo
Bidimensional de Tipos Primitivos

En esta tarea, usted utilizará la herramienta ObjectTool para crear un arreglo


bidmensional de enteros y para descubrir cómo el arreglo se organiza en la
memoria. Siga los siguientes pasos para crear el arreglo:

1. Ejecute la herramienta ObjectTool digitando java OT desde la línea de


comando.
2. Escriba lo siguiente en el campo Code Entry:

a. int intArray [][];


b. intArray = new int[5][4];

Esto crea un objeto de tipo Array, con cinco elementos, cada uno de los
cuales referencia a un Array of int. Si se visualizara como una matriz, se
estaría creando un arreglo de cinco arreglos de cuatro elementos cada
uno, o una matriz similar a la de la figura .

1. Haga doble clic en la columna valor en Local Variables. Cada elemento


en el primer arreglo es una referencia a otro objeto de tipo Array of int.
2. Haga doble clic sobre la dirección del primer arreglo de referencias. Un
arreglo de cuatro enteros se desplegará.
3. Escriba lo siguiente en el campo "Code Entry":

a. intArray[0][0] = 100;
b. intArray[0][1] = 50;

Los valores de los dos primeros elementos del primer Array of int
cambian a 100 y 50 respectivamente. Ahora se tiene un arreglo
bidmensional similar al de la figura .
Realice las asignaciones que correspondan de forma tal que el arreglo
resultante sea el que se muestra en la figura .
10.20 Ejercicio 5: Resumen

Discusión – Tómese unos minutos para discutir las experiencias, temas y


descubrimientos que ha realizado durante estos ejercicios.

• Experiencias
• Interpretaciones
• Conclusiones
• Aplicaciones

10.21 Ejercicios de Arreglos Bidimensional

Ahora le proponemos que resuelva los siguientes ejercicios para poner a


prueba sus conocimientos.

Ejercicios de Arreglos Bidimensional

Definir una clase conteniendo un método para emular el sorteo de la


loteria. Utilizar un arreglo bidimensional en el que cada fila representa la
posición de sorteo, a cada posición le corresponderá un número del 0 al 99
respectivamente. Utilizar el método Math.random() para generar los números
en forma aleatoria.

Definir una clase conteniendo un método para realizar la carga de un arreglo


bidimensional en el que cada fila representa un mes, y los días que posee (no
se preveen años bisiestos).

Definir los métodos para leer los arreglos de los ejercicios anteriores en sus
respectivas clases.

10.22 Preguntas de Repaso

Ahora le proponemos que resuelva el siguiente ejercicio para poner a prueba


sus conocimientos.

Defina por Verdadero o Falso las siguientes afirmaciones según corresponda.


¡Éxitos!
11 Implementación de Herencia

En este módulo aprenderemos acerca de la herencia, podrá explicar el


concepto de abstracción e identificará las bibliotecas de clases

11.1 Repaso del Capítulo Anterior

Los principales temas del capítulo anterior son:

Creación de un Arreglo Unidimensional

El lenguaje de programación Java permite agrupar múltiples valores del mismo


tipo (listas) usando arreglos unidimensionales.
Se pueden crear arreglos de distintos tipos, como por ejemplo: un arreglo de
tipos primitivos (como por ejemplo int), un arreglo de referencias a objetos.
Se puede acceder a cada elemento en el arreglo usando su ubicación o índice
en el arreglo.

Declaración de Arreglos Unidimensionales

Al igual que con otros objetos, se debe:


• declarar una referencia a un objeto arreglo,
• instanciar un objeto Array e
• inicializar el arreglo antes de usarlo
La sintaxis para declarar un arreglo unidimensional es:

tipo [ ] identificador_arreglo;
O
tipo identificador_arreglo [ ];

Ejemplo:

char[] status;
int[] ages;

Inicialización de un Arreglo Unidimensional

Antes de que se pueda inicializar un arreglo, se debe instanciar el objeto con


un tamaño suficiente como para almacenar todos sus valores. La instanciación
de un arreglo consiste en definir su cantidad de elementos. La sintaxis utilizada
para instanciar un objeto Array es:

identificador_arreglo = new tipo [largo];


Ejemplo:
status = new char [20];
ages = new int [5];

La sintaxis para asignar valores a un arreglo es:

identificador_arreglo[índice] = valor;

Ejemplo1:
ages[0] = 19;
ages[1] = 42;
ages[2] = 92;

Ejemplo2:
shirts[0] = new Shirt();
shirts[1] = new Shirt('G');
shirts[2] = new Shirt('G', 1000);

Se utiliza la plabra clave new para crear los objetos Shirt. A cada posición del
arreglo se asigna la referencia al objeto creado.

Declaración, Instanciación e Inicialización de Arreglos Unidimensionales

Si al momento de declarar un arreglo se conocen los valores que se desean


almacenar, se puede realizar su declaración, instanciación e inicialización en la
misma línea de código.
La sintaxis para esta declaración, instanciación e inicialización de un arreglo es:

tipo [] identificador_arreglo = {lista _valores_o_expresiones


_separados_por_comas};
Ej. int[] ages = {19, 42, 92, 33, 46}

Acceso a un Valor en un Arreglo

Para acceder a un valor en un arreglo, se coloca el nombre del arreglo seguido


por el número del índice entre corchetes.
El código en el siguiente ejemplo, muestra cómo asignar un valor a un índice
particular en un arreglo:

status[0] = '3';
names[1] = "Fred Smith";
ages[1] = 19;
prices[2] = 9.99F;

El código siguiente muestra cómo recuperar el valor de un índice particular del


arreglo.

char s = status[0];
String name = names [1];
int age = ages[1];
double price = prices[2];

La Creación de Arreglos de Referencia

Usted puede crear arreglos de objetos. Para esto, use la misma sintaxis:

p = new Point[10];

Esta línea crea un arreglo de 10 referencias del tipo Point. Sin embargo, no
crea 10 objetos del tipo Point. Estos se crean en otra etapa.
En el caso del arreglo p, cada valor es inicializado con el valor null, indicando
que aún no refiere a un objeto Point. Después de la asignación p[0] = new
Point(), el primer elemento del arreglo se refiere a un objeto Point real.
El lenguaje de programación Java no provee arreglos multidimensionales como
lo hacen otros lenguajes. Dado que se puede declarar un arreglo que tenga
elementos de cualquier tipo de dato, se pueden crear arreglos de arreglos (y
arreglos de arreglos de arreglos y así sucesivamente). El siguiente ejemplo
muestra un arreglo de dos dimensiones.

int[][] twoDim = new int [4][];


twoDim[0] = new int[5];
twoDim[1] = new int[5];

Dada esta separación, puede crear arreglos de arreglos no rectangulares.


Puede inicializar los elementos de twoDim así:

twoDim[0] = new int[2];


twoDim[1] = new int[4];
twoDim[2] = new int[6];
twoDim[3] = new int[8];

Por ejemplo, puede usar lo siguiente para crear un arreglo compuesto de


cuatro arreglos, de cinco enteros cada uno:

int[][] twoDim = new int[4][5];

Los Límites del Arreglo

En el lenguaje de programación Java, todos los índices de los arreglos


comienzan en 0.
El número de elementos en un arreglo es almacenado como parte del objeto
arreglo, en el atributo length.
El siguiente ejemplo ilustra cómo iterar a través de un arreglo usando un bucle.

int [] myArray;
myArray = new int[100];
for (int count = 0; count < myArray.length; count++) {
myArray[count] = count;
}

La Copia de un Arreglo

El lenguaje de programación Java provee un método especial en la clase


System, para copiar arreglos (arraycopy()).

Arreglos bidimensionales

Los arreglos bidmensionales requiren un par de corchetes adicional. El proceso


para crear y usar arreglos bidmensionales es el mismo que para crear arreglos
unidimensionales. La sintaxis para declarar arreglos bidmensionales es:

tipo [][] identificador_arreglo;


Instanciación de un Arreglo Bidimensional

La sintaxis para la instanciación de un arreglo bidmensional es:

Identificador_arreglo = new tipo [cantidad_de_arreglos]


[largo];

Inicialización de un Arreglo Bidimensional

Cuando se asignan valores en un arreglo bidmensional, se indica el número


correspondiente al índice en el arreglo usando un número para representar la
fila, seguido por un número para representar la columna.

11.3 Objetivos

Una vez completado este capítulo, usted debería ser capaz de:

• Definir y probar el uso de la herencia.


• Explicar el concepto de abstracción.
• Identificar explícitamente las bibliotecas de clases que se utilizan en el
código.

Este capítulo describe los conceptos de herencia y abstracción y cómo


implementar la herencia y la abstracción en el lenguaje de programación Java.

Discusión – Las siguientes preguntas son relevantes para la comprensión del


concepto de herencia:

• El concepto de herencia refiere al hecho de trasmitir algo desde un


organismo a otro. ¿Cuáles son algunas de las características físicas que
usted ha heredado?
• ¿De quién heredó usted esas características?
• ¿De qué jerarquía de herencia es Usted?
• ¿Ha heredado usted características desde múltiples clases?
• ¿Qué entiende usted cuando se dice que algo es "abstracto"?
• ¿Qué cree usted que es una clase abstracta?

11.4 Recursos Adicionales

Recursos adicionales – Las siguientes referencias ofrecen información


adicional sobre los temas descritos en este capítulo:

• The Java Tutorial. [Online].


Disponible en:

http://java.sun.com/docs/books/
tutorial/java/IandI/subclasses.html

11.5 Herencia

Caso de Estudio – El siguiente caso de estudio se presenta para ilustrar


algunos principios de OOAD.

El gerente ejecutivo (CEO) de DirectClothing, Inc. ha decidido que la compañía


expandirá su línea de productos incluyendo para ello sombreros, medias y
pantalones. Por tal motivo, el CEO realizó ciertos cambios al sistema de
ingreso de órdenes de compra que usted desarrolló. Por lo tanto, usted ha sido
contratado por DirectClothing para modificar
el sistema de ingreso de órdenes de compra a efectos de incorporar estos tipos
de productos.

Antes de que usted pueda desarrollar las clases necesarias para modificar el
sistema, usted debe realizar un análisis del nuevo sistema y determinar que los
sombreros, las medias y los pantalones son los únicos nuevos objetos en el
sistema (el dominio del problema). Usted ha obtenido también la siguiente
información:

• Los sombreros y las medias se venderán en una única talla.


• Los pantalones serán diferentes para hombres y para mujeres.
• Los pantalones se venderán en color azul y canela, mientras que los
sombreros, medias y pantalones se venderán en color rojo, azul y verde.

Seguidamente, usted diseñará el nuevo sistema modelando las clases que


serán usadas para crear estos objetos. La figura muestra las nuevas clases
y la clase Shirt definida previamente.

Estas nuevas clases tienen varias de las características que tiene la clase Shirt.
Por ejemplo, todas las nuevas clases tienen un ID, un precio y una descripción.
También, todas estas clases tienen una operación para calcular un ID y para
desplegar sus datos. Sin embargo, el método display difiere en su
comportamiento.

Se puede evitar la duplicación de esta información en cada clase


implementando el concepto de herencia de orientación a objetos.
11.5 Herencia
11.5.1 Superclases y Subclases

La herencia permite a los programadores incluir los miembros comunes


(variables y métodos) en una clase y disponer de otras clases que hereden
estos miembros comunes desde esta nueva clase común.
La clase que contiene los miembros comunes a varias clases se denomina la
superclase o la clase padre. Las clases que heredan, o extienden, desde la
superclase se denominan subclases o clases hijas.
La herencia da como resultado una jerarquía de clases de tecnología Java
similar a la taxonomía que se encuentra en biología, como por ejemplo "La
Ballena Azul es una subclase de las Ballenas".

La figura ilustra una jerarquía de ballenas. "Sangre caliente" es un atributo


de la superclase mamíferos. La frase "respirar aire" representa alguna
operación que también es parte de la superclase mamífero. Las aletas (flippers)
y la aleta de la cola (fluyes) son atributos específicos de las ballenas, que
constituyen una subclase de la clase mamíferos
11.5 Herencia
11.5.2 Prueba de la Relación Entre Superclase y Subclase

En el lenguaje de programación Java, cada clase puede heredar los miembros


de una única clase, por lo que es muy importante considerar la mejor manera
de heredar y usarla sólo cuando sea completamente válido o inevitable.

La forma de verificar si una propuesta de herencia es válida, consiste en usar la


frase es un/a ( is a ). La frase "una Camisa es una Prenda de Vestir"
expresa una relación de herencia válida. La frase "un Sombrero es una Media"
expresa una relación de herencia no válida.
En el caso de estudio, las medias, los pantalones, los sombreros y las medias
son tipos de prendas de vestir (clothing). Por lo tanto, Clothing es un buen
nombre para la superclase de la subclase (tipo) de prenda de vestir.

La figura ilustra esta nueva jerarquía de clases


11.5 Herencia
11.5.3 Modelado de Superclases y Subclases

Después de haber reconocido la relación entre superclases y subclases, se


pueden remodelar las clases. Las superclases sólo contendrán las
características que son específicas a ellas. Las tres figuras muestran las
nuevas clases: la clase Shirt definida anteriormente y la subclase Clothing.
Las características comunes a todas las subclases están en la clase Clothing.
11.5 Herencia
11.5.4 Declaración de una Subclase

Se utiliza la palabra clave extends para indicar que una clase hereda desde
otra clase. Para declarar que una clase es subclase de otra, utilice la siguiente
sintaxis en su declaración de clase:

[modificador_clase] class identificador_clase extends


identificador_superclase

donde:

• modificador_clase es opcional y puede ser alguna de las siguientes


palabras clave: public, abstract o final. Si no se especifica ningún
modificador, el valor por defecto es default (o nivel de paquete) que
significa que sólo puede ser accedida por otras clases en el mismo
paquete.
• La palabra clave class indica al compilador que el bloque de código es
una declaración de clase.
• identificador_clase es el nombre que se le ha dado a la subclase.
• La palabra clave extends indica al compilador que es una subclase de
otra clase.
• identificador_superclase es el nombre de la superclase desde la que
extiende.

El código en la figura contiene una superclase Clothing.

La clase Clothing contiene los métodos usados por todas las subclases, tales
como la clase Shirt o la clase Pants. Cada subclase heredará estos métodos.
El código de la figura ejemplifica la clase Shirt modificada para que extienda
la clase Clothing.

La clase Shirt extiende la clase Clothing en la Línea 1. Notar que la clase Shirt
contiene las variables y los métodos que son específicos a un objeto Shirt.
11.6 Ejercicio 1: Creación de Superclases y Subclases
El objetivo de este ejercicio es que usted diseñe e implemente varias subclases
y superclases.

11.6 Ejercicio 1: Creación de


Superclases y Subclases
11.6.1 Tarea - Diseñar Subclases y Superclases

En esta tarea, usted diseñará subclases y una superclase usando la


información en los siguientes párrafos (como un grupo).

Publishing, Inc. tiene empleados en diferentes cargos. Estos cargos son:


Escritores Técnicos (Technical Writers), Dibujantes Gráficos (Graphic
Illustrators), Gerentes (Managers) y Editores (Editors). Publishing, Inc. quiere
crear un programa para realizar el seguimiento de la información sobre sus
trabajadores. Esta información consiste en: el nombre del trabajador, el nombre
del cargo, el ID del empleado (un número entero entre 1 y 10000) y el nivel (1,
2 o 3). Adicionalmente:

• Los gerentes deben tener una lista de los empleados a su cargo.


• Los Escritores Técnicos, los Dibujantes Gráficos y los Editores deben
tener asociada una lista de las competencias que tienen.
• Los Editores deben tener un valor indicando si prefieren edición
electrónica o en papel.
• Todas las subclases deben tener un método para desplegar la
información del tipo de empleado.

Para diseñar sus subclases y superclases:

1. Cree una jerarquía basada en la relación superclase y subclase para los


empleados en Publishing, Inc. Use la frase "es un/a" para probar la
jerarquía de clase
2. Modele la jerarquía de clase usando diagramas similares a los
presentados en este módulo.
3. Encapsule cada una de las clases, incluyendo los métodos get y set en
su diseño.

11.6 Ejercicio 1: Creación de


Superclases y
Subclases
11.6.2 Tarea - Desarrollar las Subclases y una
Superclase

En esta tarea, usted desarrollará las subclases y una superclase para la


jerarquía de empleados. Siga los siguientes pasos para crear sus clases:
1. Vaya al directorio inheritance.
2. Crear archivos con cada una de las clases determinadas en la primera
parte de este ejercicio.
3. Cree los métodos get y set, como se detallaron en su diseño y utilice las
construcciones de decisión para asegurar que no se asignen datos no
válidos.
4. Opcional. Intente usar métodos de las clases String y Character para
asegurar que el título y el nombre no contienen caracteres numéricos.
5. Opcional. Utilice el método Math.random para calcular un ID de
empleado úncio. (Sugerencia: vea el Módulo 10, "Creación y Uso de
Arreglos").
6. Cree una clase de prueba para probar la jerarquía de clases

11.7 Ejercicio 1: Resumen

Discusión – Tómese unos minutos para discutir las experiencias, temas y


descubrimientos realizados durante este ejercicio.

• Experiencias
• Interpretaciones
• Conclusiones
• Aplicaciones

11.8 Ejercicio 2: Visualización de Jerarquías de Clases Usando


ObjectTool

El objetivo de este ejercicio es identificar herencias (jerarquía de clases) en la


API de Java usando la herramienta ObjectTool.

11.8 Ejercicio 2: Visualización


de Jerarquías de Clases
Usando ObjectTool
11.8.1 Tarea - Identificar Jerarquías de Clases

En esta tarea, usted determina la jerarquía de clases para varias clases de la


API de Java que se utilizan con frecuencia. Para determinar la jerarquía de
clases para estas clases:
1. Ingrese lo siguiente en el campo Code Entry para crear una instancia de
un objeto de tipo Rectangle.

Rectangle myRectangle =
new Rectangle();

Esta sintaxis crea un objeto Rectangle desde la clase java.awt.Rectangle.


2. Haga clic sobre la dirección en el campo valor del panel Local Variables
para desplegar la pantalla Display Controller. El objeto Rectangle que
usted creó aparecerá en el Display Controller. La cabecera en la
descripción de este objeto contiene la jerarquía de clases para la clase
de este objeto. La clase Rectangle tiene varias superclases listadas en
el cabezal, incluyendo java.awt .geom. Rectangle2D y java.lang.Object.
3. Haga clic con el botón derecho sobre el objeto java.lang.Object para
desplegar un menú con opciones relacionados a la superclase.
4. Seleccione del menú, el item Show Class. Todas las clases en la API de
Java son subclases de la clase java.lang.Object. Estas clases heredan
todos los métodos de la
clase java.lang.Object.
5. Cree un objeto Shirt.
¿Cuáles son las superclases de la clase Shirt?

11.9 Ejercicio 2: Resumen

Discusión – Tómese unos minutos para discutir las experiencias, temas y


descubrimientos realizados durante estos ejercicios.

• Experiencias
• Interpretaciones
• Conclusiones
• Aplicaciones

11.10 Abstracción

Una de las características de una buena solución de programación usando


tecnología Java, es la generalidad de la parte superior de la jerarquía, dando
lugar a agregar nuevas clases en el futuro.

11.10 Abstracción
11.10.1 Abstracción en su Análisis y Diseño

El término Abstracción hace referencia a la creación de clases que son muy


generales y no contienen métodos con una implementación particular o código
en el cuerpo del método. Un buen ejemplo de una clase abstracta es la clase
Item. Un ítem es un concepto abstracto (generalmente no se va a una tienda y
se dice Quiero un ítem ), que puede referir a cualquier artículo que se venda
en la tienda. Sin embargo, todos los ítem tienen características similares en el
contexto de un sistema de ingreso de órdenes de compra, como por ejemplo un
ID o un método para desplegar la información acerca del ítem.

Las clases que son genéricas y no pueden ser definidas completamente, se


refieren como clases abstractas. Las clases que extienden una clase abstracta
pueden implementar los métodos vacíos de la clase abstracta con código
específico para la subclase.

Usted debería dedicar un tiempo adicional para asegurar que su análisis y su


diseño son lo suficientemente abstractos como para asegurar la flexibilidad
mencionada

11.11 Clases en la API de Java

Como programador novato en la tecnología Java, una de sus primeras tareas


será familiarizarse con las clases en la API de Java.

Las clases en el lenguaje de programación Java están agrupadas en paquetes


dependiendo de su funcionalidad. Por ejemplo, todas las clases relacionadas al
"core" del lenguaje de la tecnología Java están en el paquete java.lang, el cual
contiene las clases que son fundamentales para el lenguaje de programación
Java, tales como String, Math e Integer.

Hay dos tipos de categorías de clases en la API de Java: las clases que
pueden referenciarse implícitamente en cualquier código que el programador
escriba (tales como la clase System que se utiliza para acceder al método
println) y las clases que deben ser importadas o calificadas completamente.

11.11 Clases en la API de Java


11.11.1 Clases Disponibles Implícitamente

Las clases en el paquete java.lang pueden ser referidas implícitamente en


todos los programas. Este concepto significa que no se necesita mencionar el
nombre del paquete o de la clase cuando se utiliza la clase.

11.11 Clases en la API de


Java
11.11.2 Importación y Calificación de Clases
Muchas de las clases en la API de Java no están implícitamente disponibles
para cualquier programa. Se deben utilizar sentencias import o referir las clases
completamente (nombres de paquetes y clases) para todos los paquetes que
se quieran utilizar en el programa. Estos paquetes incluyen:

• El paquete java.awt contiene clases que definen la abstract windowing


toolkit (AWT). Este paquete es utilizado para construir y gestionar la
interfaz gráfica de usuario (graphical user interface (GUI) de la aplicación)
• El paquete java.applet contiene las clases que proven el comportamiento
específco de los applets.
• El paquete java.net contiene las clases para realizar las operaciones
vinculadas a las redes, tales como sockets y URLs.
• El paquete java.io contiene las clases para manejar la entrada salida
(input/output (I/O)), tales como lectura y escritura a discos duros.
• El paquete java.util contiene clases de utilidades para tareas, tales como
generación de números aleatorios, definición de propiedades del sistema
y uso de funciones vinculadas a fechas y calendarios.

Hay dos formas para que estas clases queden disponibles en un programa

• Importar las clases utilizando la sentencia import


• Referir a las clases usando los nombres de las clases totalmente
cualificado.

La Sentencia import

Se puede utilizar la sentencia import para acortar el código que debe


ingresarse para referenciar una clase de una API de Java explícitamente. La
sentencia import tiene dos formas:

import nombre_paquete. nombre_clase;


import nombre_paquete.*;

donde:

• La palabra clave import permite referir a las clases de la API de Java


usando un nombre abreviado.
• nombre_paquete es el nombre del paquete en que se ubica la clase.
• nombre_clase es el nombre de la clase específica que se desea importar.
Si no se especifica el nombre de la clase, todas las clases en el paquete
pueden ser usadas en el programa.
• Opcionalmente, se puede reemplazar el nombre de la clase con un
asterisco (*) para permitir que se pueda referir cada una de las clases en
el paquete por su nombre de clase.

Por ejemplo, la siguiente clase importa todo el paquete java.awt para permitir
que la clase acceda a cualquiera de las clases en el AWT GUI.

import java.awt.*;
public class MyPushButton1 extends Button {
// class statements
}

Especificación del Nombre Completamente Cualificado

En lugar de importar el paquete java.awt, se puede referir a la clave Button en


un programa como java.awt.Button. Por ejemplo, la siguiente es una
declaración de una clase que usa el nombre completamente cualificado
especificando su superclase. La sintaxis para el nombre completamente
cualificado es el siguiente:

nombre_paquete. nombre_clase

donde:

• nombre_paquete es el nombre del paquete donde la clase está ubicada.


• nombre_clase es el nombre de la clase para la clase específica a la que
se quiere referir.

Por ejemplo, la siguiente declaración de clase usa el nombre completamente


cualificado para extender la clase Button.

public class MyPushButton2 extends java.awt.Button {


// class statements
}
11.12 Preguntas de Repaso

Ahora le proponemos que resuelva el siguiente ejercicio para poner a prueba


sus conocimientos.

Defina por Verdadero o Falso las siguientes afirmaciones según corresponda.


¡Éxitos!
12 La Programación Orientada a Objetos

Este capítulo es el primero de tres que describen el paradigma de la orientación


de objetos (OO) y las funciones de la orientación a objetos del lenguaje de
programación Java.

12.1 Repaso del Capítulo Anterior

Los principales temas del capítulo anterior son:

Herencia

La herencia permite a los programadores incluir los miembros comunes


(variables y métodos) en una clase y disponer de otras clases que hereden
estos miembros comunes desde esta nueva clase común.

Superclases y Subclases

La clase que contiene los miembros comunes a varias clases se denomina la


superclase o la clase padre.
Las clases que heredan, o extienden, desde la superclase se denominan
subclases o clases hijas.
La herencia da como resultado una jerarquía de clases de tecnología Java
similar a la taxonomía que se encuentra en biología, como por ejemplo "La
Ballena Azul es una subclase de las Ballenas".

Prueba de la Relación Entre Superclase y Subclase

En el lenguaje de programación Java, cada clase puede heredar los miembros


de una única clase.
La forma de verificar si una propuesta de herencia es válida, consiste en usar la
frase es un/a.
La frase "una Camisa es una Prenda de Vestir" expresa una relación de
herencia válida.

Modelado de Superclases y Subclases

Las superclases sólo contendrán las características que son específicas a ellas.

Declaración de una Subclase

Se utiliza la palabra clave extends para indicar que una clase hereda desde
otra clase.
Utilice la siguiente sintaxis en su declaración de clase:
[modificador_clase] class identificador_clase extends
identificador_superclase

Ejemplo:

public class Short extends Clothing

Abstracción

El término Abstracción hace referencia a la creación de clases que son muy


generales y no contienen métodos con una implementación particular o código
en el cuerpo del método.
Las clases que son genéricas y no pueden ser definidas completamente, se
refieren como clases abstractas.
Las clases que extienden una clase abstracta pueden implementar los métodos
vacíos de la clase abstracta con código específico para la subclase.

Clases en la API de Java

Las clases en el lenguaje de programación Java están agrupadas en paquetes


dependiendo de su funcionalidad.

Clases Disponibles Implícitamente

Las clases en el paquete java.lang pueden ser referidas implícitamente en


todos los programas. Este concepto significa que no se necesita mencionar el
nombre del paquete o de la clase cuando se utiliza la clase.

Importación y Calificación de Clases

Muchas de las clases en la API de Java no están implícitamente disponibles


para cualquier programa. Se deben utilizar sentencias import o referir las clases
completamente (nombres de paquetes y clases) para todos los paquetes que
se quieran utilizar en el programa.

La sentencia import tiene dos formas:

import nombre_paquete. nombre_clase;


import nombre_paquete.*;

12.3 Objetivos

Una vez completado este capítulo, usted debería ser capaz de:
• Definir los conceptos de modelado: abstracción, encapsulación y
empaquetado.
• Describir por qué es posible reusar el código de las aplicaciones de la
tecnología Java.
• Usar los modificadores de acceso private y public como apropiados para
las líneas de encapsulación.
• Invocar el método de un objeto en particular.
• Usar la documentación en línea de la API de la tecnología Java.

Discusión – Las respuestas a las siguientes preguntas son relevantes con


respecto al material presentado en este capítulo.

• ¿Comprende usted el análisis y diseño del software?


• ¿Comprende usted el concepto de reutilización (o reúso) del código y del
diseño?
• ¿Cuáles son las funciones que el lenguaje de programación Java posee
y que lo hacen un lenguaje orientado a objetos?
• ¿Cómo define la expresión "orientado a objetos"?

12.4 Recursos Adicionales

Recursos adicionales – Las siguientes referencias ofrecen información


adicional sobre los temas descritos en este capítulo:

• The Java Tutorial. [Online]. Disponible en:

http://java.sun.com/docs/books/
tutorial/java/concepts/index.html

http://java.sun.com/docs/books/
tutorial/java/concepts/object.html

12.5 La Ingeniería de Software

La ingeniería de software es una disciplina difícil y con frecuencia desafiante.


Desde la mitad del siglo pasado, los científicos de computadoras, ingenieros de
software y arquitectos de sistemas han tratado de crear sistemas de software
fáciles, proveyendo código reutilizable. La Figura muestra una breve historia
de la ingeniería de software.

Algunos investigadores crearon lenguajes de programación para ocultar la


complejidad de los lenguajes de máquina y agregar procedimientos invocables
para manejar las operaciones comunes de los sistemas operativos, tales como
abrir, leer o escribir archivos.
Otros agruparon colecciones de funciones y procedimientos comunes, en
bibliotecas para, por ejemplo: calcular cargas estructurales en ingeniería
(NASTRAN), transmitir cadenas de bytes y caracteres entre computadoras en
una red (TCP/IP), acceder a los datos a través de un método de acceso
secuencial indexado (ISAM) y crear ventanas, gráficos u otros símbolos
gráficos de GUI en un monitor de mapeo de bits (X-Windows y Open Look).

Muchas de estas bibliotecas manejan datos con forma de registros abiertos,


como el struct del lenguaje C. El principal problema con las estructuras de
registros es que los diseñadores de bibliotecas no pueden separar la
implementación de los datos usados en los procedimientos. Esto hace difícil de
modificar la implementación de la biblioteca, sin
afectar el código del cliente porque el código está con frecuencia atado a
detalles particulares de la estructura de datos.

A finales de los años 80, la programación orientada a objetos (OOP) se volvió


particularmente popular con C++. Una de sus mayores ventajas era la
posibilidad de ocultar ciertos aspectos de la implementación, de manera que
las actualizaciones no afectaban el código del cliente (asumiendo que la
interfaz no cambiaba). Otra ventaja importante era que los
procedimientos estaban asociados con la estructura de datos. Una clase es la
combinación de los atributos de los datos y de los procedimientos (llamados
métodos).

Las bibliotecas de clases o grupo de programas y rutinas (toolkits) equivalen a


las bibliotecas de funciones. Ellas proveen (como bibliotecas funcionales)
clases para realizar muchas operaciones. Pero, con el uso de subclases, los
programadores pueden fácilmente extender dichas herramientas para sus
propias aplicaciones. Los frameworks proveen API que diferentes vendedores
pueden implementar y le permiten elegir aquel, que por su flexibilidad y
eficiencia, se ajusta mejor a su aplicación.

La tecnología Java es una plataforma que se extiende continuamente a través


de nuevas API y frameworks, como:

• la Java Foundation Classes/Swing (J.F.C./Swing) y otras tecnologías


J.F.C.,
• la arquitectura JavaBean (Arquitectura de componentes de tecnología
Java) y
• la Java DataBase Connectivity API (JDBC API). La lista de API de Java
es larga y está en constante crecimiento.
12.5 La Ingeniería de
Software
12.5.1 La Fase de Análisis y de Diseño

Hay cinco flujos de trabajo principales en el desarrollo de un proyecto de


software:

• La Captura de los Requerimientos.


• El Análisis.
• El Diseño.
• La Implementación .
• Las Pruebas.

Todos son importantes. Sin embargo, conviene, sobre todo, asignarles


suficiente tiempo a las fases de análisis y diseño.

Durante la fase de Análisis, usted define qué se supone que el sistema debe
cumplir. Esto se hace seleccionando un conjunto de actores (usuarios,
dispositivos y otros sistemas que interaccionan con el sistema propuesto) y
actividades que el sistema propuesto debe adecuar. También, la fase de
Análisis debe identificar los objetos del dominio (tanto físicos como
conceptuales) que el sistema propuesto manipulará y los comportamientos e
interacciones entre ellos. Estos comportamientos implementan las actividades
que el sistema propuesto debe soportar. La descripción de las actividades
debería ser lo suficientemente detallada como para crear una línea de base de
los criterios para la fase de Prueba.

Durante la fase de Diseño, usted debe definir cómo el sistema debe lograr sus
objetivos. Usted crea un modelo de actores, actividades, objetos y
comportamientos para el sistema propuesto. Use el Lenguaje Unificado de
Modelado (UML) como su herramienta de modelado.

12.5 La Ingeniería de Software


12.5.2 Un Ejemplo de Análisis y de Diseño

Este módulo usa el ejemplo de una compañía de embarque. Usted asume un


conjunto simple de requerimientos:

• El software debe soportar una compañía simple de embarque.


• La compañía de embarque mantiene una flota de vehículos que
transportan cajas.
• El peso de las cajas es el único factor importante al momento de la
carga del vehículo.
• La compañía de embarque es propietaria de dos tipos de vehículos:
camiones y barcas de transporte de río.
• Las cajas son pesadas en escalas cuantificadas en kilogramos. Sin
embargo, los algoritmos para calcular el poder del motor del vehículo
requieren que la carga total del vehículo sea medida en newtons.
• Use una GUI para mantener la ruta de las cajas agregadas al vehículo.
Debe generar varios reportes de los registros de la flota.

A partir de estos requerimientos, usted puede crear un diseño de alto


nivel:

• Los siguientes objetos deben estar representados en el sistema: una


compañía y dos tipos de vehículos.
• Una compañía es una agregación de múltiples objetos de vehículos.
• Existen otros objetos funcionales: varios reportes y pantallas de GUI.

12.5 La Ingeniería de Software


12.5.3 La Abstracción

El diseño de software se ha movido de las construcciones de bajo nivel, tales


como la escritura en código de máquina, hasta algunas de muy alto nivel. Hay
dos fuerzas interrelacionadas que guían el proceso: simplificación y abstracción.
La simplificación fue un trabajo realizado por los primeros diseñadores de
lenguajes, que consistió en construir
lenguajes de alto nivel, con las sentencias if y las iteraciones for, código
máquina especial. La abstracción es el proceso que oculta las
implementaciones privadas detrás de las interfaces públicas.

El concepto de abstracción conduce al uso de subrutinas (funciones) en


lenguajes de alto nivel y a las funciones y los datos dentro de objetos. En
niveles más altos, la abstracción conduce al desarrollo de frameworks y APIs.

12.5 La Ingeniería de
Software
12.5.4 Las Clases Entendidas como Plantillas para
Objetos

Así como un dibujante crea un proyecto para un dispositivo que puede ser
usado varias veces para construir dispositivos, una clase es un diseño de
software que se puede utilizar para instanciar (esto es crear) varios objetos
individuales.

Una clase define un conjunto de datos (atributos) que definen los objetos, así
como el conjunto de comportamientos o funciones (denominados métodos) que
manipulan el objeto o realizan interacciones entre objetos relacionados. Los
atributos y los métodos son llamados miembros.

Por ejemplo, un objeto vehículo en una aplicación de embarques debe realizar


el seguimiento de su carga máxima y actual a través de los métodos para
agregar una caja (con cierto peso) al vehículo.

El lenguaje de programación de la tecnología Java soporta tres funciones clave


de OOP: encapsulación, herencia y polimorfismo.
12.6 La Estructura de las Clases de Tecnología Java

La estructura de las clases de tecnología Java es la que se muestra en la


Figura

El <nombre_clase> puede ser cualquier identificador válido que denomine la


clase que declara. Hay varias palabras posibles para <modificador>, pero por
ahora, use solo public. Esto indica que la clase es accesible para el universo.
El cuerpo de la clase incluye el conjunto de atributos, constructores y métodos
del dato asociados con ella.
12.7 La Declaración de los Atributos

La declaración de un atributo de un objeto tiene la siguiente forma:

<modificador>* <tipo> <nombre> [ = <valor_inicial> ];

El <nombre> puede ser cualquier identificador válido y denomina el atributo que


está siendo declarado. Hay varios valores posibles para el <modificador>, pero
por ahora, use public o private. La palabra clave private indica que el atributo
es accesible sólo a los métodos dentro de la clase. El <tipo> del atributo puede
ser cualquier tipo primitivo (int, float, etc.) o alguna clase. (ver Figura )
12.8 La Declaración de los Métodos

Para definir los métodos, el lenguaje de programación Java usa un enfoque


similar al de otros lenguajes, particularmente C y C++. Toma la siguiente forma
básica:

<modificador>* <tipo_retorno> <nombre> ( <argumento>>* ) {


<statement>*
}

El <nombre> puede ser cualquier identificador válido, con algunas restricciones


basadas en los nombres que ya están en uso. El segmento del <modificador>
es opcional y puede contener varios modificadores diferentes incluyendo a
public, protected y private (aunque no está limitado a estos). El modificador de
acceso public indica que el método puede ser llamado desde otro código. El
método private indica que un método puede ser llamado sólo por los otros
métodos en la clase.

El método protected será descrito durante este curso.

El <tipo_retorno> indica el tipo de valor devuelto por el método. Si el método no


devuelve un valor, debe ser declarado void. La tecnología Java es rigurosa
acerca de los valores de retorno. Si el tipo de retorno en la declaración del
método es un int, por ejemplo, el método debe devolver un int desde todos los
posibles caminos de retorno (y puede ser invocado solamente en contextos que
esperan un int para ser devuelto). Use la sentencia return dentro de un método
para devolver un valor.

La lista de <argumento> permite que los valores de argumentos sean pasados


hacia el método. Los elementos de la lista están separados por comas y cada
elemento consiste en un tipo y un identificador

12.8 La Declaración de los Métodos


12.8.1 Ejemplos

El Código en la Figura muestra dos métodos para la clase Dog. El método


getWeight devuelve el dato del atributo weight (peso) y no usa parámetros.
El valor se devuelve usando la sentencia return (Línea 4). El método setWeight
modifica el valor de weight con el parámetro newWeight, no retorna ningún
valor. Este método usa una sentencia condicional para restringir el código del
cliente de modificar el peso (weight) a un número negativo o cero

12.9 El Acceso a los Métodos de los Objetos

En el ejemplo del apartado 2.6.1, usted vio la siguiente línea de código en el


método TestDog.main:

d.setWeight(42);
Esta línea de código le dice al objeto d (actualmente una variable, d,
manteniendo una referencia a un objeto del tipo Dog) que debe ejecutar su
método setWeight. Esto es llamado notación punto (o dot anotation). El
operador punto permite acceder a los atributos no privados y a los métodos
miembros de la clase.

Dentro de la definición de un método, no se necesita usar el punto para


acceder a los miembros locales. Por ejemplo, el método setWeight de la clase
Dog no usa el punto para acceder al atributo weight.

El Código de la Figura muestra el comportamiento de los métodos de la


clase Dog. Cuando se crea el objeto Dog, la instancia de la variable weight está
inicializada en 0. Por lo tanto, el método getWeight devuelve 0. En la Línea 6
de este código le modifica el peso a 42 (este es un argumento válido) y el
método setWeight modifica la variable weight. Sin embargo, modificar el peso a
-42 (Línea 9) es ilegal y el método setWeight no altera la variable weight.

12.10 El Ocultamiento de la Información

Suponga que tiene una clase MyDate que incluye los atributos: day, month y
year. La Figura muestra un diagrama de clases de una posible
implementación de la clase MyDate.
Una implementación simple permite el acceso directo a los atributos de los
datos, por ejemplo:

public class MyDate {


public int day;
public int month;
public int year;
}

El código del cliente accede directamente a los atributos y comete errores,


como por ejemplo (d refiere a un objeto MyDate):

d.day = 32;
// día inválido

d.month = 2; d.day = 30;


// plausible pero mal

d.day = d.day + 1;
// no realiza la validación de pasaje al siguiente mes

Para resolver el problema, oculte los atributos de los datos haciéndolos


privados y proporcione métodos de acceso para recuperarlos, getXyz()
(denominados getters) y métodos de asignación de almacenamiento, setXyz()
(denominados setters).

Estos métodos le permiten a la clase modificar la información interna, pero más


importante aún, verificar que los cambios requeridos son válidos. Por ejemplo:

MyDate d = new MyDate();


d.setDay(32);
//día inválido, devuelve false

d.setMonth(2); d.setDay(30);
//plausible pero mal, setDay devuelve false

d.setDay(d.getDay() + 1);
// retornara false si wrap around es necesario que ocurra
12.11 La Encapsulación
La encapsulación es la metodología para ocultar ciertos elementos de la
implementación de una clase, proporcionando una interfaz pública para el
software del cliente. Esta es una extensión del ocultamiento de la información
porque la información en los atributos de los datos constituye un elemento
significativo de la implementación de una clase.

Por ejemplo, el programador de la clase MyDate debe decidir si implementar


nuevamente la representación interna de una fecha, como la cantidad de días
desde el comienzo de alguna época. Esto podría hacer comparaciones y
cálculos de intervalos de tiempo de manera mucho más fácil. Dado que el
programador encapsuló los atributos ocultos detrás de
una interfaz pública, el programador puede hacer estos cambios sin afectar el
código del cliente. La Figura muestra su variación en la clase MyDate.

12.12 La Declaración de los Constructores

Un constructor es un conjunto de instrucciones diseñado para inicializar una


instancia. Los parámetros pueden ser pasados a un constructor de la misma
manera que a un método. La declaración básica toma la siguiente forma:

[<modificador>] <cnombre_clase> ( <argumento>* ) {


<sentencia>*
}

El nombre del constructor debe ser siempre el mismo que el de la clase.


Actualmente, los únicos modificadores válidos (<modificador>) para los
constructores son public, protected y private. La lista de <argumentos> es la
misma que la de las declaraciones de los métodos

Por ejemplo:

1 public class Dog {


2 private int weight;
3
4 public Dog() {
5 weight = 42;
6 }

La clase Dog tiene una instancia simple de la variable weight. El constructor


(sin parámetros) inicia la variable weight en 42. Los constructores pueden ser
declarados con parámetros. Esto se discutirá en el correr del curso.

1
2 public class PrivateShirt1 {
3
4 private int shirtID = 0; // Default ID for the shirt
5 private String description = "-description required-"; // default
6
7 // The color codes are R=Red, B=Blue, G=Green, U=Unset
8 private char colorCode = 'U';
9 private double price = 0.0; // Default price for all items
10 private int quantityInStock = 0; // Default quantity for all items
11
12 public char getColorCode() {
13 return colorCode;
14 }
15
16 public void setColorCode(char newCode) {
17 colorCode = newCode;
18 }
19
20 // Additional get and set methods for shirtID, description,
21 // price, and quantityInStock would follow
22
23 } // end of class
24
25
1
2 public class PrivateShirt1Test {
3
4 public static void main (String args[]) {
5
6 PrivateShirt1 privShirt = new PrivateShirt1();
7 char colorCode;
8
9 // Set a valid colorCode
10 privShirt.setColorCode('R');
11 colorCode = privShirt.getColorCode();
12
13 // The PrivateShirtTest1 class can set a valid colorCode
14 System.out.println("Color Code: " + colorCode);
15
16 // Set an invalid color code
17 privShirt.setColorCode('Z');
18 colorCode = privShirt.getColorCode();
19
20 // The PrivateShirtTest1 class can set an invalid colorCode
21 System.out.println("Color Code: " + colorCode);
22 }
23 }
24
1
2 public class PrivateShirt2 {
3
4 private int shirtID = 0; // Default ID for the shirt
5 private String description = "-description required-"; // default
6
7 // The color codes are R=Red, B=Blue, G=Green, U=Unset
8 private char colorCode = 'U';
9 private double price = 0.0; // Default price for all items
10 private int quantityInStock = 0; // Default quantity for all items
11
12 public char getColorCode() {
13 return colorCode;
14 }
15
16 public void setColorCode(char newCode) {
17
18 switch (newCode) {
19 case 'R':
20 case 'G':
21 case 'B':
22 colorCode = newCode;
23 break;
24 default:
25 System.out.println("Invalid colorCode. Use R, G, or B");
26 }
27 }
28
29 // Additional get and set methods for shirtID, description,
30 // price, and quantityInStock would follow
31
32 } // end of class
33

1
2 public class PrivateShirt2Test {
3
4 public static void main (String args[]) {
5 PrivateShirt2 privShirt = new PrivateShirt2();
6 char colorCode;
7
8 // Set a valid colorCode
9 privShirt.setColorCode('R');
10 colorCode = privShirt.getColorCode();
11
12 // The PrivateShirtTest2 class can set a valid colorCode
13 System.out.println("Color Code: " + colorCode);
14
15 // Set an invalid color code
16 privShirt.setColorCode('Z');
17 colorCode = privShirt.getColorCode();
18
19 // The PrivateShirtTest2 class cannot set an invalid colorCode.
20 // Color code is still R
21 System.out.println("Color Code: " + colorCode);
22 }
23 }
24

12.12 La Declaración de los


Constructores
12.12.1 El Constructor por Defecto

Cada clase tiene al menos un constructor. Si usted no escribe un constructor, el


lenguaje de programación Java le provee uno. Este constructor no posee
argumentos y tiene un cuerpo vacío. El constructor por defecto lo habilita a
crear las instancias de los objetos con new Xyz() De otro modo, usted puede
proveer un constructor para cada caso.

12.13 Estructura de los Archivos Fuente


Un archivo fuente de la tecnología Java tiene la siguiente forma:

[<declaración_paquete>]
<declaración_importacion>*
<declaración_clase>+

El orden de estos puntos es importante. Cualquier sentencia de importación


debe preceder todas las declaraciones de clases. Si usa una declaración de
paquetes, debe preceder ambas, las clases y las importaciones.

El nombre del archivo fuente tiene que ser el mismo que el nombre de la
declaración de la clase pública en ese archivo. Un archivo fuente puede incluir
más de una declaración de clase, pero sólo una puede ser declarada pública.
Si un archivo fuente no contiene clases públicas declaradas, el nombre del
archivo fuente no está restringido. Sin embargo, es una buena práctica tener un
archivo fuente para cada clase declarada y que el nombre del archivo sea
idéntico al nombre de la clase.

Por ejemplo, el archivo VehicleCapacityReport.java debería lucir como se


muestra en le Figura

12.14 Los Paquetes de Software


La mayoría de los sistemas de software son grandes. Es común agrupar clases
dentro de paquetes para el fácil mantenimiento del sistema. UML incluye el
concepto de paquetes en su lenguaje de modelado. Los paquetes pueden
contener clases, así como también otros paquetes, que integran una jerarquía
de paquetes.

Hay muchas maneras de agrupar clases dentro de paquetes significativos. No


hay una forma correcta o incorrecta, pero una técnica común es agruparlas por
su similitud semántica.

Por ejemplo, un sistema de software de embarque puede contener un conjunto


de objetos de dominio (así como la compañía y vehículos, cajas, destinos y aún
más) un conjunto de reportes y un conjunto de paneles GUI, que se usan para
crear la entrada principal de los datos de la aplicación. La GUI y los
subsistemas reports (reportes) dependen del paquete domain. Los paquetes
UML pueden ser útiles para modelar subsistemas u otros grupos de acuerdo
con sus necesidades. Todos estos paquetes están contenidos en el panel más
alto en jerarquía llamado shipping.

12.15 La Sentencia package

El lenguaje de programación de la tecnología Java le provee la sentencia


package como la forma de agrupar clases relacionadas. La sentencia package
tiene la siguiente forma:
package <nombre_paq_sup>[.<nombre_sub_paq>]* ;

Usted puede indicar qué clases, en un archivo fuente, pertenecen a un paquete


en particular, usando la sentencia package. Por ejemplo:

1 package shipping.domain;
2
3 // Clase Vehicle del sub-paquete 'domain' dentro del
4 // paquete de la aplicación 'embarque'
5 public class Vehicle {
6 ...
7 }

La declaración package, en caso de existir, debe estar al principio del archivo


fuente. Puede precederla con espacios en blanco y comentarios, pero nada
más. Solamente la declaración de un paquete está permitida. Esta se aplica a
todo el archivo fuente. Si un archivo fuente de la tecnología Java no contiene
una declaración de paquete, entonces la clase declarada en dicho archivo
pertenecerá al paquete sin nombre (por defecto).

Los nombres de los paquetes son jerárquicos y están separados por puntos. Es
usual que se escriban en minúscula. Sin embargo, el nombre de la clase
generalmente comienza con una mayúscula. Se puede, también, poner la
primera letra de cada palabra en mayúscula para resaltar palabras en el
nombre del archivo.
12.16 La Sentencia import

La sentencia import tiene las siguientes formas:

import <nombre_paq> [.<nombre_sub_paq>]. <nombre_clase> ;


o
import <nombre_paq> [.<nombre_sub_paq>].* ;

Cuando quiera usar paquetes, use la sentencia import para decirle al


compilador dónde encontrar las clases. En efecto, el nombre del paquete (por
ejemplo, shipping.domain) forma parte del nombre de las clases dentro del
paquete. Usted puede referenciar a la clase Company como
shipping.domain.Company o puede usar la sentencia de import y solamente el
nombre de la clase Company.

El siguiente es el fragmento de un archivo que utiliza sentencias import.

1 package shipping. reports;


2
3 import shipping. domain.*;
4 import java.util.List;
5 import java.io.*;
6
7 public class Vehicle Capacity Report {
8 private Company company ForReport;
9 ...
10 }

Cuando use una declaración de paquete, no necesita importar los mismos


paquetes o algún elemento del paquete. Recuerde que la sentencia import se
usa para hacer que las clases en otros paquetes sean accesibles a la clase
actual.

La sentencia import especifica la clase a la que quiere acceder. Por ejemplo, si


quiere solamente la clase Writer (del paquete java.io) incluida en el espacio de
nombres actual, entonces podría usar: import java.io.Writer;

Si quiere acceso a todas las clases dentro de un paquete, use "*". Por ejemplo,
para acceder a todas las clases del paquete java.io, use: import java.io.*;
12.17 El Esquema de Directorios y Paquetes

Los paquetes se guardan en un árbol de directorios; cada rama contiene el


nombre de un paquete. Por ejemplo, el archivo Company.class debería existir
en la estructura mostrada en la Figura
12.17 El Esquema de Directorios y
Paquetes
12.17.1 El Desarrollo

Es común estar trabajando en distintos proyectos de desarrollo a la vez.

Hay varios caminos para organizar sus archivos de desarrollo. Esta sección
describe una de estas técnicas.

La Figura muestra un ejemplo de jerarquía de directorios de desarrollo para


un proyecto de desarrollo. El elemento importante de esta jerarquía es que el
archivo fuente de cada proyecto esté separado de los archivos ya compilados
(.class).
12.17 El Esquema de Directorios y
Paquetes
12.17.2 La Compilación con la Opción
-d

Normalmente, el compilador Java ubica los archivos class en el mismo


directorio que los archivos fuente. Es posible cambiar la ubicación de los
archivos clase a otro directorio usando la opción -d del comando javac. La
manera más simple de compilar archivos dentro de un paquete es trabajar en el
directorio un nivel arriba del comienzo del paquete. (En este ejemplo, el
directorio src.)

Para compilar todos los archivos dentro del paquete shipping.domain y tener
las clases compiladas finalmente en su correcto directorio de paquete bajo
ShippingPrj/class/, escriba lo siguiente:

cd JavaProjects/ ShippingPrj/ src


javac -d ../classes shipping / domain/ *.java

12.17 El Esquema de Directorios y


Paquetes
12.17.3 El Despliegue
Usted puede realizar el despliegue de una aplicación en la máquina del cliente
sin manipular la variable de ambiente CLASSPATH del usuario. Usualmente,
es mejor crear un archivo ejecutable Java (JAR). Para hacer un archivo JAR
ejecutable, debe crear un archivo temporal que indique el nombre de la clase
que contiene su método main, como este:

Main-Class: mypackage.MyClass

A continuación, construya el archivo JAR con normalidad. Tenga en cuenta que


debe agregar una opción adicional, de forma tal que el contenido del archivo
temporal sea copiado dentro del archivo METAINF/ MANIFIEST.MF. Haga esto
usando la opción 'm', como en este ejemplo:

jar cmf tempfile MyProgram. jar

Finalmente, el programa puede ser ejecutado, usando un comando sencillo


como este:

java -jar /camino/al/archivo/ MyProgram. jar

El Despliegue de la Biblioteca

Algunas veces necesita desplegar el código de la biblioteca en un archivo JAR.


En esas situaciones, es posible copiar el archivo JAR dentro del subdirectorio
ext del directorio lib en el directorio principal de la JRE. Sea cuidadoso cuando
lo hace, porque las clases que despliega de esta manera obtienen privilegios
totales de seguridad y pueden causar problemas en tiempo de ejecución si
tienen conflictos de nombres con otras clases en el núcleo del JDK o si han
sido instaladas de esta manera.

12.18 La Recapitulación de Terminología

Descripción de algunos términos introducidos en este módulo:

• Clase - Una manera de definir nuevos tipos de objetos en el lenguaje de


programación Java. Puede ser considerada como un diseño (blueprint),
que es un modelo del objeto que se está describiendo.
• Objeto - Una instancia actual de una clase. Un objeto es lo que se
obtiene cada vez que se instancia una clase usando new.
• Atributo - Un dato de un objeto. Un atributo guarda información de un
objeto. Es también conocido como un miembro del objeto, una variable
de una instancia, una variable de instancia o un campo de datos.
• Método - Un elemento funcional de un objeto, una función o un
procedimiento.
• Constructor - Un método constructor usado para inicializar (o construir)
un nuevo objeto. Los constructores tienen el mismo nombre que las
clases.
• Paquete - Un grupo de clases, subpaquetes o ambos.

12.19 El Uso de la Documentación de la API de la Tecnología Java

Un conjunto de archivos HTML documentan la API suministrada. El diseño de


esta documentación es jerárquico. Por lo tanto, la página de inicio lista todos
los paquetes como hyperlinks (hipervínculos). Cuando usted selecciona el link
de un paquete en especial, las clases que son miembros de ese paquete son
listadas. Seleccionando el link de una clase desde la página de un paquete
presenta una página de información acerca de esta clase. La Figura
muestra una de estas clases.

La sección principal del documento de una clase incluye :

• La jerarquía de clases.
• Una descripción de la clase y su propósito general.
• Una lista de atributos.
• Una lista de constructores
• Una lista de métodos.
• Una lista detallada de atributos con descripciones.
• Una lista detallada de constructores con descripciones y lista de
parámetros formales.
• Una lista detallada de métodos con descripciones y listas de parámetros
formales
12.20 Laboratorios

Laboratorio 2 - La Programación Orientada a Objetos

12.20 Laboratorios
12.20.1 Objetivos

Una vez completado este laboratorio, usted debería ser capaz de:

• Utilizar la documentación de la API de Java para buscar una clase.


• Explorar información ocultándola y encapsulándola.
• Usar paquetes para organizar sus clases.

12.20 Laboratorios
12.20.2 Ejercicio 1: Uso de la Documentación de la API de
Java

Ejercicio 1 - Uso de la Documentación de la API de Java


12.20 Laboratorios
12.20.3 Ejercicio 2: Exploración del Concepto de
Encapsulación Versión 1 (Nivel 1)

Ejercicio 2 - Exploración del Concepto de Encapsulación Versión 1 (Nivel 1)

12.20 Laboratorios
12.20.4 Ejercicio 2: Exploración del Concepto de
Encapsulación Versión 1 (Nivel 2)

Ejercicio 2 - Exploración del Concepto de Encapsulación Versión 1 (Nivel 2)

12.20 Laboratorios
12.20.5 Ejercicio 2: Exploración del Concepto de
Encapsulación Versión 1 (Nivel 3)

Ejercicio 2 - Exploración del Concepto de Encapsulación Versión 1 (Nivel 3)

12.20 Laboratorios
12.20.6 Ejercicio 3: Exploración del Concepto de
Encapsulación Versión 2 (Nivel 1)

Ejercicio 3 - Exploración del Concepto de Encapsulación Versión 2 (Nivel 1)

12.20 Laboratorios
12.20.7 Ejercicio 3: Exploración del Concepto de
Encapsulación Versión 2 (Nivel 2)

Ejercicio 3 - Exploración del Concepto de Encapsulación Versión 2 (Nivel 2)

12.20 Laboratorios
12.20.8 Ejercicio 3: Exploración del Concepto de
Encapsulación Versión 2 (Nivel 3)

Ejercicio 3 - Exploración del Concepto de Encapsulación Version 2 (Nivel 3)

12.20 Laboratorios
12.20.9 Ejercicio 4: Creación de un Paquete Simple para el
Banco (Nivel 1))

Ejercicio 4 - Creación de un Paquete Simple para el Banco (Nivel 1)

12.20 Laboratorios
12.20.10 Ejercicio 4: Creación de un Paquete Simple para el Banco
(Nivel 2)

No hay Nivel 2 en este ejercicio. Si necesita sugerencias adicionales, utilice el


Nivel 3, el cual comienza en el apartado siguiente

12.20 Laboratorios
12.20.11 Ejercicio 4: Creación de un Paquete Simple para el
Banco (Nivel 3)

Ejercicio 4 - Creación de un Paquete Simple para el Banco (Nivel 3)

12.20 Laboratorios
12.20.12 Resumen del Ejercicio

Discusión – Dedique unos minutos para discutir qué experiencias, temas o


descubrimientos realizó durante el ejercicio de laboratorio.

• Experiencias
• Interpretaciones
• Conclusiones
• Aplicaciones

12.21 Preguntas de Repaso

Ahora le proponemos que resuelva el siguiente ejercicio para poner a prueba


sus conocimientos.

Defina por Verdadero o Falso las siguientes afirmaciones según corresponda.


¡Éxitos!
13 El Diseño de Clases

Este capítulo es el segundo de tres que describen el paradigma orientado a


objetos y las características de orientación a objetos del lenguaje de
Programación Java.

13.1 Repaso del Capítulo Anterior

Los principales temas del capítulo anterior son:

La Ingeniería de Software

Algunos investigadores crearon lenguajes de programación para ocultar la


complejidad de los lenguajes de máquina y agregar procedimientos invocables
para manejar las operaciones comunes de los sistemas operativos, tales como
abrir, leer o escribir archivos.
A finales de los años 80, la programación orientada a objetos (OOP) se volvió
particularmente popular con C++. Una de sus mayores ventajas era la
posibilidad de ocultar ciertos aspectos de la implementación, de manera que
las actualizaciones no afectaban el código del cliente (asumiendo que la
interfaz no cambiaba). Otra ventaja importante era que los procedimientos
estaban asociados con la estructura de datos. Una clase es la combinación de
los atributos de los datos y de los procedimientos (llamados métodos).
La tecnología Java es una plataforma que se extiende continuamente a través
de nuevas API y frameworks.

La Fase de Análisis y de Diseño

Hay cinco flujos de trabajo principales en el desarrollo de un proyecto de


software:

• La Captura de los Requerimientos.


• El Análisis.
• El Diseño.
• La Implementación .
• Las Pruebas.

Durante la fase de Análisis, usted define qué se supone que el sistema debe
cumplir.
En la fase de Análisis debe identificar los objetos del dominio (tanto físicos
como conceptuales) que el sistema propuesto manipulará y los
comportamientos e interacciones entre ellos.
Durante la fase de Diseño, usted debe definir cómo el sistema debe lograr sus
objetivos.

La Abstracción

La abstracción es el proceso que oculta las implementaciones privadas detrás


de las interfaces públicas.
El concepto de abstracción conduce al uso de subrutinas (funciones) en
lenguajes de alto nivel y a las funciones y los datos dentro de objetos. En
niveles más altos, la abstracción conduce al desarrollo de frameworks y APIs.

El Acceso a los Métodos de los Objetos

Dentro de la definición de un método, no se necesita usar el punto para


acceder a los miembros locales.

El Ocultamiento de la Información

Oculte los atributos de los datos haciéndolos privados y proporcione métodos


de acceso para recuperarlos, getXyz() (denominados getters) y métodos de
asignación de almacenamiento, setXyz() (denominados setters).

La Encapsulación

La encapsulación es la metodología para ocultar ciertos elementos de la


implementación de una clase, proporcionando una interfaz pública para el
software del cliente.

La Declaración de los Constructores


Un constructor es un conjunto de instrucciones diseñado para inicializar una
instancia.
La declaración básica toma la siguiente forma:

[<modificador>] <nombre_clase> ( <argumento>* ) {


<sentencia>*
}

El Constructor por Defecto

Cada clase tiene al menos un constructor. Este constructor no posee


argumentos y tiene un cuerpo vacío.

Estructura de los Archivos Fuente

Un archivo fuente de la tecnología Java tiene la siguiente forma:

[<declaración_paquete>]
<declaración_importacion>*
<declaración_clase>+

El orden de estos puntos es importante.


El nombre del archivo fuente tiene que ser el mismo que el nombre de la
declaración de la clase pública en ese archivo. Un archivo fuente puede incluir
más de una declaración de clase, pero sólo una puede ser declarada pública.
Si un archivo fuente no contiene clases públicas declaradas, el nombre del
archivo fuente no está restringido. Sin embargo, es una buena práctica tener un
archivo fuente para cada clase declarada y que el nombre del archivo sea
idéntico al nombre de la clase.

Los Paquetes de Software

Es común agrupar clases dentro de paquetes para el fácil mantenimiento del


sistema.
Los paquetes pueden contener clases, así como también otros paquetes, que
integran una jerarquía de paquetes.
No hay una forma correcta o incorrecta, pero una técnica común es agruparlas
por su similitud semántica.

La Sentencia package

El lenguaje de programación de la tecnología Java le provee la sentencia


package como la forma de agrupar clases relacionadas.
La sentencia package tiene la siguiente forma:

package <nombre_paq_sup>[.<nombre_sub_paq>]* ;

La Sentencia import
Cuando quiera usar paquetes, use la sentencia import para decirle al
compilador dónde encontrar las clases.

El Esquema de Directorios y Paquetes

Los paquetes se guardan en un árbol de directorios; cada rama contiene el


nombre de un paquete.

La Compilación con la Opción -d

Normalmente, el compilador Java ubica los archivos class en el mismo


directorio que los archivos fuente. Es posible cambiar la ubicación de los
archivos clase a otro directorio usando la opción -d del comando javac.

13.3 Objetivos

Una vez completado este capítulo, usted debería estar en condiciones de:

• Definir herencia, polimorfismo, sobrecarga, sobrescritura e invocación de


métodos virtuales.
• Usar los modificadores de acceso protected y el por defecto (package-
friendly)
• Describir los conceptos de constructor y de sobrecarga de métodos.
• Describir la operación de construcción y de inicialización de objetos en
forma completa.

Discusión – La siguiente pregunta es relevante para el material presentado en


este capítulo:

• ¿Cómo soporta el lenguaje de programación Java la herencia de objetos?

13.4 Recursos Adicionales

Recursos adicionales – Las siguientes referencias ofrecen información


adicional sobre los temas descritos en este capítulo:

• The Java Tutorial. [Online]. Disponible en:

http://java.sun.com/docs/books/
tutorial/java/javaOO/classes.html

http://java.sun.com/docs/books/
tutorial/java/javaOO/classdecl.html
http://java.sun.com/docs/books/
tutorial/java/javaOO/variables.html

http://java.sun.com/docs/books/
tutorial/java/javaOO/
constructors.html

13.5 Las Subclases

En programación, con frecuencia se modela algo (por ejemplo, un empleado) y


luego se necesita una versión más especializada del modelo original. Por
ejemplo, se quiere modelar a un gerente. Un Gerente es un empleado, pero
con características adicionales.

La Figura muestra los diagramas de clases UML que modelan las clases
Employee y Manager.

Los Códigos en las Figuras y muestran posibles implementaciones de las


clases Employee y Manager, de acuerdo con el modelo de la Figura .

Este ejemplo ilustra la duplicación de datos entre las clases Manager y


Employee. Adicionalmente, pueden existir métodos aplicables a ambas,
Employee y Manager. Por lo tanto, se necesita alguna manera de crear una
nueva clase a partir de otra existente. Esto se denomina subclase.

En los lenguajes orientados a objetos, se brindan mecanismos especiales para


que se pueda definir una clase a partir de otra definida previamente. La Figura
muestra un diagrama de clases UML, en el cual la clase Manager es una
subclase de la clase Employees.

El Código de la Figura muestra una implementación de la clase Manager,


que hereda de la clase Employee como se modela en la Figura .
13.5 Las Subclases
13.5.1 La Herencia Simple

El lenguaje de programación Java permite que una clase sea extendida


solamente de otra clase. Esta restricción se denomina herencia simple.

Los beneficios relativos de la herencia simple y múltiple son tema de largas


discusiones entre los programadores orientados a objetos. En el Capítulo 7,
“Características Avanzadas de las Clases", se analiza una característica del
lenguaje denominada interfaz que provee la mayoría de los beneficios de la
herencia múltiple y no padece ninguno de sus inconvenientes

La Figura muestra la clase base Employee y tres subclases: Engineer,


Manager y Secretary. La clase Director es una subclase de Manager.

La clase Employee contiene tres atributos (name, salary y birthDate) y también


un método (getDetails). La clase Manager hereda todos estos miembros y
especifica un atributo adicional, department y, además, el método getDetails.
La clase Director hereda todos los miembros de Employee y Manager y
especifica un atributo carAllowance y un nuevo método, increaseAllowance.

De forma similar, las clases Engineer y Secretary heredan los miembros de la


clase Employee y podrían especificar miembros adicionales (estos no se
muestran).
13.6 Los Controles de Acceso

Las variables y los métodos pueden estar en uno de los siguientes cuatro
niveles de acceso: public, protected, default o private. Las clases pueden estar
a nivel public o default.La Tabla en la Figura muestra los niveles de acceso

Una variable o método indicado con private está sólo accesible a los métodos
miembros de la clase en la que la variable o el método private se declararon.

Una variable, un método o una clase tienen accesibilidad por defecto (default)
si no poseen un modificador de acceso explícito, como parte de su declaración.
Esto le permite el acceso desde cualquier método perteneciente a las clases
que sean miembros del mismo paquete donde se ubica la variable, el método o
la clase. Se denomina, con frecuencia, package-friendly o package-private.

Una variable o método indicado con el modificador protected es, de hecho, más
accesible que uno con control de acceso por defecto. Un método o variable
protected es accesible desde los métodos definidos en las clases que
pertenezcan al mismo paquete y desde cualquier método de cualquier subclase.

Usted debe usar el acceso protegido cuando es apropiado para una subclase
de la clase, pero no para una clase sin relación.

Se accede, universalmente, a una variable o un método marcado con el


modificador public.
13.7 La Sobrescritura de Métodos

Además de crear una nueva clase basada en una anterior agregándole


características, se puede crear una nueva clase basada en una anterior
cambiando el comportamiento de la clase padre.

Si un método se define en una subclase de forma tal que el nombre, el tipo de


retorno y la lista de argumentos sean exactamente los mismos que en la clase
padre, entonces se dice que el nuevo método sobrescribe al anterior.
Considere estos métodos ejemplo en las clases Employee y Manager de la
Figura

La clase Manager tiene un método por definición, dado que hereda de la clase
Employee. Sin embargo, el método original fue reemplazado, o sobrescrito por
el de la versión de la clase hijo

Figura 1:

public class Employee {


protected String name;
protected double salary;
protected Date birthDate;

public String getDetails() {


return “Name: “ + name + “\n”
+ “Salary: “ + salary;
}
}
public class Manager extends Employee {
protected String department;

public String getDetails() {


return “Name: “ + name + “\n”
+ “Salary: “ + salary + “\n”
+ “Manager of: “ + department;
}
}

13.7 La Sobrescritura de
Métodos
13.7.1 Los Métodos sobrescritos No Pueden
Ser Menos Accesibles
Recuerde que el nombre del método y el orden de los argumentos de un
método hijo deben ser idénticos a los del método en la clase padre para que el
método sobrescriba la versión del padre. Además, un método que sobrescribe
no puede ser menos accesible que el método sobrescrito. Considere la
situación no válida de la Figura

La semántica del lenguaje de programación Java hace que se ejecute la


versión hijo del método p2.method(), pero, dado que el método es declarado
privado, p2 (declarado como parent) no puede acceder a él. De ese modo, la
semántica del lenguaje se quebranta.

13.7 La Sobrescritura de
Métodos
13.7.2 La Invocación de los Métodos
Sobrescritos

Un método de una subclase puede invocar a un método de la superclase


usando la palabra clave super. La palabra clave super refiere a la superclase
de la clase en la cual la
palabra clave se usa. Se utiliza para referenciar las variables o los métodos
miembros de la superclase. Esto puede ser llevado a cabo usando la palabra
clave super como se muestra en la Figura
Una llamada de la forma super.method() invoca al método y el comportamiento
que se produce es el mismo que el que se produciría si el método fuera
aplicado directamente a un objeto de la clase padre. El método no tiene por
qué estar definido en la clase padre; podría ser heredado de alguna clase
superior en la jerarquía.

Figura 1:

public class Employee {


private String name;
private double salary;
private Date birthDate;

public String getDetails() {


return "Name: " + name + "\nSalary: " + salary;
}
}

public class Manager extends Employee {


private String department;

public String getDetails() {


// call parent method
return super.getDetails()
+ "\nDepartment: " + department;
}
}
13.8 El Polimorfismo

Decir que un Manager es un Employee no es la manera más conveniente de


describir la relación entre estas dos clases. Recuerde que el Manager tiene
todos los miembros, tanto atributos como métodos, de la clase padre Employee.
Esto significa que cualquier operación que puede ser aplicada a un Employee,
también puede serlo para un Manager. Si el Employee tiene el método
getDetails, entonces la clase Manager también.

Podría parecer irreal crear un Manager y asignar deliberadamente la referencia


a él a una variable de tipo Employee. Sin embargo, esto es posible y hay
razones de por qué se debería querer lograr este efecto.

Un objeto tiene solamente una forma (la que se le da cuando es construido).


Sin embargo, una variable es polimórfica porque puede referenciar a objetos
con formas diferentes.

El lenguaje de programación Java, como la mayoría de los lenguajes


orientados a objetos, permite referenciar a un objeto con una variable que tiene
uno de los tipos de las clases padre del objeto. Por lo tanto, se puede decir:

Employee e = new Manager(); //legal


Usando la variable e de esta manera, se podría acceder sólo a las partes del
objeto que son propias de Employee; las partes específicas de Manager están
ocultas. Esto ocurre porque para el compilador, e es un empleado, no un
Manager. Por consiguiente, lo que se describe a continuación no está permitido:

// Intento ilegal de asignar un atributo Manager e.department = "Sales";


// la variable está declarada como de tipo Employee,
// aún cuando el objeto Manager tiene ese atributo

13.8 El
Polimorfismo
13.8.1 La Invocación de Métodos Virtuales

Piense que se da la siguiente situación:

Employee e = new Employee();


Manager m = new Manager();

Si solicita e.getDetails() y m.getDetails(), se invocan comportamientos


diferentes. El objeto Employee ejecuta la versión de getDetails(), asociada con
la clase Employee y el objeto Manager ejecuta la versión de getDetails(),
asociada con la clase Manager. Es menos obvio lo que ocurre si se tiene:

Employee e = new Manager();


e.getDetails();

o algo similar, tal como un argumento de un método general o un ítem desde


una colección heterogénea.

De hecho, el comportamiento está asociado con el objeto al cual la variable


hace referencia en tiempo de ejecución. El comportamiento no está
determinado por el tipo de la variable al momento de la compilación.

Este es un aspecto del polimorfismo y una característica importante de los


lenguajes orientados a objetos. Este comportamiento se conoce, con
frecuencia, como una invocación a un método virtual.

En el ejemplo anterior, el método e.getDetails() se ejecuta desde el tipo real del


objeto, un Manager. Si usted es un programador C++, debería tener en cuenta
que hay una importante distinción entre el lenguaje de programación Java y
C++. En C++, se obtiene este comportamiento sólo si se explicita al método
como virtual en el código fuente. En lenguajes orientados a objetos puros, esto
no es normal. El lenguaje C++ hace esto para incrementar la velocidad de
ejecución.

13.8 El Polimorfismo
13.8.2 Las Colecciones Heterogéneas

Se pueden crear colecciones de objetos que tengan una clase en común. Estas
colecciones se denominan homogéneas. Por ejemplo:

MyDate[] dates = new MyDate[2];


dates[0] = new MyDate(22, 12, 1964);
dates[1] = new MyDate(22, 7, 1964);

En el lenguaje de programación Java existe una clase denominada Object, por


lo tanto, se pueden hacer colecciones de cualquier tipo de elementos dado que
todas las clases extienden de la clase Object. Estas colecciones se denominan
homogéneas. Una colección heterogénea tiene objetos diferentes. En los
lenguajes orientados a objetos, se pueden crear colecciones de varios
elementos. Todos tienen una clase ancestro en común: la clase Object. Por
ejemplo:

Employee [] staff = new Employee[1024];


staff[0] = new Manager();
staff[1] = new Employee();
staff[2] = new Engineer();

Incluso, se puede escribir un método que ordene los empleados por edad o
salario, sin importar si algunos de esos son empleados (Employee) o son
gerentes (Manager).
13.8 El Polimorfismo
13.8.3 Los Argumentos Polimórficos

Se puede escribir métodos que aceptan un objeto genérico (en este caso, la
clase Employee) y trabajar adecuadamente sobre objetos de cualquier
subclase de este objeto.

Podría crear un método en una clase de la aplicación que reciba un empleado y


lo compare con un cierto tope de salarios para determinar los impuestos
correspondientes a ese empleado. Usando las características polimórficas esto
se puede resolver como se muestra en la Figura

Esto es legal porque un Manager es un Employee. Sin embargo, el método


findTaxRate sólo tiene acceso a los miembros (variables y métodos) que se
definen en la clase Employee
13.9 El Operador instanceof

Dado que es posible pasar objetos usando referencias a su clase padre,


algunas veces se podría querer saber cuál es el objeto corriente que se tiene.
Este es el propósito del operador instanceof. Supóngase que la jerarquía de
clases herencia se extiende de manera tal que se tenga lo siguiente:

public class Employee extends Object


public class Manager extends Employee
public class Engineer extends Employee

Si se recibe un objeto usando una referencia de tipo Employee, se debería


determinar si se trata de un Manager o de un Engineer. Esto se puede verificar
usando instanceof como se muestra en la Figura
13.9 El Operador instanceof
13.9.1 La Conversión de Objetos

En circunstancias en las que se tiene que recibir una referencia a una clase
padre y se ha determinado, efectivamente, que el objeto es una subclase en
particular usando el operador instanceof, se puede acceder a la funcionalidad
completa del objeto convirtiendo (casting) la referencia.

Si no se realiza la conversión, un intento de ejecutar e.getDepartment() fallará


dado que el compilador no podrá ubicar el método getDepartment en la clase
Employee.

Si no se realiza la verificación usando instanceof, se corre el riesgo de que la


conversión no funcione. Generalmente, cualquier intento de convertir una
referencia al objeto está sujeto a varias validaciones

• La conversión hacia arriba (ascendente) en la herencia de clases está


siempre permitida y, de hecho, no requiere el operador de conversión.
Es posible hacerlo simplemente con una asignación.
• Para realizar una conversión hacia abajo (descendente), el compilador
debe aceptar que la conversión es, al menos, posible. Por ejemplo,
cualquier intento de convertir una referencia a Manager a una referencia
a Engineer no está permitido, porque un Engineer no es un Manager. La
clase para la cual la conversión tiene lugar, debe ser
una una subclase del tipo actual de la referencia.
• Si el compilador permite la conversión, entonces el tipo del objeto es
verificado en tiempo de ejecución. Por ejemplo, si se omitió la
verificación instanceof en el código, y el
objeto a ser convertido no es un objeto del tipo al que será convertido,
entonces ocurrirá un error en tiempo de ejecución (excepción). Las
excepciones son un tipo de error que ocurre en tiempo de ejecución.
Son el tema de un módulo más adelante.

13.10 Sobrecarga de Métodos

En algunas circunstancias, se podría necesitar escribir varios métodos en la


misma clase que hacen básicamente el mismo trabajo pero que reciben
diferentes argumentos. Por ejemplo, un método simple del que se desea que
despliegue una representación textual de su argumento. Este método podría
llamarse println().

Ahora suponga que necesita un método diferente que despliegue cada uno de
los tipos int, float y String. Esto es razonable, dado que cada tipo de datos
requiere un formato diferente y, probablemente, distinto manejo. Para ello se
podrían crear tres métodos, denominados printInt(), printFloat() y printString(),
respectivamente. Sin embargo esto resultaría tedioso.

El lenguaje de programación Java, así como también otros tantos lenguajes de


programación, permite utilizar un mismo nombre para más de un método. Esto
funciona solamente si en el momento de realizar la llamada al método se puede
determinar a cual de ellos se necesita. En el caso de los tres métodos para
desplegar un valor, esta distinción está
basada en la cantidad y en el tipo de los argumentos.

Reutilizando el nombre de los métodos, se tendrían entonces los siguientes


métodos.

public void println(int i)


public void println(float f)
public void println(String s)

Cuando se escribe un código que llama a alguno de estos métodos, se


selecciona el método apropiado de acuerdo con el tipo del argumento o de los
argumentos que se le suministra.

Existen dos reglas que se aplican a la sobrecarga de métodos:

• Las listas de argumentos deben diferir.


Las listas de argumentos en la sentencia de llamada al método deben
diferir lo suficiente, de forma tal que se pueda determinar, sin
ambigüedades, el método apropiado a llamar. Las promociones
normales de tipos podrían ser aplicadas (por ejemplo, float a double), lo
cual podría causar confusiones en el momento de la determinación del
método.
• Los tipos de retorno pueden ser diferentes.
El tipo de retorno de los métodos puede ser diferente, pero no es
suficiente que la única diferencia entre los métodos sea esta. Las listas
de argumentos de los métodos sobrecargados debería distinguirse.

13.10 Sobrecarga de
Métodos
13.10.1 Métodos que Usan Argumentos Variables

Un caso de variación en la sobrecarga consiste en utilizar una cantidad


diferente de argumentos del tipo. Un ejemplo de ello lo constituye un método
que calcula el promedio de un conjunto de enteros. Se podrían definir los
métodos presentados en la Figura

Estos métodos podrían ser invocados como se muestra en la Figura

Estos tres métodos sobrecargados comparten la misma funcionalidad. Sería


bueno consolidar estos tres métodos en uno solo. J2SE versión 5.0 provee una
funcionalidad, llamada varargs o argumentos variable, que permite escribir un
método más genérico. (Figura )

Este nuevo método varargs puede ser invocado de la misma manera que el
conjunto de métodos sobrecargados. Fíjese que el argumento nums es un
arreglo de objetos del tipo int[]. Esto permite al método iterar sobre los
elementos y obtener la cantidad de ellos (esto es, el largo del arreglo).
13.11 La Sobrecarga de Constructores

Cuando un objeto es instanciado, el programa debería ser capaz de proveer


múltiples constructores basados en los datos para el objeto que está siendo
creado. Por ejemplo, un sistema de nómina debería ser capaz de crear un
objeto Employee cuando conoce todos los datos básicos de la persona:
nombre, salario inicial y fecha de nacimiento. Algunas veces el sistema no
conoce el salario inicial o la fecha de nacimiento.

El Código de la FIgura muestra cuatro constructores sobrecargados para la


clase Employee. El primer constructor (Líneas 7-11) inicializa todas las
variables de instancia. En el segundo (Líneas 12-14), la fecha de nacimiento no
es provista. La referencia this se usa para realizar una llamada a otro
constructor (siempre dentro de la misma clase); en este caso el primer
constructor. Asimismo, el tercer constructor (Líneas 15-17) llama al primer
constructor pasándole la constante de clase BASE_SALARY. El cuarto
constructor (Líneas 18-20) llama al segundo constructor pasándole la constante
BASE_SALARY, el que llama al primer constructor pasándole null como fecha
de nacimiento.
La palabra clave this en un constructor debe ser la primera línea de código en
el constructor. Puede haber más código de inicialización después de la llamada
a this, pero no antes.

1 public class Employee {


2 private static final double BASE_SALARY = 15000.00;
3 private String name;
4 private double salary;
5 private Date birthDate;
6
7 public Employee(String name, double salary, Date DoB){
8 this.name = name;
9 this.salary = salary;
10 this.birthDate = DoB;
11 }
12 public Employee(String name, double salary) {
13 this(name, salary, null);
14 }
15 public Employee(String name, Date DoB) {
16 this(name, BASE_SALARY, DoB);
17 }
18 public Employee(String name) {
19 this(name, BASE_SALARY);
20 }
21 // more Employee code...
22 }

13.11 La Sobrecarga de
Constructores
13.11.1 Los Constructores no se Heredan

Aunque una subclase hereda todos los métodos y variables desde una clase
padre, no hereda sus constructores. Hay sólo dos maneras para que una clase
tenga un constructor

• Que el programador escriba un constructor


• Que el programador no escriba un constructor y se asigne el constructor
por defecto.

Siempre se llama a un constructor de la clase padre, además de llamar al


constructor hijo. Esto se describe, más adelante, en este módulo.

13.11 La Sobrecarga de Constructores


13.11.2 Invocación a los
Constructores de la Clase
Padre
Al igual que los métodos, los constructores pueden llamar a los constructores
no privados de su superclase inmediata.

Con frecuencia, usted define un constructor que toma argumentos y quiere usar
esos argumentos para controlar la construcción de la del objeto que hereda del
padre. Puede invocar al constructor de una clase padre en particular como
parte de la primera línea del constructor hijo. Para controlar la invocación del
constructor específico, debe proveer los
argumentos apropiados a super(). Cuando no hay llamadas a super con
argumentos, se llama implícitamente al constructor padre con cero argumentos.
En este caso, si no existe un constructor con cero argumentos, el resultado es
un error de compilación.

La llamada a super() puede tomar cualquier cantidad de argumentos


apropiados para los distintos constructores disponibles en la clase padre, pero
esta debe ser la primera sentencia del constructor.

Asumiendo que la clase Employee tiene un conjunto de constructores como los


que están definidos en el Código de la Figura en el apartado 6.9, entonces
los siguientes constructores en la clase Manager deben estar definidos.

El constructor en la línea 12 es ilegal porque el compilador inserta una llamada


implícita a super() y la clase Employee no le provee un constructor sin
argumentos.

Cuando se usa, se debe colocar super o this en la primera línea del constructor.
Si escribe un constructor que no tiene ni una llamada a super(...) ni una
llamada a this(...), el compilador inserta automáticamente una llamada al
constructor de la clase padre, sin argumentos. Otros constructores pueden,
incluso, llamar a super(...) o this(...), invocando una cadena de constructores.
Lo que ocurre finalmente es que el constructor de la clase padre (o
posiblemente varios) ejecutan antes cualquier constructor de la clase hijo en la
cadena.

1 public class Manager extends Employee {


2 private String department;
3
4 public Manager(String name, double salary, String
dept) {
5 super(name, salary);
6 department = dept;
7 }
8 public Manager(String name, String dept) {
9 super(name);
10 department = dept;
11 }
12 public Manager(String dept) { // This code fails: no
super()
13 department = dept;
14 }
15 }

13.12 La Construcción y la Inicialización de Objetos: Un Vistazo Rápido

La inicialización de objetos es un proceso bastante complejo. En la sección "La


Construcción y la Inicialización de Objetos" en el Capítulo 3, “Los
Identificadores, las Palabras Clave y los Tipos”, se presentó una breve
explicación sobre ello. En esta sección, se analizará el proceso en detalle. En
primer lugar, se destina la memoria para el objeto completo y se asignan los
valores por defecto para las variables de instancia. En segundo lugar, se llama
al constructor de máximo nivel y se siguen estos pasos recurrentemente
bajando por el árbol de herencia:

1. Se asocian los parámetros del constructor.


2. Si existe un this explícito, se llama recursivamente y se salta al paso
3. Se llama recursivamente al super(...) implícito o explícito, excepto por la
clase Object dado que dicha clase no tiene una clase padre.
4. Se ejecuta el inicializador de la variable de instancia explícita.
5. Se ejecuta el cuerpo del constructor actual.

13.12 La Construcción y la
Inicialización de Objetos: Un
Vistazo Rápido
13.12.1 Ejemplos de Construcción e
Inicialización

Por ejemplo, utilice el código de la Figura para las clases Manager


y Employee

Lo presentado en la Figura son los pasos para construir un objeto new


Manager("Joe Smith", "Sales")
0 Inicialización Básica
0.1. Reserva memoria para el objeto Manager completo
0.2. Inicializa todas las variables de instancia a sus valores por defecto
1 Llama al constructor: Manager("Joe Smith", "Sales")
1.1. Asigna los parámetros pasados al constructor: n="Joe Smith", d="Sales"
1.2. No hay llamada a this() explícita
1.3. Llama a super(n) para Employee(String)
1.3.1. Asigna los parámetros del constructor: n="Joe Smith"
1.3.2. Llama a this(n, null) para Employee(String, Date)
1.3.2.1. Asigna los parámetros del constructor: n="Joe Smith", DoB=null
1.3.2.2. No hay llamada a this() explícita
1.3.2.3. Llama a super para Object()
1.3.2.3.1. No hay asignaciones necesarias
1.3.2.3.2. No hay llamadas a this
1.3.2.3.3. No hay llamadas a super() (Objeto es el raíz)
1.3.2.3.4. No hay inicialización de variables explícitas para Object
1.3.2.3.5. No hay llamadas al cuerpo del método
1.3.2.4. Inicializa explícitamente las variables Employee: salary=15000.00;
1.3.2.5. Ejecuta el cuerpo: nombre="Joe Smith"; date=null;
1.3.3. -1.3.4 Pasos salteados.
1.3.5. Ejecuta el cuerpo: No hay cuerpo en Employee(String)
1.4. No hay inicilizadores para Manager
1.5. Ejecuta el cuerpo: department="Sales"

13.12 La Construcción y la Inicialización


de Objetos: Un Vistazo Rápido
13.12.2 Ejercicios de Constructores

Ahora le proponemos que resuelva los siguientes ejercicios para poner a


prueba sus conocimientos.

Ejercicios de Constructores

Definir una clase llamada Bicicleta, con los siguientes atributos:

cantidadDeMarchas (1-21)
rodado (20-28)
modelo (Inglesa, Playera, Cross, Mountaine)

Definir un constructor para la clase que valide los atributos al momento de la


construcción de un nuevo objeto llamado miBicicleta.

Definir otro constructor que posibilite la creación de otras instancias sin pase de
argumentos, para lo cuál serán por defecto:

cantidadDeMarchas = 1;
rodado (20-28) = 20;
modelo = Playera;

Definir la clase Test correspondiente y realizar la ejecución correspondiente


13.13 La Clase Object

La clase Object es la raíz de todas las clases en el lenguaje de programación


Java. Si una clase es declarada sin cláusula extends, entonces el
compilador le agrega implícitamente el código extends Object a la
declaración; por ejemplo:

public class Employee {


// Incluir el código aquí
}

es equivalente a:

public class Employee extends Object {


// Incluir el código aquí
}

Esto permite sobrescribir varios métodos heredados de la clase Object.


La siguiente sección describe dos métodos importantes de Object.

13.13 La Clase Object


13.13.1 El Método equals

El operador == realiza una comparación de equivalencia. Esto es, para


cualquier valor de referencia x e y, x==y devuelve verdadero si y sólo si x e y
refieren al mismo objeto.

La clase Object en el paquete java.lang tiene el método public boolean


equals(Object obj), el cual compara dos objetos para determinar si son iguales.
Cuando no está sobrescrito, un método equals() de un objeto devuelve true
sólo si las dos referencias que están siendo comparadas refieren al mismo
objeto. Sin embargo, la intención del método equals() es la de comparar los
contenidos de los dos objetos cuando esto sea posible. Este es el motivo por el
que este método generalmente se sobrescribe. Por ejemplo, el método equals()
en la clase String devuelve true si y sólo si el argumento no es null y es un
objeto String que representa la misma secuencia de caracteres que el objeto
String con el cual el método es invocado

Un Ejemplo de equals

En este ejemplo, se han realizado modificaciones a la clase MyDate para incluir


un método equals que compare los atributos year, month y day. (Figura )

El método hashCode implementa un XOR a nivel de bits de los atributos. Esto


garantiza que el código hash para objetos MyDate iguales tienen el mismo
valor mientras que haciéndolo sobre diferentes fechas devolverá valores
diferentes.
El programa de la Figura verifica dos objetos MyDate que no son idénticos,
pero que son iguales considerando la verificación del año-mes-día

La ejecución de este programa de verificación genera la siguiente salida:

date1 is not identical to date2


date1 is equal to date2
set date2 = date1;
date1 is identical to date2

1 public class MyDate {


2 private int day;
3 private int month;
4 private int year;
5
6 public MyDate(int day, int month, int year) {
7 this.day = day;
8 this.month = month;
9 this.year = year;
10 }
11
12 public boolean equals(Object o) {
13 boolean result = false;
14 if ( (o != null) && (o instanceof MyDate) ) {
15 MyDate d = (MyDate) o;
16 if ( (day == d.day) && (month == d.month)
17 && (year == d.year) ) {
18 result = true;
19 }
20 }
21 return result;
22 }
23
24 public int hashCode() {
25 return (day ^ month ^ year);
26 }
27 }

1 class TestEquals {
2 public static void main(String[] args) {
3 MyDate date1 = new MyDate(14, 3, 1976);
4 MyDate date2 = new MyDate(14, 3, 1976);
5
6 if ( date1 == date2 ) {
7 System.out.println("date1 is identical to date2");
8 } else {
9 System.out.println("date1 is not identical to
date2");
10 }
11
12 if ( date1.equals(date2) ) {
13 System.out.println("date1 is equal to date2");
14 } else {
15 System.out.println("date1 is not equal to date2");
16 }
17
18 System.out.println("set date2 = date1;");
19 date2 = date1;
20
21 if ( date1 == date2 ) {
22 System.out.println("date1 is identical to date2");
23 } else {
24 System.out.println("date1 is not identical to
date2");
25 }
26 }
27 }

13.13 La Clase
Object
13.13.2 El Método toString
El método toString convierte un objeto en una representación de String. Este es
utilizado por el compilador cuando se lleva a cabo una conversión automática a
cadena de caracteres. Por ejemplo, la llamada a

System.out.println():

Date now = new Date();


System.out.println(now);

es equivalente a:

System.out.println(now.toString());

La clase Object define un método toString por defecto que devuelve el nombre
de la clase y su dirección de referencia (normalmente esta no resulta útil).
Muchas clases sobrescriben toString para ofrecer información más útil. Por
ejemplo, todas las clases de envoltura (presentadas más adelante en este
módulo) sobrescriben toString para ofrecer una cadena de caracteres
correspondiente al valor que representan. Incluso las clases que representan
elementos que no tienen una representación como cadena de caracteres,
frecuentemente implementan toString para devolver información del estado de
un objeto para propósitos de depuración.

13.14 La Clase de Envoltura (Wrapper Classes)

El lenguaje de programación Java no trata a los tipos primitivos de datos como


objetos. Por ejemplo, los datos numéricos, Boolean y carácter son tratados en
la forma primitiva a efectos de obtener un buen nivel de eficiencia. El lenguaje
de programación Java ofrece clases de envoltura para manejar elementos de
datos primitivos como objetos. Estos elementos de datos son envueltos en un
objeto creado alrededor de ellos. Cada tipo de dato primitivo tiene su clase de
envoltura correspondiente, dentro del paquete java.lang. Cada objeto de una
clase de envoltura encapsula un único valor primitivo (Figura )

Puede construir un objeto de la clase de envoltura pasándole el valor a ser


envuelto dentro del constructor apropiado, como se muestra en el Código de la
Figura

Las clases de envoltura son útiles cuando se convierten tipos de datos


primitivos, dado que se dispone de los métodos de la clase de envoltura. Por
ejemplo:

int x = Integer.valueOf(str).intValue();

or:

int x = Integer.parseInt(str);
13.14 La Clase de Envoltura (Wrapper
Classes)
13.14.1 Autoboxing de Tipos
Primitivos

Si se debe cambiar el tipo de dato primitivo a su objeto equivalente


(denominado boxing), entonces se necesita usar la clase de envoltura.
También, para obtener el tipo de dato primitivo desde la referencia al objeto
(denominado unboxing) se necesitan los métodos de la clase de envoltura.
Estas operaciones de boxing y unboxing pueden desordenar el
código y por lo tanto hacerlo difícil de entender. En J2SE versión 5.0, la función
de autoboxing permite asignar y devolver tipos primitivos sin la necesidad de
utilizar las clases de envoltura.

El Código de la Figura muestra dos casos simples de autoboxing y


autounboxing. Compare este código con el Código de la Figura en el
apartado 6.12.

El compilador de J2SE versión 5.0 crea la clase de envoltura automáticamente


cuando asigna un tipo primitivo a una clase de envoltura. El compilador también
extrae el valor primitivo cuando asigna desde un objeto de envoltura a un tipo
primitivo.
Esto puede realizarse al pasar parámetros a un método o en una expresión
aritmética
13.15 Laboratorios

Laboratorio 6 - El Diseño de las Clases

13.15 Laboratorios
13.15.1 Objetivos

Una vez completado este laboratorio, usted debería ser capaz de:

• Crear subclases con herencia y sobrescritura de métodos.


• Crear una colección heterogénea y usar polimorfismo

13.15 Laboratorios
13.15.2 Ejercicio 1: Creación de una Subclase de la Cuenta
Bancaria (Nivel 1)

Ejercicio 1 - Creación de una Subclase de la Cuenta Bancaria (Nivel 1)

13.15 Laboratorios
13.15.3 Ejercicio 1: Creación de una Subclase de la Cuenta
Bancaria (Nivel 2)
Ejercicio 1 - Creación de una Subclase de la Cuenta Bancaria (Nivel 2)

13.15 Laboratorios
13.15.4 Ejercicio 1: Creación de una Subclase de la Cuenta
Bancaria (Nivel 3)

Ejercicio 1 - Creación de una Subclase de la Cuenta Bancaria (Nivel 3)

13.15 Laboratorios
13.15.5 Ejercicio 2: Creación de una Colección Heterogénea
de Cuentas de Cliente (Nivel 1)

Ejercicio 2 - Creación de una Colección Heterogénea de Cuentas de Cliente


(Nivel 1)

13.15 Laboratorios
13.15.6 Ejercicio 2: Creación de una Colección Heterogénea
de Cuentas de Cliente (Nivel 2)

Ejercicio 2 - Creación de una Colección Heterogénea de Cuentas de Cliente


(Nivel 2)

13.15 Laboratorios
13.15.7 Ejercicio 2: Creación de una Colección Heterogénea
de Cuentas de Cliente (Nivel 3)

Ejercicio 2 - Creación de una Colección Heterogénea de Cuentas de Cliente


(Nivel 3)

13.15 Laboratorios
13.15.8 Ejercicio 3: Creación de un Programa de
Procesamiento en Lote (Avanzado)

Ejercicio 3 - Creación de un Programa de Procesamiento en Lote (Avanzado)

13.15 Laboratorios
13.15.9 Resumen del Ejercicio
Discusión – Dedique unos minutos para discutir qué experiencias, temas o
descubrimientos realizó durante el ejercicio de laboratorio.

• Experiencias
• Interpretaciones
• Conclusiones
• Aplicaciones

13.16 Preguntas de Repaso

Ahora le proponemos que resuelva el siguiente ejercicio para poner a prueba


sus conocimientos.

Defina por Verdadero o Falso las siguientes afirmaciones según corresponda.


¡Éxitos!
14 Características Avanzadas de las Clases

Este capítulo completa la discusión acerca de las características de la


orientación a objetos del lenguaje de programación de la tecnología Java.

14.1 Repaso del Capítulo Anterior

Los principales temas del capítulo anterior son:

Las Subclases

La Herencia Simple

El lenguaje de programación Java permite que una clase sea extendida


solamente de otra clase. Esta restricción se denomina herencia simple.

Los Controles de Acceso

Las variables y los métodos pueden estar en uno de los siguientes cuatro
niveles de acceso: public, protected, default o private.
Una variable o método indicado con private está sólo accesible a los métodos
miembros de la clase en la que la variable o el método private se declararon.
La accesibilidad por default le permite el acceso desde cualquier método
perteneciente a las clases que sean miembros del mismo paquete donde se
ubica la variable, el método o la clase.
Un método o variable protected es accesible desde los métodos definidos en
las clases que pertenezcan al mismo paquete y desde cualquier método de
cualquier subclase.
Se accede, universalmente, a una variable o un método marcado con el
modificador public.

La Sobrescritura de Métodos

Si un método se define en una subclase de forma tal que el nombre, el tipo de


retorno y la lista de argumentos sean exactamente los mismos que en la clase
padre, entonces se dice que el nuevo método sobrescribe al anterior. Además,
un método que sobrescribe no puede ser menos accesible que el método
sobrescrito.

La Invocación de los Métodos Sobrescritos

La palabra clave super refiere a la superclase de la clase en la cual la palabra


clave se usa. Se utiliza para referenciar las variables o los métodos miembros
de la superclase. El método no tiene por qué estar definido en la clase padre;
podría ser heredado de alguna clase superior en la jerarquía.

El Polimorfismo

Un objeto tiene solamente una forma (la que se le da cuando es construido).


Sin embargo, una variable es polimórfica porque puede referenciar a objetos
con formas diferentes. El lenguaje de programación Java, como la mayoría de
los lenguajes orientados a objetos, permite referenciar a un objeto con una
variable que tiene uno de los tipos de las clases padre del objeto.

Las Colecciones Heterogéneas

Se pueden crear colecciones de objetos que tengan una clase en común. Estas
colecciones se denominan homogéneas.
Una colección heterogénea tiene objetos diferentes. En los lenguajes
orientados a objetos, se pueden crear colecciones de varios elementos. Todos
tienen una clase ancestro en común: la clase Object.

Los Argumentos Polimórficos

Se puede escribir métodos que aceptan un objeto genérico y trabajar


adecuadamente sobre objetos de cualquier subclase de este objeto.

El Operador instanceof
Algunas veces se podría querer saber cuál es el objeto corriente que se tiene.
Este es el propósito del operador instanceof.

La Conversión de Objetos

La conversión hacia arriba (ascendente) en la herencia de clases está siempre


permitida.
Para realizar una conversión hacia abajo (descendente), el compilador debe
aceptar que la conversión es, al menos, posible. La clase para la cual la
conversión tiene lugar, debe ser una una subclase del tipo actual de la
referencia.
Si el compilador permite la conversión, entonces el tipo del objeto es verificado
en tiempo de ejecución.

Sobrecarga de Métodos

En algunas circunstancias, se podría necesitar escribir varios métodos en la


misma clase que hacen básicamente el mismo trabajo pero que reciben
diferentes argumentos. lenguaje de programación Java, así como también
otros tantos lenguajes de programación, permite utilizar un mismo nombre para
más de un método. Esto funciona solamente si en el momento de realizar la
llamada al método se puede determinar a cual de ellos se necesita.
Existen dos reglas que se aplican a la sobrecarga de métodos:

• Las listas de argumentos deben diferir.


• Los tipos de retorno pueden ser diferentes.

Un caso de variación en la sobrecarga consiste en utilizar una cantidad


diferente de argumentos del tipo.

La Sobrecarga de Constructores

La palabra clave this en un constructor debe ser la primera línea de código en


el constructor. Puede haber más código de inicialización después de la llamada
a this, pero no antes.

Los Constructores no se Heredan

Aunque una subclase hereda todos los métodos y variables desde una clase
padre, no hereda sus constructores.

Invocación a los Constructores de la Clase Padre

Al igual que los métodos, los constructores pueden llamar a los constructores
no privados de su superclase inmediata.
Puede invocar al constructor de una clase padre en particular como parte de la
primera línea del constructor hijo.

La Clase Object
La clase Object es la raíz de todas las clases en el lenguaje de programación
Java. Si una clase es declarada sin cláusula extends, entonces el compilador le
agrega implícitamente el código extends Object a la declaración.

El Método equals

La intención del método equals() es la de comparar los contenidos de los dos


objetos cuando esto sea posible. Este es el motivo por el que este método
generalmente se sobrescribe. Por ejemplo, el método equals() en la clase
String devuelve true si y sólo si el argumento no es null y es un objeto String
que representa la misma secuencia de caracteres que el objeto String con el
cual el método es invocado.

El Método toString

El método toString convierte un objeto en una representación de String. Este es


utilizado por el compilador cuando se lleva a cabo una conversión automática a
cadena de caracteres.

La Clase de Envoltura (Wrapper Classes)

El lenguaje de programación Java ofrece clases de envoltura para manejar


elementos de datos primitivos como objetos. Si se debe cambiar el tipo de dato
primitivo a su objeto equivalente (denominado boxing), entonces se necesita
usar la clase de envoltura.

14.3 Objetivos

Una vez completado este capítulo, usted debería poder:

• Crear variables, métodos e inicializadores estáticos.


• Crear clases, métodos y variables finales.
• Crear y usar tipos enumerados.
• Crear clases y métodos abstractos.
• Crear y usar una interfaz.

Discusión – Las siguientes preguntas son relevantes al material presentado en


este capítulo:

• ¿Cómo se puede crear una constante?


• ¿Cómo puede declarar datos que son compartidos por todas las
instancias de una clase dada?
• ¿Cómo puede evitar que una clase o un método sea sobrescrito o tenga
subclases?

14.4 Recursos Adicionales


Recursos adicionales – Las siguientes referencias ofrecen información
adicional sobre los temas descritos en este capítulo:

• The Java Tutorial. [Online]. Disponible en:

http://java.sun.com/docs/books/
tutorial/java/IandI/interfaceDef.html

http://java.sun.com/docs/books/
tutorial/java/IandI/usinginterface.html

http://java.sun.com/docs/books/
tutorial/java/IandI/nogrow.html

http://java.sun.com/docs/books/
tutorial/java/javaOO/enum.html

14.5 La Palabra Clave static

La palabra clave static declara a los miembros de una clase (atributos, métodos
y clases anidadas) que están asociados con la clase, en lugar de estarlo con
una instancia de ella en particular.

Las siguientes secciones describen los usos más comunes de la palabra clave
static: las variables de clase y los métodos de clase.

14.5 La Palabra Clave static


14.5.1 Los Atributos de la Clase

Algunas veces es deseable tener una variable compartida entre todas las
instancias de una clase. Por ejemplo, se puede utilizar esta variable como base
para la comunicación entre las distintas instancias o para realizar un
seguimiento de la cantidad de instancias que han sido creadas (vea Figura ).

Se puede lograr este efecto de compartimiento, declarando a la variable con la


palabra clave static. Estas variables son algunas veces denomindas variables
de clase, para distinguirlas de las de miembro o de instancia, que no son
compartidas. (ver Figura )

En este ejemplo, a cada objeto creado se le asigna un número serial único,


comenzando en uno y contando en forma creciente. La variable counter está
compartida por todas las instancias. Por lo tanto, cuando el constructor de un
objeto incrementa a counter, el objeto siguiente se crea recibiendo el valor
incrementado.

Una variable static es similar en muchas formas a una variable global en otros
lenguajes. El lenguaje de programación Java no tiene variables globales, pero
una variable static es una variable simple a la que se accede desde cualquier
instancia de la clase.

Si una variable static no se declara como private, se puede acceder a ella


desde fuera de la clase. Para hacer esto, no se necesita crear una instancia de
la clase, se la puede referir a través del nombre de la clase. (ver Figura )
14.5 La Palabra Clave static
14.5.2 Los Métodos de Clase

Algunas veces se necesita acceder al código del programa cuando no se tiene


una instancia de un objeto particular. Un método que está declarado con la
palabra clave static puede usarse de esta manera y es, algunas veces, llamado
método de clase.(ver Figura )

Para acceder a los métodos que son estáticos, se debe usar el nombre de la
clase, en lugar de una referencia a un objeto, como se muestra en la Figura

La salida del programa TestCounter es:

Number of counter is 0
Number of counter is 1

Dado que se puede invocar un método static sin una instancia de la clase a la
que pertenece, no existe el valor this. Como consecuencia, se tiene que un
método static puede acceder solamente a las variables locales, a los atributos
static y a sus parámetros. Si se intenta acceder a atributos que no sean static,
se producirá un error de compilación.

Los atributos que no son static están asociados a una instancia y se puede
acceder a ellos, solamente, a través de una referencia a la instancia (ver Figura
)

Debe tener en cuenta lo siguiente cuando usa métodos static:

• No se puede sobrescribir un método static pero se puede ocultar. Para


que un método sea sobrescrito no debe ser static. Dos métodos static
con la misma signatura en una herencia de clases, indican que hay dos
métodos de clase independientes. Si un método de una clase se aplica a
una referencia de objeto, el método invocado es el de la clase para la
cual se declaró dicha variable.
• El método main() es un método static porque la JVM no crea una
instancia de la clase cuando se ejecuta el método main. Por lo tanto, si
tiene un miembro en la clase, debe crear un objeto para acceder a él.
14.5 La Palabra Clave static
14.5.3 Los Inicializadores Estáticos

Una clase puede contener código con bloques estáticos que no son parte de
los métodos normales. Los bloques de código estáticos se ejecutan solamente
una vez, cuando se carga la clase. Si una clase contiene más de un bloque
estático, estos se ejecutan en el orden en el que aparecen en la clase.

El código en la línea 4 de la clase Count4 mostrada en la Figura usa un


método static de la clase Integer getInteger(String), el cual devuelve un objeto
Integer que representa el valor de una propiedad del sistema. Esta propiedad,
que tiene el nombre myApp.Count4.counter, se determina en la línea de
comandos usando la opción -D. El método intValue en el objeto Integer
devuelve el valor como un int.

El resultado es el siguiente:

java -DmyApp.Count4.counter=47 TestStaticInit


counter = 47
14.5 La Palabra Clave static
14.5.4 La Palabra Clave final

Esta sección describe las clases final, los métodos final y las variables final.

14.5 La Palabra Clave static


14.5.5 Las Clases Finales

El lenguaje de programación Java permite aplicar la palabra clave final a las


clases. Si se hace esto, la clase no podrá tener subclases. Por ejemplo, la
clase java.lang.String es una clase final. Esto está hecho por razones de
seguridad, dado que asegura que si el método referencia a una cadena de
caracteres, esta es definitivamente una cadena de caracteres de la clase String
y no una cadena de caracteres de una clase que es una subclase modificada
de String

14.5 La Palabra Clave static


14.5.6 Los Métodos Finales

También se puede declarar a cualquier método como final. Los métodos


declarados como final no pueden ser sobrescritos. Por razones de seguridad,
se debe declarar un método como final si tiene una implementación que no
debe ser cambiada y es crítica a la consistencia del objeto.
Los métodos declarados final pueden ser optimizados. El compilador puede
generar código que causa una llamada directa al método, al contrario de la
usual invocación a métodos virtuales que involucra búsquedas en tiempo de
ejecución. Los métodos señalados como static o private pueden ser
optimizados por el compilador como si fueran señalados final, porque en este
caso no se puede aplicar asignación dinámica (dynamic binding)

14.5 La Palabra Clave static


14.5.7 Las Variables Finales

Si una variable se declara como final, se transforma en una constante.


Cualquier intento de cambiar el valor de una variable final, causa un error de
compilación. El ejemplo en la Figura muestra una variable final, definida
adecuadamente:

Las Variables Finales en Blanco

Una variable final en blanco es una variable final que no está inicializada en su
declaración. La inicialización queda postergada. Una variable de instancia final
en blanco debe ser asignada en un constructor y puede hacerse esto
solamente una vez. Una variable final en blanco, que es una variable local,
puede ser asignada en cualquier momento en el cuerpo del método, pero
puede hacerse una sola vez. El fragmento de código presentado en la Figura
es un ejemplo de cómo una variable final en blanco puede ser asignada una
sola vez y usada en una clase.
14.6 Ejercicios de Modificador Final

Ahora le proponemos que resuelva los siguientes ejercicios para poner a


prueba sus conocimientos.

Ejercicios de Modificador Final

Utilizar la clase obtenida sobre la realización del ejercicio del capítulo anterior
13.12.2 Ejercicios de Constructores.

Comprobar la funcionalidad del modificador final a los atributos y métodos de la


clase Bicicleta.

14.7 Los Tipos Enumerados

Un requerimiento común en programación es tener conjuntos finitos de


nombres simbólicos que representan los valores de un atributo. Por ejemplo,
para representar los distintos palos de la baraja se podría crear un conjunto de
símbolos: CORAZONES, DIAMANTES, TRÉBOLES y PICAS. Esto se
denomina con frecuencia un tipo enumerado.

14.7 Los Tipos


Enumerados
14.7.1 El Viejo Estilo de los Tipos Enumerados

El Código en la Figura ilustra cómo se creaban los tipos enumerados en las


versiones anteriores (antes de J2SE versión 5.0), esto es, usando constantes
enteras para los nombres simbólicos.

Además, la interfaz del usuario necesita desplegar una cadena de caracteres


en lugar de desplegar enteros que representen los palos de la baraja. El
Código de la Figura muestra el método getSuitName de la clase
PlayingCard. Fíjese en las líneas 37 y 38; estas se usan cuando el atributo suit
mantiene un valor entero inválido.

El Código de la Figura muestra el programa TestPlayingCard que crea dos


objetos PlayingCard y despliega su rank y suit. Este programa ilustra el primer
problema con el viejo estilo de creación de un tipo enumerado. El constructor
PlayingCard tiene dos argumentos: un entero para el palo (suit) y otro para el
número (rank). La primera llamada al constructor PlayingCard (Línea 9) usa
una constante simbólica apropiada, pero la segunda (Línea 14) usa un valor
entero arbitrario. Ambas son válidas desde la perspectiva del compilador.
Sin embargo, cuando se ejecuta el programa se puede ver que el segundo
objeto carta es inválido y no se despliega apropiadamente (ver Figura )

La sentencia "Invalid suit." es el mensaje de error enviado por el método


getSuitName en la línea 38 en el Código de la Figura .

Esta forma de tratar a los tipos enumerados tiene varios problemas:

• No mantiene la seguridad de tipos - Dado que el atributo suit es un


entero, se puede pasar cualquier otro valor int en el que se requiere un
palo. El programador puede aplicar operaciones aritméticas sobre dos
suit, lo cual no tiene sentido.
• No utiliza un espacio de nombres - Debe prefijar constantes de un tipo
enumerado int con una cadena de caracteres (en este caso, SUIT_) para
evitar colisiones con otro tipo enumerado int. Por ejemplo, si hubiera
usado un tipo enumerado int para el valor del rank, entonces se habría
creado un conjunto de constantes int RANK_XYZ.
• Carácter frágil - Dado que los enumerados int son constantes en tiempo
de compilación, se compilan dentro de los clientes que los usarán. Si
una nueva constante se agrega entre otras dos ya existentes o si el
cliente cambia el orden, entonces debe ser recompilada. Si no se
recompila, se mantiene ejecutando, pero su comportamiento es
indefinido.
• Valores impresos sin información - Dado que son sólo valores int, al
desplegarlo, todo lo que se despliega es un número, lo cual no dice nada
acerca de lo que representa, o de qué tipo es. Esto se debe a que la
clase PlayingCard necesita el método getSuitName. 7 DIAMONDS

1 package cards.domain;
2
3 public class PlayingCard {
4
5 // seudo tipo enumerado
6 public static final int SUIT_SPADES = 0;
7 public static final int SUIT_HEARTS = 1;
8 public static final int SUIT_CLUBS = 2;
9 public static final int SUIT_DIAMONDS = 3;
10
11 private int suit;
12 private int rank;
13
14 public PlayingCard(int suit, int rank) {
15 this.suit = suit;
16 this.rank = rank;
17 }
18
19 public int getSuit() {
20 return suit;
21 }
22 public String getSuitName() {
23 String name = ““;
24 switch ( suit ) {
25 case SUIT_SPADES:
26 name = “Spades”;
27 break;
28 case SUIT_HEARTS:
29 name = “Hearts”;
30 break;
31 case SUIT_CLUBS:
32 name = “Clubs”;
33 break;
34 case SUIT_DIAMONDS:
35 name = “Diamonds”;
36 break;
37 default:
38 System.err.println(“Invalid suit.”);
39 }
40 return name;
41 }

1 package cards.tests;
2
3 import cards.domain.PlayingCard;
4
5 public class TestPlayingCard {
6 public static void main(String[] args) {
7
8 PlayingCard card1
9 = new PlayingCard(PlayingCard.SUIT_SPADES, 2);
10 System.out.println("gcard1 is the "g +
card1.getRank()
11 + "g of "g + card1.getSuitName());
12
13 // Puede crear una carta con un palo erroneo.
14 PlayingCard card2 = new PlayingCard(47, 2);
15 System.out.println("gcard2 is the "g +
card2.getRank()
16 + "g of "g + card2.getSuitName());
17 }
18 }
14.7 Los Tipos
Enumerados
14.7.2 El Nuevo Tipo Enumerado

El J2SE versión 5.0 del lenguaje de programación Java incluye una


característica de tipo enumerado de tipo seguro. El Código de la Figura
muestra un tipo enumerado para los palos de la baraja de naipes. Se puede
pensar en el tipo Suit como en una clase con un conjunto finito de valores
determinado por los nombres simbólicos listados en la definición de tipo. Por
ejemplo, Suit.SPADES es del tipo Suit.

El Código de la Figura muestra la clase PlayingCard usando el tipo Suit para


los tipos de datos del atributo suit.

Esto resuelve el tema del tipo seguro con el viejo estilo de tratamiento de tipos
enumerados. El Código de la Figura muestra un programa de prueba
actualizado. Si se quita el comentario de la Línea 14, se producirá un error de
compilación, dado que el valor int 47 no corresponde al tipo Suit.

1 package cards.domain;
2
3 public class PlayingCard {
4
5 private Suit suit;
6 private int rank;
7
8 public PlayingCard(Suit suit, int rank) {
9 this.suit = suit;
10 this.rank = rank;
11 }
12
13 public Suit getSuit() {
14 return suit;
15 }
16 public String getSuitName() {
17 String name = ““;
18 switch ( suit ) {
19 case SPADES:
20 name = “Spades”;
21 break;
22 case HEARTS:
23 name = “Hearts”;
24 break;
25 case CLUBS:
26 name = “Clubs”;
27 break;
28 case DIAMONDS:
29 name = “Diamonds”;
30 break;
31 default:
32 // No need for error checking as the Suit
33 // enum is finite.
34 }
35 return name;
36 }

1 package cards.tests;
2
3 import cards.domain.PlayingCard;
4 import cards.domain.Suit;
5
6 public class TestPlayingCard {
7 public static void main(String[] args) {
8
9 PlayingCard card1
10 = new PlayingCard(Suit.SPADES, 2);
11 System.out.println("gcard1 is the "g +
card1.getRank()
12 + "g of "g + card1.getSuitName());
13
14 // PlayingCard card2 = new PlayingCard(47, 2);
15 // This will not compile.
16 }
17 }

14.7 Los Tipos


Enumerados
14.7.3 Los Tipos Enumerados Avanzados

Desafortunadamente, la clase PlayingCard aún requiere un método


getSuitName para desplegar un nombre amigable en la interfaz de usuario. Si
el programa tuviera que desplegar el valor actual de Suit, este mostraría el
nombre simbólico del valor del tipo. Por ejemplo, Suit.SPADES se desplegaría
como SPADES. Esto es más amigable que '0', pero todavía no es tan amigable
como Spades.

Además, el nombre del palo no debería ser parte de la clase PlayingCard, pero
si parte del tipo Suit. La nueva característica del tipo enumerado permite el uso
de atributos y métodos, tal como se realiza con las clases regulares. El
Código de la Figura muestra un refinamiento del primer tipo Suit con un
atributo name y un método getName. Fíjese en el uso apropiado del
ocultamiento de la información con los atributos privados y los métodos de
acceso públicos

Un constructor enumerado debería usar siempre la accesibilidad privada. Los


argumentos al constructor son suministrados después de cada valor declarado.
Por ejemplo, en la Línea 4, la cadena de caracteres "Spades" es el argumento
al constructor de enumerados para el valor SPADES. Los tipos enumerados
pueden tener cualquier cantidad de atributos y métodos.

Finalmente, el Código de la Figura muestra el programa de prueba


modificado que usa un nuevo método getName del tipo Suit. En la Línea 12, el
método getSuit devuelve un valor Suit y el método getName devuelve el
atributo name del valor Suit.
1 package cards.tests;
2
3 import cards.domain.PlayingCard;
4 import cards.domain.Suit;
5
6 public class TestPlayingCard {
7 public static void main(String[] args) {
8
9 PlayingCard card1
10 = new PlayingCard(Suit.SPADES, 2);
11 System.out.println("card1 is the " + card1.getRank()
12 + " of " + card1.getSuit().getName());
13
14 // NewPlayingCard card2 = new NewPlayingCard(47, 2);
15 // This will not compile.
16 }
17 }

14.8 Las Importaciones Estáticas


Si se tiene que acceder a los miembros estáticos de una clase, entonces es
necesario calificar las referencias con la clase a partir de la cual proviene. Esto
también se aplica a los valores de tipo enumerado. La Línea 10 del Código en
la Figura del apartado 7.5.3, muestra el acceso al valor SPADES del tipo
Suit usando la notación de punto Suit.SPADES.

EL J2SE versión 5.0 provee la característica de la importación estática, la que


permite el acceso a los miembros estáticos sin tener que calificarlos con el
nombre de la clase. El Código de la Figura muestra el uso de importaciones
estáticas.

La Línea 4 le indica al compilador que incluya los valores de los tipos


enumerados (o algún miembro estático de cualquier tipo) en la tabla de
símbolos cuando está compilando el programa. Por lo tanto, la Línea 9 puede
usar SPADES sin incluir el prefijo correspondiente al espacio de nombres Suit.

1 package cards.tests;
2
3 import cards.domain.PlayingCard;
4 import static cards.domain.Suit.*;
5
6 public class TestPlayingCard {
7 public static void main(String[] args) {
8
9 PlayingCard card1 = new PlayingCard(SPADES, 2);
10 System.out.println("card1 is the " + card1.getRank()
11 + " of " + card1.getSuit().getName());
12
13 // NewPlayingCard card2 = new NewPlayingCard(47, 2);
14 // This will not compile.
15 }
16 }
14.9 Las Clases Abstractas

En nuestro ejemplo de embarcación, suponga que el sistema necesita elaborar


un reporte que liste cada vehiculo de la flota de la compañía y el combustible
que precisa para sus futuros viajes. Asuma que el Sistema de Embarque tiene
una clase que construye la lista de la flota de vehículos de la compañía y
genera el reporte de combustible necesario.

La Figura muestra el modelo UML de la compañía y su colección


heterogénea de vehículos (la asociación fleet).

El programa ShippingMain muestra la inicialización de una flota de ejemplo.


(Figura )

Usted debería escribir el código del reporte como se muestra en la Figura )

El cálculo del requerimiento de combustible se realiza dividiendo la distancia


del viaje (en kilómetros) entre la eficiencia de combustible del vehículo (en
kilómetros por litro).
1 public class FuelNeedsReport {
2 private Company company;
3
4 public FuelNeedsReport(Company company) {
5 this.company = company;
6 }
7
8 public void generateText(PrintStream output) {
9 Vehicle1 v;
10 double fuel;
11 double total_fuel = 0.0;
12
13 for ( int i = 0; i < company.getFleetSize(); i++ ) {
14 v = company.getVehicle(i);
16 // Calculate the fuel needed for this trip
17 fuel = v.calcTripDistance() /
v.calcFuelEfficency();
18
19 output.println("Vehicle " + v.getName() + "needs "
20 + fuel + " liters of fuel.");
21 total_fuel += fuel;
22 }
23 output.println("Total fuel needs is " + total_fuel +
" liters.");
24 }
25 }

14.9 Las Clases Abstractas


14.9.1 El Problema

Los cálculos para determinar la eficiencia de combustible de un camión (Truck)


comparado con el de una barca de río (RiverBarge) pueden diferir radicalmente.
La clase Vehicle no puede suministrar los dos métodos, pero sus subclases
(Truck y RiverBarge) sí pueden.

14.9 Las Clases Abstractas


14.9.2 La Solución

El lenguaje de programación Java permite al diseñador de clases especificar


que una superclase declara un método pero que no provee una
implementación para él. A estos métodos se les denomina métodos abstractos.
La implementación de los mismos se realiza en la subclase. Cualquier clase
con uno o más métodos abstractos se denomina una clase abstracta (vea la
Figura ).
La Figura presenta un modelo UML de la solución. Vehicle (Vehículo) es una
clase abstracta con dos métodos públicos abstractos.

El compilador Java le avisa en caso de querer instanciar una clase abstracta.


Por ejemplo, la sentencia new Vehicle() sería ilegal.

La declaración de la clase Vehicle se muestra en la Figura


La clase Truck debe crear una implementación como se muestra en la Figura

La clase RiverBage debe crear una implementación como se muestra en la


Figura

Sin embargo, las clases abstractas pueden tener atributos, métodos concretos
y constructores. Por ejemplo, la clase Vehicle podría incluir los atributos load y
maxLoad y un constructor para inicializarlos. Es una buena práctica crear estos
constructores protected en lugar de public, dado que estos solamente tienen
sentido para los constructores de las subclases de la clase abstracta. Haciendo
estos constructores protected se hace más obvio al programador que el
constructor no es llamado por una clase arbitraria.
14.10 Las Interfaces

La interfaz pública de una clase es un contrato entre el código del cliente y la


clase que provee el servicio. Las clases concretas implementan cada método.
Sin embargo, una clase abstracta puede diferir la implementación declarando
que el método sea abstract, mientras que una interfaz Java solamente realiza
la declaración del contrato y no la implementación.

Una clase concreta implementa una interfaz definiendo todos los métodos
declarados por la interfaz. Muchas clases pueden implementar la misma
interfaz. Estas clases no necesitan compartir la misma herencia de clase y,
además, una clase puede implementar más de una interfaz. Esto está descrito
en la siguiente sección.

Al igual que con las clases abstractas, utilice un nombre de interfaz de la


misma forma que el tipo de una variable de referencia. La asignación dinámica
(dynamic binding) usual tiene efecto. Las referencias son convertidas a los
tipos de la interfaz o a partir de los mismos. Se puede utilizar el operador
instanceof para determinar si la clase de un objeto implementa una interfaz.
14.10 Las Interfaces
14.10.1 El Ejemplo del Flyer (Volador)

Imagine un grupo de objetos que comparten la misma habilidad: vuelan. Usted


puede construir una interfaz pública, llamada Flyer, que soporta tres
operaciones: takeOff (despegar), land (aterrizar) y fly (volar). Vea las
Figuras y

Puede haber múltiples clases que implementan la interfaz Flyer, como se


muestra en la Figura Un aeroplano (airplane) puede volar, un pájaro (bird)
también, Superman puede volar, etc.

Un aeroplano es un vehículo, y puede volar. Un pájaro es un animal, y puede


volar. Estos ejemplos muestran que una clase puede heredar de otra, pero
también implementar otras interfaces.

Esto resulta similar a la herencia múltiple, pero no es así. El riesgo del uso de
la herencia múltiple radica en que una clase puede heredar dos
implementaciones distintas del mismo método. Con interfaces esto no es
posible dado que la declaración de un método de una interfaz no suministra
implementación, como se muestra en la Figura .

En la Figura se describe la clase Bird

La cláusula extends debe incluirse antes de la cláusula de implements. La


clase Bird puede suministrar sus propios métodos (buildNest y layEggs), así
cómo sobrescribir los métodos de la clase Animal (eat).

Suponga que está construyendo un sistema de software de control de una


aeronave. Debe conceder el permiso para despegar y aterrizar a cualquier
objeto Flyer de todos los tipos. Esto se muestra en la Figura

El código para el aeropuerto podría lucir como el presentado en la Figura


1 public interface Flyer {
2 public void takeOff();
3 public void land();
4 public void fly();
5 }

1 public class Airplane implements Flyer {


2 public void takeOff() {
3 // accelerate until lift-off
4 // raise landing gear
5 }
6 public void land() {
7 // lower landing gear
8 // decelerate and lower flaps until touch-down
9 // apply brakes
10 }
11 public void fly() {
12 // keep those engines running
13 }
14 }
public class Bird extends Animal implements Flyer {
public void takeOff() { /* implementación de takeOff*/
}
public void land() { /* implementación de despegue*/ }
public void fly() { /* implementation de vuelo*/ }
public void buildNest(){ /* comportamiento de
construer el nido */ }
public void layEggs() { /* comportamiento de lanzar
huevos */ }
public void eat() { /* sobrescribe el comportamiento
de comer */ }
}

1 public class Airport {


2 public static void main(String[] args) {
3 Airport metropolisAirport = new Airport();
4 Helicopter copter = new Helicopter();
5 SeaPlane sPlane = new SeaPlane();
7 metropolisAirport.givePermissionToLand(copter);
8 metropolisAirport.givePermissionToLand(sPlane);
9 }
10
11 private void givePermissionToLand(Flyer f) {
12 f.land();
13 }
14 }

14.10 Las Interfaces


14.10.2 Ejemplo de Interfaces Múltiples

Una clase puede implementar más de una interfaz. El hidroavión (SeaPlane) no


sólo puede volar sino que también debe navegar. La clase SeaPlane extiende
de la clase Airplane. Por lo tanto, hereda la implementación de la interfaz Flyer.
La clase SeaPlane también implementa la interfaz Sailer (Navegante). Esto se
muestra en la Figura

Ahora escriba la clase Harbor (Puerto) para darle permisos de atracar en el


muelle. (Figura )
El hidroavión (seaplane) puede despegar del aeropuerto de Metrópolis y
aterrizar en el muelle de Boston
14.10 Las Interfaces
14.10.3 Los Usos de las Interfaces

Las interfaces pueden utilizarse para:

• Declarar los métodos que, se espera, una o más clases implementen.


• Mostrar una interfaz de programación de un objeto sin revelar el cuerpo
de la clase (esto puede ser útil cuando se envía un paquete de clases a
otros desarrolladores).
• Capturar la similitud entre clases no relacionadas sin forzarlas a una
relación de clases.
• Simular herencia múltiple declarando una clase que implementa varias
interfaces

14.11 Laboratorios

Laboratorio 7 - Características Avanzadas de las Clases

14.11 Laboratorios
14.11.1 Objetivos
Una vez completado este laboratorio, usted debería ser capaz de:

• Aplicar miembros estáticos de una clase para resolver una decisión de


diseño.
• Crear clases abstractas e interfaces y explorar las propiedades
polimórficas de estos tipos de componentes

14.11 Laboratorios
14.11.2 Ejercicio 1: Aplicación de los Miembros Estáticos a
un Diseño (Nivel 1)

Ejercicio 1 - Aplicación de los Miembros Estáticos a un Diseño (Nivel 1)

14.11 Laboratorios
14.11.3 Ejercicio 1: Aplicación de los Miembros Estáticos a
un Diseño (Nivel 2)

Ejercicio 1 - Aplicación de los Miembros Estáticos a un Diseño (Nivel 2)

14.11 Laboratorios
14.11.4 Ejercicio 1: Aplicación de los Miembros Estáticos a
un Diseño (Nivel 3)

Ejercicio 1 - Aplicación de los Miembros Estáticos a un Diseño (Nivel 3)

14.11 Laboratorios
14.11.5 Ejercicio 2: Trabajo con Interfaces y Clases
Abstractas (Nivel 1)

Ejercicio 2 - Trabajo con Interfaces y Clases Abstractas (Nivel 1)

14.11 Laboratorios
14.11.6 Ejercicio 2: Trabajo con Interfaces y Clases Abstractas (Nivel 2)

Ejercicio 2 - Trabajo con Interfaces y Clases Abstractas (Nivel 2)

14.11 Laboratorios
14.11.7 Ejercicio 2: Trabajo con Interfaces y Clases
Abstractas (Nivel 3)

Ejercicio 2 - Trabajo con Interfaces y Clases Abstractas (Nivel 3)

14.11 Laboratorios
14.11.8 Resumen del Ejercicio
Discusión – Dedique unos minutos para discutir que experiencias, temas o
descubrimientos tuvo durante el ejercicio de laboratorio.

• Experiencias
• Interpretaciones
• Conclusiones
• Aplicaciones

14.12 Preguntas de Repaso

Ahora le proponemos que resuelva el siguiente ejercicio para poner a prueba


sus conocimientos.

Defina por Verdadero o Falso las siguientes afirmaciones según corresponda.

¡Éxitos!
15 Las Excepciones y las Aserciones

Este capítulo cubre las facilidades provistas para el manejo de errores por el
lenguaje de programación Java.

15.1 Repaso del Capítulo Anterior

Los principales temas del capítulo anterior son:

La Palabra Clave static

La palabra clave static declara a los miembros de una clase (atributos, métodos
y clases anidadas) que están asociados con la clase, en lugar de estarlo con
una instancia de ella en particular.

Los Atributos de la Clase

Algunas veces es deseable tener una variable compartida entre todas las
instancias de una clase. Se puede lograr este efecto de compartimiento,
declarando a la variable con la palabra clave static.
Si una variable static no se declara como private, se puede acceder a ella
desde fuera de la clase.

Los Métodos de Clase

Algunas veces se necesita acceder al código del programa cuando no se tiene


una instancia de un objeto particular. Un método que está declarado con la
palabra clave static puede usarse de esta manera y es, algunas veces, llamado
método de clase.
Para acceder a los métodos que son estáticos, se debe usar el nombre de la
clase, en lugar de una referencia a un objeto.
El método main() es un método static porque la JVM no crea una instancia de
la clase cuando se ejecuta el método main.

Los Inicializadores Estáticos

Los bloques de código estáticos se ejecutan solamente una vez, cuando se


carga la clase.

La Palabra Clave final

El lenguaje de programación Java permite aplicar la palabra clave final a las


clases. Si se hace esto, la clase no podrá tener subclases.

Los Métodos Finales

Los métodos declarados como final no pueden ser sobrescritos. Los métodos
señalados como static o private pueden ser optimizados por el compilador
como si fueran señalados final, porque en este caso no se puede aplicar
asignación dinámica.

Las Variables Finales

Si una variable se declara como final, se transforma en una constante.


Una variable final en blanco, que es una variable local, puede ser asignada en
cualquier momento en el cuerpo del método, pero puede hacerse una sola vez.

Los Tipos Enumerados

Un requerimiento común en programación es tener conjuntos finitos de


nombres simbólicos que representan los valores de un atributo.

Las Importaciones Estáticas

Si se tiene que acceder a los miembros estáticos de una clase, entonces es


necesario calificar las referencias con la clase a partir de la cual proviene.

Las Clases Abstractas


El lenguaje de programación Java permite al diseñador de clases especificar
que una superclase declara un método pero que no provee una
implementación para él. A estos métodos se les denomina métodos abstractos.
La implementación de los mismos se realiza en la subclase.

Las Interfaces

Una clase concreta implementa una interfaz definiendo todos los métodos
declarados por la interfaz.
Muchas clases pueden implementar la misma interfaz. Estas clases no
necesitan compartir la misma herencia de clase y, además, una clase puede
implementar más de una interfaz.
La cláusula extends debe incluirse antes de la cláusula de implements.
Una clase puede implementar más de una interfaz.

Los Usos de las Interfaces

• Declarar los métodos que, se espera, una o más clases implementen.


• Mostrar una interfaz de programación de un objeto sin revelar el cuerpo
de la clase (esto puede ser útil cuando se envía un paquete de clases a
otros desarrolladores).
• Capturar la similitud entre clases no relacionadas sin forzarlas a una
relación de clases.
• Simular herencia múltiple declarando una clase que implementa varias
interfaces.

15.3 Objetivos

Una vez completado este capítulo, usted debe ser capaz de:

• Definir excepciones.
• Usar las sentencias try, catch y finally.
• Describir las categorías de excepciones.
• Identificar las excepciones comunes.
• Desarrollar programas para manejar sus propias excepciones.
• Usar aserciones.
• Distinguir los usos apropiados e inapropiados de las aserciones.
• Habilitar aserciones en tiempo de ejecución.

Discusión – Las siguientes preguntas son relevantes al material presentado en


este capítulo:

• ¿Cómo se resuelven los errores en tiempo de ejecución, en la mayoría


de los lenguajes de programación?
• ¿Qué podría suceder si hace suposiciones acerca de cómo trabaja el
código de su programa y esas suposiciones son erróneas?
• ¿Es siempre necesario o deseable dedicar el poder de la CPU probando
aserciones en programas de producción?
15.4 Recursos Adicionales

Recursos adicionales – Las siguientes referencias ofrecen información


adicional sobre los temas descritos en este capítulo:

• The Java Tutorial. [Online]. Disponible en:

http://java.sun.com/docs/books/
tutorial/essential/exceptions/
definition.html

http://java.sun.com/docs/books/
tutorial/essential/exceptions/
try.html

http://java.sun.com/docs/books/
tutorial/essential/exceptions/
catch.html

http://java.sun.com/docs/books/
tutorial/essential/exceptions/
finally.html

15.5 Las Excepciones y las Aserciones

Las excepciones son un mecanismo usado por varios lenguajes de


programación para describir qué hacer cuando sucede algo inesperado. Algo
inesperado es un error de algún tipo, por ejemplo, un método que se invoca
con argumentos no válidos, una conexión de red que falla o la solicitud de un
usuario para abrir un archivo inexistente.

Las aserciones son una manera de probar ciertas suposiciones acerca de la


lógica de un programa. Por ejemplo, si usted cree que en un punto en particular
el valor de una variable será siempre positivo, una aserción puede probarlo.
Las aserciones se usan generalmente para probar suposiciones acerca de la
lógica local del programa dentro de un método y no para probar que se
cumplen resultados externos esperados.

Una característica importante de las aserciones es que pueden ser eliminadas


completamente del código cuando este se ejecuta. Esto posibilita habilitar las
aserciones solamente durante el desarrollo del programa y que estas no se
ejecuten en tiempo de ejecución, cuando el producto final ha sido entregado al
cliente. Esta es una importante diferencia entre aserciones y excepciones.

15.6 Las Excepciones


El lenguaje de programación Java provee dos amplias categorías de
excepciones, conocidas como excepciones verificadas y no verificadas.

a) Las excepciones verificadas son aquellas que se supone que el programador


manejará en el programa y que se originan a partir de condiciones externas
que ocurren en un programa que está ejecutando. Algunos ejemplos pueden
ser: una petición de un archivo que no se encuentra o una falla de la red.

b) Las excepciones no verificadas pueden originarse a partir de condiciones


que representan bugs o de situaciones que resultan difíciles de manejar en
forma razonable por un programa. Se denominan no verificadas porque no es
necesario controlarlas o hacer algo si ocurren.

Las excepciones que se originan de una categoría de situaciones que


probablemente representan bugs se denominan excepciones de tiempo en
ejecución (runtime exceptions). Un ejemplo de una excepción en tiempo de
ejecución es intentar acceder más allá del final de un arreglo. Las excepciones
que se originan como resultado de temas del entorno que
son lo suficientemente raras o graves como para recuperarse se denominan
errores. Un ejemplo de un error es la ejecución de un programa sin memoria
suficiente. Los errores son, con frecuencia, excepciones no verificadas.

La clase Exception es la clase base que representa las excepciones verificadas


y no verificadas. En lugar de terminar el programa, es mejor escribir un código
para manejar la excepción y continuar.

La clase Error es la clase base usada para las excepciones no verificadas,


condiciones de error severas de las cuales no se espera que el programa
intente recuperarse. En la mayoría de los casos, usted debería permitirle al
programa terminar cuando encuentra este tipo de errores, aunque debería
intentar preservar el trabajo del usuario tanto como sea posible.

La clase RuntimeException es la clase base que se usa para las excepciones


no verificadas que pueden originarse como el resultado de bugs del programa.
En la mayoría de los casos, debería terminar el programa cuando uno de estos
se origina. Intentar preservar el trabajo del usuario es una buena idea.

Cuando una excepción ocurre en su programa, el método que encuentra el


error puede manejar la excepción por sí mismo o lanzar la excepción al método
que lo llamó para indicarle que un problema ha ocurrido. Luego, el método que
lo llamó tiene las mismas opciones:

• manejar la excepción o
• lanzarla atrás a quien le llamó.

Si una excepción alcanza el tope de un hilo, entonces ese hilo es destruido.


Este esquema le da al programador la opción de escribir un manejador que
trate a la excepción en un punto apropiado del código.
Usted puede determinar las excepciones verificadas que un método lanza
buscándolas en la API. Algunas veces puede encontrar documentación
describiendo una o más excepciones no verificadas, pero esto generalmente es
innecesario y no puede confiar en que esa información esté listada.

15.6 Las
Excepciones
15.6.1 Ejemplo de una Excepción

El Código en la Figura muestra un programa que agrega todos argumentos


de la línea de comando.

Este programa trabaja perfectamente, si todos los argumentos pasados desde


la línea de comando son enteros.

java AddArguments 1 2 3 4
Sum = 10

El programa de la Figura falla si alguno de los argumentos no es un entero.


15.7 La Sentencia try-catch

El lenguaje de programación Java provee un mecanismo para resolver qué


excepción será lanzada y cómo recuperarse de ella. El Código en la Figura
muestra el programa addArguments modificado, que usa una sentencia
trycatch para atrapar la excepción. En este caso, se trata de la excepción
java.lang. NumberFormat Exception.

Este programa captura la excepción y luego sale con un mensaje de error:

java AddArguments2 1 two 3.0 4


One of the command-line arguments is not an integer.

Manejo Refinado de Excepciones

La sentencia try-catch puede usarse en pequeños segmentos de código. El


Código en la Figura muestra la sentencia try-catch usada dentro de la
iteración. Esto le permite al programa descartar los argumentos de la línea de
comando que no son enteros. Este código demuestra una mejor derivación del
original (AddArguments2).

Este programa captura la excepción para cada argumento no entero de la línea


de comando y genera un mensaje de alerta. Realiza, además, la suma de
todos los enteros válidos:
java AddArguments3 1 two 3.0 4
[two] is not an integer and will not be included in the sum.
[3.0] is not an integer and will not be included in the sum.
Sum = 5

El Uso de Múltiples Sentencias catch

Puede haber múltiples bloques catch después de un bloque try. Cada uno
maneja un tipo de excepción diferente. El Código en la Figura ilustra la
sintaxis para usar múltiples cláusulas catch.

Si se tienen múltiples cláusulas catch para un bloque try, el orden de ellas es


relevante. Esto se debe a que una excepción lanzada de un bloque try será
manejada por la primera cláusula catch que lo pueda hacer. Si en este ejemplo,
la cláusula catch de Exception se pone al principio, entonces manejará todas
las excepciones y las cláusulas de MyException o MyOtherException nunca se
invocarán.

15.7 La Sentencia try-catch


15.7.1 Ejercicio de try-catch

Ahora le proponemos que resuelva los siguientes ejercicios para poner a


prueba sus conocimientos.
Ejercicios de try - catch

Definir una clase que permita cálculos aritméticos de numeros enteros.

Las opercaciones se selecccionarán mediante un identificador numérico:


1:suma, 2: resta, 3:multiplicación, 4 división.

Definir dentro de la clase los atributos, constructores y métodos necesarios


como así tambien el manejo de excepciones correspondiente

15.7 La Sentencia try-


catch
15.7.2 El Mecanismo de Stack de Llamadas

Si una sentencia lanza una excepción, y esta excepción no es manejada en el


método incluido inmediatamente, entonces la excepción es lanzada al método
que llamó a dicho método. Este proceso continúa. Si la excepción no manejada
llega hasta el método main y este no la maneja, la excepción hace que el
programa finalice en forma anormal.

Considere un caso en el cual el método main() llama a otro método llamado


first(), y este, en su momento, llama a otro método de nombre second(). Si una
excepción ocurre en second() y no es manejada allí, es lanzada hacia first().
Imagine que en first() hay un catch para ese tipo de excepciones. En ese caso,
la excepción es manejada y no avanza más. Sin embargo, si first() no tiene
ningún catch para este tipo de excepciones, el siguiente método en el stack de
llamadas, main(), es verificado. Si la excepción no es manejada en el método
main(), la excepción se despliega en la salida estándar y el programa finaliza
su ejecución

15.7 La Sentencia try-catch


15.7.3 La Cláusula finally

La cláusula finally define un bloque de código que siempre se ejecuta, sin


importar si alguna excepción fue atrapada. El siguiente ejemplo de código y de
descripción son tomados del white paper, Low Level Security in Java, de Frank
Yellin:

1 try {
2 startFaucet();
3 waterLawn();
4 } catch (BrokenPipeException e) {
5 logProblem(e);
6 } finally {
7 stopFaucet();
8 }

En el ejemplo previo, el grifo (faucet) es cerrado (stopFaucet) sin importar si


una excepción ocurre mientras se está abriendo el grifo (startFaucet) o
mientras se riega el césped (waterLawn). El código dentro de los paréntesis del
try se llama código protegido.

La única situación que previene que la cláusula finally se ejecute es que se


realice un shutdown a la máquina virtual (por ejemplo, que se ejecute el método
System.exit o se apague la máquina o se apague su fuente de poder). Esto
implica que el control del flujo puede desviarse de la secuencia normal de
ejecución. Por ejemplo, si se incluye una sentencia return en el código
correspondiente al bloque try, el código en la cláusula finally se ejecuta antes
del return.

15.8 Las Categorías de Excepción

En este módulo se introdujeron tres categorías de excepciones. Esta sección


examina la jerarquía de clases que representan estas categorías.

La clase java.lang.Throwable actúa como la clase padre para todos los objetos
que pueden ser lanzados y atrapados usando el mecanismo de manejo de
excepciones.

Los métodos definidos en la clase Throwable devuelven el mensaje de error


asociado con la excepción y despliegan la traza del stack mostrando dónde
ocurrió la excepción. Hay tres subclases claves de Throwable:

• Error.
• RuntimeException.
• Exception.

Se muestran en la Figura

Usted no debe usar la clase Throwable. Utilice una de las excepciones de


subclases para describir cualquier excepción en particular. A continuación se
describe el propósito de cada excepción:

• El valor Error indica un problema severo del que recuperarse es difícil, si


no, imposible. Un ejemplo es el quedarse sin memoria. No se espera
que un programa maneje estas condiciones.
• El valor RuntimeException indica un problema de diseño o
implementación. Esto es, la excepción indica una condición que nunca
debería de haber ocurrido, si el programa funcionara adecuadamente.
Una NullPointerException, por ejemplo, nunca debería ser lanzada, si el
desarrollador recordara asociar la variable de referencia con un objeto
nuevo o existente en el heap. Dado que un programa, diseñado e
implementado con corrección, nunca emite ese tipo de excepciones,
usualmente se deja sin manejar. Esto resulta en un mensaje en tiempo
de ejecución y asegura la acción que se deberá tomar para corregir el
problema, antes de ocultarlo donde (usted piensa) que nadie lo notará.
• Otras excepciones -que también pueden ser manejadas- indican una
dificultad en tiempo de ejecución causada, con frecuencia, por los
efectos del entorno.

Unos ejemplos son:

• incluir un archivo no encontrado o


• una excepción de URL inválida (el usuario escribió una URL inválida)

Pueden ocurrir usualmente como resultado de algún error del usuario. Le


sugerimos manejar estas excepciones.

15.9 Las Excepciones Comunes

El lenguaje de programación Java provee varias excepciones predefinidas.


Algunas de las más comunes son:

• NullPointerException
Este es un intento de acceder a un atributo o a un objeto de un método,
usando una variable que no referencia a ningún objeto. Un ejemplo es
cuando la variable no ha sido inicializada o cuando ningún objeto ha sido
instanciado.

Employee emp = null;


System.out.println( emp.getName() );

• FileNotFoundException

Esta excepción se produce cuando se intenta leer un archivo que no


existe.

• NumberFormatException

Esta excepción es un intento de analizar una cadena de caracteres


como un número (entero o de punto flotante) que tiene un formato ilegal
de número.

• ArithmeticException

Este es el resultado de dividir entre cero en una operación entre enteros.

int i = 0;
int j = 12 / i;

• SecurityException

Esta excepción es típicamente lanzada por un navegador. La clase


SecurityManager lanza una excepción para applets que intentan realizar
cualquier operación que podría ser peligrosa para el servidor o sus
archivos. Los siguientes son ejemplos de operaciones que pueden
causar excepciones de seguridad:
• Acceder al sistema de archivos local.
• Abrir un socket a un servidor que no es el mismo servidor que sirve el
applet.
• Ejecutar otro programa en un ambiente de ejecución.

15.10 La Regla de Manejo o Declaración

Para alentar la escritura de código robusto, el lenguaje de programación Java


requiere que si alguna excepción verificada (esto es, una subclase de
Exception , pero no una subclase de RuntimeException) ocurriera en cualquier
punto dado del código, el método que contiene este punto debe definir
explícitamente la acción que se tomará si el problema se origina.

Puede hacer lo siguiente para satisfacer este requerimiento:


• Manejar la excepción usando el bloque try-catch-finally: Tener el método
adjunto que maneja la excepción incluyéndole un bloque try-catch
alrededor del posible problema. La cláusula catch debe nombrar o la
clase de la excepción esperada o una superclase. Esto significa manejar
la situación, incluso en el caso en que la cláusula catch esté vacía.
• Declarar las Excepciones que un Métod puede Lanzar: El método al que
se llama tiene que indicar que no maneja la excepción y que esta es
enviada al método que lo lanzó. Esto declara la excepción y le pasa la
responsabilidad de declararla o lanzarla al método que realizó la llamada.
Un método puede declarar que una excepción puede ser lanzada en el
cuerpo del método con una cláusula throws como se describe a
continuación:

void trouble() throws IOException{...}

Después de la palabra clave throws, se incluye una lista con todas las
excepciones que ese método puede lanzar a quien lo llame. Aunque en
este ejemplo se muestra sólo una, se puede usar una lista con múltiples
excepciones, separándolas por comas. Por ejemplo:

void trouble() throws IOException, OtherException {...}

No se necesita declarar excepciones de tiempo de ejecución o errores. Todas


las subclases de RuntimeException y Error son no verificadas y no necesitan
ser incluidas en la cláusula throws de la declaración del método.

Se puede elegir manipular excepciones en tiempo de ejecución. Usualmente la


mayoría de las excepciones en tiempo de ejecución indican un error en la
lógica de programación. Es mejor descubrir estas fallas durante la fase de
prueba y arreglarlas antes de que el código sea puesto en producción. Por lo
tanto, no hay necesidad de incluir cláusulas catch
para las excepciones en tiempo de ejecución. Hay unas pocas excepciones
notables a esta regla. Por ejemplo, es útil atrapar una NumberFormatException
cuando se analiza sintácticamente una cadena de caracteres ingresada por el
usuario.

Elegir si manejar o declarar una excepción depende de si considera que es


usted mismo o quien le llame, el candidato más apropiado para tratarla.
15.11 La Sobrescritura de los Métodos y de las Excepciones

Cuando se sobrescribe un método que lanza excepciones, este puede declarar


sólo excepciones que son de la misma clase o de una subclase de las
excepciones.

Por ejemplo, si el método de la superclase lanza una IOException, entonces el


método que la sobrescribe puede lanzar una IOException, una
FileNotFoundException (una subclase de IOException), pero no una Exception
(la superclase de IOException). Usted puede declarar excepciones más o
menos específicas en la cláusula throws. En el ejemplo de la Figura , se
declaran tres clases: TestA, TestB1 y TestB2. TestA es la superclase de
TestB1 y TestB2.

La clase TestB1 compila porque EOFException es una subclase de


IOException. Sin embargo, la clase TestB2 falla al compilar porque Exception
es una superclase de IOException.

Se permite declarar que el método que sobrescribe lance menos excepciones


que el método de la superclase, incluyendo el hecho de no contener ninguna
excepción. El nuevo conjunto restringido se convertirá en lo que deberá ser
lanzado por cualquiera de las subclases que será creada.
15.12 La Creación de sus Propias Excepciones

Las excepciones definidas por el usuario se crean extendiendo la clase


Exception.

Las clases Exception contienen lo mismo que una clase regular. El siguiente es
un ejemplo de una excepción definida por el usuario conteniendo un
constructor, algunas variables y métodos.

Para lanzar una excepción que usted ha creado, use la sintaxis siguiente:

throw new ServerTimedOutException("Could not connect", 80);

Siempre cree una instancia a la excepción en la misma línea de la cual usted la


lanzará, porque la excepción puede llevar información del número de línea que
será agregado cuando se cree la excepción
15.12 La Creación de sus Propias
Excepciones
15.12.1 El Lanzamiento de una Excepción
Definida por el Usuario

Considere un programa cliente-servidor. En el código del cliente, intente


conectarse al servidor y aguarde la respuesta del servidor por 5 segundos. Si el
servidor no le responde, su código podría lanzar una excepción (como una
definida por el usuario ServerTimedOutException) como se muestra en la
Figura

15.12 La Creación de sus Propias


Excepciones
15.12.2 El Manejo de una Excepción
Definida por el Usuario

Para atrapar su excepción, use la sentencia try (ver Figura )

Puede procesar una excepción parcialmente y luego lanzarla, por ejemplo:

try {
connectMe(defaultServer);
} catch (ServerTimedOutException e) {
System.out.println(“Error caught and rethrown”);
throw e;
}
15.13 Las Aserciones

Las sentencias de aserciones tienen dos formas sintácticas permitidas:

assert <boolean_expression> ;
assert <boolean_expression> : <detail_expression> ;

En ambos casos, si la expresión Boolean se evalúa como false, entonces se


lanza un AssertionError. Este error no debería ser atrapado ya que el programa
debería terminar anormalmente.

Si se usa la segunda forma, entonces la segunda expresión, la cual puede ser


de cualquier tipo, se convierte a String y se usa para complementar el mensaje
que se despliega cuando se reporta la aserción
15.13 Las Aserciones
15.13.1 Usos Recomendados de Aserción

Las aserciones pueden proveer documentación de valor acerca de las


suposiciones o expectativas del programador. Por este motivo, las aserciones
pueden ser especialmente consideradas durante el mantenimiento, cuando
otros programadores están trabajando en el código.

Las aserciones se utilizan comúnmente para verificar la lógica interna de un


método simple o de un pequeño grupo de métodos acoplados. Generalmente,
las aserciones no deberían usarse para verificar que el código está siendo
usado correctamente, sino que se deberían usar para verificar que sus
condiciones internas se cumplen.

Ejemplos del buen uso de aserciones son los invariantes internos, los
invariantes de control de flujo, las post-condiciones y las clases invariantes.

Los Invariantes Internos

Un invariante interno existe cuando una condición o situación ocurre siempre o


nunca y, por lo tanto, se codifica el programa de esa manera. Por ejemplo,
considere el siguiente código:

1 if (x > 0) {
2 // do this
3 } else {
4 // do that
5 }

Si se supone que x tiene el valor 0 y nunca es negativo y x aún toma un valor


negativo, entonces el código debe realizar algo no planeado. Casi con certeza,
el comportamiento de x no es el correcto. Para peor, dado que el código no se
detiene cuando reporta el problema, no encontrará nada acerca del problema
hasta mucho más tarde cuando ocurra un problema mucho mayor. El lugar
donde se origina el problema se volverá muy difícil de determinar.

En este caso, agregar una sentencia de aserción al inicio del bloque else
ayudará a determinar durante el mantenimiento que esta suposición es correcta.
Con la aserción en este lugar, el código debería quedar así:

1 if (x > 0) {
2 // do this
3 } else {
4 assert (x == 0);
5 // realiza esto a menos que x sea negativo
6 }

Los Invariantes en el Control del Flujo

Los invariantes de control de flujo describen suposiciones similares a las


suposiciones de invariantes internos, pero están relacionados al camino del
control del flujo, más que al valor o a la relación entre las variables.

Por ejemplo, en una sentencia switch, se podría creer que se ha enumerado


cada valor posible de la variable de control y, por lo tanto, una sentencia default
nunca se ejecutará. Esto se verifica agregando una sentencia de aserción
como la que se muestra en la Figura

Las Post-condiciones y las Clases Invariantes

Las post-condiciones son suposiciones acerca del valor o la relación de las


variables al completarse un método. Un ejemplo simple de una prueba de post-
condición debería estar después de un método pop de un stack. El stack
debería contener un elemento menos que cuando se llamó al método, a menos
que ya estuviera vacío. Esto se puede codificar como se muestra en la Figura

Fíjese que si el método pop no lanzara una excepción cuando se lo llama con
un stack vacío, entonces sería muy difícil de expresar la aserción, porque con
un tamaño original de cero resultaría en un tamaño final de cero. También
fíjese que la prueba de precondición, que es la prueba que determina si el
método es llamado con un stack vacío, no usa una
aserción. Esto es porque no es un error en la lógica local de un método si dicha
situación ocurre, sino que es un error en cómo el objeto está siendo usado.
Dicha prueba en el comportamiento externo debería, en general, no usar
aserciones, pero debería usar excepciones simples. Esto asegura que la
prueba es siempre hecha y que no puede deshabilitarse como en el caso de
las aserciones.

Una clase invariante es algo que puede ser probado al final de la llamada de
cada método para la clase. Para el ejemplo de una clase de stack, una
condición invariante es una en la cual la cantidad de elementos no es nunca
negativa.

Usos Inapropiados de Aserciones

No use aserciones para verificar los parámetros de un método público. En lugar


del método debería verificar cada parámetro y lanzar una excepción apropiada,
tal como IllegalArgumentException o NullPointerException. La razón de por
qué no debería usar aserciones para la verificación de los parámetros, es
porque el mecanismo de aserción puede ser apagado, pero todavía puede
querer realizar la verificación de los parámetros.

No use métodos en la verificación de las aserciones, que puedan causar


efectos laterales, porque la verificación de la aserción puede ser apagada en
tiempo de ejecución. Si su aplicación depende de esos efectos laterales,
entonces su aplicación exhibirá diferentes comportamientos cuando la
verificación de la aserción sea apagada.
15.13 Las
Aserciones
15.13.2 El Control de Evaluación de las Aserciónes en
Tiempo

Una de las mayores ventajas de usar aserciones sobre excepciones es que la


verificación de las aserciones puede ser deshabilitada en tiempo de ejecución.
Si esto se realiza, entonces no hay una sobrecarga de tiempo implicada en la
verificación de las aserciones y el código ejecutará tan rápido como si la prueba
de aserción no estuviera presente. Esto es mejor que usar una compilación
condicional por dos razones.

Primero, no requiere una fase de compilación diferente para producción.


Segundo, si se requiere, las aserciones pueden ser habilitadas nuevamente
para determinar si algunas de ellas se han vuelto inválidas debido a algunos
efectos del ambiente imprevisto.

Por defecto, las aserciones están deshabilitadas. Para habilitar las aserciones,
use cualquiera de estas formas:

java -enableassertions MyProgram


java -ea MyProgram
Las aserciones pueden ser controladas en paquetes, herencia de paquetes o
clases individuales

15.14 Laboratorios

Laboratorio 8 - Las Excepciones y las Aserciones

15.14 Laboratorios
15.14.1 Objetivos

Una vez completado este laboratorio, usted debería poder crear una excepción
de una aplicación y aplicar la regla para declarar o manejar a las clases de
Account.

15.14 Laboratorios
15.14.2 Ejercicio: Creación de su Propia Excepción (Nivel 1)

Ejercicio - Creación de su Propia Excepción (Nivel 1)

15.14 Laboratorios
15.14.3 Ejercicio: Creación de su Propia Excepción (Nivel 2)

Ejercicio - Creación de su Propia Excepción (Nivel 2)

15.14 Laboratorios
15.14.4 Ejercicio: Creación de su Propia Excepción (Nivel 3)

Ejercicio - Creación de su Propia Excepción (Nivel 3)

15.14 Laboratorios
15.14.5 Resumen del Ejercicio

Discusión – Dedique unos minutos para discutir qué experiencias, temas o


descubrimientos realizó durante el ejercicio de laboratorio.

• Experiencias
• Interpretaciones
• Conclusiones
• Aplicaciones

15.15 Preguntas de Repaso


16 Las Aplicaciones Basadas en Texto

Este capítulo cubre una variedad de temas que ampliarán su conocimiento del
Java 2 SDK. Abarca:

• La parametrización del comportamiento de un programa en tiempo de


ejecución.
• La lectura y grabación de archivos de texto.
• El manejo de colecciones

16.1 Repaso del Capítulo Anterior

Los principales temas del capítulo anterior son:

Las Excepciones y las Aserciones

Las excepciones son un mecanismo usado por varios lenguajes de


programación para describir qué hacer cuando sucede algo inesperado.
Las aserciones son una manera de probar ciertas suposiciones acerca de la
lógica de un programa.
Una característica importante de las aserciones es que pueden ser eliminadas
completamente del código cuando este se ejecuta.

Las Excepciones

Java provee dos amplias categorías de excepciones, conocidas como


excepciones verificadas y no verificadas.

Las excepciones verificadas son aquellas que se supone que el programador


manejará en el programa y que se originan a partir de condiciones externas
que ocurren en un programa que está ejecutando.
Las excepciones no verificadas pueden originarse a partir de condiciones que
representan bugs o de situaciones que resultan difíciles de manejar en forma
razonable por un programa.
Las excepciones que se originan de una categoría de situaciones que
probablemente representan bugs se denominan excepciones de tiempo en
ejecución (runtime exceptions). Un ejemplo de una excepción en tiempo de
ejecución es intentar acceder más allá del final de un arreglo.
La clase Exception es la clase base que representa las excepciones verificadas
y no verificadas.
La clase Error es la clase base usada para las excepciones no verificadas,
condiciones de error severas de las cuales no se espera que el programa
intente recuperarse.
La clase RuntimeException es la clase base que se usa para las excepciones
no verificadas que pueden originarse como el resultado de bugs del programa.

La Sentencia try-catch

El lenguaje de programación Java provee un mecanismo para resolver qué


excepción será lanzada y cómo recuperarse de ella.
La sentencia try-catch puede usarse en pequeños segmentos de código.

public class AddArguments2 {


public static void main (String args[ ]) {
try {
int sum = 0;
for ( int i = 0; i < args.length; i++) {
sum += Integer.parseInt (args[i]); }
System.out.prinln(“Sum = “ + sum);
} catch (NumberFormatException nfe) {
System.err.prinln (“One of the command-line “ + “arguments is not an integer.”);
}
}
}

Este programa captura la excepción para cada argumento no entero de la línea


de comando y genera un mensaje de alerta.
Puede haber múltiples bloques catch después de un bloque try.
Si se tienen múltiples cláusulas catch para un bloque try, el orden de ellas es
relevante. Esto se debe a que una excepción lanzada de un bloque try será
manejada por la primera cláusula catch que lo pueda hacer.

El Mecanismo de Stack de Llamadas

Si una sentencia lanza una excepción, y esta excepción no es manejada en el


método incluido inmediatamente, entonces la excepción es lanzada al método
que llamó a dicho método.
Si la excepción no manejada llega hasta el método main y este no la maneja, la
excepción hace que el programa finalice en forma anormal.

La Cláusula finally

La cláusula finally define un bloque de código que siempre se ejecuta, sin


importar si alguna excepción fue atrapada.

Las Categorías de Excepción

La clase java.lang.Throwable actúa como la clase padre para todos los objetos
que pueden ser lanzados y atrapados usando el mecanismo de manejo de
excepciones.

Las Excepciones Comunes

El lenguaje de programación Java provee varias excepciones predefinidas.


Algunas de las más comunes son:

NullPointerException
FileNotFoundException
NumberFormatException
ArithmeticException
SecurityException

La Regla de Manejo o Declaración

Para alentar la escritura de código robusto, el lenguaje de programación Java


requiere que si alguna excepción verificada (esto es, una subclase de
Exception , pero no una subclase de RuntimeException) ocurriera en cualquier
punto dado del código, el método que contiene este punto debe definir
explícitamente la acción que se tomará si el problema se origina.

La Sobrescritura de los Métodos y de las Excepciones

Cuando se sobrescribe un método que lanza excepciones, este puede declarar


sólo excepciones que son de la misma clase o de una subclase de las
excepciones

La Creación de sus Propias Excepciones


Las excepciones definidas por el usuario se crean extendiendo la clase
Exception.

El Manejo de una Excepción Definida por el Usuario

Para atrapar su excepción, use la sentencia try, puede procesar una excepción
parcialmente y luego lanzarla, por ejemplo:

try {
connectMe(defaultServer);
} catch (ServerTimedOutException e) {
System.out.println(“Error caught and rethrown”);
throw e;
}

Las Aserciones

Las sentencias de aserciones tienen dos formas sintácticas permitidas:

assert <boolean_expression> ;
assert <boolean_expression> : <detail_expression> ;

En ambos casos, si la expresión Boolean se evalúa como false, entonces se


lanza un AssertionError.
Este error no debería ser atrapado ya que el programa debería terminar
anormalmente.

Usos Recomendados de Aserción

Las aserciones se utilizan comúnmente para verificar la lógica interna de un


método simple o de un pequeño grupo de métodos acoplados. Generalmente,
las aserciones no deberían usarse para verificar que el código está siendo
usado correctamente, sino que se deberían usar para verificar que sus
condiciones internas se cumplen.

Ejemplos del buen uso de aserciones son los invariantes internos, los
invariantes de control de flujo, las post-condiciones y las clases invariantes.

16.3 Objetivos

Una vez completado este capítulo, usted debería ser capaz de:

• Escribir un programa que use argumentos desde la línea de comandos y


propiedades del sistema.
• Escribir un programa que lea desde la entrada estándar.
• Describir la entrada y salida con formato al estilo del lenguaje C (C-type
formatted).
• Escribir un programa que pueda crear, leer y escribir archivos.
• Describir la jerarquía básica de las colecciones en el Java 2 Software
Development Kit (Java 2 SDK).
• Escribir un programa que use conjuntos (sets) y listas (lists).
• Escribir un programa para iterar sobre una colección.
• Escribir un programa que use colecciones genéricas (generic collections).

Discusión – Las siguientes preguntas son relevantes al material presentado en


este capítulo:

• Frecuentemente los programadores se enfrentan al problema de que


ciertos valores literales constantes no deberían incluirse como tales en
un programa (hard coded). Por ejemplo: nombres de archivos, nombre
de una base de datos, etc. ¿Cómo se puede codificar un programa para
suministrar estos elementos en tiempo de ejecución?
• Los arreglos simples son demasiado estáticos para la mayoría de las
colecciones (esto es, un número fijo de elementos). ¿Qué características
de la tecnología Java existen para soportar colecciones más flexibles?
• ¿Qué otros elementos, además de los cálculos propiamente dichos, son
clave en cualquier aplicación basada en texto?

16.4 Recursos Adicionales

Recursos adicionales – Las siguientes referencias ofrecen información


adicional sobre los temas descritos en este capítulo:

• The Java Tutorial. [Online]. Disponible en:

http://java.sun.com/docs/books/
tutorial/essential/
io/cl.html

http://java.sun.com/docs/books/
tutorial/essential/
io/file.html

http://java.sun.com/docs/books/
tutorial/essential/
io/scanning.html

http://java.sun.com/docs/books/
tutorial/essential/
collections/index.html

16.5 Los Argumentos desde la Línea de Comando

Cuando un programa basado en la tecnología Java se ejecuta desde una


ventana de comandos, se le pueden pasar cero o más argumentos desde la
línea de comandos. Ellos son cadenas de caracteres: una única cadena, como,
por ejemplo, arg1, o cadenas de caracteres entre comillas, como, por ejemplo,
"otro argumento".

La secuencia de argumentos se escribe a continuación del nombre de la clase


del programa y se almacena en un arreglo de objetos String pasados al método
estático main. (ver Figura )

Este programa despliega cada argumento pasado al programa TestArgs desde


la línea de comandos. Por ejemplo, la ejecución del programa anterior con los
siguientes argumentos produce la salida que se detalla:

java TestArgs arg1 arg2 "another arg"


args[0] is 'arg1'
args[1] is 'arg2'
args[2] is 'another arg'

16.6 Las Propiedades del Sistema

Las propiedades del sistema son otro mecanismo para indicar parámetros en
un programa en tiempo de ejecución. Una propiedad es una correspondencia
entre un nombre de propiedad y un valor. Ambos elementos son cadenas de
caracteres. La clase Properties representa esta clase de correspondencias. El
método System.getProperties devuelve el objeto de las propiedades del
sistema. El método System.getProperty(String) devuelve el valor, como cadena
de caracteres, de la propiedad cuyo nombre es el explicitado en el parámetro
String. El método System.getProperty(String, String) ofrece un valor por defecto
(el segundo parámetro), el cual es devuelto en caso de que el nombre de la
propiedad pasada como primer argumento no exista en el sistema.

Hay también miembros estáticos en las clases de envoltura que realizan


conversiones de los valores de las propiedades: Boolean.getBoolean(String),
Integer.getInteger(String) y Long.getLong(String). El argumento de tipo String
es el nombre de la propiedad. Si la propiedad no existe, entonces devolverá
false o null (respectivamente).

16.7 La Clase Properties

Un objeto de la clase Properties contiene una correspondencia entre los


nombres de la propiedad (String) y sus valores (String). Tiene dos métodos
principales para devolver la propiedad de un valor: getProperty(String) y
getProperty(String, String). Este último ofrece la posibilidad de especificar un
valor por defecto, el cual será devuelto si el nombre de la propiedad pasado
como parámetro no existe.

Puede iterar a través de un conjunto completo de nombres de propiedades


usando el método propertyNames y, llamando a getProperty en cada nombre,
puede devolver todos los valores.
Finalmente, el conjunto de propiedades puede ser almacenado y recuperado
de cualquier flujo de entrada/salida usando los métodos load y store. El
programa en la Figura lista el conjunto completo de propiedades que existe
en el momento en que se ejecuta el programa

La Línea 6 obtiene el conjunto de propiedades del sistema y la Línea 7


recupera una enumeración de todos los nombres de las propiedades en el
conjunto de propiedades.

Se utiliza un objeto Enumeration para iterar sobre los elementos en


una colección. Este es muy similar a un Iterator, el cual se describe en
"Iteradores", apartado 9.10

El método hasMoreElements devuelve el valor true si hay más elementos sobre


los cuales iterar y el método nextElement devuelve el siguiente elemento en la
enumeración.

La Línea 11 devuelve el valor de la propiedad y las líneas 12-13 despliegan el


nombre y el valor de la propiedad.

java -DmyProp=theValue TestProperties

La Figura es un extracto de la salida

1 import java.util.Properties;
2 import java.util.Enumeration;
3
4 public class TestProperties {
5 public static void main(String[] args) {
6 Properties props = System.getProperties();
7 Enumeration propNames = props.propertyNames();
8
9 while ( propNames.hasMoreElements() ) {
10 String propName = (String)
propNames.nextElement();
11 String property = props.getProperty(propName);
12 System.out.println("property '" + propName
13 + "' is '" + property + "'");
14 }
15 }
16 }
16.8 La Consola de EntradaSalida

La mayoría de las aplicaciones deben interaccionar con el usuario. Algunas


veces, esta interacción se lleva a cabo a través de la entrada de un texto y de
la salida en la consola (usando el teclado como entrada estándar y la ventana
de comandos como la salida estándar).

Java 2 SDK soporta una consola de Entrada/Salida (E/S) con tres variables
públicas, en la clase java.lang.System:

• La variable System.out es un objeto PrintStream que referencia


(inicialmente) a la ventana de comandos que lanza la aplicación de
tecnología Java.
• La variable System.in es un objeto InputStream que referencia
(inicialmente) al teclado del usuario.
• La variable System.err es un objeto PrintStream que referencia
(inicialmente) a la ventana de comandos que lanzó la aplicación de
tecnología Java.

Es posible cambiar esta asignación de flujos usando los métodos static:

System.setOut, System.setIn y System.setErr.


Por ejemplo, se puede cambiar el flujo de error estándar a un flujo de archivo.

16.8 La Consola de
EntradaSalida
16.8.1 La Escritura a la Salida Estándar

Se puede escribir a la salida estándar a través del método


System.out.println(String). Este método de la clase PrintStream despliega el
argumento String a la consola y agrega un carácter de nueva línea al final. Los
siguientes métodos son también soportados para desplegar otros tipos:
primitivos, un buffer de caracteres y un objeto arbitrario. Todos agregan un
carácter de nueva línea al final de la salida.

void println(boolean)
void println(char)
void println(double)
void println(float)
void println(int)
void println(long)
void println(char[])
void println(Object)

Existe un conjunto de métodos sobrecargados, denominados print, que no


agregan el carácter de nueva línea.
16.8 La Consola de
EntradaSalida
16.8.2 La Lectura de la Entrada Estándar

El ejemplo en la Figura muestra una técnica que se debería usar para leer
información en un objeto String desde la consola de entrada estándar

La Línea 5 declara una variable String de nombre s, que el programa usa para
mantener cada línea leída de la entrada estándar. La Líneas 8-10 asocian el
System.in con dos objetos de soporte que manejan el flujo de bytes
provenientes de la entrada estándar. El objeto ir de la calse InputStreamReader
lee caracteres y los convierte en bytes en
caracteres Unicode. El objeto in asociado a la clase BufferedReader provee el
método readLine, el cual permite al programa leer desde la entrada estándar de
una línea a la vez.

El código para KeyboardInput continúa en la Figura

La Línea 15 lee la primera línea del texto desde la entrada estándar. La


iteración while (Líneas 16-19) despliega en forma iterativa la línea actual y lee
la línea siguiente. Este código puede ser reescrito de forma más concisa (pero
más críptica) como (ver Figura )
Dado que el método readLine puede lanzar una excepción de E/S, se debe
incluir todo el código en un bloque try-catch. (ver Figura )

La Línea 23 cierra el flujo de entrada más lejano para liberar cualquier recurso
del sistema relacionado con crear ese flujo de objetos. (ver Figura )

Finalmente, el programa maneja cualquier excepción de I/O que pueda ocurrir.


16.8 La Consola de
EntradaSalida
16.8.3 La Salida con Formato Simple

El lenguaje de programación Java versión 1.5 provee la funcionalidad de printf,


al estilo del lenguaje C. Este ofrece salida formateada estándar desde el
programa, la que permite a los programadores migrar código heredado. Se
puede usar el método printf siguiendo la sintaxis normal de C y C++. Esta
funcionalidad de formateo está también disponible en la
clase String en el método format. Se puede usar el método printf como sigue:

System.out.printf("name count\n");
String s = String.format("%s %5d%n", user, total);

La Tabla en la Figura muestra algunos códigos de formato comunes.

Se puede usar %n para los caracteres de nueva línea, en lugar del \n, para la
independencia de plataforma.
16.8 La Consola de EntradaSalida
16.8.4 La Entrada con Formato Simple

La clase Scanner ofrece la funcionalidad de entrada formateada, que es parte


del paquete java.util. Dicho paquete ofrece métodos para obtener valores
primitivos y para realizar bloqueos mientras espera por una entrada desde el
usuario. Un ejemplo se muestra en el Código de la Figura .

En el Código dela Figura , la Línea 5 crea una referencia a Scanner


pasándole directamente la entrada de la consola. La línea 6 recupera el valor
de la cadena de caracteres desde la entrada dada usando el método next de
Scanner. La línea 8 obtiene el valor entero de la entrada dada usando el
método nextInt. La línea 10 cierra la entrada para el usuario.

16.9 Los Archivos de EntradaSalida (ES)

La E/S es uno de los más importantes elementos de programación. La


tecnología Java incluye un rico conjunto de flujos de E/S. En la sección previa
se mostró cómo usar los flujos para comunicarse con el usuario a través de la
salida estándar (usualmente una ventana de comandos) y la entrada estándar
(usualmente el teclado). Esta sección examina varias
técnicas simples para leer y escribir archivos con foco en los datos de los
caracteres, incluyendo:

• Creación de objetos File.


• Manipulación de objetos File.
• Lectura y escritura al flujo de archivo.

16.9 Los Archivos de EntradaSalida


(ES)
16.9.1 La Creación de un Nuevo Objeto
File

La clase File provee varias utilidades para manejar archivos y obtener


información de ellos. En la tecnología Java, un directorio es otro archivo. Puede
crear un objeto File que representa un directorio y luego usarlo para identificar
otros archivos, como se muestra en la tercera viñeta.

• File myFile;
myFile = new File("myfile.txt");
• myFile = new File("MyDocs", "myfile.txt");
• File myDir = new File("MyDocs");
myFile = new File(myDir, "myfile.txt");

El constructor que se usa, frecuentemente depende del objeto archivo al que se


quiera acceder.

Por ejemplo, si se usa sólo un archivo en la aplicación, se utiliza el primer


constructor. Sin embargo, si se utilizan varios archivos de un directorio común,
se sugiere usar el segundo o el tercer constructor, ya que debería ser más fácil.

La clase File define métodos independientes de la plataforma para manipular


un archivo mantenido por un sistema de archivos nativos. Sin embargo, no se
le permite acceder al contenido del archivo.
16.9 Los Archivos de EntradaSalida
(ES)
16.9.2 Las Utilidades y Pruebas de File

Después de haber creado un objeto File, se puede usar cualquier método en la


siguiente sección para recoger información acerca del archivo.

El Nombre de Archivo

Los siguientes métodos retornan nombres de archivos:

• String getName()
• String getPath()
• String getAbsolutePath()
• String getParent()
• boolean renameTo(File newName)

Las Utilidades de Directorio

Los siguientes métodos proveen utilidades de directorio:

• boolean mkdir()
• String[] list()
La Información General de Archivo y Utilidades

Los siguientes métodos retornan información general de archivos:

• long lastModified()
• long length()
• boolean delete()

La Verificación de Archivos

Los siguientes métodos devuelven información acerca de los atributos del


archivo:

• boolean exists()
• boolean canWrite()
• boolean canRead()
• boolean isFile()
• boolean isDirectory()
• boolean isAbsolute()

16.9 Los Archivos de EntradaSalida (ES)


16.9.3 El Flujo de ES de Archivos

La plataforma J2SE soporta entrada de archivos de dos formas:

• Usando la clase FileReader para leer caracteres.


• Usando la clase BufferedReader para usar el método readLine.

La plataforma J2SE soporta la salida de archivos de dos formas:

• Usando la clase FileWriter para escribir caracteres.


• Usando la clase PrintWriter para usar los métodos print y println.

Ejemplo de la Entrada desde un Archivo

El código en la Figura lee un archivo de texto y reproduce cada línea a la


salida estándar, la cual despliega el archivo.

La Línea 5 crea un nuevo objeto File basado en el primer argumento pasado


desde la línea de comando. Las Líneas 10 y 11 crean un buffered reader que
contiene un lector de
archivos. Este código lanza una FileNotFoundException, si el archivo no existe.

El Código de la Figura lee un archivo de texto y reproduce cada línea a la


salida estándar. De esta manera, se despliega el archivo. En éste Código, la
iteración while en las Líneas 15-19 realiza exactamente lo mismo que el
programa KeyboardInput. Este lee cada línea de texto en el buffered reader y lo
reproduce en la salida estándar.La Línea 21 cierra el buffered reader, que, a su
vez, cierra el lector de archivo asociado al buffer del archivo.El código de
manejo de excepción entre las Líneas 23-25 atrapa la excepción
FileNotFoundException que puede ser lanzada por el constructor FileReader.
Las Líneas 27-30 manejan cualquier otra excepción basada en I/O que pueda
ser lanzada (por los métodos readLine y close).

Ejemplo de Salida a Archivo

El Código de la Figura lee líneas de la entrada desde el teclado y envía


cada una de ellas a un archivo.

Al igual que el Código de la Figura , la Línea 6 del Código de la Figura


crea un objeto File, basado en el primer argumento de la línea de comandos.
Las Líneas 10-11 crean un flujo de lectura de caracteres desde el flujo binario
(System.in). Las Líneas 12-13 crean un buffered reader para la entrada
estándar. Las Líneas 15-16 crean un print writer asociado a un file writer para
el archivo creado en la Línea 6.

Las Líneas 19-20 (ver Figura ) indican al usuario que debe ingresar las
líneas de texto que serán grabadas en el archivo y que presione Control-d para
finalizar dicho ingreso. Las Líneas 23-25 leen del flujo de entrada y graban en
el archivo, de una línea por vez.

Las Líneas 28 y 29 cierran los flujos de entrada y salida. Las Líneas 31-33
manejan las excepciones de I/O que deben ser lanzadas. (ver Figura )

14 // Lee cada linea del archivo


15 s = in.readLine();
16 while (s != null) {
17 System.out.println("Read: " + s);
18 s = in.readLine();
19 }
20 // Cierra el buffered reader
21 in.close();
22
23 } catch (FileNotFoundException e1) {
24 // Si este archivo no existe
25 System.err.println("File not found: " + file);
26
27 } catch (IOException e2) {
28 // Atrapa cualquier otra excepcion IO.
29 e2.printStackTrace();
30 }
31 }
32 }

1 import java.io.*;
2
3 public class WriteFile {
4 public static void main (String[] args) {
5 // Crear Archivo
6 File file = new File(args[0]);
7
8 try {
9 // Crea un buffered reader
10 InputStreamReader isr
11 = new InputStreamReader(System.in);
12 BufferedReader in
13 = new BufferedReader(isr);
14 // Crea un escritor de la impresora en este archivo.
15 PrintWriter out
16 = new PrintWriter(new FileWriter(file));
17 String s;
16.10 La API de Colecciones

Una colección es un objeto simple que representa un grupo de objetos,


llamados elementos. Típicamente, las colecciones tratan con muchos objetos,
todos de un tipo en particular (esto es, todos descienden del mismo tipo padre)

• Collection - Un grupo de objetos, conocidos como elementos. La


implementación determina si hay un orden específico y si se permiten
elementos duplicados.
• Set - Una colección desordenada en la que no se admiten elementos
duplicados.
• List - Una colección ordenada en la que se admiten elementos
duplicados.

Las colecciones mantienen referencias a objetos del tipo Object. Esto permite
que cualquier objeto pueda guardarse en una colección. También necesita el
uso de una correcta conversión de tipos antes de usar el objeto y luego de
recuperarlo de la colección.

La Figura muestra un diagrama UML primario con las interfaces yl las clases
de implementación de la API de colecciones.La clase HashSet da una
implementación de la interfaz Set. Las clases ArrayList y LinkedList suministran
una implementación de la interfaz List.
16.10 La API de Colecciones
16.10.1 Un Ejemplo Set

En el ejemplo de la Figura , el programa declara una variable (set) del tipo


Set y la inicializa con un nuevo objeto HashSet. Luego, le agrega algunos
elementos y envía el conjunto a la salida estándar. El intento de agregar los
ítems duplicados en las líneas 11 y 12 falla; de esa manera, el método add
devuelve el valor false.

La salida generada del programa debe ser:

[one, second, 5.0, 3rd, 4]


16.10 La API de
Colecciones
16.10.2 Un Ejemplo de List

En el siguiente ejemplo, el programa declara una variable (list) del tipo List y es
inicializada a un nuevo objeto ArrayList. Este, luego agrega unos pocos
elementos y despliega la lista en la salida estándar. Dado que las listas le
permiten duplicados, las Líneas 11 y 12 son exitosas.

La salida generada desde el programa es:

[one, second, 3rd, 4, 5.0, second, 4]


16.10 La API de Colecciones
16.10.3 Las Colecciones en la JDK Versión 1.1

La API de Colecciones es una característica mejorada de la Java 2 SDK,


aunque existían unas pocas clases de colecciones en la JDK versión 1.0 y JDK
versión 1.1. Estas clases todavía existen en el SDK con la misma interfaz, pero
han sido actualizadas para interaccionar con la nueva API de colecciones.

La clase Vector implementa la interfaz List. La clase Stack es una extensión de


Vector que agrega las típicas operaciones de stack: push, pop y peek.
HashTable es una implementación de Map. La clase Properties (vista en
"Propiedades del Sistema" en el apartado 9.4) es una extensión de HashTable
que sólo usa String para las claves y los valores.

Cada una de estas colecciones tiene un método elements que devuelve un


objeto Enumeration. La interfaz Enumeration es una interfaz similar a la interfaz
Iterator, pero incompatible con ella. Por ejemplo, hasNext es reemplazado por
hasMoreElements en la interfaz Enumeration.

16.11 Los Genéricos

Las clases de Collection usan el tipo Object para permitir diferentes entradas y
tipos de retorno. Usted necesita convertirlos explícitamente para obtener el
objeto que necesita. Esto no es seguro. La solución para este problema es
hacer uso de la funcionalidad genérica. Esta provee información para el
compilador acerca del tipo de colección usada. Por lo tanto, la verificación de
tipos está resuelta automáticamente en tiempo de ejecución. Esto elimina la
conversión explícita de los tipos de datos a ser usados en la colección. Con la
adición del autoboxing a los tipos primitivos, se pueden usar genéricos y
escribir un código más simple y comprensible.

Antes de usar genéricos, el código podría aparecer como:

ArrayList list = new ArrayList();


list.add(0, new Integer(42));
int total = ((Integer)list.get(0)).intValue();

En el segmento de código previo se necesita una clase de envoltura Integer


para convertir los tipos (typecasting) mientras traen el valor entero de la lista.
En tiempo de ejecución, el programa necesita verificar el tipo para la lista. El
ArrayList debería haberse declarado como ArrayList<Integer>, de manera que
el compilador esté informado sobre el tipo de la colección a ser usada. Mientras
que, al recuperar el valor, no hay necesidad de una clase de envoltura Integer.

El uso de genéricos para este mismo código se describe aquí:


ArrayList<Integer> list = new ArrayList<Integer>();
list.add(0, new Integer(42));
int total = list.get(0).intValue();

La facilidad de autoboxing y la mejora para la iteración se adecuan


perfectamente a la API Generic. Usando autoboxing este ejemplo de código
podría ser escrito como:

ArrayList<Integer> list = new ArrayList<Integer>();


list.add(0, 42);
int total = list.get(0);

La Figura muestra un diagrama UML de las interfaces primarias y la


implementación de las clases para la API de Colecciones.
16.11 Los Genéricos
16.11.1 Las Advertencias del Compilador

Cuando usa el nuevo compilador de J2SE versión 5 con código que ya tenía
escrito o no genérico, usted debe ver una advertencia (warning) especial. Una
clase de ejemplo que envía esta advertencia es la ilustrada en el Código de la
Figura

Si usted solamente compila esta clase usando el siguiente comando:

javac GenericsWarning.java

verá la siguiente advertencia:

Note: GenericsWarning.java uses unchecked or unsafe operations.


Note: Recompile with -Xlint:unchecked for details.

Si compila esta clase usando el siguiente comando:

javac -Xlint:unchecked GenericsWarning.java

verá la siguiente advertencia:

GenericsWarning.java:5: warning: [unchecked] unchecked call


to add(int,E) as a member of the raw type java.util.List
list.add(0, new Integer(42));
^
1 warning

Estas son solo advertencias. La clase compila bien y esas advertencias pueden
ser ignoradas. Sin embargo, puede usarlas para recordarle de modificar su
código para que este sea un código generic-friendly. Para resolver esta
advertencia, necesitará cambiar la Línea 4:List<Integer> list = new
ArrayList<Integer>();
16.12 Los Iteradores

Usted puede explorar una colección usando un iterador. La interfaz básica


Iterator le permite explorar hacia adelante una colección. En el caso de una
iteración sobre un conjunto, el orden no es determinante.. El orden de una
iteración sobre una lista que se mueve hacia adelante a través de los
elementos. Un objeto List también soporta un ListIterator, el cual permite
recorrer la lista hacia atrás.

El siguiente fragmento de código demuestra el uso de un iterador.

List list = new ArrayList();


// agrega algunos elementos
Iterator elements = list.iterator();
while ( elements.hasNext() ) {
System.out.println (elements. next());
}

La Figura muestra un diagrama UML de la interfaz genérica Iterator para la


API de Colecciones.

El método remove permite al código borrar el ítem actual en la iteración (el ítem
devuelto por el método next o previous más reciente). Si el método remove no
es soportado por la colección subyacente, entonces se produce una excepción
de tipo Unsupported OperationException.

Mientras se usa una ListIterator, es común moverse a través de la lista sólo en


una dirección: o hacia delante usando next o hacia atrás usando previous. Si
usa el previous inmediatamente después del next, entonces recibirá el mismo
elemento; así como si se llama a next después de previous.

El método set cambia el elemento de la colección que está siendo referenciado


por el cursor del iterador. El método add inserta el nuevo elemento dentro de la
colección inmediatamente antes del cursor del iterador. Por lo tanto, si llama a
previous después de add, entonces devolverá el elemento recién agregado. Sin
embargo, una llamada a next no se verá afectada. Si la colección subyacente
no soporta el método set o el método add, entonces lanzará una excepción del
tipo Unsupported OperationException.

16.12 Los Iteradores


16.12.1 La Iteración for Mejorada

Se puede iterar a través de una colección en una forma mucho más simple con
el uso de las iteraciones for mejoradas. Las iteraciones for mejoradas hacen el
recorrido a través de una colección de manera simple, clara y segura. Los
iteradores son propensos a errores por las siguientes razones:
• Las variables de los iteradores ocurren tres veces por iteración.
• Esto crea la oportunidad al código de volverse erróneo.

Una iteración for mejorada supera estas fallas asignando directamente el objeto
Collection a la variable iterator en la iteración for. Se puede usar al recorrer los
elementos de un arreglo además de eliminar la necesidad del elemento índice
mientras recorre los elementos de un arreglo. El uso de las iteraciones for
mejoradas comparada con los iteradores se ilustra a continuación:

public void deleteAll(Collection<NameList> c) {


for ( Iterator<NameList> i=c.iterator(); i.hasNext(); ) {
NameList nl = i.next();
nl.deleteItem();
}
}

En el segmento de código previo, el método deleteAll usa la variable i tres


veces en el bucle for y produce la opción de error cuando se usa la variable por
segunda vez. También, la iteración for usa el método hasNext de Iterator para
obtener el próximo elemento a ser procesado. Será difícil para usted recordar
todos estos métodos. La iteración for mejorada elimina el uso de métodos de
iteración separados y minimiza el número de ocurrencias de la variable de
iteración. El método deleteAll con la iteración for mejorada se ilustra a
continuación:

public void deleteAll(Collection<NameList> c) {


for ( NameList nl : c ) {
nl.deleteItem();
}

La iteración for mejorada en arreglos le permite recorrer a través de los


elementos del arreglo sin usar una variable índice para él. Esto se ilustra en el
siguiente segmento de código.

public int sum(int[] array) {


int result = 0;
for ( int element : array ) {
result += element;
}
return result;
}

Con la funcionalidad de la iteración for mejorada, el recorrido de los for


anidados se hace simple y fácil de comprender reduciendo la ambigüedad de
las variables que están siendo usadas en las iteraciones for anidadas.

List<Subject> subjects=...;
List<Teacher> teachers=...;
List<Course> courseList = ArrayList<Course)();
for ( Subject subj : subjects ) {
for ( Teacher tchr : teachers ) {
courseList.add(new Course(subj, tchr));
}
}

16.13 Laboratorios

Laboratorio 9 - Las Aplicaciones Basadas en Texto

16.13 Laboratorios
16.13.1 Objetivos

Una vez completado este laboratorio, usted debería ser capaz de:

• Leer un archivo de datos usando la API Scanner.


• Usar una colección genérica para manejar las asociaciones uno-a-
muchos

16.13 Laboratorios
16.13.2 Ejercicio 1: Lectura de un Archivo de Datos (Nivel 1)

Ejercicio 1 - Lectura de un Archivo de Datos (Nivel 1)

16.13 Laboratorios
16.13.3 Ejercicio 1: Lectura de un Archivo de Datos (Nivel 2)

Ejercicio 1 - Lectura de un Archivo de Datos (Nivel 2)

16.13 Laboratorios
16.13.4 Ejercicio 1: Lectura de un Archivo de Datos (Nivel 3)

Ejercicio 1 - Lectura de un Archivo de Datos (Nivel 3)

16.13 Laboratorios
16.13.5 Ejercicio 2: Uso de Colecciones para Representar
Asociaciones (Nivel 1)

Ejercicio 2 - Uso de Colecciones para Representar Asociaciones (Nivel 1)

16.13 Laboratorios
16.13.6 Ejercicio 2: Uso de Colecciones para Representar
Asociaciones (Nivel 2)

Ejercicio 2 - Uso de Colecciones para Representar Asociaciones (Nivel 2)


16.13 Laboratorios
16.13.7 Ejercicio 2: Uso de Colecciones para Representar
Asociaciones (Nivel 3)

Ejercicio 2 - Uso de Colecciones para Representar Asociaciones (Nivel 3)

16.13 Laboratorios
16.13.8 Resumen del Ejercicio

Discusión – Dedique unos minutos para discutir que experiencias, temas o


descubrimientos realizó durante el ejercicio de laboratorio.

• Experiencias
• Interpretaciones
• Conclusiones
• Aplicaciones

16.14 Preguntas de Repaso

Ahora le proponemos que resuelva el siguiente ejercicio para poner a prueba


sus conocimientos.

Defina por Verdadero o Falso las siguientes afirmaciones según corresponda.


¡Éxitos!
17 La Construcción de GUI en Java

Este capítulo cubre la configuración y el layout de GUIs. Introduce AWT, un


paquete de clases a partir de las cuales se construye una GUIs.

17.1 Repaso del Capítulo Anterior

Los principales temas del capítulo anterior son:

Los Argumentos desde la Línea de Comando

Cuando un programa basado en la tecnología Java se ejecuta desde una


ventana de comandos, se le pueden pasar cero o más argumentos desde la
línea de comandos.
La secuencia de argumentos se escribe a continuación del nombre de la clase
del programa.

Las Propiedades del Sistema

Las propiedades del sistema son otro mecanismo para indicar parámetros en
un programa en tiempo de ejecución. Una propiedad es una correspondencia
entre un nombre de propiedad y un valor.
Un objeto de la clase Properties contiene una correspondencia entre los
nombres de la propiedad (String) y sus valores (String).

Finalmente, el conjunto de propiedades puede ser almacenado y recuperado


de cualquier flujo de entrada/salida usando los métodos load y store.

La Consola de Entrada/Salida

Algunas veces, esta interacción se lleva a cabo a través de la entrada de un


texto y de la salida en la consola (usando el teclado como entrada estándar y la
ventana de comandos como la salida estándar).
Java 2 SDK soporta una consola de Entrada/Salida (E/S) con tres variables
públicas, en la clase java.lang.System: System.out, System.in y System.err

La Escritura a la Salida Estándar

Se puede escribir a la salida estándar a través del método


System.out.println(String).

La Salida con Formato Simple

El lenguaje de programación Java versión 1.5 provee la funcionalidad de printf,


al estilo del lenguaje C. Este ofrece salida formateada estándar desde el
programa, la que permite a los programadores migrar código heredado.

La Entrada con Formato Simple

La clase Scanner ofrece la funcionalidad de entrada formateada, que es parte


del paquete java.util.

Los Archivos de Entrada/Salida (E/S)

La tecnología Java incluye un rico conjunto de flujos de E/S. En la sección


previa se mostró cómo usar los flujos para comunicarse con el usuario a través
de la salida estándar (usualmente una ventana de comandos) y la entrada
estándar (usualmente el teclado).

La Creación de un Nuevo Objeto File

En la tecnología Java, un directorio es otro archivo. Puede crear un objeto File


que representa un directorio y luego usarlo para identificar otros archivos, como
se muestra en la tercera viñeta.

• File myFile;
myFile = new File("myfile.txt");
• myFile = new File("MyDocs", "myfile.txt");
• File myDir = new File("MyDocs");
myFile = new File(myDir, "myfile.txt");
Las Utilidades y Pruebas de File

Después de haber creado un objeto File, se puede usar cualquier método en la


siguiente sección para recoger información acerca del archivo.
Los siguientes métodos retornan nombres de archivos:

• String getName()
• String getPath()
• String getAbsolutePath()
• String getParent()
• boolean renameTo(File newName)

Las Utilidades de Directorio

Los siguientes métodos proveen utilidades de directorio:

• boolean mkdir()
• String[] list()

El Flujo de E/S de Archivos

La plataforma J2SE soporta entrada de archivos de dos formas:

• Usando la clase FileReader para leer caracteres.


• Usando la clase BufferedReader para usar el método readLine.

La API Collection

Una colección es un objeto simple que representa un grupo de objetos,


llamados elementos. Típicamente, las colecciones tratan con muchos objetos,
todos de un tipo en particular (esto es, todos descienden del mismo tipo padre).

• Collection - Un grupo de objetos, conocidos como elementos. La


implementación determina si hay un orden específico y si se
permiten elementos duplicados.
• Set - Una colección desordenada en la que no se admiten elementos
duplicados.
• List - Una colección ordenada en la que se admiten elementos duplicados.

Los Iteradores

Usted puede explorar una colección usando un iterador. La interfaz básica


Iterator le permite explorar hacia adelante una colección. En el caso de una
iteración sobre un conjunto, el orden no es determinante.. El orden de una
iteración sobre una lista que se mueve hacia adelante a través de los
elementos. Un objeto List también soporta un ListIterator, el cual permite
recorrer la lista hacia atrás.

17.3 Objetivos
Una vez finalizado este capítulo, usted debe estar en condiciones de:

• Describir el paquete AWT (Abstract Window Toolkit) y sus compontes.


• Definir los términos contenedores, componentes y Administradores de
Disposición y describir cómo funcionan conjuntamente para construir
una GUI.
• Usar Administradores de Disposición (layout manager).
• Usar los administradores FlowLayout, BorderLayout y GridLayout para
construir un layout dinámico.
• Agregar componentes a un contenedor.
• Usar los contenedores Frame y Panel apropiadamente.
• Describir cómo funcionan los layout complejos con contenedores
anidados.

Discusión - La siguiente pregunta es relevante para el material presentado en


este capítulo:

• ¿Cómo debería ser utilizada la tecnología Java - que contiene un


lenguaje de programación independiente - para construir una GUI
independiente de la plataforma?

17.4 Recursos Adicionales

Recursos adicionales – Las siguientes referencias ofrecen información


adicional sobre los temas descritos en este capítulo:

• The Java Tutorial. [Online]. Disponible en:

http://java.sun.com/docs/books/
tutorial/ui/overview/intro.html

http://java.sun.com/docs/books/
tutorial/ui/features/components.html

17.5 Abstract Window Toolkit (AWT)

El AWT ofrece componentes GUI básicos que pueden ser utilizados en los
applets y en las aplicaciones Java. El AWT provee una interfaz de
programación independiente de la máquina para las aplicaciones. Esto asegura
que lo que aparezca en un computador es comprable a lo que aparecerá en
cualquier otro.

Antes de analizar el AWT, revise la jerarquía de objetos. Recuerde que puede


extender las superclases y que ellas heredan sus propiedades. También, las
clases pueden ser abstractas, lo que significa que pueden contener templates
que son subclases para proveer implementaciones concretas de la clase.
Típicamente, en AWT se construyen interfaces de usuario por composición de
componentes disponibles.

Cada componente GUI que aparezca en la pantalla es una subclase de la clase


abstracta Component o MenuComponent. Esto es, los componentes GUI
básicos heredan de la clase Component una cantidad de métodos y variables
instancia. De la misma forma, los componentes Menu heredan desde la clase
MenuComponent.

La clase Container es una subclase abstracta de Component. Permite que


otros componentes puedan ser anidados en ella. Estos componentes pueden
también ser contenedores, que permiten que otros componentes sean
anidados dentro, lo cual crea una estructura jerárquica completa. Los
contenedores son útiles en el arreglo de componentes GUI en la pantalla.

Los contenedores Panel y Frame son los contenedores más utilizados.

17.6 El Paquete java.awt

El paquete java.awt contiene las clases que generan los componentes GUI.
Una visión general básica de este paquete se muestra en la Figura . Las
clases señaladas en negrita son las que constituyen el foco principal de este
módulo.
17.7 La Construcción de Interfaces Gráficas de Usuarios

La siguiente sección describe los componentes que se utilizarán para construir


una GUI.

17.7 La Construcción de Interfaces Gráficas de Usuarios


17.7.1 Contenedores

Para agregar los componentes GUI a un contenedor, se utiliza el método add.

Hay dos tipos principales de contenedores: Windows y Panel.

Un contenedor Window es una ventana nativa que se ubica libremente en el


lugar donde se desplegará y que es independiente de otros contenedores. Hay
dos tipos principales de contenedores Window: Frame y Dialog. Un Frame es
una ventana con un título y esquinas, a la que se puede cambiar su tamaño. Un
Dialog es una ventana simple y no puede
tener una barra de menú. Esto significa que puede moverse en la pantalla, pero
no puede cambiarse el tamaño.

Un Panel debe estar contenido en otro Container o dentro de una ventana de


un navegador web. Un Panel identifica un área rectangular dentro de la cual se
pueden incluir otros componentes. Se debe ubicar dentro de un contenedor
Window ( o una subclase de Window) para ser desplegado.
17.7 La Construcción de Interfaces
Gráficas de Usuarios
17.7.2 El Posicionamiento de
Componentes

La posición y el tamaño de un componente en un contenedor están


determinados por el Administrador de Disposición. Un contenedor mantiene
una referencia a una instancia particular de un Administrador de Disposición.
Cuando el contenedor necesita ubicar un componente, invoca al administrador
para realizarlo. La misma delegación ocurre cuando se debe decidir el tamaño
de un componente. El Administrador de Disposición toma el control de todos
los componentes en el contenedor. Es responsable de calcular y de definir el
tamaño preferido del objeto en el contexto del tamaño de la pantalla actual.

17.7 La Construcción de Interfaces Gráficas


de Usuarios
17.7.3 La Definición del Tamaño
de los Componentes
Dado que el Administrador de Disposición generalmente es responsable del
tamaño y de la ubicación de los componentes en su contenedor, usted no es
responsable por la definición del tamaño y posición de ellos. Si trata de usarlo
(utilizando cualquiera de los métodos setLocation, setSize o setBounds) el
Administrador de Disposición puede
sobrescribir lo que haya decidido.
Si se desea controlar el tamaño o la posición de los componentes sin usar el
Administrador de Disposición estándar, debe deshabilitarlo, usando el siguiente
método del contenedor:

cont.setLayout(null);

Luego de este paso, se debe usar setLocation, setSize o setBounds sobre


todos los componentes para ubicarlos en el contenedor.

Este enfoque da como resultado varios layouts dependientes de la plataforma,


debido a las diferencias entre los sistemas y de ventanas y tamaños de los
fuentes.

Una mejor aproximación consiste en crear una nueva subclase de


LayoutManager

17.8 Frames

Un Frame es una subclase de Window. Se trata de un objeto de tipo Window


con un título y esquinas para cambiar el tamaño. Hereda sus características
desde la clase Container, de forma tal que se puede utilizar el método add para
agregarle componentes. El Administrador de Disposición por defecto para un
Frame es BorderLayout. Esto puede cambiarse utilizando el método setLayout.

El constructor Frame(String) en la clase crea un nuevo objeto Frame invisible,


con el título que se especifica en el argumento String. Los componentes se
agregan al Frame mientras este es invisible.

El ejemplo en Código de la Figura crea un Frame que tiene un título, tamaño


y color de fondo específico (se muestra en la Figura ).

La Figura muestra las pantallas correspondientes al programa


FrameExample en Código de la Figura para dos ambientes: Solaris OS
Common Desktop Environment (CDE) y Microsoft Windows.

Un Frame debe hacerse visible (invocando el método setVisible(true) y definir


su tamaño (invocando el método setSize o pack) antes de ser desplegado en la
pantalla.

1 import java.awt.*;
2
3 public class FrameExample {
4 private Frame f;
5
6 public FrameExample() {
7 f = new Frame("Hello Out There!");
8 }
9
10 public void launchFrame() {
11 f.setSize(170,170);
12 f.setBackground(Color.blue);
13 f.setVisible(true);
14 }
15
16 public static void main(String args[]) {
17 FrameExample guiWindow = new FrameExample();
18 guiWindow.launchFrame();
19 }
20 }>

17.9 Panel

Un Panel, al igual que un Frame, ofrece el espacio para asignar cualquier


componente GUI, incluyendo otros Panels. Cada Panel (que hereda de
Container), puede tener su propio Administrador de Disposición.
Después de crear un Panel, se debe agregar a un Window o Frame. Esto se
realiza usando el método add de la clase Container.

A continuación se hace visible el Frame, de forma tal que el Frame y el Panel


se desplieguen. El ejemplo en Código de la Figura crea un Panel amarillo
pequeño y lo agrega a un Frame.

La Figura muestra las pantallas para el programa FrameExample en


Código de la Figura para dos ambientes: Solaris OS CDE y Microsoft
Windows.

1 import java.awt.*;
2
3 public class FrameWithPanel {
4 private Frame f;
5 private Panel pan;
6
7 public FrameWithPanel(String title) {
8 f = new Frame(title);
9 pan = new Panel();
10 }
11
12 public void launchFrame() {
13 f.setSize(200,200);
14 f.setBackground(Color.blue);
15 f.setLayout(null); // Override default layout mgr
16
17 pan.setSize(100,100);
18 pan.setBackground(Color.yellow);
19 f.add(pan);
20 f.setVisible(true);
21 }
22
23 public static void main(String args[]) {
24 FrameWithPanel guiWindow =
25 new FrameWithPanel("Frame with Panel");
26 guiWindow.launchFrame();
27
28 }
17.10 Layouts de Contenedores

El layout de componentes en un contenedor usualmente está gobernado por un


Administrador de Disposición. Cada contenedor (como por ejemplo Panel o
Frame) tiene un Administrador de Disposición asociado por defecto, el que
puede cambiarse invocando al método setLayout. El Administrador de
Disposición es responsable de decidir la política de layout y el tamaño de cada
uno de los componentes hijos del contenedor

17.10 Layouts de Contenedores


17.10.1 Los Administradores de Disposición

Los siguientes Administradores de Disposición están incluidos en el lenguaje


de programación:

• FlowLayout - El FlowLayout es el Administrador de Disposición por


defecto de Panel y Applet.
• BorderLayout - El BorderLayout es el Administrador de Disposición por
defecto para Window, Dialog y Frame.
• GridLayout - Este es un Administrador de Disposición que provee
flexibilidad para la ubicación de componentes.
• CardLayout - No se trata en este capítulo. Este Administrador de
Disposición brinda funcionalidades comparable a un Panel de pestañas
primitivo. La principal diferencia es la ausencia de pestañas.
• GridBagLayout - Tampoco se trata en este curso.

17.10 Layouts de
Contenedores
17.10.2 Los Administradores de Disposición por
Defecto

Por defecto, todas las clases Window usan el administrador BorderLayout y las
clases Panel usan el administrador FlowLayout. Se puede cambiar el
administrador de contenido para cualquier instancia de estas clases usando el
método setLayout.

17.10 Layouts de Contenedores


17.10.3 Un Ejemplo Simple de FlowLayout

El Código de la Figura muestra un ejemplo simple del mecanismo de


FlowLayout. La Figura muestra las pantallas resultantes en Solaris OS CDE
y el ambiente Microsoft Windows para el programa FrameExample en el
Código de la Figura

El Método main

El método main en la Línea 22 del ejemplo en la Figura realiza dos acciones.


En primer lugar, se crea una instancia del objeto LayoutExample. Recuerde
que hasta que exista una instancia, no hay ítems reales denominados f, b1 y b2
para usar. En segundo lugar, el constructor (Líneas 8 a 12) crea tres objetos
GUI.

El Constructor f = new Frame("GUI Example")

El constructor f = new Frame("GUI Example") crea una instancia de la clase


Frame. Un Frame en el lenguaje de programación Java es una ventana de alto
nivel, con una barra de título que está definida por el argumento del constructor,
que en este ejemplo es "GUI Example". En este caso, administra los cambios
de tamaños y otros elementos de la ventana, de acuerdo con las convenciones
locales. El tamaño de Frame es 0x0 y por defecto no está visible.

Los Constructores b1 = new Button("Press Me") y b2 = new Button("Don't


Press Me")

Los constructores b1 = new Button("Press Me") y b2 = new Button("Don't Press


Me") crean instancias de la clase Button. Un botón es el push-button estándar
tomado desde el toolkit de manejo de ventanas local. La etiqueta del botón se
define mediante el argumento de tipo String del constructor.

Después de que el espacio de datos es creado, el método main llama al


método de la instancia launchFrame en el contexto de esta instancia. Las
acciones reales ocurren en launchFrame.

El Método f.setLayout(new FlowLayout())

El metodo f.setLayout(new FlowLayout()) crea una instancia del Administrador


de Disposición de Flujo y la instala en el Frame. El Administrador de
Disposición por defecto para cada Frame, BorderLayout, no se utiliza en este
ejemplo. El Administrador FlowLayout es el Administrador de Disposición más
simple en el AWT y ubica los componentes en una forma similar a como se
ubican las palabras en una página, colocándolas línea por línea. El
Administrador de Disposición FlowLayout, por defecto, centra cada linea.

Los Métodos f.add(b1) y f.add(b2)

Los métodos f.add(b1) y f.add(b2) llaman al Frame f (que es un contenedor)


que engloba los componentes b1 y b2. El tamaño y posición de b1 y b2 están,
a partir de ese momento, bajo el control del Administrador de Disposición del
Frame.

El Método f.pack()
El método f.pack() le indica al frame que defina un tamaño para incluir
apropiadamente los componentes que contiene. Para determinar el tamaño a
usar para el Frame, f.pack() consulta al Administrador de Disposición, el cual es
responsable por el tamaño y la posición de todos los componentes en el Frame.

El Método f.setVisible(true)

El método f.setVisible(true) hace que el Frame y su contenido se hagan visibles


al usuario

1 import java.awt.*;
2
3 public class LayoutExample {
4 private Frame f;
5 private Button b1;
6 private Button b2;
7
8 public LayoutExample() {
9 f = new Frame("GUI example");
10 b1 = new Button("Press Me");
11 b2 = new Button("Don ft press Me");
12 }

13
14 public void launchFrame() {
15 f.setLayout(new FlowLayout());
16 f.add(b1);
17 f.add(b2);
18 f.pack();
19 f.setVisible(true);
20 }
21
22 public static void main(String args[]) {
23 LayoutExample guiWindow = new LayoutExample();
24 guiWindow.launchFrame();
25 }
26 }
17.11 El Administrador de Disposición

Esta sección describe los Administradores de Disposición (Layout Managers).

17.11 El Administrador de
Disposición
17.11.1 El Administrador FlowLayout

El administrador FlowLayout usado en el ejemplo para este tema, posiciona los


componentes línea por línea. Cada vez que se completa una línea, se
comienza una nueva. Al contrario que otros Administradores de Disposición, el
administrador FlowLayout no restringe el tamaño de los componentes que
gestiona y admite que tengan el tamaño que se prefiera.

Los argumentos del constructor FlowLayout permiten alinear los componentes


a la izquierda o la derecha, en lugar del comportamiento por defecto que
los alinea al centro. Se pueden especificar espacios, si se quiere crear un
espacio mínimo entre los componentes.

Los siguientes ejemplos muestran cómo crear objetos FlowLayout y cómo


instalarlos usando el método setLayout de la clase Container. Es importante
tener en cuenta que el administrador FlowLayout tiene tres constructores
diferentes:
FlowLayout()
FlowLayout(int align)
FlowLayout(int align,int hgap,int vgap)

El valor del argumento align debe ser alguno de los siguientes:


FlowLayout.LEFT: FlowLayout.RIGHT o FlowLayout.CENTER. Por ejemplo

setLayout(new FlowLayout(FlowLayout.RIGHT, 20, 40));

La siguiente sentencia construye e instala un nuevo administrador FlowLayout


con la alineación especificada y un espacio por defecto de 5 unidades
verticales y 5 unidades horizontales.

setLayout(new FlowLayout(FlowLayout.LEFT));

La siguiente sentencia construye e instala un nuevo administrador


FlowLayout con alineación en el centro y un espaciado por defecto de cinco
unidades horizontales y cinco unidades verticales.

setLayout(new FlowLayout());

Cuando un usuario modifica el área que está siendo gestionada por un


FlowLayout, el layout puede cambiar. (ver Figura )

El Código de la Figura agrega varios botones a un layout de un Frame.


1 import java.awt.*;
2
3 public class GridExample {
4 private Frame f;
5 private Button b1, b2, b3, b4, b5, b6;
6
7 public GridExample() {
8 f = new Frame("Grid Example");
9 b1 = new Button("1");
10 b2 = new Button("2");
11 b3 = new Button("3");
12 b4 = new Button("4");
13 b5 = new Button("5");
14 b6 = new Button("6");
15 }
16
17 public void launchFrame() {
18 f.setLayout (new GridLayout(3,2));
19 f.add(b1);
20 f.add(b2);
21 f.add(b3);
22 f.add(b4);
23 f.add(b5);
24 f.add(b6);
25 f.pack();
26 f.setVisible(true);
27 }
28
29 public static void main(String args[]) {
30 GridExample grid = new GridExample();
31 grid.launchFrame();
32 }
33 }

17.11 El Administrador de
Disposición
17.11.2 El Administrador BorderLayout

El administrador BorderLayout ofrece un esquema complejo para la ubicación


de componentes en un contenedor. Este es el administrador por defecto para
las clases Frame y Dialog. Contiene cinco áreas distintas: NORTH, SOUTH,
EAST, WEST y CENTER, indicadas por BorderLayout.NORTH,etc.

La Figura muestra la organización de los bordes de los componentes. El


área NORTH ocupa la parte superior de un frame, EAST ocupa la parte
derecha y así sucesivamente. El área CENTER representa el área que queda
luego de que se completaron las áreas NORTH, SOUTH, EAST y WEST.
Cuando un elemento Window es extendido verticalmente, las regiones EAST,
WEST y CENTER también lo hacen así. Cuando la ventana se extiende
horizontalmente, también lo hacen así, las regiones NORTH, SOUTH y
CENTER.

La siguiente línea construye e instala un nuevo elemento BorderLayout sin


espacio entre los componentes:

setLayout(new BorderLayout());

Se utiliza el constructor BorderLayout para indicar el espacio entre


componentes, especificando los valores para hgap y vgap.

BorderLayout(int hgap, int vgap)

Se deben agregar componentes a las regiones en el administrador


BorderLayout, o de lo contrario, estas no serán visibles. Se debe ser cuidadoso
en la correcta escritura de los nombres, especialmente si se decide no utilizar
constantes (por ejemplo,

add(button,"Center") en lugar de
add(button, BorderLayout.CENTER)).

La correcta escritura y el uso de las mayúsculas es crucial para esto.


Se puede utilizar el administrador BorderLayout para producir elementos que
se extiendan en una dirección, la dirección opuesta o ambas, cuando se
cambia su tamaño. Si se deja una región de un BorderLayout sin usar, se
comporta como si su tamaño fuese 0 por 0. La región CENTER aparece como
fondo, aun cuando no contenga componentes.

Se puede agregar sólo un componente a cada una de las cinco regiones del
administrador BorderLayout. Si se trata de agregar más de uno, solo el último
quedará visible. Más adelante, en esta unidad, un ejemplo muestra cómo se
pueden utilizar contenedores intermedios para permitir más de un componente
en una región gestionada por BorderLayout.

El Código de la Figura es una modificación del Código de la Figura del


apartado 10.9.1 y muestra el comportamiento del administrador BorderLayout.
Se puede asignar el BorderLayout a un layout usando el método setLayout
heredado desde la clase Container. El ejemplo de la Figura crea un frame
con un botón en cada zona de borde. La Figura muestra una pantalla para
esta clase.

La Figura muestra las características de cambio de tamaño de los bordes


del layout
1 import java.awt.*;
2
3 public class BorderExample {
4 private Frame f;
5 private Button bn, bs, bw, be, bc;
6
7 public BorderExample() {
8 f = new Frame("Border Layout");
9 bn = new Button("B1");
10 bs = new Button("B2");
11 bw = new Button("B3");
12 be = new Button("B4");
13 bc = new Button("B5");
14 }
15
16 public void launchFrame() {
17 f.add(bn, BorderLayout.NORTH);
18 f.add(bs, BorderLayout.SOUTH);
19 f.add(bw, BorderLayout.WEST);
20 f.add(be, BorderLayout.EAST);
21 f.add(bc, BorderLayout.CENTER);
22 f.setSize(200,200);
23 f.setVisible(true);
24 }
25
26 public static void main(String args[]) {
27 BorderExample guiWindow2 = new BorderExample();
28 guiWindow2.launchFrame();
29 }
30 }
17.11 El Administrador de
Disposición
17.11.3 El Administrador GridLayout

El administrador GridLayout ofrece flexibilidad para ubicar los componentes. Se


crea el administrador con una cierta cantidad de filas y columnas. Los
componentes completan las celdas definidas por el administrador. Por ejemplo,
un GridLayout con tres filas y dos columnas creado por la sentencia new
GridLayout(3, 2) creará seis celdas.

Al igual que con el administrador BorderLayout, la posición relativa de los


componentes no cambia cuando se modifica el tamaño del área. Sólo cambian
los tamaños de los componentes. El administrador GridLayout siempre ignora
los componentes preferidos del componente. El ancho de todas las celdas es
idéntico y está determinado por el resultado de la división del ancho disponible
entre la cantidad de columnas. De la misma manera, el alto de cada celda se
fija por el resultado de la división entre el alto disponible y la cantidad de
columnas. El orden en que los componentes se agregan a la cuadrícula
determina la celda que se ocupa. Las líneas de las celdas son completadas de
izquierda a derecha y la página se llena con líneas desde arriba hacia abajo.

La siguiente línea crea e instala un GridLayout con una columna por


componente en una única fila por defecto: setLayout(new GridLayout()); Se
utiliza el siguiente constructor para crear una cuadrícula con la cantidad de filas
y columnas especificada. A todos los componentes en el layout se les asigna el
mismo tamaño.

GridLayout(int rows, int cols)

El siguiente constructor crea un GridLayout con la cantidad de filas y columnas


especificada:

GridLayout(int rows, int cols, int hgap, int vgap)

Todos los componentes en el layout tienen el mismo tamaño. Los valores hgap
y vgap especifican el espaciado respectivo entre ellos. El espaciado horizontal
se ubica entre cada una de las columnas. El espaciado vertical, entre cada una
de las filas.

El ejemplo en el Código de la Figura crea un frame con un botón en cada


celda de una cuadrícula de dos filas por tres columnas.

La Figura muestra las pantallas para estas clases.


1 import java.awt.*;

3 public class GridExample {

4 private Frame f;

5 private Button b1, b2, b3, b4, b5, b6;

7 public GridExample() {

8 f = new Frame("Grid Example");

9 b1 = new Button("1");

10 b2 = new Button("2");

11 b3 = new Button("3");

12 b4 = new Button("4");

13 b5 = new Button("5");

14 b6 = new Button("6");

15 }

16

17 public void launchFrame() {

18 f.setLayout (new GridLayout(3,2));

19

20 f.add(b1);

21 f.add(b2);

22 f.add(b3);

23 f.add(b4);

24 f.add(b5);

25 f.add(b6);
26

27 f.pack();

28 f.setVisible(true);

29 }

30

31 public static void main(String args[]) {

32 GridExample grid = new GridExample();

33 grid.launchFrame();

34 }

35 }

17.12 La Creación de Paneles y de Layout Complejos


El Código de la Figura usa Panel para habilitar dos botones para ubicar la
región NORTH del borde de un layout. Este tipo de anidamiento es
fundamental para la construcción de layout complejos. El Panel se trata como
otro componente cualquiera al igual que el Frame.

La Figura muestra pantallas para este programa.

Cuando se ejecuta el ejemplo, el resultado desplegado es similar al de la


Figura

La región NORTH de BorderLayout contiene dos botones. En realidad, contiene


solamente el Panel, que tiene dos botones.

El tamaño y posición del Panel están determinados por el administrador


BorderLayout, y el tamaño preferido de Panel está determinado por el tamaño
preferido de los componentes en el Panel. El tamaño y posición de los botones
en el Panel están controlados por el FlowLayout, asociado con el Panel por
defecto.

1 import java.awt.*;

3 public class ComplexLayoutExample {

4 private Frame f;

5 private Panel p;

6 private Button bw, bc;

7 private Button bfile, bhelp;

9 public ComplexLayoutExample() {

10 f = new Frame("GUI example 3");

11 bw = new Button("West");

12 bc = new Button("Work space region");

13 bfile = new Button("File");

14 bhelp = new Button("Help");

15 }
34

16

17 public void launchFrame() {

18 // Add bw and bc buttons in the frame border

19 f.add(bw, BorderLayout.WEST);

20 f.add(bc, BorderLayout.CENTER);

21 // Create panel for the buttons in the north

border

22 p = new Panel();

23 p.add(bfile);

24 p.add(bhelp);

25 f.add(p, BorderLayout.NORTH);

26 // Pack the frame and make it visible

27 f.pack();

28 f.setVisible(true);

29 }

30

31 public static void main(String args[]) {

32 ComplexLayoutExample gui

33 = new ComplexLayoutExample();

34 gui.launchFrame();

35 }

36 }
17.13 La Creación de Dibujos con AWT

Cada componente AWT tiene un método paint que dibuja el componente


especificado. Es posible dibujar directamente en la pantalla usando el objeto
Graphics de un componente dado. AWT provee las clases Canvas y Panel para
realizar estos dibujos.

El método paint es llamado cada vez que un Canvas o Panel necesita ser
redibujado. Las circunstancias en las que el método paint se llama, quedan
determinadas por el hilo AWT. Por ejemplo, Paint se llama cuando un Canvas o
un Panel se hace visible, cuando un Window superior es eliminado y cuando un
Canvas o Panel es parte de un Frame que ha sido minimizado y restaurado. El
programador puede también forzar a AWT a redibujar un Canvas o Panel
llamando al método repaint.

El método paint recibe un único parámetro: un objeto Graphics. La clase


Graphics incluye métodos para dibujar varias formas: arcos, óvalos, círculos,
polígonos, rectángulos, rectángulos con esquinas redondeadas, rectángulos
tridimensionales, líneas y cadenas de caracteres. Las primeras seis pueden ser
rellenadas con un color.
La Figura muestra un Panel con varias formas dibujadas en él y el nombre
del método debajo de ellas.

17.14 Laboratorios

Laboratorio 10 - La Construcción de GUI en Java

17.14 Laboratorios
17.14.1 Objetivos

Una vez completado este laboratorio, usted deberá ser capaz de:

• Crear una GUI para el proyecto de la sala de Chat.


• (Opcional) Crear una GUI para el proyecto Banking.

17.14 Laboratorios
17.14.2 Ejercicio 1: Crear la GUI de ChatClient Parte 1 (Nivel
1)

Ejercicio 1 - Crear la GUI de ChatClient Parte 1 (Nivel 1)

17.14 Laboratorios
17.14.3 Ejercicio 1: Crear la GUI de ChatClient Parte 1 (Nivel
2)

Ejercicio 1 - Crear la GUI de ChatClient Parte 1 (Nivel 2)

17.14 Laboratorios
17.14.4 Ejercicio 1: Crear la GUI de ChatClient Parte 1 (Nivel
3)

Ejercicio 1 - Crear la GUI de ChatClient Parte 1 (Nivel 3)

17.14 Laboratorios
17.14.5 Ejercicio 2: Creación de la GUI de una ATM de un
Banco Parte 1 (Avanzado)

Ejercicio 2 - Creación de la GUI de una ATM de un Banco Parte 1 (Avanzado)

17.14 Laboratorios
17.14.6 Resumen del Ejercicio

Discusión – Dedique unos minutos para discutir que experiencias, temas o


descubrimientos realizó durante el ejercicio de laboratorio.

• Experiencias
• Interpretaciones
• Conclusiones
• Aplicaciones

17.15 Preguntas de Repaso

Ahora le proponemos que resuelva el siguiente ejercicio para poner a prueba


sus conocimientos.

Defina por Verdadero o Falso las siguientes afirmaciones según corresponda.


¡Éxitos!
18 El Manejo de Eventos GUI

Este capítulo cubre el mecanismo de entrada de usuario GUI, basado en


eventos.

18.1 Repaso del Capítulo Anterior

Los principales temas del capítulo anterior son:

Abstract Window Toolkit (AWT)

El AWT ofrece componentes GUI básicos que pueden ser utilizados en los
applets y en las aplicaciones Java.
Cada componente GUI que aparezca en la pantalla es una subclase de la clase
abstracta Component o MenuComponent.
De la misma forma, los componentes Menu heredan desde la clase
MenuComponent.
La clase Container es una subclase abstracta de Component. Permite que
otros componentes puedan ser anidados en ella.

El Paquete java.awt

El paquete java.awt contiene las clases que generan los componentes GUI.

La Construcción de Interfaces Gráficas de Usuarios

Para agregar los componentes GUI a un contenedor, se utiliza el método add.


Un contenedor Window es una ventana nativa que se ubica libremente en el
lugar donde se desplegará y que es independiente de otros contenedores. Hay
dos tipos principales de contenedores Window: Frame y Dialog.
Un Panel debe estar contenido en otro Container o dentro de una ventana de
un navegador web. Un Panel identifica un área rectangular dentro de la cual se
pueden incluir otros componentes.

El Posicionamiento de Componentes

La posición y el tamaño de un componente en un contenedor están


determinados por el Administrador de Disposición.

La Definición del Tamaño de los Componentes

Si se desea controlar el tamaño o la posición de los componentes sin usar el


Administrador de Disposición estándar, debe deshabilitarlo, usando el siguiente
método del contenedor:
cont.setLayout(null);

Luego de este paso, se debe usar setLocation, setSize o setBounds sobre


todos los componentes para ubicarlos en el contenedor.

Frames

Un Frame es una subclase de Window. Hereda sus características desde la


clase Container, de forma tal que se puede utilizar el método add para
agregarle componentes.
El Administrador de Disposición por defecto para un Frame es BorderLayout.

Panel

Un Panel, al igual que un Frame, ofrece el espacio para asignar cualquier


componente GUI, incluyendo otros Panels.
Después de crear un Panel, se debe agregar a un Window o Frame.

Layouts de Contenedores

El layout de componentes en un contenedor usualmente está gobernado por un


Administrador de Disposición.
El Administrador de Disposición es responsable de decidir la política de layout y
el tamaño de cada uno de los componentes hijos del contenedor.

Los Administradores de Disposición

• FlowLayout - El FlowLayout es el Administrador de Disposición por defecto de


Panel y Applet.
• BorderLayout - El BorderLayout es el Administrador de Disposición por
defecto para Window, Dialog y Frame.
• GridLayout - Este es un Administrador de Disposición que provee flexibilidad
para la ubicación de componentes.
• CardLayout - No se trata en este capítulo. Este Administrador de Disposición
brinda funcionalidades comparable a un Panel de pestañas primitivo. La
principal diferencia es la ausencia de pestañas.
• GridBagLayout - Tampoco se trata en este curso.

Administradores de Disposición por Defecto

Por defecto, todas las clases Window usan el administrador BorderLayout y las
clases Panel usan el administrador FlowLayout.

La Creación de Dibujos con AWT

Cada componente AWT tiene un método paint que dibuja el componente


especificado. El método paint recibe un único parámetro: un objeto Graphics.
La clase Graphics incluye métodos para dibujar varias formas: arcos, óvalos,
círculos, polígonos, rectángulos, rectángulos con esquinas redondeadas,
rectángulos tridimensionales, líneas y cadenas de caracteres
18.3 Objetivos

Una vez completado este capítulo, usted estará en condiciones de:

• Definir eventos y manejadores de eventos.


• Escribir código para manejar eventos que ocurran en una GUI.
• Describir el concepto de clases adaptadoras, incluyendo cómo y cuándo
usarlas.
• Determinar la acción del usuario que originó el evento a partir de los
detalles del objeto del evento.
• Identificar la interfaz Listener apropiada para una gran variedad de tipos
de eventos.
• Crear los métodos manejadores de eventos apropiados para una gran
variedad de tipos de eventos.
• Comprender el uso de clases internas y clases anónimas en
manejadores de evento.

Discusión – Las siguientes preguntas son relevantes para el material


presentado en este capítulo.

• ¿Qué partes son necesarias para hacer útil a una GUI?


• ¿Cómo maneja un programa gráfico un clic en el ratón o cualquier otro
tipo de interacción del usuario?

18.4 Recursos Adicionales

Recursos adicionales – Las siguientes referencias ofrecen información


adicional sobre los temas descritos en este capítulo:

• The Java Tutorial. [Online]. Disponible en:

http://java.sun.com/docs/books/
tutorial/uiswing/events/
intro.html

http://java.sun.com/docs/books/
tutorial/uiswing/events/
generalrules.html

18.5 ¿Qué es un Evento?

Cuando el usuario realiza una acción a nivel de la interfaz de usuario (hace clic
sobre el ratón o presiona una tecla), esto produce un evento. Los eventos son
objetos que describen lo que ha sucedido. Hay una gran cantidad de clases de
eventos para describir las diferentes categorías de acciones del usuario.

La Figura muestra una vista abstracta del modelo delegacional de eventos.


Cuando un usuario hace clic sobre un botón de la GUI, entonces la JVM crea
un objeto evento y lo envía al manejador de eventos para ese botón. Esto se
realiza llamando al método actionPerformed

18.5 ¿Qué es un Evento?


18.5.1 Las Fuentes de Eventos

Una fuente es un generador de un evento. Por ejemplo, un clic con el ratón


sobre un componente Button genera una instancia ActionEvent con el botón
como fuente. La instancia ActionEvent es un objeto que contiene información
acerca de los eventos que tuvieron lugar. Dicha instancia contiene:

• getActionCommand - Devuelve el nombre del comando asociado con la


acción.
• getModifiers - Devuelve cualquier modificador asociado a la acción

18.5 ¿Qué es un Evento?


18.5.2 Los Manejadores de Eventos
Un manejador de eventos es un método que recibe un objeto evento, lo
descifra y procesa la interacción del usuario.

18.6 El Modelo de Eventos J2SE

Esta sección describe el modelo delegacional de eventos.

18.6 El Modelo de Eventos


J2SE
18.6.1 El Modelo Delegacional de Eventos

El modelo delegacional de eventos proviene de la versión 1.1 del JDK. Con


este modelo, los eventos se envían al componente desde el evento que los
originó. Este, a su vez, es enviado a cada componente para propagar el evento
a una o más clases registradas, denominadas listeners (oyentes). Los listeners
contienen manejadores de eventos que los reciben y procesan. De esta
manera, el manejador de evento puede estar en un objeto independiente del
componente. Los listeners son clases que implementan la interfaz
EventListener.

Los eventos son objetos reportados solo por los listeners registrados. Cada
evento tiene una correspondiente interfaz Listener que determina los métodos
que deben ser definidos en una clase apropiada para recibirlos. La clase que
implementa la interfaz define estos métodos y puede ser registrada como un
listener.

Los eventos de los componentes que no tienen listeners registrados no se


propagan.
18.6 El Modelo de Eventos
J2SE
18.6.2 Un Ejemplo de Listener

Por ejemplo, el Código de la Figura muestra el código para un Frame simple


con un único objeto Button en él.

La clase ButtonHandler, mostrada en el Código de la Figura , es la clase


manejador para la cual el evento es delegado.

El Código de la Figura tiene las siguientes características:

• La clase Button tiene un método addActionListener (ActionListener).


• La interfaz ActionListener define un único método, actionPerformed, que
recibe un ActionEvent.
• Después de ser creado, un objeto Button puede tener un objeto
registrado como un listener para ActionEvents a través del método
addActionListener(). El listener registrado es instanciado a partir de una
clase que implementa la interfaz ActionListener.
• Cuando se hace clic sobre el objeto Button, se envía un ActionEvent. El
ActionEvent es recibido a través del método actionPerformed() de
cualquier ActionListener, que está registrado en el botón a través del
método addActionListener().
• El método getActionCommand() de la clase ActionEvent devuelve el
nombre del comando asociado con la acción. En la Línea 10, el
comando de la acción para este botón se asigna a ButtonPressed.

Los eventos no son manejados accidentalmente. Los objetos que se


programan para escuchar eventos particulares sobre un componente GUI se
registran con ese componente.

• Cuando ocurre un evento, solo los objetos registrados reciben un


mensaje de que esto sucedió.
• El modelo delegacional resulta útil para la distribución de trabajo entre
las clases.

Los eventos no tienen por qué estar relacionados con los componentes AWT.
Este modelo de eventos ofrece soporte para la arquitectura JavaBeans
(Apéndice A parte 1 y parte 2)

1 import java.awt.*;

3 public class TestButton {

4 private Frame f;

5 private Button b;

7 public TestButton() {

8 f = new Frame("Test");

9 b = new Button("Press Me!");

10 b.setActionCommand("ButtonPressed");

11 }

12

13 public void launchFrame() {

14 b.addActionListener(new ButtonHandler());
15 f.add(b,BorderLayout.CENTER);

16 f.pack();

17 f.setVisible(true);

18 }

19

20 public static void main(String args[]) {

21 TestButton guiApp = new TestButton();

22 guiApp.launchFrame();

23 }

24 }

1 import java.awt.event.*;

2 public class ButtonHandler implements ActionListener

3 {

4 public void actionPerformed(ActionEvent e) {

5 System.out.println("Action occurred");

6 System.out.println("Button´s command is: "

7 + e.getActionCommand());

8 }

9 }
18.7 El Comportamiento GUI

Esta sección describe las categorías de eventos.


18.7 El Comportamiento GUI
18.7.1 Las Categorías de Eventos

El mecanismo general para recibir eventos desde componentes ha sido


descrito en el contexto de un tipo único de evento. Muchas de las clases de
eventos residen en el paquete java.awt.event, pero existen otras clases. La
Figura muestra una jerarquía de clases UML de las clases de eventos GUI.

Para cada categoría de eventos, hay una interfaz que tiene que ser
implementada por la clase de objetos que se quiere reciba los eventos. Esta
interfaz demanda que uno o más métodos sean definidos. Estos métodos son
llamados cuando acontecen eventos particulares.

La Tabla de la Figura lista estas categorías e interfaces.


18.7 El Comportamiento GUI
18.7.2 Un Ejemplo Complejo

Esta sección examina un ejemplo con un código Java más complejo. Dicho
código registra el movimiento del ratón cuando este se presiona (Mouse
dragging). También detecta los movimientos del ratón aun cuando los botones
no están presionados (Mouse moving).

Los eventos causados por el movimiento del ratón - con o sin el botón
presionado - pueden ser determinados por los objetos de una clase que
implementa la interfaz. MouseMotionListener. Esta interfaz requiere dos
métodos: mouseDragged() y mouseMoved(). Aun cuando se está interesado
sólo en el movimiento, ambos métodos deben implementarse. Sin embargo, en
este caso, el cuerpo del método mouseMoved() puede estar vacío.

Para obtener eventos del ratón, incluyendo un clic sobre un botón, debe
implementarse la interfaz MouseListener. Esta interfaz incluye varios eventos,
como, por ejemplo, mouseEntered, mouseExited, mousePressed,
mouseReleased y mouseClicked.

Cuando ocurren eventos del teclado o del ratón, queda disponible, en el evento
que lo originó, información sobre la posición del ratón y la tecla presionada. En
el Código de la Figura sobre manejo de eventos, hay una clase
ButtonHandler que maneja eventos. En el Código de la Figura , los eventos
se manejan dentro de la clase denominada TwoListener.

Varios puntos del código mostrados en el Cóigo de la Figura está descritos


en los siguientes párrafos.

La Implementación de Interfaces Múltiples

La clase está declarada en las Líneas 4 y 5 usando lo siguiente:


implements MouseMotionListener, MouseListener

Se pueden declarar múltiples interfaces separándolas por comas.

Métodos para Escuchar Múltiples Fuentes

Si se realizan las llamadas en las Líneas 20 y 21 a los métodos

f.addMouseListener(this);
f.addMouseMotionListener(this);

ambos tipos de eventos causan que los métodos sean llamados en la clase
TwoListener.

Un objeto puede escuchar tantas fuentes de eventos como se requiera. La


clase del objeto necesita implementar solo las interfaces requeridas.

La Obtención de Detalles acerca del Evento

Los argumentos del evento que manejan los métodos, tal como
mouseDragged(), contienen información potencialmente útil acerca del evento
original. Para determinar los detalles de qué información está disponible para
cada categoría del evento, verifique la documentación de clase apropiada en el
paquete java.awt.event.

1 import java.awt.*;
2 import java.awt.event.*;
3
4 public class TwoListener
5 implements MouseMotionListener, MouseListener {
6 private Frame f;
7 private TextField tf;
8
9 public TwoListener() {
10 f = new Frame("Two listeners example");
11 tf = new TextField(30);
12 }
13
14 public void launchFrame() {
15 Label label = new Label("Click and drag the
mouse");
16 // Add components to the frame
17 f.add(label, BorderLayout.NORTH);
18 f.add(tf, BorderLayout.SOUTH);
19 // Add this object as a listener
20 f.addMouseMotionListener(this);
21 f.addMouseListener(this);
22 // Size the frame and make it visible
23 f.setSize(300, 200);
24 f.setVisible(true);
25 }
26
27 // These are MouseMotionListener events
28 public void mouseDragged(MouseEvent e) {
29 String s = "Mouse dragging: X = " + e.getX()
30 + " Y = " + e.getY();
31 tf.setText(s);
32 }
33
34 public void mouseEntered(MouseEvent e) {
35 String s = "The mouse entered";
36 tf.setText(s);
37 }
38
39 public void mouseExited(MouseEvent e) {
40 String s = "The mouse has left the building";
41 f.setText(s);
42 }
43
44 // Unused MouseMotionListener method.
45 // All methods of a listener must be present in the
46 // class even if they are not used.
47 public void mouseMoved(MouseEvent e) { }
48
49 // Unused MouseListener methods.
50 public void mousePressed(MouseEvent e) { }
51 public void mouseClicked(MouseEvent e) { }
52 public void mouseReleased(MouseEvent e) { }
53
54 public static void main(String args[]) {
55 TwoListener
56 two.launchFrame();
57 }
58 }

18.7 El Comportamiento GUI


18.7.3 Múltíples Listeners

El framework de listening (escucha) de eventos de AWT permite asociar


múltiples listeners al mismo componente. En general, si se desea escribir un
programa que realice muchas acciones basadas en un único evento, se debe
incluir código para este comportamiento en el método manejador. Sin embargo,
algunas veces el diseño del programa requiere varias partes del mismo
programa que reaccionen al mismo evento. Esto podría suceder, por ejemplo,
si un sistema de ayuda sensible al contexto se agregara al programa existente.
El mecanismo de listener permite llamar a un método addXxxListener(), tantas
veces como sea necesario, y se pueden especificar tantos listeners diferentes
como el diseño requiera. Todos los listeners registrados tendrán sus métodos
manejadores cuando los eventos ocurran

18.8 El Desarrollo de Listeners de Eventos

En esta sección, usted aprenderá las opciones de diseño e implementación de


listeners de eventos.
18.8 El Desarrollo de Listeners de
Eventos
18.8.1 Los Adaptadores de Eventos

La implementación de todos los métodos para cada interfaz listener es un


trabajo muy tedioso, particularmente para las interfaces MouseListener y
WindowListener.

Por ejemplo, la interfaz MouseListener declara los siguientes métodos:

public void mouseClicked(MouseEvent event)


public void mouseEntered(MouseEvent event)
public void mouseExited(MouseEvent event)
public void mousePressed(MouseEvent event)
public void mouseReleased(MouseEvent event)

Sin embargo, el lenguaje de programación Java ofrece clases adaptadoras que


implementan cada interfaz conteniendo más de un método. Los métodos en
estas clases adaptadoras están vacíos.

Usted puede extender una clase adaptadora y sobrescribir solo aquellos


métodos que necesite, como se muestra en el Código de la Figura

18.8 El Desarrollo de Listeners de


Eventos
18.8.2 El Manejo de Eventos Usando
Clases Internas

Las Líneas 26 y 12-18 del Código de la Figura muestran cómo crear


manejadores como clases internas. El uso de clases internas para
manejadores de eventos ofrece acceso a los datos privado de la clase externa
(Línea 16).

1 import java.awt.*;
2 import java.awt.event.*;
3 public class TestInner {
4 private Frame f;
5 private TextField tf;
6
7 public TestInner() {
8 f = new Frame("Inner classes example");
9 tf = new TextField(30);
10 }
11
12 class MyMouseMotionListener extends MouseMotionAdapter {
13 public void mouseDragged(MouseEvent e) {
14 String s = "Mouse dragging: X = "+ e.getX()
15 + " Y = " + e.getY();
16 tf.setText(s);
17 }
18 }
19
20 public void launchFrame() {
21 Label label = new Label("Click and drag the mouse");
22 // Add components to the frame
23 f.add(label, BorderLayout.NORTH);
24 f.add(tf, BorderLayout.SOUTH);
25 // Add a listener that uses an Inner class
26 f.addMouseMotionListener(new
MyMouseMotionListener());
27 f.addMouseListener(new MouseClickHandler());
28 // Size the frame and make it visible
29 f.setSize(300, 200);
30 f.setVisible(true);
31 }
32
33 public static void main(String args[]) {
34 TestInner obj = new TestInner();
35 obj.launchFrame();
36 }
37 }

18.8 El Desarrollo de Listeners de


Eventos
18.8.3 El Manejo de Eventos Usando
Clases Anónimas

Se puede incluir la definición de una clase completa dentro del alcance de una
expresión. Este enfoque define lo que se denomina una clase interna anónima
y crea la instancia correspondiente al mismo tiempo. Las clases internas
anónimas se utilizan frecuentemente en manejo de eventos, tales como el
ejemplo que se muestra en el Código de la Figura

1 import java.awt.*;
2 import java.awt.event.*;
3
4 public class TestAnonymous {
5 private Frame f;
6 private TextField tf;
7
8 public TestAnonymous() {
9 f = new Frame("Anonymous classes example");
10 tf = new TextField(30);
11 }
12
13 public void launchFrame() {
14 Label label = new Label("Click and drag the mouse");
15 // Add components to the frame
16 f.add(label, BorderLayout.NORTH);
17 f.add(tf, BorderLayout.SOUTH);
18 // Add a listener that uses an anonymous class
19 f.addMouseMotionListener(new MouseMotionAdapter() {
20 Developing Event Listeners
21 public void mouseDragged(MouseEvent e) {
22 String s = "Mouse dragging: X = "+ e.getX()
23 + " Y = " + e.getY();
24 tf.setText(s);
25 }
26 }); // <- note the closing parenthesis
27 f.addMouseListener(new MouseClickHandler()); // Not
shown
28 // Size the frame and make it visible
29 f.setSize(300, 200);
30 f.setVisible(true);
31 }
32
33 public static void main(String args[]) {
34 TestAnonymous obj = new TestAnonymous();
35 obj.launchFrame();
36 }
37 }
18.9 Laboratorios

Laboratorio 11 - El Manejo de Eventos GUI

18.9 Laboratorios
18.9.1 Objetivos

Una vez completado este laboratorio, usted debería ser capaz de:

• Crear los manejadores de eventos para el proyecto ChatRoom.


• (Opcional) Crear los manejadores de eventos para el proyecto Banking.

18.9 Laboratorios
18.9.2 Ejercicio 1: Creación de la GUI de ChatClient Parte 2
(Nivel 1)

Ejercicio 1 - Crear la GUI de ChatClient Parte 2 (Nivel 1)

18.9 Laboratorios
18.9.3 Ejercicio 1: Creación de la GUI de ChatClient Parte 2
(Nivel 2)

Ejercicio 1 - Crear la GUI de ChatClient Parte 2 (Nivel 2)

18.9 Laboratorios
18.9.4 Ejercicio 1: Creación de la GUI de ChatClient Parte 2
(Nivel 3)

Ejercicio 1 - Crear la GUI de ChatClient Parte 2 (Nivel 3)

18.9 Laboratorios
18.9.5 Ejercicio 2: Creación de la GUI de una ATM de un
Banco Parte 2 (Avanzado)

Ejercicio 2 - Creación de la GUI de una ATM de un Banco Parte 2 (Avanzado)

18.9 Laboratorios
18.9.6 Resumen del Ejercicio

Discusión – Dedique unos minutos para discutir que experiencias, temas o


descubrimientos realizó durante el ejercicio de laboratorio.

• Experiencias
• Interpretaciones
• Conclusiones
• Aplicaciones

18.10 Preguntas de Repaso

Ahora le proponemos que resuelva el siguiente ejercicio para poner a prueba


sus conocimientos.

Defina por Verdadero o Falso las siguientes afirmaciones según corresponda.


¡Éxitos!
19 Las Aplicaciones Basadas en GUI

Este capítulo cubre temas generales acerca de la construcción de aplicaciones


usando presentación GUI.

19.1 Repaso del Capítulo Anterior

Los principales temas del capítulo anterior son:

¿Qué es un Evento?

Cuando el usuario realiza una acción a nivel de la interfaz de usuario (hace clic
sobre el ratón o presiona una tecla), esto produce un evento. Los eventos son
objetos que describen lo que ha sucedido.

Las Fuentes de Eventos

Una fuente es un generador de un evento. Por ejemplo, un clic con el ratón


sobre un componente Button genera una instancia ActionEvent con el botón
como fuente.

Dicha instancia contiene:

• getActionCommand - Devuelve el nombre del comando asociado con la


acción.
• getModifiers - Devuelve cualquier modificador asociado a la acción.

Los Manejadores de Eventos

Un manejador de eventos es un método que recibe un objeto evento, lo


descifra y procesa la interacción del usuario.

El Modelo de Eventos J2SE

El Modelo Delegacional de Eventos

Con este modelo, los eventos se envían al componente desde el evento que
los originó. Este, a su vez, es enviado a cada componente para propagar el
evento a una o más clases registradas, denominadas listeners.
Cada evento tiene una correspondiente interfaz Listener que determina los
métodos que deben ser definidos en una clase apropiada para recibirlos.

El Comportamiento GUI

Las Categorías de Eventos


Muchas de las clases de eventos residen en el paquete java.awt.event. Esta
interfaz demanda que uno o más métodos sean definidos. Estos métodos son
llamados cuando acontecen eventos particulares.
Los eventos no son manejados accidentalmente. Los objetos que se
programan para escuchar eventos particulares sobre un componente GUI se
registran con ese componente.

Múltíples Listeners

El framework de listening (escucha) de eventos de AWT permite asociar


múltiples listeners al mismo componente. En general, si se desea escribir un
programa que realice muchas acciones basadas en un único evento, se debe
incluir código para este comportamiento en el método manejador. Sin embargo,
algunas veces el diseño del programa requiere varias partes del mismo
programa que reaccionen al mismo evento.

19.3 Objetivos

Una vez completado este capítulo, usted será capaz de:

• Identificar los principales componentes AWT y los eventos que


desencadenan.
• Describir cómo construir una barra de menú, un menú y opciones de
menú en una Java GUI.
• Entender cómo se cambia el color y la fuente de un componente.

Discusión – Las siguientes preguntas son relevantes para el material


presentado en este capítulo:

• ¿Qué otros componentes serían útiles en una GUI?


• ¿Cómo podría usted crear un menú para su frame GUI?

Hasta ahora, usted sabe cómo asignar una Java GUI tanto para una
presentación gráfica como interactiva. Sin embargo, sólo se describieron
algunos de los componentes con los que se puede construir una GUI.

19.4 Recursos Adicionales

Recursos adicionales – Las siguientes referencias ofrecen información


adicional sobre los temas descritos en este capítulo:
• The Java Tutorial. [Online]. Disponible en:

http://java.sun.com/docs/books/
tutorial/uiswing/learn/example1.html

http://java.sun.com/docs/books/
tutorial/uiswing/learn/example2.html

http://java.sun.com/docs/books/
tutorial/uiswing/learn/example3.html

19.5 Los Componentes AWT

En el Capítulo 17, “La Construcción de GUI en Java", se presentaron algunos


componentes (Button) y varios contenedores (Panel y Frame). La Tabla en la
Figura presenta brevemente a todos los componentes AWT.
19.5 Los Componentes AWT
19.5.1 Listeners AWT

La Tabla de la Figura muestra los componentes AWT básicos y los listeners


de eventos que pueden asociarse con cada tipo de componente.
19.6 La Creacón de un Menú

Un Menu es diferente a otros componentes dado que no se puede agregar un


Menu a cualquier contenedor y además, debe ser gestionado desde fuera del
Administrador de Disposición (gestor de layout). Solamente se pueden agregar
menús a un contenedor Menu. Esta sección describe los procedimientos para
crear un menú:

1. Crear un objeto MenuBar y asignarlo a un contenedor menú, como, por


ejemplo, un Frame.
2. Crear uno o más objetos Menu y agregarlos al objeto barra del menú.
3. Crear uno o más objetos MenuItem y agregarlos al objeto menú

19.6 La Creacón de un
Menú
19.6.1 El Menú de Ayuda

Usando la barra de menú, se puede diseñar un Menú de Ayuda. Cada sistema


trata los menús de ayuda de forma diferente y, por consiguiente, es importante
para los usuarios que la aplicación se comporte como ellos esperan.
Por ejemplo, en un sistema X/Motif el menú de ayuda debe estar ubicado en el
borde superior derecho del barra de menú. La especificación de cuál es el
menú de ayuda se realiza utilizando el método setHelpMenu.

19.6 La Creacón de un
Menú
19.6.2 La Creación de un Componente MenuBar

Un componente MenuBar es un menú horizontal. Este objeto solamente puede


ser agregado a objetos Frame y forma la raíz de todos los árboles del menú.
Un Frame despliega un MenuBar por vez. Sin embargo, se puede cambiar el
MenuBar basado en el estado del programa de forma tal que en distintos
puntos aparezcan menús diferentes. Por ejemplo:

1. Frame f = new Frame("MenuBar");


2. MenuBar mb = new MenuBar();
3. f.setMenuBar(mb);

La Figura muestra las pantallas que resultan del código anterior.

El MenuBar no soporta listeners. Todos los hechos que se producen sobre una
barra de menú son procesados por los menús que se agregan a la barra de
menú.
19.6 La Creacón de un Menú
19.6.3 La Creación de un Menú

El componente Menu ofrece un menú pull-down básico, aunque se le puede


agregar a un MenuBar o a otro Menu. Por ejemplo:

1 Frame f = new Frame("Menu");


2 MenuBar mb = new MenuBar();
3 Menu m1 = new Menu("File");
4 Menu m2 = new Menu("Edit");
5 Menu m3 = new Menu("Help");
6 mb.add(m1);
7 mb.add(m2);
8 mb.setHelpMenu(m3);
9 f.setMenuBar(mb);
La Figura muestra las pantallas resultantes del código anterior.

Se puede agregar un objeto ActionListener a un objeto Menu, pero esto no es


usual. Generalmente, se utilizan menús para desplegar y controlar los ítems de
un menú, los que son descritos en la siguiente sección

19.6 La Creacón de un Menú


19.6.4 La Creación de un MenuItem

Los componentes MenuItem constituyen los nodos que son hojas del árbol de
un menú, los cuales contienen texto. Estos se agregan al menú para
completarlo, como se muestra en el siguiente ejemplo:

1 MenuItem mi1 = new MenuItem("New");


2 MenuItem mi2 = new MenuItem("Save");
3 MenuItem mi3 = new MenuItem("Load");
4 MenuItem mi4 = new MenuItem("Quit");
5 mi1.addActionListener(this);
6 mi2.addActionListener(this);
7 mi3.addActionListener(this);
8 mi4.addActionListener(this);
9 m1.add(mi1);
10 m1.add(mi2);
11 m1.add(mi3);
12 m1.addSeparator();
13 m1.add(mi4);

La Figura muestra las pantallas resultantes de este código.

Generalmente, se agrega un ActionListener a un objeto MenuItem para proveer


comportamiento a los menús

19.6 La Creacón de un Menú


19.6.5 La Creación de un
CheckboxMenuItem

El componente CheckboxMenuItem es un ítem de un menú con opciones de


verificación (on u off). Por ejemplo:

1 MenuBar mb = new MenuBar();


2 Menu m1 = new Menu("File");
3 Menu m2 = new Menu("Edit");
4 Menu m3 = new Menu("Help");
5 mb.add(m1);
6 mb.add(m2);
7 mb.setHelpMenu(m3);
8 f.setMenuBar(mb);
9 .....
10 MenuItem mi2 = new MenuItem("Save");
11 mi2.addActionListener(this);
12 m1.add(mi2);
13 ......
14 CheckboxMenuItem mi5
15 = new CheckboxMenuItem("Persistent");
16 mi5.addItemListener(this);
17 m1.add(mi5);

La Figura muestra las pantallas resultantes del código anterior.

Se puede monitorizar el CheckboxMenuItem usando la interfaz ItemListener.

19.7 El Control de Aspectos Visuales

Se pueden controlar los colores usados para la presentación y fondo de los


componentes AWT
19.7 El Control de Aspectos Visuales
19.7.1 Los Colores

Se pueden utilizar dos métodos para asignar los colores a un componente:

setForeground()
setBackground()

Ambos toman como argumento una instancia de la clase java.awt.Color. Se


pueden usar colores constantes tales como Color.red, Color.blue.

El rango completo de colores predefinidos se lista en la página de


documentación para la clase Color. También se puede construir un objeto Color
específico, indicando el color como una combinación de tres enteros de tamaño
byte (0-255). Cada uno de estos tres valores corresponde a un color primario:
rojo, azul y verde.

Por ejemplo:

Color purple = new Color(255, 0, 255);


Button b = new Button("Purple");
b.setBackground(purple);

19.8 La Tecnología J.F.C.Swing

AWT es el toolkit GUI predominante en el desarrollo basado en la tecnología


Java. Sin embargo, a partir de Java 2 SDK hay otra opción: tecnología
J.F.C./Swing.

La tecnología J.F.C./Swing (parte de la extensión de la Java Foundation


Classes) es un toolkit GUI de segunda generación que se incluye en el Java 2
SDK como una extensión estándar. La tecnología J.F.C./Swing tiene varias
mejoras sobre AWT. Solamente algunas de ellas se describen en este capítulo.

La tecnología J.F.C./Swing está construida sobre AWT (utiliza Color, Font, etc.),
pero implementa sus clases componentes sin usar puntos dependientes de la
plataforma, utilizados en los componentes AWT. Esto hace que los
componentes de la tecnología J.F.C./Swing sean livianos. La tecnología
J.F.C./Swing también agrega componentes nuevos y más complejos,
incluyendo componentes para tablas y árboles
19.9 Laboratorios

Laboratorio 12 - Las Aplicacionmes Basadas en GUI

19.9 Laboratorios
19.9.1 Objetivos

Una vez completado este laboratorio, usted deberá ser capaz de agregar
menús a la GUI del proyecto del ChatRoom.

19.9 Laboratorios
19.9.2 Ejercicio: Creación de la GUI ChatClient Parte 3 (Nivel 1)

Ejercicio 1 - Crear la GUI de ChatClient Parte 3 (Nivel 1)

19.9 Laboratorios
19.9.3 Ejercicio: Creación de la GUI ChatClient Parte 3 (Nivel 2)

Ejercicio 1 - Crear la GUI de ChatClient Parte 3 (Nivel 2)

19.9 Laboratorios
19.9.4 Ejercicio: Creación de la GUI ChatClient
Parte 3 (Nivel 3)

Ejercicio 1 - Crear la GUI de ChatClient Parte 3 (Nivel 3)

19.9 Laboratorios
19.9.5 Resumen del Ejercicio

Discusión – Dedique unos minutos para discutir que experiencias, temas o


descubrimientos realizó durante el ejercicio de laboratorio.

• Experiencias
• Interpretaciones
• Conclusiones
• Aplicaciones

19.10 Preguntas de Repaso

Ahora le proponemos que resuelva el siguiente ejercicio para poner a prueba


sus conocimientos.
Defina por Verdadero o Falso las siguientes afirmaciones según corresponda.
¡Éxitos!
20 Los Hilos

Este capítulo cubre los temas de multihilos, que permiten a un programa


realizar varias tareas al mismo tiempo

20.1 Repaso del Capítulo Anterior

Los principales temas del capítulo anterior son:

Aplicaciones Basadas en GUI

La Creación de un Menú

Un Menu es diferente a otros componentes dado que no seuede agregar un


Menu a cualquier contenedor y además, debe ser gestionado desde fuera del
Administrador de Disposición (gestor de layout).

El Menú de Ayuda

Usando la barra de menú, se puede diseñar un Menú de Ayuda. Cada sistema


trata los menús de ayuda de forma diferente y, por consiguiente, es importante
para los usuarios que la aplicación se comporte como ellos esperan.
La Creación de un Componente MenuBar

Un componente MenuBar es un menú horizontal. Este objeto solamente puede


ser agregado a objetos Frame y forma la raíz de todos los árboles del menú.

Por ejemplo:

1. Frame f = new Frame("MenuBar");


2. MenuBar mb = new MenuBar();
3. f.setMenuBar(mb);

La Creación de un Menu

El componente Menu ofrece un menú pull-down básico, aunque se le puede


agregar a un MenuBar o a otro Menu. Por ejemplo:

1 Frame f = new Frame("Menu");


2 MenuBar mb = new MenuBar();
3 Menu m1 = new Menu("File");
4 Menu m2 = new Menu("Edit");
5 Menu m3 = new Menu("Help");
6 mb.add(m1);
7 mb.add(m2);
8 mb.setHelpMenu(m3);
9 f.setMenuBar(mb);

La Creación de un MenuItem

Los componentes MenuItem constituyen los nodos que son hojas del árbol de
un menú, los cuales contienen texto.

La Creación de un CheckboxMenuItem

El componente CheckboxMenuItem es un ítem de un menú con opciones de


verificación (on u off).
Se puede monitorizar el CheckboxMenuItem usando la interfaz ItemListener.

El Control de Aspectos Visuales

Se pueden controlar los colores usados para la presentación y fondo de los


componentes AWT.

Los Colores

Se pueden utilizar dos métodos para asignar los colores a un componente:

setForeground()
setBackground()

La Tecnología J.F.C./Swing
La tecnología J.F.C./Swing tiene varias mejoras sobre AWT.

20.3 Objetivos

Una vez completado este capítulo, usted estará en condiciones de:

• Definir un hilo.
• Crear hilos separados en un programa basado en la tecnología Java,
controlando el código y los datos que son usados por cada hilo.
• Controlar la ejecución de un hilo y escribir código con hilos
independiente de la plataforma.
• Describir las dificultades que se pueden producir cuando múltiples hilos
comparten datos.
• Usar wait y notify para realizar comunicaciones entre hilos.
• Usar synchronized para proteger datos.

Discusión – La siguiente pregunta es relevante para el material presentado en


este capítulo:

• ¿Cómo hacen los programas para realizar múltiples tareas al mismo


tiempo?

20.4 Recursos Adicionales

Recursos adicionales – Las siguientes referencias ofrecen información


adicional sobre los temas descritos en este capítulo:

• The Java Tutorial. [Online]. Disponible en:

http://java.sun.com/docs/books/
tutorial/essential/concurrency/
procthread.html

http://java.sun.com/docs/books/
tutorial/essential/concurrency/
runthread.html

20.5 Los Hilos

Una visión simplificada de un computador consiste en una CPU que realiza


cálculos, una memoria que contiene el programa que la CPU ejecuta y una
memoria que almacena los datos sobre los cuales el programa opera. De
acuerdo con esta visión, hay solo un trabajo realizándose en un determinado
momento. Una visión más completa de los sistemas de computadores
modernos ofrece la posibilidad de realizar más de un trabajo simultáneamente.

El programador no necesita conocer cómo se realizan estos trabajos, sino


solamente considerar las implicaciones desde el punto de vista de la
programación. Realizar más de un trabajo es similar a tener más de un
computador. En este módulo, un hilo o un contexto de ejecución se consideran
como la encapsulación de una CPU virtual con su propio
código y sus propios datos de programa.

La clase java.lang.Thread permite crear y controlar hilos.

Un hilo o contexto de ejecución, está compuesto de tres partes principales:

• Una CPU virtual.


• El código que la CPU ejecuta.
• Los datos sobre los cuales el código trabaja.

Un proceso es un programa en ejecución. Uno o más hilos constituyen un


proceso. Un hilo está compuesto de la CPU, el código y los datos, como se
ilustra en la figura .

El código puede ser compartido por múltiples hilos independientemente de los


datos. Dos hilos comparten igual código cuando ejecutan código a partir de
instancias de la misma clase.

También, los datos pueden ser compartidos por múltiples hilos, sin tener en
cuenta el código. Dos hilos comparten los mismos datos cuando comparten el
acceso a un objeto común.

En la programación Java, la CPU virtual está encapsulada en una instancia de


la clase Thread. Cuando se construye un hilo, el código y los datos que definen
su contexto están especificados por el objeto pasado al constructor.
20.5 Los Hilos
20.5.1 La Creación del Hilo

Esta sección examina cómo crear un hilo y cómo usar los argumentos de un
constructor para proporcionar el código y los datos para un hilo cuando este se
ejecute. Un constructor Thread toma un argumento que es una instancia de la
clase Runnable. Una instancia de la clase Runnable se realiza a partir de una
clase que implementa la interfaz Runnable (esto es, provee un método public
void run()).

El método main construye una instancia r de la clase HelloRunner. La instancia


r tiene sus propios datos, en este caso el entero i. Dado que la instancia r es
pasada al constructor de la clase Thread, el entero i de r es el dato con el cual
el hilo trabajará cuando ejecute. El hilo siempre comienza su ejecución en el
método run de su instancia Runnable cargada (r en la Figura ).

Un entorno de programación multihilo permite crear múltiples hilos basados en


la misma instancia Runnable.

Esto se puede hacer de la siguiente manera:

Thread t1 = new Thread(r);


Thread t2 = new Thread(r);

En este caso, ambos hilos comparten los mismos datos y el mismo código.
Para resumir, un hilo se referencia a través de una instancia de un objeto
Thread. El hilo comienza su ejecución al comienzo del método run de la
instancia Runnable cargada. Los datos sobre los que el hilo trabaja son
tomados de la instancia específica de Runnable, la cual se pasa al constructor
Thread (Figura )

1 public class ThreadTester {


2 public static void main(String args[]) {
3 HelloRunner r = new HelloRunner();
4 hread t = new Thread(r);
5 t.start();
6 }
7 }
8
9 class HelloRunner implements Runnable {
10 int i;
11
12 public void run() {
13 i = 0;
14
15 while (true) {
16 System.out.println(“Hello “ + i++);
17 if ( i == 50 ) {
18 break;
19 }
20 }
21 }
22 }

20.5 Los Hilos


20.5.2 El Inicio de Ejecución de un Hilo

Un nuevo hilo no comienza su ejecución en forma automática. Se debe llamar


al método start para esto. Por ejemplo, se tiene que ejecutar el siguiente
comando, como en la Línea 5 de la Figura del apartado 13.3.1:

t.start();

La llamada al método start coloca la CPU virtual embebida en el hilo, en un


estado de ejecución. Esto significa que el hilo está habilitado para planificar su
ejecución por la JVM. No significa, sin embargo, que el hilo comienza a
ejecutarse inmediatamente.

20.5 Los Hilos


20.5.3 La Planificación de Hilos
Generalmente, los hilos de la tecnología Java son preemtivos (preemptive),
pero no necesariamente time-sliced (el proceso de asignar a cada hilo una
cantidad igual de tiempo de CPU). Es un error común creer que preemtivo es
un sinónimo de time-slicing.

El modelo de un planificador preemtivo hace que muchos hilos pueden estar en


estado runnable (listo para ser ejecutado), pero solo uno se está ejecutando
efectivamente. Este hilo continúa ejecutándose hasta que deja de ser runnable
o hasta que otro hilo de mayor prioridad se transforme en runnable. En este
último caso, el hilo de menor prioridad es pasado a estado preemtivo
(preempted) por el hilo de mayor prioridad.

Un hilo puede dejar de ser runnable (esto es, transformarse en blocked


(bloqueado)) por muchas razones. El código del hilo puede ejecutar una
llamada a Thread.sleep(), solicitándole al hilo que se detenga deliberadamente
por un período fijo de tiempo. El hilo podría tener que esperar por un acceso a
un recurso y, por lo tanto, no continuar hasta que
el recurso quedara disponible.

Todos los hilos que están en estado runnable se mantienen en un pool de


acuerdo con su prioridad. Cuando un hilo bloqueado se transforma en runnable,
vuelve a su pool runnable. A los hilos del pool no vacío de mayor prioridad se
les asigna tiempo de CPU.

Un objeto Thread, durante su ciclo de vida, puede existir en varios estados


diferentes, como se muestra en la Figura

Por otra parte, cuando un hilo se transforma en runnable, no comienza a


ejecutarse inmediatamente. Se realiza solo una acción a la vez sobre una
máquina con una CPU. Los siguientes párrafos describen cómo se asigna la
CPU cuando más de un hilo está en estado runnable.

Dado que los hilos de Java no son necesariamente time-sliced, se debe


asegurar que el código para los hilos dé la oportunidad de ejecutar a otros hilos
cada determinado tiempo. Esto puede lograrse llamando al método sleep cada
cierto intervalo de tiempo, como se muestra en el Código de la Figura

El Código de la Figura muestra cómo se utilizan los bloques try y catch. El


método Thread.sleep() y otros métodos que pueden detener un hilo por
períodos de tiempo tienen la posibilidad de ser interrumpidos. Los hilos pueden
llamar a otros métodos interrupt de hilos, que indican al hilo detenido con un
InterruptedException.

El método sleep es un método estático de la clase Thread, dado que opera


sobre el hilo corriente y se debe referir a él como Thread.sleep(x). El
argumento del método sleep especifica la cantidad mínima de milisegundos
durante la cual el hilo deberá estar inactivo. La ejecución del hilo no se
reestablece hasta este periodo a menos que sea interrumpido, en cuyo caso la
ejecución se reestablece antes.
20.5 Los Hilos
20.5.4 La Finalización de un Hilo

Cuando un hilo completa su ejecución y termina, no puede ejecutarse


nuevamente. Se puede detener un hilo usando un flag (una bandera) que
indique que el método run debe finalizar. (ver Figura )

En un segmento de código particular, se puede obtener una referencia al hilo


corriente usando el método estático currentThread. (ver Figura )

1 public class Runner implements Runnable {


2 private boolean timeToQuit=false;
3
4 public void run() {
5 while ( ! timeToQuit ) {
6 // ejecutar hasta que sea interrumpido
7 }
8 // actividades antes de finalizar el run()
9 }
10
11 public void stopRunning() {
12 timeToQuit=true;
13 }
14 }

1 public class ThreadController {


2 private Runner r = new Runner();
3 private Thread t = new Thread(r);
4
5 public void startThread() {
6 t.start();
7 }
8
9 public void stopThread() {
10 // usar una instancia específica de Runner
11 r.stopRunning();
12 }
13 }
20.6 El Control Básico de Hilos

Esta sección describe cómo controlar hilos.

20.6 El Control Básico de Hilos


20.6.1 La Prueba de Hilos

Un hilo puede estar en un estado desconocido. Para determinar si un hilo


todavía está disponible, se puede utilizar el método isAlive. El término Alive no
implica que el hilo se esté ejecutando. El método devolverá true para un hilo
que haya comenzado y que no haya completado su tarea.

20.6 El Control Básico de


Hilos
20.6.2 El Acceso a la Prioridad de un Hilo

Para determinar la prioridad actual de un hilo se utiliza el método getPriority.


Para asignar una determinada prioridad a un hilo se utiliza el método setPriority.
La prioridad es un valor entero. La clase Thread incluye las siguientes
constantes:

Thread.MIN_PRIORITY
Thread.NORM_PRIORITY
Thread.MAX_PRIORITY

20.6 El Control Básico de Hilos


20.6.3 Los Hilos Pendientes

Existen mecanismos que pueden bloquear temporalmente la ejecución de un


hilo. Dicha ejecución se puede restablecer después, como si nada hubiese
pasado. Podrá parecer que el hilo ejecuta una instrucción en forma muy lenta.

El Método Thread.sleep()

El método sleep constituye una manera de detener un hilo por un tiempo.


Recuerde que el hilo no necesariamente reestablece su ejecución en el
momento exacto en que expira su período de sleep. Esto puede deberse a que
otro hilo podría estar ejecutando en ese instante y no se podría replanificar a
menos que:

• El hilo despertado fuera de una prioridad mayor.


• El hilo que está ejecutando quedará bloqueado por alguna otra razón.

El Método join

El método join hace que el hilo corriente quede esperando hasta que el hilo
sobre el que se aplica termine. Ver Figura

También se puede llamar al método join con un valor de time-out en


milisegundos.

Por ejemplo:

void join(long timeout);

Para este ejemplo, el método join o suspende al hilo corriente por timeout
milisegundos o hasta que el hilo que lo llamó finalice.

El Método Thread.yield()

Se utiliza el método Thread.yield() para darle a los hilos runnable la


oportunidad de ejecutarse. Si otros hilos están runnable, yield coloca al hilo
llamado en el pool runnable y le permite a otro hilo runnable ejecutarse. Si no
hay otros hilos en estado runnable, yield no hace nada. Una llamada al método
sleep le da a los hilos de menor prioridad la oportunidad de ejecutarse. El
método yield da a otros hilos runnables una oportunidad de ejecutarse.
20.7 Las Otras Formas de Crear Hilos

Se ha visto cómo crear contextos de hilos con una clase que implementa
Runnable. Sin embargo, esta no es la única opción. La clase Thread
implementa la interfaz Runnable. Por lo tanto, se puede crear un hilo creando
una clase que extienda Thread en lugar de implementar Runnable.
20.7 Las Otras Formas de Crear
Hilos
20.7.1 La Selección de una Forma para
Crear Hilos

Dadas varias opciones para crear un hilo, ¿cómo decide entre ellas? Cada
opción tiene sus ventajas, que se describen en esta sección:

Las ventajas de la implementación de Runnable:

• Desde un punto de vista de diseño orientado a objetos, la clase Thread


es estrictamente una encapsulación de una CPU virtual y, como tal,
debe extenderse solamente cuando se cambia o extiende el
comportamiento del modelo CPU. Por eso y por la distinción entre CPU,
código y datos de un hilo en ejecución, en este módulo del curso
se utilizó esta opción.
• Dado que la tecnología Java permite solamente herencia simple, no se
puede extender de otra clase, tal como Applet, si ya se extendió de
Thread. En algunas situaciones esto fuerza a tomar la opción de
implementar Runnable.
• Ya que algunas veces se está obligado a implementar Runnable, se
debe preferir ser consistente y hacerlo siempre de esta manera.
Las ventajas de extender Thread:

• El código tiende a ser más simple

20.8 El Uso de la Palabra Clave synchronized

Esta sección describe el uso de la palabra clave synchronized. Esta provee al


lenguaje de programación Java un mecanismo que le permite al programador
controlar los hilos que comparten datos.

20.8 El Uso de la Palabra Clave synchronized


20.8.1 El Problema

Imagine una clase que representa un stack. La clase podría parecerse a la


presentada en la Figura

La clase no realiza ninguna actividad para controlar los estados de overflow o


underflow del stack. Sin embargo, su capacidad es limitada, aunque este
aspecto no es relevante para la discusión.
El comportamiento de este modelo requiere que el valor de idx corresponda al
índice del arreglo de la siguiente celda vacía en el stack. La opción de pre-
decremento, post-incremento genera esta información.

Imagine ahora que dos hilos tienen una referencia a una única instancia de
esta clase. Un hilo realiza inserciones (pushs) en el stack, mientras que el otro
realiza extracciones (pops) en forma independiente. En principio, los datos se
agregan y eliminan satisfactoriamente. Sin embargo, hay un problema potencial.

Suponga que el hilo a está agregando caracteres y que el hilo b está quitando
caracteres. El hilo a agregó un carácter, pero aún no realizó el incremento del
índice. Por alguna razón, el hilo entra en estado preemtivo. En ese momento, el
modelo de datos representado en el objeto es inconsistente.

buffer |p|q|r| | | |
idx = 2 ^

Específicamente, la consistencia requiere o que idx sea igual a 3 o que el


carácter no haya sido agregado.

Si el hilo a retoma la ejecución, no habrá daños. Pero suponga que el hilo b


estaba esperando para eliminar un carácter. Mientras que el hilo a está
esperando por otra oportunidad para ejecutar, el hilo b tiene la oportunidad de
eliminar un carácter. Esto genera una situación de datos inconsistentes sobre
la entrada para el método pop, dado que este procede
a disminuir el valor del índice.

buffer |p|q|r| | | |
idx = 1 ^

Esto efectivamente produce que se ignore el carácter r. Luego, el método


devuelve el carácter q. Por lo tanto, el comportamiento que tuvo lugar hizo de
cuenta que la letra r nunca fue agregada al stack, y, por lo tanto, se produjo un
problema.

Supongamos ahora que a retoma su ejecución, en el método push, y procede a


incrementar el valor del índice. Esto producirá la siguiente situación:

buffer |p|q|r| | | |
idx = 2 ^

Esta configuración implica que q es válido y la celda que contiene a r es la


siguiente celda vacía. En otras palabras, es como si q hubiese sido agregado
dos veces y el elemento r nunca se hubiese agregado.

Este es un ejemplo simple de un problema general que puede producirse


cuando varios hilos acceden a los mismos datos. Por consiguiente, se necesita
un mecanismo para asegurar que los datos compartidos estén en un estado
consistente antes de que cualquier otro hilo comience a usarlos para una tarea
particular.
Una opción puede consistir en advertir al hilo que abandone su ejecución hasta
que complete la sección crítica de código. Esta opción es común en
programación de máquinas de bajo nivel, pero, generalmente inapropiada para
sistemas multi-usuarios.

Otra opción - sobre la que la tecnología Java trabaja - consiste en proveer un


mecanismo que trate los datos con delicadeza. Esta opción provee un hilo
indivisible con acceso a los datos sin importar si abandona su ejecución en el
medio de la realización de dicho acceso

20.8 El Uso de la Palabra Clave


synchronized
20.8.2 La Bandera de Lock de un
Objeto

En la tecnología Java, cada objeto tiene una bandera asociada con él. Se
puede interpretar dicha bandera como una bandera de lock (o bloqueo). La
palabra clave synchronized permite la interacción de esta bandera y provee
acceso exclusivo al código que afecta los datos compartidos. El fragmento de
código modificado se muestra a continuación:

public class MyStack {


...
public void push(char c) {
synchronized(this) {
data[idx] = c;
idx++;
}
}
...
}

Cuando el hilo alcanza la sentencia synchronized, examina el objeto pasado


como argumento y trata de obtener la bandera de lock del objeto antes de
continuar (Ver Figura )

La Figura muestra un ejemplo de uso de la sentencia synchronized después


de un hilo.

Si se realiza esto, no se protegen los datos. Si el método pop del objeto con
datos compartidos no está protegido por un synchronized y pop es invocado
por otro hilo, hay riesgo de daño a la consistencia de los datos. Todos los
métodos que acceden a los datos compartidos deben sincronizar sobre el
mismo lock, si el lock se va a hacer efectivo.

La Figura ilustra lo que pasa si pop está protegido por synchronized y otro
hilo trata de ejecutar el método pop de un objeto, mientras el hilo original
suspende la bandera de lock del objeto sincronizado.

Cuando el hilo trata de ejecutar la sentencia synchronized(this), intenta tomar la


bandera de lock del objeto this. Dado que la bandera no está presente, el hilo
no puede continuar su ejecución. Entonces, se une al pool de hilos en estado
de espera que están asociados con esa bandera de lock del objeto. Cuado la
bandera es devuelta al objeto, alguno de los hilos que está esperando por la
bandera la toma y el hilo continúa su ejecución.
20.8 El Uso de la Palabra Clave
synchronized
20.8.3 La Liberación de una
Bandera de Lock

La bandera debe estar disponible para que un hilo que está esperando por una
bandera de lock de un objeto pueda retomar su ejecución. Por lo tanto, es
importante para el hilo que está reteniendo la bandera, devolverla cuando ya no
la necesite.

La bandera de lock se devuelve automáticamente a su objeto. Cuando el hilo


que retiene la bandera de lock pasa el fin del bloque de código synchronized
para el que el lock fue obtenido, la bandera de lock es liberada. También, si un
hilo ejecuta bloques anidados de código que están sincronizados sobre el
mismo objeto, estas banderas del objeto se
liberan correctamente a partir del bloque más externo y los bloques interiores
se ignoran.

Esta regla hace que el manejo de bloques sincronizados sea más simple que
otras opciones equivalentes en otros sistemas.

20.8 El Uso de la Palabra Clave


synchronized
20.8.4 El Uso de synchronized

El mecanismo de sincronización funciona solo si todos los accesos a los datos


delicados ocurren dentro de bloques sincronizados.

Se deben marcar los datos delicados protegidos por bloques sincronizados


como private. Si no se realiza esto, se puede acceder a los datos delicados
desde cualquier código fuera de la definición de la clase. Esta situación podría
permitir a otros programadores saltear esta protección y causar una corrupción
de los datos durante el momento de
ejecución.

Un método que tiene todo su código en un bloque sincronizado con la instancia


this podría tener la palabra clave synchronized en su cabezal. Los dos
fragmentos de código siguientes son equivalentes:

¿Por qué utilizar una técnica en lugar de la otra?

Si se utiliza synchronized como un modificador de método, el método completo


se transforma en un bloque sincronizado. Esto puede provocar que la bandera
de lock quede asignada por más tiempo que el necesario.

Sin embargo, si se hace al método de esta manera, los usuarios pueden saber
que el método está sincronizado usando la documentación generada por el
utilitario javadoc. Esto es muy importante cuando se diseña para prevenir
deadlock (el cual es descrito en la siguiente sección). El generador de
documentación javadoc propaga el modificador synchronized en los archivos
de documentación, pero no puede realizar lo mismo con synchronized(this),
dado que se encuentra dentro del bloque del método.

20.8 El Uso de la Palabra Clave


synchronized
20.8.5 Los Estados de un Hilo

La sincronización es un estado especial del hilo.

La Figura ilustra el nuevo diagrama de transición de estados para un hilo.


20.8 El Uso de la Palabra Clave
synchronized
20.8.6 Abrazo Mortal (Deadlock)

En programas donde múltiples hilos están compitiendo para acceder a recursos


compartidos, puede ocurrir una condición conocida como deadlock. Esto
sucede cuando un hilo está esperando por un lock que tiene otro hilo, pero este
otro hilo está esperando por un lock que ya tiene el primer hilo. En esta
condición, ninguno puede proceder hasta que el otro haya pasado el fin de su
bloque synchronized. Por lo tanto, ninguno alcanzará el fin de su bloque.

Dijkstra describió el problema con la cena de los famosos cinco filósofos chinos
que sólo tenían cinco palillos para comer arroz . Si dos filósofos adyacentes
intentan tomar el mismo palillo a la vez, se produce una condición de carrera:
ambos compiten por tomar el mismo palillo, y uno de ellos se queda sin comer.
Si ellos no se ponían de acuerdo y tomaban un palillo cada uno, creaban un
deadlock y morían de hambre pues se necesitaban dos palillos para comer. El
concepto de abrazo mortal (deadlock) obtiene su solución a través de
semáforos y regiones de código con acceso exclusivo. Esta es la base de la
programación concurrente y una parte fundamental de cualquier sistema
operativo.

La tecnología Java no puede detectar cómo impedir esta situación. Es


responsabilidad del programador asegurar que un deadlock no puede suceder.
Una regla general para evitar un deadlock es la siguiente: si usted tiene
múltiples objetos que quiere que tengan acceso sincronizado, tome una
decisión global acerca del orden en que obtendrá estos locks y siga este orden
a través del programa. Libere los locks en el orden inverso al que los obtuvo.

20.9 La Interacción de Hilos - wait y notify

Distintos hilos se crean para realizar tareas específicas. Sin embargo, algunas
veces los trabajos que realizan están relacionados y es necesario que los
mismos interaccionen.

20.9 La Interacción de Hilos - wait y notify


20.9.1 La Situación

Considere los siguientes dos hilos: un taxista y usted. Usted necesita un taxi
para llegar a su destino y el taxista necesita que un pasajero tome el taxi para
cobrar por su trabajo. Por lo tanto, cada uno tiene una tarea.

20.9 La Interacción de Hilos - wait y


notify
20.9.2 El Problema
Usted espera tomar el taxi y viajar hasta que el taxista le notifique que ha
llegado a su destino. Resultaría molesto para usted y para él preguntar cada
dos segundos "¿ya llegamos a destino?". Entre distintos viajes, el taxista quiere
dormir en el taxi hasta que otro pasajero necesite ser conducido hasta algún
lugar. No desea despertarse de su siesta cada cinco minutos para ver si arribó
un pasajero. Por lo tanto, ambos hilos preferirían realizar su trabajo de la
manera más relajada posible.

20.9 La Interacción de Hilos - wait y notify


20.9.3 La Solución

Usted y el taxi necesitan alguna forma de comunicar sus necesidades al otro.


Mientras usted está ocupado caminando por la calle buscando un taxi, el
taxista está durmiendo plácidamente en la cabina. Cuando usted le avisa que
quiere tomar el taxi, él se despierta y comienza a conducir. Cuando usted arriba
a su destino, el taxista le notifica que ha llegado y continúa con su trabajo. El
taxista debe ahora esperar y dormir nuevamente una siesta hasta que el
próximo pasajero llegue

20.10 La Interacción de Hilos

Esta sección describe cómo interaccionan los hilos


20.10 La Interacción de Hilos
20.10.1 Los Métodos wait y notify

La clase java.lang.Object provee dos métodos, wait y notify, para la


comunicación entre hilos. Si un hilo envía una llamada a un wait sobre un
objeto rendezvous x, ese hilo pausa su ejecución hasta que otro hilo envíe una
llamada notify sobre el mismo objeto rendezvous x.

La situación anterior - el taxista esperando en el taxi - puede traducirse como


"el hilo taxista ejecutando una llamada taxi.wait" y su necesidad de usar el taxi
se puede traducir como "su hilo ejecutando una llamada taxi.notify".

Cuando un hilo llama a un wait o a un notify sobre un objeto, el hilo debe tener
el lock para ese objeto particular. En otras palabras, wait y notify son llamados
solo desde adentro de un bloque sincronizado sobre la instancia desde la cual
son llamados. Para este ejemplo, se requiere un bloque que comience con
synchronized(taxi) para permitir tanto la llamada taxi.wait como la llamada
taxi.notify().

El Funcionamiento del Pool

Cuando un hilo ejecuta un código sincronizado que contiene una llamada a un


wait sobre un objeto particular, el hilo se coloca en el pool de wait para ese
objeto. Además, el hilo que llama al wait, libera, automáticamente la bandera
de lock del objeto. Hay distintas formas de invocar al método wait:

wait().
wait(long timeout)

Cuando una llamada notify se ejecuta sobre un objeto particular, un hilo


arbitrario se traslada desde el pool de wait del objeto al pool de lock, donde los
hilos permanecen hasta que la bandera de lock del objeto quede disponible. El
método notifyAll saca a todos los hilos que esperan sobre un objeto del pool de
wait y los coloca en el pool de lock. Solo desde el pool de lock un hilo puede
obtener la bandera de lock del objeto, la cual le permite al hilo continuar
ejecutándose a partir de donde realizó la llamada wait.

En muchos sistemas que implementan el mecanismo wait-notify, el hilo que se


despierta es el que lleva más tiempo esperando. Sin embargo, esto no se
garantiza en la tecnología Java.

Usted puede enviar una llamada notify sin importar si otros hilos están
esperando. Si el método notify es llamado sobre un objeto cuando no hay hilos
bloqueados en el pool de wait para una bandera de lock sobre otros objetos, la
llamada no tiene efecto. Las llamadas a notify no se almacenan.
20.10 La Interacción de Hilos
20.10.2 Los Estados de los Hilos

El pool de wait es también un estado de hilo especial. La Figura ilustra el


diagrama de transición final para un hilo.

20.10 La Interacción de
Hilos
20.10.3 El Modelo de Monitores para la
Sincronización

La coordinación entre dos hilos que necesitan acceder a datos comunes puede
resultar compleja. Usted debe asegurarse de que no hay hilos que dejan los
datos compartidos en un estado inconsistente cuando existe la posibilidad de
que cualquier otro hilo acceda a ese dato. Usted debe asegurarse también de
que su programa no produce una situación de
deadlock, dado que los hilos no pueden liberar el lock apropiado cuando un hilo
está esperando por ese lock.

En el ejemplo del taxista, el código depende de un objeto rendezvous, el taxi,


sobre el que se ejecutan wait y notify . Si alguien estaba esperando un ómnibus,
entonces se necesitaría un objeto ómnibus separado sobre el cual aplicar un
notify. Recuerde que todos los hilos en el mismo pool de wait deben ser
satisfechos por notificaciones del objeto que controla el pool de wait. Nunca
diseñe código que coloque los hilos esperando a ser notificados por diferentes
condiciones en el mismo pool de wait.

20.11 Un Ejemplo Completo

El código en esta sección es un ejemplo de la interacción de hilos que muestra


el uso de los métodos wait y notify para resolver un problema clásico de
productor-consumidor.

Comience por mirar la situación del objeto stack y los detalles de los hilos que
acceden a él. Entonces, mire los detalles del stack y los mecanismos usados
para proteger los datos del stack y para implementar la comunicación entre los
hilos, basada en el estado del stack.

La clase Stack del ejemplo, denominada SyncStack para distinguirla de la clase


java.util.Stack, ofrece la siguiente API pública:

public synchronized void push(char c);


public synchronized char pop();

20.11 Un Ejemplo Completo


20.11.1 El Hilo Producer

El hilo Producer genera nuevos caracteres a ser colocados en el stack.

El Código de la Figura genera 200 caracteres en mayúscula en forma


aleatoria y los coloca en un stack a intervalos aleatorios de 0 a 300
milisegundos. Cada carácter agregado es reportado en la consola junto con un
identificador del hilo productor que está ejecutando.

1 package mod13;
2
3 public class Producer implements Runnable {
4 private SyncStack theStack;
5 private int num;
6 private static int counter = 1;
7
8 public Producer (SyncStack s) {
9 theStack = s;
10 num = counter++;
11 }
12
13 public void run() {
14 char c;
15
16 for (int i = 0; i < 200; i++) {
17 c = (char)(Math.random() * 26 +’A’);
18 theStack.push(c);
19 System.out.println(“Producer” + num + “: “ + c);
20 try {
21 Thread.sleep((int)(Math.random() * 300));
22 } catch (InterruptedException e) {
23 // ignore it
24 }
25 }
26 } // Fin del método run
27
28 } // Fin de la Clase Producer

20.11 Un Ejemplo Completo


20.11.2 El Hilo Consumer

El hilo consumidor elimina caracteres del stack. El Código de la Figura


muestra la clase Consumer.

Este ejemplo toma 200 caracteres del stack, a intervalos de 0 a 300


milisegundos entre cada intento. Cada carácter eliminado se reporta a la
consola junto con un identificador del hilo que está ejecutando.

Ahora considere la construcción de la clase Stack. Usted creará un stack que


luzca como de tamaño ilimitado, usando la clase ArrayList. Con este diseño,
sus hilos tienen que comunicarse solamente basados en si el stack está vacío.

1 package mod13;
2
3 public class Consumer implements Runnable {
4 private SyncStack theStack;
5 private int num;
6 private static int counter = 1;
7
8 public Consumer (SyncStack s) {
9 theStack = s;
10 num = counter++;
11 }
12
13 public void run() {
14 char c;
15 for (int i = 0; i < 200; i++) {
16 c = theStack.pop();
17 System.out.println(“Consumer” + num + “: “ + c);
18
19 try {
20 Thread.sleep((int)(Math.random() * 300));
21 } catch (InterruptedException e) {
23 // ignore it
24 }
25 }
26 } // Fin del método run
27
28 } // Fin de la Clase Consumer

20.11 Un Ejemplo Completo


20.11.3 La Clase SyncTest

Un nuevo buffer del objeto SyncStack construido podría estar vacío. Usted
podría utilizar el código de la Figura para construir su clase

No hay constructores. Se considera un buen estilo incluir un constructor, pero


fue omitido en este caso por brevedad.

El Método pop

Consideremos ahora los métodos push y pop. Estos deben ser sincronizados
para proteger el buffer compartido. Adicionalmente, si el stack está vacío
cuando se ejecuta el método pop, el hilo que lo ejecuta debe esperar (wait).
Cuando el stack en el método push ya no quede vacío, los hilos que están
esperando son notificados. (Ver Figura )

La llamada a wait se realiza con respecto al objeto stack que muestra cómo el
rendezvous está siendo realizado con un objeto particular. Nada puede
extraerse del stack cuando está vacío y, por lo tanto, el hilo que está tratando
de realizar la extracción del stack debe esperar hasta que el stack ya no esté
vacío.

La llamada a wait se coloca en un bloque try-catch porque una llamada a


interrupt puede terminar el período de espera del hilo. El wait debe estar
también dentro de un bucle para este ejemplo. Si por alguna razón (como, por
ejemplo, una interrupción) el hilo despierta y descubre que el stack todavía está
vacío, entonces el hilo debe volver a entrar en la condición de espera.

El método pop para el stack está sincronizado por dos razones. La primera,
porque la extracción de un carácter del stack afecta el buffer de datos
compartidos. La segunda porque la llamada a this.wait() debe estar dentro de
un bloque sincronizado con el objeto stack, el cual está representado por this.

El método push usa this.notify() para liberar un hilo desde el pool de wait del
objeto stack.Después que un hilo fue liberado, puede obtener el lock del stack y
continuar ejecutando el método pop, que elimina un carácter del buffer del
stack.

Usted debería también considerar la verificación de error. Debería notar que no


hay código explícito para prevenir una situación de underflow en el stack. Esto
no es necesario porque la única forma de eliminar caracteres del stack es a
través del método pop y este método causa que la ejecución del hilo entre en
un estado wait si no hay caracteres disponibles. Por lo tanto, la verificación de
errores no es necesaria.

El Método push

El método push es similar al método pop. Este afecta el buffer compartido y


debe también ser sincronizado. Adicionalmente, dado que el método push
agrega un carácter al buffer, es responsable de notificar a los hilos que están
esperando por la situación de stack no-vacío. Esta notificación se realiza con
respecto al objeto stack. (Ver Figura )

La llamada a this.notify sirve para liberar un único hilo que haya llamado a wait
debido a que el stack estaba vacío. La llamada a notify antes de cambiar los
datos compartidos no tiene consecuencias. El lock del objeto stack es liberado
solamente a la salida del bloque sincronizado. Por lo tanto, los hilos que
esperan por ese lock pueden obtenerlo mientras los datos del stack están
siendo cambiados por el método pop.

El Código de la Figura muestra la clase SyncStack que junta todas las


partes.
1 package mod13;
2
3 import java.util.*;
4
5 public class SyncStack {
6 private List<Character> buffer
7 = new ArrayList<Character>(400);
8
9 public synchronized char pop() {
10 char c;
11 while (buffer.size() == 0) {
12 try {
13 this.wait();
14 } catch (InterruptedException e) {
15 // ignore it...
16 }
17 }
18 c = buffer.remove(buffer.size()-1);
19 return c;
20 }
21
22 public synchronized void

23 this.notify();
24 buffer.add(c);
25 }
26 }

20.11 Un Ejemplo Completo


20.11.4 El Ejemplo SyncTest

Los códigos del productor, del consumidor y del stack deberían unirse para
completar cada clase. Se requiere una verificación conjunta para unir estas
piezas. Ponga particular atención en cómo SyncTest crea un único objeto stack
que es compartido por todos los hilos. El Código de la Figura muestra la
clase SyncTest.

El siguiente es un ejemplo de la salida al ejecutar java mod13.SyncTest. Cada


vez que el código del hilo es ejecutado, el resultado varía.

Producer2: F
Consumer1: F
Producer2: K
Consumer2: K
Producer2: T
Producer1: N
Producer1: V
Consumer2: V
Consumer1: N
Producer2: V
Producer2: U
Consumer2: U
Consumer2: V
Producer1: F
Consumer1: F
Producer2: M
Consumer2: M
Consumer2: T

1 package mod13;
2
3 public class SyncTest {
4
5 public static void main(String[] args) {
6
7 SyncStack stack = new SyncStack();
8
9 Producer p1 = new Producer(stack);
10 Thread prodT1 = new Thread (p1);
11 prodT1.start();
12
13 Producer p2 = new Producer(stack);
14 Thread prodT2 = new Thread (p2);
15 prodT2.start();
16
17 Consumer c1 = new Consumer(stack);
18 Thread consT1 = new Thread (c1);
19 consT1.start();
20
21 Consumer c2 = new Consumer(stack);
22 Thread consT2 = new Thread (c2);
23 consT2.start();
24 }
25 }

20.12 Laboratorios

Laboratorio 13 - Los Hilos

20.12 Laboratorios
20.12.1 Objetivos

Una vez completado este laboratorio, usted deberá ser capaz de crear una
aplicación multihilo simple.
20.12 Laboratorios
20.12.2 Ejercicios: Uso de Programación Multihilo (Nivel 1)

Ejercicio 1 - Uso de Programación Multihilo (Nivel 1)

20.12 Laboratorios
20.12.3 Ejercicios: Uso de Programación Multihilo (Nivel 2)

Ejercicio 1 - Uso de Programación Multihilo (Nivel 2)

20.12 Laboratorios
20.12.4 Ejercicios: Uso de Programación Multihilo (Nivel 3)

Ejercicio 1 - Uso de Programación Multihilo (Nivel 3)

20.12 Laboratorios
20.12.5 Resumen del Ejercicio

Discusión – Dedique unos minutos para discutir que experiencias, temas o


descubrimientos realizó durante el ejercicio de laboratorio.

• Experiencias
• Interpretaciones
• Conclusiones
• Aplicaciones

20.13 Preguntas de Repaso

Ahora le proponemos que resuelva el siguiente ejercicio para poner a prueba


sus conocimientos.

Defina por Verdadero o Falso las siguientes afirmaciones según corresponda.


¡Éxitos!
21 Los Flujos de Entrada y de Salida Avanzados

Este capítulo examina como el lenguaje de programación Java usa flujos para
manejar bytes, caracteres y objetos de Entrada/Salida (E/S). También describe
los flujos envueltos (a nivel de fuente (source) y de recepción de datos (sink)),
así como flujos de procesamiento.

21.1 Repaso del Capítulo Anterior

Los principales temas del capítulo anterior son:

Los Hilos

Una visión más completa de los sistemas de computadores modernos ofrece la


posibilidad de realizar más de un trabajo simultáneamente.
En este módulo, un hilo o un contexto de ejecución se consideran como la
encapsulación de una CPU virtual con su propio código y sus propios datos de
programa.
La clase java.lang.Thread permite crear y controlar hilos.
Un hilo o contexto de ejecución, está compuesto de tres partes principales:

• Una CPU virtual.


• El código que la CPU ejecuta.
• Los datos sobre los cuales el código trabaja.

Dos hilos comparten igual código cuando ejecutan código a partir de instancias
de la misma clase.

La Creación del Hilo

Un constructor Thread toma un argumento que es una instancia de la clase


Runnable. Una instancia de la clase Runnable se realiza a partir de una clase
que implementa la interfaz Runnable (esto es, provee un método public void
run()).
Un entorno de programación multihilo permite crear múltiples hilos basados en
la misma instancia Runnable.
Esto se puede hacer de la siguiente manera:

Thread t1 = new Thread(r);


Thread t2 = new Thread(r);

Un hilo se referencia a través de una instancia de un objeto Thread. El hilo


comienza su ejecución al comienzo del método run de la instancia Runnable
cargada. Los datos sobre los que el hilo trabaja son tomados de la instancia
específica de Runnable, la cual se pasa al constructor Thread.

La Planificación de Hilos

El modelo de un planificador preemtivo hace que muchos hilos pueden estar en


estado runnable (listo para ser ejecutado), pero solo uno se está ejecutando
efectivamente.
El código del hilo puede ejecutar una llamada a Thread.sleep(), solicitándole al
hilo que se detenga deliberadamente por un período fijo de tiempo.

El argumento del método sleep especifica la cantidad mínima de milisegundos


durante la cual el hilo deberá estar inactivo.

La Finalización de un Hilo

Cuando un hilo completa su ejecución y termina, no puede ejecutarse


nuevamente. Se puede detener un hilo usando un flag (una bandera) que
indique que el método run debe finalizar.

El Control Básico de Hilos

La Prueba de Hilos
Para determinar si un hilo todavía está disponible, se puede utilizar el método
isAlive. El método devolverá true para un hilo que haya comenzado y que no
haya completado su tarea.

El Acceso a la Prioridad de un Hilo

Para determinar la prioridad actual de un hilo se utiliza el método getPriority.


Para asignar una determinada prioridad a un hilo se utiliza el método setPriority.

Los Hilos Pendientes

El método sleep constituye una manera de detener un hilo por un tiempo.


El método join hace que el hilo corriente quede esperando hasta que el hilo
sobre el que se aplica termine.
Se utiliza el método Thread.yield() para darle a los hilos runnable la
oportunidad de ejecutarse. Si otros hilos están runnable, yield coloca al hilo
llamado en el pool runnable y le permite a otro hilo runnable ejecutarse.

Las Otras Formas de Crear Hilos

Se puede crear un hilo creando una clase que extienda Thread en lugar de
implementar Runnable.

El Uso de la Palabra Clave synchronized

Esta provee al lenguaje de programación Java un mecanismo que le permite al


programador controlar los hilos que comparten datos.

El Problema

Suponga que el hilo a está agregando caracteres y que el hilo b está quitando
caracteres. El hilo a agregó un carácter, pero aún no realizó el incremento del
índice. Por alguna razón, el hilo entra en estado preemtivo. En ese momento, el
modelo de datos representado en el objeto es inconsistente.
Por consiguiente, se necesita un mecanismo para asegurar que los datos
compartidos estén en un estado consistente antes de que cualquier otro hilo
comience a usarlos para una tarea particular.
Una opción puede consistir en advertir al hilo que abandone su ejecución hasta
que complete la sección crítica de código.
Otra opción - sobre la que la tecnología Java trabaja - consiste en proveer un
mecanismo que trate los datos con delicadeza. Esta opción provee un hilo
indivisible con acceso a los datos sin importar si abandona su ejecución en el
medio de la realización de dicho acceso.

La Bandera de Lock de un Objeto

En la tecnología Java, cada objeto tiene una bandera asociada con él. La
palabra clave synchronized permite la interacción de esta bandera y provee
acceso exclusivo al código que afecta los datos compartidos.
Cuando el hilo alcanza la sentencia synchronized, examina el objeto pasado
como argumento y trata de obtener la bandera de lock del objeto
Si se realiza esto, no se protegen los datos. Si el método pop del objeto con
datos compartidos no está protegido por un synchronized y pop es invocado
por otro hilo, hay riesgo de daño a la consistencia de los datos.
Cuando el hilo trata de ejecutar la sentencia synchronized(this), intenta tomar la
bandera de lock del objeto this. Dado que la bandera no está presente, el hilo
no puede continuar su ejecución. Entonces, se une al pool de hilos en estado
de espera que están asociados con esa bandera de lock del objeto. Cuado la
bandera es devuelta al objeto, alguno de los hilos que está esperando por la
bandera la toma y el hilo continúa su ejecución.

La Liberación de una Bandera de Lock

La bandera debe estar disponible para que un hilo que está esperando por una
bandera de lock de un objeto pueda retomar su ejecución. La bandera de lock
se devuelve automáticamente a su objeto.
Cuando el hilo que retiene la bandera de lock pasa el fin del bloque de código
synchronized para el que el lock fue obtenido, la bandera de lock es liberada.

El Uso de synchronized

El mecanismo de sincronización funciona solo si todos los accesos a los datos


delicados ocurren dentro de bloques sincronizados.
Si se utiliza synchronized como un modificador de método, el método completo
se transforma en un bloque sincronizado. Esto puede provocar que la bandera
de lock quede asignada por más tiempo que el necesario.

Deadlock (Abrazo Mortal)

Esto sucede cuando un hilo está esperando por un lock que tiene otro hilo, pero
este otro hilo está esperando por un lock que ya tiene el primer hilo.
La tecnología Java no puede detectar cómo impedir esta situación.
Una regla general para evitar un deadlock es la siguiente: si usted tiene
múltiples objetos que quiere que tengan acceso sincronizado, tome una
decisión global acerca del orden en que obtendrá estos locks y siga este orden
a través del programa. Libere los locks en el orden inverso al que los obtuvo.

Los Métodos wait y notify

La clase java.lang.Object provee dos métodos, wait y notify, para la


comunicación entre hilos. Si un hilo envía una llamada a un wait sobre un
objeto rendezvous x, ese hilo pausa su ejecución hasta que otro hilo envíe una
llamada notify sobre el mismo objeto rendezvous x.
Wait y notify son llamados solo desde adentro de un bloque sincronizado sobre
la instancia desde la cual son llamados

21.3 Objetivos
Al finalizar este capítulo, usted debería estar en condiciones de:

• Describir las características principales del paquete java.io.


• Construir flujos envueltos (node stream) y de procesamiento y usarlos
apropiadamente.
• Distinguir entre lectores y escritores a un flujo y seleccionar
apropiadamente entre ellos.

Discusión – Las siguientes preguntas son relevantes para el material


presentado en este capítulo:

• ¿Qué mecanismos tienen lugar en el lenguaje de programación Java


para leer y escribir a un flujo (o a un receptor de datos) además de los
archivos?
• ¿Cómo son soportados los conjuntos de caracteres internacionales en
las operaciones de entrada/salida?
• ¿Cuáles son las posibles fuentes y receptores de flujos de caracteres y
bytes?

21.4 Recursos Adicionales

Recursos adicionales – Las siguientes referencias ofrecen información


adicional sobre los temas descritos en este capítulo:

• The Java Tutorial. [Online]. Disponible en:

http://java.sun.com/docs/books/
tutorial/essential/io/streams.html

http://java.sun.com/docs/books/
tutorial/essential/io/bytestreams.html

http://java.sun.com/docs/books/
tutorial/essential/io/charstreams.html

http://java.sun.com/docs/books/
tutorial/essential/io/buffers.html

21.5 Los Fundamentos de ES


Un stream (o flujo) es un flujo de datos desde una fuente a un receptor de
datos. Típicamente, los programas constituyen un extremo de este flujo y algún
otro nodo constituye el otro extremo (por ejemplo, un archivo).

Las fuentes y los receptores también se conocen como flujos de entrada y


flujos de salida, respectivamente (Tabla de la Figura ). Se puede leer desde
un flujo de entrada, pero no se puede escribir en él. Por el contrario, se puede
escribir a un flujo de salida, pero no se puede leer de él

21.5 Los Fundamentos de ES


21.5.1 Los Datos en los Flujos

La tecnología Java soporta dos tipos de datos en los flujos: bytes o caracteres
Unicode. Normalmente, el término flujo refiere a flujos de bytes y los términos
lector (reader) y escritor (writer) refieren a flujos de caracteres.

Más específicamente, los flujos de entrada de caracteres están implementados


por subclases de la clase Reader. Los flujos de salida de caracteres están
implementados por subclases de la clase Writer. Los flujos de bytes están
implementados por subclases de la clase InputStream. Los flujos de salida de
bytes están implementados por subclases de la clase OutputStream.
21.6 Los Flujos de Byte

Las siguientes secciones describen los flujos de byte fundamentales.

21.6 Los Flujos de Byte


21.6.1 Los Métodos de InputStream

Los siguientes tres métodos ofrecen acceso a los datos desde un flujo de
entrada:

int read()
int read(byte[] buffer)
int read(byte[] buffer, int offset, int length)

El primer método devuelve un elemento int, el que contiene o bien un byte leído
desde un flujo o un valor -1. Este último indica que se produjo la condición de
fin de archivo. Los otros dos métodos leen el flujo en un arreglo de bytes y
devuelven la cantidad de bytes leídos. Los dos argumentos de tipo int en el
tercer método indican un sub-rango en el arreglo destino que necesita ser
completado.
void close()

Cuando se ha finalizado de trabajar con un flujo, este debe ser cerrado. Si tiene
un stack de flujos, utilice flujos de filtro para cerrar el flujo en el tope del stack.
Esta operación también cierra los flujos inferiores.

int available()

Este método reporta la cantidad de bytes que están inmediatamente


disponibles para ser leídos desde un flujo. Una operación de lectura a
continuación de esta llamada podría llegar a devolver más bytes.

long skip(long n)

Este método descarta del flujo la cantidad de bytes especificada.

boolean markSupported()
void mark(int readlimit)
void reset()

Estos métodos se pueden utilizar para realizar devoluciones al flujo, si esta


operación es soportada por él. El método markSupported() devuelve el valor
true si los métodos mark() y reset() están operativos para este flujo particular.
El método mark(int) indica que el puntero corriente en el flujo debe ser
notificado y un buffer de un tamaño mayor debe ser asignado, con al menos la
cantidad de bytes especificada en el argumento. El parámetro del método
mark(int) especifica la cantidad de bytes que pueden ser releídos por una
llamada al método reset(). Después de subsecuentes operaciones read(), la
llamada al método reset() devuelve el flujo de entrada apuntando al lugar
indicado. Si se realiza una
lectura pasando el buffer indicado, la invocación a reset() no tiene efecto
21.6 Los Flujos de Byte
21.6.2 Los Métodos de OutputStream

Los siguientes métodos escriben al flujo de salida.

void write(int)
void write(byte[] buffer)
void write(byte[] buffer, int offset, int length)

Al igual que en la entrada, siempre se trata de escribir, en la práctica, los datos


en bloques lo más largos posible

void close()

Los flujos de salida deben cerrarse cuando se ha finalizado el trabajo con ellos.
Nuevamente, si se tiene un stack de flujos y se cierra el que se encuentra en el
tope, se cierran los restantes flujos.

void flush()

Algunas veces, un flujo de salida acumula escrituras antes de realizar su


grabación efectiva. El método flush() permite forzar estas grabaciones.
21.7 Los Flujos de Caracteres

Los siguientes métodos describen los flujos de caracteres fundamentales

21.7 Los Flujos de


Caracteres
21.7.1 Los Métodos de Reader

Los siguientes tres métodos ofrecen acceso a los datos caracteres desde un
lector:

int read()
int read(char[] cbuf)
int read(char[] cbuf, int offset, int length)

El primer método devuelve un int, el que contiene o bien un carácter Unicode


leído desde el flujo o el valor -1. Este último indica que se ha alcanzado la
condición de fin de archivo.

Los otros dos métodos leen, almacenan el resultado en un arreglo de


caracteres y devuelven la cantidad de bytes leídos. Los dos argumentos int en
el tercer método indican un sub-rango en el arreglo destino que necesita ser
completado.

void close()
boolean ready()
long skip (long n)
boolean markSupported()
void mark(int readAheadLimit)
void reset()

Estos métodos son análogos a las versiones de los flujos de entrada


21.7 Los Flujos de Caracteres
21.7.2 Los Métodos de Writer

Los siguientes métodos escriben a un escritor (writer):

void write(int c)
void write(char[] cbuf)
void write(char[] cbuf, int offset, int length)
void write(String string)
void write(String string, int offset, int length)

Al igual que en los flujos de salida, los escritores incluyen los métodos close y
flush.

21.8 Los Flujos Envueltos

En el Java 2 SDK, hay tres tipos fundamentales de envoltura (Tabla de la


Figura ):
a. archivos,
b. memoria (tales como los arreglos y los objetos String)
c. pipes (un canal entre un proceso o hilo a otro; el flujo de salida a un pipe
desde un hilo se asocia al flujo del pipe de entrada de otro hilo).

Es posible crear nuevas clases de flujos envueltos, pero esto requiere manejar
llamadas a funciones nativas a un driver del dispositivo. Esto hace que el
código no sea portable.

21.9 Un Ejemplo Simple

El Código de la Figura lee caracteres desde un archivo cuyo nombre es


pasado como parámetro desde la línea de comandos y escribe los caracteres a
un archivo cuyo nombre es pasado como un segundo argumento, también
desde la línea de comando. Luego, copia el archivo. Esta es la forma en que el
programa debe ser invocado:

java TestNodeStream file1 file2

Aunque parezca fácil, manejar el buffer es tedioso y propenso a errores. Es por


este motivo que hay clases que manejan el proceso de buffering y permiten
leer un flujo de una línea por vez. Esta clase es BufferedReader y es un tipo de
flujo denominado flujo de procesamiento.
1 import java.io.*;
2
3 public class TestNodeStreams {
4 public static void main(String[] args) {
5 try {
6 FileReader input = new FileReader(args[0]);
7 FileWriter output = new FileWriter(args[1]);
8 char[] buffer = new char[128];
9 int charsRead;
10
11 // read the first buffer
12 charsRead = input.read(buffer);
13
14 while ( charsRead != -1 ) {
15 // write the buffer out to the output file
16 output.write(buffer, 0, charsRead);
17
18 // read the next buffer
19 charsRead = input.read(buffer);
20 }
21
22 input.close();
23 output.close();
24 } catch (IOException e) {
25 e.printStackTrace();
26 }
27 }
28 }

21.10 Los Flujos que Utilizan Buffer

Este programa realiza la misma función que el programa del Código de la


Figura del apartado 14.7 (Ver Figura )

Este programa tiene el mismo flujo que el programa anterior. Sin embargo, en
lugar de leer de un buffer, lee una línea por vez, utilizando la variable line para
almacenar el String devuelto por el método readLine (Líneas 13 y 20), que
brinda mayor eficiencia. La Línea 7 asocia el objeto de lectura del archivo con
un flujo de lectura con buffer. Usted debe manejar el flujo más externo en la
cadena (bufInput), el cual manipula el flujo más interno (input).

1 import java.io.*;
2
3 public class TestBufferedStreams {
4 public static void main(String[] args) {
5 try {
6 FileReader input
= new FileReader(args[0]);
7 BufferedReader bufInput
= new BufferedReader(input);
8 FileWriter output
= new FileWriter(args[1]);
9 BufferedWriter bufOutput
= new BufferedWriter(output);
10 String line;
11
12 // read the first line
13 line = bufInput.readLine();
14
15 while ( line != null ) {
16 // write the line out to the output file
17 bufOutput.write(line, 0, line.length());
18 bufOutput.newLine();
19 // read the next line
20 line = bufInput.readLine();
21 }
22 bufInput.close();
23 bufOutput.close();
24 } catch (IOException e) {
25 e.printStackTrace();
26 }
27 }

21.11 Las Cadenas de Flujos de EntradaSalida

Un programa raramente utiliza un único objeto de flujo. Por el contrario,


encadena una serie de flujos para procesar los datos. La Figura muestra un
ejemplo de un flujo de entrada. En este caso, un flujo asociado a un archivo es
procesado a través de un buffer por eficiencia y luego convertido a los datos
correspondientes (primitivas Java).

La Figura muestra un ejemplo de flujos de salida. En este caso, los datos se


escriben, luego se procesan a través del buffer y finalmente se escriben a un
archivo.
21.12 Los Flujos de Procesamiento

Un flujo de procesamiento realiza conversiones a otro flujo. Los flujos de


procesamiento también se conocen como flujos de filtro. Un flujo de entrada de
filtro se crea con una conexión a un flujo de entrada existente. Esto se hace de
forma tal que cuando se trata de leer desde un flujo de entrada con filtro, se
proporcionan caracteres que originalmente provienen de otro flujo de entrada.
Esto permite convertir una secuencia de datos en una forma más usable para
la aplicación que se está escribiendo. La Tabla de la Figura lista los flujos de
procesamiento predefinidos que están incluidos en el paquete java.io.

Resulta bastante fácil crear nuevos filtros de procesamiento. Esto se describe


en la siguiente sección.
21.13 Las Clases Básicas de Flujo de Byte

La Figura y la Figura ilustran la jerarquía de las clases de flujos de bytes


de entrada y salida en el paquete java.io. Las más comunes están descriptas
en las siguientes secciones.
21.13 Las Clases Básicas de Flujo de
Byte
21.13.1 Las Clases FileInputStream y
FileOutputStream

Las clases FileInputStream y FileOutputStream son flujos envueltos y, como el


nombre lo sugiere, utilizan archivos grabados en disco. Los constructores para
estas clases permiten especificar el camino del archivo al cual deben
conectarse. Para construir un FileInputStream, el archivo asociado debe existir
y además debe poder leerse. Si se construye un FileOutputStream y el archivo
de salida ya existe, se sobrescribe.

FileInputStream infile= new FileInputStream("myfile.dat");


FileOutputStream outfile= new
FileOutputStream("result.dat");

En el caso de querer anexar los datos a partir del final del archivo sin
sobreescribir el mismo debe utilizarse la siguiente forma, dónde el tipo boolean
debe ser true:

FileOutputStream(String n, boolean a)
21.13 Las Clases Básicas de Flujo de
Byte
21.13.2 Las Clases
BufferedInputStream y
BufferedOutputStream

Utilice las clases BufferedInputStream y BufferedOutputStream


correspondientes a flujos de filtro para incrementar la eficiencia de las
operaciones de entrada/salida.

21.13 Las Clases Básicas de Flujo de


Byte
21.13.3 Las Clases PipedInputStream y
PipedOutputStream

Estas clases se utilizan para realizar comunicaciones entre hilos. Un objeto


PipedInputStream en un hilo recibe su entrada desde un objeto
PipedOutputStream complementario, en otro hilo. Los flujos que se comunican
de esta forma deben tener un extremo de entrada y un extremo de salida para
resultar útiles.

21.13 Las Clases Básicas de Flujo de


Byte
21.13.4 Las Clases DataInputStream y
DataOutputStream

Los flujos de filtro de clase DataInputStream y DataOutputStream permiten leer


y escribir tipos de datos primitivos de Java con algunos formatos especiales
utilizando flujos. Para ello se proveen varios métodos para los distintos tipos
primitivos:

Los Métodos DataInputStream

Los métodos DataInputStream son como se describe a continuación:

Byte readByte()
Long readLong()
Double readDouble()

Los Métodos DataOutputStream


Los métodos DataOutputStream son como se describe a continuación:

Void writeByte(byte)
Void writeLong(long)
Void writeDouble(double)

Cada método de DataInputStream tiene su par en la clase DataOutputStream.

Estos flujos tienen métodos para leer y para escribir cadenas de caracteres,
pero se recomienda no utilizarlos. No están aprobados y se sustituyen por
lectores y escritores que se describirán más adelante.

21.14 Las Clases Básicas de Flujo de Caracteres

La Figura y la Figura ilustran la jerarquía de las clases de flujo de


caracteres Reader y Writer en el paquete java.io. Algunas de las más comunes
se describen en la siguientes secciones
21.14 Las Clases Básicas de Flujo de
Caracteres
21.14.1 Los Métodos
InputStreamReader y
OutputStreamWriter

Las versiones más comunes de lectores y escritores son InputStreamReader y


OutputStreamWriter. Estas clases se utilizan para comunicar flujos de bytes y
lectores y escritores de caracteres.

Cuando se construye un InputStreamReader o un OutputStreamWriter, se


definen las reglas de conversión para cambiar entre la representación Unicode
de 16-bits y otra representación específica de la plataforma.

21.14 Las Clases Básicas de Flujo de


Caracteres
21.14.2 Las Conversiones de
Bytes y de Caracteres

Por defecto, si se construye un lector o un escritor conectado a un flujo, las


reglas de conversión cambian entre los bytes utilizados por la codificación de
caracteres de la plataforma por defecto y Unicode. En los países de habla
inglesa, se utiliza la codificación de bytes Internacional Organization for
STandarization (ISO) 8859-1.

Si es necesario, se puede especificar una codificación de bytes alternativa


utilizando una de las formas de codificación soportada. Si se tiene la
documentación instalada, se podrá encontrar la lista de formas de codificación
soportadas. Esta se encuentra en el directorio docs/ guid/intl/
encoding.doc.html bajo la instalación de Java.

Utilizando este esquema de conversión, la tecnología Java utiliza la más


completa flexibilidad del conjunto de caracteres de la plataforma local y
continúa manteniendo la independencia de la plataforma a través del uso
interno de Unicode.

21.14 Las Clases Básicas de Flujo de


Caracteres
21.14.3 El Uso de otra
Codificación de Caracteres

Si se necesita leer desde la entrada un caracter con una codificación que no se


corresponde con la local (por ejemplo, lectura desde una conexión de la red
con un tipo de máquina diferente), se puede construir el InputStreamReader
con una codificación de caracteres explícita, tal como:

InputStreamReader ir=new InputStreamReader(System.in, "ISO-8859-1");


21.14 Las Clases Básicas de Flujo de
Caracteres
21.14.4 Las Clases FileReader y
FileWriter

Las clases FileReader y FileWriter son flujos de envoltura análogos a los


caracteres Unicode de las clases FileInputStream y FileOutputStream.
21.14 Las Clases Básicas de Flujo de
Caracteres
21.14.5 Las Clases
BufferedReader y
BufferedWriter

Los flujos de caracteres con filtro de las clases BufferedReader y


BufferedWriter se utilizan para incrementar la eficiencia de las operaciones de
Entrada/Salida.

21.14 Las Clases Básicas de Flujo de


Caracteres
21.14.6 Las Clases StringReader
y StringWriter

Las clases StringReader y StringWriter son flujos de caracteres de envoltura


que leen caracteres desde objetos String o los graban a objetos String de la
tecnología Java.

Suponga que se grabó un conjunto de clases de reporte que contiene métodos


que aceptan un parámetro de tipo Writer (el destino del texto de un reporte).
Dado que el método realiza llamadas contra una interfaz genérica, el programa
puede pasar un objeto FileWriter o un objeto StringWriter, sin problemas. Luego,
se crea el objeto para escribir el reporte a un archivo. Se podría utilizar este
objeto para escribir el reporte a la memoria en un String que debe ser
desplegado en un área de texto GUI. En este caso, el código de escritura del
reporte permanece igual

21.14 Las Clases Básicas de Flujo de


Caracteres
21.14.7 Las Clases PipedReader y
PipedWriter

Los flujos pipe se utilizan para comunicar hilos. Un objeto PipedReader en un


hilo recibe su entrada desde un objeto PipedWriter complementario en otro hilo.
Los flujos comunicados desde esta manera deben tener ambos un extremo de
lectura y un extremo de salida para ser útiles.
21.15 Preguntas de Repaso

Ahora le proponemos que resuelva el siguiente ejercicio para poner a prueba


sus conocimientos.

Defina por Verdadero o Falso las siguientes afirmaciones según corresponda.


¡Éxitos!
22 Networking

Este capítulo discute el soporte Java 2 SDK para sockets y programación con
sockets. La programación con sockets permite la comunicación con otros
programas que se ejecutan sobre otros computadores en una misma red.

22.1 Repaso del Capítulo Anterior

Los principales temas del capítulo anterior son:

Los Fundamentos de E/S

Un stream (o flujo) es un flujo de datos desde una fuente a un receptor de


datos. Las fuentes y los receptores también se conocen como flujos de entrada
y flujos de salida, respectivamente.

Los Datos en los Flujos

La tecnología Java soporta dos tipos de datos en los flujos: bytes o caracteres
Unicode. Normalmente, el término flujo refiere a flujos de bytes y los términos
lector (reader) y escritor (writer) refieren a flujos de caracteres.

Los Flujos de Byte

Los Métodos de InputStream

Los siguientes tres métodos ofrecen acceso a los datos desde un flujo de
entrada:

int read()
int read(byte[] buffer)
int read(byte[] buffer, int offset, int length)

Cuando se ha finalizado de trabajar con un flujo, este debe ser cerrado.

void close()

El método int available()reporta la cantidad de bytes que están inmediatamente


disponibles para ser leídos desde un flujo.

Los Métodos de OutputStream

Los siguientes métodos escriben al flujo de salida:


void write(int)
void write(byte[] buffer)
void write(byte[] buffer, int offset, int length)

Al igual que en la entrada, siempre se trata de escribir, en la práctica, los datos


en bloques lo más largos posibles.

void close()

Algunas veces, un flujo de salida acumula escrituras antes de realizar su


grabación efectiva. El método flush() permite forzar estas grabaciones.

Los Flujos de Caracteres

Los Métodos de Reader

Los siguientes tres métodos ofrecen acceso a los datos caracteres desde un
lector:

int read()
int read(char[] cbuf)
int read(char[] cbuf, int offset, int length)

Los siguientes metodos tambien son validos y con uso similar a las versiones
de flujo de entrada:

void close()
boolean ready()
long skip (long n)
boolean markSupported()
void mark(int readAheadLimit)
void reset()

Los Métodos de Writer

Los siguientes métodos escriben a un escritor (writer):

void write(int c)
void write(char[] cbuf)
void write(char[] cbuf, int offset, int length)
void write(String string)
void write(String string, int offset, int length)

Las Cadenas de Flujos de Entrada/Salida

Un flujo asociado a un archivo es procesado a través de un buffer por eficiencia


y luego convertido a los datos correspondientes (primitivas Java).

Los Flujos de Procesamiento


Un flujo de procesamiento realiza conversiones a otro flujo. Los flujos de
procesamiento también se conocen como flujos de filtro.

Las Clases Básicas de Flujo de Byte

Las Clases FileInputStream y FileOutputStream

Las clases FileInputStream y FileOutputStream son flujos envueltos y, como el


nombre lo sugiere, utilizan archivos grabados en disco. Para construir un
FileInputStream, el archivo asociado debe existir y además debe poder leerse.

Las Clases BufferedInputStream y BufferedOutputStream

Utilice las clases BufferedInputStream y BufferedOutputStream


correspondientes a flujos de filtro para incrementar la eficiencia de las
operaciones de entrada/salida.

Las Clases PipedInputStream y PipedOutputStream

Estas clases se utilizan para realizar comunicaciones entre hilos. Un objeto


PipedInputStream en un hilo recibe su entrada desde un objeto
PipedOutputStream complementario, en otro hilo.

Las Clases DataInputStream y DataOutputStream

Los flujos de filtro de clase DataInputStream y DataOutputStream permiten leer


y escribir tipos de datos primitivos de Java con algunos formatos especiales
utilizando flujos.

Las Clases Básicas de Flujo de Caracteres

Los Métodos InputStreamReader y OutputStreamWriter

Estas clases se utilizan para comunicar flujos de bytes y lectores y escritores


de caracteres.

Las Conversiones de Bytes y de Caracteres

Por defecto, si se construye un lector o un escritor conectado a un flujo, las


reglas de conversión cambian entre los bytes utilizados por la codificación de
caracteres de la plataforma por defecto y Unicode.

Las Clases FileReader y FileWriter

Las clases FileReader y FileWriter son flujos de envoltura análogos a los


caracteres Unicode de las clases FileInputStream y FileOutputStream.

Las Clases BufferedReader y BufferedWriter


Los flujos de caracteres con filtro de las clases BufferedReader y
BufferedWriter se utilizan para incrementar la eficiencia de las operaciones de
Entrada/Salida.

Las Clases StringReader y StringWriter

Las clases StringReader y StringWriter son flujos de caracteres de envoltura


que leen caracteres desde objetos String o los graban a objetos String de la
tecnología Java.

Las Clases PipedReader y PipedWriter

Los flujos pipe se utilizan para comunicar hilos. Un objeto PipedReader en un


hilo recibe su entrada desde un objeto PipedWriter complementario en otro hilo.

22.3 Objetivos

Al finalizar este capítulo, usted estará en condiciones de:

• Desarrollar un código para configurar la conexión a una red.


• Comprender el protocolo TCP/IP.
• Utilizar las clases ServerSocket y Socket para la implementación de
clientes y servidores TCP/IP

Discusión – La siguiente pregunta es relevante al material presentado en este


capítulo:

• ¿Cómo se puede establecer un vínculo de comunicación entre una


máquina cliente y un servidor?

22.4 Recursos Adicionales

Recursos adicionales – Las siguientes referencias ofrecen información


adicional sobre los temas descritos en este capítulo:

• The Java Tutorial. [Online]. Disponible en:

http://java.sun.com/docs/books/
tutorial/networking/overview/
networking.html
http://java.sun.com/docs/books/
tutorial/networking/sockets/
definition.html

http://java.sun.com/docs/books/
tutorial/networking/sockets/
readingWriting.html

http://java.sun.com/docs/books/
tutorial/networking/sockets/
clientServer.html

22.5 Networking

La siguiente sección describe el concepto de networking utilizando sockets.

22.5 Networking
22.5.1 Sockets

Socket es el nombre dado, en un modelo de programación particular, a los


puntos finales de una comunicación entre procesos. Dada la particularidad de
este modelo de programación especial, el término socket se utiliza también en
otros modelos de programación, incluyendo la tecnología Java.

Cuando los procesos se comunican en una red, la tecnología Java utiliza el


modelo de flujos.

Un socket puede tener asociados dos flujos: un flujo de entrada y un flujo de


salida. Un proceso envía datos a otros procesos a través de la red escribiendo
al flujo de salida asociado con el socket. Un proceso lee los datos escritos por
otro proceso, leyendo desde el flujo de entrada asociado con el socket.

Después que la conexión de red fue establecida, el uso de los flujos asociados
con la conexión, es similar al uso de cualquier otro flujo.
22.5 Networking
22.5.2 La Configuración de una Conexión

Para configurar la conexión, una máquina debe ejecutar un programa que esté
esperando por una conexión y una segunda máquina debe intentar alcanzar a
la primera. Esto es similar a un sistema telefónico, en el cual una parte debe
hacer la llamada, mientras que otra está esperando que la llamada se realice.

En este capítulo se presenta una descripción de las conexiones de red TCP/IP.


Un ejemplo de una conexión de red se muestra en la Figura .
22.6 Networking con Tecnología Java

Esta sección describe el concepto de networking usado por la tecnología Java.

22.6 Networking con Tecnología Java


22.6.1 Direccionando la Conexión

Cuando usted realiza una llamada telefónica, necesita conocer el número de


teléfono. Cuando se realiza una conexión de red, se necesita conocer la
dirección o el nombre de la máquina remota. Además, una conexión de red
requiere un número de puerto, que puede pensarse como un número de
extensión del teléfono. Después de conectarse a la computadora correcta, se
debe identificar un propósito particular para la conexión. De la misma forma
que se puede usar un número de extensión telefónica particular para hablar al
departamento de contabilidad, se puede utilizar un número de puerto para
comunicarse con el programa de contabilidad.
22.6 Networking con Tecnología
Java
22.6.2 Los Números de Puerto

Los números de puerto en los sistemas TCP/IP son números de 16-bit y los
valores están comprendidos entre 0 y 65535. En la práctica, los números
inferiores a 1024 están reservados para servicios predefinidos y se debería
evitar utilizarlos a menos que la comunicación se realizara con algunos de esos
servicios (como, por ejemplo, telnet, Simple Mail Transport Protocol [SMTP],
mail, ftp, etc.). Los números de puerto del cliente son asignados por el sistema
operativo, mientras que los números de puerto del servidor son especificados
por el programador y se usan para identificar un servicio particular.

Tanto el cliente como el servidor deben acordar cuál será el puerto que
utilizarán. Si los números de puerto usados por las dos partes del sistema no
acuerdan este valor, la comunicación no se establece.
22.6 Networking con Tecnología
Java
22.6.3 El Modelo Networking de Java

En el lenguaje de programación Java, las conexiones de socket TCP/IP son


implementadas con clases en el paquete java.net. La Figura ilustra lo que
ocurre en el servidor y en el cliente.

En la Figura :

• El servidor asigna un número de puerto. Cuando el cliente solicita una


conexión, el servidor abre la conexión del socket con el método accept().
• El cliente establece una conexión con host sobre el puerto port#.
• Tanto el cliente como el servidor se comunican usando un objeto
InputStream y un objeto OutputStream
22.7 Un Servidor TCP/IP Mínimo

Las aplicaciones servidor TCP/IP yacen sobre las clases de networking


ServerSocket y Socket provistas por el lenguaje de programación Java. La
mayor parte del trabajo de la clase ServerSocket consiste en establecer una
conexión servido

1 import java.net.*;
2 import java.io.*;
3
4 public class SimpleServer {
5 public static void main(String args[]) {
6 ServerSocket s = null;
7
8 // Register your service on port 5432
9 try {
10 s = new ServerSocket(5432);
11 } catch (IOException e) {
12 e.printStackTrace();
13 }
14
15 // Run the listen/accept loop forever
16 while (true) {
17 try {
18 // Wait here and listen for a connection
19 Socket s1 = s.accept();
20
21 // Get output stream associated with the socket
22 OutputStream s1out = s1.getOutputStream();
23 BufferedWriter bw = new BufferedWriter(
24 new OutputStreamWriter(s1out));
25
26 // Send your string!
27 bw.write(“Hello Net World!\n”);
28
29 // Close the connection, but not the server
socket
30 bw.close();
31 s1.close();
32 } catch (IOException e) {
33 e.printStackTrace();
34 }
35 }
36 }
37 }

22.8 Un Cliente TCP/IP Mínimo

La aplicación TCP/IP que reside en el lado del cliente recae sobre la clase
Socket. Nuevamente, la mayor parte del trabajo involucrado en establecer
conexiones es realizado por la clase Socket. El cliente se conecta al servidor
presentado en "Un Servidor TCP/IP Mínimo" del apartado 15.5 y despliega todo
lo enviado por el servidor, a la consola.

1 import java.net.*;
2 import java.io.*;
3
4 public class SimpleClient {
5 public static void main(String args[]) {
6 try {
7 // Open your connection to a server, at port
5432
8 // localhost used here
9 Socket s1 = new Socket(“127.0.0.1”, 5432);
10
11 // Get an input stream from the socket
12 InputStream is = s1.getInputStream();
13 // Decorate it with a "data" input stream
14 DataInputStream dis = new DataInputStream(is);
15
16 // Read the input and print it to the screen
17 System.out.println(dis.readUTF());
18
19 // When done, just close the steam and connection
20 br.close();
21 s1.close();
22 } catch (ConnectException connExc) {
23 System.err.println(“Could not connect.”);
24 } catch (IOException e) {
25 // ignore
26 }
27 }
28 }

22.9 Laboratorios

Laboratorio 15 – Networking

22.9 Laboratorios
22.9.1 Objetivos

Una vez completado este laboratorio, usted deberá ser capaz de construir una
GUI cliente para conectarse a un servidor remoto usando Transmission
ControlProtocol/Internet Protocol (TCP/IP).

22.9 Laboratorios
22.9.2 Ejercicio: Creación de un Socket Client (Nivel 1)

Ejercicio 1 - Creación de un Socket Client (Nivel 1)

22.9 Laboratorios
22.9.3 Ejercicio: Creación de un Socket Client (Nivel 2)

Ejercicio 1 - Creación de un Socket Client (Nivel 2)

22.9 Laboratorios
22.9.4 Ejercicio: Creación de un Socket Client (Nivel 3)
Ejercicio 1 - Creación de un Socket Client (Nivel 3)

22.9 Laboratorios
22.9.5 Resumen del Ejercicio

Discusión – Tómese unos pocos minutos para discutir que experiencias,


tópicos o descubrimientos tuvo durante el ejercicio de laboratorio.

• Experiencias
• Interpretaciones
• Conclusiones
• Aplicaciones

22.10 Preguntas de Repaso

Ahora le proponemos que resuelva el siguiente ejercicio para poner a prueba


sus conocimientos.

Defina por Verdadero o Falso las siguientes afirmaciones según corresponda.


¡Éxitos!

Você também pode gostar