Corrigé QCM Programmation 1 -Concours QCM

Ce document présente le corrigé détaillé d'un Questionnaire à Choix Multiples (QCM) de Programmation 1, spécifiquement élaboré pour les étudiants de Licence d'informatique. Datant du 1er décembre 2004, il constitue une ressource pédagogique essentielle pour l'approfondissement des concepts fondamentaux de la programmation orientée objet, avec un accent particulier sur le langage Java.

Ce corrigé couvre un éventail de notions cruciales, incluant l'encapsulation, le fonctionnement de la JVM, la gestion des objets et des références, le transtypage, ainsi que les subtilités des méthodes d'instance et statiques.

Corrigé QCM Programmation 1 -Concours QCM

Corrigé QCM Programmation 1 -Concours QCM

Télécharger PDF

Corrigé du QCM de Programmation 1

Ce document présente le corrigé détaillé d'un Questionnaire à Choix Multiples (QCM) sur les fondamentaux de la programmation, spécialement conçu pour les étudiants en Licence d'informatique. Il aborde des concepts clés de la programmation orientée objet et de Java.

Questions Générales

Barème : bonne réponse = 1 pt ; mauvaise réponse = -0,5 pt ; pas de réponse = 0 pt.

Question 1 : L'Encapsulation en Programmation Orientée Objet

Considérez le code Java suivant :

class B {
    private int x;
    private float y;

    public void setX(int x) {
        this.x = x;
    }

    public void setY(float y) {
        this.y = y;
    }
}

Le code ci-dessus est une illustration du principe d'encapsulation. L'encapsulation est un concept fondamental de la programmation orientée objet qui consiste à regrouper les données (ici, x et y) et les méthodes (setX, setY) qui opèrent sur ces données au sein d'une même unité, la classe. En déclarant les attributs x et y comme private, leur accès direct est restreint, et leur modification est contrôlée uniquement par des méthodes publiques, ce qui protège l'intégrité de l'objet.

Question 2 : Rôle de la JVM

La fonction principale de la Machine Virtuelle Java (JVM) est d'interpréter le bytecode. Le bytecode est un ensemble d'instructions de haut niveau, indépendantes de la plateforme, générées par le compilateur Java à partir du code source. La JVM lit et exécute ce bytecode, permettant ainsi aux programmes Java d'être portables et de fonctionner sur n'importe quel système d'exploitation pourvu d'une JVM.

Question 3 : Langages Orientés Objet

Parmi les langages de programmation, le C n'est pas un langage orienté objet. En revanche, SIMULA est généralement reconnu comme le premier langage à avoir introduit les concepts de l'orientation objet, tandis que C++ est une extension du langage C qui y ajoute des fonctionnalités orientées objet.

Question 4 : Héritage dans les classes Java

En Java, toutes les classes héritent implicitement ou explicitement de la classe Object. Cela signifie que Object est la super-classe ultime de toutes les classes dans la hiérarchie de classes Java, fournissant des méthodes de base que toutes les classes peuvent utiliser ou redéfinir.

Question 5 : Passage de paramètres en Java

Le passage de paramètres en Java est toujours effectué par valeur. Cela signifie qu'une copie de la valeur du paramètre est transmise à la méthode. Pour les types primitifs, c'est la valeur elle-même qui est copiée. Pour les objets, ce sont les références aux objets qui sont passées par valeur. Bien que la référence soit copiée, les modifications apportées à l'objet via cette référence dans la méthode affecteront l'objet original.

Question 6 : Communication entre Objets

Un objet traite un message reçu en exécutant une méthode d'instance. C'est simplement une terminologie différente, souvent utilisée dans le contexte de la programmation orientée objet, pour décrire le mécanisme par lequel un objet invoque une fonction (méthode) d'un autre objet ou de lui-même pour accomplir une tâche.

Questions Spécifiques

Barème : bonne réponse = 3 pt ; mauvaise réponse = -1 pt ; pas de réponse = 0 pt.

Question 7 : Gestion des Instances et du Garbage Collector

Combien d'instances de la classe A sont créées pendant l'exécution du code suivant ? Combien en reste-t-il après le passage du Garbage Collector ?

A u, b, c;
A a = new A();
b = new A();
c = b;
a = b;

Deux instances de la classe A sont créées par les deux appels à new A(). Initialement, la première instance est référencée par a et la deuxième par b. L'instruction a = b; réassigne la référence a pour qu'elle pointe également vers la deuxième instance. La première instance n'est alors plus référencée par aucune variable accessible et devient "orpheline". Par la suite, cette instance non référencée sera automatiquement détectée et nettoyée de la mémoire par le Garbage Collector de Java.

La réponse est donc : 2 instances créées ; 1 instance restante après le passage du Garbage Collector.

Question 8 : Polymorphisme, Upcasting et Downcasting

Étant donné que la classe Sardine étend la classe Poisson, trouvez la ligne de code qui compile correctement mais produit une erreur à l'exécution parmi les propositions suivantes :

  1. Poisson y = new Poisson(); Sardine x = (Sardine)y; Poisson z = x;

    Cette séquence de code compile sans erreur. Le compilateur accepte l'affectation sans transtypage (upcasting implicite de Sardine vers Poisson), le downcasting explicite ((Sardine)y), et l'upcasting implicite. Cependant, à l'exécution, l'objet référencé par y est un simple Poisson, et tenter de le transtyper en Sardine (un type plus spécifique) via un downcasting explicite provoquera une ClassCastException. Cette ligne correspond à l'énoncé.

  2. Sardine y = new Sardine(); Poisson x = y; Sardine z = (Sardine)x;

    Cette séquence compile correctement. Il y a une affectation simple, un upcasting implicite (Sardine en Poisson), et un downcasting explicite. À l'exécution, le downcasting (Sardine)x se déroule sans erreur, car l'objet référencé par x est bien une instance de Sardine à l'origine. Aucune erreur n'est produite.

  3. Poisson y = new Sardine(); Object x = y; Sardine z = x;

    La dernière affectation, Sardine z = x;, est un downcasting implicite d'un Object vers un Sardine, ce qui est interdit en Java sans transtypage explicite. Cette ligne générera une erreur à la compilation.

  4. Poisson y = new Poisson(); Sardine z = new Sardine(); y = z;

    Cette séquence ne contient aucune opération de transtypage illégale. L'affectation y = z; est un upcasting implicite valide (une Sardine peut être traitée comme un Poisson). Le code compilera et s'exécutera sans problème.

Question 9 : Variables Statiques et d'Instance

Pour la classe D définie comme suit :

class D {
    public static int x;
    public int y;

    public static void travailler() { // Ajout de 'void' pour une syntaxe Java correcte
        x++;
    }

    public D() {
        x++;
        y--;
    }
}

Qu'affichera le code suivant ?

D.travailler();
D a = new D();
D b = new D();
a.travailler();
System.out.println(b.x + " et " + b.y);

Analyse des valeurs :

  • x est une variable de classe (déclarée static). Elle est unique pour toute la classe D et partagée par toutes ses instances. Sa valeur est initialisée à 0 par défaut.
    • D.travailler(); incrémente x (x devient 1).
    • D a = new D(); appelle le constructeur de D, qui incrémente x (x devient 2).
    • D b = new D(); appelle le constructeur de D, qui incrémente x (x devient 3).
    • a.travailler(); appelle la méthode statique travailler() (équivalent à D.travailler()), qui incrémente x (x devient 4).
    La valeur finale de x est donc 4.
  • y est une variable d'instance. Chaque objet de la classe D possède sa propre variable y, initialisée à 0 par défaut.
    • La variable b.y est affectée uniquement par le constructeur de b. Lors de l'appel à new D() pour b, le constructeur y-- est exécuté.
    La valeur finale de b.y est donc -1.

La sortie affichée sera : 4 et -1.

Note : Dans la définition de la classe D fournie, la méthode travailler() manquait du type de retour void. Pour être un code Java valide, elle devrait être déclarée comme public static void travailler().

Question 10 : Chaînage de Constructeurs et Initialisation en Java

Pour les classes Oeuf et Poule définies comme suit :

class Oeuf {
    public int x;

    public Oeuf() {
        x = 5;
    }

    public Oeuf(int y) {
        x = y;
    }
}

class Poule extends Oeuf {
    public Poule() {
        // Appel implicite à super() qui exécute Oeuf()
    }

    public Poule(int i) {
        this(); // Appelle le constructeur Poule() de la même classe
        x = x * i;
    }

    public Poule(String s) {
        super(33); // Appelle le constructeur Oeuf(int y) de la super-classe
        x--;
    }
}

Qu'affichera le code suivant ?

Poule b1 = new Poule("2004");
Poule b2 = new Poule(2004);
Poule b3 = new Poule();
System.out.println(b1.x + " et " + b2.x + " et encore " + b3.x);

Analyse des initialisations :

  • Pour b1 = new Poule("2004"); :
    • Le constructeur Poule(String s) est appelé.
    • Il contient un appel explicite super(33), ce qui exécute Oeuf(int y). b1.x est initialisé à 33.
    • Après l'appel à super(), l'instruction x-- est exécutée dans Poule(String s). Donc, b1.x devient 32.
    La valeur finale de b1.x est 32.
  • Pour b2 = new Poule(2004); :
    • Le constructeur Poule(int i) est appelé.
    • Il contient un appel explicite this(), ce qui exécute le constructeur par défaut Poule().
    • Le constructeur Poule() contient un appel implicite super(), ce qui exécute le constructeur par défaut Oeuf(). b2.x est initialisé à 5.
    • De retour dans Poule(int i), l'instruction x = x * i; est exécutée. Donc, b2.x = 5 * 2004, ce qui donne 10020.
    La valeur finale de b2.x est 10020.
  • Pour b3 = new Poule(); :
    • Le constructeur Poule() est appelé.
    • Il contient un appel implicite super(), ce qui exécute le constructeur par défaut Oeuf(). b3.x est initialisé à 5.
    La valeur finale de b3.x est 5.

La sortie affichée sera : 32 et 10020 et encore 5.

Question 11 : Liaison Statique vs. Dynamique (Polymorphisme)

Pour les classes A et B définies comme suit :

class A {
    public int f(int x) {
        return (x + 1);
    }

    public static int g(int x) {
        return 6;
    }
}

class B extends A {
    public int f(int x) {
        return (x + 2);
    }

    public static int g(int x) {
        return (x + 4);
    }
}

Qu'affichera le code suivant ?

B b = new B();
A a = b;
System.out.println(a.f(2) * a.g(3));

Analyse de l'exécution :

  • Pour la méthode d'instance f :
    • La méthode f est définie dans la classe A et redéfinie (surchargée) dans la classe B.
    • Le choix de la version de f à exécuter s'effectue à l'exécution (mécanisme de liaison dynamique ou polymorphisme) et dépend du type réel de l'objet référencé.
    • Puisque la variable a, bien que déclarée de type A, référence en réalité un objet de type B (A a = b;), la JVM exécutera la méthode f de la classe B.
    • a.f(2) appellera donc B.f(2), ce qui retourne (2 + 2) = 4.
  • Pour la méthode statique g :
    • Les méthodes statiques sont associées à une classe et non à une instance. Leur version est choisie statiquement par le compilateur (liaison statique), basée sur le type déclaré de la variable, et non sur le type réel de l'objet.
    • Ici, a est déclarée de type A. Par conséquent, a.g(3) est interprété comme un appel à la méthode statique A.g(3).
    • A.g(3) retourne 6.

Le calcul final effectué par System.out.println() sera 4 * 6 = 24.

La sortie affichée sera donc : 24.

Foire Aux Questions (FAQ) sur la Programmation Orientée Objet en Java

Qu'est-ce que l'encapsulation en programmation orientée objet ?

L'encapsulation est un principe fondamental de la programmation orientée objet (POO) qui consiste à regrouper les données (attributs) et les méthodes (fonctions) qui les manipulent au sein d'une seule entité, la classe. Elle permet de masquer les détails d'implémentation internes de l'objet et de protéger les données contre les accès et modifications directs non autorisés. L'accès aux données se fait de manière contrôlée, généralement via des méthodes publiques appelées "getters" et "setters", garantissant ainsi la cohérence et l'intégrité de l'état de l'objet.

Quelle est la différence entre le passage par valeur et le passage par référence en Java ?

En Java, tous les paramètres sont passés par valeur. Cela signifie qu'une copie de la valeur du paramètre est transmise à la méthode appelée. Pour les types primitifs (comme int, float, boolean), c'est la valeur elle-même qui est copiée. Ainsi, modifier ce paramètre dans la méthode n'affecte pas la variable d'origine. Pour les objets, c'est la référence (l'adresse mémoire) de l'objet qui est copiée par valeur. La méthode reçoit donc une copie de cette référence. Elle peut utiliser cette référence pour modifier l'état interne de l'objet original, mais elle ne peut pas changer la référence elle-même pour faire pointer la variable d'origine vers un nouvel objet.

Quel est le rôle du Garbage Collector en Java ?

Le Garbage Collector (GC), ou "ramasse-miettes", est un mécanisme automatique de gestion de la mémoire en Java, intégré à la JVM. Son rôle principal est de libérer l'espace mémoire occupé par les objets qui ne sont plus référencés par aucune partie active du programme. En d'autres termes, il identifie et supprime les objets inutilisés (les "déchets") pour rendre leur mémoire disponible. Cela évite aux développeurs de devoir gérer manuellement l'allocation et la libération de la mémoire, réduisant ainsi les risques de fuites de mémoire et de problèmes liés à la gestion manuelle.

Cela peut vous intéresser :

Partagez vos remarques, questions , propositions d'amélioration ou d'autres cours à ajouter dans notre site

Enregistrer un commentaire (0)
Plus récente Plus ancienne