CodeGym /Blog Java /Random-ES /La diferencia entre clases abstractas e interfaces.
Autor
Aditi Nawghare
Software Engineer at Siemens

La diferencia entre clases abstractas e interfaces.

Publicado en el grupo Random-ES
¡Hola! En esta lección, hablaremos sobre cómo las clases abstractas difieren de las interfaces y consideraremos algunos ejemplos con clases abstractas comunes. La diferencia entre clases abstractas e interfaces - 1Hemos dedicado una lección separada a las diferencias entre una clase abstracta y una interfaz, porque este tema es muy importante. Se le preguntará acerca de la diferencia entre estos conceptos en el 90% de las entrevistas futuras. Eso significa que debe asegurarse de averiguar lo que está leyendo. Y si no entiende completamente algo, lea fuentes adicionales. Entonces, sabemos qué es una clase abstracta y qué es una interfaz. Ahora repasaremos sus diferencias.
  1. Una interfaz solo describe el comportamiento. No tiene estado. Pero una clase abstracta incluye estado: describe ambos.

    Por ejemplo, tome la Birdclase abstracta y la CanFlyinterfaz:

    
    public abstract class Bird {
       private String species;
       private int age;
    
       public abstract void fly();
    
       public String getSpecies() {
           return species;
       }
    
       public void setSpecies(String species) {
           this.species = species;
       }
    
       public int getAge() {
           return age;
       }
    
       public void setAge(int age) {
           this.age = age;
       }
    }
    

    Vamos a crear una MockingJayclase de pájaro y hacer que herede Bird:

    
    public class MockingJay extends Bird {
    
       @Override
       public void fly() {
           System.out.println("Fly, bird!");
       }
    
       public static void main(String[] args) {
    
           MockingJay someBird = new MockingJay();
           someBird.setAge(19);
           System.out.println(someBird.getAge());
       }
    }
    

    Como puede ver, podemos acceder fácilmente al estado de la clase abstracta: sus speciesy agevariables.

    Pero si tratamos de hacer lo mismo con una interfaz, el panorama es diferente. Podemos intentar agregarle variables:

    
    public interface CanFly {
    
       String species = new String();
       int age = 10;
    
       public void fly();
    }
    
    public interface CanFly {
    
       private String species = new String(); // Error
       private int age = 10; // Another error
    
       public void fly();
    }
    

    Ni siquiera podemos declarar variables privadas dentro de una interfaz. ¿Por qué? Porque el modificador privado se creó para ocultar la implementación al usuario. Y una interfaz no tiene implementación en su interior: no hay nada que ocultar.

    Una interfaz solo describe el comportamiento. En consecuencia, no podemos implementar getters y setters dentro de una interfaz. Esta es la naturaleza de las interfaces: son necesarias para trabajar con el comportamiento, no con el estado.

    Java 8 introdujo métodos predeterminados para las interfaces que tienen una implementación. Ya los conoces, así que no nos repetiremos.

  2. Una clase abstracta conecta y une clases que están muy relacionadas. Al mismo tiempo, una sola interfaz puede ser implementada por clases que no tienen absolutamente nada en común.

    Volvamos a nuestro ejemplo con las aves.

    Nuestra Birdclase abstracta es necesaria para crear pájaros basados ​​en esa clase. ¡Solo pájaros y nada más! Por supuesto, habrá diferentes tipos de pájaros.

    La diferencia entre clases abstractas e interfaces - 2

    Con la CanFlyinterfaz, cada uno se las arregla a su manera. Solo describe el comportamiento (volar) asociado con su nombre. Muchas cosas no relacionadas 'pueden volar'.

    La diferencia entre clases abstractas e interfaces - 3

    Estas 4 entidades no están relacionadas entre sí. Ni siquiera están todos vivos. Sin embargo, todos ellos CanFly.

    No podríamos describirlos usando una clase abstracta. No comparten el mismo estado ni campos idénticos. Para definir un avión, probablemente necesitaríamos campos para el modelo, el año de producción y el número máximo de pasajeros. Para Carlson, necesitaríamos campos para todos los dulces que comió hoy y una lista de los juegos que jugará con su hermanito. Para un mosquito, ... eh... Ni siquiera sé... ¿Tal vez, un 'nivel de molestia'? :)

    El punto es que no podemos usar una clase abstracta para describirlos. Son demasiado diferentes. Pero sí tienen un comportamiento compartido: pueden volar. Una interfaz es perfecta para describir todo lo que hay en el mundo que puede volar, nadar, saltar o exhibir algún otro comportamiento.

  3. Las clases pueden implementar tantas interfaces como desee, pero solo pueden heredar una clase.

    Ya lo hemos mencionado más de una vez. Java no tiene herencia múltiple de clases, pero admite herencia múltiple de interfaces. Este punto se deriva en parte del anterior: una interfaz conecta muchas clases diferentes que a menudo no tienen nada más en común, mientras que una clase abstracta se crea para un grupo de clases muy relacionadas. Por lo tanto, tiene sentido que solo pueda heredar una de esas clases. Una clase abstracta describe una relación 'es-un'.

Interfaces estándar: InputStream y OutputStream

Ya hemos repasado varias clases responsables de los flujos de entrada y salida. Consideremos InputStreamy OutputStream. En general, estas no son interfaces en absoluto, sino clases abstractas completamente genuinas. Ahora ya sabe lo que eso significa, por lo que será mucho más fácil trabajar con ellos :) InputStreames una clase abstracta responsable de la entrada de bytes. Java tiene varias clases que heredan InputStream. Cada uno de ellos está diseñado para recibir datos de diferentes fuentes. Debido InputStreama que es el principal, proporciona varios métodos que facilitan el trabajo con flujos de datos. Cada descendiente de InputStreamtiene estos métodos:
  • int available()devuelve el número de bytes disponibles para lectura;
  • close()cierra el flujo de entrada;
  • int read()devuelve una representación entera del siguiente byte disponible en la secuencia. Si se ha llegado al final de la transmisión, se devolverá -1;
  • int read(byte[] buffer)intenta leer bytes en el búfer y devuelve el número de bytes leídos. Cuando llega al final del archivo, devuelve -1;
  • int read(byte[] buffer, int byteOffset, int byteCount)escribe parte de un bloque de bytes. Se usa cuando es posible que la matriz de bytes no se haya llenado por completo. Cuando llega al final del archivo, devuelve -1;
  • long skip(long byteCount)omite byteCount bytes en el flujo de entrada y devuelve el número de bytes ignorados.
Te recomiendo que estudies la lista completa de métodos . En realidad, hay más de diez clases de niños. Por ejemplo, aquí hay algunos:
  1. FileInputStream: el tipo más común de InputStream. Se utiliza para leer información de un archivo;
  2. StringBufferInputStream: Otro tipo útil de InputStream. Convierte una cadena en un InputStream;
  3. BufferedInputStream: un flujo de entrada almacenado en búfer. Se utiliza con mayor frecuencia para aumentar el rendimiento.
¿Recuerdas cuando fuimos BufferedReadery dijimos que no tienes que usarlo? Cuando escribimos:

BufferedReader reader = new BufferedReader(new InputStreamReader(System.in))
…no tienes que usar BufferedReader: An InputStreamReaderpuede hacer el trabajo. Pero BufferedReadermejora el rendimiento y también puede leer líneas completas de datos en lugar de caracteres individuales. Lo mismo se aplica a BufferedInputStream! La clase acumula datos de entrada en un búfer especial sin acceder constantemente al dispositivo de entrada. Consideremos un ejemplo:

import java.io.BufferedInputStream;
import java.io.FileInputStream;
import java.io.InputStream;

public class BufferedInputExample {

   public static void main(String[] args) throws Exception {
       InputStream inputStream = null;
       BufferedInputStream buffer = null;

       try {

           inputStream = new FileInputStream("D:/Users/UserName/someFile.txt");

           buffer = new BufferedInputStream(inputStream);

           while(buffer.available()>0) {

               char c = (char)buffer.read();

                System.out.println("Character read: " + c);
           }
       } catch(Exception e) {

           e.printStackTrace();

       } finally {

           inputStream.close();
           buffer.close();
       }
   }
}
En este ejemplo, leemos datos de un archivo ubicado en una computadora en ' D:/Users/UserName/someFile.txt '. Creamos 2 objetos: ay FileInputStreama BufferedInputStreamque lo 'envuelve'. Luego leemos los bytes del archivo y los convertimos en caracteres. Y lo hacemos hasta que finaliza el archivo. Como puedes ver, no hay nada complicado aquí. Puede copiar este código y ejecutarlo en un archivo real en su computadora :) La OutputStreamclase es una clase abstracta que representa un flujo de salida de bytes. Como ya sabes, esto es lo opuesto a un InputStream. No es responsable de leer datos de algún lugar, sino de enviar datos a algún lugar . Al igual que InputStream, esta clase abstracta brinda a todos sus descendientes un conjunto de métodos convenientes:
  • void close()cierra el flujo de salida;
  • void flush()borra todos los búferes de salida;
  • abstract void write(int oneByte)escribe 1 byte en el flujo de salida;
  • void write(byte[] buffer)escribe una matriz de bytes en el flujo de salida;
  • void write(byte[] buffer, int offset, int count)escribe un rango de bytes de conteo de una matriz, comenzando en la posición de desplazamiento.
Estos son algunos de los descendientes de la OutputStreamclase:
  1. DataOutputStream. Un flujo de salida que incluye métodos para escribir tipos de datos Java estándar.

    Una clase muy simple para escribir cadenas y tipos de datos Java primitivos. Probablemente entenderá el siguiente código incluso sin una explicación:

    
    import java.io.*;
    
    public class DataOutputStreamExample {
    
       public static void main(String[] args) throws IOException {
    
           DataOutputStream dos = new DataOutputStream(new FileOutputStream("testFile.txt"));
    
           dos.writeUTF("SomeString");
           dos.writeInt(22);
           dos.writeDouble(1.21323);
           dos.writeBoolean(true);
    
       }
    }
    

    Tiene métodos separados para cada tipo: writeDouble(), writeLong(), writeShort(), etc.


  2. FileOutputStream. Esta clase implementa un mecanismo para enviar datos a un archivo en el disco. Por cierto, ya lo usamos en el último ejemplo. ¿Te diste cuenta? Lo pasamos a DataOutputStream, que actuó como un 'envoltorio'.

  3. BufferedOutputStream. Un flujo de salida almacenado en búfer. Tampoco hay nada complicado aquí. Su propósito es análogo a BufferedInputStream(o BufferedReader). En lugar de la lectura secuencial habitual de datos, escribe datos utilizando un búfer 'acumulativo' especial. El búfer permite reducir el número de veces que se accede al sumidero de datos, lo que aumenta el rendimiento.

    
    import java.io.*;
    
    public class DataOutputStreamExample {
    
         public static void main(String[] args) throws IOException {
    
               FileOutputStream outputStream = new FileOutputStream("D:/Users/Username/someFile.txt");
               BufferedOutputStream bufferedStream = new BufferedOutputStream(outputStream);
    
               String text = "I love Java!"; // We'll convert this string to a byte array and write it to a file
    
               byte[] buffer = text.getBytes();
    
               bufferedStream.write(buffer, 0, buffer.length);
         }
    }
    

    Nuevamente, puede jugar con este código usted mismo y verificar que funcionará en archivos reales en su computadora.

Tendremos una lección separada sobre FileInputStream, FileOutputStreamy BuffreredInputStream, por lo que esta es suficiente información para un primer contacto. ¡Eso es todo! Esperamos que comprenda las diferencias entre las interfaces y las clases abstractas y que esté listo para responder cualquier pregunta, incluso las preguntas engañosas :)
Comentarios
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION