LANGAGE JAVA 2008-2009 Prof. HAMID ZOUAKI PRESENTATION GENERALE Langage récent Orienté objet dérivé du C++, répond aux trois principes: Encapsulation Polymorphisme Héritage Multi plateformes Bibliothèques de classes très riches Mécanisme de gestion des erreurs performant Applets et Applications La machine virtuelle Java est un langage qui doit être compilé et interprété. Dans une première phase on compile un programme (un ou plusieurs fichiers source .java) en fichiers .class. Le compilateur génère un fichier .class pour chacune des classes définies dans le(s) fichier(s) .java. L'ensemble des fichier .class est ensuite interprété par la Machine Virtuelle Java pour exécuter le programme (seule la JVM dépend du système). La machine virtuelle Caractéristiques Long à démarrer Gourmand en mémoire Rapide une fois initialisé (JIT) Conséquences Java beaucoup utilisé coté serveur Très bon langage pour les couches hautes (IHM, Web …etc) Non approprié pour couches basses (calcul scientifique) La machine virtuelle Pourquoi ne pas interpréter directement le programme Java ? Les fichiers .class contiennent du byte code, une sorte de code machine Java. L'interpréteur exécute beaucoup plus rapidement ce byte code que les fichiers sources .java. Seuls les fichiers .class sont nécessaires à l'exécution d'un programme Java. Ils contiennent du code machine et ne peuvent donc pas être lus par des tiers, protégeant ainsi le code source. Etant compilés, les fichiers .class sont de taille plus petite que le code source Java ; ceci est un argument important pour transférer les programmes sur Internet. Chaque fichier .class décrivant une classe d'objet, une même classe peut être utilisée par différents programmes sans que cette classe ne soit dupliquée dans chacun des programmes. Particularités Simple Utilisation transparente des pointeurs Gestion automatique de la mémoire (Garbage Collector) Robuste Nombreux contrôles à la compilation Gestion des exceptions Contrôle du débordement de bornes Multithreads Contrôle de la synchronisation des processus Particularités Sécurisé Vérification du byte code par la JVM Gestion des pointeurs: évite l’accès à certaines zones mémoires Sauf paramétrage, une applet ne peut pas accéder au disque dur Introspection Permet de déterminer les variables et méthodes d’une classe sans disposer du code source API java.lang, java.io Fonctionnalités Types de données et entrées sorties Java.awt graphisme et multi fenêtrage Java.swing Graphisme 2D et IHM JDBC Accès aux bases de données relationelles Thread multithreading Servlet/JSP Aspect web Java 3D Graphisme 3D … API Java Un grand nombre de classes prédéfinies et standardisées (> 6000) Classes conçues pour fonctionner sur toute plate forme Gratuit Principes de la POO Toute chose est un objet penser à un objet comme à une variable améliorée : il stocke des données, mais on peut « effectuer des requêtes » sur cet objet, lui demander de faire des opérations sur lui-même. En théorie, on peut prendre n'importe quel composant conceptuel du problème qu'on essaye de résoudre (un chien, un immeuble, un service administratif, etc...) et le représenter en tant qu'objet dans le programme. Un programme est un ensemble d'objets qui communiquent entre eux en s'envoyant des messages. Pour qu'un objet effectue une requête, on « envoie un message » à cet objet. Un message est un appel à une fonction « méthode « appartenant à un objet particulier. Principes de la POO Chaque objet a son propre espace de mémoire composé d'autres objets: On peut ainsi cacher la complexité d'un programme par la simplicité des objets mis en œuvre. Chaque objet est d'un type précis: Chaque objet est une instance (ou une réalisation) d'une classe. « classe » a une signification proche de « type ». Tous les objets d'un type particulier peuvent recevoir le même message. un objet de type « cercle » est aussi un objet de type « forme géométrique ». Un cercle se doit d'accepter les messages destinés aux formes géométriques. Cela veut dire qu'on peut écrire du code parlant aux formes géométriques qui sera accepté par tout ce qui correspond à la description d'une forme géométrique. Notion de classe tous les objets, tout en étant uniques, appartiennent à un ensemble d’objets qui ont des caractéristiques et des comportements communs Pour représenter (implémenter) des objets, on dispose de classes: chaque objet manipulé est le représentant d’une classe. Classe = type générique dont les objets sont des instances. Deux objets sont des instances d’une même classe: ils partagent une certaine structure et des traitements qu’on peut leur appliquer. N’empêche qu’ils soient différents l’un de l’autre. Notion de classe Exemple: Toutes les voitures sont des objets qui ont 4 roues, un volant, un moteur et une carrosserie Peuvent être mise en marche, stoppées, conduites, repaintes … etc. Mais, Certaines voitures ont 3 portes, des moteurs de puissances différentes … etc. Tous les objets de type voiture (instance de la classe voiture) ont les mêmes attributs, mais ces attributs prennent des valeurs différentes. La classe voiture représente la structure générale que partagent toutes les voitures. Notion de classe Utiliser un objet Ampoule amp = new Ampoule() ; amp.Allumer() ; type Interface: Puissance Ampoule Allumer Eteindre Notion de classe L'interface précise quelles sont les caractéristiques d’un objet (ici type et puissance) et quelles sont les opérations que l’on peut effectuer sur un objet particulier (ici Allumer et Eteindre). L’interface ne précise pas la manière dont elles sont effectuées (c’est-àdire le code correspondant à l’opération) : c’est le rôle de l’implémentation. Du point de vue de la programmation, une classe dispose de fonctions associées à chaque requête possible (appelée méthodes). Lorsqu’on effectue une requête particulière sur un objet, ces fonctions sont appelées: on dit qu'on « envoie un message » à un objet, l'objet se « débrouillant » pour l'interpréter (exécution du code associé). Ici, le nom de la classe est Ampoule, le nom de l'objet Ampoule créé est amp. D’après l’interface, on peut demander à un objet Ampoule de s'allumer et de s'éteindre. Un objet Ampoule est créé en définissant une « référence » (amp) pour cet objet et en appelant new pour créer un nouvel objet de cette classe. Exemple java Fichier: CompteEnBanque.java import java.io.*; import java.util.*; import java.lang.*; public class CompteEnBanque { protected int solde; /* Constructeur */ public ComptEnBanque() { setSolde(0); } public void setSolde(int montant) { solde= montant; } public int getSolde() { return solde; } public static void main(String[] args) { CompteEnBanque cpt = new CompteEnBanque(); Cpt.setSolde(50); System.out.printn(« solde courant: « + cpt.getSolde() ); } } La composition La réutilisation des classe créées et testées est l’un des grands avantages des langages orientés objets. On peut placer un objet d’une classe à l'intérieur d'une nouvelle classe : c’est ce qu’on appelle « créer un objet membre ». La nouvelle classe peut être constituée de n'importe quel nombre d'objets d'autres types, selon la combinaison nécessaire pour que la nouvelle classe puisse réaliser ce pour quoi elle a été conçue. ce concept est appelé composition (agrégation). La composition class Moteur { float Cylindree ; float Puissance ; void Allumer () ; void Eteindre () ; } class Voiture { Moteur m ; String Marque ; Color Couleur ; } L’héritage Une fois votre classe créée et testée, il serait dommage de devoir en créer une nouvelle pour implémenter des fonctionnalités similaires. Il est plus intéressant de prendre la classe existante, de la cloner et de faire des ajouts ou des modifications. C'est ce que permet l'héritage. Avec la restriction suivante : si la classe d’origine ( classe de base ou classe mère) est modifiée, la classe fille ( classe dérivée, héritée ou sous-classe) répercutera ces changements. Les classes Java sont ordonnées dans un arbre d’héritage. Une classe peut être déclarée comme classe fille en utilisant le mot clef extends. Une classe fille peut être à nouveau dérivée. Une classe fille hérite de tous les membres de sa classe mère qui ne sont pas privés. L’héritage Bien que l'héritage puisse parfois impliquer l’ajout de nouvelles fonctions à l'interface, il existe une autre manière de différencier la nouvelle classe : changer le comportement d'une des méthodes existantes de la superclasse. C’est ce que l’on appelle redéfinir (ou surcharger) cette fonction. Pour redéfinir une fonction, il suffit de créer une nouvelle définition pour la fonction dans la classe dérivée. C'est comme dire : « j'utilise la même interface ici, mais je la traite d'une manière différente dans cette nouvelle classe ». L’héritage class Animal { float poids; … void manger() { …. } void dormirr() { …. } … } } class Mammifère extends Animal { int rythmeCardiaque; // herite de poids … void respirer() { … } // herite de manger() et dormir() L’héritage Un objet de type Mammifère possède à la fois la variable d’instance poids et la méthode manger(). class Chat extends Mammifère { boolean poilLong; // hérite de poids et rythmeCardiaque … void ronronner() { … } // redéfinit dormir() void dormirr() { …. } // hérite de manger() et respirer() } Chat minou = new Chat(); Animal créature = minou; Le chat minou peut être affecté à la variable créature de type Animal, car Chat est un type dérivé de Animal. créature.dormir(); // c’est la méthode dormir() de Chat qui est appelée. Le Polymorphisme Il arrive que l’on veuille traiter un objet non en tant qu'objet de la classe spécifique, mais en tant qu'objet de sa classe de base. Cela permet d'écrire du code indépendant des classes spécifiques. Par exemple pour une forme géométrique, les fonctions manipulent des formes génériques sans se soucier de savoir si ce sont des ellipses, des rectangles, des triangles ou même des formes non encore définies. Toutes les formes peuvent être dessinées, effacées … etc. Ces fonctions envoient simplement un message à un objet forme, elles ne se soucient pas de la manière dont l'objet traite le message. Par exemple, on peut dériver une nouvelle classe de forme appelée « pentagone » sans modifier les fonctions qui traitent des formes génériques. Cette capacité à étendre facilement un programme en dérivant de nouvelles sous-classes est important car il améliore considérablement la conception tout en réduisant le coût de maintenance. Exemple import java.util.*; class Instrument { public void play() { System.out.println("Instrument.play()"); } public String what() { return "Instrument"; } public void adjust() {} } class Wind extends Instrument { public void play() { System.out.println("Wind.play()"); } public String what() { return "Wind"; } public void adjust() {} } class Percussion extends Instrument { public void play() { System.out.println("Percussion.play()"); } public String what() { return "Percussion"; } public void adjust() {} Exemple class Stringed extends Instrument { public void play() { System.out.println("Stringed.play()"); } public String what() { return "Stringed"; } public void adjust() {} } class Brass extends Wind { public void play() { System.out.println("Brass.play()"); } public void adjust() { System.out.println("Brass.adjust()"); } } class Woodwind extends Wind { public void play() { System.out.println("Woodwind.play()"); } public String what() { return "Woodwind"; } } Exemple public class Music3 { static void tune(Instrument i) { i.play(); } static void tuneAll(Instrument[] e) { for(int i = 0; i < e.length; i++) tune(e[i]); } public static void main(String[] args) { Instrument[] orchestra = new Instrument[5]; int i = 0; // Upcasting : orchestra[i++] = new Wind(); orchestra[i++] = new Percussion(); orchestra[i++] = new Stringed(); orchestra[i++] = new Brass(); orchestra[i++] = new Woodwind(); tuneAll(orchestra); } } Exemple Résultat de l’exécution: Wind.play() Percussion.play() Stringed.play() Brass.play() Woodwind.play()