Mon blog

Récapitulatif de ma révision sur la Programmation Orientée Objet en Java

La POO est un style de programmation qui permet de modéliser des concepts du monde réel sous forme d’objets dans un programme informatique. Ces objets interagissent entre eux pour effectuer diverses tâches.

Concepts de Base de la POO

En programmation orientée objet, les attributs (ou propriétés) d'une classe peuvent avoir différents niveaux d'accessibilité, qui déterminent la manière dont ils peuvent être utilisés et modifiés depuis d'autres classes. En Java, il existe quatre niveaux d'accessibilité principaux :

 

1. Public (public) :

Les attributs marqués comme public sont accessibles depuis n'importe quelle autre classe. Ils ne sont soumis à aucune restriction d'accès.

public class Personne {
public String nom;
}

public class Main {
public static void main(String[] args) {
Personne personne = new Personne();
personne.nom = "Alice"; // Accessible directement
}
}
2. Privé (private) :

Les attributs marqués comme private sont uniquement accessibles au sein de la classe où ils sont déclarés. Ils ne peuvent pas être accédés directement depuis d'autres classes, ce qui assure l'encapsulation et la protection des données sensibles.

public class CompteBancaire {
private double solde;

public void deposer(double montant) {
if (montant > 0) {
solde += montant;
}
}

public double getSolde() {
return solde;
}
}

public class Main {
public static void main(String[] args) {
CompteBancaire compte = new CompteBancaire();
compte.deposer(100);
// compte.solde = 200; // Erreur : solde est privé
System.out.println(compte.getSolde());
}
}
3. Protégé (protected) :

Les attributs marqués comme protected sont accessibles dans la classe où ils sont déclarés ainsi que dans les classes dérivées (ou sous-classes), même si ces dernières se trouvent dans un autre package.

public class Animal {
protected String nom;

public Animal(String nom) {
this.nom = nom;
}
}

public class Chien extends Animal {
public Chien(String nom) {
super(nom);
}

public void afficherNom() {
System.out.println(nom); // Accessible car nom est protégé
}
}
4. Package-Privé (ou Niveau de Package, sans modificateur) :

Les attributs sans modificateur d'accessibilité explicite sont accessibles uniquement au sein du même package. C'est souvent appelé "package-private".

class Voiture {
String marque; // Accessible uniquement dans le même package
}

public class Main {
public static void main(String[] args) {
Voiture voiture = new Voiture();
voiture.marque = "Toyota"; // Accessible car dans le même package
}
}

Pour résumer :

En Java, les méthodes peuvent être classifiées en plusieurs catégories selon leur comportement et leur utilisation. Voici une explication des types les plus courants :

1. Méthodes Statiques (static) :

Les méthodes statiques appartiennent à la classe plutôt qu'à une instance spécifique de la classe. Elles peuvent être appelées sans créer d'objet de la classe.

public class MathUtil {
public static int ajouter(int a, int b) {
return a + b;
}
}

public class Main {
public static void main(String[] args) {
int somme = MathUtil.ajouter(5, 3);
System.out.println("Somme: " + somme);
}
}
2. Méthodes Abstraites (abstract) :

Les méthodes abstraites sont déclarées sans implémentation dans une classe abstraite. Elles doivent être implémentées par les sous-classes (Utilisées pour définir un comportement que les sous-classes doivent fournir).

public abstract class Animal {
public abstract void parler();
}

public class Chien extends Animal {
@Override
public void parler() {
System.out.println("Woof!");
}
}

public class Chat extends Animal {
@Override
public void parler() {
System.out.println("Meow!");
}
}
3. Méthodes Finales (final) :

Les méthodes marquées comme final ne peuvent pas être redéfinies par les sous-classes. Cela empêche les sous-classes de modifier le comportement de ces méthodes (Utilisées pour garantir que la méthode ne sera pas modifiée).

public class Parent {
public final void afficherMessage() {
System.out.println("Message du parent");
}
}

public class Enfant extends Parent {
// public void afficherMessage() {
//Erreur de compilation : ne peut pas redéfinir la méthode finale
// }
}
4. Méthodes Surchargées :

La surcharge de méthodes permet d'avoir plusieurs méthodes avec le même nom dans la même classe, mais avec des paramètres différents (Utilisées pour définir plusieurs comportements similaires avec des signatures de méthodes différentes).

