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 cœur 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.