MÓDULO
Área: TECNOLOGÍA
3
Curso: PROGRAMACIÓN ORIENTADA A OBJETOS (JAVA)
Módulo: Conceptos básicos
Área: TECNOLOGÍA M2
Curso: PROGRAMACIÓN ORIENTADA A OBJETOS (JAVA)
Conceptos básicos
Índice
Tabla de contenido
Índice .....................................................................................................................................................................2
Mapa de Contenido ................................................................................................................................................3
Introducción ...........................................................................................................................................................4
1. Polimorfismo ................................................................................................................................................5
1.1 Polimorfismo en Tiempo de Ejecución (Dinámico) ......................................................................................5
1.2 Polimorfismo en Tiempo de Compilación (Estático)....................................................................................8
2. Resumen Polimorfismo ..............................................................................................................................10
3. Encapsulamiento........................................................................................................................................11
3.1 Principios ...................................................................................................................................................11
4. Abstracción ................................................................................................................................................15
4.1 Clases abstractas ........................................................................................................................................15
5. Interfaces ...................................................................................................................................................16
5.1 Diferencias entre Interfaces y clases abstractas ........................................................................................18
6. Cierre..........................................................................................................................................................19
Área: TECNOLOGÍA M2
Curso: PROGRAMACIÓN ORIENTADA A OBJETOS (JAVA)
Conceptos básicos
Mapa de Contenido
Área: TECNOLOGÍA M2
Curso: PROGRAMACIÓN ORIENTADA A OBJETOS (JAVA) Pág. 4
Conceptos básicos
Tras explorar los fundamentos de la Programación Orientada a Objetos
RESULTADO DE (POO) en Java, los lectores entenderán la relevancia del polimorfismo,
APRENDIZAJE DEL encapsulamiento y abstracción en esteparadigma.
MODULO
Introducción
En el vasto y dinámico mundo de la programación orientada a objetos (POO), tres conceptos juegan roles
cruciales en el diseño y la implementación de sistemas de software robustos y mantenibles: polimorfismo,
encapsulamiento y abstracción. Estos principios no solo ayudan a los desarrolladores a gestionar y organizar
el código de manera más eficiente, sino que también facilitan la reutilización de código, la extensibilidad y
la modularidad. A través de ejemplos prácticos y teoría fundamental, exploraremos las profundidades de
estos conceptos, destacando sus diferencias, aplicaciones y la importancia en Java, un lenguaje que encarna
plenamente los principios de la POO.
Área: TECNOLOGÍA M2
Curso: PROGRAMACIÓN ORIENTADA A OBJETOS (JAVA) Pág. 5
1. Polimorfismo
El polimorfismo es un concepto fundamental en la programación orientada a objetos (POO), que permite a los
objetos ser tratados como instancias de su propia clase o de una clase base desde la que heredan. Esto
significa que un método puede realizar diferentes acciones dependiendo del objeto que lo invoca o de la clase
de dicho objeto.
Hay dos tipos principales de polimorfismo en Java: polimorfismo en tiempo de ejecución (también conocido
como polimorfismo dinámico) y polimorfismo en tiempo de compilación (polimorfismo estático).
1.1 Polimorfismo en Tiempo de Ejecución (Dinámico)
El polimorfismo en tiempo de ejecución se logra mediante el uso de la herencia y el método de sobrescritura.
En este caso, la llamada a un método sobrescrito se resuelve en tiempo de ejecución, lo que significa que Java
utiliza el objeto en tiempo de ejecución para determinar qué versión del método sobrescrito ejecutar.
Ejemplo: Supongamos que tienes una clase base llamada Animal y dos clases derivadas llamadas Perro y Gato.
Cada una de estas clases derivadas tiene su propia implementación del método hacerSonido().
Área: TECNOLOGÍA M2
Curso: PROGRAMACIÓN ORIENTADA A OBJETOS (JAVA) Pág. 6
En este ejemplo, aunque miPerro y miGato son del tipo Animal, el método hacerSonido() que se invoca es el
de la clase correspondiente (Perro o Gato), gracias al polimorfismo en tiempo de ejecución.
TestPolimorfismo ilustra el concepto de polimorfismo en tiempo de ejecución en Java.
Esto muestra cómo Java determina el método correcto a ejecutar no basándose en el tipo de la referencia
(que es Animal para todos los objetos en este ejemplo), sino en el tipo real del objeto en tiempo de ejecución.
Este mecanismo permite que un mismo método tenga múltiples formas (polimorfismo) dependiendo del
objeto que lo invoca, lo cual es una pieza fundamental del diseño orientado a objetos, facilitando la
extensibilidad y la reutilización del código.
Área: TECNOLOGÍA M2
Curso: PROGRAMACIÓN ORIENTADA A OBJETOS (JAVA) Pág. 7
Estructura del Programa
El programa define una clase base Animal y dos clases derivadas Perro y Gato. Cada una de estas clases tiene
su propia implementación del método hacerSonido().
• La clase Animal tiene una implementación básica del método hacerSonido(), que simplemente
imprime "Algun sonido".
• La clase Perro, que extiende Animal, sobrescribe el método hacerSonido() para imprimir "Guau".
• La clase Gato, que también extiende Animal, sobrescribe el método hacerSonido() para imprimir
"Miau".
Ejecución del Programa TestPolimorfismo
En el método main, se crean tres objetos, cada uno referenciado por un tipo Animal:
1. miAnimal es una instancia de Animal.
2. miPerro es una instancia de Perro.
3. miGato es una instancia de Gato.
Aunque todos los objetos se declaran como tipo Animal, los objetos miPerro y miGato son instancias de las
clases Perro y Gato, respectivamente. Esto es crucial para el polimorfismo.
Demostración de Polimorfismo
Cuando se llama al método hacerSonido() en cada uno de estos objetos, el comportamiento varía según el tipo
real del objeto:
• miAnimal.hacerSonido() imprime "Algun sonido", porque miAnimal es una instancia de la clase
Animal.
• miPerro.hacerSonido() imprime "Guau", aunque miPerro se declara como un tipo Animal, en tiempo
de ejecución es un objeto Perro, por lo que se llama al método hacerSonido() definido en la clase
Perro.
• miGato.hacerSonido() imprime "Miau" por la misma razón; en tiempo de ejecución, miGato es tratado
como un objeto Gato, y por lo tanto, se utiliza la implementación de hacerSonido() de la clase Gato.
Área: TECNOLOGÍA M2
Curso: PROGRAMACIÓN ORIENTADA A OBJETOS (JAVA) Pág. 8
1.2 Polimorfismo en Tiempo de Compilación (Estático)
El polimorfismo en tiempo de compilación, también conocido como polimorfismo estático, se refiere a la
capacidad de un lenguaje de programación, como Java, para diferenciar entre métodos con el mismo nombre,
pero diferentes firmas dentro de la misma clase. La "firma" de un método incluye su nombre y su lista de
parámetros (tanto el número como el tipo de los parámetros). Este tipo de polimorfismo se resuelve en el
momento de la compilación, de ahí el término "estático".
Ejemplo Conceptual
Para ilustrar el polimorfismo en tiempo de compilación, vamos a utilizar un ejemplo diferente al de
polimorfismo en tiempo de ejecución, ya que involucra la sobrecarga de métodos en lugar de la herencia y
la sobreescritura. Imagina que tienes una clase Calculadora que puede sumar números, pero los números
pueden ser enteros o decimales (flotantes/dobles).
En este caso, la clase Calculadora tiene dos métodos suma(): uno que acepta dos enteros y otro que acepta
dos números de punto flotante. El compilador determina cuál método llamar basándose en los tipos de los
argumentos suministrados en la llamada al método.
Área: TECNOLOGÍA M2
Curso: PROGRAMACIÓN ORIENTADA A OBJETOS (JAVA) Pág. 9
Explicación del código
• Clase Calculadora: Esta clase define dos métodos suma(). Aunque ambos métodos comparten el mismo
nombre, se diferencian por sus listas de parámetros:
• El primer método suma(int a, int b) está diseñado para sumar dos números enteros.
• El segundo método suma(double a, double b) está diseñado para sumar dos números de punto
flotante.
• Sobrecarga de Métodos: Este es un ejemplo clásico de sobrecarga de métodos, donde un método se define
varias veces con diferentes listas de parámetros. La sobrecarga es una forma de polimorfismo en tiempo
de compilación, ya que el compilador determina, en el momento de la compilación, cuál versión del
método se debe llamar basándose en los argumentos proporcionados en la llamada.
• Método main: En el método main, se crea una instancia de Calculadora y se llaman ambos métodos
suma(). El compilador selecciona el método adecuado para llamar en función de los tipos de los
argumentos pasados a suma():
• Cuando se llama a suma(5, 3), se utiliza la versión que toma dos enteros, porque ambos
argumentos son de tipo int.
• Cuando se llama a suma(5.5, 3.5), se utiliza la versión que toma dos números de punto flotante,
porque ambos argumentos son de tipo double.
El programa Test se utiliza para demostrar cómo funciona la sobrecarga de métodos (polimorfismo estático)
en la clase Calculadora. Específicamente, muestra cómo se pueden llamar diferentes versiones del método
suma() dependiendo de los argumentos pasados.
Área: TECNOLOGÍA M2
Curso: PROGRAMACIÓN ORIENTADA A OBJETOS (JAVA) Pág. 10
2. Resumen Polimorfismo
Ambas formas de polimorfismo son poderosas herramientas que nos permiten escribir código más flexible,
reutilizable y mantenible.
Polimorfismo en Tiempo de Ejecución (Dinámico)
El polimorfismo dinámico se logra a través de la herencia y la sobrescritura de métodos. Permite que un objeto
sea referenciado como una instancia de una clase base o de cualquier subclase derivada de ella, lo que significa
que el método que se invoca en un objeto depende de la clase del objeto que se está ejecutando en tiempo de
ejecución. Esto es crucial para la implementación de interfaces comunes en clases que comparten la misma
superclase pero tienen implementaciones específicas que difieren entre sí.
• Ventajas: Facilita la flexibilidad y la reutilización del código al permitir que nuevas clases se integren
fácilmente con el código existente a través de la herencia.
• Desventajas: Puede introducir una sobrecarga en tiempo de ejecución debido a la verificación de tipos
y la resolución de métodos durante la ejecución del programa.
Polimorfismo en Tiempo de Compilación (Estático)
El polimorfismo estático, por otro lado, se logra mediante la sobrecarga de métodos dentro de la misma clase.
Permite que múltiples métodos compartan el mismo nombre, pero con diferentes listas de parámetros
(diferente número de parámetros, tipos de parámetros, o ambos), lo que hace que el compilador determine
cuál versión del método debe ser llamada basándose en los argumentos proporcionados en tiempo de
compilación.
• Ventajas: Mejora la legibilidad del código al permitir que métodos que realizan operaciones similares
(pero con diferentes tipos o números de argumentos) compartan el mismo nombre, facilitando así su
identificación y uso.
• Desventajas: La sobrecarga de métodos puede llevar a confusiones si no se maneja cuidadosamente,
especialmente en casos donde la conversión de tipos puede hacer que el método llamado no sea el
que el programador esperaba.
Área: TECNOLOGÍA M2
Curso: PROGRAMACIÓN ORIENTADA A OBJETOS (JAVA) Pág. 11
3. Encapsulamiento
El encapsulamiento es otro concepto fundamental en la programación orientada a objetos (POO), que se
refiere a la práctica de ocultar los detalles internos de la implementación de una clase y exponer solo aquellos
componentes que sean seguros para el uso de otras clases. Este concepto ayuda a mantener el código más
seguro y fácil de mantener.
3.1 Principios
Principios del Encapsulamiento
• Ocultamiento de Datos: Se evita el acceso directo a los campos internos de una clase (también
conocidos como propiedades o atributos) y se proporciona acceso a ellos a través de métodos (como
getters y setters). Esto permite cambiar la implementación interna sin afectar a aquellos que usan la
clase.
• Reducción de Acoplamiento: Al limitar el acceso directo a los componentes de una clase, se reduce la
dependencia de otras clases en su implementación interna, facilitando modificaciones y mejorando la
modularidad del código.
El encapsulamiento, como pilar de la programación orientada a objetos (POO), no solo gestiona la visibilidad
de los estados y comportamientos de un objeto sino que también establece una arquitectura robusta que
promueve la modularidad y la flexibilidad. Profundicemos aún más en los aspectos clave y beneficios del
encapsulamiento en Java.
Detalles Internos de Encapsulamiento
Modificadores de Acceso
Java proporciona varios modificadores de acceso para controlar la visibilidad de las clases, métodos y variables:
• private: El miembro solo es accesible dentro de la misma clase. Este es el nivel más restrictivo y es
fundamental para ocultar los detalles de implementación.
• default (sin modificador): El miembro es accesible solo dentro de paquetes por clases que están en el
mismo paquete.
• protected: El miembro es accesible dentro del mismo paquete y por subclases, incluso si están en
diferentes paquetes.
• public: El miembro es accesible desde cualquier otra clase en cualquier paquete. Este es el nivel menos
restrictivo.
Área: TECNOLOGÍA M2
Curso: PROGRAMACIÓN ORIENTADA A OBJETOS (JAVA) Pág. 12
Principios de Diseño:
• Principio de Responsabilidad Única: Cada clase debe tener una responsabilidad única y bien definida.
El encapsulamiento ayuda a lograr esto al permitir que una clase administre sus propios datos y
comportamientos.
• Principio de Abstracción: El encapsulamiento permite exponer solo lo que es necesario para el uso
de una clase, manteniendo ocultos los detalles complejos. Esto promueve una interfaz de
programación más sencilla y abstracta.
Beneficios del Encapsulamiento:
• Seguridad de Datos: Al restringir el acceso directo a los campos de una clase, se protege la integridad
de los datos al evitar modificaciones no autorizadas o erróneas.
• Flexibilidad y Mantenibilidad: Se pueden hacer cambios en la implementación interna de una clase
sin afectar a las clases que la utilizan, siempre que se mantenga la interfaz pública. Esto hace que el
código sea más fácil de mantener y actualizar.
• Modularidad: El encapsulamiento fomenta la división del código en módulos o clases independientes
con interfaces bien definidas, lo que facilita el desarrollo y la prueba de componentes individuales de
manera aislada.
Área: TECNOLOGÍA M2
Curso: PROGRAMACIÓN ORIENTADA A OBJETOS (JAVA) Pág. 13
Aplicación Práctica del Encapsulamiento
Vamos a expandir el ejemplo de la clase CuentaBancaria para demostrar cómo el encapsulamiento
puede ser utilizado para añadir una funcionalidad más compleja, como el cálculo de intereses,
manteniendo seguros y privados los detalles de implementación. Añadiremos un método para calcular
los intereses sobre el balance y actualizar el balance en función de esos intereses, todo ello sin exponer
directamente el campo balance ni el método de cálculo de intereses.
Área: TECNOLOGÍA M2
Curso: PROGRAMACIÓN ORIENTADA A OBJETOS (JAVA) Pág. 14
Explicación del Código:
• Campos Privados balance y tasaDeInteres: Estos campos almacenan el balance de la cuenta y la tasa
de interés anual, respectivamente. Ambos son privados para prevenir el acceso y la modificación
directa desde fuera de la clase.
• Constructor: Inicializa el objeto CuentaBancaria con un balance inicial y una tasa de interés inicial.
• Getter getBalance(): Antes de retornar el balance, llama al método aplicarInteres() para asegurar
que cualquier interés acumulado sea aplicado. Esto demuestra cómo el encapsulamiento permite
modificar el comportamiento interno de la clase (como aplicar intereses) sin cambiar la forma en que
las clases externas acceden a los datos de la cuenta.
• Métodos depositar() y retirar(): Permiten modificar el balance de la cuenta de manera controlada,
asegurando que solo se realicen operaciones válidas.
• Método Privado aplicarInteres(): Calcula y aplica el interés al balance. Este método es privado
porque es un detalle de implementación interno que no necesita ser expuesto fuera de la clase.
• Setter setTasaDeInteres(double nuevaTasa): Permite cambiar la tasa de interés de la cuenta.
Proporciona un control sobre cómo se puede modificar la tasa de interés, incluyendo la validación
para asegurar que la nueva tasa sea positiva.
Ejecutar el Programa: Para este ejemplo específico, necesitas un método main para crear una instancia de
CuentaBancaria y demostrar su funcionalidad. Para que funcione debes añadir un método main en una clase
separada. Aquí tienes un ejemplo simple de cómo podría lucir:
Este ejemplo demuestra cómo el encapsulamiento permite a CuentaBancaria gestionar su estado interno de
manera segura y flexible. Los usuarios de la clase pueden interactuar con ella a través de una interfaz pública
bien definida (como depositar, retirar, y obtener el balance), mientras que los detalles de implementación,
como el cálculo de intereses, permanecen ocultos y protegidos. Esto facilita la mantenibilidad y extensibilidad
del código, ya que los desarrolladores pueden modificar la lógica interna sin afectar a las clases que
dependen de CuentaBancaria.
Área: TECNOLOGÍA M2
Curso: PROGRAMACIÓN ORIENTADA A OBJETOS (JAVA) Pág. 15
4. Abstracción
La abstracción es un principio clave de la programación orientada a objetos (POO) que implica ocultar los
detalles complejos de implementación y mostrar solo las características esenciales de un objeto al usuario.
Java implementa la abstracción principalmente a través de clases abstractas e interfaces. Aunque ambas se
utilizan para lograr la abstracción, tienen diferencias significativas y sus propios casos de uso específicos.
4.1 Clases abstractas
Una clase abstracta es una clase que no se puede instanciar y puede contener tanto métodos implementados
(con cuerpo) como métodos abstractos (sin cuerpo, solo declaraciones). Se utiliza como superclase de otras
clases para compartir una estructura común o implementaciones mientras se obliga a las clases derivadas a
implementar ciertos métodos específicos.
Características:
• Puede tener métodos abstractos y métodos con implementación.
• Puede tener miembros de datos (campos) que pueden ser heredados.
• Se utiliza para compartir código base entre varias clases relacionadas.
Creando una Clase Abstracta
1. Definición de la Clase Abstracta: Comienza con la palabra clave abstract seguida de class y el
nombre de la clase.
2. Archivo: Guarda el código en un archivo con el mismo nombre que la clase y con la extensión
.java. Por ejemplo, Animal.java.
Área: TECNOLOGÍA M2
Curso: PROGRAMACIÓN ORIENTADA A OBJETOS (JAVA) Pág. 16
Creando una Clase que Extiende de la Clase Abstracta
1. Extender la Clase Abstracta: Usa la palabra clave extends para heredar de una clase
abstracta.
2. Archivo: El código se guarda en un archivo con el nombre de la clase, por ejemplo,
Perro.java.
5. Interfaces
Una interfaz es un contrato que define un conjunto de métodos abstractos que la clase que la
implementa debe proveer. A diferencia de las clases abstractas, las interfaces no pueden contener
implementación de métodos (hasta Java 7). Desde Java 8 en adelante, las interfaces pueden
contener métodos default y static con implementación. Una clase puede implementar múltiples
interfaces, lo que permite una forma de herencia múltiple.
Características:
• Todos los métodos en una interfaz son abstractos por defecto (excepto métodos default y
static desde Java 8).
• No puede tener miembros de datos (todos los campos son públicos, estáticos y finales por
defecto).
• Se utiliza para especificar un contrato que las clases deben cumplir, permitiendo una forma de
herencia múltiple.
Creando una Interfaz
1. Definición de la Interfaz: Usa la palabra clave interface.
2. Archivo: El código se guarda en un archivo con el nombre de la interfaz, por ejemplo,
AnimalInterface.java.
Área: TECNOLOGÍA M2
Curso: PROGRAMACIÓN ORIENTADA A OBJETOS (JAVA) Pág. 17
Creando una Clase que Implementa la Interfaz
1. Implementar la Interfaz: Usa la palabra clave implements para implementar una interfaz.
2. Archivo: El código se guarda en un archivo con el nombre de la clase, por ejemplo,
PerroInterface.java.
Ejecutar el Programa
Para ejecutar tu programa, necesitas una clase con un método main. Recuerda que el método main es el
punto de entrada de tu programa. Si ninguna de tus clases actuales tiene un método main, necesitarás
agregarlo a una de ellas o crear una nueva clase que lo contenga para ejecutar tu programa.
Ejemplo:
Área: TECNOLOGÍA M2
Curso: PROGRAMACIÓN ORIENTADA A OBJETOS (JAVA) Pág. 18
5.1 Diferencias entre Interfaces y clases abstractas
• Propósito: Las clases abstractas se utilizan para capturar la esencia de una categoría de objetos,
incluyendo implementaciones comunes. Las interfaces definen un contrato que otras clases pueden
implementar.
• Herencia vs Implementación: Las clases solo pueden heredar de una clase abstracta, pero pueden
implementar múltiples interfaces.
• Miembros de Datos: Las clases abstractas pueden tener miembros de datos completos (estáticos y no
estáticos). Las interfaces solo pueden tener campos públicos, estáticos y finales.
• Métodos: Las clases abstractas pueden tener métodos con implementaciones completas y métodos
abstractos. Las interfaces tienen métodos abstractos por defecto, pero desde Java 8, también pueden
tener métodos default y static con cuerpos.
Cuando Usar Cada Uno
• Usa clases abstractas cuando varias clases comparten una estructura común o métodos
implementados, pero también tienen comportamientos específicos que deben ser proporcionados por
las clases derivadas.
• Usa interfaces cuando diferentes clases pueden compartir el mismo conjunto de métodos, pero no
necesariamente tienen una relación de tipo padre-hijo. Las interfaces son ideales para definir
capacidades comunes para clases no relacionadas, permitiendo una forma de herencia múltiple.
La elección entre usar una clase abstracta o una interfaz depende de la jerarquía de herencia de tus clases y
de cómo deseas organizar los métodos comunes y los contratos en tu aplicación.
Área: TECNOLOGÍA M2
Curso: PROGRAMACIÓN ORIENTADA A OBJETOS (JAVA) Pág. 19
6. Cierre
Al adentrarnos en el mundo de la programación orientada a objetos (POO) y desentrañar los principios de
polimorfismo, encapsulamiento y abstracción, hemos navegado a través de los pilares fundamentales que
sostienen el diseño y desarrollo de software en Java. Estos conceptos no son meras abstracciones teóricas, sino
herramientas prácticas que, cuando se entienden y aplican correctamente, pueden transformar la manera en
que estructuramos, escribimos y pensamos sobre el código. Hemos visto cómo el polimorfismo enriquece
nuestro arsenal permitiéndonos interactuar con objetos de múltiples formas, cómo el encapsulamiento
protege y oculta la complejidad innecesaria, y cómo la abstracción nos ayuda a concentrarnos en las
interacciones y comportamientos esenciales, dejando de lado los detalles específicos de implementación.
La aplicación de estos principios facilita la creación de sistemas robustos, flexibles y fácilmente mantenibles, lo
que es esencial en un mundo donde los requisitos cambian rápidamente y la capacidad de adaptarse y
evolucionar es invaluable. Al diseñar software que capitaliza el polimorfismo, el encapsulamiento y la
abstracción, los desarrolladores pueden construir aplicaciones que no solo cumplen con los requisitos actuales
de manera eficiente, sino que también están preparadas para los desafíos y cambios futuros.
En última instancia, la comprensión profunda y la aplicación efectiva de polimorfismo, encapsulamiento y
abstracción no solo mejoran la calidad de nuestro software, sino que también enriquecen nuestra capacidad
para pensar y resolver problemas de manera abstracta y flexible. Este es el verdadero poder de la POO, y por
extensión, el corazón de la maestría en programación en Java y más allá.