public class Calculatrice {
public int ajouter(int a, int b) {
return a + b;
}

public double ajouter(double a, double b) {
return a + b;
}
}

public class Main {
public static void main(String[] args) {
Calculatrice calc = new Calculatrice();
System.out.println(calc.ajouter(5, 3)); // Utilise int
System.out.println(calc.ajouter(5.5, 3.3)); // Utilise double
}
}
5. Méthodes Synchronisées (synchronized) :

Les méthodes synchronisées sont utilisées dans des contextes multi-thread pour contrôler l'accès concurrent aux ressources partagées (Utilisées pour éviter les conditions de course).

public class CompteBancaire {
private double solde;

public synchronized void deposer(double montant) {
solde += montant;
}

public synchronized double getSolde() {
return solde;
}
}
6. Méthodes Privées (private) :

Les méthodes privées sont accessibles uniquement au sein de la classe où elles sont définies. Elles ne peuvent pas être appelées depuis des sous-classes ou d'autres classes (Utilisées pour encapsuler les détails d'implémentation).

public class Exemple {
private void methodePrivee() {
System.out.println("Méthode privée");
}

public void appelerMethodePrivee() {
methodePrivee();
}
}

public class Main {
public static void main(String[] args) {
Exemple ex = new Exemple();
ex.appelerMethodePrivee();
// ex.methodePrivee(); // Erreur : méthode privée
}
}

L'encapsulation est le mécanisme qui consiste à restreindre l'accès direct à certains composants d'un objet et à les protéger contre des modifications involontaires.

Exemple en Java :

public class CompteBancaire {
private double solde;

public CompteBancaire(double solde) {
this.solde = solde;
}

public void deposer(double montant) {
if (montant > 0) {
solde += montant;
}
}

public void retirer(double montant) {
if (montant > 0 && montant <= solde) {
solde -= montant;
}
}

public void afficherSolde() {
System.out.println("Solde: " + solde);
}

public static void main(String[] args) {
CompteBancaire monCompte = new CompteBancaire(1000);
monCompte.deposer(500);
monCompte.retirer(200);
monCompte.afficherSolde();
}
}

L'héritage permet à une classe de dériver les propriétés et méthodes d'une autre classe. Cela favorise la réutilisation du code.

Exemple en Java :

class Animal {
protected String nom;

public Animal(String nom) {
this.nom = nom;
}

public void parler() {
// Méthode à être redéfinie par les sous-classes
}
}

class Chien extends Animal {
public Chien(String nom) {
super(nom);
}

@Override
public void parler() {
System.out.println("Woof!");
}
}

class Chat extends Animal {
public Chat(String nom) {
super(nom);
}

@Override
public void parler() {
System.out.println("Meow!");
}
}

public class Main {
public static void main(String[] args) {
Chien chien = new Chien("Rex");
Chat chat = new Chat("Whiskers");
chien.parler();
chat.parler();
}
}

Le polymorphisme permet à des objets de différents types de réagir différemment à la même méthode ou propriété.

Exemple en Java :

class Oiseau {
public void parler() {
System.out.println("Cui-cui!");
}
}

class Chien {
public void parler() {
System.out.println("Woof!");
}
}

public class Main {
public static void faireParler(Object animal) {
if (animal instanceof Oiseau) {
((Oiseau) animal).parler();
} else if (animal instanceof Chien) {
((Chien) animal).parler();
}
}

public static void main(String[] args) {
Oiseau oiseau = new Oiseau();
Chien chien = new Chien();
faireParler(oiseau);
faireParler(chien);
}
}

L'abstraction est un concept fondamental de la programmation orientée objet (POO) qui permet de simplifier la complexité en se concentrant sur les caractéristiques essentielles d'un objet plutôt que sur les détails spécifiques de son implémentation. En Java, l'abstraction est réalisée à travers les classes abstraites et les interfaces.

1. Classe Abstraite :

Une classe abstraite est une classe qui ne peut pas être instanciée directement. Elle peut contenir des méthodes abstraites (sans implémentation) que les sous-classes doivent implémenter, ainsi que des méthodes concrètes (avec implémentation).

