Un guide de création d'objets en Java

1. Vue d'ensemble

En termes simples, avant de pouvoir travailler avec un objet sur la JVM, il doit être initialisé.

Dans les sections suivantes, nous examinerons différentes manières d'initialiser des types et des objets primitifs.

2. Déclaration vs initialisation

Commençons par nous assurer que nous sommes sur la même longueur d'onde.

La déclaration est le processus de définition de la variable avec son type et son nom.

Ici, nous déclarons la variable id :

int id;

L'initialisation, en revanche, consiste à attribuer une valeur; par exemple:

id = 1;

Pour démontrer, nous allons créer une classe User avec un nom et des propriétés d' identifiant :

public class User { private String name; private int id; // standard constructor, getters, setters, }

Ensuite, nous verrons que l'initialisation fonctionne différemment selon le type de champ que nous initialisons.

3. Objets vs primitifs

Java fournit deux types de représentation des données: les types primitifs et les types de référence. Dans cette section, nous discuterons des différences entre les deux en ce qui concerne l'initialisation.

Java a huit types de données intégrés, appelés types primitifs Java; les variables de ce type contiennent directement leurs valeurs.

Les types de référence contiennent des références à des objets (instances de classes). Contrairement aux types primitifs qui conservent leurs valeurs dans la mémoire où la variable est allouée, les références ne contiennent pas la valeur de l'objet auquel elles font référence.

Au lieu de cela, une référence pointe vers un objet en stockant l'adresse mémoire où se trouve l'objet.

Notez que Java ne nous permet pas de découvrir quelle est l'adresse mémoire physique. Au contraire, nous ne pouvons utiliser la référence que pour faire référence à l'objet.

Jetons un coup d'œil à un exemple qui déclare et initialise un type de référence hors de notre classe User :

@Test public void whenIntializedWithNew_thenInstanceIsNotNull() { User user = new User(); assertThat(user).isNotNull(); }

Comme nous pouvons le voir ici, une référence peut être affectée à un nouveau en utilisant le mot-clé new, qui est responsable de la création du nouvel objet User .

Continuons en apprenant plus sur la création d'objets.

5. Création d'objets

Contrairement aux primitives, la création d'objets est un peu plus complexe. C'est parce que nous ne nous contentons pas d'ajouter la valeur au champ; à la place, nous déclenchons l'initialisation à l'aide du nouveau mot-clé. Ceci, en retour, appelle un constructeur et initialise l'objet en mémoire.

Discutons plus en détail des constructeurs et du nouveau mot-clé.

Le mot-clé new est responsable de l'allocation de mémoire pour le nouvel objet via un constructeur.

Un constructeur est généralement utilisé pour initialiser des variables d'instance représentant les propriétés principales de l'objet créé .

Si nous ne fournissons pas explicitement un constructeur, le compilateur créera un constructeur par défaut qui n'a pas d'arguments et alloue juste de la mémoire pour l'objet.

Une classe peut avoir de nombreux constructeurs tant que leurs listes de paramètres sont différentes ( surcharge ) . Chaque constructeur qui n'appelle pas un autre constructeur de la même classe a un appel à son constructeur parent, qu'il ait été écrit explicitement ou inséré par le compilateur via super () .

Ajoutons un constructeur à notre classe User :

public User(String name, int id) { this.name = name; this.id = id; }

Nous pouvons maintenant utiliser notre constructeur pour créer un objet User avec des valeurs initiales pour ses propriétés:

User user = new User("Alice", 1);

6. Portée variable

Dans les sections suivantes, nous examinerons les différents types d'étendues dans lesquelles une variable Java peut exister et comment cela affecte le processus d'initialisation.

6.1. Variables d'instance et de classe

Instance and class variables don't require us to initialize them. As soon as we declare these variables, they are given a default value as follows:

Now, let's try to define some instance and class-related variables and test whether they have a default value or not:

@Test public void whenValuesAreNotInitialized_thenUserNameAndIdReturnDefault() { User user = new User(); assertThat(user.getName()).isNull(); assertThat(user.getId() == 0); }

6.2. Local Variables

Local variables must be initialized before use, as they don't have a default value and the compiler won't let us use an uninitialized value.

For example, the following code generates a compiler error:

public void print(){ int i; System.out.println(i); }

7. The Final Keyword

The final keyword applied to a field means that the field's value can no longer be changed after initialization. In this way, we can define constants in Java.

Let's add a constant to our User class:

private static final int YEAR = 2000;

Constants must be initialized either when they're declared or in a constructor.

8. Initializers in Java

In Java, an initializer is a block of code that has no associated name or data type and is placed outside of any method, constructor, or another block of code.

Java offers two types of initializers, static and instance initializers. Let's see how we can use each of them.

8.1. Instance Initializers

We can use these to initialize instance variables.

To demonstrate, let’s provide a value for a user id using an instance initializer in our User class:

{ id = 0; }

8.2. Static Initialization Block

A static initializer or static block – is a block of code which is used to initialize static fields. In other words, it’s a simple initializer marked with the keyword static:

private static String forum; static { forum = "Java"; }

9. Order of Initialization

When writing code that initializes different types of fields, of course, we have to keep an eye on the order of initialization.

In Java, the order for initialization statements is as follows:

  • static variables and static initializers in order
  • instance variables and instance initializers in order
  • constructors

10. Object Life Cycle

Now that we've learned how to declare and initialize objects, let's discover what happens to objects when they're not in use.

Unlike other languages where we have to worry about object destruction, Java takes care of obsolete objects through its garbage collector.

All objects in Java are stored in our program's heap memory. In fact, the heap represents a large pool of unused memory, allocated for our Java application.

On the other hand, the garbage collector is a Java program that takes care of automatic memory management by deleting objects that are no longer reachable.

For a Java object to become unreachable, it has to encounter one of the following situations:

  • The object no longer has any references pointing to it
  • All reference pointing to the object are out of scope

In conclusion, an object is first created from a class, usually using the keyword new. Then the object lives its life and provides us with access to its methods and fields.

Finally, when it's no longer needed, the garbage collector destroys it.

11. Other Methods for Creating Objects

In this section, we’ll take a brief look at methods other than new keyword to create objects and how to apply them, specifically reflection, cloning, and serialization.

Reflection is a mechanism we can use to inspect classes, fields, and methods at run-time. Here's an example of creating our User object using reflection:

@Test public void whenInitializedWithReflection_thenInstanceIsNotNull() throws Exception { User user = User.class.getConstructor(String.class, int.class) .newInstance("Alice", 2); assertThat(user).isNotNull(); }

In this case, we're using reflection to find and invoke a constructor of the User class.

The next method, cloning, is a way to create an exact copy of an object. For this, our User class must implement the Cloneable interface:

public class User implements Cloneable { //... }

Maintenant, nous pouvons utiliser la méthode clone () pour créer un nouvel objet clonedUser qui a les mêmes valeurs de propriété que l' objet utilisateur :

@Test public void whenCopiedWithClone_thenExactMatchIsCreated() throws CloneNotSupportedException { User user = new User("Alice", 3); User clonedUser = (User) user.clone(); assertThat(clonedUser).isEqualTo(user); }

Nous pouvons également utiliser la classe sun.misc.Unsafe pour allouer de la mémoire à un objet sans appeler de constructeur:

User u = (User) unsafeInstance.allocateInstance(User.class);

12. Conclusion

Dans ce tutoriel, nous avons couvert l'initialisation des champs en Java. Nous avons découvert différents types de données en Java et comment les utiliser. Nous avons également approfondi plusieurs méthodes de création d'objets en Java.

La mise en œuvre complète de ce didacticiel est disponible à l'adresse over sur Github.