Qu'est-ce que le chaînage d'exceptions en Java | Gestion des exceptions
Qu'entendez-vous par exceptions chaînées en Java ?
Le chaînage d'exceptions , ou emballage d'exceptions , est une technique de programmation orientée objet permettant de gérer les exceptions . Le chaînage d'exceptions se produit lorsqu'une exception provoque une autre exception. une exception chaînée est une exception causée par une autre exception. Les exceptions chaînées sont associées de sorte que l'exception précédente provoque chaque exception de la chaîne.
· Classe jetable
· Utilisation d'exceptions chaînées
· Pourquoi des exceptions chaînées ?
· Difficultés avec les exceptions enchaînées
· Programme
Classe jetable
Constructeurs de la classe Throwable qui prennent en charge les exceptions chaînées en Java :
- Throwable(Throwable cause) :- Où cause est l'exception qui provoque l'exception actuelle.
- Throwable(String msg, Throwable cause) :- Où msg est le message d'exception et cause est l'exception qui provoque l'exception actuelle.
- Méthode getCause() :- Cette méthode renvoie la cause réelle d'une exception.
- Méthode initCause(Throwable cause) : - Cette méthode définit la cause de l'exception d'appel.
Souvent, vous souhaitez intercepter une exception et en lancer une autre, tout en conservant les informations sur l'exception d'origine - c'est ce qu'on appelle le chaînage d'exceptions. L'exception d'origine est la cause de la deuxième exception. Ainsi, les exceptions chaînées permettent de relier une exception à une autre exception, c'est-à-dire qu'une exception décrit la cause d'une autre exception.
Avant JDK 1.4, les programmeurs devaient écrire leur propre code pour conserver les informations d'exception d'origine, mais maintenant toutes les sous-classes Throwable ont la possibilité de prendre un objet cause dans leur constructeur. La cause est censée être l'exception d'origine et, en la transmettant, vous maintenez la trace de la pile jusqu'à son origine, même si vous créez et levez une nouvelle exception.
Le chaînage d'exception est également appelé emballage d'exception car nous le faisons en relançant une exception interceptée après l'avoir enveloppée dans une nouvelle exception. L'exception d'origine est enregistrée en tant que propriété (telle que cause ) de la nouvelle exception.
Pratique
try {
// creating an exception
NullPointerException e = new NullPointerException("Actual cause");
// wrapping the original exception in a new exception
ArithmeticException exc = new ArithmeticException("Apearent cause",e);
// throwing the exception
throw exc;
} catch(ArithmeticException e) {
// display top level exception (aprearent cause)
System.out.println("Caught: " + e);
// Getting the actual cause of the exception
System.out.println("Original cause: " + e.getCause());
}
}
Caught: java.lang.ArithmeticException: Apearent cause
Original cause: java.lang.NullPointerException: Actual cause
Par exemple, considérez une méthode qui lève une ArithmeticException en raison d'une tentative de division par zéro mais la cause réelle de l'exception était une erreur d'E/S qui a fait que le diviseur était égal à zéro. La méthode lèvera l' ArithmeticException à l'appelant. L'appelant ne serait pas au courant de la cause réelle d'une Exception . L'exception chaînée est utilisée dans de telles situations.
Il est intéressant de noter que les seules sous-classes Throwable qui fournissent l'argument cause dans le constructeur sont les trois classes d'exception fondamentales Error (utilisées par la JVM pour signaler les erreurs système), Exception et RuntimeException . Si vous souhaitez enchaîner d'autres types d'exceptions, vous le faites via la méthode initCause() plutôt que via le constructeur.
Exemple
Voici un exemple qui illustre les mécanismes de gestion des exceptions chaînées :
// Demonstrate exception chaining.
class ChainExcDemo {
static void demoproc() {
// create an exception
NullPointerException e = new NullPointerException("top layer");
// add a cause
e.initCause(new ArithmeticException("cause"));
throw e;
}
public static void main(String args[]) {
try {
demoproc();
} catch (NullPointerException e) {
// display top level exception
System.out.println("Caught: " + e);
// display cause exception
System.out.println("Original cause: " + e.getCause());
}
}
}
Caught: java.lang.NullPointerException: top layer
Original cause: java.lang.ArithmeticException: cause
Les exceptions enchaînées peuvent être poursuivies jusqu'à la profondeur nécessaire. Ainsi, l'exception de cause peut, elle-même, avoir une cause. Sachez que des chaînes d'exceptions trop longues peuvent indiquer une mauvaise conception.
Les exceptions chaînées ne sont pas quelque chose dont chaque programme aura besoin. Cependant, dans les cas où la connaissance d'une cause sous-jacente est utile, ils offrent une solution élégante.
Pourquoi enchaîner les exceptions ?
Le chaînage d'exceptions vous permet de mapper un type d'exception à un autre, afin qu'une méthode puisse lever des exceptions définies au même niveau d'abstraction que la méthode elle-même, sans ignorer les informations de débogage importantes.
Autrement dit, si vous avez une méthode qui charge un objet à partir d'une base de données, vous voudrez peut-être plutôt une ResourceLoadException (plus proche du niveau d'abstraction des méthodes) au lieu d'une SQLException de bas niveau même si c'était la source originale du problème. Cependant, si vous interceptez simplement l' exception SQLException et lancez une exception ResourceLoadException à la place, vous risquez de perdre des informations de débogage importantes.
Ainsi, enchaîner les exceptions est une bonne alternative. Vous lancez une exception "de haut niveau", bien adaptée à la méthode particulière, mais enchaînez-la avec l'exception qui l'a provoquée.
Nous devons enchaîner les exceptions pour rendre les journaux lisibles. Écrivons deux exemples. D'abord sans enchaîner les exceptions et ensuite, avec des exceptions enchaînées. Plus tard, nous comparerons le comportement des journaux dans les deux cas.
Pour commencer, nous allons créer une série d'exceptions :
class NoLeaveGrantedException extends Exception {
public NoLeaveGrantedException(String message, Throwable cause) {
super(message, cause);
}
public NoLeaveGrantedException(String message) {
super(message);
}
}
class TeamLeadUpsetException extends Exception {
// Both Constructors
}
Écrivons un exemple de programme sans enchaîner nos exceptions personnalisées.
public class MainClass {
public void main(String[] args) throws Exception {
getLeave();
}
void getLeave() throws NoLeaveGrantedException {
try {
howIsTeamLead();
} catch (TeamLeadUpsetException e) {
e.printStackTrace();
throw new NoLeaveGrantedException("Leave not sanctioned.");
}
}
void howIsTeamLead() throws TeamLeadUpsetException {
throw new TeamLeadUpsetException("Team Lead Upset");
}
}
com.baeldung.chainedexception.exceptions.TeamLeadUpsetException:
Team lead Upset
at com.baeldung.chainedexception.exceptions.MainClass
.howIsTeamLead(MainClass.java:46)
at com.baeldung.chainedexception.exceptions.MainClass
.getLeave(MainClass.java:34)
at com.baeldung.chainedexception.exceptions.MainClass
.main(MainClass.java:29)
Exception in thread "main" com.baeldung.chainedexception.exceptions.
NoLeaveGrantedException: Leave not sanctioned.
at com.baeldung.chainedexception.exceptions.MainClass
.getLeave(MainClass.java:37)
at com.baeldung.chainedexception.exceptions.MainClass
.main(MainClass.java:29)
Ensuite, écrivons un exemple en enchaînant nos exceptions personnalisées :
public class MainClass {
public void main(String[] args) throws Exception {
getLeave();
}
public getLeave() throws NoLeaveGrantedException {
try {
howIsTeamLead();
} catch (TeamLeadUpsetException e) {
throw new NoLeaveGrantedException("Leave not sanctioned.", e);
}
}
public void howIsTeamLead() throws TeamLeadUpsetException {
throw new TeamLeadUpsetException("Team lead Upset.");
}
}
Exception in thread "main" com.baeldung.chainedexception.exceptions
.NoLeaveGrantedException: Leave not sanctioned.
at com.baeldung.chainedexception.exceptions.MainClass
.getLeave(MainClass.java:36)
at com.baeldung.chainedexception.exceptions.MainClass
.main(MainClass.java:29)
Caused by: com.baeldung.chainedexception.exceptions
.TeamLeadUpsetException: Team lead Upset.
at com.baeldung.chainedexception.exceptions.MainClass
.howIsTeamLead(MainClass.java:44)
at com.baeldung.chainedexception.exceptions.MainClass
.getLeave(MainClass.java:34)
... 1 more
Difficultés avec les exceptions chaînées
Nous pouvons utiliser le chaînage d'exceptions dans des situations où une autre exception provoque une exception. Cependant, il est important de noter que le chaînage peut rendre notre code plus difficile à lire et à comprendre. Par conséquent, nous devons utiliser le chaînage des exceptions avec parcimonie et uniquement lorsque cela est nécessaire.
Si nous utilisons des exceptions chaînées, c'est une bonne idée de documenter la chaîne dans notre code. Cela aidera les autres à comprendre notre code et facilitera le débogage en cas d'erreur.
Programme
//: exceptions/DynamicFields.java
// A Class that dynamically adds fields to itself.
// Demonstrates exception chaining.
import static net.mindview.util.Print.*;
class DynamicFieldsException extends Exception {}
public class DynamicFields {
private Object[][] fields;
public DynamicFields(int initialSize) {
fields = new Object[initialSize][2];
for(int i = 0; i < initialSize; i++)
fields[i] = new Object[] { null, null };
}
public String toString() {
StringBuilder result = new StringBuilder();
for(Object[] obj : fields) {
result.append(obj[0]);
result.append(": ");
result.append(obj[1]);
result.append("\n");
}
return result.toString();
}
//...................hasField(String id)........................//
private int hasField(String id) {
for(int i = 0; i < fields.length; i++)
if(id.equals(fields[i][0]))
return i;
return -1;
}
//......................getFieldNumber(String id)....................//
private int getFieldNumber(String id) throws NoSuchFieldException {
int fieldNum = hasField(id);
if(fieldNum == -1)
throw new NoSuchFieldException();
return fieldNum;
}
//........................makeField(String id).....................//
private int makeField(String id) {
for(int i = 0; i < fields.length; i++)
if(fields[i][0] == null) {
fields[i][0] = id;
return i;
}
// No empty fields. Add one:
Object[][] tmp = new Object[fields.length + 1][2];
for(int i = 0; i < fields.length; i++)
tmp[i] = fields[i];
for(int i = fields.length; i < tmp.length; i++)
tmp[i] = new Object[] { null, null };
fields = tmp;
// Recursive call with expanded fields:
return makeField(id);
}
//.............................getField(String id).......................//
public Object getField(String id) throws NoSuchFieldException {
return fields[getFieldNumber(id)][1];
}
//.........................setField(String id, Object value)..........................
public Object setField(String id, Object value) throws DynamicFieldsException {
if(value == null) {
// Most exceptions don’t have a "cause" constructor.
// In these cases you must use initCause(),
// available in all Throwable subclasses.
DynamicFieldsException dfe = new DynamicFieldsException();
dfe.initCause(new NullPointerException());
throw dfe;
}
int fieldNumber = hasField(id);
if(fieldNumber == -1)
fieldNumber = makeField(id);
Object result = null;
try {
result = getField(id); // Get old value
} catch(NoSuchFieldException e) {
// Use constructor that takes "cause":
throw new RuntimeException(e);
}
fields[fieldNumber][1] = value;
return result;
}
// ......................main...................//
public static void main(String[] args) {
DynamicFields df = new DynamicFields(3);
print(df);
try {
df.setField("d", "A value for d");
df.setField("number", 47);
df.setField("number2", 48);
print(df);
df.setField("d", "A new value for d");
df.setField("number3", 11);
print("df: " + df);
print("df.getField(\"d\") : " + df.getField("d"));
Object field = df.setField("d", null); // Exception
} catch(NoSuchFieldException e) {
e.printStackTrace(System.out);
} catch(DynamicFieldsException e) {
e.printStackTrace(System.out);
}
}
}
null: null
null: null
null: null
d: A value for d
number: 47
number2: 48
df: d: A new value for d
number: 47
number2: 48
number3: 11
df.getField("d") : A new value for d
DynamicFieldsException
at DynamicFields.setField(DynamicFields.java:64)
at DynamicFields.main(DynamicFields.java:94)
Caused by: java.lang.NullPointerException
at DynamicFields.setField(DynamicFields.java:66)
... 1 more
*///:~
En tant que valeur de retour, setField() récupère également l'ancienne valeur à cet emplacement de champ à l'aide de getField() , ce qui peut déclencher une NoSuchFieldException . Si le programmeur client appelle getField() , alors il est responsable de la gestion de NoSuchFieldException , mais si cette exception est levée à l'intérieur de setField() , c'est une erreur de programmation, donc NoSuchFieldException est convertie en RuntimeException en utilisant le constructeur qui prend un argument de cause.
Vous remarquerez que toString( ) utilise un StringBuilder pour créer son résultat. Vous en apprendrez plus sur StringBuilder dans le chapitre Strings, mais en général vous voudrez l'utiliser chaque fois que vous écrivez un toString() qui implique une boucle, comme c'est le cas ici.
Bon apprentissage
Rejoindre Mouad Oumous Java WhatsApp Group REJOINDRE
Rejoindre Mouad Oumous Telegram Channel REJOINDRE
Soutenez notre publication en la suivant
Maximiser le potentiel de votre équipe : comment occuper votre équipe de livraison de logiciels Question d'entrevue basée sur le flux Java8 la plus fréquemment posée — Dernière partie Comment créer un projet Quarkus simple avec intégration de base de données Relancer une exception en Java | Gestion des exceptions Je gagne 300 $ en écrivant un livre en 2 jours - Voici comment 10 façons simples mais efficaces d'améliorer votre qualité de vie![Qu'est-ce qu'une liste liée, de toute façon? [Partie 1]](https://post.nghiatu.com/assets/images/m/max/724/1*Xokk6XOjWyIGCBujkJsCzQ.jpeg)



































