1.3. Un programme minimal en Java



Plan de ce chapitre:   ...........retour au plan général

1.Une classe suffit

1.1 Syntaxe d'une classe exécutable
1.2 Exemples d'applications à une classe


2. Les méthodes sont des fonctions

    2.1 Méthode élémentaire de classe
    2.2 Déclaration d'une méthode
    2.3 Appel d'une méthode


3.Transmission des paramètres en Java

    3.1 Compatibilité des types des paramètres
    3.2 Les deux modes de transmission des paramètres
    3.3 Les retours de résultat de méthode-fonction


4. Visibilité des variables

    4.1 Visibilité de bloc
    4.2 Variables dans une classe, dans une méthode
    4.3 Variables dans un bloc autre qu'une classe ou une méthode


Avant d'utiliser les possibilités offertes par les classes et les objets en Java, apprenons à utiliser et exécuter des applications simples Java ne nécessitant pas la construction de nouveaux objets, ni de navigateur pour s'exécuter.

Comme Java est un langage orienté objet, un programme Java est composé de plusieurs classes, nous nous limiterons à une seule classe.
 

1.Une classe suffit
 

On peut très grossièrement assimiler un programme Java ne possédant qu'une seule classe, à un programme principal classique d'un langage de programmation algorithmique.
 


 

1.1 Syntaxe d'une classe exécutable

Exemple1 de classe minimale :
class Exemple1 {  }

Cette classe ne fait rien et ne produit rien.

Comme en fait, une classe quelconque peut s'exécuter toute seule à condition qu'elle possède dans ses déclarations internes la méthode main qui sert à lancer l'exécution de la classe (fonctionnement semblable au lancement du programme principal).

Exemple2 de squelette d'une classe minimale exécutable :
class Exemple2
{
    static void main(String[ ] args)
   { // c'est ici que vous écrivez votre programme principal
    }
}

Exemple3 trivial d'une classe minimale exécutable :
class Exemple3
{
    static void main(String[ ] args)
   { System.out.println("Bonjour !");
    }
}

 

1.2 Exemples d'applications à une classe

Nous reprenons deux exemples de programme utilisant la boucle for, déjà donnés au chapitre sur les instructions, cette fois-ci nous les réécrirons sous la forme d'une application exécutable.

Exemple1
class Application1
{
   static void main(String[ ] args)
   { /* inverse d'une suite de caractère dans un tableau par
       permutation des deux extrêmes */
    char [ ] Tablecar ={'a','b','c','d','e','f'} ;
    int i, j ;
    System.out.println("tableau avant : " + String.valueOf(Tablecar));
    for ( i = 0 , j = 5 ;  i<j ; i++ , j-- )
     { char car ;
       car = Tablecar[i];
       Tablecar[i ]= Tablecar[j];
       Tablecar[j] = car;
     }
    System.out.println("tableau après : " + String.valueOf(Tablecar));
  }
}

Il est impératif de sauvegarder la classe dans un fichier qui porte le même nom (majuscules et minuscules inclues) ici "Application1.java". Lorsque l'on demande la compilation (production du bytecode) de ce fichier source  "Application1.java" le fichier cible produit en bytecode se dénomme "Application1.class", il est alors prêt à être interprété par une machine virtuelle java.

Le résultat de l'exécution de ce programme est le suivant :
tableau avant : abcdef
tableau après : fedcba

Exemple2
class Application2
{
   static void main(String[ ] args)
   { // recherche séquentielle dans un tableau
     int [ ] table= {12,-5,7,8,-6,6,4,78,2};
     int elt = 4, i ;
     for ( i = 0 ; i<8 ; i++ )
       if (elt= =table[i]) break ;
     if (i = = 8) System.out.println("valeur : "+elt+" pas trouvée.");
     else System.out.println("valeur : "+elt+" trouvée au rang :"+i);
   }
}

Après avoir sauvegardé la classe dans un fichier qui porte le même nom (majuscules et minuscules inclues) ici "Application2.java", la compilation de ce fichier "Application2.java" produit le fichier "Application2.class" prêt à être interprété par notre machine virtuelle java.

