Archive pour le mot-clef ‘java’

Niveaux d’accès et syntaxe Java

Lundi 1 février 2010

Enfin nous allons faire ce point tant attendu sur les niveaux d’accès en Java. Youpi !!!

Nous en profitons aussi pour faire un point syntaxe java.

La première partie de ce tutoriel vient en complément du tutoriel précédent sur la POO java. Dans le tutoriel précédent,vous avez pu voir comment déclarer des classes. Dans ce tutoriel, vous allez voir comment déclarer des attributs et des méthodes dans une classe et comment définir leurs niveaux de droit d’accès. En gros, comment écrire le corps d’une classe. Nous serons obligés pour être complet de présenter les règles d’écriture des “identifiers”. Ces mots qui vont vous permettre de nommer vos classes et vos variables. Nous ferons un tour des types primitifs en java, ça peut toujours servir. Puis pour être complet sur la syntaxe java, nous ferons un point sur les conditionnelles (if, while, for, …).

Corps de classe Java

Attributs

Les attributs, ou variables de classes, définissent un ensemble d’indicateurs de l’état des objets.

La classe définit les attributs et leurs types.

Un objet (instance de classe) a effectivement une liste d’attributs associés, ce sont des instances des attributs déclarés dans la classe. L’ensemble de ces valeurs définit l’état de l’objet, on les appelle d’ailleurs aussi variables d’état.

Les attributs sont typés. Ils peuvent être de type primitif ou être des objets.

