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.
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.
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.
Aujourd'hui, on trouve principalement le langage Java dans les domaines suivants :
Les applications classiques ( server only ) s'exécutant sur un machine comme tout programme. Borland JBuilder en est un exemple. De nombreuses grosses applications sont développées en Java pour bénéficier de la portabilité: les éditeurs n'ont qu'une version à maintenir pour tous les processeurs et systèmes d'exploitation.
Les applets qui sont des programmes Java particuliers s'exécutant dans un navigateur web. Une applet est un programme Java inséré dans une page HTML téléchargée d'un serveur web et s'exécutant sur le poste client.
Le domaine du multimédia ( son, traitement de l'image ) qui tire avantage des API Java spécialisées.
Le domaine du réseau pour réaliser des programmes client-serveur de façon très simple ( ftp, http, pages HTML dynamiques via JSP... )
De gros progiciels possèdent un installeur unique écrit en Java pour ne maintenir qu'une version et qu'une documentation utilisateur. Par exemple, Oracle 8i est fourni avec l'Oracle Universal Installer en Java.
Programmes embarqués ( aéronautique, applications industrielles... )
On trouve en java deux types de programmes:
Les applications autonomes ( ou stand-alone ) exécutables à partir du système d'exploitation comme tout programme. Ces applications sont souvent graphiques et ne nécessitent pour fonctionner qu'une JVM.
Les applets, programmes Java spécialisés qui ne sont exécutables qu'au sein d'un navigateur( ou browser ) Internet tel Netscape ou Internet Explorer. Les applets permettent de "donner vie"à l'Internet en permettant d'insérer des programmes dans les pages Web alors que le langage HTML est statique.
Le code java étant interprété, les exceptions 'hard' de type division par zéro ne peuvent se produire car la JVM contrôle le code binaire généré. Il n'y a pas de plantage mais uniquement propagation d'exceptions de façon totalement contrôlable.
De la même façon, la JVM contrôle les actions du code pour interdire certaines actions comme une utilisation illégale de ressource.
Il n'y a pas de pointeurs en Java ni de gestion de la mémoire, ce qui élimine selon SUN de 50 à 70% des bugs potentiels.
Les objets peuvent être déclarés privés ou à accès restreint, ce qui limite l'utilisation de ces objets aux classes autorisées.
Dans les paragraphes suivants, nous nous attacherons à décrire les règles et syntaxes du langage.
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:
Les entiers: Le format par défaut d'un entier est l'int qui en Java est toujours codé sur 32bits ( de -231 à 231-1soit de-2147483648 à 2147483647).
Les flottants: float ou double. Attention! le format d'un flottant est par défaut le double et non le float comme en C, ce qui conduit à de nombreuses erreurs de compilation; il faut écrire:
double
d = 1.25;
et
float f = 1.25f;
Les booléens: boolean
Les caractères: char
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 |
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é |
Ces traitements sont similaires au langage C. Succinctement:
L'instruction if
if
( i ==0 ){
i++;
}
L'instruction while et do while
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
L'instruction for
for
(int i=0;i<5;i++){
}
L'instruction switch (manipule uniquement les entiers int et les caractères char )
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.
Il est existe en java deux types de variables:
Les variables d'instance ou de classe qui sont déclarées hors méthodes et sont alors des propriétés de l'objet ou de la classe. Les propriétés peuvent être des primitifs ou des objets. Les propriétés primitives sont automatiquement initialisées ( exemple : 0 pour les int ).
Les variables locales qui sont
déclarées à l'intérieur de méthodes.
Elles ne sont visibles que dans la méthode et peuvent être
des primitifs ou des objets. Les variables locales primitives ne
sont pas initialisées automatiquement et doivent
l'être avant utilisation.
La déclaration d'une classe doit respecter la syntaxe suivante:
class
MaClasse{
}
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){
...
}
}
L'opérateur this désigne l'objet courant.
this( [arguments] ) est un appel d'un constructeur ( appel valide uniquement dans un autre constructeur de la classe ).
L'opérateur super désigne la mère de l'objet.
super( [arguments]) est un appel du constructeur de la classe mère. Un appel d'un constructeur de la classe mère ne peut se faire qu'en première ligne d'un constructeur de la classe. Si rien n'est précisé, l'appel au constructeur vide super() de la classe mère est automatiquement ajouté à la compilation. Lorsqu'on instancie une classe dérivant d'une classe mère, le constructeur appelle toujours celui de la classe mère. Par défaut, il cherche à exécuter super(); et si ce constructeur particulier n'existe pas, il y a erreur à la compilation. Un constructeur peut appeler un autre constructeur ( this() ou super() ) mais il faut que cet appel soit la première ligne du constructeur.
Notons que l'appel à un constructeur ne se fait que par les mots this() et super() et non par le nom du constructeur. Enfin, il n'est pas possible d'appeler un constructeur ailleurs que dans un autre constructeur.
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;
}
}
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.
Le mot-clé new est utilisé pour instancier un objet. Par exemple:
Voiture
une106;
une106=new Voiture();
La première ligne ne fait que préciser que une106 est une référence sur un objet de type Voiture. Pour le moment, cette référence pointe sur null.
Le new crée l'objet en mémoire et exécute son constructeur. A la fin de la construction, new renvoie la référence du nouvel objet.
La référence une106 est affectée à la valeur renvoyée par le new; elle pointe bien maintenant sur la zone mémoire occupée par la nouvelle Voiture.
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.
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.
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.
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
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.
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.
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;
}
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.
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' .
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.
Programme javac
'javac' est le compilateur java. Il compile un fichier .java en classe .class. Par exemple,
javac
HelloWorld.java
Construira
le fichier HelloWorld.class
Programme java
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)...
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.
L'indentation se fait ainsi:
L'accolade de début se met à la fin de l'instruction précédente.
L'accolade de fin s'aligne au premier caractère de la ligne de début.
Les blancs sont des tabulations.
Nous obtenons ainsi l'indentation suivante:
if (i ==
0){
i++;
}
[première lettre type en majuscule]+suite de mots dont chaque première lettre est en majuscule:
Integer, Objet, MaClasse, CeciEstUnNomDeClasseUnPeuLong...
[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;
[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 :
get+[nom] pour accéder à une variable d'instance. Exemple: int getNbRoues()
set+[nom] pour modifier une variable d'instance. Exemple : setNbRoues(int i)
is+[nom] pour accéder à une variable de type boolean. Exemple : boolean isVoiture()
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 ...
Les mots-clés
comme return, instanceof, if, while,
static, private, public, package,
class... sont toujours en minuscule.
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...)
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.
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 )
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 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.
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.
Écrire un programme affichant "Hello World" sachant que l'instruction permettant d'afficher un chaîne à l'écran est: System.out.println("[chaîne]");
Écrire un programme affichant 10 fois "Hello World"
Écrire un programme affichant le premier argument donné en lançant la classe ( par exemple java Test A ou java Test B ).
Écrire un programme qui affiche tous les nombres premiers inférieurs à 100000 .
Écrire une classe Voiture possédant les propriétés iAge et iKilometrage ainsi que la méthode getKilometrage() qui renvoie le kilométrage courant du véhicule. La classe Voiture aura deux constructeurs: le premier prenant uniquement l'âge et l'autre l'âge et le kilométrage. Ces deux constructeurs n'auront pas de code redondant. Par défaut, le kilométrage sera de zéro km.
Écrire une classe Test ayant une méthode main et instanciant deux voitures. Affichez l'âge et le kilométrage de ces voitures.
Définir dans la classe Voiture un attribut primitif iNbRoues statique qui donne le nombre de roues.
Dans Test, fixer la valeur de iNbRoues d'une voiture à 4 et afficher la valeur iNbRoues des autres instances de Voiture. Conclusion?