Cours de base d’introduction au langage Java

4.74.7 étoiles sur 5 a partir de 3 votes.
Votez ce document:

Cours de base Java

Java Le Langage

Cours de base Java / Renaud Zigmann / Copyright 2000 XSALTO sarl                       1

Cours de base Java / Renaud Zigmann / Copyright 2000 XSALTO sarl                       2

Machine virtuelle et JDK

Machine virtuelle et JDK (suite)

Machine virtuelle Java

?Architecture d'exécution complète

Jeu d'instructions précis des registres une pile

?La sécurité est fondamentale

Le langage et le compilateur gèrent entièrement les pointeurs

Un programme de vérification du bytecode veillent à l'intégrité du code java Le chargeur de classes (class loader) est chargé d'autoriser ou de refuser le chargement d'une classe.

Une classe est chargée d'effectuer la vérification des appels aux API

?Disponibilité

Machines virtuelles JDK

Machines virtuelles intégrées aux navigateurs

Machines virtuelles des environnements de développement

Java Development Kit

Ensemble de composants permettant le développement, la mise au point et l'exécution des programmes Java. lUn ensemble d'outils; lUn jeu de classes et de services; lun ensemble de spécifications.

Un développement java peut être entièrement réalisé avec le JDK,avec des outils en ligne de commande.

Les fichiers sources ont l'extension .java

Les fichiers compilés ont l'extension .class

N o m

Description

Machine virtuelle java

Compilateur java

Machine virtuelle java pour l’exécution d’applets

Permet la création d’archives java

Générateur de documentation java

Désassembleur de classes compilées

Débogueur en ligne de commande

Version du JDK

?JDK 1.0, 1.1, 1.2 (java2), 1.3 (nouvelle plate-forme java2)

Les JDK sont disponibles sur Internet

?JDK 1.02

première version réellement opérationnelle, API et classes élémentaires première version de la bibliothèque AWT (Abstract Windowing Toolkit) Utilisé seulement pour une compatibilité maximale.

?JDK 1.1: 1.1.8

améliorations et extensions du langage, améliorations de AWT

Apparition des composants Java et JavaBeans, JDBC (Java Database

Connectivity), RMI (Remote Method Invocation)

Nouveau modèle d'événements, amélioration de la sécurité (signature des applets) Java côté serveur (Servlets), JNI (Java Native Interface)

?JDK 1.2

Intégration des composants Java Swing, dans les JFC (Java Foundation Classes)

Amélioration des JavaBeans, intégration de l'API Java 2D, API d'accessibilité Intégration de l'IDL en standard pour le support natif de CORBA, intégration des bibliothèques CORBA Support du drag anddrop.

Bibliothèque

AWT: composants d'interface homme machine portables, basé sur des classes d'implémentation spécifiques à chaque plate-forme. Un composant AWT correspond à un composant natif.

SWING : nouvelle génération de composants; 100% Java, Look & Feel paramétrable, modèle VMC (Vue Modèle Contrôleur)

Exercice 1: mon premier programme

Fichier :

public class HelloWorld { public static void main(String[] args)

{

.println("Hello World!"); }

}

Exemple construit sur une classe publique

La méthode main est le point d'entrée du programme

Le nom du fichier et le nom de la classe doivent concorder

Compilation javac

Exécution java HelloWorld

            Cours de base Java / Renaud Zigmann / Copyright 2000 XSALTO sarl                       5

            Cours de base Java / Renaud Zigmann / Copyright 2000 XSALTO sarl                       6

Jbuilder 3

Le langage Java

?Des experts et assistants

?Basé sur le JDK 1.2, mais support des JDK passés

?Apports

support intégré du JDK 1.2 bibliothèques de composants séparation DataExpress/JBCL navigation dans les sources environnement plus simple d'accès intégration de swing nouveaux experts et nouvelles versions d'experts explorateur et moniteur SQL améliorés intégration de VisiBroker 3.4

?IDE

Mode conception pour le développement

Mode exécution réservée aux phases de tests et de mise au point

?Caractéristiques des versions

Voir page 23, "Le programmeur Jbuilder3"

Eléments généraux du langage

?Codage du texte

Java peut utiliser un codage Unicodeou ASCII

?Commentaires

// ceci est un commentaire sur une seule ligne

/* ceci est un commentaire

qui peut s'étaler sur plusieurs lignes */

?Commentaires javadoc

