Java c0ding
aka c0dit0, erg0 sum
Note à moi même : putain pourquoi
je me suis lancé dans cet article de programmation Java ?!?! Et merde
et merde et galère et GALÈRE !!
Je suis déjà à la seconde page web, arf !! Mais puisque
c'est commencé on continue ;)
Pour ceux qui ne connaissent pinuts de la programmation
objet, vous allez voir c'est pas si difficile.
Tout d'abord la programmation objet (POO = Programmation Orienté Objet)
est une TECHNIQUE de programmation. Par exemple en C++ vous pouvez programmer
en objet mais vous n'y êtes pas obligé.
Avec Java par contre c'est obligatoire, c'est bien pour cela que je vous en
parle (si vous comprenez pas cette phrase jettez vous immédiatement
par la fenêtre. Pourquoi ? Sais pas !).
Ne pas réinventer la roue
Au fur et à mesure que les langages de programmation évoluaient,
le travail du programmeur était facilité.
Vous connaissez tous la programmation procédurale ; elle consite à
créer des fonctions capables de traiter un type fixe de donnée.
Ce type de programmation est celui du C. Si vous créez une fonction
qui prend deux int comme arguments et que vous lui filez deux caractères,
la fonction vas vous tirez la gueule. Mais c'est la programmation de papa
tout ça !!!
Dans C++ on peut par exemple créer objet 'couple'. Ce couple est constiué
de deux éléments du même type. Ce type peut être
des pantoufles, des chats, des numéros de LOTFREE, n'importe quoi.
Maintenant comment on fait pour savoir si les pantoufles, les chats etc sont
les mêmes ?
En C vous allez gentiment faire une fonction egalChat(chat x,chat y), une
fonction egalPantoufle(pantoufle a, pantoufle b) etc
En C++ vous faites une class qui servira de modèle :
template <class elem> class couple{
elem p,s;
public:
void crecouple(elem x,elem y){p=x; s=y}
elem premier(){return p;}
elem second(){return s;}
friend int egal(couple a, couple b);
}
Vous inquiétez pas on vas pas faire du
C++. C'est juste un exemple sur la POO. Le mot template signifie que la classe
couple est générique et quelle pourra accepter n'importe quel
type de donnée.
Lors de la construction de cette classe il faut bien évidemment pouvoir
désigner (même si c'est de façon abstraite) les données
que on devra manipuler. Pour cela on déclare une donnée du type
'elem'.
Un couple est donc un objet qui contient deux éléments, l'un
est noté p comme premier, et l'autre s comme second.
On fait une fonction qui renvoit le premier élément et une autre
pour le second. Là aucun problème.
Mais comment faire une fonction générique qui saura si les deux
éléments sont égaux ?
En effet on ne compare pas 2 chats de la même façons que l'on
compare deux pantoufles. Là encore on est limité. Il va falloir
faire en sorte que chaque type de couple ai une fonction de comparaison qui
lui est propre.
En attendant si on veux créer un type couple de chat il nous suffira
taper :
typedef couple<chat> couple_de_chat;
Bien sûr on considére que l'on a déclaré un type
'chat' avant. De la même façon on peut dire qu'un point est composé
de deux valeurs : abscisse et ordonnée.
typedef couple <float> point;
A partir d'une simple notion de couple on a défini ce qu'était
un point. Maintenant pourquoi pas définir carrément une ligne
?
typedef couple<point> ligne;
Hé oui, une ligne c'est deux points.
Fantastique : vous avez compris l'un des avantages de la programmation objet
: la généricité.
On vas profiter de cet exemple pour voir un autre
concept... Dans l'intro je vous ai parlé de droits d'accès aux
données. En effet en C vous avez le mot clé 'struct' qui permet
de rassembler plusieurs variables sous un seul nom. Par exemple on peut définir
un objet CD :
typedef struct{
char * artiste;
char * album;
int annee;
int categorie;
float prix;
} CD;
De cette manière on peut créer
le dernier Rancid de cette manière :
CD rancid_indestructible;
Mais là les valeurs du CD ne sont pas fixées, on est donc obligé
de les mettre nous même :
rancid_indestructible.artiste="rancid";
rancid_indestructible.album="indestructible";
rancid_indestructible.annee=2003;
C'est lourd non ? Et puis imaginons que l'on veuille protéger certaines
valeurs d'un CD. En effet sur un CD seul le prix change. Or là si le
programmeur fait une erreur il en subit les conséquences.
On peut considérer que la notion de 'class' que l'on trouve en C++
et en Java est en fait une 'struct' avec des notions de droit.
Par défaut tous les éléments d'une struct sont 'public',
c'est à dire que n'importe qui peut y accèder.
A l'opposé dans une class tous les éléments sont 'private'
par défaut en C++. Cela veut dire que seul l'objet en lui même
peut y accéder.
Si vous regardez plus en détail l'exemple du couple vous appercevez
que nous avons déclaré deux éléments p et s dans
une CLASSE couple. Donc ces deux éléments sont privés.
Si vous essayez de les modifiez de cette façon :
monpoint.p=5;
(dans l'exemple d'un point) alors vous aurez une erreur. Même juste
un :
cout<<"Les absisses du point valent "<<monpoint.p<<endl;
Vous affichera une erreur. Alors que
cout<<"Les absisses du point valent "<<monpoint.premier()<<endl;
affichera l'absisse du point. Pourquoi on a accès à la fonction
premier() ? Tout simplement parce que avant on a mis le mot clé 'public:'
Ca y est vous avez compris un second avantage de la POO : l'encapsulation.
C'est le fait de pouvoir 'cacher' les valeurs de tel ou tel objet. Il reste
cependant un mot clé intermédiaire 'protected' que l'on verras
tout à l'heure.
Java dans la pratique
Maintenant on va utiliser la syntaxe Java. Vous inquiétez pas : c'est
plus simple (pas de template).
Maintenant on est embauché par un zoo pour faire un programme de gestion
des animaux. Tous les animaux sont différents bien sûr, ne serais-cequ'au
niveau de leur race... mais ils ont tous des points communs : ils ont un age,
un poids, voire un nom.
Vous pouvez bien entendu programmer bétement et faire une classe chimpanzé
avec comme valeur l'age, le poids, le nom. Ensuite quand vous faire la classe
ours vous remmetez l'age, le poids...
STOP !!! Vous avez pas compris le principe de la programmation Objet ou quoi
?
Il est bien evidemment possible de créer une classe comune :
class animal{
int age;
int poids;
String nom;
}
Déjà on remarque que les char*
c'est fini... Putain ça fait du bien.
Maintenant créons une classe kangourou qui contient les même
éléments que tous les animaux mais aussi ses données
propres :
class kangourou extends animal{
int longueur_de_saut;
}
Ca y est. Tout est fait dans le mot clé 'extends'. Il est là
pour dire que la classe kangourou 'hérite' des données de la
classe animal.
Bien, maintenant faisons un peu de pratique. On met le premier texte (classe
animal) dans un fichier que l'on nomme pet.java (pet=animal en anglais pour
ceux que j'ai vu rigoler (bande de nazes !!)).
[sirius@localhost java]$ javac pet.java
[sirius@localhost java]$ ls
animal.class pet.java
Comme vous pouvez le constater, notre fichier
pet.java ne s'est pas compilé en un pet.class mais en un animal.class
Cela veut dire que le compilateur ne prend en compte que le nom de la classe.
A partir de maintenant je vous conseillerais (c même plus qu'un conseil)
de donner exactement le même nom à votre fichier que celui donné
à votre class.
Si vous faites une classe MaClasseJ4v4 alors le fichier devra s'appeler MaClasseJ4v4.java
Il faut aussi respecter majuscules et minuscules. Pourquoi il faut faire ça
? Déjà pour vous ce sera plus facile pour vous y retrouver dans
vos fichiers. Ensuite le compilateur Javac n'aime pas toujours que l'on donne
des noms différents, on va voir pourquoi.
Maintenant on efface les deux fichiers et on met les deux classes dans un
fichier kangourou.java
[sirius@localhost java]$ javac kangourou.java
[sirius@localhost java]$ ls
animal.class kangourou.class kangourou.java
On remarque que plutôt que de créer un seul fichier kangourou.class
il a créé deux fichiers .class correspondant chacun à
une des classes que l'on a défini. Bref autant mettre chaque classe
dans un fichier : la classe kangourou dans kangourou.java et la classe animal
dans animal.java
Faisons une autre expérience : on vient de faire ce que je viens de
dire (on a deux fichiers java). On enlève les fichiers .class
[sirius@localhost java]$ ls
animal.java kangourou.java
Que se passe t'il si on compile la classe kangourou sans avoir compilé
la classe dont elle hérite ?
[sirius@localhost java]$ javac kangourou.java
[sirius@localhost java]$ ls
animal.class animal.java kangourou.class kangourou.java
Putain Fantomas est passé !! Nan en fait c'est le compilateur qui a
lu qu'il avait besoin d'une class animal pour compiler la classe kangourou.
Alors il a pris ses baskets et il est allé chercher la classe en question
puis il a compilé. Ensuite il a tranquillement pu compiler kangourou.java
Maintenant répétons cette opération mais cette fois on
a mit la class animal dans un fichier pet.java
[sirius@localhost java]$ ls kangourou.java pet.java [sirius@localhost java]$ javac kangourou.java kangourou.java:1: cannot resolve symbol symbol : class animal location: class kangourou class kangourou extends animal{ ^ 1 error
Comprenez par là que le compilateur n'a
pas trouvé la classe animal. Pourquoi ? Parce quand il a mis ses baskets
pour aller chercher le fichier animal.java, il l'a pas trouvé !!! Voilà
pourquoi il faut que le nom de la classe = nom du fichier.java
Profitez en pour regarder les informations fournies par le compilateur. D'abord
il nous dit dans quel fichier et à quelle ligne se trouve l'erreur.
Jusque là rien de particulier. Ensuite il nous dit le type d'erreur
: 'cannot resolve symbol'. C'est une erreur assez fréquente. Elle est
liée aux classes. Dans notre cas il n'a pas trouvé la classe
animal mais ce type d'erreur peut apparaitre si on fait référence
à une variable ou une fonction qui n'est pas présente dans une
classe.
Premier programme
On va passer le vitesse supérieure pour vous expliquer ça. Modifier
votre fichier kangourou.java pour qu'il ressemble à ça :
class kangourou extends animal{
private int longueur_de_saut;
public static void main(String args[]){
kangourou Kangoo=new
kangourou();
Kangoo.xpoids=25;
Kangoo.nom="Kangoo";
System.out.println("Le
poids de "+Kangoo.nom+" est de "+Kangoo.poids);
Kangoo.longueur_de_saut=25;
}
}
Que se passe t'il si on compile kangourou.java ?
kangourou.java:5: cannot resolve symbol symbol : variable xpoids location: class kangourou Kangoo.xpoids=25; ^
Même type d'erreur sauf qu'il s'agit
d'une variable inexistante dans la classe kangourou. Il s'agit souvent d'une
faute de frappe.
Si on étudie un peu la nouvelle classe kangourou on remarque pas
mal de changements. En particulier l'apparition d'une nouvelle méthode.
Jusqu'à présent nous n'avions fait que des classes avec des
variables mais on avait pas fait de programmes à vraissemblement
parler. Pour faire un programme il faut bien évidemment un
point d'entrée (une expression très courante en cracking ;).
Il s'agit s'implement du début du programme. Ce point d'entrée
a toujours le même profil (aprennez le par coeur) :
public static void main(String [] args)
Décomposons un peu ça. Tout d'abord
le static permet de dire que la fonction est la même pour toutes les
instances de la classe. Comme ça doit pas vous sembler ultra clair
on va refaire un peu de POO.
Nous on a déclaré une classe kangourou. C'est bien joli mais
c'est générique. En gros notre classe désigne tous
les kangourous à travers le monde. Nous on veut travailler avec un
seul kangourou. Dans notre exemple on en veut un précis qui s'appelle
Kangoo (comme la caisse). On peut dire que Kangoo est une instance de tous
les kangourous à travers le monde. On peut aussi entendre le terme
occurence mais il est moins rattaché à la programmation. Bref
une instance c'est un exemplaire.
Si on veut expliquer un peu plus le mot static
on peut par exemple mettre la variable moyenne_de_vie en static dans la
classe kangourou parce que tous les kangourous ont la même moyenne
de vie (moyenne sur le monde).
public on a déjà vu ; ça veut dire que cette fonction
est accessible par tout le monde... heureusement parce que c'est le point
d'entrée du programme.
void main je pense que vous connaissez (mais pour ceux qui n'ont pas encore
programmé ça veut dire que c'est la fonction principale et
quelle ne renvoie aucun résultat).
Ensuite le String []args. Il s'agit évidemment des arguments du programme.
Quelle est la différence avec le C ? Piou !! La classe String possède
pas mal de méthodes et c'est largement à notre avantage. Le
nom de la String n'est pas figé, rien ne vous empéche de mettre
argv. Ensuite les crochets : ils peuvent être aussi bien devant que
après le nom de la chaîne.
Autre spécificité : le premier argument est accessible par
args[0] et le nombre d'agument s'obtient avec args.length(). Cette méthode
renvoie un entier (int).
Programmation Objet et droits d'accès
Je vous ai dit qu'en Java tout était des classes. String en est une
par exemple. Alors comment se fait-il qu'on ne fait pas appelle à
sa librairie ?
L'explication est simple. La librairie java.lang.* est chargée automatiquement
car elle contient... le noyau de Java. Sans elle pas de programme !
Maintenant vous allez me demander pourquoi on ne charge pas
la classe int... Il y a quelques exceptions en Java : certains type basiques
ont été gardés. Ainsi int, char, float, short, byte,
long, double et boolean ne sont pas des classes. En revanche les tableaux
(int [] ou byte [] etc) sont vraiment spéciaux, on peut les classer
entre les types basiques et les classes. On verra peut-être ça
(j'insiste sur le peut-être). Le mot clé null a aussi été
conservé. Le type char a subit quelques modifications car il n'est
plus sur 1 mais sur 2 octets !! Oui 2 pour le prix d'1 (en dirait du Bellemare
au T.V. H.I.É.). Pourquoi deux octets ? Pour respecter la norme unicode.
Ainsi on peut représenter tous les caractères à travers
le monde. Tous ? Non ! Une bonne dose d'irréductibles caractères
Chinois prennent trop de place pour en faire partie.
Pour en finir avec cette fonction main()... aprennez là par cur
et faites pas chier.
La première opération de notre programme est d'instancier un objet kangourou. Pour cela on utilise le même opérateur qu'en C++, l'opérateur new(). Cet opérateur permet de réserver de la place pour mettre au monde notre objet (un kangourou dans notre cas). Dans notre cas le kangourou s'appelle Kangoo. On verra la création d'objets plus en détail dans pas longtemps.
La ligne suivante est, une fois l'erreur corrigée
(poids à la place de xpoids), une affectation tout ce qu'il y a de
plus simple. On pourrait se demander si cela va provoquer une erreur car
la classe kangourou ne possède pas d'attribut nommé 'poids'.
Mais ça marche parce que la classe kangourou hérite de la
classe animal qui possède justement l'attribut poids.
Une classe hérite des attributs et des méthodes de son père.
La morale c'est vous pouvez être con, c'est pas de votre faute mais
de celle de vos ancètres LoL.
J'en voit certains qui cogitent. En effet puisque
l'attribut poids n'est pas public il doit être private et par conséquent
la classe kangourou, bien que fille de la classe animal, ne devrait pas
pouvoir accèder à cet atribut. En fait à la différence
de C++, le droit d'accès par défaut est un droit qui s'appelle
'friendly' (nom non-officiel pompé sur le C++).
Un élément friendly est accessible uniquement aux classes
de son package (pour tout à l'heure). Or dans notre cas nous n'avons
pas défini de package donc l'accès se fait sans problème.
Il reste un type d'accès dont j'ai parlé ya pas mal de temps : protected. Un élément protected est public vis à vis de son package, de sa classe et de ses sous classes mais private vis à vis du reste.
Bref du plus accessible au moins accessible on a : public ; protected ; friendly ; private
Je sais que autant de texte à la suite c'est lourd alors je met une
image entre les deux. Hésitez pas à faire une pause aussi.
Maintenant passons à un concept sympa
: les packages.
Dans l'intro j'ai dit que les packages étaient l'équivalent
des librairies. Contrairement à de simples fichiers d'entêtes
dans le C et C++, les packages permettent de regrouper des classes par affinité.
Java propose une bonne dose de packages que l'on utilise pour programmer.
Par exemple le package java.lang.* est automatiquement chargé. Il
permet d'utiliser les fonctions System etc.
Donc un package c'est un ensemble de classes. Le symbole étoile de
java.lang.* signifie que l'on prend toutes les classes du package java.lang.
Il est bien évidemment possible de créer ses propres packages
mais là encore il y a des règles à respecter : Si on
veut créer un package MonPackage il faut d'abord créer un
répertoire du même nom et y mettre les classes du package.
Ensuite il faut que les classes commencent par la ligne package MonPackage;
pour déclarer la classe comme partie du package.
Les avantages : tout simplement la possibilité
d'utiliser ses librairies personnels. Ainsi si on veut utiliser ses classes
perso on mettra dans notre programme : import MonPackage.*;
J'ai aussi parlé aussi d'afinité et non sans raison puisque
les accès friendly et protected se basent sur les relations inter-classes.
En gros un package c'est un groupe de potes qui se connaissent bien, ils
se dépannent quand ils sont dans la merde... Ils s'échangent
donc plus facilement les données que deux classes étrangères.
Au niveau du compilateur : le compilateur recherche le répertoire qui porte exactement le même nom que le package. Il regarde dans les librairies standart de Java, dans le répertoire courant et dans les répertoires fixés dans la variable $CLASSPATH. Personnellement je ne l'ai pas fixé sous Linux car je met tout dans le même répertoire.
On reprend le travail avec nos classes habituelles
:
---------------animal.java-------------
package p1;
class animal{
protected int age;
int poids;
String nom;
}
------------fin animal.java----------
-----------kangourou.java----------
package p1;
class kangourou extends animal{
private int longueur_de_saut;
}
---------fin kangourou.java--------
--------------prog.java---------------
import p1.*;
public class prog{
animal inconu;
prog()
{
try{
inconu
= new animal();
inconu.age=5;
}
catch(Exception ex){System.out.println(ex);}
}
public static void main(String []args)
{
prog p=new prog();
}
}
----------fin prog.java-------------
Déjà vous constatez que on a
une classe dévoué à l'exécution du programme
; c'est bien plus 'propre' niveau programmation. Comme ça nos classes
animal et kangourou sont totalement indépendantes et on pourra les
réutiliser quand on le voudra.
Les classes animal et kangourou font partie du package p1. Les fichiers
animal.java et kangourou.java se trouvent dans le répertoire p1 (qui
se trouve dans mon rép courant). prog.java est dans le rep courant.
Quand on compile on a :
prog.java:4: p1.animal is not public in p1; cannot be accessed from outside package animal inconu; ^ prog.java:11: p1.animal is not public in p1; cannot be accessed from outside package inconu = new animal(); ^ prog.java:11: animal() is not public in p1.animal; cannot be accessed from outside package inconu = new animal(); ^ prog.java:13: age has protected access in p1.animal inconu.age=5; ^ 4 errors
Normal puisque on a pas déclaré nos classes
comme public. Il faut mettre public class animal...
On va passer à une autre page car celle-ci est pas mal remplie.
Mais avant on va revoir très rapidement ce que on
a vu et on va ajouter quelques truc :
Une classe est une description générique. Elle décrit
un 'type'.
Une instance d'une classe est un 'objet'. Chaque objet a son existence propre
cependant ils peuvent avoir des valeurs communes que l'on déclare
par static.
Pour créer un objet (une instance) on utilise la fonction new().
Une classe doit se trouver dans un fichier java du même nom.
Par convention le nom d'une classe commence par une majuscule (et le nom
du fichier .java aussi par conséquent).
En POO une variable est appelée un 'attibut' et une fonction est
appelée une 'méthode'.
Par convention toujours, les noms des attributs et des méthodes commencent
par une minuscules.
Il y a 4 types d'accès : private (accessible seulement par l'objet
lui même), friendly (par défaut), protected (accessible si
dans la même 'famille') et public (accessible par tous).
La notion de packages permet de créer des librairies et/ou de faire
un système d'affinité.
La fonction principale a toujours le même profil : public static void
main(String []args).
Pour ce qui est des conventions, des fois je les respecte
des fois non...
Je viens de voir que j'ai oublié quelques concepts importants, on
y go.