Entretien Java : questions sur la POO
1. Quelles sont les caractéristiques de Java ?
Répondre:-
Notions de POO :
- orientation objet
- héritage
- encapsulation
- polymorphisme
- abstraction
-
Multiplateforme : un programme Java peut être exécuté sur n'importe quelle plate-forme sans aucune modification. Bien sûr, cela nécessite une JVM (machine virtuelle Java) installée.
-
Hautes performances : Le compilateur Just-In-Time (JIT) permet des performances élevées. Le compilateur JIT convertit le bytecode en code machine, puis la JVM démarre l'exécution.
- Multithreading : la JVM crée un thread d'exécution appelé le
main thread
. Un programmeur peut créer plusieurs threads en dérivant de la classe Thread ou en implémentant l'Runnable
interface.
2. Qu'est-ce que l'héritage ?
L'héritage signifie qu'une classe peut hériter d'une autre classe (en utilisant le mot clé extend ). Cela signifie que vous pouvez réutiliser le code de la classe dont vous héritez. La classe existante est connue sous le nom desuperclass
et la classe nouvellement créée est le subclass
. Les gens disent aussi utiliser les termes parent et child
.
public class Animal {
private int age;
}
public class Dog extends Animal {
}
où Animal
est le parent
et Dog
est le child
.
3. Qu'est-ce que l'encapsulation ?
Cette question est souvent posée dans les entretiens pour les postes de développeur Java. L'encapsulation masque l'implémentation en utilisant des modificateurs d'accès, des getters et des setters. Ceci est fait afin d'empêcher l'accès externe partout où les développeurs pensent que c'est nécessaire. Un exemple simple de la vie réelle est la voiture. Nous n'avons pas d'accès direct au fonctionnement du moteur. Tout ce que nous avons à faire est de mettre la clé dans le contact et d'allumer le moteur. Les processus qui se déroulent sous le capot ne nous concernent pas. De plus, si nous devions interférer dans l'activité du moteur, cela pourrait conduire à une situation imprévisible, pouvant endommager la voiture et entraîner des lésions corporelles. Il se passe exactement la même chose en programmation. C'est bien décrit sur Wikipédia. Il y a aussi un article sur l'encapsulation sur CodeGym .4. Qu'est-ce que le polymorphisme ?
Le polymorphisme est la capacité d'un programme à traiter des objets avec la même interface de la même manière, sans information sur le type spécifique de l'objet. Comme le dit le dicton, "une interface - plusieurs implémentations". Avec le polymorphisme, vous pouvez combiner et utiliser différents types d'objets en fonction de comportements partagés. Par exemple, nous avons une classe Animal qui a deux descendants : Dog et Cat. La classe générique Animal a un comportement partagé par tous, la capacité de faire un son. Nous utilisons des capacités polymorphes lorsque nous devons rassembler tout ce qui hérite de la classe Animal et exécuter la méthode "make sound". Voici à quoi ça ressemble :
List<Animal> animals = Arrays.asList(new Cat(), new Dog(), new Cat());
animals.forEach(animal -> animal.makeSound());
En d'autres termes, le polymorphisme est utile. Et cela s'applique également aux méthodes polymorphes (surchargées). Comment utiliser le polymorphisme
Questions d'entretien sur la syntaxe Java
5. Qu'est-ce qu'un constructeur en Java ?
Les constructeurs ont les caractéristiques suivantes :- Lorsqu'un nouvel objet est créé, le programme utilise le constructeur approprié pour le créer.
- Un constructeur est comme une méthode. Ses particularités résident dans le fait qu'il n'y a pas de valeur de retour (y compris void) et que son nom est le même que le nom de la classe.
- Si aucun constructeur n'est créé explicitement, un constructeur vide est créé automatiquement.
- Un constructeur peut être remplacé.
- Si vous déclarez un constructeur avec des paramètres mais que vous en avez également besoin d'un sans paramètres, vous devez le créer séparément, car il ne sera pas créé automatiquement.
6. Quelles sont les deux classes qui n'héritent pas d'Object ?
Ne vous laissez pas berner par des questions pièges - il n'y a pas de telles classes. Toutes les classes héritent de la classe Object soit directement, soit par l'intermédiaire d'ancêtres !7. Qu'est-ce qu'une variable locale ?
C'est une autre question d'entretien populaire pour les développeurs Java. Une variable locale est une variable qui est définie à l'intérieur d'une méthode et qui existe tant que la méthode est en cours d'exécution. Dès que l'exécution se termine, la variable locale cesse d'exister. Voici un programme qui utilise une variable locale nommée helloMessage dans la méthode main() :
public static void main(String[] args) {
String helloMessage;
helloMessage = "Hello, World!";
System.out.println(helloMessage);
}
8. Qu'est-ce qu'une variable d'instance ?
Une variable d'instance est une variable déclarée dans une classe. Il existe tant qu'un objet existe. Par exemple, nous avons une classe Bee, qui a deux variables d'instance — nectarLoad et maxNectarLoad :
public class Bee {
/**
* Current nectar load
*/
private double nectarLoad;
/**
* Maximum nectar that can the bee can collect.
*/
private double maxNectarLoad = 20.0;
...
}
9. Que sont les modificateurs d'accès ?
Les modificateurs d'accès sont un mécanisme permettant de personnaliser l'accès aux classes, aux méthodes et aux variables. Les modificateurs suivants existent, répertoriés par ordre croissant d'accès :private
— Ce modificateur d'accès est utilisé sur les méthodes, les champs et les constructeurs. L'accès est limité à la classe dans laquelle ils sont déclarés.package-private (default)
— Il s'agit du niveau d'accès par défaut pour les classes. L'accès est limité au package spécifique dans lequel une classe, une méthode, une variable ou un constructeur est déclaré.protected
— Ce modificateur d'accès offre le même niveau d'accès qu'avecpackage-private
l'ajout d'un accès pour les classes qui héritent d'une classe avec leprotected
modificateur.public
— Ce niveau d'accès est également utilisé pour les cours. Ce niveau d'accès signifie qu'il y a un accès complet à travers l'application.
10. Qu'est-ce que le remplacement de méthode ?
Nous redéfinissons les méthodes lorsqu'une classe enfant souhaite modifier le comportement de sa classe parent. Si nous devons également faire ce qui se trouve dans la méthode parent, nous pouvons utiliser super.methodName() dans l'enfant, qui exécutera la méthode parent. Nous pouvons ajouter notre logique supplémentaire après cela. Exigences à respecter :- la signature de la méthode doit être la même
- la valeur de retour doit être la même
11. Que sont les signatures de méthode ?
Une signature de méthode est la combinaison du nom de la méthode et des arguments que la méthode prend. La signature de méthode est l'identifiant unique d'une méthode lors de la surcharge de méthodes.12. Qu'est-ce que la surcharge de méthode ?
La surcharge de méthode est une caractéristique du polymorphisme dans laquelle nous modifions la signature de la méthode pour créer plusieurs méthodes qui effectuent la même action :- Le même nom
- arguments différents
- il peut y avoir différents types de retour
ArrayList
de la classe add()
peut être surchargée, ce qui nous permet d'ajouter de différentes manières en fonction des arguments d'entrée :
add(Object o)
— Cette méthode ajoute simplement un objetadd(int index, Object o)
— Cette méthode ajoute un objet à un index spécifiqueadd(Collection<Object> c)
— Cette méthode ajoute une liste d'objetsadd(int index, Collection<Object> c)
— Cette méthode ajoute une liste d'objets à partir d'un index spécifique.
13. Qu'est-ce qu'une interface ?
Java ne prend pas en charge l'héritage multiple. Pour surmonter cette limitation, des interfaces ont été ajoutées sous la forme que nous connaissons et aimons ;) Pendant longtemps, les interfaces n'avaient que des méthodes sans aucune implémentation. Dans le cadre de cette réponse, parlons-en. Par exemple:
public interface Animal {
void makeSound();
void eat();
void sleep();
}
Quelques détails en découlent :
- Toutes les méthodes d'une interface sont publiques et abstraites
- Toutes les variables sont publiques static final
- Les classes n'héritent pas des interfaces (c'est-à-dire que nous n'utilisons pas le mot clé extend). Au lieu de cela, les classes les implémentent (c'est-à-dire que nous utilisons le mot clé implements). De plus, vous pouvez implémenter autant d'interfaces que vous le souhaitez.
- Les classes qui implémentent une interface doivent fournir une implémentation de toutes les méthodes qui se trouvent dans l'interface.
public class Cat implements Animal {
public void makeSound() {
// Method implementation
}
public void eat() {
// Implementation
}
public void sleep() {
// Implementation
}
}
14. Qu'est-ce qu'une méthode par défaut dans une interface ?
Parlons maintenant des méthodes par défaut. À quoi servent-ils? Qui sont-ils pour? Ces méthodes ont été ajoutées pour servir "les deux mains". De quoi je parle ? Eh bien, d'une part, il était nécessaire d'ajouter de nouvelles fonctionnalités : les lambdas et l'API Stream. D'un autre côté, il était nécessaire de conserver ce pour quoi Java est célèbre : la rétrocompatibilité. Pour ce faire, les interfaces avaient besoin de nouvelles solutions toutes faites. C'est ainsi que nous sont venues les méthodes par défaut. Une méthode par défaut est une méthode implémentée dans une interface, marquée par ledefault
mot clé. Par exemple, la méthode bien connue stream()
dans l' Collection
interface. Croyez-moi, cette interface n'est pas aussi simple qu'il y paraît. Ou encore la forEach()
méthode non moins célèbre duIterable
interface. Il n'existait pas non plus jusqu'à ce que les méthodes par défaut soient ajoutées. Au fait, vous pouvez également lire à ce sujet sur CodeGym ici .
15. Comment hérite-t-on alors de deux méthodes par défaut identiques ?
La réponse précédente sur ce qu'est une méthode par défaut soulève une autre question. Si vous pouvez implémenter des méthodes dans des interfaces, vous pouvez théoriquement implémenter deux interfaces avec la même méthode. Comment fait-on cela? Voici deux interfaces différentes avec la même méthode :
interface A {
default void foo() {
System.out.println("Foo A");
}
}
interface B {
default void foo() {
System.out.println("Foo B");
}
}
Et nous avons une classe qui implémente ces deux interfaces. Mais comment choisir une méthode spécifique dans l'interface A ou B ? La construction spéciale suivante le permetA.super.foo()
:
public class C implements A, B {
public void fooA() {
A.super.foo();
}
public void fooB() {
B.super.foo();
}
}
Ainsi, la fooA()
méthode utilisera la foo()
méthode par défaut de l' A
interface, tandis que la fooB()
méthode utilisera la foo()
méthode de l' B
interface.
16. Que sont les méthodes abstraites et les classes ?
En Java,abstract
est un mot réservé. Il est utilisé pour désigner des classes et des méthodes abstraites. Premièrement, nous avons besoin de définitions. Une méthode abstraite est une méthode déclarée à l'aide du abstract
mot-clé sans implémentation dans une classe abstraite. C'est-à-dire qu'il s'agit d'une méthode comme dans une interface, mais avec l'ajout d'un mot-clé, par exemple :
public abstract void foo();
Une classe abstraite est une classe également marquée du abstract
mot-clé :
public abstract class A {
}
Une classe abstraite a plusieurs fonctionnalités :
- vous ne pouvez pas créer un objet d'une classe abstraite
- il peut avoir des méthodes abstraites
- il peut aussi ne pas avoir de méthodes abstraites
17. Quelle est la différence entre String, StringBuilder et StringBuffer ?
String
les valeurs sont stockées dans un pool de chaînes constantes. Dès qu'une chaîne est créée, elle apparaît dans ce pool. Et vous ne pouvez pas le supprimer. Par exemple:
String name = "book";
La variable pointera vers le pool de chaînes constantes En définissant la variable name sur une valeur différente, nous avons :
name = "pen";
Le pool de chaînes constantes ressemble à ceci : En d'autres termes, les deux valeurs y restent. Tampon de chaîne :
String
les valeurs sont stockées dans une pile. Si une valeur est modifiée, la nouvelle valeur remplacera l'ancienne.String Buffer
est synchronisé et est donc thread-safe.- En raison de la sécurité des threads, ses performances sont médiocres.
StringBuffer name = “book”;
Dès que la valeur de la variable name change, la valeur dans la pile change : StringBuilder est exactement le même que StringBuffer
, sauf qu'il n'est pas thread-safe. En conséquence, il est nettement plus rapide que StringBuffer
.
18. Quelle est la différence entre une classe abstraite et une interface ?
Classe abstraite :- Les classes abstraites ont un constructeur par défaut. Elle est appelée chaque fois qu'un descendant de la classe abstraite est créé.
- Ils peuvent inclure à la fois des méthodes abstraites et des méthodes non abstraites. En général, une classe abstraite n'a pas besoin d'avoir des méthodes abstraites.
- Une classe qui hérite d'une classe abstraite ne doit implémenter que des méthodes abstraites.
- Une classe abstraite peut avoir des variables d'instance (voir Question #5).
- Une interface n'a pas de constructeur et ne peut pas être initialisée.
- Seules les méthodes abstraites peuvent être ajoutées (à l'exception des méthodes par défaut).
- Les classes qui implémentent l'interface doivent implémenter toutes les méthodes (à l'exception des méthodes par défaut).
- Les interfaces ne peuvent avoir que des constantes.
19. Pourquoi l'accès à un élément d'un tableau est-il O(1) ?
Cette question a été littéralement posée lors de ma dernière interview. Comme je l'ai appris plus tard, le but de cette question est de voir comment une personne pense. De toute évidence, ces connaissances ont peu de valeur pratique. Le simple fait de le savoir suffit. Tout d'abord, nous devons préciser que O(1) est la notation de la complexité temporelle d'un algorithme "à temps constant". En d'autres termes, cette désignation indique le temps d'exécution le plus rapide. Pour répondre à cette question, nous devons considérer ce que nous savons sur les tableaux. Pour créer unint
tableau, nous devons écrire ce qui suit :
int[] intArray = new int[100];
Plusieurs conclusions peuvent être tirées de cette syntaxe :
- Lorsqu'un tableau est déclaré, son type est connu. Si le type est connu, la taille de chaque cellule du tableau est connue.
- La taille de l'ensemble du tableau est connue.
Alors, comment arrivons-nous à O(1) lors de l'accès aux objets d'une ArrayList ?
Cette question suit immédiatement la précédente. La vérité est que lorsque vous travaillez avec un tableau contenant des primitives, nous connaissons à l'avance (au moment de la création) la taille du type d'élément. Mais que faire si nous avons ce type de hiérarchie d'héritage et que nous voulons créer une collection pour les éléments de type A et ajouter différentes implémentations (B, C et D) :
List<A> list = new ArrayList();
list.add(new B());
list.add(new C());
list.add(new D());
list.add(new B());
Dans cette situation, comment calcule-t-on la taille de chaque cellule ? Après tout, chaque objet sera différent, éventuellement avec des champs supplémentaires différents. Ce qu'il faut faire? Ici, la question est posée d'une manière qui est destinée à vous confondre. Nous savons que la collection ne stocke pas directement les objets. Il ne stocke que les références aux objets. Et toutes les références ont la même taille, et c'est connu. Par conséquent, nous calculons ici les adresses de la même manière que dans la question précédente.
21. Boîte automatique et déballage
Contexte historique : l'autoboxing et l'unboxing sont quelques-unes des principales innovations du JDK 5. L'autoboxing est le processus de conversion automatique d'un type primitif en une classe wrapper correspondante. Le déballage est l'exact opposé de l'autoboxing. C'est le processus de conversion d'une classe wrapper en une primitive. Mais si la valeur d'un wrapper estnull
, alors un NullPointerException
sera lancé lors du déballage.
Primitives et leurs enveloppes correspondantes
Primitif | Classe wrapper |
---|---|
booléen | booléen |
entier | Entier |
octet | Octet |
carboniser | Personnage |
flotter | Flotter |
long | Long |
court | Court |
double | Double |
// L'autoboxing se produit :
-
lors de l'affectation d'une primitive à une référence à une classe wrapper :
AVANT Java 5 :
// Manual boxing (the way it was BEFORE Java 5). public void boxingBeforeJava5() { Boolean booleanBox = new Boolean(true); Integer intBox = new Integer(3); // And so on for other types } After Java 5: // Automatic boxing (the way it became in Java 5). public void boxingJava5() { Boolean booleanBox = true; Integer intBox = 3; // And so on for other types }
-
lorsqu'une primitive est passée en argument à une méthode qui attend un wrapper :
public void exampleOfAutoboxing() { long age = 3; setAge(age); } public void setAge(Long age) { this.age = age; }
// Le déballage a lieu :
-
lorsque nous affectons une instance d'une classe wrapper à une variable primitive :
// BEFORE Java 5: int intValue = new Integer(4).intValue(); double doubleValue = new Double(2.3).doubleValue(); char c = new Character((char) 3).charValue(); boolean b = Boolean.TRUE.booleanValue(); // And after JDK 5: int intValue = new Integer(4); double doubleValue = new Double(2.3); char c = new Character((char) 3); boolean b = Boolean.TRUE;
-
Lors d'opérations arithmétiques. Les opérations s'appliquent uniquement aux types primitifs, il est donc nécessaire de déballer les primitives.
// BEFORE Java 5: Integer integerBox1 = new Integer(1); Integer integerBox2 = new Integer(2); // A comparison used to require this: integerBox1.intValue() > integerBox2.intValue() // In Java 5 integerBox1 > integerBox2
-
lors du passage d'une instance d'une classe wrapper à une méthode qui prend la primitive correspondante :
public void exampleOfAutoboxing() { Long age = new Long(3); setAge(age); } public void setAge(long age) { this.age = age; }
22. Quel est le mot-clé final et où est-il utilisé ?
Lefinal
mot-clé peut être utilisé sur des variables, des méthodes et des classes.
- La valeur d'une variable finale ne peut pas être modifiée après son initialisation.
- Une classe terminale est stérile :) Elle ne peut pas avoir d'enfants.
- Une méthode finale ne peut pas être remplacée par un descendant.
Variables finales
Java nous donne deux manières de déclarer une variable et de lui affecter une valeur :- Vous pouvez déclarer une variable et l'initialiser plus tard.
- Vous pouvez déclarer une variable et lui attribuer une valeur immédiatement.
public class FinalExample {
// A static final variable that is immediately initialized:
final static String FINAL_EXAMPLE_NAME = "I'm likely the final one";
// A final variable that is not initialized, but will only work if you
// initialize it in the constructor:
final long creationTime;
public FinalExample() {
this.creationTime = System.currentTimeMillis();
}
public static void main(String[] args) {
FinalExample finalExample = new FinalExample();
System.out.println(finalExample.creationTime);
// The final FinalExample.FINAL_EXAMPLE_NAME field cannot be accessed
// FinalExample.FINAL_EXAMPLE_NAME = "Not you're not!";
// The final Config.creationTime field cannot be accessed
// finalExample.creationTime = 1L;
}
}
Une variable finale peut-elle être considérée comme une constante ?
Comme nous ne pouvons pas attribuer de nouvelles valeurs aux variables finales, il semble qu'il s'agisse de variables constantes. Mais seulement à première vue : si le type de données de la variable estimmutable
, alors, oui, c'est une constante. Mais si le type de données est mutable
, c'est-à-dire modifiable, il sera alors possible d'utiliser des méthodes et des variables pour modifier la valeur de l'objet référencé par une final
variable. Pour cette raison, il ne peut pas être appelé une constante. L'exemple suivant montre que certaines variables finales sont de véritables constantes, tandis que d'autres ne le sont pas, puisqu'elles peuvent être modifiées.
public class FinalExample {
// Immutable final variables
final static String FINAL_EXAMPLE_NAME = "I'm likely the final one";
final static Integer FINAL_EXAMPLE_COUNT = 10;
// Mutable final variables
final List<String> addresses = new ArrayList();
final StringBuilder finalStringBuilder = new StringBuilder("Constant?");
}
Variables finales locales
Lorsqu'unefinal
variable est créée dans une méthode, on l'appelle une local final
variable :
public class FinalExample {
public static void main(String[] args) {
// You can do this
final int minAgeForDriveCar = 18;
// Or you can do this, in a for-each loop:
for (final String arg : args) {
System.out.println(arg);
}
}
}
Nous pouvons utiliser le mot clé final dans une boucle for améliorée, car une nouvelle variable est créée après chaque itération de la boucle. Gardez à l'esprit que cela ne s'applique pas à une boucle for normale, nous aurons donc une erreur de compilation.
// The final local j variable cannot be assigned
for (final int i = 0; i < args.length; i ++) {
System.out.println(args[i]);
}
Classe finale
Une classe déclarée commefinal
ne peut pas être étendue. Pour le dire plus simplement, aucune autre classe ne peut en hériter. Un excellent exemple de final
classe dans le JDK est String. La première étape pour créer une classe immuable est de la marquer comme final
, l'empêchant ainsi d'être étendue :
public final class FinalExample {
}
// Compilation error!
class WantsToInheritFinalClass extends FinalExample {
}
Méthodes finales
Lorsqu'une méthode est marquée final, elle est appelée méthode finale (c'est logique, n'est-ce pas ?). Une méthode finale ne peut pas être remplacée dans une classe enfant. Incidemment, les méthodes wait() et notify() de la classe Object sont finales, nous n'avons donc pas la possibilité de les remplacer.
public class FinalExample {
public final String generateAddress() {
return "Some address";
}
}
class ChildOfFinalExample extends FinalExample {
// Compilation error!
@Override
public String generateAddress() {
return "My OWN Address";
}
}
Comment et où utiliser final en Java
- Utilisez le mot-clé final pour définir certaines constantes au niveau de la classe ;
- Créez des variables finales pour les objets que vous ne voulez pas modifier. Par exemple, des propriétés spécifiques à un objet que nous pouvons utiliser à des fins de journalisation.
- Si vous ne voulez pas qu'une classe soit prolongée, marquez-la comme finale.
- Si vous devez créer une classe immuable, vous devez la rendre finale.
- Si vous souhaitez que l'implémentation d'une méthode ne change pas dans ses descendants, marquez la méthode comme
final
. Ceci est très important pour être sûr que la mise en œuvre ne change pas.
23. Que sont les types mutables et immuables ?
Mutable
Les objets mutables sont des objets dont l'état et les variables peuvent être modifiés après la création. Des exemples de classes mutables incluent StringBuilder et StringBuffer. Exemple:
public class MutableExample {
private String address;
public MutableExample(String address) {
this.address = address;
}
public String getAddress() {
return address;
}
// This setter can change the name field
public void setAddress(String address) {
this.address = address;
}
public static void main(String[] args) {
MutableExample obj = new MutableExample("First address");
System.out.println(obj.getAddress());
// We are updating the name field, so this is a mutable object
obj.setAddress("Updated address");
System.out.println(obj.getAddress());
}
}
Immuable
Les objets immuables sont des objets dont l'état et les variables ne peuvent pas être modifiés après la création de l'objet. Une excellente clé pour un HashMap, vous ne pensez pas ? :) Par exemple, String, Integer, Double, etc. Exemple:
// We'll make this class final so no one can change it
public final class ImmutableExample {
private String address;
ImmutableExample(String address) {
this.address = address;
}
public String getAddress() {
return address;
}
// We remove the setter
public static void main(String[] args) {
ImmutableExample obj = new ImmutableExample("Old address");
System.out.println(obj.getAddress());
// There is no way to change this field, so it is an immutable object
// obj.setName("new address");
// System.out.println(obj.getName());
}
}
Dans la partie suivante, nous examinons les questions et réponses sur les collections. Mon profil sur GitHub Top 50 des questions et réponses d'entretien d'embauche pour Java Core. Partie 2
GO TO FULL VERSION