Java es un lenguaje de programación orientado a objetos (OOP) basado en clases que se basa en el concepto de objetos. Los conceptos OOP (OOP) pretenden mejorar la legibilidad y reutilización del código al definir cómo estructurar un programa Java de manera eficiente. Los principios fundamentales de la programación orientada a objetos son:
  1. Abstracción
  2. Encapsulacion
  3. Herencia
  4. Polimorfismo
  5. Asociación
  6. Agregación
  7. Composición
Java viene con estructuras de código específicas para cada principio OOP. Por ejemplo, la extendspalabra clave para los métodos de herencia o getter y setter para la encapsulación.

¿Qué son los conceptos de POO en Java?

Los conceptos OOP nos permiten crear interacciones específicas entre objetos Java. Permiten reutilizar el código sin crear riesgos de seguridad o hacer que un programa Java sea menos legible.
Aquí están los cuatro principios principales en más detalle.

Abstracción

La abstracción tiene como objetivo ocultar la complejidad de los usuarios y mostrarles solo la información relevante. Por ejemplo, si desea conducir un automóvil, no necesita conocer su funcionamiento interno. Lo mismo ocurre con las clases de Java. Puede ocultar los detalles de la implementación interna utilizando clases abstractas o interfaces. En el nivel abstracto, solo necesita definir las firmas del método (nombre y lista de parámetros) y dejar que cada clase las implemente a su manera.
Abstracción en Java:
  • Oculta la complejidad subyacente de los datos.
  • Ayuda a evitar el código repetitivo.
  • Presenta únicamente la firma de funcionalidad interna.
  • Da flexibilidad a los programadores para cambiar la implementación del comportamiento abstracto.
  • La abstracción parcial (0-100%) se puede lograr con clases abstractas
  • La abstracción total (100%) se puede lograr con interfaces

Encapsulacion

La encapsulación nos permite proteger los datos almacenados en una clase del acceso a todo el sistema. Como su nombre lo indica, protege los contenidos internos de una clase como una cápsula de la vida real. Puede implementar la encapsulación en Java manteniendo los campos (variables de clase) privados y proporcionando métodos públicos de obtención y establecimiento para cada uno de ellos. Java Beans son ejemplos de clases totalmente encapsuladas.
Encapsulación en Java:
  • Restringe el acceso directo a los miembros de datos (campos) de una clase.
  • Los campos se establecen en privado
  • Cada campo tiene un método getter y setter.
  • Métodos de Getter devuelven el campo
  • Los métodos de Setter nos permiten cambiar el valor del campo.

Polimorfismo

[El polimorfismo ( https://en.wikipedia.org/wiki/Polymorphism) se refiere a la capacidad de realizar una determinada acción de diferentes maneras. En Java, el polimorfismo puede tomar dos formas: sobrecarga de métodos y anulación de métodos. La sobrecarga de métodos ocurre cuando varios métodos con el mismo nombre están presentes en una clase. Cuando se llaman, se diferencian por el número, orden y tipos de sus parámetros. La anulación del método se produce cuando la clase secundaria anula un método de su padre.
Polimorfismo en Java:
  • El mismo nombre de método se usa varias veces.
  • Se pueden llamar diferentes métodos del mismo nombre desde el objeto.
  • Todos los objetos Java pueden considerarse polimórficos (como mínimo, son de su propio tipo e instancias de la clase Objeto).
  • Un ejemplo de polimorfismo estático en Java es la sobrecarga de métodos.
  • Ejemplo de polimorfismo dinámico en Java es el método de anulación.

Herencia

La herencia hace posible crear una clase secundaria que hereda los campos y métodos de la clase principal. La clase secundaria puede anular los valores y métodos de la clase principal, sin embargo, no es necesario. También puede agregar nuevos datos y funcionalidad a su padre. Las clases principales también se denominan superclases o clases básicas, mientras que las clases secundarias también se conocen como subclases o clases derivadas. Java usa la extendspalabra clave para implementar el principio de herencia en el código.
Herencia en Java:
  • Una clase (clase secundaria) puede extender otra clase (clase primaria) heredando sus características.
  • Implementa el principio de programación DRY (Don't Repeat Yourself).
  • Mejora la reutilización del código.
  • La herencia multinivel está permitida en Java (una clase secundaria también puede tener su propia clase secundaria).
  • No se permiten múltiples herencias en Java (una clase no puede extender más de una clase).

Asociación

Además de los cuatro principios principales de OOP, Java también trabaja con otros tres conceptos (asociación, agregación, composición) que puede utilizar al diseñar sus programas. La agregaciónes una forma especial de asociación , mientras que la composición es una forma especial de agregación .
Asociación simplemente significa el acto de establecer una relación entre dos clases no relacionadas. Por ejemplo, cuando declara dos campos de diferentes tipos (por ejemplo, CarBicycle) dentro de la misma clase y los hace interactuar entre sí, ha realizado la asociación.
Asociación en Java:
  • Dos clases separadas están asociadas a través de sus objetos.
  • Las dos clases no están relacionadas, cada una puede existir sin la otra.
  • Puede ser una relación de uno a uno, de uno a muchos, de muchos a uno o de muchos a muchos.

Agregación

La agregación es un tipo más estrecho de asociación. Ocurre cuando hay una relación unidireccional (HAS-A) entre las dos clases que asocia a través de sus objetos. Por ejemplo, todo Passengertiene un Carpero un Carno tiene necesariamente un PassengerCuando declara la Passengerclase, puede crear un campo del Cartipo que muestre a qué automóvil pertenece el pasajero. Luego, cuando crea una instancia de un nuevo Passengerobjeto, también puede acceder a los datos almacenados en el relacionado Car.
Agregación en Java:
  • Asociación unidireccional.
  • Representa una relación HAS-A entre dos clases.
  • Sólo una clase depende de la otra.

Composición

La composición es una forma más estricta de agregación. Ocurre cuando las dos clases que asocia son mutuamente dependientes unas de otras y no pueden existir una sin la otra. Por ejemplo, tomar una Cary una Engineclase. Carno se puede ejecutar sin Engine, mientras que Enginetampoco puede funcionar sin estar integrado en a CarEste tipo de relación entre objetos también se llama una relación PARTE-DE.
Composición en Java:
  • Una forma restringida de agregación.
  • Representa una relación PART-OF entre dos clases
  • Ambas clases dependen unas de otras
  • Si una clase deja de existir, la otra no puede sobrevivir sola

Conceptos de OOP en la infografía de Java

Conceptos OOP en Java con ejemplos infográficos.Usa esta infografía en tu blog.

¿Cuáles son las características de la POO?

Ahora, veamos las características de la vida real de los cuatro conceptos principales de POO en Java: abstracción, encapsulación, herencia y polimorfismo.

Abstracción

Con la abstracción, puede ocultar el funcionamiento interno de un objeto y solo mostrar las características que el usuario necesita conocer. Java proporciona dos formas de implementar la abstracción: clases abstractas e interfaces. Con las clases abstractas, puede lograr una abstracción parcial, mientras que las interfaces hacen posible la abstracción total (100%).

Clases abstractas

Una clase abstracta es una superclase (clase principal) que no puede ser instanciada. Debe crear una instancia de una de sus clases secundarias si desea crear un nuevo objeto. Las clases abstractas pueden tener métodos tanto abstractos como concretos. Los métodos abstractos contienen solo la firma del método, mientras que los métodos concretos también declaran un cuerpo del método. Las clases abstractas se definen con la abstractpalabra clave.
En el siguiente ejemplo, puede ver una clase abstracta llamada Animalcon dos métodos abstractos y uno concreto.
abstract class Animal {
// abstract methods
abstract void move();
abstract void eat();

// concrete method
void label() {
System.out.println("Animal's data:");
}
}
Amplíe la Animalclase abstracta con dos clases secundarias: BirdFishAmbos establecieron su propia funcionalidad para las move()eat()abstractos métodos.
class Bird extends Animal {

void move() {
System.out.println("Moves by flying.");
}
void eat() {
System.out.println("Eats birdfood.");
}
}

class Fish extends Animal {
void move() {
System.out.println("Moves by swimming.");
}
void eat() {
System.out.println("Eats seafood.");
}
}
Ahora, pruébalo con las clases TestBirdTestFishAmbos llaman al método concreto ( label()) y los dos métodos abstractos ( move()eat()).
class TestBird {
public static void main(String[] args) {
Animal myBird = new Bird();

myBird.label();
myBird.move();
myBird.eat();
}
}

class TestFish {
public static void main(String[] args) {
Animal myFish = new Fish();

myFish.label();
myFish.move();
myFish.eat();
}
}
En la consola, el método concreto ha sido llamado desde la Animalclase abstracta, mientras que los dos métodos abstractos han sido llamados desde Bird()Fish(), respectivamente.
[Console output of TestBird]
Animal's data:
Moves by flying.
Eats birdfood.

[Console output of TestFish]
Animal's data:
Moves by swimming.
Eats seafood.

Interfaces

Una interfaz es una clase 100% abstracta. Solo puede tener campos estáticos, finales, públicos y métodos abstractos. Con frecuencia, también se lo denomina modelo de una clase. Las interfaces de Java nos permiten implementar múltiples herencias en nuestro código, ya que una clase puede implementar cualquier número de interfaces. Las clases pueden acceder a una interfaz usando la implementspalabra clave.
En el ejemplo, defina dos interfaces, AnimalBirdAnimaltiene dos métodos abstractos, mientras que Birdtiene dos campos estáticos y un método abstracto.
interface Animal {
public void eat();
public void sound();
}

interface Bird {
int numberOfLegs = 2;
String outerCovering = "feather";

public void fly();
}
La clase Eagleimplementa ambas interfaces. Define su propia funcionalidad para los tres métodos abstractos. Los eat()y los sound()métodos provienen de la Animalclase, mientras que fly()viene de Bird.
class Eagle implements Animal, Bird {
public void eat() {
System.out.println("Eats reptiles and amphibians.");
}
public void sound() {
System.out.println("Has a high-pitched whistling sound.");
}
public void fly() {
System.out.println("Flies up to 10,000 feet.");
}
}
En la TestEagleclase de prueba, cree una instancia de un nuevo Eagleobjeto (llamado myEagle) e imprima todos los campos y métodos en la consola.
Como los campos estáticos no pertenecen a un objeto específico sino a toda una clase, debe acceder a ellos desde la Birdinterfaz en lugar del myEagleobjeto.
class TestEagle {
public static void main(String[] args) {
Eagle myEagle = new Eagle();

myEagle.eat();
myEagle.sound();
myEagle.fly();

System.out.println("Number of legs: " + Bird.numberOfLegs);
System.out.println("Outer covering: " + Bird.outerCovering);
}
}
La consola de Java devuelve toda la información a la que desea acceder:
[Console output of TestEagle]
Eats reptiles and amphibians.
Has a high-pitched whistling sound.
Flies up to 10,000 feet.
Number of legs: 2
Outer covering: feather

Encapsulacion

Con la encapsulación, puede proteger los campos de una clase. Para hacerlo, declare los campos como privados y proporcione acceso a ellos con los métodos de obtención y establecimiento.
La Animalsiguiente clase está completamente encapsulada. Tiene tres campos privados y cada uno de ellos tiene su propio conjunto de métodos de obtención y configuración.
class Animal {
private String name;
private double averageWeight;
private int numberOfLegs;

// Getter methods
public String getName() {
return name;
}
public double getAverageWeight() {
return averageWeight;
}
public int getNumberOfLegs() {
return numberOfLegs;
}

// Setter methods
public void setName(String name) {
this.name = name;
}
public void setAverageWeight(double averageWeight) {
this.averageWeight = averageWeight;
}
public void setNumberOfLegs(int numberOfLegs) {
this.numberOfLegs = numberOfLegs;
}
}
La TestAnimalclase primero establece un valor para cada campo con los métodos de establecimiento, luego imprime los valores usando los métodos de obtención.
public class TestAnimal {
public static void main(String[] args) {
Animal myAnimal = new Animal();

myAnimal.setName("Eagle");
myAnimal.setAverageWeight(1.5);
myAnimal.setNumberOfLegs(2);

System.out.println("Name: " + myAnimal.getName());
System.out.println("Average weight: " + myAnimal.getAverageWeight() + "kg");
System.out.println("Number of legs: " + myAnimal.getNumberOfLegs());
}
}
Como puede ver a continuación, la consola de Java devuelve correctamente todos los valores que configuró con los métodos de establecimiento:
[Console output of TestAnimal]
Name: Eagle
Average weight: 1.5kg
Number of legs: 2

Herencia

La herencia nos permite extender una clase con clases secundarias que heredan los campos y métodos de la clase principal. Es una excelente manera de lograr la reutilización del código. En Java, necesitamos usar la extendspalabra clave para crear una clase secundaria.
En el ejemplo, la Eagleclase extiende la Birdclase padre. Hereda todos sus campos y métodos, y define dos campos adicionales que pertenecen solo a Eagle.
class Bird {
public String reproduction = "egg";
public String outerCovering = "feather";

public void flyUp() {
System.out.println("Flying up...");
}
public void flyDown() {
System.out.println("Flying down...");
}
}

class Eagle extends Bird {
public String name = "eagle";
public int lifespan = 15;
}
La TestEagleclase crea una instancia de un nuevo Eagleobjeto e imprime toda la información (tanto los campos y métodos heredados como los dos campos adicionales definidos en la Eagleclase).
class TestEagle {
public static void main(String[] args) {
Eagle myEagle = new Eagle();

System.out.println("Name: " + myEagle.name); System.out.println("Reproduction: " + myEagle.reproduction);
System.out.println("Outer covering: " + myEagle.outerCovering);
System.out.println("Lifespan: " + myEagle.lifespan);
myEagle.flyUp();
myEagle.flyDown();
}
}
Puedes ver la salida de la consola a continuación:
[Console output of TestEagle]
Reproduction: another egg
Outer covering: feather
Lifespan: 15
Flying up...
Flying down...

Polimorfismo

El polimorfismo hace posible utilizar la misma entidad en diferentes formas. En Java, esto significa que puede declarar varios métodos con el mismo nombre hasta que sean diferentes en ciertas características. Java nos proporciona dos formas de implementar el polimorfismo: la sobrecarga de métodos y la cancelación de métodos.

Polimorfismo estático

La sobrecarga de métodos significa que puede tener varios métodos con el mismo nombre dentro de una clase. Sin embargo, el número, los nombres o los tipos de sus parámetros deben ser diferentes.
Por ejemplo, la Bird()siguiente clase tiene tres fly()métodos. El primero no tiene ningún parámetro, el segundo tiene un parámetro ( height) y el tercero tiene dos parámetros ( nameheight).
class Bird {
public void fly() {
System.out.println("The bird is flying.");
}
public void fly(int height) {
System.out.println("The bird is flying " + height + " feet high.");
}
public void fly(String name, int height) {
System.out.println("The " + name + " is flying " + height + " feet high.");
}
}
La clase de prueba crea una instancia de un nuevo Birdobjeto y llama al fly()método tres veces. En primer lugar, sin parámetros, en segundo lugar, con un parámetro entero para height, y en tercer lugar, con dos parámetros para nameheight.
class TestBird {
public static void main(String[] args) {
Bird myBird = new Bird();

myBird.fly();
myBird.fly(10000);
myBird.fly("eagle", 10000);
}
}
En la consola, podemos ver que Java podría haber diferenciado los tres fly()métodos polimórficos :
[Console output of TestBird]
The bird is flying.
The bird is flying 10000 feet high.
The eagle is flying 10000 feet high.

Polimorfismo dinámico

Al utilizar la función de anulación de métodos de Java, puede anular los métodos de una clase principal de su clase secundaria.
La Birdclase extiende la Animalclase en el siguiente ejemplo. Ambos tienen un eat()método. Por defecto, Birdhereda el eat()método de su padre Sin embargo, como también define su propio eat()método, Java anulará el método original y llamará eat()desde la clase secundaria.
class Animal {
public void eat() {
System.out.println("This animal eats insects.");
}
}

class Bird extends Animal {

public void eat() {
System.out.println("This bird eats seeds.");
}

}
La TestBirdclase primero crea una instancia de un nuevo Animalobjeto y llama a su eat()método. Luego, también crea un Birdobjeto y eat()vuelve a llamar al método polimórfico .
class TestBird {
public static void main(String[] args) {
Animal myAnimal = new Animal();
myAnimal.eat();

Bird myBird = new Bird();
myBird.eat();
}
}
La consola devuelve correctamente los valores de los métodos relevantes. Por lo tanto, Java podría haber diferenciado los dos eat()métodos de hecho.
[Console output of TestBird]
This animal eats insects.
This bird eats seeds.

Conclusión

Los conceptos de OOP en Java definen cómo estructurar un problema de Java de manera más eficiente.