Page précédenteIndexPage suivante

CHAPITRE 2 - Introduction au langage Java

Il sera ici question de placer Java dans le cadre des langages à objets, de rappeler succinctement son historique et de donner ses spécificités. Dans un second temps, nous étudierons les règles et la syntaxe du langage. Les specifications exhaustives du langage Java sont regroupées dans le document The Java language specification que vous trouverez sur le site de Sun Microsystems.


2.1- Historique du langage et environnement industriel

Java est né dans les laboratoires de Sun Microsystems en 1990 avec le JDK 1.0 (Java Development Toolkit ). C'est donc un langage propriétaire mais très ouvert car les sources du langage sont fournies par SUN et ont permis à d'autres sociétés comme IBM de fournir leurs propres kits de développement. Sun fourni depuis début 2000 le JDK 1.3 appelée également Java2 depuis la version 1.2 en 1998.

Cependant si ces kits de développement contiennent principalement un compilateur : javac , un interpréteur : java et un visualiseur d'applet: l'AppletViewer, ils ne comprennent pas d'interface de programmation et il faut développer avec un simple éditeur de texte. C'est pourquoi des éditeurs de logiciels ont produit des éditeurs performants: Borland avec JBuilder, Sun Microsystems avec Forté...

De leur coté, les applets sont des programmes fonctionnant avec un navigateur Internet. Chaque navigateur possède une JVM ( Java Virtual Machine, voir plus loin) qui fait partie du JRE ( Java Runtime Environment ). C'est cette JVM incluse dans le navigateur qui interprète et exécute l'applet.



2.2- Portabilité du langage / JVM

La source d'un programme C est de type .c et après compilation, nous obtenons un fichier exécutable. Ce runtime est directement exécutable par le processeur pour lequel il a été compilé. La source d'un programme Java est un fichier de type .java et après compilation nous obtenons une classe ( .class ).

Comme en C, il existe des compilateurs pour quasiment tous les types de machines ( PC Intel, PPC, Sparc...) et d'OS ( AIX, Linux, HP-UX, Solaris, MacOS, Microsoft Windows...) mais le fichier .class n'est pas directement exécutable par le processeur. En réalité, cette classe est identique sur toutes les machines et contient du byte code c'est à dire du code exécutable par un processeur Java. Le processeur virtuel ( ou JVM: Java Virtual Machine ) est le programme 'java' qui exécute le byte code à la volée.

Cela permet à Java d'obtenir une portabilité de la source ( comme en C ) mais surtout du code binaire. Il est ainsi possible d'exécuter indifféremment une applet sur Internet selon que l'on navigue avec un Mac ou un PC.

Remarque: Il existe également des processeurs physiques spécialisés dans l'exécution du byte code. Dans ce cas, la majorité des instructions Java sont câblées en dur. Il existe plusieurs constructeurs fabriquant ce type de processeurs qui permettent alors d'exécuter le code java jusqu'à vingt fois plus vite qu'avec une machine virtuelle logicielle.



2.3- Domaines classiques d'utilisation de Java

Aujourd'hui, on trouve principalement le langage Java dans les domaines suivants :



2.4- Applications et applets

On trouve en java deux types de programmes:



2.5- Un langage sûr et stable



Dans les paragraphes suivants, nous nous attacherons à décrire les règles et syntaxes du langage.



2.6- Notion de variable primitive

Contrairement à certains langages 100% objet dans lesquels tout est objet, Java offre la possibilité d'utiliser des types primitifs.

Il est possible de manipuler des objets mais dans de nombreux cas, il est préférable d'utiliser des variables "classiques" comme en C. Ce sont les types primitifs dont les principaux sont int, float, boolean, char.

Ce ne sont pas des objets. Ils ne sont pas instanciés mais simplement déclarés.

La transmission de ces variables se fait par valeur:

int a=3;
methodeQuelconque(a);

La méthode "methodeQuelconque" reçoit la valeur "3" et non l'adresse mémoire de la variable a.

Principaux primitifs:

double d = 1.25;
et
float f = 1.25f;



Taille des primitifs en Java:

Type

Taille ( bits )

long

64bits

int

32bits

boolean

8bits (faux 1 bit)

byte

8 bits 

float

32 bits 

double

64 bits 





2.7- Syntaxe du langage

Les déclarations de variables

Les variables peuvent être déclarées n'importe où dans le code MAIS restent locales aux accolades courantes.

Exemple:

if (i == 1){
   int j=2;
   System.out.println(j); //affiche '2'
}

System.out.println(j);//Erreur, j est inconnu