Le résultat de l'exécution de ce programme est le suivant :
valeur : 4 trouvée au rang :6

Conseil de travail :
reprenez tous les exemples simples du chapitre sur les instructions de boucle et le switch en les intégrant dans une seule classe (comme nous venons de le faire avec les deux exemples précédents) et exécutez votre programme.

 
 

2. Les méthodes sont des fonctions
 

Les méthodes ou bien fonctions représentent une encapsulation des instructions qui déterminent le fonctionnement d'une classe. Sans méthodes pour agir une classe ne fait rien de particulier, dans ce cas elle ne fait que contenir des attributs.
 

2.1 Méthode élémentaire de classe

Bien que Java distingue deux sortes de méthodes : les méthodes de classe et les méthodes d'instance, pour l'instant dans cette première partie nous décidons à titre pédagogique et simplificateur de n'utiliser que les méthodes de classe, le chapitre sur Java et la programmation orientée objet apportera les compléments adéquats.
 
Une méthode de classe commence obligatoirement par le mot clef    static.

Donc par la suite dans ce document lorsque nous emploierons le mot méthode sans autre adjectif, il s'agira d'une méthode de classe, comme nos applications ne possèdent qu'une seule classe, nous pouvons assimiler ces méthodes aux fonctions de l'application et ainsi retrouver une utilisation classique de Java en mode appplication.
 
 

2.2 Déclaration d'une méthode

La notion de fonction en Java est semblable à celle du C et  à Delphi, elle comporte une en-tête avec des paramètres formels et un corps de fonction ou de méthode qui contient les instructions de la méthode qui seront exécutés lors de son appel. La déclaration et l'implémentation doivent être consécutives comme l'indique la syntaxe ci-dessous :

Syntaxe :

corps de fonction :

Nous dénommons en-tête de fonction la partie suivante :
<qualificateurs><type du résultat><nom de fonction> (<liste paramètres formels>)

Sémantique :


Exemples d'en-tête de méthodes sans paramètres en Java
int calculer( ){.....} renvoie un entier de type int
boolean tester( ){.....} renvoie un entier de type boolean
void uncalcul( ){.....} procédure ne renvoyant rien

Exemples d'en-tête de méthodes avec paramètres en Java
int calculer(byte a, byte b, int x )
{.....}
fonction à 3 paramètres
boolean tester( int k)
{.....}
fonction à 1 paramètre
void uncalcul(int x, int y, int z )
{.....}
procédure à 3 paramètres

 

2.3 Appel d'une méthode

L'appel de méthode en Java s'effectue très classiquement avec des paramètres effectifs dont le nombre doit obligatoirement être le même que celui des paramètres formels et le type doit être soit le même, soit un type compatible ne nécessitant pas de transtypage.

Exemple d'appel de méthode-procédure sans paramètres en Java
class Application3
     {
        static void main(String[ ] args)
        { 
               afficher( );
        }
       static void afficher( )
      {
          System.out.println("Bonjour");
       }
     }
Appel de la méthode afficher

Exemple d'appel de méthode-procédure avec paramètres de même type en Java
class Application4
{  static void main(String[ ] args)
     { // recherche séquentielle dans un tableau
        int [ ] table= {12,-5,7,8,-6,6,4,78,2};
        long elt = 4;
        int i ;
        for ( i = 0 ; i<=8 ; i++ )
        if (elt= =table[i]) break ;
        afficher(i,elt);
      }
   static void afficher (int rang , long val)
   { if (rang == 8)
        System.out.println("valeur : "+val+" pas trouvée.");
      else
        System.out.println("valeur : "+val+" trouvée au rang :"+ rang);
   }
}
Appel de la méthode afficher
afficher(i,elt);
Les deux paramètres effectifs "i" et "elt"  sont du même type que le paramètre formel associé. 

- Le paramètre effectif  "i" est associé au paramètre formel rang.

- Le paramètre effectif "elt" est associé au paramètre formel val.


 
 
 

3. Transmission des paramètres en Java

Rappelons tout d'abord quelques principes de base :