La syntaxe pour déclarer un attribut est : le modifier de droit d’accés, le type de l’attribut, le nom de l’attribut. [modifier] [type] [nom de l'attribut] ;

exemple :

private int id ;

Eventuellement, vous pouvez aussi associer une valeur à l’attribut. Ce sera la valeur de l’attribut lors de l’instanciation d’un objet de la classe (sauf si vous modifiez cette valeur dans le constructeur) :

exemple :

private int id = 0 ;

Les modifiers d’accés permettent d’associer un droit d’accés à un attribut. Nous détaillerons les différents modifiers et leurs effets un peu plus loin dans ce tutoriel.

Méthodes

Les méthodes sont déclarées dans les classes, elles définissent le comportement des objets.

Elles peuvent être appelées par un objet, la syntaxe d’appel étant : monObjet.maMethode(param1, param2, …);

Lors de l’appel d’une méthode, celle-ci renvoie “quelque chose”. Cette “chose” peut être un objet, une valeur ou rien. Il faut donc préciser le type de ce qui est renvoyé par une méthode. Si la méthode ne renvoie rien, on utilise le mot clef void.

La syntaxe de déclaration d’une méthode est : le modifier de droit d’accés, le type de retour de la méthode,  le nom de la méthode, les paramètres entre parenthéses, le corps de la méthode entre accolades.

[modifier] [nom méthode] ( [type][param1] , [type][param2] , [type][param3], …, [type][param n]) { [corps de méthode] }

Pour dire dans le corps de la méthode de renvoyer quelque chose, on emploie le mot clef return.

exemple :

public int getId() { return id; }

public void setId( int nouvel_id ) { id = nouvel_id ; }

Pour préciser que l’on fait référence à l’id de notre objet, j’aurai pu écrire this.id au lieu de id. Mais dans ce cas, il n’y a pas de confusion possible, donc this n’est pas nécessaire.

exemple :

public int getId() { return this.id; }

public void setId( int nouvel_id ) { this.id = nouvel_id ; }

Surcharge et re-définition de méthodes

L’opération de surcharge consiste à définir plusieurs méthodes ayant le même nom dans une même classe. Ces méthodes peuvent avoir des types de retour identiques ou différents. Par contre, il est obligatoire que le nombre ou le type des paramètres change.

L’opération de re-définition de méthodes concerne l’héritage et consiste à redéfinir dans une classe fille une méthode déjà définie dans une classe mère.

Droits d’accès

Nous allons maintenant présenter les différents modifiers et les droits d’accés qu’ils permettent de définir. Nous ne traitons ici que des modifiers d’accés.

Les modifiers d’accés sont :

  • default
  • public
  • private
  • protected

Nous avons déjà vu dans les tutoriels précédents l’utilisation de default et public dans le cas de déclaration de classes. Encore une fois, default et public sont les seuls modifiers d’accés possibles sur des classes.

Effet des modifiers d’accés sur des classes

  • default : c’est le niveau d’accés par défaut. Ne pas utiliser de modifier d’accés ou utiliser le modifier default revient au même. Une classe d’accés par défaut n’est visible que par les autres classes de son package.
  • public : Une classe d’accés public est visible par toutes les autres classes.

Effet des modifiers sur les membres (attributs et méthodes) d’une classe

  • default : est le niveau d’accés par défaut. Ne pas utiliser de modifier ou utiliser le modifier default revient au même. Un membre déclaré de niveau d’accés par défaut est visible dans la classe et dans toute classe du même package.
  • public : Tout membre déclaré public appartenant à une classe A, est visible par toute classe qui a accés à la classe A.
  • private : Tout membre déclaré private n’est visible que dans la classe ou il est déclaré. Soulignons que ceci est vrai même par héritage. Soit une classe A dont un membre est private. Une classe B hérite de A. La classe B n’hérite pas des membres private. Redéclarer dans la classe B un membre qui est présent et private dans A n’est ni une surcharge ni une re-définition.
  • protected : Un membre déclaré protected est visible dans la classe, dans toute classe du même package et par héritage dans toute classe fille. Soit une classe A dont un membre est protected. La classe B hérite de A. La classe B hérite le membre protected de A.

Autres Modifier

Nous allons traiter ici de quelques autres modifiers bien utiles: final, abstract et static.

Il faut retenir qu’il est possible d’associer plusieurs modifiers à une déclaration (que ce soit une classe ou un membre). Bien sûr, on ne peut utiliser qu’un seul modifier d’accés. Mais on peut faire des combinaisons avec les autres modifiers.

On peut par exemple déclarer un membre static et final et public.

  • final : Une classe marquée final ne pourra pas être héritée (pas de classes filles possible). Une méthode marquée final ne peut pas être redéfinie dans une classe fille. Une variable marquée finale ne pourra pas avoir une nouvelle valeur assignée. Marquer un attribut comme étant final revient à définir une constante. Marquer un paramètre final dans une déclaration de méthode revient à garantir que la méthode ne pourra pas modifier la valeur de ce paramétre.
  • abstract : Abstract ne s’utilise que sur les classes et les méthodes. Une classe marquée abstract est dite abstraite, elle ne peut pas être instanciée (on ne peut pas faire d’objet par appel à new de cette classe). Une méthode abstraite n’a pas de corps (pas de définition) et une classe ayant une méthode abstraite doit nécessairement être abstraite. Les classes et les méthodes abstraites sont utiles dans un contexte d’héritage. Elles permettent de déclarer des classes d’un niveau d’abstraction élevé. Une classe héritant d’une classe abstraite doit implémenter toutes les méthodes abstraites héritées, à moins d’être elle-même abstraite.
  • static : Une méthode ou un attribut marqué static existe indépendamment de toute instance de classe. On dit souvent que toutes les instances d’une classe partagent les mêmes membres static. En fait, c’est pire que ça. Un membre static est unique quelquesoit le nombre d’instances de la classe et il existe même si il n’y a aucune instance de cette classe.

Identifiers

Nous allons ici présenter les éléments de syntaxe essentiels pour les identifiers. Les identifiers étant les noms de variables (variables de classe, paramétres, variables locales), les noms de méthodes, les noms de classe.

Nous nous en tenons ici à ce qu’il est possible ou impossible de faire. Mais il existe beaucoup de bonnes pratiques ou conventions de codage que nous vous invitons à connaître et adopter : les javabeans (nous leur consacrerons un tutoriel) et la convention de codage recommandée par sun.

Il existe un plugin eclipse, check style qui vous permet de définir une convention de codage et de faire contrôler par eclipse que vous la respectez bien.

Il existe un ensemble de règles de bases (dites légales) à connaître concernant le nommage de classes, variables et méthodes.

Règles générales sur les identifiers :

  • il est interdit de réutiliser un mot-clef java pour nommer une classe, une variable ou une méthode (vous ne pourrez pas appeler une variable void par exemple, ou public).
  • les identifiers ne doivent être composés que de caractères unicodes, de nombres, de symboles monétaires, et de caractères de liens (”_” par exemple mais pas “-”).
  • les identifiers doivent commencer par une lettre, un symbole monétaire ou un caractère de lien.
  • les identifiers ne doivent pas commencer par un nombre.
  • les identifiers sont sensibles à la casse.

Nommage de classes

  • il ne peut y avoir qu’une seule classe public par fichier java. Le fichier java doit porter le même nom que cette classe public.

La règle suivante n’est qu’une convention. Mais même eclipse vous rappelera à l’ordre si vous ne la respectez pas.

  • le nom d’une classe commence par une majuscule. Si le nom d’une classe est la concaténation de plusieurs mots, la première lettre de chaque mot devrait être en majuscule.

Nommage de variables et constantes

Ce sont aussi des conventions.

  • le nom d’une variable commence par une minuscule.
  • le nom d’une constante est en majuscule et on utilise un “_” pour séparer les mots constituants le nom de la constante.

Nommage de méthodes

C’est encore une convention.

  • la premièe lettre d’une méthode est une minuscule. Ensuite chaque mot commence par une majuscule. En règle générale, on essaye d’utiliser des verbes.

Types primitifs

Les types primitifs en java sont représentés par des mots-clefs commençant par une minuscule. Le type String par exemple n’est pas un type primitif, c’est une classe héritant de Object.

Certaint types primitifs ont des équivalents objets comme int et Integer par exemple. La conversion entre int et Integer étant aisée et permettant de traiter des variables de type primitif comme des objets et vice versa.

Vous retrouverez ces informations sur la page de sun sur les types primitifs.

La liste des types primitifs :

  • byte : codé sur 8 bits signés. Permet de représenter un entier entre -128 et 127 inclus.
  • short : codé sur 16 bits signés. Permet de représenter un entier entre -32 768 et 32 767 inclus.
  • int : codé sur 32 bits signés. Permet de représenter un entier entre -2 147 483 648 et 2 147 483 647 inclus.
  • long : codé sur 64 bits signés. Permet de représenter un entier entre -9 223 372 036 854 775 808 et 9 223 372 036 854 775 807 inclus.
  • float : Permet de représenter un nombre décimal avec une précision sur 32 bits.
  • double : Permet de représenter un nombre décimal avec une précision sur 64 bits.
  • boolean : Permet de représenter un booléen. Prend les valeurs true ou false.
  • char : Permet de représenter un caractère unicode codé sur 16 bits.

Les conditionnelles en Java

Nous présentons ici les syntaxes de la plupart des conditionnelles Java. Sun les appelle les “control flow statements”, et leur consacre une partie de son tutoriel.

Les conditionnelles sont des sentences qui permettent de ne pas faire uniquement des exécutions linéaires du code. En règle générale, une ligne de code correspond à une instruction. Les instructions s’exécutent donc dans leur ordre d’apparition dans le code.

Les conditionnelles peremettent de casser cette linéarité en mettant en place des tests conditionnels (if then else) ou des boucles (for, while).

Il n’y a que deux conditionnelles à connaître : if et for.

A partir de ces deux conditionnelles toutes les autres peuvent être définies.

  • if then else : permet de décrire une exécution de code ou une autre en fonction du résultat d’un test. Si le test est vrai exécuter ce code, si le test est faux, exécuter cet autre code.
  • for : permet de mettre en place une boucle d’exécution. Pour chaque élément de cette liste, exécuter ce code. Donc exécuter n fois ce code.

Exemple de if then else :

if( test )

{

System.out.print(”test vrai”);

}

else

{

System.out.print(”test faux”);

}

il est possible d’enchaîner les tests de if. Par exemple :

if ( id== 0)

{

System.out.print(”id = 0″);

}

else if (id == 1)

{

System.out.print(”id=1″);

}

else …

Il est aussi possible de ne pas préciser de else si on a aucun code alternatif à faire exécuter.

Exemple de for :

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

{

System.out.print( ” tour n°” + i );

}

Cette boucle s’exécutera 5 fois et affichera : tour n°0 tour n°1 tour n°2 tour n°3 tour n°4

Les autres :

  • switch : Cette conditionnelle revient à écrire un ensemble de tests if sur la valeur d’une variable.
  • while : Cette boucle s’exécute tant qu’un test est vrai. Très pratique pour créer des boucles infinies.
  • do-while : C’est l’utilisation inverse de la boucle while. Au lieu de dire “tant que ce test est vrai, faire cette action”, on dit “faire cette action tant que ce test est vrai”.

Exemple de switch :

soit un int month représentant un mois. Le code suivant permet de tester la valeur de month et d’afficher le mois en lettres en fonction de sa valeur numérique.

switch(month)

{

case 1:  System.out.println("January"); break;
case 2:  System.out.println("February"); break;
case 3:  System.out.println("March"); break;
case 4:  System.out.println("April"); break;
case 5:  System.out.println("May"); break;
case 6:  System.out.println("June"); break;
case 7:  System.out.println("July"); break;
case 8:  System.out.println("August"); break;
case 9:  System.out.println("September"); break;
case 10: System.out.println("October"); break;
case 11: System.out.println("November"); break;
case 12: System.out.println("December"); break;
default: System.out.println("Invalid month.");break;

}

Le mot clef case permet de préciser “si month prend cette valeur alors faire tout ce qui suit”.

Le mot clef default revient à dire “si la valeur n’est pas prise en compte par un case alors faire …”.

Le mot clef break permet de dire qu’il faut s’arréter là ou break est rencontré. En réalité, case dit c’est à partir d’ici qu’il faut exécuter. Et s’il n’y a pas de break, le code des case suivant est aussi exécuté.

Par exemple dans notre cas précédent, s’il n’y avait pas de break et avec month = 10, il serait affiché : October November December Invalid month.

Nous revenons sur break dans les interrupteurs de boucles.

exemple de while :

while (true)

{

System.out.println(”toto”);

}

C’est un exemple de boucle infinie. Le test de notre while est un booléen vrai. Donc le corps du while sera exécuté à l’infini. Ce genre de code peut-être dangereux et très pratique. Très dangereux parce que l’on peut facilement faire une boucle infinie sans le vouloir. Mais parfois on a besoin de boucles infinies. Quand on veut garder des processus atifs par exemple.

Un exemple de do while :

do

{

System.out.println(”toto”);

}

while(false)

C’est l’exemple inverse du cas précédent. Ici “toto” sera affiché une fois uniquement. Le test du while étant faux, la boucle ne sera pas exécutée.

La grande différence entre while et do-while est que dans le cas du do-while, le test est exécuté à chaque fin de tour et non au début. Du coup, le corps de do est exécuté au moins une fois.

Les interrupteurs de boucles :

  • break : permet d’interrompre l’exécution d’une boucle. A la rencontre d’un break, la boucle est interrompue.
  • continue : permet de finir un tour d’exécution de boucle. A la rencontre de continue, on passe au tour suivant d’exécution de la boucle.
  • return : permet d’interrompre l’exécution d’une méthode et retourne une variable ou une valeur. return peut donc aussi être utilisé pour sortir d’une boucle, mais il terminera aussi l’exécution de la méthode dans laquelle la boucle est définie.

Nous en avons fini avec ce tutoriel sur la syntaxe et les niveaux de droits d’accés. Il ne vous reste plus qu’à pratiquer.

Dans la suite des tutoriels, nous allons nous orienter vers des composants logiciels java orientés pour du web essentiellement.

Il nous reste un cours sur les javabeans pour présenter une convention de nommage incontournable dans le monde professionnel. Un cours sur les Enums, qui permettent de définir des structures très utiles principalement pour le traitement de String.

Puis nous définirons un environnement de production qui nous semble otpimal pour des développements web (un ensemble de plugins eclipse, l’utilisation de Maven, et le framework hibernate).

Enfin, nous détaillerons les composants qui nous intéressent le plus, à savoir les Servlet et les Portlet.

Programmation Orientée Objet en JAVA

Dimanche 24 janvier 2010

Dans cet article, second de la série de nos tutoriels sur java, nous vous proposons une petite introduction à la POO (Programmation Orientée Objet). Il existe de nombreux tutoriels sur ce sujet. Nous essayons ici de rester simple et concis. Nous présenterons un exemple complet après un rapide cadre théorique.

Nous recommandons les excellents tutoriaux de sun à ce sujet.

Un peu de Théorie

Sur Wikipedia, on trouve cette définition :

La programmation orientée objet (POO) ou programmation par objet, a été élaborée par Alan Kay dans les années 1970. C’est un paradigme de programmation informatique qui consiste en la définition et l’interaction de briques logicielles appelées objets.

Qu’est-ce qu’un objet ? C’est une représentation informatique d’une entité physique ou d’une idée. Un objet est caractérisé par des attributs et des méthodes. On parle aussi de variables d’état (permettant de caractériser l’état de l’objet) et de comportements. Un objet est aussi une instance d’un modèle. Le modèle, que l’on appelle une Classe, représente donc un concept. Une classe est un descripteur des objets, elle va préciser quels sont les attributs (et les valeurs que ces attributs peuvent prendre) et méthodes de l’objet.

L’une des motivations de la programmation Objet est la réutilisabilité. Le but est que des classes définies pour un projet puissent être réutilisées dans d’autres sans avoir pour autant à comprendre le mécanisme interne de ces classes. Pour cela, deux notions importantes : celle d’interface et celle d’héritage.

Dans cette optique de réutilisabilité, il est important de souligner la possibilité de communiquer la signature d’une Classe. La signature d’une classe est sa description, la liste de ses attributs, la liste de ses méthodes. Cette description permet à tout un chacun de savoir comment peut être utilisé un objet d’une classe, comment on peut interagir avec un objet (certains diront “comment on peut communiquer avec cet objet”, pour moi on leur donne des ordres tout au plus, ce n’est pas une véritable communication). La signature permet de voir une Classe et ses instances comme une sorte de boîte noire dont on ne connait que les points d’entrées et de sorties. Ces signatures peuvent être commentées et publiées, c’est le cas de l’api en ligne ou javadoc.

C’est dans cette optique de signature qu’ont été définies les interfaces. On dit d’une interface que c’est un contract entre la classe et le monde extérieur. Une interface décrit exactement une signature mais restreinte à un ensemble de méthodes. Et une interface ne définit en aucun cas le fonctionnement de ses méthodes. Une classe qui implémente une interface garantit donc que tous les objets instances de cette classe ont une méthode qui s’appelle de cette façon et qui renvoie ce type de résultat.

L’héritage est la possibilité de spécialiser ou généraliser les Classes. Prenez une classe A avec ses attributs et ses méthodes. Une classe B doit avoir les mêmes attributs et méthodes mais avec quelques éléments en plus. On dit que B spécialise A. On peut alors grâce à l’héritage dire explicitement B est identique à A avec ces éléments supplémentaires. L’héritage permet d’économiser du code. Il est aussi possible de redéfinir l’une des méthodes de A dans B. Ainsi, B a bien une méthode portant le même nom que celle de A mais elle ne fait pas le même traitement. En gros, une ferrari et une deux chevaux sont toutes les deux des voitures qui peuvent accélérer mais pas de la même façon. Il est important de retenir que tout objet instance de B est aussi instance de A. C’est ce que l’on appelle le polymorphisme.

Dernier point sur l’héritage : dans certains langages, il est possible de faire de l’héritage multiple. L’héritage multiple consiste à dire : soit deux classes distinctes (non liées par héritage) A et B, une troisième classe C peut hériter de A et de B. C’est à dire que C hérite toutes les méthodes et attributs de A et de B. Il n’est pas possible en Java de faire de l’héritage multiple. Cependant, il est possible d’implémenter plusieurs interfaces.

Concrétement on fait comment ?

De nombreux tutoriaux existent ou on vous fera coder des voitures ou des distributeurs de billets. Vous en trouverez de très bons sur développez par exemple.

Ici, nous allons nous contenter de l’essentiel. Comment on écrit une classe, comment on hérite d’une classe, comment on déclare une interface, comment on implémente une interface, et enfin comment une interface hérite d’une autre.

Je vous recommande de lire notre premier tutoriel débuter java. Nous ne détaillons toujours pas dans ce tutoriel tous les usages de tous les mots clefs java liés aux niveaux de droits d’accés. Nous consacrerons un tutoriel à ce sujet.

Avant de commencer, je voudrais insister sur l’ambiguité du mot objet. On l’utilise souvent à tort et à travers. On ne sait plus vraiment si on désigne une classe ou une instance de classe avec le mot objet. L’existence d’une classe Object en java n’aide pas à lever cette ambiguité. Donc une bonne fois pour toute : un objet est une instance d’une classe.

Définition d’une classe

Avant tout un petit rappel sur les classes en Java:

  • On peut déclarer autant de classes que l’on souhaite dans un fichier java.
  • On ne peut déclarer qu’une seule classe public.
  • Le fichier doit porter le même nom que la classe public s’il y en a une.
  • S’il n’y a pas de classe public dans le fichier, le nom du fichier n’a pas d’importance.

Il faut aussi noter qu’une classe en Java est censée faire partie d’un package. Ceci n’est pas obligatoire mais c’est une bonne pratique. Dans Eclipse par exemple, quand vous créez une classe sans la mettre dans un package, celle-ci est placée dans un package par défaut (default package).

Donc en règle générale, un fichier .java débute par une ligne de déclaration de package, par exemple pour signaler que les classes de ce fichier sont dans le package tutoriels lui même inclus dans le package natoine, on écrira :

package natoine.tutoriels;

Un point sur les différents niveaux de droits d’accés pour les classes. Celles-ci peuvent avoir comme droit d’accés : default ou public.

  • default : Le niveau d’accés par défaut. Dans ce cas, la classe n’est visible que par les autres classes de son package.
  • public: Le niveau d’accés publique. C’est à dire qu’elle est visible par toutes les autres classes, même les classes qui ne sont pas du même package.

Pour indiquer qu’une classe est de niveau d’accés par défaut ou publique, on utilise des mots-clefs que l’on appelle des modifiers. Le mot-clef pour le niveau par défaut est default, celui pour publique est public. En ce qui concerne le niveau d’accés par défaut, le mot-clef n’est pas indispensable. Ne rien mettre ou mettre le modifier default revient au même.

Pour déclarer une classe, on utilise le mot-clef class. Par convention, le nom d’une classe commence par une majuscule.

Déclaration d’une classe d’accés par défaut DefaultClass :

class DefaultClass

ou bien :

default class DefaultClass

Déclaration d’une classe d’accés publique PublicClass :

public class PublicClass

Ensuite il faut ouvrir le corps de la définition de la classe par une acolade puis refermer cette acolade une fois la définition finie.

Héritage de classe

Pour hériter d’une classe, il faut utiliser le mot-clef extends. Par exemple nous allons faire un classe ExtendDefaultClass :

public class ExtendDefaultClass extends DefaultClass

Vous remarquerez que vous êtes obligés de mettre cette nouvelle classe dans le même package que DefaultClass. Sinon, la classe DefaultClass ne sera pas visible.

A l’opposé si nous faisons unc classe ExtendPublicClass qui hérite de la classe PublicClass, nous pouvons la mettre dans un autre package que celui contenant la classe PublicClass.

Il reste deux modifiers important à aborder concernant la définition de classes. Ces mots-clefs sont final et abstract :

  • final : Le modifier final indique que la classe ne peut pas avoir de classe fille. Aucune classe ne pourra hériter d’une classe marquée final.
  • abstract : Le modifier abstract permet de déclarer une classe comme étant abstraite. Une classe abstraite ne peut pas être instanciée, sa seule raison d’être est d’être héritée. Notez qu’une classe abstraite n’est pas obligée de déclarer des méthodes abstraites. Par contre, une classe déclarant une méthode abstraite doit nécessairement être une classe abstraite.

Déclarons une classe final FinalExtendDefaultClass qui hérité de DefaultClass :

final class FinalExtendDefaultClass extends DefaultClass

Déclarons une classe abstraite AbstractPublicClass qui hérite de la classe PublicClass :

public abstract class AbstractPublicClass extends PublicClass

Implémentation d’une interface

Pour déclarer une interface, il faut utiliser le mot-clef interface. Les interfaces comme les classes peuvent être accessible par défaut ou publique. Elles suivent les mêmes règles de droit d’accés et les modifiers d’accés sont les mêmes (default, public).

L’intérêt d’une interface est de préciser des méthodes à obligatoirement implémenter. Une classe implémentant une interface doit nécessairement implémenter les méthodes de cette interface.

Pour l’exemple, nous allons déclarer une interface publique PublicInterface avec une méthode printName().

public interface PublicInterface
{
void printName();
}

Petite remarque au sujet des méthodes d’une interface (pour ceux qui ont pris de l’avance concernant les droits d’accés des méthodes) : elles sont abstraites et publiques obligatoirement. Elles sont nécessairement publiques parce que sinon, elles ne seraient pas visibles par les classes qui doivent les implémenter. Et elles sont abstraites parce que chaque classe doit définir leur implémentation et l’interface ne suppose rien d’autre au sujet des classes (l’interface ne sait pas quels sont les attributs des classes l’implémentant ni les autres méthodes).

La méthode printName aurait pu être écrite de la façon suivante, cela revient au même :

public abstract void printName();

Pour qu’une classe implémente une interface, il faut utiliser le mot-clef implements et bien sur, déclarer et implémenter les méthodes de l’interface en question. Par exemple, nous allons déclarer une classe publique PublicClassImplementPublicInterface.

public class PublicClassImplementPublicInterface implements PublicInterface
{
public void printName()
{
System.out.println(”PublicClassImplementPublicInterface”);
}
}

Si une classe doit implémenter plusieurs interfaces, il suffit de séparer chaque nom d’interface par une virgule.

Une classe peut hériter d’une autre classe et implémenter une ou plusieurs interfaces. Dans ce cas, le mot clef extends et la déclaration de la classe mère précéde le mot-clef implements et la liste des interfaces. Par exemple :

public class PublicClassExtendsImplements extends AbstractPublicClass implements PublicInterface

Héritage d’interface

Il est possible pour une interface d’hériter d’une autre interface. Il suffit de préciser l’interface héritée par le mot-clef extends.

Déclarons une interface InterfaceExtendsPublicInterface qui hérite de notre interface précédente PublicInterface :

public interface InterfaceExtendsPublicInterface extends PublicInterface
{
void printInterfaceList();
}

Cette interface n’a pas à redéclarer les méthodes héritées par ses interfaces mères mais les classes implémentant cette interface devront les implémenter.

Instanciation de classe

Instancier un objet se fait simplement par l’utilisation du mot clef new. Par exemple, instancions un objet de la classe PublicClassImplementInterface:

new PublicClassImplementPublicInterface();

Lors de l’appel à new PublicClassImplementPublicInterface(), un nouvel objet de la classe PublicClassImplementPublicInterface est construit. Cette construction fait appel au constructeur de la classe PublicClassImplementPublicInterface.

Dans notre exemple, nous n’avons pas défini de constructeur dans la classe PublicClassImplementPublicInterface. Du coup, le constructeur appelé est celui de la classe dont hérite PublicClassImplementPublicInterface. Si celle-ci ne définit pas de constructeur non plus, le constructeur appelé est celui de la classe mère de la classe dont hérite PublicClassImplementPublicInterface. Etc jusqu’à la classe Object qui elle déclare bien un constructeur.

En java, il existe une super classe, la classe Object. Toutes les classes que vous définirez héritent donc de cette classe Object et ce sans avoir à utiliser le mot-clef extends.

Nous allons maintenant déclarer une classe FinTutoClass qui elle déclare un constructeur. Un constructeur de classe doit porter le même nom que la classe et se déclare de la même façon qu’une méthode à part qu’il n’a pas de type de retour. Afin de bien finir ce tutoriel, cette classe FinTutoClass hérite de la classe PublicClassExtendsImplements et implémente l’interface InterfaceExtendsPublicInterface.

public class FinTutoClass extends PublicClassExtendsImplements implements InterfaceExtendsPublicInterface
{
public FinTutoClass()
{
super();
}
public void printInterfaceList()
{
for(Class _interface : this.getClass().getInterfaces())
{
System.out.println(” ” + _interface.getSimpleName());
}
}
}

Encore une fois, le constructeur peut avoir le même niveau de droits que toute méthode (donc default, public, private ou protected).

L’utilisation de super(); signifie que le constructeur a le même comportement que le constructeur de la classe mère. Ensuite il est toujours possible d’ajouter d’autres instructions. En réalité, super() est appelé dans tous les cas.

Maintenant nous allons consacrer la fin de ce tutoriel au polymorphisme. Nous allons écrire une classe MainClass avec une méthode main afin de pouvoir l’exécuter. Les questions auxquelles nous allons tenter de répondre sont : quelle est la nature (ou le type) de notre objet? Peut-il changer de nature?

Cette dernière partie va nous permettre d’introduire l’opération de casting d’une classe. Le casting d’une classe (on dit que l’on caste un objet d’une classe) consiste à demander à un objet d’agir en tant qu’objet d’un autre type que celui déclaré lors de sa construction. Il est possible de caster l’objet selon les classes dont il est instance (c’est à dire la classe déclarée lors de l’appel à new et toutes les classes méres) et selon les interfaces qu’il implémente.

Le contenu de la classe MainClass :

public class MainClass {
public static void main(String[] args)
{
FinTutoClass _object = new FinTutoClass();
_object.printInterfaceList();
_object.printName();
System.out.println(_object.getClass().getSimpleName());
System.out.println(((PublicClassExtendsImplements)_object).getClass().getSimpleName());
PublicClassExtendsImplements _object2 = new FinTutoClass();
_object2.printName();
((FinTutoClass)_object2).printInterfaceList();
System.out.println(_object2.getClass().getSimpleName());
PublicInterface _object3 = new FinTutoClass();
_object3.printName();
((InterfaceExtendsPublicInterface)_object3).printInterfaceList();
InterfaceExtendsPublicInterface _object4 = new FinTutoClass();
_object4.printInterfaceList();
_object4.printName();
}
}

Que fait-on dans ce main?

  • Le début est simple : on crée un objet _object de type FinTutoClass en appelant le constructeur de FinTutoClass.
  • ensuite, on appelle la méthode printInterfaceList qui est implémentée dans la classe FinTutoClass
  • La troisième ligne montre que notre objet _object peut aussi appeler les méthodes dont il a hérité. La méthode printName est implémentée dans la classe PublicClassExtendsImplements dont hérite la classe FinTutoClass et n’est pas réimplémentée dans FinTutoClass.
  • Dans la quatrième ligne, on demande à afficher la classe de l’objet _object. Il sera affiché FinTutoClass.
  • Dans la cinquième ligne on caste notre objet : (PublicClassExtendsImplements)_object signifie que _object est ici traité comme étant une instance de la classe PublicClassExtendsImplements. Puis on demande d’afficher le nom de la classe dont il est l’instance. Il sera affiché FinTutoClass malgrè le cast. Bien que l’on ait casté l’objet, celui-ci reste de la classe dont on a appelé le constructeur.
  • Dans la sixième ligne, on déclare un objet _object2 comme étant de la classe  PublicClassExtendsImplements, cependant on fait appel au constructeur de FinTutoClass. Ceci est possible car FinTutoClass hérite de PublicClassExtendsImplements.
  • Du coup, dans la septième ligne, on peut appeler une méthode implémentée dans la classe PublicClassExtendsImplements.
  • Par contre, pour appeler une méthode de la classe FinTutoClass, nous somme obligés de caster l’objet _object2 en FinTutoClass. Ceci est possible car on a effectivement affaire à un FinTutoClass.
  • D’ailleurs si on demande à _object2 d’afficher le nom de sa classe, il affichera bien FinTutoClass.
  • Ensuite, on construit un objet _object3 mais en déclarant cette fois qu’il est du type de l’interface PublicInterface. L’objet est construit via l’appel au constructeur de la classe FinTutoClass. On peut donc typer un objet selon les interfaces que sa classe implémente.
  • Du coup on peut appeler les méthodes décrites dans l’interface. Et que les méthodes de l’interface tant que l’on ne caste pas l’objet.
  • On est par exemple obligé de caster l’objet pour faire appel à la méthode printInterfaceList. On peut là aussi caster l’objet en un type d’interface (ici InterfaceExtendsPublicInterface) que sa classe implémente.
  • Cette fois on construit un objet _object4 que l’on type selon l’interface InterfaceExtendsPublicInterface.
  • Du coup on peut appeler sans cast la méthode printInterfaceList
  • On peut aussi appeler sans cast une méthode définie dans une interface dont hérite l’interface InterfaceExtendsPublicInterface.

Sources du tutoriel

Vous pouvez télécharger le contenu de mon projet eclipse pour ce tutoriel.

Bien entendu les noms de classe ont ici été choisis pour le tutoriel. Vous n’êtes pas obligé de mettre le mot Default dans le nom d’une classe d’accés pas défaut.

IPC ou communication inter portlet 2. JSR286

Vendredi 22 janvier 2010

Un tutoriel sur la communication interportlet. Les ressources sur le sujet sont encore rares, il m’a semblé nécessaire de faire ce tutoriel assez rapidement du coup.

Si vous suivez les autres tutoriels de ce site sur la programmation JAVA, il arrive bien en avance de ce qui était prévu. Mais je le réintégrerai plus tard dans un tutoriel plus long consacré aux portlets.

Pour l’instant, ce tutoriel va traiter de la communication entre portlet conforme à la JSR286.

En particulier nous ne traiterons que de la communication par passage d’événement. Cette communication étant à nos yeux la plus compatible avec une vision composant. Pour ceux qui veulent en savoir plus, la ressource issue du blog xebia est la meilleure référence que j’ai trouvé. Dans cette ressource, vous retrouverez l’essentiel de ce qui est détaillé ici et une explication de l’autre solution de communication qu’est le partage d’état. Il y a aussi une discussion dans un des forums de JBoss.

Je précise que dans le cadre de ce tutoriel, les portlets ont été développés sous eclipse (mais ça n’a pas d’importance) et déployés sous JBoss Portal (version 2.7 + AS 4.2.3).

Cependant, le tutoriel s’adresse à tous développeurs de portlet 2.0, c’est à dire dans le cadre d’un développement de portlet pour un portail respectant la JSR286.

Partage d’état vs passage d’événement

Il y a deux façons de communiquer avec la JSR 286. La première solution est le partage d’état. Nous ne détaillerons pas dans ce tutoriel cette solution mais allons en expliquer le principe. La deuxième solution, celle que nous détaillerons ici en pratique est le passage d’événement. Ce paragraphe va donc exposer les principes de ces deux solutions et va nous permettre de justifier pourqoi le passage d’événement nous semble plus proche d’une approche composant et donc d’un développement orienté vers l’intégration de composants.

Le partage d’état : dans cette solution, chaque portlet déclare les paramètre qu’il partage avec les autres portlets et chaque portlet déclare les paramètres des autres portlets qu’il veut pouvoir atteindre. Ainsi, au moment ou un portlet effectue son rendu (calcule sa vue), il rend ses paramètres visibles ou accéde à ceux des autres.

Le passage d’événement : dans cette solution, chaque portlet déclare les événements qu’il peut lever et les événements qu’il peut écouter. Un portlet léve ensuite un événement, et les portlets écouteurs sont automatiquement au courant.

Discussion : La première solution (celle du passage d’état) peut être recommandée quand les paramètres échangés ne sont utiles que pour l’affichage. La solution est facile à mettre en place, il suffit de déclarer dans le descripteur du portlet les paramétres partagés. Cependant, cette solution n’est pas recommandé quand les paramétres doivent être traités (c’est à dire quand ils risquent d’être modifiés par un calcul effectué par un autre portlet) car les paramètres ne sont échangés qu’au moment de l’appel à la méthode Render. Mais la principale critique est que cette solution rend les portlets fortement dépendants les uns des autres puisque ils supposent l’existence de ces paramètres afin de rendre leur affichage. La deuxième solution quand à elle permet d’effectuer des traitements sur les paramètres et reste très proche d’une vision composant. Le mécanisme d’événements garantit que si le paramètre existe il y aura traitement, et que si le paramètre n’existe pas, il y aura un fonctionnement par défaut.

Passage d’événement pratique

Alors en pratique comment cela fonctionne? L’exemple que je vous propose est très simple: nous développons deux portlets, un PortletA et un PortletB. Chaque portlet a son propre projet dans Eclipse (bien que j’aurai pu faire un seul projet, mais pour l’exemple, je voulais détailler un cas ou les portlets auraient pu être développés sans connaissance l’un de l’autre). Le PortletA sera l’écouteur d’événement, le PortletB se chargera de l’envoi. L’événement levé permet d’échanger une donnée de type String.

Il est possible d’échanger autre chose que des String. Tout objet serializable peut être échangé par passage d’événement.

Les erreurs classiques à éviter (si je le dis c’est que je suis passé par là croyez-moi ;) ) :

  • Ne pas utiliser la bonne api de portlet. N’oubliez pas que nous faisons des portlets avec JSR 286 donc on utillise la nouvelle api.
  • Si vous faites des JSP, n’oubliez toujours pas que vous faites des portlets version 2. Donc le taglib à utiliser est : <%@ taglib uri=”http://java.sun.com/portlet_2_0″ prefix=”portlet” %>
  • N’oubliez toujours pas que vous faites des portlets 2.0. Donc dans vos descripteurs portlet.xml, les attributs du noeud portlet-app changent : <portlet-app xmlns=”http://java.sun.com/xml/ns/portlet/portlet-app_2_0.xsd” version=”2.0″ xmlns:xsi=”http://www.w3.org/2001/XMLSchema-instance”          xsi:schemaLocation=”http://java.sun.com/xml/ns/portlet/portlet-app_2_0.xsd http://java.sun.com/xml/ns/portlet/portlet-app_2_0.xsd”>

