"Amigo, dix-hutte!"

"Je suis content d'apprendre Java, Capitaine !"

"A l'aise, Amigo. Aujourd'hui, nous avons un sujet super intéressant. Nous allons parler de la façon dont un programme Java interagit avec des ressources externes et nous étudierons une déclaration Java très intéressante. Mieux vaut ne pas se boucher les oreilles."

"Je suis tout ouïe."

"Lorsqu'un programme Java s'exécute, il interagit parfois avec des entités extérieures à la machine Java. Par exemple, avec des fichiers sur disque. Ces entités sont généralement appelées ressources externes."

« Alors, qu'est-ce qu'on considère comme des ressources internes ? »

"Les ressources internes sont les objets créés à l'intérieur de la machine Java. Généralement, l'interaction suit ce schéma :

Instruction Try-with-resources

"Le système d'exploitation garde rigoureusement une trace des ressources disponibles et contrôle également l'accès partagé à celles-ci à partir de différents programmes. Par exemple, si un programme modifie un fichier, un autre programme ne peut pas modifier (ou supprimer) ce fichier. Ce principe n'est pas limités aux fichiers, mais ils fournissent l'exemple le plus facilement compréhensible.

"Le système d'exploitation a des fonctions (API) qui permettent à un programme d'acquérir et/ou de libérer des ressources. Si une ressource est occupée, seul le programme qui l'a acquise peut travailler avec elle. Si une ressource est libre, alors n'importe quel programme peut acquérir il.

"Imaginez qu'un bureau partage des tasses à café. Si quelqu'un prend une tasse, les autres ne peuvent plus la prendre. Mais une fois la tasse utilisée, lavée et remise à sa place, n'importe qui peut la reprendre."

"Compris. C'est comme les sièges dans le métro ou dans d'autres transports en commun. Si un siège est libre, alors n'importe qui peut le prendre. Si un siège est occupé, alors il est contrôlé par la personne qui l'a pris."

"C'est vrai. Et parlons maintenant de l'acquisition de ressources externes . Chaque fois que votre programme Java commence à travailler avec un fichier sur disque, la machine Java demande au système d'exploitation un accès exclusif à celui-ci. Si la ressource est libre, alors la machine Java acquiert il.

"Mais une fois que vous avez fini de travailler avec le fichier, cette ressource (fichier) doit être libérée, c'est-à-dire que vous devez informer le système d'exploitation que vous n'en avez plus besoin. Si vous ne le faites pas, la ressource continuera à être détenus par votre programme. »

"Cela semble juste."

"Pour que cela reste ainsi, le système d'exploitation maintient une liste des ressources occupées par chaque programme en cours d'exécution. Si votre programme dépasse la limite de ressources attribuée, le système d'exploitation ne vous donnera plus de nouvelles ressources.

"C'est comme des programmes qui peuvent consommer toute la mémoire..."

"Quelque chose comme ça. La bonne nouvelle est que si votre programme se termine, toutes les ressources sont automatiquement libérées (le système d'exploitation le fait lui-même)."

"Si c'est la bonne nouvelle, est-ce que ça veut dire qu'il y a une mauvaise nouvelle ?"

"Précisément. La mauvaise nouvelle est que si vous écrivez une application serveur..."

"Mais est-ce que j'écris de telles applications?"

"De nombreuses applications serveur sont écrites en Java, vous les écrirez donc très probablement pour le travail. Comme je le disais, si vous écrivez une application serveur, votre serveur doit fonctionner sans arrêt pendant des jours, des semaines, des mois, etc."

"En d'autres termes, le programme ne se termine pas, et cela signifie que la mémoire n'est pas automatiquement libérée."

"Exactement. Et si vous ouvrez 100 fichiers par jour et que vous ne les fermez pas, dans quelques semaines, votre application atteindra sa limite de ressources et se bloquera."

« C'est bien loin de mois de travail stable ! Que peut-on faire ?

"Les classes qui utilisent des ressources externes ont une méthode spéciale pour les libérer : close().

"Voici un exemple de programme qui écrit quelque chose dans un fichier puis ferme le fichier une fois terminé, c'est-à-dire qu'il libère les ressources du système d'exploitation. Il ressemble à peu près à ceci :

Code Note
String path = "c:\\projects\\log.txt";
FileOutputStream output = new FileOutputStream(path);
output.write(1);
output.close();
Le chemin d'accès au fichier.
Obtenir l'objet fichier : acquérir la ressource.
Écrire dans le fichier
Fermer le fichier - libérer la ressource

"Ah... Donc, après avoir travaillé avec un fichier (ou d'autres ressources externes), je dois appeler la close()méthode sur l'objet lié à la ressource externe."

"Oui. Tout semble simple. Mais des exceptions peuvent se produire lors de l'exécution d'un programme et la ressource externe ne sera pas libérée."

« Et c'est très mauvais. Que faire ?

"Pour nous assurer que la close()méthode est toujours appelée, nous devons envelopper notre code dans un bloc try- catch- finallyet ajouter la close()méthode au finallybloc. Cela ressemblera à ceci :

try
{
   FileOutputStream output = new FileOutputStream(path);
   output.write(1);
   output.close();
}
catch (IOException e)
{
   e.printStackTrace();
}
finally
{
   output.close();
}

"Hmm... Quelque chose ne va pas ici ?"

"Bien. Ce code ne compilera pas, car la outputvariable est déclarée à l'intérieur du try{}bloc, et n'est donc pas visible dans le finallybloc.

Réparons-le :

FileOutputStream output = new FileOutputStream(path);

try
{
   output.write(1);
   output.close();
}
catch (IOException e)
{
   e.printStackTrace();
}
finally
{
   output.close();
}

"Est-ce que tout va bien maintenant?"

"C'est bon, mais cela ne fonctionnera pas si une erreur se produit lors de la création de l' FileOutputStreamobjet, et cela pourrait se produire assez facilement.

Réparons-le :

FileOutputStream output = null;

try
{
   output = new FileOutputStream(path);
   output.write(1);
   output.close();
}
catch (IOException e)
{
   e.printStackTrace();
}
finally
{
   output.close();
}

« Et est-ce que tout fonctionne maintenant ?

"Il y a encore quelques critiques. Premièrement, si une erreur se produit lors de la création de l' FileOutputStreamobjet, alors la outputvariable sera nulle. Cette possibilité doit être prise en compte dans le finallybloc.

"Deuxièmement, la close()méthode est toujours appelée dans le finallybloc, ce qui signifie qu'elle n'est pas nécessaire dans le trybloc. Le code final ressemblera à ceci :

FileOutputStream output = null;

try
{
   output = new FileOutputStream(path);
   output.write(1);
}
catch (IOException e)
{
   e.printStackTrace();
}
finally
{
   if (output!=null)
      output.close();
}

"Même si nous ne considérons pas le catchbloc, qui peut être omis, nos 3 lignes de code deviennent 10. Mais nous venons d'ouvrir le fichier et d'écrire 1."

"Ouf... C'est une bonne chose qui clôt l'affaire. Relativement compréhensible, mais un peu fastidieux, n'est-ce pas ?"

"C'est vrai. C'est pourquoi les créateurs de Java nous ont aidés en ajoutant du sucre syntaxique. Passons maintenant au point culminant du programme, ou plutôt à cette leçon :

try-avec-ressources

"A partir de sa 7e version, Java a une nouvelle tryinstruction -with-resources.

"Il a été créé précisément pour résoudre le problème de l'appel obligatoire à la close()méthode."

« Cela semble prometteur ! »

« Le cas général semble assez simple :

try (ClassName name = new ClassName())
{
   Code that works with the name variable
}

"Alors c'est une autre variante de la try déclaration ?"

"Oui. Vous devez ajouter des parenthèses après le trymot clé, puis créer des objets avec des ressources externes à l'intérieur des parenthèses. Pour chaque objet entre parenthèses, le compilateur ajoute une finallysection et un appel à la close()méthode.

" Ci-dessous deux exemples équivalents :

Code long Code avec try-with-resources
FileOutputStream output = null;

try
{
   output = new FileOutputStream(path);
   output.write(1);
}
finally
{
   if (output!=null)
   output.close();
}
try(FileOutputStream output = new FileOutputStream(path))
{
   output.write(1);
}

"Cool ! Le code utilisant try-with-resources est beaucoup plus court et plus facile à lire. Et moins nous avons de code, moins nous avons de chances de faire une faute de frappe ou une autre erreur."

"Je suis content que vous l'aimiez. Au fait, nous pouvons ajouter catchdes finallyblocs à l' tryinstruction -with-resources. Ou vous ne pouvez pas les ajouter s'ils ne sont pas nécessaires.

Plusieurs variables à la fois

"Vous pouvez souvent rencontrer une situation où vous devez ouvrir plusieurs fichiers en même temps. Supposons que vous copiez un fichier, vous avez donc besoin de deux objets : le fichier à partir duquel vous copiez des données et le fichier vers lequel vous copiez des données. .

"Dans ce cas, l' tryinstruction -with-resources vous permet de créer un mais plusieurs objets. Le code qui crée les objets doit être séparé par des points-virgules. Voici l'apparence générale de cette instruction :

try (ClassName name = new ClassName(); ClassName2 name2 = new ClassName2())
{
   Code that works with the name and name2 variables
}

Exemple de copie de fichiers :

Petit code Code long
String src = "c:\\projects\\log.txt";
String dest = "c:\\projects\\copy.txt";

try(FileInputStream input = new FileInputStream(src);

FileOutputStream output = new FileOutputStream(dest))
{
   byte[] buffer = input.readAllBytes();
   output.write(buffer);
}
String src = "c:\\projects\\log.txt";
String dest = "c:\\projects\\copy.txt";

FileInputStream input = null;
FileOutputStream output = null;

try
{
   input = new FileInputStream(src);
   output = new FileOutputStream(dest);

   byte[] buffer = input.readAllBytes();
   output.write(buffer);
}
finally
{
   if (input!=null)
      input.close();
   if (output!=null)
      output.close();
}

"Eh bien, que pouvons-nous dire ici ? try-avec-des-ressources est une chose merveilleuse !"

"Ce que nous pouvons dire, c'est que nous devrions l'utiliser."