Dans tous les langages possédant la notion de sous-programme (ou fonction ou procédure), il se pose une question à savoir, les paramètres formels décrits lors de la déclaration d'un sous-programme ne sont que des variables muettes servant à expliquer le fonctionnement du sous-programme sur des futures variables lorsque le sous-programme s'exécutera effectivement.

La démarche en informatique est semblable à celle qui, en mathématiques, consiste à écrire  la fonction f(x) = 3*x - 7, dans laquelle x  alors une variable muette indiquant comment f est calculée : en informatique elle joue le rôle du paramètre formel. Lorsque l'on veut obtenir une valeur effective de la fonction mathématique f, par exemple pour x=2, on écrit f(2) et l'on calcule f(2)=3*2 - 7 = -1. En informatique on "passera" un paramètre effectif dont la valeur vaut 2 à la fonction. D'une manière générale, en informatique, il y a un sous-programme appelant et un sous-programme appelé par le sous-programme appelant.
 
 

3.1 Compatibilité des types des paramètres

Resituons la compatibilité des types entier et réel en Java. Un moyen mémotechnique pour retenir cette compatibilité est indiqué dans les figures ci-dessous, par la taille en nombre décroissant de bits de chaque type que l'on peut mémoriser sous la forme "qui peut le plus peut le moins" ou bien un type à n bits accueillera un sous-type à p bits, si p est inférieur à n.
 
Les types entiers compatibles :

Les types réels compatibles :


 
 
 
 

 

Exemple d'appel de la même  méthode-procédure avec paramètres de type compatibles en Java
class Application5
{  static void main(String[ ] args)
     { // recherche séquentielle dans un tableau
        int [ ] table= {12,-5,7,8,-6,6,4,78,2};
        byte elt = 4;
        short i ;
        for ( i = 0 ; i<8 ; i++ )
        if (elt= =table[i]) break ;
        afficher(i,elt);
      }
   static void afficher (int rang , long val)
   { if (rang == 8)
        System.out.println("valeur : "+val+" pas trouvée.");
      else
        System.out.println("valeur : "+val+" trouvée au rang :"+ rang);
   }
}
Appel de la méthode afficher
afficher(i,elt);
Les deux paramètres effectifs "i" et "elt"  sont d'un type compatible avec celui du paramètre formel associé. 

- Le paramètre effectif  "i" est associé au paramètre formel rang.(short = entier signé sur 16 bits et int = entier signé sur 32 bits)

- Le paramètre effectif "elt" est associé au paramètre formel val.(byte = entier signé sur 8 bits et long = entier signé sur 64 bits)
 


 

3.2 Les deux modes de transmission des paramètres

Un paramètre effectif transmis au sous-programme appelé est en fait un moyen d’utiliser ou d’accéder à une information appartenant au bloc appelant (le bloc appelé peut être le même que le bloc appelant, il s’agit alors de récursivité).

En Java, il existe deux modes de transmission (ou de passage) des paramètres (semblables à Delphi).

Le passage par valeur uniquement réservé à tous les types élémentaires (int, byte, short, long, boolean, double, float, char).


 

Le passage par référence uniquement réservé à tous les types objets.

Remarque :
Le choix de passage selon les types élimine les inconvénients dûs à l'encombrement mémoire et à la lenteur de recopie de la valeur du paramètre par exemple dans un passage par valeur, car nous verrons plus loin que les tableaux en Java sont des objets et qu'ils sont donc passés par référence.

 
 

3.3 Les retours de résultat de méthode-fonction

Les méthodes en Java peuvent renvoyer un résultat de n'importe quel type élémentaire ou objet. Bien qu'une méthode ne puisse renvoyer qu'un seul résultat, l'utilisation du passage par référence d'objets permet aussi d'utiliser les paramètres de type objet d'une méthode comme des variables d'entrée/sortie.

En Java le retour de résultat est passé grâce au mot clef return placé n'importe où dans le corps de la méthode.

Syntaxe :

Sémantique :


