Documentos de Académico
Documentos de Profesional
Documentos de Cultura
1. INTRODUCCIÓN 3
2. Encapsulación4
EJERCICIO 1.................................................................................................... 6
3. Sobrecarga de métodos 10
4. Herencia 11
EJERCICIO 2.................................................................................................. 18
5. Sobrescritura de métodos 20
EJERCICIO 3.................................................................................................. 21
7. Polimorfismo 26
EJERCICIO 4.................................................................................................. 28
1. INTRODUCCIÓN
Java es un lenguaje de programación totalmente orientado a objetos, lo que significa
que todos los conceptos definidos por este paradigma de programación son aplicables a este
lenguaje.
Encapsulación
Sobrecarga de métodos
Herencia
Sobreescritura de métodos
Polimorfismo
Para poder afrontar con garantías este tema, es necesario haber estudiado
previamente el apartado “Clases y objetos” desarrollado en el tema anterior y comprender los
conceptos tratados en el mismo.
2. ENCAPSULACIÓN
La encapsulación, aplicada al contexto de creación de una clase, es un concepto que
se basa en mantener aislados del exterior los datos miembro o atributos de la clase. Para ello,
dichos atributos se definen con el modificador private, permitiendo que el acceso a los mismos
solo pueda realizarse desde el interior de la clase.
objeto_coche.potencia=-10;
getNombre_propiedad
setNombre_propiedad
2.2. JAVABEANS
El siguiente listado nos muestra un ejemplo de una clase JavaBean que encapsula la
información asociada a una cuenta bancaria:
En este listado de ejemplo observamos el uso de uno de las palabras reservadas del
lenguaje Java; se trata de this y representa una referencia al objeto actual sobre el que se está
ejecutando el código. En este ejemplo, se utiliza para acceder a los atributos del propio objeto,
si bien resulta redundante en este caso pues podría referirse al atributo desde el interior de la
clase simplemente utilizando el nombre de éste:
codigo_cuenta=codigo;
aunque en el caso del atributo saldo si resulta necesario su uso, pues su nombre
coincide con el del parámetro:
this.saldo=saldo;
Otro aspecto importante a tener en cuenta con los JavaBeans es el hecho de que
resulta conveniente que dispongan de un constructor sin parámetros. Aunque no es un
requerimiento de obligado cumplimiento para la compilación y utilización de la clase, si resulta
imprescindible cuando esta va a ser manejada por algún framework.
EJERCICIO 1
En este primer ejercicio vamos a implementar una nueva versión del programa
desarrollado en el ejercicio 11 del tema anterior, consistente en la gestión de una lista de
contactos.
Para esta nueva versión, los contactos estarán caracterizados por un nombre, teléfono,
dirección de correo electrónico y DNI, utilizándose además este último dato como clave
asociada a cada contacto en la colección.
2. Eliminar contacto
3. Mostrar contactos
4. Salir
Lo primero será crear JavaBean al que llamaremos Contacto y que agrupará los cuatro
campos relativos a un contacto. El código de esta clase será el que se indica en el siguiente
listado:
package javabeans;
public Contacto(){
}
public Contacto(String nombre, long telefono, String email,
int dni){
this.nombre=nombre;
this.telefono=telefono;
this.email=email;
this.dni=dni;
}
public String getNombre() {
return nombre;
}
La colección Hashtable utilizada para almacenar los contactos deberá ser creada en
este ejercicio para un tipo específico Contacto. El siguiente listado corresponde a la nueva
versión de la clase de gestión de contactos:
import java.util.*;
import javabeans.*;
public class GestionContactos {
public static void main(String[] args) {
Hashtable<Integer,Contacto> lista= new
Hashtable<Integer,Contacto>();
int opcion;
Scanner sc=new Scanner(System.in);
sc.useDelimiter("\n");
do{
System.out.println("1. Añadir contacto");
System.out.println("2. Eliminar contacto");
System.out.println("3. Mostrar contactos");
System.out.println("4. Salir");
opcion=sc.nextInt();
String nombre, email;
Integer dni;
long telefono;
switch(opcion){
case 1:
System.out.println("Introduce Nombre: ");
nombre=sc.next();
System.out.println("Introduce emai: ");
email=sc.next();
System.out.println("Introduce telefono: ");
telefono=sc.nextLong();
System.out.println("DNI: ");
dni=sc.nextInt();
Contacto c=new Contacto(nombre,telefono,email,dni);
guardarContacto(c,dni,lista);
break;
case 2:
3. SOBRECARGA DE MÉTODOS
La sobrecarga de métodos consiste en la posibilidad de definir más de un método con
el mismo nombre dentro de una clase. La sobrecarga de métodos simplifica la utilización de
la clases por parte de los programadores puesto que permite disponer de distintas versiones de
una operación respetando el mismo nombre de método en todas ellas.
Métodos
valueOf()
Figura. 1.
4. HERENCIA
La herencia es quizá la característica más interesante y potente que ofrecen los
lenguajes orientados a objetos. Mediante ella, es posible crear clases que dispongan de forma
automática de todos los miembros (atributos y métodos) definidos en clases ya existentes.
Esto es particularmente útil en aquellos contextos donde necesitamos utilizar una clase
con los métodos incluidos en otra ya existente, pero a la que queremos añadir una nueva
funcionalidad; en vez de modificar la clase original, emplearemos la herencia para crear una
nueva clase con todos los métodos definidos en la primera y sobre ella incluir los nuevos
elementos que se necesiten.
subclase
Figura. 2.
Puede darse el caso también que una clase herede a otra clase que a su vez herede a
otra, es lo que se llama la herencia multinivel (figura 3).
ClaseA
ClaseB
ClaseC
Figura. 3.
En este caso, ClaseC heredaría todos los métodos definidos en ClaseA más todos
aquellos que hayan sido definidos en ClaseB.
Aunque hay lenguajes orientados a objetos como C++ donde si es posible utilizarla,
Java no permite la herencia múltiple, esto es, la posibilidad de heredar dos o más clases
diferentes (figura 4).
Clase1 Clase2
ClaseX
Figura. 4.
Deportista Figura
Informático
Programador TecnicoSistemas
Figura. 5.
En todas ellos, las superclases incluirían los miembros (atributos y métodos) generales
comunes a determinadas familias de objetos, añadiendo las subclases los métodos y atributos
específicos de cada tipo particular. Esto se muestra en otro nuevo ejemplo de clases
relacionadas mediante herencia que se muestra en la figura 6, donde la clase Vehiculo incluye
atributos y métodos que son comunes a todo tipo de vehículo, como el color o el numero de
ruedas de este, mientras que Coche añade características que son propias solamente de este
tipo de vehículos, tales como la potencia o las operaciones de acelerado y frenado.
Vehiculo
Atributos:
numeroRuedas
color
Métodos:
arrancar()
detener()
Coche
Atributos:
potencia
Métodos:
acelerar()
frenar()
cambiarDireccion()
Figura. 6.
Cuando creamos un clase Java y queramos que esta clase herede a otra existente,
debemos utilizar la palabra reservada extends en la definición de la misma, indicando a
continuación el nombre de la clase a heredar:
Por ejemplo:
Hay que indicar que a pesar de que la subclase hereda todos los miembros definidos
en la superclase, los miembros que estén definidos dentro de ésta como private no serán
accesibles directamente desde la subclase. Así, si tenemos la siguiente definición de la clase
Vehiculo:
class Vehiculo{
private String color;
:
}
Esto no es ni mucho menos una limitación puesto que, además de los métodos set/get
que suelen proporcionar las clases para el acceso controlado a ciertos de sus atributos, las
superclases pueden realizar de manera indirecta desde su constructor la inicialización de los
atributos privados de la superclase, tal y como veremos en la siguiente sección.
No obstante, es posible definir un atributo o método dentro de una clase de manera que
sea accesible únicamente desde el interior de su clase y de sus subclases. Para ello, se
utilizará el modificador protected en la definición del miembro:
class Vehiculo{
private String color;
protected void detener(){
:
}
:
}
Ninguna otra clase que no sea subclase de Vehiculo tendrá acceso al método
protegido, aunque Vehiculo esté declarada como pública o aunque ambas clases estén en el
mismo paquete:
class Prueba{
public void método(){
//la siguiente instrucción compila si Vehiculo
//es visible desde prueba
Vehiculo v = new Vehiculo();
//la siguiente instrucción no compilaría en ningún caso:
v.frenar();
}
}
Cuando se compila una clase Java, el compilador introduce en todos los constructores
de la clase, como primera línea de código, la siguiente instrucción:
super();
Por ejemplo, supongamos que tenemos definidas las clases Vehiculo y Coche de la
siguiente manera:
class Prueba{
public static void main(String [] args){
Coche c=new Coche(100);
System.out.println(“color “+c.getColor());
System.out.println(“potencia “+c.getPotencia());
}
}
color verde
potencia 100
Lo anterior debe ser tenido en cuenta a la hora de crear una clase que herede otra
clase, ya que si la superclase no tiene definido (explícita o implícitamente) un constructor sin
parámetros no será posible crear un objeto de la subclase.
super(lista_parametros)
EJERCICIO 2
Vamos a crear en este ejercicio un sencillo programa que opere sobre una cuenta
bancaria de tipo vivienda. El núcleo del programa estará formado por una clase que defina los
atributos y operaciones sobre la cuenta.
En primer lugar definiremos una clase base con los atributos y métodos comunes a
cualquier tipo de clase a la que llamaremos Cuenta. El código de dicha clase se muestra en el
siguiente listado:
package banco;
public class Cuenta {
private float saldo;
private String codigo;
public Cuenta(float saldo, String codigo){
this.saldo=saldo;
this.codigo=codigo;
}
public float getSaldo(){
return saldo;
}
public String getCodigo(){
return codigo;
}
public void ingresar(float c){
saldo+=c;
}
public void extraer(float c){
saldo-=c;
}
}
La clase sobre la que se realizarán las operaciones será una subclase de esta a la que
llamaremos CuentaVivienda. Dicha clase añadirá un atributo llamado limite que represente el
número máximo de años de vida de la cuenta, así como un método llamado ampliarLimite() que
permita ampliar la vida de la cuenta si el saldo está por debajo de una determinada cantidad.
import banco.*;
public class Principal {
public static void main(String[] args) {
CuentaVivienda cv=new CuentaVivienda(3, 200, "28900");
cv.ingresar(200);
System.out.println("Saldo después de ingreso: "+
cv.getSaldo());
cv.extraer(100);
System.out.println("Saldo después de extracción: "+
cv.getSaldo());
cv.ampliarLimite(2);
System.out.println("Nuevo límite: "+cv.getLimite());
}
}
Así pues, la clase Object proporciona métodos básicos que son aplicables a cualquier
clase, como:
El objetivo de incluir estos métodos en la clase Object es forzar a que todas las clases
dispongan de estos métodos, pero dado que la versión de estos métodos definida en Object
puede no ser adecuada en toda las clases, será recomendable que cada nueva clase que se
cree sobrescriba estos métodos a fin de adaptarlos a sus requerimientos.
5. SOBRESCRITURA DE MÉTODOS
En determinados contextos de herencia, no todos los métodos heredados por la
subclase se ajustan a los requerimientos de la misma. Puede ocurrir que alguno de los
métodos heredados deba ser redefinido en la nueva clase para poder cumplir mejor con su
funcionalidad.
Por ejemplo, podría ocurrir que las cuentas viviendas tuvieran un saldo máximo
alcanzado el cual no se pudieran realizar más ingresos en las cuentas. En este caso, el método
ingresar(), tal y como está definido en la clase Cuenta, no sería válido para la clase
CuentaVivienda, por lo que tendría que volver a definirse en la misma.
una posible superclase podría sobrescribir dicho método con el siguiente formato:
Como vemos, la nueva versión del método está definida como public, que es menos
restrictivo que el acceso de paquete (por defecto cuando no se utiliza modificador) definido en
la superclase.
Si la subclase define el método ejemplo() de la siguiente manera:
EJERCICIO 3
En este caso, el método ingresar() heredado de la clase Cuenta no será válido, por lo
que tendremos que volver a definirlo para que compruebe el saldo actual antes de realizar el
ingreso.
package banco;
public class CuentaVivienda extends Cuenta{
private int limite;
private float saldoMaximo;
public CuentaVivienda(int limite, float saldoMaximo,
float saldo, String codigo){
super(saldo, codigo);
this.limite=limite;
this.saldoMaximo=saldoMaximo;
}
public int getLimite(){
return limite;
}
public void ampliarLimite(int n){
if(getSaldo()<10000){
limite+=n;
}
}
public void ingresar(float cantidad){
if((getSaldo()+cantidad)<=saldoMaximo){
super.ingresar(cantidad);
}
}
}
Un clase abstracta es una clase en la que alguno de sus métodos se encuentra sin
definir. Es decir, se ha declarado su existencia pero no se ha realizado su implementación.
Por ejemplo, si tenemos una clase llamada FiguraGeometrica con los miembros
comunes a cualquier figura geométrica, uno de esos miembros podría ser el método
calculoArea() encargado de calcular el área de una figura geométrica. Este método estaría
declarado en la clase FiguraGeometrica porque sabemos que todas las figuras lo deben tener,
pero no podríamos implementarlo porque desconocemos la forma de hacerlo, ya que esto
depende del tipo de figura en cuestión.
A los métodos que se declaran en una clase pero no se implementan se les llama
métodos abstractos y se declaran indicando el modificador abstract delante del tipo de
devolución. Así mismo, cuando una clase contiene un método abstracto deberá ser declarada
también con el modificador abstract. El siguiente código de ejemplo corresponde a una posible
implementación de la clase FiguraGeometrica:
Dado que las clases abstractas disponen de métodos que no están definidos, no es
posible crear objetos de una clase abstracta. Por ejemplo, la siguiente instrucción no
compilaría:
Así pues, cuando una clase abstracta es heredada por otra clase, la nueva clase está
obligada a sobrescribir todos los métodos abstractos definidos en la clase abstracta, de no
hacerlo tendrá que ser declarada también como abstracta.
Por ejemplo, una posible clase de figura geométrica de tipo esfera podría ser definida a
partir de FiguraGeometrica de la siguiente manera:
Vemos como el método calculoArea(), al ser sobrescrito por la clase Esfera y dejar de
ser abstracto ya no tiene que ser declarado con el modificador abstract.
6.2. INTERFACES
interface Nombre_interfaz{
tipo nombre_metodo1(parámetros);
tipo nombre_metodo2(parámetros);
El siguiente ejemplo corresponde a la definición de una interfaz que declara una serie
de métodos para la realización de movimientos:
interface Movimientos{
void giro (int direccion);
void elevacion();
void descenso();
}
Para obligar a una clase a codificar los métodos de la interfaz respetando el formato
establecido, dicha clase deberá implementar la interfaz, lo cual debe ser indicado durante la
definición de la clase utilizando la palabra implements seguido del nombre de la interfaz:
Dado que una interfaz es tan sólo declara una serie de métodos abstractos, es posible
definir una clase que herede otra clase existente y al mismo tiempo implemente una
interfaz:
class Esfera extends FiguraGeometrica implements Movimientos{
:
}
Las interfaces pues nos proporcionan una enorme flexibilidad a la hora de establecer el
formato universal de determinados métodos, pues dicho formato podrá ser seguido no solo por
las clases de una determinada jerarquía, como sucedería en el caso de que los hubiéramos
declarado en una clase abstracta, sino por cualquier clase Java herede a quién herede.
Pero no solamente una clase puede heredar otra clase e implementar una interfaz al
mismo tiempo, también puede implementar cualquier número de interfaces. Si Interfaz1 e
Interfaz2 son los nombres de dos interfaces existentes, para definir una clase que implemente
las dos interfaces sería:
Para indicar que una interfaz hereda a otra interfaz se utiliza, como en el caso de las
clases, la palabra extends:
interface MovimientoPlano{
void giro (int direccion);
}
Además, disponemos de dos clases llamadas Clase1 y Clase2 que heredan Principal y
proporcionan cada una de ellas su propia implementación de metodoEjemplo():
En nuestro ejemplo, podríamos utilizar una variable de tipo Principal para almacenar
objetos de las clases Clase1 y Clase2 y utilizar esa variable para invocar a las dos versiones
del método metodoEjemplo() definidas en cada clase:
Principal pr;
pr=new Clase1()
pr.MetodoEjemplo() 'versión del método definida en Clase1
pr=new Clase2()
pr.MetodoEjemplo() 'versión del método definida en Clase2
Como se puede ver, las dos instrucciones utilizadas para llamar a las dos versiones del
método se representan mediante una misma expresión. Es esta precisamente la potencia del
polimorfismo: utilización de una misma expresión para llamar a distintas versiones de un mismo
método.
En el ejemplo anterior sólo se ha aplicado el polimorfismo para invocar a un único
método, por lo que no se aprecia del todo su potencia. Pero imaginemos que además de
metodoEjemplo, la clase Principal proporciona otros dos métodos (abstractos o no) llamados
“metodo1” y “metodo2”. En este caso, y suponiendo que quisiéramos llamar a los tres métodos
para los objetos de las dos subclases de Principal, el código quedaría:
Principal pr;
pr=new Clase1()
pr.MetodoEjemplo()
pr.metodo1();
pr.metodo2();
pr=new Clase2()
pr.MetodoEjemplo()
pr.metodo1();
pr.metodo2();
package figuras;
import static java.lang.Math.*;
public class Cilindro extends FiguraGeometrica{
private int radio;
private int altura;
public Cilindro(){
super(3);
radio=1;
altura=1;
}
public Cilindro(int dimension, int radio, int altura){
super(dimension);
this.radio=radio;
this.altura=altura;
}
public float calculoArea(){
float areaBase=(float)(PI*radio*radio);
float areaCuerpo=(float)(2*PI*radio*altura);
return areaBase+areaCuerpo;
}
public String toString(){
return "figura de tipo Cilindro";
}
package figuras;
import static java.lang.Math.*;
public class Esfera extends FiguraGeometrica{
private int radio;
public Esfera(){
super(3);
this.radio=1;
}
public Esfera (int dimension, int radio){
super(dimension);
this.radio=radio;
}
public float calculoArea(){
return (float)(4*PI*radio*radio);
}
public String toString(){
return "figura de tipo esfera";
}
}
Finalmente, la clase que realiza la instanciación de los objetos y las llamadas a los
métodos de forma polimórfica quedará como se indica a continuación:
import figuras.*;
public class ManejaFiguras {
public static void main(String[] args) {
//crea un objeto Esfera cualquiera
volcarDatos(new Esfera(5,4));
//crea un objeto Cilindro cualquiera
volcarDatos(new Cilindro(3,6,10));
}
//polimorfismo
private static void volcarDatos(FiguraGeometrica fg){
System.out.println(fg.toString());
System.out.println("Dimensión: "+fg.getDimension());
System.out.println("Área: "+fg.calculoArea());
}
}
AUTOEVALUACIÓN
1. Para indicar que una clase debe heredar a otra ya existente se utiliza la palabra
reservada:
A. implements
B. inherits
C. extends
D. inner
Indica cual de los siguientes métodos no podría estar definido también en dicha
clase:
A. int test ()
C. void test()
A. La nueva versión del método debe tener exactamente los mismos parámetros
que la original
class ClaseUno{
public ClaseUno(){
System.out.println(“Primer constructor”);
public ClaseDos(){
A. Se mostrarán en pantalla:
Primer constructor
B. Se mostrarán en pantalla:
Segundo constructor
C. Se mostrarán en pantalla:
Primer constructor
D. El programa no compilará
class ClaseUno{
public ClaseUno (int n){
System.out.println(“Segundo constructor”);
public ClaseDos(){
A. Se mostrarán en pantalla:
Segundo constructor
B. Se mostrarán en pantalla:
Segundo constructor
C. Se mostrarán en pantalla:
D. El programa no compilará
6. Indica cual de las siguientes afirmaciones sobre las clases abstractas es correcta
C. ClaseDos cd = ClaseTres;
1. Desarrollar una nueva versión del programa propuesto en el ejercicio 5 del tema
anterior, encargado de la gestión de una base de datos de productos.
2. Crear una clase llamada Empleado que tenga los siguientes miembros:
¾ cuota_salarial. Valor de tipo float que sirve para calcular el salario mensual
del empleado
antiguedad*cuota_salarial+numero_clientes*10
Se debería crear después una clase con un método main() que probara el
funcionamiento de la clase Gerente, creando un objeto de esta, añadiéndole
clientes y mostrando el salario asociado al Gerente.
3. Desarrollar una interfaz llamada Dibujo que disponga de un único método llamado
pintar() que no reciba ningún parámetro y su tipo de devolución sea void. A
continuación, crearemos dos clases una llamada Punto que encapsule las
coordenadas de un punto de tres dimensiones y otra llamada Linea caracterizada
por una longitud y un ángulo de inclinación. Ambas clases implementarán la
interfaz Dibujo, proporcionando cada una de ellas su versión del método pintar()
que en definitiva tiene como misión mostrar en pantalla los valores de los datos
miembro del objeto.