Les Exceptions en Java
Définition
Une exception est une condition anormale qui interrompt l'exécution normale
d'un programme. Elle est utilisée pour signaler et gérer les erreurs ou situations
inattendues. Lorsqu'une exception est levée, une instance de la classe
correspondante est créée et contient des informations sur l'erreur.
Hiérarchie des Exceptions
Toutes les exceptions dérivent de la classe Throwable, divisée en deux sous-
classes principales :
1. Error : Représente des erreurs graves liées à l'environnement d'exécution,
souvent irrécupérables. Exemples :
o OutOfMemoryError
o StackOverflowError
2. Exception : Représente des erreurs pouvant être gérées par le
programme. Deux sous-types :
o Checked Exceptions : Doivent être déclarées ou gérées avec try-
catch.
- IOException: Erreur d'entrée/sortie (lecture/écriture de
fichiers, connexion réseau).
- ClassNotFoundException: Classe introuvable dans le
classpath de l'application.
- SQLException: Erreur lors de l'accès à une base de
données.
- InterruptedException: Thread interrompu pendant son
exécution.
- ParseException: Erreur lors de l'analyse d'une chaîne
en date ou nombre.
o Unchecked Exceptions (RuntimeException) : Liées à des erreurs
de programmation et ne nécessitent pas de gestion explicite.
- NullPointerException: Tentative d'accès à une
référence d'objet nulle.
- ArrayIndexOutOfBoundsException: Tentative d'accès à
un élément d'un tableau avec un index invalide.
- IllegalArgumentException: Appel d'une méthode avec
un argument invalide.
3. Différences entre Checked et Unchecked Exceptions
Aspect Checked Exceptions Unchecked Exceptions
Obligation Doivent être capturées ou Pas d'obligation explicite.
déclarées.
1
Moment de Vérifiées à la compilation. Vérifiées uniquement à
vérification l'exécution.
Causes Problèmes externes (fichiers, Erreurs de programmation.
courantes bases de données).
Exemples IOException, SQLException. NullPointerException,
ArithmeticException.
Gestion des Exceptions
1. Le Bloc try-catch
Permet de capturer et de gérer les exceptions.
try {
int[] array = {1, 2, 3};
int value = array[5]; // Provoque ArrayIndexOutOfBoundsException
} catch (ArrayIndexOutOfBoundsException e) {
[Link]("Index hors limites : " + [Link]());
2. Le Bloc finally
Le bloc finally s’exécute toujours, qu'une exception soit levée ou non. Il est
souvent utilisé pour libérer des ressources (fichiers, connexions).
FileReader reader = null;
try {
reader = new FileReader("[Link]");
int data = [Link]();
} catch (IOException e) {
[Link]("Erreur de lecture : " + [Link]());
} finally {
try {
if (reader != null) [Link]();
} catch (IOException e) {
[Link]("Erreur lors de la fermeture du fichier.");
2
}
3. Déclaration avec throws
Une méthode peut signaler qu'elle peut lever une exception à la méthode
appelante.
public void readFile(String fileName) throws IOException {
FileReader reader = new FileReader(fileName);
Exemple Checked :
try {
FileReader reader = new FileReader("[Link]");
} catch (FileNotFoundException e) {
[Link]("Fichier introuvable.");
Exemple Unchecked :
String str = null;
[Link]([Link]()); // Provoque NullPointerException
Création d'Exceptions Personnalisées
Pour signaler des erreurs spécifiques, on peut créer ses propres exceptions.
Exemple :
public class InvalidAgeException extends Exception {
public InvalidAgeException(String message) {
super(message);
}
}
public class Person {
private int age;
public Person(int age) throws InvalidAgeException {
if (age < 0 || age > 120) {
throw new InvalidAgeException("Âge invalide : " + age);
3
}
[Link] = age;
}
}
Utilisation :
try {
Person p = new Person(150);
} catch (InvalidAgeException e) {
[Link]("Erreur : " + [Link]());
}
Blocs try-catch Multiples
On peut capturer plusieurs types d'exceptions avec des blocs catch.
try {
int result = 10 / 0;
} catch (ArithmeticException e) {
[Link]("Division par zéro non autorisée.");
} catch (Exception e) {
[Link]("Une erreur s'est produite : " + [Link]());
}
Exemple Complet : Gestion d'Exceptions avec Nettoyage
Voici un exemple combinant plusieurs concepts :
import [Link].*;
public class Main {
public static void main(String[] args) {
FileReader reader = null;
try {
reader = new FileReader("[Link]");
4
int data = [Link]();
while (data != -1) {
[Link]((char) data);
data = [Link]();
}
} catch (FileNotFoundException e) {
[Link]("Erreur : Fichier introuvable.");
} catch (IOException e) {
[Link]("Erreur lors de la lecture.");
} finally {
try {
if (reader != null) [Link]();
} catch (IOException e) {
[Link]("Erreur lors de la fermeture du fichier.");
}
}
}
}
7. Bonnes Pratiques
1. Valider en amont :
o Validez les entrées pour éviter des exceptions inutiles.
o Exemple : Vérifiez si un dénominateur est nul avant une division.
2. Gérer les exceptions spécifiques :
o Capturez des exceptions précises avant de capturer une exception
générique.
o Exemple :
try {
int result = 10 / 0;
} catch (ArithmeticException e) {
[Link]("Erreur arithmétique.");
} catch (Exception e) {
5
[Link]("Erreur générique.");
}
3. Utiliser le bloc finally pour libérer les ressources.
4. Créer des exceptions personnalisées pour des erreurs spécifiques.
5. Évitez d'abuser de throws Exception, car cela rend le débogage plus
difficile.
8. Méthodes Utiles dans Throwable
1. getMessage() :
o Retourne le message associé à l'exception.
2. printStackTrace() :
o Affiche l'état de la pile d'exécution au moment de l'exception.
3. getStackTrace() :
o Retourne un tableau des éléments de la pile d'exécution.
Exemple :
try {
int[] arr = {1, 2, 3};
[Link](arr[5]);
} catch (ArrayIndexOutOfBoundsException e) {
[Link]("Erreur : " + [Link]());
[Link]();
}
Les Interfaces en Java
1. Définition
Une interface en Java est un contrat ou un ensemble de règles que toute classe
qui l'implémente doit respecter. Elle contient uniquement des méthodes
abstraites (sans corps) et des constantes.
Les interfaces servent à définir les fonctionnalités qu'une classe doit
implémenter, sans spécifier comment elles doivent être implémentées.
Elles permettent de simuler l'héritage multiple, une fonctionnalité qui n'est
pas autorisée avec les classes en Java.
6
2. Caractéristiques principales
1. Méthodes abstraites :
o Les méthodes déclarées dans une interface sont abstraites par
défaut (pas de corps).
o Syntaxe :
public interface MonInterface {
void methode1();
int methode2(String str);
}
2. Constantes :
o Une interface ne peut contenir que des variables static et final
(constantes).
o Exemple :
public interface MonInterface {
int MAX_VAL = 100; // Constant par défaut
}
3. Héritage multiple :
o Une classe peut implémenter plusieurs interfaces.
o Une interface peut hériter d'autres interfaces.
4. Mots-clés utilisés :
o interface : Pour définir une interface.
o implements : Pour qu'une classe implémente une interface.
o extends : Pour qu'une interface hérite d'une autre.
3. Création d'une interface
Pour créer une interface, on utilise le mot-clé interface.
Exemple :
public interface Animal {
void manger();
void dormir();
}
7
4. Implémentation d'une interface
Une classe peut implémenter une ou plusieurs interfaces avec le mot-clé
implements.
La classe doit fournir une implémentation pour toutes les méthodes
abstraites de l'interface.
Si elle ne le fait pas, elle doit être déclarée comme classe abstraite.
Exemple :
public class Chat implements Animal {
public void manger() {
[Link]("Le chat mange.");
}
public void dormir() {
[Link]("Le chat dort.");
}
}
5. Héritage multiple avec les interfaces
En Java, une classe ne peut pas hériter de plusieurs classes (pas d’héritage
multiple), mais peut implémenter plusieurs interfaces.
Exemple :
public interface Volant {
void voler();
}
public interface Nageur {
void nager();
}
public class Oiseau implements Volant, Nageur {
public void voler() {
8
[Link]("L'oiseau vole.");
}
public void nager() {
[Link]("L'oiseau nage.");
}
}
6. Méthodes par défaut
Depuis Java 8, une interface peut inclure des méthodes par défaut avec une
implémentation.
Ces méthodes ne sont pas abstraites et peuvent être directement utilisées
dans les classes implémentant l'interface.
Une classe peut redéfinir une méthode par défaut.
Exemple :
public interface MathOperations {
int add(int x, int y);
default int divide(int x, int y) {
return x / y;
}
}
public class Calculatrice implements MathOperations {
public int add(int x, int y) {
return x + y;
}
public int divide(int x, int y) {
return [Link](x, y) / 2;
}
}
9
7. Méthodes statiques
Une interface peut aussi contenir des méthodes statiques, accessibles
uniquement via le nom de l'interface.
Exemple :
public interface MathUtils {
static int subtract(int x, int y) {
return x - y;
}
}
public class Demo {
public static void main(String[] args) {
int result = [Link](10, 5);
[Link]("Résultat : " + result);
}
}
8. Comparaison : Interface vs Classe Abstraite
Caractéristiq Interface Classe Abstraite
ue
Méthodes 100% abstraites (avant Java Peut contenir des méthodes
8) abstraites et concrètes.
Héritage Une classe peut implémenter Une classe ne peut hériter que
plusieurs interfaces. d'une seule classe abstraite.
Attributs Uniquement des constantes Peut contenir des variables
(static final). d'instance.
Constructeur Pas de constructeurs. Peut avoir des constructeurs.
s
Usage Définir des contrats Fournir une base commune
principal (fonctionnalités). pour une hiérarchie.
1
0
9. Règles et limitations
1. Une interface ne peut pas être instanciée.
2. Toutes les méthodes sont publiques et abstraites par défaut.
3. Une méthode dans une interface ne peut pas être à la fois statique et par
défaut.
4. Une interface ne peut pas contenir de variables non constantes.
10. Exemples pratiques
Exemple 1 : Interface simple
public interface Vehicule {
void demarrer();
void arreter();
}
public class Voiture implements Vehicule {
public void demarrer() {
[Link]("La voiture démarre.");
}
public void arreter() {
[Link]("La voiture s'arrête.");
}
}
Exemple 2 : Interface avec héritage multiple
public interface Terrestre {
void marcher();
}
public interface Marin {
void nager();
}
1
1
public class Tortue implements Terrestre, Marin {
public void marcher() {
[Link]("La tortue marche.");
}
public void nager() {
[Link]("La tortue nage.");
}
}
Exemple 3 : Méthodes par défaut
public interface Animal {
void manger();
default void dormir() {
[Link]("L'animal dort.");
}
}
public class Chien implements Animal {
public void manger() {
[Link]("Le chien mange.");
}
}
11. Applications des Interfaces
1. Définir des contrats :
o Exemple : Une API peut utiliser des interfaces pour garantir que les
classes implémentent certaines méthodes.
2. Encapsulation des comportements communs :
o Exemple : Les méthodes par défaut permettent d'inclure une
logique de base.
3. Remplacer l’héritage multiple :
1
2
o Une classe peut hériter d’une classe et implémenter plusieurs
interfaces.
4. Support pour les frameworks :
o Les interfaces comme Runnable, Serializable ou Comparable sont
largement utilisées dans les bibliothèques Java.
Les Collections
Collections en Java : Concepts clés
Une collection est une structure de données pour stocker et manipuler un
groupe d'objets en mémoire. Elle permet des opérations comme ajouter,
supprimer, rechercher et parcourir des éléments.
Java propose deux grandes catégories de collections :
1. Collections de base :
o Incluent des classes comme ArrayList, LinkedList, HashSet, TreeSet,
HashMap, TreeMap, Hashtable.
o Utilisées pour stocker des données simples.
2. Collections de haut niveau :
o Incluent des classes comme Stack, Queue, Deque, PriorityQueue,
ArrayDeque.
o Fournissent des fonctionnalités avancées pour des opérations
d'empilement ou de file d'attente.
1
3
Hiérarchie des Collections
L'interface de base, Collection, fournit des méthodes standard pour manipuler
des éléments :
Ajouter : add()
Supprimer : remove()
Parcourir : via boucle ou itérateur (iterator()).
Certaines classes dérivées implémentent des interfaces spécifiques pour des
fonctionnalités avancées :
List : ArrayList, LinkedList
Set : HashSet, TreeSet
Map : HashMap, TreeMap
Création et manipulation d'une collection
Créer une liste :
List<Integer> myList = new ArrayList<>(10); // Taille initiale : 10
[Link](1);
[Link](2);
1
4
[Link](3);
Parcourir les éléments :
for (Integer i : myList) {
[Link](i);
}
Collections vs Tableaux
Aspect Tableaux Collections
Taille Fixe à la création. Dynamique, extensible et
réductible.
Type Types primitifs ou objets Objets uniquement (possibilité
d'éléments (homogènes). d'hétérogénéité).
Manipulatio Accès direct via index. Méthodes sophistiquées : tri,
n recherche, suppression.
Flexibilité Limité aux manipulations de Grande flexibilité (tri, filtrage,
base. etc.).
Performanc Plus rapide pour l'accès Plus rapide pour les opérations
e direct et les opérations complexes (tri, recherche).
simples.
Mémoire Plus compacte (allocation Peut être fragmentée (allocation
contiguë). dynamique).
Quand choisir quoi ?
Tableaux : pour des besoins simples, lorsque la taille des données est
connue et fixe.
Collections : pour des manipulations complexes, lorsque la flexibilité et la
dynamique sont importantes.
Méthodes principales de l'interface Collection en Java :
Méthode Description Retour Paramètres
add(E e) Ajoute un élément. boolean e : élément à
ajouter.
addAll(Collection<? Ajoute tous les boolean c : collection
extends E> c) éléments d'une autre source.
collection.
clear() Supprime tous les void N/A
1
5
éléments.
contains(Object o) Vérifie si un élément boolean o : élément à
est présent. rechercher.
containsAll(Collection< Vérifie si tous les boolean c : collection
?> c) éléments d'une à rechercher.
collection sont
présents.
isEmpty() Vérifie si la collection boolean N/A
est vide.
iterator() Retourne un itérateur. Iterator<E N/A
>
remove(Object o) Supprime un élément boolean o : élément à
spécifique. supprimer.
removeAll(Collection<? Supprime tous les boolean c : collection
> c) éléments présents à supprimer.
dans une autre
collection.
retainAll(Collection<?> Garde uniquement les boolean c : collection
c) éléments présents de référence.
dans une autre
collection.
size() Retourne le nombre int N/A
d'éléments.
toArray() Retourne un tableau Object[] N/A
des éléments.
toArray(T[] a) Retourne un tableau <T> T[] a : tableau de
des éléments dans un destination.
tableau existant si
possible.
Comparatif : ArrayList vs Vector
Critère ArrayList Vector
Implémentation Non synchronisé, Synchronisé, adapté au
performant. multithread.
Performances Supérieures. Inférieures (synchronisation).
Capacité initiale Spécifiable. Spécifiable.
Augmentation +50% de la capacité Double de la capacité actuelle.
taille actuelle.
Obsolescence Non obsolète. Obsolète depuis Java 1.2.
1
6
Utilisez ArrayList par défaut. Pour la synchronisation, préférez
[Link]().
Classe Collections : Méthodes principales
Méthode Description
binarySearch(List, Recherche binaire dans une liste triée.
T)
copy(List dest, Copie les éléments d'une liste source vers une liste
List src) destination.
fill(List, T) Remplit une liste avec une valeur donnée.
max(Collection) Retourne le plus grand élément d'une collection.
min(Collection) Retourne le plus petit élément d'une collection.
reverse(List) Inverse l'ordre des éléments d'une liste.
shuffle(List) Mélange aléatoirement les éléments d'une liste.
sort(List) Trie les éléments dans l'ordre naturel.
swap(List, int i, Échange deux éléments d'une liste.
int j)
Exemples
1. Tri avec Comparable
Si les éléments implémentent Comparable, [Link]() utilise leur méthode
compareTo().
List<String> fruits = new ArrayList<>([Link]("pomme", "banane", "orange",
"kiwi"));
[Link](fruits);
[Link](fruits); // [banane, kiwi, orange, pomme]
2. Tri avec Comparator
Si les éléments n'implémentent pas Comparable, utilisez un Comparator
personnalisé.
class Fruit {
String nom, couleur;
// Constructeur, getters et toString()
}
1
7
class FruitNomComparator implements Comparator<Fruit> {
public int compare(Fruit f1, Fruit f2) {
return [Link]().compareTo([Link]());
}
}
List<Fruit> fruits = new ArrayList<>();
[Link](new Fruit("pomme", "rouge"));
[Link](fruits, new FruitNomComparator());
[Link](fruits);
Différence clé : Collection est une interface définissant la structure des
collections, tandis que Collections est une classe utilitaire offrant des
algorithmes.
Set en Java
Un Set est une collection qui garantit que les éléments sont uniques et non
ordonnés (selon l'implémentation). Contrairement aux listes, les éléments d'un
ensemble ne sont pas indexés directement.
Principales implémentations :
1. HashSet :
o Stocke les éléments dans une table de hachage.
o Garantit des opérations rapides (insertion, recherche, suppression)
avec une complexité moyenne de O(1).
o Les éléments ne sont pas triés.
o Autorise un seul élément null.
2. TreeSet :
o Implémente NavigableSet et maintient les éléments triés.
o Tri basé sur l'ordre naturel (Comparable) ou un comparateur externe
(Comparator).
o Complexité des opérations : O(log n).
o Autorise un seul élément null, mais uniquement si le TreeSet est
vide au départ.
1
8
Différences entre List et Set
Critère List Set
Ordonné (indexation Non ordonné (ou trié dans
Ordre
disponible). TreeSet).
Doublons Autorisés. Interdits.
Exemples HashSet, TreeSet,
ArrayList, LinkedList.
d'implémentations LinkedHashSet.
Complexité O(n) non trié, O(log n) O(1) (HashSet), O(log n)
(recherche) trié. (TreeSet).
Comparaison : TreeSet vs HashSet
Critère TreeSet HashSet
Maintient un ordre (naturel ou Pas de tri ; éléments non
Tri
personnalisé). ordonnés.
Complexité O(log n) (arbre binaire O(1) en moyenne (table de
(opérations) équilibré). hachage).
Un seul élément null, si vide au
Null autorisé Un seul élément null.
départ.
Plus rapide pour des
Performance Plus lent à cause du tri.
données non triées.
Pour un accès rapide sans
Usage Lorsque le tri est nécessaire.
besoin de tri.
Exemples
HashSet
HashSet<Fruit> fruits = new HashSet<>();
[Link](new Fruit("Apple", 0.99));
[Link](new Fruit("Banana", 0.55));
[Link](new Fruit("Apple", 0.99)); // Ignoré car déjà présent
[Link]("Nombre de fruits : " + [Link]()); // 2
Important : Le fonctionnement repose sur les méthodes hashCode() et equals()
pour garantir l'unicité.
TreeSet
TreeSet<Fruit> fruits = new TreeSet<>();
[Link](new Fruit("Banane", 0.5));
1
9
[Link](new Fruit("Pomme", 0.3));
[Link](new Fruit("Orange", 0.6));
// Affichage trié par nom (nécessite que Fruit implémente `Comparable`).
for (Fruit fruit : fruits) {
[Link]([Link]() + " (" + [Link]() + " kg)");
}
Exemple de Comparable dans la classe Fruit :
class Fruit implements Comparable<Fruit> {
private String name;
private double weight;
public int compareTo(Fruit other) {
return [Link]([Link]);
}
}
Choix entre TreeSet et HashSet
Utilisez HashSet pour des performances optimales si le tri n'est pas
requis.
Utilisez TreeSet si un ordre spécifique est nécessaire, par exemple, pour
trier ou parcourir les éléments dans un ordre défini.
Voici une version détaillée et bien structurée avec tous les tableaux et les détails
demandés :
Map en Java
Une Map est une collection qui stocke des paires clé-valeur.
Les clés doivent être uniques, mais les valeurs peuvent être dupliquées.
Elle permet des opérations efficaces comme la recherche, l'insertion et la
suppression.
Méthodes principales de l'interface Map
Méthode Description Type de Type de retour
paramètre
put(K key, V value) Ajoute ou met à K (clé), V V (valeur
jour une paire clé- précédente ou
2
0
valeur. Retourne (valeur) null)
la valeur
précédente ou
null.
get(Object key) Récupère la valeur Object (clé) V
associée à une
clé. Retourne null
si la clé n'existe
pas.
remove(Object key) Supprime une Object (clé) V (valeur
paire clé-valeur. supprimée ou
Retourne la valeur null)
supprimée ou null.
containsKey(Object Vérifie si une clé Object (clé) boolean
key) existe dans la
Map.
containsValue(Obje Vérifie si une Object boolean
ct value) valeur existe dans (valeur)
la Map.
size() Retourne le - int
nombre de paires
clé-valeur dans la
Map.
isEmpty() Vérifie si la Map - boolean
est vide.
keySet() Retourne un Set - Set<K>
contenant toutes
les clés de la Map.
values() Retourne une - Collection<V>
Collection
contenant toutes
les valeurs de la
Map.
entrySet() Retourne un Set - Set<[Link]<
contenant toutes K, V>>
les paires clé-
valeur sous forme
d'objets
[Link].
putIfAbsent(K key, Ajoute une paire K (clé), V V (valeur
V value) clé-valeur (valeur) précédente ou
uniquement si la null)
clé n'existe pas
encore dans la
2
1
Map.
getOrDefault(Object Récupère la valeur Object (clé), V V (valeur
key, V defaultValue) associée à une (valeur par associée ou par
clé, ou retourne défaut) défaut)
une valeur par
défaut si la clé
n'existe pas.
replace(K key, V Remplace la K (clé), V V
value) valeur associée à (valeur)
une clé. Retourne
la valeur
précédente ou
null.
replace(K key, V Remplace une K (clé), V boolean
oldValue, V valeur (ancienne et
newValue) uniquement si elle nouvelle
correspond à une valeur)
valeur existante.
Retourne true si le
remplacement a
eu lieu.
forEach(BiConsumer Applique une BiConsumer<? -
<? super K, ? super action à chaque super K, ?
V> action) paire clé-valeur. super V>
Principales implémentations de Map
Implémentati Caractéristiques
on
HashMap - Non ordonnée.
- Utilise une table de hachage.
- Complexité moyenne : O(1) pour les opérations de
recherche, insertion et suppression.
TreeMap - Maintient les éléments triés (ordre naturel ou avec
Comparator).
- Implémentation basée sur un arbre binaire équilibré.
- Complexité moyenne : O(log n) pour les opérations.
LinkedHashM - Maintient l'ordre d'insertion des éléments.
ap
- Similaire à HashMap, mais légèrement plus lent en raison de
la gestion de l'ordre.
2
2
Différences entre HashMap et TreeMap
Critère HashMap TreeMap
Ordre des Pas d'ordre (non trié). Trié selon l'ordre naturel ou un
éléments Comparator.
Complexité O(1) en moyenne. O(log n).
Utilisation Rapide pour des accès Nécessaire lorsque l'ordre des clés
sans tri. est important.
Exemple d'utilisation : TreeMap
Objectif : Trier des fruits par nom.
import [Link].*;
public class Main {
public static void main(String[] args) {
// Création d'une TreeMap
Map<String, Fruit> fruits = new TreeMap<>();
// Ajout des fruits
[Link]("banane", new Fruit("Banane", 0.5));
[Link]("pomme", new Fruit("Pomme", 0.5));
[Link]("orange", new Fruit("Orange", 0.2));
[Link]("kiwi", new Fruit("Kiwi", 0.5));
// Affichage des fruits triés par clé
for ([Link]<String, Fruit> entry : [Link]()) {
[Link]([Link]());
}
}
}
Sortie :
Banane (0.5 kg)
Kiwi (0.5 kg)
2
3
Orange (0.2 kg)
Pomme (0.5 kg)
[Link] : Manipulation des paires clé-valeur
La méthode entrySet() permet de parcourir toutes les paires clé-valeur.
Exemple :
Map<String, Integer> myMap = new HashMap<>();
[Link]("one", 1);
[Link]("two", 2);
[Link]("three", 3);
// Parcours des paires clé-valeur
for ([Link]<String, Integer> entry : [Link]()) {
[Link]([Link]() + " : " + [Link]());
}
Sortie :
one : 1
two : 2
three : 3
Quand utiliser quelle implémentation ?
Scénario Implémentation
recommandée
Stockage rapide sans HashMap
tri
Maintenir l'ordre LinkedHashMap
d'insertion
Maintenir un ordre trié TreeMap
Avec ce tableau et ces exemples détaillés, vous avez une vue d'ensemble
complète sur l'utilisation des Map en Java et leurs particularités.
2
4