Exemple la fonction f(x)=3x-7
class Application6
{  static void main(String[ ] args)
     { // ...
        int x , y ;
        x = 4 ;
        y = f(5) ;
        y = f(x) ;
        System.out.println("f(x)="+ f(x) );
        System.out.println("f(5)="+ f(5) );
      }
   static int f (int x )
   {  return 3*x-7;
   }
}
Appel de la méthode f
f ( 5 ) ;
ou bien
f ( x ) ;
Dans les deux cas la valeur 5 ou la valeur 4 de x est recopiée dans la zone de pile de la machine virtuelle java.

Exemple de méthode-procédure
class Application7
{  static void main(String[ ] args)
    
        int a = 0  ;
        procedure ( a );
        procedure ( a+4 );
      }
   static void procedure(int x)
    {
      if (x = = 0)
      { System.out.println("avant return");
          return ;
       }
      System.out.println("après return");
    }
}
Appel de la méthode procedure

Dans le cas du premier appel (x ==0) est true donc ce sont les instructions:
System.out.println("avant return");
return ;
qui sont exécutées, puis interruption de la méthode.

Dans le cas du second appel (x ==0) est false c'est donc l'instruction:
System.out.println("avant return");
qui est exécutée, puis fin normale de la méthode.
 


 
 

4. Visibilité des variables

Le principe de base est que les variables en Java sont visibles (donc utilisables) dans le bloc dans lequel elles ont été définies.
 

4.1 Visibilité de bloc

Java est un langage à structure de blocs ( comme pascal et C ) dont le principe général de visibilité est :
Toute variable déclarée dans un bloc est visible dans ce bloc et dans tous les blocs imbriqués dans ce bloc.

En java les blocs sont constitués par :
  • les classes,
  • les méthodes,
  • les instructions composées,
  • les corps de boucles,
  • les try...catch

Le masquage des variables n'existe que pour les variables déclarées dans des méthodes :
Il est interdit de redéfinir une variable déjà déclarée dans une méthode soit :
  • comme paramètre de la méthode,
  • comme variable locale à la méthode,
  • dans un bloc inclus dans la méthode.
Il est possible de redéfinir  une variable déjà déclarée dans une classe.

 
 

4.2 Variables dans une classe, dans une méthode

Les variables définies (déclarées, et/ou initialisées) dans une classe sont accessibles à toutes les méthodes de la classe, la visibilité peut être modifiée par les qualificateurs public ou private que nous verrons au chapitre POO.

Exemple de visibilité dans une classe
class ExempleVisible1 {
  int a = 10;

  int g (int x )
  {  return  3*x-a
  }

  int f (int x, int a )
  {  return 3*x-a
  }

}

 La variable "a" définie dans int a =10;  :

- Est une variable de la classe ExempleVisible.

- Elle est visible dans la méthode g et dans la méthode f. C'est elle qui est utilisée dans la méthode g pour évaluer l'expression 3*x-a.

- Dans la méthode f, elle est masquée par le paramètre du même nom qui est utilisé pour évaluer l'expression 3*x-a.
 

Contrairement à ce que nous avions signalé plus haut nous n'avons pas présenté un exemple fonctionnant sur des méthodes de classes (qui doivent obligatoirement être précédées du mot clef static), mais sur des méthodes d'instances dont nous verrons le sens plus loin en POO. Remarquons avant de présenter le même exemple cette fois-ci sur des méthodes de classes, que quelque soit le genre de méthode la visibilité des variables est identique.

Exemple identique sur des méthodes de classe
class ExempleVisible2 {
  static int a = 10;

  static int g (int x )
  {  return  3*x-a
  }

  static int f (int x, int a )
  {  return 3*x-a
  }

}

 La variable "a" définie dans static int a =10;  :

- Est une variable de la classe ExempleVisible.

- Elle est visible dans la méthode g et dans la méthode f. C'est elle qui est utilisée dans la méthode g pour évaluer l'expression 3*x-a.

- Dans la méthode f, elle est masquée par le paramètre du même nom qui est utilisé pour évaluer l'expression 3*x-a.
 

