-
Notifications
You must be signed in to change notification settings - Fork 3
04. Programación Orientada a Objetos
La programación orientada a objetos (POO) es un paradigma de programación que se basa en el uso de objetos y sus interacciones para diseñar aplicaciones y programas de software.
Los objetos son instancias de clases, las cuales pueden incluir variables de instancia, métodos y otros datos necesarios para su funcionamiento.
- Una clase representan un modelo o plantilla que define las características (atributos) y comportamientos (métodos) que deben tener los objetos de un tipo específico.
- Una instancia es un objeto que se ha creado utilizando la plantilla de esa clase, mediante el operador
new. Cada instancia puede acceder a los métodos que se han definido en la clase.
Note
Una clase consiste en un conjunto de instrucciones que especifican cómo deben construirse las instancias de dicha clase.
Ejemplo:
- Clase:
public class Perro {
// Atributos
private String nombre;
private String raza;
private int edad;
// Constructor
public Perro(String nombre, String raza, int edad) {
this.nombre = nombre;
this.raza = raza;
this.edad = edad;
}
}- Instancia:
public class Main {
public static void main(String[] args) {
// Crear una instancia de la clase Perro
Perro miPerro = new Perro("Max", "Labrador", 3);
}
}La palabra clave this en Java se utiliza para referirse al objeto actual dentro de una clase.
Usos:
- Referenciar propiedades del objeto desde métodos o constructores.
- Invocar a otro constructor dentro de la misma clase usando
this(...), lo que se llama delegación de constructores.
- Un constructor es un método especial diseñado para inicializar objetos de una clase. Tiene el mismo nombre que la clase y no devuelve ningún valor (ni siquiera
void). - Tiene como propósito establecer los valores iniciales de los atributos de un objeto cuando se crea.
- Si no se define un constructor, Java proporciona uno sin parámetros conocido como constructor por defecto.
- El constructor predeterminado (o por defecto) crea una instancia de la clase con los valores predeterminados de sus campos:
-
0para tipos numéricos -
falseparaboolean -
nullpara referencias de objetos
Ejemplo:
public class Persona {
String nombre; // propiedad de los objetos de la clase
public Persona() {} // constructor por defecto, no hace falta declararlo
}La sobrecarga de constructores permite definir varios constructores con diferentes listas de parámetros. Esto permite inicializar objetos de distintas maneras.
Ejemplo de sobrecarga:
public class Persona {
private String nombre;
private Integer edad;
// Constructor por defecto
public Persona() {
this("Desconocido", 0); // Se conoce c omo Llamada al constructor o delegación al constructor
}
// Constructor con parámetros
public Persona(String nombre, int edad) {
this.nombre = nombre;
this.edad = edad;
}
}
- El constructor por defecto utiliza
this(...)para invocar al constructor que tomanombreyedadcomo parámetros, evitando valoresnullen las propiedades.- La llamada
this(...)debe ser la primera línea del constructor.
Una variable estática pertenece a la clase y no a las instancias individuales de esa clase. Esto significa que:
- Solo existe una copia de la variable estática, sin importar cuántas instancias de la clase se creen.
- Todas las instancias de la misma clase comparten el mismo valor de la variable estática.
- Es útil para almacenar valores que deben ser comunes y persistentes para todas las instancias.
Note
Dentro de los métodos que pertenecen a las instancias (métodos no estáticos), podemos acceder a variables estáticas y variables de instancia. Sin embargo, en los métodos estáticos, no se pueden acceder directamente a las variables de instancia, ya que estas requieren una instancia de la clase. Los métodos estáticos solo pueden acceder a variables estáticas. Si no es estático no se puede referenciar dentro de un contexto estático.
Un método estático pertenece a la clase y no a una instancia específica, lo cual implica que:
- No puede acceder a variables o métodos de instancia, ya que estos dependen de una instancia de la clase.
- Suelen usarse para operaciones generales que no dependen de los datos de un objeto específico.
Ejemplo de variables y métodos estáticos:
public class Clase {
Integer variableNoEstatica; // Variable de instancia
static Integer variableEstatica; // Variable estática
public static void metodoEstatico() {
// Método estático
}
public void metodoNoEstatico() {
// Método no estático
}
}Note
- Las variables estáticas se declaran dentro de la clase, pero fuera de métodos, constructores o bloques.
- No se pueden declarar variables estáticas dentro de métodos, a menos que estén en un bloque estático, donde su alcance se limita a ese bloque.
- Para invocar métodos y variables estáticas, se usa el nombre de la clase directamente, como
Clase.metodoEstatico()oClase.variableEstatica.
Los métodos getters y setters son esenciales para la encapsulación y ocultación de datos en Java. Estos métodos permiten obtener (getters) y establecer (setters) el valor de un atributo privado de una clase, controlando el acceso y modificación de los datos.
Ejemplo:
public class Persona {
// Atributos privados
private String nombre;
private int edad;
// Constructor
public Persona(String nombre, int edad) {
this.nombre = nombre;
this.edad = edad;
}
// Getter para nombre
public String getNombre() {
return nombre;
}
// Setter para nombre
public void setNombre(String nombre) {
this.nombre = nombre;
}
// Getter para edad
public int getEdad() {
return edad;
}
// Setter para edad
public void setEdad(int edad) {
this.edad = edad;
}
}Los atributos
nombreyedadson privados, lo que significa que solo pueden ser accedidos y modificados dentro de la clase.
Ventajas:
- Encapsulación: Los datos privados solo pueden ser modificados a través de métodos controlados.
- Modularidad y Mantenibilidad: Permiten acceder y modificar los valores de forma segura, contribuyendo a un diseño de código más modular y fácil de mantener.
- Mejora la seguridad y robustez del código al evitar el acceso directo a los atributos privados desde fuera de la clase.
| Práctica | Descripción |
|---|---|
| Entiende los Objetos | Visualiza cada objeto como un pequeño programa con sus propias características y funcionalidades. Esta mentalidad te permitirá descomponer problemas complejos en unidades más manejables. |
| Uso de Clases y Objetos | Aprende a emplear clases como modelos para crear objetos. Cada instancia de objeto que creas es una representación única de una clase. |
| Importancia de los Atributos | Los atributos actúan como las "etiquetas" que definen las propiedades de tus objetos. Por ejemplo, en una clase "Perro", los atributos podrían ser su raza, color y edad. |
| Métodos para Acciones | Los métodos representan las "acciones" que pueden realizar tus objetos. Si tienes un objeto "Automóvil", puede tener métodos como "arrancar" o "frenar". |
| Práctica Constante | La práctica es fundamental. Cuantos más ejemplos y ejercicios realices, más familiarizado estarás con los conceptos de la POO y más habilidades desarrollarás. |
Los modificadores de acceso son etiquetas que se les agregan a los atributos y métodos de una clase para controlar el acceso. Pueden ser:
-
private: La clase, el método o el atributo es accesible solamente dentro de la clase. -
public: La clase, el método o el atributo es accesible desde cualquier lugar. -
protected: La clase, el método o el atributo es accesible dentro del mismo paquete y también por subclases de cualquier paquete. -
default(sin modificador): La clase, el método o el atributo es solo accesible dentro del mismo paquete. Es el comportamiento por defecto. -
Encapsulamiento: Es el proceso de agrupar los datos (atributos) y los métodos que los manipulan dentro de una clase, formando una única unidad lógica. De esta manera, la clase puede gestionar sus datos y comportamientos de forma coherente y controlada.
-
Ocultamiento de Información: Es una técnica dentro del encapsulamiento que restringe el acceso directo a los atributos de una clase. Al marcar los atributos como
private, estos no pueden ser accedidos o modificados directamente desde fuera de la clase; en su lugar, se accede a ellos mediante métodos públicos (como getters y setters), permitiendo así un control seguro y definido de cómo se manipulan los datos internos de la clase.
Beneficios del Encapsulación y Ocultación
-
Control de Acceso: Se obtiene un control completo sobre cómo y cuándo se accede o modifica la información de un objeto.
-
Flexibilidad y Mantenimiento: Al ocultar los detalles internos de la implementación de una clase, es posible cambiar esa implementación sin afectar otras partes del código que usan la clase.
-
Seguridad de los Datos: Al proteger los atributos de acceso directo, se asegura la integridad y coherencia de los datos, evitando que sean modificados de formas no deseadas o inesperadas.
La asociación implica que una clase sea "consciente" de otra y mantenga una referencia de ella.
La asociación implica el uso de propiedades que son instancias de clases personalizadas.
La agregación es una forma de asociación en la que el ciclo de vida de un objeto no depende del otro. Es el comportamiento por defecto.
La composición es otra forma de asociación en la que el ciclo de vida de una clase depende del otro. En este caso, los objetos contenidos no pueden existir independientemente del objeto contenedor. Si el objeto contenedor se destruye, los objetos contenidos también se destruyen.
Note
Todas las asociaciones por defecto son de agregación, a menos que implementemos una lógica específica que haga que sea de composición.
En Programación Orientada a Objetos, las relaciones entre clases pueden representarse como composición o agregación. Estas relaciones pueden ser:
- Uno a Uno: Relación con un único objeto.
- Uno a Muchos: Relación con múltiples objetos.
- Cero a Uno o Cero a Muchos: Menos comunes, reflejan relaciones opcionales.
Una relación donde un objeto está vinculado a otro único objeto.
Ejemplo: Un curso tiene un único profesor.
public class Profesor {
private String nombre;
private String apellido;
}
public class Curso {
private String nombre;
private char division;
private Profesor profesor;
public Profesor getProfesor() {
return profesor;
}
public void setProfesor(Profesor p) {
this.profesor = p;
}
}Una relación donde un objeto está asociado a múltiples objetos de otra clase.
Ejemplo: Un curso tiene muchos estudiantes.
public class Estudiante {
private String nombre;
private String apellido;
}
public class Curso {
private String nombre;
private char division;
private Estudiante[] estudiantes;
public Estudiante[] getEstudiantes() {
return estudiantes;
}
public void setEstudiantes(Estudiante[] e) {
this.estudiantes = e;
}
}- Permite reutilizar el código y organizar clases de forma jerárquica.
- Permite que una clase adquiera propiedades y comportamientos de otra clase (conocida como la clase padre o superclase) y a la vez añadir sus propios atributos y métodos específicos.
- Permite una organización más clara y estructurada del código.
- Java admite la herencia simple (una clase puede heredar SOLO de una clase padre).
- La clase padre o superclase se declara como se haría normalmente.
- Las propiedades, en lugar de ser privadas (
private), deben declararse como protegidas (protected), lo que permité que las propiedades SÓLO puedan ser utilizadas en la clase padre y en las hijas. - En las subclases se utiliza la palabra clave
extendspara indicar que la clase debe heredar todos los atributos y métodos de otra clase:
public class Estudiante extends Persona {
private String grado;
}La clase
Estudiantehereda las propiedades de la clasePersona.
super se utiliza en Java para hacer referencia a los métodos y constructores de la clase padre (superclase). Se usa de manera similar a la palabra clave this, pero en este caso se refiere a la clase superior.
Ejemplo:
// Superclase o clase padre
public class Persona {
protected String nombre;
protected int edad;
public Persona() {
}
public Persona(String nombre, int edad) {
this.nombre = nombre;
this.edad = edad;
}
}
// Subclase o clase hija
public class Estudiante extends Persona {
private String grado;
public Estudiante() {
super(); // Llama al constructor de la clase padre
this.edad = 14;
this.nombre = "Elias";
this.grado = "segundo";
}
public Estudiante(String nombre, int edad, String grado) {
super(nombre, edad); // Llama al constructor de la clase padre con parámetros
this.grado = grado;
}
}
La subclase
Estudianteutilizasuper()para llamar al constructor de la clasePersona. Esto permite que la clase hija acceda a los constructores de la clase padre, lo que es útil cuando la clase padre tiene un constructor con parámetros.Las propiedades
nombreyedadde la clasePersonason accesibles en la claseEstudianteporque están declaradas con el modificador de accesoprotected.
Los métodos de la clase padre pueden ser sobrescritos en la clase hija utilizando la anotación @Override.
public class Persona {
public void hablar() {
System.out.println("Soy una persona");
}
}
public class Estudiante extends Persona {
@Override
public void hablar() {
System.out.println("Soy un estudiante");
}
}Si un objeto de la clase
Estudianteinvoca el métodohablar(), se imprimirá "Soy un estudiante". Si el método no se sobrescribe, se ejecutará el método de la clasePersona, imprimiendo "Soy una persona".
En Java, todos los objetos heredan de una clase base común llamada Object. Esta herencia es implícita, lo que significa que no es necesario declarar extends Object para que ocurra. La clase Object proporciona varios métodos que pueden ser sobrescritos en las clases derivadas para personalizar su comportamiento. Algunos de estos métodos son:
-
equals(): Compara dos objetos para determinar si son iguales. Puede ser sobrescrito en clases personalizadas para comparar los atributos de los objetos en lugar de la referencia en memoria. -
hashCode(): Devuelve un código hash único para un objeto, útil en estructuras de datos como tablas hash. Es recomendable sobrescribirlo si se sobrescribeequals()para asegurar la coherencia. -
toString(): Devuelve una representación en cadena de texto del objeto, útil para imprimir información legible, especialmente para depuración o visualización. -
clone(): Crea una copia superficial del objeto. Este método ha caído en desuso, y se recomienda implementar métodos de copia personalizados. -
finalize(): Se llama antes de que el recolector de basura elimine un objeto, permitiendo la limpieza de recursos. Este método está obsoleto desde Java 9 y no se recomienda su uso. -
getClass(): Obtiene la clase en tiempo de ejecución del objeto. No puede ser sobrescrito. Es útil en operaciones de reflexión. -
notify(),notifyAll(),wait(): Métodos relacionados con la sincronización de hilos.
-
equals()yhashCode(): Sobrescribir estos métodos es crucial cuando se crean objetos personalizados. Permiten comparaciones basadas en los valores de los atributos, en lugar de en la referencia en memoria. -
toString(): Sobrescribir este método permite devolver una representación legible de los atributos del objeto, lo que es útil para su impresión o depuración.
El polimorfismo es un concepto fundamental en la programación orientada a objetos que se refiere a la capacidad de un método u objeto de tomar múltiples formas.
Se produce mediante la sobrecarga y sobrescritura de métodos en una clase. Esto permite que un método tenga múltiples implementaciones con el mismo nombre, pero con diferentes comportamientos según el contexto.
- Sobrecarga de Métodos: Permite que un método tenga el mismo nombre pero diferente número o tipo de parámetros.
- Sobrescritura de Métodos: En una subclase, se redefine un método de la superclase para cambiar su comportamiento.
Se produce cuando una superclase se utiliza para referirse a un objeto de una subclase. También ocurre cuando una clase implementa interfaces. En este caso, un objeto de una subclase puede ser tratado como un objeto de la superclase.
-
Uso de
instanceof: Permite verificar si un objeto es una instancia de una clase o interfaz. - Implementación de Interfaces: Las clases que implementan interfaces permiten que objetos de diferentes clases sean tratados de manera similar si comparten la misma interfaz.
- Herencia única: Una subclase hereda de una sola superclase.
- Herencia jerárquica: Una superclase sirve de base para varias subclases.
- Herencia multinivel: Una clase derivada hereda de una clase base y actúa como base para otra clase.
- Herencia múltiple: No compatible en Java con clases, pero se puede lograr mediante interfaces.
El operador instanceof se usa para verificar si un objeto es una instancia de una clase, subclase o interfaz.
class Animal {}
class Perro extends Animal {}
public class Instanceof {
public static void main(String[] args) {
Animal animal = new Animal();
Animal perro = new Perro();
System.out.println(perro instanceof Animal); // true
System.out.println(perro instanceof Perro); // true
System.out.println(animal instanceof Perro); // false
}
}El Pattern Matching es una técnica que permite verificar si un valor coincide con un patrón específico.
Permite declarar la variable directamente al usar instanceof, eliminando la necesidad de conversión explícita.
public class Main {
public static void main(String[] args) {
Object obj = "Hola mundo";
if (obj instanceof String s) {
System.out.println(s.toLowerCase());
}
}
}(En revisión en Java 20)
Permite usar patrones complejos en declaraciones switch, mejorando su flexibilidad.
class Animal {}
class Perro extends Animal {}
public class Main {
public static void main(String[] args) {
Object[] objects = { "Hola", 10, 20.0, true, new Animal(), new Perro() };
for (Object obj : objects) {
switch (obj) {
case String s -> System.out.println("String: " + s);
case Integer i -> System.out.println("Integer: " + i);
case Double d -> System.out.println("Double: " + d);
case Boolean b -> System.out.println("Boolean: " + b);
case Perro p -> System.out.println("Es un perro");
case Animal a -> System.out.println("Es un animal");
default -> System.out.println("Tipo desconocido");
}
}
}
}- En Java, una clase abstracta es una clase que no se puede instanciar directamente (no se pueden crear objetos directamente).
- Se utilizan como plantillas para derivar subclases concretas que heredan sus atributos y métodos.
- Pueden contener métodos abstractos.
Son útiles porque:
- Proveen una forma de definir una estructura común para todas las subclases.
- Facilitan la reutilización de código y promueven un diseño más limpio y mantenible.
- Ayudan a desarrollar programas más flexibles y escalables.
- Son métodos sin una implementación concreta.
- Los metodos abstractos (y las clases abstractas) ofrecen un mecanismo para definir estructuras y comportamientos genéricos que deben ser implementados por las clases hijas.
-
Declaración sin cuerpo: Solo se especifica el nombre, parámetros y tipo de retorno.
public abstract void hacerSonido();
-
Presente en clases abstractas: Las clases que contienen métodos abstractos deben ser declaradas como
abstract.public abstract class Animal { public abstract void hacerSonido(); }
-
Implementación obligatoria en subclases: Las subclases concretas deben proporcionar la implementación de todos los métodos abstractos.
Ejemplo:
// Clase abstracta
public abstract class Animal {
public abstract void hacerSonido();
}
// Subclase concreta: Perro
public class Perro extends Animal {
@Override
public void hacerSonido() {
System.out.println("Guau Guau");
}
}
// Subclase concreta: Gato
public class Gato extends Animal {
@Override
public void hacerSonido() {
System.out.println("Miau Miau");
}
}
- La clase abstracta
Animaldefine el método abstractohacerSonido().- Las subclases
PerroyGatoimplementan el método con comportamientos específicos: -Perroimprime "Guau Guau". -Gatoimprime "Miau Miau".
- Consistencia: Define un contrato común para las subclases.
- Flexibilidad: Permite implementar detalles específicos según las necesidades de cada subclase.
- Diseño modular: Facilita la extensión y mantenimiento del código.
- Proporciona un medio para definir contratos de comportamiento que pueden ser implementados por cualquier clase.
- Es una colección de métodos abstractos que puede implementar la clase.
- Se pueden implementar múltiples interfaces en la clase.
- Permiten definir métodos a implementar en la clase.
- También permiten definir constantes.
- Propósito: Definen un conjunto de métodos que deben ser implementados por las clases que las utilicen.
- Implementación: Especifican qué hacer, pero no cómo hacerlo.
public interface Interfaz {
public final int CONSTANTE = 10;
public void metodo();
public int sumar();
}La interfaz
Interfazincluye: - Una constanteCONSTANTE. - Dos métodos abstractos:metodo()ysumar().
Implementación de la interfaz en una clase:
public class MiClase implements Interfaz {
@Override
public void metodo() {
System.out.println("Ejecutando método en MiClase");
}
@Override
public int sumar() {
int resultado = 10 + 20;
System.out.println("La suma es: " + resultado);
return resultado;
}
}La clase
MiClase: - Utilizaimplementspara indicar que implementa la interfaz. - Proporciona implementaciones específicas para los métodosmetodo()ysumar(). - Usa@Overridepara asegurar que sobrescribe correctamente los métodos.
- Flexibilidad: Una interfaz puede ser implementada por múltiples clases, lo que permite compartir comportamientos sin usar herencia directa.
- Versatilidad: Ideal para diseñar sistemas modulares y desacoplados.
-
Nombres descriptivos: Las interfaces deben tener nombres en PascalCase que indiquen su propósito, como
SerializableoComparable. -
Variables constantes: Las variables en una interfaz son
public static finalpor defecto, útiles para definir constantes compartidas.
[!NOTE] Material Extra
En Java, una clase puede:
- Extender de una clase padre.
- Heredar y proporcionar implementaciones para métodos abstractos de una clase
- Implementar una o más interfaces, asegurando la implementación de los métodos definidos en ellas.
- Esta capacidad permite combinar herencia y contratos de comportamiento.
Ejemplo:
// Definición de la interfaz
public interface MiClaseInter {
void metodoInterfaz();
}
// Definición de la clase abstracta
public abstract class MiClaseAbstracta {
public abstract void metodoAbstracto();
}
// Clase que extiende la clase abstracta e implementa la interfaz
class MiClase extends MiClaseAbstracta implements MiClaseInter {
@Override
public void metodoAbstracto() {
System.out.println("Implementación del método abstracto");
}
@Override
public void metodoInterfaz() {
System.out.println("Implementación del método de la interfaz");
}
} Con este enfoque, se puede aprovechar la herencia para atributos y métodos, mientras se cumple con el contrato definido por las interfaces.
[!NOTE] Documentación oficial
Una clase anónima es una clase que no tiene un nombre específico y se utiliza para crear una implementación única y concreta de una clase abstracta o una interfaz, generalmente en el momento en que se necesita.
Estas clases son especialmente útiles cuando queremos sobrescribir métodos de una clase abstracta o una interfaz sin crear una clase separada.
- No tiene nombre: Se declara e implementa directamente en el lugar donde se utiliza.
- Implementación específica: Proporciona una implementación concreta para los métodos de una clase abstracta o de una interfaz.
- Uso limitado: Normalmente, se utiliza solo una vez o en un contexto específico.
En este ejemplo, implementamos una clase abstracta llamada Abuelo mediante una clase anónima:
abstract class Abuelo {
abstract void saludar();
}
public class Main {
public static void main(String[] args) {
Abuelo abueloAnonimo = new Abuelo() {
@Override
void saludar() {
System.out.println("Hola, soy una clase anónima que implementa Abuelo.");
}
};
abueloAnonimo.saludar();
}
}Dado que las interfaces son abstractas, también podemos crear una clase anónima que las implemente:
interface MiInterfaz {
void metodo();
}
public class Main {
public static void main(String[] args) {
MiInterfaz interfazAnonima = new MiInterfaz() {
@Override
public void metodo() {
System.out.println("Hola, soy una clase anónima que implementa una interfaz.");
}
};
interfazAnonima.metodo();
}
}- Reducen la cantidad de código cuando la implementación solo se necesita una vez.
- Útiles para sobrescribir rápidamente métodos de clases o interfaces sin crear clases adicionales.
- Debido a que no tienen nombre, no se pueden reutilizar directamente.
- Solo pueden implementar una interfaz o extender una clase abstracta, ya que Java no admite la herencia múltiple directa.
El modificador final se utiliza para garantizar control y seguridad en el diseño de clases y métodos. Cierra la jerarquía de herencia.
-
Clases
final: Prohíben la herencia, cerrando la jerarquía y asegurando la integridad del diseño.public final class Animal { // ... }
-
Métodos
final: Impiden la sobrescritura, preservando la implementación original.public class Animal { public final void hacerSonido() { // ... } }
El uso de final asegura una estructura más robusta y confiable en el código.
Las clases selladas permite restringir la herencia al especificar qué subclases pueden extender una clase sellada. Esto mejora la claridad y seguridad del código, evitando extensiones no deseadas.
- Clases finales: No permiten ninguna extensión.
- Clases selladas: Permiten herencia, pero solo a subclases específicas.
- Final: No puede ser extendida posteriormente.
- Sealed: Permite herencia limitada a subclases explícitas.
- Non-sealed: Permite herencia sin restricciones.
public sealed class FiguraGeometrica permits Triangulo, Circulo, Paralelogramo {
// ...
}Las clases selladas promueven una jerarquía bien definida y un mantenimiento más sencillo del código.
- Son una manera de representar un conjunto limitado de opciones posibles.
- Son un tipo de datos especial que permite definir una colección de constantes nombradas.
La palabra clave enum se utiliza para declarar un enumerador.
Ejemplo:
public enum DiaSemana {LUNES, MARTES, MIERCOLES, JUEVES, VIERNES, SABADO, DOMINGO }-
Convención:
- Los nombres de las constantes en un enum deben escribirse en mayúsculas.
- Es buen práctica que los Enum estén alojados en una carpeta independiente.
- Inmutabilidad: Los valores definidos en un enum son constantes y no pueden cambiarse.
- Uso: Un enum puede declararse dentro o fuera de una clase, pero no dentro de un método.

En Java, los enums son tratados como clases especiales que pueden incluir campos, métodos y constructores. Esto permite asociar valores adicionales a cada constante y definir comportamientos específicos.
public enum DiaSemana {
LUNES("Primer día de la semana"),
MARTES("Segundo día de la semana"),
MIERCOLES("Tercer día de la semana"),
JUEVES("Cuarto día de la semana"),
VIERNES("Quinto día de la semana"),
SABADO("Sexto día de la semana"),
DOMINGO("Séptimo día de la semana");
private final String descripcion;
// Constructor privado para inicializar el campo 'descripcion'
private DiaSemana(String descripcion) {
this.descripcion = descripcion;
}
// Método para obtener la descripción
public String getDescripcion() {
return descripcion;
}
}
- Cada constante del
enumtiene un valor adicional asociado (en este caso,descripcion).- El constructor de un
enumsiempre es privado o tiene un modificador de acceso implícito, ya que no se pueden crear instancias manualmente.- Las constantes de un
enumson finales e inmutables. Por este motivo, no se define un métodosetDescripcion().- Todas las constantes y sus propiedades se cargan durante la inicialización de la clase. No es posible modificar las constantes ni sus valores en tiempo de ejecución.
Ejemplo del uso:
public class Main {
public static void main(String[] args) {
for (DiaSemana dia : DiaSemana.values()) {
System.out.println(dia + ": " + dia.getDescripcion());
}
}
}
// SALIDA
// LUNES: Primer día de la semana
// MARTES: Segundo día de la semana
// ...
// DOMINGO: Séptimo día de la semana
DiaSemana.values()devuelve un arreglo (DiaSemana[]) que contiene todas las constantes delenum, en el mismo orden en el que fueron declaradas.
Enum en Java es una clase final extendida implícitamente por el compilador, por lo que hereda métodos de la clase java.lang.Enum.
-
values()
Devuelve un arreglo con todas las constantes en el orden declarado. -
valueOf(String name)
Busca una constante delenumpor su nombre. -
ordinal()
Devuelve la posición (índice) de la constante en elenum, comenzando desde0. -
name()
Retorna el nombre de la constante como una cadena.
public class App {
public static void main(String[] args) {
DiaSemana dia = DiaSemana.LUNES;
// Acceder a las constantes del enum
System.out.println(dia);
// Obtener todos los valores del enum
DiaSemana[] dias = DiaSemana.values();
for (DiaSemana d : dias) {
System.out.println(d);
}
// Comparar valores de enum
if (dia == DiaSemana.LUNES) {
System.out.println("Es LUNES");
}
// Utilizar enums en estructuras de control
switch (dia) {
case LUNES, MARTES, MIERCOLES, JUEVES, VIERNES ->
System.out.println("Es un día laboral");
case SABADO, DOMINGO -> System.out.println("Es fin de semana");
}
}
}Note
Material Descargable
Material Extra
Una excepción es un evento que ocurre durante la ejecución de un programa y que interrumpe el flujo normal de las instrucciones del mismo.
En Java, existen dos tipos principales de excepciones:
- Marcadas
- No marcadas.
Las excepciones marcadas son aquellas que deben ser manejadas explícitamente en el código mediante una estructura try-catch o declaradas en la firma del método con throws. Estas excepciones son previsibles y representan situaciones que pueden ocurrir durante la ejecución normal del programa.
El compilador de Java exige que se manejen o declaren, asegurando así que el programador considere su posible impacto y mitigue las interrupciones en el flujo del programa.
Ejemplos:
-
IOException-
Descripción: Se lanza durante operaciones fallidas de entrada/salida, como al leer o escribir archivos.
-
Ejemplo:
try { BufferedReader lector = new BufferedReader(new FileReader("ruta/al/archivo")); } catch (IOException e) { System.out.println("Ocurrió un error al leer el archivo"); }
-
-
ClassNotFoundException
-
Descripción: Ocurre cuando se intenta cargar una clase que no existe o no está en el
classpath. -
Ejemplo:
try { Class.forName("ClaseNoExistente"); } catch (ClassNotFoundException e) { System.out.println("La clase no existe"); }
-
Note
El classpath le dice a la JVM y a las herramientas de Java dónde buscar las clases y recursos requeridos por el programa. Si una clase o recurso no se encuentra en el classpath, se producirá un error como ClassNotFoundException o NoClassDefFoundError.
-
FileNotFoundException
-
Descripción: Se lanza cuando se intenta acceder a un archivo que no existe en la ruta especificada.
-
Ejemplo:
try { new FileInputStream("archivo_no_existente.txt"); } catch (FileNotFoundException e) { System.out.println("Archivo no encontrado"); }
-
-
SQLException
-
Descripción: Se lanza cuando ocurre un error relacionado con una operación de base de datos, como consultas o conexión.
-
Ejemplo:
try { // Supongamos que tienes un objeto de conexión "conexion" conexion.executeQuery("SELECT * FROM tabla_no_existente"); } catch (SQLException e) { System.out.println("Error en la consulta SQL"); }
-
Las excepciones marcadas:
- Obligan a manejar errores previsibles: Esto permite al desarrollador implementar soluciones específicas o alertar adecuadamente al usuario.
- Incrementan la robustez del código: Evitan que el programa falle inesperadamente.
- Mejoran la claridad del código: Declaran explícitamente posibles fallos, ayudando a otros desarrolladores a entender mejor el flujo.
Las excepciones no marcadas (Runtime Exceptions) son problemas generados por errores en el código y se consideran bugs. Java no exige que se capturen, ya que asume que el programador evitará estas situaciones con un código adecuado. Las excepciones relacionadas con la lógica de negocio deben ser anticipadas y manejadas por el programador.
Ejemplo:
-
NullPointerException
-
Descripción: Se lanza al intentar acceder a un miembro de un objeto nulo.
-
Ejemplo:
String str = null; try { System.out.println(str.length()); } catch (NullPointerException e) { System.out.println("El objeto es nulo"); }
-
-
ArrayIndexOutOfBoundsException
-
Descripción: Se lanza al intentar acceder a un índice inexistente de un arreglo.
-
Ejemplo:
int[] arr = new int[5]; try { int numero = arr[10]; } catch (ArrayIndexOutOfBoundsException e) { System.out.println("El índice está fuera de los límites del arreglo"); }
-
-
ArithmeticException
-
Descripción: Ocurre al realizar operaciones aritméticas ilegales, como dividir por cero.
-
Ejemplo:
try { int resultado = 10 / 0; } catch (ArithmeticException e) { System.out.println("División por cero"); }
-
-
IllegalArgumentException
-
Descripción: Se lanza al pasar un argumento inapropiado a un método.
-
Ejemplo:
try { Thread.sleep(-1000); } catch (IllegalArgumentException e) { System.out.println("Argumento ilegal"); }
-
-
NumberFormatException
-
Descripción: Se lanza al intentar convertir un
Stringa un número con formato inválido. -
Ejemplo:
try { int num = Integer.parseInt("no_es_un_numero"); } catch (NumberFormatException e) { System.out.println("Formato de número inválido"); }
-
-
ClassCastException
-
Descripción: Ocurre al intentar hacer un casting a un tipo incompatible.
-
Ejemplo:
Object x = new Integer(0); try { System.out.println((String) x); } catch (ClassCastException e) { System.out.println("No se puede convertir a String"); }
-
-
NegativeArraySizeException
-
Descripción: Se lanza al intentar crear un arreglo con tamaño negativo.
-
Ejemplo:
try { int[] arr = new int[-1]; } catch (NegativeArraySizeException e) { System.out.println("El tamaño del arreglo no puede ser negativo"); }
-
-
StringIndexOutOfBoundsException
-
Descripción: Se lanza al intentar acceder a un índice inexistente en un
String. -
Ejemplo:
String str = "Hola"; try { char ch = str.charAt(10); } catch (StringIndexOutOfBoundsException e) { System.out.println("Índice fuera de los límites del String"); }
-
Es posible crear excepciones personalizadas extendiendo alguna clase de la jerarquía de excepciones. Esto permite un manejo de errores más específico y significativo.
Ejemplo:
public class MiExcepcionPersonalizada extends Exception {
// Constructor sin argumentos
public MiExcepcionPersonalizada() {
super();
}
// Constructor con mensaje personalizado
public MiExcepcionPersonalizada(String mensaje) {
super(mensaje);
}
// Constructor con mensaje y causa
public MiExcepcionPersonalizada(String mensaje, Throwable causa) {
super(mensaje, causa);
}
}Ventajas:
- Facilitan la identificación de errores específicos en el código.
- Ofrecen mensajes personalizados que mejoran la claridad al depurar errores.
- Permiten incluir información adicional relevante al contexto del error.
El bloque finally se utiliza para ejecutar código importante como la limpieza de recursos. Este bloque siempre se ejecuta, independientemente de si una excepción fue lanzada o no.
Ejemplo:
public class Main {
public static void main(String[] args) {
try {
// Código que puede lanzar una excepción
} catch (Exception e) {
// Manejar la excepción
} finally {
// Limpiar recursos
}
}
}- Lanza una excepción explícitamente dentro del código.
- Se usa con el operador
newpara crear una instancia de la excepción.
public void metodoA() throws MiExcepcion {
if (condicion) {
throw new MiExcepcion("Mensaje de error");
}
}- Declara en la firma de un método que este puede lanzar una excepción.
- Indica que el método puede propagar la excepción al método que lo llama, por lo que el método invocador debe manejarla o declararla a su vez.
public void metodoB() throws MiExcepcion {
// Código que puede lanzar MiExcepcion
}Note
Diferencia:
-
throw: Lanza una excepción en el cuerpo del método. -
throws: Declara qué excepciones puede lanzar un método.
La clase base para todas las excepciones en Java.
-
Atributos:
-
detailMessage: Mensaje que describe el motivo de la excepción.
-
-
Métodos:
-
getMessage(): Retorna el mensaje de error. -
printStackTrace(): Imprime la pila de llamadas para depuración. -
getCause(): Retorna la causa de la excepción, si existe. -
getStackTrace(): Retorna un array con los elementos de la pila de llamadas.
-
Indica errores graves controlados por la JVM que generalmente no deben ser manejados por la aplicación.
Ejemplos comunes:
-
OutOfMemoryError: La JVM no puede asignar memoria suficiente.int[] matriz = new int[Integer.MAX_VALUE];
-
StackOverflowError: Se produce por una recursión infinita.void funcionRecursiva() { funcionRecursiva(); }
Utilizada para manejar errores en el código.
- Puede ser de dos tipos:
- Excepciones Marcadas (Checked): Deben ser manejadas o declaradas.
- Excepciones No Marcadas (Unchecked): Relacionadas con errores de programación.
Esta jerarquía asegura un manejo estructurado de los errores, separando aquellos controlables por el programador de los errores críticos del sistema.