103. Quelles sont les règles de vérification des exceptions en matière d'héritage ?
Si je comprends bien la question, ils posent des questions sur les règles pour travailler avec des exceptions lors de l'héritage, et elles sont les suivantes :- Une méthode remplacée ou implémentée dans un descendant/une implémentation ne peut pas lever d'exceptions vérifiées qui sont plus élevées dans la hiérarchie que les exceptions de la méthode superclasse/interface.
public interface Animal {
void voice() throws IOException;
}
Dans l'implémentation de cette interface, nous ne pouvons pas lancer une exception levée plus générale (par exemple, Exception , Throwable ), mais nous pouvons la remplacer par une exception descendante, telle que FileNotFoundException :
public class Cat implements Animal {
@Override
public void voice() throws FileNotFoundException {
// некоторая реализация
}
}
- Le constructeur de sous-classe doit inclure dans son bloc throws toutes les classes d'exception levées par le constructeur de superclasse appelé lors de la création de l'objet.
public class Animal {
public Animal() throws ArithmeticException, NullPointerException, IOException {
}
Ensuite l'héritier de classe doit également les indiquer dans le constructeur :
public class Cat extends Animal {
public Cat() throws ArithmeticException, NullPointerException, IOException {
super();
}
Ou, comme dans le cas des méthodes, vous pouvez spécifier non pas les mêmes exceptions, mais des exceptions plus générales. Dans notre cas, il suffira de préciser une exception plus générale - Exception , puisqu'il s'agit de l'ancêtre commun des trois exceptions considérées :
public class Cat extends Animal {
public Cat() throws Exception {
super();
}
104. Pourriez-vous écrire du code lorsque le bloc final ne sera pas exécuté ?
Tout d’abord, rappelons-nous ce qu’est finalement . Auparavant, nous avons examiné le mécanisme de capture des exceptions : le bloc try décrit la zone de capture, tandis que le ou les blocs catch sont le code qui fonctionnera lorsqu'une exception spécifique est levée. Enfin est le troisième bloc de code après enfin qui est interchangeable avec catch mais ne s'exclut pas mutuellement. L'essence de ce bloc est que le code qu'il contient fonctionne toujours, quel que soit le résultat du try ou du catch (indépendamment du fait qu'une exception ait été levée ou non). Les cas de défaillance sont très rares et anormaux. Le cas d'échec le plus simple est lorsque la méthode System.exit(0) est appelée dans le code ci-dessus , ce qui termine le programme (l'éteint) :try {
throw new IOException();
} catch (IOException e) {
System.exit(0);
} finally {
System.out.println("Данное сообщение не будет выведенно в консоль");
}
Il existe également d'autres situations dans lesquelles cela ne fonctionnera finalement pas :
- Arrêt anormal du programme causé par des problèmes critiques du système, ou la chute d'une erreur qui fera "planter" l'application (un exemple d'erreur peut être la même StackOwerflowError qui se produit lorsque la mémoire de la pile déborde).
- Lorsque le thread démon passe par le bloc ry...finally et en parallèle, le programme se termine. Après tout, le thread démon est un thread pour les actions en arrière-plan, c'est-à-dire qu'il n'est ni prioritaire ni obligatoire, et l'application n'attendra pas la fin de son travail.
- La boucle infinie la plus courante, dans try ou catch , une fois dans laquelle le flux y restera pour toujours :
try { while (true) { } } finally { System.out.println("Данное сообщение не будет выведенно в консоль"); }
105. Écrivez un exemple de gestion de plusieurs exceptions dans un seul bloc catch
1) Peut-être que la question n’a pas été posée correctement. Autant que je sache, cette question fait référence à plusieurs captures pour un bloc try :try {
throw new FileNotFoundException();
} catch (FileNotFoundException e) {
System.out.print("Упс, у вас упало исключение - " + e);
} catch (IOException e) {
System.out.print("Упс, у вас упало исключение - " + e);
} catch (Exception e) {
System.out.print("Упс, у вас упало исключение - " + e);
}
Si une exception se produit dans un bloc try , alors les blocs catch tentent alternativement de l'attraper de haut en bas. Si un certain bloc catch réussit, il obtient le droit de gérer l'exception, tandis que le reste des blocs ci-dessous ne le seront plus. capables d'essayer de l'attraper et de le traiter à leur manière. Par conséquent, les exceptions plus étroites sont placées plus haut dans la chaîne de blocs catch , et les exceptions plus larges sont placées plus bas. Par exemple, si dans notre premier bloc catch une exception de la classe Exception est interceptée , alors les exceptions vérifiées ne pourront pas entrer dans les blocs restants (les blocs restants avec des descendants d'Exception seront absolument inutiles). 2) La question a été posée correctement. Dans ce cas, notre traitement ressemblera à ce qui suit :
try {
throw new NullPointerException();
} catch (Exception e) {
if (e instanceof FileNotFoundException) {
// некоторая обработка с сужением типа (FileNotFoundException)e
} else if (e instanceof ArithmeticException) {
// некоторая обработка с сужением типа (ArithmeticException)e
} else if(e instanceof NullPointerException) {
// некоторая обработка с сужением типа (NullPointerException)e
}
Après avoir détecté une exception via catch , nous essayons de découvrir son type spécifique via la méthode instanceof , qui est utilisée pour vérifier si un objet appartient à un certain type, afin que nous puissions plus tard le réduire à ce type sans conséquences négatives. Les deux approches envisagées peuvent être utilisées dans la même situation, mais j'ai dit que la question est incorrecte car je ne qualifierais pas la deuxième option de bonne et je ne l'ai jamais vue dans ma pratique, alors qu'en même temps la première méthode avec multi-prises s'est répandue. attention.
106. Quel opérateur vous permet de forcer la levée d'une exception ? Écrivez un exemple
Je l'ai déjà utilisé plusieurs fois ci-dessus, mais je répéterai néanmoins ce mot-clé - throw . Exemple d'utilisation (forcer une exception) :throw new NullPointerException();
107. La méthode main peut-elle lever une exception throws ? Si oui, où sera-t-il transféré ?
Tout d'abord, je tiens à noter que main n'est rien de plus qu'une méthode régulière, et oui, elle est appelée par la machine virtuelle pour démarrer l'exécution du programme, mais en plus de cela, elle peut être appelée à partir de n'importe quel autre code. Autrement dit, il est également soumis aux règles habituelles pour spécifier les exceptions vérifiées après les lancements :public static void main(String[] args) throws IOException {
En conséquence, des exceptions peuvent également s'y produire. Si main n'a pas été appelé dans une méthode, mais a été démarré comme point de lancement de programme, alors l'exception levée par celui-ci sera gérée par l' intercepteur .UncaughtExceptionHandler . Ce gestionnaire est un par thread (c'est-à-dire un gestionnaire dans chaque thread). Si nécessaire, vous pouvez créer votre propre gestionnaire et le définir à l'aide de la méthode setDefaultUncaughtExceptionHandler appelée sur l' objet Thread .
Multithreading
108. Quels outils connaissez-vous pour travailler avec le multithreading ?
Outils de base/de base pour l'utilisation du multithreading en Java :- Synchronisé est un mécanisme permettant de fermer (bloquer) une méthode/un bloc lorsqu'un thread y entre, à partir d'autres threads.
- Volatile est un mécanisme permettant de garantir un accès cohérent à une variable par différents threads, c'est-à-dire qu'avec la présence de ce modificateur sur une variable, toutes les opérations d'affectation et de lecture doivent être atomiques. En d’autres termes, les threads ne copieront pas cette variable dans leur mémoire locale et ne la modifieront pas, mais modifieront sa valeur d’origine.
- Runnable est une interface qui peut être implémentée (en particulier sa méthode run) dans une certaine classe :
public class CustomRunnable implements Runnable {
@Override
public void run() {
// некоторая логика
}
}
Et après avoir créé un objet de cette classe, vous pouvez démarrer un nouveau thread en définissant cet objet dans le constructeur du nouvel objet Thread et en appelant sa méthode start() :
Runnable runnable = new CustomRunnable();
new Thread(runnable).start();
La méthode start exécute la méthode run() implémentée dans un thread séparé.
- Thread est une classe dont hérite (tout en remplaçant la méthode run ) :
public class CustomThread extends Thread {
@Override
public void run() {
// некоторая логика
}
}
Et en créant un objet de cette classe et en le lançant à l'aide de la méthode start() , nous lancerons ainsi un nouveau thread :
new CustomThread().start();
- Concurrency est un package comprenant des outils permettant de travailler dans un environnement multithread.
- Concurrent Collections - un ensemble de collections spécialisées pour travailler dans un environnement multithread.
- Files d'attente - files d'attente spécialisées pour un environnement multithread (bloquantes et non bloquantes).
- Les synchroniseurs sont des utilitaires spécialisés permettant de travailler dans un environnement multithread.
- Les exécuteurs sont des mécanismes permettant de créer des pools de threads.
- Verrous - mécanismes de synchronisation des threads (plus flexibles que les mécanismes standard - synchronisé, attendre, notifier, notifierAll).
- Les atomiques sont des classes optimisées pour une exécution multithread ; chaque opération est atomique.
109. Parlez de synchronisation entre les threads. À quoi servent les méthodes wait(), notify() - notifyAll() join() ?
Pour autant que je comprends la question, la synchronisation entre les threads concerne le modificateur de clé - synchronisé . Ce modificateur peut être placé soit directement à côté du bloc :synchronized (Main.class) {
// некоторая логика
}
Ou directement dans la signature de la méthode :
public synchronized void move() {
// некоторая логика}
Comme je l'ai dit plus tôt, synchronisé est un mécanisme qui vous permet de fermer un bloc/une méthode à partir d'autres threads lorsqu'un thread y est déjà entré. Considérez un bloc/une méthode comme une pièce. Un ruisseau, étant venu vers elle, y entrera et la verrouillera, d'autres ruisseaux, étant venus dans la pièce et voyant qu'elle est fermée, attendront près d'elle jusqu'à ce qu'elle soit libre. Ayant fait son affaire, le premier fil quitte la pièce et libère la clé. Et ce n'est pas pour rien que je parlais constamment de la clé, car elle existe vraiment. Il s'agit d'un objet spécial qui a un état occupé/libre. Cet objet est attaché à chaque objet Java, donc lors de l'utilisation d'un bloc synchronisé , nous devons indiquer entre parenthèses l'objet dont nous voulons fermer la porte avec le mutex :
Cat cat = new Cat();
synchronized (cat) {
// некоторая логика
}
Vous pouvez également utiliser un mutex de classe, comme je l'ai fait dans le premier exemple ( Main.class ). Lorsqu’on utilise synchronisé sur une méthode, on ne précise pas l’objet sur lequel on veut fermer, non ? Dans ce cas, pour une méthode non statique, elle se fermera sur le mutex de cet objet , c'est-à-dire l'objet actuel de cette classe. Le statique se fermera sur le mutex de la classe actuelle ( this.getClass(); ). Vous pouvez en savoir plus sur le mutex ici . Eh bien, lisez à propos de la synchronisation ici . Wait() est une méthode qui libère le mutex et met le thread actuel en mode veille, comme s'il était attaché au moniteur actuel (quelque chose comme une ancre). Pour cette raison, cette méthode ne peut être appelée qu'à partir d'un bloc ou d'une méthode synchronisée (sinon, que devrait-elle libérer et à quoi devrait-elle s'attendre). Notez également qu'il s'agit d'une méthode de la classe Object . Plus précisément, pas un, mais même trois :
-
Wait() - met le thread actuel en mode attente jusqu'à ce qu'un autre thread appelle la méthode notify() ou notifyAll() pour cet objet (nous parlerons de ces méthodes plus tard).
-
Wait (long timeout) - met le thread actuel en mode attente jusqu'à ce qu'un autre thread appelle la méthode notify() ou notifyAll() sur cet objet ou que le délai d'attente spécifié expire .
-
Attendre (long timeout, int nanos) - similaire au précédent, seul nanos vous permet de spécifier des nanosecondes (réglage du temps plus précis).
-
Notify() est une méthode qui vous permet de réveiller un thread aléatoire du bloc de synchronisation actuel. Encore une fois, il ne peut être appelé que dans un bloc ou une méthode synchronisé (après tout, ailleurs, il n'y aura personne à dégeler).
-
NotifyAll() est une méthode qui réveille tous les threads en attente sur le moniteur actuel (également utilisée uniquement dans un bloc ou une méthode synchronisée ).
110. Comment arrêter le flux ?
La première chose à dire est que lorsque la méthode run() est entièrement exécutée , le thread est automatiquement détruit. Mais parfois, vous devez le tuer plus tôt que prévu, avant que cette méthode ne soit terminée. Alors que devrions-nous faire alors ? Peut-être que l' objet Thread devrait avoir une méthode stop() ? Peu importe comment c'est ! Cette méthode est considérée comme obsolète et peut entraîner des pannes du système. Eh bien, et alors ? Il existe deux manières de procéder : La première consiste à utiliser votre indicateur booléen interne. Regardons un exemple. Nous avons notre propre implémentation d'un fil de discussion qui devrait afficher une certaine phrase à l'écran jusqu'à ce qu'elle s'arrête complètement :public class CustomThread extends Thread {
private boolean isActive;
public CustomThread() {
this.isActive = true;
}
@Override
public void run() {
{
while (isActive) {
System.out.println("Поток выполняет некую логику...");
}
System.out.println("Поток остановлен!");
}
}
public void stopRunningThread() {
isActive = false;
}
}
Lors de l'utilisation de la méthode stopRunning() , l'indicateur interne devient faux et la méthode run cesse de s'exécuter. Exécutons-le dans main :
System.out.println("Начало выполнения программы");
CustomThread thread = new CustomThread();
thread.start();
Thread.sleep(3);
// пока наш основной поток спит, вспомогательный CustomThread работает и выводит в коноль своё сообщение
thread.stopRunningThread();
System.out.println("Конец выполнения программы");
En conséquence, nous verrons quelque chose comme ceci dans la console :
public class CustomThread extends Thread {
@Override
public void run() {
{
while (!Thread.interrupted()) {
System.out.println("Поток выполняет некую логику...");
}
System.out.println("Поток остановлен!");
}
}
}
Exécuter en principal :
System.out.println("Начало выполнения программы");
Thread thread = new CustomThread();
thread.start();
Thread.sleep(3);
thread.interrupt();
System.out.println("Конец выполнения программы");
Le résultat de l'exécution sera le même que dans le premier cas, mais j'aime mieux cette approche : nous écrivons moins de code et utilisons davantage de fonctionnalités standard toutes faites. C'est là que nous nous arrêterons aujourd'hui.
GO TO FULL VERSION