Sunteți pe pagina 1din 80

Introducción a la OOP y al Lenguaje de

Programación Java

I.S.C. Edgardo Vera Ordoñez

Enero de 2020
ÍNDICE
Módulo 1.- Fundamentos de OOP .................................................................................. 5
Introducción ................................................................................................................. 6
Abstracción ................................................................................................................... 6
Tipos de abstracción .................................................................................................... 6
Objetos .......................................................................................................................... 7
Relevancia de un objeto ............................................................................................... 8
Independencia de un objeto ........................................................................................ 8
Atributos y operaciones ............................................................................................... 8
Encapsulamiento .......................................................................................................... 9
Partes de un objeto encapsulado................................................................................. 9
Implementación del encapsulamiento ...................................................................... 10
Mensajes ...................................................................................................................... 10
Clases ........................................................................................................................... 12
Generalización ............................................................................................................ 12
Herencia ...................................................................................................................... 13
Polimorfismo............................................................................................................... 13
Clase abstracta ........................................................................................................... 14
Módulo 2.- Fundamentos de Java ................................................................................. 15
Antecedentes ............................................................................................................... 16
Los productos de la tecnología Java ......................................................................... 16
Componentes del J2SDK Estandar Edition............................................................. 17
Ciclo de elaboración de programas en Java ............................................................ 17
Palabras reservadas ................................................................................................... 18
Espacios en blanco ..................................................................................................... 18
Identificadores ............................................................................................................ 19
Separadores ................................................................................................................ 20
Comentarios ................................................................................................................ 20
Bloques de código ....................................................................................................... 21
Tipos de datos primitivos de Java ............................................................................ 21
Valores literales .......................................................................................................... 22
Identificación de los componentes de una clase....................................................... 23
Declaración de una clase............................................................................................ 24
Declaración e inicialización de los atributos de una clase ...................................... 24
Declaración de los métodos de una clase .................................................................. 25
Declaración e inicialización de las variables de un método .................................... 26
Métodos de inicialización dinámicos ........................................................................ 27
Constantes ................................................................................................................... 27
El método main( ) ....................................................................................................... 28
Mi primer programa en Java .................................................................................... 28
Declaración, instanciamiento e inicialización de objetos ........................................ 28
Operadores matemáticos ........................................................................................... 31
Operadores relacionales ............................................................................................ 32
Operadores condicionales.......................................................................................... 33
Prioridad de operadores ............................................................................................ 34
Tomas de decisión ...................................................................................................... 34
Enunciado switch ....................................................................................................... 36
Ciclo for ....................................................................................................................... 38
Ciclo while ................................................................................................................... 39
Ciclo do/while ............................................................................................................. 40
Sentencias de salto ...................................................................................................... 41
Ciclos infinitos ............................................................................................................ 42
Promoción y casting de datos .................................................................................... 43
Cuidados en el manejo de datos enteros y reales .................................................... 45
Almacenamiento de variables, referencias y objetos en memoria ......................... 47
Uso de la clase String ................................................................................................. 48
Conceptos avanzados sobre invocación de métodos ............................................... 49
Declaración e invocación de métodos con argumentos y valores de retorno ........ 51
Creación de métodos y atributos estáticos ............................................................... 52
Métodos con sobrecarga ............................................................................................ 53
Encapsulamiento de atributos y métodos ................................................................ 54
Métodos Get y Set ...................................................................................................... 55
Alcance de las variables ............................................................................................. 57
Métodos constructores ............................................................................................... 58
El constructor por default ......................................................................................... 59
Sobrecarga de constructores ..................................................................................... 59
Arreglos unidimensionales ........................................................................................ 60
El atributo length ....................................................................................................... 62
Almacenamiento de arreglos unidimensionales en memoria ................................. 63
Empleo de ciclos con arreglos ................................................................................... 64
Empleo del arreglo de argumentos en el método main........................................... 64
Arreglos bidimensionales .......................................................................................... 66
Paso de argumentos de tipo objeto ........................................................................... 67
Retorno de valores de tipo objeto ............................................................................. 69
Implementación de herencia ..................................................................................... 69
Sobrescritura de métodos y atributos ...................................................................... 71
Métodos abstractos .................................................................................................... 73
Los constructores y la herencia................................................................................. 73
Acceso a miembros de una superclase...................................................................... 77
Importación de paquetes de clases ........................................................................... 77
Conceptos avanzados de encapsulamiento .............................................................. 79
Protección de acceso para clases ............................................................................... 80
Módulo 1.- Fundamentos de OOP
Lenguajes de programación Fundamentos de OOP

Introducción
El análisis y diseño Orientado a Objetos, es un paradigma desarrollado con la intención de
resolver un sin número de problemas, inclusive la programación de sistemas computacionales. Es
un concepto relativamente nuevo que emplea lo mejor de la programación estructurada clásica y
le añade una funcionalidad mayor, dado que divide la complejidad de un sistema entero en
pequeños módulos manejables y reutilizables gracias a un método conocido como abstracción.
Muchos lenguajes modernos como el C++ y Java toman ventaja del OOP.

El OOP modela a los sistemas (sean basados en software o no) tratándolos como una serie
de objetos que interactúan entre sí, de forma muy similar a como la gente común ve su medio
ambiente.

Supongamos el siguiente ejemplo: Un programador de camino a su trabajo entra a una


cafetería, ordena un café, se sienta, se lo toma y continúa su camino. La cafetería, el café, la
mesa, la silla, el programador, etc., son ejemplos de posibles objetos. Más aún, cada uno de ellos
poseen características que los identifican: La cafetería puede estar abierta o cerrada, el café puede
ser pequeño, mediano o grande y tener un precio específico, las mesas pueden estar disponibles o
no disponibles, etc.

Abstracción
Abstraer significa “ignorar los detalles y centrarse en lo esencial”. La abstracción es la
forma elemental de enfrentarse con algo complejo y promueve el reúso dado que las
características específicas que hacen a cada objeto menos “genérico”, son ignoradas. La
Abstracción simplifica la manera en que los usuarios interactúan con los objetos.

La gente ve el mundo en diferentes niveles de abstracción: Un conductor ve a un automóvil


como un medio de transporte con ruedas, asientos y tanque de combustible, mientras que un
ingeniero ve además al motor, a la transmisión y a cada uno de sus componentes funcionales. Por
otra parte, todos sabemos que un teléfono sirve para realizar llamadas y podemos usarlo sin
necesidad de saber cómo funciona internamente.

Así, la abstracción se concentra en las partes más importantes de los objetos, como lo son
su significado y sus funciones (qué es y para qué sirve) pero ignorando los detalles innecesarios
(cómo es que lo hace).

Por ejemplo: Si observamos a un gato doméstico y a un gato montés, notaremos que ambos
tienen garras, pelo, bigotes y cuatro patas. Sin embargo, estos detalles no muestran las diferencias
entre ellos, ya que los gatos domésticos son mascotas y los monteses son animales salvajes.

Tipos de abstracción
Existen dos tipos de abstracción: La abstracción funcional, y la abstracción de datos.

La abstracción funcional se caracteriza por ocultar los detalles acerca del procesamiento de
los datos; en otras palabras, la forma en que un proceso se realiza permanece oculta. Por ejemplo,
cuando usted visita un restaurante y ordena un platillo, no necesita decirle al mesero cómo
preparar cada ingrediente, ya que el chef conoce perfectamente la receta. Así, las principales
características de la abstracción funcional son:
6
Lenguajes de programación Fundamentos de OOP

• Ignorar los detalles de cómo se realiza un proceso.


• Hace uso de abstracciones de lenguaje de alto nivel.
• Emplea abstracciones creadas por los usuarios (procedimientos y subrutinas).

La abstracción de datos es similar a la funcional, excepto que esta trabaja con los datos y
sus tipos. Permite que los detalles de cómo un dato es representado internamente sean ignorados.
Por ejemplo, si usted desea representar una fecha, podría hacerlo con tres cantidades: Una para el
día, otra para el mes y otra para el año; o podría usar una cadena para representar el nombre del
mes y números para el día y el año. En cualquier caso, a usted no le interesa la manera en que
esos datos son almacenados en memoria, o de hecho, cómo son empleados en operaciones
matemáticas. Por esto último se dice que la abstracción de datos involucra también a la
abstracción funcional. En resumen, la abstracción de datos:

• Se encarga de los tipos de datos.


• Permite que los detalles de la representación interna de un tipo de dato sean
ignorados.
• Involucra a la abstracción funcional.

Objetos
Un objeto es una abstracción de la realidad. Dicho de manera más formal, un objeto es una
entidad discreta que contiene tanto estructuras de datos como un comportamiento definido. Más
coloquialmente, un objeto es una pequeña caja negra con funcionalidad propia, que cuenta con
una total independencia del código que lo utiliza o hace referencia a él.

Los objetos pueden ser simples o complejos, reales o imaginarios. Una antena parabólica es
un objeto complejo y real; un empleado (representación de los detalles y actividades de una
persona) es un objeto imaginario. A los objetos reales se les conoce también como concretos, y a
los imaginarios como conceptuales.

Un párrafo, un número complejo y una cuenta de banco son también representaciones de


cosas intangibles y además son objetos. Los objetos por tanto no tienen que ser una entidad física;
pueden ser tan solo un concepto.

Es recomendable analizar los diversos escenarios de un problema con el fin de identificar


nombres. Los nombres son un buen punto de inicio para determinar qué objetos existen en un
sistema; sin embargo, tome en cuenta que algunos nombres son en realidad variaciones de otros y
pueden estar expresando simplemente acciones o ser atributos de otros objetos. Como puede
apreciarse, el principal reto en el OOP es encontrar los objetos necesarios para el sistema en
cuestión.

En resumen, los objetos tienen las siguientes características:

• Son cosas.
• Son simples o complejos.
• Son reales o imaginarios.

7
Lenguajes de programación Fundamentos de OOP

Relevancia de un objeto
Una vez que se han seleccionado todos los posibles objetos de un dominio, se les debe
evaluar para asegurarse de que son válidos. Generalmente se emplea el siguiente criterio:

• Deberán ser relevantes al dominio del problema en cuestión.


• Deberán de poder existir de manera independiente.
• Deberán tener atributos y operaciones asociadas.

Identificar los objetos de un sistema no es una ciencia, es un arte. Mientras que los objetos
a incluir deben cumplir con los criterios antes establecidos, dependen aún del contexto y del
punto de vista de la persona que realiza el modelo y de los usuarios del sistema a modelar. Como
ayuda extra para determinar si el objeto es relevante al sistema, pregúntese lo siguiente:

• ¿El objeto existe dentro de los límites del problema?


• ¿Se requiere de la presencia de ese objeto para que el sistema cumpla su cometido?
• ¿Se requiere de la existencia del objeto como parte de la interacción entre los
usuarios y el sistema?

Independencia de un objeto
Como se mencionó antes, para que un objeto sea un objeto y no un atributo de otro objeto,
deberá existir de manera independiente en el contexto del problema abordado. Los objetos
pueden conectarse entre sí y aún poseer una existencia independiente.

Cuando se evalúa a objetos potenciales, pregúntese si el objeto necesita existir de manera


independiente, en vez de ser simplemente un atributo de otro objeto; esto dependiendo siempre
del contexto o de la perspectiva. Suponga una computadora que tiene un procesador Pentium III;
desde el punto de vista de un vendedor, la velocidad y modelo del procesador es simplemente una
característica más de la computadora, pero si se tratase del punto de vista de un ingeniero
modelando componentes de cómputo, el procesador por sí mismo sería un objeto independiente
del equipo en donde sea instalado.

Atributos y operaciones
Los objetos poseen atributos y operaciones. Los atributos (también llamados propiedades)
son sus características y las operaciones (también llamados métodos) son lo que el objeto puede
hacer. Suponga que una nube es un objeto; sus atributos serían tamaño, forma, color y sus
operaciones serían llover, nevar y relampaguear.

Cuando se definen las operaciones de un objeto, en realidad se le asignan las operaciones


que se realizan con el objeto mismo. Por ejemplo, una cuenta de banco puede abrirse o cerrarse,
conciliarse o actualizarse. Todas ellas serán las operaciones del objeto.

Un objeto debe poseer atributos y operaciones; si no pueden definirse atributos y


operaciones para un objeto, entonces probablemente no se trata de un objeto válido sino de un
atributo u operación de otro objeto.

8
Lenguajes de programación Fundamentos de OOP

Todo objeto posee una identidad de manera inherente, por lo tanto, aunque dos objetos
posean los mismos atributos, se considerarán objetos diferentes.

Otro ejemplo sería el siguiente:

Objeto Gato
Atributos Operaciones
Color de ojos Ronronear
Color de pelo Cazar ratones
Numero de patas Desgarrar el sillón
Raza Comerse las plantas

Aunque no es una analogía perfecta, piense en los atributos en términos de adjetivos y en


las operaciones en términos de verbos.

Encapsulamiento
Encapsular significa separar los aspectos externos de un objeto que son accesibles al
mundo exterior, de los detalles de implementación interna del mismo, los cuales deberán estar
ocultos. El encapsulamiento tiene una enorme ventaja: Siempre y cuando la interfase permanezca
igual, un objeto puede en un futuro cambiar sin afectar al resto del sistema, dado que los demás
objetos desconocen (y para el caso no les importa) la forma en que funciona internamente el
objeto que están usando.

La idea es la siguiente: Los objetos deben poder usarse aún cuando no se sabe a ciencia
cierta qué contienen; lo único que deberá preocuparnos es qué necesita el objeto para funcionar
y qué es lo que debemos esperar del mismo.

Suponga que un teléfono es un objeto en su sistema. ¿Se ha puesto a pensar alguna vez
cómo es que un teléfono comunica a las personas en ambos extremos? Lo más seguro es que no,
dado que lo anterior no es necesario para llamar por teléfono. A usted lo único que le debe
preocupar es cómo emplear la interfase del teléfono, que está compuesta por los botones, el
auricular y el micrófono.

Más aún, si su teléfono ayer era analógico y hoy es digital, usted aún puede usarlo de la
misma forma para hacer llamadas, siempre y cuando siga contando con botones, auricular y
micrófono.

Partes de un objeto encapsulado


Un objeto encapsulado debe poseer tres elementos o aspectos:

• Interfaz pública: La parte externa del objeto que se usa para interactuar con el
mismo.
• Implementación: Las operaciones internas del objeto; esto es, qué se puede hacer
con él y cuál es su propósito.
• Información interna: Lo que el objeto debe saber para realizar su implementación.

9
Lenguajes de programación Fundamentos de OOP

Implementación del encapsulamiento


Las operaciones y los atributos de un objeto son llamados en conjunto miembros. Dichos
miembros pueden ser públicos o privados. Si un miembro es público, ya sea un atributo o una
operación, entonces será miembro de la interfaz pública. Si por el contrario es privado, entonces
forma parte de su implementación.

En los sistemas orientados a objetos puros, todos los atributos son privados y pueden ser
cambiados o accedidos únicamente mediante operaciones a través de la interfaz pública. La
obtención y establecimiento de los atributos para la implementación son operaciones
comúnmente realizadas con dicha interfaz.

Por ejemplo: Para cambiar un radio a una estación en particular, el usuario presionará o
moverá el dial (operación en la interfaz pública) que causará que el sintonizador interno del radio
(implementación) se sincronice con la estación deseada (atributo privado). Así, el usuario puede
cambiar la estación del radio sin saber cómo es que el radio ubica la señal.

Mensajes
Se entiende por mensaje a la forma en que un objeto accede o se comunica con otro.
Consiste básicamente de dos partes: El nombre de la operación y sus argumentos. Así, un objeto
A (emisor) le envía un mensaje al objeto B (receptor) cuando necesita que realice alguna acción o
le retorne alguna información.

Cuando un objeto recibe un mensaje, lleva a cabo la operación que le fue requerida
ejecutando uno de sus métodos; en la práctica, no son más que algoritmos específicos cuyo
nombre es igual al del método invocado.

Los mensajes son parte de la interfaz pública y los métodos son parte de la implementación.
Al conjunto de mensajes a los que un objeto puede o sabe responder, se les denomina entonces
comportamiento o interfaz del objeto.

10
Lenguajes de programación Fundamentos de OOP

Caso de Estudio
Instrucciones: Lea el siguiente caso de estudio y posteriormente resuelva los ejercicios anexos:

La compañía Avon vende ropa por catálogo. El negocio está creciendo a un promedio de
30% anual y requieren de un nuevo sistema de entrada de pedidos. Usted ha sido contratado por
Avon para diseñar el nuevo sistema.

Los catálogos de ropa se producen mensualmente y son enviados por correo a los
suscriptores. Dichos catálogos cuentan con una sección para artículos en liquidación, ofertas del
mes y para los artículos a precio regular. Cada artículo del catálogo puede etiquetarse con un
precio diferente mes con mes, así que cuando un cliente hace un pedido, debe conocerse el
catálogo del cual está ordenando para identificar el precio correctamente. Si el catálogo del
cliente tiene más de seis meses de antigüedad, entonces el precio deberá actualizarse según el
catálogo más reciente.