Il faut déclarer le type des paramétres échangés, ou des event levés si vous préférez. Pour cela, il faut modifier le descripteur de l’application portlet.

Il faut ajouter un noeud <event-definition> comme fils du noeud <portlet-app> dans votre fichier portlet.xml.

PortletB

Dans l’exemple, le descripteur portlet.xml associé au projet du portletB devient :

<?xml version=”1.0″ encoding=”UTF-8″?>
<portlet-app xmlns=”http://java.sun.com/xml/ns/portlet/portlet-app_2_0.xsd”
version=”2.0″
xmlns:xsi=”http://www.w3.org/2001/XMLSchema-instance”
xsi:schemaLocation=”http://java.sun.com/xml/ns/portlet/portlet-app_2_0.xsd
http://java.sun.com/xml/ns/portlet/portlet-app_2_0.xsd”>
<portlet>
<portlet-name>PortletB</portlet-name>
<portlet-class>tuto.portlet.PortletB</portlet-class>
<supports>
<mime-type>text/html</mime-type>
<portlet-mode>VIEW</portlet-mode>
</supports>
<portlet-info>
<title>Enverra des infos à Portlet A</title>
</portlet-info>
<supported-publishing-event>
<qname>totoString</qname>
</supported-publishing-event>
</portlet>
<event-definition>
<qname>totoString</qname>
<value-type>java.lang.String</value-type>
</event-definition>
</portlet-app>