Les principaux opérateurs arithmétiques et logiques

Opérateur

Description

+

Addition

-

Soustraction

*

Produit

/

Quotient de division euclidienne

%

Reste de division euclidienne

&&

"et" conditionnel

||

"ou" conditionnel

=

Affectation

==

Comparaison de valeurs

< <=

Inférieur - inférieur ou égal

> >=

Supérieur - supérieur ou égal

!=

Différent de

!

"non" logique

~

Complément à deux ( inverse le signe )

&

"et" arithmetique

|

"ou" arithmetique

^

"ou" exclusif (xor) arithmetique

<<

Décalage à gauche

>>

Décalage à droite signé

>>>

Décalage à droite non signé



Les boucles et conditions

Ces traitements sont similaires au langage C. Succinctement:

if ( i ==0 ){
    i++;
}

while (i<5){
   i++ ;
}

do{
    i++;
}while (i <5);

L'instruction continue; permet de stopper l'itération en cours et d'en commencer une nouvelle:

int i=0;
while (true){
   if (i ==4 ) {
       continue;

   }
    i++ ;  //si i vaut 4, cette ligne n'est pas exécutée
}

L'instruction break; permet de stopper l'itération et de sortir de la boucle

while (i<5){
   if (i==4) {
      break;
   }
   i++; // si i vaut 4, cette instruction n'est pas exécutée

}
i=5; // après l'instruction break, nous passons directement à cette ligne

for (int i=0;i<5;i++){
}

switch(i){
   case 1:           // case '1': pour un char
      j=1;
      break;
    case 2:
      j=2;

    default:
      j=0;

}

Comme pour les while ou autres instructions conditionelles, l'instruction break permet de forcer la sortie du switch.

Variables et propriétés

Il est existe en java deux types de variables:

Définition d'une classe

La déclaration d'une classe doit respecter la syntaxe suivante:

class MaClasse{
}


Constructeurs

Un constructeur est une partie de code appelée lors de l'instanciation de la classe ( new ) et permettant d'initialiser l'objet. Une même classe peut posséder plusieurs constructeurs possédant des arguments différents. Un constructeur ne renvoie pas de valeur et possède le nom de sa classe. Typiquement, le constructeur peut initialiser ses variables d'instances.

Exemple:

class Voiture{
 int iAge;
   Voiture(int iAge){
      this.iAge=iAge;
   }
   Voiture(int iAge,int iKilometrage){
      ...
   }
}



Opérateurs this et super

Exemple:

class Voiture extends Vehicule{
    int iAge; // variable d'instance
    int iKilometrage;
    Voiture(int iAge){
        this(iAge,10000);
    }
    Voiture (int iAge,int iKilometrage){
        super(); //appel du constructeur vide de la classe mère, on pourrait appeler un autre constructeur s'il existe.
        this.iAge=iAge;   //this.iAge désigne la propriété iAge de la classe. Si on ne précise pas 'this', iAge désigne la variable locale iAge de cette méthode qui masque la propriété.
        this.iKilometrage=iKilometrage;
    }
}

null

Une référence ne pointant sur aucun objet instancié vaut une valeur particulière: null. Toute tentative de manipulation sur cette référence provoquera une erreur de type NullPointerException.

Instancier un objet

Le mot-clé new est utilisé pour instancier un objet. Par exemple:

Voiture une106;
une106=new Voiture();

Obtenir le type d'un objet

Le mot-clé instanceof permet de vérifier qu'un objet est bien d'un type donné. Par exemple,

Voiture une106=new Voiture();
une106 instanceof Voiture    renverra true

Attention, instanceof n'est pas une méthode mais un mot-clé du langage.

La classe d'instanciation d'objet est donné par la méthode getClass() de la classe Object:

une106.getClass();   renvoie Voiture.


Comparaison d'objets

Il ne faut pas confondre référence sur objet et contenu de l'objet. Par exemple:

Integer i1=new Integer(1);
Integer i2=new Integer(1);
i1 == i2     renverra false car l'opérateur ' == ' compare les références

Mais i1.equals(i2); renverra true car la méthode equals() compare le contenu des objets et non les objets eux-mêmes.

Héritage

Pour spécifier qu'une classe dérive d'une autre classe, il faut utiliser l'opérateur extends lors de la déclaration de la classe:

class Voiture extends Vehicule{
}

La classe mère commune à toutes les autres classes est la classe Object. Tous les objets dérivent d'Object et les déclarations suivantes sont identiques:

class Voiture{
}

class Voiture extends Object{
}

Consulter le chapitre sur l'héritage pour davantage d'informations.