// Déclaration de la classe abstraite
public abstract class Animal {
// Méthode abstraite (sans implémentation)
public abstract void parler();

// Méthode concrète (avec implémentation)
public void manger() {
System.out.println("Cet animal mange.");
}
}

// Sous-classe concrète de Animal
public class Chien extends Animal {
@Override
public void parler() {
System.out.println("Woof!");
}
}

// Sous-classe concrète de Animal
public class Chat extends Animal {
@Override
public void parler() {
System.out.println("Meow!");
}
}

// Utilisation des classes
public class Main {
public static void main(String[] args) {
Animal chien = new Chien();
chien.parler(); // Affiche "Woof!"
chien.manger(); // Affiche "Cet animal mange."

Animal chat = new Chat();
chat.parler(); // Affiche "Meow!"
chat.manger(); // Affiche "Cet animal mange."
}
}
2. Interface :

Une interface en Java est une référence de type qui est similaire à une classe abstraite, mais qui ne peut contenir que des méthodes abstraites (à partir de Java 8, elle peut également contenir des méthodes par défaut et des méthodes statiques). Les classes qui implémentent une interface doivent fournir des implémentations pour toutes les méthodes abstraites de l'interface.

// Déclaration de l'interface
public interface Animal {
void parler();
void manger();
}

// Classe implémentant l'interface
public class Chien implements Animal {
@Override
public void parler() {
System.out.println("Woof!");
}

@Override
public void manger() {
System.out.println("Le chien mange.");
}
}

// Classe implémentant l'interface
public class Chat implements Animal {
@Override
public void parler() {
System.out.println("Meow!");
}

@Override
public void manger() {
System.out.println("Le chat mange.");
}
}

// Utilisation des classes
public class Main {
public static void main(String[] args) {
Animal chien = new Chien();
chien.parler(); // Affiche "Woof!"
chien.manger(); // Affiche "Le chien mange."

Animal chat = new Chat();
chat.parler(); // Affiche "Meow!"
chat.manger(); // Affiche "Le chat mange."
}
}
Différences entre Classe Abstraite et Interface
  • Instantiation : Une classe abstraite ne peut pas être instanciée directement, tandis qu'une interface ne peut jamais être instanciée.
  • Héritage : Une classe peut hériter de plusieurs interfaces (héritage multiple), mais elle ne peut hériter que d'une seule classe abstraite.
  • Implémentation : Une classe abstraite peut contenir des méthodes abstraites et concrètes, alors qu'une interface ne peut contenir que des méthodes abstraites (jusqu'à Java 7). À partir de Java 8, une interface peut aussi contenir des méthodes par défaut et des méthodes statiques.
  • Constructeurs : Une classe abstraite peut avoir des constructeurs, mais une interface ne peut pas en avoir.

Structures de Données et Collections

En Java, les listes sont des collections ordonnées d'éléments qui permettent des duplications et un accès séquentiel. Elles font partie de l'interface List dans le framework des collections Java (java.util.List). Les listes peuvent être implémentées de différentes manières, les plus courantes étant ArrayList et LinkedList.

1. Interface List

L'interface List fournit un ensemble de méthodes pour manipuler les collections d'éléments, notamment pour ajouter, supprimer, accéder et parcourir les éléments.

2. ArrayList

ArrayList est une implémentation de l'interface List basée sur un tableau dynamique qui peut croître selon les besoins. Elle offre un accès rapide par index, mais les opérations d'insertion et de suppression peuvent être coûteuses en termes de performance si elles sont effectuées au milieu de la liste.

3. LinkedList

LinkedList est une implémentation de l'interface List basée sur une liste chaînée (doubly-linked list). Elle permet des insertions et des suppressions rapides aux extrémités, mais l'accès par index est plus lent comparé à ArrayList.

 

En résumé, ArrayList est généralement plus efficace pour l'accès aléatoire tandis que LinkedList est plus efficace pour l'insertion et la suppression fréquentes en début ou fin de liste.

En Java, les collections de type Map sont utilisées pour stocker des paires clé-valeur, où chaque clé unique est associée à une valeur. L'interface Map fait partie du framework des collections Java (java.util.Map) et offre diverses implémentations, telles que HashMap, TreeMap et LinkedHashMap, chacune ayant ses propres caractéristiques et avantages.

1. Interface Map