Para realizar un pedido, los compradores pueden llamar a un representante de ventas, enviar
la orden por correo o faxearla. Una vez recibidas las órdenes son ingresadas al sistema por un
capturista.

Avon desea expandirse y le gustaría darle al comprador la opción de realizar sus pedidos en
línea vía Internet. Los artículos disponibles en línea estarían entonces disponibles y tendrían
asociado el precio del catálogo actualizado. Después de que la orden se introduce en el sistema,
deberá verificarse en inventario y de ser posible asignado inmediatamente. Si el artículo no está
en inventario, pero ya ha sido solicitado al proveedor, entonces la orden se pondrá en estado de
espera hasta que sea surtido.

Una vez que la orden esté completa, se verificará el pago y se enviará al departamento de
empaques en donde se le preparará para su envío. Avon acepta como forma de pago cheques y
tarjeta de crédito.

• Actividad 1: Identifique los nombres del caso de estudio anterior en busca de


posibles objetos.
• Actividad 2: Asigne posibles atributos y operaciones a los objetos pedido y cliente.
• Actividad 3: Realice la prueba de relevancia a los objetos identificados en la
actividad 1 y descarte aquellos que no la superen.
• Actividad 4: Realice la prueba de independencia a los objetos restantes y genere la
lista final de objetos del sistema.

11
Lenguajes de programación Fundamentos de OOP

Clases
Se define como clase a un grupo de objetos que comparten los mismos atributos y
operaciones. El principio a seguir es el siguiente: Cuando múltiples objetos cuentan con la misma
estructura de datos (Atributos) y comportamiento (Operaciones) se pueden aglutinar para formar
una clase. Así, se dice que todo objeto es una instancia de su clase. Las clases no son objetos,
sólo los describen; funcionan como una plantilla.

Una clase podrá contener un conjunto infinito de objetos individuales, conteniendo un valor
propio para cada uno de sus atributos, pero compartiendo el nombre de dichos atributos y las
mismas operaciones con el resto de su clase. Todo objeto contiene una referencia implícita a su
propia clase; en otras palabras, sabe la clase de objeto que es.

Generalización
Cuando se habla de objetos, es conveniente clasificarlos en grupos con atributos y
operaciones en común. Por ejemplo, la clase Medio de Transporte podría contener a los objetos
Tren, Automóvil, Bicicleta o Caballo. El hecho de que todos estos medios de transporte operen
de manera diferente puede ser irrelevante para el análisis que se está haciendo; por ejemplo, si
Medio de Transporte implica únicamente la acción de trasladarse de un lugar a otro, entonces la
implementación específica para hacerlo no importa y las características de la clase Medio de
Transporte podrán re usarse en todas las clases que la comprenden.

Por generalización se entiende al proceso de identificar y definir los atributos y operaciones


comunes de una colección de objetos durante el desarrollo de un sistema. Este proceso ayuda
enormemente a concentrarnos en las clases principales del sistema, a la vez que nos ayuda a
identificar nuevas clases.

Mediante la identificación de factores comunes, se reduce la redundancia en el desarrollo y


por tanto el código a generar será más reducido, ya que se promueve el rehúso del mismo.

Por ejemplo, cuando se evaluó a los objetos Representante de Ventas y Capturista, en el


caso de estudio, se determinó que eran prácticamente idénticos y se les unió en la clase
Empleado. Si merecen una subclasificación posterior estará por supuesto sujeto a análisis. Aún
más, si la clase Cliente y la clase Empleado comparten atributos, es posible crear una clase
Persona que las comprenda a las dos. Gráficamente podría representarse de la siguiente manera:

Persona
Medio de
Transporte
Cliente Empleado
Tren Avión Automóvil

RDV Capturista

12
Lenguajes de programación Fundamentos de OOP

Herencia
Un término asociado directamente a la generalización es la herencia. Se entiende por
herencia el que clases diferentes con una relación jerárquica compartan atributos y operaciones.
Para ello, se deberá definir una clase que posteriormente se irá refinando produciendo subclases.
Así, las subclases generadas poseerán o heredarán todas y cada una de las propiedades de su clase
superior o superclase, añadiendo además sus propiedades exclusivas.

La capacidad de heredar propiedades de una superclase común reduce significativamente el


trabajo en el diseño, así como en la programación. Podría decirse que la herencia es la capacidad
más potente de la OOP.

La herencia se representa generalmente en forma de un árbol (similar a la figura anterior).


Si nos movemos hacia abajo en el árbol, se dice que vamos especializándonos en la clasificación;
si nos movemos hacia arriba se dice que generalizamos.

Polimorfismo
Se entiende como polimorfismo al hecho de que una misma operación pueda comportarse
de manera diferente en distintas clases. Por ejemplo, suponga que tenemos a la clase Pelota y a
las subclases Pelota de Soccer y Pelota de Tenis y que ambas clases tienen la operación Lanzar.
La forma en que se lanza una pelota de soccer es con ambas manos y hacia el frente en el saque
de banda, mientras que la de tenis se lanza hacia arriba con una sola mano en el servicio.

Entiéndase operación como una acción o transformación que se aplica a un objeto. Un


método sería entonces la implementación específica de una operación para una clase específica.
Así, una operación polimórfica puede requerir de la implementación de más de un método, según
la clase de objeto que afecte.

Para que exista polimorfismo deberá existir herencia y obtenerse el mismo resultado
semántico; de lo contrario se trata únicamente de una operación con el mismo nombre. En
resumen:

• El polimorfismo se basa en la herencia.

• Puede aplicarse a cualquier operación que es heredada de una superclase.

• Las subclases que redefinen una operación heredada para reflejar de manera más
exacta las acciones propias están proveyendo otra forma a la operación, de ahí el
término polimorfismo.

Caso de Estudio
Usando a la Internet como fuente principal de información, clasifique a los objetos Perro y
Lobo partiendo de la superclase Seres Vivos. Incluya la mayor cantidad de clases intermedias
posibles, indicando las características distintivas de dichas clases.

13
Lenguajes de programación Fundamentos de OOP

Clase abstracta
Cuando las operaciones o métodos de una clase son tan generales que es casi imposible
definir una implementación, se dice que dicha operación es abstracta. Cualquier clase con al
menos una operación de este tipo es llamada clase abstracta. Por este motivo, las clases
abstractas no se pueden instanciar.

Suponga que se requiere implementar clases para los objetos Círculo, Rectángulo y
Triángulo; dichas clases deberán poseer las operaciones Fijar Color y Estilo de Borde. Dado que
cada figura realiza estas acciones de forma completamente diferente, se decide crear una
superclase abstracta Figura Geométrica que contenga dos operaciones abstractas llamadas Fijar
Color y Estilo de Borde pero que no provean ninguna implementación. Cuando las clases
Círculo, Rectángulo o Triángulo hereden de la clase Figura Geométrica las operaciones
anteriores, deberán redefinirlas dado que estas no poseen código de implementación alguno.

14
Módulo 2.- Fundamentos de Java
Lenguajes de programación Fundamentos de Java

Antecedentes
El lenguaje de programación Java (llamado inicialmente Oak) se originó en 1991 como
parte de un proyecto de investigación para desarrollar un lenguaje capaz de romper el vacío de
comunicación existente entre muchos productos electrónicos de consumo general, como los
hornos de microondas, las televisiones y las videograbadoras. Dado que el concepto original
falló, el equipo de programadores que diseñó este lenguaje (conocido formalmente como el
Green Team) se vio obligado a encontrarle otro uso.

Afortunadamente el world wide web comenzó a ser más popular y el lenguaje Oak probó
ser ideal para desarrollar pequeños componentes que permitieran enriquecer con multimedia el
contenido de las páginas de Internet. Estas pequeñas aplicaciones conocidas hoy en día como
applets, se convirtieron en la punta de lanza de lo que en breve sería el lenguaje de programación
más usado del mundo.

En 1995 Sun Microsystems lanza al mercado la tecnología de programación Java,


promoviéndola como la primera plataforma de software universal bajo el eslogan “Write Once,
Run Anywhere”. Entre otras cosas, el lenguaje Java fue diseñado para ser:

• Orientado a objetos: Java sigue las reglas del paradigma de programación


orientado a objetos (OOP), ya que trabaja con pequeñas piezas de código autónomo
que interactúan entre sí para resolver un problema en común.
• Distribuido: Provee soporte para las tecnologías de cómputo distribuido de
vanguardia, como RMI (Remote Method Invocation), CORBA (Common Object
Request Broker Arquitecture) y URL (Universal Resource Locator).
Adicionalmente, cuenta con la capacidad de permitir que los applets sean
descargados de un servidor a través de la Internet y ejecutados en un equipo remoto.
• Multihilos: Soporta la tecnología multihilos o multiprocesamiento, la cual permite
que diversas tareas sean ejecutadas de manera simultánea.
• Seguro: Emplea medidas de seguridad que son capaces de controlar el acceso a
disco por parte de los programas y prohibir manipulaciones de memoria con
apuntadores. Además, permite la firma digital de aplicaciones.
• Multiplataforma: Gracias al empleo de una plataforma de software conocida como
JVM (Java Virtual Machine), el código generado por los compiladores de Java
puede ejecutarse en cualquier plataforma de hardware.

Los productos de la tecnología Java


Sun Microsystems provee una línea completa de productos para crear programas con
tecnología Java:

• J2SE (Java 2 Platform, Standard Edition): Usada para desarrollar applets,


aplicaciones orientadas a Web y programas de usuario final.
• J2EE (Java 2 Platform, Enterprise Edition): Usada en empresas grandes para
desarrollar aplicaciones de cómputo distribuido como el ecommerce.

16
Lenguajes de programación Fundamentos de Java

• J2ME (Java 2 Platform, Micro Edition): Usada en la creación de aplicaciones para


dispositivos con recursos limitados; actualmente es empleada en Palm Computers y
teléfonos celulares.

Componentes del J2SDK Estandar Edition


Sun microsystems provee una versión gratuita de su kit de desarrollo estándar de Java para
plataformas Solaris, Linux y Windows conocida como J2SDK SE que incluye principalmente:

• El ambiente en tiempo de ejecución Java (Java Runtime Environment), constituido


de la Máquina Virtual de Java (Java Virtual Machine) para la plataforma elegida y
la Librería de Clases de Java (Java Class Library) o API (Application Program
Interface).
• El compilador de Java.

Ciclo de elaboración de programas en Java


El ciclo de creación de un programa en Java comprende los siguientes pasos:

• Generación el código fuente: Los códigos fuente Java pueden capturarse usando el
editor de texto de su preferencia (como el Bloc de Notas de Windows). Su extensión
deberá ser .java y su nombre tendrá que coincidir exactamente con el de la clase
que esté conteniendo.
• Compilación del código fuente: Para compilar un código Java basta con
posicionarse en la ruta en donde se encuentra el compilador e invocarlo mediante la
sentencia: javac nombredearchivo.java. El resultado será un archivo
binario altamente optimizado interpretable por la JVM (conocido popularmente
como bytecode) con extensión .class.
• Ejecución del bytecode: Para ejecutar un programa de Java se necesita invocar a la
JVM mediante la sentencia: java nombredearchivoclase. Dado que el
resultado de la compilación de un programa en Java no es un código ejecutable
directamente por la plataforma de nuestro equipo, se dice que Java es un lenguaje
interpretable.

En Java, un archivo fuente es conocido formalmente como unidad de compilación y en


cada uno de ellos se incluirá una o más definiciones de clase. Dado que la extensión de estos
archivos contiene cuatro caracteres, es imposible (al menos por el momento) crear y compilar
programas de lenguaje Java en plataformas DOS o Windows 3.x.

El compilador asigna el mismo nombre del código fuente al bytecode resultante y lo


almacena en la misma ruta; de manera más precisa, se crea un bytecode por cada clase definida
en el código y se le asigna a cada uno el mismo nombre de la definición de clase desde la cual se
originó. Por el momento, incluiremos una sola definición de clase por cada archivo fuente.

Cuando el código fuente se encuentran en un directorio diferente al directorio en el que se


encuentra el compilador, deberá incluirse la ruta completa (absoluta o relativa) al especificar
nombredearchivo. La JVM (ejecutada por el comando java) se integra automáticamente a la

17
Lenguajes de programación Fundamentos de Java

ruta de búsqueda por defecto del sistema, por lo que puede invocarse desde cualquier unidad o
ruta local.

Palabras reservadas
Como todo lenguaje de programación, Java posee un conjunto de palabras reservadas (48
en total) que no pueden ser usadas para nombrar ningún identificador:

abstract double int strictfp


boolean else interface super
break extends long switch
byte final native synchronized
case finally new this
catch float package throw
char for private throws
class goto protected transient
const if public try
continue implements return void
default import short volatile
do instanceof static while

Cabe hacer notar que, aunque las palabras const y goto están reservadas, no se utilizan.
Adicionalmente, Java emplea los nombres true, false y null para definir los valores
constantes falso, verdadero y nulo respectivamente, por lo que tampoco pueden emplearse para
definir identificadores propios.

Espacios en blanco
En Java, un espacio entre dos palabras, un tabulador o un carácter de nueva línea son
considerados un espacio en blanco. Los espacios en blanco son obligatorios únicamente para
delimitar elementos del código que no están separados de antemano por algún operador o
separador válido.

Cualquier forma de indentación que usemos tendrá entonces como único objetivo hacer
nuestro código más legible, mas no tendrá efecto en el código mismo (a no ser que se encuentre
dentro de una cadena literal, en cuyo caso se considerará parte de la misma).

Así, técnicamente hablando (aunque no es lo más recomendable) podríamos tener todo un


código escrito en una sola línea o dividido en centenares de ellas. Por ejemplo:

if (i > 5)
System.out.println(“Y es mayor que 5”);

Es exactamente lo mismo que:

if(i>5)System.out.println(“Y es mayor que 5”);

18
Lenguajes de programación Fundamentos de Java

Y que:
if(
i>
5)
System.
out.println
(“Y es mayor que 5”
)
;

Es inválido sin embargo dividir identificadores, palabras reservadas o cadenas literales con
caracteres de nueva línea; lo siguiente por ejemplo causaría un error de compilación:

System.out.println(“Y es mayor
que 5”);

Para separar una cadena literal en dos líneas, coloque una diagonal invertida ( \ ) antes del
carácter de nueva línea de la siguiente forma:

System.out.println(“Y es mayor\
que 5”);

Tome en cuenta sin embargo que la indentación que precede al segundo renglón de la
cadena se considerará parte de la cadena misma.

Identificadores
Un identificador es un nombre creado por el programador para dar nombre a variables,
métodos, clases y objetos. Las reglas generales a seguir son las siguientes:

• Todos los identificadores deberán empezar con una letra, un carácter de subrayado
( _ ) o un símbolo de moneda ( $ ). Después del primer carácter se permite el uso de
dígitos numéricos; no se permite emplear símbolos de puntuación, caracteres
especiales o espacios en blanco. Las mayúsculas y minúsculas son distinguibles y
no hay límite de longitud.

• Se acostumbra construir nombres de identificadores concatenando palabras


completas que describan su contenido lo más claramente posible sin emplear
abreviaturas o acrónimos, a menos que estos últimos sean más comunes. Los
nombres en inglés son siempre preferidos.

• Aunque es más etiqueta que obligatorio, se acostumbra poner en mayúscula la


primera letra de cada palabra al nombrar clases. Para todos los identificadores
restantes, se acostumbra poner en mayúscula la primera letra de la segunda palabra
en adelante.

Ejemplos:
MyFirstClass ItemOnSale loopCounter webServerURL itemPrice

19
Lenguajes de programación Fundamentos de Java

Separadores
La tabla siguiente muestra los separadores del lenguaje Java:

Símbolo Nombre Propósito

( ) Paréntesis • Contener una lista de parámetros al definir o invocar métodos.


• Definir precedencia en las operaciones matemáticas.
• Contener expresiones en las sentencias de control de flujo y
conversiones de tipo.

{ } Llaves • Definir bloques de código.


• Contener valores de inicialización de arreglos.

[ ] Corchetes • Declarar datos de tipo arreglo.


• Referenciar elementos de arreglo.

; Punto y coma • Separar sentencias.

, Coma • Separar identificadores consecutivos al declarar variables.


• Separar expresiones en ciclos for.

. Punto • Separar referencias de variables o métodos


• Separar nombres de paquetes.

Comentarios
Se emplean comentarios para determinar fácilmente qué se hace en cada parte de nuestro
programa. Existen dos formas de comentarios:

• De una sola línea: El marcador // le indica al compilador que ignore lo que sigue
hasta el final de la línea actual.
• De varias líneas: Estos son los tradicionales comentarios usados en programación;
el marcador /* le dice al compilador que ignore cualquier cosa hasta que se
encuentre con un marcador de terminación */.

Ejemplo de comentarios de una línea:


public class Shirt { // Aquí comienza nuestra definición de clase
} // y aquí termina!!!

Ejemplo de comentario de múltiples líneas:

/**************************************
*Esta clase no hace ABSOLUTAMENTE NADA*
**************************************/

20
Lenguajes de programación Fundamentos de Java

NOTA: Existe un tercer tipo de comentario conocido como comentario de documentación


que comienza con un /** y termina con un */. Se les llama así porque ayudan a generar
archivos de documentación de nuestro código cuando se emplea el programa javadoc. De
hecho, este es el mecanismo utilizado por Sun Microsystems para construir la
documentación oficial del API de Java.

Bloques de código
Un bloque de código es la agrupación de una o más sentencias encerradas entre un par de
llaves ({}). Una vez creados, los bloques se comportan como una unidad lógica indivisible, tal
cual y como si se tratara de una sola sentencia. Como se verá en el transcurso de este manual, los
bloques de código tienen múltiples propiedades y usos.

Tipos de datos primitivos de Java


Existen ocho tipos de datos primitivos en el lenguaje Java, divididos en cuatro grandes
grupos:

• Enteros, los cuales almacenan datos numéricos que no contienen punto decimal:
byte, short, int y long.
• De punto flotante o reales, los cuales almacenan datos numéricos que contienen
punto decimal: float y double.
• De carácter, los cuales almacenan el valor Unicode (http://www.unicode.org) del
carácter que se guarda en ellos: char. La siguiente liga contiene una práctica
calculadora para realizar conversiones:
http://code.cside.com/3rdpage/us/javaUnicode/converter.html.
• Lógicos, los cuales almacenan uno de dos valores lógicos (true o false):
boolean

En la siguiente tabla se muestra el tamaño en bits y el rango de valores aceptado por cada
uno de estos tipos de datos:

Tipo Tamaño Rango

byte 8 bits -128 a 127 (256 valores)


short 16 bits -32,768 a 32,767 (65,535 valores)
int 32 bits -2,147’483,648 a 2,147’483,647 (4,294,967,296 valores)
long 64 bits -263 a 263-1
float 32 bits -3.438 a -1.4-45 y 3.438 a 1.4-45 (IEEE 754)
double 64 bits -1.8308 a –4.9-324 y 1.8308 a 4.9-324 (IEEE 754)
char 16 bits 65,534 caracteres
boolean 1 bit true o false

21
Lenguajes de programación Fundamentos de Java

NOTAS:

• Para almacenar más de un carácter se usan las variables de tipo String. Dado que
las cadenas no son tipos primitivos sino objetos, se describirán en un apartado más
adelante.
• Se recomienda (cuando la longitud del valor lo permita) utilizar el tipo double
para números reales y el tipo int para los enteros.

Valores literales
Un valor literal (como su nombre lo indica) es aquel que se escribe literalmente en nuestro
código; por ejemplo:

100 98.6 ‘X’ “Esto es una cadena”

En este caso tenemos (de izquierda a derecha) un valor literal entero, uno de punto flotante,
uno de carácter y una cadena. Comúnmente, los valores literales se emplean cuando deseamos
asignar valores a nuestros identificadores; por ello, vale la pena tomar en cuenta lo siguiente:

Todos los valores literales enteros se consideran de tipo int a menos que se les anexe
como posfijo la letra L, en cuyo caso se considerarán de tipo long. Pueden expresarse también
en formato octal (precediéndolos de un cero) o hexadecimal (precediéndolos de un cero y una
equis minúscula o mayúscula).

Ejemplos:

Literal entera de tipo int: 147334778


Literal entera de tipo long: 20368L
Literal entera de tipo int en formato octal: 073
Literal entera de tipo int en formato hexadecimal: 0xFF
Literal entera de tipo long en formato hexadecimal: 0x2AL

Dado que no hay forma de especificar que una literal entera es de tipo byte o short, lo
único que deberá preocuparnos es que el valor literal no rebase los límites permitidos por el tipo
de dato en cuestión.

Por otra parte, todos los valores literales de punto flotante se consideran de tipo double a
menos que se les anexe como posfijo la letra F. Pueden expresarse también en notación científica
agregándoles como posfijo la letra ‘e’ minúscula o mayúscula y un entero positivo o negativo que
indique la potencia de 10 del exponente.

Ejemplos:

Literal de punto flotante de tipo double: 2.0


Literal de punto flotante de tipo float: 3.14159F
Literal de punto flotante de tipo double en notación científica: 645.36E10
Literal de punto flotante de tipo float en notación científica: 2E-5F

22
Lenguajes de programación Fundamentos de Java

Los valores literales de tipo char deben escribirse encerrados entre comillas simples
(‘‘). Cuando se trata de caracteres no imprimibles directamente desde el teclado, podremos
recurrir a una secuencia de escape con el formato que indica la tabla siguiente:

Secuencia de escape Descripción


\ddd Carácter en formato octal
\uxxxx Carácter UNICODE en formato hexadecimal
\’ Comilla simple
\” Comilla doble
\\ Diagonal invertida
\r Retorno de carro
\n Nueva línea
\f Avance de página
\t Tabulador
\b Retroceso

Ejemplos:

Letra ‘G’: ‘G’


Letra ‘G’ en formato hexadecimal UNICODE: ‘\u0047’
Letra ‘G’ en formato octal: ‘\107’
Letra ‘G’ en formato decimal UNICODE: 71

NOTA: Tome en cuenta que existe una equivalencia directa de valores entre la tabla ASCII
y la tabla UNICODE. De hecho, aunque legalmente los valores de tipo char no son
números, pueden tratarse como tales: Es posible inicializar un identificador de tipo carácter
asignándole el valor numérico correspondiente de la tabla ASCII o UNICODE; aún más,
con las debidas consideraciones pueden incluirse como operandos en expresiones
matemáticas unarias o binarias.

Por último están las literales de cadena, las cuales se especifican encerradas entre un par de
comillas dobles (“”); es posible incluir en una cadena tanto caracteres imprimibles como
secuencias de escape. Por ejemplo:

“Hello world” “Esta cadena\nocupa dos lineas”

Identificación de los componentes de una clase


Obedeciendo el OOP, un programa en Java deberá estar compuesto por clases y
preferentemente cada clase deberá estar contenida en un archivo fuente independiente. Toda
aplicación de escritorio estará constituida por al menos una clase llamada clase controladora,
clase principal o clase de prueba que sirve de punto de entrada al programa. En toda clase
pueden distinguirse las siguientes partes, secciones o apartados:

• La declaración de la clase (obligatoria)


23
Lenguajes de programación Fundamentos de Java

• La declaración e inicialización de los atributos (opcional)


• Sus métodos (opcional)
• Los comentarios (opcionales)

NOTA: Aunque el compilador no objete al respecto, una clase deberá contener al menos un
método o un atributo, ya que de lo contrario no tendrá razón de existir.

Declaración de una clase


Como se indicó con anterioridad, se deberá crear un archivo de texto en donde
declararemos una clase por cada objeto identificado en nuestro problema; la sintaxis para declarar
una clase es la siguiente:

[modificadores] class identificador{


}

En donde:

• [modificadores] determinan la accesibilidad de otras clases a la clase que se


está declarando, así como el comportamiento de su herencia. Todos son opcionales
y por el momento se empleará public.
• La palabra reservada class es obligatoria y le dice al compilador que el bloque de
código siguiente es una declaración de clase.
• identificador será el nombre de la clase según las reglas vistas con
anterioridad. Como se mencionó ya, el nombre de la clase debe coincidir
exactamente con el nombre del archivo .java que la contiene.
• Las llaves de apertura y cierre ( { } ) delimitan el cuerpo de nuestra definición de
clase. Todas las variables y métodos de la clase deberán ir encerrados en este
bloque.

Ejemplos:
public class Shirt public class Order public class SalesManager

Declaración e inicialización de los atributos de una clase


Las variables de clase, conocidas formalmente como atributos, se colocan después de la
llave de apertura de la clase. Como cualquier tipo de variable, los atributos se utilizan para
almacenar y recuperar datos en nuestros programas.

Los atributos deben colocarse de manera obligatoria afuera de cualquier método. También
se les conoce como variables miembro o variables de instancia dado que cuando un objeto es
instanciado, estas variables se encargarán de contener los datos específicos de cada objeto creado
(en otras palabras, habrá una copia de ellas por objeto). Aunque inicialmente todas las variables
miembro de un objeto contendrán el mismo valor, esto podrá cambiar después.

La declaración e inicialización de los atributos de una clase tiene la siguiente sintaxis:

24
Lenguajes de programación Fundamentos de Java

[modificadores] tipo identificador [= valor];

En donde:

• [modificadores] representan diversas palabras reservadas que definen la


forma en que los atributos de la clase son accedidos. Todos son opcionales y por
ahora se empleará el modificador public.
• tipo representa el tipo de dato primitivo que la variable contendrá. Así, algunas
variables contendrán números enteros, reales, caracteres o valores booleanos.
• identificador será el nombre que se le asignará a la variable según las reglas
vistas con anterioridad.
• valor será el valor que se desea asignar a la variable y es opcional.

Ejemplos de declaración de atributos:

public int shirtID = 0;


public char colorCode = ‘G’;
public float orderTotal;

Es posible de hecho, declarar simultáneamente (e incluso inicializar) varios atributos del


mismo tipo; para ello se emplea el separador coma con la siguiente sintaxis:
[modificadores] tipo identificador[=valor][,identificador2[=valor]…];

Ejemplos:

public String firstName, lastName, address;


public int count=0, total=1;

Declaración de los métodos de una clase


Los métodos siguen a los atributos de la clase; su sintaxis es la siguiente:

[modificadores] tipoderetorno identificador([argumentos]){


bloque_de_instrucciones
}

En donde:

• [modificadores] representan diversas palabras reservadas que definen la


forma en que los métodos son accedidos. Todos son opcionales y por el momento se
empleará public.
• tipoderetorno indica el tipo de valor que el método regresa (en caso de
haberlo). Los valores retornados por un método pueden ser empleados por el
método que los invoca y podrá ser a lo mucho uno. Si el método no retorna nada, la
palabra clave void deberá usarse como tipo de retorno.

25
Lenguajes de programación Fundamentos de Java

• identificador será el nombre del método según las reglas vistas con
anterioridad.
• [argumentos] representa la lista de variables cuyos valores son pasados al
método para que éste trabaje y son opcionales. Los paréntesis de apertura “( “ y
cierre “ ) “ son obligatorios aún cuando el método no requiera ningún argumento.
• bloque_de_instrucciones será la lista de sentencias que el método ejecuta.
Una gran variedad de tareas puede realizarse en el bloque de código o cuerpo de un
método. Así como en otros lenguajes de programación, podrán crearse tantos
bloques de instrucciones internos como se desee o convenga.
• Las llaves de apertura y cierre ( { } ) delimitan el cuerpo de nuestro método.

Ejemplo de un método que contiene una sentencia:

public void printMessage(){


System.out.println(“Hola a todos!”);
}// Fin del método printMessage

Declaración e inicialización de las variables de un método


Al igual que una clase posee variables, los métodos tienen las suyas. Las variables locales
(como se les conoce formalmente) tienen visibilidad únicamente en el método en el cual se
declaran.

Las variables locales pueden declararse con toda libertad en cualquier parte del bloque
principal de un método y existirán en memoria hasta que el método al que pertenecen perdure.
Como se verá más adelante, es posible incluso construir bloques anidados dentro del bloque
principal de un método con la finalidad de reducir el tiempo de vida de una variable. Sin
embargo, a pesar de lo anterior, no podrá haber dos variables locales con el mismo nombre en un
método dado.

La declaración e inicialización de las variables puede hacerse en una línea según la


siguiente sintaxis:

tipo identificador = valor;

O en dos líneas, según la siguiente sintáxis:

tipo identificador;
identificador = valor;

En donde:

• tipo representa el tipo de dato primitivo que la variable contendrá.


• identificador será el nombre que se le asignará a la variable según las reglas
vistas con anterioridad.
• valor será el valor que se desea asignar a la variable.

26
Lenguajes de programación Fundamentos de Java

Ejemplos:

double itemPrice;
int customerAge = 30;
float orderTotal = 99F

Al igual que con los atributos, es posible declarar e inicializar más de una variable local en
la misma línea empleando al separador coma; por ejemplo:

float orderTotal, salesTax = 0.15F;

Métodos de inicialización dinámicos


Independientemente de asignar valores literales de inicio a una variable o atributo, también
podemos:

• Asignar el valor de una variable o atributo a otra variable o atributo usando


identificadores:
int saleID = ID;
• Asignar el resultado de una expresión a una variable o atributo (inclusive a las
booleanas):
float price = casePrice * numberOrdered;
boolean isOpen = (hour > 8);
• Asignar el valor de retorno de un método a una variable (este método se verá con
mas detalle en un apartado más adelante):
int ID = calculateID();

NOTAS:

• Como puede observarse en todos los casos, es el signo de igual ( = ) el que se


emplea en Java para representar la asignación.
• Cuando un atributo no es inicializado toma los siguientes valores por default:
Enteros: 0 Reales: 0.0 Carácter: \u0000 Boolean: false
• Las variables locales NO SON INICIALIZADAS por lo que deberá asignárseles un
valor antes de usarlas por primera vez.
Constantes
En Java, las constantes serán en realidad variables locales o atributos cuyo valor no se
puede cambiar. Para convertir una variable local o atributo en constante se le debe anteponer la
palabra clave final como se muestra a continuación:

final float SALES_TAX = 5.5F;

Además, por convención, se recomienda nombrar a todas las constantes empleando


únicamente letras mayúsculas.
27
Lenguajes de programación Fundamentos de Java

El método main( )
Como se mencionó anteriormente, una aplicación de escritorio estará constituida por al
menos una clase llamada clase controladora, clase principal o clase de prueba que sirve de
punto de entrada al programa. La característica principal de esta clase es que debe contener al
menos un método y ese método deberá tener la siguiente sintáxis:

public static void main(String args[]){


bloque;
}

NOTAS: Sólo podrá haber un método main() por cada programa de consola. El nombre
de la cadena de argumentos args puede cambiarse si así de desea.

Mi primer programa en Java


Con los elementos hasta este momento aprendidos, más la ayuda del método println
podemos crear nuestro primer programa en Java de la siguiente manera:

public class Hello{


public static void main(String args[]){
System.out.println("Hola a todos!");
}
}

Declaración, instanciamiento e inicialización de objetos


Normalmente todo programa en Java deberá emplear cuando menos un objeto. Para usar
objetos debemos:

1. Definir la clase del objeto en un archivo fuente en donde se detallen sus atributos y
métodos.
2. Definir la clase principal que usará el o los objetos creados.
3. Declarar, instanciar e inicializar los objetos en la clase principal.
4. Invocar sus métodos de las instancias creadas.

Tomemos por ejemplo el siguiente código Java en donde se define a una clase de objeto
llamada Message:

public class Message{


public void printMessage(){
System.out.println("Hola a todos!");
}
}

Como podemos notar, esta clase no tiene atributo alguno y únicamente cuenta con un
método llamado printMessage(), el cual no retorna o recibe ningún valor, únicamente
imprime un mensaje.

28
Lenguajes de programación Fundamentos de Java

Dado que esta clase no contiene un método main(), será imposible ejecutarla, únicamente
podremos compilarla. Para usarla, deberemos crear una clase principal o de prueba que
llamaremos MessageTest en donde crearemos una instancia del objeto de clase Message e
invocaremos su único método:

public class MessageTest{


public static void main(String args[]){
Message myMessage = new Message();
myMessage.printMessage();
}
}

En el código anterior notamos una sintaxis nueva en la tercera línea; ésta es la declaración,
instanciamiento e inicialización de un objeto de clase Message. La sintaxis genérica es la
siguiente:

nombredeclase identificador = new nombredeclase();

En donde:

• nombredeclase representa el nombre de la clase de objeto a instanciar.


• identificador será el nombre que se le asignará a la instancia (también
llamada referencia de objeto).
• new será la palabra clave encargada de instanciar un objeto empleando como
plantilla a la clase especificada por nombredeclase.

Lo anterior puede realizarse también en dos líneas:

nombredeclase identificador;
identificador = new nombredeclase();

Para el caso anterior tendríamos:

Message myMessage;
myMessage = new Message();

Por último, analicemos la cuarta línea; aquí invocamos el método del objeto empleando una
sintaxis genérica:

identificadordeobjeto.identificadordemétodo();

Dado que la sintaxis completa que se emplea para invocar un método varía
considerablemente según la situación, se verá con detalle en un apartado más adelante.

NOTA: Contrario a como sucede en C o C++, Java no permite el uso de apuntadores por
parte del programador. Lo anterior podría verse como una desventaja aunque en realidad es
todo lo contrario: gracias a que no puede haber manipulación de memoria es imposible

29
Lenguajes de programación Fundamentos de Java

invadir entornos ajenos al de nuestras aplicaciones y por lo tanto la seguridad de nuestro


sistema estará siempre garantizada.

Ejercicio 1
Capture los siguientes códigos, compílelos y ejecútelos. Identifique las partes de las clases
a manera de repaso.

Código Fuente Shirt.java:

public class Shirt{


public int shirtID = 0;
public String description = "-description required-";
public char colorCode = 'U';
public double price = 0.0;
public int quantityInStock = 0;
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);
}
}

