1. Variables de référence

Dans le langage Java, il existe deux types de variables : les variables primitives et tout le reste. En l'occurrence, nous allons parler de "tout le reste" maintenant.

En fait, il serait plus correct de dire qu'il existe des variables primitives et des variables de référence . Quelles sont donc ces variables de référence ?

Contrairement aux types primitifs, dont les variables stockent directement les valeurs, les variables de référence stockent les références aux objets. Autrement dit, il y a un objet quelque part en mémoire et la variable de référence stocke simplement l'adresse de cet objet en mémoire (une référence à l'objet).

Seuls les types primitifs stockent les valeurs directement dans les variables. Tous les autres types stockent uniquement une référence d'objet . Soit dit en passant, vous avez déjà rencontré deux types de variables de ce type - Stringles variables et les variables de tableau .

Un tableau et une chaîne sont des objets stockés quelque part en mémoire. Stringles variables et les variables de tableau ne stockent que les références aux objets.

Variables de référence en Java

int a, int b and double dsont des variables primitives qui stockent leurs valeurs à l'intérieur d'elles-mêmes.

Une String strvariable est une référence et stocke l'adresse (référence) d'un Stringobjet en mémoire.

Lors de l'affectation d'une valeur primitive à une variable de type primitif, sa valeur est copiée (dupliquée). Lors de l'affectation d'une variable de référence, seule l'adresse de l'objet est copiéel'objet lui-même n'est pas copié .


2. En quoi consistent les références ?

Quelle est la différence fondamentale entre les variables de référence et les variables primitives ?

Une variable primitive est comme une boîte : vous pouvez y stocker une valeur. Une variable de référence ressemble plus à une feuille de papier avec un numéro de téléphone dessus.

Une voiture contre les clés de la voiture

Imaginez que vous décidiez d'offrir une voiture à votre ami pour son anniversaire. Vous ne l'emballerez pas dans une boîte et ne l'emporterez pas avec vous : la voiture est trop grande pour cela.

Il est beaucoup plus pratique de présenter uniquement les clés de la voiture dans une boîte suffisamment grande pour les contenir. Votre ami comprendra tout quand il sortira les clés de la boîte. Il n'est pas nécessaire de transporter toute la voiture avec vous lorsque vous pouvez simplement remettre les clés.

Une personne contre son numéro de téléphone

Ou voici une autre comparaison : une personne et son numéro de téléphone. Un numéro de téléphone n'est pas la personne, mais un numéro de téléphone peut être utilisé pour l'appeler, lui demander des informations ou lui donner des instructions.

De même, une référence est utilisée pour interagir avec un objet. Tous les objets interagissent les uns avec les autres à l'aide de références. Au lieu "d'échanger des personnes", nous échangeons simplement des numéros de téléphone.

Lors de l'affectation d'une valeur à une variable primitive, sa valeur est copiée (dupliquée). Lors de l'attribution d'une valeur à une variable de référence, seule l'adresse (numéro de téléphone) de l'objet est copiée — l'objet lui-même n'est pas copié.

Une référence offre un avantage supplémentaire : vous pouvez passer une référence d'objet à une méthode, et la méthode pourra modifier (changer) l'objet en utilisant la référence à celui-ci, en appelant ses méthodes et en accédant aux données à l'intérieur de l'objet.


3. Attribuer des références

Lors de l'affectation de variables de référence, seule l'adresse de l'objet en mémoire est affectée. Les objets eux-mêmes n'apparaissent ni ne disparaissent.

Cette approche évite de copier de grandes quantités de mémoire. Si vous avez besoin de passer un objet très volumineux à une méthode, nous passons simplement la référence de l'objet et c'est tout. La référence prend beaucoup moins de place.

Attribuer des références

La taille de toutes les variables de référence (quel que soit leur type) est la même — 4 octets (comme un int). Mais! Si votre application s'exécute sur une machine Java 64 bits, toutes les références auront une taille de 8 octets (64 bits).

De plus, les références ne peuvent être attribuées qu'entre elles. Vous ne pouvez pas modifier les références ou attribuer des valeurs arbitraires aux variables de référence :

Code Description
String hello = "Hello";
String s = hello;
Ceci est autorisé
String hello = "Hello";
hello++;
Mais ce n'est pas autorisé
String hello = 0x1234;
Et ce n'est pas autorisé

4. Une nullréférence

Et que stocke une variable de référence si rien ne lui a encore été assigné ?

Il stocke une référence nulle . nullest un mot-clé Java spécial signifiant l'absence de référence (une référence vide). La nullvaleur peut être affectée à n'importe quelle variable de référence.

Toutes les variables de référence le sont nullà moins qu'une sorte de référence ne leur ait été attribuée.

Exemples:

Code Description
class Person
{
   public static String name;
   public static int age;
}


La String namevariable a une valeur par défaut : null.
La int agevariable a une valeur par défaut : 0.

Les variables locales auxquelles aucune valeur n'a été affectée sont considérées comme non initialisées pour les types primitifs et de référence.

Si une variable stocke une référence à un objet et que vous souhaitez effacer la valeur de la variable, affectez-lui simplement une référence nulle.

Code Description
String s = null;
s = "Hello";
s = null;
smagasins null.
sstocke une référence à un objet de chaîne
sstocke null.

5. Passer des références aux méthodes

Si une méthode a des paramètres qui sont des types référence , les valeurs sont transmises à la méthode de la même manière que lorsque vous travaillez avec des variables non-références. Le paramètre reçoit simplement la valeur de l'autre variable.

Exemple:

Code Description
class Solution
{
   public static void fill(String[] array, String value)
   {
      for (int i = 0; i < array.length; i++)
        array[i] = value;
   }

   public static void main(String[] args)
   {
     String[] data = new String[10];
     fill(data, "Hello");
   }
}


Le fillremplit le tableau passé ( array) avec la valeur passée ( value).

Lorsque la fillméthode est appelée, le arrayparamètre se voit attribuer une référence au datatableau. La valuevariable est affectée d'une référence à l'objet chaîne ("Hello").

Voici à quoi ressemble la mémoire avant d'appeler la fill méthode :

Passer des références à des méthodes

Voici à quoi ressemble la mémoire lorsque la fill méthode est en cours d'exécution :

Passer des références aux méthodes 2

Les variables dataet arrayfont référence (stockent les références) au même conteneur en mémoire.

La valuevariable stocke une référence à l'objet chaîne ( "Hello").

Les cellules du tableau stockent également les références à l' "Hello"objet.

En fait, aucun objet n'est dupliqué — seules les références sont copiées.



6. Comparaison avec le langage C/C++

Dans les entretiens, on demande parfois aux programmeurs Java comment les données sont transmises aux méthodes en Java ? Et parfois la question est de savoir si les données sont transmises par référence ou par valeur ?

Cette question vient de C++, mais n'est pas très significative en Java . En Java, les paramètres reçoivent toujours simplement les valeurs des arguments. La bonne réponse serait donc " en valeur ".

Mais soyez prêt à expliquer votre position , car vous pourriez immédiatement entendre la réplique : "les types primitifs sont passés par valeur et les types de référence sont passés par référence".

L'origine de ce problème vient du fait que de nombreux programmeurs Java étaient des programmeurs C++ dans le passé. Dans ce langage de programmation, la question de savoir comment les paramètres sont passés aux méthodes était très importante.

En Java, tout est sans ambiguïté : les types primitifs stockent des valeurs et les types référence stockent également une valeur — une référence. Il s'agit de savoir si une variable est considérée comme une valeur .

En C++, une variable peut stocker à la fois une référence à un objet et l'objet lui-même. Il en était de même pour les types primitifs : une variable primitive pouvait stocker une valeur ou déclarer la variable comme référence à un int. Ainsi, pour éviter toute confusion, les programmeurs C++ font toujours référence à l' objet d'une référence en tant que référence et à l'objet lui-même — en tant que valeur.

En C++, vous pourriez facilement avoir la situation où une variable contient un objet, mais l'autre contient une référence à cet objet. En conséquence, la question de savoir ce qu'une variable stocke - l'objet lui-même ou simplement une référence à celui-ci - était très importante. Lorsqu'un objet était passé à une méthode, il était copié (s'il était passé par valeur) et non copié (s'il était passé par référence).

En Java, cette dualité n'existe pas, donc la bonne réponse est : les arguments sont passés aux méthodes Java par value . C'est juste que lorsque nous parlons de variables de référence, cette valeur est une référence.