CodeGym /Blog Java /Random-FR /Explorer les questions et réponses d'un entretien d'embau...
John Squirrels
Niveau 41
San Francisco

Explorer les questions et réponses d'un entretien d'embauche pour un poste de développeur Java. Partie 2

Publié dans le groupe Random-FR
Re-bonjour à tous ! Nous continuons à chercher des réponses aux questions des développeurs Java juniors, intermédiaires et seniors. Les questions sont super intéressantes. Personnellement, j'aime les analyser, car cela m'aide à trouver des lacunes dans mes connaissances théoriques, et parfois dans les endroits les plus inattendus. Explorer les questions et réponses d'un entretien d'embauche pour un poste de développeur Java.  Partie 2 - 1La partie précédente se trouve dans cet article . Mais avant de commencer, je tiens à vous rappeler que :
  1. Je sauterai les questions qui chevauchent cette série d'articles afin de ne pas dupliquer inutilement les informations. Je recommande de lire ces articles car ils couvrent les questions d'entretien Java Core les plus courantes (populaires).
  2. Je pourrais décrire les réponses plus en détail, mais je ne le ferai pas, car chaque réponse pourrait alors s'éterniser sur tout l'article. Et personne ne vous demandera ce niveau de détail lors d’un entretien d’embauche.
Je laisserai des liens pour une étude plus approfondie si vous le souhaitez. Volons!

11. Nommez toutes les méthodes de la classe Object

La classe Object dispose de 11 méthodes :
  1. Class<?> getClass() — récupère la classe de l'objet actuel ;

  2. int hashCode() — récupère le code de hachage de l'objet actuel ;

  3. boolean equals(Object obj) — compare l'objet actuel avec un autre objet ;

  4. Object clone() — crée et renvoie une copie de l'objet actuel ;

  5. String toString() — obtient la représentation sous forme de chaîne de l'objet ;

  6. void notify() — réveille un thread en attente sur le moniteur de cet objet (le choix du thread est aléatoire) ;

  7. void notifyAll() — réveille tous les threads en attente sur le moniteur de cet objet ;

  8. void wait() — fait attendre le thread actuel sur le moniteur actuel (gele le thread actuel) jusqu'à ce qu'un appel notify ou notifyAll réveille le thread (ne fonctionne que dans un bloc synchronisé) ;

  9. void wait(long timeout) — fait attendre le thread actuel sur le moniteur actuel (sur le bloc synchronisé actuel), mais avec un délai d'attente pour quitter l'état d'attente (ou encore, jusqu'à ce qu'un appel notify ou notifyAll réveille le thread) ;

  10. void wait(long timeout, int nanos) — cette méthode est comme la méthode précédente, mais avec un délai d'attente plus précis ;

  11. void finalize() — cette méthode est appelée (enfin) avant que l'objet ne soit supprimé par le garbage collector. Il est utilisé pour nettoyer les ressources acquises.

Pour utiliser correctement les méthodes hashCode , equals , clone , toString et finalize , elles doivent être remplacées en fonction des spécificités de la tâche en cours.

12. Quelle est la différence entre try-with-resources et try-catch-finally lorsque l'on travaille avec des ressources ?

Généralement, lors de l'utilisation de try-catch-finally , le bloc final est utilisé pour fermer les ressources. Java 7 introduit la nouvelle instruction try-with-resources . C'est analogue à try-catch-finally pour libérer des ressources, mais plus compact et lisible. Rappelons à quoi ressemble try-catch-finally :
String text = "some text......";
BufferedWriter bufferedWriter = null;
try {
   bufferedWriter = new BufferedWriter(new FileWriter("someFileName"));
   bufferedWriter.write(text);
} catch (IOException e) {
   e.printStackTrace();
} finally {
   try {
       bufferedWriter.close();
   } catch (IOException e) {
       e.printStackTrace();
   }
}
Réécrivons maintenant ce code, mais en utilisant try-with-resources :
String text = "some text......";
try(BufferedWriter bufferedWriter =new BufferedWriter(new FileWriter("someFileName"))) {
   bufferedWriter.write(text);
} catch (IOException e) {
   e.printStackTrace();
}
C'est en quelque sorte plus simple, vous ne trouvez pas ? En plus du code plus simple, il y a quelques autres points à noter :
  1. Dans try-with-resources , les ressources déclarées entre parenthèses (ressources qui seront fermées) doivent implémenter l' interface AutoCloseable et sa seule méthode close() .

    La méthode close est exécutée dans un bloc final implicite , sinon, comment le programme déterminerait-il exactement comment fermer la ressource ?

    Mais vous écrirez probablement rarement vos propres implémentations de ressources et leur méthode de fermeture.

  2. Les blocs sont exécutés dans cet ordre :

    1. Le bloc try .
    2. Le bloc implicite final .
    3. Le bloc catch , qui intercepte les exceptions qui se produisent au cours des étapes précédentes.
    4. Le bloc final explicite.

    En règle générale, les exceptions lancées plus bas dans la liste interrompent celles lancées plus haut.

Imaginez que vous utilisez un try-catch-finally et que vous obtenez une exception dans le bloc try . Ensuite, le bloc catch spécifié commence immédiatement à s'exécuter, dans lequel nous avons écrit une autre exception (par exemple, avec un message décrivant l'erreur plus en détail), et vous souhaitez que la méthode renvoie cette exception vers le haut. Ensuite, le bloc final est exécuté et une exception y est également levée. Mais une autre cette fois. Laquelle de ces deux exceptions cette méthode va-t-elle finalement générer ? L'exception levée par le bloc final ! Mais nous arrivons maintenant à un autre point concernant try-with-resources . Voyons comment se comporte try-with-resources dans la même situation. Nous obtenons une exception dans le bloc try lorsque nous essayons de fermer des ressources dans la méthode close() , c'est-à-dire dans le bloc implicite enfin . Laquelle de ces exceptions le bloc catch interceptera-t- il ? Celui lancé par le bloc try ! L’exception du bloc implicite final (de la méthode lose() ) sera ignorée. Cette ignorance des exceptions est également appelée suppression des exceptions.

13. Que sont les opérations au niveau du bit ?

Les opérations au niveau du bit sont des opérations sur des séquences de bits. Ils incluent des opérations logiques et des décalages au niveau des bits. Opérateurs logiques:
  • ET au niveau du bit — Compare les valeurs de bits. Tout bit défini sur 0 (faux) définit le bit correspondant dans le résultat sur 0. Autrement dit, si un bit est 1 (vrai) dans les deux valeurs comparées, alors le bit résultant sera également 1.

    Noté AND ou &

    Exemple : 10111101 & 01100111 = 00100101

  • OU au niveau du bit — Cette opération est à l’opposé de la précédente. Tout bit défini sur 1 définit le bit correspondant dans le résultat sur 1. Par conséquent, si le bit est 0 dans les deux valeurs comparées, alors le bit résultant sera également 0.

    Noté OR ou |

    Exemple : 10100101 | 01100011 = 11100111

  • NOT au niveau du bit — Cet opérateur est appliqué à une seule valeur. Il retourne (inverse) les bits. Autrement dit, les bits qui étaient 1 deviennent 0 ; et ceux qui étaient 0 deviennent 1.

    Noté PAS ou ~

    Exemple : ~10100101 = 01011010

  • OU exclusif au niveau du bit — Compare les valeurs de bits. Si les deux bits sont 1, alors le bit résultant est 0. Si les deux bits sont 0, alors le bit résultant est 0. En d’autres termes, pour que le bit résultant soit 1, un seul des bits doit être 1, et l'autre bit doit être 0.

    Noté XOR ou ^

    Exemple : 10100101 ^ 01100011 = 11000110

Les décalages au niveau des bits ( >> et << ) décalent les bits de l'opérande dans la direction spécifiée, du nombre de places spécifié. Les postes vacants sont remplis de zéros. Par exemple:
  1. 01100011 >> 4 = 00000110
  2. 01100011 << 3 = 00011000
L'exception est lorsque vous décalez un nombre négatif vers la droite. Comme vous vous en souviendrez, le premier bit d'un nombre signé indique le signe. Si ce bit est à 1, alors le nombre est négatif. Si vous décalez un nombre négatif, les positions libérées ne sont pas remplies de zéros, mais plutôt de uns, car le bit de signe doit être conservé. Par exemple : 10100010 >> 2 = 11101000 Cela dit, Java possède un opérateur de décalage vers la droite non signé supplémentaire (>>>). Cet opérateur est analogue à >>, mais lorsqu'il est décalé, les positions vacantes sont remplies par 0, que l'opérande soit un nombre négatif ou un nombre positif. Par exemple : 10100010 >>> 2 = 00101000 En savoir plus sur les opérations au niveau du bit ici . Explorer les questions et réponses d'un entretien d'embauche pour un poste de développeur Java.  Partie 2 - 2Vous pouvez prendre la méthode hash() dans HashMaps comme exemple de décalages au niveau des bits en Java. Cette méthode est utilisée pour déterminer le hashcode interne spécial de la clé : Explorer les questions et réponses d'un entretien d'embauche pour un poste de développeur Java.  Partie 2 - 3Cette méthode vous permet de répartir uniformément les données dans un HashMap, afin de minimiser le nombre de collisions.

14. Quels objets immuables standards existe-t-il en Java ?

Un objet est immuable s’il ne permet pas à ses valeurs d’origine de changer. Il peut avoir des méthodes qui renvoient de nouveaux objets du même type avec des valeurs différentes. Certains objets immuables standard incluent :
  • sans aucun doute, le type immuable le plus célèbre de Java est String ;
  • instances des classes wrapper qui encapsulent les types standard : Boolean, Character, Byte, Short, Integer, Long, Double, Float ;
  • Objets BigInteger et BigDecimal, qui sont généralement utilisés pour les nombres particulièrement GRANDS ;
  • Objets StackTraceElement qui constituent une trace de pile (par exemple, la trace de pile d'une exception) ;
  • un objet de la classe File — il peut modifier des fichiers, mais en même temps l'objet lui-même reste inchangé ;
  • Les UUID, qui sont souvent utilisés pour identifier des éléments de manière unique ;
  • tous les objets des classes du package java.time ;
  • Objets locaux, utilisés pour identifier une région géographique, politique ou culturelle.

15. Quels sont les avantages d’un objet immuable par rapport aux objets ordinaires ?

  1. Les objets immuables peuvent être utilisés en toute sécurité dans un environnement multithread . Ils font en sorte que vous n'ayez pas à vous soucier de la perte de données due à des conditions de concurrence. C'est différent lorsque vous travaillez avec des objets ordinaires. Dans ce cas, vous devez réfléchir et proposer de bons mécanismes lors de l’utilisation de l’objet dans un environnement parallèle.

  2. Les objets immuables sont bons comme clés dans une carte. Si vous utilisez un objet mutable comme clé HashMap et que l'état de l'objet change, alors la structure des données pourrait être confuse : l'objet sera toujours présent, mais si vous utilisez containKey(), vous risquez de ne pas le trouver.

  3. Les objets immuables sont parfaits pour stocker des données immuables (constantes) qui ne doivent jamais être modifiées pendant l'exécution du programme.

  4. Un autre avantage est l’atomicité des échecs. Si un objet immuable lève une exception, il ne sera pas laissé dans un état indésirable (cassé).

  5. Ces cours sont faciles à tester.

  6. Vous n'avez besoin d'aucun mécanisme supplémentaire tel qu'un constructeur de copie ou la mise en œuvre du clonage d'objet.

Questions sur la POO

16. Quels sont les avantages de la POO en général et par rapport à la programmation procédurale ?

Bon, avantages de la POO :
  1. Les applications complexes sont plus faciles à écrire en POO qu’en programmation procédurale puisque tout est décomposé en petits modules – des objets qui interagissent les uns avec les autres – et par conséquent, la programmation est réduite à des relations entre objets.

  2. Les applications écrites en POO sont beaucoup plus faciles à modifier (lorsque les principes de conception sont correctement respectés).

  3. Étant donné que les données et les opérations de données forment une seule entité, elles ne sont pas réparties partout dans l'application (ce qui est souvent le cas dans la programmation procédurale).

  4. Le principe d'encapsulation protège les données les plus critiques de l'utilisateur.

  5. Le même code peut être réutilisé avec différentes données car les classes vous permettent de créer de nombreux objets, chacun avec ses propres valeurs.

  6. L'héritage et le polymorphisme vous permettent également de réutiliser et d'étendre le code existant (au lieu de dupliquer des fonctionnalités similaires).

  7. Prolonger une demande est plus simple qu’avec une approche procédurale.

  8. L'approche POO permet de faire abstraction des détails de mise en œuvre.

17. Dites-nous quels sont les inconvénients de la POO

Malheureusement, ils existent aussi :
  1. La POO nécessite beaucoup de connaissances théoriques qu’il faut maîtriser avant de pouvoir écrire quoi que ce soit.

  2. Les idées de POO ne sont pas si faciles à comprendre et à appliquer dans la pratique (il faut être un peu philosophe dans l'âme).

  3. La POO réduit légèrement les performances d'un programme en raison de la complexité accrue du système.

  4. L'approche POO nécessite plus de mémoire puisque tout est constitué de classes, d'interfaces, de méthodes, qui occupent beaucoup plus de mémoire que les variables ordinaires.

  5. Le temps requis pour l’analyse initiale est plus long que pour une approche procédurale.

18. Qu'est-ce que le polymorphisme statique par rapport au polymorphisme dynamique ?

Le polymorphisme permet aux objets de la même classe ou interface de se comporter différemment. Il existe deux types de polymorphisme, également appelés liaisons précoces et tardives. Polymorphisme statique, ou liaison précoce :
  • se produit au moment de la compilation (au début du cycle de vie du programme) ;
  • décide quelle méthode exécuter au moment de la compilation ;
  • la surcharge de méthode est un exemple de polymorphisme statique ;
  • la liaison anticipée inclut les méthodes privées, statiques et finales ;
  • l'héritage n'est pas impliqué dans la liaison précoce ;
  • le polymorphisme statique n'implique pas d'objets spécifiques, mais plutôt des informations sur le type de classe qui apparaissent à gauche du nom d'une variable.
Polymorphisme dynamique, ou liaison tardive :
  • se produit au moment de l'exécution (pendant que le programme est en cours d'exécution) ;
  • le polymorphisme dynamique décide quelle implémentation spécifique une méthode aura au moment de l'exécution ;
  • le remplacement de méthode est un exemple de polymorphisme dynamique ;
  • une liaison tardive signifie attribuer un objet spécifique, une référence de son type ou sa superclasse ;
  • l'héritage est associé au polymorphisme dynamique.

19. Fournir une définition du principe d'abstraction en POO

En POO, l'abstraction est un moyen d'isoler un ensemble de caractéristiques significatives d'un objet, tout en excluant les détails insignifiants. Autrement dit, lors de la conception d'un programme avec une approche POO, vous vous concentrez sur des modèles généraux, sans entrer dans les détails de leur mise en œuvre. En Java, l'abstraction est réalisée via des interfaces . Par exemple, vous avez une voiture et ce sera une interface. Et les différentes interactions avec celui-ci — par exemple, le démarrage du moteur, le changement de vitesse — sont des fonctions que nous utilisons sans entrer dans les détails de mise en œuvre. En effet, lorsque vous conduisez, vous ne pensez pas exactement à la manière dont la boîte de vitesses remplit sa fonction, ni à la manière dont la clé démarre le moteur, ni à la manière exacte dont le volant fait tourner les roues. Et si vous remplacez l'implémentation de certaines fonctionnalités (par exemple, le moteur), vous ne le remarquerez peut-être même pas. Cela n'a pas d'importance pour vous : vous n'entrez pas dans les détails de la mise en œuvre. Ce qui vous importe, c'est que l'action soit réalisée. Essentiellement, cela fait abstraction des détails de mise en œuvre. À ce stade, nous nous arrêterons aujourd’hui : à suivre !
Commentaires
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION