JavaRush /Blog Java /Random-FR /Top 50 des questions et réponses d'entretien avec Java Co...
Roman Beekeeper
Niveau 35

Top 50 des questions et réponses d'entretien avec Java Core. Partie 1

Publié dans le groupe Random-FR
Bonjour à tous, mesdames et messieurs ingénieurs logiciels ! Parlons des questions d'entretien. À propos de ce à quoi vous devez vous préparer et de ce que vous devez savoir. C’est une excellente raison de répéter ou d’étudier ces points à partir de zéro. Top 50 des questions et réponses d'entretien avec Java Core.  Partie 1 - 1J'ai une collection assez complète de questions fréquemment posées sur la POO, la syntaxe Java, les exceptions en Java, les collections et le multithreading, que pour plus de commodité je diviserai en plusieurs parties. Important:nous ne parlerons que des versions Java jusqu'à 8. Toutes les innovations à partir des 9, 10, 11, 12, 13 ne seront pas prises en compte ici. Toutes les idées/commentaires sur la façon d’améliorer les réponses sont les bienvenus . Bonne lecture, c'est parti !

Entretien Java : questions POO

1. Quelles sont les fonctionnalités de Java ?

Répondre:
  1. Concepts de POO :

    1. orientation de l'objet ;
    2. héritage;
    3. encapsulation ;
    4. polymorphisme;
    5. abstraction.
  2. Multiplateforme : un programme Java peut être exécuté sur n’importe quelle plateforme sans aucune modification. La seule chose dont vous avez besoin est une JVM installée (machine virtuelle Java).

  3. Hautes performances : JIT (compilateur Just In Time) permet des performances élevées. JIT convertit le bytecode en code machine, puis la JVM démarre l'exécution.

  4. Multithreading : un thread d'exécution appelé Thread. La JVM crée un thread appelé main thread. Un programmeur peut créer plusieurs threads en héritant de la classe Thread ou en implémentant une interface Runnable.

2. Qu'est-ce que l'héritage ?

L'héritage signifie qu'une classe peut hériter (« étend ») d'une autre classe. De cette façon, vous pouvez réutiliser le code de la classe dont vous héritez. La classe existante est connue sous le nom de superclass, et celle en cours de création est connue sous le nom de subclass. Ils disent aussi parentet child.
public class Animal {
   private int age;
}

public class Dog extends Animal {

}
Animalest parent, et Dog- child.

3. Qu’est-ce que l’encapsulation ?

Cette question revient souvent lors des entretiens avec les développeurs Java. L'encapsulation masque l'implémentation à l'aide de modificateurs d'accès, à l'aide de getters et de setters. Ceci est fait afin de fermer l'accès pour un usage externe dans les endroits où les développeurs le jugent nécessaire. Un exemple accessible de la vie est une voiture. Nous n'avons pas d'accès direct au fonctionnement du moteur. Pour nous, le travail consiste à mettre la clé dans le contact et à démarrer le moteur. Et quels processus se dérouleront sous le capot ne nous regardent pas. De plus, notre ingérence dans cette activité peut conduire à une situation imprévisible, à cause de laquelle nous pouvons casser la voiture et nous blesser. Exactement la même chose se produit en programmation. Bien décrit sur Wikipédia . Il existe également un article sur l'encapsulation sur JavaRush .

4. Qu'est-ce que le polymorphisme ?

Le polymorphisme est la capacité d'un programme à utiliser de manière identique des objets avec la même interface sans informations sur le type spécifique de cet objet. Comme on dit, une interface - plusieurs implémentations. Avec le polymorphisme, vous pouvez combiner et utiliser différents types d'objets en fonction de leur comportement commun. Par exemple, nous avons une classe Animal, qui a deux descendants : Chien et Chat. La classe générique Animal a un comportement commun à tous : émettre un son. Dans le cas où nous devons rassembler tous les descendants de la classe Animal et exécuter la méthode « faire un son », nous utilisons les possibilités du polymorphisme. Voici à quoi cela ressemblera :
List<Animal> animals = Arrays.asList(new Cat(), new Dog(), new Cat());
animals.forEach(animal -> animal.makeSound());
Le polymorphisme nous aide donc. De plus, cela s'applique également aux méthodes polymorphes (surchargées). Pratique de l'utilisation du polymorphisme

Questions d'entretien - Syntaxe Java

5. Qu'est-ce qu'un constructeur en Java ?

