1. Ressources externes
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. Les ressources internes sont les objets créés à l'intérieur de la machine Java.
En règle générale, l'interaction suit ce schéma :
Ressources de suivi
Le système d'exploitation suit rigoureusement les 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é aux fichiers, mais ils fournissent l'exemple le plus facilement compréhensible.
Le système d'exploitation possède 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 l'utiliser. Si une ressource est gratuite, alors n'importe quel programme peut l'acquérir.
Imaginez que votre 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. La situation avec des sièges dans un bus ou un métro est la même. Si une place est libre, n'importe qui peut la prendre. Si un siège est occupé, il est alors contrôlé par la personne qui l'a occupé.
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 l'acquiert.
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 notifier au système d'exploitation que vous n'en avez plus besoin. Si vous ne le faites pas, la ressource continuera d'être détenue par votre programme.
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 assignée, le système d'exploitation ne vous donnera plus de nouvelles ressources.
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).
La mauvaise nouvelle est que si vous écrivez une application serveur (et de nombreuses applications serveur sont écrites en Java), votre serveur doit pouvoir fonctionner pendant des jours, des semaines et des mois sans s'arrêter. 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 loin de mois de travail stable.
2. close()
méthode
Les classes qui utilisent des ressources externes ont une méthode spéciale pour les libérer : close()
.
Ci-dessous, nous donnons 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. Cela ressemble à ceci :
Code | Note |
---|---|
|
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 |
Après avoir travaillé avec un fichier (ou d'autres ressources externes), vous devez appeler la close()
méthode sur l'objet lié à la ressource externe.
Des exceptions
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.
Pour nous assurer que la close()
méthode est toujours appelée, nous devons envelopper notre code dans un bloc try
- catch
- finally
et ajouter la close()
méthode au finally
bloc. Cela ressemblera à ceci :
try
{
FileOutputStream output = new FileOutputStream(path);
output.write(1);
output.close();
}
catch (IOException e)
{
e.printStackTrace();
}
finally
{
output.close();
}
Ce code ne compilera pas, car la output
variable est déclarée à l'intérieur du try {}
bloc, et n'est donc pas visible dans le finally
bloc.
Réparons-le :
FileOutputStream output = new FileOutputStream(path);
try
{
output.write(1);
output.close();
}
catch (IOException e)
{
e.printStackTrace();
}
finally
{
output.close();
}
Ce n'est pas grave, mais cela ne fonctionnera pas si une erreur se produit lors de la création de l' FileOutputStream
objet, et cela peut 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();
}
Il y a encore quelques critiques. Tout d'abord, si une erreur se produit lors de la création de l' FileOutputStream
objet, alors la output
variable sera nulle. Cette possibilité doit être prise en compte dans le finally
bloc.
Deuxièmement, la close()
méthode est toujours appelée dans le finally
bloc, ce qui signifie qu'elle n'est pas nécessaire dans le try
bloc. 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 catch
bloc, qui peut être omis, alors nos 3 lignes de code deviennent 10. Mais nous venons d'ouvrir le fichier et d'écrire 1. Un peu lourd, vous ne trouvez pas ?
3. try
-avec-ressources
Et ici, les créateurs de Java ont décidé de nous saupoudrer de sucre syntaxique. À partir de sa 7e version, Java a une nouvelle try
instruction -with-resources.
Il a été créé précisément pour résoudre le problème de l'appel obligatoire à la close()
méthode. Le cas général semble assez simple :
try (ClassName name = new ClassName())
{
Code that works with the name variable
}
Ceci est une autre variante de la try
déclaration . Vous devez ajouter des parenthèses après le try
mot-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 finally
section et un appel à la close()
méthode.
Ci-dessous deux exemples équivalents :
Code long | Code avec try-with-resources |
---|---|
|
|
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.
Au fait, nous pouvons ajouter catch
et finally
bloquer à l' try
instruction -with-resources. Ou vous ne pouvez pas les ajouter s'ils ne sont pas nécessaires.
4. Plusieurs variables en même temps
Au fait, 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 dans lequel vous copiez des données.
Dans ce cas, l' try
instruction -with-resources vous permet d'y 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 déclaration :
try (ClassName name = new ClassName(); ClassName2 name2 = new ClassName2())
{
Code that works with the name and name2 variables
}
Exemple de copie de fichiers :
Code long | Petit code |
---|---|
|
|
Eh bien, que pouvons-nous dire ici? try
-avec-ressources est une chose merveilleuse !
5. AutoCloseable
interfaces
Mais ce n'est pas tout. Le lecteur attentif commencera immédiatement à rechercher les pièges qui limitent la manière dont cette déclaration peut être appliquée.
Mais comment try
fonctionne l'instruction -with-resources si la classe n'a pas de close()
méthode ? Eh bien, supposons que rien ne sera appelé. Pas de méthode, pas de problème.
Mais comment try
fonctionne l'instruction -with-resources si la classe a plusieurs close()
méthodes ? Et ils ont besoin qu'on leur passe des arguments ? Et la classe n'a pas de close()
méthode sans paramètres ?
J'espère que vous vous êtes vraiment posé ces questions, et peut-être d'autres encore.
Pour éviter de tels problèmes, les créateurs de Java ont créé une interface spéciale appelée AutoCloseable
, qui n'a qu'une seule méthode — close()
, qui n'a pas de paramètres.
Ils ont également ajouté la restriction selon laquelle seuls les objets des classes qui implémententAutoCloseable
peuvent être déclarés comme ressources dans une try
instruction -with-resources. Par conséquent, ces objets auront toujours une close()
méthode sans paramètres.
Au fait, pensez-vous qu'il soit possible pour une try
instruction -with-resources de déclarer comme ressource un objet dont la classe a sa propre close()
méthode sans paramètre mais qui ne l'implémente pas AutoCloseable
?
La mauvaise nouvelle : la bonne réponse est non — les classes doivent implémenter l' AutoCloseable
interface.
La bonne nouvelle : Java a beaucoup de classes qui implémentent cette interface, il est donc très probable que tout fonctionnera comme il se doit.
GO TO FULL VERSION