L'interface Map définit un ensemble de méthodes pour manipuler les collections de paires clé-valeur.

2. HashMap

HashMap est une implémentation de l'interface Map basée sur une table de hachage. Elle offre une performance constante pour les opérations de base (put, get, remove), mais ne garantit pas l'ordre des éléments.

3. TreeMap

TreeMap est une implémentation de l'interface Map basée sur une structure d'arbre rouge-noir. Elle garantit que les paires clé-valeur sont triées par ordre naturel des clés ou par un comparateur fourni.

4. LinkedHashMap

LinkedHashMap est une implémentation de l'interface Map basée sur une table de hachage avec une liste doublement chaînée qui maintient l'ordre d'insertion des éléments.

 

Les collections de type Map en Java offrent une façon flexible et efficace de stocker et de manipuler des paires clé-valeur. Selon les besoins spécifiques, telles que la performance ou l'ordre des éléments, vous pouvez choisir parmi différentes implémentations comme HashMap, TreeMap, et LinkedHashMap.

Gestion des exceptions

Une exception est un événement anormal qui interrompt le flux normal de l'exécution d'un programme. En Java, les exceptions sont des objets qui décrivent une condition d'erreur qui s'est produite dans le programme.

En Java, les exceptions sont classées en trois grandes catégories:

  • Checked Exceptions: Ce sont des exceptions vérifiées au moment de la compilation. Elles doivent être soit capturées (caught) soit déclarées dans la clause throws de la méthode.
  • Unchecked Exceptions: Ce sont des exceptions qui ne sont pas vérifiées au moment de la compilation. Elles incluent les erreurs de programmation, telles que les NullPointerException et les ArrayIndexOutOfBoundsException.
  • Errors: Ce sont des conditions d'erreur graves qui ne sont généralement pas récupérables par le programme, telles que les erreurs de mémoire (OutOfMemoryError).

En Java, les exceptions sont gérées à l'aide des blocs try, catch, finally et throw.

  • try : Le bloc de code à surveiller pour les exceptions.
  • catch : Le bloc de code pour gérer l'exception.
  • finally : Le bloc de code qui s'exécute toujours, qu'une exception soit lancée ou non.
  • throw : Utilisé pour lancer une exception.

Vous pouvez également définir vos propres exceptions en étendant la classe Exception ou RuntimeException.

// Déclaration de l'exception personnalisée
class MonException extends Exception {
public MonException(String message) {
super(message);
}
}

public class ExempleExceptionPersonnalisee {
// Méthode qui lance l'exception personnalisée
public static void verifierAge(int age) throws MonException {
if (age < 18) {
throw new MonException("L'âge doit être supérieur ou égal à 18 ans.");
}
}

public static void main(String[] args) {
try {
verifierAge(15); // Ceci lancera MonException
} catch (MonException e) {
System.out.println("Erreur: " + e.getMessage());
}
}
}

Concepts avancés

La généricité en Java permet de créer des classes, des interfaces et des méthodes paramétrées par des types, offrant ainsi une plus grande flexibilité et une meilleure sécurité de type au moment de la compilation. Les génériques permettent d'écrire du code qui peut fonctionner avec différents types de données tout en évitant les castings explicites et en réduisant les erreurs de type.

public class Box<T> {
  private T content;

public void setContent(T content) {
this.content = content;
}

public T getContent() {
return content;
}
}

Les Streams en Java, introduits dans Java 8, permettent de traiter des collections de manière déclarative et fonctionnelle. Les Streams offrent une manière fluide de manipuler et traiter des données, ce qui rend le code plus lisible et concis.

Les streams peuvent être créés à partir de différentes sources comme les collections, les tableaux, les générateurs, etc.

public class StreamExample {
  public static void main(String[] args) {
    List<String> myList = Arrays.asList("apple", "banana", "cherry", "apple", "date");

    List<String> result = myList.stream()
      .filter(s -> s.startsWith("a")) // Filtrer les éléments commençant par "a"
      .sorted() // Trier les éléments
      .distinct() // Supprimer les doublons
      .collect(Collectors.toList()); // Collecter le résultat dans une liste

    result.forEach(System.out::println); // Afficher chaque élément du résultat
  }
}

© 2016 | Powred By Ayoub Ben Khiroun