Luis Fernando
García
Llinás
ientada
a objetos en Java
¨
de la
para la codificación
(especialmente usando el lenguaje C),
y que, además, deseen aprender acerca
del desarrollo de aplicacione
s orientadas
a objetos en Java
®
, sin neces
idad de leer
la abundante y extensa literatura
que,
por lo general, contienen
los manuales
y guías de usuarios. Precisamente, la
síntesis y la precisión de la información,
es uno de los atractivos de este texto
que
compila en 260 páginas los contenidos
básicos para un curso de programación
orientada a objetos en Java
®
.
Programaci—
n orientada a objetos en Java
¨
Luis Fernando
García
Llinás
Luis F
ernando
García
Llinás
Aplicar el programa orientado
a objetos
Codificar interfaces y gráficas
de usuario sencillas y
atractivas
Diseñar a partir de teorías
básicas
Ejemplos y casos prácticos
Ingeniero de Sistemas, Universidad del Norte (Barranquilla). Magíster
en Ingeniería de Sistemas y Computación con énfasis en Ingeniería de
Información, Universidad de los Andes (Bogotá). Desde el 2005 está
vinculado a la Universidad del Norte como docente del Departamento de
Ingeniería de Sistemas y Computación, y actualmente hace parte del grupo de
investigación “Redes de Computadores e Ingeniería de Software - GReCIS”
a objetos en Java®
LUIS FERNANDO GARCÍA LLINÁS
© Ediciones Uninorte, 2010 © Ediciones de la U, 2010
© Luis Fernando García Llinás, 2010
Coordinación editorial Zoila Sotomayor O. Diseño y diagramación Nilson Ordoñez Diseño de portada Álvaro Bernal Corrección de textos Mercedes Castilla 258 p. ; 16 x 24 cm. ISBN 978-958-741-062-4
1. Java (Lenguaje de programación de computadores). 2. Programación orientada a objetos (Computadores). I. Tít.
(005.117 G216 Ed. 22) (CO-BrUNB)
www.uninorte.edu.co
Km 5 vía a Puerto Colombia, A.A. 1569, Barranquilla (Colombia)
http://edicionesdelau.com/ Calle 24A n.° 43-22 Bogotá (Colombia)
Impreso y hecho en Colombia X-press Proceso
Bogotá
1. TÓPICOS BÁSICOS ...1
1.1. Sobre el paradigma de programación estructurada o procedimental ... 1
1.2. Objeto ... 5
1.3. Clase ... 6
1.4. Atributo ... 7
1.5. Instanciación ... 12
1.6. Método ... 22
Método constructor, 29
1.7. Encapsulamiento ... 39
1.8. Atributos finales ... 52
1.9. Atributos y métodos estáticos ... 54
1.10. Herencia ... 59
1.11. Métodos y clases abstractas ... 96
1.12. Casting ... 105
1.13. Polimorfismo ... 107
1.14. Métodos y clases finales ... 114
1.15. Herencia simple y múltiple ... 123
2. TÓPICOS AVANZADOS ...135
2.1. Colecciones ... 136
Listas, 144. Conjuntos, 154. Mapas, 157. Genéricos, 159.
2.2. Manejo de excepciones ... 171
Generación y lanzamiento de excepciones, 174. Captura de excepciones, 177. Definición de excepciones personales, 179.
3. CREACIÓN DE INTERFACES GRÁFICAS DE USUARIO ...199
3.1. Componentes gráficos ... 201
3.2. Layouts ... 208
FlowLayout, 209. GridLayout, 211. BorderLayout, 213.
3.3. Bordes ... 221
3.4. Manejo de eventos ... 226
El aprendizaje del paradigma de la programación orientada a objetos presenta considerables
problemas a quienes están acostumbrados a trabajar bajo el paradigma estructurado, sobre
todo cuando estos tienen varios años de experiencia. Quizás el cambio de mentalidad que se
requiere o la gran cantidad de nociones que surgen acentúan esta situación.
Inicialmente la programación orientada a objetos puede llegar a verse como un paradigma
complejo o complicado, aun cuando en realidad lo que busca es organizar de mejor manera
el código de una aplicación desarrollada con el enfoque procedimental. Esto con el fin de
facilitar su entendimiento y, por ende, si es necesario, su modificación.
La metodología que se emplea en este libro para introducir los tópicos consiste en plantear
un problema sumamente sencillo, luego entrar a resolverlo empleando la programación
estructurada y después de forma progresiva ir desarrollando la aplicación empleando la
programación orientada a objetos. Durante este proceso se van introduciendo cada una de
las nociones más importantes para adicionarlas al desarrollo de la solución (en algunos
casos se presentan más ejemplos para lograr un mejor entendimiento de cada uno de los
tópicos). Es realmente importante analizar cómo se va configurando la solución orientada a
objetos y compararla con la solución estructurada.
O
bjetivOs•
Ser conciso. Uno de los principales objetivos de este libro era escribirlo de manera
sumamente concisa, es decir que no excediera las 250 páginas. Por eso únicamente
se tocan los temas básicos de la codificación de aplicaciones orientadas a objetos
empleando a Java como lenguaje de programación. Por consiguiente, se excluyen
temas como el trabajo con redes, la interacción con bases de datos, el desarrollo de
aplicaciones para la web, etc.
•
Servir como punto de partida. En ningún momento este libro pretende reemplazar
textos más robustos y completos que tratan estos y muchos otros tópicos. Más bien se
presenta como un punto de partida para los programadores y un eficaz complemento
de otros textos, pues en la medida en que se comprenda la totalidad de esta obra será
¿A
quiénvAdirigidOestelibrO?
Este libro fue ideado como texto guía para un curso de programación orientada a objetos
donde llegan estudiantes con nociones básicas de programación y conocimiento de la
teoría de algunas estructuras de datos. Por consiguiente, la obra se dirige especialmente a
programadores que emplean el paradigma procedimental como principal aproximación a la
codificación de soluciones (sobre todo empleando C como lenguaje de programación), y que
desean aprender las nociones del desarrollo de aplicaciones orientadas a objetos empleando
Java.
¿q
uéserequiereAntesdeleerlO?
•
Descargar e instalar la máquina virtual de Java que puede ser obtenida de forma
gratuita de la web oficial de Sun (http://java.sun.com/javase/downloads/index.
jsp)
•
Leer del Tutorial oficial de Java (http://java.sun.com/tutorial) los apartados que
llevan por títulos “About the Java Technology” (http://java.sun.com/docs/books/
tutorial/getStarted/intro/definition.htm) y “Hello World! For Microsoft Windows”
(http://java.sun.com/docs/books/tutorial/getStarted/cupojava/win32.html).
•
Descargar e instalar un entorno de desarrollo (IDE) para Java. Esto facilitará en gran
medida la codificación de los ejemplos. Existen muchos entornos gratuitos entre
los que sobresalen: Netbeans (http://www.netbeans.org/), JDeveloper (http://
www.oracle.com/technology/products/jdev/index.html), y Eclipse (http://www.
eclipse.org/).
O
rgAnizAciónEl libro está organizado en tres capítulos.
•
En el primer capítulo se presentan los tópicos básicos de la programación orientada a
objetos. También se analizan las nociones necesarias para la creación de aplicaciones
orientadas a objetos sumamente básicas.
•
En el segundo capítulo se presentan unos tópicos un poco más avanzados de la
teoría de objetos. Aquí las nociones analizadas posibilitan la creación de aplicaciones
mas robustas y completas orientadas a objetos.
•
En el tercer capítulo se presentan las teorías básicas para diseñar y codificar interfaces
gráficas de usuario sencillas y moderadamente atractivas.
Adicionalmente, en todos estos capítulos se incluye un conjunto de apartados donde se
presentan explicaciones más amplias, aclaraciones pertinentes o simplemente ejemplos más
completos de los tópicos que se abordan.
r
etrOAlimentAciónCualquier comentario, observación, sugerencia o recomendación que se desee efectuar
sobre la presente obra será bien recibida en las siguientes direcciones de correo electrónico:
1.
TÓPICOS BÁSICOS
1.1. s
Obre
el
pArAdigmA
de
prOgrAmAción
estructurAdA
O
prOcedimentAl
Dentro del mundo del desarrollo de aplicaciones bajo el paradigma estructurado (también
conocido como programación procedimental o tradicional) la idea general consiste en
especificar el conjunto de instrucciones que brindan solución a un problema específico; este
conjunto de instrucciones se definen al interior de las marcas de inicio y fin del bloque
de codificación principal del algoritmo. Durante el aprendizaje de este paradigma de
programación las recomendaciones iniciales son: entender el problema, definir las variables
globales que harán parte de la solución, identificar aquellas secciones de código que
conviene incluirlas dentro de funciones o subrutinas para futura reutilización, definir datos
de entrada y de salida, entre otras.
Suponga que ha sido encargado del desarrollo de la aplicación de software a una compañía
que la desea para efectuar el cálculo mensual de su nómina. Esta compañía contrata
empleados a quienes les paga dependiendo del número de horas trabajadas y del valor por
hora convenido previamente con cada uno. Como información básica de cada empleado
debe registrarse el número de la cédula, su nombre y su apellido.
Aclaraciones:
•
Tanto el valor del número de horas trabajadas por cada empleado como el valor de
•
Para efectos de mantener la simplicidad del ejemplo no se contemplan acciones para
manejar la persistencia
1de los datos.
A continuación una posible solución al problema planteado empleando el paradigma
estructurado.
Inicio
Entero: numeroEmpleados, i
Caracteres: cedulas[50], apellidos[50], nombres[50] Real: horasTrabajadas[50], sueldoXHora[50]
Caracteres: cedula, apellido, nombre Real: horas, sueldo
Real: total <- 0
Esc ‘Digite número de empleados: ‘ Lea numeroEmpleados
Para i=0,numeroEmpleados-1,1
Esc ‘Digite la cédula del empleado: ‘ Lea cedula
Esc ‘Digite el apellido del empleado: ‘ Lea apellido
Esc ‘Digite el nombre del empleado: ‘ Lea nombre
Esc ‘Digite número de horas trabajadas del empleado: ‘ Lea horas
Esc ‘Digite valor de sueldo por hora del empleado:’ Lea sueldo cedulas[i] <- cedula apellidos[i] <- apellido nombres[i] <- nombre horasTrabajadas[i] <- horas sueldoXHora[i] <- sueldo Fin-Para
Para i=0, numeroEmpleados-1, 1
total <- total + horasTrabajadas[i] * sueldoXHora[i] Fin-Para
Esc ‘La nómina total es: ‘ + total Fin
Entender el anterior seudocódigo no debe presentar mayores problemas para cualquier
programador. Sin embargo, es conveniente realizar las siguientes aclaraciones y comentarios:
•
Aunque se puede condensar el código incluyendo las instrucciones del segundo
‘Para’ dentro del primero, de manera intencional se ha dejado así intencionalmente
para delimitar funcionalmente cada bloque de código.
•
En el algoritmo se captura información, como la cédula, el nombre y el apellido,
que no se utiliza; sin embargo esta información se mantiene porque posteriormente
puede ser útil para ampliar la funcionalidad de la aplicación.
•
Como la intención es que sea un ejemplo didáctico, inicialmente el algoritmo no
contempla validaciones como impedir el doble ingreso de un mismo número de
cédula.
C
odifiCaCiónenJ
avadelalgoritmoparaelCálCulodelanómina empleandoprogramaCiónestruCturadaSe presenta a continuación la codificación del ejemplo anterior aunque apenas se estén dando
los primeros pasos en el aprendizaje del lenguaje de programación Java. Esto porque es más
sencillo aprender a través de ejemplos y del establecimiento de analogías, asociaciones y
comparaciones.
import java.io.BufferedReader; import java.io.InputStreamReader; class Nomina {
public static void main(String[] args) throws Exception {
int numeroEmpleados;
String[] cedulas = new String[50]; String[] apellidos = new String[50]; String[] nombres = new String[50];
double[] horasTrabajadas = new double[50]; double[] sueldoXHora = new double[50]; String cedula, apellido, nombre; double horas, sueldo;
double total = 0;
BufferedReader br = new BufferedReader( new InputStreamReader( System.in ) );
System.out.print(“Digite numero de empleados: “);
numeroEmpleados = Integer.valueOf(br.readLine()).intValue(); for(int i=0;i<numeroEmpleados;i++){
System.out.print(“\nDigite la cedula del empleado: “ ); cedula = br.readLine();
System.out.print(“Digite el apellido del empleado: “ ); apellido = br.readLine();
System.out.print(“Digite el nombre del empleado: “ ); nombre = br. ();
System.out.print(“Digite num de horas trabajadas del empleado: “); horas = Double.valueOf(br.readLine()).doubleValue();
sueldo = Double.valueOf(br.readLine()).doubleValue(); cedulas[i] = cedula; apellidos[i] = apellido; nombres[i] = nombre; horasTrabajadas[i] = horas; sueldoXHora[i] = sueldo; }
for (int i=0;i<numeroEmpleados;i++)
total = total + sueldoXHora[i]*horasTrabajadas[i];
System.out.println(“\nLa nómina total es: “+ total); }
}
Del código anterior vale la pena resaltar algunos puntos:
•
Las dos primeras líneas donde aparecen instrucciones
importsirven para importar
componentes necesarios para la lectura de datos por pantalla.
•
La instrucción
public static void main(String args[])corresponde a la
definición del método principal.
•
El tipo de dato en Java
doublese corresponde con el tipo
Realen el seudocódigo; el
tipo de dato en Java
Stringse corresponde con el tipo
Caracteresen seudocódigo; y
el tipo de dato
inten Java se corresponde con el tipo de dato
Enteroen seudocódigo.
•
La lectura de información por pantalla requiere la configuración de un componente de
tipo
BufferedReader.
•
La instrucción que permite leer información de la pantalla (lo que en seudocódigo se
escribe como la instrucción
Lea, o en C la instrucción
cin) corresponde a la función
readLine()
. Esta función siempre devuelve lo que digita el usuario como una cadena
de caracteres; si se desea trabajar con un tipo de dato distinto, deben realizarse
conversiones adicionales.
•
La instrucción que permite escribir información en pantalla (lo que en seudocódigo se
escribe como la instrucción
Esc, o en C la instrucción
cout) corresponde a la función
System.out.println()
.
El carácter de escape ‘\n’ corresponde al retorno de carro y
nueva línea
.
•
El segundo bloque ‘for’ no define llave de apertura(
{) y de cierre(
}), debido a que solo
posee una línea de código.
•
La concatenación de un número a una cadena de caracteres se realiza con el simple uso
•
Es conveniente identificar cómo es el manejo de los arrays en Java y cómo se realiza la
conversión entre tipos de datos.
•
En Java, los índices de los arrays comienzan en el valor 0; es por ello que los contadores
de los bloques de código ‘Para’ se inicializan en dicho valor, y llegan hasta el valor
menos 1.
1.2. O
bjetO
Dentro del paradigma de desarrollo de aplicaciones orientadas a objetos cambia el enfoque
de la solución. Lo importante para el paradigma procedimental o estructurado es el bloque
de código principal (instrucciones dentro de la marca de inicio y fin). Para el paradigma
orientado a objetos lo principal es entender y modelar el problema, y luego sí definir el
bloque de código principal que empleando el modelo definido brinde solución al problema
específico.
La programación orientada a objetos requiere inicialmente identificar y modelar cada uno de
los entes que hace parte del problema. Facilita la comprensión del tema hacerse una imagen
mental de una posible situación; por ejemplo, para el caso del cálculo de la nómina suponga
que la empresa cuenta únicamente con tres empleados cuya información se muestra en el
siguiente gráfico.
Para un programador con poca experiencia en la orientación a objetos es moderadamente
sencillo identificar que para el caso anterior los entes que toman parte del problema
empleado en la empresa, aparecerá otro objeto que seguramente poseerá las características
descriptivas de cédula, apellido, nombre, sueldo por hora y horas trabajadas, y demás
valores para dichas características.
Es conveniente presentar una definición para la noción de objeto.
Definición de objeto
Un objeto es un concepto, abstracción o cosa con límites bien definidos y con
significado dentro del problema.
Como se puede apreciar la definición de objeto es realmente amplia pues en realidad
cualquier cosa puede ser un objeto. De allí que puedan existir objetos que representen cosas
concretas (como automóviles, casas, libros, etc.) y objetos que representan cosas abstractas
(como pensamientos, ideas, etc.).
1.3. c
lAse
Para traducirlo a algún lenguaje de programación se requiere una estructura de datos a fin
de almacenar la información de cada uno de los objetos del problema. Sin embargo, no tiene
sentido definir una estructura de datos independiente para cada uno de los posibles objetos
de tipo empleado (como, por ejemplo, EstructuraTrabajor1, EstructuraTrabajador2, etc.), es
más conveniente definir una única estructura de datos que pueda servir para almacenar la
información de cualquier objeto del mismo tipo. La siguiente tabla muestra la información
que debe permitir registrar esta estructura genérica y su correspondiente tipo de dato.
Campo
Tipo dato
Descripción
cédula
Caracteres
En este campo se registra el número de la cédula de
ciudadanía de un empleado.
apellido
Caracteres
En este campo se registra la cadena de caracteres que
corresponde al apellido de un empleado.
nombre
Caracteres
En este campo se registra la cadena de caracteres que
corresponde al nombre de un empleado.
horasTrabajadas
Real
En este campo se registra el número de horas trabajadas
por un empleado; por ejemplo, el valor de 1.5 indica que
el empleado ha trabajado una hora y media (90 minutos).
sueldoXHora
Real
En este campo se registra el valor que debe ser pagado a
Definición de clase
Una clase describe a un conjunto de objetos que comparten una estructura y un
comportamiento común.
Una clase es un molde o plantilla que indica cómo será un objeto de dicha clase. En el área de
la construcción, una clase podría ser el plano de una casa que indica la estructura que debe
tener cada una de las casas, y los objetos son la materialización de las casas construidas a
partir de dicho plano. Es por ello que se define a un objeto como una instancia de una clase.
Para definir una clase en Java se utiliza la palabra reservada ‘
class’
. La sintaxis de dicha
instrucción requiere además de la especificación de un nombre para la clase. La comunidad
de programadores de Java maneja una convención de nombramiento para las clases (más
que una regla es una simple sugerencia); dicha convención establece que el nombre de la
clase debe escribirse todo en minúsculas a excepción de la primera letra del nombre de la
clase. Si para establecer el nombre de la clase se requieren varias palabras se deben unir las
letras de todas las palabras y la primera letra de cada palabra debe estar en mayúscula (por
ejemplo,
EmpleadoDeEmpresa). A continuación se presenta el código que permite definir en
Java la clase para describir a objetos tipo empleado.
class Empleado{ String cedula; String apellido; String nombre; double horasTrabajadas; double sueldoXHora; }
Cada uno de elementos incluidos dentro de una clase recibe el nombre de atributos.
1.4. A
tributO
Definición de atributo
Un atributo es una propiedad que ayuda a describir un objeto.
Es conveniente tener en cuenta lo siguiente:
•
Hasta el momento y con las definiciones dadas, dos objetos distintos, pero de la
•
El orden de definición de los atributos es irrelevante.
•
El concepto de atributo está estrechamente ligado al concepto de variable; en
realidad todo atributo es un tipo de variable, sin embargo, no toda variable que
pueda definirse en un programa en Java es un atributo.
Note que en Java para definir una variable se requiere, además de un nombre
2, su tipo de
dato. La comunidad de programadores de Java maneja una convención de nombramiento
para los atributos (más que una regla es una sugerencia), con la cual establece que el nombre
del atributo debe escribirse en letras minúsculas. Si el nombre del atributo está compuesto
por varias palabras, se unen las letras de todas las palabras y se colocan en mayúsculas las
primeras letras de cada palabra a excepción de la primera letra del nombre del atributo (por
ejemplo, horasTrabajadas).
Básicamente los tipos de datos para los atributos (y variables) pueden ser de dos clases:
•
Tipo de dato primitivo
3. Corresponde a un tipo de dato predefinido por el lenguaje.
Cuando se define un atributo (o variable) de este tipo, entonces hay que separar un
espacio en memoria para guardar su valor. Los ocho tipos de datos primitivos son:
byte
,
short,
int,
long,
float,
double,
booleany
char. Se hace relativamente sencillo
inferir el propósito de cada uno de ellos. Es conveniente resaltar que el tipo de dato
char
corresponde a un solo carácter, lo que significa que no existe un tipo de dato
primitivo en Java para una cadena de caracteres. La buena noticia es que existe
String
que no corresponde a un tipo de dato primitivo, sino a un tipo de dato de
referencia.
•
Tipo de dato de referencia. Corresponde a un objeto de una clase (no obligatoriamente
distinta a la definida). En este punto es donde radica gran parte de la importancia,
flexibilidad y reutilización del paradigma de orientación a objetos; porque cuando
se definen atributos cuyo tipo sea una clase, se amplía el espectro de posibilidades.
Cuando se define un atributo de este tipo no se separa espacio en memoria para
un nuevo objeto, sino que se define una referencia que apuntará a un espacio de
memoria con la estructura definida en la clase.
2 Detalles sobre la convención de nombramiento de variables pueden ser consultados en la siguiente referencia: http:// java.sun.com/docs/books/tutorial/java/nutsandbolts/variables.html
3 Mayor información sobre cada uno de estos tipos de datos puede ser consultada en la siguiente referencia: http://java. sun.com/docs/books/tutorial/java/nutsandbolts/datatypes.html
s
obrelaComposiCióndeobJetosUno de los pilares fundamentales de la programación orientada a objetos corresponde a la
reutilización. Aquí la idea fundamental no es reinventar la rueda cada vez que se necesite,
sino poder reutilizar la que ya esta inventada.
La siguiente podría ser una buena definición para la clase Casa.
class Casa{ String colorTecho; String tipoTecho; double largoTecho; double anchoTecho; double altoTecho; String colorParedes; String tipoParedes; int numeroDeVentanas; }
En la anterior definición el atributo
tipoTechohace referencia al material con que está
construido el techo de la casa, que puede tomar los valores de: paja, cinc, teja, etc.; similarmente,
el atributo
tipoParedeshace referencia al material que compone las paredes de la casa, por
ejemplo: ladrillo, madera, barro, etc. El resto de la anterior definición de la clase
Casano es
demasiado compleja y la mayoría de los atributos se entienden fácilmente.
Una definición alternativa para modelar una casa podría ser la siguiente:
class Techo{ String color; String tipo; int largo; int ancho; int alto; }
class Pared{ String tipo; String color; } class Casa{ Techo elTecho; Pared lasParedes; int numeroDeVentanas; }
Note que en este caso para lograr la definición de la clase
Casaha sido necesaria la previa
definición de las clases
Techoy
Pared.
Adicionalmente cabe resaltar que en la clase
Casaexiste un atributo de tipo de referencia de clase
Techocuyo nombre es
elTecho, y, además,
existe un atributo de tipo de referencia de la clase
Paredde nombre
lasParedes.
¿Cuál de las dos definiciones es la mejor? Depende del problema. La primera definición de la
clase
Casaes simple y sencilla de trabajar, pero no muy reutilizable. La segunda es un poco
más compleja, aunque más reutilizable (imagine que se necesita la clase
Edificio, para la cual
s
obreladefiniCiónymanipulaCióndelostiposdedatosprimitivosylosdereferenCiaEs realmente importante tener claridad sobre los tipos de datos de los atributos pues Java trata
de forma muy distinta a cada uno de ellos.
Imagínese un objeto de la clase
Casadonde solo se utilicen datos primitivos (la primera
definición que aparece en el apartado sobre la composición de objetos). Java reservaría espacio
en memoria de la siguiente manera (el texto en la parte superior del rectángulo es simplemente
para denotar la clase del objeto)
:
: Casa
colorTecho = Negro
poTecho = teja
largoTecho = 15
anchoTecho = 10
altoTecho = 2.5
colorParedes = Rojo
poParedes = ladrillo
numeroDeVentanas = 3
Para la segunda definición de la clase
Casa(en el ejemplo donde se usa composición en el
apartado sobre la composición de objetos), el mismo ejemplo anterior tendría la siguiente
representación:
Como se mencionó previamente para los atributos cuyo tipo de dato son de referencia no se
separan espacios en memoria, es decir, para atributos de tipo
Pared,
Techoy
String.
En vez
de eso, se definen apuntadores a referencias de objetos de dichos tipos.
Simplemente por facilidad y para no complejizar innecesariamente las gráficas, a lo largo de
este libro se obviará la definición de objetos para los tipos de datos de referencia
String,
es
Simplemente para hacer claridad en este último punto, la estructura en memoria para el objeto
de la clase
Casanunca es como se muestra en el siguiente gráfico.
La definición de clase hace referencia a dos puntos: primero, la estructura y segundo, el
comportamiento. La definición de la estructura de los objetos de una clase se consigue a
través del establecimiento de sus atributos.
1.5. i
nstAnciAción
Es conveniente introducir en estos momentos la instrucción que posibilita la instanciación
de una clase, de tal manera que se pueda crear un nuevo objeto para el almacenamiento
de valores en sus atributos. En Java, para crear un nuevo objeto es necesario utilizar el
comando
new, seguido del nombre de la clase que se desea instanciar
4; entonces para crear
un nuevo empleado se utiliza la siguiente instrucción
new Empleado();
Con esta instrucción Java separa un espacio en memoria con la estructura definida para
la clase
Empleado(es decir, debe haber un espacio
cedulapara almacenar la cédula del
empleado, un espacio
apellidopara almacenar el apellido del empleado, etc.).
La pregunta que debe surgir en estos momentos es, si con la instrucción
new Empleado()se separa espacio en memoria para crear un objeto de la clase
Empleado, ¿con qué valores
inician los atributos de este nuevo objeto? La respuesta, Java inicializa los valores de los
atributos con valores por defecto de la siguiente manera:
•
Para atributos cuyo tipo sea de referencia, el valor por defecto es
null;es decir, la
referencia no apunta a ningún objeto.
•
Para atributos cuyo tipo sea un tipo de dato primitivo, el valor depende del tipo de
dato: si es un valor numérico (
byte,
short,
int,
long,
floaty
double), su valor inicial
es 0; para tipo
boolean,el valor inicial es falso (
false), y, para tipo
char,el valor
inicial es ‘\u0000’, que corresponde al primer carácter que puede ser representado.
Es decir, después de ejecutar la instrucción
new Empleado();se crea un objeto de la siguiente
manera:
Luego de haber instanciado la clase, puede surgir la siguiente pregunta: ¿cómo se le asignan
valores a los atributos del objeto (para poder cambiar los valores por defecto con los que
se inicializa)? Como el objeto se crea en un espacio de la memoria, es necesario obtener la
referencia a esa posición en memoria y para poder hacerlo se define una variable, a la que
se le asigna dicha posición en memoria. Se requiere modificar la anterior instrucción por las
siguientes:
Empleado elEmpleado;
elEmpleado = new Empleado();
O se puede simplificar en una sola instrucción:
El entendimiento de esta definición suele causar problemas a algunos programadores.
La siguiente idea puede ayudar a clarificar la situación; al trabajar en cualquier lenguaje
de programación a la hora de definir una variable se emplea una instrucción semejante a
int a = 50,
es decir, primero se define el tipo de dato, luego el nombre de la variable y
posteriormente su valor. Si se analiza con detenimiento esta última instrucción y se compara
con la de creación de una instancia de
Empleado,se obtiene lo siguiente:
Aquí se mencionan los siguientes puntos:
•
La primera parte de la definición indica el tipo de dato de la variable (que puede
ser un tipo de dato primitivo o de referencia). Esta parte cobra gran importancia al
trabajar el tópico de herencia.
•
La segunda parte de la definición corresponde al nombre que identificará a la
variable. Para la primera definición, el nombre de la variable es
elEmpleado;para la
segunda, simplemente el carácter
a.
•
La tercera parte de la definición especifica el valor que le será asignado a la variable.
•
Es importante considerar que aunque se ha intentado realizar una comparación de
ambas instrucciones, por definir variables de tipo de datos distintos, la manipulación
interna que hace el lenguaje de programación de estas variables es también distinto.
Recuerde que en la primera instrucción la variable
elEmpleadocorresponde a
una referencia de una posición en memoria, mientras que la segunda variable
efectivamente guarda el valor que se le asigna.
Una vez se tiene una referencia a un objeto (a través de la definición de una variable), es
posible consultar o modificar el valor de cualquiera de sus atributos. Para hacer cualquiera de
estas dos operaciones basta con generar una instrucción donde se especifique la referencia al
objeto y al atributo del objeto que se desea manipular; la delimitación entre ambos se logra
empleando el carácter punto (
‘
.’); por ejemplo, suponiendo que la referencia a un objeto de
la clase Empleado se llame
elEmpleado,
•
Se puede cambiar el valor del atributo
horasTrabajadasempleando la siguiente
instrucción:
elEmpleado.horasTrabajadas = 1.5;•
Se puede cambiar el valor del atributo sueldoXHora empleando la siguiente
instrucción:
elEmpleado.sueldoXHora = 100;•
Se puede consultar el valor del atributo cédula empleando la siguiente instrucción:
u
neJemploampliadosobrelainstanCiaCiónConsidere el siguiente código:
Empleado elEmpleado = new Empleado(); elEmpleado.cedula = “12345”;
elEmpleado.apellido = “Pérez”; elEmpleado.nombre = “Pedro”; elEmpleado.sueldoXHora = 50; elEmpleado.horasTrabajadas = 20;
Empleado otroEmpleado = new Empleado(); otroEmpleado.cedula = “98765”;
otroEmpleado.apellido = “Sánchez”; otroEmpleado.nombre = “María”; otroEmpleado.sueldoXHora = 120; otroEmpleado.horasTrabajadas = 10;
Después de ejecutar este código en Java se obtiene una configuración en memoria similar a la
siguiente:
La anterior gráfica debe entenderse de la siguiente manera: existen dos variables de tipo
Empleado
que referencian a objetos de la clase
Empleado; la primera referencia tiene el
nombre de
elEmpleadoy apunta a un objeto cuyo valor para el atributo
cedulaes
12345;
la
segunda referencia tiene el nombre de
otroEmpleadoy apunta a un objeto cuyo valor para el
atributo
cedulaes
98765.
Es recomendable tener pendiente que los valores para los atributos
de un objeto (por lo menos de la manera en que han sido definidos hasta el momento) son
independientes de los valores que pueda tener cualquier otro objeto.
s
obreeloperadorlógiCodeComparaCiónysufunCionamientosobre lasvariablesdetipoprimitivoydereferenCiaEl lector perspicaz debe estarse preguntando lo siguiente, si los valores de los atributos de un
objeto son totalmente independientes de los valores de los atributos de otros objetos de la misma
clase, ¿qué evita que se puedan crear dos objetos con exactamente los mismos valores para sus
atributos? La respuesta a este cuestionamiento es: nada. Considere el siguiente código:
Empleado elEmpleado = new Empleado(); elEmpleado.cedula = “12345”;
elEmpleado.apellido = “Pérez”; elEmpleado.nombre = “Pedro”; elEmpleado.sueldoXHora = 50; elEmpleado.horasTrabajadas = 20;
Empleado otroEmpleado = new Empleado(); otroEmpleado.cedula = “12345”;
otroEmpleado.apellido = “Pérez”; otroEmpleado.nombre = “Pedro”; otroEmpleado.sueldoXHora = 50; otroEmpleado.horasTrabajadas = 20;
En memoria se obtendría la siguiente configuración:
Es conveniente analizar el comportamiento de las variables al trabajar con el operador lógico
de comparación que provee Java (este operador se denota empleando el símbolo del doble
¿Cuál sería el resultado de ejecutar la siguiente instrucción?
elEmpleado == otroEmpleado
Aunque parezca ilógico, la respuesta de esta instrucción es el valor lógico de falso
(
false).
La explicación de esta respuesta es simple: si
elEmpleadoy
otroEmpleadoson referencias
a objetos, la anterior instrucción no compara los valores que componen a los objetos sino las
posiciones en memoria donde se encuentran, como son dos objetos distintos, son dos posiciones
en memoria distintas.
Desde este punto de vista, ¿cuál sería el resultado de ejecutar la siguiente instrucción?
elEmpleado.horasTrabajadas == otroEmpleado.horasTrabajadas
El error más común que cometen los programadores que apenas están conociendo la teoría
de la programación orientada a objetos en Java es responder que la instrucción arrojaría
como resultado el valor lógico falso (
false). En este caso, la respuesta es el valor verdadero
(
true). El porqué de esta respuesta es también sencillo: en esta última instrucción no se están
comparando referencias sino tipos primitivos, y como los tipos primitivos almacenan el valor,
esta instrucción sí está comparando los valores de ambas variables.
Como se mencionó en el apartado sobre la definición y manipulación de los tipos de datos
primitivos y los de referencia es realmente importante tener claridad sobre los tipos de datos
y sobre la manipulación que realiza Java sobre ellos.
Un último interrogante que puede surgirle a un programador perspicaz en estos momentos
es, ¿cómo comparar dos objetos basándose en su contenido? Aunque después se analizará más
ampliamente esta situación, por lo pronto es conveniente mencionar que para realizar dicho
tipo de comparación es necesario cotejar uno a uno los valores correspondientes de todos los
atributos de los objetos.
s
obrelareutilizaCióndelasvariablesdereferenCiaAnalícese el siguiente código:
Empleado e1 = new Empleado(); e1.cedula = “12345”; e1.apellido = “Pérez”; e1.nombre = “Pedro”; e1.sueldoXHora = 50; e1.horasTrabajadas = 20; Empleado e2 = null;
Empleado e3 = new Empleado(); e2=e1;
e3=e1;
Después de ejecutadas las anteriores instrucciones en memoria, se obtendría la siguiente
configuración:
Dos puntos para mencionar en esta situación:
•
Existen en memoria tres referencias:
e1,
e2y
e3.
Todas tres apuntan al mismo objeto.
Si empleando la referencia
e1se modifica el valor del atributo
horasTrabajadas,
ese nuevo valor también podrá ser consultado empleando cualquiera de las restantes
referencias.
•
En la gráfica aparece un objeto
Empleadocon valores por defecto; cuando un objeto
se queda sin ningún tipo de referencia para ser manipulado, un elemento especial de
Java, el recolector de basura, lo elimina automáticamente.
s
obrelainstanCiaCiónyutilizaCióndearraysEn Java, un array es un objeto que representa un conjunto finito y ordenado de elementos
homogéneos. En el apartado donde se presenta la codificación en Java del algoritmo para el
cálculo de la nómina empleando programación estructurada se encuentran algunos ejemplos
de definiciones de arrays. Se pueden definir arrays de tipos de datos primitivos y de referencia;
los valores con que se inicializan cada una de las posiciones del array siguen las mismas
pautas establecidas para la inicialización de los valores de los atributos explicadas en la sección
de la instanciación.
La manipulación de un array de un tipo de dato primitivo en Java es análoga a la manipulación
que hace cualquier otro lenguaje de programación. Por otro lado, el entendimiento de la
manipulación de arrays de tipo de datos de referencia generalmente causa problemas a los
programadores que apenas comienzan a aprender Java y la programación orientada a objetos.
Es conveniente tener presente lo siguiente:
•
Un array (sin importar el tipo de dato del que es definido) es un objeto que representa
un conjunto finito, por ello, al momento de crearlo es necesario especificar la cantidad
de elementos que van a ser parte de este conjunto (este valor debe ser especificado
explícitamente).
•
Resulta útil pensar en un array de un tipo de dato de referencia como una referencia a
referencias, es decir, en un apuntador de memoria en donde se registran referencias a
otras posiciones en memoria.
Analice el código que se presenta a continuación:
Empleado e1 = new Empleado(); e1.cedula = “12345”;
e1.apellido = “Pérez”; e1.nombre = “Pedro”; e1.sueldoXHora = 50; e1.horasTrabajadas = 20; Empleado e2 = new Empleado(); e2.cedula = “98765”;
e2.apellido = “Sánchez”; e2.nombre = “María”; e2.sueldoXHora = 120; e2.horasTrabajadas = 10;
Empleado[] conjunto = new Empleado[10]; conjunto[0] = e1;
conjunto[1] = e2; conjunto[2] = e1;
Después de ejecutarse el anterior código, la memoria queda de la siguiente manera:
Cabe anotar que al instanciar un array de tipo de datos de referencia todas las posiciones del
array se inicializan en el valor de
null(como ocurre con los atributos)
El error más común entre los programadores cuando empiezan a conocer el mundo de Java es
imaginarse que después de ejecutar el anterior código el estado de la memoria es similar al que
se muestra a continuación:
La clave para comprender esta situación se encuentra en tener claro que la instrucción
newEmpleado[10]
no instancia diez objetos de la clase
Empleado,
sino que define que el array
almacenará hasta un máximo de diez objetos. Considerando esta situación, a lo largo del código
solo se instancian dos objetos
Empleado; por consiguiente, solo deben existir en memoria dos
1.6. m
étOdO
p
rimeraaproximaCiónalaapliCaCióndelCálCulodelanómina medianteprogramaCiónorientadaaobJetosCon los temas cubiertos hasta el momento es posible codificar un primer prototipo para la
aplicación. Se empleará una línea para dividir el código de las distintas clases que se presentan.
El primer componente a codificar corresponde a la clase
Empleado(
se empleará la codificación
presentada anteriormente).
class Empleado{ String cedula; String apellido; String nombre; double horasTrabajadas; double sueldoXHora; } import java.io.BufferedReader; import java.io.InputStreamReader; class Nomina{public static void main(String[] args) throws Exception{ int numeroEmpleados;
Empleado[] losEmpleados = new Empleado[50]; String cedula, apellido, nombre;
double horas, sueldo; double total = 0;
BufferedReader br = new BufferedReader( new InputStreamReader( System.in ) );
System.out.print(“Digite numero de empleados: “);
numeroEmpleados = Integer.valueOf(br.readLine()).intValue();
for(int i=0;i<numeroEmpleados;i++){
System.out.print(“\nDigite la cedula del empleado: “ ); cedula = br.readLine();
System.out.print(“Digite el apellido del empleado: “ ); apellido = br.readLine();
nombre = br.readLine();
System.out.print(“Digite num de horas trabajadas del empleado: “); horas = Double.valueOf(br.readLine()).doubleValue();
System.out.print(“Digite sueldo por hora del empleado: “ ); sueldo = Double.valueOf(br.readLine()).doubleValue(); Empleado unEmpleado = new Empleado();
unEmpleado.cedula = cedula; unEmpleado.apellido = apellido; unEmpleado.nombre = nombre; unEmpleado.horasTrabajadas = horas; unEmpleado.sueldoXHora = sueldo; losEmpleados[i]=unEmpleado; }
for (int i=0;i<numeroEmpleados;i++)
total = total + losEmpleados[i].sueldoXHora* losEmpleados[i].horasTrabajadas;
System.out.println(“\nLa nómina total es: “+ total);
} }
Puntos convenientes a resaltar en el anterior código:
•
Note que en vez de tener arrays independientes para cada uno de los datos a registrar
de un empleado (como la cédula, apellido, etc.) en este código se crea un único array
de elementos de tipo
Empleado.
•
Note cómo es la manipulación del array de objetos de tipo
Empleadoque lleva por
a
proximaCiónalternativadelaapliCaCióndelCálCulodelanómina medianteprogramaCiónorientadaaobJetosLa aplicación presentada en el anterior apartado inicialmente indaga la cantidad de empleados
que posee la empresa y luego va preguntando la información puntual de cada uno de ellos. Se
podría definir una aplicación alternativa que funcione a través de un menú. En este se podrían
definir como opciones el ingreso de un nuevo empleado en la nómina de la empresa, calcular
la nómina total y, finalmente, abandonar la aplicación. El siguiente código en Java representa
esta última situación (se reutiliza la definición de la clase
Empleadoaunque no es necesario
volverla a codificar):
class Empleado{ String cedula; String apellido; String nombre; double horasTrabajadas; double sueldoXHora; } import java.io.BufferedReader; import java.io.InputStreamReader; class Nomina2{public static void main(String[] args) throws Exception{ int numeroEmpleados=0, opcionMenu=0;
Empleado[] losEmpleados = new Empleado[50]; String cedula, apellido, nombre;
double horas, sueldo; double total = 0;
BufferedReader br = new BufferedReader( new InputStreamReader( System.in ) ); while(opcionMenu!=3){
System.out.println(“Menu de opciones”); System.out.println(“1- Adicionar Empleado”); System.out.println(“2- Calcular nómina total”); System.out.println(“3- Salir”);
opcionMenu = Integer.valueOf(br.readLine()).intValue(); if (opcionMenu==1){
System.out.print(“\nDigite la cedula del empleado: “ ); cedula = br.readLine();
System.out.print(“Digite el apellido del empleado: “ ); apellido = br.readLine();
System.out.print(“Digite el nombre del empleado: “ ); nombre = br.readLine();
System.out.print(“Digite num de horas trabajadas: “); horas = Double.valueOf(br.readLine()).doubleValue(); System.out.print(“Digite sueldo por hora del empleado: “ ); sueldo = Double.valueOf(br.readLine()).doubleValue(); Empleado unEmpleado = new Empleado();
unEmpleado.cedula = cedula; unEmpleado.apellido = apellido; unEmpleado.nombre = nombre; unEmpleado.horasTrabajadas = horas; unEmpleado.sueldoXHora = sueldo; losEmpleados[numeroEmpleados]=unEmpleado; numeroEmpleados++; } else if (opcionMenu==2){
for (int i=0;i<numeroEmpleados;i++)
total = total + losEmpleados[i].sueldoXHora* losEmpleados[i].horasTrabajadas;
System.out.println(“\nLa nómina total es: “+ total); }
} } }
La definición de la clase
Empleadopuede ser reutilizada en una gran variedad de aplicaciones
(como, por ejemplo, los códigos presentados en los apartados: Primera aproximación de
la aplicación del cálculo de la nómina mediante programación orientada a objetos y
Aproximación alternativa de la aplicación del cálculo de la nómina según programación
orientada a objetos). En caso de ser necesario calcular cuánto se le debe pagar a un empleado
bastaría con multiplicar el número de horas trabajadas por el sueldo que devenga por hora;
previendo que el uso de esta operación puede que sea necesaria utilizarla en posteriores
ocasiones, sería conveniente incluirla como parte de una función (muchos en este momento
pueden estar pensando en no definir ninguna función debido a la simplicidad del cálculo;
sin embargo, se debe considerar que la fórmula del sueldo mensual de un empleado podría
complejizarse al tener en cuenta la serie de aportes parafiscales como pensión, salud,
retención en la fuente, entre otros ). ¿Pero dónde definir la función? Se manejan las siguientes
alternativas:
•
Como la clase
Empleadopuede que sea reutilizada en varias aplicaciones, sería
necesario definir dicha función en cada una de ellas; sin embargo, esta no sería una
muy buena alternativa, pues se estaría replicando el código en muchos lados, y,
además, representaría un serio problema en caso de necesitarse la realización de una
modificación en la fórmula del cálculo del sueldo del empleado.
•
Crear una librería independiente de funciones e incluirla allí. Esta librería podría
abarcar esta función y cualquier otra que aparezca en el desarrollo de la aplicación.
•
La mejor alternativa correspondería definirla dentro de la propia clase; de todas
formas qué mejor librería a incluirse que la propia clase que genera la necesidad.
En Java desaparecen los conceptos de subrutinas, procedimientos o funciones (que existen
dentro del paradigma de programación procedimental) y se reemplazan por la noción
de método (sin embargo, el comportamiento y la forma de trabajo son análogos al de las
subrutinas). La idea principal es la de ubicar los métodos junto con los datos sobre los que
operan (en este caso, ubicar el método que posibilite el cálculo del salario mensual de un
empleado junto con los datos del empleado).
Definición de método
Abstracción de una acción, servicio, comportamiento o tarea que puede ser realizado
por un objeto. Generalmente, un método manipula la información registrada en los
atributos a través de una o más instrucciones.
La definición de un método en Java requiere básicamente especificar:
•
El tipo de dato que retornará como resultado el método. Este tipo de dato puede
corresponder a un tipo de dato primitivo, a cualquier tipo de dato de referencia, o en
caso de no retornar ningún valor debe especificarse el valor de
void.
•
El nombre del método. La comunidad de programadores de Java maneja una
convención de nombramiento para los métodos, que establece que el nombre del
método debe escribirse en minúsculas. Si el nombre de un método está compuesto
por varias palabras; se unen las letras de todas las palabras (sin usar ningún carácter
especial adicional) y se colocan en mayúsculas las primeras letras de cada palabra, a
excepción de la primera letra del nombre del método (por ejemplo, calcularSalario).
•
Un paréntesis de apertura y un paréntesis de cierre (
‘
()’), y en caso de requerirse
dentro del juego de paréntesis, la definición del conjunto de parámetros que necesita
el método para su funcionamiento.
Añadiendo la definición del método que posibilite el cálculo del salario del empleado, la
clase
Empleadoquedaría de la siguiente manera:
class Empleado{ String cedula; String apellido; String nombre; double horasTrabajadas; double sueldoXHora; double calcularSalario(){
return horasTrabajadas * sueldoXHora; }
}
En pocas palabras, la definición del método
calcularSalarioindica que se debe multiplicar
el valor que se tenga en los atributos
horasTrabajadasy
sueldoXHora, y retornar el resultado.
Para invocar un método se requiere una instrucción donde se especifique la referencia al
objeto y al método dentro de ese objeto; la delimitación entre ambos se logra empleando
el carácter punto (
‘
.’); por ejemplo, suponiendo que la referencia a un objeto de la clase
Empleado
se llame
elEmpleado, se puede invocar el método
calcularSalarioa través de la
siguiente instrucción:
elEmpleado.calcularSalario();Teniendo en cuenta el siguiente código:
Empleado e1 = new Empleado(); e1.cedula = “12345”;
e1.apellido = “Pérez”; e1.nombre = “Pedro”; e1.sueldoXHora = 50; e1.horasTrabajadas = 20; Empleado e2 = new Empleado(); e2.cedula = “98765”;
e2.apellido = “Sánchez”; e2.nombre = “María”; e2.sueldoXHora = 10; e2.horasTrabajadas = 120;
corrobora que el resultado de la invocación del método
calcularSalariodepende de los
valores de los atributos del objeto al que se hace referencia.
s
obrelosparámetrosenlosmétodosDeterminar qué parámetros deben definirse como parte de un método es una de las situaciones
que causan problemas para los programadores novatos en la programación orientada a objetos.
Por ejemplo, para el caso de la definición del método
calcularSalario,
muchos preguntarían
por qué no se incluyen como parámetros el valor de las horas trabajadas y el sueldo por hora
del empleado.
Supóngase por un momento que la definición del método se ha alterado para reflejar dicho
cambio, por consiguiente el código de la clase
Empleadoquedaría de la siguiente manera:
class Empleado{ String cedula; String apellido; String nombre; double horasTrabajadas; double sueldoXHora;
double calcularSalario(double horasTrabajadas, double sueldoXHora){ return horasTrabajadas * sueldoXHora;
} }
Suponga, además, que se tiene el siguiente código:
Empleado e1 = new Empleado(); e1.cedula = “12345”;
e1.apellido = “Pérez”; e1.nombre = “Pedro”; e1.sueldoXHora = 50; e1.horasTrabajadas = 20;
La instrucción para invocar el método
calcularSalariopara el objeto e1 sería:
e1.calcularSalario(20,50);
El lector perspicaz debe estar notando que en esta instrucción existe algo extraño. El siguiente
gráfico ilustra la situación.
Lo extraño de la situación corresponde a que a través de los parámetros se está suministrando
información que el objeto ya conoce y almacena. Esta instrucción no tiene sentido alguno. El
escenario sería el mismo si se ejecutase la siguiente instrucción:
e1.calcularSalario(e1.horasTrabajadas, e1.sueldoXHora);
Cuando se presentó la definición de clase, se hacía referencia a que esta describe la estructura
y comportamiento común de un conjunto de objetos. La descripción de la estructura se
alcanza a través de la definición de los atributos; y la descripción del comportamiento se
alcanza a través de la definición de los métodos.
1.6.1. Método constructor
Hasta el momento la forma presentada para posibilitar la creación de un objeto corresponde
al empleo de la instrucción
newseguida del nombre de la clase; para la asignación de los
valores a los atributos se requieren instrucciones posteriores. Si se analiza el código de las
instanciaciones efectuadas hasta el momento se podrá notar la cantidad de código que se
requiere; se podría pensar en definir un método para facilitar dicho proceso. Dentro de la
programación orientada a objetos ese método recibe el nombre de método constructor.
Definición de método constructor
Método especial que crea un objeto de la clase y que puede emplearse para
especificar aquellas tareas que deban realizarse en el momento de la creación como,
por ejemplo, la inicialización de los valores de los atributos.
El método constructor es un método especial, y su definición varía ligeramente del resto.
Las particularidades son:
•
Un método constructor no puede tener definido un tipo de dato de retorno cuando
ni siquiera el valor de
voides válido.
•
El nombre del método debe ser el mismo nombre de la clase (debe coincidir
exactamente con el nombre cuidando hasta el uso de letras mayúsculas y minúsculas).
s
obreelámbitodelasvariablesylapalabrareservada‘
this’
Con los lineamientos que se tienen hasta el momento el código de la clase
Empleadoquedaría
de la siguiente forma:
class Empleado{ String cedula; String apellido; String nombre; double horasTrabajadas; double sueldoXHora;Empleado(String pcedula, String papellido, String pnombre, double phorasTrabajadas, double psueldoXHora){
cedula = pcedula; apellido = papellido; nombre = pnombre; sueldoXHora = psueldoXHora; horasTrabajadas = phorasTrabajadas; } double calcularSalario(){
return horasTrabajadas * sueldoXHora; }
Del anterior código hay varios puntos por analizar:
•
La cantidad de parámetros definidos es la misma que el número de atributos de la
clase. Esta situación se presenta porque para crear un nuevo objeto empleado es
necesario que se suministre información para cada uno de los atributos. Sin embargo,
no debe seguirse esto como una regla general, porque hay escenarios donde el número
de parámetros puede diferir del número de atributos.
•
En el apartado sobre los parámetros en los métodos se estuvo analizando una
situación especial, que no se presenta con los parámetros del método constructor
porque, a diferencia del ejemplo allí presentado en este caso el objeto no conoce de
antemano la información del empleado
•
Para evitar que se presente una confusión con los nombres de los parámetros del
método constructor y de los atributos de la clase, se ha optado por anteponerle la letra
‘p
’
al nombre del parámetro.
•
El código del método constructor se encarga de asignar a cada uno de los atributos del
objeto los valores que se suministran en los parámetros correspondientes (es decir, al
atributo
cedulase le asigna el valor que se recibe en el parámetro
pcedula, etc.).
En Java también existe la noción de variable de ámbito local y de ámbito global, por consiguiente,
nada evita que la definición del método constructor de Empleado tenga la siguiente signatura:
Empleado(String cedula, String apellido, String nombre, double horasTrabajadas, double sueldoXHora)
El problema que se presentaría con la última definición del constructor de la clase Empleado es
que tanto los parámetros como los atributos tienen los mismos nombres; por ende, instrucciones
como las que se muestran a continuación, además de triviales, son ambiguas e inútiles.
cedula = cedula; apellido = apellido; nombre = nombre;
sueldoXHora = sueldoXHora;
horasTrabajadas = horasTrabajadas
;
¿Qué podrían significar las anteriores instrucciones? Habría cuatro posibilidades:
•
Asígnese al parámetro el valor que tiene el parámetro. Esto carece de sentido porque
dicho valor ya se encuentra asignado.
•
Asígnese al atributo el valor que tiene el atributo. Esto carece de sentido porque uno
de los objetivos de un método constructor es inicializar el valor de los atributos del
objeto; entonces, la instrucción no está alterando dicho valor.
•
Asígnese al parámetro el valor del atributo. También carece de sentido porque se están
reemplazando los valores suministrados para instanciar al objeto por los valores por
defecto con los que se inicializó la instancia.
•
Asígnese al atributo el valor suministrado en el parámetro. Esta instrucción si tiene
sentido y es la que se busca darle a entender al lenguaje de programación.
Es conveniente detenerse en este punto para explicar esta situación con más detalle. Dentro del
paradigma procedimental una variable global
2es aquella que está declarada para el programa
o algoritmo principal, del que dependen todos los subprogramas. En otras palabras, es una
variable que puede ser usada por cualquier procedimiento. Si se piensa que dentro de una
clase un atributo puede ser utilizado en cualquiera de los métodos que se definan al interior
de la clase entonces puede asemejarse el concepto de variable global al de atributo (solo
bajo la consideración presentada anteriormente de variable global pero únicamente para la
clase). Por su parte, una variable local
3es aquella que está declarada y definida dentro de un
subprograma. Con esta definición se puede decir que cualquier parámetro declarado para un
método, o cualquier variable definida dentro de un método, cae dentro de la definición de una
variable local.
Considérese el siguiente código de ejemplo:
class Ejemplo{ int a=10; void metodo(){ int a = 20; System.out.println(a); } }
En este ejemplo se define una clase de nombre
Ejemplo, que posee un único atributo de nombre
a
que se inicializa en el valor de
10, y, además, posee un método donde se define una variable
a
que se inicializa con un valor de
20. Hay que comenzar diciendo que por el análisis previo
efectuado sobre las variables de ámbito global y local, este ejemplo es totalmente válido. El
interrogante sería, cuál es el valor que se mostrará por pantalla en caso de invocar la ejecución
de
metodo. La respuesta es
20, y la razón es porque el método cuando necesita trabajar con la
variable
aprimero busca dentro de las definiciones de variables locales que tenga; si encuentra,
una trabaja con ella; de lo contrario, trabaja con la definición global (exactamente así ocurre
dentro del manejo de variables en el paradigma procedimental).
Volviendo al caso que inició este análisis, ya se mencionó que para la siguiente definición del
método constructor de la clase Empleado
Empleado(String cedula, String apellido, String nombre, double horasTrabajadas, double sueldoXHora){
cedula = cedula; apellido = apellido; nombre = nombre; sueldoXHora = sueldoXHora; horasTrabajadas = horasTrabajadas; }
Existían cuatro alternativas de ejecución, pero después de conocer cómo es el comportamiento
con el ámbito de las variables, se pueden descartar tres de las posibilidades; queda únicamente
la alternativa mencionada donde la instrucción ocasionaría la asignación al parámetro del
mismo valor que ya tiene (que correspondería a una instrucción trivial).
La solución a este asunto se encuentra evitando la trivialidad en el código del constructor de
la clase
Empleado, y cuando se especifica que la acción a ejecutar es la de asignarla al atributo
el valor que se recibe en el parámetro. Existe en Java una palabra reservada cuyo significado
ayuda a eliminar la trivialidad; esta palabra es
this.
this es una propiedad que tienen todos los objetos en Java y a través de ella un objeto puede
obtener su propia referencia en memoria (su propia posición). Para ejemplificar esta situación,
considere el siguiente código:
Empleado e1 = new Empleado(); Empleado e2 = new Empleado();
Suponga que el espacio de memoria que se reserva para el primer objeto
(e1)tiene la dirección
16f0472
y que el segundo objeto
(e2)tiene la dirección
18d107f; si al interior de cualquier
método en el objeto
(e1)se hace referencia a
thisse obtendrá la dirección
16f0472, y si al
interior de cualquier método en el objeto
e2se hace referencia a
thisse obtendrá la dirección
18d107f
.
¿Cómo ayuda
thisa especificar dentro del constructor que se le asignen a los atributos los
valores que se pasan en los parámetros? Si dentro de cualquier método se emplea la instrucción
conformada por la auto referencia
thisy el nombre del atributo (delimitándose uno del otro
al atributo en mención. Por consiguiente, las instrucciones dentro del constructor de
Empleadodebieran definirse de la siguiente manera:
this.cedula = cedula; this.apellido = apellido; this.nombre = nombre;
this.sueldoXHora = sueldoXHora;
this.horasTrabajadas = horasTrabajadas;
Tomando en cuenta lo presentado anteriormente (y en especial lo analizado en el apartado
sobre el ámbito de las variables y la palabra reservada ‘this’), el código de la clase
Empleadoqueda de la siguiente manera:
class Empleado{ String cedula; String apellido; String nombre; double horasTrabajadas; double sueldoXHora;
Empleado(String cedula, String apellido, String nombre, double horasTrabajadas, double sueldoXHora){
this.cedula = cedula; this.apellido = apellido; this.nombre = nombre; this.sueldoXHora = sueldoXHora; this.horasTrabajadas = horasTrabajadas; } double calcularSalario(){
return horasTrabajadas * sueldoXHora; }
}
Con esta nueva definición de la clase se simplifica la instanciación de objetos, pues se pasa
de tener que escribir el siguiente código:
Empleado e1 = new Empleado(); e1.cedula = “12345”;
e1.apellido = “Pérez”; e1.nombre = “Pedro”; e1.sueldoXHora = 50; e1.horasTrabajadas = 20;