Les caractéristiques suivantes sont valables :
  1. Lorsqu'un nouvel objet est créé, le programme utilise le constructeur approprié pour le faire.
  2. Un constructeur est comme une méthode. Sa particularité est qu'il n'y a pas d'élément de retour (y compris void), et son nom est le même que le nom de la classe.
  3. Si aucun constructeur n'est écrit explicitement, un constructeur vide sera créé automatiquement.
  4. Le constructeur peut être remplacé.
  5. Si un constructeur avec paramètres a été créé, mais que vous en avez également besoin sans paramètres, vous devez l'écrire séparément, car il n'est pas créé automatiquement.

6. Quelles sont les deux classes qui n'héritent pas d'Object ?

Ne vous laissez pas berner par les provocations, de telles classes n'existent pas : toutes les classes directement ou via leurs ancêtres sont héritées de la classe Object !

7. Qu'est-ce qu'une variable locale ?

Une autre question populaire lors d'un entretien avec un développeur Java. Une variable locale est une variable définie dans une méthode et qui existe jusqu'au moment où la méthode est exécutée. Une fois l'exécution terminée, la variable locale cessera d'exister. Voici un programme qui utilise la variable locale 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éfinie à l'intérieur d'une classe et elle existe jusqu'au moment où l'objet existe. Un exemple est la classe Bee, qui a deux variables nectarCapacity et maxNectarCapacity :
public class Bee {

   /**
    * Current nectar capacity
    */
   private double nectarCapacity;

   /**
    * Maximal nectar that can take bee.
    */
   private double maxNectarCapacity = 20.0;

  ...
}

9. Que sont les modificateurs d'accès ?

Les modificateurs d'accès sont un outil qui vous permet de personnaliser l'accès aux classes, méthodes et variables. Il existe les modificateurs suivants, classés par ordre d'accès croissant :
  1. private- utilisé pour les méthodes, les champs et les constructeurs. Le niveau d'accès est uniquement la classe dans laquelle il est déclaré.
  2. package-private(default)- peut être utilisé pour les cours. Accès uniquement dans un package spécifique dans lequel une classe, une méthode, une variable, un constructeur est déclaré.
  3. protected— le même accès que package-private+ pour les classes qui héritent d'une classe avec le modificateur protected.
  4. public- également utilisé pour les cours. Accès complet à toute l’application.
  5. Top 50 des questions et réponses d'entretien avec Java Core.  Partie 1 - 2

10. Qu'est-ce que les méthodes prioritaires ?

Le remplacement de méthode se produit lorsque l'enfant souhaite modifier le comportement de la classe parent. Si vous souhaitez que ce qui se trouve dans la méthode parent soit exécuté, vous pouvez utiliser une construction comme super.methodName() dans l'enfant, qui fera le travail de la méthode parent, et ensuite seulement ajouter de la logique. Conditions à remplir :
  • la signature de la méthode doit être la même ;
  • la valeur de retour doit être la même.

11. Qu'est-ce qu'une signature de méthode ?

Top 50 des questions et réponses d'entretien avec Java Core.  Partie 1 à 3Une signature de méthode est un ensemble du nom de la méthode et des arguments qu'elle accepte. Une signature de méthode est un identifiant unique pour une méthode lors de la surcharge des méthodes.

12. Qu'est-ce que la surcharge de méthode ?

La surcharge de méthode est une propriété du polymorphisme dans laquelle, en modifiant la signature de la méthode, vous pouvez créer différentes méthodes pour les mêmes actions :
  • même nom de méthode ;
  • des arguments différents ;
  • il peut y avoir un type de retour différent.
Par exemple, le même add()of ArrayListpeut être surchargé comme suit et effectuera l'addition d'une manière différente, en fonction des arguments entrants :
  • add(Object o)- ajoute simplement un objet ;
  • add(int index, Object o)— ajoute un objet à un index spécifique ;
  • add(Collection<Object> c)— ajoute une liste d'objets ;
  • add(int index, Collection<Object> c)— ajoute une liste d'objets, à partir d'un certain index.

13. Qu'est-ce qu'Interface ?

L'héritage multiple n'est pas implémenté en Java, donc pour pallier ce problème, des interfaces telles que nous les connaissons ont été ajoutées ;) Pendant longtemps, les interfaces n'avaient que des méthodes sans les implémenter. Dans le cadre de cette réponse, nous en parlerons. Par exemple:

public interface Animal {
   void makeSound();
   void eat();
   void sleep();
}
Certaines nuances en découlent :
  • toutes les méthodes de l'interface sont publiques et abstraites ;
  • toutes les variables sont publiques statiques finales ;
  • les classes n'en héritent pas (étend), mais les implémentent (implémente). De plus, vous pouvez implémenter autant d’interfaces que vous le souhaitez.
  • les classes qui implémentent une interface doivent fournir des implémentations de toutes les méthodes dont dispose l'interface.
Comme ça:
public class Cat implements Animal {
   public void makeSound() {
       // method implementation
   }

   public void eat() {
       // implementation
   }

   public void sleep() {
       // implementation
   }
}

14. Quelle est la méthode par défaut dans l'interface ?

Parlons maintenant des méthodes par défaut. Pour quoi, pour qui ? Ces méthodes ont été ajoutées pour que tout soit « à la fois le vôtre et le nôtre ». De quoi je parle ? Oui, d'une part, il fallait ajouter de nouvelles fonctionnalités : lambdas, Stream API, d'autre part, il fallait laisser ce qui fait la renommée de Java : la rétrocompatibilité. Pour ce faire, il a fallu introduire des solutions toutes faites dans les interfaces. C'est ainsi que les méthodes par défaut nous sont parvenues. Autrement dit, la méthode par défaut est une méthode implémentée dans l'interface qui possède le mot-clé default. Par exemple, la méthode bien connue stream()du Collection. Regardez-le, cette interface n'est pas aussi simple qu'il y paraît ;). Ou encore une méthode tout aussi connue forEach()du Iterable. Il n’existait pas non plus jusqu’à l’ajout des méthodes par défaut. À propos, vous pouvez également en savoir plus sur JavaRush .

15. Comment alors hériter de deux méthodes par défaut identiques ?

Sur la base de la réponse précédente concernant la méthode par défaut, vous pouvez poser une autre question. Si vous pouvez implémenter des méthodes dans des interfaces, alors théoriquement vous pouvez implémenter deux interfaces avec la même méthode, et comment faire cela ? Il existe 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 il existe une classe qui implémente ces deux interfaces. Pour éviter toute incertitude et compiler le code, nous devons remplacer la méthode foo()dans la classe Cet nous pouvons simplement appeler une méthode foo()de l'une des interfaces qu'elle contient - Aou B. Mais comment choisir une méthode d'interface spécifique Аou В? Il existe une structure comme celle-ci pour celaA.super.foo() :
public class C implements A, B {
   @Override
   public void foo() {
       A.super.foo();
   }
}
ou:
public class C implements A, B {
   @Override
   public void foo() {
       B.super.foo();
   }
}
Ainsi, une méthode foo()de classe Cutilisera soit la méthode par défaut foo()de l'interface A, soit une méthode foo()de l'interface B.

16. Que sont les méthodes et classes abstraites ?

Java a un mot réservé abstractutilisé pour désigner les classes et méthodes abstraites. Tout d’abord, quelques définitions. Une méthode abstraite est une méthode créée sans implémentation avec un mot-clé abstractdans une classe abstraite. C'est-à-dire qu'il s'agit d'une méthode comme dans l'interface, uniquement avec l'ajout d'un mot-clé, par exemple :
public abstract void foo();
Une classe abstraite est une classe qui contient également abstractle mot :
public abstract class A {

}
Une classe abstraite a plusieurs fonctionnalités :
  • un objet ne peut pas être créé sur cette base ;
  • il peut avoir des méthodes abstraites ;
  • il ne peut pas avoir de méthodes abstraites.
Les classes abstraites sont nécessaires pour généraliser une sorte d'abstraction (désolé pour la tautologie), qui n'existe pas dans la vie réelle, mais qui contient de nombreux comportements et états communs (c'est-à-dire des méthodes et des variables). Il existe suffisamment d’exemples tirés de la vie. Tout est autour de nous. Il peut s'agir d'un « animal », d'une « voiture », d'une « figure géométrique », etc.

17. Quelle est la différence entre String, String Builder et String Buffer ?

Les valeurs Stringsont stockées dans un pool de chaînes constantes. Une fois qu’une ligne est créée, elle apparaîtra dans ce pool. Et il ne sera pas possible de le supprimer. Par exemple:
String name = "book";
...la variable fera référence au pool de chaînes Pool de chaînes constantes Top 50 des questions et réponses d'entretien avec Java Core.  Partie 1 à 4 Si vous définissez le nom de la variable sur une valeur différente, vous obtiendrez ce qui suit :
name = "pen";
Pool de chaînes constant Top 50 des questions et réponses d'entretien avec Java Core.  Partie 1 à 5Ces deux valeurs resteront donc là. Tampon de chaîne :
  • les valeurs Stringsont stockées sur la pile. Si la valeur est modifiée, la nouvelle valeur sera remplacée par l'ancienne ;
  • String Buffersynchronisé et donc thread-safe ;
  • En raison de la sécurité du filetage, la vitesse de fonctionnement laisse beaucoup à désirer.
Exemple:
StringBuffer name = "book";
Top 50 des questions et réponses d'entretien avec Java Core.  Partie 1 à 6Dès que la valeur de name change, la valeur sur la pile change : Top 50 des questions et réponses d'entretien avec Java Core.  Partie 1 à 7StringBuilder Exactement la même chose que StringBuffer, sauf qu'il n'est pas thread-safe. Par conséquent, sa vitesse est nettement supérieure à celle de 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 ; il est appelé chaque fois qu'un enfant de cette classe abstraite est créé ;
  • contient à la fois des méthodes abstraites et non abstraites. Dans l'ensemble, il se peut qu'elle ne contienne pas de méthodes abstraites, mais qu'elle reste néanmoins une classe abstraite ;
  • une classe qui hérite d'une classe abstraite doit implémenter uniquement des méthodes abstraites ;
  • une classe abstraite peut contenir une variable d'instance (voir question n°5).
Interface:
  • n'a pas de constructeur et ne peut pas être initialisé ;
  • seules les méthodes abstraites doivent être ajoutées (sans compter les méthodes par défaut) ;
  • les classes qui implémentent une interface doivent implémenter toutes les méthodes (sans compter les méthodes par défaut) ;
  • les interfaces ne peuvent contenir que des constantes.

19. Pourquoi l'accès à un élément d'un tableau nécessite-t-il O(1) ?

Cette question vient littéralement de la dernière interview. Comme je l'ai appris plus tard, cette question est posée afin de voir comment une personne pense. Il est clair que cette connaissance a peu de signification pratique : il suffit de connaître ce fait. Tout d’abord, nous devons clarifier que O(1) est une désignation de la complexité temporelle d’un algorithme lorsque l’opération se déroule en temps constant. C'est-à-dire que cette désignation est l'exécution la plus rapide. Pour répondre à cette question, nous devons comprendre ce que nous savons sur les tableaux ? Pour créer un tableau int, il faut écrire ce qui suit :
int[] intArray = new int[100];
Plusieurs conclusions peuvent être tirées de cet enregistrement :
  1. Lors de la création d'un tableau, son type est connu. Si le type est connu, alors il est clair quelle sera la taille de chaque cellule du tableau.
  2. On sait quelle sera la taille du tableau.
Il en découle : pour comprendre dans quelle cellule écrire, il suffit de calculer dans quelle zone mémoire écrire. Pour une voiture, cela ne pourrait pas être plus simple. La machine dispose d'un début de mémoire allouée, d'un certain nombre d'éléments et d'une seule taille de cellule. Il en ressort clairement que l'espace d'enregistrement sera égal à l'emplacement de départ du tableau + la taille de la cellule multipliée par sa taille.

Comment obtenir O(1) en accédant aux objets dans une ArrayList ?

Cette question suit immédiatement la précédente. C’est vrai que lorsqu’on travaille avec un tableau et qu’il y a des primitives là-bas, on sait à l’avance quelle est la taille de ce type lors de sa création. Mais que se passe-t-il si nous avons un schéma comme celui de l'image : Top 50 des questions et réponses d'entretien avec Java Core.  Partie 1 à 8et que nous voulons créer une collection avec des éléments de type A, et ajouter différentes implémentations - B, C, 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 pouvez-vous comprendre quelle sera la taille de chaque cellule, car chaque objet sera différent et peut avoir des champs supplémentaires différents (ou être complètement différent). Ce qu'il faut faire? Ici, la question est posée de manière à prêter à confusion et à confondre. On sait qu'en effet, la collection ne stocke pas d'objets, mais seulement des liens vers ces objets. Et tous les liens ont la même taille, et c’est connu. Donc compter l’espace ici fonctionne de la même manière que dans la question précédente.

21. Autoboxing et unboxing

Contexte historique : l'autoboxing et l'autounboxing sont l'une des principales innovations du JDK 5. L'autoboxing est le processus de conversion automatique d'un type primitif vers la classe wrapper appropriée. Le déballage automatique - fait exactement le contraire de l'auto-boxing - convertit la classe wrapper en primitive. Mais s'il existe une valeur wrapper null, alors une exception sera levée lors du déballage NullPointerException.

Primitive correspondante - wrapper

Primitif Classe d'emballage
booléen Booléen
int Entier
octet Octet
carboniser Personnage
flotter Flotter
long Long
court Court
double Double

L'emballage automatique se produit :

  • lors de l'attribution à une primitive d'une référence à la classe wrapper :

    AVANT Java 5 :

    //manual packaging or how it was BEFORE Java 5.
    public void boxingBeforeJava5() {
       Boolean booleanBox = new Boolean(true);
       Integer intBox = new Integer(3);
       // and so on to other types
    }
    
    после Java 5:
    //automatic packaging or how it became in Java 5.
    public void boxingJava5() {
       Boolean booleanBox = true;
       Integer intBox = 3;
       // and so on to other types
    }
  • lors du passage d'une primitive comme 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 automatique se produit :

  • lorsque nous attribuons une variable primitive à la classe wrapper :

    //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;
  • Dans les cas d'opérations arithmétiques. Ils s'appliquent uniquement aux types primitifs ; pour cela, vous devez effectuer un déballage sur la primitive.

    // Before Java 5
    Integer integerBox1 = new Integer(1);
    Integer integerBox2 = new Integer(2);
    
    // for comparison it was necessary to do this:
    integerBox1.intValue() > integerBox2.intValue()
    
    //в Java 5
    integerBox1 > integerBox2
  • lorsqu'il est transmis à un wrapper dans une méthode qui accepte 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ù l'utiliser ?

Le mot-clé finalpeut être utilisé pour les variables, les méthodes et les classes.
  1. Une variable finale ne peut pas être réaffectée à un autre objet.
  2. la classe finale est stérile)) elle ne peut pas avoir d'héritiers.
  3. La méthode final ne peut pas être remplacée sur un ancêtre.
Nous avons couvert le haut, parlons-en maintenant plus en détail.

variables finales

;Java nous offre deux façons de créer une variable et de lui attribuer une valeur :
  1. Vous pouvez déclarer une variable et l'initialiser plus tard.
  2. Vous pouvez déclarer une variable et l'affecter immédiatement.
Exemple utilisant la variable finale pour ces cas :
public class FinalExample {

   //final static variable, which is immediately initialized:
   final static String FINAL_EXAMPLE_NAME = "I'm likely final one";

   //final is a variable that is not initialized, but will only work if
   //initialize this 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);

       // final field FinalExample.FINAL_EXAMPLE_NAME cannot be assigned
//    FinalExample.FINAL_EXAMPLE_NAME = "Not you're not!";

       // final field Config.creationTime cannot be assigned
//    finalExample.creationTime = 1L;
   }
}

La variable Final peut-elle être considérée comme une constante ?

Comme nous ne pourrons pas attribuer une nouvelle valeur à une variable finale, il semble que ce soient des variables constantes. Mais ce n’est qu’un premier coup d’œil. Si le type de données auquel la variable fait référence est immutable, alors oui, c'est une constante. Mais si le type de données mutableest mutable, à l'aide de méthodes et de variables, il sera possible de modifier la valeur de l'objet auquel finalla variable fait référence, et dans ce cas, elle ne peut pas être appelée constante. Ainsi, l’exemple montre que certaines des variables finales sont en réalité des constantes, mais d’autres ne le sont pas et peuvent être modifiées.
public class FinalExample {

   //immutable final variables:
   final static String FINAL_EXAMPLE_NAME = "I'm likely final one";
   final static Integer FINAL_EXAMPLE_COUNT  = 10;

   // mutable filter variables
   final List<String> addresses = new ArrayList();
   final StringBuilder finalStringBuilder = new StringBuilder("constant?");
}

Variables finales locales

Lorsqu'une finalvariable est créée dans une méthode, elle est appelée local finalvariable :
public class FinalExample {

   public static void main(String[] args) {
       // This is how you can
       final int minAgeForDriveCar = 18;

       // or you can do it this way, in the foreach loop:
       for (final String arg : args) {
           System.out.println(arg);
       }
   }

}
Nous pouvons utiliser le mot-clé finalen boucle étendue forcar une fois l'itération de la boucle terminée, forune nouvelle variable est créée à chaque fois. Mais cela ne s'applique pas à une boucle for normale, donc le code ci-dessous générera une erreur de compilation.
// final local changed j cannot be assigned
for (final int i = 0; i < args.length; i ++) {
   System.out.println(args[i]);
}

Cours final

Vous ne pouvez pas étendre une classe déclarée comme final. En termes simples, aucune classe ne peut hériter de celle-ci. Un bon exemple finalde classe dans le JDK est String. La première étape pour créer une classe immuable consiste à la marquer comme final, afin qu'elle ne puisse pas être étendue :
public final class FinalExample {
}

// Compilation error here
class WantsToInheritFinalClass extends FinalExample {
}

Méthodes finales

Lorsqu’une méthode est marquée comme finale, on l’appelle une méthode finale (logique, non ?). La méthode Final ne peut pas être substituée dans une classe descendante. À propos, les méthodes de la classe Object - wait() et notify() - sont définitives, nous n'avons donc pas la possibilité de les remplacer.
public class FinalExample {
   public final String generateAddress() {
       return "Some address";
   }
}

class ChildOfFinalExample extends FinalExample {

   // compile error here
   @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 lorsque vous ne souhaitez pas qu'ils soient modifiés. Par exemple, des propriétés spécifiques à un objet que nous pouvons utiliser à des fins de journalisation ;
  • si vous ne souhaitez pas que le cours soit étendu, marquez-le comme final ;
  • 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, désignez la méthode comme final. Ceci est très important pour garantir que la mise en œuvre ne change pas.

23. Qu'est-ce qui est mutable et immuable ?

Mutable

Les mutables sont des objets dont les états et les variables peuvent être modifiés après la création. Par exemple, des classes telles que StringBuilder, 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());

       // update 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 les états et les variables ne peuvent pas être modifiés après la création de l'objet. Pourquoi pas une excellente clé pour un HashMap, n'est-ce pas ?) Par exemple, String, Integer, Double, etc. Exemple:
// 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;
   }

   //remove the setter

   public static void main(String[] args) {

       ImmutableExample obj = new ImmutableExample("old address");
       System.out.println(obj.getAddress());

       // Therefore, do not change this field in any way, so this is an immutable object
       // obj.setName("new address");
       // System.out.println(obj.getName());

   }
}

24. Comment écrire une classe immuable ?

Après avoir compris ce que sont les objets mutables et immuables, la question suivante est naturelle : comment l'écrire ? Pour écrire une classe immuable immuable, vous devez suivre des étapes simples :
  • rendre le cours final.
  • rendez tous les champs privés et créez uniquement des getters pour eux. Bien entendu, les setters ne sont pas nécessaires.
  • Rendre tous les champs mutables définitifs afin que la valeur ne puisse être définie qu'une seule fois.
  • initialiser tous les champs via le constructeur, en effectuant une copie complète (c'est-à-dire en copiant l'objet lui-même, ses variables, les variables des variables, etc.)
  • clonez des objets variables mutables dans les getters pour renvoyer uniquement des copies de valeurs et non des références à des objets réels.
Exemple:
/**
* An example of creating an immutable object.
*/
public final class FinalClassExample {

   private final int age;

   private final String name;

   private final HashMap<String, String> addresses;

   public int getAge() {
       return age;
   }


   public String getName() {
       return name;
   }

   /**
    * Clone the object before returning it.
    */
   public HashMap<String, String> getAddresses() {
       return (HashMap<String, String>) addresses.clone();
   }

   /**
    * In the constructor, deep copy the mutable objects.
    */
   public FinalClassExample(int age, String name, HashMap<String, String> addresses) {
       System.out.println("Performing a deep copy in the constructor");
       this.age = age;
       this.name = name;
       HashMap<String, String> temporaryMap = new HashMap<>();
       String key;
       Iterator<String> iterator = addresses.keySet().iterator();
       while (iterator.hasNext()) {
           key = iterator.next();
           temporaryMap.put(key, addresses.get(key));
       }
       this.addresses = temporaryMap;
   }
}
Commentaires
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION