miércoles, 26 de octubre de 2016

Clases y objetos 

 En la programación orientada a objetos, POO, el problema a resolver se modela mediante componentes de código llamados objetos que son abstracciones de los objetos, animados e inanimados, del mundo real. Una abstracción es una representación parcial de los atributos y comportamiento de un objeto real. Los atributos son las características que definen al objeto y el comportamiento representa lo que el objeto sabe hacer, su funcionalidad. El comportamiento de un objeto es modelado por piezas de código llamados métodos. Los atributos y comportamiento representados por el objeto son los que son relevantes al problema que se está modelando. Una clase es la clasificación de los objetos que son relevantes para representar el problema en grupos de objetos que compartan el mismo tipo de atributos y métodos. La lista de atributos y métodos de cada grupo de objetos (un grupo podría estar formada por un sólo objeto) se conoce como una clase, esto es, una clase son los atributos y métodos comunes a un grupo de objetos. Una clase constituye una plantilla con la que se construyen objetos.

 Atributos

 Los atributos son las características individuales que diferencian un objeto de otro y determinan su apariencia, estado u otras cualidades. Los atributos se guardan en variables denominadas de instancia, y cada objeto particular puede tener valores distintos para estas variables. Las variables de instancia también denominados miembros dato, son declaradas en la clase, pero sus valores son fijados y cambiados en el objeto. Además de las variables de instancia hay variables de clase, las cuales se aplican a la clase y a todas sus instancias. Por ejemplo, el número de ruedas de un automóvil es el mismo cuatro, para todos los automóviles.

Métodos 

 El método o el comportamiento de los objetos de una clase se implementa mediante funciones miembro o métodos. Un método es un conjunto de instrucciones que realizan una determinada tarea y son similares a las funciones de los lenguajes estructurados. Del mismo modo que hay variables de instancia y de clase, también hay métodos de instancia y de clase. En el primer caso, un objeto llama a un método para realizar una determinada tarea, en el segundo, el método se llama desde la propia clase.

Los constructores 

Un objeto de una clase se crea llamando a una función especial denominada constructor de la clase. El constructor se llama de forma automática cuando se crea un objeto, para situarlo en memoria e inicializar los miembros dato declarados en la clase. El constructor tiene el mismo nombre que la clase. Lo específico del constructor es que no tiene tipo de retorno.

 class Rectangulo{

 int x;
 int y;
 int ancho;
 int alto;
 Rectangulo(int x1, int y1, int w, int h){
 x=x1;
 y=y1;
 ancho=w;
 alto=h;
 }
}

 El constructor recibe cuatro números que guardan los parámetros x1, y1, w y h, y con ellos inicializa los miembros dato x, y, ancho y alto. Una clase puede tener más de un constructor. Por ejemplo, el siguiente constructor crea un rectángulo cuyo origen está en el punto (0, 0).

class Rectangulo{
 int x;
 int y;
 int ancho;
 int alto;
   Rectangulo(int w, int h){
 x=0;
 y=0;
 ancho=w;
 alto=h;
   }
} 


Este constructor crea un rectángulo de dimensiones nulas situado en el punto (0, 0).


 class Rectangulo{
 int x;
 int y;
 int ancho;
 int alto;
 Rectangulo(){
 x=0;
 y=0;
 ancho=0;
 alto=0;
 }
}

Modelado UML 

El término “lenguaje” ha generado bastante confusión respecto a lo que es UML. En realidad, el término lenguaje quizás no es el más apropiado, ya que no es un lenguaje propiamente dicho, sino una serie de normas y estándares gráficos respecto a cómo se deben representar los esquemas relativos al software. Mucha gente piensa por confusión que UML es un lenguaje de programación y esta idea es errónea: UML no es un lenguaje de programación. Como decimos, UML son una serie de normas y estándares que dicen cómo se debe representar algo. UML es una herramienta propia de personas que tienen conocimientos relativamente avanzados de programación y es frecuentemente usada por analistas funcionales (aquellos que definen qué debe hacer un programa sin entrar a escribir el código) y analistas-programadores (aquellos que, dado un problema, lo estudian y escriben el código informático para resolverlo en un lenguaje como Java, C#, Python o cualquier otro). Por tanto, si estás dando tus primeros pasos en programación, te recomendaríamos que te olvides de UML hasta que tengas unos conocimientos mínimos como uso de condicionales, bucles, y conocimiento de la programación orientada a objetos. Esto es solo una recomendación, en realidad prácticamente cualquier persona puede usar UML, incluso podría usarse para realizar esquemas o documentación de procesos que no tengan que ver con la informática.



 Modelo de clases en UML

Un diagrama de clases sirve para visualizar las relaciones entre las clases que involucran el sistema, las cuales pueden ser asociativas, de herencia, de uso y de contenimiento.

Un diagrama de clases este compuesto por los siguientes elementos:
Clase: atributos, métodos y visibilidad.
Relaciones: Herencia, Composición, Agregación, Asociación y Uso.

Elementos: 

 Clase.-    Es la unidad básica que encapsula toda la información de un Objeto (un objeto es una instancia de una clase). A través de ella podemos modelar el entorno en estudio (una Casa, un Auto, una Cuenta Corriente, etc.). En UML, una clase es representada por un rectángulo que posee tres divisiones:


   En donde: 

  • Superior: Contiene el nombre de la Clase 
  • Intermedio: Contiene los atributos (o variables de instancia) que caracterizan a la Clase (pueden ser private, protected o public). 
  • Inferior: Contiene los métodos u operaciones, los cuales son la forma como interactúa el objeto con    su entorno (dependiendo de la visibilidad: private, protected o public). 


   Ejemplo: 
   Una Cuenta Corriente que posee como característica:

  • Balance 

Puede realizar las operaciones de:

  • Depositar 
  • Girar 
  • Balance 

 El diseño asociado es:



 Atributos y Métodos: 


Atributos: Los atributos o características de una Clase pueden ser de tres tipos, los que definen el grado de comunicación y visibilidad de ellos con el entorno, estos son:

  • public (+, ): Indica que el atributo será visible tanto dentro como fuera de la clase, es decir, es accesible desde todos lados. 
  • private (-, ): Indica que el atributo sólo será accesible desde dentro de la clase (sólo sus métodos lo pueden acceder). 
  • protected (#, ): Indica que el atributo no será accesible desde fuera de la clase, pero si podrá ser accedido por métodos de la clase además de las subclases que se deriven (ver herencia). 

Métodos: Los métodos u operaciones de una clase son la forma en como ésta interactúa con su entorno, éstos pueden tener las características:

  • public (+, ): Indica que el método será visible tanto dentro como fuera de la clase, es decir, es accesible desde todos lados. 
  • private (-, ): Indica que el método sólo será accesible desde dentro de la clase (sólo otros métodos de la clase lo pueden acceder). 
  • protected (#, ): Indica que el método no será accesible desde fuera de la clase, pero si podrá ser accedido por métodos de la clase además de métodos de las subclases que se deriven (ver herencia).

Relaciones entre Clases: 

Ahora ya definido el concepto de Clase, es necesario explicar cómo se pueden interrelacionar dos o más clases (cada uno con características y objetivos diferentes). Antes es necesario explicar el concepto de cardinalidad de relaciones: En UML, la cardinalidad de las relaciones indica el grado y nivel de dependencia, se anotan en cada extremo de la relación y éstas pueden ser:

  • uno o muchos: 1..* (1..n) 
  • 0 o muchos: 0..* (0..n) 
  • número fijo: m (m denota el número). 

Variables y métodos estáticos 

Cuando creamos varias instancias de una misma clase, cada instancia tiene su propia copia de cada atributo. Sin embargo, hay ocasiones en las que deseamos que de un atributo de una clase sólo haya una copia y que todas las instancias de esa clase compartan ese atributo. En esos casos debemos declarar al atributo como estático usando el modificador static. La sintaxis para un atributo estático es la siguiente:

 [modificadorAcceso] static tipo nomAtributo

A un atributo puede aplicarse los modificadores static y final al mismo tiempo. Por ejemplo, el atributo PI de la clase Math:

 public final class Math
 { public static final double PI = 3.141592653589793d;
 ... 


 No se requiere instanciar una clase para usar sus atributos estáticos. Podemos acceder a un atributo estático mediante la siguiente sintaxis:

 nomClase.nomAtributoEstatico 

Por ejemplo, en el siguiente código note que no se creo un objeto del tipo Math para acceder a su atributo PI:

 area = Math.PI*radio*radio; 

 Una clase también puede tener métodos estáticos. Un método estático se declara con el modificador static, usando la siguiente sintaxis:

 [modificadorAcceso] static tipo nomMétodo(lista de parámetros) { 
declaraciones de variables locales 
 sentencias 


 Encapsulación: 

La encapsulación Se refiere a la capacidad de agrupar y condensar en un entorno con límites bien-definidos distintos elementos. Cuando hablemos de encapsulación en general siempre nos referiremos, pues, a encapsulación abstracta. De manera informal, primero generalizamos (la abstracción) y luego decimos: la generalización está bien, pero dentro de un cierto orden: hay que poner límites (la encapsulación), y dentro de esos límites vamos a meter, a saco, todo lo relacionado con lo abstraído: no sólo datos, sino también métodos, comportamientos, etc. Por un lado, es una abstracción pues, de acuerdo con la definición establecida anteriormente, es en ésta donde se definen las propiedades y atributos genéricos de determinados objetos con características comunes (recordemos el ejemplo de la sala de cine). La Clase es, por otro lado, una encapsulación porque constituye una cápsula o saco que encierra y amalgama de forma clara tanto los datos de que constan los objetos como los procedimientos que permiten manipularlos. Las Clases se constituyen, así, en abstracciones encapsuladas. La encapsulación También conocida como ocultamiento. Cuando me acuesto a ver televisión no me preocupo del modo como éste funciona, o lo que hace para cambiar de canal o aumentar el volumen. A menos que seas experto en electrónica o técnico en televisores, te pasará lo mismo: no lo sabes y no te importa; sólo sabes que al presionar un botón ocurre la magia. La encapsulación se encarga de mantener ocultos los procesos internos que necesita para hacer lo que sea que haga, dándole al programador acceso sólo a lo que necesita. Esto da dos ventajas iniciales: Lo que hace el usuario puede ser controlado internamente (incluso sus errores), evitando que todo colapse por una intervención indeseada (tú no quieres que tu mamá, que no tiene ni idea de electrónica, abra tu televisor y empiece a jugar con los circuitos para cambiar los canales manualmente ¿verdad?). La segunda ventaja es que, al hacer que la mayor parte del código esté oculto, puedes hacer cambios y/o mejoras sin que eso afecte el modo como los usuarios van a utilizar tu código. Sólo tienes que mantener igual la forma de acceder a él (en el caso del control de la tele, que los botones sigan siendo los mismos y que el botón de “apagado” no cambie el volumen). Por cierto, estas puertas de acceso que das a los usuarios son lo que se conoce como interfaz. La encapsulación define los niveles de acceso para elementos de esa clase. Estos niveles de acceso definen los derechos de acceso para los datos, permitiéndonos el acceso a datos a través de un método de esa clase en particular, desde una clase heredada o incluso desde cualquier otra clase. Existen tres niveles de acceso:

  • público: funciones de toda clase pueden acceder a los datos o métodos de una clase que se define con el nivel de acceso público. Este es el nivel de protección de datos más bajo 
  • protegido: el acceso a los datos está restringido a las funciones de clases heredadas, es decir, las funciones miembro de esa clase y todas las subclases 
  • privado: el acceso a los datos está restringido a los métodos de esa clase en particular. Este es nivel más alto de protección de datos 

 Clases Envolventes 

Java es un lenguaje de programación orientado a objetos. Un programa Java debe contener objetos y las operaciones entre ellos. La única excepción a esto en un programa Java son los tipos de datos primitivos (int, double, char, etc.)
Los tipos de datos primitivos no son objetos pero en ocasiones es necesario tratarlos como tales. Por ejemplo, hay determinadas clases que manipulan objetos (ArrayList, HashMap, …). Para poder utilizar tipos primitivos con estas clases Java provee las llamadas clases envolventes también llamadas clases contenedoras o wrappers. Cada tipo primitivo tiene su correspondiente clase envolvente:

 
Tipo primitivoClase Envolvente
byteByte
shortShort
intInteger
longLong
floatFloat
doubleDouble
charCharacter
booleanBoolean

 Estas clases proporcionan métodos que permiten manipular el tipo de dato primitivo como si fuese un objeto.

 Clase Integer

 En la siguiente tabla aparecen algunos métodos de la clase Integer. El resto de clases envolventes correspondientes a tipos primitivos numéricos tienen métodos similares. 

Integer(int valor) Integer(String valor)
Constructor a partir de un int
Integer n = new Integer (20);

Constructor a partir de un String
String s = "123456";
Integer a = new Integer(s);

int intValue ()
float floatValue ()
double doubleValue()
 …

Devuelve el valor equivalente
Integer n = new Integer (30);
int x = n.intValue();
double y = n.doubleValue();

int parseInt(String s)
Método estático que devuelve un int a partir de un String.
String s = "123456";
int z = Integer.parseInt(s);

String toBinaryString(int i) String toOctalString(int i) String toHexString(int i)
Métodos estáticos que devuelven un String con la representación binaria, octal o hexadecimal del número.

int numero = 12;

String binario = Integer.toBinaryString(numero);

Integer valueOf(String s)
Método Estático. Devuelve un Integer a partir de un String.
Integer m = Integer.valueOf("123");


 


Clase Character 

Provee una serie de métodos para manipular los datos de tipo char. En la siguiente tabla aparecen algunos de estos métodos.


Character(char c)
Constructor a partir de un
char char car = 'x';
Character a = new Character(car);

char charValue()
Devuelve el char equivalente
 Character n = new Character('q');
char c = n.charValue();

boolean isLowerCase(char ch)
boolean isUpperCase(char ch) boolean isDigit(char ch)     boolean isLetter(char ch)

Comprueba si es un carácter en minúsculas.
Comprueba si es un carácter en mayúsculas.
Comprueba si es un dígito (carácter del 0 al 9).
Comprueba si es una letra. Todos son estáticos. if(Character.isUpperCase(c)){
.....
}

char toLowerCase(char ch) char toUpperCase(char ch)
Devuelve el char en mayúsculas.
Devuelve el char en minúsculas.
Métodos estáticos. char car = 'u'; System.out.println(Character.toUpperCase(car));

Character valueOf(char c)
Método Estático. Devuelve un Character a partir de un char.
 Character m = Character.valueOf('a');



API de la clase Integer:

 http://docs.oracle.com/javase/7/docs/api/java/lang/Integer.html 

API de la clase Character:

 http://docs.oracle.com/javase/7/docs/api/java/lang/Character.html 

Fuentes: 

http://users.dcc.uchile.cl/~psalinas/uml/modelo.html http://www.sc.ehu.es/sbweb/fisica/cursoJava/fundamentos/clases1/clases.htm 
libro: ITSON programación orientado a objetos/Manuel Domitsu

lunes, 24 de octubre de 2016

Polimorfismo

Polimorfismo en Java con ejemplos


El Polimorfismo es uno de los 4 pilares de la programación orientada a objetos (POO) junto con la AbstracciónEncapsulación y Herencia. Para entender que es el polimorfismo es muy importante que tengáis bastante claro el concepto de la Herencia, por tanto recomendamos que veáis la entrada en la que hablamos de la Herencia: Herencia en Java, con ejemplos.
Para empezar con esta entrada, se ha de decir que el término “Polimorfismo” es una palabra de origen griego que significa “muchasformas”. Este termino se utiliza en la POO para “referirse a la propiedad por la que es posible enviar mensajes sintácticamente iguales a objetos de tipos distintos“. Como esta definición quizás sea algo difícil de entender, vamos a explicarla con el ejemplo que pusimos en la entrada de la herencia en la que queríamos simular el comportamiento que tendrían los diferentes integrantes de la selección española de fútbol; tanto los Futbolistas como el cuerpo técnico (Entrenadores, Masajistas, etc…). Para este ejemplo nos vamos a basar en el siguiente diagrama de clases:
PolimorfismoFutbol-diag
NOTA: en este diagrama y en adelante no vamos a poner los constructores y métodos getter y setter con el fin de que el diagrama nos quede grande e “intendible” aunque en un buen diagrama de clases deberían aparecer para respetar el principio de encapsulación de la POO

En este ejemplo vamos a tener una clase padre (SelecciónFutbol) en la que tendremos los atributos y métodos comunes a todos los integrantes que forman la selección española de fútbol (Futbolistas, Entrenadores, Masajistas, etc.) y en ella se van a implementar los métodos del comportamiento “genérico” que deben de tener todos los integrantes de la selección. Como ya dijimos en la entrada de la herencia, la herencia no es más que sacar “factor común” del código que escribimos, así que los atributos y métodos de la clase SeleccionFutbol los tendrán también los objetos de las clases Futbolista, Entrenador y Masajista. Antes de seguir vamos a mostrar el código de la clase “SeleccionFutbol” para ver algunas peculiaridades:
public abstract class SeleccionFutbol {

 protected int id;
 protected String nombre;
 protected String apellidos;
 protected int edad;

 // constructores, getter y setter

 public void viajar() {
      System.out.println("Viajar (Clase Padre)");
 }

 public void concentrarse() {
      System.out.println("Concentrarse (Clase Padre)");
 }

 // IMPORTANTE -> METODO ABSTRACTO => no se implementa en la clase abstracta pero si en la clases hijas
 public abstract void entrenamiento();

 public void partidoFutbol() {
      System.out.println("Asiste al Partido de Fútbol (Clase Padre)");
 }
}
Lo primero que nos debe de llamar la atención al ver este código es que utilizamos dos veces la palabra reservada “abstract“. Esta palabra nos indica que la clase “SeleccionFutbol” es una clase abstracta y las clases abstractas no se pueden instanciar, por tanto nunca podremos hacer un “new SeleccionFutbol()”. Otra cosa que vemos es que también utilizamos la palabra reservada abstract en un método (en el método entrenamiento). Esto quiere decir que todas las clases hijas de la clase “SeleccionFubol” tienen que tener implementado ese método obligatoriamente. Por tanto con esto que se acaba de contar y diciendo que la palabra “Polimorfismo” significa “muchas formas”, podéis deducir que la clase “SeleccionFutbol” es una clase que puede adoptar diferentes formas y en este ejemplo puede adoptar las formas de “Futbolista”, “Entrenador” y “Masajista”.
Ejm_polimorfismo_jarroba
Como vemos un “Entrenador”, un “Futbolista” y un “Masajista” pertenecen a la misma clase padre y por eso se instancian diciendo que es una SeleccionFutbol y son nuevos objetos de las clases hijas. Por otro lado vemos que no se pueden crear objetos de una clase abstracta, por tanto el crearnos el objeto “casillas” nos da un error.
Y ahora si hemos dicho que hemos definido en la clase padre un método abstracto que es obligatorio implementar en las clases hijas ¿Como lo hacemos?. Bueno vamos por partes. Una cosa muy buena que tiene la herencia y el polimorfismo, es que las clases hijas no solo heredan los métodos (o la implementación de los métodos) de las clases padre, sino que las clases hijas se pueden especializar.  Esto significa que una clase hija puede “redefinir” los métodos de su clase padre; es decir, que se puede volver a escribir ese método y de ahi la especialización. Para ello vamos a ver la implementación de las clases hijas:
public class Futbolista extends SeleccionFutbol {

   private int dorsal;
   private String demarcacion;

   // constructor, getter y setter

   @Override
   public void entrenamiento() {
      System.out.println("Realiza un entrenamiento (Clase Futbolista)");
   }

   @Override
   public void partidoFutbol() {
      System.out.println("Juega un Partido (Clase Futbolista)");
   }

   public void entrevista() {
      System.out.println("Da una Entrevista");
   }
}
public class Entrenador extends SeleccionFutbol {

   private int idFederacion;

   // constructor, getter y setter
 
   @Override
   public void entrenamiento() {
      System.out.println("Dirige un entrenamiento (Clase Entrenador)");
   }

   @Override
   public void partidoFutbol() {
      System.out.println("Dirige un Partido (Clase Entrenador)");
   }

   public void planificarEntrenamiento() {
      System.out.println("Planificar un Entrenamiento");
   }
}
public class Masajista extends SeleccionFutbol {

   private String titulacion;
   private int aniosExperiencia;

   // constructor, getter y setter
 
   @Override
   public void entrenamiento() {
      System.out.println("Da asistencia en el entrenamiento (Clase Masajista)");
   }

   public void darMasaje() {
      System.out.println("Da un Masaje");
   }
}
Como vemos en el código todas las clases hijas tienen implementada el método “entrenamiento()” ya que como dijimos al tenerlo en la clase padre como método abstracto, es obligatorio que todas las clases hijas tengan ese método. Por otro lado observamos en el código que encima del método “entrenamiento()” y otros métodos, tenemos la etiqueta “@Override“. Esta etiqueta sirve para indicar en el código que estamos “re-escribiendo o especializando” un método que se encuentra en la clase padre y que queremos redefinir en la clase hija. Si os fijáis esta etiqueta solo y exclusivamente esta en los métodos de las clases hijas que tenemos definida en la clase padre, por tanto cuando se llame a esos métodos, las clases hijas ejecutaran el método redefinido en la clase hija y las que no lo hayan redefinido se ejecutará es método de la clase padre. En la siguiente imagen vemos como hacemos estas especializaciones:
Polimorfismo_especializacion_jarroba
Con todo esto ya podemos empezar a ejecutar el programa que simulará el comportamiento de los integrantes de la selección española y ver las diferentes formas que adoptan cada uno de los integrantes de la selección. Para ello empecemos mostrando el siguiente fragmento de código:
public class Main {

 // ArrayList de objetos SeleccionFutbol. Idenpendientemente de la clase hija a la que pertenezca el objeto
 public static ArrayList<SeleccionFutbol> integrantes = new ArrayList<SeleccionFutbol>();

 public static void main(String[] args) {
  
  SeleccionFutbol delBosque = new Entrenador(1, "Vicente", "Del Bosque", 60, 28489);
  SeleccionFutbol iniesta = new Futbolista(2, "Andres", "Iniesta", 29, 6, "Interior Derecho");
  SeleccionFutbol raulMartinez = new Masajista(3, "Raúl", "Martinez", 41, "Licenciado en Fisioterapia", 18);

  integrantes.add(delBosque);
  integrantes.add(iniesta);
  integrantes.add(raulMartinez);

  // CONCENTRACION
  System.out.println("Todos los integrantes comienzan una concentracion. (Todos ejecutan el mismo método)");
  for (SeleccionFutbol integrante : integrantes) {
   System.out.print(integrante.getNombre() + " " + integrante.getApellidos() + " -> ");
   integrante.concentrarse();
  }

  // VIAJE
  System.out.println("nTodos los integrantes viajan para jugar un partido. (Todos ejecutan el mismo método)");
  for (SeleccionFutbol integrante : integrantes) {
   System.out.print(integrante.getNombre() + " " + integrante.getApellidos() + " -> ");
   integrante.viajar();
  }

      .........
}
Como vemos nos hemos creado tres objetos de la clase SeleccionFutbol que adoptan una de las tres formas que pueden adaptar (Entrenador, Futbolista y Masajista)  y los metemos en un “ArrayList” de objetos de la clase “SeleccionFutbol”. Ahora al ejecutar este fragmento de código vamos a ver que todos tienen el mismo comportamiento a la hora de “concentrarse()” y “viajar()”, por tanto ejecutarán el método de la clase padre:
Todos los integrantes comienzan una concentracion. (Todos ejecutan el mismo método)
Vicente Del Bosque -> Concentrarse (Clase Padre)
Andres Iniesta -> Concentrarse (Clase Padre)
Raúl Martinez -> Concentrarse (Clase Padre)

Todos los integrantes viajan para jugar un partido. (Todos ejecutan el mismo método)
Vicente Del Bosque -> Viajar (Clase Padre)
Andres Iniesta -> Viajar (Clase Padre)
Raúl Martinez -> Viajar (Clase Padre)
Hasta el momento nada nuevo y sorprendente, pero ahora vamos a ver como cada uno de los integrante al lanzarse los mismos métodos (“entrenamiento()” y “partidoFutbol()”) tienen un comportamiento diferente:
     ........
// ENTRENAMIENTO
System.out.println("nEntrenamiento: Todos los integrantes tienen su función en un entrenamiento (Especialización)");
for (SeleccionFutbol integrante : integrantes) {
 System.out.print(integrante.getNombre() + " " + integrante.getApellidos() + " -> ");
 integrante.entrenamiento();
}

// PARTIDO DE FUTBOL
System.out.println("nPartido de Fútbol: Todos los integrantes tienen su función en un partido (Especialización)");
for (SeleccionFutbol integrante : integrantes) {
 System.out.print(integrante.getNombre() + " " + integrante.getApellidos() + " -> ");
 integrante.partidoFutbol();
}
     ........
Vemos el resultado al ejecutar este fragmento de código:
Entrenamiento: Todos los integrantes tienen su función en un entrenamiento (Especialización)
Vicente Del Bosque -> Dirige un entrenamiento (Clase Entrenador)
Andres Iniesta -> Realiza un entrenamiento (Clase Futbolista)
Raúl Martinez -> Da asistencia en el entrenamiento (Clase Masajista)

Partido de Fútbol: Todos los integrantes tienen su función en un partido (Especialización)
Vicente Del Bosque -> Dirige un Partido (Clase Entrenador)
Andres Iniesta -> Juega un Partido (Clase Futbolista)
Raúl Martinez -> Asiste al Partido de Fútbol (Clase Padre)
En este caso vemos que todos los integrantes ejecutan el método “entrenamiento()” de forma diferente ya que al ser este método abstracto en la clase padre, les forzamos a las clases hijas a que implementen ese método. Por el contrario al ejecutar el método “partidoFutbol()” vemos que el objeto de la clase Masajista utiliza el método implementado en la clase padre y en cambio los objetos de la clase Futbolista y Entrenador ejecutan sus método “re-implementados o especializados” que se volvieron a escribir en sus clases.
Por último vamos a ver que cada uno de los objetos puede ejecutar métodos propios que solamente ellos los tienen como son el caso de “planificarEntrenamiento(), entrevista() y  darMasaje()” que solo los pueden ejecutar objetos de la clase Entrenador, Futbolista y Masajista respectivamente:
     ........
// PLANIFICAR ENTRENAMIENTO
System.out.println("nPlanificar Entrenamiento: Solo el entrenador tiene el método para planificar un entrenamiento:");
System.out.print(delBosque.getNombre() + " " + delBosque.getApellidos() + " -> ");
((Entrenador) delBosque).planificarEntrenamiento();

// ENTREVISTA
System.out.println("nEntrevista: Solo el futbolista tiene el método para dar una entrevista:");
System.out.print(iniesta.getNombre() + " " + iniesta.getApellidos() + " -> ");
((Futbolista) iniesta).entrevista();

// MASAJE
System.out.println("nMasaje: Solo el masajista tiene el método para dar un masaje:");
System.out.print(raulMartinez.getNombre() + " " + raulMartinez.getApellidos() + " -> ");
((Masajista) raulMartinez).darMasaje();
     ........
Como resultado de la ejecución de este fragmento de código tenemos lo siguiente:
Planificar Entrenamiento: Solo el entrenador tiene el método para planificar un entrenamiento:
Vicente Del Bosque -> Planificar un Entrenamiento

Entrevista: Solo el futbolista tiene el método para dar una entrevista:
Andres Iniesta -> Da una Entrevista

Masaje: Solo el masajista tiene el método para dar un masaje:
Raúl Martinez -> Da un Masaje

CONCLUSIONES Y ACLARACIONES:

Como hemos visto el polimorfismo es un concepto un poco más avanzado que la herencia y puede ser muy util a la hora de jerarquizar y querer dar un patrón de comportamiento común a una serie de objetos que heredan de la misma clase. En esta entrada no hemos visto todo lo referente al polimorfismo ya que nos quedaría ver un concepto un poco más avanzado en Java (y en otros lenguajes también) como son las “Interface” (clases abstractas puras) de las cuales hablaremos en otra entrada para terminar de ver lo que es el polimorfismo.
Por último es muy probable para los que estéis empezando con la POO que no veáis mucho sentido a esto del polimorfismo y al principio es normal. Solo os debo de decir que a base de experiencia se le encuentra sentido al polimorfismo, por tanto si teneis que hacer alguna práctica en la universidad o lo que sea en la que tengais que usar el polimorfismo intentar entenderlo y hacer lo que os pidan porque entenderlo 100% es complicado y se requiere de experiencia para ello.

http://jarroba.com/polimorfismo-en-java-parte-i-con-ejemplos/