Les variables définies dans une méthode (de classe ou d'instance) subissent les règles classiques de la visibilité du bloc dans lequel elles sont définies :
 
Elles sont visibles dans toute la méthode et dans tous les blocs imbriqués dans cette méthode et seulement à ce niveau (les autres méthodes de la classe ne les voient pas), c'est pourquoi on emploie aussi le terme de variables locales à la méthode.

Reprenons l'exemple précédent en y adjoignant des variables locales aux deux méthodes f et g.

Exemple de variables locales
class ExempleVisible3 {
  static int a = 10;

  static int g (int x )
  {  char car = 't'; 
      long a = 123456;
       ....
      return  3*x-a
  }

  static int f (int x, int a )
  {  char car ='u';
       ....
      return 3*x-a
  }

}

 La variable de classe "a" définie dans static int a = 10; est masquée dans les deux méthodes f et g.

Dans la méthode g, c'est la variable locale longa = 123456 qui masque la variable de classe static int a. char car ='t'; est une variable locale à la méthode g.

- Dans la méthode f, char car ='u'; est une variable locale à la méthode f, le paramètre inta masque la variable de classe static int a.

Les variables locales char car n'existent que dans la méthode où elles sont définies, les variables "car" de f et celle de g n'ont aucun rapport entre elles, bien que portant le même nom.
 


 
 

4.3 Variables dans un bloc autre qu'une classe ou une méthode

Les variables définies dans des blocs du genre instructions composées, boucles, try..catch ne sont visibles que dans le bloc et ses sous-blocs imbriqués, dans lequel elle sont définies.

Toutefois attention aux redéfinitions de variables locales. Les blocs du genre instructions composées, boucles, try..catch ne sont utilisés qu'à l'intérieur du corps d'une méthode (ce sont les actions qui dirigent le fonctionnement de la méthode), les variables définies dans de tels blocs sont automatiquement considérées par Java comme des variables locales à la méthode. Tout en respectant à l'intérieur d'une méthode le principe de visibilité de bloc, Java n'accepte pas le masquage de variable à l'intérieur des blocs imbriqués.

Nous donnons des exemples de cette visibilité :

Exemple correct de variables locales
class ExempleVisible4 {

static int a = 10, b = 2;

  static int f (int x )

  {  char car = 't'; 

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

         if (a < 7)
          {int b = 8;
           b = 5-a+i*b;
          }

       else b = 5-a+i*b;
       }

       return  3*x-a+b
  }
}
 


La variable de classe "a" définie dans static int a = 10; est masquée dans la méthode f  dans le bloc imbriqué for.

La variable de classe "b" définie dans static int b = 2; est masquée dans la méthode f  dans le bloc imbriqué if.

Dans l'instruction { intb = 8; b = 5-a+i*b; } , c'est la variable b interne à ce bloc qui est utilisée car elle masque la variable b de la classe.

Dans l'instruction else b = 5-a+i*b; , c'est la variable b de la classe qui est utilisée (car la variable int b = 8 n'est plus visible ici) .
 

Exemple de variables locales générant une erreur
class ExempleVisible5 {

static int a = 10, b = 2;

  static int f (int x )

  {  char car = 't'; 

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

         if (a < 7)
          {int b = 8, a = 9;
           b = 5-a+i*b;
          }

       else b = 5-a+i*b;
       }

       return  3*x-a+b
  }
}
 

Toutes les remarques précédentes restent valides puisque l'exemple ci-contre est quasiment identique au précédent. Nous avons seulement rajouté dans le bloc if la définition d'une nouvelle variable interne a à ce bloc.

Java produit une erreur de compilation int b = 8, a = 9; sur la variable a, en indiquant que c'est une redéfinition de variable à l'intérieur de la méthode f, car nous avions déjà défini une variable a  ({ int a=7;...) dans le bloc englobant for {...}.
 

Remarquons que le principe de visiblité des variables adopté en Java est identique au principe inclus dans tous les langages à structures de bloc y compris pour le masquage, s'y rajoute en Java uniquement l'interdiction de la redéfinition à l'intérieur d'une même méthode (semblable en fait, à l'interdiction de redéclaration sous le même nom, de variables locales à un bloc).