CodeGym/Blog Java/Random-FR/La différence entre les classes abstraites et les interfa...
Auteur
Aditi Nawghare
Software Engineer at Siemens

La différence entre les classes abstraites et les interfaces

Publié dans le groupe Random-FR
membres
Salut! Dans cette leçon, nous allons parler de la différence entre les classes abstraites et les interfaces et examiner quelques exemples avec des classes abstraites courantes. La différence entre les classes abstraites et les interfaces - 1Nous avons consacré une leçon séparée aux différences entre une classe abstraite et une interface, car ce sujet est très important. On vous posera des questions sur la différence entre ces concepts dans 90 % des entretiens futurs. Cela signifie que vous devez être sûr de comprendre ce que vous lisez. Et si vous ne comprenez pas bien quelque chose, lisez des sources supplémentaires. Ainsi, nous savons ce qu'est une classe abstraite et ce qu'est une interface. Nous allons maintenant passer en revue leurs différences.
  1. Une interface ne décrit que le comportement. Il n'a pas d'état. Mais une classe abstraite inclut l'état : elle décrit les deux.

    Prenons par exemple la Birdclasse abstraite et l' CanFlyinterface :

    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;
       }
    }

    Créons une MockingJayclasse bird et faisons-en hériter 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());
       }
    }

    Comme vous pouvez le voir, nous pouvons facilement accéder à l'état de la classe abstraite - ses variables specieset age.

    Mais si nous essayons de faire la même chose avec une interface, le tableau est différent. On peut essayer d'y ajouter des 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();
    }

    Nous ne pouvons même pas déclarer de variables privées dans une interface. Pourquoi? Parce que le modificateur privé a été créé pour masquer l'implémentation à l'utilisateur. Et une interface n'a pas d'implémentation à l'intérieur : il n'y a rien à cacher.

    Une interface ne décrit que le comportement. Par conséquent, nous ne pouvons pas implémenter les getters et les setters à l'intérieur d'une interface. C'est la nature des interfaces : elles sont nécessaires pour travailler avec le comportement, pas avec l'état.

    Java 8 a introduit des méthodes par défaut pour les interfaces qui ont une implémentation. Vous les connaissez déjà, nous ne nous répéterons donc pas.

  2. Une classe abstraite relie et unit des classes qui sont très étroitement liées. Dans le même temps, une même interface peut être implémentée par des classes qui n'ont absolument rien en commun.

    Reprenons notre exemple avec les oiseaux.

    Notre Birdclasse abstraite est nécessaire pour créer des oiseaux basés sur cette classe. Juste des oiseaux et rien d'autre ! Bien sûr, il y aura différentes sortes d'oiseaux.

    La différence entre les classes abstraites et les interfaces - 2

    Avec l' CanFlyinterface, chacun s'en sort à sa manière. Il ne décrit que le comportement (vol) associé à son nom. Beaucoup de choses sans rapport « peuvent voler ».

    La différence entre les classes abstraites et les interfaces - 3

    Ces 4 entités ne sont pas liées les unes aux autres. Ils ne sont même pas tous vivants. Cependant, ils ont tous CanFly.

    Nous ne pouvions pas les décrire en utilisant une classe abstraite. Ils ne partagent pas le même état ou des champs identiques. Pour définir un avion, nous aurions probablement besoin de champs pour le modèle, l'année de production et le nombre maximum de passagers. Pour Carlson, nous aurions besoin de champs pour toutes les sucreries qu'il a mangées aujourd'hui et d'une liste des jeux auxquels il jouera avec son petit frère. Pour un moustique, ... euh... je ne sais même pas... Peut-être, un 'niveau de gêne' ? :)

    Le fait est que nous ne pouvons pas utiliser une classe abstraite pour les décrire. Ils sont trop différents. Mais ils ont un comportement commun : ils peuvent voler. Une interface est parfaite pour décrire tout ce qui dans le monde peut voler, nager, sauter ou présenter un autre comportement.

  3. Les classes peuvent implémenter autant d'interfaces que vous le souhaitez, mais elles ne peuvent hériter que d'une seule classe.

    Nous l'avons déjà mentionné plus d'une fois. Java n'a pas d'héritage multiple de classes, mais il prend en charge l'héritage multiple d'interfaces. Ce point découle en partie du précédent : une interface connecte de nombreuses classes différentes qui n'ont souvent rien d'autre en commun, tandis qu'une classe abstraite est créée pour un groupe de classes très étroitement liées. Par conséquent, il est logique que vous ne puissiez hériter que d'une seule de ces classes. Une classe abstraite décrit une relation "est-un".

Interfaces standards: InputStream et OutputStream

Nous avons déjà passé en revue différentes classes responsables des flux d'entrée et de sortie. Considérons InputStreamet OutputStream. En général, ce ne sont pas du tout des interfaces, mais plutôt des classes abstraites entièrement authentiques. Maintenant que vous savez ce que cela signifie, il sera beaucoup plus facile de travailler avec eux :) InputStreamest une classe abstraite responsable de l'entrée d'octets. Java a plusieurs classes qui héritent de InputStream. Chacun d'eux est conçu pour recevoir des données de différentes sources. Parce InputStreamqu'il est le parent, il fournit plusieurs méthodes qui facilitent l'utilisation des flux de données. Chaque descendant de InputStreama ces méthodes :
  • int available()renvoie le nombre d'octets disponibles pour la lecture ;
  • close()ferme le flux d'entrée ;
  • int read()renvoie une représentation entière du prochain octet disponible dans le flux. Si la fin du flux a été atteinte, -1 sera renvoyé ;
  • int read(byte[] buffer)essaie de lire les octets dans le tampon et renvoie le nombre d'octets lus. Lorsqu'il atteint la fin du fichier, il renvoie -1 ;
  • int read(byte[] buffer, int byteOffset, int byteCount)écrit une partie d'un bloc d'octets. Il est utilisé lorsque le tableau d'octets n'a peut-être pas été entièrement rempli. Lorsqu'il atteint la fin du fichier, il renvoie -1 ;
  • long skip(long byteCount)ignore byteCount octets dans le flux d'entrée et renvoie le nombre d'octets ignorés.
Je vous recommande d'étudier la liste complète des méthodes . Il y a en fait plus de dix classes pour enfants. Par exemple, en voici quelques-unes :
  1. FileInputStream: le type le plus courant de InputStream. Il est utilisé pour lire des informations à partir d'un fichier ;
  2. StringBufferInputStream: Un autre type utile de InputStream. Il convertit une chaîne en un InputStream;
  3. BufferedInputStream: Un flux d'entrée mis en mémoire tampon. Il est utilisé le plus souvent pour augmenter les performances.
Rappelez-vous quand nous sommes allés BufferedReaderet avons dit que vous n'étiez pas obligé de l'utiliser ? Quand on écrit :
BufferedReader reader = new BufferedReader(new InputStreamReader(System.in))
…vous n'êtes pas obligé d'utiliser BufferedReader: An InputStreamReaderpeut faire le travail. Mais BufferedReaderaméliore les performances et peut également lire des lignes entières de données plutôt que des caractères individuels. La même chose s'applique à BufferedInputStream! La classe accumule les données d'entrée dans un tampon spécial sans accéder constamment au périphérique d'entrée. Prenons un exemple :
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();
       }
   }
}
Dans cet exemple, nous lisons les données d'un fichier situé sur un ordinateur à ' D:/Users/UserName/someFile.txt '. Nous créons 2 objets - a FileInputStreamet a BufferedInputStreamqui "l'enveloppent". Ensuite, nous lisons les octets du fichier et les convertissons en caractères. Et on fait ça jusqu'à la fin du fichier. Comme vous pouvez le voir, il n'y a rien de compliqué ici. Vous pouvez copier ce code et l'exécuter sur un vrai fichier sur votre ordinateur :) La OutputStreamclasse est une classe abstraite qui représente un flux de sortie d'octets. Comme vous le savez déjà, c'est l'opposé d'un InputStream. Il n'est pas responsable de la lecture de données quelque part, mais plutôt de l'envoi de données quelque part . Comme InputStream, cette classe abstraite donne à tous ses descendants un ensemble de méthodes pratiques :
  • void close()ferme le flux de sortie ;
  • void flush()efface tous les tampons de sortie ;
  • abstract void write(int oneByte)écrit 1 octet dans le flux de sortie ;
  • void write(byte[] buffer)écrit un tableau d'octets dans le flux de sortie ;
  • void write(byte[] buffer, int offset, int count)écrit une plage de nombre d'octets à partir d'un tableau, en commençant à la position de décalage.
Voici quelques-uns des descendants de la OutputStreamclasse :
  1. DataOutputStream. Un flux de sortie qui inclut des méthodes pour écrire des types de données Java standard.

    Une classe très simple pour écrire des types de données et des chaînes Java primitifs. Vous comprendrez probablement le code suivant même sans explication :

    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);
    
       }
    }

    Il a des méthodes distinctes pour chaque type — writeDouble(), writeLong(), writeShort(), etc.


  2. FileOutputStream. Cette classe implémente un mécanisme d'envoi de données vers un fichier sur disque. Au fait, nous l'avons déjà utilisé dans le dernier exemple. As-tu remarqué? Nous l'avons transmis à DataOutputStream, qui a agi comme un "wrapper".

  3. BufferedOutputStream. Un flux de sortie mis en mémoire tampon. Il n'y a rien de compliqué ici non plus. Son but est analogue à BufferedInputStream(ou BufferedReader). Au lieu de la lecture séquentielle habituelle des données, il écrit les données à l'aide d'un tampon "cumulatif" spécial. La mémoire tampon permet de réduire le nombre d'accès au puits de données, augmentant ainsi les performances.

    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);
         }
    }

    Encore une fois, vous pouvez jouer avec ce code vous-même et vérifier qu'il fonctionnera sur de vrais fichiers sur votre ordinateur.

Nous aurons une leçon séparée sur FileInputStream, FileOutputStreamet BuffreredInputStream, donc c'est assez d'informations pour une première connaissance. C'est ça! Nous espérons que vous comprenez les différences entre les interfaces et les classes abstraites et que vous êtes prêt à répondre à toutes les questions, même les questions pièges :)
Commentaires
  • Populaires
  • Nouveau
  • Anciennes
Tu dois être connecté(e) pour laisser un commentaire
Cette page ne comporte pas encore de commentaires