J’ai décidé de passer des String uniquement comme paramétre et d’appeler cet événement “totoString”.

Au niveau du code, j’ai décidé de faire un simple portlet qui charge un jsp et qui léve l’événement totoString lors d’une action. L’action est réalisé lors d’un clic sur un lien dans le portlet (cf portletB.jsp).

Le code du PortletB :

package tuto.portlet;

import java.io.IOException;
import javax.portlet.ActionRequest;
import javax.portlet.ActionResponse;
import javax.portlet.GenericPortlet;
import javax.portlet.PortletException;
import javax.portlet.PortletRequestDispatcher;
import javax.portlet.RenderRequest;
import javax.portlet.RenderResponse;

public class PortletB extends GenericPortlet
{
protected void doView(RenderRequest rRequest, RenderResponse rResponse) throws IOException, PortletException
{
rResponse.setContentType(”text/html”);
PortletRequestDispatcher prd;
prd = getPortletContext().getRequestDispatcher(rResponse.encodeURL(”/WEB-INF/jsp/portletB.jsp”));
prd.include(rRequest, rResponse);
}

public void processAction(ActionRequest request, ActionResponse response)
throws PortletException,IOException {
System.out.println(”processAction”);
this.sendEvent(response);
}
public void sendEvent(ActionResponse response)
{
String _toto = “passée en event”;
response.setEvent(”totoString”, _toto);
}
}