Transtypage / cast

Pour transtyper un objet d'un type vers un autre type, la notation est la suivante:

Integer i=(Integer)j;

Nous détaillerons le mécanisme de transtypage objet au chapitre sur l'héritage.

En ce qui concerne les types primitifs, la plupart des transtypages sont acceptés si on transtype un primitif d'un type donné vers un type de taille supérieure ou égale. Notons que la syntaxe est identique à celle du cast objet même si la signification est totalement différente.

Exemple:

int i=10;
long l=(long)i; // ok

Mais pas :

double d=10.0;
float f=(float)d; //erreur de compilation



2.8- Notion de propriété ou méthodes statiques

Lorsqu'une variable ou une méthode est statique, elle ne dépend pas de l'instance particulière auquel elle appartient mais elle possède la même valeur ou le même comportement quelque soit l'instance concernée.

Propriétés statiques

Les objets possèdent propriétés et méthodes. Cependant, certaines propriétés ou attributs sont définies statiques. Par exemple, si iNbRoues est une propriété de la classe Voiture, et que l'on désire s'assurer que toutes les instances de Voiture possèdent le même nombre de roues, on note:

class Voiture{
   static int iNbRoues=4;

}

Dès lors, une modification de cette valeur sur une instance de Voiture est répercutée automatiquement sur toutes les autres instances de cette classe. Si on instancie une Voiture nommé v1:
Voiture v1=new Voiture();

Puis une autre Voiture nommé v2:
Voiture v2=new Voiture();

Si l'on modifie iNbRoues de v1 alors la propriété iNbRoues de v2 prendra cette même valeur. En cela, nous voyons que iNbRoues ne dépend pas de l'instance de Voiture auquel elle est rattachée mais de façon générale à la classe Voiture : c'est une variable de classe. Il est possible d'accéder à une variable de classe par la syntaxe <Nom de la classe>.<Nom de la variable de classe>. Ainsi, il est préférable d'appeler cette propriété par Voiture.iNbRoues plutôt que par v1.iNbRoues car nous rappelons ainsi que cette propriété est statique.


Méthodes statiques

Les méthodes peuvent également être statiques. On note:

static void maMethode(){
   ....

}

Par exemple, dans la classe Voiture , nous pouvons définir la méthode getNbRoues() comme étant statique. Cela signifie que la méthode n'utilise que des variables statiques de la classe et donc qu'elle ne dépend pas non plus de l'instance dans laquelle on se trouve mais uniquement de la classe. Il est alors préférable d'appeler cette méthode par Voiture.getNbRoues() que par v1.getNbRoues() car nous rappelons ainsi que cette méthode est statique. Une méthode statique ne peut en aucun cas appeler de méthodes non statiques et ne peut manipuler de variables d'instance. En revanche une méthode non statique peut utiliser les propriétés et méthodes statiques.

Constantes

Une constante de classe est une variable de classe ( donc une variable statique ) qui de plus est déclarée final c'est à dire non modifiable ni surchargeable par des classes filles. Exemple:

class Voiture{
   static final int iNbRoues=4;
}


2.9- Notion de polymorphisme paramétrique

Un objet peut comporter plusieurs méthodes de même nom mais possédant des arguments différents. Ce sont des méthodes polymorphiques. Nous en avons vu un exemple avec les constructeurs .

void rouler(Chemin monChemin){ } //la façon dont nous roulons ne dépend que du chemin
void rouler(Chemin monChemin ,Meteo mMaMeteo){ } //la façon dont nous roulons dépend du chemin et de la météo
void rouler(Chemin monChemin,Meteo mMaMeteo,Circulation cMaCirculation){ } //la façon dont nous roulons dépend du chemin, de la météo et de la circulation

A l'exécution, en fonction du nombre et/ou des types transmis, la JVM sait quelle méthode est concernée par cet appel.



2.10- Création d'une application autonome ou "stand-alone"

Une application autonome est une classe qui peut s'exécuter à travers la JVM. C'est une classe qui possède la méthode particulière main().

La méthode main doit être de la forme:

static void main(String[] sArgs){
   ....
}

La méthode doit absolument être statique et accepter en argument un tableau de chaîne de caractères qui sont les arguments du programme en ligne de commande. Une telle classe peut être lancée par la JVM, c'est à dire le programme 'java' .



2.11- le JDK ou comment exécuter ou compiler du code Java

Le JDK est le Java Development Kit. Il en existe plusieurs versions écrites par plusieurs sociétés dont principalement Sun Microsystems et IBM. Un JDK est composé de binaires permettant principalement d'exécuter un programme Java ou de le compiler. Il existe des JDK pour la plupart des processeurs et des OS.

