Logo Passei Direto
Buscar
Material
páginas com resultados encontrados.
páginas com resultados encontrados.

Prévia do material em texto

Informática 
ed
ici
on
es
de la
Luis Fernando García Llinás
ientada 
a objetos en Javä
ed
ici
on
es
de la
Este texto
 está dirig
ido a per
sonas con
 
conocimi
entos pre
vios en pr
ogramació
n 
que emple
an el parad
igma proc
edimenta
l 
para la 
codificaci
ón de 
solucione
s 
(especialm
ente usan
do el len
guaje C),
 
y que, ad
emás, des
een apren
der acerc
a 
del desarr
ollo de ap
licaciones
 orientada
s 
a objetos 
en Java
®, sin nece
sidad de l
eer 
la abunda
nte y ext
ensa liter
atura que
, 
por lo ge
neral, con
tienen los
 manuale
s 
y guías d
e usuario
s. Precisa
mente, la
 
síntesis y 
la precisió
n de la in
formación
, 
es uno de
 los atract
ivos de es
te texto q
ue 
compila e
n 260 pá
ginas los 
contenido
s 
básicos p
ara un cu
rso de pro
gramación
 
orientada
 a objetos
 en Java
®.
P
ro
gr
am
ac
i—
n 
or
ie
nt
ad
a 
a 
ob
je
to
s 
en
 J
av
ä
Luis Fernando García Llinás
Lu
is 
Fe
rn
an
do
 G
ar
cí
a 
Ll
in
á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” 
de esta misma institución.
9 789587 410624
Programaci—ón or
Todo lo básico que debería saber sobre 
Programación orientada
a objetos en Java®
Todo lo básico que debería saber sobre 
Programación orientada
a objetos en Java®
LUIS FERNANDO GARCÍA LLINÁS
2 0 1 0
© 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 
 
García Llinás, Luis Fernando.
 
 Todo lo que debería saber sobre programación orientada a objetos en 
Java / Luis Fernando García Llinás. – Barranquilla : Ediciones Uninorte ; 
Grupo Editorial Ibánez, 2010. 
 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á
Printed and made in Colombia
JAVA MK.indd 4 13/08/2010 06:46:45 p.m.
v
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
1.16. Interfaces ............................................................................................................................... 124
CONTENIDO
vi
Todo lo básico que debería saber sobre Programación orientada a objetos en Java
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
vii
PREFACIO
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.
ObjetivOs•	 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á 
mucho más sencillo entender cualquier otra que trate los mismos temas.
viii
Todo lo básico que debería saber sobre Programación orientada a objetos en Java
¿A quién vA dirigidO este librO?
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.
¿qué se requiere Antes de leerlO?
•	 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/).
OrgAnizAción
El 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.
retrOAlimentAción
Cualquier 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: 
garcialf@uninorte.edu.co o luisgarciallinas@gmail.com 
1
1. 
TÓPICOS BÁSICOS
1.1. sObre 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 
su sueldo por hora puede variar de un empleado a otro.
•	 Se supondrá que la aplicación solo se requiere para calcular el valor de la nómina de 
un único mes.
2
Todo lo básico que debería saber sobre Programación orientada a objetos en Java
•	 Para efectos de mantener la simplicidad del ejemplo no se contemplan acciones para 
manejar la persistencia 1 de 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, 
1 Acciones para preservar de forma permanente y para recuperar los datos, como, por ejemplo, guardarlos en un archivo 
en disco.
3
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.
CodifiCaCión en Java del algoritmo para el CálCulo de la nómina 
empleando programaCión estruCturada
Se 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(); 
 System.out.print(“Digite sueldo por hora del empleado: “ );
1. TÓPICOS BÁSICOS
4
Todo lo básico que debería saber sobre Programación orientada a objetos en Java
 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 import sirven 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 double se corresponde con el tipo Real en el seudocódigo; el 
tipo de dato en Java String se corresponde con el tipo Caracteres en seudocódigo; y 
el tipo de dato int en Java se corresponde con el tipo de dato Entero en 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 
del operador ‘+’.
5
•	 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. ObjetO
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 
corresponden a los empleados de la empresa. En caso de que se necesite incluir un nuevo 
1. TÓPICOS BÁSICOS
6
Todo lo básico que debería saber sobre Programación orientada a objetos en Java
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. clAse
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 
un empleado por cada hora de trabajo.
7
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. AtributO
Definición de atributo
Un atributo es una propiedad que ayuda a describir un objeto.
Es conveniente tener en cuentalo siguiente: 
•	 Hasta el momento y con las definiciones dadas, dos objetos distintos, pero de la 
misma clase tienen la misma estructura; es decir, comparten los mismos atributos, 
pero los valores para cada uno de los atributos son independientes. 
1. TÓPICOS BÁSICOS
8
Todo lo básico que debería saber sobre Programación orientada a objetos en Java
•	 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 nombre2, 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 primitivo3. 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, boolean y 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
9
sobre la ComposiCión de obJetos
Uno 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 tipoTecho hace 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 tipoParedes hace 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 Casa no 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;
}
1. TÓPICOS BÁSICOS
10
Todo lo básico que debería saber sobre Programación orientada a objetos en Java
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 Casa ha sido necesaria la previa 
definición de las clases Techo y Pared. Adicionalmente cabe resaltar que en la clase Casa 
existe un atributo de tipo de referencia de clase Techo cuyo nombre es elTecho, y, además, 
existe un atributo de tipo de referencia de la clase Pared de nombre lasParedes.
¿Cuál de las dos definiciones es la mejor? Depende del problema. La primera definición de la 
clase Casa es 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 
se podrían reutilizar algunas clases como Pared y Techo).
11
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 de los atributos pues Java trata 
de forma muy distinta a cada uno de ellos. 
Imagínese un objeto de la clase Casa donde 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, Techo y 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 
decir, que de ahora en adelante se presentarán los diagramas de la siguiente forma.
1. TÓPICOS BÁSICOS
12
Todo lo básico que debería saber sobre Programación orientada a objetos en Java
Simplemente para hacer claridad en este último punto, la estructura en memoria para el objeto 
de la clase Casa nunca 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. instAnciAció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 instanciar4; entonces para crear 
un nuevo empleado se utiliza la siguiente instrucción
new Empleado();
4 Cuando se llegue al concepto de método constructor se redefine esta afirmación. Por ahora, el objetivo es mantener la 
explicación lo más simple posible.
13
Con esta instrucción Java separa un espacio en memoria con la estructura definida para 
la clase Empleado (es decir, debe haber un espacio cedula para almacenar la cédula del 
empleado, un espacio apellido para 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 
inicianlos 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, float y 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:
Empleado elEmpleado = new Empleado();
1. TÓPICOS BÁSICOS
14
Todo lo básico que debería saber sobre Programación orientada a objetos en Java
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 elEmpleado corresponde 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 horasTrabajadas empleando 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: 
elEmpleado.cedula
15
un eJemplo ampliado sobre la instanCiaCión
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 = “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 elEmpleado y apunta a un objeto cuyo valor para el atributo cedula es 12345; la 
segunda referencia tiene el nombre de otroEmpleado y apunta a un objeto cuyo valor para el 
atributo cedula es 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.
1. TÓPICOS BÁSICOS
16
Todo lo básico que debería saber sobre Programación orientada a objetos en Java
sobre el operador lógiCo de ComparaCión y su funCionamiento sobre 
las variables de tipo primitivo y de referenCia
El 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 
igual ‘==’ )
17
¿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 elEmpleado y otroEmpleado son 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ónes necesario cotejar uno a uno los valores correspondientes de todos los 
atributos de los objetos.
1. TÓPICOS BÁSICOS
18
Todo lo básico que debería saber sobre Programación orientada a objetos en Java
sobre la reutilizaCión de las variables de referenCia
Analí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, e2 y e3. Todas tres apuntan al mismo objeto. 
Si empleando la referencia e1 se 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 Empleado con 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.
19
sobre la instanCiaCión y utilizaCión de arrays
En 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;
1. TÓPICOS BÁSICOS
20
Todo lo básico que debería saber sobre Programación orientada a objetos en Java
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:
21
La clave para comprender esta situación se encuentra en tener claro que la instrucción new 
Empleado[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 
objetos Empleado, y un objeto del tipo array de objetos Empleado.
1. TÓPICOS BÁSICOS
22
Todo lo básico que debería saber sobre Programación orientada a objetos en Java
1.6. métOdO
primera aproximaCión a la apliCaCión del CálCulo de la nómina 
mediante programaCión orientada a obJetos
Con 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();
 System.out.print(“Digite el nombre del empleado: “ );
23
 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 Empleado que lleva por 
nombre losEmpleados.
1. TÓPICOS BÁSICOS
24
Todo lo básico que debería saber sobre Programación orientada a objetos en Java
aproximaCión alternativa de la apliCaCión del CálCulo de la nómina 
mediante programaCión orientada a obJetos
La 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 Empleado aunque 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”);
 System.out.print(“Escoja opción: “);
25
 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 Empleado puede 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 
1. TÓPICOS BÁSICOS
26
Todo lo básico que debería saber sobre Programación orientada a objetos en Java
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 Empleado puede 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 
27
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 Empleado quedarí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 calcularSalario indica que se debe multiplicar 
el valor que se tenga en los atributos horasTrabajadas y 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 calcularSalario a 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;
El resultado de ejecutar la instrucción e1.calcularSalario() sería 1000, mientras que la 
ejecución de e2.calcularSalario() daría como resultado 1200. Lo anterior simplemente 
1. TÓPICOS BÁSICOS
28
Todo lo básico que debería saber sobre Programación orientada a objetos en Java
corrobora que el resultado de la invocación del método calcularSalario depende de los 
valores de los atributos del objeto al que se hace referencia. 
sobre los parámetros en los métodos
Determinar 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 Empleado quedarí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;
29
La instrucción para invocar el método calcularSalario para 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 new seguida 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.
1. TÓPICOS BÁSICOS
30
Todo lo básico que debería saber sobre Programación orientada a objetos en Java
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 void es 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).
sobre el ámbito de las variables y la palabra reservada ‘this’
Con los lineamientos que se tienen hasta el momento el código de la clase Empleado quedarí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;
 }
}
31
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 cedula se 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.
1. TÓPICOS BÁSICOS
32
Todo lo básico que debería saber sobre Programación orientada a objetos en Java
•	 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 global2 es 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 local3 es 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 a primero 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).
33
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 this se obtendrá la dirección 16f0472, y si al 
interior de cualquier método en el objeto e2 se hace referencia a this se obtendrá la dirección 
18d107f.
¿Cómo ayuda this a 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 this y el nombre del atributo (delimitándose uno del otro 
con el carácter punto ‘.’) se le indica al programa que no debe acceder a la variable local, sino
1. TÓPICOS BÁSICOS
34
Todo lo básico que debería saber sobre Programación orientada a objetos en Java
al atributo en mención. Por consiguiente, las instrucciones dentro del constructor de Empleado 
debieran 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 Empleado 
queda 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;
35
a simplemente la siguiente instrucción:
Empleado e1 = new Empleado(“12345”, “Pérez”, “Pedro”, 20, 50);
Al trabajar con métodos constructores los siguientes puntos deben ser tenidos en cuenta:
•	 Es conveniente que el método constructor inicialice los valores para todos los 
atributos.
•	 Una clase puede tener definido más de un método constructor. Existen varias 
restricciones que deben cumplirse, pero este tema será abordado en un capítulo 
posterior.
•	 Por la forma en que se han presentado los ejemplos de las clases empleadas hasta el 
momento, puede pensarse que dichas estas clases carecían de un método constructor; 
sin embargo, la situación real es totalmente opuesta. El lenguaje de programación 
Java cuando encuentra una clase que no tiene definido explícitamente un constructor 
genera de forma automática un constructor por defecto. Este constructor (que no se 
le presenta al programador) tiene la particularidad de no tener ningún parámetro 
definido y, además, lo único que hace es inicializar los atributos con sus valores 
por defecto (estos valores fueron analizados en la sección donde se habla de la 
instanciación). Si una clase tiene definido por lo menos un constructor, Java no 
generará de forma automática el constructor por defecto.
•	 En la sección donde se habla de la instanciación se mencionó que se debe utilizar el 
comando new seguido por el nombre de la clase; esta definición no es del todo correcta, 
en realidad para instanciar una clase se debe utilizar el comando new seguido de 
uno de los posibles constructores de la clase. El lector perspicaz seguramente habrá 
notado que la clase Empleado que se utilizó para dicho ejemplo no tenía constructor 
y, por ende, Java generaba el constructor por defecto; era ese constructor el que se 
utilizaba en aquel entonces.
•	 El lector con un alto nivel de suspicacia debe estarse formulando la siguiente 
pregunta: si String es un tipo de dato de referencia por qué no es necesario utilizar 
su constructor para crear objetos de este tipo (corrobore esta situación analizando 
todos los ejemplos presentados hasta el momento donde se haya requerido un 
nuevo objeto de la clase String). La clase String es una clase excepcional en Java, es 
la única que permite instanciar nuevos objetos simplemente definiendo el texto de la 
cadena de caracteres a instanciar dentro de comillas5. También se pueden instanciar 
objetos de esta clase empleando cualquiera de los constructores que esta provee.
•	 El funcionamiento y la manipulación de un método constructor lo hacen un método 
atípico pues, a diferencia de los otros métodos definidos en los ejemplos que se han 
presentado hasta el momento, este no requiere la referencia a una instancia para 
ser invocado. Analícese lo siguiente: para invocar el método calcularSalario de la 
clase Empleado, primero debe tenerse un objeto para luego sí invocar la instrucción; 
en cambio, para invocar la instrucción new Empleado() no es necesaria la referencia.
5 En realidad en Java no es lo mismo instanciar la clase String empleando uno de sus constructores o empleando el con-
structor especial; pero esto corresponde a una noción mucho más profunda de la que se desea presentar en este libro
1. TÓPICOS BÁSICOS
36
Todo lo básico que debería saber sobre Programación orientada a objetos en Java
segunda aproximaCión a la apliCaCión del CálCulo de la nómina 
mediante programaCión orientada a obJetos
Con los temas cubiertos hasta el momento es posible codificar un segundo prototipo para 
la aplicación. El primer componente a codificar corresponde a la clase Empleado. El código 
que aparece en negrilla resalta las modificaciones efectuadas con respecto a la anterior 
aproximación. 
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;
 }
}
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;
37
 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.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(cedula, apellido, nombre,
 horas, sueldo);
 losEmpleados[i]=unEmpleado;
 }
 for (int i=0;i<numeroEmpleados;i++)
 total = total + losEmpleados[i].calcularSalario();
 
 System.out.println(“\nLa nómina total es: “+ total);
 
 }
}
Puntos convenientes a resaltar en el anterior código:
•	 El proceso de instanciación de un objeto de la clase Empleado ahora es mucho más 
simple.
•	 Cuando se requiere calcular cuánto devenga un empleado por concepto de salario se 
invoca la ejecución del método calcularSalario.
1. TÓPICOS BÁSICOS
38
Todo lo básico que debería saber sobre Programación orientada a objetos en Java
sobre la utilizaCión de paquetes 
Con los temas tratados hasta el momento es evidente que no debe permitirse la definición 
de dos clases con el mismo nombre, pues esto podría causarle problemas al sistema a la hora 
de la instanciación. Entonces, cómo se podría garantizar que dos programadores cualquiera 
ubicados en dos lugares del mundo no coincidan en el nombre de una clase.
El lenguaje de programación Java a través de la definición de paquetes4 brinda una forma de 
organizar clases que se encuentren relacionadas. Un paquete es un espacio de nombres, cuyo 
concepto es igual al de una carpeta dentro de un sistema de archivos. Java permite definir 
paquetes dentro de paquetes (igual que se pueden definir carpetas dentro de carpetas) y definir 
dentro de ellos clases. De esta manera sí pueden existir clases con el mismo nombre siempre y 
cuando pertenezcan a distintos paquetes.
Para definir que una clase pertenece a un paquete específico, se debe incluir la siguiente 
instrucción como la primera línea en el archivo donde se define la clase
package nombredelpaquete;
Por convención en Java los nombres de los paquetes se encuentran escritos siempre en 
minúsculas. En caso de querer asignar la clase a un subpaquete de un determinado paquete, 
los nombres de los paquetes deben separarse empleando el carácter punto ‘.’ (esto implica 
que dentro de la definición del nombre de un paquete no puede utilizarse dicho carácter). Por 
ejemplo:
package nombredelpaquete.nombredelsubpaquete;
Cuando en la definición de una determinada clase se necesite la funcionalidad de otra que se 
encuentra definida dentro de un paquete, bastará con declarar explícitamente la importación 
de la clase en mención, lo cual se logra a través de la siguiente instrucción:
import nombredelpaquete.nombredelsubpaquete.nombredelaclase;
De esta manera se importa la definición de la clase dentro de la clase. Si lo que se desea es 
importar todas las definiciones de clases de dicho paquete, esto bien puede conseguirse a 
través de la siguiente instrucción:
import nombredelpaquete.nombredelsubpaquete.*;
39
Existe una alternativa que evita el tener que declarar la instrucción de importación de la clase, 
basta incluir dicha información a la hora de la declaración de la variable; por ejemplo, si se 
desea definir una referencia del tipo de dato de los ejemplos presentados hasta el momento en 
este apartado, se debe colocar la siguiente instrucción:
nombredelpaquete.nombredelsubpaquete.nombredelaclase a = 
 new nombredelpaquete.nombredelsubpaquete.nombredelaclase();
En el ejemplo anterior a corresponde al nombre de la variable a definir.
1.7. encApsulAmientO
Muchas de las críticas que recibe el paradigma de programación procedimental van dirigidas 
a la poca protección que se brinda a los datos que utilizan las aplicaciones. El porqué de esta 
afirmación se explica a continuación. Considere la siguiente gráfica:
El esquema anterior corresponde al esquema general que presenta cualquier aplicación 
desarrollada bajo el paradigma procedimental. Dicho esquema debe entenderse de la 
siguiente manera: toda aplicación está compuesta por un conjunto de variables globales 
junto con un conjunto de subrutinas (incluida la subrutina principal) en las que se definen, 
en caso de ser necesarios, un conjunto de variables locales para dichas subrutinas. 
El problema principal de la programación procedimental radica en la poca protección que 
se brinda a las variables globales porque cualquier subrutina puede alterar sus valores 
indiscriminadamente. Imagine el siguiente escenario: dos variables globales enteras 
de nombre v1 y v2; dentro de la aplicación, para la primera de ella solo tiene sentido la 
asignación de valores positivos, pero la segunda puede tomar cualquier valor. Por un simple 
descuido (de digitación) dentro de una subrutina se le asigna el valor de -2 a la variable v1 
cuando en realidad dicho valor debía ser asignado a v2; el resultado de ese descuido pone 
a mal funcionar todas las subrutinas que trabajan con el valor de v1 y, por ende, a toda la 
aplicación. 
En el apartado sobre el ámbito de las variables y la palabra reservada ‘this’ se analizó cómo 
es el comportamiento del ámbito de las variables cuando se trabaja bajo el paradigma de 
orientación a objetos. El siguiente gráfico ejemplifica, a grosso modo, cómo es la situación.
1. TÓPICOS BÁSICOS
40
Todo lo básico que debería saber sobre Programación orientada a objetos en Java
El anterior esquema debe entenderse de la siguiente manera: las aplicaciones orientadas 
a objetos están conformadas por un conjunto de clases; estas poseen atributos (que se 
pueden entender como variables globales pero solo al interior de las clases) y métodos en 
los que se definen, en caso de ser necesarias, variables locales. Aunque el enfoque de ambos 
paradigmas varía considerablemente; los dos presentan el mismo problema: permitir que 
desde cualquier clase se altere el valor del atributo de otro objeto (como, por ejemplo, cuando 
se accede y modifica el valor de la cédula sobre un objeto empleado). 
El encapsulamiento ayuda a corregir este problema.
Definición de encapsulamiento
El encapsulamiento hace referencia a ocultar los detalles de implementación internos 
del objeto a los demás. Esta propiedad permite asegurar que el contenido de la 
información de un objeto se encuentra seguro del mundo exterior.
La forma de implementar el encapsulamiento en una clase se logra a través del uso de 
niveles de visibilidad (también conocidos como modificadores de acceso).
Con los temas vistos hasta el momento se pueden definir dos niveles de visibilidad6:
6 Además del nivel de visibilidad por defecto (el que se ha manejado en los ejemplos hasta el momento), existe otro que se 
presentará en la sección correspondiente a la herencia. 
41
Definición de nivel de visibilidad público
Cuando se aplica a un atributo o método de una clase se especifica que dicha información 
o acción es visible; se puede acceder a aquel tanto en el interior como en el exterior de la 
clase donde se encuentra definido. Cuando se aplica a una clase5, se especifica esta clase 
puede ser utilizada desde cualquier clase de cualquier paquete
El siguiente diagrama puede facilitar la comprensión del nivel de visibilidad público. Las 
flechas que aparecen en él representan la utilización del elemento señalado por la punta de 
la flecha y el otro extremo (de la flecha), el sitio de donde es invocado el elemento. Suponga 
que todos los atributos y métodos de la clase Clase1 son públicos, por ende, ellos pueden ser 
accedidos tanto al interior (cuando un método de Clase1 invoca otro método de la misma 
clase, o cuando un método deClase1 accede a un atributo de la propia clase) como al exterior 
de la clase donde se encuentra definido (cuando un método de Clase2 accede a un atributo 
de Clase1, o cuando un método de Clase3 invoca un método de Clase1).
Para especificar un atributo o método público en Java se le debe anteponer a su definición 
la palabra reservada public. Para especificar que una clase es pública se debe anteponer la 
palabra reservada public a la definición de la clase. 
1. TÓPICOS BÁSICOS
42
Todo lo básico que debería saber sobre Programación orientada a objetos en Java
Definición de nivel de visibilidad privado
Cuando se aplica a un atributo o método de una clase se especifica que dicha 
información o acción solo puede ser visible (accesible) al interior de la clase donde 
se encuentra definido. 
El siguiente diagrama puede facilitar la comprensión del nivel de visibilidad privado. Las 
flechas que aparecen en él representan la utilización del elemento señalado por la punta de 
la flecha y el otro extremo (de la flecha), el sitio de donde es invocado el elemento. Para este 
caso suponga que todos los atributos y métodos de Clase1 son privados, por ende, se puede 
acceder a ellos solo desde el interior de la propia clase (cuando un método de Clase1 invoca 
otro método de la misma clase, o cuando un método de Clase1 accede a un atributo de la 
propia clase).
Para especificar un atributo o método privado en Java se le debe anteponer a su definición 
la palabra reservada private. No es posible definir una clase con un nivel de visibilidad 
privado. 
43
sobre los niveles de visibilidad públiCo y privado
Considérese el siguiente código:
class Empleado{
 private String cedula;
 private String apellido;
 private String nombre;
 private double horasTrabajadas;
 private 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;
 }
}
Al definir todos los atributos de la clase Empleado como privados se especifica que a estos solo 
se les puede acceder o modificar mediante instrucciones ubicadas en el interior de algún método 
de la clase; ninguna clase externa podrá acceder ni mucho menos modificar dichos valores. 
Por ejemplo, las siguientes instrucciones ubicadas en cualquier clase distinta a Empleado no 
solamente no se podrían ejecutar, sino que ni siquiera podrían ser compiladas por Java. El 
error en compilación se marcaría en la segunda instrucción porque no se puede modificar el 
valor de un atributo privado. Si los atributos fueran públicos, no se presentaría ningún tipo de 
error de compilación.
Empleado e1 = new Empleado(“12345”, “Pérez”, “Pedro”, 20, 50);
e1.apellido = “Pérez Sosa”;
¿Por qué la implementación de niveles de visibilidad en el código corrigen los problemas 
de protección de los datos? El problema a corregir radica en que en la programación 
estructurada cualquier subrutina puede alterar el valor de cualquier variable global. 
desde el paradigma de orientación a objetos y con la utilización del encapsulamiento se ha 
restringido esa situación porque ahora con la utilización del modificador de acceso privado 
solo pueden alterar los valores de los atributos los métodos ubicados al interior de la clase. 
Esto aumenta el control sobre los datos. 
1. TÓPICOS BÁSICOS
44
Todo lo básico que debería saber sobre Programación orientada a objetos en Java
En este punto, cualquier programador debe estarse preguntando, ¿cómo se puede alterar 
el valor del atributo de algún objeto en caso de ser absolutamente requerido? A través de 
métodos públicos codificados especialmente para poder obtener el valor de un atributo 
(este tipo de métodos se conocen como métodos accesores) y métodos públicos que 
permitan modificar el valor del atributo (este tipo de métodos se conocen como métodos 
modificadores). 
Aunque no es una imposición del lenguaje Java, como recomendación general el nombre del 
método accesor para un atributo debe formarse anteponiendo la palabra ‘get’ (del inglés 
obtener) al nombre del atributo; de la misma forma, el nombre método modificador debe 
formarse anteponiendo la palabra ‘set’ (del inglés establecer) al nombre del atributo. 
El código de la clase Empleado queda de la siguiente forma:
public class Empleado{
 private String cedula;
 private String apellido;
 private String nombre;
 private double horasTrabajadas;
 private double sueldoXHora;
 public 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;
 }
 public double calcularSalario(){
 return horasTrabajadas * sueldoXHora;
 }
 public String getCedula(){
 return cedula;
 }
 public void setCedula(String cedula){
 this.cedula = cedula;
 }
 public String getApellido(){
 return apellido;
 }
 public void setApellido(String apellido){
 this.apellido = apellido;
 }
 public String getNombre(){
 return nombre;
 }
 public void setNombre(String nombre){
 this.nombre = nombre;
 }
45
 public double getHorasTrabajadas(){
 return horasTrabajadas;
 }
 public void setHorasTrabajadas(double horas ){
 this.horasTrabajadas = horas;
 }
 public double getSueldoXHora(){
 return sueldoXHora;
 }
 public void setSueldoXHora(double sueldo ){
 this.sueldoXHora = sueldo;
 }
}
Puntos convenientes a resaltar en el anterior código:
•	 Note que la clase Empleado se ha especificado como pública.
•	 Note que tanto el método constructor de la clase como el método para calcular el 
salario del empleado se encuentran definidos con un nivel de visibilidad público, 
puesto que se desea que cualquier otra clase pueda invocar este servicio que exhibe 
el objeto.
•	 Para cada atributo de la clase se ha definido un método accesor y uno modificador. 
No es obligatorio que un atributo tenga ambos métodos.
Un programador atento debe estar pensando lo siguiente, si uno de los objetivos de los 
niveles de visibilidad era no permitir que desde clases externas se alteren los valores de los 
atributos de una clase, al definir métodos accesores y modificadores se pierde todo lo que 
se había avanzado; y ahora se regresa a una posición aún peor que la inicial en la medida 
en que es necesario escribir una mayor cantidad de código7 (pues las instrucciones que se 
requieren para codificar estos métodos). Aun cuando aparentemente se ha terminado en un 
estado peor al inicial, la realidad es distinta. 
•	 Antes (en la programación estructurada), cualquiera podría alterar los valores de 
las variables globales. Ahora también es posible alterar el valor de los atributos a 
través de métodos modificadores; pero la diferencia es que se pueden escoger cuáles 
métodos modificadores exhibirá una clase a las restantes clases (piense que ahora si 
no se desea cambiar el valor de la cédula de un empleado, simplemente no se define 
como público el método setCedula).
•	 Antes, a una variable global entera definida para aceptar únicamente valores 
positivos podía asignársele un valor fuera del rango en cualquier subrutina de la 
aplicación. Ahora también es posible hacerlo; la diferencia es que hoy la realización 
de dicha asignación se ejecuta invocando un método en el cual puede ubicarse 
7 La mayoría de los entornos de desarrollo para Java brindan la capacidad de generar de forma automática (con unos 
cuantos clics) los métodos accesores y modificadores para los atributos de una clase.
1. TÓPICOS BÁSICOS
46
Todo lo básico que debería saber sobre Programación orientada a objetos en Java
el código de validación para dicho atributo. Por ejemplo, el código parael método 
setHorasTrabajadas puede definirse de la siguiente manera, de tal forma que el objeto no 
pueda aceptar valores negativos para dicho atributo: 
 
public void setHorasTrabajadas(double horas){ 
 if (horas>=0) this.horasTrabajadas=horas; 
 else this.horasTrabajadas=0;
}
terCera aproximaCión a la apliCaCión del CálCulo de la nómina 
mediante programaCión orientada a obJetos
Para esta tercera aproximación los únicos cambios profundos a implementar se encuentran en 
la clase Empleado. La clase Nomina permanece casi que sin alteraciones. 
El código que aparece en negrilla resalta las modificaciones efectuadas con respecto a la anterior 
aproximación. 
public class Empleado{
 private String cedula;
 private String apellido;
 private String nombre;
 private double horasTrabajadas;
 private double sueldoXHora;
 public Empleado(String cedula, String apellido, String nombre, 
 double horasTrabajadas, double sueldoXHora){
 setCedula(cedula);
 setApellido(apellido);
 setNombre(nombre);
 setSueldoXHora(sueldoXHora);
 setHorasTrabajadas(horasTrabajadas);
 }
 public double calcularSalario(){
 return horasTrabajadas * sueldoXHora;
 }
 public String getCedula(){
 return cedula;
 }
 private void setCedula(String cedula){
 if (cedula!=null) this.cedula = cedula;
 else this.cedula = “”;
 }
 public String getApellido(){
 return apellido;
 }
47
 public void setApellido(String apellido){
 if (apellido!=null) this.apellido = apellido;
 else this.apellido=””;
 }
 public String getNombre(){
 return nombre;
 }
 public void setNombre(String nombre){
 if (nombre!=null) this.nombre = nombre;
 else this.nombre=””;
 }
 public double getHorasTrabajadas(){
 return horasTrabajadas;
 }
 public void setHorasTrabajadas(double horas ){
 if (horas>=0) this.horasTrabajadas = horas;
 else this.horasTrabajadas = 0;
 }
 public double getSueldoXHora(){
 return sueldoXHora;
 }
 public void setSueldoXHora(double sueldo ){
 if (sueldo>=0) this.sueldoXHora = sueldo;
 else this.sueldoXHora = 0;
 }
}
import java.io.BufferedReader;
import java.io.InputStreamReader;
public 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();
1. TÓPICOS BÁSICOS
48
Todo lo básico que debería saber sobre Programación orientada a objetos en Java
 System.out.print(“Digite el nombre del empleado: “ );
 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(cedula, apellido, nombre,
 horas, sueldo);
 losEmpleados[i]=unEmpleado;
 }
 for (int i=0;i<numeroEmpleados;i++)
 total = total + losEmpleados[i].calcularSalario();
 
 System.out.println(“\nLa nómina total es: “+ total);
 
 }
}
Puntos convenientes a resaltar en el anterior código:
•	 Como se definen dos clases públicas estas deben crearse en archivos independientes. 
En Java no puede haber dos clases públicas definidas en el mismo archivo.
•	 Se ha agregado código de validaciones simples a cada uno de los métodos modificadores 
de los atributos. Se deja al lector la implementación de demás validaciones que 
considere convenientes.
•	 Note que el constructor de la clase ya no le asigna valores directamente a los atributos, 
sino que ahora utiliza los métodos modificadores; con esto se garantiza que a la hora 
de crear un nuevo objeto de la clase se cumplan las validaciones.
•	 Note que el método para alterar el valor del atributo cédula de la clase Empleado es 
privado. Al definirlo de esta manera, desde ninguna otra clase distinta a Empleado 
podrá ser invocado. 
•	 Una situación que generalmente ocasiona problemas a los programadores que apenas 
comienzan a entender sobre la teoría de objetos corresponde a lo que debe hacerse 
en caso error cuando se suministre un valor para crear un objeto Empleado. La idea 
general que hasta el momento se va a manejar corresponde simplemente a asignar un 
valor por defecto que sea correcto sin indicarle al usuario que se ha presentado un 
error. Ideas como la creación de un ciclo para luego preguntar indefinidamente hasta 
que se pasen parámetros correctos no deben ni siquiera considerarse como alternativas 
de solución. Más adelante, en la sección de manejo de excepciones, se analizará cómo 
debe manejarse esta situación.
49
•	 Es conveniente notar que la clase Empleado simplemente describe la estructura y 
el comportamiento de un conjunto de empleados. Dentro de esta descripción es 
importante resaltar que no se encuentra definido código para leer o escribir información 
por pantalla. Note que todo el código de lectura y de escritura de los datos reside en 
la clase Nomina. Realizar este tipo de desacoplamiento corresponde a una muy buena 
práctica de programación porque incita y facilita la reutilización de código. Imagínese 
que existiera otra aplicación que requiera de trabajar con los objetos de la clase 
Empleado, pero a diferencia de la aplicación de la nómina, esta última trabaja en un 
ambiente gráfico con ventanas y botones; ¿se podría trabajar con la clase Empleado si 
estuviera codificada de tal manera que los datos se leen de pantalla preguntándoselos 
al usuario? La respuesta es un rotundo no.
La idea detrás del encapsulamiento es realmente sencilla, lo que se busca es proteger el 
estado interno de los atributos. Como consecuencia, cuando otra clase requiera trabajar 
con una clase encapsulada, lo hará sin acoplarse a su estructura interna. Al no hacerlo, los 
cambios dentro de la estructura de la clase encapsulada no repercutirán en cambios en el 
código de las clases que la utilicen. 
Analice cómo se encuentran definidas las cosas en el mundo real. Ponga por ejemplo una 
habitación que cuente con iluminación en una casa cualquiera. ¿Qué tan complejo es realizar 
el cambio para que la iluminación de la habitación deje de ser incandescente y se vuelva 
fluorescente? En realidad lo que se requiere es muy poco, simplemente basta con cambiar el 
tipo de foco que alumbra a la habitación. En el mundo de la programación, hay ocasiones 
en que la realización de un cambio que se cree sencillo requeriría, en términos del ejemplo 
anterior: quitar la antigua base del foco, reventar el techo, reventar la pared, quitar los 
alambres que conducen la corriente, instalar nuevos alambres, repellar la pared, arreglar el 
techo, instalar la nueva base y, finalmente, instalar el nuevo tipo de foco. 
El cambio del tipo de foco en una habitación no es una actividad traumática en la vida real 
porque de antemano los fabricantes se ponen de acuerdo en los servicios que exhibirá el 
foco y en lo que se necesita para su funcionamiento. De allí en adelante los fabricantes crean 
focos a partir de las convenciones establecidas (diámetro, profundidad, tipo de material, 
conductibilidad del material, etc.). Así debiera ser también el mundo del desarrollo de 
software, los fabricantes de componentes (software) debieran especificar detalladamente 
como son los servicios que ha de exhibir un elemento (se puede pensar en los servicios 
a exhibir cómo en los métodos públicos de la clase). Los clientes (otros programaso 
aplicaciones) se acoplan a los servicios y no a cómo es que encuentran implementados. Esta 
última idea corresponde a, quizás, la mejor de las prácticas que se pueden tener en el mundo 
de la programación.
Analice el caso de ejemplo que se presenta a continuación. Imagine que Java carece de un 
componente que le permita manipular elementos de tipo fecha y que se encuentra interesado 
en la construcción de tal tipo de componente. 
1. TÓPICOS BÁSICOS
50
Todo lo básico que debería saber sobre Programación orientada a objetos en Java
Los servicios que debe prestar el componente son:
•	 El de conocer el valor del número correspondiente al mes de la fecha.
•	 El de conocer el nombre del mes correspondiente a la fecha.
•	 El de obtener la fecha en el siguiente formato “dd/mm/aaaa”.
•	 El de obtener la fecha en el siguiente formato “dd de nombremes de aaaa”.
Aclaraciones: 
Para efectos de mantener la simplicidad del ejemplo no se contemplan validaciones para 
garantizar que las fechas en realidad sean válidas; por ejemplo, que no exista un 30 de 
febrero, etc. Se deja al lector la implementación de estas y cualquier otra validación que 
considere conveniente.
Los servicios que debe exhibir el componente pueden traducirse a métodos, para los cuales 
se presenta una posible definición:
public int numeroMes()
public String nombreMes()
public String formato1()
public String formato2()
Analícese la definición de las clases que se presentan a continuación 
public class Fecha1{
 private int dia;
 private int mes;
 private int año;
 private String[] nombresMeses;
 public Fecha1(int dia, int mes, int año){
 this.dia = dia;
 this.mes = mes;
 this.año = año;
 this.nombresMeses = new String[13];
 nombresMeses[1]=new String(“Enero”);
 nombresMeses[2]=new String(“Febrero”);
 nombresMeses[3]=new String(“Marzo”);
 nombresMeses[4]=new String(“Abril”);
 nombresMeses[5]=new String(“Mayo”);
 nombresMeses[6]=new String(“Junio”);
 nombresMeses[7]=new String(“Julio”);
 nombresMeses[8]=new String(“Agosto”);
 nombresMeses[9]=new String(“Septiembre”);
 nombresMeses[10]=new String(“Octubre”);
 nombresMeses[11]=new String(“Noviembre”);
 nombresMeses[12]=new String(“Diciembre”);
 }
 public int numeroMes(){
 return mes;
 }
51
 public String nombreMes(){
 return nombresMeses[mes];
 }
 public String formato1(){
 return dia+”/”+mes+”/”+año;
 }
 public String formato2(){
 return dia + “ de “ + nombresMeses[mes] + “ de “ + año;
 }
}
public class Fecha2{
 private int dia;
 private int mes;
 private int año;
 public Fecha2(int dia, int mes, int año){
 this.dia = dia;
 this.mes = mes;
 this.año = año;
 }
 public int numeroMes(){
 return mes;
 }
 public String nombreMes(){
 if (mes==1) return “Enero”;
 if (mes==2) return “Febrero”;
 if (mes==3) return “Marzo”;
 if (mes==4) return “Abril”;
 if (mes==5) return “Mayo”;
 if (mes==6) return “Junio”;
 if (mes==7) return “Julio”;
 if (mes==8) return “Agosto”;
 if (mes==9) return “Septiembre”;
 if (mes==10) return “Octubre”;
 if (mes==11) return “Noviembre”;
 if (mes==12) return “Diciembre”;
 return null;
 }
 public String formato1(){
 return dia+”/”+mes+”/”+año;
 }
 public String formato2(){
 return dia + “ de “ + nombreMes() + “ de “ + año;
 }
}
1. TÓPICOS BÁSICOS
52
Todo lo básico que debería saber sobre Programación orientada a objetos en Java
Imagine que una aplicación empiece a trabajar con la primera implementación; más 
adelante, si dicha aplicación quiere cambiar a la segunda implementación, lo podrá hacer 
sin traumatismo alguno8. Esto se hace posible porque la primera clase exhibe unos métodos 
(servicios) bien definidos, que también los posee la segunda. 
En contraste, imagine que una aplicación utilice una versión modificada de la clase Fecha1. 
En dicha versión modificada, no se definen métodos, sino que simplemente se acceden a los 
atributos. El cambio de la clase Fecha1 por la clase Fecha2 será traumático si en la aplicación 
existen muchas llamadas al atributo nombresMeses, ya que dicho atributo no se encuentra 
presente en la segunda implementación. 
1.8. AtributOs finAles 
Hasta el momento un atributo es una variable para un objeto que puede tomar distintos 
valores, o expresado de otra manera, cuyo valor puede variar en el tiempo. En el paradigma 
de programación procedimental era posible definir variables que fueran constantes, es decir, 
cuyo valor no podía ser modificado; en el paradigma de programación orientada a objetos 
existe también la misma noción enmarcada desde el concepto de atributo final.
Definición de atributo final
Corresponde a un atributo cuyo valor para un objeto no cambia durante la ejecución 
del programa. Toma el primer valor que le sea asignado. 
Para definir un atributo como final se le debe colocar la palabra reservada ‘final� antes de su 
declaración. Por ejemplo, para la clase Empleado se puede pensar en definir el atributo donde 
se almacena la cédula del empleado como un atributo final de tal forma que dicho valor no 
pueda ser alterado posteriormente durante la ejecución de un programa. Aplicando dicho 
cambio, el código quedaría de la siguiente manera:
public class Empleado{
 final private String cedula;
 private String apellido;
 private String nombre;
 private double horasTrabajadas;
 private double sueldoXHora;
 public Empleado(String cedula, String apellido, String nombre, 
 double horasTrabajadas, double sueldoXHora){
 if (cedula!=null) this.cedula = cedula;
 else this.cedula = “”;
 setApellido(apellido);
8 Como ocurría en el ejemplo de la habitación y el tipo de iluminación.
53
 setNombre(nombre);
 setSueldoXHora(sueldoXHora);
 setHorasTrabajadas(horasTrabajadas);
 }
 public double calcularSalario(){
 return horasTrabajadas * sueldoXHora;
 }
 public String getCedula(){
 return cedula;
 }
 
 public String getApellido(){
 return apellido;
 }
 public void setApellido(String apellido){
 if (apellido!=null) this.apellido = apellido;
 else this.apellido=””;
 }
 public String getNombre(){
 return nombre;
 }
 public void setNombre(String nombre){
 if (nombre!=null) this.nombre = nombre;
 else this.nombre=””;
 }
 public double getHorasTrabajadas(){
 return horasTrabajadas;
 }
 public void setHorasTrabajadas(double horas ){
 if (horas>=0) this.horasTrabajadas = horas;
 else this.horasTrabajadas = 0;
 }
 public double getSueldoXHora(){
 return sueldoXHora;
 }
 public void setSueldoXHora(double sueldo ){
 if (sueldo>=0) this.sueldoXHora = sueldo;
 else this.sueldoXHora = 0;
 }
}
Del anterior código es conveniente resaltar que el método modificador del atributo cédula 
ha desaparecido y, además, que el código de validación de dicho atributo ha pasado al 
método constructor. 
Al trabajar con atributos finales tenga en cuenta los siguientes puntos:
•	 Es un grave error de programación definir métodos modificadores para los atributos 
finales. El error en tiempo de compilación que generan evita que se pueda ejecutar 
el código).
1. TÓPICOS BÁSICOS
54
Todo lo básico que debería saber sobre Programación orientada a objetos en Java
•	 En caso de definir un atributo final al que no se le asigna inmediatamente valor en 
su definición, es una obligación inicializar el valor que tendrá dentro del método 
constructor. No hacerlo corresponde a un error grave de programación que evitaría 
la compilación de la clase.
•	 Si se define como final un atributo de tipo de dato primitivo, el primer valor que 
tome este atributo será el que se mantenga durante toda la ejecución del programa; 
en caso de definir como final un atributo de tipo de dato de referencia la referencia, 
inicialmente asignada no podrá ser alterada durante la ejecución del programa.
1.9. AtributOs y métOdOs estáticOs
Al instanciar dos objetos de una misma clase, los valores de los atributosde estos dos objetos 
son totalmente independientes (es decir, la alteración del valor de uno de los atributos en 
un objeto no afecta al valor del mismo atributo en el otro objeto). El siguiente gráfico toma 
como ejemplo la clase Fecha1 y presenta dos posibles objetos que se pueden crear a partir 
de dicha clase.
En determinados escenarios sería deseable lograr que el valor de un atributo lo compartan 
por todos los objetos de una clase, por ejemplo, en el caso presentado en el anterior gráfico 
sería conveniente que todos los objetos de la clase Fecha1 compartieran los valores a 
almacenar en el atributo nombreMeses, así se evitaría tener que instanciar un nuevo array y 
una cantidad de objetos String (para los nombres de los meses) con cada nueva creación de 
un objeto Fecha1.
55
Definición de atributo estático
Corresponde a un atributo de la clase más que a un atributo para los objetos. Al ser 
un atributo de la clase no se crea un valor nuevo por cada nueva instancia de la clase. 
Este atributo toma valor, aunque no existan objetos de la clase. Todos los objetos de 
la clase comparten dicho atributo y pueden acceder y modificar su valor. 
Para definir un atributo como estático basta con anteponerle la palabra reservada ‘static’ 
a su definición. Por ejemplo, se podría realizar la modificación a la clase Fecha1 para que 
trabaje con un atributo estático, el código de dicha clase quedaría de la siguiente manera: 
public class Fecha1{
 private int dia;
 private int mes;
 private int año;
 static private String[] nombresMeses;
 public Fecha1(int dia, int mes, int año){
 this.dia = dia;
 this.mes = mes;
 this.año = año;
 this.nombresMeses = new String[13];
 nombresMeses[1]=new String(“Enero”);
 nombresMeses[2]=new String(“Febrero”);
 nombresMeses[3]=new String(“Marzo”);
 nombresMeses[4]=new String(“Abril”);
 nombresMeses[5]=new String(“Mayo”);
 nombresMeses[6]=new String(“Junio”);
 nombresMeses[7]=new String(“Julio”);
 nombresMeses[8]=new String(“Agosto”);
 nombresMeses[9]=new String(“Septiembre”);
 nombresMeses[10]=new String(“Octubre”);
 nombresMeses[11]=new String(“Noviembre”);
 nombresMeses[12]=new String(“Diciembre”);
 }
 public int numeroMes(){
 return mes;
 }
 public String nombreMes(){
 return nombresMeses[mes];
 }
 public String formato1(){
 return dia+”/”+mes+”/”+año;
 }
 public String formato2(){
 return dia + “ de “ + nombresMeses[mes] + “ de “ + año;
 }
}
1. TÓPICOS BÁSICOS
56
Todo lo básico que debería saber sobre Programación orientada a objetos en Java
La situación sería más clara de entender si se piensa que al instanciar dos objetos con esta 
nueva definición se obtiene una configuración en memoria parecida a la siguiente:
Es conveniente detenerse un momento y analizar cuidadosamente el anterior gráfico. En 
él aparecen dos instancias de la clase Fecha1 (una, con los valores de “1/1/2009” y la otra, 
con los valores de “21/2/2009”). Note que para ambos objetos la referencia para el atributo 
nombresMeses los conduce a otra posición en memoria. 
La definición ubicada en esta última posición de memoria no corresponde a un objeto, sino 
a la definición de la clase. Note que para la clase se encuentra la definición del atributo 
nombresMeses, que al final es el que apunta al objeto array en memoria donde se almacenan 
los nombres de los meses. Es de esperarse que aunque no existan objetos en memoria de la 
clase Fecha1, sí exista la definición de la clase. 
57
sobre la manipulaCión de los atributos estátiCos
Considérese el siguiente código:
class EjemploEstatico{
 public static int a = 50;
}
EjemploEstatico ej1 = new EjemploEstatico();
EjemploEstatico ej2 = new EjemploEstatico();
Como los objetos ej1 y ej2 comparten el valor del atributo a y además este es público, se 
podría modificar dicho valor con cualquiera de las siguientes instrucciones:
ej1.a = 200;
ej2.a = 150;
Pero como este atributo se encuentra definido para la clase, su valor existe aun cuando no existan 
instancias de la clase. La pregunta que se podría estar formulando cualquier programador en 
estos momentos es, ¿cómo es posible conocer en tiempo de ejecución el valor del atributo si no 
existen objetos referenciados a los que se les pueda preguntar el valor? La respuesta es que sí 
es posible conocer o alterar dicho valor, y para ello se debe emplear el nombre de la clase, por 
ejemplo, el siguiente código imprime por pantalla el valor del atributo a
System.out.println(EjemploEstatico.a);
Así como existen atributos estáticos, también es posible definir métodos estáticos.
Definición de método estático
Método definido para la clase. Generalmente opera sobre los atributos de clase. 
Generalmente se define un método estático para manipular las variables estáticas que tenga 
una clase, o para crear librerías de métodos que puedan ser utilizados por varias clases. 
Para especificar que un método de una clase será estático, se debe anteponer a su propia 
definición la palabra reservada ‘static’. 
Considérese el siguiente ejemplo.
public class Fecha1{
 private int dia;
1. TÓPICOS BÁSICOS
58
Todo lo básico que debería saber sobre Programación orientada a objetos en Java
 private int mes;
 private int año;
 static private String[] nombresMeses;
 public Fecha1(int dia, int mes, int año){
 this.dia = dia;
 this.mes = mes;
 this.año = año;
 this.nombresMeses = new String[13];
 nombresMeses[1]=new String(“Enero”);
 nombresMeses[2]=new String(“Febrero”);
 nombresMeses[3]=new String(“Marzo”);
 nombresMeses[4]=new String(“Abril”);
 nombresMeses[5]=new String(“Mayo”);
 nombresMeses[6]=new String(“Junio”);
 nombresMeses[7]=new String(“Julio”);
 nombresMeses[8]=new String(“Agosto”);
 nombresMeses[9]=new String(“Septiembre”);
 nombresMeses[10]=new String(“Octubre”);
 nombresMeses[11]=new String(“Noviembre”);
 nombresMeses[12]=new String(“Diciembre”);
 }
 public int numeroMes(){
 return mes;
 }
 public String nombreMes(){
 return nombresMeses[mes];
 }
 public String formato1(){
 return dia+”/”+mes+”/”+año;
 }
 public String formato2(){
 return dia + “ de “ + nombresMeses[mes] + “ de “ + año;
 }
 public static String nombreMes(int numMes){
 return nombresMeses[numMes];
 }
}
En el anterior código se definió un método estático que permite obtener el nombre de un 
mes pasando como parámetro el número de dicho mes. La diferencia con el otro método que 
tiene el mismo propósito es que uno recibe como parámetro el número del mes, mientras 
que el otro trabaja con el número de mes que guarda el objeto. Para ejecutar el método 
estático se puede emplear la siguiente instrucción.
Fecha1.nombreMes(11);
59
Al trabajar con métodos estáticos tenga en cuenta los siguientes puntos:
•	 Al igual que los atributos estáticos, se puede acceder a un método estático puede 
ser accedido sin necesidad de haber instanciado la clase previamente. Se emplea el 
nombre de la clase como la referencia para la invocación del método.
•	 Es un grave error de programación emplear variables no estáticas desde métodos 
estáticos. En caso de hacerlo, se generará un error en tiempo de compilación. Las 
variables que pueden utilizarse al interior de estos métodos son aquellas variables 
locales que han sido definidas dentro del método, variables locales que correspondan 
a parámetros del método o atributos estáticos de la clase. 
•	 Es un grave error de programación invocar métodos no estáticos desde métodos 
estáticos. En caso de hacerlo, se generará un error en tiempo de compilación. Desde 
un método estático solo pueden invocarse otros métodos estáticos de la clase.
•	 Hay tener cuidado de no degenerar el uso tanto de los métodos como de los atribu-
tos estáticos. Si se analiza con cuidado, cualquier aplicación implementada bajo el 
paradigma procedimental puede ser codificada bajo el paradigma orientado a objeto 
como una serie de atributos y métodosestáticos, que distorsiona el sentido para el 
cual fue creado este modificador. 
1.10. HerenciA
Cuarta aproximaCión a la apliCaCión del CálCulo de la nómina 
mediante programaCión orientada a obJetos
En esta cuarta aproximación serán tenidos en cuenta los cambios sugeridos en la sección donde 
se trató los atributos finales; y adicionalmente se incluirá la definición de dos interfaces de 
usuario (una donde se pide el número de empleados de la empresa y otra donde se trabaja bajo 
un menú de opciones)
El código que aparece en negrilla resalta las modificaciones efectuadas con respecto a la anterior 
aproximación. 
public class Empleado{
 final private String cedula;
 private String apellido;
 private String nombre;
 private double horasTrabajadas;
 private double sueldoXHora;
1. TÓPICOS BÁSICOS
60
Todo lo básico que debería saber sobre Programación orientada a objetos en Java
public Empleado(String cedula, String apellido, String nombre, 
 double horasTrabajadas, double sueldoXHora){
 if (cedula!=null) this.cedula = cedula;
 else this.cedula = “”;
 setApellido(apellido);
 setNombre(nombre);
 setSueldoXHora(sueldoXHora);
 setHorasTrabajadas(horasTrabajadas);
 }
 public double calcularSalario(){
 return horasTrabajadas * sueldoXHora;
 }
 public String getCedula(){
 return cedula;
 }
 public String getApellido(){
 return apellido;
 }
 public void setApellido(String apellido){
 if (apellido!=null) this.apellido = apellido;
 else this.apellido=””;
 }
 public String getNombre(){
 return nombre;
 }
 public void setNombre(String nombre){
 if (nombre!=null) this.nombre = nombre;
 else this.nombre=””;
 }
 public double getHorasTrabajadas(){
 return horasTrabajadas;
 }
 public void setHorasTrabajadas(double horas ){
 if (horas>=0) this.horasTrabajadas = horas;
 else this.horasTrabajadas = 0;
 }
 public double getSueldoXHora(){
 return sueldoXHora;
 }
 public void setSueldoXHora(double sueldo ){
 if (sueldo>=0) this.sueldoXHora = sueldo;
 else this.sueldoXHora = 0;
 }
}
61
import java.io.BufferedReader;
import java.io.InputStreamReader;
public class Nomina1{
 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();
 System.out.print(“Digite el nombre del empleado: “ );
 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(cedula, apellido, nombre,
 horas, sueldo);
 losEmpleados[i]=unEmpleado;
 }
 for (int i=0;i<numeroEmpleados;i++)
 total = total + losEmpleados[i].calcularSalario();
 
 System.out.println(“\nLa nómina total es: “+ total);
 
 }
}
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;
1. TÓPICOS BÁSICOS
62
Todo lo básico que debería saber sobre Programación orientada a objetos en Java
 
 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”);
 System.out.print(“Escoja opción: “);
 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(cedula, apellido, nombre,
 horas, sueldo);
 losEmpleados[numeroEmpleados]=unEmpleado;
 numeroEmpleados++;
 } else if (opcionMenu==2){
 for (int i=0;i<numeroEmpleados;i++)
 total = total + losEmpleados[i].calcularSalario();
 
 System.out.println(“\nLa nómina total es: “+ total);
 }
 }
 }
}
Puntos convenientes a resaltar en el anterior código:
•	 Note que tanto la clase Nomina1, como Nomina2 trabajan con la misma definición de la 
clase Empleado. Tal y como se mencionó anteriormente, este grado de desacoplamiento 
es deseable dentro del mundo del desarrollo de aplicaciones.
63
quinta aproximaCión a la apliCaCión del CálCulo de la nómina 
mediante programaCión orientada a obJetos
Existe una particularidad en el código presentado en la Cuarta aproximación a la aplicación 
del cálculo de la nómina. Las clases Nomina1 y Nomina2 presentan código que se encarga de 
la interacción con el usuario para poder realizar el cálculo de la nómina. Ambas clases tienen 
un estilo de interacción distinto, sin embargo, ambas comparten una gran cantidad de código. 
Analícese que ambas definen un array de empleados, ambas requieren de una variable que 
cuenta el número de empleados registrados, ambas realizan la misma operación para registrar 
un nuevo empleado y ambas tienen exactamente el mismo código para calcular la nómina total 
de la empresa. No sería de extrañar que estas mismas coincidencias aparecieran si se quisiera 
codificar una nueva clase que representa la interacción con el usuario.
Esta situación da para pensar que el código presentado en esa cuarta aproximación puede ser 
mejorado aún más. Para hacerlo sería necesario incluir una nueva clase donde se pudieran 
incluir todas esas instrucciones que se repiten. 
Pero, ¿por qué ha ocurrido esto? Si supuestamente, hasta el momento, se ha estado siguiendo a 
cabalidad el enfoque de la orientación a objetos. Lo que ha sucedido es que desde el principio 
del modelamiento del problema se ha cometido un error, se ha obviado la definición de una 
clase. Si se lee detenidamente el enunciado del caso de ejemplo de la aplicación del cálculo de 
la nómina, puede darse cuenta que allí se hace referencia, además de los objetos empleados, al 
objeto que los agrupa: la empresa.
En realidad el escenario debió ser imaginado de la siguiente manera:
1. TÓPICOS BÁSICOS
64
Todo lo básico que debería saber sobre Programación orientada a objetos en Java
La idea con la que se va a trabajar es la siguiente: un empleado por sí solo no tiene mucho 
sentido; para el problema en mención, él debe trabajar en una empresa. En una empresa trabaja 
un conjunto de empleados; si se calcula cuánto debe pagarle a cada empleado y totaliza dicha 
cantidad, se conocerá cuánto paga finalmente la empresa por concepto de nómina.
Se hace necesario entonces codificar la clase Empresa. Para poder describir una empresa, 
se incluirán los atributos nit, nombre y dirección. Adicionalmente, como en una empresatrabajan empleados, entonces se hace necesario incluir como un atributo un array de objetos 
de la clase Empleado y su respectivo contador para conocer cuántos empleados posee dicha 
empresa. La alternativa que se adoptará es que un objeto Empresa al crearse no contenga 
empleados, y según se requiera pueden irse adicionando. Se requiere un método para esto 
último y, además, se debe definir un método para obtener el cálculo total de la nómina.
public class Empresa{
 final private String nit;
 private String nombre;
 private String direccion;
 private Empleado[] empleados;
 private int numeroEmpleados;
 public Empresa(String nit, String nombre, String direccion){
 if (nit!=null) this.nit = nit;
 else this.nit = “”;
 setNombre(nombre);
 setDireccion(direccion);
 empleados = new Empleado[50];
 numeroEmpleados = 0; 
 }
 
 public String getNit(){
 return nit;
 }
 public String getNombre(){
 return nombre;
 }
 public void setNombre(String nombre){
 if (nombre!=null) this.nombre = nombre;
 else this.nombre=””;
 }
 public String getDireccion(){
 return direccion;
 }
 public void setDireccion(String direccion){
 if (direccion!=null) this.direccion = direccion;
 else this.direccion=””;
 }
65
 public int getNumeroEmpleados(){
 return numeroEmpleados;
 }
 public void adicionarEmpleado(String cedula, String apellido, 
 String nombre, double horasTrabajadas, double sueldoXHora){
 Empleado e = new Empleado(cedula, apellido, nombre, horasTrabajadas,
 sueldoXHora);
 empleados[numeroEmpleados]=e;
 numeroEmpleados++;
 }
 public double calcularNominaTotal(){
 double total = 0;
 for (int i=0; i<numeroEmpleados; i++){
 total = total + empleados[i].calcularSalario();
 }
 return total;
 }
}
Puntos convenientes a resaltar en el anterior código:
•	 Para varios atributos definidos en la clase, no se pasan valores iniciales a través de 
parámetros. La idea es que cuando se cree un objeto de tipo Empresa, no contenga 
ningún Empleado; por eso el numeroEmpleados es 0 y el array de empleados empieza 
vacío. 
•	 Para garantizar el encapsulamiento de la clase, no se permite acceder directamente al 
array de empleados que maneja la empresa ni tampoco alterar directamente el valor 
del atributo numeroEmpleados. 
•	 El método accesor para el atributo numeroEmpleados devuelve el valor incrementándolo 
en una unidad. Esto se debe a que en Java los arrays inician en la posición 0.
•	 El método adicionarEmpleado requiere de toda la información del Empleado (porque 
no la conoce de antemano). Los parámetros que se utilizan son los mismos que define 
el constructor de la clase.
•	 El método calcularNominaTotal recorre todo el array y le invoca a cada Empleado el 
método que tiene dicha clase que permite calcular su salario.
•	 Al igual como se mencionó en el último punto, es conveniente resaltar en el apartado 
de la Tercera aproximación a la aplicación del cálculo de la nómina, la clase Empresa 
describe la estructura y comportamiento de conjunto de objetos Empresa, pero en 
ella no se incluyen instrucciones para leer los datos, o para escribir por pantalla 
información; todo esto para aumentar el desacoplamiento entre los componentes y 
facilitar su reutilización.
1. TÓPICOS BÁSICOS
66
Todo lo básico que debería saber sobre Programación orientada a objetos en Java
Permaneciendo inalterada la definición de la clase Empleado, y considerando la nueva 
definición de la clase Empresa, se podría reescribir las implementación de la clase Nomina1 de 
la siguiente manera (la reimplementación de la clase Nomina2 se deja como trabajo al lector). El 
código que aparece en negrilla resalta las modificaciones efectuadas con respecto a la anterior 
aproximación.
import java.io.BufferedReader;
import java.io.InputStreamReader;
public class Nomina1{
 public static void main(String[] args) throws Exception{
 Empresa pyme = new Empresa(“123”,”Acme inc. “,”805 Silicon Valley”);
 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: “); 
 int 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.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();
 pyme.adicionarEmpleado(cedula, apellido, nombre, horas, sueldo);
 }
 total = pyme.calcularNominaTotal();
 
 System.out.println(“\nLa nómina total es: “+ total);
 
 }
}
 
67
 
Puntos convenientes a resaltar del anterior código:
•	 Note que en esta nueva versión de la clase desaparece la definición del contador y del 
array de empleados. Todo eso será administrado ahora a través de un objeto de la clase 
Empresa.
•	 Para mantener simple el código, se han asignado unos valores iniciales para los 
atributos nit, nombre y dirección de la empresa. Se podría alterar la definición de 
la clase para hacer que estos datos también se le preguntarán al usuario al iniciarse la 
ejecución de la aplicación.
•	 Note que ahora la clase Nomina1 no instancia al objeto Empleado, simplemente invoca 
al método adicionarEmpleado de la clase Empresa que se encarga de dicha función. 
•	 Todo el código del cálculo de la nómina desaparece y en su lugar ahora se encuentra 
la invocación al método calcularNominaTotal de la clase Empresa.
•	 La clase Nomina2 también puede escribirse en función de la clase Empresa. Esta nueva 
aproximación es una mejor solución al problema del cálculo de la nómina porque 
facilitaría la modificación de la aplicación, en caso de ser necesaria una actualización 
en la fórmula del cálculo de la nómina total de la empresa, para considerar todos los 
aportes parafiscales (como pensión, salud, entre otras). Ante dichas circunstancias, 
solo sería necesario modificar el código del método de una clase, y permanecerían sin 
cambios las clases Nomina1 y Nomina2.
En aras de analizar más características y conceptos de la programación orientada a objetos 
se aumenta la complejidad del enunciado del caso de ejemplo del cálculo de la nómina, 
que queda de la siguiente manera. Cierta compañía que paga a sus empleados de manera 
mensual desea ayuda para el desarrollo un programa a fin de efectuar el cálculo de su 
nómina. La empresa contrata empleados bajo las siguientes modalidades:
•	 Existen empleados a quienes se les paga según el número de horas trabajadas y del 
valor por hora convenido previamente con cada uno. 
•	 Existen empleados a quienes se les paga al mes un sueldo básico más un porcentaje 
sobre las ventas efectuadas en el mes. El valor del sueldo básico y el porcentaje sobre 
las ventas son convenidos previamente con cada uno.
•	 Existen empleados a quienes se les paga un sueldo fijo mensual
Se mantendrán las restantes limitaciones iniciales contempladas en el enunciado original 
del caso de ejemplo.
1. TÓPICOS BÁSICOS
68
Todo lo básico que debería saber sobre Programación orientada a objetos en Java
sexta aproximaCión a la apliCaCión del CálCulo de la nómina 
mediante programaCión orientada a obJetos
Aunque se podría considerar la posibilidad de utilizar la clase Empleado para representar y 
trabajar con los demás tipos de Empleado, es más conveniente en este punto definir una serie 
de clases adicionales que ayuden en dicho sentido.
 
Se creará una nueva clase para representar a los empleados cuyo salariose calcula de acuerdo 
a su sueldo básico y su comisión. El nombre que se le asignará a esta clase corresponde a 
EmpleadoXComision.
public class EmpleadoXComision{
 final private String cedula;
 private String apellido;
 private String nombre;
 private double sueldoBasico;
 private double porcentaje;
 private double ventasTotales;
 public EmpleadoXComision(String cedula, String apellido, 
 String nombre, double sueldoBasico, double porcentaje, 
 double ventasTotales){
 if (cedula!=null) this.cedula = cedula;
 else this.cedula = “”;
 setApellido(apellido);
 setNombre(nombre);
 setSueldoBasico(sueldoBasico);
 setPorcentaje(porcentaje);
 setVentasTotales(ventasTotales);
 }
 public double calcularSalario(){
 return sueldoBasico + porcentaje * ventasTotales;
 }
 public String getCedula(){
 return cedula;
 }
 public String getApellido(){
 return apellido;
 }
 public void setApellido(String apellido){
 if (apellido!=null) this.apellido = apellido;
 else this.apellido=””;
 }
69
 public String getNombre(){
 return nombre;
 }
 public void setNombre(String nombre){
 if (nombre!=null) this.nombre = nombre;
 else this.nombre=””;
 }
 public double getSueldoBasico(){
 return sueldoBasico;
 }
 public void setSueldoBasico(double sueldo){
 if (sueldo>=0) this.sueldoBasico = sueldo;
 else this.sueldoBasico = 0;
 }
 public double getPorcentaje(){
 return porcentaje;
 }
 public void setPorcentaje(double porcentaje ){
 if (porcentaje>=0 && porcentaje<=1) this.porcentaje = porcentaje;
 else this.porcentaje = 0;
 }
 public double getVentasTotales(){
 return ventasTotales;
 }
 public void setVentasTotales(double ventas){
 if (ventas>=0) this.ventasTotales = ventas;
 else this.ventasTotales = 0;
 }
} 
Se definirá una clase que represente a los empleados que devengan un sueldo fijo mensual. El 
nombre de esta clase será EmpleadoSueldoFijo.
public class EmpleadoSueldoFijo{
 final private String cedula;
 private String apellido;
 private String nombre;
 private double sueldoFijo;
 public EmpleadoSueldoFijo(String cedula, String apellido, 
 String nombre, double sueldoFijo){
 if (cedula!=null) this.cedula = cedula;
 else this.cedula = “”;
1. TÓPICOS BÁSICOS
70
Todo lo básico que debería saber sobre Programación orientada a objetos en Java
 setApellido(apellido);
 setNombre(nombre);
 setSueldoFijo(sueldoFijo);
 }
 public double calcularSalario(){
 return sueldoFijo;
 }
 public String getCedula(){
 return cedula;
 }
 public String getApellido(){
 return apellido;
 }
 public void setApellido(String apellido){
 if (apellido!=null) this.apellido = apellido;
 else this.apellido=””;
 }
 public String getNombre(){
 return nombre;
 }
 public void setNombre(String nombre){
 if (nombre!=null) this.nombre = nombre;
 else this.nombre=””;
 }
 public double getSueldoFijo(){
 return sueldoFijo;
 }
 public void setSueldoFijo(double sueldo){
 if (sueldo>=0) this.sueldoFijo = sueldo;
 else this.sueldoFijo = 0;
 }
}
Como la intención es la de asignar nombres representativos a las clases, a partir de esta 
aproximación a la aplicación de la nómina a la clase Empleado se le cambiará el nombre por 
EmpleadoXHora. 
La clase Empresa debe ser ajustada para que ahora se permita el trabajo con todos los tipos 
de empleados presentados anteriormente. El código que aparece en negrilla resalta las 
modificaciones efectuadas con respecto a la anterior aproximación.
71
public class Empresa{
 final private String nit;
 private String nombre;
 private String direccion;
 private EmpleadoXHora[] empleados1;
 private EmpleadoXComision[] empleados2;
 private EmpleadoSueldoFijo[] empleados3;
 private int numeroEmpleados1, numeroEmpleados2, numeroEmpleados3;
 public Empresa(String nit, String nombre, String direccion){
 if (nit!=null) this.nit = nit;
 else this.nit=””;
 setNombre(nombre);
 setDireccion(direccion);
 empleados1 = new EmpleadoXHora[50];
 empleados2 = new EmpleadoXComision[50];
 empleados3 = new EmpleadoSueldoFijo[50];
 numeroEmpleados1= 0; numeroEmpleados2= 0; numeroEmpleados3= 0;
 }
 public String getNit(){
 return nit;
 }
 public String getNombre(){
 return nombre;
 }
 public void setNombre(String nombre){
 if (nombre!=null) this.nombre = nombre;
 else this.nombre=””;
 }
 public String getDireccion(){
 return direccion;
 }
 public void setDireccion(String direccion){
 if (direccion!=null) this.direccion = direccion;
 else this.direccion=””;
 }
 public int getNumeroEmpleados1(){
 return numeroEmpleados1;
 }
 public int getNumeroEmpleados2(){
 return numeroEmpleados2;
 }
 public int getNumeroEmpleados3(){
 return numeroEmpleados3;
 }
1. TÓPICOS BÁSICOS
72
Todo lo básico que debería saber sobre Programación orientada a objetos en Java
 public void adicionarEmpleadoXHora(String cedula, String apellido, 
 String nombre, double horasTrabajadas, double sueldoXHora){
 empleados1[numeroEmpleados1] = new EmpleadoXHora(cedula, apellido,
 nombre, horasTrabajadas, sueldoXHora);
 numeroEmpleados1++;
 }
 public void adicionarEmpleadoXComision(String cedula, String apellido,
 String nombre, double sueldoBasico, double porcentaje, 
 double ventasTotales){
 empleados2[numeroEmpleados2] = new EmpleadoXComision(cedula,
 apellido, nombre, sueldoBasico, porcentaje, ventasTotales);
 numeroEmpleados2++;
 }
 public void adicionarEmpleadoSueldoFijo(String cedula, 
 String apellido, String nombre, double sueldoFijo){
 empleados3[numeroEmpleados3] = new EmpleadoSueldoFijo(cedula, 
 apellido, nombre, sueldoFijo);
 numeroEmpleados3++;
 }
 public double calcularNominaTotal(){
 double total = 0;
 for(int i=0;i<numeroEmpleados1; i++)
 total = total + empleados1[i].calcularSalario();
 
 for(int i=0;i<numeroEmpleados2; i++)
 total = total + empleados2[i].calcularSalario();
 for(int i=0;i<numeroEmpleados3; i++)
 total = total + empleados3[i].calcularSalario();
 return total; 
 }
}
 Puntos convenientes a resaltar en el código anterior:
•	 Note que en esta definición aparecen tres atributos de tipo array. Un array para 
almacenar referencias a objetos de tipo EmpleadoXHora, otro array para almacenar 
referencias a objetos de tipo EmpleadoXComision y, finalmente, otro para almacenar 
referencias a objetos tipo EmpleadoSueldoFijo. 
•	 Cada array tiene su propio contador.
•	 Note que por cada tipo de empleado existe un método para adicionar empleados a la 
empresa. 
•	 Note que el método calcularNominaTotal fue modificado. Ahora recorre todos los 
arrays de los objetos. 
73
La clase que administra la interacción con el usuario también requiere una serie de 
modificaciones. El código que aparece en negrilla resalta las modificaciones efectuadas con 
respecto a la anterior aproximación.
import java.io.BufferedReader;
import java.io.InputStreamReader;
public class Nomina1{
 public static void main(String[] args) throws Exception{
 Empresa pyme = new Empresa(“123”,”Acme inc. “,”805 Silicon Valley”);
 String cedula, apellido, nombre; 
 double total = 0;
 
 BufferedReader br = new BufferedReader( 
 new InputStreamReader( System.in ) );
 
 System.out.print(“Digite numero de empleados sueldo por hora: “); 
 int numeroEmpleados1 = Integer.valueOf(br.readLine()).intValue();
 
 for(int i=0;i<numeroEmpleados1;i++){
 double horas, sueldo;
 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 del empleado: “);
 horas = Double.valueOf(br.readLine()).doubleValue();System.out.print(“Digite sueldo por hora del empleado: “ );
 sueldo = Double.valueOf(br.readLine()).doubleValue();
 pyme.adicionarEmpleadoXHora(cedula, apellido, nombre, horas,
 sueldo);
 }
 System.out.print(“Digite numero de empleados por comision: “); 
 int numeroEmpleados2 = Integer.valueOf(br.readLine()).intValue();
 
 for(int i=0;i<numeroEmpleados2;i++){
 double sueldo, porcentaje, ventas;
 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();
1. TÓPICOS BÁSICOS
74
Todo lo básico que debería saber sobre Programación orientada a objetos en Java
 System.out.print(“Digite sueldo basico empleado: “);
 sueldo = Double.valueOf(br.readLine()).doubleValue(); 
 System.out.print(“Digite porcentaje del empleado: “ );
 porcentaje = Double.valueOf(br.readLine()).doubleValue();
 System.out.print(“Digite ventas totales del empleado: “ );
 ventas = Double.valueOf(br.readLine()).doubleValue();
 pyme.adicionarEmpleadoXComision(cedula, apellido, nombre, sueldo,
 porcentaje, ventas);
 }
 System.out.print(“Digite numero de empleados sueldo fijo: “); 
 int numeroEmpleados3 = Integer.valueOf(br.readLine()).intValue()
 
 for(int i=0;i<numeroEmpleados3;i++){
 double sueldo;
 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 sueldo basico empleado: “);
 sueldo = Double.valueOf(br.readLine()).doubleValue(); 
 pyme.adicionarEmpleadoSueldoFijo(cedula, apellido, nombre, 
 sueldo);
 }
 total = pyme.calcularNominaTotal();
 
 System.out.println(“\nLa nómina total es: “+ total);
 
 }
}
La alternativa de solución escogida para la aplicación de la nómina requiere la codificación 
de tres clases distintas, una para cada tipo de empleado (Esta solución se presenta en 
el apartado Sexta aproximación a la aplicación del cálculo de la nómina mediante 
programación orientada a objetos). Es evidente que cada una de estas clases será diferente 
de las restantes, pero también es obvio que tendrán muchos códigos en común (la definición 
de algunos atributos, definición de algunos métodos accesores y modificadores, etc.). 
Existe una noción dentro del mundo de la programación orientada a objetos que podría 
evitar tener que escribir el mismo código varias veces para este caso en particular. Esta 
noción se conoce con el nombre de herencia.
75
Definición de herencia
Capacidad de crear clases que adquieren de manera automática los atributos y métodos 
de otras ya existentes, al mismo tiempo que añade atributos y métodos propios o 
modificar algunos de los existentes.
La idea detrás de la herencia es definir una clase en función de otra u otras. Para el caso 
que se analiza podría ser útil emplear esta noción, definiendo las clases EmpleadoXHora, 
EmpleadoXComision y EmpleadoSueldoFijo en función de alguna otra, de tal manera que las 
clases de los tipos de empleados ganen la definición de los atributos y métodos de otra clase 
más general. En esta clase general debe quedar lo que es similar a todas. A continuación se 
muestra una posible codificación para esta clase que ha sido denominada como Empleado.
public class Empleado{
 private String cedula;
 private String apellido;
 private String nombre; 
 public String getCedula(){
 return cedula;
 }
 public String getApellido(){
 return apellido;
 }
 public void setApellido(String apellido){
 if (apellido!=null) this.apellido = apellido;
 else this.apellido=””;
 }
 public String getNombre(){
 return nombre;
 }
 public void setNombre(String nombre){
 if (nombre!=null) this.nombre = nombre;
 else this.nombre=””;
 }
}
Puntos convenientes a resaltar en el código anterior:
•	 Para hacer más simple la explicación de la noción de herencia, por lo pronto se 
omitirá la definición del atributo cedula como final. 
•	 Aunque el método para realizar el cálculo del salario es común a todos los tipos 
de empleados, su código interno no fue incluido dentro de la definición de la clase 
Empleado porque es distinto en todas. 
1. TÓPICOS BÁSICOS
76
Todo lo básico que debería saber sobre Programación orientada a objetos en Java
Teniendo la definición de la clase general, que en adelante será denominada superclase o 
clase padre, solo falta por conocer cómo debe ser la definición de las clases específicas, que 
desde ahora serán denominadas subclases o clases hijas, para ganen los atributos y métodos. 
La palabra reservada extends posibilita esta acción, la cual debe colocarse después de la 
instrucción de definición de la clase para entonces utilizarla. Después de esta definición 
se dice que la subclase extiende o hereda de la superclase. La relación existente entre una 
subclase y su superclase puede denotarse con la frase ‘es un tipo de’, de tal forma que se 
puede leer que una subclase es un tipo de una superclase (no es correcto leerlo en el sentido 
contrario). 
El siguiente gráfico busca facilitar la comprensión del concepto de herencia. ClaseHija1 y 
ClaseHija2 extienden de ClasePadre (denotado en la gráfica con la flecha que apunta hacia 
ClasePadre); al definir esta relación entre clases, automáticamente ClaseHija1 y ClaseHija2 
ganan la definición de los atributos y métodos definidos en ClasePadre. Adicionalmente, 
ClaseHija1 y ClaseHija2 añaden más atributos y métodos a su definición.
La definición de las subclases para el ejemplo del cálculo de la nómina quedaría de la 
siguiente manera. 
public class EmpleadoXHora extends Empleado{
 private double horasTrabajadas;
 private double sueldoXHora;
 public EmpleadoXHora(String cedula, String apellido, String nombre, 
 double horasTrabajadas, double sueldoXHora){
 if (cedula!=null) this.cedula = cedula;
 else this.cedula = “”;
77
 setApellido(apellido);
 setNombre(nombre);
 setSueldoXHora(sueldoXHora);
 setHorasTrabajadas(horasTrabajadas);
 }
 public double calcularSalario(){
 return horasTrabajadas * sueldoXHora;
 }
 public double getHorasTrabajadas(){
 return horasTrabajadas;
 }
 public void setHorasTrabajadas(double horas ){
 if (horas>=0) this.horasTrabajadas = horas;
 else this.horasTrabajadas = 0;
 }
 public double getSueldoXHora(){
 return sueldoXHora;
 }
 public void setSueldoXHora(double sueldo ){
 if (sueldo>=0) this.sueldoXHora = sueldo;
 else this.sueldoXHora = 0;
 }
}
public class EmpleadoXComision extends Empleado{
 private double sueldoBasico;
 private double porcentaje;
 private double ventasTotales;
 public EmpleadoXComision(String cedula, String apellido, String nombre, 
 double sueldoBasico, double porcentaje, double ventasTotales){
 if (cedula!=null) this.cedula = cedula;
 else this.cedula = “”;
 setApellido(apellido);
 setNombre(nombre);
 setSueldoBasico(sueldoBasico);
 setPorcentaje(porcentaje);
 setVentasTotales(ventasTotales);
 }
 public double calcularSalario(){
 return sueldoBasico + porcentaje * ventasTotales;
 }
 public double getSueldoBasico(){
 return sueldoBasico;
 }
 public void setSueldoBasico(double sueldo){
 if (sueldo>=0) this.sueldoBasico = sueldo;
 else this.sueldoBasico = 0;
1. TÓPICOS BÁSICOS
78
Todo lo básico que debería saber sobre Programación orientada a objetos en Java
 }
 public double getPorcentaje(){
 return porcentaje;
 }
 public void setPorcentaje(double porcentaje ){
 if (porcentaje>=0 && porcentaje<=1) this.porcentaje = porcentaje;
 else this.porcentaje = 0;}
 public double getVentasTotales(){
 return ventasTotales;
 }
 public void setVentasTotales(double ventas){
 if (ventas>=0) this.ventasTotales = ventas;
 else this.ventasTotales = 0;
 }
}
public class EmpleadoSueldoFijo extends Empleado{
 private double sueldoFijo;
 public EmpleadoSueldoFijo(String cedula, String apellido, String nombre, 
 double sueldoFijo){
 if (cedula!=null) this.cedula = cedula;
 else this.cedula = “”;
 setApellido(apellido);
 setNombre(nombre);
 setSueldoFijo(sueldoFijo);
 }
 public double calcularSalario(){
 return sueldoFijo;
 }
 public double getSueldoFijo(){
 return sueldoFijo;
 }
 public void setSueldoFijo(double sueldo){
 if (sueldo>=0) this.sueldoFijo = sueldo;
 else this.sueldoFijo = 0;
 }
}
La cantidad de código se redujo considerablemente. Lastimosamente, la definición de las 
anteriores clases marca errores en tiempo de compilación con la manipulación del atributo 
cedula. Para explicar el porqué de este error hay que retomar lo analizado en la definición 
del nivel de visibilidad privado. Este tipo de visibilidad define que sólo al interior de la clase 
Empleado (donde se encuentra definido como privado) puede ser visible el contenido del 
atributo cedula. Como las clases EmpleadoXHora, EmpleadoXComision y EmpleadoSueldoFijo 
no son la clase Empleado, por eso se marca el error. El error no se presenta con los atributos 
79
apellido y nombre porque en los métodos constructores de las subclases no se hace referencia 
directa al valor del atributo, sino que se utilizan los métodos modificadores (públicos) de 
cada atributo.
Para poder trabajar con este tipo de situaciones, cuando existen atributos o métodos privados 
dentro de la superclase que necesitan ser manipulados por la subclase, se define un nuevo 
nivel de visibilidad.
Definición de nivel de visibilidad protegido
Cuando se aplica este nivel de visibilidad a un atributo o método, se especifica 
que dicha información o acción es visible al interior de la clase donde se encuentra 
definido y al interior de cualquier subclase que esta presente. Cuando se aplica a una 
clase, esta sólo puede ser utilizada en otra clase que pertenezca al mismo paquete. 
Adicionalmente a todo lo mencionado para este nivel de visibilidad, Java permite que los 
atributos y métodos declarados como protegidos sean visibles para cualquier clase que se 
encuentre en el mismo paquete. Para especificar un atributo o método protegido en Java se 
le debe anteponer a su definición la palabra reservada protected. 
Después de presentado este nuevo nivel de visibilidad se disponen de herramientas para 
solucionar esta situación de dos maneras:
•	 La primera alternativa consistiría en definir dentro de la clase Empleado el método 
modificador del atributo cedula. Este método debe ser utilizado por el constructor 
de todas las subclases para inicializar el valor de ese atributo. El problema con esta 
alternativa es que si se deseara definir el atributo cedula de los empleados como 
final, no se puede definir un método modificador para dicho atributo.
•	 La segunda alternativa consistiría en modificar la definición de la clase Empleado y 
marcar al atributo cedula como protegido; asi las subclases pueden manipularlo. La 
desventaja con esta alternativa es que el código de validación de este campo debería 
repetirse en todos los constructores de las subclases.
Mientras el código de las clases EmpleadoXHora, EmpleadoXComision y EmpleadoSueldoFijo 
permanece sin cambios, la clase Empleado quedaría de la siguiente manera:
public class Empleado{
 protected final String cedula;
 private String apellido;
 private String nombre; 
 public String getCedula(){
 return cedula;
 }
 public String getApellido(){
 return apellido;
 }
1. TÓPICOS BÁSICOS
80
Todo lo básico que debería saber sobre Programación orientada a objetos en Java
 public void setApellido(String apellido){
 if (apellido!=null) this.apellido = apellido;
 else this.apellido=””;
 }
 public String getNombre(){
 return nombre;
 }
 public void setNombre(String nombre){
 if (nombre!=null) this.nombre = nombre;
 else this.nombre=””;
 }
}
sobre la Clase obJeCt
El lenguaje de programación Java tiene dentro de su definición una clase especial, la clase 
Object.
Por definición, esta clase es la superclase de todas las clases en Java. Todas las clases que existen 
y que lleguen a existir (a excepción de ella misma) terminan extendiendo de forma directa o 
indirecta a esta clase. Esto quiere decir que todas las clases adquieren de forma automática 
todos los atributos y métodos de esta clase.
Un lector perspicaz debe estar pensando, ¿cómo hace el lenguaje de programación para 
garantizar que todas las clases extiendan de la clase Object? Además, si ninguno de los 
ejemplos que han sido codificados hasta el momento incluyen la definición extends Object, 
¿cómo pueden decir que estas clases heredan de esta superclase? Ambos cuestionamientos 
se solucionan con la misma respuesta; Java de forma automática, a toda clase que no tenga 
especificado que extiende de otra le incluye la definición extends Object; así se asegura, de 
forma directa o indirecta, que todas las clases extiendan Object. 
En otras palabras, da lo mismo realizar cualquiera de las siguientes definiciones para una 
clase:
public class Ejemplo{}
public class Ejemplo extends Object{}
Antes de continuar examinando más cuestiones sobre la noción de herencia es conveniente 
tener claro lo presentado hasta el momento. Considérese el siguiente ejemplo:
public class EstudiantePreescolar{
 public void leer(){}
 public void escribir(){}
}
81
public class EstudiantePrimaria extends EstudiantePreescolar{
 public void sumar(){}
 public void restar(){}
 public void multiplicar(){}
 public void dividir(){}
}
public class EstudianteBachillerato extends EstudiantePrimaria{
 public void factorizar(){}
 public void resolverEcuaciones(){}
}
public class EstudianteIngenieria extends EstudianteBachillerato{
 public void integrar(){}
 public void derivar(){}
}
Puntos a tener en cuenta sobre el anterior código:
•	 Sobre un objeto de la clase EstudiantePreescolar solo se pueden invocar los métodos 
leer y escribir. Sobre un objeto de la clase EstudiantePrimaria se pueden invocar 
los métodos sumar, restar, multiplicar, dividir, leer y escribir (estos dos últimos 
heredados de la clase EstudiantePreescolar). En general, cualquier objeto de una 
subclase tendrá, además de los métodos que tenga definidos, los métodos de su clase 
padre. 
•	 De lo anterior se puede concluir que un objeto de una clase hijo sabe hacer todo lo 
que puede hacer un objeto de una clase padre de él; sin embargo, no todo padre sabe 
hacer todo lo que puede uno de sus hijos. 
•	 Suponga que una definición como la presentada corresponde con exactitud a 
las habilidades y destrezas que deben poseer los diversos tipos de estudiantes. 
Suponga además, que en determinada situación se requiere de alguien con las 
habilidades de un estudiante de bachillerato para un curso básico de matemáticas 
y alfabetización. ¿Qué objetos, de qué clases, podrían suplir dicho requerimiento? 
Por lo comentado anteriormente, los objetos de las clases EstudianteBachillerato y 
EstudianteIngenieria lo cumplen. Esto se traduce en lo siguiente: suponga que se 
hace la siguiente definición de variable 
EstudianteBachillerato a. 
¿Objetos de cuáles clases pueden ser referenciados empleando dicho apuntador? 
Nuevamente la respuesta es: EstudianteBachillerato y EstudianteIngenieria. Por 
ende, cualquiera de las dos siguientes definiciones es correcta: 
EstudianteBachillerato a = new EstudianteBachillerato();
EstudianteBachillerato a = new EstudianteIngenieria();
Considerando lo presentado anteriormente, ¿cuál sería la diferencia entre las 
siguientes dos definiciones? 
1. TÓPICOS BÁSICOS
82
Todo lo básico que debería saber sobre Programación orientada a objetos enJava
EstudianteBachillerato a = new EstudianteIngenieria(); 
EstudianteIngenieria b = new EstudianteIngenieria(); 
La primera parte de la definición (antes del igual) especifica el tipo de la variable. 
Con esta especificación se establece qué servicios pueden ser invocados sobre dicha 
variable. La segunda parte de la definición (después del igual) especifica el objeto 
que va a brindar los servicios. En otras palabras, la primera parte de la definición 
especifica qué se le puede pedir a la referencia y la segunda es el objeto que va a 
cumplir con los servicios.
Por consiguiente, la diferencia entre las definiciones presentadas anteriormente, es 
que sobre la referencia a solo se van a poder invocar los métodos definidos en la 
clase EstudianteBachillerato (y en cualquiera de sus superclases), mientras que 
en la referencia b se van a poder invocar todos los métodos definidos en la clase 
EstudianteIngenieria. 
En resumidas cuentas, con la referencia a no se podrá invocar el método integrar, 
porque dicho método pertenece a la clase EstudianteIngenieria, pero la referencia 
es de tipo EstudianteBachillerato.
Generalmente, los programadores al llegar a este punto se preguntan si el objeto 
referenciado con el nombre a pierde los métodos que aparecen en la subclase, pero 
que no se encuentran en la superclase. La respuesta es que no los pierde, pero cuando 
se emplea esa referencia no pueden utilizarse.
La siguiente pregunta que se hacen los programadores cuando llegan a este punto es, 
¿por qué alguien preferiría realizar la primera definición en vez de la segunda?, ya 
que con la primera no se pueden invocar ciertos métodos de la clase. La respuesta es 
que en ocasiones trabajar con una referencia de un tipo de dato más general favorece 
la codificación (un ejemplo de esta situación se analizará más adelante).
•	 Se presenta un error en tiempo de compilación definir un objeto de una clase y 
trabajarlo a través de un referencia cuyo tipo no sea la propia clase o alguna de sus 
superclases.
83
sobre la instanCiaCión, la palabra reservada ‘super’ 
y el orden de invoCaCión de los ConstruCtores en las subClases
Cuando se explicaba el proceso de instanciación, se mencionó que Java separa un espacio en 
memoria con la estructura definida de la clase; un interrogante que puede surgir en estos 
momentos es cómo se realiza este proceso estando de por medio la herencia de clases.
La situación es la siguiente: como un objeto de una subclase es un tipo de objeto de una 
superclase, al pedir instanciar un objeto de una clase específica, primero debe separarse 
memoria para un objeto de su superclase. Si la jerarquía de la herencia tiene muchos niveles, es 
decir, cuando la superclase a su vez tiene una superclase, entonces, primero debe reservarse un 
espacio en memoria para el objeto superior de la jerarquía (que en el caso de Java corresponde 
a la clase Object).
Examínese el ejemplo de la jerarquía de herencia del caso de ejemplo de cálculo de la nómina; 
por simplicidad, nada más se presentará la definición de la clase y sus respectivos atributos 
para el caso de la clase EmpleadoXHora (la situación es análoga para cualquier tipo de jerarquía).
public class Empleado{
 protected final String cedula;
 private String apellido;
 private String nombre; 
}
public class EmpleadoXHora extends Empleado{
 private double horasTrabajadas;
 private double sueldoXHora;
}
Suponiendo que se pide ejecutar la instrucción:
new EmpleadoXHora(…)
Antes de que el lenguaje de programación separe un espacio en memoria con la estructura de 
la clase EmpleadoXHora (solo dos atributos), primero debe separar un espacio con la estructura 
de la clase Empleado (superclase de EmpleadoXHora), pero antes debe separar espacio para 
un objeto de la clase Object (superclase de EmpleadoXHora). Luego de separarse espacio 
para un Object (con todos los atributos que pueda definir esta clase), se separa un espacio en 
memoria para almacenar valores para los atributos cedula, apellido y nombre, y, finalmente, 
se separa espacio en memoria para almacenar valores para los atributos horasTrabajadas y 
sueldoXHora. 
1. TÓPICOS BÁSICOS
84
Todo lo básico que debería saber sobre Programación orientada a objetos en Java
Si el orden de la separación de espacio en memoria es como se ha presentado anteriormente, 
¿cómo debe realizarse la inicialización de cada atributo de la clase? La respuesta es que 
la inicialización de los atributos debe realizarse de acuerdo a lo establecido en el ente que 
determina cómo se efectúa dicho proceso: el constructor de la clase. Por ende, puede suponerse 
que conforme se va separando el espacio en memoria se va ejecutando el constructor de la 
clase correspondiente. Lo anterior significa que en el caso de instanciar un objeto de la clase 
EmpleadoXHora, como el primer espacio que se separa es para un objeto de tipo Object, el 
constructor de Object es el primero en ejecutarse; luego se separa espacio para un Objeto de 
tipo Empleado, ejecutándose el constructor de Empleado y, finalmente, después de separarse 
espacio para un EmpleadoXHora se ejecuta el constructor de EmpleadoXHora.
¿Cómo hace el lenguaje Java para garantizar que siempre se invoquen los métodos 
constructores de las superclases antes que de las subclases?
Así como existe en Java una palabra reservada para hacer referencia al mismo objeto (this) 
también existe una palabra reservada para hacer referencia a su superclase. Esta palabra 
corresponde a ‘super’. A través de esta palabra se puede referenciar al objeto de la clase padre, 
a uno de sus atributos o a cualquiera de sus métodos.
•	 Empleando la instrucción super.atributo, se puede manipular los atributos públicos 
o protegidos del padre.
•	 Empleando la instrucción super.nombreMetodo, se puede invocar la ejecución de un 
método público o protegido del padre.
•	 Empleando la instrucción super() (en caso de ser necesario pueden especificarse 
parámetros), se invoca al constructor público o protegido de la clase padre.
El lenguaje de programación Java define de forma automática, y como primera instrucción 
de un constructor de una subclase, el llamado al constructor que no recibe parámetros de su 
superclase a través de la instrucción: 
super();
¿Qué sucede si la superclase no tiene un constructor que no reciba parámetros? Si la superclase 
no tiene este constructor, entonces es una obligación de la subclase especificar explícitamente 
el constructor de la superclase que debe invocarse cuando se utiliza la instrucción super y se 
pasan como parámetros los valores que requiere el constructor de la clase.
85
Con todo lo presentado en el tema de herencia es posible recodificar las clases Empleado, 
EmpleadoXHora, EmpleadoXComision y EmpleadoSueldoFijo como se presenta a continuación.
public class Empleado{
 protected final String cedula;
 private String apellido;
 private String nombre;
 public Empleado(String cedula, String apellido, String nombre){
 if (cedula!=null) this.cedula = cedula;
 else this.cedula = “”;
 setApellido(apellido);
 setNombre(nombre);
 } 
 public String getCedula(){
 return cedula;
 }
 public String getApellido(){
 return apellido;
 }
 public void setApellido(String apellido){
 if (apellido!=null) this.apellido = apellido;
 else this.apellido=””;
 }
 public String getNombre(){
 return nombre;
 }
 public void setNombre(String nombre){
 if (nombre!=null) this.nombre = nombre;
 else this.nombre=””;
 }
}
public class EmpleadoXHora extends Empleado{
 private double horasTrabajadas;
 private double sueldoXHora;
 public EmpleadoXHora(String cedula, String apellido, String nombre, 
 double horasTrabajadas, double sueldoXHora){
 super(cedula, apellido, nombre);
 setSueldoXHora(sueldoXHora);
 setHorasTrabajadas(horasTrabajadas);
 }
 public double calcularSalario(){
 return horasTrabajadas * sueldoXHora;
 }
 public double getHorasTrabajadas(){
 return horasTrabajadas;1. TÓPICOS BÁSICOS
86
Todo lo básico que debería saber sobre Programación orientada a objetos en Java
 }
 public void setHorasTrabajadas(double horas ){
 if (horas>=0) this.horasTrabajadas = horas;
 else this.horasTrabajadas = 0;
 }
 public double getSueldoXHora(){
 return sueldoXHora;
 }
 public void setSueldoXHora(double sueldo ){
 if (sueldo>=0) this.sueldoXHora = sueldo;
 else this.sueldoXHora = 0;
 }
}
public class EmpleadoXComision extends Empleado{
 private double sueldoBasico;
 private double porcentaje;
 private double ventasTotales;
 public EmpleadoXComision(String cedula, String apellido, String nombre, 
 double sueldoBasico, double porcentaje, double ventasTotales){
 super(cedula, apellido, nombre);
 setSueldoBasico(sueldoBasico);
 setPorcentaje(porcentaje);
 setVentasTotales(ventasTotales);
 }
 public double calcularSalario(){
 return sueldoBasico + porcentaje * ventasTotales;
 }
 public double getSueldoBasico(){
 return sueldoBasico;
 }
 public void setSueldoBasico(double sueldo){
 if (sueldo>=0) this.sueldoBasico = sueldo;
 else this.sueldoBasico = 0;
 }
 public double getPorcentaje(){
 return porcentaje;
 }
 public void setPorcentaje(double porcentaje ){
 if (porcentaje>=0 && porcentaje<=1) this.porcentaje = porcentaje;
 else this.porcentaje = 0;
 }
 public double getVentasTotales(){
 return ventasTotales;
 }
 public void setVentasTotales(double ventas){
 if (ventas>=0) this.ventasTotales = ventas;
 else this.ventasTotales = 0;
 }
}
87
public class EmpleadoSueldoFijo extends Empleado{
 private double sueldoFijo;
 public EmpleadoSueldoFijo(String cedula, String apellido, String nombre, 
 double sueldoFijo){
 super(cedula, apellido, nombre);
 setSueldoFijo(sueldoFijo);
 }
 public double calcularSalario(){
 return sueldoFijo;
 }
 public double getSueldoFijo(){
 return sueldoFijo;
 }
 public void setSueldoFijo(double sueldo){
 if (sueldo>=0) this.sueldoFijo = sueldo;
 else this.sueldoFijo = 0;
 }
}
Es conveniente resaltar en el anterior código:
•	 La primera instrucción en cada uno de los constructores de las subclases es la 
invocación al constructor que recibe tres parámetros (cedula, apellido, y nombre) del 
padre.
•	 Note cómo con esta implementación se puede mantener la definición de final del 
atributo cedula.
La clase Empresa podría modificarse para intentar aprovechar el hecho de que se puede 
manipular un objeto de la subclase empleando una referencia de su superclase.
public class Empresa{
 final private String nit;
 private String nombre;
 private String direccion;
 private Empleado[] empleados;
 private int numeroEmpleados;
 public Empresa(String nit, String nombre, String direccion){
 if (nit!=null) this.nit = nit;
 else this.nit=””;
 setNombre(nombre);
 setDireccion(direccion);
 empleados = new Empleado[50];
 numeroEmpleados = 0;
 }
 public String getNombre(){
 return nombre;
 }
1. TÓPICOS BÁSICOS
88
Todo lo básico que debería saber sobre Programación orientada a objetos en Java
 public void setNombre(String nombre){
 if (nombre!=null) this.nombre = nombre;
 else this.nombre=””;
 }
 public String getDireccion(){
 return direccion;
 }
 public void setDireccion(String direccion){
 if (direccion!=null) this.direccion = direccion;
 else this.direccion=””;
 }
 public int getNumeroEmpleados(){
 return numeroEmpleados;
 }
 public void adicionarEmpleadoXHora(String cedula, String apellido, 
 String nombre, double horasTrabajadas, double sueldoXHora){
 empleados[numeroEmpleados] = new EmpleadoXHora(cedula, apellido, nombre,
 horasTrabajadas, sueldoXHora);
 numeroEmpleados++;
 }
 public void adicionarEmpleadoXComision(String cedula, String apellido,
 String nombre, double sueldoBasico, double porcentaje, 
 double ventasTotales){
 empleados[numeroEmpleados] = new EmpleadoXComision(cedula, apellido, 
 nombre, sueldoBasico, porcentaje, ventasTotales);
 numeroEmpleados++;
 }
 public void adicionarEmpleadoSueldoFijo(String cedula, 
 String apellido, String nombre, double sueldoFijo){
 empleados[numeroEmpleados] = new EmpleadoSueldoFijo(cedula, apellido,
 nombre, sueldoFijo);
 numeroEmpleados++;
 }
}
Del código anterior es conveniente resaltar:
•	 Que esta implementación no requiere de tres arrays ni de tres contadores.
•	 La definición del array se hace en función de la superclase Empleado y no de sus 
subclases. De esta forma, se pueden almacenar dentro de éste referencias a objetos 
que sean de la clase Empleado o de cualquier subclase de ella.
•	 Dentro de los métodos de la clase que permiten adicionar los diversos tipos de 
empleados siempre se almacenan los nuevos objetos en un único array
•	 Aún queda pendiente la implementación del método calcularNominaTotal, pues su 
implementación no es tan sencilla como simplemente recorrer el array de empleados 
e invocar el método calcularSalario; porque como el array está declarado de 
89
tipo Empleado, no puede invocarse dicho método, pues no aparece definido en 
la superclase. La solución trivial sería la de incluir dicho método en la clase; sin 
embargo, quedaría pendiente la implementación que debería dársele, pues como la 
clase Empleado es una clase general no posee ninguno de los atributos (que sí poseen 
las subclases de Empleado) que ayudan al cálculo del salario. La solución más simple 
sería la de colocar una implementación por defecto para el método calcularSalario 
en empleado que retorne el valor de 0.
séptima aproximaCión a la apliCaCión del CálCulo de la nómina 
mediante programaCión orientada a obJetos
Por practicidad se vuelve a presentar la definición (parcial hasta el momento) de todas las 
clases que se han utilizado en la aplicación del cálculo de la nómina. El código que aparece en 
negrilla resalta las modificaciones efectuadas con respecto a la anterior aproximación.
public class Empleado{
 protected final String cedula;
 private String apellido;
 private String nombre;
 public Empleado(String cedula, String apellido, String nombre){
 if (cedula!=null) this.cedula = cedula;
 else this.cedula = “”;
 setApellido(apellido);
 setNombre(nombre);
 }
 public double calcularSalario(){
 return 0;
 } 
 public String getCedula(){
 return cedula;
 }
 public String getApellido(){
 return apellido;
}
 public void setApellido(String apellido){
 if (apellido!=null) this.apellido = apellido;
 else this.apellido= “”;
 }
 public String getNombre(){
 return nombre;
 }
1. TÓPICOS BÁSICOS
90
Todo lo básico que debería saber sobre Programación orientada a objetos en Java
 
 public void setNombre(String nombre){
 if (nombre!=null) this.nombre = nombre;
 else this.nombre= “”;
 }
}
public class EmpleadoXHora extends Empleado{
 private double horasTrabajadas;
 private double sueldoXHora;
 public EmpleadoXHora(String cedula, String apellido, String nombre, 
 double horasTrabajadas, double sueldoXHora){
 super(cedula, apellido, nombre);
 setSueldoXHora(sueldoXHora);
 setHorasTrabajadas(horasTrabajadas);
 }
 public double calcularSalario(){
 return horasTrabajadas * sueldoXHora;
 }
 public double getHorasTrabajadas(){
 return horasTrabajadas;
 }
 public void setHorasTrabajadas(double horas ){
 if (horas>=0) this.horasTrabajadas = horas;
 else this.horasTrabajadas = 0;
 }
public double getSueldoXHora(){
 return sueldoXHora;
 }
 public void setSueldoXHora(double sueldo ){
 if (sueldo>=0) this.sueldoXHora = sueldo;
 else this.sueldoXHora = 0;
 }
}
public class EmpleadoXComision extends Empleado{
 private double sueldoBasico;
 private double porcentaje;
 private double ventasTotales;
 public EmpleadoXComision(String cedula, String apellido, 
 String nombre, double sueldoBasico, double porcentaje, 
 double ventasTotales){
 
91
 super(cedula,apellido, nombre);
 setSueldoBasico(sueldoBasico);
 setPorcentaje(porcentaje);
 setVentasTotales(ventasTotales);
 }
 public double calcularSalario(){
 return sueldoBasico + porcentaje * ventasTotales;
 }
 public double getSueldoBasico(){
 return sueldoBasico;
 }
 public void setSueldoBasico(double sueldo){
 if (sueldo>=0) this.sueldoBasico = sueldo;
 else this.sueldoBasico = 0;
 }
 public double getPorcentaje(){
 return porcentaje;
 }
 public void setPorcentaje(double porcentaje ){
 if (porcentaje>=0 && porcentaje<=1) this.porcentaje = porcentaje;
 else this.porcentaje = 0;
 }
 public double getVentasTotales(){
 return ventasTotales;
 }
 public void setVentasTotales(double ventas){
 if (ventas>=0) this.ventasTotales = ventas;
 else this.ventasTotales = 0;
 }
}
public class EmpleadoSueldoFijo extends Empleado{
 private double sueldoFijo;
 public EmpleadoSueldoFijo(String cedula, String apellido, 
 String nombre, double sueldoFijo){
 super(cedula, apellido, nombre);
 setSueldoFijo(sueldoFijo);
 }
 
 public double calcularSalario(){
 return sueldoFijo;
 }
1. TÓPICOS BÁSICOS
92
Todo lo básico que debería saber sobre Programación orientada a objetos en Java
 public double getSueldoFijo(){
 return sueldoFijo;
 }
 public void setSueldoFijo(double sueldo){
 if (sueldo>=0) this.sueldoFijo = sueldo;
 else this.sueldoFijo = 0;
 }
}
public class Empresa{
 final private String nit;
 private String nombre;
 private String direccion;
 private Empleado[] empleados;
 private int numeroEmpleados;
 public Empresa(String nit, String nombre, String direccion){
 if (nit!=null) this.nit = nit;
 else this.nit=””;
 setNombre(nombre);
 setDireccion(direccion);
 empleados = new Empleado[50];
 numeroEmpleados= 0;
 }
 public String getNit(){
 return nit;
 }
 public String getNombre(){
 return nombre;
 }
 public void setNombre(String nombre){
 if (nombre!=null) this.nombre = nombre;
 else this.nombre=””;
 }
 public String getDireccion(){
 return direccion;
 }
 public void setDireccion(String direccion){
 if (direccion!=null) this.direccion = direccion;
 else this.direccion=””;
 }
 public int getNumeroEmpleados(){
 return numeroEmpleados;
 }
93
 public void adicionarEmpleadoXHora(String cedula, String apellido, 
 String nombre, double horasTrabajadas, double sueldoXHora){
 empleados[numeroEmpleados] = new EmpleadoXHora(cedula, apellido,
 nombre, horasTrabajadas, sueldoXHora);
 numeroEmpleados++;
 }
 public void adicionarEmpleadoXComision(String cedula, String apellido,
 String nombre, double sueldoBasico, double porcentaje, 
 double ventasTotales){
 empleados[numeroEmpleados] = new EmpleadoXComision(cedula, apellido, 
 nombre, sueldoBasico, porcentaje, ventasTotales);
 numeroEmpleados++;
 }
 public void adicionarEmpleadoSueldoFijo(String cedula, 
 String apellido, String nombre, double sueldoFijo){
 empleados[numeroEmpleados] = new EmpleadoSueldoFijo(cedula,
 apellido, nombre, sueldoFijo);
 numeroEmpleados++;
 }
 public double calcularNominaTotal(){
 double total = 0;
 for (int i=0;i<numeroEmpleados;i++)
 total = total + empleados[i].calcularSalario();
 return total;
 }
}
Gracias al nivel de encapsulamiento con el que se ha trabajado la clase Empresa no es necesaria 
la modificación de ninguna línea de código de la clase Nomina1. Por simple practicidad se 
vuelve a mostrar este código:
import java.io.BufferedReader;
import java.io.InputStreamReader;
public class Nomina1{
 public static void main(String[] args) throws Exception{
 Empresa pyme = new Empresa(“123”,”Acme inc.”,”805 Silicon Valley”);
 String cedula, apellido, nombre; 
 double total = 0;
 
 BufferedReader br = new BufferedReader( 
 new InputStreamReader( System.in ) );
 
1. TÓPICOS BÁSICOS
94
Todo lo básico que debería saber sobre Programación orientada a objetos en Java
 System.out.print(“Digite numero de empleados sueldo por hora: “); 
 int numeroEmpleados1 = Integer.valueOf(br.readLine()).intValue();
 
 for(int i=0;i<numeroEmpleados1;i++){
 double horas, sueldo;
 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 del empleado: “);
 horas = Double.valueOf(br.readLine()).doubleValue(); 
 System.out.print(“Digite sueldo por hora del empleado: “ );
 sueldo = Double.valueOf(br.readLine()).doubleValue();
 pyme.adicionarEmpleadoXHora(cedula, apellido, nombre, horas,
 sueldo);
 }
 System.out.print(“Digite numero de empleados por comision: “); 
 int numeroEmpleados2 = Integer.valueOf(br.readLine()).intValue();
 
 for(int i=0;i<numeroEmpleados2;i++){
 double sueldo, porcentaje, ventas;
 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 sueldo basico empleado: “);
 sueldo = Double.valueOf(br.readLine()).doubleValue(); 
 System.out.print(“Digite porcentaje del empleado: “ );
 porcentaje = Double.valueOf(br.readLine()).doubleValue();
 System.out.print(“Digite ventas totales del empleado: “ );
 ventas = Double.valueOf(br.readLine()).doubleValue();
 pyme.adicionarEmpleadoXComision(cedula, apellido, nombre, sueldo,
 porcentaje, ventas);
 }
 System.out.print(“Digite numero de empleados sueldo fijo: “); 
 int numeroEmpleados3 = Integer.valueOf(br.readLine()).intValue();
 
95
 for(int i=0;i<numeroEmpleados3;i++){
 double sueldo;
 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 sueldo basico empleado: “);
 sueldo = Double.valueOf(br.readLine()).doubleValue(); 
 pyme.adicionarEmpleadoSueldoFijo(cedula, apellido, nombre, 
 sueldo);
 }
 total = pyme.calcularNominaTotal();
 
 System.out.println(“\nLa nómina total es: “+ total);
 }
}
Al llegar a este punto todos los programadores que se encuentran habituados a la 
programación procedimental se realizan la misma pregunta, ¿cómo hace el programa para 
saber cuál de las tres implementaciones del método calcularSalario debe ejecutarse en 
cada caso? Antes de brindar solución a este cuestionamiento es conveniente entender la 
situación. Considérese el siguiente código:
Empresa e = new Empresa(“123”,”Acme inc.”,”805 Silicon Valley”);
e.adicionarEmpleadoXHora(“72303”, “García”, “Luis”, 100, 15);
e.adicionarEmpleadoXComision(“98765”, “Sánchez”, “María”, 1000, 0.5, 1000);
e.adicionarEmpleadoSueldoFijo(“12345”, “Pérez”, “Pedro”, 1200);
En memoria se obtendría una configuración como la que se muestra a continuación:
1. TÓPICOS BÁSICOS
96
Todo lo básico que debería saber sobre Programación orientada a objetos en Java
Efectivamente se han codificado cuatro métodos calcularSalario en las clases Empleado, 
EmpleadoXHora, EmpleadoXComision y en EmpleadoSueldoFijo. En el momento de ejecutarse 
el método calcularNominaTotal, del array empleados se extraen las referencias a todos los 
objetos subclases de Empleado que se han adicionado. Analícese el caso del primer objeto 
almacenado en el array que aparece en el ejemplo; la pregunta no es cuál de los cuatro 
métodos se ejecutan porque enla clase EmpleadoXHora solo se encuentran definidos dos 
métodos calcularSalario (una implementación, que ganó de su superclase por herencia, y 
otra que la misma clase tiene definida). La misma situación aplica para cualquier otro objeto 
de una subclase de Empleado. El cuestionamiento en este punto surge porque existen dos 
métodos para el cálculo del salario en la clase, y dentro del método calcularNominaTotal de 
Empresa se pide invocar dicho método. Para este caso se ejecutará la versión del método que 
haya sido definida en la subclase. Lo cual garantizaría la obtención del resultado adecuado 
durante su ejecución.
Cuando se trabaja con herencia se deben tener en cuenta los siguientes puntos: 
•	 Mediante la herencia se refina el comportamiento y estructura de una clase bien 
sea añadiendo nuevos atributos y métodos, o bien sobrescribiendo la definición de 
atributos y métodos de su superclase. 
•	 Como un objeto de una subclase posee todos los métodos de su superclase, entonces 
este objeto puede hacer todo lo que su superclase; por eso se dice que un objeto de 
una subclase puede reemplazar a cualquier objeto de su superclase.
•	 Cuando una subclase redefine o sobrescribe un método de su superclase, al momento 
de invocarse la ejecución de dicho método prevalecerá la implementación de la 
subclase sobre la de la superclase. 
1.11. métOdOs y clAses AbstrActAs
Durante la modificación del código de la aplicación de la nómina para trabajar el concepto 
de herencia fue necesario incluir el método calcularSalario dentro de la clase Empleado. En 
dicho momento se explicó que era necesaria la inclusión de este método a fin de definir un 
único array dentro de la clase Empresa para manejar cualquier tipo de Empleado y realizar 
el cálculo total de la nómina de la empresa. Se asignó una implementación por defecto 
que simplemente retornaba el valor de 0, ya que la clase Empleado no posee información 
suficiente para realizar este cálculo. Existe una alternativa de solución más elegante en 
términos de programación para no tener que asignar esta implementación por defecto, por 
ello es necesario definir el método calcularSalario de la clase Empresa como un método 
abstracto.
97
Definición de método abstracto
Un método abstracto es un método cuya signatura se encuentra definida, pero no 
su implementación.
Analícese el siguiente ejemplo:
public double calcularSalario(){
 return 0;
}
Debe entenderse como signatura del método el conjunto de caracteres donde se especifica el 
nivel de visibilidad, el tipo de retorno, el nombre y los parámetros que requiere el método 
para su funcionamiento. La implementación corresponde al conjunto de instrucciones que 
aparecen demarcadas entre la apertura y el cierre de llaves (‘{‘, ’}’). 
Un método abstracto define qué debe hacerse, pero no específica cómo se realiza la operación. 
Para definir un método abstracto se debe anteponer la palabra abstract a su definición, y 
colocar el carácter ‘;’ en vez de la apertura y cierre de llaves (‘{‘, ’}’). 
Ante estas consideraciones, el código de la clase Empleado debe quedar de la siguiente 
manera:
public class Empleado{
 protected final String cedula;
 private String apellido;
 private String nombre;
 public Empleado(String cedula, String apellido, String nombre){
 if (cedula!=null) this.cedula = cedula;
 else this.cedula = “”;
 setApellido(apellido);
 setNombre(nombre);
 }
 public abstract double calcularSalario();
 public String getCedula(){
 return cedula;
 }
 public String getApellido(){
 return apellido;
 }
 public void setApellido(String apellido){
 if (apellido!=null) this.apellido = apellido;
 else this.apellido=””;
 }
1. TÓPICOS BÁSICOS
98
Todo lo básico que debería saber sobre Programación orientada a objetos en Java
 public String getNombre(){
 return nombre;
 }
 public void setNombre(String nombre){
 if (nombre!=null) this.nombre = nombre;
 else this.nombre=””;
 }
}
El manejo de esta clase requiere cuidado porque sería peligroso que se instanciará y se 
pidiera la ejecución del método calcularSalario. ¿Qué proceso ejecutaría un objeto de esta 
clase si no tiene definido implementación para el mismo? Para evitar situaciones como esta, 
es necesario introducir el concepto de clase abstracta.
Definición de clase abstracta
Una clase abstracta es una clase que no se encuentra completamente definida. Como no 
está completa, no es posible instanciar objetos de dicha clase.
Es paradójico que dentro del mundo de la programación orientada a objetos, donde los 
principales componentes son las clases y sus instancias, se especifique un concepto para 
evitar instanciar una clase. Sin embargo, tal y como se analizó previamente en algunas 
situaciones es conveniente definir este comportamiento.
Al trabajar con clases abstractas se debe tener en cuenta lo siguiente:
•	 Si en una clase se encuentra definido por lo menos un método abstracto, la clase 
donde debe convertirse en abstracta.
•	 Se puede definir como abstracta una clase a pesar de que no tenga un método 
abstracto. ¿Para qué? Para evitar su instanciación.
•	 Intentar definir un método abstracto dentro de una clase sin volver abstracta la clase 
generará un error en tiempo de compilación.
•	 Una clase abstracta puede tener definido un método constructor. Obviamente este 
método no podrá hacer parte de una instrucción new, es decir no puede utilizarse 
para crear objetos pero si se puede utilizar para especificar cómo debe efectuarse la 
inicialización de los atributos al instanciarse un objeto de una de sus subclases (este 
tópico fue analizado en el apartado Sobre la instanciación, la palabra reservada 
‘super’ y el orden de invocación de los constructores en las subclases)
•	 Cuando se define una clase abstracta con uno o más métodos abstractos es obligación 
de las subclases de ésta brindar implementación a todos los métodos abstractos; estos 
métodos en las subclases deben poseer exactamente la misma definición (mismo 
nombre, mismo dato de retorno y mismos parámetros) que tienen en la superclase. 
99
Si una subclase que extiende una clase abstracta, que cuenta con varios métodos 
abstractos, no brinda implementación a todos los métodos abstractos se presenta un 
error en tiempo de compilación.
Para establecer una clase como abstracta se le debe anteponer a su definición la palabra 
reservada ‘abstract’. 
oCtava aproximaCión a la apliCaCión del CálCulo de la nómina 
empleando programaCión orientada a obJetos
Después de especificar cómo abstracta la clase Empleado el código quedaría de la siguiente 
manera (El código que aparece en negrilla resalta las modificaciones efectuadas con respecto a 
la anterior aproximación) :
public abstract class Empleado{
 protected final String cedula;
 private String apellido;
 private String nombre;
 public Empleado(String cedula, String apellido, String nombre){
 if (cedula!=null) this.cedula = cedula;
 else this.cedula = “”;
 setApellido(apellido);
 setNombre(nombre);
 }
 public abstract double calcularSalario();
 public String getCedula(){
 return cedula;
 }
 public String getApellido(){
 return apellido;
 }
 public void setApellido(String apellido){
 if (apellido!=null) this.apellido = apellido;
 else this.apellido=””;
 }
 public String getNombre(){
 return nombre;
 }
 public void setNombre(String nombre){
 if (nombre!=null) this.nombre = nombre;
 else this.nombre=””;
 }
}
1. TÓPICOS BÁSICOS
100
Todo lo básico que debería saber sobre Programación orientada a objetos en Java
 El cambio implementado sólo implica modificaciones en la clase Empleado, en ninguna otra 
clase es necesario realizar algún tipo de modificación. Simplemente por practicidad se presenta 
nuevamente el código de las restantes clases.
public class EmpleadoXHora extends Empleado{
 private double horasTrabajadas;
 private double sueldoXHora;
 public EmpleadoXHora(String cedula, String apellido,String nombre, 
 double horasTrabajadas, double sueldoXHora){
 super(cedula, apellido, nombre);
 setSueldoXHora(sueldoXHora);
 setHorasTrabajadas(horasTrabajadas);
 }
 public double calcularSalario(){
 return horasTrabajadas * sueldoXHora;
 }
 public double getHorasTrabajadas(){
 return horasTrabajadas;
 }
 public void setHorasTrabajadas(double horas ){
 if (horas>=0) this.horasTrabajadas = horas;
 else this.horasTrabajadas = 0;
 }
 public double getSueldoXHora(){
 return sueldoXHora;
 }
 public void setSueldoXHora(double sueldo ){
 if (sueldo>=0) this.sueldoXHora = sueldo;
 else this.sueldoXHora = 0;
 }
}
public class EmpleadoXComision extends Empleado{
 private double sueldoBasico;
 private double porcentaje;
 private double ventasTotales;
 public EmpleadoXComision(String cedula, String apellido, 
 String nombre, double sueldoBasico, double porcentaje, 
 double ventasTotales){
 
101
 super(cedula, apellido, nombre);
 setSueldoBasico(sueldoBasico);
 setPorcentaje(porcentaje);
 setVentasTotales(ventasTotales);
 }
 public double calcularSalario(){
 return sueldoBasico + porcentaje * ventasTotales;
 }
 public double getSueldoBasico(){
 return sueldoBasico;
 }
 public void setSueldoBasico(double sueldo){
 if (sueldo>=0) this.sueldoBasico = sueldo;
 else this.sueldoBasico = 0;
 }
 public double getPorcentaje(){
 return porcentaje;
 }
 public void setPorcentaje(double porcentaje ){
 if (porcentaje>=0 && porcentaje<=1) this.porcentaje = porcentaje;
 else this.porcentaje = 0;
 }
 public double getVentasTotales(){
 return ventasTotales;
 }
 public void setVentasTotales(double ventas){
 if (ventas>=0) this.ventasTotales = ventas;
 else this.ventasTotales = 0;
 }
}
public class EmpleadoSueldoFijo extends Empleado{
 private double sueldoFijo;
 public EmpleadoSueldoFijo(String cedula, String apellido, 
 String nombre, double sueldoFijo){
 super(cedula, apellido, nombre);
 setSueldoFijo(sueldoFijo);
 }
 public double calcularSalario(){
 return sueldoFijo;
 }
1. TÓPICOS BÁSICOS
102
Todo lo básico que debería saber sobre Programación orientada a objetos en Java
 public double getSueldoFijo(){
 return sueldoFijo;
 }
 public void setSueldoFijo(double sueldo){
 if (sueldo>=0) this.sueldoFijo = sueldo;
 else this.sueldoFijo = 0;
 }
}
public class Empresa{
 final private String nit;
 private String nombre;
 private String direccion;
 private Empleado[] empleados;
 private int numeroEmpleados;
 public Empresa(String nit, String nombre, String direccion){
 if (nit!=null) this.nit = nit;
 else this.nit=””;
 setNombre(nombre);
 setDireccion(direccion);
 empleados = new Empleado[50];
 numeroEmpleados = 0;
 }
 public String getNit(){
 return nit;
 }
 public String getNombre(){
 return nombre;
 }
 public void setNombre(String nombre){
 if (nombre!=null) this.nombre = nombre;
 else this.nombre=””;
 }
 public String getDireccion(){
 return direccion;
 }
 public void setDireccion(String direccion){
 if (direccion!=null) this.direccion = direccion;
 else this.direccion=””;
 }
 public int getNumeroEmpleados(){
 return numeroEmpleados;
 }
103
 public void adicionarEmpleadoXHora(String cedula, String apellido, 
 String nombre, double horasTrabajadas, double sueldoXHora){
 empleados[numeroEmpleados] = new EmpleadoXHora(cedula, apellido,
 nombre, horasTrabajadas, sueldoXHora);
 numeroEmpleados++;
 }
 public void adicionarEmpleadoXComision(String cedula, String apellido,
 String nombre, double sueldoBasico, double porcentaje, 
 double ventasTotales){
 empleados[numeroEmpleados] = new EmpleadoXComision(cedula, apellido, 
 nombre, sueldoBasico, porcentaje, ventasTotales);
 numeroEmpleados++;
 }
 public void adicionarEmpleadoSueldoFijo(String cedula, 
 String apellido, String nombre, double sueldoFijo){
 empleados[numeroEmpleados] = new EmpleadoSueldoFijo(cedula,
 apellido, nombre, sueldoFijo);
 numeroEmpleados++;
 }
 public double calcularNominaTotal(){
 double total = 0;
 for (int i=0;i<numeroEmpleados;i++)
 total = total + empleados[i].calcularSalario();
 return total;
 }
}
import java.io.BufferedReader;
import java.io.InputStreamReader;
public class Nomina1{
 public static void main(String[] args) throws Exception{
 Empresa pyme = new Empresa(“123”,”Acme inc.”,”805 Silicon Valley”);
 String cedula, apellido, nombre; 
 double total = 0;
 BufferedReader br = new BufferedReader( 
 new InputStreamReader( System.in ) );
 
 System.out.print(“Digite numero de empleados sueldo por hora: “); 
 int numeroEmpleados1 = Integer.valueOf(br.readLine()).intValue();
 
1. TÓPICOS BÁSICOS
104
Todo lo básico que debería saber sobre Programación orientada a objetos en Java
 for(int i=0;i<numeroEmpleados1;i++){
 double horas, sueldo;
 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 del empleado: “);
 horas = Double.valueOf(br.readLine()).doubleValue(); 
 System.out.print(“Digite sueldo por hora del empleado: “ );
 sueldo = Double.valueOf(br.readLine()).doubleValue();
 pyme.adicionarEmpleadoXHora(cedula, apellido, nombre, horas,
 sueldo);
 }
 System.out.print(“Digite numero de empleados por comision: “); 
 int numeroEmpleados2 = Integer.valueOf(br.readLine()).intValue();
 
 for(int i=0;i<numeroEmpleados2;i++){
 double sueldo, porcentaje, ventas;
 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 sueldo basico empleado: “);
 sueldo = Double.valueOf(br.readLine()).doubleValue(); 
 System.out.print(“Digite porcentaje del empleado: “ );
 porcentaje = Double.valueOf(br.readLine()).doubleValue();
 System.out.print(“Digite ventas totales del empleado: “ );
 ventas = Double.valueOf(br.readLine()).doubleValue();
 pyme.adicionarEmpleadoXComision(cedula, apellido, nombre, sueldo,
 porcentaje, ventas);
 }
 System.out.print(“Digite numero de empleados sueldo fijo: “); 
 int numeroEmpleados3 = Integer.valueOf(br.readLine()).intValue();
 
 for(int i=0;i<numeroEmpleados3;i++){
 double sueldo;
 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 sueldo basico empleado: “);
 sueldo = Double.valueOf(br.readLine()).doubleValue(); 
105
 
 pyme.adicionarEmpleadoSueldoFijo(cedula, apellido, nombre, 
 sueldo);
 }
 total = pyme.calcularNominaTotal();
 
 System.out.println(“\nLa nómina total es: “+ total);
 }
}
1.12. cAsting
Durante la presentación del concepto de herencia se trabajó el siguiente ejemplo: 
public class EstudiantePreescolar{
 public void leer(){}
 public void escribir(){}
}
public class EstudiantePrimaria extends EstudiantePreescolar{
 public void sumar(){}
 public void restar(){}
 public void multiplicar(){}
 public void dividir(){}
}
public class EstudianteBachillerato extends EstudiantePrimaria{
 public void factorizar(){}
 public void resolverEcuaciones(){}
}
public class EstudianteIngenieria extends EstudianteBachillerato{
 publicvoid integrar(){}
 public void derivar(){}
}
En ese punto se mencionó que son totalmente correctas cualquiera de las siguientes 
definiciones:
EstudianteBachillerato a = new EstudianteIngenieria(); 
EstudianteIngenieria b = new EstudianteIngenieria();
La principal diferencia entre ambas definiciones corresponde a la cantidad de métodos que 
pueden ser ejecutados en ambas referencias; mientras con la referencia b se pueden invocar 
todos los métodos que estén definidos en EstudianteIngenieria (y, por ende, en cualquiera 
de sus superclases), con la referencia a solo podrán ser invocados los métodos que se 
encuentren definidos en la clase EstudianteBachillerato (y, por ende, en cualquiera de sus 
superclases). Según se comentó en dicha sección, el objeto referenciado a través de a es un 
1. TÓPICOS BÁSICOS
106
Todo lo básico que debería saber sobre Programación orientada a objetos en Java
objeto de la clase EstudianteIngenieria y por ello posee todos los métodos definidos para esa 
clase, pero como está referenciado a través de una variable de tipo EstudianteBachillerato, 
sobre ella solo podrán ser invocados métodos que se encuentren definidos en esta última 
clase. En otras palabras: el objeto sí tiene los métodos, pero no pueden ser utilizados a través 
de esta referencia.
En ocasiones es imperativo ejecutar estos métodos que no pueden ser invocados (porque 
el tipo de la referencia no los permite) y para lograr tal fin ayudará el concepto de casting.
Definición de casting
Casting hace referencia a forzar la conversión de una referencia de un tipo a otro.
En Java para forzar la conversión de la referencia basta con colocarle antes, entre paréntesis, 
el nombre del tipo de dato al que se desea realizar la conversión. Por ejemplo, para una 
referencia definida así:
EstudianteBachillerato a = new EstudianteIngenieria();
Para realizar la conversión de la variable a del tipo EstudianteBachillerato al tipo 
EstudianteIngenieria y para poder ejecutar el método derivar se debe emplear la siguiente 
instrucción:
((EstudianteIngenieria)a).derivar();
Es conveniente notar de la anterior instrucción lo siguiente:
•	 Cómo se toma la referencia y se le antepone entre paréntesis el tipo de dato al que se 
quiere forzar la conversión.
•	 Cómo se encierra entre paréntesis toda la instrucción de transformación para después 
sí ejecutar el método en mención.
Adicionalmente, es conveniente tener en cuenta los siguientes puntos al trabajar con el 
casting.
•	 La transformación de la referencia solo ocurre durante la ejecución de la instrucción. 
Si lo que se requiere es cambiar permanentemente el tipo de la referencia de dicho 
objeto, es necesario definir una nueva variable y asignarle el resultado del casting.
•	 Se marca como error en tiempo de compilación intentar convertir una referencia de 
un tipo a otra de otro tipo, si ambas no se encuentran relacionadas en la jerarquía de 
herencia como que una extiende de la otra.
•	 Carece de sentido realizar conversiones de tal manera que una referencia de tipo 
de una subclase se transforme a una referencia de una superclase, por ejemplo, 
107
(EstudiantePreescolar)a. Este casting está demás y es totalmente trivial, pues un 
objeto de una subclase posee todos los métodos definidos en la superclase. 
Existen ocasiones en que el error solo puede detectarse en tiempo de 
compilación, por ejemplo, considere la siguiente situación: 
 
Object a = new String(“Ejemplo”); 
EstudiantePrimaria b = (EstudiantePrimaria)a; 
Note que en este caso no se presenta error alguno en tiempo de compilación, pues 
los castings se realizan entre clases que se encuentran relacionadas en la jerarquía de 
herencia (pues Object es superclase tanto de String como de EstudiantePrimaria), 
pero cuando se ejecute este código se presentará un grave error al intentar convertir 
la referencia.
1.13. pOlimOrfismO
Definición de polimorfismo
El polimorfismo hace referencia a la propiedad de que un elemento (generalmente el 
nombre de un método) de adquirir muchas formas (implementaciones).
La definición presentada anteriormente corresponde a una definición muy general y amplia 
de la etimología de palabra. Aplicado al campo de la programación orientada a objetos 
existen básicamente dos formas de trabajar este concepto.
•	 Sobrecarga: la sobrecarga hace referencia a la propiedad que permite definir varios 
métodos dentro de una clase que tengan el mismo nombre, pero con diferentes 
parámetros. A manera de ejemplo considérese el siguiente código.
public class Empresa{
 final private String nit;
 private String nombre;
 private String direccion;
 private Empleado[] empleados;
 private int numeroEmpleados;
 public Empresa(String nit, String nombre, String direccion){
 if (nit!=null) this.nit = nit;
 else this.nit=””;
 setNombre(nombre);
 setDireccion(direccion);
 empleados = new Empleado[50];
 numeroEmpleados = 0;
 }
1. TÓPICOS BÁSICOS
108
Todo lo básico que debería saber sobre Programación orientada a objetos en Java
 public Empresa(String nit){
 if (nit!=null) this.nit = nit;
 else this.nit=””;
 setNombre(“”);
 setDireccion(“”);
 empleados = new Empleado[50];
 numeroEmpleados = 0;
 }
 public Empresa(String nit, int numero){
 if (nit!=null) this.nit = nit;
 else this.nit=””;
 setNombre(“”);
 setDireccion(“”);
 empleados = new Empleado[numero];
 numeroEmpleados = 0;
 }
 public Empresa(int numero, String nit){
 if (nit!=null) this.nit = nit;
 else this.nit=””;
 setNombre(“”);
 setDireccion(“”);
 empleados = new Empleado[numero];
 numeroEmpleados = 0;
 }
 public Empresa(String nit, String nombre){
 if (nit!=null) this.nit = nit;
 else this.nit=””;
 setNombre(nombre);
 setDireccion(“”);
 empleados = new Empleado[50];
 numeroEmpleados = 0;
 }
 public String getNombre(){
 return nombre;
 }
 public void setNombre(String nombre){
 if (nombre!=null) this.nombre = nombre;
 else this.nombre=””;
 }
 public String getDireccion(){
 return direccion;
 }
 public void setDireccion(String direccion){
 if (direccion!=null) this.direccion = direccion;
 else this.direccion=””;
 }
109
 public int getNumeroEmpleados(){
 return numeroEmpleados;
 }
 public void adicionarEmpleadoXHora(String cedula, String apellido, 
 String nombre, double horasTrabajadas, double sueldoXHora){
 empleados[numeroEmpleados] = new EmpleadoXHora(cedula, apellido,
 nombre, horasTrabajadas, sueldoXHora);
 numeroEmpleados++;
 }
 public void adicionarEmpleadoXComision(String cedula, String apellido,
 String nombre, double sueldoBasico, double porcentaje, 
 double ventasTotales){
 empleados[numeroEmpleados] = new EmpleadoXComision(cedula, apellido, 
 nombre, sueldoBasico, porcentaje, ventasTotales);
 numeroEmpleados++;
 }
 public void adicionarEmpleadoSueldoFijo(String cedula, 
 String apellido, String nombre, double sueldoFijo){
 empleados[numeroEmpleados] = new EmpleadoSueldoFijo(cedula,
 apellido, nombre, sueldoFijo);
 numeroEmpleados++;
 }
 public double calcularNominaTotal(){
 double total = 0;
 for (int i=0;i<numeroEmpleados;i++)
 total = total + empleados[i].calcularSalario();
 return total;
 }
}
Note que en este caso el método constructor de la clase se encuentra sobrecargado. 
Existen cinco métodos que permiten instanciar objetos; todos poseen el mismo 
nombre, lo único que varía en ellos son los parámetros. Cabe resaltar que además 
existen dos métodos constructores que reciben los mismos parámetros (String e 
int), pero en distinto orden; por ello no existe problema alguno en su definición.
Ninguno de los siguientes métodos polimórficos pueden ser añadidos a la definición 
antes presentada.
public Empresa(String direccion){
 this.nit = “”;
 setNombre(“”);
 setDireccion(direccion);
 empleados = new Empleado[50];
 empleados = 0;
}
public Empresa(String nombre, String nit){
 if(nit!=null) this.nit = nit;
 else this.nit=””;
 setNombre(nombre);
 setDireccion(“”);
1. TÓPICOS BÁSICOS
110
Todo lo básico que debería saber sobre Programación orientada a objetos en Java
 empleados = new Empleado[50];
 empleados = 0;
}
La razón del porqué no pueden ser incluidos es que para el primer constructor que 
se muestra ya existe un método que recibe exactamente un parámetro de tipo String, 
y para el segundo ya existe un método que recibe exactamente dos parámetros de 
tipo String.
•	 Redefinición o sobrescritura: esta propiedad permite que una subclase redefina o 
sobrescriba el comportamiento (implementación) de un método que hereda de su 
superclase. La situación acá es que una subclase hereda todos los atributos y métodos 
de su superclase, pero en determinadas circunstancias la subclase estaría interesada 
en modificar la implementación de uno de esos métodos. Para ello es posible que 
la subclase redefina el comportamiento del método con otra serie de instrucciones. 
Esto conllevaría a la situación en la que la subclase tendría dos métodos polimórficos 
con el mismo nombre y los mismos parámetros, uno que hereda de su padre y otro 
que el mismo define. 
Cuando se presentó el concepto de herencia se dijo que si una subclase redefine un 
método de su superclase, al momento de invocarse la ejecución de dicho método 
prevalecerá la implementación de la subclase sobre la de la superclase. Para la 
presentación de un ejemplo de esta situación se retomará parte del código presentado 
anteriormente. 
public class Empleado{
 protected final String cedula;
 private String apellido;
 private String nombre;
 public Empleado(String cedula, String apellido, String nombre){
 if (cedula!=null) this.cedula = cedula;
 else this.cedula = “”;
 setApellido(apellido);
 setNombre(nombre);
 }
 public double calcularSalario(){
 return 0;
 } 
 public String getCedula(){
 return cedula;
 }
 public String getApellido(){
 return apellido;
 }
 public void setApellido(String apellido){
 if (apellido!=null) this.apellido = apellido;
 else this.apellido=””;
 }
111
 public String getNombre(){
 return nombre;
 }
 public void setNombre(String nombre){
 if (nombre!=null) this.nombre = nombre;
 else this.nombre=””;
 }
}
public class EmpleadoXHora extends Empleado{
 private double horasTrabajadas;
 private double sueldoXHora;
 public EmpleadoXHora(String cedula, String apellido, String nombre, 
 double horasTrabajadas, double sueldoXHora){
 super(cedula, apellido, nombre);
 setSueldoXHora(sueldoXHora);
 setHorasTrabajadas(horasTrabajadas);
 }
 public double calcularSalario(){
 return horasTrabajadas * sueldoXHora;
 }
 public double getHorasTrabajadas(){
 return horasTrabajadas;
 }
 public void setHorasTrabajadas(double horas ){
 if (horas>=0) this.horasTrabajadas = horas;
 else this.horasTrabajadas = 0;
 }
 public double getSueldoXHora(){
 return sueldoXHora;
 }
 public void setSueldoXHora(double sueldo ){
 if (sueldo>=0) this.sueldoXHora = sueldo;
 else this.sueldoXHora = 0;
 }
}
En el código que se presenta, la clase Empleado no es abstracta y posee una 
implementación por defecto para el método calcularSalario (retornar el valor 
de 0). Por su parte, la clase EmpleadoXHora extiende la clase Empleado y redefine el 
comportamiento de dicho método. Al instanciar un objeto de clase EmpleadoXHora e 
invocar el método calcularSalario, la respuesta que se obtendría sería el producto de 
los atributos horasTrabajadas y sueldoXHora, y no 0 como lo define la implementación 
heredada de su superclase. 
La pregunta que podría surgir en estos momentos corresponde a si una subclase 
puede cambiarle a un método heredado el nivel de visibilidad establecido por su 
superclase. La respuesta es que la subclase sí puede redefinir el nivel de visibilidad, 
1. TÓPICOS BÁSICOS
112
Todo lo básico que debería saber sobre Programación orientada a objetos en Java
pero únicamente para ampliarlo, nunca restringiéndolo. El siguiente gráfico ilustra 
esta situación:
La flecha superior marca la dirección en que no es válido realizar la redefinición; es 
decir, si la superclase define un método público, la subclase no puede redefinirlo como 
privado. Por otro lado, la flecha inferior marca la dirección válida de la redefinición; 
es decir, si una superclase define un método protegido, la subclase puede redefinirlo 
como público.
sobre la ComparaCión de variables de tipos de referenCia
En el apartado Sobre el operador lógico de comparación y su funcionamiento sobre las 
variables de tipo primitivo y de referencia se estuvo analizando la comparación de variables. 
En ese apartado se mencionó que la utilización del operador ‘==’ para tipos de datos de 
referencia tenía un comportamiento especial, pues no comparaba el contenido de los objetos, 
sino las posiciones en memoria. En ese momento se mencionó que para realizar la comparación 
del contenido era necesario cotejar uno a uno los valores de todos los atributos de los objetos. 
Como es conveniente que todas estas instrucciones de comparación sean definidas en un 
método y que este método se encuentre dentro de la clase a comparar, Java define un método 
para tal fin en la clase Object (por herencia es adquirido por todas las clases en Java).
Este método tiene la siguiente definición en la clase Object.
public boolean equals(Object obj){
 return (this==obj);
}
113
Por defecto este método lo que hace es comparar las referencias de los objetos y si son la misma 
devuelve verdadero (true), en caso contrario devuelve falso (false). Corresponde a una clase 
especifica sobrescribir esta implementación y brindar una que posibilite el cotejar los valores 
de los atributos del objeto. Para el caso de análisis de la aplicación del cálculo de la nómina 
podría definirse la clase EmpleadoXHora de la siguiente manera:
public class EmpleadoXHora extends Empleado{
 private double horasTrabajadas;
 private double sueldoXHora;
 public EmpleadoXHora(String cedula, String apellido, String nombre, 
 double horasTrabajadas, double sueldoXHora){
 super(cedula, apellido, nombre);
 setSueldoXHora(sueldoXHora);
 setHorasTrabajadas(horasTrabajadas);
 }
 public double calcularSalario(){
 return horasTrabajadas * sueldoXHora;
 }
 public double getHorasTrabajadas(){
 return horasTrabajadas;
 }
 public void setHorasTrabajadas(double horas ){
 if (horas>=0) this.horasTrabajadas = horas;
 else this.horasTrabajadas = 0;
 }
 public double getSueldoXHora(){
 return sueldoXHora;
 }
 public void setSueldoXHora(double sueldo ){
 if (sueldo>=0) this.sueldoXHora = sueldo;
 else this.sueldoXHora = 0;
 }
 public boolean equals(Object o){
 if (!(o instanceof EmpleadoXHora)) return false;
 
 EmpleadoXHora temp = (EmpleadoXHora)o;
 
 if ( !this.getCedula().equals(temp.getCedula()) ) return false;
 if ( !this.getApellido().equals(temp.getApellido()) ) return false;
 if ( !this.getNombre().equals(temp.getNombre()) ) return false;
 if ( this.getSueldoXHora()!=temp.getSueldoXHora() ) return false;
 if ( this.getHorasTrabajadas()!=temp.getHorasTrabajadas() ) 
 return false;
 return true;
 }
}
1. TÓPICOS BÁSICOS
114
Todo lo básico que debería saber sobre Programación orientada a objetos en Java
Sobre la anterior implementación es conveniente destacar:
•	 El método equals recibe como parámetro el objeto contra el cual se compara a otro. 
Esta situación debe pensarse de la siguiente manera: suponga que tiene dos referencias 
de tipo EmpleadoXHora a y b, para compararlas da lo mismo utilizar la instrucción 
a.equals(b) que emplear b.equals(a). El resultado del método corresponde a un 
dato de tipo booleano. 
•	 En la primera instrucción definida dentro del método equals aparece la utilización 
de la palabra reservada instanceof. Con esta instrucción se puede determinar si una 
variablees una instancia de una determinada clase. 
•	 Como el método equals recibe un parámetro de tipo Object, y puesto que Object es la 
superclase de todas las demás clases, se puede recibir en este parámetro una referencia 
a cualquier objeto (sea o no de la clase EmpleadoXHora). El propósito de la primera 
instrucción de este método es corroborar que efectivamente se van a comparar dos 
objetos de la misma clase (en este caso EmpleadoXHora). En caso de que los objetos no 
sean de la misma clase, se devuelve falso. 
•	 En la siguiente línea se define una variable temp (de temporal), se toma el objeto o 
que se recibe como parámetro se le hace casting y se le asigna el resultado de este a la 
variable temp.
•	 Las siguientes instrucciones comparan uno a uno los valores de los atributos de cada 
objeto. Cabe resaltar que la comparación de los atributos cedula, apellido y nombre 
se hace empleado el método equals en este caso de la clase String. Todo esto porque 
String es una clase y, por lo tanto, se maneja como tipo de dato de referencia. Los 
atributos sueldoXHora y horasTrabajadas si se comparan empleando el operador de 
desigualdad ‘!=’.
•	 Si algún valor de un atributo no se corresponde con el valor del atributo en el otro 
objeto finaliza el método y se devuelve el valor lógico de falso (false) como resultado. 
En caso de que ninguna de esas condiciones se cumpla entonces los dos objetos son 
iguales y, por ende, se devuelve el valor lógico de verdadero (true).
1.14. métOdOs y clAses finAles
Hasta el momento una subclase puede cambiar la implementación de un método que hereda 
de su superclase, sin embargo existe un concepto que ayuda a que dicha situación.
Definición de método final 
Corresponde a un método que no puede ser redefinido o sobrescrito por las subclases.
115
En Java para declarar un método final se le debe anteponer la palabra ‘final’ a la definición. 
Intentar sobrescribir un método final de una clase genera un error en tiempo de compilación. 
novena aproximaCión a la apliCaCión del CálCulo de la nómina 
mediante programaCión orientada a obJetos
Los cambios más relevantes en esta novena implementación de la aplicación de la nómina 
son la inclusión de constructores polimórficos para la clase Empresa, la definición de métodos 
finales en la clase Empleado y la definición de los métodos equals en las subclases de Empleado.
El código que aparece en negrilla resalta las modificaciones efectuadas con respecto a la anterior 
aproximación.
public abstract class Empleado{
 protected final String cedula;
 private String apellido;
 private String nombre;
 public Empleado(String cedula, String apellido, String nombre){
 if (cedula!=null) this.cedula = cedula;
 else this.cedula = “”;
 setApellido(apellido);
 setNombre(nombre);
 }
 public abstract double calcularSalario();
 public final String getCedula(){
 return cedula;
 }
 public final String getApellido(){
 return apellido;
 }
 public final void setApellido(String apellido){
 if (apellido!=null) this.apellido = apellido;
 else this.apellido=””;
 }
 public final String getNombre(){
 return nombre;
 }
public final void setNombre(String nombre){
 if (nombre!=null) this.nombre = nombre;
 else this.nombre=””;
 }
}
1. TÓPICOS BÁSICOS
116
Todo lo básico que debería saber sobre Programación orientada a objetos en Java
public class EmpleadoXHora extends Empleado{
 private double horasTrabajadas;
 private double sueldoXHora;
 public EmpleadoXHora(String cedula, String apellido, String nombre, 
 double horasTrabajadas, double sueldoXHora){
 super(cedula, apellido, nombre);
 setSueldoXHora(sueldoXHora);
 setHorasTrabajadas(horasTrabajadas);
 }
 public double calcularSalario(){
 return horasTrabajadas * sueldoXHora;
 }
 public double getHorasTrabajadas(){
 return horasTrabajadas;
 }
 public void setHorasTrabajadas(double horas ){
 if (horas>=0) this.horasTrabajadas = horas;
 else this.horasTrabajadas = 0;
 }
 public double getSueldoXHora(){
 return sueldoXHora;
 }
 public void setSueldoXHora(double sueldo ){
 if (sueldo>=0) this.sueldoXHora = sueldo;
 else this.sueldoXHora = 0;
 }
 public boolean equals(Object o){
 if (!(o instanceof EmpleadoXHora)) return false;
 
 EmpleadoXHora temp = (EmpleadoXHora)o;
 
 if ( !this.getCedula().equals(temp.getCedula()) ) return false;
 if ( !this.getApellido().equals(temp.getApellido()) ) return false;
 if ( !this.getNombre().equals(temp.getNombre()) ) return false;
 if ( this.getSueldoXHora()!=temp.getSueldoXHora() ) return false;
 if ( this.getHorasTrabajadas()!=temp.getHorasTrabajadas() ) 
 return false;
 return true;
 }
}
117
public class EmpleadoXComision extends Empleado{
 private double sueldoBasico;
 private double porcentaje;
 private double ventasTotales;
 public EmpleadoXComision(String cedula, String apellido, 
 String nombre, double sueldoBasico, double porcentaje, 
 double ventasTotales){
 
 super(cedula, apellido, nombre);
 setSueldoBasico(sueldoBasico);
 setPorcentaje(porcentaje);
 setVentasTotales(ventasTotales);
 }
 public double calcularSalario(){
 return sueldoBasico + porcentaje * ventasTotales;
 }
 public double getSueldoBasico(){
 return sueldoBasico;
 }
 public void setSueldoBasico(double sueldo){
 if (sueldo>=0) this.sueldoBasico = sueldo;
 else this.sueldoBasico = 0;
 }
 public double getPorcentaje(){
 return porcentaje;
 }
 public void setPorcentaje(double porcentaje ){
 if (porcentaje>=0 && porcentaje<=1) this.porcentaje = porcentaje;
 else this.porcentaje = 0;
 }
 public double getVentasTotales(){
 return ventasTotales;
 }
 public void setVentasTotales(double ventas){
 if (ventas>=0) this.ventasTotales = ventas;
 else this.ventasTotales = 0;
 }
1. TÓPICOS BÁSICOS
118
Todo lo básico que debería saber sobre Programación orientada a objetos en Java
 
 public boolean equals(Object o){
 if (!(o instanceof EmpleadoXComision)) return false;
 
 EmpleadoXComision temp = (EmpleadoXComision)o;
 
 if ( !this.getCedula().equals(temp.getCedula()) ) return false;
 if ( !this.getApellido().equals(temp.getApellido()) ) return false;
 if ( !this.getNombre().equals(temp.getNombre()) ) return false;
 if ( !this.getSueldoBasico()==temp.getSueldoBasico() ) 
 return false;
 if ( !this.getPorcentaje()==temp.getPorcentaje() ) return false;
 if ( !this.getVentasTotales()==temp.getVentasTotales() ) 
 return false;
 return true;
 }
}
public class EmpleadoSueldoFijo extends Empleado{
 private double sueldoFijo;
 public EmpleadoSueldoFijo(String cedula, String apellido, 
 String nombre, double sueldoFijo){
 super(cedula, apellido, nombre);
 setSueldoFijo(sueldoFijo);
 }
 public double calcularSalario(){
 return sueldoFijo;
 }
 public double getSueldoFijo(){
 return sueldoFijo;
 }
 public void setSueldoFijo(double sueldo){
 if (sueldo>=0) this.sueldoFijo = sueldo;
 else this.sueldoFijo = 0;
 }
 public boolean equals(Object o){
 if (!(o instanceof EmpleadoSueldoFijo)) return false;
 
 EmpleadoSueldoFijo temp = (EmpleadoSueldoFijo)o;
 
 if ( !this.getCedula().equals(temp.getCedula()) ) return false;
 if ( !this.getApellido().equals(temp.getApellido()) ) return false;
 if ( !this.getNombre().equals(temp.getNombre()) ) return false;
119
 if ( !this.getSueldoFijo()!=temp.getSueldoFijo() ) return false;
 return true;
 }
}
public class Empresa{
 final private String nit;
 private String nombre;
 private String direccion;
 private Empleado[] empleados;
 private int numeroEmpleados;
 public Empresa(String nit, String nombre, String direccion){
 if (nit!=null) this.nit = nit;
 else this.nit=””;
 setNombre(nombre);
 setDireccion(direccion);
 empleados = new Empleado[50];
 numeroEmpleados = 0;
 }
 publicEmpresa(String nit){
 if (nit!=null) this.nit = nit;
 else this.nit=””;
 setNombre(“”);
 setDireccion(“”);
 empleados = new Empleado[50];
 numeroEmpleados = 0;
 }
 public Empresa(String nit, int numero){
 if (nit!=null) this.nit = nit;
 else this.nit=””;
 setNombre(“”);
 setDireccion(“”);
 empleados = new Empleado[numero];
 numeroEmpleados = 0;
 }
 public String getNit(){
 return nit;
 }
1. TÓPICOS BÁSICOS
120
Todo lo básico que debería saber sobre Programación orientada a objetos en Java
 public String getNombre(){
 return nombre;
 }
 public void setNombre(String nombre){
 if (nombre!=null) this.nombre = nombre;
 else this.nombre=””;
 }
 public String getDireccion(){
 return direccion;
 }
 public void setDireccion(String direccion){
 if (direccion!=null) this.direccion = direccion;
 else this.direccion=””;
 }
 public int getNumeroEmpleados(){
 return numeroEmpleados;
 }
 public void adicionarEmpleadoXHora(String cedula, String apellido, 
 String nombre, double horasTrabajadas, double sueldoXHora){
 empleados[numeroEmpleados] = new EmpleadoXHora(cedula, apellido,
 nombre, horasTrabajadas, sueldoXHora);
 numeroEmpleados++;
 }
 public void adicionarEmpleadoXComision(String cedula, String apellido,
 String nombre, double sueldoBasico, double porcentaje, 
 double ventasTotales){
 empleados[numeroEmpleados] = new EmpleadoXComision(cedula, apellido, 
 nombre, sueldoBasico, porcentaje, ventasTotales);
 numeroEmpleados++;
 }
 public void adicionarEmpleadoSueldoFijo(String cedula, 
 String apellido, String nombre, double sueldoFijo){
 empleados[numeroEmpleados] = new EmpleadoSueldoFijo(cedula,
 apellido, nombre, sueldoFijo);
 numeroEmpleados++;
 }
 public double calcularNominaTotal(){
 double total = 0;
 for (int i=0;i<numeroEmpleados;i++)
 total = total + empleados[i].calcularSalario();
 return total;
 }
}
121
import java.io.BufferedReader;
import java.io.InputStreamReader;
public class Nomina1{
 public static void main(String[] args) throws Exception{
 Empresa pyme = new Empresa(“123”,”Acme inc.”,”805 Silicon Valley”);
 String cedula, apellido, nombre; 
 double total = 0;
 
 BufferedReader br = new BufferedReader( 
 new InputStreamReader( System.in ) );
 System.out.print(“Digite numero de empleados sueldo por hora: “); 
 int numeroEmpleados1 = Integer.valueOf(br.readLine()).intValue();
 
 for(int i=0;i<numeroEmpleados1;i++){
 double horas, sueldo;
 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 del empleado: “);
 horas = Double.valueOf(br.readLine()).doubleValue(); 
 System.out.print(“Digite sueldo por hora del empleado: “ );
 sueldo = Double.valueOf(br.readLine()).doubleValue();
 pyme.adicionarEmpleadoXHora(cedula, apellido, nombre, horas,
 sueldo);
 }
 System.out.print(“Digite numero de empleados por comision: “); 
 int numeroEmpleados2 = Integer.valueOf(br.readLine()).intValue();
 
 for(int i=0;i<numeroEmpleados2;i++){
 double sueldo, porcentaje, ventas;
 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 sueldo basico empleado: “);
 sueldo = Double.valueOf(br.readLine()).doubleValue(); 
 System.out.print(“Digite porcentaje del empleado: “ );
 porcentaje = Double.valueOf(br.readLine()).doubleValue();
 System.out.print(“Digite ventas totales del empleado: “ );
 ventas = Double.valueOf(br.readLine()).doubleValue();
1. TÓPICOS BÁSICOS
122
Todo lo básico que debería saber sobre Programación orientada a objetos en Java
 pyme.adicionarEmpleadoXComision(cedula, apellido, nombre, sueldo,
 porcentaje, ventas);
 }
 System.out.print(“Digite numero de empleados sueldo fijo: “); 
 int numeroEmpleados3 = Integer.valueOf(br.readLine()).intValue();
 
 for(int i=0;i<numeroEmpleados3;i++){
 double sueldo;
 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 sueldo basico empleado: “);
 sueldo = Double.valueOf(br.readLine()).doubleValue(); 
 pyme.adicionarEmpleadoSueldoFijo(cedula, apellido, nombre, 
 sueldo);
 }
 total = pyme.calcularNominaTotal();
 
 System.out.println(“\nLa nómina total es: “+ total);
 
 }
}
Definición de clase final
Corresponde a una clase que no puede ser extendida.
Para declarar una clase final se debe anteponer la palabra reservada ‘final’ a su definición. 
Intentar crear una clase que extienda una clase final generará un error en tiempo de 
compilación. A manera de ejemplo suponga que se desea definir la clase Empresa como final; 
lo anterior se logra a través de la siguiente definición (se obvia el resto de código que hace 
parte de la definición de la clase):
public final class Empresa
123
1.15. HerenciA simple y múltiple
Suponga que la codificación de un juego requiere la definición de las siguientes clases.
public class Vehiculo …
public class VehiculoCivil extends Vehiculo …
public class VehiculoMilitar extends Vehiculo …
public class VehiculoTerrestre extends Vehiculo …
public class VehiculoAcuatico extends Vehiculo …
Los ejemplos presentados anteriormente (y todos los demás presentados hasta el momento 
en este libro) donde se emplea el concepto de herencia muestran de lo que se conoce como 
herencia simple.
Definición de herencia simple
Capacidad de definir una clase de tal manera de que esta extienda de forma directa a 
una sola superclase.
Cualquier lector podría estar pensando en estos momentos que la clase VehiculoTerrestre 
tiene como superclase a la clase Vehiculo, y también por transitividad (de forma indirecta) 
a la clase Object (por ser la superclase de Vehiculo). 
Esto efectivamente es así, pero note que dentro de la definición de herencia simple se menciona 
la palabra directa, por ende, de forma directa la única superclase de VehiculoTerrestre es 
Vehiculo.
Suponga que para el juego en mención adicionalmente se requiere la codificación de una 
clase que represente a tanques de guerra; se podría pensar en realizar la siguiente definición 
para aprovechar las características definidas en clases previas.
public class TanqueGuerra extends VehiculoMilitar, VehiculoTerrestre …
En el código presentado anteriormente se ejemplifica un caso de herencia múltiple.
Definición de herencia múltiple
Capacidad de definir una clase de tal manera que esta tenga más de una superclase 
directa y, por ende, pueda así adquirir de manera automática todos los atributos y 
métodos definidos en todas sus superclases.
La diferencia entre este tipo de herencia es el número de superclases directa que tiene la 
subclase. Es conveniente mencionar en este punto que Java no permite la utilización de 
1. TÓPICOS BÁSICOS
124
Todo lo básico que debería saber sobre Programación orientada a objetos en Java
la herencia múltiple para la definición de nuevas clases (como sí es permitido en otros 
lenguajes orientados a objetos); sin embargo, sí posibilita la utilización de un concepto 
extremadamente importante como las interfaces, que posibilita la simulación de ciertas 
características de la herencia múltiple. 
1.16.interfAces
El lenguaje de programación Java permite la definición de dos tipos de componentes básicos 
para las aplicaciones orientadas a objetos: la clase y la interface.
Definición de interfaces
Una interface define la signatura de un conjunto de servicios que deben ser exhibidos por 
una clase. Corresponde a la definición señalar qué debe saber hacer una clase, pero no 
cómo debe realizar la implementación de dichos servicios. 
La sintaxis para la definición en Java de una interface no varía mucho respecto a la definición 
de una clase. Para la presentación de un ejemplo se retomará el caso presentado en la sección 
de encapsulamiento referente a la codificación de un componente para la representación de 
una fecha, en donde, pudo haberse encapsulado la funcionalidad requerida que debía ser 
exhibida por el componente en una interface, por ejemplo:
public interface ServiciosFecha{
 public int numeroMes();
 public String nombreMes();
 public String formato1();
 public String formato2();
}
Del anterior código cabe notar lo siguiente:
•	 La línea de definición de la interface es similar a la de la definición de una clase, lo 
único que varía es que en esta aparece la palabra interface.
•	 La definición del nivel de visibilidad de la interface, que para este caso es público.
•	 Aparece como contenido de la interface la definición de varios métodos, mas no sus 
respectivas implementaciones.
Como una interface es simplemente la definición de un contrato de servicio, es decir, 
qué debe saber hacerse, es necesario una contraparte donde se defina el cómo va a ser la 
implementación del servicio. Para esto las interfaces se ayudan con las clases, es decir, al 
definir una clase esta puede especificar explícitamente que va a dar implementación a una 
interface. Esto se logra a través de la utilización de la palabra reservada implements en la 
definición de la clase. Por ejemplo, considere la siguiente definición:
125
public class Fecha1 implements ServiciosFecha
 
Los siguientes puntos deben ser tenidos en cuenta al trabajar y manipular interfaces.
•	 Los niveles de visibilidad permitidos para la definición de una interface son público 
o el nivel de visibilidad por defecto. 
•	 Dentro de una interfaz pueden definirse atributos, pero únicamente finales. El valor 
inicial de estos atributos debe ser asignado en la misma declaración.
•	 El único nivel de visibilidad permitido para la definición de métodos o atributos en 
las interfaces es público. Si no se define explícitamente a través de la utilización de la 
palabra reservada ‘public’, Java lo asume de forma automática.
•	 Los métodos definidos dentro de una interface deben ser abstractos obligatoriamente. 
Si no se definen explícitamente a través de la utilización de la palabra reservada 
‘abstract’, Java lo asume de forma automática.
•	 Es posible declarar interfaces donde no se definan métodos.
•	 Cuando en la definición de una clase se declara que esta implementará una interface, 
es una obligación que esta clase implemente todos los métodos definidos en la 
interface. En caso de que la clase solo pueda implementar algunos cuantos, la clase 
debe declararse como abstracta.
•	 Al igual que como sucede con la relación de herencia, la relación de implementación 
de una interface puede denotarse con la frase ‘es un tipo de’, de tal manera que la 
clase (que implementa) es un tipo de elemento de la interface. 
•	 Una clase puede implementar cualquier número de interfaces; es por ello que se 
mencionó anteriormente que con el uso de interfaces se pueden simular determinadas 
características de la herencia múltiple. Para definirse en Java, solo se necesita colocar 
la distintas interfaces separadas por comas después de la definición de implements. 
Por ejemplo:
 public class Ejemplo implements Interface1, Interface2, Interface3
En este caso, la clase Ejemplo debe proporcionar implementación para todos los métodos 
definidos en todas las interfaces. En caso de que existan métodos con la misma signatura 
en diferentes clases, solo será necesario implementarlo una vez en la clase. 
•	 Una interface puede extender varias interfaces; en otras palabras una interface puede 
definirse en función de varias interfaces, por consiguiente, en Java se encuentra 
permitida la herencia múltiple entre interfaces. Considere el siguiente ejemplo.
 public interface Nadador{
 public void bracear(); 
 public void respirar();
 }
 public interface Ciclista{
1. TÓPICOS BÁSICOS
126
Todo lo básico que debería saber sobre Programación orientada a objetos en Java
 public void pedalear(); 
 public void respirar();
 }
 public interface Corredor{
 public void trotar(); 
 public void respirar();
 }
Con la declaración de las anteriores interfaces se podría pensar en la definición de la 
interface TriAtleta de la siguiente forma:
 public interface TriAtleta extends Nadador, Ciclista, Corredor{}
•	 No habría necesidad de definir ningún método dentro de la interface TriAtleta, 
y cuando una clase declare que implementará esta interface será necesario que le 
asigne código a todos los métodos definidos en las interfaces Nadador, Ciclista y 
Corredor (en realidad solo se deberán implementar cuatro métodos que corresponden 
a bracear, pedalear, trotar y respirar; este último unicamente debe implementarse 
una sola vez).
•	 Es posible definir referencias a objetos cuyo tipo de dato sea una inter face, por 
ejemplo, la siguiente definición sería totalmente válida: TriAtleta a = null;
•	 Al igual que como sucedía con las clases abstractas, no se puede utilizar una interface 
como parte de una instrucción new, a menos que en la misma declaración se le asigne 
implementación a los métodos abstractos9.
déCima aproximaCión a la apliCaCión del CálCulo de la nómina 
empleando programaCión orientada a obJetos
En esta implementación de la aplicación de la nómina se incluye la definición y el trabajo con 
una nueva interface. El código que aparece en negrilla resalta las modificaciones efectuadas 
con respecto a la anterior aproximación.
public interface PersonaAsalariada{
 public double calcularSalario();
}
public abstract class Empleado implements PersonaAsalariada{
 protected final String cedula;
 private String apellido;
 private String nombre;
9 Esta última modalidad corresponde a tópicos avanzados que serán analizados en capítulos posteriores.
127
 public Empleado(String cedula, String apellido, String nombre){
 if (cedula!=null) this.cedula = cedula;
 else this.cedula = “”;
 setApellido(apellido);
 setNombre(nombre);
 }
 public final String getCedula(){
 return cedula;
 }
 public final String getApellido(){
 return apellido;
 }
 public final void setApellido(String apellido){
 if (apellido!=null) this.apellido = apellido;
 else this.apellido=””;
 }
 public final String getNombre(){
 return nombre;
 }
 public final void setNombre(String nombre){
 if (nombre!=null) this.nombre = nombre;
 else this.nombre=””;
 }
}
Note cómo en la clase Empleado no aparece la definición del método calcularSalario. La 
razón es que este método se encuentra definido en la interface, y se sobreentiende que la clase 
Empleado es abstracta porque aún no le ha brindado implementación a dicho método.
En las subclases de Empleado no se ha producido cambio alguno.
public class EmpleadoXHora extends Empleado{
 private double horasTrabajadas;
 private double sueldoXHora;
 public EmpleadoXHora(String cedula, String apellido, String nombre, 
 double horasTrabajadas, double sueldoXHora){
 super(cedula, apellido, nombre);
 setSueldoXHora(sueldoXHora);
 setHorasTrabajadas(horasTrabajadas);
 }
1. TÓPICOS BÁSICOS
128
Todo lo básico que debería saber sobre Programación orientada a objetos en Java
 public double calcularSalario(){
 return horasTrabajadas * sueldoXHora;
 }
 public double getHorasTrabajadas(){
 return horasTrabajadas;
 }
 public voidsetHorasTrabajadas(double horas ){
 if (horas>=0) this.horasTrabajadas = horas;
 else this.horasTrabajadas = 0;
 }
 public double getSueldoXHora(){
 return sueldoXHora;
 }
 public void setSueldoXHora(double sueldo ){
 if (sueldo>=0) this.sueldoXHora = sueldo;
 else this.sueldoXHora = 0;
 }
 public boolean equals(Object o){
 if (!(o instanceof EmpleadoXHora)) return false;
 
 EmpleadoXHora temp = (EmpleadoXHora)o;
 
 if ( !this.getCedula().equals(temp.getCedula()) ) return false;
 if ( !this.getApellido().equals(temp.getApellido()) ) return false;
 if ( !this.getNombre().equals(temp.getNombre()) ) return false;
 if ( this.getSueldoXHora()!=temp.getSueldoXHora() ) return false;
 if ( this.getHorasTrabajadas()!=temp.getHorasTrabajadas() ) 
 return false;
 return true;
 }
}
public class EmpleadoXComision extends Empleado{
 private double sueldoBasico;
 private double porcentaje;
 private double ventasTotales;
 public EmpleadoXComision(String cedula, String apellido, 
 String nombre, double sueldoBasico, double porcentaje, 
 double ventasTotales){
 
 super(cedula, apellido, nombre);
 setSueldoBasico(sueldoBasico);
 setPorcentaje(porcentaje);
 setVentasTotales(ventasTotales);
 }
129
 public double calcularSalario(){
 return sueldoBasico + porcentaje * ventasTotales;
 }
 public double getSueldoBasico(){
 return sueldoBasico;
 }
 public void setSueldoBasico(double sueldo){
 if (sueldo>=0) this.sueldoBasico = sueldo;
 else this.sueldoBasico = 0;
 }
 public double getPorcentaje(){
 return porcentaje;
 }
 public void setPorcentaje(double porcentaje ){
 if (porcentaje>=0 && porcentaje<=1) this.porcentaje = porcentaje;
 else this.porcentaje = 0;
 }
 public double getVentasTotales(){
 return ventasTotales;
 }
 public void setVentasTotales(double ventas){
 if (ventas>=0) this.ventasTotales = ventas;
 else this.ventasTotales = 0;
 }
 public boolean equals(Object o){
 if (!(o instanceof EmpleadoXComision)) return false;
 
 EmpleadoXComision temp = (EmpleadoXComision)o;
 
 if ( !this.getCedula().equals(temp.getCedula()) ) return false;
 if ( !this.getApellido().equals(temp.getApellido()) ) return false;
 if ( !this.getNombre().equals(temp.getNombre()) ) return false;
 if ( this.getSueldoBasico()!=temp.getSueldoBasico() ) return false;
 if ( this.getPorcentaje()!=temp.getPorcentaje() ) return false;
 if ( this.getVentasTotales()!=temp.getVentasTotales() ) 
 return false;
 return true;
 }
}
1. TÓPICOS BÁSICOS
130
Todo lo básico que debería saber sobre Programación orientada a objetos en Java
public class EmpleadoSueldoFijo extends Empleado{
 private double sueldoFijo;
 public EmpleadoSueldoFijo(String cedula, String apellido, 
 String nombre, double sueldoFijo){
 super(cedula, apellido, nombre);
 setSueldoFijo(sueldoFijo);
 }
 public double calcularSalario(){
 return sueldoFijo;
 }
 public double getSueldoFijo(){
 return sueldoFijo;
 }
 public void setSueldoFijo(double sueldo){
 if (sueldo>=0) this.sueldoFijo = sueldo;
 else this.sueldoFijo = 0;
 }
 public boolean equals(Object o){
 if (!(o instanceof EmpleadoSueldoFijo)) return false;
 
 EmpleadoSueldoFijo temp = (EmpleadoSueldoFijo)o;
 
 if ( !this.getCedula().equals(temp.getCedula()) ) return false;
 if ( !this.getApellido().equals(temp.getApellido()) ) return false;
 if ( !this.getNombre().equals(temp.getNombre()) ) return false;
 if ( this.getSueldoFijo()!=temp.getSueldoFijo() ) return false;
 return true;
 }
}
131
public class Empresa{
 final private String nit;
 private String nombre;
 private String direccion;
 private Empleado[] empleados;
 private int numeroEmpleados;
 public Empresa(String nit, String nombre, String direccion){
 if (nit!=null) this.nit = nit;
 else this.nit=””;
 setNombre(nombre);
 setDireccion(direccion);
 empleados = new Empleado[50];
 numeroEmpleados = 0;
 }
 public Empresa(String nit){
 if (nit!=null) this.nit = nit;
 else this.nit=””;
 setNombre(“”);
 setDireccion(“”);
 empleados = new Empleado[50];
 numeroEmpleados = 0;
 }
 public Empresa(String nit, int numero){
 if (nit!=null) this.nit = nit;
 else this.nit=””;
 setNombre(“”);
 setDireccion(“”);
 empleados = new Empleado[numero];
 numeroEmpleados = 0;
 }
 public String getNit(){
 return nit;
 }
 public String getNombre(){
 return nombre;
 }
 public void setNombre(String nombre){
 if (nombre!=null) this.nombre = nombre;
 else this.nombre=””;
 }
 public String getDireccion(){
 return direccion;
1. TÓPICOS BÁSICOS
132
Todo lo básico que debería saber sobre Programación orientada a objetos en Java
 }
 public void setDireccion(String direccion){
 if (direccion!=null) this.direccion = direccion;
 else this.direccion=””;
 }
 public int getNumeroEmpleados(){
 return numeroEmpleados;
 }
 public void adicionarEmpleadoXHora(String cedula, String apellido, 
 String nombre, double horasTrabajadas, double sueldoXHora){
 empleados[numeroEmpleados] = new EmpleadoXHora(cedula, apellido,
 nombre, horasTrabajadas, sueldoXHora);
 numeroEmpleados++;
 }
 public void adicionarEmpleadoXComision(String cedula, String apellido,
 String nombre, double sueldoBasico, double porcentaje, 
 double ventasTotales){
 empleados[numeroEmpleados] = new EmpleadoXComision(cedula, apellido, 
 nombre, sueldoBasico, porcentaje, ventasTotales);
 numeroEmpleados++;
 }
 public void adicionarEmpleadoSueldoFijo(String cedula, 
 String apellido, String nombre, double sueldoFijo){
 empleados[numeroEmpleados] = new EmpleadoSueldoFijo(cedula,
 apellido, nombre, sueldoFijo);
 numeroEmpleados++;
 }
 public double calcularNominaTotal(){
 double total = 0;
 for (int i=0;i<numeroEmpleados;i++)
 total = total + empleados[i].calcularSalario();
 return total;
 }
}
import java.io.BufferedReader;
import java.io.InputStreamReader;
public class Nomina1{
 public static void main(String[] args) throws Exception{
 Empresa pyme = new Empresa(“123”,”Acme inc.”,”805 Silicon Valley”);
 String cedula, apellido, nombre; 
 double total = 0;
133
 BufferedReader br = new BufferedReader( 
 new InputStreamReader( System.in ) );
 
 System.out.print(“Digite numero de empleados sueldo por hora: “); 
 int numeroEmpleados1 = Integer.valueOf(br.readLine()).intValue();
 
 for(int i=0;i<numeroEmpleados1;i++){
 double horas, sueldo;
 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 del empleado: “);
 horas = Double.valueOf(br.readLine()).doubleValue(); 
 System.out.print(“Digite sueldo por hora del empleado: “ );
 sueldo = Double.valueOf(br.readLine()).doubleValue();
 pyme.adicionarEmpleadoXHora(cedula, apellido, nombre, horas,
 sueldo);
 }
 System.out.print(“Digite numero de empleados por comision: “); 
 int numeroEmpleados2 = Integer.valueOf(br.readLine()).intValue();
 
 for(int i=0;i<numeroEmpleados2;i++){
 double sueldo, porcentaje, ventas;
 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 sueldo basico empleado: “);
 sueldo = Double.valueOf(br.readLine()).doubleValue(); 
 System.out.print(“Digite porcentaje del empleado: “ );porcentaje = Double.valueOf(br.readLine()).doubleValue();
 System.out.print(“Digite ventas totales del empleado: “ );
 ventas = Double.valueOf(br.readLine()).doubleValue();
 pyme.adicionarEmpleadoXComision(cedula, apellido, nombre, sueldo,
 porcentaje, ventas);
 }
 System.out.print(“Digite numero de empleados sueldo fijo: “); 
 int numeroEmpleados3 = Integer.valueOf(br.readLine()).intValue();
 
 for(int i=0;i<numeroEmpleados3;i++){
 double sueldo;
 System.out.print(“\nDigite la cedula del empleado: “ );
1. TÓPICOS BÁSICOS
134
Todo lo básico que debería saber sobre Programación orientada a objetos en Java
 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 sueldo basico empleado: “);
 sueldo = Double.valueOf(br.readLine()).doubleValue(); 
 pyme.adicionarEmpleadoSueldoFijo(cedula, apellido, nombre, 
 sueldo);
 }
 total = pyme.calcularNominaTotal();
 
 System.out.println(“\nLa nómina total es: “+ total);
 
 }
}
135
2. 
TÓPICOS AVANZADOS
La décima aproximación a la aplicación del cálculo de la nómina es un buen ejemplo de 
una aplicación orientada a objetos. En esencia el problema a resolver es muy sencillo, pero 
en su interior se han podido trabajar varias de las nociones de la programación orientada a 
objetos. Analizándola en profundidad, esta aplicación posee un alto nivel de organización y 
delimitación funcional del código; sin embargo posee dos grandes problemas:
•	 El primero de ellos tiene que ver con la cantidad de objetos de tipo Empleado que 
pueden registrarse dentro de un objeto Empresa. La clase Empresa cuenta con tres 
constructores, dos de ellos definen un máximo de 50 empleados por empresa, y 
el restante constructor maneja como número máximo un valor especificado por 
el usuario. Sin importar el constructor que se utilice, todos presentan el mismo 
problema: el tamaño de un array no puede modificarse, ni para incrementarse, ni 
para decrementarse. En otras palabras: qué pasa si después de haber registrado 
empleados en los primeros 50 espacios del array es necesario ingresar el empleado 
número 51 (la misma situación aplica si se utilizase el otro constructor). 
•	 El segundo problema se encuentra relacionado con el manejo que se da a las situaciones 
anormales, por ejemplo, al instanciar la clase EmpleadoXHora si se suministra un valor 
negativo para el atributo horasTrabajadas se crea el objeto y se le asigna el valor por 
defecto de 0 a dicho campo. En ocasiones este tipo de comportamiento es adecuado 
y suficiente, pero existen escenarios donde sería recomendable informarle al usuario 
que se ha producido un error y explicarle en qué consiste. La décima aproximación 
a la aplicación de la nómina no posee código que permita avisarle a un usuario que 
se ha presentado una situación excepcional.
136
Todo lo básico que debería saber sobre Programación orientada a objetos en Java
Los temas que se presentan en este capítulo están dirigidos a mejorar la forma en que se 
desarrollan las aplicaciones con el fin de hacerlas más completas y robustas.
2.1. cOlecciOnes
Como cualquier otro lenguaje de programación, Java permite trabajar con estructuras de 
datos básicas como arrays y matrices. La gran limitante que existe al trabajar con estas 
estructuras (no solo en Java, sino en la mayoría de lenguajes de programación) es que su 
tamaño es estático10, es decir, una vez que se crea una estructura con un tamaño determinado 
no se le puede alterar. Como ejemplo, considere el siguiente código y el diagrama que 
representa una posible distribución de los objetos en memoria (se trabaja con la definición 
de clases presentada en la décima aproximación a la aplicación del cálculo de la nómina). 
Empresa e = new Empresa(“123”,3);
e.adicionarEmpleadoXHora(“72303”,”García”,”Luis”,100,15);
e.adicionarEmpleadoXComision(“98765”,”Sánchez”,”María”,1000,0.5,1000);
e.adicionarEmpleadoSueldoFijo(“12345”,”Pérez”,”Pedro”,1200);
Si se adicionase un nuevo empleado a la empresa, se generaría un error porque no hay 
espacio en el array donde incluirlo. No puede intentar corregirse el código porque no existe 
una instrucción en Java que permita cambiarle el tamaño al array para volverlo más grande.
Para resolver este problema existen dos alternativas11:
•	 La primera consiste en incluir en el código un artilugio de tal manera que cuando se 
llene el array se cree uno nuevo con mayor capacidad, se copien las referencias del 
10 En este contexto la palabra estático hace referencia a que su estado no cambia. No tiene relación con las definiciones que 
sobre métodos y atributos estáticos se presentaron anteriormente.
11 La alternativa de simplemente definir un array de gran tamaño no se contempla por el enorme gasto de recursos que se 
presentaría cuando no se requiera utilizarse tanto espacio en memoria.
137
antiguo array al nuevo y, finalmente, que el programa continúe trabajando con el 
nuevo array. Esta operación deberá efectuarse cada vez que se llene el array con el 
que trabaja el objeto empresa.
•	 La segunda consiste en no emplear estructuras estáticas como los arrays, sino 
dinámicas como las listas. 
Teniendo en mente la reutilización de código, sería más conveniente no codificar una 
solución particular para este problema en la clase Empresa, sino más bien idear una solución 
general que pueda ser reutilizada en otros escenarios. 
Si se analiza detenidamente, esta misma situación se repite constantemente en una infinidad 
de problemas, como, por ejemplo, al registrar los libros que han sido prestados en una 
biblioteca por un usuario, o al registrar los carros vendidos en una agencia, o al registrar los 
correos que le llegan a una persona; en realidad la lista de ejemplos es interminable. 
Como existen dos alternativas de solución (una que emplea artilugios de programación 
con arrays y otra, listas) se podría pensar en definir una clase para cada una de las posibles 
soluciones. Sin embargo, como ambas clases servirían para el mismo propósito podría 
pensarse en la definición de los métodos que deben poseer ambas clases en una interface.
public interface ServiciosColeccionElementos{
 public void agregar(Object o);
 public Object obtener(int i);
 public int getCantidadElementos();
}
De la anterior definición es conveniente resaltar los siguientes puntos:
•	 Como se está intentando construir una solución general no se hace referencia a un 
tipo de dato específico, sino que más bien se manejan referencias de tipo general 
como Object.
•	 Existe un método agregar que permite adicionar un elemento a la colección.
•	 Existe un método obtener que devuelve el i-ésimo elemento almacenado en la 
colección. Este método retorna el elemento, mas no lo elimina de la colección.
•	 Existe un método getCantidadElementos que retorna el número de elementos que 
han sido almacenados en la colección.
•	 Quedaría pendiente la definición de otros métodos útiles: como permitir eliminar 
elementos de la colección o mover un elemento de la colección de una posición 
a otra. Estos métodos no serán tenidos en cuenta por el momento para facilitar y 
simplificar la presentación del tema.
2. TÓPICOS AVANZADOS
138
Todo lo básico que debería saber sobre Programación orientada a objetos en Java
CodifiCaCión de la estruCtura de datos para maneJar 
una ColeCCión de elementos empleando arrays
Se presenta a continuación una posible implementación para la estructura de datos en 
mención.
public class ColeccionElementosConArrays
 implements ServiciosColeccionElementos {
 private Object[] losElementos;
 private int indice, tamano;
 public ColeccionElementosConArrays() {
 losElementos = new Object[10];
 indice = 0; tamano=10;
 }
 
 public void agregar(Object o){if (indice >= tamano){
 Object[] elnuevoarray = new Object[tamano+10];
 for (int i=0;i<indice;i++)
 elnuevoarray[i] = losElementos[i];
 losElementos = elnuevoarray;
 tamano = tamano +10;
 }
 
 losElementos[ indice ] = o;
 indice++;
 
 }
 
 public Object obtener(int i){
 if (i>=0 && i<indice)
 return losElementos[i]; 
 else return null;
 }
 public int getCantidadElementos() {
 return indice;
 }
}
Del anterior código es conveniente resaltar los siguientes puntos:
•	 La clase define tres atributos, un atributo de tipo array de Object, que guarda la 
referencia al array donde se almacenan los datos; un atributo indice, que registra 
la cantidad de elementos que han sido almacenados en el array; y, finalmente, un 
atributo tamano, que guarda el tamaño máximo de elementos que se pueden registrar 
en el array.
139
•	 Cuando el índice del nuevo elemento a registrar es igual (o mayor) a la cantidad 
máxima de elementos que se pueden registrar en el array, se crea un nuevo array que 
es más grande que el inicial en 10 posiciones. Se copian las referencias del antiguo 
array al nuevo; y, finalmente, el objeto ColeccionElementosConArrays cambia la 
referencia del atributo losElementos del antiguo array al nuevo. 
•	 Cuando se desea obtener un elemento empleando el método obtener, si el parámetro 
suministrado es menor que 0 o mayor a la cantidad de elementos que se han registrado 
en el array, se devuelve null. 
•	 Hay que tener en cuenta que el primer elemento se almacena en la posición cero del 
array, por ende, si se desea extraer el elemento n, debe pasarse como parámetro el 
valor de n-1.
CodifiCaCión de la estruCtura de datos para maneJar 
una ColeCCión de elementos empleando listas
La codificación de una solución alternativa mediante el empleo de listas requiere inicialmente 
definir la clase de los elementos que harán las veces de nodos.
class Nodo{
 private Nodo siguiente;
 private Object info;
 public Nodo(Object o){
 this.info = o;
 }
 public Nodo getSiguiente(){
 return siguiente;
 }
 public void setSiguiente(Nodo n){ 
 this.siguiente = n;
 }
 public Object getInfo(){
 return info;
 }
}
2. TÓPICOS AVANZADOS
140
Todo lo básico que debería saber sobre Programación orientada a objetos en Java
public class ColeccionElementosConListas
 implements ServiciosColeccionElementos {
 private Nodo elPrimero;
 private Nodo elUltimo;
 private int numero;
 public void agregar(Object o){
 Nodo nuevo = new Nodo(o);
 if (elPrimero==null)
 elPrimero=nuevo;
 else
 elUltimo.setSiguiente(nuevo);
 elUltimo = nuevo;
 numero = numero+1;
 }
 public Object obtener(int i){
 
 if (i<0 || i>=numero) return null;
 if (i==0) return elPrimero.getInfo();
 if (i==numero-1) return elUltimo.getInfo();
 Nodo temp=elPrimero;
 for (int contador=0;contador<i;contador++)
 temp = temp.getSiguiente();
 return temp.getInfo();
 }
 public int getCantidadElementos(){
 return numero;
 }
}
 
Note que en el método obtener el código se encuentra optimizado de tal forma que cuando se 
pide el último elemento su referencia se devuelve inmediatamente sin necesidad de recorrer 
la lista.
141
eJemplo de utilizaCión de las estruCturas de 
datos que posibilitan la manipulaCión de ColeCCiones de elementos
A continuación se presenta un ejemplo de utilización de objetos de las clases definidas en los 
apartados anteriores.
ServiciosColeccionElementos e = new ColeccionElementosConArrays();
e.agregar( new EmpleadoXHora(“72303”, “García”, “Luis”, 100, 15) );
e.agregar( new EmpleadoXComision(“98765”, “Sánchez”, “María”, 
 1000, 0.5, 1000) );
e.agregar( new EmpleadoSueldoFijo(“12345”, “Pérez”, “Pedro”, 1200) );
EmpleadoXHora e1 = (EmpleadoXHora)e.obtener(0);
System.out.println( e1.calcularSalario() );
Del anterior código es conveniente resaltar los siguientes puntos:
•	 Note que el objeto e tiene por tipo de dato ServiciosColeccionElementos, es 
decir, una interface. Esta definición es muy útil porque si se desea trabajar con la 
estructura de datos que maneja listas, solo es necesario cambiar la instrucción new 
ColeccionElementosConArrays() por new ColeccionElementosConListas(). No se 
requiere alterar ni una sola instrucción del restante código.
•	 El método obtener retorna una referencia almacenada en la colección, solo requiere 
suministrarse la posición. Este método tiene una particularidad; como fue definido 
para devolver referencias de tipo Object, debe realizarse un casting a lo que retorne 
antes de poder trabajar con la referencia almacenada.
Es interesante analizar que aunque ambas implementaciones para las colecciones son diferentes, 
los métodos que satisfacen son los mismos, y que para utilizar una de estas implementaciones 
solo se requiere alterar una línea de código. Conviene en este punto analizar cómo sería 
la estructura de los objetos en memoria en caso de ejecutar el código con cada una de las 
implementaciones.
2. TÓPICOS AVANZADOS
142
Todo lo básico que debería saber sobre Programación orientada a objetos en Java
Con las implementaciones y ejemplos presentados previamente es posible brindar una defi-
nición al concepto de colección.
143
Definición de colección
Estructura de datos que permite guardar y manipular referencias a otros 
objetos.
Programar empleando este tipo de estructuras de datos corresponde a una buena práctica 
de este tipo, ya que hace que el programador se desentienda de las operaciones de bajo nivel 
(administración de arrays, trabajo con listas, código de corrimiento de información) y se 
centre en la lógica propia de la aplicación. 
Afortunadamente, Java cuenta con una estructura unificada (framework) para el trabajo 
con colecciones12. Este marco de trabajo para las colecciones en Java, al igual que en los 
ejemplos presentados previamente, encapsula en la interface Collection todos los métodos 
que deben ser exhibidos por estas estructuras de datos. A continuación se presentan algunos 
de los más relevantes: 
•	 int size(). Retorna el número de elementos almacenados en la colección.
•	 boolean isEmpty(). Retorna verdadero (true) si la colección no tiene elementos.
•	 boolean contains(Object o). Retorna verdadero (true) si la colección contiene el 
elemento especificado. Requiere que la clase del elemento que se pasa por parámetro 
tenga redefinido apropiadamente el método equals y hashCode. Del método equals 
se ha hablado anteriormente en este libro; por su parte, el método hashCode lo poseen 
todos los objetos ya que lo heredan de la clase Object. La finalidad del método 
hashCode es el de retornar un valor hash para un objeto; este valor es de gran utilidad 
al trabajar con tablas hash. Si dos objetos son iguales de acuerdo al método equals, 
entonces la invocación del método hashCode en cada uno de ellos debe producir el 
mismo valor entero.
•	 boolean add(Object o). Adiciona a la colección el elemento que se pasa como 
parámetro. En caso de que no sea posible adicionar el elemento en la colección 
(posiblemente porque ya fue adicionado), retorna falso (false); en caso contrario, 
retorna verdadero (true).
•	 boolean remove(Object o). Remueve un elemento de la colección que sea igual 
al suministrado por parámetro (la comparación se realiza empleando el método 
equals). Retorna verdadero(true) si la colección contenía dicho elemento. 
•	 boolean containsAll(Collection c). Retorna verdadero (true) si la colección contiene todos 
los elementos que contiene la colección que se pasa por parámetro. 
12 Mayor información sobre las colecciones y el framework de trabajo en Java puede ser consultada en la siguiente refer-
encia: http://java.sun.com/docs/books/tutorial/collections/intro/index.html
2. TÓPICOS AVANZADOS
144
Todo lo básico que debería saber sobre Programación orientada a objetos en Java
•	 boolean addAll(Collection c). Adiciona a la colección todos los elementos que 
contengala colección que se pasa por parámetro. Devuelve verdadero (true) si la 
colección original cambia como resultado de la ejecución de la instrucción.
•	 boolean removeAll(Collection c). Remueve todos los elementos de la colección 
que se encuentren contenidos en la colección que se pasa por parámetro. Devuelve 
verdadero (true) si la colección original cambia como resultado de la ejecución de la 
instrucción. 
•	 boolean retainAll(Collection c). Mantiene dentro de la colección solamente los 
objetos que se encuentren contenidos en la colección que se pasa por parámetro. 
Devuelve verdadero (true) si la colección original cambia como resultado de la 
ejecución de la instrucción.
•	 void clear(). Remueve todos los elementos que tiene la colección.
El framework de colecciones de Java define básicamente tres tipos estructuras de datos 
(listas, conjuntos y colas13) y, adicionalmente, define una estructura de datos (mapas), que 
no es una colección, sino que se construye con base en las colecciones. Cada uno de los 
elementos antes definidos posee una interface (donde se definen puntualmente los servicios 
que se van a prestar) y, adicionalmente, tiene variadas implementaciones a dichas interfaces. 
2.1.1. Listas 
Definición de lista
Estas estructuras de datos corresponden a colecciones ordenadas y pueden 
poseer elementos repetidos. Al encontrarse ordenadas se hace posible que un 
usuario pueda manipular los elementos empleando sus posiciones.
Java define la interface List donde se especifican los métodos que debe proveer una 
implementación de este tipo de estructura de datos. Esta interface extiende Collection por 
lo cual también gana la definición de todos los métodos definidos en la superinterface. A 
continuación se presentan algunos de los métodos más relevantes de esta interface. 
•	 Object get(int pos). Retorna el elemento que se encuentra en la posición especificada 
por el parámetro que se pasa.
•	 Object set(int pos, Object o). Reemplaza el elemento que se encuentra en la 
posición indicada en el parámetro por el elemento que tambien se pasa por parámetro. 
Retorna el elemento que inicialmente se encontraba en dicha posición.
13 Esta colección no será analizada detalladamente como las restantes colecciones. Mayor información sobre esta puede ser 
consultada en la siguiente referencia: http://java.sun.com/docs/books/tutorial/collections/interfaces/queue.html
145
•	 void add(int pos, Object o). Inserta el elemento especificado por parámetro en la 
posición especificada.
•	 Object remove(int pos). Remueve el elemento que se encuentra en la posición 
especificada por parámetro. Retorna el elemento que se ha eliminado de la colección.
•	 boolean addAll(int pos, Collection c). Adiciona los elementos especificados en la 
colección que se pasa por parámetro a partir de la posición que se especifique. 
•	 int indexOf(Object o). Retorna el índice o posición de la primera ocurrencia dentro 
de la colección, del objeto que se pasa por parámetro. Retorna el valor de -1 en caso 
de que el objeto no se encuentre en la colección.
•	 int lastIndexOf(Object o). Retorna el índice o posición de la última ocurrencia 
dentro de la colección del objeto que se pasa por parámetro. 
•	 List subList(int inicial, int final). Retorna una porción de la lista especificada 
a partir de una posición inicial (incluida) hasta una posición final (excluida). Los 
elementos que se retornan se encapsulan dentro de un objeto, que debe cumplir con 
la interface List.
Java brinda tres implementaciones para esta interface, ellas son ArrayList, LinkedList y 
Vector. 
•	 ArrayList. Corresponde a una implementación donde se trabaja internamente 
con arrays (en esencia es similar a la implementación presentada en el apartado 
Codificación de la estructura de datos para manejar una colección de elementos 
empleando arrays). Es la implementación más popular y la más frecuentemente 
utilizada. Por las características de su implementación, es capaz de acceder a un 
elemento dentro de ella a una tasa constante en el tiempo.
•	 LinkedList. Corresponde a una implementación donde se trabaja internamente con 
listas (en esencia es similar a la implementación presentada en el apartado Codificación 
de la estructura de datos para manejar una colección de elementos empleando listas). 
Se recomienda su utilización si generalmente se estarán adicionando elementos al 
principio de la lista. Por las características de implementación su tasa de acceso a los 
elementos es lineal en el tiempo.
•	 Vector. Vector es una de las primeras implementaciones en Java que buscaba 
trabajar el concepto de colecciones. En la actualidad se mantiene por cuestiones de 
compatibilidad con antiguas versiones de Java.
2. TÓPICOS AVANZADOS
146
Todo lo básico que debería saber sobre Programación orientada a objetos en Java
deCimoprimera aproximaCión a la apliCaCión del CálCulo de la nómina 
mediante programaCión orientada a obJetos
En esta implementación de la aplicación se incluyen los cambios requeridos en la clase Empresa 
para que deje de trabajar con arrays y se pase a trabajar con un objeto de tipo ArrayList. 
Las restantes clases permanecen invariantes. El código que aparece en negrilla resalta las 
modificaciones efectuadas con respecto a la anterior aproximación.
public interface PersonaAsalariada{
 public double calcularSalario();
}
public abstract class Empleado implements PersonaAsalariada{
 protected final String cedula;
 private String apellido;
 private String nombre;
 public Empleado(String cedula, String apellido, String nombre){
 if (cedula!=null) this.cedula = cedula;
 else this.cedula = “”;
 setApellido(apellido);
 setNombre(nombre);
 }
 public final String getCedula(){
 return cedula;
 }
 public final String getApellido(){
 return apellido;
 }
 public final void setApellido(String apellido){
 if (apellido!=null) this.apellido = apellido;
 else this.apellido=””;
 }
 public final String getNombre(){
 return nombre;
 }
 public final void setNombre(String nombre){
 if (nombre!=null) this.nombre = nombre;
 else this.nombre=””;
 }
}
147
public class EmpleadoXHora extends Empleado{
 private double horasTrabajadas;
 private double sueldoXHora;
 public EmpleadoXHora(String cedula, String apellido, String nombre, 
 double horasTrabajadas, double sueldoXHora){
 super(cedula, apellido, nombre);
 setSueldoXHora(sueldoXHora);
 setHorasTrabajadas(horasTrabajadas);
 }
 public double calcularSalario(){
 return horasTrabajadas * sueldoXHora;
 }
 public double getHorasTrabajadas(){
 return horasTrabajadas;
 }
 public void setHorasTrabajadas(double horas ){
 if (horas>=0) this.horasTrabajadas = horas;
 else this.horasTrabajadas = 0;
 }
 public double getSueldoXHora(){
 return sueldoXHora;
 }
 public void setSueldoXHora(double sueldo ){
 if (sueldo>=0) this.sueldoXHora = sueldo;
 else this.sueldoXHora = 0;
 }
 public boolean equals(Object o){
 if (!(o instanceof EmpleadoXHora)) return false;
 
 EmpleadoXHora temp = (EmpleadoXHora)o;
 
 if ( !this.getCedula().equals(temp.getCedula()) ) return false;
 if ( !this.getApellido().equals(temp.getApellido()) ) return false;
 if ( !this.getNombre().equals(temp.getNombre()) ) return false;
 if ( this.getSueldoXHora()!=temp.getSueldoXHora() ) return false;
 if ( this.getHorasTrabajadas()!=temp.getHorasTrabajadas() ) 
 return false;
 return true;
 }
}
2. TÓPICOS AVANZADOS
148
Todo lo básico que debería saber sobre Programación orientada a objetos en Java
public class EmpleadoXComision extends Empleado{
 private double sueldoBasico;
 private double porcentaje;
 private double ventasTotales;
 public EmpleadoXComision(String cedula, String apellido, 
 String nombre, double sueldoBasico, double porcentaje, 
 double ventasTotales){
 
 super(cedula, apellido, nombre);
 setSueldoBasico(sueldoBasico);
 setPorcentaje(porcentaje);setVentasTotales(ventasTotales);
 }
 public double calcularSalario(){
 return sueldoBasico + porcentaje * ventasTotales;
 }
 public double getSueldoBasico(){
 return sueldoBasico;
 }
 public void setSueldoBasico(double sueldo){
 if (sueldo>=0) this.sueldoBasico = sueldo;
 else this.sueldoBasico = 0;
 }
 public double getPorcentaje(){
 return porcentaje;
 }
 public void setPorcentaje(double porcentaje ){
 if (porcentaje>=0 && porcentaje<=1) this.porcentaje = porcentaje;
 else this.porcentaje = 0;
 }
 public double getVentasTotales(){
 return ventasTotales;
 }
 public void setVentasTotales(double ventas){
 if (ventas>=0) this.ventasTotales = ventas;
 else this.ventasTotales = 0;
 }
149
 public boolean equals(Object o){
 if (!(o instanceof EmpleadoXComision)) return false;
 
 EmpleadoXComision temp = (EmpleadoXComision)o;
 
 if ( !this.getCedula().equals(temp.getCedula()) ) return false;
 if ( !this.getApellido().equals(temp.getApellido()) ) return false;
 if ( !this.getNombre().equals(temp.getNombre()) ) return false;
 if ( this.getSueldoBasico()!=temp.getSueldoBasico() ) return false;
 if ( this.getPorcentaje()!=temp.getPorcentaje() ) return false;
 if ( this.getVentasTotales()!=temp.getVentasTotales() ) 
 return false;
 return true;
 }
}
public class EmpleadoSueldoFijo extends Empleado{
 private double sueldoFijo;
 public EmpleadoSueldoFijo(String cedula, String apellido, 
 String nombre, double sueldoFijo){
 super(cedula, apellido, nombre);
 setSueldoFijo(sueldoFijo);
 }
 public double calcularSalario(){
 return sueldoFijo;
 }
 public double getSueldoFijo(){
 return sueldoFijo;
 }
 public void setSueldoFijo(double sueldo){
 if (sueldo>=0) this.sueldoFijo = sueldo;
 else this.sueldoFijo = 0;
 }
 public boolean equals(Object o){
 if (!(o instanceof EmpleadoSueldoFijo)) return false;
 
 EmpleadoSueldoFijo temp = (EmpleadoSueldoFijo)o;
2. TÓPICOS AVANZADOS
150
Todo lo básico que debería saber sobre Programación orientada a objetos en Java
 
 if ( !this.getCedula().equals(temp.getCedula()) ) return false;
 if ( !this.getApellido().equals(temp.getApellido()) ) return false;
 if ( !this.getNombre().equals(temp.getNombre()) ) return false;
 if ( this.getSueldoFijo()!=temp.getSueldoFijo() ) return false;
 return true;
 }
}
import java.util.ArrayList; 
public class Empresa{
 final private String nit;
 private String nombre;
 private String direccion;
 private ArrayList empleados;
 public Empresa(String nit, String nombre, String direccion){
 if (nit!=null) this.nit = nit;
 else this.nit=””;
 setNombre(nombre);
 setDireccion(direccion);
 empleados = new ArrayList();
 }
 public Empresa(String nit){
 if (nit!=null) this.nit = nit;
 else this.nit=””;
 setNombre(“”);
 setDireccion(“”);
 empleados = new ArrayList();
 }
 public Empresa(String nit, int numero){
 if (nit!=null) this.nit = nit;
 else this.nit=””;
 setNombre(“”);
 setDireccion(“”);
 empleados = new ArrayList(numero);
 }
 public String getNit(){
 return nit;
 }
151
 public String getNombre(){
 return nombre;
 }
 public void setNombre(String nombre){
 if (nombre!=null) this.nombre = nombre;
 else this.nombre=””;
 }
 public String getDireccion(){
 return direccion;
 }
 public void setDireccion(String direccion){
 if (direccion!=null) this.direccion = direccion;
 else this.direccion=””;
 }
 public int getNumeroEmpleados(){
 return empleados.size();
 }
 public void adicionarEmpleadoXHora(String cedula, String apellido, 
 String nombre, double horasTrabajadas, double sueldoXHora){
 empleados.add (new EmpleadoXHora(cedula, apellido, nombre, 
 horasTrabajadas, sueldoXHora) );
 }
 public void adicionarEmpleadoXComision(String cedula, String apellido,
 String nombre, double sueldoBasico, double porcentaje, 
 double ventasTotales){
 empleados.add( new EmpleadoXComision(cedula, apellido, nombre,
 sueldoBasico, porcentaje, ventasTotales) );
 }
 public void adicionarEmpleadoSueldoFijo(String cedula, 
 String apellido, String nombre, double sueldoFijo){
 empleados.add( new EmpleadoSueldoFijo(cedula, apellido, nombre, 
 sueldoFijo) );
 }
 public double calcularNominaTotal(){
 double total = 0;
 for (int i=0;i<empleados.size();i++)
 total = total + ((Empleado)empleados.get(i)).calcularSalario();
 return total;
 }
}
2. TÓPICOS AVANZADOS
152
Todo lo básico que debería saber sobre Programación orientada a objetos en Java
Sobre la implementación de la clase Empresa es conveniente resaltar los siguientes puntos:
•	 Como la estructura de datos es la que maneja y conoce la cantidad de elementos que 
posee, desaparece de la clase el atributo donde se registraba la cantidad de empleados 
de la empresa. Cuando se requiera trabajar con este valor, se utilizará la invocación al 
método size() de la colección.
•	 La clase ArrayList, por defecto, tiene un constructor que no recibe parámetros. 
Este constructor trabaja con una estructura interna de tamaño inicial 10; en caso de 
requerirse, esta estructura se incrementa. Esta clase también tiene un constructor que 
recibe la cantidad de elementos con los que el usuario sugiere la creación inicial de la 
estructura interna del objeto. Este constructor polimórfico también es utilizado en la 
clase Empresa.
•	 Note que para adicionar elementos en la colección se invoca el método add.
•	 Para obtener el elemento en una determinada posición se invoca el método get.
•	 Note que para poder invocar el método calcularSalario en los objetos almacenados 
en el ArrayList de nombre empleados, es necesario primero realizar un casting.
import java.io.BufferedReader;
import java.io.InputStreamReader;
public class Nomina1{
 public static void main(String[] args) throws Exception{
 Empresa pyme = new Empresa(“123”,”Acme inc.”,”805 Silicon Valley”);
 String cedula, apellido, nombre; 
 double total = 0;
 
 BufferedReader br = new BufferedReader( 
 new InputStreamReader( System.in ) );
 
 System.out.print(“Digite numero de empleados sueldo por hora: “); 
 int numeroEmpleados1 = Integer.valueOf(br.readLine()).intValue();
 
 for(int i=0;i<numeroEmpleados1;i++){
 double horas, sueldo;
 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 del empleado: “);
 horas = Double.valueOf(br.readLine()).doubleValue(); 
 System.out.print(“Digite sueldo por hora del empleado: “ );
 sueldo = Double.valueOf(br.readLine()).doubleValue();
153
 pyme.adicionarEmpleadoXHora(cedula, apellido, nombre, horas,
 sueldo);
 }
 System.out.print(“Digite numero de empleados por comision: “); 
 int numeroEmpleados2 = Integer.valueOf(br.readLine()).intValue();
 for(int i=0;i<numeroEmpleados2;i++){
 double sueldo, porcentaje, ventas;
 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 sueldo basico empleado: “);
 sueldo = Double.valueOf(br.readLine()).doubleValue(); 
 System.out.print(“Digite porcentaje del empleado: “ );
 porcentaje = Double.valueOf(br.readLine()).doubleValue();
 System.out.print(“Digite ventas totales del empleado: “ );
 ventas = Double.valueOf(br.readLine()).doubleValue();
 pyme.adicionarEmpleadoXComision(cedula, apellido, nombre,sueldo,
 porcentaje, ventas);
 }
 System.out.print(“Digite numero de empleados sueldo fijo: “); 
 int numeroEmpleados3 = Integer.valueOf(br.readLine()).intValue();
 
 for(int i=0;i<numeroEmpleados3;i++){
 double sueldo;
 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 sueldo basico empleado: “);
 sueldo = Double.valueOf(br.readLine()).doubleValue(); 
 pyme.adicionarEmpleadoSueldoFijo(cedula, apellido, nombre, 
 sueldo);
 }
 total = pyme.calcularNominaTotal();
 
 System.out.println(“\nLa nómina total es: “+ total);
 
 }
}
2. TÓPICOS AVANZADOS
154
Todo lo básico que debería saber sobre Programación orientada a objetos en Java
2.1.2. Conjuntos
Definición de conjunto
Esta colección modela el concepto matemático de conjunto, su principal característica 
es que dentro de ella no puede haber elementos repetidos.
Java define la interface Set para la implementación de los conjuntos, sin embargo, como 
Set extiende Collection y no adiciona ningún método, los métodos que se exhiben en 
ambas interfaces son los mismos. Adicionalmente, Java define una interface para trabajar 
implementaciones de conjuntos donde se guarde algún tipo de ordenamiento, para ello 
ha sido declarada la interface SortedSet; esta interface extiende a Set. A continuación se 
presentan algunos de los métodos más relevantes de esta interface.
•	 Comparator comparator(). Retorna el objeto Comparator que utiliza el conjunto 
para establecer el criterio de ordenamiento. Retorna null si se está trabajando el 
ordenamiento natural de los elementos (para trabajar con este tipo de ordenamiento 
es necesario que las clases de los objetos que se almacenan en la estructura de datos 
implementen la interface Comparable). 
•	 Object first(). Retorna el primer (menor) elemento de la colección. 
•	 SortedSet headSet(Object o). Retorna la porción del conjunto cuyos elementos sean 
estrictamente menores al objeto pasado por parámetro. Los objetos que se devuelven 
se encapsulan dentro de un objeto que debe cumplir también la interface SortedSet.
•	 Object last(). Retorna el último (mayor) elemento de la colección.
•	 SortedSet subset(Object inicial, Object final). Retorna una porción del conjunto 
comprendida entre el objeto inicial y el final. Los objetos que se devuelven se 
encapsulan dentro de un objeto que debe cumplir también la interface SortedSet.
•	 SortedSet tailSet(Object o). Retorna la porción del conjunto cuyos elementos sean 
mayores o iguales al objeto pasado por parámetro. Los objetos que se devuelven se 
encapsulan dentro de un objeto que debe cumplir también la interface SortedSet.
Java brinda tres implementaciones para conjuntos (HashSet, LinkedHashSet y TreeSet), pero 
solo una de ellas es también implementación de la interface SortedSet (TreeSet). 
•	 HashSet. Esta implementación basa su estructura en una tabla hash. Es una de 
las implementaciones más empleadas gracias a su desempeño y eficiencia, pero 
al no implementar la interface SortedSet no hay garantía de que los elementos se 
almacenen bajo algún criterio de ordenamiento.
155
•	 TreeSet. Esta implementación trabaja internamente con una estructura de datos de 
tipo árbol para mantener los elementos ordenados; por esa razón, su desempeño no 
es tan elevado como la implementación HashSet.
•	 LinkedHashSet. Corresponde a una implementación intermedia a las anteriormente 
presentadas. Su estructura interna trabaja con una lista doblemente enlazada. 
eJemplo de utilizaCión de una implementaCión de la interfaCe set
El ejemplo que se presenta a continuación trabaja con la clase EmpleadoSueldoFijo definida 
en la decimoprimera aproximación de la aplicación del cálculo de la nómina. Anteriormente 
se mencionó que las implementaciones de Set requieren redefinir los métodos equals y 
hashCode de las clases de los objetos que se almacenarán en ellas.
public interface PersonaAsalariada{
 public double calcularSalario();
}
public abstract class Empleado implements PersonaAsalariada{
 protected final String cedula;
 private String apellido;
 private String nombre;
 public Empleado(String cedula, String apellido, String nombre){
 if (cedula!=null) this.cedula = cedula;
 else this.cedula = “”;
 setApellido(apellido);
 setNombre(nombre);
 }
 public final String getCedula(){
 return cedula;
 }
 public final String getApellido(){
 return apellido;
 }
 public final void setApellido(String apellido){
 if (apellido!=null) this.apellido = apellido;
 else this.apellido=””;
 }
 public final String getNombre(){
2. TÓPICOS AVANZADOS
156
Todo lo básico que debería saber sobre Programación orientada a objetos en Java
 return nombre;
 }
 public final void setNombre(String nombre){
 if (nombre!=null) this.nombre = nombre;
 else this.nombre=””;
 }
}
public class EmpleadoSueldoFijo extends Empleado{
 private double sueldoFijo;
 public EmpleadoSueldoFijo(String cedula, String apellido, 
 String nombre, double sueldoFijo){
 super(cedula, apellido, nombre);
 setSueldoFijo(sueldoFijo);
 }
 public double calcularSalario(){
 return sueldoFijo;
 }
 public double getSueldoFijo(){
 return sueldoFijo;
 }
 public void setSueldoFijo(double sueldo){
 if (sueldo>=0) this.sueldoFijo = sueldo;
 else this.sueldoFijo = 0;
 }
 public boolean equals(Object o){
 if (!(o instanceof EmpleadoSueldoFijo)) return false;
 
 EmpleadoSueldoFijo temp = (EmpleadoSueldoFijo)o;
 
 if ( !this.getCedula().equals(temp.getCedula()) ) return false;
 if ( !this.getApellido().equals(temp.getApellido()) ) return false;
 if ( !this.getNombre().equals(temp.getNombre()) ) return false;
 if ( this.getSueldoFijo()!=temp.getSueldoFijo() ) return false;
 return true;
 }
 public int hashCode(){
 return cedula.hashCode();
 }
}
157
Sobre la clase anterior es conveniente resaltar la implementación del método hashCode, que 
simplemente pide retornar el valor del hashCode de la cedula del empleado.
Finalmente considere el siguiente código:
 
Set conjunto = new HashSet();
conjunto.add(new EmpleadoSueldoFijo(“12345”,”Pérez”,”Pedro”,1000));
conjunto.add(new EmpleadoSueldoFijo(“12345”,”Pérez”,”Pedro”,1000));
conjunto.add(new EmpleadoSueldoFijo(“2345”,”Sanchez”,”Maria”,1000));
System.out.println(conjunto.size());
Al ejecutar el anterior código la respuesta es 2. Esto indica que a pesar de haberse definido 3 
instrucciones de inserción dentro del conjunto sólo dos fueron efectivas. La segunda no lo fue 
porque ya existía dentro de la estructura de datos un objeto con la misma información. Note 
además que no se incluyo código particular para evitar ingresar dos veces el mismo objeto, ya 
que dicha implementación se gana por la definición de conjunto (Set).
2.1.3. Mapas
Definición de mapa
Un mapa es una estructura de datos que mapea llaves en valores. Al registrar un 
objeto dentro de esta estructura de datos, además del objeto o valor a almacenar, se 
debe suministrar un objeto que haga las veces de llave, y al extraer objetos de esta 
se suministra el objeto llave para que se retorne el objeto almacenado. Un mapa no 
puede poseer llaves repetidas (se logra a través de la utilización de un conjunto) y solo 
se almacena un valor para cada llave.
Java define la interface map donde se especifican los métodos que debe proveer una 
implementación de este tipo de estructura de datos. Esta interface no extiende Collection. 
A continuación se presentan algunos de los métodos más relevantes de esta interface.
•	 Object put (Object llave, Object valor). Inserta el objeto que se recibe en el 
parámetro valor bajo el valor de la llave especificada. Retornael antiguo valor 
asociado a la llave, o null, si no había mapeo para dicha llave. 
•	 Object get(Object llave). Retorna el valor que se encuentra asociado a la llave que 
se pasa por parámetro. 
2. TÓPICOS AVANZADOS
158
Todo lo básico que debería saber sobre Programación orientada a objetos en Java
•	 Object remove(Object llave). Remueve el mapeo para la llave que se pasa por 
parámetro. Retorna el antiguo valor asociado a la llave, o null, si no había mapeo 
para dicha llave.
•	 boolean containsKey(Object llave). Devuelve verdadero (true), si el mapa contiene 
como llave al objeto especificado como parámetro. 
•	 boolean containsValue(Object valor). Retorna verdadero (true), si el mapa 
contiene como uno de sus valores al objeto especificado como parámetro.
•	 int size(). Retorna el número de mapeos llave-valor almacenados en la estructura 
de datos.
•	 boolean isEmpty(). Retorna verdadero (true), si el mapa no tiene registrados mapeos 
llave-valor. 
•	 void putAll(Map m). Copia todos los mapeos del objeto pasado como parámetro.
•	 void clear(). Remueve todos los mapeos del mapa.
•	 Set keySet(). Retorna un Set con todas las llaves registradas en el mapa.
•	 Collection values(). Retorna una Collection con todos los valores almacenados en 
el mapa.
Java brinda varias implementaciones a la interface map. De todas ellas la más eficiente en 
término de tiempo corresponde a HashMap. Esta implementación basa su estructura en 
una tabla hash, y provee un desempeño constante en el tiempo para las operaciones básicas 
de inserción y extracción. 
eJemplo de utilizaCión de una implementaCión de la interfaCe map
Se presenta a continuación el ejemplo de implementación de una agenda telefónica simple. 
En aras de hacer más sencillo el ejemplo y su entendimiento se definen las siguientes 
restricciones:
•	 De un contacto de la agenda telefónica solo se registrará un único número telefónico, 
y, adicionalmente, se guardará información de una única dirección.
•	 No existen dentro del directorio dos contactos que tengan el mismo nombre.
La siguiente clase representa los contactos de la agenda.
public class Contacto{
 private final String nombre;
 private String telefono;
 private String direccion;
159
 public Contacto(String nombre, String telefono, String direccion){
 this.nombre = nombre;
 this.telefono = telefono;
 this.direccion = direccion;
 }
 public String getNombre(){
 return nombre;
 }
 public String getTelefono(){
 return telefono;
 }
 public String getDireccion(){
 return direccion;
 }
}
Considere el siguiente código: 
HashMap directorio = new HashMap();
directorio.put( “Homero Simpson”, new Contacto(“Homero Simpson”,
 “000-1245”,”Avenida Siempre Viva 742”) );
directorio.put( “Montgomery Burns”, new Contacto(“Montgomery Burns”, 
 “000-2467”,”Mansion Burns”) );
System.out.println(
 ((Contacto)directorio.get(“Homero Simpson”)).getTelefono() );
 
Sobre el anterior código es conveniente resaltar:
•	 Note como para ingresar valores dentro del mapa directorio se emplea el método put. 
Este método recibe la llave y el valor. Para el ejemplo, la llave corresponde al nombre 
del contacto, mientras que el valor es el propio contacto.
•	 Para extraer elementos del mapa directorio se emplea el método get. Este método 
recibe únicamente la llave y retorna el valor almacenado en el mapa. Para el ejemplo, 
la llave que se suministra corresponde al nombre del contacto. 
•	 Como el tipo de retorno del método es Object, se hace necesaria la realización de 
un casting para convertir la referencia al tipo Contacto con el fin de poder invocar el 
método getTelefono().
2.1.4. Genéricos
Un lector atento habrá podido notar que hasta el momento cada vez que se pide obtener una 
referencia almacenada en cualquier estructura de datos ya presentada se hace necesaria la 
utilización de la operación de casting. Esto se debe a que seguramente la estructura interna 
de la estructura de datos trabaja con referencias de tipo Object para permitir almacenar 
cualquier tipo de objeto en ella. 
2. TÓPICOS AVANZADOS
160
Todo lo básico que debería saber sobre Programación orientada a objetos en Java
Existen ocasiones en que el programador conoce exactamente el tipo de referencias que se 
estarán almacenando dentro de la estructura de datos. Desde la versión 5 (JDK5.0) de Java, 
este lenguaje de programación permite al programador que cuando declare una estructura 
de datos del framework de colecciones, indique el tipo de dato a registrar en ella. Para 
hacerlo simplemente se requiere colocar entre los símbolos de menor y mayor que (‘<>’) el 
tipo de referencia de los objetos (bien puede ser una clase o una interface). Por ejemplo, para 
declarar un ArrayList donde solo se almacenarán referencias de tipo Empleado, se requiere 
la siguiente instrucción:
ArrayList <Empleado> losEmpleados = new ArrayList<Empleado>();
Permitir este tipo de definiciones le trae muchos beneficios al programador.
•	 En primer lugar, ya no se requiere código de validación para garantizar que dentro 
de la estructura de datos solo se registren objetos de un determinado tipo de dato. 
Un intento de almacenar objetos dentro de una estructura de datos de un tipo de 
dato distinto al definido causará un error. 
•	 En segundo lugar, como la estructura de datos ya conoce el tipo de referencia que 
está almacenando, ya no se hace necesaria la operación de casting.
deCimosegunda aproximaCión a la apliCaCión del CálCulo 
de la nómina mediante programaCión orientada a obJetos
En esta implementación de la aplicación se modifica la clase Empresa para que trabaje con 
ArrayList genéricos. Las restantes clases permanecen invariantes. El código que aparece en 
negrilla resalta las modificaciones efectuadas con respecto a la anterior aproximación.
public interface PersonaAsalariada{
 public double calcularSalario();
}
public abstract class Empleado implements PersonaAsalariada{
 protected final String cedula;
 private String apellido;
 private String nombre;
 public Empleado(String cedula, String apellido, String nombre){
 if (cedula!=null) this.cedula = cedula;
 else this.cedula = “”;
161
 setApellido(apellido);
 setNombre(nombre);
 }
 public final String getCedula(){
 return cedula;
 }
 public final String getApellido(){
 return apellido;
 }
 public final void setApellido(String apellido){
 if (apellido!=null) this.apellido = apellido;
 else this.apellido=””;
 }
 public final String getNombre(){
 return nombre;
 }
 public final void setNombre(String nombre){
 if (nombre!=null) this.nombre = nombre;
 else this.nombre=””;
 }
}
public class EmpleadoXHora extends Empleado{
 private double horasTrabajadas;
 private double sueldoXHora;
 public EmpleadoXHora(String cedula, String apellido, String nombre, 
 double horasTrabajadas, double sueldoXHora){
 super(cedula, apellido, nombre);
 setSueldoXHora(sueldoXHora);
 setHorasTrabajadas(horasTrabajadas);
 }
 public double calcularSalario(){
 return horasTrabajadas * sueldoXHora;
 }
 public double getHorasTrabajadas(){
 return horasTrabajadas;
 }
 public void setHorasTrabajadas(double horas ){
 if (horas>=0) this.horasTrabajadas = horas;
 else this.horasTrabajadas = 0;
 }
 public double getSueldoXHora(){
 return sueldoXHora;
 }
 public void setSueldoXHora(double sueldo ){
2. TÓPICOS AVANZADOS
162
Todo lo básico que debería saber sobre Programación orientada a objetos en Java
 if (sueldo>=0) this.sueldoXHora = sueldo;
 else this.sueldoXHora = 0;
 }
 public boolean equals(Object o){
 if (!(o instanceof EmpleadoXHora)) return false;
 
 EmpleadoXHora temp = (EmpleadoXHora)o;
 
 if ( !this.getCedula().equals(temp.getCedula()) ) return false;
 if ( !this.getApellido().equals(temp.getApellido()) ) return false;
 if ( !this.getNombre().equals(temp.getNombre()) ) return false;
 if ( this.getSueldoXHora()!=temp.getSueldoXHora()) return false;
 if ( this.getHorasTrabajadas()!=temp.getHorasTrabajadas() ) 
 return false;
 return true;
 }
}
public class EmpleadoXComision extends Empleado{
 private double sueldoBasico;
 private double porcentaje;
 private double ventasTotales;
 public EmpleadoXComision(String cedula, String apellido, 
 String nombre, double sueldoBasico, double porcentaje, 
 double ventasTotales){
 
 super(cedula, apellido, nombre);
 setSueldoBasico(sueldoBasico);
 setPorcentaje(porcentaje);
 setVentasTotales(ventasTotales);
 }
 public double calcularSalario(){
 return sueldoBasico + porcentaje * ventasTotales;
 }
 public double getSueldoBasico(){
 return sueldoBasico;
 }
 public void setSueldoBasico(double sueldo){
 if (sueldo>=0) this.sueldoBasico = sueldo;
 else this.sueldoBasico = 0;
 }
 public double getPorcentaje(){
 return porcentaje;
 }
163
 public void setPorcentaje(double porcentaje ){
 if (porcentaje>=0 && porcentaje<=1) this.porcentaje = porcentaje;
 else this.porcentaje = 0;
 }
 public double getVentasTotales(){
 return ventasTotales;
 }
 public void setVentasTotales(double ventas){
 if (ventas>=0) this.ventasTotales = ventas;
 else this.ventasTotales = 0;
 }
 public boolean equals(Object o){
 if (!(o instanceof EmpleadoXComision)) return false;
 
 EmpleadoXComision temp = (EmpleadoXComision)o;
 
 if ( !this.getCedula().equals(temp.getCedula()) ) return false;
 if ( !this.getApellido().equals(temp.getApellido()) ) return false;
 if ( !this.getNombre().equals(temp.getNombre()) ) return false;
 if ( this.getSueldoBasico()!=temp.getSueldoBasico() ) return false;
 if ( this.getPorcentaje()!=temp.getPorcentaje() ) return false;
 if ( this.getVentasTotales()!=temp.getVentasTotales() ) 
 return false;
 return true;
 }
}
public class EmpleadoSueldoFijo extends Empleado{
 private double sueldoFijo;
 public EmpleadoSueldoFijo(String cedula, String apellido, 
 String nombre, double sueldoFijo){
 super(cedula, apellido, nombre);
 setSueldoFijo(sueldoFijo);
 }
 public double calcularSalario(){
 return sueldoFijo;
 }
2. TÓPICOS AVANZADOS
164
Todo lo básico que debería saber sobre Programación orientada a objetos en Java
 public double getSueldoFijo(){
 return sueldoFijo;
 }
 public void setSueldoFijo(double sueldo){
 if (sueldo>=0) this.sueldoFijo = sueldo;
 else this.sueldoFijo = 0;
 }
 public boolean equals(Object o){
 if (!(o instanceof EmpleadoSueldoFijo)) return false;
 
 EmpleadoSueldoFijo temp = (EmpleadoSueldoFijo)o;
 
 if ( !this.getCedula().equals(temp.getCedula()) ) return false;
 if ( !this.getApellido().equals(temp.getApellido()) ) return false;
 if ( !this.getNombre().equals(temp.getNombre()) ) return false;
 if ( this.getSueldoFijo()!=temp.getSueldoFijo() ) return false;
 return true;
 }
}
import java.util.ArrayList; 
public class Empresa{
 final private String nit;
 private String nombre;
 private String direccion;
 private ArrayList<Empleado> empleados;
 public Empresa(String nit, String nombre, String direccion){
 if (nit!=null) this.nit = nit;
 else this.nit=””;
 setNombre(nombre);
 setDireccion(direccion);
 empleados = new ArrayList<Empleado>();
 }
 public Empresa(String nit){
 if (nit!=null) this.nit = nit;
 else this.nit=””;
 setNombre(“”);
 setDireccion(“”);
 empleados = new ArrayList<Empleado> ();
 }
 public Empresa(String nit, int numero){
 if (nit!=null) this.nit = nit;
 else this.nit=””;
165
 setNombre(“”);
 setDireccion(“”);
 empleados = new ArrayList<Empleado>(numero);
 }
 public String getNit(){
 return nit;
 }
 public String getNombre(){
 return nombre;
 }
 public void setNombre(String nombre){
 if (nombre!=null) this.nombre = nombre;
 else this.nombre=””;
 }
 public String getDireccion(){
 return direccion;
 }
 public void setDireccion(String direccion){
 if (direccion!=null) this.direccion = direccion;
 else this.direccion=””;
 }
 public int getNumeroEmpleados(){
 return empleados.size();
 }
 public void adicionarEmpleadoXHora(String cedula, String apellido, 
 String nombre, double horasTrabajadas, double sueldoXHora){
 empleados.add (new EmpleadoXHora(cedula, apellido, nombre, 
 horasTrabajadas, sueldoXHora) );
 }
 public void adicionarEmpleadoXComision(String cedula, String apellido,
 String nombre, double sueldoBasico, double porcentaje, 
 double ventasTotales){
 empleados.add( new EmpleadoXComision(cedula, apellido, nombre,
 sueldoBasico, porcentaje, ventasTotales) );
 }
 public void adicionarEmpleadoSueldoFijo(String cedula, 
 String apellido, String nombre, double sueldoFijo){
 empleados.add( new EmpleadoSueldoFijo(cedula, apellido, nombre, 
 sueldoFijo) );
 }
2. TÓPICOS AVANZADOS
166
Todo lo básico que debería saber sobre Programación orientada a objetos en Java
 public double calcularNominaTotal(){
 double total = 0;
 for (int i=0;i<empleados.size();i++)
 total = total + empleados.get(i).calcularSalario();
 return total;
 }
}
Sobre la implementación de la clase Empresa es conveniente resaltar los siguientes puntos:
•	 Note cómo se realiza la definición e instanciación del array de empleados.
•	 En el método calcularNominaTotal ya no es necesario el casting.
import java.io.BufferedReader;
import java.io.InputStreamReader;
public class Nomina1{
 public static void main(String[] args) throws Exception{
 Empresa pyme = new Empresa(“123”,”Acme inc.”,”805 Silicon Valley”);
 String cedula, apellido, nombre; 
 double total = 0;
 
 BufferedReader br = new BufferedReader( 
 new InputStreamReader( System.in ) );
 System.out.print(“Digite numero de empleados sueldo por hora: “); 
 int numeroEmpleados1 = Integer.valueOf(br.readLine()).intValue();
 
 for(int i=0;i<numeroEmpleados1;i++){
 double horas, sueldo;
 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 del empleado: “);
 horas = Double.valueOf(br.readLine()).doubleValue(); 
 System.out.print(“Digite sueldo por hora del empleado: “ );
 sueldo = Double.valueOf(br.readLine()).doubleValue();
 pyme.adicionarEmpleadoXHora(cedula, apellido, nombre, horas,
 sueldo);
 }
 System.out.print(“Digite numero de empleados por comision: “); 
 int numeroEmpleados2 = Integer.valueOf(br.readLine()).intValue();
 
 for(int i=0;i<numeroEmpleados2;i++){
 double sueldo, porcentaje, ventas;
167
 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 sueldo basico empleado: “);
 sueldo = Double.valueOf(br.readLine()).doubleValue(); 
 System.out.print(“Digite porcentaje del empleado: “ );
 porcentaje = Double.valueOf(br.readLine()).doubleValue();
 System.out.print(“Digite ventas totales del empleado: “ );
 ventas = Double.valueOf(br.readLine()).doubleValue();
 pyme.adicionarEmpleadoXComision(cedula, apellido, nombre, sueldo,
 porcentaje, ventas);
 }
 System.out.print(“Digite numero de empleados sueldo fijo: “); 
 int numeroEmpleados3 = Integer.valueOf(br.readLine()).intValue();
 
 for(int i=0;i<numeroEmpleados3;i++){
 double sueldo;
 System.out.print(“\nDigite la cedula del empleado: “ );
 cedula = br.readLine();
 System.out.print(“Digiteel apellido del empleado: “ );
 apellido = br.readLine();
 System.out.print(“Digite el nombre del empleado: “ );
 nombre = br.readLine();
 System.out.print(“Digite sueldo basico empleado: “);
 sueldo = Double.valueOf(br.readLine()).doubleValue(); 
 pyme.adicionarEmpleadoSueldoFijo(cedula, apellido, nombre, 
 sueldo);
 }
 total = pyme.calcularNominaTotal();
 
 System.out.println(“\nLa nómina total es: “+ total);
 
 }
}
2. TÓPICOS AVANZADOS
168
Todo lo básico que debería saber sobre Programación orientada a objetos en Java
eJemplo de uso para una implementaCión de la interfaCe set
 que emplea estruCturas genériCas
El ejemplo que se presenta a continuación trabaja con la clase EmpleadoSueldoFijo definida 
en la décimo segunda aproximación de la aplicación del cálculo de la nómina. 
public interface PersonaAsalariada{
 public double calcularSalario();
}
public abstract class Empleado implements PersonaAsalariada{
 protected final String cedula;
 private String apellido;
 private String nombre;
 public Empleado(String cedula, String apellido, String nombre){
 if (cedula!=null) this.cedula = cedula;
 else this.cedula = “”;
 setApellido(apellido);
 setNombre(nombre);
 }
 public final String getCedula(){
 return cedula;
 }
 public final String getApellido(){
 return apellido;
 }
 public final void setApellido(String apellido){
 if (apellido!=null) this.apellido = apellido;
 else this.apellido=””;
 }
 public final String getNombre(){
 return nombre;
 }
 public final void setNombre(String nombre){
 if (nombre!=null) this.nombre = nombre;
 else this.nombre=””;
 }
}
169
public class EmpleadoSueldoFijo extends Empleado{
 private double sueldoFijo;
 public EmpleadoSueldoFijo(String cedula, String apellido, 
 String nombre, double sueldoFijo){
 super(cedula, apellido, nombre);
 setSueldoFijo(sueldoFijo);
 }
 public double calcularSalario(){
 return sueldoFijo;
 }
 public double getSueldoFijo(){
 return sueldoFijo;
 }
 public void setSueldoFijo(double sueldo){
 if (sueldo>=0) this.sueldoFijo = sueldo;
 else this.sueldoFijo = 0;
 }
 public boolean equals(Object o){
 if (!(o instanceof EmpleadoSueldoFijo)) return false;
 
 EmpleadoSueldoFijo temp = (EmpleadoSueldoFijo)o;
 
 if ( !this.getCedula().equals(temp.getCedula()) ) return false;
 if ( !this.getApellido().equals(temp.getApellido()) ) return false;
 if ( !this.getNombre().equals(temp.getNombre()) ) return false;
 if ( this.getSueldoFijo()!=temp.getSueldoFijo() ) return false;
 return true;
 }
 public int hashCode(){
 return cedula.hashCode();
 }
}
Sobre la clase anterior es conveniente resaltar la implementación del método hashCode, sim-
plemente se pide retornar el valor del hashCode de la cedula del empleado.
Finalmente, considere el siguiente código:
 
Set<EmpleadoSueldoFijo> conjunto = new HashSet<EmpleadoSueldoFijo>();
conjunto.add(new EmpleadoSueldoFijo(“12345”,”Pérez”,”Pedro”,1000));
conjunto.add(new EmpleadoSueldoFijo(“12345”,”Pérez”,”Pedro”,1000));
conjunto.add(new EmpleadoSueldoFijo(“2345”,”Sanchez”,”Maria”,1000));
System.out.println(conjunto.size());
2. TÓPICOS AVANZADOS
170
Todo lo básico que debería saber sobre Programación orientada a objetos en Java
eJemplo de uso para una implementaCión de la interfaCe map
 que emplea estruCturas genériCas
Aqui se trabajará con el ejemplo que se utilizó en el apartado; ejemplo de uso para una 
implementación de la interface map. La diferencia radica en que para este ejemplo se estará 
utilizando una estructura de datos mapa genérico.
public class Contacto{
 private final String nombre;
 private String telefono;
 private String direccion;
 public Contacto(String nombre, String telefono, String direccion){
 this.nombre = nombre;
 this.telefono = telefono;
 this.direccion = direccion;
 }
 public String getNombre(){
 return nombre;
 }
 public String getTelefono(){
 return telefono;
 }
 public String getDireccion(){
 return direccion;
 }
}
Considere el siguiente código:
HashMap<String,Contacto> directorio = new HashMap<String,Contacto>();
directorio.put( “Homero Simpson”, new Contacto(“Homero Simpson”, 
 “000-1245”,”Avenida Siempre Viva 742”) );
directorio.put( “Montgomery Burns”, new Contacto(“Montgomery Burns”, 
 “000-2467”,”Mansion Burns”) );
System.out.println( directorio.get(“Homero Simpson”).getTelefono() );
Sobre el anterior código es conveniente resaltar:
•	 Note que para definir el HashMap fue necesario establecer cuál es el tipo de dato de 
las llaves y cuál es el tipo de dato de los valores que se estarán almacenando. Dicha 
definición se enmarca dentro de los símbolos de mayor y menor que y se separan con 
coma. 
•	 La última línea de código no requiere el casting del objeto que se extrae de la estructura 
de datos.
171
2.2. mAnejO de excepciOnes
Una aplicación de software no se encuentra exenta a errores, por ello, y sin lugar a dudas, 
una buena aplicación debe advertir la aparición y sobre todo el manejo de este tipo de 
situaciones excepcionales. Por ejemplo, si una persona debe tener una cédula se debe prever 
qué debe suceder en caso de que se suministre para dicho atributo un valor inválido, como 
que la cadena esté vacía o que esta contenga un carácter no numérico. Para estos casos, el 
manejo del error podría consistir simplemente en informarle al usuario dicha situación para 
que este se encargue de corregirlo. 
Analice cómo podría incluirse dentro de la aplicación del cálculo de la nómina la validación 
para no permitir que el atributo cedula de un Empleado tome como valor la cadena vacía. 
Aunque todo el análisis que se efectúe va dirigido a validar este punto específico, este 
análisis puede ser extendido fácilmente para la validación de cualquier situación. 
Considérense las siguientes alternativas de solución. 
•	 La primera alternativa consistiría en redefinir el código de la clase Nomina1 (definición 
presentada en la décimo segunda aproximación de la aplicación del cálculo de la 
nómina) La idea general es la de colocar código de validación cuando se recibe el 
valor de la cédula, si el valor no es válido, simplemente no se ejecuta la instrucción 
de adicionar un tipo de empleado específico a la empresa. El código quedaría de la 
siguiente manera.
import java.io.BufferedReader;
import java.io.InputStreamReader;
public class Nomina1{
 public static void main(String[] args) throws Exception{
 Empresa pyme = new Empresa(“123”,”Acme inc.”,”805 Silicon Valley”);
 String cedula, apellido, nombre; 
 double total = 0;
 
 BufferedReader br = new BufferedReader( 
 new InputStreamReader( System.in ) );
 
 System.out.print(“Digite numero de empleados sueldo por hora: “); 
 int numeroEmpleados1 = Integer.valueOf(br.readLine()).intValue();
 
 for(int i=0;i<numeroEmpleados1;i++){
 double horas, sueldo;
 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 del empleado: “);
 horas = Double.valueOf(br.readLine()).doubleValue(); 
 System.out.print(“Digite sueldo por hora del empleado: “ );
 sueldo = Double.valueOf(br.readLine()).doubleValue();
2. TÓPICOS AVANZADOS
172
Todo lo básico que debería saber sobre Programación orientada a objetos en Java
 if (cedula.trim().equals(“”)){
 System.out.println(“Valor de cédula no válida.”);
 i--;
 } else {
 pyme.adicionarEmpleadoXHora(cedula, apellido, nombre, horas,
 sueldo);
 }
 }
 System.out.print(“Digite numero de empleados por comision: “); 
 int numeroEmpleados2 = Integer.valueOf(br.readLine()).intValue();for(int i=0;i<numeroEmpleados2;i++){
 double sueldo, porcentaje, ventas;
 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 sueldo basico empleado: “);
 sueldo = Double.valueOf(br.readLine()).doubleValue(); 
 System.out.print(“Digite porcentaje del empleado: “ );
 porcentaje = Double.valueOf(br.readLine()).doubleValue();
 System.out.print(“Digite ventas totales del empleado: “ );
 ventas = Double.valueOf(br.readLine()).doubleValue();
 if (cedula.trim().equals(“”)){
 System.out.println(“Valor de cédula no válida.”);
 i--;
 } else {
 pyme.adicionarEmpleadoXComision(cedula, apellido, nombre,
 sueldo, porcentaje, ventas);
 }
 }
 System.out.print(“Digite numero de empleados sueldo fijo: “); 
 int numeroEmpleados3 = Integer.valueOf(br.readLine()).intValue();
 
 for(int i=0;i<numeroEmpleados3;i++){
 double sueldo;
 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 sueldo basico empleado: “);
 sueldo = Double.valueOf(br.readLine()).doubleValue(); 
 if (cedula.trim().equals(“”)){
 System.out.println(“Valor de cédula no válida.”);
 i--;
 } else {
 pyme.adicionarEmpleadoSueldoFijo(cedula, apellido, nombre, 
 sueldo);
 }
 }
173
 total = pyme.calcularNominaTotal();
 
 System.out.println(“\nLa nómina total es: “+ total);
 
 }
}
El método trim() de la clase String devuelve una copia de la cadena de caracteres 
original de la que elimina los espacios en blanco que se encuentren al principio y al 
final de la misma. Está claro que la ubicación de este código en esta clase prevé la 
aparición de este error, y, además, lo maneja. Sin embargo, se hace necesario duplicar 
este código a lo largo de esta clase (al validar el registro de un nuevo EmpleadoXHora, o 
un EmpleadoXComision o un EmpleadoSueldoFijo) y en cualquier otra clase que funcione 
como interfaz de usuario (por ejemplo, Nomina2). Esto sugiere que debe encontrarse otro 
lugar para ubicar este código de validación.
•	 La segunda alternativa consistiría en ubicar el código de validación no en la clase 
Nomina01 (con el fin de evitar la redundancia de código), sino en la clase Empresa 
(ya que la interfaz de usuario utiliza dicha clase). El código en dicha clase quedaría 
de la siguiente manera (para evitar la inclusión de demasiadas líneas de código que 
puedan despistar al lector y alejarlo del punto de la explicación solo se mostrará 
el código del método adicionarEmpleadoXHora de la clase Empresa, pero dichas 
instrucciones deberían extenderse también a los métodos adicionarEmpleadoXComision 
y adicionarEmpleadoSueldoFijo):
public void adicionarEmpleadoXHora(String cedula, String apellido, 
 String nombre, double horasTrabajadas, double sueldoXHora){
 if (cedula.trim().equals(“”)){
 System.out.println(“Valor de cédula no valida”);
 }else{
 empleados.add (new EmpleadoXHora(cedula, apellido, nombre, 
 horasTrabajadas, sueldoXHora) );
 }
}
• La ubicación del código de validación en este método presenta varios inconvenientes. 
Uno de ellos sería que en caso de reutilizar la clase Empleado (o alguna de sus subclases) 
en otra aplicación se requiere duplicar el código de validación de este método. 
Otro inconveniente que se presentaría corresponde a la duplicación del código al 
interior de la propia clase, ya que debe ubicarse también en los restantes métodos 
adicionarEmpleadoXComision y adicionarEmpleadoSueldoFijo. Finalmente, el último 
inconveniente que se podría presentar, pero no por ello el menos importante, tiene que 
ver con el hecho de que la clase Empresa no tiene por qué conocer qué tipo de interfaz 
de usuario está utilizando sus servicios; es decir, puede que la interfaz sea de tipo 
consola (como la que presenta la clase Nomina01) o puede que sea de tipo gráfica (con 
ventanas, botones y demás), luego allí estaría mal desplegar el mensaje que le informa 
al usuario dicha situación empleando un mensaje en consola. En realidad el problema 
que presenta este último inconveniente tiene que ver con que no existe una forma clara 
2. TÓPICOS AVANZADOS
174
Todo lo básico que debería saber sobre Programación orientada a objetos en Java
de informarle a la clase de la interfaz de usuario que hubo un error para que tome la 
acción correspondiente.
•	 La tercera alternativa permitiría corregir uno de los inconvenientes que se presentan en 
la alternativa anterior. Esta alternativa consistiría en ubicar el código de validación en 
la propia clase EmpleadoXHora (o incluso sería mejor hacerlo en el método constructor 
de la clase Empleado, ya que por herencia lo ganarían todas las subclases de ella). Al 
ubicarse allí el código de validación no importaría si la clase es reutilizada por alguna 
otra aplicación. El interrogante que surge es qué hacer en caso de que el valor de la 
cédula no sea válido. Si el problema permite crear un objeto EmpleadoXHora con 
un valor de cédula desconocido, no hay mayor inconveniente; pero sí existiría un 
gran problema en caso de que esta última situación no sea una alternativa válida. 
 
A manera de resumen con la tercera alternativa, que sería la más apropiada desde el 
punto de vista de reutilización de código, se presentarían los siguientes problemas:
o Cuando no se permita la creación de un objeto con valores inválidos, cómo hacer 
para detener la ejecución de un método como el método constructor.
o No hay una forma clara de informarle a los objetos de la clase de la interfaz de 
usuario que se ha presentado un error de tal manera que estos tomen las medidas 
necesarias.
El marco de trabajo para el manejo de excepciones que brinda Java contempla las 
soluciones para ambos problemas presentados anteriormente.
Definición de excepción
Una excepción es un evento que ocurre durante la ejecución de un programa con el 
cual se interrumpe el flujo de ejecución normal de las instrucciones del mismo. 
Este evento será representado dentro de la aplicación a través de un objeto; en él, además, 
se podrá almacenar información explicatoria adicional del evento. En Java existe una 
clase predefinida para la representación de una Excepción, que es la clase Exception, y se 
encuentra dentro del paquete java.lang. Esta clase posee un atributo llamado message cuya 
finalidad es la de registrar un mensaje relativo al evento; adicionalmente, esta clase posee el 
método accesor getMessage() y dos métodos constructores: uno que no recibe parámetros y 
otro que recibe un String con el mensaje relativo a la Excepción.
2.2.1. Generación y lanzamiento de excepciones
La idea detrás del manejo de excepciones es la de crear un objeto Exception por cada posible 
evento excepcional que se pueda presentar durante la ejecución de un programa. El código 
175
para realizar la validación del atributo cedula en el constructor de la clase Empleado quedaría 
de la siguiente manera:
public Empleado(String cedula, String apellido, String nombre){
 if (cedula.trim().equals(“”))
 new Exception(“Valor de la cédula no válido”);
 this.cedula = cedula;
 setApellido(apellido);
 setNombre(nombre);
}
El código anterior prevé la posible condición que generaría una excepción, pero aún no se ha 
encontrado respuesta a los dos problemas que se mencionaron en la tercera alternativa de 
solución. Para detener la ejecución de un método donde se ha presentado alguna excepción 
y dar a conocer esta situación (la que se denominalanzar una Excepción) se debe emplear 
la instrucción throw. Esta instrucción requiere, además, del objeto Exception a lanzar. El 
código del método constructor de la clase Empleado quedaría de la siguiente manera:
public Empleado(String cedula, String apellido, String nombre){
 if (cedula.trim().equals(“”))
 throw new Exception(“Valor de la cédula no válido”);
 this.cedula = cedula;
 setApellido(apellido);
 setNombre(nombre);
}
Una vez generada la excepción sólo queda definir el código que la manejara; para este 
caso particular el manejo simplemente consistirá en informarle la situación al usuario. En 
este momento debe realizarse la siguiente pregunta: tiene la clase Empleado la información 
suficiente para hacer el manejo del error, es decir, puede decidir la clase Empleado si presenta 
el error en consola o en una ventana. La respuesta es obvia: no; como esta clase puede ser 
empleada por cualquier otra, desconoce el tipo de interfaz de usuario que la puede terminar 
utilizando.
Si la clase no puede hacer el manejo del error, deberá delegarle esta función al objeto de la 
clase que invocó el método constructor de Empleado, es decir, se debe delegar el manejo del 
error al método constructor de cada una de las subclases de Empleado. En Java, la delegación 
del procesamiento del error se logra definiendo el lanzamiento de los errores que en el 
mismo método se presenten, más la adición a la declaración del método de la instrucción 
throws seguida del tipo de error que se estará lanzando. Teniendo en cuenta lo anterior, el 
código del método constructor de la clase Empleado quedaría de la siguiente manera:
public Empleado(String cedula, String apellido, String nombre) 
 throws Exception{
 if (cedula.trim().equals(“”))
 throw new Exception(“Valor de la cédula no válido”);
2. TÓPICOS AVANZADOS
176
Todo lo básico que debería saber sobre Programación orientada a objetos en Java
 this.cedula = cedula;
 setApellido(apellido);
 setNombre(nombre);
}
Del anterior código cabe resaltar que la instrucción throws se define a nivel del método que 
no va a hacer el manejo de la excepción, no a nivel de la definición de la clase.
La teoría de lanzamiento de excepciones define que ahora la excepción será lanzada al 
método constructor de las subclases de Empleado. En este momento se debe volver a repetir la 
pregunta: ¿tiene la subclase EmpleadoXHora (se toma como ejemplo esta clase, pero el análisis 
puede extenderse tanto a EmpleadoXComision como a EmpleadoSueldoFijo) la información 
suficiente para el manejo del error? La respuesta vuelve a ser no, por consiguiente este 
método debe seguir lanzando el error, hasta que llegue al método de la clase que pueda 
hacer su manejo. Las definiciones de los métodos constructores de las subclases de Empleado 
deberían quedar de la siguiente manera: 
public EmpleadoXHora(String cedula, String apellido, String nombre, 
 double horasTrabajadas, double sueldoXHora) throws Exception {
 super(cedula, apellido, nombre);
 setSueldoXHora(sueldoXHora);
 setHorasTrabajadas(horasTrabajadas);
}
public EmpleadoXComision(String cedula, String apellido, String nombre,
 double sueldoBasico, double porcentaje, double ventasTotales) 
 throws Exception {
 
 super(cedula, apellido, nombre);
 setSueldoBasico(sueldoBasico);
 setPorcentaje(porcentaje);
 setVentasTotales(ventasTotales);
}
public EmpleadoSueldoFijo(String cedula, String apellido, String nombre,
 double sueldoFijo) throws Exception {
 super(cedula, apellido, nombre);
 setSueldoFijo(sueldoFijo);
}
Es conveniente resaltar del anterior código las instrucciones super que invocan al 
constructor de la superclase de cada una de las clases que allí aparecen. Es este método el 
que eventualmente, y dependiendo de los valores que se pasen, el que puede generar una 
excepción. 
Como ninguno de los constructores de las subclases puede manejar la excepción esta será 
lanzada a los métodos de los objetos que invocan dichos métodos; en este caso, los métodos 
adicionarEmpleadoXHora, adicionarEmpleadoXComision y adicionarEmpleadoSueldoFijo 
de la clase Empresa. Como esta clase tampoco posee información para realizar un correcto 
manejo del error, será necesario que estos métodos también lancen las excepciones. 
177
Con todo el código definido, ahora la excepción llegará a cualquier método de la clase 
que haya invocado alguno de los métodos adicionar de la clase Empresa, es decir, ahora 
la excepción ha llegado a la clase Nomina01. Al repetir la pregunta de si está clase tiene la 
información suficiente para hacer el manejo de la excepción, la respuesta sería sí, en este 
caso. 
Es conveniente hacer un alto en este punto y analizar que con este mecanismo de manejo 
de excepciones se ha obtenido la ventaja que ofrecía la tercera alternativa de solución 
(presentada anteriormente) y, además, se han corregido las dos grandes deficiencias que 
esta poseía. 
2.2.2. Captura de excepciones
El proceso contrario a la generación y lanzamiento de excepciones es conocido como la 
captura de la excepción. A través de este proceso una clase maneja el error y, además, le 
permite reanudar la ejecución del programa. Se requiere la utilización de varios tipos de 
bloques de código para llevar a cabo este proceso.
Hasta el momento se conoce que durante la ejecución de un programa puede haber métodos 
que ante determinadas situaciones lancen excepciones, es decir, son métodos que pueden 
o no fallar. El mecanismo de manejo de errores requiere que el objeto de la clase que vaya 
a hacer el manejo del error incluya la invocación del método (o métodos) dentro de un 
bloque de código que debe ser nombrado con la palabra reservada try. La intención de 
este bloque de código es definir una o un conjunto de instrucciones que el programa debe 
intentar ejecutar (se menciona como un intento porque no es seguro que siempre se puedan 
ejecutar). El método main de la clase Nomina01 debería quedar de la siguiente manera (se 
presentará únicamente el código de la validación para los EmpleadosXHora con el fin de 
no despistar la atención del lector con tanto código; sin embargo, las instrucciones que se 
definen deben ser extendidas para los restantes tipos de empleados): 
for(int i=0;i<numeroEmpleados1;i++){
 double horas, sueldo;
 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 del empleado: “);
 horas = Double.valueOf(br.readLine()).doubleValue(); 
 System.out.print(“Digite sueldo por hora del empleado: “ );
 sueldo = Double.valueOf(br.readLine()).doubleValue();
 try{
 pyme.adicionarEmpleadoXHora(cedula, apellido, nombre, horas, sueldo);
 }
 
}
Dentro del bloque de código try, pueden incluirse, además, instrucciones que no lancen 
2. TÓPICOS AVANZADOS
178
Todo lo básico que debería saber sobre Programación orientada a objetos en Java
excepciones. En caso de que alguna de las instrucciones definidas dentro del try falle, no 
se ejecutará el resto de instrucciones que dentro de él se encuentren definidas. Lo que debe 
tenerse pendiente es que este bloque de código como tal no especifica de qué manera se 
realizará el manejo de la excepción. Después de que un método lanza una excepción dentro 
de un bloque try, el flujo de control intentará encontrar un bloque catch que maneje el error.
El bloque catch es el bloque de código directo responsable del manejo de la excepción. Este 
tipo de bloque de código debe definirse con la inclusión del tipo de excepción que capturará 
y, además, asignando un nombre de referencia al objeto excepción (recordemos que este 
objeto representa el error y que contiene información acerca del mismo) para permitir su 
manipulación. El código debe modificarse como se muestra a continuación:
for(int i=0;i<numeroEmpleados1;i++){double horas, sueldo;
 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 del empleado: “);
 horas = Double.valueOf(br.readLine()).doubleValue(); 
 System.out.print(“Digite sueldo por hora del empleado: “ );
 sueldo = Double.valueOf(br.readLine()).doubleValue();
 try{
 pyme.adicionarEmpleadoXHora(cedula, apellido, nombre, horas, sueldo);
 }catch(Exception e){
 System.out.println(e.getMessage());
 i--;
 }
 
}
Del anterior código es conveniente resaltar los siguientes puntos:
•	 Note la definición del bloque de código catch. Dicho bloque captura objetos 
Exception y el objeto que se capture queda disponible al interior del bloque bajo la 
referencia de nombre e.
•	 Esta clase sí interactúa directamente con el usuario de la aplicación y puede decidir 
qué hacer con esa situación excepcional; en ese caso, el manejo es simple: se le 
despliega el mensaje por pantalla al usuario y, además, se decrementa el contador 
del for para que vuelva la petición de introducir los datos del empleado.
•	 El mensaje que se le despliega al usuario se extrae del objeto Exception capturado 
empleando el método getMessage(). 
•	 Después de ejecutado el código del manejo del error, la aplicación supone que se 
recuperó de este mismo y continúa realizando de las instrucciones que se encuentren 
después del bloque de código catch. La ejecución no se reanuda en el punto donde 
se suspendió.
179
Existe un bloque de código adicional que puede definirse dentro del manejo de excepciones. 
El bloque de código finally permite definir instrucciones que se ejecutan, bien sea que se 
lancen o no excepciones. Dentro de este bloque de código se definen instrucciones que 
siempre deben ejecutarse. Considere el siguiente ejemplo:
for(int i=0;i<numeroEmpleados1;i++){
 double horas, sueldo;
 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 del empleado: “);
 horas = Double.valueOf(br.readLine()).doubleValue(); 
 System.out.print(“Digite sueldo por hora del empleado: “ );
 sueldo = Double.valueOf(br.readLine()).doubleValue();
 try{
 pyme.adicionarEmpleadoXHora(cedula, apellido, nombre, horas, sueldo);
 }catch(Exception e){
 System.out.println(e.getMessage());
 i--;
 }finally{
 System.out.println(“*******************”);
 }
 
}
Sin importar si las instrucciones dentro del try lanzan o no excepciones, siempre se imprimirá 
por pantalla el conjunto de asteriscos para delimitar los datos de los distintos empleados.
2.2.3. Definición de excepciones personales
En Java, el mecanismo de manejo de excepciones permite que en caso de ser requerido un 
usuario defina sus propios tipos de excepciones. Esto se puede lograr definiendo una clase 
que extienda a Exception.
Para el ejemplo que se está trabajando suponga que se desea manejar en una excepción no 
sólo el mensaje del error, sino también un atributo donde se pueda explicar el motivo del 
error que lo originan: Considere el siguiente código:
public class ExcepcionAmpliada extends Exception{
 private String descripcion;
 public ExcepcionAmpliada(String mensaje){
 super(mensaje);
 this.descripcion = “”;
 }
 public ExcepcionAmpliada(String mensaje, String descripción){
 super(mensaje);
 this.descripcion = descripcion;
2. TÓPICOS AVANZADOS
180
Todo lo básico que debería saber sobre Programación orientada a objetos en Java
 }
 public String getDescripcion(){
 return descripción;
 }
}
Del anterior código es conveniente resaltar la definición de dos constructores, que reciben 
ambos como parámetro el mensaje de la excepción e invocan el constructor de la superclase 
Exception, pasando dicha información.
La generación y lanzamiento de las excepciones definidas por el propio usuario sigue el 
mismo mecanismo definido para las excepciones generales, es decir, se requiere primero 
crear un objeto del tipo de la excepción específica y luego lanzarlo con la instrucción throw. 
Si un método no va a hacer el manejo de la excepción, debe especificar lo empleando el 
lanzamiento del tipo de la excepción a través de la palabra reservada throws.
Suponga que para el caso en análisis se desea además validar que el número de la cedula no 
sea un valor negativo. Esto se podría lograr a través de la definición del siguiente código en 
el constructor de la clase Empleado.
public Empleado(String cedula, String apellido, String nombre) 
 throws Exception{
 if (cedula.trim().equals(“”))
 throw new Exception(“Valor de la cédula no válido”);
 if (Integer.parseInt(cedula)<0)
 throw new ExcepcionAmpliada(“Valor de cedula no valido”,
 “La cedula no puede tener un valor negativo”);
 this.cedula = cedula;
 setApellido(apellido);
 setNombre(nombre);
}
181
sobre el lanzamiento de exCepCiones
En caso de que un segmento de código lance varios tipos de excepciones se deben especificar 
en la instrucción throws separadas por coma; es decir la definición del método debió haber 
sido la siguiente:
public Empleado(String cedula, String apellido, String nombre) 
 throws Exception, ExcepcionAmpliada
Sin embargo, la definición que se estableció en el código que se presenta antes de este apartado 
fue:
public Empleado(String cedula, String apellido, String nombre) 
 throws Exception
Para el caso de análisis, la anterior definición es correcta, porque el método está especificando 
que se lanzarán objetos de la clase Exception, y debido a la herencia todo objeto de la clase 
ExcepcionAmpliada es un tipo de objeto de la clase Exception. En este caso se está utilizando 
una de las propiedades de la herencia que define que un objeto de una subclase puede 
reemplazar a un objeto de una superclase.
Considérese la siguiente definición del método: 
public Empleado(String cedula, String apellido, String nombre) 
 throws ExcepcionAmpliada
Esta definición sí presenta problemas debido a que no todas las excepciones que lanza el 
método son de tipo ExcepcionAmpliada. Si el código del método lanzase un objeto de tipo 
Exception, se escenifica el caso en el que un objeto de una superclase trata de reemplazar a un 
objeto de la subclase. 
2. TÓPICOS AVANZADOS
182
Todo lo básico que debería saber sobre Programación orientada a objetos en Java
sobre la definiCión de varios bloques CatCh
Así como a través de la instrucción throws se puede especificar el lanzamiento de uno o varios 
tipos de excepciones, el manejo de excepciones en Java posibilita, también, definir código 
de manejo de error particular para cada uno de los tipos de errores. Para implementar esta 
situación es necesario añadir más bloques de código catch específicos para cada tipo de 
excepción. A manera de ejemplo, examine el siguiente fragmento de código modificado de la 
clase Nomina1:
for(int i=0;i<numeroEmpleados1;i++){
 double horas, sueldo;
 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 del empleado: “);
 horas = Double.valueOf(br.readLine()).doubleValue(); 
 System.out.print(“Digite sueldo por hora del empleado: “ );
 sueldo = Double.valueOf(br.readLine()).doubleValue();
 try{
 pyme.adicionarEmpleadoXHora(cedula, apellido, nombre, horas, 
 sueldo);
 }catch(ExcepcionAmpliada e){
 System.out.println(e.getMessage()+”\n”+e.getDescripcion());
 i--;
 }catch(Exception e){System.out.println(e.getMessage());
 i--;
 }finally{
 System.out.println(“*******************”);
 }
 
}
Del anterior código es conveniente mencionar los siguientes puntos:
•	 En caso de presentarse el lanzamiento de alguna excepción en la ejecución del método 
adicionarEmpleadoXHora, el mecanismo de manejo de errores buscará el primer 
catch en donde pueda hacerse este manejo; después de ejecutado ese bloque no se 
ejecutará ningún otro de los allí definidos. 
•	 Nótese que ambos bloques catch definidos nombran a la excepción que capturan con 
el nombre de variable e. El ámbito de esta variable está supeditado al bloque de código 
donde fue definida, es decir, que fuera de dicho bloque de código la variable no existe.
183
•	 En caso de presentarse una excepción de tipo ExcepcionAmpliada, se le desplegará al 
usuario el mensaje del error y, además, su propia descripción. Esto puede realizarse 
debido a que este tipo de excepción maneja dicha información. 
•	 En caso de presentarse una excepción de tipo Exception, como por herencia un objeto 
de una superclase no puede reemplazar a un objeto de una subclase, no se entraría 
en el primer catch. En este escenario el manejo de la excepción sería realizado por el 
segundo bloque catch.
•	 En caso de no presentarse excepciones en la ejecución del método 
adicionarEmpleadoXHora, ninguna de las instrucciones dentro del bloque catch se 
ejecutarían. 
La forma en que fue descrito el mecanismo de manejo de errores podría plantear dudas en caso 
de que se tuvieran las siguientes líneas de código:
for(int i=0;i<numeroEmpleados1;i++){
 double horas, sueldo;
 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 del empleado: “);
 horas = Double.valueOf(br.readLine()).doubleValue(); 
 System.out.print(“Digite sueldo por hora del empleado: “ );
 sueldo = Double.valueOf(br.readLine()).doubleValue();
 try{
 pyme.adicionarEmpleadoXHora(cedula, apellido, nombre, horas,sueldo);
 }catch(Exception e){
 System.out.println(e.getMessage());
 i--;
 }catch(ExcepcionAmpliada e){
 System.out.println(e.getMessage()+”\n”+e.getDescripcion());
 i--;
 }finally{
 System.out.println(“*******************”);
 }
 
}
¿Cómo funcionaría el mecanismo de manejo de errores en caso de que el método 
adicionarEmpleadoXHora lanzara una excepción de tipo ExcepcionAmpliada? Al lanzarse la 
excepción, el mecanismo buscará el primer bloque catch que pueda manejar el error, en este 
caso, y aunque existe un catch específico para objetos de tipo ExcepcionAmpliada, el manejo 
del error sería efectuado por el primer bloque catch, debido a que por herencia un objeto de 
una subclase es también un tipo de objeto de la superclase. 
2. TÓPICOS AVANZADOS
184
Todo lo básico que debería saber sobre Programación orientada a objetos en Java
Esta situación plantearía el siguiente problema, el código del segundo catch no se ejecutaría en 
ningún caso. Java detecta y marca estos errores en tiempo de compilación. Como el orden de 
estos bloques sí influye en el mecanismo de manejo de excepciones, entonces la norma general 
es la de ubicar los bloques catch de las excepciones más específicas antes de los bloques catch 
de las excepciones más generales.
sobre la Captura y posterior lanzamiento de exCepCiones
Considérese la siguiente implementación alternativa para el método adicionarEmpleadoXHora 
de la clase Empresa. 
public void adicionarEmpleadoXHora(String cedula, String apellido, 
 String nombre, double horasTrabajadas, double sueldoXHora) 
 throws Exception{
 try{
 empleados.add (new EmpleadoXHora(cedula, apellido, nombre, 
 horasTrabajadas, sueldoXHora) );
 }catch(ExcepcionAmpliada e){
 throw new Exception(e.getMessage());
 }
}
Este código especifica que en caso de producirse una excepción de tipo ExcepcionAmpliada, 
esta será capturada, se creará y lanzará un nuevo objeto de tipo Exception. En este caso, 
simplemente se ha cambiado una excepción por otra.
El mecanismo de manejo de excepciones de Java posibilita la definición de este tipo acciones, 
así como también permite capturar una excepción y volver a lanzar el mismo objeto dentro del 
propio bloque catch. En ocasiones, este tipo de instrucciones se emplean para realizar algún 
proceso de control cuando se presenta una excepción pero sin concluir el manejo del error de 
la misma.
185
sobre la funCionalidad del bloque finally 
El lector cuidadoso y perspicaz debe estarse preguntando cuál es la funcionalidad del bloque 
finally, si al final resulta lo mismo ubicar el código de dicho bloque fuera de los bloques 
catch. Para ejemplificar, esta situación considere el siguiente código:
for(int i=0;i<numeroEmpleados1;i++){
 double horas, sueldo;
 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 del empleado: “);
 horas = Double.valueOf(br.readLine()).doubleValue(); 
 System.out.print(“Digite sueldo por hora del empleado: “ );
 sueldo = Double.valueOf(br.readLine()).doubleValue();
 try{
 pyme.adicionarEmpleadoXHora(cedula, apellido, nombre, horas,sueldo);
 }catch(Excepcion e){
 System.out.println(e.getMessage()+”\n”+e.getDescripcion());
 i--;
 }finally{
 System.out.println(“*******************”);
 }
 
}
Al final se obtiene el mismo resultado definiendo el código anterior o definiendo el siguiente:
for(int i=0;i<numeroEmpleados1;i++){
 double horas, sueldo;
 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 del empleado: “);
 horas = Double.valueOf(br.readLine()).doubleValue(); 
 System.out.print(“Digite sueldo por hora del empleado: “ );
 sueldo = Double.valueOf(br.readLine()).doubleValue();
2. TÓPICOS AVANZADOS
186
Todo lo básico que debería saber sobre Programación orientada a objetos en Java
 try{
 pyme.adicionarEmpleadoXHora(cedula, apellido, nombre, horas,sueldo);
 }catch(Exception e){
 System.out.println(e.getMessage());
 i--;
 }
 System.out.println(“*******************”);
 
}
¿Cuál es la finalidad del bloque finally, si todo el código que en el se ubique puede perfectamente 
existir fuera de el? La respuesta a esta pregunta se encuentra relacionada con lo presentado en 
el apartado sobre la captura y posterior lanzamiento de las excepciones. 
Aunque en la mayoría de los casos se obtiene el mismo resultado al ubicar el código fuera 
del bloque finally, existen algunos donde el resultado es totalmente distinto. Por ejemplo, 
considere el siguiente código: 
for(int i=0;i<numeroEmpleados1;i++){
 double horas, sueldo;
 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 del empleado: “);
 horas = Double.valueOf(br.readLine()).doubleValue(); 
 System.out.print(“Digite sueldo por hora del empleado: “ );
 sueldo = Double.valueOf(br.readLine()).doubleValue();
 try{
 pyme.adicionarEmpleadoXHora(cedula, apellido, nombre, horas,sueldo);
 }catch(Exception e){
 throw e;
 }
 System.out.println(“*******************”);
 
}
En caso de presentarse una excepción en la ejecución del método adicionarEmpleadoXHorade 
la clase Empresa, esta será capturada en el catch respectivo cuya única acción define su nuevo 
lanzamiento. Como la excepción se vuelve a lanzar no se ejecuta ninguna otra instrucción 
dentro de dicho método, es decir, no se imprime el conjunto de asteriscos. 
Pero si el código del método fuera como el que se presenta a continuación,
187
for(int i=0;i<numeroEmpleados1;i++){
 double horas, sueldo;
 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 del empleado: “);
 horas = Double.valueOf(br.readLine()).doubleValue(); 
 System.out.print(“Digite sueldo por hora del empleado: “ );
 sueldo = Double.valueOf(br.readLine()).doubleValue();
 try{
 pyme.adicionarEmpleadoXHora(cedula, apellido, nombre, horas,sueldo);
 }catch(Exception e){
 throw e;
 }finally{
 System.out.println(“*******************”);
 }
 
}
En este caso, al presentarse un error en el método adicionarEmpleadoXHora el error se captura 
y se relanza, pero antes de activar el mecanismo de lanzamiento se ejecuta el código definido 
dentro del bloque finally, es decir, se imprimen por pantalla el conjunto de asteriscos.
2. TÓPICOS AVANZADOS
188
Todo lo básico que debería saber sobre Programación orientada a objetos en Java
Contraste entre apliCaCiones Con y sin maneJo de errores sobre la Captura 
y posterior lanzamiento de exCepCiones
Se podría pensar que el manejo de las situaciones excepcionales bajo este mecanismo puede 
aumentar la complejidad del código, pero la realidad es totalmente opuesta. Considere el 
siguiente ejemplo, se desea definir un método en una clase que permita copiar el contenido de 
un archivo a otro. El código que se presenta a continuación presenta parte de las instrucciones 
de dicho método; por facilidad se encapsula cada uno de los comportamientos necesarios en 
métodos adicionales con nombres sumamente descriptivos.
abrirDeLectura(archivo1);
copiarContenidoDeArchivo1AMemoria();
abrirDeEscritura(archivo2);
copiarContenidoDeMemoriaAArchivo2();
cerrarArchivo(archivo2);
cerrarArchivo(archivo1);
El anterior código supone que el método debe conocer dos variables de tipo String 
correspondientes a archivo1 y archivo2 en donde se guarda el nombre del archivo a copiar y 
el nombre del nuevo archivo, respectivamente. 
Ahora bien, el código que se muestra no tiene en cuenta ninguna situación anormal. 
Generalmente, los desarrolladores tienden a manejar estas situaciones modificando la signatura 
de los métodos para que estos devuelvan valores numéricos que indiquen el error que ha 
sucedido7. Existen algunas variantes a este mecanismo de manejo de errores, pero al final la 
esencia de la solución es la misma. 
Aplicado al ejemplo que se está trabajando, se puede suponer lo siguiente:
•	 El método abrirDeLectura devuelve un valor entero. Si el valor que devuelve es 0, 
significa que el método se ejecutó sin contratiempos; si el valor que devuelve es 1, 
significa que el archivo especificado no se pudo abrir de lectura porque no existe en el 
sistema de archivos; si el valor que devuelve el método es 2, significa que no se tienen 
permisos suficientes en el sistema de archivo para abrir dicho archivo.
•	 El método abrirDeEscritura devuelve un valor entero. Si el valor que devuelve es 
0, significa que el método se ejecutó sin contratiempos; si el valor que devuelve es 1 
significa que el archivo no pudo crearse porque ya existe en el sistema de archivos, 
un archivo con el mismo nombre; si devuelve 2, significa que no se tienen permisos 
suficientes en el sistema para crear un nuevo archivo.
•	 El método copiarContenidoDeArchivo1AMemoria devuelve un valor entero. Si el 
valor que devuelve es 0, significa que el método se ejecutó sin inconvenientes; si el 
valor que devuelve es 1, significa que hubo algún tipo de problema de entrada o salida 
con la lectura del contenido del archivo.
189
• El método copiarContenidoDeMemoriaAArchivo2 devuelve un valor entero. Si el valor 
que devuelve es 0 significa que el método se ejecutó sin inconvenientes; si el valor que 
devuelve es 1, significa que se presentó algún tipo de problema de entrada o salida con la 
escritura del archivo. 
Con las consideraciones mencionadas sería necesario redefinir el código del método de la 
siguiente manera:
int v1 = abrirDeLectura(archivo1);
if (v1==1)
 System.out.println(“El archivo no existe”);
else if (v1==2)
 System.out.println(“Permisos insuficientes”);
else if (v1==0){
 int v2 = copiarContenidoDeArchivo1AMemoria();
 if (v2==1)
 System.out.println(“Problemas de entrada/salida”);
 else if (v2==0){
 int v3 = abrirDeEscritura(archivo2);
 if (v3==1)
 System.out.println(“Ya existe archivo con el nombre 
 especificado”);
 else if (v3==2)
 System.out.println(“Permisos insuficientes”);
 else if (v3==0){
 int v4 = copiarContenidoDeMemoriaAArchivo2();
 if (v4==1)
 System.out.println(“Problemas de entrada/salida”); 
 cerrarArchivo(archivo2); 
 }
 }
 cerrarArchivo(archivo1);
}
 
Ahora cuando se contempla el manejo de situaciones anormales el método es más complejo de 
entender y, por ende, en caso de ser necesario, más difícil de corregir. Como simple ejercicio, 
considérese qué tanto se vería afectado el código en caso de que el método cerrarArchivo 
también retornara un valor indicando si el método se ejecutó bien o no. 
¿Cómo sería la implementación del mismo problema si se trabajara con el mecanismo de 
manejo de excepciones? La solución que se muestra a continuación supone que los métodos 
lanzan excepciones para cada uno de los casos anormales descritos en párrafos anteriores. 
2. TÓPICOS AVANZADOS
190
Todo lo básico que debería saber sobre Programación orientada a objetos en Java
try{ abrirDeLectura(archivo1);
 copiarContenidoDeArchivo1AMemoria();
 abrirDeEscritura(archivo2);
 copiarContenidoDeMemoriaAArchivo2();
}catch(Exception e){
 System.out.println(e.getMessage());
}finally{
 cerrarArchivo(archivo2);
 cerrarArchivo(archivo1);
}
Obviamente, la solución que emplea el mecanismo de manejo de excepciones es más simple, 
más fácil de entender y por ende más fácil de corregir.
sobre las exCepCiones no Chequeadas en Java
Java tiene una clase especial de excepciones, que así se denominan, no chequeadas. Estas 
excepciones se definen a partir de la clase RuntimeExcepction que, es, a su vez, una subclase 
de Exception. Objetos creados a partir de esta clase representan errores en la programación, 
de los cuales no se espera que la aplicación se pueda recuperar. La particularidad de este tipo 
de excepciones es que no es necesario especificarlas como parte de la instrucción throws en la 
declaración del método, pero aun así cumplen con todo el proceso definido en el mecanismo 
de manejo de excepciones; en otras palabras, los métodos las lanzan, pero no hay necesidad de 
especificarlas en la signatura del método. 
Existe una gran variedad de este tipo de excepciones, entre las que se encuentran:
•	 NullPointerException: se produce al intentar ejecutar un método o acceder a una 
propiedad empleando una referencia que no apunta a ningún objeto.
•	 IndexOutofBoundsException: se produce al intentar acceder a una posición inexistente 
dentro de un array.
•	 ClassCastException: se produce al intentar convertir una referencia de un tipo a otro, 
cuando la conversión no es válida.
•	 NumberFormatException: se produce al especificar un formato de número no válido, 
por ejemplo, cuando se desea convertir el String con la cadena vacía a un número.
Generalmente cuando se producen errores de este tipo, es necesario detener la aplicación y 
corregir la lógica de codificación de la misma.
191
deCimoterCera aproximaCión a la apliCaCión del CálCulo de la 
nóminaempleando programaCión orientada a obJetos
En esta implementación de la aplicación se adiciona el manejo de excepciones, lo que repercute 
en cambios en la mayoría de las clases, más que todo en las signaturas de algunos de los 
métodos. La clase Nomina01 si se ve fuertemente modificada ya que se adiciona código para el 
manejo de errores. El código que aparece en negrilla resalta las modificaciones efectuadas con 
respecto a la anterior aproximación.
public interface PersonaAsalariada{
 public double calcularSalario();
}
public abstract class Empleado implements PersonaAsalariada{
 protected final String cedula;
 private String apellido;
 private String nombre;
 public Empleado(String cedula, String apellido, String nombre) 
 throws Exception{
 if (cedula.trim().equals(“”)) 
 throw new Exception(“valor de cedula invalido”); 
 if (cedula.matches(“.*[a-zA-Z].*”))
 throw new Exception(“La cedula no puede tener caracteres”);
 this.cedula = cedula;
 setApellido(apellido);
 setNombre(nombre);
 }
 public final String getCedula(){
 return cedula;
 }
 public final String getApellido(){
 return apellido;
 }
 public final void setApellido(String apellido){
 if (apellido!=null) this.apellido = apellido;
 else this.apellido=””;
 }
2. TÓPICOS AVANZADOS
192
Todo lo básico que debería saber sobre Programación orientada a objetos en Java
 public final String getNombre(){
 return nombre;
 }
 public final void setNombre(String nombre){
 if (nombre!=null) this.nombre = nombre;
 else this.nombre=””;
 }
}
public class EmpleadoXHora extends Empleado{
 private double horasTrabajadas;
 private double sueldoXHora;
 public EmpleadoXHora(String cedula, String apellido, String nombre, 
 double horasTrabajadas, double sueldoXHora) throws Exception {
 super(cedula, apellido, nombre);
 setSueldoXHora(sueldoXHora);
 setHorasTrabajadas(horasTrabajadas);
 }
 public double calcularSalario(){
 return horasTrabajadas * sueldoXHora;
 }
 public double getHorasTrabajadas(){
 return horasTrabajadas;
 }
 public void setHorasTrabajadas(double horas ){
 if (horas>=0) this.horasTrabajadas = horas;
 else this.horasTrabajadas = 0;
 }
 public double getSueldoXHora(){
 return sueldoXHora;
 }
 public void setSueldoXHora(double sueldo ){
 if (sueldo>=0) this.sueldoXHora = sueldo;
 else this.sueldoXHora = 0;
 }
 public boolean equals(Object o){
 if (!(o instanceof EmpleadoXHora)) return false;
 
 EmpleadoXHora temp = (EmpleadoXHora)o;
 
193
 if ( !this.getCedula().equals(temp.getCedula()) ) return false;
 if ( !this.getApellido().equals(temp.getApellido()) ) return false;
 if ( !this.getNombre().equals(temp.getNombre()) ) return false;
 if ( this.getSueldoXHora()!=temp.getSueldoXHora() ) return false;
 if ( this.getHorasTrabajadas()!=temp.getHorasTrabajadas() ) 
 return false;
 return true;
 }
}
public class EmpleadoXComision extends Empleado{
 private double sueldoBasico;
 private double porcentaje;
 private double ventasTotales;
 public EmpleadoXComision(String cedula, String apellido, 
 String nombre, double sueldoBasico, double porcentaje, 
 double ventasTotales) throws Exception {
 
 super(cedula, apellido, nombre);
 setSueldoBasico(sueldoBasico);
 setPorcentaje(porcentaje);
 setVentasTotales(ventasTotales);
 }
 public double calcularSalario(){
 return sueldoBasico + porcentaje * ventasTotales;
 }
 public double getSueldoBasico(){
 return sueldoBasico;
 }
 public void setSueldoBasico(double sueldo){
 if (sueldo>=0) this.sueldoBasico = sueldo;
 else this.sueldoBasico = 0;
 }
 public double getPorcentaje(){
 return porcentaje;
 }
 public void setPorcentaje(double porcentaje ){
 if (porcentaje>=0 && porcentaje<=1) this.porcentaje = porcentaje;
 else this.porcentaje = 0;
 }
2. TÓPICOS AVANZADOS
194
Todo lo básico que debería saber sobre Programación orientada a objetos en Java
 public double getVentasTotales(){
 return ventasTotales;
 }
 public void setVentasTotales(double ventas){
 if (ventas>=0) this.ventasTotales = ventas;
 else this.ventasTotales = 0;
 }
 public boolean equals(Object o){
 if (!(o instanceof EmpleadoXComision)) return false;
 
 EmpleadoXComision temp = (EmpleadoXComision)o;
 
 if ( !this.getCedula().equals(temp.getCedula()) ) return false;
 if ( !this.getApellido().equals(temp.getApellido()) ) return false;
 if ( !this.getNombre().equals(temp.getNombre()) ) return false;
 if ( this.getSueldoBasico()!=temp.getSueldoBasico() ) return false;
 if ( this.getPorcentaje()!=temp.getPorcentaje() ) return false;
 if ( this.getVentasTotales()!=temp.getVentasTotales() ) 
 return false;
 return true;
 }
}
public class EmpleadoSueldoFijo extends Empleado{
 private double sueldoFijo;
 public EmpleadoSueldoFijo(String cedula, String apellido, 
 String nombre, double sueldoFijo) throws Exception {
 super(cedula, apellido, nombre);
 setSueldoFijo(sueldoFijo);
 }
 public double calcularSalario(){
 return sueldoFijo;
 }
 public double getSueldoFijo(){
 return sueldoFijo;
 }
 public void setSueldoFijo(double sueldo){
 if (sueldo>=0) this.sueldoFijo = sueldo;
 else this.sueldoFijo = 0;
 }
195
 public boolean equals(Object o){
 if (!(o instanceof EmpleadoSueldoFijo)) return false;
 
 EmpleadoSueldoFijo temp = (EmpleadoSueldoFijo)o;
 
 if ( !this.getCedula().equals(temp.getCedula()) ) return false;
 if ( !this.getApellido().equals(temp.getApellido()) ) return false;
 if ( !this.getNombre().equals(temp.getNombre()) ) return false;
 if ( this.getSueldoFijo()!=temp.getSueldoFijo() ) return false;
 return true;
 }
}
import java.util.ArrayList; 
public class Empresa{
 final private String nit;
 private String nombre;
 private String direccion;
 private ArrayList<Empleado> empleados;
 public Empresa(String nit, String nombre, String direccion){
 if (nit!=null) this.nit = nit;
 else this.nit=””;
 setNombre(nombre);
 setDireccion(direccion);
 empleados = new ArrayList<Empleado>();
 }
 public Empresa(String nit){
 if (nit!=null) this.nit = nit;
 else this.nit=””;
 setNombre(“”);
 setDireccion(“”);
 empleados = new ArrayList<Empleado> ();
 }
 public Empresa(String nit, int numero){
 if (nit!=null) this.nit = nit;
 else this.nit=””;
 setNombre(“”);
 setDireccion(“”);
 empleados = new ArrayList<Empleado>(numero);
 }
2. TÓPICOS AVANZADOS
196
Todo lo básico que debería saber sobre Programación orientada a objetos en Java
 public String getNit(){
 return nit;
 }
 public String getNombre(){
 return nombre;
 }
 public void setNombre(String nombre){
 if (nombre!=null) this.nombre = nombre;
 else this.nombre=””;
 }
 public String getDireccion(){
 return direccion;
 }
 public void setDireccion(String direccion){
 if (direccion!=null) this.direccion = direccion;
 else this.direccion=””;
 }
 public int getNumeroEmpleados(){
 return empleados.size();
 }
 public void adicionarEmpleadoXHora(String cedula, String apellido, 
 String nombre, double horasTrabajadas, double sueldoXHora) 
 throws Exception {
 empleados.add (new EmpleadoXHora(cedula, apellido, nombre, 
 horasTrabajadas, sueldoXHora) );
 }
 public void adicionarEmpleadoXComision(String cedula, String apellido,
 String nombre, double sueldoBasico, double porcentaje, 
 double ventasTotales) throws Exception {
 empleados.add( new EmpleadoXComision(cedula, apellido, nombre,
 sueldoBasico, porcentaje, ventasTotales) );
 }
 public void adicionarEmpleadoSueldoFijo(String cedula, 
 String apellido, String nombre, double sueldoFijo) 
 throws Exception {
 empleados.add( new EmpleadoSueldoFijo(cedula, apellido, nombre, 
 sueldoFijo) );
 }
 public double calcularNominaTotal(){double total = 0;
 for (int i=0;i<empleados.size();i++)
 total = total + empleados.get(i).calcularSalario();
 return total;
 }
}
197
import java.io.BufferedReader;
import java.io.InputStreamReader;
public class Nomina1{
 public static void main(String[] args) throws Exception{
 Empresa pyme = new Empresa(“123”,”Acme inc.”,”805 Silicon Valley”);
 String cedula, apellido, nombre; 
 double total = 0;
 
 BufferedReader br = new BufferedReader( 
 new InputStreamReader( System.in ) );
 
 System.out.print(“Digite numero de empleados sueldo por hora: “); 
 int numeroEmpleados1 = Integer.valueOf(br.readLine()).intValue();
 
 for(int i=0;i<numeroEmpleados1;i++){
 double horas, sueldo;
 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 del empleado: “);
 horas = Double.valueOf(br.readLine()).doubleValue(); 
 System.out.print(“Digite sueldo por hora del empleado: “ );
 sueldo = Double.valueOf(br.readLine()).doubleValue();
 try{
 pyme.adicionarEmpleadoXHora(cedula, apellido, nombre, horas,
 sueldo);
 }catch(Exception e){
 System.out.println(e.getMessage());
 i--;
 }
 System.out.println(“*******************”);
 }
 System.out.print(“Digite numero de empleados por comision: “); 
 int numeroEmpleados2 = Integer.valueOf(br.readLine()).intValue();
 
 for(int i=0;i<numeroEmpleados2;i++){
2. TÓPICOS AVANZADOS
198
Todo lo básico que debería saber sobre Programación orientada a objetos en Java
 double sueldo, porcentaje, ventas;
 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 sueldo basico empleado: “);
 sueldo = Double.valueOf(br.readLine()).doubleValue(); 
 System.out.print(“Digite porcentaje del empleado: “ );
 porcentaje = Double.valueOf(br.readLine()).doubleValue();
 System.out.print(“Digite ventas totales del empleado: “ );
 ventas = Double.valueOf(br.readLine()).doubleValue();
 try{
 pyme.adicionarEmpleadoXComision(cedula, apellido, nombre,
 sueldo, porcentaje, ventas);
 }catch(Exception e){
 System.out.println(e.getMessage());
 i--;
 }
 System.out.println(“*******************”);
 }
 System.out.print(“Digite numero de empleados sueldo fijo: “); 
 int numeroEmpleados3 = Integer.valueOf(br.readLine()).intValue();
 
 for(int i=0;i<numeroEmpleados3;i++){
 double sueldo;
 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 sueldo basico empleado: “);
 sueldo = Double.valueOf(br.readLine()).doubleValue(); 
 try{
 pyme.adicionarEmpleadoSueldoFijo(cedula, apellido, nombre, 
 sueldo);
 }catch(Exception e){
 System.out.println(e.getMessage());
 i--;
 }
 System.out.println(“*******************”);
 }
 total = pyme.calcularNominaTotal();
 
 System.out.println(“\nLa nómina total es: “+ total);
 
 }
}
199
3. 
CREACIÓN DE INTERFACES 
GRÁFICAS DE USUARIO
La interfaz de usuario para la aplicación del cálculo de la nómina es hasta ahora 
supremamente sencilla. Todo el procesamiento se realiza a través de la línea de comandos, 
allí se captura y se le despliega la información al usuario. El propósito de este capítulo 
es presentar características básicas de creación de interfaces gráficas de usuario en Java 
empleando Swing. La tecnología Swing corresponde a un amplio conjunto de herramientas 
para la construcción de interfaces gráficas de usuario (GUI, del inglés Graphic User Interface) 
que las vuelve interactivas. Swing corresponde a la evolución de la tecnología AWT que se 
empleaba anteriormente en Java.
200
Todo lo básico que debería saber sobre Programación orientada a objetos en Java
sobre el diseño y desarrollo de interfaCes gráfiCas de usuario (gui)
La mayoría de lenguajes de programación brindan características para la definición de las GUI. 
Para facilitar el diseño y desarrollo de este tipo de elementos, los lenguajes de programación 
trabajan con avanzadas herramientas gráficas donde el usuario solamente requiere arrastrar 
los elementos a un formulario para después asignarle comportamiento como respuesta a los 
eventos de estos (por ejemplo, cuando se hace clic en ellos, cuando se presionen teclas, etc.). 
 
Este tipo de forma de desarrollo de las GUI generalmente da origen a dos problemas:
 
•	 Duplicación de código. Imagine el siguiente escenario: un programador necesita 
desarrollar dos interfaces de usuario que en esencia son muy parecidas (suponga que 
una de ellas se diferencia de la otra en que adiciona un campo extra). El desarrollador 
seguramente empleará la herramienta de diseño para crear la primera de las interfaces 
y luego copiará y pegará la distribución gráfica en la otra. En la segunda interfaz, 
adicionará los elementos que sean necesarios. Prácticamente el código de la primera 
interfaz se ha duplicado en la segunda.
201
La duplicación de código no es inherentemente mala; el problema comienza cuando se 
requiere realizarle algún tipo de ajuste o modificación, pues esta misma modificación debe 
ser extendida a los restantes lugares donde se haya duplicado el código. Generalmente, lo 
que termina sucediendo es que un programador olvida realizar el cambio en el código en 
alguno de los lugares.
•	 Generación de interfaces gráficas de componentes estáticos. Después de finalizar 
el desarrollo de una GUI bajo el enfoque que se está presentando, un diseñador 
se enfrenta ante el dilema de si bloquea o no el tamaño de la interfaz gráfica. 
Si no lo hace, cuando el usuario cambie el tamaño de la ventana aparecerá 
un gran espacio desaprovechado (como se muestra en la siguiente gráfica). 
 
 La alternativa de permitir la redimensión del cambio reajustando los elementos de la 
ventana no es ni siquiera considerada por los desarrolladores debido al gran trabajo 
que este demandaría realizarlo en cada ventana.
La tecnología para el desarrollo de las GUI en Java busca corregir ambas situaciones.
3.1. cOmpOnentes gráficOs
En Java todos los elementos que se utilizan son objetos (a excepción de los tipos de datos 
primitivos) por lo que no queda muy difícil deducir que los componentes gráficos que se 
requieren para la creación de las GUI también lo son. Solo basta por conocer los nombres 
de las clases y paquetes que contienen dichas definiciones, y lo que representan cada una 
de ellas. 
3. CREACIÓN DE INTERFACES GRÁFICAS DE USUARIOS
202
Todo lo básico que debería saber sobre Programación orientada a objetos en Java
Este libro se centra en los componentes básicos que se muestran en la siguiente tabla14. 
Nombre de la clase Descripción
javax.swing.JFrame Esta clase modela al componente gráfico ventana.
javax.swing.JLabel
Esta clase modela al componente gráfico etiqueta de texto. Un objeto 
de esta clase le despliega información al usuario, pero no permite 
que este la modifique directamente.
javax.swing.JTextField
Esta clase modela al componente gráfico cuadro de texto. Un objeto 
de esta clase permite desplegar y, además, acepta información 
suministrada por el usuario.
javax.swing.JButton Esta clase modela al componentegráfico botón.
javax.swing.JPanel
Un JPanel no es un componente gráfico como tal, más bien es 
un agrupador o contenedor de elementos gráficos. Además, 
posibilita establecer la posición y tamaño de los componentes 
registrados a través del uso de un objeto de la interface java.awt.
LayoutManager
No tiene sentido el despliegue de componentes gráficos en pantalla de forma aislada sin 
que estos se encuentren incluidos dentro de una ventana (por ejemplo, desplegar solo un 
botón que ande flotando por el espacio del escritorio). Por eso uno de los componentes más 
importantes en el desarrollo de las GUI es JFrame. Analice el siguiente código de definición 
de una ventana:
JFrame f = new JFrame();
f.setSize(300,300);
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.setVisible(true);
El código anterior crea un nuevo objeto JFrame (ventana); a través del método setSize le 
establece un tamaño de 300 pixeles de largo y 300 pixeles de ancho; en la siguiente instrucción 
se establece que al presionar el botón cerrar se debe terminar la aplicación (este método 
recibe una constante que es suministrada a través del atributo público, estático y final de la 
clase JFrame que lleva por nombre EXIT_ON_CLOSE), y, por último, la instrucción final hace 
visible al objeto en la pantalla. Como regla general la última operación de manipulación de 
una ventana debería ser la invocación al método setVisible. 
Al ejecutarse el anterior código aparece en la pantalla del usuario una ventana, como la que 
se muestra en la siguiente gráfica:
14 Mayor información sobre componentes gráficos puede ser consultada en la siguiente referencia: http://java.sun.com/
docs/books/tutorial/uiswing/index.html
203
Note como solo con la instrucción new JFrame se ha podido crear una ventana que posee 
un icono (que se muestra en la esquina superior izquierda), una barra de título, los botones 
para minimizar, maximizar y cerrar, además, la ventana también posee sus bordes; y si se 
hace clic en alguno de ellos y se reajusta la ventana, esta cambia de tamaño. 
sobre la instruCCión de finalizaCión de la apliCaCión
Si no se establece a través de la instrucción setDefaultCloseOperation el botón cerrar de una 
ventana no finaliza la aplicación sino que simplemente vuelve invisible una ventana. Muchas 
aplicaciones de software basan su funcionamiento en este tipo de comportamiento, como, por 
ejemplo, considere un programa de reproducción de archivos de sonido. El siguiente código 
ejemplifica esta situación.
JFrame reproductor = new JFrame(“Reproductor”);
reproductor.setSize(300,300);
reproductor.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
reproductor.setVisible(true);
JFrame ecualizador = new JFrame(“Ecualizador”);
ecualizador.setSize(300,300);
ecualizador.setVisible(true);
JFrame listaReproduccion = new JFrame(“Lista de Reproducción”);
listaReproduccion.setSize(300,600);
listaReproduccion.setVisible(true);
3. CREACIÓN DE INTERFACES GRÁFICAS DE USUARIOS
204
Todo lo básico que debería saber sobre Programación orientada a objetos en Java
Sobre el anterior código es conveniente mencionar:
•	 Se toma como supuesto que el software de reproducción además de presentar una 
ventana para controlar la reproducción (detener, pausar, continuar la reproducción), 
trabaja con una ventana donde se muestra el ecualizador gráfico y otra ventana donde 
se despliega la lista de reproducción de los archivos.
•	 El código hace uso de un constructor polimórfico de la clase JFrame que recibe como 
parámetro el nombre a desplegar en la ventana en la barra de título. 
Al presionar el botón cerrar en las ventanas denominadas lista de reproducción y ecualizador, 
simplemente se ocultan los objetos, pues estos no desaparecen de la memoria. Al presionar el 
botón cerrar de la ventana identificada como reproductor, se cierra la aplicación. 
La siguiente gráfica muestra el resultado obtenido de la ejecución del código.
205
sobre el Componente Jpanel 
Previamente se ha mencionado que un JPanel no es un componente gráfico, sino que más 
bien hace las veces de un agrupador o contenedor de elementos gráficos. Un ejemplo ayuda 
a clarificar esta situación; suponga por un momento que un desarrollador requiere diseñar e 
implementar dos ventanas, como las que se presentan a continuación.
 
En el apartado sobre el diseño y desarrollo de interfaces gráficas de usuario (GUI) se mencionó 
que generalmente los desarrolladores optan por duplicar el código que genera dicha interfaz 
gráfica. Una mejor aproximación a esta situación sería definir una nueva clase que modelara 
esta porción de la ventana, de tal manera que si se requiere utilizar en una ventana, se instancia 
un objeto de la clase y se añade.
La clase JPanel se utiliza para representar a un conjunto de elementos gráficos; pero de nada 
sirve un contenedor de elementos gráficos si no se puede especificar el tamaño y la ubicación 
de dichos elementos.
3. CREACIÓN DE INTERFACES GRÁFICAS DE USUARIOS
206
Todo lo básico que debería saber sobre Programación orientada a objetos en Java
un eJemplo senCillo de la utilizaCión del Componente Jpanel
Para entender cómo es el funcionamiento de los JFrame con JPanel considere el código que se 
presenta a continuación:
JFrame f = new JFrame();
 JPanel p = new JPanel();
 p.add(new JButton(“Boton 1”));
 p.add(new JButton(“Boton 2”));
 p.add(new JButton(“Boton 3”));
f.getContentPane().add(p);
f.setSize(300,300);
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.setVisible(true);
Al ejecutar el anterior código se obtiene una interfaz gráfica como la que se presenta a 
continuación:
La mayoría del código de la ventana se ha presentado en anteriores ejemplos, por lo cual no 
volverá a ser explicado. Las instrucciones que vale la pena resaltar son: 
•	 Existe una línea de código que crea un nuevo objeto JPanel. Como apenas se acaba de 
crear, este objeto no almacena referencias a ningún otro componente gráfico.
•	 Se le adicionan componentes gráficos a través del método add. En este caso particular 
se han añadido tres objetos de la clase JButton. 
•	 No solamente es necesario crear un nuevo objeto JPanel, sino que también es necesario 
incluirlo dentro de la ventana. Esto se logra utilizando la instrucción getContentPane.
add() y si se pasa como referencia el JPanel a desplegar en la ventana.
207
sobre la definiCión de Clases que extiendan Jpanel 
En el apartado anterior, un ejemplo sencillo de la utilización del componente JPanel, se 
codificó un ejemplo de una interfaz gráfica de usuario; sin embargo, dicho ejemplo puede ser 
codificado también de la siguiente manera:
public class PanelInterno extends JPanel{
 public PanelInterno(){
 add(new JButton(“Boton 1”));
 add(new JButton(“Boton 2”));
 add(new JButton(“Boton 3”));
 }
}
JFrame f = new JFrame();
JPanel p = new PanelInterno(); 
f.getContentPane().add(p);
f.setSize(300,300);
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.setVisible(true);
La configuración gráfica que se obtiene al ejecutar el anterior código es igual a la que se obtuvo 
con el código del apartado anterior. La diferencia radical de esta implementación es que se ha 
definido la clase PanelInterno, que extiende a la clase JPanel en donde se ha encapsulado la 
configuración gráfica de la ventana. 
Esta forma de implementación beneficia la reutilización ya que es más sencillo emplear la 
configuración gráfica encapsulada en la clase PanelInterno en otras ventanas (situación 
analizada en el apartado Sobre el component JPanel). Por ejemplo, el siguiente código crea 
dos ventanas que tienen configuraciones gráficas similares sin necesidad de la duplicación del 
código.
JFrame f1 = new JFrame(“Ventana 1”);
JPanel p1 = new PanelInterno(); 
f1.getContentPane().add(p1);
f1.setSize(300,300);
f1.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
3. CREACIÓN DE INTERFACES GRÁFICAS DE USUARIOS
208
Todo lo básico que debería saber sobre Programación orientada a objetos en Java
f1.setVisible(true);JFrame f2 = new JFrame(“Ventana 2”);
JPanel p2 = new PanelInterno(); 
f2.getContentPane().add(p2);
f2.setSize(300,300);
f2.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f2.setVisible(true);
3.2. lAyOuts
Uno de los problemas mencionados en el apartado sobre el diseño y desarrollo de interfaces 
gráficas de usuario (GUI) encuentra solución con la utilización de objetos JPanel. El punto 
referente a la generación de las GUI de componentes estáticas encuentra solución a través 
de la utilización de objetos que implementen la interface LayoutManager. Estos objetos son 
conocidos como Layouts.
Definición de Layout
Un Layout es un algoritmo que establece ubicación y tamaño de una serie de componentes 
gráficos.
Java brinda una amplia gama de Layouts. Este libro se ocupará de detallar tres de ellos15: 
FlowLayout, GridLayout y BorderLayout; y, adicionalmente, se presentará cómo combinar 
estos elementos para obtener resultados mejores y más atractivos.
15 Mayor información sobre los restantes Layouts puede ser consultada en la siguiente referencia: http://java.sun.com/
docs/books/tutorial/uiswing/layout/visual.html
209
Antes de entrar a analizar cada uno de los Layouts mencionados es conveniente tener claro 
lo siguiente.
•	 Un JPanel puede tener definido un solo algoritmo de asignación de tamaño y 
posición de sus componentes, es decir, un solo Layout.
•	 Para especificar que un JPanel trabajará con un determinado Layout es necesario 
emplear el método setLayout definido en la clase JPanel. Pasando como parámetro 
el valor null se especificará que el JPanel no trabajará con un Layout, entonces será 
necesario definir para cada componente gráfico su ubicación y su respectivo tamaño 
a través de la utilización del método setBounds .
•	 Para adicionar elementos gráficos a un JPanel se emplea el método add y se pasa 
como parámetro la referencia al objeto gráfico que se desea añadir.
•	 Para definir que una ventana debe desplegar un conjunto de componentes gráficos 
especificados en un JPanel debe emplearse el método getContentPane().add y debe 
pasarse cómo parámetro la referencia al JPanel que se desea desplegar.
3.2.1. FlowLayout
Este es uno de los algoritmos de asignación de ubicación más simple y sencillo de utilizar. 
Como tal no modifica el tamaño de los objetos gráficos, sino que simplemente establece la 
ubicación de los mismos en un JPanel, y se obtiene a partir de la instanciación de la clase 
java.awt.FlowLayout. Considérese el siguiente ejemplo:
JFrame f = new JFrame();
 JPanel p = new JPanel();
 p.setLayout(new FlowLayout());
 p.add(new JLabel(“Cedula”));
 p.add(new JTextField(20));
 p.add(new JButton(“Haga clic aca”));
f.setSize(300,200);
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.getContentPane().add(p);
f.setVisible(true);
Después de ejecutar el anterior código se obtiene la siguiente configuración gráfica: 
3. CREACIÓN DE INTERFACES GRÁFICAS DE USUARIOS
210
Todo lo básico que debería saber sobre Programación orientada a objetos en Java
Al redimensionar la ventana se obtiene la siguiente configuración gráfica.
La forma en que organiza los componentes este Layout es evidente. Ubica los elementos 
uno después del otro, teniendo en cuenta el orden en que fueron añadidos al JPanel. Si 
hay suficiente espacio en la ventana para mostrar los componentes, estos se muestran; en 
caso contrario, los componentes para los que no hay suficiente espacio se despliegan en la 
parte de abajo. El comportamiento se repite nuevamente en caso de que no exista suficiente 
espacio para todos los componentes.
Este Layout posee los siguientes tres constructores:
•	 FlowLayout(int alineacion). Crea un nuevo objeto FlowLayout, el parámetro que 
se suministra indica cómo se alinearán los componentes que se adicionen. Se puede 
especificar que los componentes queden centrados en el JPanel, o que se alineen 
hacia la derecha o hacia la izquierda.
•	 FlowLayout(int alineacion, int espacioHorizontal, int espacioVertical). 
Crea un nuevo objeto FlowLayout, el primer parámetro indica cómo se alinearán los 
componentes, el segundo parámetro especifica la cantidad de pixeles que separarán 
a los componentes en forma horizontal y el tercer parámetro especifica la cantidad 
de pixeles que separarán a los componentes de forma vertical.
•	 FlowLayout(). Crea un nuevo objeto FlowLayout; por defecto, los objetos se centrarán 
en el JPanel. 
Considere el siguiente código que presenta una pequeña modificación al ejemplo anterior.
JFrame f = new JFrame();
211
 JPanel p = new JPanel();
 p.setLayout(new FlowLayout(FlowLayout.RIGHT,5,5));
 p.add(new JLabel(“Cedula”));
 p.add(new JTextField(20));
 p.add(new JButton(“Haga clic aca”));
f.setSize(300,200);
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.getContentPane().add(p);
f.setVisible(true);
Al ejecutar el código anterior se obtiene la siguiente configuración gráfica. Adicionalmente 
se muestra la configuración que se obtiene al redimensionar la ventana.
Note como los componentes se encuentran un poco más espaciados y alineados hacia la 
derecha.
3.2.2. GridLayout
Este algoritmo asigna la ubicación y el tamaño a los componentes y se obtiene a partir de 
la instanciación de la clase java.awt.GridLayout. El algoritmo divide el área del JPanel 
en una cuadrícula o tabla cuyas dimensiones deben ser especificadas por el usuario. Los 
componentes gráficos que se añadan al JPanel se ubican en cada una de las celdas de esta 
cuadrícula y toman todo el espacio disponible de la celda. Considere el siguiente ejemplo:
JFrame f = new JFrame();
JPanel p = new JPanel();
 p.setLayout(new GridLayout(3,3));
 for (int i=1;i<10;i++)
 p.add(new JButton(i+””));
f.setSize(300,200);
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.getContentPane().add(p);
f.setVisible(true);
Al ejecutar el anterior código se obtiene la siguiente configuración gráfica. Adicionalmente 
se muestra la configuración que se obtiene al redimensionar la ventana.
3. CREACIÓN DE INTERFACES GRÁFICAS DE USUARIOS
212
Todo lo básico que debería saber sobre Programación orientada a objetos en Java
Note como cada uno de los componentes añadidos adquiere todo el espacio de la celda de 
la cuadrícula.
Este Layout posee los siguientes constructores:
•	 GridLayout(int numFilas, int numCols). Crea un nuevo objeto GridLayout, los 
parámetros que se reciben especifican la cantidad de filas y columnas que tendrá la 
cuadrícula.
•	 GridLayout(int numFilas, int numCols, int espacioHorizontal, int 
espacioVertical). Crea un nuevo objeto GridLayout, los parámetros que se reciben 
especifican la cantidad de filas y columnas, y, adicionalmente, la cantidad de pixeles 
que separarán a los componentes de forma horizontal y vertical.
•	 GridLayout(). Crea un nuevo objeto GridLayout por defecto de una fila con una 
columna.
Considere el siguiente código que presenta una pequeña modificación al ejemplo anterior:
JFrame f = new JFrame();
 JPanel p = new JPanel();
 p.setLayout(new GridLayout(3,3,5,5));
 for (int i=1;i<10;i++)
 p.add(new JButton(i+””));
f.setSize(300,200);
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.getContentPane().add(p);
f.setVisible(true);
213
Al ejecutar el código anterior se obtiene la siguiente configuración gráfica:
Note el espaciamiento entre las celdas de la cuadrícula.
3.2.3. BorderLayout
Este algoritmo asigna la ubicación y el tamaño de los componentes; se obtiene a partir de la 
instanciación de la clase java.awt.BorderLayout. El algoritmo divide el área del JPanel en 
cinco regiones: norte, sur, este, oeste y centro. El usuario puede utilizar cualquiera o todas 
estas regiones; cuando el usuario adicione un componente, debe especificar en cuál de las 
regiones desea hacerlo. Considere el siguiente ejemplo:
JFrame f = new JFrame();
 JPanel p = new JPanel();
 p.setLayout(new BorderLayout());
 p.add(new JButton(“Norte”), BorderLayout.NORTH);
 p.add(new JButton(“Sur”), BorderLayout.SOUTH);
 p.add(new JButton(“Este”),BorderLayout.EAST);
 p.add(new JButton(“Oeste”), BorderLayout.WEST);
 p.add(new JButton(“Centro”), BorderLayout.CENTER);
f.setSize(300,200);
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.getContentPane().add(p);
f.setVisible(true);
3. CREACIÓN DE INTERFACES GRÁFICAS DE USUARIOS
214
Todo lo básico que debería saber sobre Programación orientada a objetos en Java
Al ejecutar el anterior código se obtiene la siguiente configuración gráfica. Adicionalmente 
se muestra la configuración que se obtiene al redimensionar la ventana. Note cómo se 
especifica la región donde debe ubicarse un componente a través del uso de atributos 
estáticos y finales de la clase BorderLayout. 
Este algoritmo separa espacio de la ventana para los componentes que inicialmente se 
ubiquen en las regiones norte y sur, si sobra espacio se lo asigna a los componentes ubicados 
en las regiones oeste y este, y finalmente el espacio restante es asignado para la región 
central.
Este Layout posee los siguientes dos constructores:
•	 BorderLayout(). Crea un nuevo objeto BorderLayout.
•	 BorderLayout(int espacioHorizontal, int espacioVertical). Crea un nuevo 
objeto BorderLayout, los parámetros que se reciben especifican la cantidad de pixeles 
que separarán a los componentes de forma horizontal (espaciamiento entre los 
componentes ubicados en la región oeste, central y este) y vertical (espaciamiento 
entre los componentes ubicados en la región norte, central y sur).
Considere el siguiente código que presenta una pequeña modificación al ejemplo anterior:
215
JFrame f = new JFrame();
 JPanel p = new JPanel();
 p.setLayout(new BorderLayout(5,5));
 p.add(new JButton(“Norte”), BorderLayout.NORTH);
 p.add(new JButton(“Sur”), BorderLayout.SOUTH);
 p.add(new JButton(“Este”), BorderLayout.EAST);
 p.add(new JButton(“Oeste”), BorderLayout.WEST);
 p.add(new JButton(“Centro”), BorderLayout.CENTER);
f.setSize(300,200);
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.getContentPane().add(p);
f.setVisible(true);
Al ejecutar el código anterior se obtiene la siguiente configuración gráfica.
Note el espaciamiento de los componentes del Layout.
3. CREACIÓN DE INTERFACES GRÁFICAS DE USUARIOS
216
Todo lo básico que debería saber sobre Programación orientada a objetos en Java
sobre la Jerarquía de organizaCión de las Clases 
que representan a Componentes gráfiCos
Resulta muy provechoso conocer cómo están organizadas jerárquicamente las clases de los 
componentes gráficos en Java. La estructura es la que se muestra en el siguiente gráfico.
Object
Component
Container
add ( Component )
JComponent
listenerList:EventListenerListWindow
Frame
JFrame
JTextComponent AbstractButton
addActionListener( ActionListener )
JLabel JPanel
JTextField
addActionListener(ActionListener) JButton
Los rectángulos representan las clases, la línea que sale de una clase a otra que finaliza en una 
flecha blanca representa la relación de herencia (debe leerse de la siguiente manera: la clase 
Object es la superclase de Component, la cual a su vez es la superclase de Container, etc.) y 
la información que se presenta en algunos rectángulos corresponde a algún atributo o método 
que posee la clase y que es conveniente resaltar (según la definición de un atributo en el caso 
de la clase JComponent; en las restantes clases se da conforme a la definición de método).
De la anterior gráfica es conveniente resaltar los siguientes puntos:
•	 La clase JFrame extiende la clase Frame, que a su vez extiende a la clase Window, que a 
su vez extiende a la clase Container, que a su vez extiende a la clase Component. Esto 
significa que JFrame es un tipo de Frame, que a su vez es un tipo de Window, que a su 
vez es un tipo de Container, que a su vez es un tipo de Component. 
•	 Al extender el anterior análisis a las restantes clases, se puede inferir la siguiente 
situación: tanto JFrame, como JTextField, como JButton, como JLabel, como 
JPanel terminan siendo tipos de Component. 
217
•	 La clase Container de forma indirecta termina siendo superclase de JFrame, 
JTextField, JButton, JLabel y JPanel; por eso si la clase Container define el método 
add, que recibe como parámetro una referencia de tipo Component, todas las subclases 
restantes también lo poseen. 
•	 En la mayoría de códigos de ejemplos presentados en este capítulo para adicionar 
componentes gráficos a los JPanel se emplea el método add. Este método es 
precisamente el que gana JPanel de su superclase indirecta Container. Este método 
recibe como parámetro una referencia de tipo Component, es decir, que se puede 
adicionar en un JPanel cualquier objeto de una subclase de Component. Eso es lo que 
hace posible adicionar cualquier tipo de elemento gráfico (como un JLabel, JButton, 
JTextField, etc.) a un JPanel.
•	 El anterior análisis hace pensar en la posibilidad, totalmente válida, de poder incluir 
un JPanel en otro JPanel. Sacarle provecho a esta situación es lo que flexibiliza el 
desarrollo de las GUI atractivas visualmente.
•	 También podría pensarse en la posibilidad de incluir dentro de un JPanel un objeto de 
la clase JFrame (relación contraria a la que se ha trabajado en los ejemplos presentados). 
Este tipo de acciones aunque son válidas a nivel de la estructura jerárquica de las 
clases, son restringidas a nivel del código en el método add. Si se intenta efectuar una 
acción de este tipo, se generará el lanzamiento de una excepción.
3. CREACIÓN DE INTERFACES GRÁFICAS DE USUARIOS
218
Todo lo básico que debería saber sobre Programación orientada a objetos en Java
sobre la utilizaCión de varios layout 
La utilización de forma independiente de los Layout presentados anteriormente ayuda a un 
diseñador a crear interfaces gráficas sencillas, pero a la vez poco atractivas visualmente. Se 
mejora la calidad de las interfaces si se utilizan todos estos algoritmos de forma conjunta. El 
problema es que un JPanel sólo puede tener definido un único Layout. La solución pasa por 
definir la inclusión de un JPanel dentro de otro, y jugar en la definición de los Layouts para 
cada uno de estos.
A manera de ejemplo considere desarrollar una interfaz gráfica para la aplicación del cálculo 
de la nómina como la que se muestra a continuación. 
Obviamente, la anterior interfaz gráfica no puede construirse empleando un solo JPanel. El 
siguiente gráfico muestra la estructura de la solución; en ella se han utilizado 7 JPanel cada 
uno con su propio Layout. PanelPrincipal y PanelFormulario emplean un BorderLayout; 
mientras que PanelInferior, PanelEtiquetas y PanelCampos emplean un GridLayout (sus 
respectivas dimensiones pueden ser inferidas en la imagen); finalmente, PanelBotones1 y 
PanelBoton emplean un FlowLayout. El gráfico, además, presenta las regiones que se utilizan 
de cada BorderLayout y qué componente se inserta en cada una de ellas. En PanelBotones1, 
se añadirán tres botones para adicionar los diferentes tipos de empleados; en PanelBoton, se 
ubicará el botón para calcular la nómina de la empresa; en PanelEtiquetas, se ubicarán las 
etiquetas descriptivas de los campos; y en PanelCampos, se ubicarán los campos.
219
A continuación se presenta el código de la solución.
import java.awt.BorderLayout;
import java.awt.FlowLayout;
import java.awt.GridLayout;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JTextField;
public class PanelPrincipal extends JPanel {
 private JTextField txtCedula, txtApellido, txtNombre, txtSueldoXHora,
 txtHorasTrabajadas, txtSueldoBasico, txtPorcentaje,
 txtVentasTotales, txtSueldoFijo;
 private JButton btnAdicionarEmpleadoXHora,
 btnAdicionarEmpleadoXComision, btnAdicionarEmpleadoSueldoFijo,
3. CREACIÓN DE INTERFACES GRÁFICAS DE USUARIOS
220
Todo lo básico que debería saber sobre Programación orientada a objetos en Java
 btnCalcularNomina;
 public PanelPrincipal(){
 JPanel panelFormulario = new JPanel();
 JPanel panelEtiquetas= new JPanel();
 JPanel panelCampos = new JPanel();
 JPanel panelInferior = new JPanel();
 JPanel panelBotones1 = new JPanel();
 JPanel panelBoton = new JPanel();
 
 setLayout(new BorderLayout());
 add(panelFormulario, BorderLayout.NORTH);
 panelFormulario.setLayout(new BorderLayout(10,0));
 panelFormulario.add( panelEtiquetas, BorderLayout.WEST );
 panelFormulario.add( panelCampos, BorderLayout.CENTER);
 //-------------------------------------------------
 panelEtiquetas.setLayout(new GridLayout(9,1));
 panelEtiquetas.add(new JLabel(“Cédula”));
 panelEtiquetas.add(new JLabel(“Apellido”));
 panelEtiquetas.add(new JLabel(“Nombre”));
 panelEtiquetas.add(new JLabel(“Sueldo X Hora”));
 panelEtiquetas.add(new JLabel(“Horas Trabajadas”));
 panelEtiquetas.add(new JLabel(“Sueldo Básico”));
 panelEtiquetas.add(new JLabel(“Porcentaje”));
 panelEtiquetas.add(new JLabel(“Ventas Totales”));
 panelEtiquetas.add(new JLabel(“Sueldo Fijo”));
 //-------------------------------------------------
 panelCampos.setLayout(new GridLayout(9,1));
 panelCampos.add(txtCedula=new JTextField(“”));
 panelCampos.add(txtApellido=new JTextField(“”));
 panelCampos.add(txtNombre=new JTextField(“”));
 panelCampos.add(txtSueldoXHora=new JTextField(“”));
 panelCampos.add(txtHorasTrabajadas=new JTextField(“”));
 panelCampos.add(txtSueldoBasico=new JTextField(“”));
 panelCampos.add(txtPorcentaje=new JTextField(“”));
 panelCampos.add(txtVentasTotales=new JTextField(“”));
 panelCampos.add(txtSueldoFijo=new JTextField(“”));
 add(panelInferior, BorderLayout.SOUTH);
 panelInferior.setLayout(new GridLayout(2,1));
 panelInferior.add(panelBotones1);
 panelInferior.add(panelBoton);
 //-------------------------------------------------
 panelBotones1.setLayout(new FlowLayout());
 panelBotones1.add(btnAdicionarEmpleadoXHora=
 new JButton(“Adicionar Empleado X Hora”));
 panelBotones1.add(btnAdicionarEmpleadoXComision=
 new JButton(“Adicionar Empleado X Comision”));
 panelBotones1.add(btnAdicionarEmpleadoSueldoFijo=
 new JButton(“Adicionar Empleado Sueldo Fijo”));
221
 //-------------------------------------------------
 panelBoton.setLayout(new FlowLayout()); 
 panelBoton.add(btnCalcularNomina=
 new JButton(“Calcular Nomina”));
 
 }
}
JFrame f = new JFrame();
f.setSize(670,315);
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.getContentPane().add(new PanelPrincipal());
f.setVisible(true);
Sobre el anterior código es conveniente resaltar:
La clase PanelPrincipal extiende la clase JPanel, por ello adquiere toda su estructura y 
comportamiento.
PanelPrincipal define como atributos todos los elementos JTextField y JButton de las GUI. 
Más adelante, cuando en este capítulo se esté trabajando la asignación de comportamiento al 
presionar los botones, estos atributos serán realmente útiles.
•	 Las instrucciones para crear los componentes JTextField y JButton, y adicionarlas 
al JPanel respectivo se resumen en una instrucción, por ejemplo, panelCampos.
add(txtCedula=new JTextField(“”)); 
•	 Para poder lograr una separación entre PanelEtiquetas y PanelCampos se ha 
utilizado un constructor polimórfico de la clase BorderLayout, que recibe un valor 
para el espacio horizontal.
•	 La ventana que se obtiene al ejecutar el anterior código reajusta sus componentes al 
cambiar de tamaño. Al aumentar el tamaño de la ventana, se amplía también el espacio 
de los JTextField para facilitar el ingreso de información.
3.3. bOrdes
Las GUI desarrolladas que emplea la teoría presentada anteriormente son capaces de 
reajustar el tamaño y la posición de los componentes como respuesta a alteraciones en las 
dimensiones del JPanel. El solo hecho de poder especificar la separación que deben tener 
los componentes al interior de un JPanel mejora la apariencia gráfica, sin embargo, este 
espaciamiento no aplica para los límites exteriores del JPanel. Considere la gráfica que se 
muestra a continuación. Note cómo los óvalos señalan la situación antes mencionada.
3. CREACIÓN DE INTERFACES GRÁFICAS DE USUARIOS
222
Todo lo básico que debería saber sobre Programación orientada a objetos en Java
Lograr un espaciamiento en dichas áreas mejoraría aún más la apariencia gráfica de la 
ventana. Este puede lograrse a través de la utilización de bordes. Los bordes se utilizan no 
solamente para espaciar los componentes de las orillas, sino que también permiten dibujar 
líneas; sin embargo, en este libro solo se trabajarán los bordes como elementos para insertar 
espacio en márgenes externos de un JPanel16.
Un borde funciona de la manera como se muestra en el siguiente gráfico:
Suponga que el área que posee el color gris oscuro corresponde al área del JPanel, el área 
gris claro corresponde al área asignada para el borde.
Considere el siguiente ejemplo donde se trabaja con un borde:
JFrame f = new JFrame();
 JPanel p = new JPanel();
 p.setLayout(new GridLayout(1,1));
 p.add( new JButton() );
 p.setBorder( BorderFactory.createEmptyBorder(15,15,15,15) );
16 Mayor información sobre los restantes tipos de bordes y su utilización puede ser consultada en la siguiente referencia: 
http://java.sun.com/docs/books/tutorial/uiswing/components/border.html
223
f.setSize(300,200);
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.getContentPane().add(p);
f.setVisible(true);
La ejecución del anterior código genera la siguiente ventana:
Es conveniente resaltar en este ejemplo lo siguiente:
•	 Para establecerle un borde a un JPanel se emplea el método setBorder que posee 
esta clase que recibe como parámetro una referencia de tipo Border.
•	 La creación de un objeto Border no se hace a través de la invocación de un 
constructor; para hacerlo, se debe invocar un método estático de la clase javax.
swing.BorderFactory que permite crear el borde.
•	 El borde que se crea depende del método que se invoque, en el ejemplo se crea un 
borde vacío (EmptyBorder)17 utilizando el método createEmptyBorder. Este método 
recibe cuatro parámetros de tipo entero: el primero especifica el espaciamiento en la 
parte superior, el segundo especifica el espaciamiento hacia la izquierda, el tercero 
especifica el espaciamiento en la parte inferior y, finalmente, el cuarto especifica el 
espaciamiento en la parte derecha para el componente.
17 Mayor información sobre los métodos estáticos que pueden invocarse en la clase BorderFactory consulte la siguiente 
referencia: http://java.sun.com/javase/6/docs/api/javax/swing/BorderFactory.html
3. CREACIÓN DE INTERFACES GRÁFICAS DE USUARIOS
224
Todo lo básico que debería saber sobre Programación orientada a objetos en Java
aproximaCión a las gui para la apliCaCión del CálCulo de la nómina
En el apartado sobre la utilización de varios Layouts se propuso un prototipo a una GUI para 
la aplicación del cálculo de la nómina empleando programación orientada a objetos. El código 
que se presenta a continuación mejora la apariencia gráfica de la ventana adicionando espacios 
mediante el empleo de bordes.
import javax.swing.BorderFactory;
import java.awt.BorderLayout;
import java.awt.FlowLayout;
import java.awt.GridLayout;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JTextField;
public class PanelPrincipal extends JPanel {
 private JTextField txtCedula, txtApellido, txtNombre, txtSueldoXHora,
 txtHorasTrabajadas, txtSueldoBasico, txtPorcentaje,
 txtVentasTotales, txtSueldoFijo;
 private JButton btnAdicionarEmpleadoXHora,
 btnAdicionarEmpleadoXComision, btnAdicionarEmpleadoSueldoFijo,
 btnCalcularNomina;
 public PanelPrincipal(){
 JPanel panelFormulario = new JPanel();
 JPanel panelEtiquetas = new JPanel();JPanel panelCampos = new JPanel();
 JPanel panelInferior = new JPanel();
 JPanel panelBotones1 = new JPanel();
 JPanel panelBoton = new JPanel();
 
 setLayout(new BorderLayout());
 add(panelFormulario, BorderLayout.NORTH);
 panelFormulario.setLayout(new BorderLayout(10,0));
 panelFormulario.setBorder(
 BorderFactory.createEmptyBorder(15,20,0,20) );
 panelFormulario.add( panelEtiquetas, BorderLayout.WEST );
 panelFormulario.add( panelCampos, BorderLayout.CENTER);
 //-------------------------------------------------
 panelEtiquetas.setLayout(new GridLayout(9,1));
 panelEtiquetas.add(new JLabel(“Cédula”));
 panelEtiquetas.add(new JLabel(“Apellido”));
 panelEtiquetas.add(new JLabel(“Nombre”));
 panelEtiquetas.add(new JLabel(“Sueldo X Hora”));
 panelEtiquetas.add(new JLabel(“Horas Trabajadas”));
 panelEtiquetas.add(new JLabel(“Sueldo Básico”));
 panelEtiquetas.add(new JLabel(“Porcentaje”));
225
 panelEtiquetas.add(new JLabel(“Ventas Totales”));
 panelEtiquetas.add(new JLabel(“Sueldo Fijo”));
 //-------------------------------------------------
 panelCampos.setLayout(new GridLayout(9,1));
 panelCampos.add(txtCedula=new JTextField(“”));
 panelCampos.add(txtApellido=new JTextField(“”));
 panelCampos.add(txtNombre=new JTextField(“”));
 panelCampos.add(txtSueldoXHora=new JTextField(“”));
 panelCampos.add(txtHorasTrabajadas=new JTextField(“”));
 panelCampos.add(txtSueldoBasico=new JTextField(“”));
 panelCampos.add(txtPorcentaje=new JTextField(“”));
 panelCampos.add(txtVentasTotales=new JTextField(“”));
 panelCampos.add(txtSueldoFijo=new JTextField(“”));
 add(panelInferior, BorderLayout.SOUTH);
 panelInferior.setLayout(new GridLayout(2,1));
 panelInferior.setBorder(BorderFactory.createEmptyBorder(0,0,5,0));
 panelInferior.add(panelBotones1);
 panelInferior.add(panelBoton);
 //-------------------------------------------------
 panelBotones1.setLayout(new FlowLayout());
 panelBotones1.add(btnAdicionarEmpleadoXHora=
 new JButton(“Adicionar Empleado X Hora”));
 panelBotones1.add(btnAdicionarEmpleadoXComision=
 new JButton(“Adicionar Empleado X Comision”));
 panelBotones1.add(btnAdicionarEmpleadoSueldoFijo=
 new JButton(“Adicionar Empleado Sueldo Fijo”));
 //-------------------------------------------------
 panelBoton.setLayout(new FlowLayout()); 
 panelBoton.add(btnCalcularNomina=
 new JButton(“Calcular Nomina”));
 
 }
}
JFrame f = new JFrame();
f.setSize(670,315);
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.getContentPane().add(new PanelPrincipal());
f.setVisible(true);
3. CREACIÓN DE INTERFACES GRÁFICAS DE USUARIOS
226
Todo lo básico que debería saber sobre Programación orientada a objetos en Java
La ejecución del anterior código genera la siguiente interfaz gráfica:
Del código es conveniente resaltar las dos instrucciones que se han agregado para definirles 
bordes a los paneles PanelInferior y PanelFormulario.
3.4. mAnejO de eventOs
La información presentada sobre el diseño y desarrollo de GUI hasta el momento sólo ha 
permitido definir cómo será la presentación gráfica de la misma y cómo deben reajustarse 
los componentes ante cambios en el tamaño de la ventana, pero no se ha efectuado la 
definición de qué instrucciones deben ejecutarse cuando se presione un determinado botón. 
Esta sección presentará toda la teoría necesaria para poder tratar dicha situación.
Así se debe estar al tanto de cuándo un determinado botón es presionado por el usuario. 
Existen básicamente dos alternativas para realizar esto:
•	 Crear un objeto que periódicamente esté chequeando el estado del botón para saber 
si ha sido presionado o no.
•	 Que el propio botón le avise a un objeto cuando el usuario lo haya presionado18.
Obviamente, esta segunda opción corresponde a una mejor alternativa de solución.
18 Esta alternativa de solución guarda relación con el propósito definido en el patrón de diseño observador (Observer).
227
sobre el patrón de diseño observador 
La idea general detrás del patrón observador puede ser entendida a través del siguiente ejemplo: 
suponga que en una pareja de novios la chica desea estar enterada de cualquier suceso o evento 
que le ocurra a su novio (al igual que sucede con los botones en las GUI); suponga, además, 
que la pareja cuenta con teléfonos celulares que posibilitan y facilitan la intercomunicación.
La alternativa de que la chica esté llamando continuamente a su novio (a través del teléfono 
celular), obviamente, no es una muy buena idea. Suponga, que la chica establece 24 horas 
como período de tiempo entre llamadas, en ese lapso de tiempo puede que al chico le ocurran 
muchas cosas de las cuales la novia no se enteraría. Sí ella decide cambiar el tiempo entre 
llamadas a 12, o 6, o 3, o 1 hora, aun así podrían ocurrirle varios eventos al chico en ese tiempo 
de los cuales ella no se enteraría. Al llevar esta situación al extremo, ella puede optar por llamar 
cada 30 minutos, o cada 10 minutos, esto solo conllevaría a saturar el canal de comunicación 
y en últimas hasta a fastidiar al novio con semejante intensidad. La mejor alternativa es que el 
novio llame a su chica cada vez que le ocurra algo fuera de lo común. 
La situación podría complejizarse aún más si se añaden al problema la mamá del novio (otra 
persona interesada en lo que le suceda al muchacho) y, además, el conductor del bus que 
ocasionalmente toma el novio para ir a la universidad (este último no tiene relación alguna con 
el chico y, por ende, no se encuentra interesado en lo que a él le suceda).
Para que todo este mecanismo suceda se necesita que previamente tanto la mamá como la 
novia del muchacho le expresen explícitamente su deseo de que este les avise cualquier suceso 
que le ocurra. El novio debe anotar en un listado los números telefónicos de estas dos personas 
para poder comunicarse con ellas. Es obvio que simplemente no puede informarles a todos los 
objetos los eventos que le ocurran porque existen entes que no se encuentran interesados en 
ellos (por ejemplo, el conductor del bus).
Luego de haberse registrado la dependencia hay que esperar que algún evento extraordinario 
le pase al muchacho para que este les dé aviso a las personas interesadas llamándolas a todas 
a los teléfonos que tenga registrados en su listado.
La aplicación de esta situación en objetos requiere inicialmente que ellos mismos, como 
interesados en lo que le suceda al botón, se suscriban en calidad de observadores (también 
llamados escuchas). Posteriormente, si algo le pasa al botón, este deberá avisarles a todos los 
objetos que se han suscrito como escuchas; pero, para poder lograrlo en el mundo de los
3. CREACIÓN DE INTERFACES GRÁFICAS DE USUARIOS
228
Todo lo básico que debería saber sobre Programación orientada a objetos en Java
objetos es necesaria la invocación de un método. A fin de garantizar la invocación de un método 
común a todos los escuchas se hace necesario que estos sean objetos de clases que implementen 
una interface específica, todo esto porque puede haber escuchas de diversos tipos de clases. 
Generalmente, estas interfaces definen en los métodos a implementar parámetros donde se 
detalla el evento ocurrido.
En Java, para que un objeto se pueda declarar como interesado en conocer los eventos 
que le ocurren a un botón, su clase debe implementar la interface java.awt.event.
ActionListener, y debe suscribirse explícitamente como escucha del botón, empleando el 
método addActionListener de la clase JButton. Esta interface se encuentra definida como se 
muestra en el siguiente código:
public interface ActionListener{
 public void actionPerformed(ActionEvent e);
}
La interface solo posee un método que se ejecutará cuando se presione

Mais conteúdos dessa disciplina