Commentaires javadoc introduits par /**

Tag

Description

Applicable à

@see

Nom de classe associé

Class, method ou variable

@author

Nom de l’auteur

Classe

@version

Numéro de version

Classe

@param

Nom de paramètre et description

Méthode

@return

Description de la valeur de retour

Méthode

@exception

Nom de l’exception et description

Méthode

@deprecated

Déclare un item obsolete

Classe, méthode ou variable

Types

Langage typé: toute variable a un type, connu au moment de la compilation.

Java maintient aussi des informations consultables au moment de l'exécution.

Types primitifs : boolean, char, byte, short, int, long, float, double.

Les types primitifs ne sont pas des objets, mais disposent de classes Wrapper. ?Déclaration et initialisation

int toto; double b1, b2; int foo = 12;

Les variables non initialisées sont automatiquement positionnées à "zero".

?Références

Les objets sont toujours manipulés par référence. Les références sont passées par valeur.

// deux références sur le même objet

Car myCar = new Car();

Car anotherCar = myCar;

// passage de l'objet par référence myMethod(myCar);

            Cours de base Java / Renaud Zigmann / Copyright 2000 XSALTO sarl                       7

            Cours de base Java / Renaud Zigmann / Copyright 2000 XSALTO sarl                       8

Le langage Java

Le langage Java : expressions

Chaînes

?Les chaînes sont des objets de classe String

.println("Hello my string…");

String s = "I am a value";

String t = "Je dis \"I am a string\"";

String quote = "Ceci " + "est " + s;

Instructions ?Blocs de code

Paranthensés par { }

?Méthodes

Blocs de code paramétrés

?Variables

Durée de vie = limites du bloc dans lequel elle est déclarée

?Instructions

if ( condition ) instruction; [ else instruction; ] if ( condition ) { instructions; } while ( condition ) instruction; do instruction; while (condition ); for (initialisation; condition; incrementation ) instruction; switch (int expression) { case int expression: instruction; break;

[ case in expression: instruction;

default: instruction; ]

}

break : sortie du bloc en cours

continue: passage à l'itération de boucle successive sans continuer le code

?Instructions et affectations

Une initialisation est une instruction

Une affectation est une expression

Expression

?Opérateurs

++ et -- incrémenter, décrémenter

+, -, *, /: opérateurs unaires et binaires arithmétiques

+: concaténation de chaînes

<<, >>: décalages à gauche et à droite

>>> décalage à droite sans extension de signe

<, <=, >, >=: comparaison numérique

~: complément binaire, !: complément logique

( type ): conversion

==, !=: égalité et différence de valeur et de référence

& : 'et' binaire et logique, | : 'ou' binaire et logique

^ : 'ou exclusif' binaire et booléen

&&  et ||: 'et' conditionnel, 'ou' conditionnel

?: : opérateur conditionnel ternaire, =: affectation

*=, /=, %=, +=, -=, <<=, >>=, >>>=, &=, &=, ^=, |=: affectation avec opération instanceof: comparaison de types

?Affectation

Une affectation est une expression qui peut être utilisée comme une valeur dans une autre expression

?null

Peut être assignée à une référence pour indiquer "pas de référence"

?Accès aux variables et appel de méthodes

Accès aux variables dans un objet: '.' int i; String s; i = myObject.length; s = ; int len = .length();

int initialLen = .substring(5, 10).length();

?Création d'objet

Object o = new Object(); ?instanceof

Déterminer le type d'un objet à l'exécution. Rend une valeur booléenne.

Boolean b;

String str = "foo"; b = ( str instanceof String ); b = ( str instanceof Object ); // true b = ( str instanceof Date ); b = ( str instanceof Date[] ); // false str = null; b = ( str instanceof String ); b = ( str instanceof Object ); // false

Exercice 2: le tri par bulle

Enoncé: développer un programme de tri par bulle d'un tableau d'entiers

            Cours de base Java / Renaud Zigmann / Copyright 2000 XSALTO sarl                       9

            Cours de base Java / Renaud Zigmann / Copyright 2000 XSALTO sarl                     10

Le langage Java : la gestion des exceptions

Le langage Java : Tableaux

Classes

?Les exceptions sont des instances de .Exception et ses dérivés

?Les classes dérivées peuvent contenir des informations spécifiques

Gestion des exceptions

try {

readFromFile("toto");

}

catch( FileNotFoundException e) {

.println("Fichier non trouvé"); …

}

catch( Exception e) {

.println("Exception pendant la lecture du fichier: "+e);

}

Un seul bloc catch est exécuté.

On remonte au bloc try qui s'applique le plus proche, en déroulant la pile.

On ne revient pas à l'emplacement de l'erreur après traitement

L'objet de classe Exception contient des informations contextuelles try {   // instructions

}

catch( Exception e)

{ e.printStackTrack(); }

?Checked et Unchecked

Les exceptions java sont divisées en "vérifiées" et "non vérifiées". Les méthodes doivent déclarer les exceptions qui sont vérifiées.

Void readFile(String s) throws IOException, InterruptedException { … }

La méthode appelante peut traiter l'exception ou déclarer qu'elle peut elle-même en rejeter.

Les exceptions qui sont des dérivées de .RunTimeExceptionou .Error ne sont pas vérifiées. Les méthodes n'ont pas besoin de déclarer qu'elles peuvent déclencher ces exceptions.

?Déclencher des exceptions

throw new Exception(); throw new Exception("This is my error message");

throw new SecurityException("Accès au fichier untel interdit"); Toutes les exceptions ont un constructeur avec un paramètre String.

?La clausefinally est exécutée après les autres clauses

?Pas de pénalités en termes de performances

Les tableaux

?Java crée une classe spécifique lors de la déclaration d'une variable de type tableau.

?L'opérateur [] permet l'accès aux éléments du tableau

?Création d'une instance de tableau par l'opérateur new

?Syntaxe

int [] arrayOfInts; int arrayOfInts[];

String [] someStrings;

?Création

arrayOfInts = new int[42];

double [] someNumbers = new double[20];

Initialisation à la valeur par défaut du type.

La chaîne contient des références aux objets et pas les objets eux-même.

Int [] prime = {1,2,3,4,5}; ?Utilisation

Le premier indice commence à 0.

La variable publique length donne la longueur du tableau L'accès après le dernier élément déclenche une exception ArrayIndexOutOfBoundsException.

La méthode System.arraycopypermet de copier deux tableaux

System.arraycopy(source, srcstart, destination, dststart, longueur)

?Tableaux anonymes

Créer un tableau temporaire qui n'est plus utilisé/référencé par la suite.

setPets(new Animal [] { pokey, squiggles, jasmine } );

Tableaux multidimensionnels

Color [][][] rgbCube = new Color [256] [256] [256]; rgbCube [0] [0] [0] = Color.black; rgbCube [255] [255] [0] = Color.yellow;

                Cours de base Java / Renaud Zigmann / Copyright 2000 XSALTO sarl                     11

                Cours de base Java / Renaud Zigmann / Copyright 2000 XSALTO sarl                     12

Les objets en Java

Les objets en Java

Introduction

?Penser les objets en termes d'interface et non d'implémentation.

?Se placer le plus possible en termes abstraits.

?Éviter les variables publiques.

?Utiliser les relations de composition (has-a) et d'héritage (is-a) à bon escient.

?Minimiser les relations entre objets et organiser les objets en packages (paquets)

Classes

?La base de toute application Java.

?Une classe contient

des méthodes des variables un code d'initialisation

?Exemples

Définition class Pendule { float masse; float length = 1.0; int cycles;

float position (float time) { …

}

}

Utilisation

Pendule p; p = new Pendule(); p.masse=5.0;

float pos = p.position(1.0);

?Accéder aux membres

Dans une classe, on accède aux méthodes et aux variables en utilisant directement leurs noms.

Les autres classes accèdent aux membres à travers une référence. Le mot clé private limite l'accès d'un membre à sa classe.

Class Pendule { private int cycles; void resetEverything() { cycles = 0;  mass = 1.0;

… float startingPosition = position(0.0); …

}

?Membres statiques : associés à la classe.

Variables de classe et méthodes de classe.

Mot clé: static

Class Pendule { private int cycles;

static float gravAccel = 9.80;

public float getWeight() { return mass* gravAccel; }

}

Pendule.gravAccel = 8.76;

Déclaration de constantes par le mot clé final

Class Pendule { private int cycles;

static final float EARTH_G = 9.80;

public float getWeight() { return mass* EARTH_G; } }

Les méthodes

?Une méthode doit toujours spécifier un type de retour

?Une méthode a un nombre fixe d'arguments

?Les variables locales sont temporaires et connues seulement dans le scope de la méthode.

?Les variables locales cachent les données membres lorsqu'elles ont le même nom

?La référence this fait référence à l'objet courant.

?Les méthodes statiques sont des méthodes de classe

?Les variables locales doivent être initialisées avant d'être utilisées

?Pour modifier la référence elle-même dans une méthode, il faut passer l'objet dans un container

?Il est possible de surcharger des méthodes

            Cours de base Java / Renaud Zigmann / Copyright 2000 XSALTO sarl                     13

            Cours de base Java / Renaud Zigmann / Copyright 2000 XSALTO sarl                     14

Les objets en Java

Exercice 3

Création d'objet

?Le constructeur est une méthode qui porte le même nom que la classe

?Le constructeur peut être surchargé

class Date { long time;

Date() { time = currentTime(); }

Date(String date) { time = parseDate(date); } …}

?Un constructeur ne peut pas être abstract, synchronized ou final.

?Si un constructeur appelle un autre constructeur, ce doit être le premier appel dans le constructeur

?Une bloc de code statique est exécuté une fois lors de l'initialisation de la classe.

Class ColorModel { static Hashtable colors = new Hashtable();

// set up colors static {

("Red", ); ("Green", Color.green); ("Blue", );

.. }

}

Destruction d'objet

?Garbage Collector

Java gère la destruction des objets par lui-même

Le mécanisme est le Garbage collecting ouGarbage collector

Chaque machine virtuelle java peut le gérer comme elle le souhaite

Legarbage collector est exécuté dans unthreadà basse priorité

Il est possible de forcer le GC en exécutant ()

?Finalization (Destructeur)

Le méthode finalize() est appelée pour faire les actions pre-mortem.

La méthode est appelée par leGarbage Collector juste avant la destruction de l'objet

Définir une classe Personne, avec :

Variables privées Nom et Prénom

Méthodes: constructeur, display, accesseurs et modificateurs

Définir une classe Etudiant dérivant de Personne

Variables privées: université et bourse(o/n)

Méthodes: constructeur, display, accesseurs et modificateurs

Définir une classe Salarié dérivant de Personne

Variables privées: société et salaire

Méthodes: constructeur, display, accesseurs et modificateurs

Ajouter un comptage d'instances en utilisant des variables statiques

            Cours de base Java / Renaud Zigmann / Copyright 2000 XSALTO sarl                     15

            Cours de base Java / Renaud Zigmann / Copyright 2000 XSALTO sarl                     16

Relations entre classes

Relations entre classes

Dérivation et héritage

class Animal { float weight;

void eat() { … }

}

class Mammal extends Animal { int heartRate; void breathe() { … }

..

}

Cat simon = new Cat();

Animal creature = simon;

?Une classe hérite de tous les membres de la classe même (ou superclasse) non marqués private.

?La référence super permet de faire référence à la superclasse.

Redéfinition des méthodes

?SI la méthode de la classe fille a la même signature, alors elle redéfinit la méthode mère.

?On ne peut pas redéfinir une méthode statique en une méthode non statique

?Une méthode marquée final ne peut plus être redéfinie

?Une méthode dérivée doit adhérer ou rafiner les exceptions gérées par la méthode mère.

Conversion

?Le casting en java fonctionne sur les références

?Il permet permet de spécialiser une référence

Constructeurs

?super permet d'appeler explicitement un constructeur de la classe mère (superclasse)

class Doctor extends Person {

Doctor(String name, String specialty) { super(name);

}

}

Méthodes et classes abstraites

?Une classe abstraite ne peut pas être instanciée

Il n'est pas possible de créer un objet dans cette classe

?Si une classe est déclarée abstraite ou si l'une de ses méthodes est abstraite, la classe ne peut pas être instanciée

abstract class truc { abstract void machin();

}

Les classes abstraites fournissent un cadre général, qui doit être "rempli" par les classes dérivées.

Interfaces

?Une interface est un prototype de classe sans implémentation

?Elle spécifie un ensemble de points d'entrée qu'une classe doit implémenter, une interface.

?Exemple

interface Driveable { boolean startEngine(); void stopEngine(); float accelerate(float acc); boolean turn(Direction dir);

}

Il est possible d'utiliser le modifieur abstract mais ce n'est pas nécessaire.

Les méthodes sont toujours public.

?Le mot clé implements signale qu'une classe implémente une interface.

Class Car implements Driveable {

public boolean startEngine() { if (notTooCold) engineRunning = true; } ..

}

Car auto = new Car();

Lawnmower mower = new Lawnmower();

Driveable vehicle;

vehicle = auto; vehicle.startEngine(); vehicle.stopEngine(); vehicle = mower; vehicle.startEngine(); vehicle.stopEngine();

            Cours de base Java / Renaud Zigmann / Copyright 2000 XSALTO sarl                     17

            Cours de base Java / Renaud Zigmann / Copyright 2000 XSALTO sarl                     18

Relations entre classes

Relations entre classes

Interfaces (suite)

?Les interfaces sont utilisées pour implémenter des callbacks

?Les interfaces peuvent contenir des constantes (static final)

?Les mécanismes d'héritage peuvent être utilisés entre les interfaces

interface Foo extends Driveable, Edible { void good();

}

Package et unité de compilation

?Unité de compilation

Le code source d'une classe java est une unité de compilation; Une unité de compilation ne contient qu'une classe publique. Elle doit porter le nom de cette classe publique.

Une unité de compilation = un fichier avec une extension .java

Une classe est déclarée appartenir à un package par le mot clé package.

package ; class TextComponent {

}

?Les noms de package sont conceptuellement hiérarchiques. C'est simplement une convention de nommage.

?Visibilité

Par défaut une classe n'est accessible qu'aux classes du même package. Pour être disponible elle doit être déclarée publique.

?L'instruction import permet d'importer des classes ou des packages

import .TextEitor; import .*;

?Par défaut, une classe est définie dans le package "nameless".

Gestion des accès de base

Par défaut les variables et méthodes sont accessibles aux classes du même package.

Public: les membres sont accessibles par tout le monde

Private: les membres sont accessibles seulement de la classe elle-même

Protected: les membres sont accessibles dans le même package et dans les classes dérivées. Les membres de la superclasse ne sont accessibles que dans l'objet dérivé.

Il est possible de redéfinir une méthode private en public

Il n'est pas possible de redéfinir une méthode public en private

Les interfaces se comportent comme des classes Classes internes (innerclasses)

class Animal { class Brain {

}

void faireQuelqueChose(); }

Les objets Brain peuvent faire des accès aux membres de l'objet Animal dans lequel ils ont été déclarés, auquel ils appartiennent. Un objet Brainn'a pas de sens hors de l'animal.

?Classes internes anonymes

new Thread( new Runnable() { public void run() { performSomething(); } }).start();

            Cours de base Java / Renaud Zigmann / Copyright 2000 XSALTO sarl                     19

            Cours de base Java / Renaud Zigmann / Copyright 2000 XSALTO sarl                     20

Exercice 4

Utilitaires sur les Objets et les Classes

Interfaces

?Définir une Interface Item, avec la capacité de s'afficher et de se cloner

?Définir une classe List manipulant des Item, avec les méthodes

constructeur, ajout en tête, suppression en tête, comptage, affichage des éléments, destructeur

?Créer des listes de personnes

Classes abstraites et interfaces ?Reprise exercice ci-dessus.

?Création de la classe abstraite conteneur

?List hérite de conteneur

?Réaliser un comptage d'instance au niveau conteneur

?Montrer en créant une nouvelle classe, que l'on peut ajouter d'autres types d'éléments dans une liste.

La classe Object

?.Objectest la classe mère de toutes les classes ?Méthodes communes:

toString(): appelée pour représenter un objet sous forme de valeur textuelle equals(): détermine si deux objets sont équivalents. Est différent de ==, qui fonctionne au niveau référence. Attention l'arguement est de type Object hashCode(): par défaut rend un entier unique. S'il existe une notion de classes d'équivalence, il est intéressant de redéfinir cette classe clone(): autoriser le clonage et le pratiquer. Méthode protégée. Si l'objet n'est pas clonable, doit renvoyer CloneNotSupportedException. Pour être clonable, un objet doit implémenter l'interface .Cloneable.

class Sneakers extends Shoes { public boolean equals(Object arg) { if((arg != null) && (arg instanceof Sneakers)) {

// comparer ici return true;

}

return false;

}

}

import .Hashtable;

public class Sheep implements Cloneable { Hashtable flock = new Hashtable(); public Object clone() { try {

return super.clone();

}

catch(CloneNotSupportedException e) { throw new Error("This should never Happen!"); }

}

}

Sheep one = new Sheep();

Sheep anotherOne = (Sheep)one.clone();

Attention, il faudrait faire une copie "en profondeur" public Object clone() { try {

Sheep copy = (Sheep)super.clone(); copy.flock = (Hashtable)flock.clone(); return copy;

}

catch(CloneNotSupportedException e) { throw new Error("This should never Happen!"); }

}

            Cours de base Java / Renaud Zigmann / Copyright 2000 XSALTO sarl                     21

            Cours de base Java / Renaud Zigmann / Copyright 2000 XSALTO sarl                     22

Utilitaires sur les classes et les Objects

Les conventions de nommage

La classe Object(suite)

?Méthode clone()

Il est possible de rendre la méthode publique si nécessaire

La classe Class

?La méthode getClass() de Objectrend une référence à un objet Class

?Les classes sont des instances de .Class.

Il y a un objet Class pour chaque classe utilisée. Il permet la réflection.

?Exemple

String myString = "Foo!";

Class c = myString.getClass(); // dynamique

Class c = String.class; // statique

.println(c.getName()); // ".String"

Produire une nouvelle instance d'un objet: newInstance(). Type retour: Object try {

String s2 = (String)c.newInstance();

} catch(InstantiationException(e)) { …} catch(IllegalAccessException(e)) { …}

Recherche l'objet classe correspondant à une chaine try {

Class sneakersClass = Class.forName("Sneakers");

}

catch(ClassNotFoundException e) { } ?Réflexion (197)

Le package .reflect permet au langage "de s'examiner lui-même". Méthodes: getFields(),getMethods(),getConstructors(),etc…

Nommage

Les noms doivent être en anglais.

Les noms doivent être "parlants". Éviter d'utiliser des abréviations. Utiliser la complétion automatique de l'éditeur de texte, pour cela, sous emacs, taper les premières lettres du nom puis M-/.

Les noms de paquetage ne doivent contenir que des minuscules et chaque mot séparé par un ".".

Les noms des classes et des interfaces doivent commencer par une majuscule et avoir la forme suivante s'ils contiennent plusieurs noms : MyClass.

Les noms de classe héritant de la classe Exception doivent se terminer par Exception : MyException.

Les noms des attributs static final doivent être en majuscule et avoir la forme suivante s'ils contiennent plusieurs noms : MY_CONSTANT.

Les noms des autres attributs et des méthodes doivent commencer par une minuscule et avoir la forme suivante s'ils contiennent plusieurs noms : myAttribut, myMethod().

Les recommandations

Minimiser les * dans les imports.

Écrire une méthode main pour chacune des classes testable.

Le main de l'application doit être dans une classe séparée Main.

Utiliser des interfaces aussi souvent que possible si vous pensez que votre implémentation est susceptible d'être remplacée par une autre ultérieurement.

Évitez de déclarer des attributs comme public. Implantez plutôt pour un attribut 'attribut', deux méthodes : setAttribut() et getAttribute().

Initialisez explicitement les attributs.

Initialisez les objets qui peuvent retourner une Enumeration(Vector, HashTable ) avec autre chose que null.

Minimisez les static non final.

Éviter de masquer les attributs par héritage.

Utilisez la forme Type [] var; pour déclarer les tableaux.

Écrire des méthodes qui ne font qu'une chose et en utilisent d'autres.

Déclarer toutes les méthodes public comme synchronized si elles peuvent être utilisée dans un contexte concurrent (threads).

Déclarer les méthodes qui sont souvent utilisées comme final.

Pensez à implanter la méthode clone et déclarez la classe comme implantant Cloneable.

Si possible écrire un constructeur par défaut pour pouvoir utiliser (newInstance()).

Utilisez equals() plutôt que ==.

Ne déclarez les variables locales qu'à l'endroit où vous en avez réellement besoin.

Affectez null à toutes les références qui ne sont plus utilisées.

Éviter la réutilisation de variable.

Évitez les affectations à l'intérieur de "()" (if, appel de méthodes, ).

            Cours de base Java / Renaud Zigmann / Copyright 2000 XSALTO sarl                     23

            Cours de base Java / Renaud Zigmann / Copyright 2000 XSALTO sarl                     24

Applets

Développement d'applets

Programme exécuté à l'intérieur d'un autre.

?étend java.applet.Applet.

?La classeapplet ne maîtrise  pas totalement son exécution, controlée par le navigateur au moyen des méthodes suivantes : public void init() appelée après le chargement del'applet.

public void start() appelée chaque fois que l'on entre dans le document la contenant. public voidstop() appelée chaque fois que l'on sort du document la contenant. public voiddestroy() appelée pour détruire les ressources allouées par l'applet. La méthode stop() ne fait rien par défaut,l'applet pourra donc continuer à s'exécuter en arrière plan.

Entre un appel à start() et l'appel suivant à stop() la méthode isAlive retourne true.

?Une applet est incluse dans un document HTML grâce au tag , un exemple d'inclusion est le suivant :

?Récupérer les paramètres passés dans la page HTML :

public String getParameter(String name)

?Exécution sûre pour le navigateur l'incluant. Manipulations interdites:

accès au système de fichier local lancement de tache au moyen de exec() chargement de librairies ou définition de méthodes natives accès au System.getProperty() donnant des informations sur l'utilisateur, la machine locale.

modification des propriétés système accès à un autre groupe de thread.

changement de ClassLoader, SocketImplFactory, SecurityManager, ContentHandlerFactory, URLStreamHandlerFactory ouvrir un connexion réseau vers une autre machine que celle dontelle provient accepter des connexions.

?Récupérer des informations sur le document HTML :

public URLgetDocumentBase() retourne l'URL de base du document contenant l'applet public URLgetCodeBase() retourne l'URL de base de l'applet.

            Cours de base Java / Renaud Zigmann / Copyright 2000 XSALTO sarl                     25

            Cours de base Java / Renaud Zigmann / Copyright 2000 XSALTO sarl                     26

Applets

?Interagir avec le navigateur exécutant l'applet ou avec les autres applets de la page. Il faut récupérer un objet implémentant l'interface AppletContext.

public AppletContext getAppletContext()

Méthodes de AppletContext public abstract Applet getApplet(String name) public abstract Enumeration getApplets()

Le nom à passer en paramètre de la première méthode est le champ NAME donné àl'appletdans le tag HTML (Horloge dans l'exemple précédent).

Pour interagir avec le navigateur, il est possible d'appeler les méthodes de l'objet implémentant AppletContext:

public abstract void showDocument(URL url)

public abstract void showDocument(URL url,String target)

qui permettent de charger un nouveau document. La deuxième méthode permet de choisir une frame (_self , _parent, _top) particulière, une nouvelle fenetre(_blank) ou une fenetre de nom particulier.

La méthode public void showStatus(String msg) de la classe Applet permet également de visualiser une ligne d'état souvent en bas du navigateur.

?Récupérer simplement des images ou des sons :

public Image getImage(URL url)

public Image getImage(URL url, String name) public AudioClip getAudioClip(URL url)

public AudioClip getAudioClip(URL url, String name)

?Manipuler les sons : méthodes de java.applet.AudioClip :

public abstract void play() public abstract void loop() public abstract void stop()

Des méthodes sont également disponibles pour jouer directement les sons : public voidplay(URL url)

public voidplay(URL url, String name)

Ceci est un message

         Cours de base Java / Renaud Zigmann / Copyright 2000 XSALTO sarl                     27

         Cours de base Java / Renaud Zigmann / Copyright 2000 XSALTO sarl                     28

Threads

Threads

Qu'est qu'un thread ?

?Conceptuellement: un flux de calcul dans un programme

?Plusieurs threads s'exécutent dans la même application

?Problème: la synchronisation

La classe Thread et l'interface Runnable

?Un thread est représenté par une instance de .Thread.

Actions possibles: démarrer le thread, le terminer, le suspendre, etc..

?Il est possible d'étendre la classe Thread, mais ce n'est pas la solution la plus naturelle.

?Les ordres sont transmis via l'interface .Runnable

public interface Runnable { abstract public void run();

}

Un thread commence en exécutant la méthode rund'un objet particulier

?Créer et démarrer des threads

class Animation implements Runnable {   … public void run() {     … while(true) {

// Do something

}}}

Animation happy = new Animation("Mr Happy"); Thread myThread = new Thread(happy); myThread.start();

… startest appelée une fois dans la vie d'unthread stop permet de tuer lethread class Animation implements Runnable {

Thread myThread;

Animation (String name) { myThread = new Thread(this);

myThread.start();

}

utilisation d'une classe adapter class Animation { public void startAnimating() { myThread = new Thread (new Runnable() { public void run() { drawFrames(); }

});

myThread.start();

} private void drawFrames() {

// faire quelquechose

            Cours de base Java / Renaud Zigmann / Copyright 2000 XSALTO sarl                     29

}

            Cours de base Java / Renaud Zigmann / Copyright 2000 XSALTO sarl                     30

Les Threads

Les Threads

Contrôler les threads

?méthodes

stop, suspend, resume, sleep, interrupt try {

Thread.sleep(1000);

// Thread.currentThread().sleep(1000);

}

catch(InterruptedException e)

{ //do

} join: bloquer l'appelant jusqu'à complétion. Peut être paramétré par un nombre de millisecondes

Applets

?Les applets peuvent être startées et stoppées plusieurs fois.

?L'applet est démarrée lors de l'affichage, et l'arrête quand l'utilisateur va dans une autre page ou scrolle l'applet hors de vue

public class UpdateApplet extends java.applet.Applet implements Runnable { private Thread updateThread; int updateInterval = 1000;

public void run() { while(true) { try{

Thread.sleep(updateInterval); } catch(InterruptedException) {} repaint();

}

}

public void start() { if(updateThread == null) { updateThread = new Thread(this); updateThread.start();

}

public vois stop() { if(updateThread!=null) { (); updateThread=null;

}

}

}

public class Clock extends UpdateApplet { public void paint(.Graphics g) {

g.drawString(new ().toString(), 10, 25);

}

}

Considérations

L'appletest tuée à chaque arrêt (dépendant des navigateurs).

Le problème est que nous n'avons aucun contrôle sur les mouvements de l'utilisateur. Si l'applet est simplement suspendue lorsqu'elle est hors de vue, nous n'avons aucune garantie de la tuer un jour. Dans les cas simples, on garde l'exemple précédent. Dans les cas complexes, on utilise la méthode destroy appelée lorsque l'applet va être supprimée (du cache par exemple) public void start() { if(updateThread == null) { updateThread = new Thread(this); updateThread.start();

}

else updateThread.resume();

public void stop() updateThread.suspend();

}

public void destroy() { if(updateThread!=null) { (); updateThread = null;

}

Synchronisation

?Utilisation de moniteurs cf (Hoare)

?Chaque objet dispose d'un verrou (lock).

?Chaque classe et chaque instance d'une classe peut être verrouillée pour séquentialiser l'accès aux ressources.

?Le mot clé synchronized  indique que le threaddoit être seul à exécuter simultanément une action sur l'objet.

Une seule méthode synchronisée est possible sur l'objet à un instant donné.

synchronized int sumRow() { return cellA1 + cellA2 + cellA3; }

Si deux méthodes différentes de la même classe ne peuvent pas s'exécuter simultanément, il suffit de les déclarer les deux synchronized: la ressource partagée est la classe.

Autre possibilité : poser un verrou sur un objet particulier synchronized (myObject) {

// des choses qui doivent être protégées }

?wait() et notify()

wait et notifysont des méthodes de Object, donc tout le monde en hérité wait abandonne lelocket s'endort notifyrend lelocket réveille le wait

            Cours de base Java / Renaud Zigmann / Copyright 2000 XSALTO sarl                     31

            Cours de base Java / Renaud Zigmann / Copyright 2000 XSALTO sarl                   32

Les Threads

Threads

?Exemple

class Quelquechose { synchronized void attente() {

// je fais quelque chose

// j'ai besoin d'attente un événement externe pour continuer wait();

// je reprends là où j'ai laissé

}

synchronized void notifierMethod() {

// faire quelque chose // on signale qu'on l'a fait notify();

// faire des choses en plus

}

}

Scheduling et priorités

?La méthode de scheduling est spécifique à chaque implémentation.

?Chaque thread a une priorité

?Par défaut tous les threads ont la même priorité

?Quand un threadcommence, il continue jusqu'à:

un appel à sleep ou wait une attente de verrouillage sur une instance une attente en entréé/sortie, type read(), un appel à yield() un arrêt duthread par stop().

?La plupart des machines virtuelles font du timeslicing

?Pour tester le timeslicing

class PtitThread { public static void main(String args[]) { new MonThread("coucou").start(); new MonThread("toto").start();

}

}

class MonThread extends Thread {

String message;

MonThread(String msg) { message = msg; }

public void run() { while(true) .println(message); } }

?Priorité

Méthode Thread.setPriority(x), où x est Thread.MIN_PRIORITY, NORM_PRIORITY, MAX_PRIORITY.

Scheduling et priorités (suite)

?Donner du temps

class MonThread extends Thread {

public void run() { while(true) {

.printmn(message); yield();

}

}

}

Permet de rendre du temps au système pour éviter qu'un thread s'accapare le temps machine

Les threas natifs sont différents

Il est possible de gérer des groupes de threads (cours avancé)

            Cours de base Java / Renaud Zigmann / Copyright 2000 XSALTO sarl                     33

            Cours de base Java / Renaud Zigmann / Copyright 2000 XSALTO sarl                     34

Classes utilitaires de base

Utilitaires

Strings

?les strings sont constantes. Pour changer une chaîne il faut en créer une nouvelle

length() rend la longueur d'une chaîne concat() concatène deux chaînes

+ est équivalent à concat

String machaine = new String({'L','a','l','a'});

String.valueOfutilise la méthode toString de Object pour convertir vers une String Méthode Double.valueOf, Integer.valueOf pour convertir depuis des chaînes charAt() rend le caractère à une position donnée toCharArray() rend une chaîne de caractères

== compare les références equals compare les chaines equalsIgnoreCasecompare en caseInsensitive compareTo fait une comparaison lexicale

startsWithet endsWith comparent respectivement le début et la fin d'une chaîne indexOfrend l'indice d'occurrence d'une sous-chaîne trim() suppression des blancs

toUpperCase et toLowerCase

substring() extraction d'une sous-chaîne replace(): remplacement de sous-chaînes, etc…

?StringBuffer

.StringBuffer est un buffer qui peutcroitre par opposition aux Strings. La méthode append() permet d'ajouter des caractères toSTring() pour se ramener à des chaînes

?.StringTokenizer

décodage d'un buffer, par défaut utilisation des délimiteurs 'blancs' méthodes: hasMoreTokens, nextToken, countTokens

String text = "Now is the time for, all goods"; StringTokenizer st = new StringTokenizer(text);

while(st.hasMoreTokens()) { String word=st.nextToken();

}

?

Toutes les méthodes sont statiques et math ne peut pas être instanciée. Méthodes: cos, abs, sin, atan, random, pow, etc…

?Classes wrapper pour les types de base

void-> , boolean -> .Boolean, etc…

Méthodes de conversion de type

Utilisation de conteneurs d'objets

            Cours de base Java / Renaud Zigmann / Copyright 2000 XSALTO sarl                     35

            Cours de base Java / Renaud Zigmann / Copyright 2000 XSALTO sarl                     36

Classes utilitaires de base

Principaux packages

Dates

? et .GregorianCalendar

?.DateFormat

Vector et Hashtables

?tableau dynamique qui peut grandir pour ajout de nouveaux items.

String one = "one";Integer i= new Integer(0);

String two = "two";

String three = "three"; Vector things = new Vector();

things.addElement(one);things.addElement(i); things.addElement(three); things.insertElementAt(two, 1);

String bibi = (String)things.firstElement();

Méthodes :elementAt(),firstElement(),lastElement(),indexOf(),removeElement(), size()

?hashtable = dictionnaire

Hashtable dates = new Hashtable();

("christmas", new GregorianCalendar(1997, Calendar.DECEMBER,

25));

("independence", new GregorianCalendar(1997, ,

4));

("groundhog", new GregorianCalendar(1997, Calendar.FEBRUARY, 2));

GregorianCalendar g = (GregorianCalendar )("christmas"); dates.remove("christmas");

Autres méthodes:containsKey(), keys(),elements() Properties

Principaux packages java

Package

Remarques

Java.applet

Applet qui permet de gérer l’ensemble du comportement des applets

Classes d’IHM de AWT

Java.beans

Classes et interfaces nécessaires à la mise en œuvre des composants Javabeans

Entrées/sorties

Classes de base du langage java

Classes mathématiques

Implémentation des sockets et gestion des urls

Communication entre deux machines virtuelles java

Java.security

Signatures numériques

Connection à des bases de données via JDBC

Formatage de données

Outils

            Cours de base Java / Renaud Zigmann / Copyright 2000 XSALTO sarl                     37

            Cours de base Java / Renaud Zigmann / Copyright 2000 XSALTO sarl                     38

Les Streams

Fichiers

Représentent un canal de communication.

?Utilisés pour la lecture ou l'écriture depuis un terminal, un fichier ou le réseau.

?Les données sont écrites ou lues les unes à la suite des autres.

Les données ne sont pas accédées de façon aléatoire en fonction d'une position absolue, comme c'est le cas pour un tableau, mais séquentiellement, dans l'ordre du flot et relativement à la position courante.

?Propriétés d'un flot

les données sont lues dans le flot dans l'ordre où elles ont été écrites ; les limites des données ne sont pas préservées par le flot, c'est-à-dire que si l'écriture d'un octet dans le flot est suivie de l'écriture de deux octets, rien n'oblige le lecteur à lire un octet puis deux. Il peut lire, par exemple, deux octets puis un ou toute autre combinaison ; un flot peut utiliser un tampon pour améliorer les performances ou encore remettre un ou plusieurs octets dans le flot pour faciliter l'analyse des données qu'il véhicule.

Les classes et interfaces de manipulation de flot sont regroupées dans le paquetage en deux  groupes :

?celles qui manipulent des octets

?celles qui manipulent des caractères.

Les caractères nécessitent un traitement particulier en Java car ils sont codés sur deux octets (UNICODE).

Toutes les méthodes sur les flots peuvent renvoyer IOException

            Cours de base Java / Renaud Zigmann / Copyright 2000 XSALTO sarl                     39

            Cours de base Java / Renaud Zigmann / Copyright 2000 XSALTO sarl                     40

Flots d'octets

Les flots de caractères

Toutes les classes qui manipulent des flots d'octets héritent de InputStreamou OutputStream

Les entrées-sortie standards sont des flots d'octets. Ils sont accessibles comme des membres statiques (in, out et err) de la classe .System.

InputStream stdin = ;

OutputStream stdout = ;

?Les méthodes d'InputStream

int read() retourne l'octet suivant dans le flot ou -1 si la fin du flot est atteinte ; int read(byte[] b) lit dans le flot au plus b.lengthoctets qui sont placés dans le tableau b. Les octets sont stockés dans le tableau dans l'ordre de lecture, de la première case vers la dernière. Le nombre d'octets effectivementlu est retourné ou

-1, si la fin du flot est atteinte ;

int read(byte[] b, int offset,int len) est équivalente à la méthode précédente si ce n'est que les octets sont stockés à partir de la case d'indice offset et qu'au plus len octets sont lus.

?Les méthodes d'OuputStream

void write(int b) permet d'écrire l'octet de poids faible de b dans le flot ; void write(byte[] b) écrit dans le flot les b.length octets stockés dans le tableau. L'ordre d'écriture des octets dans le flot est celui du tableau ; void write(byte[] b, intoffset,int len) a le même comportement que la méthode précédente, mais écrit len octets de b en partant de la case d'indice offset .

try{ byte b; int val = (); if(val != -1) b= (byte)val;

.write(b);

}

catch(IOException e)

La méthode voidclose() permet de libérer les ressources associées au flot.

Les flots permettent un accès séquentiel aux données.

Parfois, il est utile de modifier ce comportement. La méthode long skip(long n) permet de consommer les n premiers octets d'un flot. Le nombre d'octets effectivement consommé est retourné par cette méthode.

Certains flots supportent un marquage en vue d'un retour arrière(boolean markSupported()).

mark(int taillemax) et reset() permettent, respectivement, le marquage de la position courante dans le flot et un retour arrière vers la position préalablement marquée.

if(in.markSupported()){ (100);

// Diverses actions sur le flot

in.reset(); // Retourne à la position marquée si le nombre // d'octets consommés est inférieur à 100 }

?Les classes de flots de caractères dérivent des classes abstraites Reader et Writer

?Les méthodes de cette classe sont équivalentes à celles des classes InputStreamet OuputStream; seul le type des données lues est différent.

?Dans un flot de caractères, l'unité de lecture n'est plus l'octet mais le caractère. Ainsi, les méthodes read() assurent la lecture de deux octets insécables.

?Le passage d'un flot d'octets à un flot de caractères se fait relativement à un codage au moyen des classes OutputStreamWriter de InputStreamReader.

import .*; public class Converter {

String fromEnc; String toEnc;

public Converter(String fromEnc, String toEnc) { this.fromEnc = fromEnc; this.toEnc = toEnc;

}

public void convert(InputStream in, OutputStream out) throws IOException {

InputStreamReader reader = new InputStreamReader(in, fromEnc);

OutputStreamWriter writer = new OutputStreamWriter(out, toEnc); char[] tampon = new char[256]; int nbLus;

while((nbLus = (tampon))!= -1) { writer.write(tampon,0,nbLus);

} writer.close(); reader.close();

}

public static void main(String[] args) throws Exception{ Converter conv = new Converter("8859_1","Cp037"); conv.convert(,);

}

}

            Cours de base Java / Renaud Zigmann / Copyright 2000 XSALTO sarl                     41

            Cours de base Java / Renaud Zigmann / Copyright 2000 XSALTO sarl                     42

Les flots concrets

Les fichiers

Classes qui permettent de créer des instances de flots liées à des ressources concrètes telles que des fichiers ou des tableaux.

Quatre principaux types de flots :

?flots construits sur des tableaux, qui sont ByteArrayInputStream, ByteArrayOutputStream, CharArrayReader et CharArrayWriter. Les flots sur des tableaux en écriture sont très utiles pour construire des tableaux dont la taille n'est pas connue au moment où les premières données à stocker sont obtenues ;

ByteArrayOutputStream out = new ByteArrayOutputStream(); int b;

while((b = ())!=-1) { out.write(b);              // La taille du tampon augmente

}

byte[] tab = out.toByteArray();

out.reset();                   // Les données sont vidées du tampon

?les flots construits sur des chaînes de caractères, qui sont StringReader et StringWriter ;

String str = "ABC";

StringReader reader = new StringReader(str); int c;

while((c = ())!= -1) { .println((char)c);

}                              // Affiche A, puis B et C

?Les tubes, qui sont PipedInputStream, PipedOutputStream, PipedReader et PipedWriter. Un flot en lecture est connecté à un flot en écriture (ou l'inverse) en passant ce dernier en paramètre du constructeur du flot.

PipedWriter out = new PipedWriter(); PipedReader in = new PipedReader(out);

out.write('A');

.println((char)()); // Affiche A ?les flots sur des fichiers.

La classe File encapsule les accès aux informations relatives au système de fichiers.

import .*;

public class ListDir { public static void main(String[] args) throws Exception { for(int i=0;i<args.length;i++){ listDirectory(args[i]);

}

}

public static void listDirectory(String dirName) throws FileNotFoundException {

File f = new File(dirName); if (!f.exists() || !f.isDirectory()){ throw new FileNotFoundException();

}

String[] ls (); for(int i=0;i< ls.length;i++){

StringBuffer s = new StringBuffer(20);

File file = new File(ls[i]);

s.append(file.isDirectory()?"d":"-");

s.append(file.canRead()?"r":"-");

s.append(file.canWrite()?"w\t":"-\t");

s.append(file.length());

s.append("\t");

s.append(file.getName()); .println(s);

}

}

}

Les classes FileReader, FileWriter, FileInputStream,

FileOutputStreampermettent de créer des flots concrets associés à des fichiers.

Les classes FileReader et FileWriter ( classes de commodité) utilisent le codage de la plate-forme pour écrire les caractères dans le fichier.

Constructeur prend en argument le nom du fichier ou un objet File encapsulant la référence du fichier. En lecture, le flot est systématiquement placé en début de fichier. En écriture, le flot est placé en début du fichier préalablement tronqué, ou placé en fin de fichier

FileInputStream fistream = new FileInputStream(new File("toto"));

FileOutputStream fostream = new FileOutputStream("toto",true); // Ajout à la fin du fichier

            Cours de base Java / Renaud Zigmann / Copyright 2000 XSALTO sarl                     43

            Cours de base Java / Renaud Zigmann / Copyright 2000 XSALTO sarl                     44

Les flots

Les flots

Les fichiers

?La classeRandomAccessFile permet à la fois la lecture et l'écriture dans le même flot.

La lecture ou l'écriture s'effectue à la position courante dans le fichier (récupérable par la méthode getFilePointer()).

?La position courante est ensuite augmentée de la taille de la donnée lue ou écrite.

Ce flot permet également un déplacement aléatoire dans le fichier grâce à la méthode void seek(long pos).

RandomAccessFile raf = new RandomAccessFile("toto","rw"),

Les filtres

?Pour faciliter leur utilisation, il est possible positionner un filtre sur un flot. Un filtre est un flot particulier qui enveloppe un autre flot. Les données lues dans un filtre proviennent du flot enveloppé.

Les filtres peuvent hériter des classes abstraites FilterInputStream, FilterOutputStream, FilterReader ouFilterWriter.

Le flot enveloppé est stocké dans le champ in (resp. out) pour les flots en lecture (resp. en écriture).

DataInputStreamet DataOutputStream

?Lecture et écriture binaire des types primitifs (int, long, etc.), ainsi que des chaînes de caractères, indépendamment de la plate-forme.

ByteArrayOutputStream out = new ByteArrayOutputStream(); DataOutputStream dataOut = new DataOutputStream(out); int i = 10; double d = 3.14; dataOut.writeInt(i); dataOut.writeDouble(d); dataOut.flush();

byte [] tab = out.toByteArray();

// Taille de tab = taille int (4) + taille double (8)

.println(tab.length); // Affiche 12

LineNumberReader

La classeLineNumberReader lit un flot de caractères ligne par ligne grâce à la méthode readLine(). Ce filtre permet également de connaître le numéro de la ligne courante.

InputStreamReader systemReader = new InputStreamReader();

LineNumberReader reader = new LineNumberReader(systemReader); String line;

while((line = reader.readLine())!=null) {

// Affiche les lignes lues sur l'entrée standard

// et les affiche précédées de leur numéro

.println(reader.getLineNumber() + ":\t" + line); }

ObjectInputStreamet ObjectOutputStream

?Ces interfaces généralisent les précédentes en permettant de lire aussi des objets.

?Les objets écrits ou lus doivent implémenter l'interface .Serializable ou .Externalizable.

ObjectInputStream p = new ObjectInputStream(new

FileInputStream("tmp")); int i = p.readInt();

String today = (String)p.readObject(); Date date = (Date)p.readObject(); p.close();

PrintReader ou PrintStream

?Permet d'écrire simplement toute variable sous forme chaîne de caractères.

Dans le cas des objets la méthode toString (qui peut être masquée) est appelée. Contrairement aux autres flux il n'y a pas d'exception levée mais une méthode checkError().

?PrintStreamfait les conversions de caractères par défaut en ISO8859-1.

PrintWriter out = new PrintWriter(new OutputStreamWriter(,

8859_1), true); out.println(1); out.println("un"); out.println(out);

            Cours de base Java / Renaud Zigmann / Copyright 2000 XSALTO sarl                     45

            Cours de base Java / Renaud Zigmann / Copyright 2000 XSALTO sarl                     46

Les tampons

Pour améliorer les performances des entrées-sorties il est préférable d'utiliser des tampons de lecture et d'écriture.

Un tel tampon peut permettre le marquage et le retour arrière pour certains flots qui ne le supportent pas par défaut.

BufferInputStream bu = new BufferInputStream(,256); (10);

Lecture de données sous forme de chaîne de caractères

?Le filtre StreamTokenizer permet de lire de façon simple des valeurs numériques entrées sous forme de chaîne de caractères.

import .*; import .*; public class Polonaise{ public static void main(String[] arg) throws IOException {

Stack stack = new Stack();

Double value;

StreamTokenizer in = new StreamTokenizer(new InputStreamReader()); in.wordChars('+','+'); in.wordChars('-','-'); in.wordChars('/','/'); in.wordChars('*','*'); in.eolIsSignificant(false); do { in.nextToken();

if(in.ttype == StreamTokenizer.TT_NUMBER){ (new Double());

}

if(in.ttype == StreamTokenizer.TT_WORD) { if(.length() == 1) { switch(.charAt(0)) { case '+':

try{ value = new Double(((Double)()).doubleValue()

+ ((Double)()).doubleValue());

(value);

.println(value);

}

catch(EmptyStackException e)

{

.println("Empty stack!");

} break;

.

}}}} while(in.ttype != StreamTokenizer.TT_EOF);

La sérialisation

}}

            Cours de base Java / Renaud Zigmann / Copyright 2000 XSALTO sarl                     47

            Cours de base Java / Renaud Zigmann / Copyright 2000 XSALTO sarl                     48

La sérialisation

Sérialisation (exemple)

Le mécanisme de sérialisation permet d'enregistrer ou de récupérer des objets dans un flux: persistance des objets ou envoi d'objets sur le réseau.

La sérialisation d'un objet est effectuée lors de l'appel de la méthode writeObject() sur un objet implémentant l'interfaceObjectOutput (par exemple un objet de la classe ObjectOutputStream).

La désérialisation d'un objet est effectuée lors de l'appel de la méthode readObject() sur un objet implantant l'interface ObjectInput (par exemple un objet de la classe ObjectInputStream).

Lorsqu'un objet est sauvé, tous les objet qui peuvent être atteints depuis cet objet sont également sauvés. En particulier si l'on sauve le premier élément d'une liste tous les éléments sont sauvés.

Un objet n'est sauvé qu'une fois grâce à un mécanisme de cache. Il est possible de sauver une liste circulaire.

Pour qu'un objet puisse être sauvé ou récupéré sans déclencher une exception NotSerializableExceptionil doit implémenter l'interfaceSerializable ou l'interface Externalizable.

L'interfaceSerializable ne contient pas de méthode. Tout objet implantant l'interface Serializable peut être enregistré ou récupéré même si une version différentede sa classe (mais compatible) est présente. Le comportement par défaut est de sauvegarder dans le flux tous les champs qui ne sont pas static, ni transient. Des informations sur la classe (nom, version), le type et le nom des champs sont également sauvegardées afin de permettre la récupération de l'objet.

import .*;

public class Point implements Serializable {

int x; int y;

public Point(int x, int y){ this.x = x; this.y = y;

}

public String toString() { return "(" + x + "," + y + ")";

}

public static void main(String [] args) throws Exception { Point a = new Point(1,2);

File f = File.createTempFile("tempFile","test");

f.deleteOnExit(); // Effacer le fichier à la fin du programme

ObjectOutputStream out = new ObjectOutputStream( new FileOutputStream(f));

out.writeObject(a); // Écriture de l'objet out.close();

ObjectInputStream in = new ObjectInputStream( new FileInputStream(f));

Point aPrime = (Point) in.readObject(); // Lecture de l'objet in.close();

.println("a = " + a);

.println("aPrime = " + aPrime);

.println("Equals = " + a.equals(aPrime)); }

}

Produit :

% java Point a = (1,2) aPrime = (1,2) Equals = false

Vector v = new Vector(); Point a=new Point(10,10);

v.addElement(new Point(10,20));

v.addElement(a);

v.addElement(new String("kjsdfhjhsd"));

v.addElement(a); out.writeObject(v);

Vector v2=(Vector)in.readObject(); if(v2.elementAt(1)==v2.elementAt(3)) // j'ai gagné

            Cours de base Java / Renaud Zigmann / Copyright 2000 XSALTO sarl                     49

            Cours de base Java / Renaud Zigmann / Copyright 2000 XSALTO sarl                     50

Sérialisation (suite)

La sauvegarde des objets

?L'objet récupéré est une copie de l'objet sauvé.

?Si l'objet implante les méthodes :

private void writeObject(ObjectInputStream s) throws IOException private void readObject(ObjectOutputStreams) throws IOException celles-ci sont appelées pour sauvegarder ou lire les champs propres de la classe (pas ceux de la classe héritée) plutôt que les méthodes par défaut. Ceci peut servir pour sauvegarder des informations complémentaires ou sauvegarder des champs non sérialisables.

Les méthodes suivantes sont équivalentes à la sauvegarde par défaut : private void writeObject(ObjectInputStream s) throws IOException{ s.defaultWriteObject(); }

private void readObject(ObjectOutputStream s) throws IOException { s.defaultReadObject();

}

?L'interfaceExternalizable laisse toute liberté (et problèmes) à l'utilisateur. Utiliser les méthodes :

public void writeExternal(ObjectOutput s) thows IOException

public void readExternal(ObjectInput s) thows IOException

Toutes les sauvegardes et lecture, en particulier celles des super-classes, sont laissées à la charge de l'utilisateur. De plus toute sous-classe peut masquer ces méthodes ce qui peut poser des problèmes de sécurité.

?Une certain nombre de classes ne sont pas sérialisables :

Thread

InputStreamet OutputStream

Peer

JDBC

Chaque fois qu'un objet est sauvé dans un flux, un objet handle, unique pour ce flux, est également sauvé.

Cehandle est attaché à l'objet dans une table dehashage. Chaque fois que l'on demande de sauver à nouveau l'objet, seul le handle est sauvé dans le flux. Ceci permet de casser les circularités.

Les objets de la classe Class sont sauvegardés et restaurés de façon particulière.

?L'objet Class n'est pas sauvé ou récupéré directement dans le flux. Un objet de la classeObjectStreamClass qui lui correspond prend sa place. Cette classe contient les méthodes suivante :

public static ObjectStreamClass lookup(Class cl) retourne l'objet

ObjectStreamClass qui correspond à l'objet Class passé en paramètre si la classe implémente .Serializableou .Externalizable. public String getName() retourne le nom de la classe correspondante.

public long getSerialVersionUID() retourne le numéro de version de l'objet classe. Deux classes sont compatibles si elles ont même numéro de version.

public Class forClass() retourne l'objet Class qui correspond à l'objet courant. public String toString() affiche une description de l'objet.

?Lors de l'écriture dans le flux d'un tel objet sont écrits dans le flux :

son nom ; leserialVersionUID ;

le nombre de champs sauvables ; la liste des noms, types et modifieur des champs ; l'objet ObjectStreamClass de la super-classe.

Il est possible d'ajouter des informations dans le flux. Par exe mple une URL où récupérer le .class correspondant, comme dans le cas des RMI, ou bien le .class lui-même. Pour cela il faut masquer dans le classe ObjectOutputStream la méthode

: protected void annotateClass(Class cl) throws IOException

            Cours de base Java / Renaud Zigmann / Copyright 2000 XSALTO sarl                     51

            Cours de base Java / Renaud Zigmann / Copyright 2000 XSALTO sarl                     52

La sérialisation (exemple)

Exemple utilisant la classe util.NetworkClassLoader:

package test.serial; import .*; import util.*;

public class MyObjectOutputStream extends ObjectOutputStream{ String baseUrl;

public MyObjectOutputStream(OutputStream out, String baseUrl) throws

IOException{ super(out);

this.baseUrl = baseUrl;

}

protected void annotateClass(Class cl) throws IOException{ this.writeObject(baseUrl);

}

public static void main(String[] args) throws IOException {

MyObjectOutputStream out = new MyObjectOutputStream(new

FileOutputStream(args[0]), ";); out.writeObject(new Cons(new Integer(1),new Nil())); out.close();

}

}

Une fois compilé on lance : prompt> java test.serial.ObjectOutputStream fichier puis si l'on lance prompt> strings fichier on obtient elemt

Ljava/lang/Object;L

tailt Lutil/List;t

.Integer valueq .Number ?

Lors de la lecture dans le flux des informations concernant la classe, l'objet ObjectStreamClass est reconstruit et la méthode :

protected Class resolveClass(ObjectStreamClass v) throws IOException, ClassNotFoundException

est appelée. Celle-ci retourne un objet Class dontserialVersionUID est comparé à celui trouvé dans le flux. Par défaut cette méthode appelle leClassLoader standard. Il est possible masquer cette méthode pour par exemple utiliser les informations utilisées sauvées dans le flux au moyen de annotateClass() pour récupérer la classe.

package test.serial; import .*; import .*; import util.*;

public class MyObjectInputStream extends ObjectInputStream{ Hashtable hashtable = new Hashtable();

public MyObjectInputStream(InputStream in) throws IOException{ super(in);

}

protected Class resolveClass(ObjectStreamClass v) throws

IOException, ClassNotFoundException {

String baseUrl = (String) this.readObject();

ClassLoader loader = (ClassLoader) (baseUrl); if(loader == null) { loader = new NetworkClassLoader(baseUrl); (baseUrl,loader);

}

if (loader == null) throw new ClassNotFoundException(); return loader.loadClass(v.getName());

}

public static void main(String[] args) { try{

MyObjectInputStream in = new MyObjectInputStream(new

FileInputStream(args[0]));

List l = (List) in.readObject(); .println(l); in.close();

}

catch(Exception e){

e.printStackTrace();

}

}

}

Une fois compilé on lance :

prompt> java test.serial.ObjectInputStream fichier et on obtient :

Loading Loading Cons(1, Nil())

         Cours de base Java / Renaud Zigmann / Copyright 2000 XSALTO sarl                     53

         Cours de base Java / Renaud Zigmann / Copyright 2000 XSALTO sarl                     54

La sécurité

Le contrôle de version

?La sécurité au niveau des classes chargées est la même que celle qui existe par défaut. En effet, le code des classes n'est pas par défaut stocké dans le flux mais récupéré par un mécanisme externe que peut contrôler l'utilisateur.

?Tous les champs sont a priori sauvés dans le flux, en particulier les champs private. Par défaut, une classe ne peut pas être sauvée, il faut qu'elle implémente une des interfaces Serializable ou Externalizable, les objets de classes vraiment sensibles ne peuvent donc pas être sauvés (sauf demande explicite du programmeur). Si le programmeur veut quand même sauver un tel objet il doit marquer transient les champs qu'il ne vaut pas voir apparaître pas dans le flux.

Java propose un mécanisme permettant de "résoudre" le problème de la lecture d'un objet d'une classe qui a évolué depuis que l'objet a été sauvegardé.

Par défaut une exceptionClassNotFoundException est levée si les deux classes sont différentes. Cas où la nouvelle classe pourrait-être utilisée à la place de la précédente:

Les changements ne concerne que des méthodes, des champs staticoutransient.

Des champs ont été ajoutés. Ils peuvent être initialisés par défaut ou par la méthodereadObject().

Des classes ont été ajoutées à la hiérarchie ou unimplement Serializable a été ajouté . Les champs associés peuvent être initialisés par défaut ou par readObject().

Des classes ont été retirées à la hiérarchie ou unimplement Serializablea été retiré. Les champs associés dans le flux peuvent être lus et détruits s'ils sont de type primitif et créés si ce sont des objets.

Une méthode readObject() a été ajoutée qui commence par appelerdefaultReadObject() et qui gère les exception pour les données supplémentaires.

La méthode readObject() n'existe plus. Il est possible d'essayer de lire l'objet avec la méthode defaultReadObject() en détruisant les champs non-attentdus.

Dans ces cas là, il est possible de spécifier qu'une classe est compatible avec la précédente. Par cela il suffit de récupérer le serialVersionUID de la classe précédente est d'ajouter un champ : staticfinal longserialVersionUID= SUID de la classe précédente; dans la nouvelle classe.

Si dans une classe un tel champ n'est pas spécifié dans une classe le serialVersionUID est calculé par unhashage (Secure Hash Algortihtm-1 du National Institute of Standardsand Technology) sur le nom de la classe, sesmodifieurs , ces interfaces, les noms et modifieurs des champs et méthodes. La valeur de ce hashage donne une clef de 64bit qui est récupérable par la commande serialver show

˜ Les objets de la classe String sont sauvés sont la forme UTF-8.

˜ Les tableaux sont sauvés sous la forme : ObjectStreamClass correspondant, suivi de la taille du tableau et des éléments.

˜ Pour les autres objets le mécanisme de sauvegarde est le suivant si l'objet est sérialisable :

sauvegarde de l'objet ObjectStreamClassde la classe la plus précise de l'objet implémentant Serializable ;

Pour cette classe et ses super-classes les champs sont sauvés dans un ordre particulier par la méthodedefaultWriteObject ou par la méthode writeObject si elle a été définie (la méthode readObject devra les lire dans le même ordre). Si les champs sont d'un type primitif la méthode write* correspondante est appelée.

Si la classe implémenteExternalizable, seule la méthode writeExternalest appelée.

Dans chacun de ces cas si l'objet n'est pas sauvable (n'implémente pas Serializable, ni Externalizable) un exception NotSerializableExceptionest retournée.

            Cours de base Java / Renaud Zigmann / Copyright 2000 XSALTO sarl                     55

            Cours de base Java / Renaud Zigmann / Copyright 2000 XSALTO sarl                     56

Création d'interfaces avec AWT

Abstract Window Toolkit

Vue d'ensemble

?La partie graphique est un ensemble de composants imbriqués

?Cette imbrication créée une hiérarchie

Principaux composants

?Conteneurs (containers)

Composants pouvant contenir d'autres composants. La forme la plus courante est le panneau (panel).

?Les canevas (canvases)

Surface de dessin.

?Les composants d'interface.

Boutons, listes, simples menus popup, cases à cocher, etc…

?Composant de construction de fenêtres

Décor: fenêtre, cadfe, barres de menu, boîtes de dialogue.

?La racine des composants est Component

Exemple: ajout d'un composant à une applet

public void init() {

Button b = new Button("OK");

add(b); }

?La position du composant à l'écran dépend du gestionnaire de fenêtre.

Etiquettes

?Label: chaîne de texte non modifiable

?Création

Label(), Label(String), Label(String,int). Int est une constante d'alignement (, Label.RIGHT, Label.CENTER).

?Police de caractères

La méthode setFont est appelée sur l'étiquette ou sur le conteneur père pour changer la police pour tous les composants du conteneur.

?Exemple

import .*;

public class LabelTest extends java.applet.Applet { public void init() { setFont(new Font("Helvetica", , 14)); setLayout(new GridLayout(3,1));

add(new Label("aligned left", )); add(new Label("aligned center", Label.CENTER));

            Cours de base Java / Renaud Zigmann / Copyright 2000 XSALTO sarl                     57

            Cours de base Java / Renaud Zigmann / Copyright 2000 XSALTO sarl                     58

Abstract Window Toolkit ()

Les conteneurs

Les composants

L'AWT est une API qui permet de construire des interfaces graphiques (gui) indépendamment de la plate-forme.

Une interface graphique est composée de composants (Component) simples et standards qui sont créés au moyen d'une de ces classes :

?Button un bouton

button = new Button("Bouton");

?Canvas une zone de dessin

canvas = new Canvas();

?Checkbox une case à cocher

checkbox = new Checkbox("un");

?des CheckBox peuvent être regroupées dans un

CheckboxGroup

group = new CheckboxGroup(); checkbox.setCheckboxGroup(group);

?Choiceun menu de sélection

choice = new Choice(); choice.addItem("un"); choice.addItem("deux");

?Label un texte

label =new Label("texte"); ?List une liste

list = new List(); ("un"); ("deux");

?Scrollbar un ascenseur

s = new Scrollbar(Scrollbar.VERTICAL,ini,pas,min,max); s.getValue();

?TextArea une zone de saisie de texte

texte = new TextArea("Hello",5,40,SCROLLBARS_BOTH); texte.append(" World"); texte.getText();

?TextField une ligne de saisie de texte

field = new TextField("texte",taille);

?Les composants simples sont inclus dans un conteneur (Container) qui est construit au moyen d'une des classes suivantes. Les composants et certains conteneurs sont inclus dans les conteneurs au moyen de la méthode add() (parfois surchargée).

?Frame une fenêtre

frame = new Frame("Titre"); (bouton,"North"); (); ();

?Dialog une fenêtre de dialogue (FileDialog permet de sélectionner un fichier)

modal = new Dialog(frame,"Titre",true); ("North",new Label("Cliquez !"); ("Center",new Button("Ok"));

?Panel une zone de fenêtre

panel = new Panel(); (new Button("Oui")); (new Button("Non"));

?ScrollPane une zone avec ascenseur d'une fenêtre

pane = new ScrollPane(); (new Canvas()); Les barres de menus

?Seule une Frame peut contenir une barre de menu (Menubar). Celle-ci contient des Menu (ajoutés avec la méthode add()) euxmêmes constiués de MenuItem, CheckboxMenuItemou de Menu. Elle est ajoutée à la fenêtre au moyen de la méthode setMenuBar().

?Tous les composants peuvent contenir un PopUpMenu ajouté au moyen de la méthode add(). Celui-ci est un Menu et peut contenir des MenuItem, des CheckboxMenu et des Menu.

?Des raccourcis clavier (MenuShortcut) peuvent être associés aux menus.

menu = new Menu("Fichier");

(new MenuItem("Ouvrir",new MenuShortcut(KeyEvent.VK_F8))); menuBar = new MenuBar(); (menu); frame.setMenuBar(menuBar);

popup = new PopupMenu(); (menu);

            Cours de base Java / Renaud Zigmann / Copyright 2000 XSALTO sarl                    59

            Cours de base Java / Renaud Zigmann / Copyright 2000 XSALTO sarl                     60

Le positionnement des composants

La gestion des événements

Généralités

?Le positionnement de chacun des composants d'une fenêtre est effectué lors de l'initialisation ou de la modification de la taille de la fenêtre grâce à un layout qui se charge suivant son type de disposer les composants.

?Il existe des layouts par défaut qui sont associés à chaque conteneur.

?Il est possible de le changer au moyen de la méthode setLayout().

?Dans une fenêtre plusieurs layoutsont utilisés en imbriquant des Panel. Pour forcer le recalcul du placement il faut appeler la méthode validate() sur le conteneur.

Il existe plusieurs type de layout :

BorderLayout arrange les composants en fonction d'une position relative (North,

South, East, West ou Center) panel.setLayout(new BorderLayout()); (new Button("Ok"),"South"); //1.1 ("North", new Button("Ok")); //1.0

CardLayout arrange les composants en onglets panel.setLayout(new CardLayout());

FlowLayout arrange les composants de gauche à droite frame.setLayout(new FlowLayout());

GridLayout arrange les composants dans une grille de taille fixe de gauche à droite et de haut en bas frame.setLayout(new GridLayout(2,3));

GridBagLayout arrange les composants relativement à une grille (à éviter) null les composants sont alors positionnés de façon absolue grâce à setLocation().

La taille des composants

?Calcul

La taille des composants et des conteneurs est calculée automatiquement (i.e. taille du bouton relative au label de celui-ci).

Pour ceux, comme les Canvas qui ont à priori une taille nulle laméthode setSize() est utilisée ou la méthode getPreferredSize() est masquée.

La méthode pack() appliquée à une fenêtre force tous ses composants et ellemême à prendre leur taille "idéale".

?L'affichage

Une fenêtre et ses composants devient visible (et/ou passe au premier plan) grâce à la méthode show().

Un composant ou un conteneur peut être rendu visible ou non au moyen de la méthode setVisible() .

La version 1.1 de la gestion des événements est basée sur la délégation.  Dérivés de AWTEvent.

?les événements bas-niveau ComponentEvent

FocusEvent

InputEvent

KeyEvent

MouseEvent

ContainerEvent

WindowEvent

?Les événements sémantiques

ActionEvent

AdjustementEvent

ItemEvent

TextEvent

?L'événement contient son type getID() et toutes les informations relatives à ce type dans la classe correspondante.

?Les événements sont envoyés aux composants au moyen de la méthode dispatchEvent().

?L'événement n'est pas géré au niveau du composant mais délégué à un autre objet qui s'est préalablement déclaré au moyen des méthodes

addListener() ou setListener() comme intéressé par l'événement. Celui-ci doit implanter Listener ou hériter de Adapter.

?Les différents sont :

Component dans Component

Focus dans Component Key dans Component

Mouse dans Component

MouseMotion dans Component

Container dans Container

Window dans Dialog et Frame

Action dans MenuItem, Button, TextField et List

Item dans Choice, Checkbox, CheckBoxMenuItem et List

Text dans TextField et TextArea

Adjustement dans Scrollbar

            Cours de base Java / Renaud Zigmann / Copyright 2000 XSALTO sarl                     61

            Cours de base Java / Renaud Zigmann / Copyright 2000 XSALTO sarl                     62

La gestion des événements (suite)

Dessiner

?Si il existe plusieurs listeners l'ordre d'envoi n'est pas normé. Tous les événements qui peuvent être modifiés (disposant d'une méthode set*) sont clonés pour chaque listener. L'envoi des événements est synchrone.

class Listener implements ActionListener {

public void actionPerformed(ActionEvent e){ }

}

class GUI extends Frame { Button b = Button("Action");

b.addActionListener(new Listener()); pack(); show(); }

?Gains de performance car le composant peut ignorer tous les événements sur lesquels aucun listener n'est enregistré.

?Il est encore possible de gérer les événements par héritage.

Pour cela un composant doit être dérivé et masquer, soit une méthode appelée pour un certain événement processEvent, soit la méthode de sélection globale processEvent().

?La méthode qui masque doit appeler la méthode de la superclasse afin d'assurer le bon traitement des événements.

?Si aucun listener n'est enregistré l'événement est ignoré il faut donc accepter les événements du type désiré au moyen de la méthode enableEvents().

class GUI extends Frame {

public GUI(String title) { super(title); enableEvents(AWTEvent.FOCUS_EVENT_MASK);

}

protected void processFocusEvent(FocusEvent e) {

super.processFocusEvent(e);

}

}

?Le composant est rendu actif ou inactif au moyen de la méthode setEnabled().

Il est possible de dessiner dans tous les composants, mais celui qui est défini pour cela est Canvas.

Trois méthodes:

?repaint() demande d'afficher de nouveau le contenu dès que possible (plusieurs demandes peuvent être regroupées)

?update() efface le contenu du composant définit le rectangle de dessin et appelle paint()

?paint() définit quoi afficher

?Les méthodes paint() et update() prennent en paramètre le contexte graphique de la classeGraphics. C'est à lui que sont associés :

la zone de dessin draw*() ou fill*() la fonte setFont() et getFont()

le rectangle de mise à jour setClip() et getClip() (getClipRect() et clipRect() en 1.0) la couleur setColor() et getColor()

l'opération (XOR oupaint) setXORMode() et setPaintMode() la couleur du XOR

            Cours de base Java / Renaud Zigmann / Copyright 2000 XSALTO sarl                     63

            Cours de base Java / Renaud Zigmann / Copyright 2000 XSALTO sarl                64

Dessiner (exemple)

Dessiner (exemple, suite)

package test; import .*; import .event.*;

/**

* @author Gilles ROUSSEL

* @version 0.1

*/ public class MyCanvas extends Canvas