'javac' est le compilateur java. Il compile un fichier .java en classe .class. Par exemple,

javac HelloWorld.java
Construira le fichier HelloWorld.class

Permet d'exécuter une classe possédant un main:

java HelloWorld

Attention aux confusions! le programme java lui-même est un binaire compilé pour un processeur et un système d'exploitation donné mais la classe HelloWorld quant à elle est interprétée et ne dépend donc pas de la plate-forme d'exécution. Il existe de nombreux autres outils dans le JDK dont : jar (outil de compression et d'archivage) , appletviewer (permet d'exécuter une applet sans navigateur), keytool ( gestion de signature des classes ), javadoc (gestion automatique de la documentation)...



2.12- Normes SUN d'écriture Java

La syntaxe des programmes Java est normalisée par Sun Microsystems et permet une standardisation absolument indispensable pour un langage orienté-objet. Une source ne respectant pas ces normes ( noter une classe en minuscule par exemple) devient rapidement très difficile à lire.

Indentation

L'indentation se fait ainsi:

Nous obtenons ainsi l'indentation suivante:

if (i == 0){
    i++;
}


Les noms de classes

[première lettre type en majuscule]+suite de mots dont chaque première lettre est en majuscule:

Integer, Objet, MaClasse, CeciEstUnNomDeClasseUnPeuLong...

Noms d'objets ou de variables primitives

[première lettre type en minuscule] + suite de mots dont chaque première lettre est en majuscule:

La première lettre ( en minuscule) sera de préférence la première lettre de sa classe pour connaître le type d'un objet sans chercher sa déclaration. Ceci est facultatif mais fort utile.

Integer iNbRoues;

int iComp;


Noms de méthodes

[première lettre type en minuscule]+suite de mots dont chaque première lettre est en majuscule:

maMethode(int iComp);

Les accesseurs ( méthodes renvoyant d'accéder à une variable d'instance de la classe ) seront notés :

Noms de package

Les packages sont des regroupements de classes. (cf. chapitre "Modificateurs d'accès et packages" ). Ils sont toujours tout en minuscule:

java.lang,    javax.swing  ...


Mots-clés Java

Les mots-clés comme return, instanceof, if, while, static, private, public, package, class... sont toujours en minuscule.


Constantes

Les constantes ( variables static final ) sont tout en majuscule et séparés d'underscore si elles sont constituées de plusieurs mots ( HTTP_ROOT...)


Underscore ( _ )

Les noms de méthodes, classes, objets, primitifs... constitués de plusieurs mots ne doivent pas contenir d'underscore ( sauf pour les constantes ) mais la séparation est faites par la majuscule de la première lettre du mot suivant.


Les commentaires

Comme en C, " // " pour la ligne en cours et " /* */ " pour un ensemble de lignes. Le code courant sera commenté avec " // " et " /* */ "  mais le code normalisé javadoc devra suivre la syntaxe javadoc comme " /** " et " **/ ". Ce code normalisé se trouvera en début des méthodes à documenter (cf. doc de Sun sur javadoc )

Accolades

De préférence, il faut poser des accolades même lorsque ce n'est pas nécessaire:

if (iComp==0){
   iComp=1;
}

au lieu de :

if (iComp= =0) iComp=1;

Cela permet d'éviter beaucoup d'erreurs.

return

return est un mot clé et non une méthode. Il vaut donc mieux écrire :

return i; ou return 2; ...

que

return(i) ou return(2); même si cette syntaxe est autorisée par le compilateur.



2.13 - Gestion de la mémoire

En Java, nul besoin de gérer la mémoire comme en C: la JVM gère elle-même les ressources de façon (presque) optimale. Un objet occupe une zone mémoire. Lorsque l'objet n'a plus aucune référence, il est supprimé de la mémoire par le Garbage Collector (GC). Voyons les différents états que peut prendre un objet:

1- Integer i; //on crée une référence sur un objet de type Integer, l'objet n'existe pas encore.

2- i=new Integer(); //l'objet est créé en mémoire.

3- Si au cours de l'exécution, l'objet n'est plus référencé ( par exemple, la référence i pointe vers un autre objet ), l'objet passe en attente de destruction.

4- L'objet est détruit par le garbage collector lorsqu'il dispose d'une plage de temps CPU, la mémoire est alors libérée.





TD 2 - Introduction pratique au langage Java

Exercice 1 - Premiers programmes en Java

Exercice 2 - Main utilisant d'autres classes


solutions





Page précédenteIndexPage suivante