Le fichier portletB.jsp :

<%@ page language=”java” contentType=”text/html; charset=UTF-8″
pageEncoding=”UTF-8″%>
<%@ taglib uri=”http://java.sun.com/portlet_2_0″ prefix=”portlet” %>
<portlet:defineObjects/>
HelloWorld !!! Portlet B!
<a href=”<portlet:actionURL/>” >Envoi de l’event</a>
PortletA

Le PortletA est le portlet traitant un événement. Il doit donc implémenter l’interface EventPortlet et sa méthode processEvent(EventRequest request, EventResponse response). La méthode processEvent sera automatiquement appelé lorsque un événement du type de ceux déclaré dans le descripteur portlet.xml sera levé par un autre portlet.

Le descripteur portlet.xml de PortletA :

<?xml version=”1.0″ encoding=”UTF-8″?>
<portlet-app xmlns=”http://java.sun.com/xml/ns/portlet/portlet-app_2_0.xsd”
version=”2.0″
xmlns:xsi=”http://www.w3.org/2001/XMLSchema-instance”
xsi:schemaLocation=”http://java.sun.com/xml/ns/portlet/portlet-app_2_0.xsd
http://java.sun.com/xml/ns/portlet/portlet-app_2_0.xsd”>
<portlet>
<portlet-name>PortletA</portlet-name>
<portlet-class>tuto.portlet.PortletA</portlet-class>
<supports>
<mime-type>text/html</mime-type>
<portlet-mode>VIEW</portlet-mode>
</supports>
<portlet-info>
<title>Recevra des infos de Portlet B</title>
</portlet-info>
<supported-processing-event>
<qname>totoString</qname>
</supported-processing-event>

</portlet>

<event-definition>
<qname>totoString</qname>
<value-type>java.lang.String</value-type>
</event-definition>
</portlet-app>

Le code de PortletA :

package tuto.portlet;

import java.io.IOException;
import java.io.PrintWriter;

import javax.portlet.Event;
import javax.portlet.EventRequest;
import javax.portlet.EventResponse;
import javax.portlet.GenericPortlet;
import javax.portlet.PortletException;
import javax.portlet.PortletRequestDispatcher;
import javax.portlet.RenderRequest;
import javax.portlet.RenderResponse;
import javax.portlet.EventPortlet;

public class PortletA extends GenericPortlet implements EventPortlet
{
protected void doView(RenderRequest rRequest, RenderResponse rResponse) throws IOException, PortletException
{
if(rRequest.getParameter(”stringEvent”)!=null)//Action avec Event
{
rResponse.setContentType(”text/html”);
PrintWriter writer = rResponse.getWriter();
writer.write(”Event value of totoString : ” + rRequest.getParameter(”stringEvent”));
writer.close();
}
else //Action sans Event
{
rResponse.setContentType(”text/html”);
PortletRequestDispatcher prd;
prd = getPortletContext().getRequestDispatcher(rResponse.encodeURL(”/WEB-INF/jsp/portletA.jsp”));
prd.include(rRequest, rResponse);
}
}

public void processEvent(EventRequest request, EventResponse response)
{
Event event = request.getEvent();
String _toto = “vide”;
if(event.getName().equals(”totoString”))
{
_toto = (String)event.getValue();
response.setRenderParameter(”stringEvent”, _toto);
}
}

}