{ static final int WIDTH = 1000; static final int HEIGHT = 1000; Image draw;

public MyCanvas() { setSize(WIDTH, HEIGHT); setBackground(Color.white); setForeground(Color.black);

}

public void update(Graphics g) { paint(g);

}

public void paint(Graphics g) { if(draw == null) { draw = createImage(WIDTH,HEIGHT);

Listener listener = new Listener(draw.getGraphics()); addMouseMotionListener(listener); addMouseListener(listener);

}

g.drawImage(draw,0,0,null); }

class Listener extends MouseMotionAdapter

implements MouseListener {

int prevX; int prevY;

Graphics graphics;

Listener(Graphics graphics) { this.graphics = graphics; }

public void mouseDragged(MouseEvent e) { graphics.drawLine(prevX,prevY,e.getX(),e.getY()); prevX = e.getX(); prevY = e.getY(); repaint();

}

public void mousePressed(MouseEvent e) { prevX = e.getX(); prevY = e.getY();

} public void mouseClicked(MouseEvent e){} public void mouseReleased(MouseEvent e) {} public void mouseEntered(MouseEvent e) {} public void mouseExited(MouseEvent e) {}

}

}

package test; import .*; import .event.*;

/**

* @author Gilles ROUSSEL

* @version 0.1

*/ public class MyFrame extends Frame