Código Fuente ShirtTest.java:

public class ShirtTest{


public static void main(String args[]){
Shirt myShirt = new Shirt();
myShirt.displayShirtInformation();
}
}

Como puede observarse en el ejercicio anterior, es posible realizar concatenaciones de


cadenas literales con variables y atributos al momento de darles salida mediante el operador de
suma, y así dar mejor entendimiento a nuestro programa.

Ejercicio 2
1. Diseñe y cree una clase llamada Customer.java que contenga los siguientes
atributos (usted decida el tipo): customerID, customerName,
customerStatus y totalPurchases.
2. Asigne a dichos atributos un valor por omisión al momento de declararlos. El
atributo customerStatus podrá tener dos valores: ‘N’ para Nuevo y ‘V’ para
Viejo.
3. Diseñe dentro de la clase un método que imprima los valores por omisión que usted
asignó. Llame al método displayCustomerInfo.

30
Lenguajes de programación Fundamentos de Java

4. Diseñe una clase principal llamada CustomerTest.java que cree un objeto de


tipo Customer e invoque a su método para desplegar la información en pantalla.
Personalice posteriormente cada uno de los valores y reimprima la información para
verificar los cambios realizados.

Operadores matemáticos
Java emplea los siguientes operadores matemáticos:

Unarios
Propósito Operador Ejemplo
Pre-incremento ++var ++i
Post-incremento var++ i++
Pre-decremento --var --i
Post-decremento var-- i--
Binarios
Propósito Operador Ejemplo
Suma + x+y
Resta - x-y
Multiplicación * x*y
División / x/y
Módulo % x%y
Asignación
Propósito Operador Ejemplo
Asignación directa = i = 2
Compuestos
Propósito Operador Ejemplo
Suma y asignación += i += 2
Resta y asignación -= i -= 2
Multiplicación y asignación *= i *= 2
División y asignación /= i /= 2
Módulo y asignación %= i %= 2

Los operadores matemáticos unarios (llamados así porque requieren únicamente de un


operando) incrementan o decrementan al operando en una unidad y pueden aplicarse en dos
modos: cuando se colocan antes del operando (modo prefijo) se realiza primero el incremento o
decremento y después se devuelve el valor de la operación resultante para ser aplicado en el resto
de la expresión (si la hubiere). Cuando se colocan después del operando (modo postfijo) el
incremento o decremento se realiza después de devolver el valor original de la variable. Por
ejemplo:

int x = 1, y = 1;
System.out.println(“Valor de x: “ + ++x);
System.out.println(“Valor de y: “ + y++);
System.out.println(“Valor de x + y: “ + (x+y));

Daría la siguiente salida:


31
Lenguajes de programación Fundamentos de Java

Valor de x: 2
Valor de y: 1
Valor de x + y: 4

El operador módulo ( % ) devuelve el residuo de una división; contrario a otros lenguajes,


en Java el módulo puede aplicarse tanto a datos numéricos enteros como a los de punto flotante.
Los operadores matemáticos compuestos combinan una operación matemática con una
asignación, ahorrándonos la necesidad de escribir el mismo término dos veces; por ejemplo:

a = a + 4;

Se escribiría de manera más breve:

a += 4;

Así, se puede decir que cualquier sentencia de la forma:

variable = variable operador expresión;

Se puede escribir con un operador compuesto con la sintaxis:

variable operador= expresión;

Por otra parte, el operador de asignación posee un atributo interesante: permite realizar
asignaciones en cadena; por ejemplo:

int x, y, z;
x = y = z = 100;

NOTA: Los operandos de las operaciones matemáticas unarias, binarias y compuestas


podrán ser de tipo numérico (entero o real) o de carácter, pero nunca booleano.

Operadores relacionales
Los operadores relacionales comparan dos valores para determinar su relación; la siguiente
tabla muestra los operadores relacionales disponibles en Java:

Condición Operador Ejemplo


Igual que == (a == b)
Diferente de != (a != b)
Menor que < (a < b)
Mayor que > (a > b)
Menor o igual que <= (a <= b)
Mayor o igual que >= (a >= b)

El resultado de una operación relacional será siempre un valor booleano (true o false)
y se emplean con mayor frecuencia en las operaciones condicionales y ciclos.

32
Lenguajes de programación Fundamentos de Java

NOTA: No use el operador de “igual que” con datos de tipo String dado que el resultado
obtenido será erróneo; recuerde que las cadenas son objetos y por lo tanto no estaría usted
comparando su contenido sino sus referencias. Para comparar cadenas recurra al método
equals como se ve a continuación:

public String name1 = “Fred Smith”;


public String name2 = “Joseph Smith”;
. . .
. . .
if (name1.equals(name2))
System.out.println(“Same name”);

equals es un método perteneciente a la clase Object (superclase raíz en la jerarquía de


clases de Java). En los objetos de tipo String regresará false con tan solo un carácter
de diferencia entre las cadenas a comparar y es sensible a mayúsculas.

Operadores condicionales
Se pueden emplear los operadores condicionales (también conocidos como operadores
lógicos) para construir expresiones booleanas más complejas:

Operación Operador Ejemplo


AND & (a == b) & (b == c )
OR | (a != b) | (b == c)
NOT ! !(a < b)
AND en cortocircuito && (a == b) && (b == c )
OR en cortocircuito || (a != b) || (b == c )
XOR (OR exclusivo) ^ (a == b) ^ (b == c )

La siguiente tabla muestra los valores de verdad para los operadores AND, OR, XOR y
NOT empleando dos expresiones:

A B A|B A&B A^B !A


Falso Falso Falso Falso Falso Verdadero
Verdadero Falso Verdadero Falso Verdadero Falso
Falso Verdadero Verdadero Falso Verdadero
Verdadero Verdadero Verdadero Verdadero Falso

Como podrá notar en la primera tabla, Java provee un segundo conjunto de operadores
AND y OR conocidos como operadores en cortocircuito. Estos operadores se llaman así porque
la expresión de la derecha no se evalúa cuando el resultado de la operación queda determinado
por el valor de verdad de la expresión de la izquierda.

En otras palabras: si el primer operando en una condición AND evalúa a falso, no se


evaluará el segundo operando, y si el primer operando de una condición OR es verdadero,
tampoco se evaluará el segundo operando.

33
Lenguajes de programación Fundamentos de Java

Lo anterior es especialmente útil cuando la evaluación satisfactoria del segundo operando


depende del valor de verdad del primero; suponga el siguiente código:

if (b != 0 && a / b > 10)

Si lo anterior se hiciera con un AND tradicional ( & ) causaría un error en tiempo de


ejecución cuando b sea igual a cero.

Prioridad de operadores
La siguiente tabla muestra la prioridad por omisión de los operadores vistos hasta el
momento:

Precedencia mas alta


++ -- !
* / %
+ -
> >= < <=
== !=
&
^
|
&&
||
?:
= *= /= %= += -=
Precedencia mas baja

Los operadores relacionales tienen menor precedencia que los matemáticos; a su vez, los
operadores condicionales tienen menos precedencia que los relacionales, a excepción de la
negación, cuya jerarquía es igual a la de los operadores matemáticos unarios.

Como en otros lenguajes de programación, se deberá encerrar entre paréntesis a todas


aquellas operaciones a las que se les desee dar mayor prioridad a la que poseen por omisión. La
anidación de paréntesis está permitida y se evaluará primero aquella operación que se encuentre
en el paréntesis más interno.

Tomas de decisión
El enunciado básico de control de programa es el enunciado if; su forma sencilla es:

if (expresión_booleana){
bloque;
}

El bloque de enunciados se ejecutará siempre y cuando la expresión_booleana


evalúe a cierta. Cuando se trate de un solo enunciado, podrá prescindirse de las llaves que
delimitan al bloque.

34
Lenguajes de programación Fundamentos de Java

Si además se requiere ejecutar enunciados cuando la expresión evalúa a falsa, es posible la


inclusión de una cláusula else:

if (expresión_booleana){
bloque;
}
else{
bloque;
}

Dentro de bloque podrán incluirse cualquier tipo y cantidad de enunciados, inclusive otro
enunciado if; a esto último se le conoce como anidación.

Ejemplo:

if (x == y)
System.out.println("x es igual a y");
else
if (x > y)
System.out.println("x es mayor que y");
else
System.out.println("x es menor que y");

Ejercicio 3
1. Diseñe y cree una clase llamada Person.java que contenga el atributo
ageInYears. Asigne a dicho atributo un valor de inicio diferente de cero.

2. Diseñe dentro de la clase un método que calcule y despliegue la edad de la persona


en días.

3. Diseñe una clase principal llamada PersonTest.java que cree un objeto de tipo
Person e invoque a su método para desplegar su edad en dias.

Ejercicio 4
1. Diseñe y cree una clase llamada CalculateDays.java que contenga el atributo
público month con valor inicial de cero.

2. Diseñe dentro de la clase un método que calcule y despliegue el número de días que
tiene un mes dado.

3. Diseñe una clase principal llamada CalculateDaysTest.java que cree un


objeto de tipo CalculateDays. Altere el valor del atributo month a un valor
cualquiera entre uno y doce e invoque a su método para probar su funcionalidad.

35
Lenguajes de programación Fundamentos de Java

Enunciado switch
Este es el enunciado de control más flexible de Java, ya que permite que el programa
ejecute diferentes bloques de instrucciones basado en una expresión que puede tener más de dos
valores. Su sintaxis es la siguiente:

switch (variable){
case plantilla_1: bloque_1;
break;
case plantilla_2: bloque_2;
break;
...
case plantilla_n: bloque_n;
break;
[default: bloque_default;]
}

En donde variable podrá ser cualquier variable de tipo byte, short, int o char.
Dicha variable se compara contra cada plantilla y:

• En caso de haber concordancia, la ejecución es transferida al bloque de


enunciados que se encuentra a continuación de la plantilla correspondiente.
Dicho bloque podrá ser cualquier cantidad de enunciados y de cualquier tipo
(inclusive otro switch).
• De no haber concordancia, la ejecución es transferida al bloque de enunciados que
se encuentra a continuación de la etiqueta opcional default.
• Si no hay concordancia ni sentencia default, la ejecución pasa al primer
enunciado después de la llave de cierre del switch.

La plantilla podrá estar constituida por una constante entera o por valores literales
válidos, pero nunca variables o llamados a métodos. En caso de buscar coincidencia con
caracteres, deberán estar encerrados en comillas simples. Es de suma importancia el agregar un
enunciado break después del último enunciado de cada plantilla, ya que de lo contrario todos
los enunciados posteriores pertenecientes a las plantillas restantes, se ejecutarán también. Esto
debe evitarse, a menos que sea útil a nuestros fines.

Ejemplo 1:

switch (opcion){
case 1: System.out.println("Seleccionó Altas!");
break;
case 2: System.out.println("Seleccionó Bajas!");
break;
case 3: System.out.println("Seleccionó Cambios!");
break;
default: System.out.println("Fuera de rango!!!");
}

36
Lenguajes de programación Fundamentos de Java

Ejemplo 2:

switch (opcion){
case 1:
case 2:
case 3:
case 4:
case 5: System.out.println("Introdujo 5 o menos!");
break;
case 6:
case 7:
case 8:
case 9:
case 10: System.out.println("Introdujo 10 o menos!");
break;
default: System.out.println("Fuera de rango!!!");
}

Ejemplo 3:

switch (opcion){
case 'a': System.out.println("Seleccionó Altas!");
break;
case 'b': System.out.println("Seleccionó Bajas!");
break;
case 'c': System.out.println("Seleccionó Cambios!");
break;
default: System.out.println("Fuera de rango!!!");
}

Ejemplo 4:

switch (contador){
case 1: switch(var){
case 0: System.out.println("var vale cero");
break;
case 1: System.out.println("var vale uno");
break;
}
break;
case 2: System.out.println("No hubo conflictos!”);
break;
default: System.out.println("Fuera de rango!!!");
}

Ejercicio 5
• Adapte el ejercicio cuatro para que la toma de decisión sea realizada por un
enunciado switch y no por toma de decisiones anidadas.

37
Lenguajes de programación Fundamentos de Java

Ciclo for
Este ciclo es una construcción que ejecuta un bloque de uno o más enunciados una
determinada cantidad de veces. En Java, tiene la siguiente estructura:

for(inicial; expresión_booleana; incremento){


bloque;
}

En donde:

• inicial será por lo general una expresión de asignación que ponga una variable a
un valor determinado de inicio y dicha variable será típicamente la que controle el
ciclo; de hecho, como normalmente no se emplea para ninguna otra función, se
acostumbra declarar dicha variable en este punto. La expresión inicial se ejecuta una
sola vez al entrar al ciclo.
• expresión_booleana será la expresión a evaluar; el ciclo se ejecutará mientras
sea verdadera y terminará cuando sea falsa.
• incremento será la expresión que determine el incremento o decremento de la
variable controladora. Esta expresión se ejecutará al final de cada iteración mientras
el ciclo continúe activo.
• bloque será el o los enunciados a realizar en el ciclo; como se vio anteriormente,
cuando se trate de un solo enunciado, podrá prescindirse de las llaves.

Ejemplos:

for (int count = 1; count <= 20; count++)


System.out.println(count);

for (int count = 0; count < 1000; count += 5)


System.out.println(count);

Los anteriores son ejemplos típicos de utilización de un ciclo for que demuestran que la
variable controladora puede ser incrementada o decrementada, y no necesariamente en una
unidad. Sin embargo, su flexibilidad es mucho mayor; por ejemplo, la variable controladora no
necesariamente debe ser inicializada dentro del ciclo:

int count = 1;
for ( ; count < 1000; count++)

Esto permite que el lugar que la inicialización ocupa, sea utilizado para incluir cualquier
expresión válida de Java:
count = 1;
for ( System.out.println("Contando..."); count < 100; count++)

Además, el incremento o decremento puede ser hecho en cualquier otra parte del ciclo:

38
Lenguajes de programación Fundamentos de Java

for ( count = 0; count < 100; )


System.out.println(count++);

Por último, es posible dividir las expresiones de inicio e incremento en dos o más
subexpresiones utilizando el separador coma, haciéndolo trabajar como un operador y
permitiéndonos realizar varias tareas al mismo tiempo:

for ( a = 1, b = 2; i < 1000; a++, j*=2)


c = a * b;

Al igual que una instrucción condicional, un ciclo for puede ser ejecutado dentro de otro,
logrando así una construcción más compleja y efectiva:

public class ForTest{


public static void main(String args[]){
int lineas = 5;
int columnas = 5;
for(System.out.println("Drawing…"); lineas > 0; lineas--){
for(int columna = columnas; columna > 0; columna--)
System.out.print("X");
System.out.print('\n');
}
}
}

Ejercicio 6
• Diseñe una clase llamada Factorial.java que contenga un método capaz de
calcular y desplegar el factorial de su atributo público number, al cual usted
asignará un valor desde la clase principal FactorialTest.java.
• Diseñe la clase principal de tal forma que pruebe la clase Factorial al menos
con tres valores diferentes.
• Diseñe el método de cálculo de tal forma que no acepte valores menores a 1 o
mayores a 30.

Ciclo while
Este ciclo es una construcción que ejecuta un bloque de uno o más enunciados mientras que
una condición específica sea cierta. En Java, tiene la siguiente estructura:

while(expresión_booleana){
bloque;
}

En donde:

• expresión_booleana será la expresión a evaluar; dicha evaluación se realiza


al principio del ciclo, el cual se ejecutará mientras sea verdadera y terminará cuando
sea falsa.
39
Lenguajes de programación Fundamentos de Java

• bloque será el o los enunciados a realizar en el ciclo; al igual que en un ciclo for,
cuando se trate de un solo enunciado no se requieren de las llaves.

Ejemplos:

x = 0;
while(x < 10){
System.out.println("El valor de x es: " + x);
x++;
}

Al igual que los ciclos for, un ciclo while puede ser ejecutado dentro de otro:

public class WhileTest{


public static void main(String args[]){
int lineas = 0;
int columnas = 0;
int altura = 3;
int ancho = 10;
while(lineas < altura){
columnas = 0;
while(columnas < ancho){
System.out.print("@");
columnas++;
}
System.out.println();
lineas++;
}
}
}

Ciclo do/while
A diferencia del ciclo while, el ciclo do/while evalúa la condición al final del ciclo en
vez de hacerlo al principio; por ello, son siempre ejecutados al menos una vez. Su forma es:

do{
bloque;
}
while(expresión_booleana);

Ejemplo:

x = 10;
do
System.out.println("El valor de x es: " + x);
while(x != 10);

40
Lenguajes de programación Fundamentos de Java

Como se puede apreciar, al igual que en todos los casos anteriores, las llaves no son
requeridas en bloques de una sola línea. Paralelamente, los ciclos do/while aceptan también la
anidación.

Ejemplo:

public class DoWhileTest{


public static void main(String args[]){
int altura = 3;
int ancho = 10;
int filas = 0;
int columnas = 0;
do{
columnas = 0;
do{
System.out.print(“@”);
columnas++;
}while(columnas < ancho);
System.out.println();
filas++;
}while(filas < altura);
}
}

Sentencias de salto
Java incorpora tres sentencias de salto diferentes: break, continue y return; se les
llama así dado que transfieren o devuelven el flujo de ejecución a otra parte del programa.

break:

Como se vio en el enunciado switch, break finaliza un bloque de instrucciones y


normalmente se incluye uno en cada plantilla. Además de lo anterior, permite la terminación
anticipada de un ciclo for, while o do/while, pasando la ejecución del programa al
enunciado que se encuentra inmediatamente después de la llave de cierre del ciclo.

Ejemplo:

for(int i=0; i<100; i++){


if(i == 10)
break;
System.out.println(“i: “ + i);
}

NOTA: Cuando se trate de un ciclo anidado, break causará la salida del ciclo más interno
únicamente.

41
Lenguajes de programación Fundamentos de Java

continue:

De manera similar a break, continue puede ser utilizado solamente en el cuerpo de un


for, de un while o de un do/while. En vez de abortarlo, continue comienza
inmediatamente la siguiente iteración del ciclo que lo contiene, omitiendo la ejecución de los
enunciados que le siguen hasta el fin del ciclo.

Si se trata de un ciclo for, continue realiza los incrementos definidos en el encabezado


del ciclo.

Ejemplo:

public class ContinueBreakTest{


public static void main(String args[]){
for(int count=0; count < 10; count++)
if (count<5){
System.out.println(count);
continue;
}
else
break;
}
}

return:

La sentencia return da por terminado el método que se está ejecutando en ese momento,
devolviendo el control del flujo al método que lo invocó originalmente; si se ubica en el método
main(), el programa terminará regresando el control al sistema operativo.

Ejemplo:

class Return{
public static void main(String args[]){
boolean t = true;
System.out.println(“Antes del return”);
if(t)
return;
System.out.println(“Esto es código muerto!!!”);
}
}

NOTA: La sentencia return nos permite (además de dar por concluido un método)
retornar un valor. Esto se verá con más detenimiento más adelante.

Ciclos infinitos
Un ciclo infinito es aquel for, while o do/while que por sí solo, se ejecutará
indefinidamente; aunque esto es una anomalía que debe evitarse, a veces puede ser útil, si se

42
Lenguajes de programación Fundamentos de Java

utiliza correctamente en combinación con uno o varios enunciados break. En Java, un ciclo
infinito se redacta como sigue:

while(true){ for (;;){ do{


bloque; bloque; bloque;
} } }while(true);

El ejemplo anterior dentro de un ciclo for infinito se vería de la siguiente manera:

public class InfiniteTest{


public static void main(String args[]){
int count=0;
for(;;)
if (count<5){
System.out.println(count++);
continue;
}
else
break;
}
}

Promoción y casting de datos


Cuando se asigna una variable o una expresión a otra variable, puede ser que los tipos de
datos del resultado obtenido y de la variable que ha de contenerlo no coincidan. Si el compilador
detecta esta situación generará un error y en caso de que no lo haga los resultados en tiempo de
ejecución serán incorrectos.

Para evitar esto, las variables y las expresiones podrán ser promovidas a un tamaño mayor;
la promoción podrá ser manual (realizada por el programador) o automática (realizada por el
compilador. La otra opción es ajustar el tamaño de las expresiones para coincidir con la variable;
a este método de ajuste se le conoce formalmente como type casting.

Promoción:

Supongamos el siguiente caso:

int num1 = 53;


int num2 = 47;
byte num3;
num3 = num1 + num2;

Aparentemente esto debería de funcionar, dado que un dato de tipo byte es capaz de
contener el valor del resultado; sin embargo el compilador arrojará el siguiente error:

possible loss of precision


found: int
required: byte

43
Lenguajes de programación Fundamentos de Java

Esto sucede dado que un dato byte es menor que un dato int; para esta situación la
promoción manual es la solución más sencilla, dado que sólo habría que redefinir el tipo de dato
de num3:

int num 3;

Sin embargo en algunos casos (si la variable es de un tipo mayor al de la expresión que se
le pretende asignar) el compilador realiza la promoción de la expresión de manera automática;
esto sucede dado que se asume que no existirá pérdida de datos. Suponga por ejemplo la siguiente
asignación directa:

long bigNumber = 6;

Como recordará, las literales numéricas enteras se consideran por omisión de tipo int.
Siendo este el caso, se realizará una promoción automática a tipo long al momento de la
compilación, como si se le hubiese adicionado el posfijo L al número seis. Lo mismo sucede
cuando pretendemos asignar un dato de tipo entero (char, byte, int o long) a uno de tipo
real (float o double). Dado que no hay posiciones decimales en los enteros, no hay datos que
perder.

NOTA IMPORTANTE

Cuando se resuelve una ecuación y antes de asignarla a una variable, se


almacena el resultado en memoria de forma temporal. Este espacio temporal creado
será siempre igual al tamaño que posea el operando más grande. Si por ejemplo se
multiplican dos datos de tipo int, el espacio temporal se creará de 32 bits.

Por ello, tenga cuidado al multiplicar operandos que puedan sobrepasar el


ámbito temporal puesto que de lo contrario habrá pérdida de información. Por
ejemplo:

int num1 = 55555;


int num2 = 66666;
long num3;
num3 = num1 * num2;

Lo anterior es incorrecto aunque num3 sea long, dado que el resultado de la


operación intermedia (3,703,629,630) rebasa por mucho el ámbito de un tipo int.
Para evitar este problema, establezca al menos un término a tipo long asegurando
así el mayor espacio temporal posible. Lo mismo si los valores fuesen literales; por
ejemplo:

long num3;
num3 = 55555 * 66666L;

Casting:

Ahora bien, cuando no es deseable cambiar el tipo de dato de la variable que habrá de
contener nuestra asignación, será necesario ajustar el tamaño de esta última por medio de un

44
Lenguajes de programación Fundamentos de Java

casting. Un casting literalmente corta el resultado a un tipo de dato menor y requiere de la


siguiente sintaxis:

identificador = (tipo_de_destino) expresión

En donde:

• identificador es el nombre de la variable que almacenará el resultado.


• tipo_de_destino es el tipo al cual se realizará la reducción.
• expresión será el valor literal, variable, operación matemática o expresión java
que se pretende ajustar. Si la expresión es compleja deberá encerrarse entre
paréntesis.

Por ejemplo, supongamos el siguiente caso:

int num1 = 53;


int num2 = 47;
byte num3;
num3 = (byte) (num1 + num2);

La expresión num1 + num2 será ajustada a tipo byte y posteriormente asignada a la


variable num3, sin causar error al momento de la compilación.

NOTA: Emplee el casting con cuidado cuando se trate de datos reales que tengan una parte
fraccional, puesto que esta será truncada por completo (a menos que eso sea lo que usted
pretende). De igual manera tome en cuenta que los datos de tipo entero serán recortados si
se les ajusta a un tipo menor dentro de su categoría.

Cuidados en el manejo de datos enteros y reales


El compilador de Java realiza ciertas suposiciones al evaluar datos primitivos en
expresiones matemáticas y de asignación; se recomienda que dichas suposiciones sean tomadas
en cuenta al realizar un casting:

Cuando los datos primitivos son enteros se convierten a tipo int (a menos que
específicamente se diga que son de tipo long) antes de realizarse cualquier operación
matemática; suponga por ejemplo:

short a, b, c;
a = 1; b = 2;
c = a + b;

Lo anterior causa un error dado que aunque a y b se declararon como short, los valores 1
y 2 son convertidos a int antes de sumarse. Para librar el problema podríamos:

• Declarar a c como un int:


int c;

45
Lenguajes de programación Fundamentos de Java

• Aplicar un cast a a + b antes de asignarlo a c:


c = (short) (a+b);

Por otra parte, cuando los datos primitivos son de tipo real se convierten a tipo double (a
menos que específicamente se diga que son de tipo float). La siguiente línea por ejemplo
causaría un error de compilación, dado que por omisión 27.9 es considerado un double:

float float1 = 27.9;

Aquí tendríamos dos opciones:

• Agregar el posfijo F a la literal 27.9:


float float1 = 27.9F;
• Aplicar un cast a tipo float a la literal 27.9:
float float1 = (float) 27.9;

Ejercicio 7
• Diseñe una clase llamada Primo.java que contenga un método capaz de definir
si un número de tipo long es o no primo. El número a definir deberá almacenarse en
el atributo público number, al cual usted asignará un valor desde la clase principal
PrimoTest.java.
• Diseñe la clase principal de tal forma que pruebe la clase Primo al menos con tres
valores diferentes.

46
Lenguajes de programación Fundamentos de Java

Almacenamiento de variables, referencias y objetos en memoria


Cuando en un programa se emplean valores literales o se crean variables y constantes para
asignarles un valor, estos son almacenados en la memoria de la computadora. La siguiente figura
ilustra que las variables locales y los atributos se almacenan en partes separadas:

Variables y constantes Objetos con sus


declaradas dentro de atributos
un método

Memoria Memoria
Stack Heap

Los objetos con sus atributos son almacenados en la memoria Heap, la cual es administrada
de manera dinámica por el programa. Por otra parte, las referencias a los objetos y las variables
de sus métodos son almacenadas en la memoria Stack, dado que sólo son empleadas por un breve
periodo de tiempo. Mientras que las variables de tipo primitivo almacenan valores, las referencias
a objetos almacenan la ubicación en memoria (dirección) de los mismos.

Dichas ubicaciones, generalmente escritas en notación hexadecimal, son únicas para cada
objeto instanciado y se crean dinámicamente en tiempo de ejecución. Considere el siguiente
código:

public static void main(String args[]){


int counter = 10;
Shirt myShirt = new Shirt();
}

Esto quedaría ordenado en memoria de la siguiente manera:

0 shirtID
0.0 price
U colorCode
counter 10
0x034009

myShirt 0x034009

Memoria Stack Memoria Heap

47
Lenguajes de programación Fundamentos de Java

Por lo anterior, tome en cuenta que cuando usted asigna el valor de una referencia a objeto
a otra no estará pasando los valores de los atributos de un objeto a otro. Lo que en realidad
sucederá es que ambas referencias contendrán ahora la misma dirección (esto es, apuntarán al
mismo objeto). Por ejemplo:

Shirt myShirt = new Shirt();


Shirt yourShirt = new Shirt();
myShirt = yourShirt;

Esta asignación dejaría a las referencias como se ve en el siguiente esquema:

0 shirtID
0.0 price
U colorCode
counter 10
0x034009

0x034009 0 shirtID
myShirt 0x99F311
0.0 price

yourShirt 0x99F311 U colorCode

0x99F311

Memoria Stack Memoria Heap

Dado que el objeto myShirt original ya no es referenciado por ningún identificador, no


tiene razón de existir en memoria. Para eliminar o destruir todos aquellos objetos en memoria
Heap que ya no son referenciados, Java implementa una herramienta de limpieza automática
conocida como garbage collector (recolector de basura) que periódicamente se encarga de
realizar dicha tarea.

Uso de la clase String


La clase String es una de las muchas clases incluidas en el API de Java y provee la
capacidad de almacenar una serie de caracteres bajo un identificador de manera sencilla. Aunque
en ejercicios pasados hemos tratado a las cadenas como un tipo de dato primitivo, son en realidad
un objeto; lo peculiar del caso es que no es necesario emplear la palabra clave new para
construirlas.

La sintaxis general para declarar e inicializar una cadena es la siguiente:

String identificador = “cadena_literal”;

Lo cual sería lo mismo a escribir:

48
Lenguajes de programación Fundamentos de Java

String identificador = new String(“cadena_literal”);

La primera forma es por supuesto más común, dada su sencillez y similitud con las
declaraciones de variables simples. Como cualquier objeto, las cadenas hacen uso de espacio en
la memoria stack para almacenar la referencia y de la memoria heap para almacenar su
contenido; sin embargo, cuando son creadas sin la palabra clave new, las cadenas se almacenan
en una parte especial del heap conocida como el pool de literales (literal pool). Esta área
reservada tiene la característica de no permitir la duplicación de cadenas idénticas.

Suponga que se redacta el siguiente código:

String myString = “Luis Muslera”;

Esto generaría lo siguiente en la memoria:

myString
0xDEF 0x0011F [C Value
0x2244C Comparator

0xDEF

Luis Muslera
0x0011F

Memoria Stack Memoria Heap

La referencia en el stack contiene la dirección del objeto String en el heap, quien a su


vez contiene un valor conocido como [C Value que guarda la dirección en el pool de literales
de la cadena almacenada.

Por último, tome en cuenta que en Java las cadenas son objetos inmutables (esto es, no se
pueden modificar). Una vez creado un objeto String no se pueden alterar los caracteres que lo
conforman. Cuando usted iguala un identificador a una cadena diferente Java crea un objeto
nuevo en el Heap y modifica la referencia empleada, dejando en manos del recolector de basura
la destrucción de la cadena original.

Conceptos avanzados sobre invocación de métodos


Como se vio anteriormente, para invocar o ejecutar un método que se encuentra en una
clase diferente a la actual, empleamos la sintaxis siguiente:

identificadordeobjeto.identificadordemétodo();

En donde:

49
Lenguajes de programación Fundamentos de Java

• identificadordeobjeto será el nombre de la referencia del objeto creado


cuyo método deseamos ejecutar.
• . será el operador que nos permitirá el acceso al método.
• identificadordemétodo() será el nombre del método a invocar. Dado que
hasta el momento no se han empleado métodos con argumentos, el paréntesis está
vacío.

Por ejemplo:

public class ShirtTest{


public static void main(String args[]){
Shirt myShirt = new Shirt();
myShirt.displayShirtInformation();
}
}

El código anterior en su línea 4 ejecuta el método displayShirtInformation() del


objeto myShirt dentro del método main(). De manera general, se le conoce como calling
method (método que llama) al método en donde colocamos la invocación y worker method
(método que trabaja) al que es invocado.

Cuando se invoca a un método que se encuentra definido en la misma clase, no es necesario


especificar el parámetro identificadordeobjeto; basta con enunciar el
identificadordemétodo().

Lo anterior es posible porque cuando no se especifica el nombre del objeto al que el método
pertenece, Java antepone por omisión la palabra clave this, que le indica al programa que el
método se encuentra en la misma clase. Podemos dejar que el compilador asigne la palabra clave
por omisión o podemos hacerlo nosotros manualmente; por ejemplo:

this.displayShirtInformation();

Invocaría al método displayShirtInformation() de la clase en la cual se está


realizando la invocación. La palabra clave this tiene uso también con variables locales, el cual
se verá más adelante.

Por último, tome además en cuenta que:

• No hay límite para el número de métodos que un método puede llamar.


• El método que llama y el que trabaja pueden estar en la misma clase o en clases
diferentes.
• Un método que llama puede ser el método que trabaja para otro método diferente.
• Los métodos pueden invocarse en cualquier orden, no importa el lugar en que son
posicionados en la clase.

50
Lenguajes de programación Fundamentos de Java

Declaración e invocación de métodos con argumentos y valores de retorno


Un método puede ser invocado y a la vez recibir una lista de argumentos; se define como
argumento a una variable o valor empleado por el método para cumplir su cometido.
Adicionalmente, un método puede ser capaz de retornar un valor al método que lo invocó para ser
empleado según convenga.

Como se vio anteriormente, la sintaxis para declarar un método es la siguiente:

[modificadores] tipoderetorno identificador([argumentos])

En donde:

• [argumentos] contendrá la lista de variables que el método recibirá; cada


argumento deberá especificar el tipo de dato y su nombre. Si se envía más de un
argumento, se deberán separar por comas.
• tipoderetorno será el tipo de dato que el método regresará. Si no regresa
ningún dato, deberá emplearse la palabra clave void.

Cualquier tipo de dato puede usarse como argumento o valor de retorno (String
inclusive). Sin embargo, aunque un método podrá aceptar cualquier número de parámetros, sólo
podrá tener un valor de retorno.

Ejemplos:

public long suma(int dato1, int dato2)


public void setID(int articleID)

Para invocar a un método con argumentos, simplemente agregue en el paréntesis los valores
requeridos en el orden especificado; tome en cuenta que los tipos y la cantidad de argumentos
deben coincidir. Por ejemplo:

suma(5,10);
setID(550);

De igual forma que pueden emplearse valores literales, es posible emplear nombres de
variables como argumentos válidos. Una vez más, los tipos y el número de argumentos deberán
coincidir con los esperados por el método.

NOTA: Un método podrá aceptar como argumentos y retornar valores de tipo objeto; lo
anterior sin embargo requiere de un análisis más detallado, por lo que se verá con
detenimiento en un apartado posterior.

Hasta ahora se han empleado métodos que no retornan nada y emplean a void como
tipoderetorno. Cuando desee que un método retorne un valor, especifique el tipo antes del
nombre del método e incluya una sentencia de retorno en su cuerpo con la siguiente sintaxis:

return expresión;

51
Lenguajes de programación Fundamentos de Java

En donde expresión podrá ser una literal, una variable o una operación matemática.
Podrá haber más de una sentencia de retorno por método siempre y cuando estén posicionadas
estratégicamente en sentencias de toma de decisión. Por ejemplo:

if (a > b)
return “A es mayor que B”;
else
return “B es igual o mayor que A”;

El valor de retorno de un método worker podrá o no ser utilizado por el método caller
según se requiera; para emplear un valor de retorno, el llamado al método deberá estar redactado
en una sentencia de asignación o ser usado directamente como parámetro de otro método. Para el
código anterior (suponiendo que esté incrustado en un método compare con valor de retorno
String, en un objeto llamado numbers) podríamos hacer lo siguiente:

String message;
message = numbers.compare();
System.out.println(message);

O también:

System.out.println(numbers.compare());

Creación de métodos y atributos estáticos


Hasta el momento, hemos accedido a los métodos y atributos de los objetos de manera
remota dado que se han declarado con el modificador public. Los métodos y los atributos que
son creados al momento de instanciar un objeto son llamados con frecuencia métodos de
instancia y variables de instancia.

Por otro lado, se ha estado empleando en las clases principales un método especial llamado
main() que no requiere ser instanciado. Lo anterior es posible dado que el método main() es
un método estático (también conocidos como métodos de clase) y estos pueden ser invocados sin
necesidad de instanciar primero el objeto que los contiene.

De la misma manera, Java permite crear atributos estáticos que pueden emplearse sin
necesidad de instanciar el objeto que los contiene.

Un método estático se declara empleando el modificador static; por ejemplo:

public static int convertSize(int numericalSize)

Dado que los métodos estáticos no forman parte de cada instancia de objeto, no se deberá
emplear una referencia a objeto para invocarlos, sólo su clase. Así, la sintaxis quedaría como
sigue:

Identificadordeclase.identificadordemétodo([argumentos]);

Para el método anterior tendríamos (suponiendo que pertenece a la clase Shirt):

52
Lenguajes de programación Fundamentos de Java

Shirt.convertSize(25);

Por otra parte, las variables de clase estáticas, se emplean cuando se requiere únicamente
una copia de la misma variable en memoria para todos los objetos instanciados, no una copia por
cada objeto. De la misma forma que los métodos estáticos, un atributo estático se declara
agregándole el modificador static; por ejemplo:

static double salesTax = 8.25;

Así mismo, se deberá emplear el nombre de la clase para acceder a las variables estáticas:

Identificadordeclase.identificadordevariable

Por ejemplo:

double myPI = Math.PI;

De hecho, los atributos estáticos pueden ser además constantes si se les agrega el
modificador final; la variable PI por ejemplo, es una variable de clase pública, estática y final
que pertenece a la clase Math. Tome además en cuenta que un método estático definido en una
clase sólo puede emplear sus atributos cuando estos son también estáticos.

Por último, considere declarar un método o atributo como estático si:

• Realizar una operación en un objeto dado o asociar una variable con un tipo de
objeto específico no es importante.
• Acceder la variable o el método antes de instanciar el objeto es importante.
• El método o variable no pertenece de manera lógica a un objeto, pero
probablemente pertenezca a una clase de utilería, como System o Math (incluidas
en el API de Java). Esta última de hecho, contiene diversos métodos estáticos que
pueden emplearse en ecuaciones matemáticas.
Ejercicio 8
• Diseñe una clase pública llamada Mate.java que contenga un método estático
capaz de calcular y desplegar el factorial de un número entero que se le pase a
manera de argumento. El método deberá tener como valor de retorno el factorial
calculado.
• Diseñe una clase principal de tal forma que pruebe el método de cálculo del
factorial de la clase anterior.
• Diseñe el método de cálculo de tal forma que no acepte valores menores a 1 o
mayores a 30.

Métodos con sobrecarga


En Java, pueden existir varios métodos en una clase que tengan el mismo nombre pero con
diferentes argumentos (esto es, diferentes firmas). A este concepto se le conoce como
sobrecarga.

53
Lenguajes de programación Fundamentos de Java

Por ejemplo, es probable que se desee crear un método que sume dos cantidades (por
ejemplo, dos datos de tipo int o dos datos de tipo float). Con una sobrecarga de método esto
puede hacerse sin mayor problema como en el ejemplo siguiente:

public class Calculator{

public int sum(int numberOne, int numberTwo){


System.out.println(“Method One”);
return numberOne + numberTwo;
}

public float sum(float numberOne, float numberTwo){


System.out.println(“Method Two”);
return numberOne + numberTwo;
}

public float sum(int numberOne, float numberTwo){


System.out.println(“Method Two”);
return numberOne + numberTwo;
}
}

Cuando se invoque el método sum(), bastará con colocar los argumentos requeridos por
cualquiera de las tres combinaciones; el compilador comparará la firma del llamado con la de la
declaración del método y elegirá el que corresponda. Suponga una clase principal para la clase
anterior como la siguiente:

public class CalculatorTest{


public static void main(String args[]){
Calculator myCalculator = new Calculator();
int totalOne = myCalculator.sum(2,3);
System.out.println(totalOne);
float totalTwo = myCalculator.sum(15.99F, 12.85F);
System.out.println(totalTwo);
float totalThree = myCalculator.sum(2, 12.85F);
System.out.println(totalThree);
}
}

Encapsulamiento de atributos y métodos


En OOP, el encapsulamiento implica ocultar los datos de un objeto para hacerlos
disponibles sólo mediante determinados métodos. El encapsulamiento es vital dado que impide
que un usuario manipule los atributos de un objeto de manera incorrecta.

Los métodos y atributos de una clase pueden tener modificadores como public, que le
indican al compilador el nivel de acceso que otros objetos pueden tener hacia ellos. El otro
modificador más comúnmente usado es private.

54
Lenguajes de programación Fundamentos de Java

Como es de imaginarse, public permite acceso al atributo o método desde la misma clase
o desde otras clases. Eso significa que el método podrá ser invocado y el atributo podrá ser
modificado por cualquiera; tan sólo agregue la palabra clave public como modificador del
atributo o método en cuestión:

public int currentFloor = 1;


public void setFloor(int desiredFloor){
...
...
}

En la mayoría de los casos, sin embargo, es preferible que sólo algunos o ninguno de los
atributos de un objeto sean públicos, sino todo lo contrario. Esto es, que sólo puedan ser
modificados dentro de la clase que los contiene. Lo anterior cobra más sentido cuando
recordamos que un objeto sabe mejor que nadie cómo se comporta, y que de hecho la forma en
que lo hace no debería de importarnos; únicamente nos debe incumbir cómo emplearlo.

El modificador private permite a un atributo o método permanecer inaccesible a otros


objetos. Como en el caso anterior, agregue la palabra clave private como modificador del
atributo o método a encapsular:

private int currentFloor = 1;


private void calculateCapacity(){
...
...
}

Como se mencionó anteriormente, en un programa ideal, todos o la mayoría de los atributos


deberán ser privados. Por otra parte, al menos un método de cada clase deberá ser público, o de lo
contrario el objeto no podrá ser usado.

De hecho, los métodos y atributos públicos de un objeto son llamados con frecuencia la
interfaz de la clase, puesto que son la única forma de interactuar con los objetos que se creen en
base a ella.

Los detalles de cómo una clase realiza una operación dada dentro de un método son
llamados la implementación del método.

Métodos Get y Set


El apartado anterior con frecuencia genera la siguiente pregunta: Si todos los atributos de
mi objeto van a ser privados, ¿Cómo es que podré acceder a ellos? Un objeto podrá ver o
modificar los atributos privados de otro objeto únicamente si dicho objeto provee métodos
públicos para cada una de las operaciones de acceso que habrán de realizarse con los valores de
dichos atributos.

Dado que las principales operaciones a realizar con los atributos de un objeto es cambiarles
su valor (Set Value) u obtener su valor (Get Value), es común que todo objeto cuente con al
menos un método público para cada una. Suponga la siguiente clase de ejemplo:

55
Lenguajes de programación Fundamentos de Java

public class PrivateShirt{


private int shirtID = 0;
private String description = “-description required-“;
private char colorCode = ‘U’;
private double price = 0.0;
private int quantityInStock = 0;
public char getColorCode(){
return colorCode;
}
public void setColorCode(char newCode){
if (newCode==’R’ || newCode==’G’ || newCode==’B’)
colorCode = newCode;
else
System.out.println(“Code error! Valid: R, G or B”);
}
}

La anterior clase define cinco atributos privados y dos métodos públicos: Uno para obtener
el código de color de la camisa y otro para cambiarle el código de color a la camisa. La clase de
prueba podría quedar como sigue:

public class PrivateShirtTest{


public static void main(String args[]){
PrivateShirt myPrivateShirt = new PrivateShirt();
System.out.println(myPrivateShirt.getColorCode());
myPrivateShirt.setColorCode('V');
myPrivateShirt.setColorCode('G');
System.out.println(myPrivateShirt.getColorCode());
}
}

En este caso, se obtiene el color de la camisa, luego se intenta cambiar por un valor no
válido (lo cual despliega un error) y por último se le asigna otro valor (esta vez válido) para
después desplegarse en pantalla. Como puede observarse, en ningún momento se menciona
siquiera el nombre del atributo colorCode, ya que de eso se encargan los métodos públicos.

Como cultura general: Los métodos para obtener y cambiar atributos de un objeto son
llamados con frecuencia métodos getter y métodos setter, respectivamente.

Ejercicio 9
• Diseñe una clase llamada Customer.java que contenga atributos privados para
almacenar el nombre completo, ID, teléfono y dirección de correo de un cliente. La
clase deberá contener métodos getter y setter para establecer y desplegar dichos
atributos.
• Diseñe una clase principal de tal forma que pruebe los métodos getter y setter de la
clase anterior.

56
Lenguajes de programación Fundamentos de Java

Alcance de las variables


El alcance de una variable (también conocido como ámbito) se refiere a la visibilidad que
dicha variable tiene en nuestro programa. Contrario a otros lenguajes, Java no posee variables
globales; únicamente atributos (llamados también variables de clase, de instancia o miembros) y
variables de método (llamadas también variables locales).

Hasta el momento hemos visto que los atributos se declaran al principio de la clase
(después de su encabezado y antes del primer método) permitiendo que sean usados a través del
objeto entero (si son privados) o por todos los objetos (si son públicos). Más delante se verán dos
formas más para especificar el ámbito de los atributos de una clase.

Las variables locales por el contrario, serán accesibles únicamente dentro del método en el
cual fueron declaradas y perdurarán en memoria mientras el método se esté ejecutando. De
manera más precisa, se dice que una variable local será visible únicamente en el bloque de
instrucciones que la contiene (permitiéndose la anidación de bloques) y su tiempo de vida será
igual al del bloque mismo.

Cuando un bloque se anida dentro de otro, las variables del bloque externo serán visibles
dentro del bloque interno, pero no a la inversa; considere como ejemplo el siguiente código:

public class Test{


public static void main(String args[]){
int x = 20;
if (x > 10){
int y = 20;
x = y * 2;
}
System.out.println(“Valor de x: “ + x);
}
}

Dado que la variable x fue declarada en el bloque principal del método main, será visible
también dentro del bloque interno. La variable y por el contrario no podrá ser referenciada fuera
de dicho bloque puesto que se declaró dentro de él.

NOTA: Contrario a como se permite en C o C++, no podrán existir dos variables locales
con el mismo nombre en el mismo método aunque pertenezcan a ámbitos diferentes.

Por su misma naturaleza, las variables locales no pueden ser estáticas ni públicas; sólo
pueden hacerse constantes con la palabra clave final. Cualquier intento por referenciar a una
variable local desde otro método u objeto causará un error de compilación.

Se puede dar el caso en que una variable local posea el mismo nombre que un atributo de la
clase en que el método se encuentra. Dadas las reglas de alcance, cuando enunciamos el
identificador de la variable estaremos haciendo referencia a la variable local, no al atributo.

Cuando estas condiciones se den y lo que deseamos es acceder al atributo, podremos


emplear la palabra clave this antes del nombre del atributo de manera similar a como lo
hicimos con los métodos; por ejemplo:
57
Lenguajes de programación Fundamentos de Java

public class ThisTrial{


private int i = 0;
public void printVar(){
int i = 5;
System.out.println("Atributo 'i': " + this.i);
System.out.println("Variable local 'i': " + i);
}
}

Métodos constructores
Un constructor es un método que tiene el propósito especial de instanciar a un objeto de una
manera definida. Generalmente son empleados para asignar valores de inicio a sus atributos y se
invocan de manera automática; su sintaxis es similar a la de un método:

[modificadores] Nombredelconstructor([argumentos]){
bloque;
}

En donde:

• [modificadores] son palabras clave que indican la forma en que los


constructores son accedidos.
• Nombredelconstructor será el nombre del método constructor y debe
coincidir exactamente con el nombre de la clase en donde está definido.
• [argumentos] será la lista opcional de argumentos que el constructor recibe.
• bloque será el código que el constructor ejecuta.

El siguiente código ilustra cómo se redactaría un constructor para la clase Shirt:

public class Shirt{


private int ShirtID = 0;
private String description = “-required-“;
private char colorCode = ‘U’;
private double price = 0.0;
private int quantityInStock = 0;
public Shirt(char startingCode){
switch(startingCode){
case ‘R’:
case ‘G’:
case ‘B’: colorCode = startingCode;
break;
default: System.out.println(“Invalid Code; use R, G or B”);
}
}
public char getColorCode(){
return colorCode;
}
}

58
Lenguajes de programación Fundamentos de Java

Como puede apreciarse, el método constructor Shirt recibe como parámetro un código de
inicio a ser validado posteriormente. El siguiente podría ser un código válido para una clase
principal de prueba:

public class ShirtTest{


public static void main(String args[]){
Shirt myShirt = new Shirt(‘G’);
System.out.println(myShirt.getColorCode());
}
}

Por último, tome en cuenta lo siguiente:

• Un constructor no puede tener valor de retorno o ser estático.

• Aunque el lenguaje permite declararlos como privados, los constructores serán


típicamente públicos o de lo contrario no podrán accederse desde otros objetos.

El constructor por default


Cuando no hay constructores declarados, el compilador inserta automáticamente un
constructor por default con la única intención de cumplir con los requisitos del compilador. Hasta
el momento, todos los objetos que se han creado empleando este método (de ahí la razón del
paréntesis al final de la sentencia de instanciamiento):

Shirt myShirt = new Shirt();

Al declarar nuestro propio constructor sin embargo, el constructor por default no será
insertado más en nuestra clase, por lo que si se desea tener uno, habrá que declararlo
explícitamente. Tan sólo declare otro constructor dentro de su clase que no acepte parámetros;
por ejemplo:

public Shirt(){
...
...
}

Sobrecarga de constructores
La razón por la cual podrá existir más de un constructor en una clase dada es porque, como
los métodos, los constructores pueden sobrecargarse. Lo anterior ofrece una gran variedad de
opciones para inicializar nuestros objetos proporcionando distintos parámetros al momento de
crearlos. Tome como muestra el siguiente código:

59
Lenguajes de programación Fundamentos de Java

public class Shirt{


private int ShirtID = 0; private char colorCode;
private String description = “-required-“;
private double price = 0.0; private int quantityInStock = 0;
public Shirt(){
colorCode = ‘U’;
}
public Shirt(char startingCode){
switch(startingCode){
case ‘R’:
case ‘G’:
case ‘B’: colorCode = startingCode;
break;
default: System.out.println(“Invalid; use R, G or B”);
}
}
public Shirt(String startingDescription){
description = startingDescription;
}
public char getColorCode(){
return colorCode;
}
}

Arreglos unidimensionales
Un arreglo es un conjunto de variables del mismo tipo tratadas como una sola entidad.; a
cada variable dentro de un arreglo se le llama elemento de arreglo. Un arreglo es útil cuando no
deseamos nombrar individualmente a un grupo considerable de variables u objetos similares. En
Java, los arreglos son en realidad objetos y deben declararse e instanciarse de la siguiente forma:

tipo [] identificadordearreglo;
identificadordearreglo = new tipo [tamaño];

O también:

tipo [] identificadordearreglo = new tipo [tamaño];

En donde:

• tipo será el tipo de datos que el arreglo contendrá; podrán usarse tanto tipos
primitivos como tipos objeto (String por ejemplo).
• [] serán los corchetes que indican al compilador que se está declarando un arreglo
y podrán ir también después del identificadordearreglo.
• identificadordearreglo será el nombre que le daremos a nuestro arreglo
• tamaño será el número de elementos que nuestro arreglo habrá de contener. Podrá
ser expresado en forma de variable o valor literal siempre y cuando sea entero, dado
que no existen las posiciones de arreglo de punto flotante. Si el arreglo es local a un
método, la variable deberá ser inicializada previamente.

60
Lenguajes de programación Fundamentos de Java

Los arreglos pueden llenarse al momento de ser instanciados o posteriormente dentro de


algún método, ya sea elemento por elemento o mediante la ayuda de un ciclo. Cuando se les
instancia sin inicializarlos, todo elemento de tipo primitivo se inicializa a cero, carácter nulo o
referencias nulas, según sea el caso.

Ejemplos:

int [] ages = new int[50];

int i = 50;
String names[] = new String[i];

Para llenar los elementos de un arreglo después de crearlo, emplee la siguiente sintaxis:

identificadordearreglo[indice] = valor;

En donde:

• identificadordearreglo será el nombre del arreglo a llenar.


• indice será el número del elemento al que queremos asignar el valor; el primer
elemento de un arreglo es el elemento con índice cero y el último es el elemento con
índice N-1 (siendo N el tamaño con el que nuestro arreglo fue declarado).
• valor será la expresión cuyo valor será asignado al elemento (una variable, una
literal, un valor de retorno, una operación, etc.). Su tipo deberá coincidir con el tipo
del arreglo o con los atributos de los objetos que el arreglo contiene.

Ejemplos:

Si ages es un arreglo de enteros:

ages[0] = 19;
ages[1] = 20 + 12;
ages[2] = a + b;
ages[3] = suma(1,2);

Si shirts es un arreglo de objetos de tipo Shirt:

shirts[0] = new Shirt(‘G’);

Para llenar los elementos de un arreglo cuando se instancia, emplee la siguiente sintaxis:
tipo [] identificadordearreglo = {valores_separados_por_coma};

En donde:

• valores_separados_por_coma será la lista de valores de inicialización


separados por una coma; podrán emplearse literales, variables, expresiones
matemáticas o incluso valores de retorno, siempre y cuando coincidan con el tipo de
datos del arreglo.
61
Lenguajes de programación Fundamentos de Java

Ejemplos:
int [] ages = {19, 42 + 92, a + b};

Shirt []shirts = {new Shirt(‘R’), new Shirt(‘G’), new Shirt(‘B’)};

NOTA: No es posible declarar e inicializar un arreglo en dos líneas ya que esto causaría un
error de compilación.

Por último (y como ya se ha podido dar cuenta) para acceder a los elementos individuales
de un arreglo de datos primitivos con la finalidad de obtener su valor o cambiarlo, basta con
enunciar el nombre del arreglo y especificar entre los corchetes el índice del elemento que
deseamos acceder, recordando siempre que a cada elemento N, le corresponde el índice N-1:

System.out.println(ages[4]);

Imprimirá en pantalla el valor del quinto elemento contenido en el arreglo ages. Por otro
lado, cuando el arreglo contiene objetos habrá que enunciar el nombre del arreglo y el índice del
elemento, seguido del operador punto y del nombre del atributo o método que se desea emplear;
por ejemplo:

System.out.println(shirts[0].shirtID);

Imprimirá en pantalla el valor del atributo shirtID del primer objeto del arreglo
shirts. Así mismo, la siguiente expresión:

shirts[0].displayShirtInformation();

Ejecutará el método displayShirtInformation()del primer objeto del arreglo


shirts.

El atributo length
Todos los objetos de tipo array cuentan con un atributo length que almacena de manera
automática el tamaño del arreglo. El largo de un arreglo es conocido formalmente como sus
límites. Como se explicó anteriormente, los límites de un arreglo de 10 elementos van de cero a
nueve, los de uno de 100 elementos van de cero a 99 y así sucesivamente, dado que el índice del
primer elemento siempre es cero. Contrario al lenguaje C, Java almacena en una variable
específica el tamaño del arreglo, y emplea a esta variable cada vez que se hace acceso al mismo.
Si el índice está fuera de sus límites se genera un error, lo cual asegura que no se invadan
posiciones de memoria no autorizadas.

Para obtener el atributo length de un objeto array dado, emplee la siguiente sintaxis:

identificadordearreglo.length

Por ejemplo, el siguiente enunciado:


System.out.println(“Tamaño del arreglo shirts: “ + shirts.length);

Imprime en pantalla el número de elementos que contiene el arreglo shirts.


62
Lenguajes de programación Fundamentos de Java

Almacenamiento de arreglos unidimensionales en memoria


Como ya se mencionó anteriormente, un arreglo es en realidad un objeto, y como tal, la
única forma de acceder a él es empleando una variable de referencia; para entender la manera en
que los arreglos de datos primitivos se almacenan en memoria, observemos el siguiente ejemplo:

char size = ‘L’;


char [] sizes = {‘S’,‘M’,’L’};

S 0
M 1
size L
L 2

sizes 0x334009 0x334009

Memoria Stack Memoria Heap

Tal como la variable size, el identificador sizes se almacena en el stack; la diferencia


radica en el hecho de que el nombre del arreglo guardará una referencia a un objeto creado en el
heap en donde se almacenarán los valores numéricos de los datos primitivos con los que dicho
arreglo fue inicializado. Ahora veamos un ejemplo de un arreglo que contiene objetos:

Shirt [] shirts = {new Shirt(), new Shirt(), new Shirt()};

0 shirtID
0.0 price
U colorCode
0x00099

0x179009 0 0x00099 0 shirtID


shirts
1 0x00327 0.0 price
2 0x00990 U colorCode

0x179009 0x00327

0 shirtID
0.0 price
U colorCode
0x00990

Memoria Stack Memoria Heap

63
Lenguajes de programación Fundamentos de Java

En este caso, el identificador almacena una referencia que apunta a otro objeto que a su vez
almacena referencias a otros tres objetos diferentes, todos ubicados en el heap.

Empleo de ciclos con arreglos


Sabiendo cómo se puede acceder a los elementos de un arreglo de manera individual,
conociendo las herramientas de bucle como el enunciado for y contando con el atributo
length, podríamos inicializar fácilmente un arreglo. Observe el siguiente ejemplo:

int myArray[] = new int[100];


for (int count = 0; count < myArray.length; count++){
myArray[count] = count;
}

Con este código, inicializaríamos un arreglo de enteros de 100 elementos con valores de
cero a noventa y nueve.

Empleo del arreglo de argumentos en el método main


El método main incluye en la definición de su encabezado un arreglo de tipo String
llamado args a manera de parámetro. Como con cualquier otro método, puede emplearse dicho
parámetro para dar a main una lista de valores que definan su comportamiento; la diferencia
radica en el hecho de que a main sólo se le pueden pasar valores desde el prompt del sistema.

Cualquier programa en Java tratará lo que se escriba después del nombre de nuestro
programa como una cadena literal; cada palabra será tomada como un elemento del arreglo args
y será responsabilidad de nosotros extraer uno a uno esos valores y convertirlos al tipo de dato
que se requiera. Tomemos como ejemplo el siguiente código:

public class ArgsTest{


public static void main(String args[]){
System.out.println(“args[0] = “ + args[0]);
System.out.println(“args[1] = “ + args[1]);
}
}

Para ejecutar el programa anterior, escribamos el siguiente comando:

java ArgsTest Hi There

Y obtendremos la siguiente salida:

args[0] = Hi
args[1] = There

Ahora bien, ¿Qué pasa cuando esperamos usar los argumentos como cantidades numéricas?
Dado que cualquier número será tratado como una cadena, tendremos que convertir cada
elemento a su equivalente numérico. Java provee una clase asociada a cada tipo primitivo de dato
(Integer para los datos de tipo int, Byte para los de tipo byte, etc.) y cada una provee un

64
Lenguajes de programación Fundamentos de Java

método para realizar la conversión (con excepción de la clase Character). Estos métodos,
conocidos como métodos de parsing tienen la siguiente forma:

Nombredeclase.parseTipo(argumento);

Por razones obvias, cada método de conversión tiene un valor de retorno igual al tipo de la
clase a la que pertenecen. Por ejemplo, para convertir el primer argumento del arreglo args del
método main a un tipo int, podríamos hacer lo siguiente:

int ID = Integer.parseInt(args[0]);

Por último, tome en cuenta lo siguiente:

• El nombre del arreglo args puede ser cambiado si así se desea.


• En caso de que nuestros programas acepten parámetros desde el prompt, deberán
contar con una implementación que despliegue la sintaxis conducente en pantalla.
Esta ayuda deberá mostrarse cuando se incluya un parámetro especial para tal efecto
o automáticamente cuando los parámetros mínimos que nuestro programa necesita
para funcionar están incompletos.

El código siguiente muestra un ejemplo completo de cómo emplear la versatilidad del


argumento args:

public class Prompt{


public static void main(String args[]){
if (args.length == 0){
System.out.println("Faltan parametros (Prompt h = ayuda)");
System.exit(0);
}
if (args[0].equals("h")){
System.out.println("Sintaxis: Prompt [h] [valor1 valor2 valor3]");
System.exit(0);
}
if (args.length < 3){
System.out.println("Faltan parametros (Prompt h = ayuda)");
System.exit(0);
}
int value1 = Integer.parseInt(args[0]);
long value2 = Long.parseLong(args[1]);
float value3 = Float.parseFloat(args[2]);
System.out.println("Valor uno como int: " + value1);
System.out.println("Valor dos como long: " + value2);
System.out.println("Valor tres como float: " + value3);
}
}

Ejercicio 10
• Diseñe un programa que reciba desde el prompt del sistema un número entero y lo
convierta a binario, desplegando posteriormente el resultado en pantalla.

65
Lenguajes de programación Fundamentos de Java

• Diséñelo de tal forma que si no recibe el parámetro esperado envíe un mensaje de


alerta informándole al usuario de la sintaxis correcta.

Arreglos bidimensionales
Los arreglos en Java pueden ser multidimensionales (comúnmente llamados matrices). Un
arreglo de dos dimensiones será en realidad un arreglo de arreglos que puede visualizarse como
una tabla de varias filas y varias columnas.

Como se imaginará, la declaración de un arreglo bidimensional requiere de un juego


adicional de corchetes; fuera de este detalle, la creación y uso es prácticamente la misma, sólo
que ahora necesitaremos controlar dos índices en lugar de uno sólo. La sintaxis será entonces:

tipo [][] identificadordearreglo;


identificadordearreglo = new tipo [filas][columnas];

O también:
tipo [][] identificadordearreglo = new tipo [filas][columnas];

En donde:

• tipo será el tipo de datos que el arreglo contendrá.


• [][] serán los corchetes que indican al compilador que se está declarando un
arreglo bidimensional; podrán ir después del identificadordearreglo.
• identificadordearreglo será el nombre que le daremos a nuestro arreglo.
• filas será el número de filas que nuestro arreglo habrá de contener.
• columnas será el número de columnas que nuestro arreglo habrá de contener.

Ejemplo:

int [][] yearSales;


yearSales = new int [12][31];

int matrix[][] = new int[5][5];

Para llenar los elementos del arreglo especifique el número de fila y el número de columna
en donde desea asignar el valor; por ejemplo, los enunciados:

matrix[0][0] = 1;
matrix[4][4] = 25;

Almacenarían el valor uno en la primera fila, primera columna y el valor veinticinco en la


última fila, última columna de la matriz matrix. Dado que este tipo de inicialización sería muy
tardada, se recomienda el empleo de ciclos anidados.

66
Lenguajes de programación Fundamentos de Java

Ejercicio 11
• Diseñe un programa que construya una matriz de cuatro por cuatro y la inicialice
con valores del 1 al 16 de tal manera que quede como la siguiente tabla:

1 2 3 4

5 6 7 8

9 10 11 12

13 14 15 16

• Despliegue posteriormente el contenido de la matriz en pantalla.

Paso de argumentos de tipo objeto


Hasta el momento, hemos usado valores literales y nombres de variables para pasar
argumentos a nuestros métodos. A esta forma de paso de argumentos se le conoce como llamada
por valor, dado que lo que en realidad estamos haciendo es almacenar el valor literal o copiar el
valor de nuestra variable en el argumento. Por tal motivo, los cambios hechos al parámetro dentro
del método no afectan en lo absoluto el valor original de nuestra variable.

Considere por ejemplo el siguiente código:

public class Test{


void method(int a, int b){
a *= 2;
b *=2;
}
}

public class TestTest{


public static void main(String args[]){
Test myTest = new Test();
int a = 10, b = 10;
System.out.println(“Antes: “ + a + “ “ + b);
myTest.method(a , b);
System.out.println(“Despues: “ + a + “ “ + b);
}
}

La salida del programa sería:

Antes: 10 10
Despues: 10 10

67
Lenguajes de programación Fundamentos de Java

Lo anterior comprueba entonces que los cambios que hagamos a las variables a y b dentro
del método method del objeto myTest no afectan en lo absoluto a las variables a y b del
método main, puesto que lo único que estamos haciendo es copiar el valor de las variables en los
argumentos del método; dichos parámetros se crean al invocar al método y se destruyen al salir
de él.

Con los objetos, sin embargo, la situación es completamente diferente, ya que el nombre de
un objeto es en realidad una referencia al mismo; por ello, cuando se pasa un objeto a un método
se dice que se está realizando un llamado por referencia. Como es de suponerse, los cambios que
realicemos en el objeto dentro del método, afectarán al objeto original, puesto que ahora el
argumento está haciendo referencia al mismo objeto.

Observe el siguiente código:


public class Test2{
int a, b;
Test2(int i, int j){
a = i;
b = j;
}
void method(Test2 ref){
ref.a *= 2;
ref.b *= 2;
}
}

public class Test2Test{


public static void main(String args[]){
Test2 myTest2 = new Test2(10,10);
System.out.println(“Antes: “ + myTest2.a + “ “ + myTest2.b);
myTest2.method(myTest2);
System.out.println(“Despues: “ + myTest2.a + “ “ + myTest2.b);
}
}

La salida del programa sería:

Antes: 10 10
Despues: 20 20

En este caso, las acciones efectuadas a los miembros del objeto referenciado por ref
dentro del método method de la clase Test2 afectan a los miembros del objeto referenciado
por myTest2 dado que en realidad se trata de dos referencias diferentes que apuntan al mismo
objeto y por lo tanto a los mismos miembros.

68
Lenguajes de programación Fundamentos de Java

Retorno de valores de tipo objeto


Un método puede devolver cualquier tipo de dato, objetos inclusive. Suponga el siguiente
caso:

public class Test3{


int a;
Test3(int i){
a = i;
}
Test3 method(){
Test3 temp = new Test3(a+10);
return temp;
}
}

public class Test3Test{


public static void main(String args[]){
Test3 myTest3_1 = new Test3(10);
Test3 myTest3_2;
myTest3_2 = myTest3_1.method();
System.out.println(“myTest3_2.a = “ + myTest3_2.a);
myTest3_2 = myTest3_2.method();
System.out.println(“myTest3_2.a = “ + myTest3_2.a);
}
}

Como puede apreciarse, cada vez que se invoca al método method, se crea un nuevo
objeto de tipo Test3 con un incremento de diez a su atributo a. El valor de retorno es la
referencia al objeto creado, la cual se almacena en myTest3_2 permitiendo que el objeto no sea
recogido por el recolector de basura.

Implementación de herencia
La palabra reservada extends indica al compilador que una clase hereda atributos y
métodos de una superclase; la sintaxis para aplicarla es la siguiente:
[modificador] class identificadordeclase extends identificadordesuperclase

En donde:

• identificadordeclase será el nombre de la clase que está heredando


atributos y métodos
• identificadordesuperclase será la clase de la cual se están heredando los
atributos y métodos.

Suponga que se cuenta con una superclase llamada Clothing que tiene la siguiente
forma:

69
Lenguajes de programación Fundamentos de Java

public class Clothing{


private int ID=0;
private String description = “-description required-“;
private double price = 0.0;
private int quantityInStock = 0;
public void calculateID(){
int uniqueID;
uniqueID = ((int)(Math.random()*10000)+1);
ID = uniqueID;
}
public int getID(){
return ID;
}
public void setDescription(String d){
description = d;
}
public String getDescription(){
return description;
}
public void setPrice(double p){
price = p;
}
public double getPrice(){
return price;
}
public void setQuantityIStock(int q){
quantityInStock = q;
}
public int getQuantityInStock(){
return quantityInStock;
}
}

Como puede observarse, la clase Clothing contiene atributos y métodos que pueden ser
empleados por cualquier artículo de ropa; sin embargo, cada artículo podrá requerir de atributos y
métodos específicos según su clase. Una camisa, por ejemplo, podría tener códigos de color
específicos, en comparación con un zapato o un cinturón. A continuación se muestra un ejemplo
de la clase Shirt extendiendo a la clase Clothing y heredando todo de ella:

public class Shirt extends Clothing{


private char colorCode = ‘U’;
public void displayShirtInformation(){
System.out.println(“Shirt ID: “ + getID());
System.out.println(“Shirt Description: “ + getDescription());
System.out.println(“Color Code: “ + colorCode);
System.out.println(“Shirt Price: “ + getPrice());
System.out.println(“Shirts in Stock: “ + getQuantityInStock());
}
}

70
Lenguajes de programación Fundamentos de Java

NOTA: Observe con detenimiento que los atributos ID, description, price y
quantityInStock de la clase Clothing son privados; aunque Shirt los hereda de
su superclase no puede accederlos directamente dado que no los puede ver. Es por ello que
requiere emplear los métodos públicos get para desplegarlos. Dependerá estrictamente de
nuestras necesidades definir si los atributos de una superclase podrán o no ser manipulados
directamente por sus subclases.

Contrario a como se estila en otros lenguajes de programación, en Java una subclase sólo
podrá heredar de una superclase a la vez; lo anterior nos obliga a crear siempre una jerarquía de
herencia en donde una superclase podrá tener muchas subclases pero una subclase sólo podrá
tener una superclase.

Java provee mecanismos para limitar la herencia de una clase mediante el empleo de dos
modificadores:

• final: El cual impide que una clase sea heredada. Cuando una clase se cataloga
como final, será imposible extenderla o subclasificarla.

• abstract: El cual impide que una clase sea instanciada. Las clases abstractas se
consideran clases incompletas que aún no implementan ciertos métodos, por lo que
no podrán utilizarse para crear objetos.

Legalmente, una clase deberá declararse como abstracta cuando al menos uno de sus
métodos sea abstracto. La declaración de métodos abstractos se explica con detenimiento más
adelante.

Ejercicio 12
• Diseñe otra clase de artículo de ropa que herede todos los atributos y métodos de la
clase Clothing. Inclúyale al menos un atributo y un método únicos.

• Diseñe una clase de prueba que verifique a la clase Shirt y a la clase recién
creada. Emplee el método calculateID() para asignarles identificadores únicos
a los objetos de prueba.

Sobreescritura de métodos y atributos


En una jerarquía de clases, puede darse el caso en que se necesite que un método de una
clase tenga el mismo nombre y parámetros que el de una superclase, pero que su implementación
sea diferente. Cuando esto sucede, se dice que el método de la subclase sobreescribe (overrides)
al método de la superclase.

Así, cuando se invoca al método dentro de un objeto de la subclase se estará llamando al


que se encuentra definido en ella, ocultándose al de la superclase. Tome en cuenta que los
llamados a los métodos deberán de ser idénticos para que se considere una sobrescritura; con un
argumento diferente se le considerará simplemente una sobrecarga.

71
Lenguajes de programación Fundamentos de Java

Ejemplo:

public class A{
public int i, j;
public void show(){
System.out.println(i + “ “ + j);
}
}

public class B extends A{


public int k;
public B(int a, int b, int c){
i=a;
j=b;
k=c;
}
public void show(){
System.out.println(i + “ “ + j + “ “ + k);
}
}

Lo mismo sucederá con los atributos: Cuando un atributo de una subclase se llame igual
que un atributo de su superclase, se considerará que este atributo se está sobrescribiendo. Por
ejemplo:

public class A{
int i;
}

public class B extends A{


int i;
void show(){
System.out.println(“i de la subclase: “ + i);
}
}

Aunque la sobrescritura de métodos es una de las características más potentes en la


herencia, habrá ocasiones en las que se desee evitarla. Para impedir que un método sea
sobreescrito, anteponga el modificador final a la declaración del método (similar a como se
hace con las clases).

Ejemplo:

class prueba{
final void finalMethod(){
System.out.println(“Este es un método final”);
}
}

72
Lenguajes de programación Fundamentos de Java

Métodos abstractos
Los métodos abstractos son aquellos que pertenecen a una determinada estructura de
herencia pero que requerirán de una implementación específica para cada subclase. Por esta razón
es que a los métodos abstractos se les conoce más comúnmente como métodos de
responsabilidad de subclase.

Para declarar un método abstracto se antepone la palabra clave abstract a la declaración


del método y no se especifica cuerpo en el mismo; por ejemplo:

abstract void calculaArea(int lado1, int lado2);

Dado que los métodos abstractos no tienen bloque principal, su implementación deberá
hacerse en cada una de las subclases que lo hereden. Tanto el nombre como los parámetros de los
métodos concretos de cada implementación deberán coincidir exactamente con lo estipulado por
el método abstracto.

Como se indicó con anterioridad, cualquier superclase con al menos un método abstracto se
considerará una clase abstracta y deberá ser declarada como tal. Recuerde además que una clase
abstracta no puede instanciarse.

Ejemplo:

public abstract class A{


abstract void callme();
void callmetoo(){
System.out.println(“Este metodo es concreto!!!”);
}
}

public class B extends A{


void callme(){
System.out.println(“Este metodo tambien!!!”);
}
}

Los constructores y la herencia


Cuando se implementa herencia, habrá ciertas consideraciones que deberemos de tomar con
los constructores (consideraciones que hasta ahora hemos obviado por mantener la simplicidad).
Antes que nada, es preciso aclarar que los constructores en la herencia se aplican de manera
descendente, esto es de la superclase a la subclase. Lo anterior es de vital importancia sobre todo
para los constructores por default de las superclases.

Suponga que contamos con las siguientes clases:

public abstract class SuperClass{


public int a=1, b=2;
public abstract void displayData();
}
73
Lenguajes de programación Fundamentos de Java

public class SubClass extends SuperClass{


public int c=3;
public void displayData(){
System.out.println(a + “ “ + b + “ “ + c);
}
}

En este caso, si se instanciara un objeto de la clase SubClass con el constructor por


default, primero se ejecutaría al constructor por default de la clase SuperClass (en donde se
crearían los atributos a y b con sus respectivos valores de inicio) y después el constructor por
default de la clase SubClass (en donde se crearía el atributo c con su respectivo valor de
inicio). Por ejemplo:

public class ClassesTest{


public static void main(String args[]){
SubClass mySub = new SubClass();
mySub.displayData();
}
}

Ahora supongamos que se desea añadir un constructor a la subclase:

public abstract class SuperClass{


public int a=1, b=2;
public abstract void displayData();
}

public class SubClass extends SuperClass{


public int c;
public SubClass(int value3){
c=value3;
}
public void displayData(){
System.out.println(a + “ “ + b + “ “ + c);
}
}

En este caso, si se instanciara un objeto de la clase SubClass, habría que enviar el


parámetro value3 que el constructor de SubClass necesita. Por ejemplo:

public class ClassesTest{


public static void main(String args[]){
SubClass mySub = new SubClass(3);
mySub.displayData();
}
}

74
Lenguajes de programación Fundamentos de Java

Siendo así, ¿Cuál es el constructor que se ejecuta en la superclase? La respuesta es obvia:


Se estará llamando al constructor por default. Ahora supongamos un escenario más completo en
donde tanto la superclase como la subclase poseen un constructor:

public abstract class SuperClass{


public int a, b;
public SuperClass(int value1, int value2){
a=value1;
b=value2;
}
public abstract void displayData();
}

public class SubClass extends SuperClass{


public int c;
public SubClass(int value1, int value2, int value3){
super(value1, value2);
c=value3;
}
public void displayData(){
System.out.println(a + " " + b + " " + c);
}
}

En este caso la situación cambia aún más. Dado que en este caso la superclase posee un
constructor, no existe un constructor por default, lo que nos obliga a invocar el constructor de la
superclase de manera manual mediante el enunciado que se encuentra en la línea 4 del código de
la clase SubClass.

super es una palabra clave que tiene dos formas generales. La primera nos permite
invocar el método constructor de nuestra superclase y tiene la siguiente forma:

super(argumentos);

Por supuesto, los argumentos deberán coincidir exactamente con los valores que los
constructores esperan (en caso de que se trate de más de uno). La invocación al constructor de la
superclase deberá ser obligatoriamente el primer enunciado en el constructor de la subclase y si
no se encuentra, se añade por omisión la invocación al constructor default de la superclase con la
sentencia super().

Por último, tomemos en cuenta un último caso:

public abstract class SuperClass{


public int a, b;
public SuperClass(int value1, int value2){
a=value1;
b=value2;
}
public abstract void displayData();
}
75
Lenguajes de programación Fundamentos de Java

public class SubClass extends SuperClass{


public int c=3;
public void displayData(){
System.out.println(a + " " + b + " " + c);
}
}

Aquí, la subclase no posee un constructor pero la superclase si; intentar compilar la


subclase generará un error como el siguiente:
SuperClass(int,int) in SuperClass cannot be applied to ()

Aquí el compilador de Java nos está advirtiendo de la falta del constructor por default en la
superclase; ante esta situación tenemos dos opciones: la primera por obvias razones sería incluir
el constructor por omisión en la superclase como en el siguiente ejemplo:

public abstract class SuperClass{


public int a, b;
public SuperClass(){
a=1;
b=2;
}
public SuperClass(int value1, int value2){
a=value1;
b=value2;
}
public abstract void displayData();
}

La segunda opción requeriría diseñar un constructor en la subclase en donde


específicamente se llame al constructor de la superclase:

public class SubClass extends SuperClass{


public int c=3;
public SubClass(int value1, int value2, int value3){
super(value1, value2);
c=value3;
}
public void displayData(){
System.out.println(a + " " + b + " " + c);
}
}

Puesto que los constructores pueden sobrecargarse, es posible utilizar a super() con
cualquier serie de argumentos, según se especifique en la superclase.

76
Lenguajes de programación Fundamentos de Java

Acceso a miembros de una superclase


La palabra clave super tiene una segunda utilización que nos permite acceder a los
miembros de la superclase con la siguiente sintaxis:

super.miembro

Donde miembro podrá ser ya sea un nombre de método o un atributo. Esta forma de super
es particularmente útil cuando los miembros de la subclase ocultan a los de la superclase debido a
la sobrescritura.

Ejemplo:

public class A{
int i;
}
public class B extends A{
int i;
B(int a, int b){
super.i = a;
i=b;
}
void show(){
System.out.println(“i de la superclase: “ + super.i);
System.out.println(“i de la subclase: “ + i);
}
}

Importación de paquetes de clases


Como programadores principiantes de Java, una de nuestras primeras tareas es
familiarizarnos con la librería de clases existentes en el API. Estas clases se encuentran agrupadas
en paquetes en base a la funcionalidad de cada una y se instalan junto con el runtime enviroment.

Por ejemplo, todas las clases básicas de Java se encuentran incluidas en el paquete
java.lang (String, Math, Integer, etc.); de hecho hemos empleado varias de estas
clases a lo largo del manual. La razón por la cual hasta el momento no hemos tenido que hacer
ninguna referencia a este paquete, es porque todas las clases contenidas en java.lang se
consideran de manera implícita en nuestro código.

Sin embargo, cuando se desea emplear las clases provistas por los otros paquetes del API,
será necesario incluir un enunciado de importación en donde se detalle el nombre calificado
completo del paquete. A continuación, se mencionan los paquetes estándares de Java:

• java.awt que contiene clases pertenecientes al abstract windowing toolkit,


empleadas en la construcción y manejo de la interfase gráfica de usuario (GUI) para
nuestras aplicaciones.
• java.applet que contiene clases para definir el comportamiento específico de
un applet.
77
Lenguajes de programación Fundamentos de Java

• java.net que contiene clases para ejecutar operaciones relacionadas con redes de
computadoras (como sockets y URLs).
• java.io que contiene clases para manejar archivos de disco y provee
herramientas para realizar tareas de escritura y lectura.
• java.util que contiene múltiples utilerías para realizar diversas tareas
(generación de números aleatorios, definir propiedades del sistema, etc.).

Para importar una clase se deberá colocar antes del nombre de la clase en donde queramos
realizar la importación un enunciado con la siguiente sintaxis:

import nombredelpaquete.nombredelaclase;

O también:

import nombredelpaquete.*;

Por ejemplo:

import java.util.Date;
import java.io.*;

Con el primer ejemplo se importa únicamente la clase específica que se desea emplear (para
este caso, la clase Date dentro del paquete java.util); este método es el preferido por los
programadores experimentados dado que reduce considerablemente el tiempo de compilación.

Con el segundo ejemplo se importan todas las clases del paquete java.io. Usar esta
forma sin embargo, tiene un inconveniente adicional: Cuando dos paquetes importados contienen
una clase con el mismo nombre, no se producirá ningún error en tiempo de compilación a menos
que hagamos referencia a la clase repetida. En este caso la única solución posible es importarla de
manera específica.

Una vez importado, podemos hacer referencia a cualquier clase del paquete, a sus atributos
o a sus métodos, como si estuvieran declarados en nuestro código. Por ejemplo:

import java.util.*;

public class ImportTest{

public static void main(String args[]){


Date myDate = new Date();
System.out.println("Today is " + myDate);
}
}

En este caso, se importan todas las clases del paquete java.util. En particular, se
instancia un objeto de la clase Date empleando el constructor por default y referenciándolo con
el nombre myDate.

78
Lenguajes de programación Fundamentos de Java

NOTA IMPORTANTE: Cabe destacar que en ningún caso estamos agregando código a
nuestro programa; lo único que import nos permite es hacer referencia a clases y métodos
empleando solamente el nombre de la clase y el nombre del método (esto es, ahorrándonos
el trabajo de escribir el nombre calificado completo). Por lo anterior, es una sentencia que
provee únicamente comodidad al programador, pero en ningún caso es requerida.

Así, si no deseamos importar una clase, podemos simplemente escribir su nombre


calificado completo al momento que se requiera; por ejemplo, para el ejemplo anterior
tendríamos:

public class DateTest{

public static void main(String args[]){


java.util.Date myDate = new java.util.Date();
System.out.println("Today is " + myDate);
}
}

Para una definición completa del API de Java, visite la siguiente liga:

https://docs.oracle.com/javase/7/docs/api/

Conceptos avanzados de encapsulamiento


Hasta el momento se han empleado los modificadores public y private para
encapsular atributos y métodos de una clase. Ahora que se conocen los conceptos elementales de
la herencia y la creación paquetes, es momento de analizar dos niveles de control de acceso más:
El que se obtiene con el modificador protected y el que se obtiene por default.

Sabemos que un miembro public podrá ser accedido por cualquier objeto de nuestro
programa; esto se cumplirá aún cuando el objeto que desee hacerlo se encuentre fuera del paquete
de clases en donde el miembro reside. Un miembro private, por el contrario, sólo podrá ser
accedido por objetos de su misma clase.

Ahora bien, cuando no se especifica ningún modificador de acceso, se considera entonces


que por default el miembro será público únicamente dentro del paquete de clases al que
pertenece. En otras palabras, será en esencia public aunque restringido a su paquete.

El cuarto nivel de acceso está definido por el modificador protected. Un miembro


protegido será aquel que podrá ser accedido por cualquier clase de su paquete, o por alguna de
sus subclases que estén fuera del mismo. Para mayor claridad, observe la siguiente tabla de
referencia:

Acceso private public protected default


Misma clase Si Si Si Si
Subclase del mismo paquete No Si Si Si
No subclase del mismo paquete No Si Si Si
Subclase de otro paquete No Si Si No
No subclase de otro paquete No Si No No

79
Lenguajes de programación Fundamentos de Java

Protección de acceso para clases


Hasta el momento todas las clases que hemos construido están precedidas por el
modificador de acceso public. Como podría suponerse, una clase pública es aquella que podrá
ser accedida desde cualquier clase de nuestro programa esté o no en el mismo paquete.

Existe tan sólo un nivel más de acceso para clases: el acceso por default. Al igual que
sucede con los miembros, una clase con acceso por default, podrá ser accedida únicamente por
las clases que se encuentran en su mismo paquete.

Como podrá suponerse, este nivel de acceso se obtiene cuando no incluimos el modificador
public antes de la palabra clave class en el encabezado de la clase.

80

S-ar putea să vă placă și