Le jsp de PortletA n’a aucune importance étant donné que c’est uniquement du HTML au final. Pour varier les plaisirs j’ai préféré utiliser un printWriter pour l’affichage après traitement de l’événement. Le but étant juste ici de constater visuellement que le PortletA a bien reçu l’événement. Dans un cas concret, il n’est pas forcément nécessaire de modifier le rendu du PortletA une fois l’événement traité.

Rendu dans JBoss Portal

Une fois déployés les deux portlets s’affichent ainsi :

capture d'écran des portlets du tuto ipc dans JBoss

capture d'écran des portlets du tuto ipc dans JBoss

Une fois cliqué sur le lien “Envoi de l’event” :

capture d'écran des portlets du tuto dans JBoss une fois event envoyé

capture d'écran des portlets du tuto dans JBoss une fois event envoyé

J’espére que ce tutoriel court et rapide vous aura suffisament éclairé et vous fera gagner du temps. Le plus difficile concernant l’IPC étant encore de trouver de la documentation.

Vous pouvez télécharger les sources de ce tutoriel.

Débuter JAVA

Mardi 24 novembre 2009

Pourquoi une suite de tutoriels Java? Et qui reprennent toutes les bases alors qu’il y en a déjà plein sur internet. Et bien en réalité, écrire un tutoriel est un excellent moyen de vérifier pour l’auteur qu’il maîtrise bien lui-même le sujet. Dans notre cas, nous étions quelques-uns à parler de passer la certification “programmer” de Sun pour Java. Mais après avoir essayé d’organiser des séances de révision et de subir la démotivation des uns et des autres, il m’a semblé qu’écrire une suite de tutoriels serait le meilleur moyen pour moi de m’assurer que j’avais bien le niveau.Maintenant, avancer seul sur une série de tutoriel me semblait encore une activité démotivante à la longue. Alors j’ai proposé à deux amis de les co-rédiger. Au final, nous sommes trois à écrire ces tutoriels. Chaque tutoriel est le fruit de séances de tests du contenu et de plusieurs lectures et corrections. L’équipe est composée de Kraft et moi même, qui n’avons pas perdu l’espoir d’un jour passer cette damnée certification, et de Fabien qui profite de l’occasion pour apprendre java. Nous testons et construisons les tutoriels lors de séances de pratiques. Il nous semble que ce contexte est pertinent pour vous offrir des tutoriels abouttis.

Ces tutoriels sont co-rédigés via mon wiki et publiés en version finale sur mon blog. Dans les deux cas, il est possible aux lecteurs de nous laisser des commentaires. Ne vous en privez pas, c’est comme ça qu’on progresse tous, vous et nous.

Première approche

Vocabulaire minimum

Dans cette première partie, nous allons revenir sur un vocabulaire de base qu’il peut être intéressant de connaître. Ce n’est pas que ce vocabulaire vous servira à coder à proprement parler, mais il vous permettra de mieux naviguer dans le monde JAVA. Et puis si au hasard d’une discussion votre interlocuteur utilise l’un de ces termes, ça vous évitera de passer pour un ***.
IDE : Integrated Development Environment. C’est un espace de développement. Donc un ensemble de programmes vous permettant de développer et de tester votre code sans sortir de l’espace de développement. En gros, l’espace de développement doit vous fournir un éditeur de code, un compilateur et vous permettre de lancer l’éxécution de vos programmes et de suivre le déroulement de ceux-ci. C’est un moyen de gagner du temps, beaucoup de temps.

JVM : Java Virtual Machine. C’est un ensemble de programmes et de structures de données qui permet d’interpréter des scripts ou programmes écrits dans un langage particulier, le bytecode java. Vous écrivez vos programmes dans un langage dit naturel et vous les compilez dans un autre langage, le bytecode. Celui-ci est interprétable, compréhensible, par toute JVM. C’est ce qui permet à java d’être un langage de programmation multi-plateformes. Chaque Architecture, chaque OS, a sa propre machine virtuelle. Ainsi un programme écrit en java et traduit en bytecode java est compréhensible sur toutes les plateformes ayant une JVM.

JRE : Jave Runtime Environment. C’est ce qui va vous permettre de comprendre le java. En fait, c’est là que se trouve votre JVM. Le JRE, c’est en gros l’ensemble du code nécessaire pour lancer une JVM. Quand un programme java est lancé, le JRE crée une JVM qui interpréte le bytecode du programme.

JDK : Java Development Kit. Le JDK est en gros le complément du JRE. Si le JRE permet d’interpréter un byte code, le JDK lui permet de compiler du bytecode à partir du code java dit “naturel”.

API : Application Programming Interface. C’est l’ensemble des fonctions et leurs descriptions mises à disposition des développeurs. C’est essentiel pour pouvoir partager le travail d’un développeur à l’autre. Dans le cas de Java, il existe une API des packages de bases. Mais chacun peut éditer la documentation de ses propres développements grâce à la javadoc. L’URL de l’api java 6: http://java.sun.com/javase/6/docs/api

Les bases java

Dans cette partie, nous allons vous faire écrire, compiler et exécuter un premier programme JAVA. Et ce premier programme sera un HelloWorld (comme c’est original). Ecrire ce programme vous aménera à écrire une première classe JAVA et nous allons tenter d’expliquer au fur et à mesure les éléments mis en jeux. Ce premier exercice doit être vu comme une initiation. C’est l’occasion de vérifier que vous avez bien tout ce qu’il faut pour faire du java (JRE, JDK). Les différents éléments sur lesquels nous allons revenir risquent donc de paraître survolés pour les développeurs avancés (mais pourquoi diable lieraient-ils ce tuto!!) et un brin compliqués voir flous pour les novices. Mais que les novices se rassurent, les tutos à venir reviendrons sur les points importants concernant la syntaxe java et la programmation objet.

1) Donc créons un fichier HelloWorld.java.

Dans ce fichier on déclare la classe HelloWorld :

public class HelloWorld
{

}

Alors de suite, ce qu’il faut retenir sur la déclaration de classes dans un fichier .java :

  • On peut déclarer autant de classes que l’on souhaite dans un fichier java.
  • On ne peut déclarer qu’une seule classe public.
  • Le fichier doit porter le même nom que la classe public s’il y en a une.
  • S’il n’y a pas de classe public dans le fichier, le nom du fichier n’a pas d’importance.

Du coup on va expliquer ce à quoi sert le mot-clef “public”.En java, il y a des niveaux d’accessibilité. Ces niveaux sont indiqués via des mots-clefs appelés “modifiers” en Anglais. En ce qui concerne les classes, il y a deux modifiers d’accessibilité possibles :

  • default. Le mot-clef default correspond au niveau de droit d’accés par défaut. On n’est pas obligé de l’écrire d’ailleurs. Ne rien marquer ou marquer default revient au même. Dans ce cas, la classe n’est visible que par les autres classes de son package.
  • public. Le mot-clef public correspond au niveau d’accés publique. C’est à dire qu’elle est visible par toutes les autres classes, même les classes qui ne sont pas du même package.

Arf, on tire sur la pelotte de laine. On se dit qu’on va juste expliquer à quoi sert le mot-clef public et on se retrouve à utiliser un nouveau gros mot qu’il faut définir.Qu’est-ce qu’un package? Un package est un regroupement de classes. C’est un peu le même principe qu’un répertoire. Un package peut aussi contenir d’autres packages.

L’idée est que, en programmation, on peut avoir envie de réutiliser un ensemble de classes d’un projet à un autre. Regrouper des classes qui se suffisent entre elles dans un même package permet de réutiliser ce package d’une application à une autre. On importe un package via le mot clef import en début de fichier. On déclare que les classes d’un fichier font partie d’un package par le mot-clef package.

2) Le but de notre classe : afficher “HelloWorld” dans une console.

Donc pour afficher HelloWorld, il va nous falloir faire une méthode main. C’est la méthode appelée au lancement d’un programme java…

C’est quoi une méthode?

Pourquoi la méthode main est appelée au lancement?

Comment l’application sait quelle méthode main lancer?

On répondra à toute ces questions juste après ce petit bout de code :

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

Alors c’est quoi une méthode? Il faut penser comportement. Une méthode est un comportement pour une instance d’une classe. … On s’enfonce là. Il va falloir tout un cours de programmation objet avec ça. Et bien oui, si vous voulez vraiment faire du java, il va falloir faire un minimum de programmation objet. Mais on y reviendra plus tard, promis. Pour l’instant, pensez plutôt fonction, c’est plus simple. Une méthode est une fonction ayant un ensemble d’arguments passés en paramétre.

Alors pourquoi main est appelée au lancement? Parce que c’est comme ça.

Comment l’application sait quel main lancer? Tout d’abord, une classe ne peut pas déclarer deux méthodes ayant exactement la même signature. La signature c’est le nom de la méthode et le type des arguments passés en paramétre. Donc, on ne peut pas avoir deux méthodes main ayant la même signature dans une même classe. Et si vous vous rappelez bien que l’on ne peut pas avoir deux classes public dans un même fichier et que je vous dis en plus qu’une méthode main ne peut pas être déclarée dans une classe autre que public, alors vous comprenez bien qu’il ne peut y avoir qu’une seule méthode main prenant un tableau de String en paramétre dans un fichier .java.

Mais c’est quoi ce static ? static est un autre mot-clef. Il peut s’appliquer notamment sur des méthodes. Cette méthode est alors spécifique de la classe. Tous les objets instances de cette classe auront alors exactement le même comportement lors de l’appel de cette méthode. En outre, une méthode static peut-être appelée sans avoir besoin d’instancier la classe. Une méthode static ne peut faire appel qu’à des méthodes ou attributs statics. Bon c’est un peu compliqué tout ça si on ne fait pas un peu d’objet avant. Donc on y reviendra quand on fera de l’objet.

Mais c’est quoi ce void ? C’est  encore un mot-clef. Void veut dire que la méthode ne renvoie rien en retour. Quand on écrit une méthode, on est censé déclarer le type de retour. C’est à dire préciser la nature de l’élément retourné par la méthode une fois qu’elle a fini son exécution. Ici on déclare que la méthode ne renvoie rien.

3) Mettons un peu de corps dans notre méthode.

public class HelloWorld
{
public static void main(String[] args)
{
System.out.print(”HelloWorld”);
}
}

System.out : on demande l’attribut public “out” de la classe System (donc out est un attribut static). Si vous ne me croyez pas vous pouvez aller faire un premier tour sur l’api java. “out” est de type PrintStream et posséde une méthode print qui prend en paramétre une String. Pour ceux qui en doutent, “HelloWorld” est une String (chaîne de caractéres).

4) Maintenant il faut qu’on compile ce fichier HelloWorld.java en un bytecode que l’on appelera HelloWorld.class. On pourra ensuite exécuter ce bytecode sur n’importe quelle JVM.

Lancez une console, ou un terminal, comme vous préférez. Ce qui vous semble le moins lourd. :)

Tapez une ligne de commande qui ressemble à ça, modulo le chemin de votre JDK :

/chemin_de_jdk/bin/javac HelloWorld.java

Normalement, si tout se passe bien, vous avez compilé votre première classe java en bytecode. Vous demandez à l’application javac de compiler le fichier HelloWorld.java plus précisément. Cela a pour conséquence de créer un fichier HelloWorld.class.

Maintenant tapez :

/chemin_de_jdk/bin/java HelloWorld

Et là dans votre terminal devrait s’afficher : HelloWorld. Vous avez demandé à l’application java d’interpréter le bytecode HelloWorld.class

Vous avez codé, compilé et exécuté votre premier programme Java. Félicitations!!! ;)