{

ScrollPane scrollPane = new ScrollPane();

MyCanvas canvas = new MyCanvas();

Graphics graphics;

public MyFrame() { scrollPane.setSize(200,200); (canvas); add(scrollPane); pack(); show();

}

public static void main(String[] args) { new MyFrame();

}

}

  Cours de base Java / Renaud Zigmann / Copyright 2000 XSALTO sarl                     65

  Cours de base Java / Renaud Zigmann / Copyright 2000 XSALTO sarl                     66

Le package : adresses internet

Classes "réseau"

Les adresses Internet : .InetAddress

?Cette classe permet de manipuler les adresses Internet.

?Il n'existe pas de constructeur. Pour obtenir une instance de cette classe il est nécessaire d'utiliser des méthodes de classe.

?Ces méthodes de classe sont les suivantes :

public static InetAddress getLocalHost() throws UnknownHostExeception Retourne un objet contenant l'adresse Internet locale.

public static synchronized InetAddress getByName(String host_name) throws UnknownHostExeception

Retourne un objet contenantl'adressse Internet de la machine dont le nom est passé en paramètre.

public static synchronized InetAddress[] getAllByName(String host_name) throws

UnknownHostExeception

Retourne un tableau d'objets contenant l'ensemble des adressses Internet de machines qui répondent au nom passé en paramètre.

?Méthodes :

public String getHostName()

Retourne le nom de la machine dont l'adresse est stockée dans l'objet.

public byte[]getAddress ()

Retourne l'adresseinternet stockée dans l'objet sous forme d'un tableau de 4 octets dans l'ordre réseau.

public String toString ()

Retourne une chaîne de caractères qui liste le nom de la machine et son adresse.

?Exemple :

InetAddress adresse = InetAdress.getLocalHost ();

.println(adresse);

            Cours de base Java / Renaud Zigmann / Copyright 2000 XSALTO sarl                     67

            Cours de base Java / Renaud Zigmann / Copyright 2000 XSALTO sarl                     68

Le package : les datagrammes udp

Le package : les datagrammes udp

Pour utiliser le protocole de transport UDP, deux classes

DatagramPacket et DatagramSocket

La classeDatagramPacket

?Cette classe permet de créer des objets qui contiendront les données envoyées ou reçues ainsi que l'adresse de destination ou en provenance du datagramme.

?Constructeurs, un pour les paquets à recevoir l'autre pour les paquets à envoyer.

public DatagramPacket(byte buffer[],int taille)

Construit un objet pour recevoir un datagramme. Le paramètre buffer correspond à la zone où doit être stocké le datagramme reçu et le paramètre taille correspond à la taille maximale des datagrammes à recevoir.

public DatagramPacket(byte buffer[],int taille, InetAddress adresse, int port)

Construit un objet pour envoyer un datagramme. Le paramètre buffer correspond à la zone où est stocké le datagramme à envoyer, le paramètre taille correspond à la taille du datagramme à envoyer, adresse correspond à l'adresse de la machine à qui envoyer le datagramme et port sur quel port UDP.

?Méthodes :

public synchronized InetAddress getAddress () Retourne l'adresse stockée dans le paquet.

public synchronized int getPort() Retourne le port stocké dans le paquet. public synchronized byte[] getData ()

Retourne les données stockées dans le paquet. public synchronized int getLength ()

Retourne la taille des données stockées dans le paquet. public synchronized void setAddress(InetAddress iaddr) Modifie ou affecte l'adresse de destination. public synchronized void setPort(int iport) Modifie ou affecte le port de destination.

public synchronized void setData(byte ibuf[])

Modifie ou affecte la référence de la zone contenant les données. public synchronized void setLength(int ilength)

Modifie ou affecte la taille de la zone contenant les données.

DatagramSocket : créer des sockets UDP qui permettent d'envoyer et de recevoir des datagrammes UDP.

?Avant toute communication en mode UDP il est nécessaire de créer une socket aussi bien du coté client que du coté serveur. Pour cela Java propose trois constructeurs :

public DatagramSocket () throws SocketException : Crée un objet de type socket et l'attache à un port disponible de la machine locale. Ce constructeur doit être utilisé dans les clients pour lesquels le port d'attachement n'a pas besoin d'être connu. public DatagramSocket (int port) throws SocketException: Crée un objet de type socket et l'attache au port UDP local passé en paramètre. En particulier, ce constructeur doit être utilisé dans les serveurs pour lesquels le port d'attachement a besoin d'être fixé préalablement afin qu'il soit connu des clients.

public DatagramSocket(intport, InetAddress laddr) throws SocketException: Crée un objet de type socket, l'attache au port UDP local passé en paramètre et à une adresse spécifique de la machine locale. Ce constructeur n'est utile que si la machine locale dispose de plusieurs adresse Internet.

?Une fois la socket (objet de type DatagramSocket) créée et attachée à un port particulier de la machine locale il est possible d'envoyer et de recevoir des datagrammes, via cette socket, au moyen des méthodes suivantes :

public void send(DatagramPacket data) throws IOException

Permet d'envoyer les données contenues dans la variable data vers la machine et le port dont les valeurs ont été préalablement spécifiées dans la variable data. public synchronized void receive(DatagramPacket data) throws IOException Permet de recevoir un datagramme qui sera stocké dans data. Après appel, data contient les données reçues, leur taille, l'adresse de l'envoyeur ainsi que son port d'attachement. Cette méthode est bloquante tant qu'il n'y a rien à recevoir. Si le message est trop long pour être stocké, celui-ci est tronqué, et le reste est perdu. Il n'est donc pas possible de recevoir des messages dont on neconnait pas préalablement la taille.

Il est possible de spécifier un délai d'attente maximal en reception. Pour cela, il faut positionner une variable detimeout sur la socket au moyen de la méthode : public synchronized void setSoTimeout(int timeout) throws SocketException Une valeur de 0 (zéro) correspond à ne pas avoir detimeout.

?Autres méthodes

public voidclose ()

Ferme la socket et libère les ressources qui lui sont associées. La socket ne pourra plus etreutilisée ni pour envoyer, ni pour recevoir des datagrammes.

public int getLocalPort()

Retourne le port d'attachement de la socket. public synchronized int getSoTimeout() throws SocketException Retourne la valeur courante du timeout associé à la socket.

            Cours de base Java / Renaud Zigmann / Copyright 2000 XSALTO sarl                     69

            Cours de base Java / Renaud Zigmann / Copyright 2000 XSALTO sarl                     70

Exemple

Le package : tcp

Deux classes :

une classe ServeurEcho qui attend une chaîne de caractères et la retourne une classe ClientEchoqui envoye une chaîne de caractères, attend que le serveur la lui retourne et l'affiche. import .*; import .*; class ServeurEcho

{ final static int port = 8532; final static int taille = 1024;

final static byte buffer[] = new byte[taille]; public static void main(String[] args]) throws Exception

{

DatagramSocket socket = new DatagramSocket(port); while(true)

{

DatagramPacket data =

new DatagramPacket(buffer,buffer.length); socket.receive(data);

.println(data.getAddress()); (data);

}

}

}

class ClientEcho

{ final static int taille = 1024;

final static byte buffer[] = new byte[taille]; public static void main(String[] args) throws Exception

{

InetAddress serveur = InetAddress.getByName(args[1]); int length = args[2].length(); byte buffer[] = new byte[length]; args[2].getBytes(0,length,buffer,0); DatagramPacket data =

new DatagramPacket(buffer,length,serveur,);

DatagramSocket socket = new DatagramSocket();

(data); socket.receive(data); .write(buffer);

}

}

L'API utilisée pour accéder au protocole TCP se décompose en deux classes, une utilisée par les clients et l'autre par les serveurs.

La classeSocket

?La classeSocket est utilisée par les clients TCP.

?Pour créer un objet de la classeSocket, il est nécessaire d'utiliser un des constructeurs suivant :

public Socket (String machine, int port) throws UnknownHostException, IOException

public Socket (InetAddress adresse, int port) throws UnknownHostException, IOException

public Socket(String machine,int port,

InetAddress local, int portLocal) throws IOException public Socket(InetAddress adresse,int port,

InetAddress local, int portLocal) throws IOException

?La création de cet objet entraîne la création d'un point de connexion (la socket) et la connexion vers une autre socket (le serveur).

?L'adresse du serveur est composée de l'adresse d'une machine (sous forme d'un nom ou d'un objet de la classeInetAddress) et d'un numéro de port.

?L'adresse locale et le port local peuvent être spécifiés.

?Par défaut, l'appel au constructeur est bloquant tant que la connexion TCP n'est pas établie.

?Une fois la connexion établie, il est possible de récupérer le flux d'entrée et le flux de sortie de la connexion TCP vers le serveur au moyen des méthodes :

public InputStream getInputStream () throws IOException public OutputStream getOutputStream() throws IOException

?Il est alors possible d'échanger des données avec le serveur au moyen de toutes les primitives de lecture et d'écriture des différentes classes du package .

Par défaut les primitives de lecture, tel que read(), sont bloquantes tant que rien n'est lisible sur le flux.

La primitive suivante permet de définir un temps maximal d'attente :

public synchronized void setSoTimeout(int timeout) throws SocketException

            Cours de base Java / Renaud Zigmann / Copyright 2000 XSALTO sarl                     71

            Cours de base Java / Renaud Zigmann / Copyright 2000 XSALTO sarl                     72

Le package : tcp

Le package : tcp

?Une fois la connexion terminée il est important de fermer le flux d'entrée et le flux de sortie, mais aussi la socket au moyen de la méthode :

public synchronized voidclose () throws IOException

Par défaut cette primitive n'est pas bloquante mais la socket reste ouverte tant qu'il reste des paquets à envoyer (en paticulier le datagramme FIN).

?Définir un temps maximum d'attente avant de fermer la socket.

public void setSoLinger(boolean on, int val) throws SocketException

?Par défaut, les implémentations du protocole TCP essaient de remplir au maximum chaque paquet (Nagle algorithm) afin de diminuer le traffic réseau.

Pour éviter ce comportement, ce qui est souvent souhaitable dans le cas d'application interactives une primitive est disponible.

public void setTcpNoDelay(boolean on) throws SocketException

?Autres méthodes :

public InetAddress getInetAddress () public int getPort() public InetAddress getLocalAddress () public int getLocalPort() int getLocalPort()

public int getSoLinger() throws SocketException public synchronized int getSoTimeout() throws SocketException public boolean getTcpNoDelay() throws SocketException

?Remarque : une applet ne peut (pour des questions de sécurité) se connecter qu'à la machine depuis laquelle elle a été chargée.

Exemple :

package test;

import .*; import .*;

public class SmtpClient { public static void main(String[] args) throws Exception { sendmail("aaa","");

}

static void sendmail(String message, String to) throws Exception  { Socket s = new Socket(InetAddress.getByName(""),25);

PrintStream output = new PrintStream(s.getOutputStream()); output.println("HELO \r"); output.println("MAIL FROM: \r"); output.println("RCPT TO:<" + to + ">\r"); output.println("DATA\r"); output.println(message); output.println("\r\n.\r");

}

}

            Cours de base Java / Renaud Zigmann / Copyright 2000 XSALTO sarl                     73

            Cours de base Java / Renaud Zigmann / Copyright 2000 XSALTO sarl                     74

Le package : tcp

Le package : la classe url

La classe ServerSocket

?Cette classe est utilisée (comme son nom l'indique) pour créer une socket du coté serveur.

?La classe ServerSocket permet de créer un point de communication, sur un port particulier, en attente de connexions en provenance de clients. Contrairement à la classe Socket elle n'ouvre pas de connexion.

Constructeurs, où port correspond au port d'attente et count au nombre maximum de connexions en attente, non encore acceptées.

public ServerSocket(int port) throws IOException public ServerSocket(int port, int count) throws IOException

public ServerSocket(int port,intcount, InetAddress locale) throws IOException

?Une fois la socket créée, on attend les connexions de clients avec la méthode bloquante :

public Socket accept() throws IOException

?Il est possible de spécifier un temps maximal d'attente. Pour cela il faut appeler la méthode suivante avant l'appel à accept() :

public synchronized void setSoTimeout(int timeout) throws SocketException Cette méthode retourne un nouvel objet de la classeSocketqui est connecté avec le client qui a demandé la connexion. Il est alors possible de récupérer le flôt d'entrée et de sortie comme pour la socket du client.

?Exemple :

public class DaytimeServeur { public static void main(String[] args) throws Exception {

ServerSocket s = new ServerSocket(0); .println(s.getLocalPort()); while(true) {

Socket serviceSocket =  s.accept(); PrintStream output =

new PrintStream(serviceSocket.getOutputStream()); output.println(new Date()); serviceSocket.close();

}}}

?Autres méthodes :

public voidclose() throws IOException public int getLocalPort() public InetAddress getInetAddress()

public synchronized int getSoTimeout() throws IOException

Interface de haut niveau pour accéder aux informations du Web en utilisant des URLs.

?Pour créer un objet de la classe URL nous disposons de quatre constructeurs :

public URL(String protocol, String machine, intport, String fichier) throws MalformedURLException

Crée une URL absolue à partir du protocole, de la machine, du port et du fichier. public URL(String protocol, String machine, String file) throws MalformedURLException

Crée une URL absolue à partir du protocole, de la machine et du fichier, en utilisant le port par défaut du protocole.

public URL(String url) throws MalformedURLException

Crée une URL absolue à partir d'une chaîne de caractères brute. public URL(URLcontext, String url) throws MalformedURLException

Crée une URL absolue à partir d'une chaîne de caractères brute et d'une URL contexte.

?Exemple

Les trois URLs suivantes représentent la meme ressource.

new URL(""); new URL("http","",80,""); new URL(";,"");

?Comparaison de deux URLs en incluant ou non l'ancre

public boolean sameFile(URLother) public boolean equals(Object other)

?Manipulation de composants de l'URL

public String getProtocol(): retourne le protocole ; public String getHost() : retourne le nom de la machine ; public int getPort() : retourne le numéro de port ; public String getFile() : retourne la partie fichier ; public String getRef() : retourne la référence ou ancre dans le fichier ("#ref") ; public String toExternalForm() : retourne lafomecanonique de l'URL.

?Une fois l'objet URL construit, pour accéder aux informations contenues dans l'URL, il faut construire un objet URLConnection en utilisant la méthode :

public URLConnection openConnection() throws IOException

Cette méthode crée (si elle n'existe pas déjà) un objet permettant de créer une connexion vers la ressource référée par l'URL. Cette méthode invoque le bon gestionnaire de protocole (httppar exemple).

            Cours de base Java / Renaud Zigmann / Copyright 2000 XSALTO sarl                     75

            Cours de base Java / Renaud Zigmann / Copyright 2000 XSALTO sarl                     76

Le package : la classe url

Le package : la classe url

Exemple :

URLConnection connexion = url1.openConnection() ;

?Il est possible, si l'on ne désire pas d'informations sur l'objet contenu, de le récupérer directement avec la méthode :

public final Object getContent () throws IOException

?qui est un racourcipour :

openConnection().getContent()

Exemple :

public class Url {

public static void main(String[] args){ try{

URL url = new URL(";);

String Object texte = (String) url.getContent(); .println(texte);

}

catch(MalformedURLException e){ .println(e);

}

catch(IOException e){

.println(e);

}

}

}

La Classe URLConnection

?Cette classe est une classe abstraite qui propose une interface simple pour manipuler les entêtes et le contenu d'une connexion via une URL, commune à tous les gestionnaire de protocole. Un objet de cette classe est construit par l'appel à la méthode openConnection() sur un objet de la classe URL.

?La première chose à faire consiste à spécifier les entêtes de requêtes :

public void setRequestProperty(String key, String value) D'autres méthodes raccourcis sont disponibles :

public long getIfModifiedSince()

?Il faut ensuite ouvrir la connexion et parser les entêtes de réponse au moyen de la méthode :

public abstract void connect() throws IOException

La Classe URLConnection (suite)

?Récupérer chacune des entêtes par une des méthodes suivantes :

public String getHeaderField(String name) : retourne l'entête dont le nom est passé en paramètre si elle est présente et null sinon.

public int getHeaderFieldInt(String name, int default) public long getHeaderFieldDate(String name,long default) public String getHeaderFieldKey(int n) public String getHeaderField(int n)

?Les méthodes suivantes sont des racourcis pour les entêtes les plus courantes :

?public int getContentLength ()

?public String getContentType ()

?public long getDate ()

?Pour récupérer le contenu du document il suffit d'utiliser la méthode : public Object getContent() throwsIOException

?D'autres intéraction de plus bas niveau sont également possible en récupérant les fulx d'entrée et de sortie sur la connexion. public InputStream getInputStream() throws IOException public OutputStream getOutputStream() throwsIOException Exemple :

int len = connexion.getContentLength();

InputStreaminput = connexion.getInputStream(); for(;len != 0; len--)

((char)());

.

            Cours de base Java / Renaud Zigmann / Copyright 2000 XSALTO sarl                     77

            Cours de base Java / Renaud Zigmann / Copyright 2000 XSALTO sarl                     78

Les Remote Method Invocation

Remote Method Invocation

?Les RMIs permettent d'invoquer de façon simple des méthodes d'objets distribués sur un réseau Internet.

?Les RMIs propose un ensemble d'outils et de classes prédéfinies qui rendent l'implantation d'appels de méthodes et d'objets distants aussi simple que leur implantation dans un contexte local.

?Les RMIs utilise le mécanisme standard de sérialisation de JAVA.

?Les RMIs sont basées sur la notion de Stub/Skeleton.

Du coté client, le Stubqui implémente la même interface que l'objet distant est chargé de transformer l'appel de méthode en une suite d'octets àenvoyer sur le réseau (Marshaling) et derecontruirele résultat reçu sous le même format

(Unmarshaling). Le format d'un appel de méthode contient l'identificateur de l'objet distant, l'indentificateur de la méthode et les paramètres sérialisés. La valeur de retour contient le type (valeur ou exception) et la valeur sérialisée.

Du coté serveur le Skeleton se charge reconstruire les paramètres, de trouver l'objet appelé et d'appeler la méthode. Il se charge ensuite de retourner le résultat.

?Pour écrire un objet distant :

Ecrire l'interface de l'objet distant.

Ecrire l'implémentation de cette interface.

Générer les Stub/Skeleton correspondant.

Exporter l'objet implémentant l'interface, l'objet distant attend alors les requêtes via le Skeleton.

Appeler une méthode de l'objet via le Stub.

?Exemple d'un serveur de Frame

L'objet distant est un objet capable d'afficher une Frame passéeen paramètre de sa méthode showFrame() .

La première chose à faire est de définir l'interface de l'objet :

package ; import .*; import .*;

public interface FrameDisplay extends Remote { public void showFrame(Frame frame) throws RemoteException; }

         Cours de base Java / Renaud Zigmann / Copyright 2000 XSALTO sarl                     79

         Cours de base Java / Renaud Zigmann / Copyright 2000 XSALTO sarl                     80

RMI (suite)

RMI (suite)

Il faut ensuite en écrire l'implémentation :

package ; import .*; import .*;

public class FrameDisplayImpl implements FrameDisplay { public FrameDisplayImpl() throws RemoteException {} public void showFrame(Frame frame) throws RemoteException { (); }}

Il faut maintenant compiler cette classe et générer les Stub/Skeleton correspondant au moyen de la commande :

prompt> javac test/rmi/FrameDisplayImpl

prompt> rmic .FrameDisplayImpl

Pour que l'objet soit accessible il faut maintenant le créer, l'exporter et l'enregistrer sous un nom pour qu'il soit possible de récupérer une référence sur celui-ci. 

package ; import .*; import .*; import .server.*; public class ExportFrameDisplay { public static void main(String[] args) throws Exception{ FrameDisplay frameDisplay = new FrameDisplayImpl();

UnicastRemoteObject.exportObject(frameDisplay);

Naming.rebind("//localhost:10003/FrameDisplay",frameDisplay); .println("FrameDisplay registered"); }}

Une fois compilée, son exécution entraîne l'attente d'appels à condition d'avoir préalablement installé le service de nommage.

prompt> javac test/rmi/ExportFrameDisplay prompt> remiregistry 10003 &

prompt> java .ExportFrameDisplay

?Le client. Celui-ci récupère un référence sur l'implémentation distant via le nom qui lui a été attribué précédemment, puis appel sur cette référence la méthode distante, comme si elle était locale.

package ; import .*; import .*; public class ClientFrame { public static void main(String[] args) throws Exception {

Frame frame = new Frame (){ public void paint(Graphics graphics) { graphics.setColor(Color.black);

graphics.drawString("Hello World!",65,60);

}    }; frame.setSize(200,100);

FrameDisplay display = (FrameDisplay)

Naming.lookup("rmi://localhost:10003/FrameDisplay"); display.showFrame(frame);

}}

Une fois compilé le lancement du client entraîne l'affichage de la fenêtre sur la machine où a été lancé le serveur.

Le serveur

?Actuellement la seule classe de serveur qu'il est possible d'instancierest la classe .server.UnicastRemoteServer. Celle-ci hérite de la classe .server.RemoteServer qui hérite elle-même de la classe .server.RemoteObject.

La classeRemoteObject implémente le comportement de la classe Objectpour les objets distants :

la méthode hashcode() pour être sûr que deux références à un même objet distant ait le même code de hashage. la méthode equals() pour la même raison.

la méthode toString() pour avoir un affichage particulier.

?La classeRemoteServer doit être la classe de base de toutes les futures (?) classes de serveurs. Celle-ci ne possède que trois méthodes :

public static String getClientHost() throws ServerNotActiveExceptionqui permet de connaître l'identité du client pendant un appel de méthode.

public static void setLog(OutputStream out) qui permet définir un flux vers lequel sont journalisés tous les appels de méthode distante.

public static PrintStream getLog() qui récupère le flux dejournalisation. Par défaut aucun flux n'est utilisé.

UnUnicastRemoteServer est un serveur basé sur TCP dont les références ne sont valide que pendant la durée du processus. Il implémente une méthode clone() permettant de créer un serveur distinct du précédent.

?Du coté du client le serveur est accessible via la classe RemoteStub qui hérite de RemoteObject.

            Cours de base Java / Renaud Zigmann / Copyright 2000 XSALTO sarl                     81

            Cours de base Java / Renaud Zigmann / Copyright 2000 XSALTO sarl                  82

RMI (suite)

RMI (suite)

Exporter un Objet

Pour pouvoir être exporté un objet doit implémenter la classe Remote. Il y a alors deux possibilités pour être exporter :

hériter de la classe UnicastRemoteObject . L'appel au constructeur exportera automatiquement l'objet courant.

appeler la méthode exportObject()de la classe UnicastRemoteObject sur l'objet à exporter. Cette méthode retourne l'objet RemoteStub associé.

Lorsqu'un objet est exporté un ensemble dethreads est créé pour attendre les appels de méthodes.

Le service de nommage

?Le service de nommage permet de récupérer le Stub d'un objet distant à partir d'un nom. La classeNaming permet d'accéder de façon simple au service de nommage d'une machine en utilisant un format de type URL : rmi://machine:port/objet

Avant de pouvoir enregistrer un objet il faut lancer un serveur de nommage, en général au moyen de rmiregistryport &

Il est alors possible d'enregistrer un objet local au moyen des méthodes :

public static void bind(String name, Remote obj) throws AlreadyBoundException, MalformedURLException, UnknownHostException, RemoteException public static void rebind(String name,Remote obj) throws RemoteException, MalformedURLException, UnknownHostException

?Il est possible de récupérer le Stub d'un objet distant à partir de son URL au moyen de la méthode :

public static Remote lookup(String name) throws NotBoundException, MalformedURLException, UnknownHostException, RemoteException

?Les méthodes suivantes permettent de désenregistrer l'objet du service de nommage et de lister l'ensemble des objets enregistrés :

public static void unbind(String name) throws RemoteException, NotBoundException, MalformedURLException, UnknownHostException public static String[] list(String name) throws RemoteException, MalformedURLException, UnknownHostException

?Cette classe ne permet pas un nommage hiérarchique.

Si l'on désire un service de nommageplus évolué il est possible de l'écrire en utilisant la classe .registry.LocateRegistry . Celle-ci possède des méthodes de manipulation d'objet implémentant l'interface .registry.Registry.

public static Registry createRegistry(int port) throws RemoteExceptionqui crée un nouveau service de nommagesur le port spécifié (ce service n'existe que pendant le temps de vie du serveur).

?L'interfaceRegistry dispose des même méthodes que la classe Naming mais elles ne sont pas static :

bind(),rebind() , lookup(), unbind() et list().

?A la place de la classeNaming on pourrait utiliser la classe suivante :

package ; import .registry.*; import .*; public class Naming { public static Remote lookup(String host, int port, String

remoteName) throws RemoteException, UnknownHostException, NotBoundException, AccessException {

Registry registry = LocateRegistry.getRegistry(host,port); return registry.lookup(remoteName);

}

public static void bind(String host, int port, String name,Remote

obj) throws RemoteException, UnknownHostException, AlreadyBoundException, AccessException {

Registry registry = LocateRegistry.getRegistry(host,port); (name, obj);

}

}

Le passage de paramètres et valeur de retour

Le passage de paramètres pour les méthodes distantes n'a pas la même sémantique que le passage de paramètres pour les méthodes locales.

Les variables de type primitif sont passées par valeurs.

Les objets qui n'implémentent pas l'interface Remotesont passés par recopie à condition qu'ils implémentent l'interface Serializable ouExternalizable. Sinon une exception est levée.

Les objets qui implémentent l'interface Remotesont remplacés par l'objet Stub correspondant lors du passage de paramètres.

Le retour de valeur a la même sémantique.

public static Registry getRegistry(int port) throws RemoteException

publicCoursstatic Registry getRegistry(String host)de base Java / Renaud Zigmann / Copyright 2000 XSALTO sarlthrows RemoteException,        83

UnknownHostException

            Cours de base Java / Renaud Zigmann / Copyright 2000 XSALTO sarl                  84

public static Registry getRegistry(String host,int port) throws RemoteException,

RMI (suite)

RMI (suite)

Les exceptions

?La levée d'exception a la même sémantique que le passage de paramètres.

?Toutes les signatures de méthodes distantes et des constructeurs d'objets distantes doivent contenir l'exception .RemoteException . Celle-ci permet de retourner une erreur en cas de problème dû à la non localité de l'appel (par exemple un problème lors de la connexion à l'objet distant).

Le chargement dynamique des classes

?Dans l'exemple précédent nous n'avons pas précisé quelles classes étaient présentes sur le client et quelles classes étaient présentes sur le serveur. Si toutes les classes nécessaires sont présentes localement la classe locale est chargée par défaut.

?Les RMIs proposent un mécanisme permettant de charger dynamiquement les classes depuis le réseau si elles ne sont pas présentes localement.

?Si le client est une applet toutes les classes sont chargées depuis soncodebase.

?Si le client est une application toutes les classes qui apparaissent explicitement dans le code du client sont chargées

avec le chargeur par défaut. Sinon, un objet de la classe RMIClassLoader est utilisé pour charger les classes (par exemple la classeStub). Le schéma de recherche d'une classe est le suivant :

Recherche dans le CLASSPATH

Pour les paramètres ou les valeurs de retour recherche à l'URL encodé avec la classe de l'objet

Pour le Stub ou Skeleton recherche de la classe à l'URL spécifiée dans la propriété système .server.codebase.

?L'URL encodée avec la classe de l'objet lors du passage de paramètre est :

l'URL depuis laquelle a été chargée l'objet si elle existe. l'URL spécifiée dans la propriété système .server.codebasesinon.

Le schéma de chargement est le même pour le serveur.

?Si la propriété système .server.useCodebaseOnly est true, une classe non locale peut uniquement être chargée que depuis l'URL spécifiée dans codebase.

?Il est possible de forcer le chargement d'une classe par le réseau en appelant explicitement le chargement d'une classe avant son utilisation. Pour cela on peut utiliser la méthode loadClass() de la classe RMIClassLoader.

Class c =

RMIClassLoader.loadClass("http://massena/CLASSES","Complex"); Il est ainsi possible de bootstrapper complètement un client en supposant qu'il implémente un interface connue localement (ici Runnable).

Class c = RMIClassLoader.loadClass("http://massena/CLASSES","Client");

(Runnable) client = c.newInstance(); ();

?Seul les URLs de type HTTP et FTP sont supportées par l'implantation actuelle.

?Il n'est pas possible de charger depuis le réseau une classe tant qu'aucun gestionnaire de sécurité n'a été mis en place au moyen de System.setSecurityManager(). Les classes chargées par le réseau sont soumises à ce gestionnaire de sécurité.

?Les RMIs propose un gestionnaire de sécurité

RMISecurityManager qui par défaut interdit tout ce qu'un

SecurityManager peut interdire, sauf la définition de classe. Pour permettre plus de chose il faut écrire son propre gestionnaire de sécurité héritant de RMISecurityManager .

            Cours de base Java / Renaud Zigmann / Copyright 2000 XSALTO sarl                   85

            Cours de base Java / Renaud Zigmann / Copyright 2000 XSALTO sarl                     86

RMI (suite)

RMI (suite)

Exemple d'un serveur de calcul de complexe ou toutes les classes sont chargées dynamiquement par le client.

Le client :

package ; import .*; import .*; public class ComplexCompute { public static void main(String [] args) throws Exception { Properties p =System.getProperties();

p.put(".server.codebase","-

;);

System.setProperties(p);

System.setSecurityManager(new RMISecurityManager());

ComplexOps obj = (ComplexOps) Naming.lookup("//"+ args[0]+

":10003/ComplexOps");

Complex c1 = obj.newComplex(1,1);

Complex c2 = obj.newComplex(2,2);

Complex c3 = obj.addComplex(c1,c2);

Complex c4 = obj.multComplex(c1,c2);

.println(c1 + " + " + c2 + " = " + c3);

.println(c1 + " * " + c2 + " = " + c4); }

}

L'interface de l'objet distant et la classe l'implémentant (le serveur).

package ; import .*;

public interface ComplexOps extends Remote{ public Complex newComplex(double re, double im) throws

.RemoteException; public Complex addComplex(Complex c1, Complex c2) throws

.RemoteException; public Complex multComplex(Complex c1, Complex c2) throws

.RemoteException;

} package ; import .*; import .server.*; import .*; import .*;

public class ComplexOpsImpl extends UnicastRemoteObject implements

ComplexOps { public ComplexOpsImpl() throws .RemoteException { super();

}

public Complex newComplex(double re, double im) throws

.RemoteException{ return new ComplexImpl(re,im); }

public Complex addComplex(Complex c1, Complex c2) throws

.RemoteException { return new

ComplexImpl(c1.getRe()+c2.getRe(),c1.getIm()+c2.getIm());

}

public Complex multComplex(Complex c1, Complex c2) throws

.RemoteException { return new ComplexImpl(c1.getRe()*c2.getRe()-

c1.getIm()*c2.getIm(),c1.getRe()*c2.getIm()+c1.getIm()*c2.getRe());

}

public static void main(String[] args) throws Exception { Properties p =System.getProperties();

p.put(".server.codebase","-

;);

System.setProperties(p);

System.setSecurityManager(new RMISecurityManager());

String bindUrl = "//" + InetAddress.getLocalHost().getHostName() +

":10003/ComplexOps";

Naming.rebind(bindUrl,new ComplexOpsImpl());

.println("ComplexOps Registered.");

}

}

L'interface et l'implémentation de l'objet complexe :

package ; public interface Complex { public double getRe(); public void setRe(double re); public double getIm(); public void setIm(double im); public String toString();

}

package ; import .*;

public class ComplexImpl implements Complex, Serializable { private double re; private double im;

public ComplexImpl(double re){ =re;

}

public ComplexImpl(double re, double im){ =re; =im;

}

public double getRe() { return re; } public void setRe(double re) { = re; } public double getIm() { return im; } public void setIm(double im) { = im; }

public String toString(){ return re + "+ i " +im;

}

  Cours de base Java / Renaud Zigmann / Copyright 2000 XSALTO sarl                    87

}

  Cours de base Java / Renaud Zigmann / Copyright 2000 XSALTO sarl                     88

Le Distributed Garbage Collector

RMI (suite)

Java propose un mécanisme de ramasse-miettes distribué. Plus exactement, il propose un mécanisme permettant à un objet exporté de savoir s'il existe de références distantes à cet objet. Pour être informé un objet implémentant la classe RemoteObject doit également implémenter l'interface .server.Unreferenced qui ne contient que la méthode public void unreferenced().

Chaque fois qu'il n'existe plus de référence distante à l'objet cette méthode est appelée. Cela ne veut pas dire qu'il n'existe plus de référence locale et donc l'objet n'est pas a priori détruit (il existe au moins un référence dans le serveur qui exporte l'objet). Après un appel à cette méthode, une application distante peut récupérer une référence sur cet objet, la méthode unreferenced()sera alors appelée au moins deux fois.

L'algorithme utilisé par Java suppose un rôle actif des clients qui chaque fois qu'ils reçoivent une référence à un objet distant informe le DGC associé à cet objet. Les clients doivent ensuite l'informer régulièrement qu'il possède toujours une référence sur l'objet. Dès que le client ne dispose plus de référence il en informe également le DGC qui peut, s'il n'y a plus de référence autre part, appeler la méthode unreferenced().

Cette méthode est particulièrement utile si l'on désire utiliser des serveur temporaire comme cela est le cas avec le mécanisme de rappel ou callback.

Le mécanisme de rappel

?Le mécanisme de rappel permet de rendre un appel de méthode distante asynchrone.

?Le client peut s'il le désire effectuer des traitement pendant que le serveur attend les résultat de la méthode.

?Ce mécanisme est particulièrement profitable dans le cas d'appel en cascade

?Sans mécanisme de rappel tous les serveurs sont bloqués pendant l'appel de méthode. Avec le mécanisme de rappel il ne sont bloqués que pendant le temps deredirection de la requête. Une autre stratégie plus coûteuse n'utilisant pas le mécanisme de rappel qui est parfois utilisée est la méthode itérative.

Exemple de serveur avec mécanisme de rappel :

Le client :

package ; import .*; import .server.*; import .*; public class CallbackClient{ public static void main(String[] args) throws Exception { Properties p =System.getProperties();

p.put(".server.codebase","-

;);

System.setProperties(p);

System.setSecurityManager(new RMISecurityManager());

CallbackServer obj = (CallbackServer) Naming.lookup("//" + args[0]+ ":10003/CallbackServer");

CallbackReturn tmpServer1 = new CallbackReturnImpl(1);

UnicastRemoteObject.exportObject(tmpServer1); ("Ca marche !",tmpServer1); tmpServer1=null;

}

}

Le serveur temporaire : package ; import .*;

public interface CallbackReturn extends Remote { public void returnString(String text) throws RemoteException;

} package ; import .*; import .server.*; import .*; import .*;

public class CallbackReturnImpl implements CallbackReturn,

Unreferenced { private int i;

public CallbackReturnImpl(int i) throws RemoteException { super(); this.i = i;

}

            Cours de base Java / Renaud Zigmann / Copyright 2000 XSALTO sarl                     89

            Cours de base Java / Renaud Zigmann / Copyright 2000 XSALTO sarl                     90

RMI (suite)

RMI (suite)

public void unreferenced(){

.println("Unreferenced !" + i);

Thread current = Thread.currentThread(); ThreadGroup group = current.getThreadGroup(); ();

}

public void returnString(String text) throws RemoteException {

Thread current = Thread.currentThread();

.println(text);

}

}

L'objet distant :

package ; import .*;

public interface CallbackServer extends Remote { public void call(String text, CallbackReturn tmpServer) throws RemoteException;

} package ; import .*; import .server.*; import .*; import .*;

public class CallbackServerImpl extends UnicastRemoteObject implements

CallbackServer { public CallbackServerImpl() throws RemoteException { super();

} public void call(String text, CallbackReturn tmpServer)

throws RemoteException {

tmpServer.returnString(text);

tmpServer=null; ();

}

public static void main(String[] args) throws Exception { Properties p =System.getProperties();

p.put(".server.codebase","-

;);

System.setProperties(p);

System.setSecurityManager(new RMISecurityManager());

String bindUrl = "//" + InetAddress.getLocalHost().getHostName() + ":10003/CallbackServer";

Naming.rebind(bindUrl,new CallbackServerImpl());

.println("CallbackServer Registered.");

}

}

Les threads et les RMIs

?Il n'est pas garanti que deux appels à une méthode distante seront exécutés dans un même thread, ni qu'ils seront exécutés dans un thread différent. Seuls deux appels provenant de deux JVM différentes sont toujours exécutés dans deux threads distincts.

?Il faut donc penser à synchroniser les méthodes distantes si cela est nécessaire.

Les RMIs à travers unfirewall

?Pour permettre l'utilisation des RMIs derrière unfirewall , Java peut encapsuler les appels de méthode dans des requêtes

HTTP. Par défaut si un objet distant ne peut pas être atteint, les RMIs encapsulent la requête dans une méthode HTTP POST, tente un connexion vers le port 80 et attend la réponse qui doit contenir le résultat. Si le serveur n'est pas un proxy, il est possible d'utiliser un CGI-script java-rmi pour se connecter à l'objet distant.

?Les appels effectués avec l'aide d'un serveur HTTP sont beaucoup plus lent.

            Cours de base Java / Renaud Zigmann / Copyright 2000 XSALTO sarl                     91

            Cours de base Java / Renaud Zigmann / Copyright 2000 XSALTO sarl                     92

JDBC

JDBC

Installation

?Installer le driver JDBC sur la machine

?Installer une passerelle JDBC-ODBC

Charger les drivers

Class.forName("jdbc.DriverXYZ");

Créer la connexion à la base de données

URL dans le cas de la passerelle: jdbc:odbc:mabase

Connection con = DriverManager.getConnection(url, "myLogin", "myPassword");

SQL

?Création d'un table en SQL

CREATE TABLE COFFEES (COF_NAME VARCHAR(32), SUP_ID INTEGER, PRICE FLOAT, SALES INTEGER, TOTAL INTEGER)

?Sélection

SELECT First_Name, Last_Name FROM Employees WHERE Last_Name LIKE "Washington" select First_Name, Last_Name from Employees where Last_Name like "Washington"

Statement

?méthode executeUpdate pour le DDL et les mises à jour de tables

Statement stmt = con.createStatement();

stmt.executeUpdate("CREATE TABLE COFFEES " + "(COF_NAME VARCHAR(32), SUP_ID INTEGER, PRICE FLOAT, " + "SALES INTEGER, TOTAL INTEGER)"); Statement stmt = con.createStatement(); stmt.executeUpdate(

"INSERT INTO COFFEES " +

"VALUES ('Colombian', 101, 7.99, 0, 0)");

Code retour de executeUpdate: nombre de lignes mises à jour

?méthode executeQuery pour les requêtes

ResultSet rs = stmt.executeQuery( "SELECT COF_NAME, PRICE FROM COFFEES"); while (()) {

String s = rs.getString("COF_NAME"); float n = rs.getFloat("PRICE");

.println(s + " " + n); }

Autre possibilité: utiliser le numéro de colonne au lieu du nom de colonne.

            Cours de base Java / Renaud Zigmann / Copyright 2000 XSALTO sarl                     93

            Cours de base Java / Renaud Zigmann / Copyright 2000 XSALTO sarl                     94

JDBC

JDBC

Méthodes utilitaires

getByte, getShort getInt getLong getFloat getDouble getBigDecimal getBoolean getString getBytes getDate getTime getTimestamp getAsciiStream getUnicodeStream getBinaryStream getObject

Mise à jour de tables

String updateString = "UPDATE COFFEES " +

"SET TOTAL = TOTAL + 75 " + "WHERE COF_NAME LIKE 'Colombian'";

stmt.executeUpdate(updateString);

String query = "SELECT COF_NAME, TOTAL FROM COFFEES " +

"WHERE COF_NAME LIKE 'Colombian'"; ResultSet rs = stmt.executeQuery(query);

while (()) {

String s = rs.getString(1); int n = rs.getInt(2);

.println(n + " pounds of " + s + " sold to date."); }

Utilisation de PreparedStatement

?L'instruction est compilé une fois pour toutes

?Intéressant lors de l'exécution d'instructions répétitives

?Exemple

PreparedStatement updateSales = con.prepareStatement( "UPDATE COFFEES

SET SALES = ? WHERE COF_NAME LIKE ?");

updateSales.setInt(1, 75);

updateSales.setString(2, "Colombian");

?Syntaxe

setXXX(index, value)

?Exemple

updateSales.setInt(1, 100);

updateSales.setString(2, "French_Roast"); updateSales.executeUpdate();

// changes SALES column of French Roast row to 100 updateSales.setString(2, "Espresso"); updateSales.executeUpdate();

// changes SALES column of Espresso row to 100 (the first

// parameter stayed 100, and the second parameter was reset // to "Espresso")

?Autre exemple

PreparedStatement updateSales;

String updateString = "update COFFEES " + "set SALES = ? where COF_NAME like ?"; updateSales = con.prepareStatement(updateString); int [] salesForWeek = {175, 150, 60, 155, 90};

String [] coffees = {"Colombian", "French_Roast", "Espresso",

"Colombian_Decaf", "French_Roast_Decaf"}; int len = coffees.length; for(int i = 0; i < len; i++) { updateSales.setInt(1, salesForWeek[i]); updateSales.setString(2, coffees[i]); updateSales.executeUpdate(); }

Transactions

?Par defaut, le mode est auto-commit

?Désactiver: con.setAutoCommit(false);

?Finir la transaction: méthode commit()

?Revenir en arrière: rollback()

?Exemple

con.setAutoCommit(false);

PreparedStatement updateSales = con.prepareStatement(

"UPDATE COFFEES SET SALES = ? WHERE COF_NAME LIKE ?"); updateSales.setInt(1, 50);

updateSales.setString(2, "Colombian");

updateSales.executeUpdate();

PreparedStatement updateTotal = con.prepareStatement(

"UPDATE COFFEES SET TOTAL = TOTAL + ? WHERE COF_NAME LIKE ?"); updateTotal.setInt(1, 50);

updateTotal.setString(2, "Colombian"); updateTotal.executeUpdate(); con.commit();

con.setAutoCommit(true);

?Les systèmes de BD qui utilisent les transactions déterminent un niveau de verrouillage (lock). Cet niveau peut être consulté et changé par getTransactionIsolation et setTransactionIsolation

            Cours de base Java / Renaud Zigmann / Copyright 2000 XSALTO sarl                     95

            Cours de base Java / Renaud Zigmann / Copyright 2000 XSALTO sarl                     96

Exemple

Exemple (suite)

import .*;

public class TransactionPairs { public static void main(String args[]) { String url = "jdbc:mySubprotocol:myDataSource";

Connection con = null;

Statement stmt;

PreparedStatement updateSales;

PreparedStatement updateTotal;

String updateString = "update COFFEES " +

"set SALES = ? where COF_NAME like ?";

String updateStatement = "update COFFEES " +

"set TOTAL = TOTAL + ? where COF_NAME like ?";

String query = "select COF_NAME, SALES, TOTAL from COFFEES"; try {

Class.forName("myDriver.ClassName");

} catch(.ClassNotFoundException e) {

.print("ClassNotFoundException: ");

.println(e.getMessage());

} try { con = DriverManager.getConnection(url,

"myLogin", "myPassword"); updateSales = con.prepareStatement(updateString); updateTotal = con.prepareStatement(updateStatement); int [] salesForWeek = {175, 150, 60, 155, 90};

String [] coffees = {"Colombian", "French_Roast",

"Espresso", "Colombian_Decaf",

"French_Roast_Decaf"}; int len = coffees.length; con.setAutoCommit(false); for (int i = 0; i < len; i++) {

updateSales.setInt(1, salesForWeek[i]); updateSales.setString(2, coffees[i]); updateSales.executeUpdate();

updateTotal.setInt(1, salesForWeek[i]); updateTotal.setString(2, coffees[i]); updateTotal.executeUpdate(); con.commit();

}

con.setAutoCommit(true); updateSales.close(); updateTotal.close(); stmt = con.createStatement();

ResultSet rs = stmt.executeQuery(query); while (()) {

String c = rs.getString("COF_NAME"); int s = rs.getInt("SALES"); int t = rs.getInt("TOTAL");

.println(c + "     " +  s + " " + t);

} stmt.close(); con.close();

} catch(SQLException ex) {

.println("SQLException: " + ex.getMessage()); if (con != null) {

try {

.print("Transaction is being ");

.println("rolled back"); con.rollback();

} catch(SQLException excep) {

.print("SQLException: ");

.println(excep.getMessage()); }}}}}

Cours de base Java / Renaud Zigmann / Copyright 2000 XSALTO sarl                     97

Cours de base Java / Renaud Zigmann / Copyright 2000 XSALTO sarl                     98

JDBC

JDBC: Warnings

Procédures stockées

?CallableStatement est dérivée de PreparedStatement

CallableStatement cs = con.prepareCall("{call SHOW_SUPPLIERS}"); ResultSet rs = cs.executeQuery();

Exceptions

?Traiter les exceptions au chargement du driver et à l'exécution de code.

try

{

// Code that could generate an exception goes here.

// If an exception is generated, the catch block below

// will print out information about it.

} catch(SQLException ex)

{

.println("SQLException: " + ex.getMessage());

}  try  {

Class.forName("myDriverClassName");

} catch(.ClassNotFoundException e) { .print("ClassNotFoundException: ");

.println(e.getMessage()); }

?Récupérer l'état de SQL

try {

// Code that could generate an exception goes here.

// If an exception is generated, the catch block below

// will print out information about it.

} catch(SQLException ex) {

.println("\n--- SQLException caught ---\n"); while (ex != null) {

.println("Message: "

+ ex.getMessage ());

.println("SQLState: " + ex.getSQLState ());

.println("ErrorCode: "

+ ex.getErrorCode ());

ex = ex.getNextException(); .println("");

}

}

Traiter les warnings

?Permet de traîter des erreurs mineurs. Exemple: DataTruncation est une classe dérivée de SQLWarning

?Exemple

Statement stmt = con.createStatement();

ResultSet rs = stmt.executeQuery("select COF_NAME from COFFEES"); while (()) {

String coffeeName = rs.getString("COF_NAME");

.println("Coffees available at the Coffee Break: 

");

.println(" " + coffeeName); SQLWarning warning = stmt.getWarnings(); if (warning != null) {

.println("\n---Warning---\n");

while (warning != null) {

.println("Message: "

+ warning.getMessage());

.println("SQLState: "

+ warning.getSQLState());

.print("Vendor error code: ");

.println(warning.getErrorCode()); .println("");

warning = warning.getNextWarning(); }

}

SQLWarning warn = rs.getWarnings(); if (warn != null) {

.println("\n---Warning---\n"); while (warn != null) {

.println("Message: "

+ warn.getMessage());

.println("SQLState: "

+ warn.getSQLState());

.print("Vendor error code: ");

.println(warn.getErrorCode()); .println(""); warn = warn.getNextWarning(); }

}

}

            Cours de base Java / Renaud Zigmann / Copyright 2000 XSALTO sarl                     99

            Cours de base Java / Renaud Zigmann / Copyright 2000 XSALTO sarl                   100

Java Beans

Java Beans

Ecriture de composants

?"Self-contained"

?exposent leurs propriétés, événements, etc car ils adhèrent à un schéma particulier (design pattern).

Concepts

?les outils visuels peuvent découvrir l'interface

?les propriétés des javabeans peuvent être modifiés au moment de la conception d'interface

?les propriétés peuvent être changées au moment de la conception

?utilisation d'événements pour communiquer

?persistence

Le BDK permet de le développement de Beans

?beanbox

?Répertorie les beans utilisables à partir du répertoire beans

?Permet de définir les interactions entre beans

?Possibilité de changement des propriétés et de sérialisation

              Cours de base Java / Renaud Zigmann / Copyright 2000 XSALTO sarl                   101

              Cours de base Java / Renaud Zigmann / Copyright 2000 XSALTO sarl                   102

JavaBean

JavaBeans

?Code simplifié:

import .*; import .Serializable;

public class SimpleBean extends Canvas implements Serializable

{

//Constructor sets inherited properties public SimpleBean(){ setSize(60,40);

setBackground();

}

}

Compilation: javac

Création du fichier manifest:

Manifest-Version: 1.0

Name: SimpleBean.class Java-Bean: True

Création du fichier jar jar cfm SimpleBean.class

Introspection java sun.beanbox.BeanBoxFrame >

Propriétés

?getXXX et setXXX

public Color getColor() { } public void setColor(Color c) { } import .*; import .Serializable;

public class SimpleBean extends Canvas implements Serializable { private Color color = Color.green; //Constructor sets inherited properties public SimpleBean(){ setSize(60,40);

setBackground();

}

public void setColor(Color newColor){ color = newColor; repaint(); }

public Color getColor(){ return color; } public void paint(Graphics g) {

g.setColor(color);

g.fillRect(20, 5, 20, 30);

}

}

Propriétés liées

?Un objet souhaite être tenu au courant des changements de propriété d'un autre objet.

?Evenement déclenché après la modification

private PropertyChangeSupport changes = new PropertyChangeSupport(this);

public void addPropertyChangeListener(

PropertyChangeListener l)

{ changes.addPropertyChangeListener(l);

}

public void removePropertyChangeListener(

PropertyChangeListener l)

{ changes.removePropertyChangeListener(l);

}

public void setLabel(String newLabel) {

String oldLabel = label; label = newLabel; sizeToFit();

changes.firePropertyChange("label", oldLabel, newLabel); }

?Implémentation du listener

OurButton button = new OurButton();

PropertyChangeAdapter adapter = new PropertyChangeAdapter();

button.addPropertyChangeListener(adapter); class PropertyChangeAdapter implements PropertyChangeListener

{ public void propertyChange(PropertyChangeEvent e)

{ reporter.reportChange(e);

}

}

Propriétés contraintes

Propriétés indexées

Evénements déclenchables

            Cours de base Java / Renaud Zigmann / Copyright 2000 XSALTO sarl                 103

            Cours de base Java / Renaud Zigmann / Copyright 2000 XSALTO sarl                   104