Welcome to Bukkit France

Inscrivez-vous maintenant pour profiter d'un accès total à tout le contenu offert par la meilleur communauté Bukkit française ! Une fois inscrit et connecté, vous pourrez contribuez à la communauté en postant vos propres sujets et questions ou en répondant à ceux existants. Vous pourrez aussi customiser votre profil, recevoir des points de réputations, communiquer avec les autres membres via le chat, et plus encore! 

  • Annonces

    • Pskyco

      Bukkit France passe sous Discord !   02/20/16

      Bukkit France est désormais passé sur Discord, au revoir donc notre vieux Teamspeak ! Téléchargez le client et venez nous rejoindre sur notre salon en suivant les instructions suivantes.
      M-à-j du 25/02/2017 : Désormais, seuls les comptes actifs sur le forum se verront donner l'accès au Discord, ce dernier n'est pas une plateforme d'aide de la même manière que le chat.
  • billets
    47
  • commentaires
    40
  • vues
    31704

À propos de ce blog

Cours de Java

Billets dans ce blog

SystemGlitch

blog-0363932001431876617.png

Vous avez peut-être remarqué, dans quelques chapitres précédents, des morceaux de code un peu surprenants tels que le suivant :

 
joueurs.put("Pixii", new Pretre());

 

Ici je n'ai pas enregistré mon objet Pretre dans une variable, pourtant il en reste fonctionnel et utilisable ! Nous avons déclaré ici un objet anonyme ! Ici nous l'insérons dans une Hashmap, nous pourrons donc le récupérer, mais si nous le faisions "dans le vide", la mémoire nécessaire à son instanciation serait bien utilisée et perdue car on ne pourrait plus utiliser ultérieurement. Faites donc bien attention à la gestion de la mémoire quand vous utilisez l'anonymat. Pas besoin de revenir là-dessus, vous savez que le constructeur sera de toute manière appelé grâce à l'opérateur new.

 

Voyons maintenant un aspect auquel vous n'avez peut-être pas pensé et que j'ai pourtant déjà utilisé dans certains exemples.

Quand une valeur nous est nécessaire mais juste pour une fois, pas besoin de la sauvegarder dans une variable.

public int addition(int nombre1, int nombre2) {
     return nombre1 + nombre2;
}


//Exemple:
int i = addition( 5+9 , 2 );

 

Ici nous aurons un Integer anonyme ("5+9"). Nous utilisons le résultat du calcul comme argument, mais sans le stocker. La méthode recevra directement 14. On aurait pu également faire comme suit :

int i = addition( addition(5,9) , 2);

 

Ce qui introduit la suite de mon cours !

 

Voyons maintenant comment ça se passe du côté des objets. A peu près pareil en réalité, sauf que si l'on appelle des méthode retournant un objet, on pourra utiliser les méthodes de celui-ci !

public Chevalier getChevalier() { //Cette méthode est bien-sûr inutile, c'est un simple exemple.
	return new Chevalier();
}

//Exemple:	
getChevalier().combattre();

 

Cela nous mène alors au code suivant :

new Chevalier().combattre();

 

Ici on instanciera un Chevalier uniquement pour appeler sa méthode combattre(). L'objet est ensuite perdu.

Allons plus loin. Nous pouvons donc enchaîner des actions et les stocker dans une seule variable, en une seule ligne donc !

Je tiens à souligner le fait de ne jamais oublier le type du retour des méthodes. Suivez attentivement cet exemple :

String[] mots = "Vive Bukkit.fr".substring(5).concat(" c'est génial!").split(" ");

 

Étape par étape :

  • Je déclare un tableau de String
  • Je crée un String anonyme "Vive Bukkit.fr".
  • Je récupère un String via la méthode substring(). (Donc pas le bon type car notre tableau est de type String[] et non String)
  • J'ajoute à ce String " c'est génial". Je récupère donc une fois de plus un String.
  • Pour finir, je split() le String final ("Bukkit.fr c'est génial!") pour obtenir un String[].

 

Vous commencez à cerner le principe?

 

Très bien! Je vais maintenant vous présenter un autre aspect de l’anonymat. Il est possible de créer des classes anonymes ! Leurs méthodes pourront être redéfinies. Je pense qu'un exemple ne serait pas de refus !

Chevalier chevalier1 = new Chevalier() {
			
	public void combattre() { //Redéfinition de la méthode combattre()
		System.out.println("Je combats pour le roi!");
	}
};    //N'oubliez pas le ';'!

chevalier1.combattre(); //Appel de la méthode redéfinie
new Chevalier().combattre(); //Appel de la méthode non redéfinie


//Résultat:
Je combats pour le roi!
Les têtes vont rouler!

 

Ce n'est pas plus compliqué que ça! Dès que vous aurez compris le concept, tout ira comme sur des roulettes !

Pour aller plus loin, il est aussi possible de redéfinir des méthodes via la classe anonyme et d'appeler une méthode ensuite !

new Chevalier() {
			
	public void combattre() {
		System.out.println("Je combats pour le roi!");
	}
}.combattre();

 

Ce concept est surtout utile pour les interfaces. Vous pouvez plus ou moins instancier une interface en redéfinissant donc toutes les méthodes qu'elle contient ! Oui cela peut paraitre surprenant, même très surprenant !

ResponsableArgent resp = new ResponsableArgent() {

	@Override
	public void gererRichesses() {
		System.out.println("J'aime l'odeur des billets au petit matin!");
	}

	@Override
	public int getRichesses() {
		return richesses; //Ici cela fonctionne car la variable est public. Pensez bien à l'accessibilité!
	}
			
};

 

Cette notion vous sera très utile plus tard, notamment quand vous vous lancerez dans le développement d'applications graphiques ou dans le développement de plugins pour Bukkit/Spigot/...

 

Ceci me mène donc à vous expliquer ce que sont les classes locales ! Il est possible de créer des classes uniques au sein d'un bloc, d'une méthode. Démonstration :

public static void main(String[] args) {
	class Exemple {
			
		public Exemple() {
			System.out.println("Instanciation de la classe locale.");
		}
			
		public void direQuelqueChose() {
			System.out.println("Quelque chose...");
		}
	}
		
	Exemple exemple = new Exemple();
	exemple.direQuelqueChose();
}

 

Ces classes ne peuvent pas être static et ont accès au champs de la classe englobante. Elles ont aussi accès aux paramètres de la méthode dans laquelle elle est définie, ainsi qu'aux variables locales à cette même méthode, à condition qu'elles aussi soient déclarées final.

 

Dernier aspect de ce chapitre: les classes internes !

 

Ces classes sont très ressemblantes aux classes locales, la différence, c'est qu'elles sont déclarées en dehors de toutes méthodes, telles des variables de classe. Exemple :

public class Cours {


	public static void main(String[] args) {
		//...
	}

	public class Exemple {

		public Exemple() {
			System.out.println("Instanciation de la classe locale.");
		}

		public void direQuelqueChose() {
			System.out.println("Quelque chose...");
		}
	}

}

 

Vous pouvez bien évidemment choisir l'accessibilité de votre classe interne à l'aide des mots-clefs prévus à cet effet. Elles ont accès aux méthodes et champs de la classe englobante.

Pour finir, elles peuvent être déclarées static. En faisant cela, votre classe deviendra indépendante de l'englobante. On peut ainsi instancier la classe interne sans avoir besoin d'instancier la classe englobante au préalable.

 

 

Résumé:

 

  • Vous pouvez déclarer un objet anonyme, c'est à dire ne pas l'enregistrer dans une variable.
  • Cet objet peut tout de même être utilisé.
  • Les classes anonymes sont déclarées au sein d'une méthode.
  • Elles ne sont accessibles que depuis cette même méthode.
  • Elles permettent de pouvoir redéfinir des méthodes du type choisi.
  • Vous pouvez instancier une classe anonyme implémentant une interface directement à l'aide du nom de l'interface.
  • Vous devrez redéfinir toutes les méthodes de cette interface.
  • Les classes locales sont des classes déclarées au sein d'une méthode, au même titre que les classes anonymes, sauf que vous créez un nouveau type.
  • Les classes internes sont définies en dehors de toute méthode.

 

 

JeremGamer

 

 

2cp1ED5.pngtNIpRtq.png

SystemGlitch

blog-0438098001431876263.png

Dans le chapitre précédent, je vous avais parlé des listes. Mais il existe deux autres types de collections: les Map et les Set.

 

Commençons par les Set. Ils ressemblent aux listes, mais ont cependant quelques différences assez déterminantes.

Pour commencer, ils ne possèdent pas autant de méthodes que les listes. Vous ne pourrez pas utiliser les index avec elles.

 

L'autre différence, c'est qu'ils n'acceptent pas deux fois la même valeur. Par exemple, si vous voulez y ajouter deux fois un String "Je suis Bukkitien", le deuxième ne sera pas ajouté. (Cela est aussi pour valable pour null ou pour tout objet)

 
HashSet<String> set = new HashSet<String>();
set.add("Je suis Bukkitien!");
set.add("Vive Bukkit.fr!");
set.add("Je suis Bukkitien!");

for(String s : set)
	System.out.println(s);

 

Si vous essayez ce code, vous obtiendrez le résultat suivant :

Je suis Bukkitien!
Vive Bukkit.fr!

 

Petite astuce, la méthode add() retourne un boolean. Vous pouvez ainsi vérifier si la valeur à été ajoutée ou non! ;)

 

Ils sont particulièrement adaptés à la manipulation d'une grande quantité de données, surtout en lecture. (Avec un grand nombre d'objets, leur performances en insertion sera moindre).

 

Il en existe plusieurs, plus ou moins restrictives. Certains n'accepteront pas null, c'est le cas des TreeSet. Commençons par le plus commun et le plus performant en lecture : le HashSet.

 

Vous en avez eu un exemple juste au dessus et vous pouvez voir qu'ils s'utilisent de la même manière que les listes en insertion. Cependant en lecture, il faudra procéder différemment car la méthode get() n'existe pas pour les Set !

Souvenez-vous, il s'agit d'une Collection, qui implémente donc Iterator. Pour la parcourir, on utilisera donc un Iterator, ou avec une boucle for comme dans l'exemple ci-dessus.

Iterator it = set.iterator();

while(it.hasNext())
    System.out.println(it.next());

 

Cependant, si vous voulez trouver précisément un objet ainsi, optez pour un LinkedHashSet. Ces derniers ont un ordre d'itération prévisible, il est donc possible de trouver un élément par son index :

String e;
int count = 0;
for(String s : set) {
	if(count == index) {
		e = s;
		break;
	}
	count++;			
}

 

Si vous voulez que votre ensemble soit constamment trié, optez pour un TreeSet. Ainsi ses éléments pourront être restitués dans un ordre bien précis et ordonné. Par exemple, si le type générique est String, alors votre objet s'occupera de ranger vos éléments par ordre alphabétique.

 

 

Voyons maintenant les Map. Ces collections fonctionnent un peu différemment et s'avèrent extrêmement pratiques. Elles fonctionnent avec un couple clé/valeur, un peu comme les listes avec index/valeur, mais cette fois-ci, n'importe quel type peut-être utilisé comme clé.

Chaque clé est unique, c'est à dire que si vous ajoutez deux valeurs différentes à la même clé, seule celle ajoutée le plus tard dans le code sera conservée. Grâce à leur fonctionnement, il vous paraîtra donc logique que plus le volume de données qu'elle contient sera élevé, plus elle sera lourde et lente, et cela car elle doit stocker une valeur en plus par élément par rapport aux autres collections !

Nous allons enfin pouvoir associer un pseudo ou un identifiant aux objets de nos joueurs ! (dans la cadre de mon exemple global)

 

Sans plus tarder, je vous présente la Hashtable ! Leur utilisation est très différente des autres collections. Tout d'avord, c'est car ces classes n'implémentent pas Collection ! Petit exemple d'utilisation :

Hashtable<String , Personne> joueurs = new Hashtable<String , Personne>();
	
joueurs.put("xXPGMdu96Xx", new Chevalier());
joueurs.put("Overlord", new Tresorier());
joueurs.put("Pixii", new Pretre());
		
System.out.println("Noms des joueurs: ");
		
for(String s : joueurs.keySet())
	System.out.println(s);
		
System.out.println("--------------------");
System.out.println("Objets des joueurs: ");
		
for(Personne p : joueurs.values())
	System.out.println(p);
		
System.out.println("--------------------");
System.out.println("Récupération de l'objet par le pseudo \"Overlord\": ");
System.out.println(joueurs.get("Overlord"));

 

Je vous invite à tester ce code et constater le résultat. Vous pouvez voir les différentes manière de parcourir ce type d'objet, et comment y ajouter des valeurs. Maintenant revenons en détails sur quelques points :

  • La méthode keySet() retourne un Set des clés. La méthode values() retourne quand à elle une Collection des valeurs. Un bon moyen de détacher les clés des valeurs et inversement.
  • La méthode get() renvoie la valeur liée à la clé donnée en argument, et null si la clé n'existe pas, ou si null lui est associé (dans le cas des HashMap, qui acceptent null, contrairement aux Hashtable).
  • J'ai parcouru mes valeurs avec ces méthodes, mais il existe un autre moyen, mais cette fois il ne s'agit pas d'Iterator mais d'Enumeration.

 

Voici comment lire votre Map avec une Enumeration.

System.out.println("Noms des joueurs: ");

Enumeration<String> cle = joueurs.keys();
while(cle.hasMoreElements())
    System.out.println(cle.nextElement());

System.out.println("--------------------");
System.out.println("Objets des joueurs: ");

Enumeration<Personne> obj = joueurs.elements();
while(obj.hasMoreElements())
    System.out.println(obj.nextElement());

 

Ensuite, voici quelques tests que vous pouvez effectuer pour savoir si votre Map contient soit une clé voulue, soit une valeur voulue :

joueurs.containsKey("Overlord"); //Renvoie true si la Hashtable contient la clé donnée en argument
joueurs.containsValue(chevalier); //Renvoie true si la Hashtable contient la valeur donnée en argument

 

Les Hashmap classent automatiquement les couples par leur clés, à la manière des TreeSet. Si vous voulez conserver l'ordre d'ajout, optez pour une LinkedHashmap.

 

En code cadeau, je vous donne une petite astuce pour récupérer une clé à partir d'une valeur. Cela peut servir, rarement, mais il est possible d'en avoir besoin. ;)

public String getNameForPerson(Personne personne) { //Choisissez évidemment le type de retour en fonction du type des valeurs de votre Map
	if(joueurs.containsValue(personne)) { //On vérifie que la personne est bien dans la Map pour éviter des actions superflues
		for (Map.Entry<String , Personne> e : joueurs.entrySet()) {
			if(e.getValue().equals(personne))
				return e.getKey();
		}
	}
	return null;
}

 

Résumé:

 

  • Les Set n'acceptent pas les doublons et ne se lisent pas avec des index.
  • Ils sont adaptés à la manipulation d'un grand volume de données.
  • Pour les parcourir, on utilise soit une boucle for, soit un Iterator.
  • Les Map fonctionnent avec un couple clé/valeur.
  • Chaque clé est unique, mais il peut y avoir la même valeur associée à plusieurs clés.
  • Souvenez-vous bien de leur utilisation, elle est très différente des autres collections.
  • A la place d'un Iterator, on utilisera une Enumeration.

 

 

JeremGamer

 

 

2cp1ED5.pngtNIpRtq.png

SystemGlitch

blog-0305781001431876249.png

Ce chapitre s'inscrit dans la longévité du précédent. Nous allons parler de certaines classes de l'API Java utilisant la généricité: les Collection. (sans 's' car je vous cite là le nom de leur super-classe)

Une fois que vous y aurez touché, vous ne pourrez plus en passer! En effet il s'agit de sortes de tableaux, mais en bien plus pratique!

 

Nous verrons les principales collections, car il en existe un grand nombre, mais certains sont beaucoup plus utilisés que d'autres.

 

Commençons par les listes. C'est sans doute celles que vous utiliserez le plus. Les listes implémentent l'interface List (qui ne peut donc être instanciée). La plus fréquente est l'ArrayList. On pourrait la comparer à un tableau à une dimension. Elles sont extrêmement simples à utiliser. Exemple :

 
ArrayList list = new ArrayList();
list.add(42);
list.add("Vive Bukkit.fr!");
list.add(3.9F);
list.add(new Tresorier());
list.add(null);

for(Object o : list)
	System.out.println(o);

 

Il y a pas mal de choses à dire sur ce code.

Commençons pas le fait que vous remarquerez que l'on peut ajouter n'importe quel type de donnée à notre liste. Il est même possible d'ajouter null ! Si vous exécutez ce code, vous pourrez constater ceci :

42
Vive Bukkit.fr!
3.9
fr.bukkit.jeremgamer.cours.[email protected]
null

 

Tous les objets sont restitués comme voulu, mais si vous voulez utiliser une méthode de l'objet de Tresorier que nous avons ajouté, vous ne pourrez pas, sauf si vous passez par un cast précédé d'un test d'instance. Mais bon ce n'est pas très pratique... Cela prendrais cette forme :

for(Object o : list) {
	if(o instanceof Tresorier)
		((Tresorier)o).gererRichesses();
	else
		System.out.println(o);
}

 

Cela nous le devons au fait que nous n'avons pas déclaré d'argument générique à notre liste! Ceux qui auront copié le code fourni dans leur IDE auront remarqué un grand nombre d'avertissements. En effet, cette classe utilise des paramètres génériques, ainsi l'utilisation de nos objets peut se faire bien plus facilement! On peut donc se permettre de faire une boucle for each comme suit :

ArrayList<String> list = new ArrayList<String>();
list.add("Vive Bukkit.fr!");
list.add("Bertrant");
list.add("Robert");
list.add("Francoise");
list.add("Germaine");

for(String s : list) {
	System.out.println(s);
}

 

Vous savez d'ailleurs que de telles boucles peuvent être utilisées avec les tableaux, mais aussi avec les collections ! Souvenez-vous, je vous en avais brièvement parlé lors du chapitre sur les boucles. ;) En réalité, vous pouvez utiliser ces boucles pour toutes les classes implémentant Iterator. Vous verrez de quoi il s'agit dans un instant.

En dehors de ça, nous avons resserré le type de nos objets en restreignant notre liste à des String et les classes en héritant. Je ne vais revenir sur le point de l'utilisation du polymorphisme, vous savez comment ça fonctionne maintenant !

 

De la même manière que pour les tableaux, on utilise des index. La différence, c'est que la taille de la liste n'est pas définie à l'avance et du coup on peut facilement gérer tout ça.

 

Maintenant voyons quelques méthodes que nous propose l'API Java pour les listes.

  • add(objet) permet d'ajouter un objet à la liste. (Souvenez-vous, les types primitifs ne sont pas acceptés !) L'objet sera ajouté au premier index non utilisé.
  • add(index , objet) permet d'ajouter un objet à l'index choisi.
  • addAll(collection) permet d'ajouter tous les éléments d'une collection à la collection courante. Il faut bien sûr que cette collection soit du même type générique. Vous pouvez aussi choisir l'index auquel votre programme commencera à ajouter, de la même manière que pour la seconde méthode présentée.
  • clear() retirera tous les éléments de la liste.
  • contains(objet) renverra un boolean. Si l'objet donné en argument se trouve dans la liste, ce sera true.
  • get(index) renverra l'objet se trouvant à l'index spécifié (null s'il n'y en a pas). Pensez bien que le premier objet est à l'index 0 !
  • indexOf(objet) retournera l'index auquel se trouve l'objet donné en argument. Si l'objet n'est pas dans la liste, elle retournera -1. Pensez donc à vérifier que l'index est bien supérieur ou égal à 0 avant d'effectuer quelque action que ce soit.
  • isEmpty() retourne un boolean. Si la liste est vide, alors ce sera true. S'il y a au moins un élément, ce sera false.
  • remove(index) et remove(objet) retirera l'objet de la liste et décalera d'un index en arrière les éléments qui le suivaient.
  • set(index , objet) permet de remplacer l'objet se trouvant à un index donné par l'objet choisi en argument.
  • size() retourne le nombre d'éléments se trouvant dans la liste. Attention, il vaudra 1 de plus que le dernier index ! (C'est logique mais mieux vaut le préciser ^_^ )
  • toArray() dont je vais rapidement expliquer l'utilisation. Cette méthode permet de transposer votre liste en un tableau du même type que le type générique de votre liste. Voici comment l'utiliser :
    Personne[] personnes = list.toArray(new Personne[list.size()]);

     

 

Ces méthodes sont communes à toutes les listes. C'est pour cela que j'enchaine avec les LinkedList, moins fréquentes mais elles s'avèrent un choix judicieux lorsque vous avez un très grand nombre d'objets dans votre liste et que vous devrez les modifier régulièrement. Pourquoi me direz-vous? Les ArrayList sont très rapides en lecture même avec beaucoup d'éléments mais plus lentes en cas de modifications de milieu de liste. Dans ce cas, il faudra alors opter pour les LinkedList.

 

Leur différence avec les ArrayList est mince. Elles sont plus rapides à modifier en cas d'un nombre important d'éléments comme dit juste au dessus, oui, mais la principale différence est le fait que chaque élément possède une référence au précédent et au suivant. Les premiers et derniers éléments en ont aussi deux, cependant la seconde sera null car il n'y a qu'un seul objet adjacent à eux. Il n'y a pas grand chose de plus à dire. ^_^

 

Ces deux classes implémentent Iterator. Ce qui veut dire que l'on peut lister leurs valeurs à l'aide d'un itérateur, ce qui reviendra exactement au même que la boucle présentée un peu plus haut.

ListIterator<String> iterator = list.listIterator();

while(iterator.hasNext())
	System.out.println(iterator.next());

 

En soit, les Iterator n'ont que cette fonction: parcourir les éléments d'une collection. Ils font partie des stratégies permettant une meilleure stabilité et réutilisabilité des objets. Ne vous en souciez pas trop pour l'instant, ces notions viendront au fur et à mesure, même si vous y avez déjà un peu touché avec l'héritage et les interfaces.

 

Dans le chapitre précédent, je vous avais parlé du wildcard. (<?>) Vous allez enfin savoir à quoi il sert !

ArrayList<?> list;

 

Il revient à dire que votre liste accepte tous les types de donnée. Il y a cependant autre chose derrière car vous avez vu dans le premier exemple que je m'étais passé du wildcard. Vous conviendrez qu'il serait idiot d'ajouter une notion au langage sans qu'il apporte quelque chose de plus. Ce qu'il apporte, c'est le fait qu'une liste marquée du wildcard devient en lecture seule. Il sera impossible d'y ajouter ou d'y retirer des éléments. Prenez ça comme une sécurité.

Vous pouvez aussi restreindre le contenu de vos listes avec extends :

ArrayList<? extends Personne> list;

 

Cette liste n'acceptera que les objets de Personne et des classes en héritant.

Vous allez me dire qu'il n'est pas terrible d'utiliser une telle liste car on ne peut rien faire avec. Voici un exemple qui vous prouvera le contraire. Ces listes peuvent être très utiles !

    public static void main(String[] args) {        
        ArrayList<? extends Personne> list = getPersonList();
    }
    
    public static ArrayList<? extends Personne> getPersonList() {
        ArrayList<Personne> list = new ArrayList<Personne>();
        list.add(new Chevalier());
        list.add(new Senechal());
        list.add(new Pretre());
        list.add(new Tresorier());
        return list;
    }

 

Ici on générera la liste, avec tous les éléments dont on aura besoin, dans une méthode à part. Une fois notre liste prête, on la "verrouille" grâce au wildcard déclaré dans le type de retour de la méthode. Ainsi notre liste est sécurisée, pas moyen d'y toucher! C'est donc un très bon moyen d'empêcher d'autres développeurs de faire ce qu'ils veulent avec votre liste. Bien sûr, le wildcard ne s'applique pas qu'aux listes mais bien à toutes les classes utilisant la généricité.

 

 

Résumé:

 

  • Les collections sont des objets contenant un certain nombre d'autres objets, leur type étant défini par un argument générique. Si aucun argument générique n'est spécifié, tous les types sont acceptés !
  • Elles sont plus pratiques que les tableaux et proposent de nombreuses méthodes pour les parcourir ou les modifier.
  • Les boucles for each sont compatibles avec les collections.
  • Les listes sont équivalentes à des tableaux à une dimension.
  • Elles fonctionnent également avec des index.
  • Elles implémentent Iterator. Un itérateur permet de parcourir les éléments d'une collection.
  • Le wildcard signifie que tous les types de donnée sont acceptés. On peut restreindre les types grâce à extends.
  • Utiliser le wildcard verrouillera la liste en écriture. Elle ne sera plus qu'en lecture seule.

 

 

JeremGamer

 

 

2cp1ED5.pngtNIpRtq.png

SystemGlitch

blog-0734157001431875653.png

Non nous n'allons pas parler de médecine générique mais d'un nouvel aspect faisant la puissance de Java: la généricité. Ce concept abstrait est assez récent puisqu'il n'existe que depuis Java 5.

 

Pour bien vous faire comprendre son utilité, je vais vous fournir un petit exemple :

 

Une de vos classes utilise un objet de type inconnu, en paramètre dans son constructeur. Toutes les classes héritant de Object, le type du paramètre sera alors Object.

 
public class ObjectsManager {

	private Object o;

	public ObjectsManager(Object o) {
		this.o = o;
	}
	
	public Object getObject() {
		return o;
	}
}

 

Pour l'utiliser, on fera alors comme suit :

Object chevalier = new Chevalier();
ObjectsManager om = new ObjectsManager(chevalier);
((Chevalier) om.getObject()).combattre();

 

Certes ce code ne sert à rien, mais c'est un exemple ! Vous constaterez que si on souhaite mettre un objet de type Chevalier, on doit le stocker dans un objet de type Object (grâce au polymorphisme) avant de l'utiliser comme argument.

Si on utilise la méthode pour récupérer cet objet, on aura donc un objet de type Object, un cast sera nécessaire pour utiliser une méthode de Chevalier. Vous conviendrez que ce n'est pas très pratique et sécurisé. Si notre objet n'est pas de type Chevalier à la base, on aura une ClassCastException !

 

Il arriverait même parfois qu'on soit contraint de multiplier les casts. Bref, tout ça n'est pas au mieux. La solution c'est la généricité ! Elle permet de gérer n'importe quel type de données, sans avoir besoin de cast quoi que ce soit !

Je pense que vous n'avez pas la moindre idée de comment cela peut fonctionner. Voyons un peu comment se construit une classe gérant la généricité...

public class ObjectsManager<T> {

	private T object;

	public ObjectsManager(T object) {
		this.object = object;
	}
	
	public T getObject() {
		return object;
	}
}

 

Vous voyez de nouvelles balises, dans la déclaration de la classe! :blink:

En effet, vous devez spécifier un type fictif (vous comprenez pourquoi je parlais de concept abstrait maintenant ^_^ ) après le nom de votre classe, entre les balises "<" et ">". On appelle cela un type générique.

 

 

L'instant conventions:

 

  • Les types génériques doivent être nommés par une seule lettre en majuscule!
  • On utilisera les lettres suivantes en fonction de leur utilisation:
  • 'E' pour "Element". Un élément d'une collection par exemple. (cf chapitre suivant)
  • 'K' pour "Key". Une clef afin de trouver une valeur associée. (Donc pour les Map et Set, aussi dans le chapitre suivant)
  • Cette valeur associée sera nommée 'V' pour "Value".
  • 'N' pour un nombre.
  • Pour les types, en dehors de ceux cités au dessus, on utilisera 'T' puis 'S', 'U', 'V', etc... si on utilise plusieurs types génériques.

 

Testons donc à nouveau notre code, mais cette fois avec l'aide de la généricité.

 
Chevalier chevalier = new Chevalier();
ObjectsManager<Chevalier> om = new ObjectsManager<Chevalier>(chevalier);
om.getObject().combattre();

 

La déclaration de notre objet om est assez différente désormais. Nous avons ajouté l'argument générique. Vous remarquerez que nous pouvons désormais utiliser notre méthode sans passer par un cast ! Si vous regardez bien, Eclipse vous dira même que la méthode "getObject()" retourne un Chevalier! Vous pouvez essayer avec n'importe quelle classe, ça fonctionnera toujours ! Vous avez une classe qui peut gérer tous les objets, quelque soit son type !

Chevalier chevalier = new Chevalier();
ObjectsManager<Chevalier> om = new ObjectsManager<Chevalier>(chevalier);
om.getObject().combattre();

ObjectsManager<Integer> om2 = new ObjectsManager<Integer>(42);
System.out.println(om2.getObject());
		
ObjectsManager<Float> om3 = new ObjectsManager<Float>(42.7F);
System.out.println(om3.getObject());

ObjectsManager<String> om4 = new ObjectsManager<String>("Vive Bukkit.fr!");
System.out.println(om4.getObject());

 

Ici vous constaterez que tout fonctionne parfaitement! Vous avez peut-être remarqué que j'ai utilisé la classe Integer à la place du primitif int. Bien vu! La généricité ne prend pas en compte les types primitifs. Souvenez-vous en! Par ailleurs, ces classes enveloppant vos types primitifs sont appelées wrappers et ajoutent les méthodes héritées de Object à vos types primitifs. Le "wrapping" est très simple depuis Java 5 grâce à l'autoboxing, le fait de passer du primitif à un wrapper et inversement directement. C'est ce que j'ai utilisé ici avec les arguments génériques et arguments "classiques" de mes objets om2 et om3.

 

Dans l'instant conventions, vous avez vu qu'il était possible d'utiliser plusieurs types génériques. En effet, rien de plus simple, ça fonctionne comme pour des paramètres "classiques". Ajoutez une petite virgule entre vos types, et le tour est joué! Vous pouvez en définir autant que vous voulez.


public class MultiGeneric<T , S> {

	private T arg0;
	private S arg1;

	public MultiGeneric() {
		arg0 = null;
		arg1 = null;
	}
	
	public MultiGeneric(T arg0 , S arg1) {
		this.arg0 = arg0;
		this.arg1 = arg1;
	}
	
	public void setValue1(T arg0) {
		this.arg0 = arg0;
	}
	
	public T getValue1() {
		return arg0;
	}
	
	public void setValue2(S arg1) {
		this.arg1 = arg1;
	}
	
	public S getValue2() {
		return arg1;
	}

}

 

Je vous propose maintenant de tester cette classe :

MultiGeneric<String , Integer> mg = new MultiGeneric<String , Integer>("Vive Bukkit.fr!" , 42);
System.out.println("Valeur1 = " + mg.getValue1());
System.out.println("Valeur2 = " + mg.getValue2());

System.out.println("-------------------");

MultiGeneric<Boolean , Float> mg2 = new MultiGeneric<Boolean , Float>(true , 12.4F);
System.out.println("Valeur1 = " + mg2.getValue1());
System.out.println("Valeur2 = " + mg2.getValue2());

 

On obtient le résultat suivant :

Valeur1 = Vive Bukkit.fr!
Valeur2 = 42
-------------------
Valeur1 = true
Valeur2 = 12.4

 

Bref, rien de bien compliqué! On a juste rajouté un type générique utilisable en plus.

 

Il est possible de restreindre le type générique, c'est à dire n'accepter que des objets héritant d'une certaines classe. Pour ce faire, rien de bien compliqué! Dans la déclaration du type générique, on ajoute extends et la classe voulue.

public class GenericiteRestrainte<T extends Personne> {...}
GenericiteRestrainte<String> gr1 = new GenericiteRestrainte<String>(); //Erreur, String n'hérite pas de Personne
GenericiteRestrainte<Chevalier> gr2 = new GenericiteRestrainte<Chevalier>(); //Pas d'erreur car Chevalier hérite de Personne
GenericiteRestrainte<Personne> gr3 = new GenericiteRestrainte<Personne>(); //Pas d'erreur non plus

 

Voyons maintenant un aspect auquel vous n'avez très certainement pas pensé. Que se passe-t-il si l'héritage vient se joindre à la fête? :D

La covariance des variables n'existe pas avec la généricité ! Vous pouvez définir des objets héritant du type générique choisi en argument générique mais un problème se posera lorsque vous essaierez de le récupérer. Vous essaierez d'affecter une référence de l'objet héritant à une référence de l'objet hérité, c'est donc interdit.

C'est là qu'entre en jeu le wildcard ! Tout simplement un '?'.

Il signifie que notre classe acceptera n'importe quel type de donnée. Cependant je ne le détaillerais pas dans ce chapitre car il est en lien direct avec la notion du chapitre suivant, sans laquelle je ne peux tout simplement pas vous faire comprendre son utilité.

 

 

Résumé:

 

  • La généricité est un concept permettant à une classe de pouvoir utiliser n'importe quel type de donnée, sans passer par des casts.
  • La généricité passe par des types abstraits nommés par une seule lettre.
  • La déclaration des types génériques se fait dans la déclaration de la classe, après son nom et entre "<...>".
  • Lors de la déclaration d'un objet d'une telle classe, on ajoute les arguments génériques de la même manière, après la déclaration du type.
  • La généricité ne supporte pas les types primitifs, utilisez les wrappers à la place.
  • Vous pouvez définir autant de types génériques que vous voulez.

 

 

JeremGamer

 

 

2cp1ED5.pngtNIpRtq.png

SystemGlitch

blog-0791760001431875491.png

Vous souvenez-vous de mon graphique UML?

 

0rzZWRG.png

 

Je vous disais qu'il était possible de l'améliorer. En effet, le Senechal hérite du Commandeur, vous savez comment ça fonctionne. Cependant, il possède aussi les pouvoirs du CommandantJerusalem, du Tresorier et du Marechal réunis.

Afin de solidifier notre hiérarchie, nous allons devoir utiliser un moyen "d'unifier" les pouvoirs. J'ai l'honneur de vous présenter les interfaces !

 

-----> Mais qu'est-ce donc? Quel est l'intérêt? :wacko:

 

Avant de répondre, je vais me permettre de vous donner un diagramme UML correspondant à mes explications :

 

 

Nj4vA9L.png

 

Voila qui est mieux! Les flèches en trait discontinu représentent l'implémentation. La classe d'où part la flèche implémente l'interface visée. Bon en soit vous allez me dire que le résultat est le même. En effet, on aura les mêmes fonctionnalités si on faisait les deux programmes représentés par ces diagrammes.

Pour éclaircir tout cela, voyons un peu les principales caractéristiques d'une interface:

  • Il est impossible d'instancier une interface, d'ailleurs elle n'a aucun constructeur.
  • Ses méthodes n'ont pas de corps, comme les méthodes abstraites (mais sans le mot-clef abstract !)
  • Les classes implémentant une interface doivent obligatoirement redéfinir les méthodes de cette interface.

 

Je pense que vous commencez à entrevoir les possibilités que cela apporte. En effet, il est désormais beaucoup plus simple de réutiliser nos objets! Il est simple de rajouter ou retirer des méthodes, et donc des pouvoirs pour nos différents status. Mais ce qui est plus pratique encore, c'est qu'en rajoutant des méthodes au Tresorier par exemple, on les rajoutera simultanément au Senechal ! De plus, vous remarquerez aussi que le polymorphisme est toujours possible et que nous avons contourné le problème de l'héritage multiple, qui est, je vous le rappelle, impossible en Java !

 

Vous l'avez très certainement remarqué, la classe Senechal implémente plusieurs interfaces. Il a donc plusieurs supertypes! Pratique non? D'autant plus que toutes les méthodes concernées auront le même nom dans toutes les classes implémentées, ce qui fait que l'on aura pas à s'embêter à définir une convention de nommage si vous travaillez à plusieurs !

 

Trêve de bavardages, passons à la pratique !

 

Pour créer une interface, cliquez-droit sur le package de votre choix, puis "New -> Interface".

 

Voici notre interface :

public interface ResponsableArgent {
    
    static final int RICHESSES = 10;
    
    public void gererRichesses();
    public int getRichesses();
}

 

Je me suis permis de rajouter une petite méthode pour récupérer le montant de richesses qu'un responsable possède.

Créons nos deux autres interfaces maintenant.

public interface ResponsableArmees {

	public void gererArmees();
	
}
 
public interface ResponsableReligion {

	public void protegerPelerins();
	
}

 

Voila qui est fait ! Maintenant implémentons tout cela! Pour ce faire, nous utiliserons le mot-clef implements.

 
public class Tresorier extends Commandeur implements ResponsableArgent {
    
    @Override
    public void gererRichesses() {
        //...
    }

    @Override
    public int getRichesses() {
        //...
        return RICHESSES;
    }

}

 

L'ordre de déclaration est très important ! D'abord extends (si la classe hérite d'une autre évidemment) puis implements.

 

Encore une fois, votre IDE vous facilite la tâche! Une fois que vous aurez déclaré l'implémentation, survolez le nom de la classe avec le curseur et Eclipse vous proposera d'ajouter toutes les méthodes nécessaires!

 

Vous remarquerez que comme pour une classe abstraite, le Tresorier a accès à la variable richesses venant de l'interface !

 

Occupons-nous du Senechal. Il implémentera donc plusieurs interfaces :

public class Senechal extends Commandeur implements ResponsableArgent,
                                                    ResponsableArmees,
                                                    ResponsableReligion {

    @Override
    public void protegerPelerins() {
        //...
    }

    @Override
    public void gererArmees() {
        //..
    }

    @Override
    public void gererRichesses() {
        //...
    }

    @Override
    public int getRichesses() {
        //...
        return RICHESSES;
    }

}

 

Constatez le résultat! On a bien toutes les méthodes présentes sur le diagramme (plus getRichesses()).

 

Le polymorphisme vous permet donc de faire ceci :

ResponsableArgent responsable = new Tresorier();

 

Si la classe instanciée implémente plusieurs interfaces, alors seules les méthodes de l'interface spécifiée pourront être utilisées :

ResponsableArgent responsable = new Senechal();

 

Ici, seules les méthodes de l'interface ResponsableArgent pourront être utilisées via l'objet "responsable". C'est à dire qu'il sera impossible d'appeler "gererArmees()" par exemple.

 

A partir de tout ce que vous savez jusque là, il est possible de faire des programmes déjà très élaborés et structurés, facilement réutilisables et modifiables! Il suffit de prendre son temps afin de bien créer ce que l'on appelle un pattern. Et même si besoin, faites un joli diagramme UML ! :)

 

 

Résumé:

 

  • Une interface est une classe 100% abstraite. Elle ne peut pas être instanciée.
  • Ses méthodes n'ont pas de corps.
  • On dit qu'une classe implémente une interface.
  • Une implémentation se fait à l'aide du mot-clef implements, après l'expression d'héritage.
  • Cette classe doit alors redéfinir toutes les méthodes de l'interface implémentée.
  • Ce qui fait de cette interface un second supertype.
  • Les interfaces permettent une grande ré-utilisabilité de vos objets ainsi qu'un pattern plus solide et malléable.
  • Le polymorphisme est toujours possible.

 

 

JeremGamer

 

2cp1ED5.pngtNIpRtq.png

SystemGlitch

Beaucoup de temps s'est écoulé depuis votre dernier véritable TP. Alors prenez bien votre temps, repensez à tout ce que vous avez appris, relisez les cours si besoin et n'hésitez pas à me demander de l'aide! En effet je vais vous en demander beaucoup pour ce TP. Il va falloir vous investir, car il n'y a que comme ça que l'on progresse !

 

Vous allez avoir besoin des notions suivantes:

  • Les boucles
  • La communication avec l'utilisateur
  • Les méthodes
  • L'utilisation de plusieurs classes et packages
  • L'héritage et l'abstraction
  • Les exceptions
  • Les autres notions déjà connues pour le premier TP


Oui, c'est presque tout ce que nous avons vu depuis le dernier TP, je vous avais prévenu! ^_^
Ne vous découragez pas, vous vous passerez peut-être de certaines de ces notions, mais si vous voulez faire du mieux possible, ce que je souhaite le plus, il faudra tout utiliser !
 

Sans plus tarder, voici votre cahier des charges :
 
Votre programme est une application console (et oui je ne vais pas vous demander de me sortir une interface graphique alors que je ne vous ai pas encore appris à en faire! :P ). Cette application sera un logiciel de gestion de zoo. Elle devra être capable de:

  • Consulter la liste des espèces d'animaux présents dans le zoo, par catégorie (Félin, reptile, amphibien, etc...), ou indifféremment.
  • Consulter la fiche de chaque espèce d'animal (ne vous embêtez pas à mettre plein d'animaux, contentez-vous de 5 ou 6 ce sera déjà très bien!)
  • Ces fiches devront recenser les informations suivantes:
    • Régime de l'animal
    • Cri de l'animal
    • Le mode de déplacement de l'animal
    • Le type de reproduction
    • La taille approximative de l'animal
    • La durée de vie approximative de l'animal
    • (Ces valeurs ne doivent pas obligatoirement être exacte, ce serait aussi un travail de recherche documentaire que je vous demanderais sinon! ^_^ Mettez des valeurs au hasard, ça fonctionnera de la même façon!)
  • Point de vue technique maintenant, votre programme devra être hiérarchisé à l'aide de l'héritage et organisé à l'aide de packages.
  • Il devra gérer les exceptions, en renvoyant à l'utilisateur un message d'erreur plus commode qu'une stacktrace. Si le cœur vous en dit, vous pouvez même ajouter vos propres exceptions ! ;)


Pour un tel projet, il ne faut surtout pas se jeter la tête la première! Propulsez-vous à un mètre de votre clavier et réfléchissez à comment vous allez vous y prendre. Notez vos idées sur un papier, prenez en compte tous les éléments donnés. Si vous le souhaitez, je vous invite même à faire des diagrammes UML de votre projet! Pour cela, vous pouvez utiliser le logiciel ArgoUML, qui est très bon en la matière, et en plus de ça, il est entièrement codé en Java !

 


Sur ce, je vous souhaite bonne chance! Travaillez bien! ;)

 

Je vous propose une correction à télécharger. Vous pouvez importer le projet dans Eclipse et tester. ;)

 

 

JeremGamer

 


2cp1ED5.pngtNIpRtq.png

SystemGlitch

Nous y voila! Votre premier TP! Il faudra rassembler vos connaissances pour y parvenir, à savoir :

  • Les variables et types primitifs,
  • Les conditions et les opérateurs,
  • Les tableaux.


Votre programme devra savoir faire les choses suivantes :

  1. Un client entre dans le restaurant avec un budget aléatoire compris entre 10€ et 50€.
  2. On lui propose un plat approprié par rapport à son budget et par rapport à la "Carte du Chef".
  3. Cette carte devra comporter deux catégories. Le plat et son prix associé.
  4. Au moins quatre plats devront être proposés. Ne cherchez pas à faire de la haute gastronomie non plus et ne cherchez pas un bon rapport qualité/prix car nous ne sommes pas là pour ça ! Si l'envie vous chante, vous pouvez mettre un steak haché frites à 50€ !


Ne vous inquiétez pas, je vous guiderais un peu !

 


Tout d'abord, quelques petites choses plutôt utiles sont à savoir:

 

Premièrement, pour générer un nombre aléatoire, il vous faudra faire comme suit :

Random rand = new Random();
int budget = rand.nextInt((Max - Min) + 1) + Min;
//Ici Max sera 50 et Min sera 10.


Deuxièmement, nous sommes dans le milieu du business ! Nous allons donc faire en sorte de prendre le prix du plat en prenant le prix le plus proche du budget du client, le plus élevé si possible. Il faut bien que les affaires tournent n'est-ce-pas?

 


Pour finir, il faut toujours penser avant d'agir! Réfléchissez bien à comment vous allez vous y prendre. Notez vos idées sur un papier ou un fichier texte et une fois que vous vous sentez prêt, allez-y ! Prenez votre temps, vous n'êtes pas chronométrés! Essayez de bien vous souvenir de toutes les petites astuces que l'on vous a donné jusque là.

 

Une dernière chose, sachez que ce TP repose en quasi totalité sur votre réflexion. Le code à effectuer est assez simple, il suffit juste de trouver le concept ! Encore une fois, prenez bien le temps de réfléchir, c'est la clef du succès de n'importe quel projet ! Si vous avez des questions pertinentes, de grosses difficultés ou bien fini votre TP, nous vous encourageons à nous contacter par message privé! Nous vous aiderons si besoin.

 

Maintenant, fini de rigoler, préparez vos neurones et vos claviers, c'est parti !

 

 

 

 

 

 

 

 

__________________________

 

 

 

 

 

Correction

 

 


Quand vous aurez terminé, prenez bien le temps de tester votre programme et de voir s'il est fonctionnel. Si c'est le cas, félicitations ! Vous êtes sur une excellente voie pour l'apprentissage de ce langage ! Cependant, si vous avez abandonné, ce n'est pas grave! On apprend de ses échecs. La correction est là pour ça ! Jetez-y donc un œil ! ;)

 

 

Spoiler

Le code vous sera directement donné à chaque correction de TP. Essayez de le comprendre et ensuite, lisez les informations se trouvant en dessous. Elles détaillerons ce que nous faisons et comment nous le faisons.

 

import java.util.Random;
 
public class Main {
	
	public static void main(String args[]) {
		
		final int PLAT_1 = 8;
		final int PLAT_2 = 15;
		final int PLAT_3 = 25;
		final int PLAT_4 = 45;

		String menu[] = {"Steak haché frites" ,
				 "Cordon bleu et petits poids" ,
				 "Poulet rôti et haricots verts" ,
				 "Magret de canard et écrasé de pommes de terre"
		};
		
		Random rand = new Random();
		int budget = rand.nextInt((50 - 10) + 1) + 10;
		
		System.out.println("Un client rentre dans le restaurant.");
		System.out.println("Il possède " + budget + "€.");
		
		if (budget < PLAT_2) {
			System.out.println("Le serveur lui propose le " + menu[0] + ".");
		} else if (budget < PLAT_3) {
			System.out.println("Le serveur lui propose le " + menu[1] + ".");
		} else if (budget < PLAT_4) {
			System.out.println("Le serveur lui propose le " + menu[2] + ".");
		} else if (budget >= PLAT_4) {
			System.out.println("Le serveur lui propose le " + menu[3] + ".");
		}
		
	}
 
}


Que faisons-nous?

  1. On défini les prix des différents plats à l'aide de constantes.
  2. On écrit le menu grâce à un tableau à une dimension.
  3. On génère aléatoirement la budget du client.
  4. On écrit dans la console que le client entre avec tel montant d'argent.
  5. On teste dans quelle tranche de prix se trouve le budget du client.
  6. On en déduit le plat à proposer et on l'écrit dans la console.


Alors c'était difficile pour un premier TP? ^_^

 

 


JeremGamer

 


2cp1ED5.pngtNIpRtq.png

SystemGlitch

blog-0625063001431874514.png

Dans ce chapitre nous continuerons d'exploiter mon exemple de système féodal.

 

Certains aspects de ma structure sont a améliorer. Par exemple il est possible d'instancier une classe du type "Personne". Cette personne n'aura alors aucun statut! Nous ne voulons pas cela possible. Nous allons alors rendre la classe "Personne" abstraite. Qu'est-ce que cela signifie?

 

Une classe abstraite est tout simplement une classe ne pouvant être instanciée ! Vous allez me demander à quoi elle peut bien servir si on ne peut pas l'instancier. Souvenez-vous bien tout ce que je vous ai raconté sur l'héritage et le polymorphisme. En effet il est donc possible d'instancier ses classes filles, y compris d'utiliser le polymorphisme !

 

Revenons là-dessus point par point.

 
public abstract class Personne {

	int i = 0;
	
	public void methodeCommune() {
		System.out.println("Tout le monde peut faire ça!");
	}
}

 

Voici notre nouvelle classe "Personne" déclarée abstract. J'ai volontairement retiré sont constructeur car il ne nous servira pas. Cependant vous pouvez le laisser afin de pouvoir faire appel à lui avec super dans ses classes filles.

 

Ainsi il est impossible de faire ceci:

VLea3rn.png

 

Eclipse nous le rappelle alors. Désormais quand vous verrez ce message, vous saurez que vous essayez d'instancier une classe abstraite.

 

Le polymorphisme est utilisable. C'est à dire qu'il est possible de faire ceci :

Personne p = new Chevalier();

 

Vous pouvez donc en déduire que les classes abstraites sont essentiellement faites pour être des super-classes visant à créer un nouveau type d'objet.

 

Pour vous faire un peu plus comprendre leur utilité, je vais utiliser un autre exemple. Nous avons une classe "Stylo" dans laquelle nous avons une méthode "getColor()". Savez-vous de quelle couleur écrira un stylo si on vous indique seulement qu'il s'agit effectivement d'un stylo? Non, c'est normal, ce sera pareil pour un programme. Ici vous conviendrez alors qu'instancier la classe "Stylo" n'est pas vraiment une bonne idée.

 

Vous admettrez également qu'il serait idiot de définir un comportement à notre méthode "getColor()" si toutes les classes filles de "Stylo" s'en occupent avec une couleur différente. Nous allons alors déclarer la méthode "getColor()" abstraite !

public abstract Color getColor();

 

Non, je n'ai pas fait d'erreur ! Ces méthodes n'ont pas de corps, faites bien attention à leur syntaxe! C'est pour cela qu'on les nomme abstraites d'ailleurs, car on ne peut tout simplement pas savoir ce qu'elle fait. Ces méthodes ne peuvent exister que dans des classes abstraites. Les classes filles devront alors les redéfinir obligatoirement.

 

N'oubliez pas cependant qu'il est également possible de déclarer des méthodes normales dans une classe abstraite !

 

Sachez qu'il y a d'autres applications pour les classes abstraites. Par exemple elle peut faire office de rangement. Je m'explique. Votre programme utilisera régulièrement des constantes qui définiront des paramètres de votre logiciel comme une adresse de téléchargement, un numéro de version, etc... Ces constantes seront alors static et public pour pouvoir les récupérer facilement depuis n'importe où.

 

Vous créerez alors une classe pour stocker tout ça.

public abstract class Constants {

	public static final String VERSION = "1.0";
	
	public static final String DOWNLOAD_ADRESS = "http://unsite.com/aFile.txt";
}

 

A quoi bon instancier une telle classe? A rien. On la déclarera donc abstract. Ainsi partout dans notre code, il sera facile et rapide de récupérer ces valeurs !

String v = Constants.VERSION;
String adress = Constants.DOWNLOAD_ADRESS;

 

Une dernière application possible à nos classes abstraites: une "trousse à outils". De même que pour les variables, on peut déclarer de nombreuses méthodes statiques qui serviront un peu partout dans votre code! Comme il n'y a rien à stocker, on déclarera cette classe abstraite également !

Au lieu de vous donner un exemple bidon, le code cadeau de ce chapitre sera une classe utilitaire permettant de récupérer l'équivalent binaire d'une valeur ! :) J'ai aussi ajouté une petite méthode pour arrondir un double. ;)

 

Spoiler

public abstract class BinaryUtils {
    
    /**
     * Arrondit une double avec le nombre de décimales choisi.
     * @param valeur
     * @param decimales
     * @return l'arrondi de la valeur au nombre de décimales choisi
     */
    public static double arrondi(double valeur , int decimales) {
        String pattern = "0.";
        for(int i = 0 ; i < decimales ; i++)
            pattern += "#";
        NumberFormat nf = new DecimalFormat(pattern);
        String s = nf.format(valeur);
        return Double.parseDouble(s.replaceAll(",", "."));
    }
    
    /**
     * Permet d'obtenir la représentation binaire d'une chaine de caractères.
     * Chaque élément du tableau est la représentation binaire d'un seul caractère.
     * @param s
     * @return Un tableau de String.
     * @throws UnsupportedEncodingException
     */
    public static String[] toBinary(String s) throws UnsupportedEncodingException {
        byte[] infoBin = null;
        infoBin = s.getBytes("UTF-8");
        ArrayList<String> returned = new ArrayList<String>();
        for (byte b : infoBin) {
            returned.add(Integer.toBinaryString(b));
        }
        return returned.toArray(new String[returned.size()]);
    }
    
    /**
     * Permet d'obtenir la représentation binaire d'un entier.
     * @param i
     * @return La représentation binaire d'un entier.
     * @throws UnsupportedEncodingException
     */
    public static String toBinary(int i) throws UnsupportedEncodingException {
        return Integer.toBinaryString(i);
    }
    
    /**
     * Permet d'obtenir la représentation binaire d'un byte.
     * @param b
     * @return La représentation binaire d'un byte.
     * @throws UnsupportedEncodingException
     */
    public static String toBinary(byte b) throws UnsupportedEncodingException {
        return Integer.toBinaryString(b);
    }
    
    /**
     * Permet d'obtenir la représentation binaire d'un short.
     * @param s
     * @return La représentation binaire d'un short.
     * @throws UnsupportedEncodingException
     */
    public static String toBinary(short s) throws UnsupportedEncodingException {
        return Integer.toBinaryString(s);
    }
    
    /**
     * Permet d'obtenir la représentation binaire d'un float.
     * @param f
     * @return La représentation binaire d'un float.
     * @throws UnsupportedEncodingException
     */
    public static String toBinary(float f) throws UnsupportedEncodingException {
        return Integer.toBinaryString(Float.floatToRawIntBits(f));
    }
    
    /**
     * Permet d'obtenir la représentation binaire d'un double.
     * @param d
     * @return La représentation binaire d'un double.
     * @throws UnsupportedEncodingException
     */
    public static String toBinary(double d) throws UnsupportedEncodingException {
        return Long.toBinaryString(Double.doubleToRawLongBits(d));
    }
    
    /**
     * Permet d'obtenir la représentation binaire d'un caractère.
     * @param c
     * @return La représentation binaire d'un caractère.
     * @throws UnsupportedEncodingException
     */
    public static String toBinary(char c) throws UnsupportedEncodingException {
        return Integer.toBinaryString(c);
    }
}

 

 

Il ne vous reste plus qu'a appeler ces méthodes ainsi :

BinaryUtils.toBinary(/*une variable*/);

 

Résumé :

 

  • Une classe abstraite ne peut pas être instanciée.
  • On peut utiliser le polymorphisme pour instancier une classe fille d'une classe abstraite.
  • Les méthodes abstraites doivent être redéfinies par ses classes filles.
  • Les classes abstraites sont aussi un bon moyen de stocker des constantes.
  • Elles peuvent aussi faire office de pack de méthodes globales.

 

 

JeremGamer

 

 

2cp1ED5.pngtNIpRtq.png

SystemGlitch

blog-0480856001431874139.png

Vous avez très certainement déjà rencontré des messages d'erreur, peu importe le logiciel utilisé. Ces messages sont le résultat de la gestion des exceptions.

 

Dans ce chapitre, je vais vous apprendre plusieurs choses extrêmement utiles comme lire une stacktrace, gérer les exceptions de manière personnalisée ou encore faire en sorte que son programme ne se termine pas soudainement à la première petite exception.

 

Commençons par en générer une volontairement.

 
Chevalier chevalier = null; //Classe du chapitre précédent
chevalier.combattre();

 

A l'exécution de ce code, un message d'erreur s'affichera dans la console. C'est ce que l'on appelle une stacktrace. Elle nous indique avec précision d'où l'erreur est survenue :

Exception in thread "main" java.lang.NullPointerException
	at fr.bukkit.jeremgamer.cours.Cours.main(Cours.java:7)

 

La première ligne donne le type d'erreur. Ici on a une NullPointerException (littéralement: "ne pointe vers rien"), c'est à dire que l'on a essayé d'utiliser un objet avec une valeur nulle. (On sait qu'ici c'est la variable "chevalier" , mais on ne sait en général pas à l'avance d'où viendra le problème)

En effet vous pouvez constater que l'on a pas instancié l'objet "chevalier". Ainsi il est impossible de faire appel à une de ses méthodes sans provoquer de NullPointerException.

Les lignes qui suivront indiquerons les lignes qui ont provoqué l'erreur. Chaque ligne du message désignera alors une ligne dans votre code. On vous indique alors dans quelle classe et à quel endroit!

Ici c'est la ligne 7 de la classe Cours (on sait également que cette ligne se trouve dans la méthode nommée "main") qui à provoqué l'erreur.

 

Voyons désormais un exemple plus poussé. J'ai volontairement lancé une erreur après l'appel de plusieurs méthodes.

java.lang.Exception
	at fr.bukkit.jeremgamer.cours.Chevalier.methodeCommune(Chevalier.java:23)
	at fr.bukkit.jeremgamer.cours.Chevalier.combattre(Chevalier.java:14)
	at fr.bukkit.jeremgamer.cours.Cours.main(Cours.java:7)

 

On peut désormais connaitre tous les détails du déroulement des évènements.

  • La ligne 7 de la classe Cours, se trouvant dans la méthode nommée "main()".
  • Cette ligne a appelé la méthode "combattre()" de la classe Chevalier.
  • Cette méthode a appelé, à la ligne 14, la méthode "methodeCommune()" aussi de la classe chevalier.
  • Enfin, à la ligne 23 de la classe chevalier, c'est là que l'erreur s'est produite. En effet on y trouve ceci :
    throw new Exception();

     

 

J'ai volontairement ajouté cette ligne. Oui il est possible de lancer une exception via une ligne de code et le mot-clef throw !

Pour rapidement trouver la ligne concernée, Eclipse propose une fonctionnalité très intéressante: vous pouvez cliquer sur ce qui se trouve entre parenthèses et il vous y conduira directement ! ;)

 

Bon au lieu d'en lancer et de faire s'arrêter votre programme à la moindre petite contrariété, apprenons plutôt à les utiliser à bon escient! Pour cela il nous allons utiliser un nouveau bloc : try {...} catch() {...}.

 

Reprenons le code du début de ce chapitre et remanions-le pour que l'exception générée soit gérée par notre programme.

Chevalier chevalier = null;
try {
	chevalier.combattre();
} catch (NullPointerException npe) {
	npe.printStackTrace();
}

 

Ce n'est pas plus compliqué que ça ! Ce bloc permet d'attraper (littéralement, d'où "catch") les erreurs qui se produiraient dedans. Ainsi on peut choisir comment réagira votre programme pour le type d'exception entre parenthèses. En cas d'erreur, les instructions se trouvant dans le bloc catch seront réalisées et celles qui suivent la ligne provoquant l'erreur seront passées. Ainsi, au lieu que votre programme s'arrête subitement, il se contentera de ne pas exécuter ce qui génère des erreurs! Une fois la gestion terminée, il continuera à exécuter ce qui se trouve après le bloc.

 

La méthode "printStackTrace()" est la méthode permettant d'écrire la stacktrace dans la console. Vous pouvez l'enlever, mais ce n'est pas très propre et fortement déconseillé!

Testez donc ceci :

Chevalier chevalier = null;
try {
	chevalier.combattre();
} catch (NullPointerException npe) {
	System.out.println("Ton objet tu instanciera, cette erreur disparaitra!");
	npe.printStackTrace();
}

 

Vous obtiendrez ceci :

Ton objet tu instanciera, cette erreur disparaitra!
java.lang.NullPointerException
	at fr.bukkit.jeremgamer.cours.Cours.main(Cours.java:8)

 

Vous venez de personnaliser la gestion d'une exception !

 

Sachez qu'il est possible de gérer plusieurs exceptions à la fois. Soit vous mettez plusieurs blocs catch afin d'exécuter du code différent pour chaque erreur, ou alors un seul bloc catch dit multi-catch et qui exécutera le même code pour différentes exceptions.

 

Les blocs multi-catch sont très récents. Ils n'existent que depuis Java 7 !

 

Un bloc multi-catch :

Chevalier chevalier = null;
try {
	int i = 5 / 0; //Générera une ArithmeticException car on ne peut pas diviser par 0.
	chevalier.combattre();
} catch (NullPointerException | ArithmeticException e) {
	e.printStackTrace();
}

 

Plusieurs catch :

Chevalier chevalier = null;
try {
	int i = 5 / 0; //Générera une ArithmeticException car on ne peut pas diviser par 0.
	chevalier.combattre();
} catch (NullPointerException npe) {
	npe.printStackTrace();
} catch (ArithmeticException ae) {
	ae.printStackTrace();
}

 

La plupart du temps, Eclipse vous demandera d'ajouter un bloc try/catch. Grâce à la magie de votre IDE, tout cela est bien plus simple! ^_^

Mais parfois, comme dans notre cas actuel, il n'était pas forcé de gérer les exceptions.

 

Eclipse vous force à gérer les exceptions pour certaines méthodes car dans, leur déclaration, il est précisé grâce au mot-clef throws qu'elle peuvent générer des erreurs. Si vous ne les gérez pas, erreur à la compilation ! Alors mieux vaut faire ce que l'on vous demande ! :D

public static double diviser(double nombre1 , double nombre2) throws ArithmeticException {
	return nombre1 / nombre2;
}

 

On prévient alors le compilateur que cette méthode peut lancer une ArithmeticException.

Pour l'appeler, on fera alors ainsi :

try{
     diviser(5 , 0);
} catch (ArithmeticException ae) {
	ae.printStackTrace();
}

 

Un dernier bloc peut-être ajouté: le bloc finally. Son contenu sera exécuté quoi qu'il arrive, qu'il y ai une erreur ou non! Il est surtout utilisé dans la manipulation des flux. S'il y a une erreur, grâce au bloc finally, on peut quand même fermer les flux, se déconnecter d'un serveur ou d'un base de donnée, etc...

try{
     diviser(5 , 0);
} catch (ArithmeticException ae) {
	ae.printStackTrace();
} finally {
	System.out.println("BlaBla");
}

 

A titre d'exemple, j'ai utilisé des exceptions simple liées à des notions que vous connaissez. Mais vous avez surement remarqué qu'Eclipse ne vous forçait pas à les gérer. C'est parce qu'en réalité, les NullPointerException et ArithmeticException sont des RuntimeException (elles héritent de cette classe), aussi appelées "unchecked exceptions" car il n'est pas obligatoire d'utiliser des blocs try/catch ou des throws sauf si vous voulez les gérer d'une certaine manière. Imaginez s'il était obligatoire de faire un bloc pour chaque objet déclaré ! Les RuntimeException sont là pour ça. :)

 

Toutes les exceptions héritent de la super-classe "Exception", qui hérite de la classe "Throwable". Vous pouvez donc créer vos propres exceptions !

 

Créez une nouvelle classe, son nom doit, par convention, se terminer par "Exception". Revenons sur notre exemple de jeu vidéo féodal. Nous allons créer une exception qui s'exécutera quand on voudra faire d'un prêtre un chevalier.

package fr.bukkit.jeremgamer.cours;

public class ConversionException extends Exception {

	public ConversionException() {
		System.out.println("Un prêtre ne peut combattre!");
	}
	
}

 

Quand votre exception sera lancée, on aura alors le message spécifié écrit dans la console!

 

----> Cette exception n'étant pas dans le JDK, comment pourra-elle être lancée?

 

Il faut la lancer soit-même comme un grand! Souvenez-vous de ce que je disais au début de ce chapitre, il est possible de lancer volontairement une exception, et ce grâce au mot-clef throw !

 
/**
 * Convertit le statut d'une personne en un autre
 * @param p1 Personne à convertir
 * @param p2 Résultat de la conversion
 */
public Personne convertir(Personne p1 , Personne p2) throws ConversionException{
	if(p1 instanceof Pretre && p2 instanceof Chevalier)
		throw new ConversionException();
	else {
		//Actions à effectuer pour la conversion
		return p2;
	}
}

 

Tout se fera alors à l'aide de tests.

Vous avez surement remarqué le mot-clef instanceof dont nous n'avons jamais encore parlé. Ce mot-clef permet de tester le type d'un objet. Ici on teste si "p1" est un objet de type "Pretre" et si "p2" est de type "Chevalier".

 

Pour appeler cette méthode, il sera alors nécessaire de faire ainsi :

 

i9JiMvd.png

 

On aura alors :

try {
	convertir(new Pretre() , new Chevalier());
} catch (ConversionException e) {
	e.printStackTrace();
}

 

Une chose à savoir : les variables déclarées dans un tel bloc ne sont pas accessibles en dehors de ce dernier !

Pensez alors à déclarer vos variables en dehors du bloc !

Chevalier chevalier = null;
try {
	chevalier = new Chevalier();
	//...
} catch (ConversionException e) {
	e.printStackTrace();
}

 

En code cadeau, je vous propose quelque chose de fort sympathique et très utile dans les projets que vous pourriez distribuer. Le code suivant permet de capturer toutes les exceptions, sans exception ! (Haha quel jeu de mot ! :P )

Ainsi vous pourrez définir un comportement par défaut pour toutes les erreurs de votre programme !

Thread.setDefaultUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() {
    public void uncaughtException(Thread t, Throwable e) {
    	e.printStackTrace();
        //Actions à effectuer
    }
});

 

 

Résumé:

 

  • Une exception est une erreur. Elle stoppe votre code.
  • Elle génère une stacktrace, message d'erreur détaillant avec précision d'où provient l'erreur.
  • Il est important de savoir lire une stacktrace.
  • Les blocs try {...} catch() {...} finally {...} permettent de capturer et gérer les exceptions.
  • Il existe des blocs catch simples, les multi-catch; mais il est aussi possible d'utiliser plusieurs catch simples.
  • Déclarer une méthode avec le mot-clef throws + <Une exception> permet de signaler au compilateur qu'elle peut être dangereuse et qu'il faut utiliser un bloc try/catch lors de son appel.
  • Vous pouvez créer vos propres exceptions en créant une classe héritant de la classe "Exception".
  • Le mot-clef throw permet de lancer une exception manuellement.

 

 

JeremGamer

 

 

2cp1ED5.pngtNIpRtq.png

SystemGlitch

blog-0480235001431873950.png

L'héritage est un des fondements de la programmation orientée objet! En apparence il peut paraître compliqué mais une fois le principe acquis, c'est très simple!

L'héritage est fortement lié à la notion d'objet, alors je vous invite à la revoir (dans le chapitre 9) si elle n'est pas encore très claire dans votre esprit. :)

 

Commençons par un exemple afin que ce soit plus facile pour vous de comprendre le concept:

 

Vous développez un jeu avec un système politique révolutionnaire dans un monde féodal. Les différents échelons auront des pouvoirs en commun, mais certains seront plus puissants que d'autres!

 

Schématisons cette hiérarchie :

 

Organisation.jpg

http://www.histogames.com

 

L'héritage, c'est exactement ça ! Pour les classes, il s'agit des méthodes. Une classe héritant d'une autre pourra utiliser les méthodes de cette dernière !

En programmation, on ne présente pas ça sous forme de pyramide, mais sous forme de diagramme. Et on aura quelques différences. Pour commencer on ne partira pas du haut de la pyramide mais du bas! Comme ça, tout le monde monde pourra hériter des pouvoirs communs à tous! On représentera alors un diagramme UML pour cette situation ainsi :

 

DhuRRYE.png

 

Les flèches représentent "hérite de". On peut donc dire ici que la classe Maitre hérite de la classe Senechal.

 

Il sera alors possible d'appeler les méthodes suivantes depuis un objet de la classe Maitre:

  • methodesCommunes()
  • gerer()
  • gererArmees()
  • protegerPelerins()
  • gererRichesses()
  • designerSenechal()

 

Vous remarquerez qu'il est possible d'améliorer cette hiérarchie. En effet la classe Senechal reproduit les méthodes des classes Marechal, CommandantJerusalem et Tresorier. Nous verrons comment faire dans un prochain chapitre.

 

En pratique ça donne quoi?

 

Dans la déclaration de la classe, on rajoutera le mot-clef extends puis le nom de la classe héritée. Attention, une classe ne peut hériter que d'une seule classe à la fois !

public class Maitre extends Senechal {
     //...
}

 

Je vous propose d'essayer afin de vous prouver mes dires. Créez une classe Personne et une classe Chevalier.

Faites hériter Personne à Chevalier puis déclarez les deux méthodes se trouvant dans le diagramme, dans leur classe respective. On aura les deux classes suivantes :

package fr.bukkit.jeremgamer.cours;

public class Personne {

	public void methodeCommune() {
		System.out.println("Tout le monde peut faire ça!");
	}
}
package fr.bukkit.jeremgamer.cours;

public class Chevalier extends Personne {

	public void combattre() {
		System.out.println("Les têtes vont rouler!");
	}
}

 

Maintenant, allons voir ce dont l'héritage est capable dans votre méthode principale! Instanciez trois objets ainsi :

Personne pers = new Personne();
Chevalier chevalier = new Chevalier();
Personne chevalier2 = new Chevalier();

 

La troisième ligne vous semble bizarre? C'est normal, nous y viendrons.

 

Désormais appelons toutes les méthodes depuis chaque objet et voyons ce que nous dira Eclipse.

pers.methodeCommune();
pers.combattre(); //Erreur
	
chevalier.methodeCommune();
chevalier.combattre();
		
chevalier2.methodeCommune();
chevalier2.combattre(); //Erreur

 

On voit bien que l'objet pers peut appeler la méthode methodeCommune() mais pas combattre() ! L'objet chevalier lui peut appeler les deux! Il a donc bien hérité des méthodes de la classe Personne !

 

Intéressons-nous maintenant à notre troisième objet. Cela peut sembler tordu d'avoir déclaré une variable de type Personne et de finalement avoir instancié la classe Chevalier. En effet, cela l'est quelque peut, mais ça reste possible et utile dans quelques cas! Vous remarquerez également que ce même objet ne peut pas appeler la méthode combattre() ! Nous avons en fait bien instancié un objet de type Personne.

 

C'est ainsi que j'introduis avec habileté une nouvelle notion: la conversion ou Cast !

Les Casts peuvent causer de grosses erreurs de compilation alors utilisez-les avec précautions !

Le principe, c'est de convertir le type d'un objet en un autre. Ici nous allons convertir chevalier2 en Chevalier afin de pouvoir appeler la méthode combattre().

((Chevalier)chevalier2).combattre();

 

Et là, ça fonctionne! Faites bien attention aux parenthèses, elles jouent un rôle crucial. Le nouvel objet obtenu par le Cast est bien (Chevalier)chevalier2 , alors il est logique de le mettre entre parenthèses.

 

Attention comme je le disais plus haut, les Cast ne sont pas toujours possibles! Il faut que l'objet convertit ait un lien avec le type en lequel il est convertit! Dans notre cas, Chevalier à un lien avec Personne car il en hérite.

Cela est également possible dans l'autre sens:

 

Ajoutons une variable d'instance nommée i dans nos deux classes. Dans Personne elle vaudra 0 et dans Chevalier elle vaudra 42.

System.out.println(chevalier.i);
System.out.println(((Personne)chevalier).i);

 

Vous remarquerez que si vous lancez votre programme, le premier nombre affiché sera 42 et le second sera 0! Les valeurs des variables sont bien modifiées après le Cast ! De même, essayez ceci et vous verrez que ce n'est pas possible !

((Personne)chevalier).combattre();

 

Pour les variables numériques, les Casts sont également possibles !

double d = 1.581061;
int i = (int)d;
		
System.out.println(i);

 

i sera égal à 1. En réalité on a réalisé ce que l'on appelle en mathématiques une troncature! Dans le sens inverse, si i était égal à 1 et que l'on convertissait i en double dans d, d serait égal à 1.0!

 

Revenons à nos classes et déclarons-y un constructeur par défaut.

//Dans la classe Personne	
public Personne() {
	System.out.println("Instanciation d'une personne!");
}
//Dans la classe Chevalier
public Chevalier() {
	super();
	System.out.println("Cette personne est un chevalier!");
}

 

Instanciez ensuite, dans la méthode principale, un objet de type Chevalier et lancez votre programme.

Vous constaterez que le contenu du constructeur de la classe mère Personne s'est aussi exécuté!

Le responsable, c'est ce nouveau mot-clef : super. Ici le mot-clef s'occupe d'appeler le constructeur de la classe mère, mais sachez qu'il fonctionne également pour les méthodes!

Petite parenthèse, il est en effet possible de redéfinir une méthode de la classe mère dans la classe fille, afin de lui faire faire des choses différentes. Il suffit juste d'ajouter l'annotation @Override au dessus de la déclaration de la méthode. ;) Exemple avec la méthode methodeCommune() (dans la classe Chevalier) :

 
@Override
public void methodeCommune() {
	System.out.println("Je sais faire!");
}

 

Si on appelle cette méthode depuis un objet Chevalier, il sera marqué "Je sais faire" dans la console. Par contre si on appelle cette méthode depuis un objet Personne, il sera marqué "Tout le monde sait faire ça!".

 

Pour revenir au mot-clef super, ce dernier peut aussi être utilisé pour accéder aux méthodes et variables de la classe mère.

@Override
public void methodeCommune() {
	super.methodeCommune();
	System.out.println("i chevalier = " + this.i);
	System.out.println("i personne = " + super.i);
}

 

Appelez cette méthode écrira ceci dans la console :

Tout le monde peut faire ça!
i chevalier = 42
i personne = 0

 

Génial n'est-ce pas?

 

Vous souvenez-vous du mot-clef protected ? Il vous permet de restreindre la portée d'un champ aux classes héritées. C'est à dire que seules les classes héritant de celle où a été déclarée le champ (ou se trouvant dans le même package) pourront l'utiliser! Si une classe hérite d'une classe se trouvant dans un autre package, elle aura quand même accès à ses champs déclarés protected !

 

Voyons maintenant, dans la continuité des Casts et de l'héritage, le polymorphisme! C'est le fait de manipuler un objet sans connaître son type. Cela rejoint ce que je disais tout à l'heure avec Personne chevalier2 = new Chevalier();. En effet, il est possible de stocker un objet de type Chevalier dans un objet Personne. Personne est alors la super-classe de Chevalier. Ce que vous ne savez pas encore, c'est que les méthodes redéfinies (Override) comme expliqué juste au dessus, sont quand à elles exécutées comme elles devraient l'être avec un objet Chevalier !

 

Essayez donc!

Personne pers = new Chevalier();
pers.methodeCommune();
Tout le monde peut faire ça!
i chevalier = 42
i personne = 0

 

C'est bien ce que nous voulons faire dans methodeCommune() du Chevalier et non pas dans celle de la classe Personne!

 

Attention cependant à ne pas confondre une méthode surchargée et une méthode polymorphe!

Souvenez-vous, les méthodes surchargées sont caractérisées par des paramètres différents.

Les méthodes polymorphes ont quand à elles exactement les mêmes paramètres mais les utilisent différemment!

 

Pour finir, vous ne vous en doutiez peut-être pas, mais absolument toutes les classes en Java héritent de la super-classe Object!

 

Voyons un peu les fonctionnalités qu'elle nous propose, et donc les méthodes que vous pourrez redéfinir! En réalité nous en verrons que trois car les autres ne sont pas encore de votre niveau.

 

Comme pour les Strings, il est possible de comparer deux objets !

 
objet.equals(autreObjet); //retourne un boolean

 

Vous ne devriez pas redéfinir celle-ci, ou du moins si vous le faites, pensez à bien appeler la méthode de la classe mère avec super. ;)

 
@Override
public boolean equals(Object obj) {
	boolean b = super.equals(obj);
	System.out.println("Comparaison d'objets...");
	System.out.println("Résultat: " + b);
	return b;
}

 

Ensuite, la méthode toString(), que vous avez surement déjà rencontré, permet de renvoyer un String décrivant la classe et/ou ses variables.

Celle-ci est la plus souvent redéfinie.

Une petite précision: quand vous mettez un objet en argument dans un System.out.println();, ce qui sera affiché sera en réalité le retour de la méthode toString() de ce dernier!

 

Pour finir, il est possible de retrouver facilement d'où vient une classe. J'entends par là dans quel package elle se trouve. Il suffit d'utiliser la méthode getClass().

System.out.println(chevalier.getClass());

 

Donnera:

class fr.bukkit.jeremgamer.cours.Chevalier

 

En réalité, nous récupérons un objet Class, mais comme vous le savez, par le biais de System.out.println();, nous appelons la méthode toString() de la classe Class! (Ne vous embrouillez pas trop avec ça non plus ^_^ )

 

Résumé:

 

  • L'héritage permet de hiérarchiser son code.
  • Les classes héritant d'une autre héritent de leur méthodes.
  • Ces méthodes peuvent être redéfinies, c'est le polymorphisme.
  • Un Cast est une conversion du type d'un objet en un autre. A utiliser avec prudence!
  • Le mot-clef super permet de faire appel aux méthodes et variables de la classe mère.
  • Toutes les classes en Java héritent d'une seule et même super-classe: Object.

 

 

JeremGamer

 

 

2cp1ED5.pngtNIpRtq.png

SystemGlitch

blog-0250611001431874385.png

Aujourd'hui, au lieu continuer sur notre lancée sur les classes, nous allons nous arrêter un moment et nous intéresser aux Strings. Ces chaines de caractères ne sont pas si simple que ça à manipuler! Alors voici quelques astuces! ;)

 

Nous allons donc faire un petit tour de ce que l'API Java nous propose.

 

Commençons par la recherche dans un String. Vous souhaitez trouver un caractère en particulier (le récupérer sous forme de char). Si vous savez à quel index vous devez chercher, alors on utilisera la méthode "charAt(int)". L'index, c'est le numéro de caractère, au même titre qu'un index dans les tableaux. Attention le premier caractère est à l'index 0! (Vous pouvez récupérer le nombre total d'index à l'aide de la méthode "length()" qui retournera un int)

String s = "Une chaine de caractères";
char c = s.charAt(5);
System.out.println(c);

 

Votre programme vous affichera 'h' ! Si vous essayez de récupérer l'index 3, vous constaterez que rien ne s'affiche dans la console. C'est parce que vous avez récupéré un espace.

 

Maintenant vous voulez savoir à quels index se trouvent tous les 'e'.

Nous allons tout d'abord lire le String caractère par caractère puis ajouter les index auxquels nous avons trouvé des 'e'. Pour finir nous afficherons tous les index que l'on cherchait dans la console.

String s = "Une chaine de caractères";
ArrayList<Integer> index = new ArrayList<Integer>();
for(int i = 0 ; i < s.length() ; i++) {
	if(s.charAt(i) == 'e') {
		index.add(i);
	}
}
System.out.println("Les \'e\' dans ce String se trouvent aux index suivants:");
for(int i : index) {
	System.out.println(i);
}

 

Le résultat donne la figure suivante :

Les 'e' dans ce String se trouvent aux index suivants:
2
9
12
22

 

Il est également possible, d'une manière plus simple, de trouver l'index du premier caractère correspondant trouvé.

Cette méthode fonctionne aussi avec une chaine de caractères.

System.out.println( s.indexOf("ne") );

 

Il est possible de donner un index comme point de départ. Ajoutez un argument de type int, il représentera l'index à partir duquel votre programme commencera les recherches.

System.out.println( s.indexOf("ne" , 4) ); //Le programme commencera à chercher à l'index 4

 

Grâce à la méthode "lastIndexOf(char/String)", vous pouvez également récupérer l'index du dernier caractère ou de la dernière chaine de caractère correspondante.

 

 

Vous souhaitez savoir si un String contient ou non un caractère ou une suite de caractères précise.

Rien de plus simple !

 
System.out.println( s.contains("bla") );

 

La méthode "contains(CharSequence)" vous retournera un boolean.

 

Vous pouvez également tester si un String commence ou termine par une certaine chaine de caractère avec les méthodes "startsWith(CharSequence)" et "endsWith(CharSequence)". Ces deux méthodes vous retournerons également des boolean.

System.out.println( s.startsWith("Une") );
System.out.println( s.endsWith("ères") );

 

Vous pouvez également définir un point de départ pour la recherche avec la méthode "startsWith(CharSequence)" comme pour la méthode "indexOf(int)"!

 

Enfin pour tester une égalité, n'utilisez pas l'opérateur "==" mais la méthode "equals(String)". La méthode "equalsIgnoreCase(String)" propose la même fonctionnalité mais ne prend pas en compte les majuscules.

s.equalsIgnoreCase("UNE CHAINE de caractèRES") //retournera true

 

Vous savez désormais fouiller dans un String. Maintenant voyons comment les modifier.

 

Avant de commencer à faire le tour des méthodes disponibles à ce sujet, j'aimerais insister sur un point: Certains caractères ne sont pas acceptés dans les Strings: " , ' , \ , les retours à la ligne et les tabulations.

Alors voici comment faire :

String s = "pour écrire \" il faut mettre un antislash";
String s2 = "pour écrire \' il faut mettre un antislash qui s'écrit ainsi: \\";
String s3 = "une tabulation ---> \t";
String s4 = "retour à la ligne \n et voila!";

 

A l'aide des antislash vous pouvez aussi utiliser les caractères ASCII en unicode :

String s = "un accent grave: \u00e8";

 

Pour tous les connaître vous pouvez vous référer à ce site. ;)

 

Passons aux choses sérieuses! Pensez à une chose, utiliser la méthode sans assigner son retour à une autre variable ou à la variable source, c'est perdre les modifications! (Voir l'exemple juste en dessous)

Il est possible de facilement mettre tous les caractères en majuscule ou en minuscule :

 
String up = s.toUpperCase(); //Met tout en majuscules
String low = s.toLowerCase(); //Met tout en minuscules

 

Remplaçons une partie de ce String par une autre.

Nous utiliserons la méthode "replaceAll(String , String)" ou "replace(String , String)". Le premier argument sera ce que l'on cherche à remplacer et le second sera par quoi on le remplacera. (Fonctionne aussi avec des char)

String replaced = s.replaceAll("e" , "beuh"); //On remplacera ici tous les "e" par des "beuh"

 

Il est possible de juste remplacer la première correspondance :

String replaced = s.replaceFirst("e" , "beuh");

 

Nous allons maintenant "découper" un String en plusieurs afin d'avoir un tableau. Dans l'exemple qui suit nous récupérerons tous les mots.

String[] mots = s.split(" ");

 

Votre String est découpée et votre tableau comporte chacune de ses parties, sans les espaces. Sachez qu'en utilisant cette méthode, les caractères coupés ne seront pas comptés. Dans votre tableau, il n'y aura aucun espace!

 

Il nous reste une méthode à voir, la plus compliquée à comprendre, j'ai nommé "substring(int , int) / substring(int)"!

Cette méthode vous permet de récupérer une partie d'un String. Le premier argument définit l'index auquel on commencera à couper et le second l'index on on s’arrêtera.

 
String sub = s.substring(4 , 8); //Ici on récupère le contenu à partir de l'index 4 jusqu'a l'index 8. Ce qui donnera "chai".

 

Si vous ne mettez qu'un seul argument, alors votre programme récupérera le bout de String depuis l'index spécifié jusqu’à là fin.

String s = "Une chaine de caractères";
String sub = s.substring(4 , 8);
String sub2 = s.substring(4); //revient au même que s.substring(4 , s.length());
System.out.println(sub);
System.out.println(sub2);

 

Ce code donnera le résultat suivant :

chai
chaine de caractères

 

Une dernière chose, il est possible d'appeler une méthode de la classe String sans avoir besoin d'initialiser une variable !

"Une chaine de caractères".toLowerCase();

 

Voila vous savez l'essentiel sur les Strings! Je vous propose donc un petit TP sur la manipulation de String. Votre programme doit être capable de trouver la chaine de caractères contenu entre les balises ":SEARCH:" dans cette page web.

Voici quand même une petite aide. ^_^

 

Pour récupérer le code source d'une page web, vous devrez faire ceci :

 
URL u = new URL(ADRESSE); 
InputStream is = u.openStream();
DataInputStream dis = new DataInputStream(new BufferedInputStream(is));
String s;

 

Ensuite vous pourrez lire une à une les lignes de la page avec une boucle while :

while ((s = dis.readLine()) != null) {...}

 

Voila voila ! Bonne chance à tous ! ;) N'oubliez pas de me joindre si vous avez besoin d'aide ! :) Je vous invite aussi à m'envoyer en MP vos travaux une fois finis !

 

Correction :

 

Spoiler

 

 

public static void main(String[] args) {
	try {
		URL u = new URL("https://github.com/JeremGamer/FRBukkitTutorials/blob/master/README.md");
		InputStream is = u.openStream();
		DataInputStream dis = new DataInputStream(new BufferedInputStream(is));
		String s;
		String toFind;

		while ((s = dis.readLine()) != null) {
			if (s.contains(":SEARCH:")) {
				toFind = s.substring(s.indexOf(":SEARCH:") , s.lastIndexOf(":SEARCH:"));
				toFind = toFind.substring(":SEARCH:".length());
				System.out.println("Trouvé: " + toFind);
				break;
			}
		}
	} catch (IOException e) {
		e.printStackTrace();
	}		
}

 

 

 

 

JeremGamer

 

 

2cp1ED5.pngtNIpRtq.png

SystemGlitch

blog-0860454001431873548.png

Comme vous le savez, nous utiliserons désormais plusieurs classes. Pour organiser notre projet et ranger nos classes, selon leur fonction ou leur "groupe", afin de s'y retrouver, on utilisera les packages. Ce sont en quelque sorte des dossiers. Les classes étant alors triées par groupe, on pourra jouer avec leur portée et l'accessibilité de leurs méthodes et variables. Les packages permettent aussi plus de lisibilité et de portabilité. C'est à dire que les classes qu'il contient seront alors plus simples à transporter dans d'autres programmes.

 

Ce chapitre sera court mais chargé en conventions.

 

Jusque là nous n'avons pas utilisé les packages dans nos projets, sauf une fois, quand nous avons importé la classe Scanner afin de communiquer avec l'utilisateur! Je vous invite alors à créer un nouveau projet, cette fois il sera bien propre et respectera la grande majorité des conventions! :)

 

Désormais, avant de commencer à coder, il faudra créer vos packages !

 

L'instant Conventions :

 

  • Aucune classe ne doit être contenue dans le package principal ("src").
  • Les packages sont comme des dossiers, cependant on utilise pas des "\" ou des "/" comme séparateur mais des points "."!
  • Il est obligatoire d'utiliser un package principal nommé d'une certaine manière. Je détaillerais ça dans un instant.
  • Tous les noms des packages doivent être intégralement en minuscules et ne pas doivent contenir de mot-clef! (Ou sinon ajoutez un "_" après. Exemple: "package_")
  • Ils peuvent contenir des chiffres.

 

Créez alors votre premier package en faisant un clic droit sur "src" dans l'explorateur à gauche dans votre IDE et choisissez "New -> Package". (Vous pouvez aussi cliquer sur l'icône située dans la barre d'outils mais il faudra bien faire attention au nom du package pour qu'il soit situé à l'endroit souhaité!)

 

eGMBu45.png

 

Nommez ensuite votre package de la manière suivante:

  • Commencez par votre nationalité ou, si vous avez un site web, la fin du nom de domaine (com/fr/org/gouv/edu/...)
  • Le nom de l'organisation pour laquelle vous travaillez ou alors le nom de votre site web.
  • Votre nom ou pseudonyme.
  • Le nom de votre programme (n'oubliez pas de le mettre intégralement en minuscules!)

 

XYQvfiM.png

 

Puis cliquez sur "Finish". Votre package principal est créé. En réalité il s'agit de sous-dossiers de "src". De manière classique on aurait écrit ça ainsi: "src/fr/bukkit/jeremgamer/cours".

 

La classe contenant la méthode principale (Portant le nom du programme en général) devra se trouver dans ce package et que les autres pourront se trouver aussi bien ici que dans d'autres sous-packages.

 

Voila il n'y a pas grand chose à savoir de plus sur les packages! ^_^ Passons à l'accessibilité maintenant.

 

 

Vous avez déjà croisé des mot-clef que j'ai refusé d'expliquer de nombreuses fois. Je parle de public , private et protected. Ces mot-clef définissent la portée des classes, variables et méthodes. C'est à dire qu'elles auront différents niveaux d'accessibilité depuis les autres classes.

  • public défini une portée illimitée. On peut accéder au champ en question depuis n'importe où.
  • private réduit la portée au maximum. Seule la classe dans laquelle il se trouve peut l'utiliser, à moins de passer par des accesseurs et mutateur (getters and setters).
  • protected réduit la portée aux classes filles et au package. Seules les classes se trouvant dans le même package ou héritant de celles-ci auront accès aux champs et méthodes. Nous verrons cela dans le chapitre suivant.
  • Seul public peut être utilisé à la déclaration d'une classe. Si vous voulez qu'elle ne puisse être utilisée que par les classes étant contenues dans le même package, alors ne mettez rien.

 

Ces mot-clefs se placent tout au début de la déclaration d'un champ :

 
private int i;

 

Petite parenthèse rapide sur les getters and setters. Souvenez-vous le chapitre sur les méthodes. Les getters and setters sont une sécurité pour pouvoir contrôler la modification d'une valeur. Ainsi elle ne peut pas être changée de manière complètement arbitraire. Par exemple si vous voulez qu'un message s'affiche dans la console lors de la modification de la valeur d'une variable, vous passerez par un setter et rendrez la variable en question private.

 
public setAmount(int i) {
      this.i = i;
      System.out.println("i à changé de valeur! i = " + i);
}

 

Il est important de restreindre au maximum l'accessibilité de vos variables. Votre code n'en sera que solidifié!

 

Clarifions un peu l'idée d'accessibilité.

Créez un nouveau sous-package à "cours" et créez-y deux classes.

 
package fr.bukkit.jeremgamer.cours.exemples;

class Exemple {} //Sa portée est alors restrinte à son package car on n'a pas spécifié de modificateur

package fr.bukkit.jeremgamer.cours.exemples;

public class Exemple2 {}
 

Créez enfin votre classe principale et instanciez dans la méthode principale ces deux classes.

 
package fr.bukkit.jeremgamer.cours;

import fr.bukkit.jeremgamer.cours.exemples.Exemple2;

public class Cours {

	public static void main(String[] args) {
		Exemple e = new Exemple();
		Exemple2 e2 = new Exemple2();
	}
}

 

Vous constaterez que Eclipse ne reconnait pas la classe Exemple. C'est tout simplement car celle-ci n'a pas de modificateur et qu'elle se trouve dans un autre package. Essayez maintenant de l'instancier dans le constructeur de la classe Exemple2.

 
package fr.bukkit.jeremgamer.cours.exemples;

public class Exemple2 {
	
	public Exemple2() {
		Exemple e = new Exemple();
	}
}

 

Et là il n'y a aucune erreur car Exemple2 se trouve bien dans le même package que Exemple!

 

Notez qu'un import de la classe est toujours nécessaire si celle-ci se trouve dans un autre package que celle où on y fait référence!

 

Maintenant, passons aux bonnes pratiques. Vous avez entendu parler juste au dessus de getter et setter. Qu'est-ce donc exactement?

Et bien c'est un moyen supplémentaire de solidifier et sécuriser son code en premier temps. Pour une raison très simple: vous contrôlez tout ce qui se passe avec vos variables. Ainsi vous êtes sûr que la personne qui utilisera votre code (ou vous-même sans y faire attention) ne modifiera pas vos variables "sensibles" n'importe comment!

 

Voici un petit exemple syntaxique dans une classe tout ce qu'il y a de plus simple.

 
public class Exemple {

     private int nombre; //Variable d'instance nombre

     public Exemple() {
          nombre = 0;
     }

     public int getNombre() { //Getter
          return nombre;
     }

     public void setNombre(int nombre) { //Setter
          this.nombre = nombre;
     }

}

 

Vous me direz que pour l'instant vous ne voyez pas trop l'intérêt. Et bien passons directement à la suite!

Nous souhaitons que la variable ne soient en aucun cas négative, et inférieure à 1000.

 

Le problème vous sautera aux yeux quand vous lirez cet exemple où l'on n'utilise pas cette pratique : (On suppose que nombre est public)

 
//Simulation d'un évènement répétitif automatique
for(int i = 0 ; i < 5413 ; i++)
	exemple.nombre++;

 

La simulation que nous faisons sera considérée comme un utilisateur qui clique sur un bouton. Et ce dernier ne respecte pas notre contrainte! On s'était fixé un maximum de 1000, et là l'utilisateur la dépasse largement. Il n'y a qu'à ajouter un petit test pour voir si nombre est égal à 1000? Bien. Mais votre variable ne sera sûre que dans cette minuscule partie du code. Pourquoi ne pas économiser du temps et faire en sorte que ça se fasse automatiquement dès que la variable est modifiée?

Vous commencez à voir le principe? Très bien, faisons les modifications qui s'imposent à notre setter.

 
public void setNombre(int nombre) {
	if(nombre > 1000) //On fait les tests nécessaires au respect des contraintes
		nombre = 1000; //On assigne une valeur plus adaptée au paramètre
	else if(nombre < 0 && nombre >= -1000)
		nombre = Math.abs(nombre); //Valeur absolue du paramètre
	else if(nombre < -1000)
		nombre = 1000;

	this.nombre = nombre; //On finit par assigner la valeur à notre variable
}

 

Vous serez tous d'accord qu'il est bien plus intéressant de n'écrire ça qu'une seule fois n'est-ce pas? ^_^ Ici notre setter s'occupe tout seul d'ajuster la valeur. Si elle est supérieure à 1000 alors il ajuste à 1000. Si elle est négative alors il fera la valeur absolue de celle-ci, mais seulement si cette dernière est tout de même supérieure ou égale à -1000.

 

Voyons désormais l'utilité du getter. Votre variable est private, et donc elle n'est accessible que depuis l'intérieur de votre classe Exemple. Le getter vous permettra de récupérer la valeur de la variable, mais pas de la modifier !

 
exemple.getNombre() = 145; //Erreur de compilation!

 

En effet, ça ne pourra pas fonctionner puisque vous essayer d'assigner une valeur à une méthode, ce qui n'a pas grand sens...

 

Dans la même idée, on pourra copier la valeur retournée dans une nouvelles variable si besoin :

 
int copie = exemple.getNombre();

 

Quand copie sera modifiée, nombre restera intacte à l'intérieur de l'objet. :) Il n'y a plus qu'un seul moyen de la modifier: utiliser le setter. Les tests sont faits à l'intérieur, donc aucun soucis à se faire! :D

 
exemple.setNombre(exemple.setNombre()+1); //Incrémentera nombre

 

Il y a d'autres avantages aux getters et aux setters, mais nous n'en parlerons pas dans ce chapitre.

 

 

Résumé:

 

  • Les packages sont des dossiers dans lesquels on range les classes.
  • De nombreuses conventions les concernant sont à retenir!
  • Le séparateur pour la navigation dans les packages n'est pas un slash mais un point.
  • public, private et protected définissent la portée d'un champ lors de sa déclaration.
  • Restreignez la portée de vos champs au maximum. N'utilisez public que si c'est nécessaire.
  • Les getter et setter sécurisent la modification de vos variables private.

 

 

JeremGamer

 

 

2cp1ED5.pngtNIpRtq.png

SystemGlitch

blog-0724736001431873040.png

Jusque là vous travaillez sur une classe sans vraiment savoir ce que c'est. Nous allons y remédier !

Ce chapitre sera chargé en vocabulaire ! Il faudra bien le connaître !

 

Tout d'abord, désolé de me répéter, vous avez travaillé jusque là avec une seule classe, or en programmation orientée objet, vous allez en utiliser une multitude! Les perspicaces auront alors deviné ce qu'est un objet, terme que j'utilise déjà depuis un certain temps.

 

Grossièrement, un objet est une variable non primitive telle les String. Et oui les String sont des objets, bien qu'ils soient un peu à part, vous allez voir pourquoi.

Les objets sont donc des classes que nous avons instancié. Nous reviendrons là-dessus dans quelques instants. Avant ça décrivons un peu la structure d'une classe.

 
class Main { //Déclaration de la classe avec le mot-clef "class"

     //Variables de classe et d'instance

     Main() { //Constructeur
          //Instructions lors de l'instanciation
     }

     void methode() {
          //Instructions
     }

} //Fin de la classe

 

N'ayez pas peur tout ce vocabulaire va vous être expliqué sous peu ! ^_^

 

Je sais que maintenant vous ne devriez plus avoir trop de difficultés avec les accolades donc je vais passer cette partie.

 

Commençons par les variables de classe.

Les variables de classe sont des variables déclarées en dehors de toute méthode et dont on défini directement la valeur. Ainsi leur valeurs seront initialement les mêmes pour tout objet de cette classe instancié. Ces variables doivent être déclarées static.

 

Les variables d'instance sont déclarées sans valeurs, on les définira dans le constructeur. Ainsi les valeurs de ces variables pourront être différentes pour chaque objet de cette classe instancié.

 

Vous remarquerez que le constructeur est en quelque sorte une méthode mais sans type, il n'est même pas un void ! Le constructeur est la méthode appelée lors de l'instanciation de la classe grâce à l'opérateur new.

 

Avant que tout cela ressorte par vos oreilles, assemblons toutes ces notions car elles sont toutes liées !

Voici un petit exemple de classe :

class Main {

     static int nombre = 5; //Une variable de classe

     int nombre2; //Une variable d'instance

     Main(int n) { //Constructeur
          System.out.println("Instanciation de la classe Main");
          this.nombre2 = n;
     }

     void methode() {
          //Instructions
     }

} //Fin de la classe

 

On instancie donc cette classe de la manière suivante :

Main obj = new Main(2); //La valeur mise en argument peut-être n'importe quel int

 

Quand cette ligne sera lue par l'ordinateur, vous pourrez constater que "Instanciation de la classe Main" s'est écrit dans la console! Le contenu du constructeur s'est bien exécuté lors de l'instanciation de notre objet obj.

 

Vous avez surement remarqué un nouveau mot-clef : this. Il fait référence à l'objet courant. C'est à dire qu'avec lui, on peut accéder à toutes les variables et méthodes de la classe dans laquelle il se trouve à l'aide de l'opérateur ".". Ici on récupère nombre2. Il n'était pas nécessaire de mettre this cette fois-ci car le paramètre porte un autre nom. Imaginez la situation suivante :

     int n;

     Main(int n) { 
          n = n;
     }

 

Comment votre programme sait à quelle variable vous faites référence? C'est là que this devient intéressant ! On rajoute this devant le premier n dans le constructeur et là on sait alors que l'on fait référence à la variable d'instance n et non au paramètre !

 

Ça va vous suivez? :lol: Je sais bien que ce n'est pas évident alors je le rappelle, vous pouvez m'envoyer des MP pour plus d'explications si vous le souhaitez !

 

Petite parenthèse : Les variables internes au constructeur ou aux méthodes sont appelées variables locales.

     Main(int n) { 
          this.n = n;
          int i = 6; //Une variable locale
     }

 

Le ou les constructeurs d'une classe doivent porter le même nom que la classe.

 

Oui j'ai bien dit "les" constructeurs ! De la même manière que les méthodes, il est possible d'avoir plusieurs constructeurs, du moment qu'ils ont des paramètres différents !

class Main {

     Main() {
          System.out.println("Instanciation de la classe Main sans argument");
     }

     Main(int n) {
          System.out.println("Instanciation de la classe Main avec l'argument " + n);
     }

     Main(int n , int j) {
          System.out.println("Instanciation de la classe Main avec les arguments " + n + " et " + j);
     }

}

 

Une telle classe pourrait donc s'instancier de trois manières différentes :

Main obj = new Main();
Main obj2 = new Main(5);
Main obj3 = new Main(7 , 42);

 

Une classe à un constructeur par défaut, qui ne fait rien et qui ne requiert aucun argument. C'est pour cela que faire ceci est inutile :

class Main {

     Main() {}

}

 

Si vous décidez de ne pas mettre de constructeur sans argument, alors il sera impossible d'instancier la classe sans argument.

class Main {

     Main(int i) {
           //Instructions
     }

}

 

Avec une telle classe, il sera impossible de faire ceci :

Main obj = new Main();

 

La seule manière d'instancier cette classe sera alors de spécifier un int en argument.

 

Une classe est aussi un "casier" à méthodes. Les méthodes contenues dans une classe lui sont spécifiques. Il est donc impossible qu'une autre classe les utilise, à moins qu'elle dispose d'une instance de cette classe !

class Main {

     int i = 5;

     void methode() {
          System.out.println("Plop!")
     }

}

 

Notre classe Main ressemble à ça. Pour qu'une autre classe puisse l'utiliser, il faudra instancier la classe Main puis appeler la méthode en passant par l'objet et en utilisant l'opérateur ".". On peut aussi récupérer les variables de cette même classe.

Main obj = new Main();

obj.methode();
int j = obj.i;

 

Il existe un moyen d'utiliser des méthodes issues d'une autre classe sans l'instancier, mais pour cela, il faut que cette méthode soit statique, désignée par le mot-clef static. Ces méthodes ne doivent en aucun cas utiliser des variables d'instances ! Tout simplement car on n'est pas certain que la classe qui la contient à été instanciée !

static void methode() {
      //Instructions
}

 

Cette méthode pourra être appelée de n'importe où à n'importe quel moment !

Attention cependant, l'utilisation des méthodes statiques peut-être dangereuse. Il faut les utiliser que dans des cas précis et ne pas en abuser car ils peuvent énormément fragiliser votre code, ce qui peut causer de nombreuses erreurs et bugs! Il faut donc être bien prudent quand à leur utilisation.

 

Ces méthodes sont essentiellement utilisées pour des actions simples ne faisant pas entrer de facteurs modifiant le fonctionnement du logiciel. Des utilitaires en général. Par exemple on souhaite faire une classe qui permet d'effectuer tout un tas d'actions concernant une liste. (Cette classe est déjà existante dans l'API Java) Toutes ses méthodes seront alors statiques !

 

Les méthodes statiques s'appellent différemment. On met d'abord le nom de la classe d'où elle provient, l'opérateur "." puis le nom de la méthode suivie des arguments.

//Méthode existante, justement pour trier une liste grâce à l'API Java
Collections.sort(list); //La méthode sort est statique et issue de la classe Collections

 

J'allais oublier ! Vous l'avez sûrement déjà oublié donc je vous le rappelle : voici comment créer une classe avec Eclipse :

 

Commencez par faire un clic droit dans l'explorateur à gauche à l'endroit où vous souhaitez créer la classe, puis "New -> Class".

 

E046LwC.png

 

Vous ne savez pas encore tout sur les classes mais je pense que vous avez eu votre dose pour aujourd'hui ! ^_^

 

Nous ferons alors une petite pause avec les classes avant d'entamer des notions complexes qui y sont liées.

Vous pouvez être fiers de vous! Vous en savez beaucoup plus sur le jargon du développeur désormais ! :P

 

Résumé:

 

  • Un objet est une instance d'une classe.
  • On utilise donc une multitude de classes dans la programmation orientée objet.
  • Les variables de classes sont déclarées et définies en dehors de toute méthode.
  • Les variables d'instances sont déclarées en dehors de toute méthode et définies dans le constructeur lors de l'instanciation de la classe.
  • Le constructeur est la méthode exécutée lors de l'instanciation de la classe.
  • Il peut y avoir plusieurs constructeurs du moment qu'ils ont des paramètres différents.
  • Une classe s'instancie à l'aide du mot-clef new.
  • Pour pouvoir utiliser les méthodes d'une classe depuis une autre, on doit utiliser une instance de celle-ci.
  • Les méthodes statiques peuvent être appelées depuis n'importe où et n'importe quand, sans avoir besoin d'instancier la classe qui la contient.
  • Elles sont à utiliser avec précaution !

 

 

JeremGamer

 

 

2cp1ED5.pngtNIpRtq.png

SystemGlitch

blog-0549339001431871409.png

Vous connaissez désormais pas mal de choses à propos du langage et vous êtes de plus en plus autonomes. Vous pouvez déjà faire vos propres programmes console tout seul comme des grands! Alors ce que je vous propose aujourd'hui, c'est d'apprendre à utiliser votre merveilleux IDE afin d'en retirer le meilleur et de parfaire vos projets ainsi que de documenter votre code en le commentant et en l'annotant!

 

Au programme, des astuces en tout genre, des fonctionnalités expliquées en détails et des explications à propos de ces commentaires et annotations! C'est parti!

 

Commençons par les commentaires. C'est tout simplement du texte qui sera ignoré par le compilateur. Il aura alors pour utilité de s'y retrouver dans son code. Au lieu de le relire intégralement et recomprendre ce qu'il fait, il suffit de lire son commentaire! Voici comment ajouter un commentaire:

 
//Un commentaire!

 

Sur Eclispe, les commentaires tels que celui-ci sont affichés en vert. Attention cependant, les commentaires écrits de cette manière ne peuvent prendre qu'une seule ligne! Il existe donc un moyen d'écrire des commentaires sur plusieurs lignes! C'est bien fait n'est-ce pas? ^_^

 
/*votre commentaire
pouvant se trouver sur
plusieurs lignes*/

 

/* et */ agissent comme des "balises". Tout ce qui se trouve entre elles seront mises en commentaire.

 

Maintenant documentons un peu notre code! Késako allez-vous me dire! La documentation, c'est le fait de renseigner ce que fait une méthode, une variable ou une classe ou tout simplement d'y ajouter des informations complémentaires comme la licence qui les accompagne. La documentation est très importante quand votre code devient imposant ou lorsqu'il est utilisé par d'autres personnes que vous. C'est le cas de l'API Java, qui est très bien documentée! Je vous apprendrais bientôt à lire la Javadoc officielle afin de pouvoir vous débrouiller avec n'importe quelle classe de cette vaste API! La syntaxe est la suivante:

 
/**
 * Votre documentation, sur plusieurs lignes 
 * Chaque ligne doit commencer par une astérisque
 * La documentation est au format HTML c'est à dire que ce <b>mot</b> sera en gras!
 */

 

Dans la documentation, il y a ce que l'on appelle les attributs qui renseignent sur de nombreuses choses comme les paramètres s'il s'agit d'une méthode, le type qu'elle retourne, etc... Les attributs commencent par @ et doivent porter un nom bien précis en fonction de ce qu'ils renseignent!

 

Attention il y a un ordre à respecter! Commencez par la description (au format HTML) puis les attributs!

 

Voici un tableau des différents attributs:

 

aHSD36F.png

 

Exemple avec la classe de calculatrice fournie en cadeau dans le chapitre précédent :

 
/**
 * Programme de calculatrice console.
 * @author JeremGamer
 * @version 1.0
 * @see http://www.bukkit.fr/index.php/topic/14174-jeremjacks-javaschool/
 */
public class Main {
	
	private static final String CALCULS[] = { "Additionner: 1",
			"Soustraire: 2", "Multiplier: 3", "Diviser: 4", "Racine: 5",
			"Cosinus: 6", "Sinus: 7", "Tangente: 8" };

	private static Scanner sc = new Scanner(System.in);
	private static double[] list;
	
	/**
	 * char permettant de relancer la boucle si l'utilisateur veut faire un autre calcul.
	 */
	private static char ch = 'O';

	public static void main(String args[]) {
		double i;
		while (ch == 'O') { //Lancement de la boucle globale
			System.out.println("Quel calcul voulez-vous effectuer?");
			for (String s : CALCULS) //On affiche les différents calculs possible et comment les choisir.
				System.out.println(s); 

			int c = sc.nextInt(); //On récupère la réponse de l'utilisateur

			switch (c) { //En fonction de sa réponse, on lui demande un ou deux chiffres et on effectue le calcul.
			case 1:
				list = ask();
				System.out.println("Résultat: " + additionner(list[0], list[1]));
				break;
			case 2:
				list = ask();
				System.out.println("Résultat: " + soustraire(list[0], list[1]));
				break;
			case 3:
				list = ask();
				System.out.println("Résultat: " + multiplier(list[0], list[1]));
				break;
			case 4:
				list = ask();
				System.out.println("Résultat: " + diviser(list[0], list[1]));
				break;
			case 5:
				System.out.println("Veuillez insérer le nombre:");
				i = sc.nextDouble();
				System.out.println("Résultat: " + racine(i));
				break;
			case 6:
				System.out.println("Veuillez insérer le nombre:");
				i = sc.nextDouble();
				System.out.println("Résultat: " + cosinus(i));
				break;
			case 7:
				System.out.println("Veuillez insérer le nombre:");
				i = sc.nextDouble();
				System.out.println("Résultat: " + sinus(i));
				break;
			case 8:
				System.out.println("Veuillez insérer le nombre:");
				i = sc.nextDouble();
				System.out.println("Résultat: " + tangente(i));
				break;
			default:
				System.out.println("Ce nombre n'est pas valable! Veuillez en entrer un compris entre 0 et 9");
				continue;
			}
			System.out.println("Voulez-vous effectuer un autre calcul? (O/N)");
			sc = new Scanner(System.in); 
			String carac = sc.nextLine(); //On récupère la réponse de l'utilisateur pour fermer le programme ou relancer la boucle.
			ch = carac.charAt(0);
			if (ch != 'O') {
				System.out.println("Merci d'avoir utilisé ma calculatrice! Au revoir!");
				sc.close();
				System.exit(1);
			}
		}
		sc.close();
	}

	/**
	 * Permet d'<b>additionner<b> deux valeurs décimales et de récupérer le resultat.
	 * @param i un nombre décimal.
	 * @param j un nombre décimal.
	 * @return la valeur de la somme des deux nombres spécifiés.
	 */
	private static double additionner(double i, double j) {
		return i + j;
	}

	/**
	 * Permet de <b>soustraire<b> deux valeurs décimales et de récupérer le resultat.
	 * @param i un nombre décimal.
	 * @param j un nombre décimal.
	 * @return la valeur de la différence des deux nombres spécifiés.
	 */
	private static double soustraire(double i, double j) {
		return i - j;
	}

	/**
	 * Permet de <b>mutliplier<b> deux valeurs décimales et de récupérer le resultat.
	 * @param i un nombre décimal.
	 * @param j un nombre décimal.
	 * @return la valeur du produit des deux nombres spécifiés.
	 */
	private static double multiplier(double i, double j) {
		return i * j;
	}

	/**
	 * Permet de <b>diviser<b> deux valeurs décimales et de récupérer le resultat.
	 * @param i un nombre décimal.
	 * @param j un nombre décimal.
	 * @return la valeur du quotient des deux nombres spécifiés.
	 */
	private static double diviser(double i, double j) {
		return i / j;
	}

	/**
	 * Permet de récupérer la <b>racine</b> d'un nombre.
	 * @param i un nombre décimal.
	 * @return la valeur de la racine du nombre spécifié.
	 */
	private static double racine(double i) {
		return Math.sqrt(i);
	}

	/**
	 * Permet de récupérer le <b>sinus</b> d'un nombre.
	 * @param i un nombre décimal.
	 * @return la valeur du sinus du nombre spécifié.
	 */
	private static double sinus(double i) {
		return Math.sin(i);
	}

	/**
	 * Permet de récupérer le <b>cosinus</b> d'un nombre.
	 * @param i un nombre décimal.
	 * @return la valeur du cosinus du nombre spécifié.
	 */
	private static double cosinus(double i) {
		return Math.cos(i);
	}

	/**
	 * Permet de récupérer la <b>tangente</b> d'un nombre.
	 * @param i un nombre décimal.
	 * @return la valeur de la tangente du nombre spécifié.
	 */
	private static double tangente(double i) {
		return Math.tan(i);
	}

	/**
	 * <b>Demande</b> à l'utilisateur d'entrer les deux nombres requis pour effectuer le calcul.
	 * @return les deux nombres décimaux requis pour le calcul.
	 */
	private static double[] ask() {
		double i;
		double j;
		System.out.println("Veuillez insérer le premier nombre:");
		i = sc.nextDouble();
		System.out.println("Veuillez insérer le deuxième nombre:");
		j = sc.nextDouble();
		return new double[] {i , j};

	}
}

 

Et voila du code bien commenté et documenté! Certes c'est long, mais ça en vaut la peine! Comme ça vous économisez du temps à rechercher de nombreuses informations dans votre code. De plus, tout le monde pourra le lire aisément!

 

Les attributs sont à ne pas confondre avec les annotations, qui commencent également avec @. Les annotations précèdent une classe, une interface, une méthode ou un champ et contiennent des méta-données, c'est à dire des données supplémentaires mais n'affectant pas le fonctionnement de l'entité concernée. Nous allons passer très rapidement dessus.

 
@Author( name = "JeremGamer", date = "05/11/2014" )
class MaClasse() { }

 

Cette annotation donne des informations concernant l'auteur et la date de création.

 

-----> Mais alors, quelle est la différence avec les commentaire de documentation? :huh:

 

La différence est que l'on peut utiliser dans le code les annotations et que le compilateur (Eclipse par la même occasion) en prend compte! Par exemple si on veut récupérer les annotations de la classe Main de notre calculatrice, on fera :

 
Class clas = Main.class;
Annotation[] annotations = clas.getAnnotations();

 

Ceux qui auront essayé de copier-coller l'annotation @Author plus haut se rendrons compte qu'il y a une erreur! En effet elle n'existe pas! Il faut d'abord la créer ainsi, puis suivre les instructions :

 

aIwq6U3.png

 

Choisissez le nom de votre annotation, ici on utilisera "Author", puis cliquez sur "Finish".

 

Il faudra ensuite donner les valeurs requises et votre annotation Author devrais ressembler à ça :

 
public @interface Author {
    String auteur();
    String date();
/*il est possible d'ajouter une valeur par défaut avec le mot-clef default
int version() default 1;
*/
}

 

Pour récupérer les valeurs il suffira donc de faire comme suit :

 
for (Annotation annotation : annotations) {
    if (annotation instanceof Author)  {
        Author info = (Author) annotation;
        System.out.println("auteur : " + info.auteur());
        System.out.println("date : " + info.date());
    }
}

 

Il existe déjà trois annotations dans Java:

  • @Deprecated qui indique l'obsolescence. Un avertissement sera donné par Eclipse et le compilateur en cas d'utilisation d'une entité dépréciée.
  • @Override redéfini une méthode. Si une méthode issue de la super-classe porte le même nom et les mêmes paramètres, il faut alors utiliser cette annotation pour redéfinir cette méthode. (Pas d'inquiétude, ces notions viendront dans le chapitre juste après celui-ci!)
  • @SuppressWarnings indique au compilateur et à Eclipse d'ignorer les avertissements concernant l'entité annotée. Pour ajouter le bon argument utilisez votre IDE: passez le curseur sur la ligne soulignée en jaune et sélectionnez "Add SuppressWarnings '<argument>' to <entité>".

 

1KlOXYY.png

 

Sans plus tarder, passons à autre chose! Vous aurez toujours une liste de choses à faire, à corriger, etc... Toutes ces choses sont des TODO. (littéralement "à faire") Vous pouvez les ajouter en commentaire directement comme suit :

 
//TODO: Chose à faire

 

Eclipse se chargera alors de mettre le mot TODO en bleu-vert gras. Et vous pourrez voir apparaitre un rectangle bleu dans la barre juste à droite de votre code! Comme ça on voit facilement où sont les choses à faire! Mieux encore, il suffit de cliquer dessus pour aller directement à la ligne concernée! C'est le même principe pour les erreurs et les avertissements, sauf que les rectangles seront respectivement rouge et jaune !

 

zSK1IXC.png

 

Eclipse propose aussi une liste de choses à faire plus avancée. Jetons-y un œil! Faites donc un clic droit dans le carré blanc en haut à droite de votre zone de travail et choisissez "New -> Task" puis "Local".

 

98tB7ae.png

 

Et là tout plein de paramètres peuvent être définis comme l'heure limite, le nom et la description précise. Remplissez ça rigoureusement de manière à bien comprendre votre tâche à la prochaine visite! S'il s'agit d'un bug pensez à ajouter le message d'erreur! ;) Vous pouvez toujours modifier rapidement certains de ces paramètres en faisant un clic droit dessus dans la liste. Ici on la marque comme terminée en cliquant sur le bouton "Complete" :

 

ZgAMLQn.png

 

Vous n'avez pas la motivation pour mettre votre code en forme et de faire un bel indent? Ou alors vous n'avez pas respecté les conventions et vous souhaitez y remédier rapidement? Rien de plus simple avec votre IDE! Faites une sélection dans votre code ou sélectionner la/les classe(s) à mettre en forme dans l'explorateur de package et faites un clic droit puis "Source -> Format".

 

R8FxAsv.png

 

Pour corriger l'indent uniquement, vous pouvez aussi utiliser Ctrl + I. ;)

 

C'est lourd de devoir relancer son programme à chaque modification? Encore une fois, bonne nouvelle! Nous allons utiliser le debug mode! Les modifications effectuées dans le code durant l'exécution sont prises en compte et le programme peut continuer à tourner! Il agit donc presque pareil que la flèche dans un rond vert pour lancer votre programme.

 

tCWGar4.png

 

Il existe bien sûr plein d'autres fonctionnalités mais celles-ci sont les plus importantes et cela prendrait vraiment beaucoup trop de temps de toutes les détailler !

 

Résumé:

 

  • Les commentaires sont importants pour se retrouver dans son code, surtout quand il commencer à prendre une certaines taille. De plus il sera plus lisible pour les autres.
  • La documentation permet de renseigner un maximum d'informations à propos d'une classe, une interface, une méthode, un champ ou un constructeur. Elle est essentielle quand le code devient imposant, quand vous travaillez à plusieurs ou quand quelqu'un d'autre est susceptible d'utiliser votre code. (Exemple de l'API Java)
  • Les attributs sont des informations contenues dans les commentaires de documentation.
  • Une annotation est aussi une sorte de documentation ses informations sont utilisables par du code.
  • Il ne faut pas confondre les attributs et les annotations car tous deux commencent par @.
  • La balise TODO est à connaître.
  • Pensez à remplir votre liste de choses à faire grâce à votre IDE!
  • Utilisez le debug mode afin de gagner du temps et ainsi ne pas avoir à redémarrer le logiciel à chaque modification.

 

 

JeremGamer

 

 

2cp1ED5.pngtNIpRtq.png

SystemGlitch

blog-0419999001431871091.png

Comme promis, je commence dès aujourd'hui à éclaircir les points noirs du chapitre précédent.

Nous allons parler aujourd'hui d'une notion extrêmement importante si ce n'est qu'un fondement même de la programmation orientée objet: Les méthodes.

 

Tout d'abord, une méthode est une série de commandes et d'actions étant effectuées lors de l'appel de cette dernière.

Tout cela peut vous paraître abstrait alors ne nous attardons pas plus, voici un exemple :

 
void direBonjour() {
     System.out.println("Bonjour!");
     System.out.println("Belle matinée n'est-ce pas?");
}

 

Important: une méthode est un bloc à part ! C'est à dire que chaque méthode est comprise entre les accolades du bloc de la classe mais pas dans le bloc de la méthode principale !

public class Main {	
	
	public static void main(String args[]) {		
		//Instructions
	}
	
	void direBonjour() {
		System.out.println("Bonjour!");
		System.out.println("Belle matinée n'est-ce pas?");
	}
}

 

J'insiste bien sur ce point: les instructions contenues dans une méthode s'effectuent uniquement lors de l'appel de celle-ci ! C'est à dire que dans le code ci-dessus, le programme n'écrira pas "Bonjour!" ni "Belle journée n'est-ce pas?". Alors comment fait-on?

 

C'est très simple: il suffit d'écrire son nom !

 

Maintenant allons plus loin et créons nos propres méthodes !

Pour se faire, il faut d'abord que je vous explique un autre élément. Les méthodes retournent une valeur ou non. Cette valeur est comme une autre, d'un certain type et peut-être stockée dans une variable du même type. Ce type est donc spécifié lors de la déclaration de la méthode. Ici nous avons une méthode void, c'est une exception car elle ne retourne rien! Elle se contente juste d'effectuer les actions demandées.

 

Voici donc la déclaration d'une méthode retournant un int :

int getInt() {
     //...
}

 

On commence donc par le type, puis le nom de la méthode, que l'on utilisera alors pour l'appeler.

 

 

L'instant Convention

 

  • Le nom des méthodes doit commencer par une minuscule, et comme pour les classes et variables peut comporter des majuscules et nombres ou même des underscores ("_") pour remplacer les espaces.
  • Comme toujours, utilisez des noms simples et descriptifs.
  • Le nom des méthodes définissant quelque chose doivent commencer par "set".
  • Le nom des méthodes retournant une valeur doivent commencer par "get".
  • Si la valeur retournée est un boolean, alors le nom de la méthode doit commencer par "is".
  • Quand il s'agit de:
  • Ajouter quelque chose, alors le nom doit commencer par "add".
  • Retirer quelque chose, alors le nom doit commencer par "remove".
  • Convertir un type en un autre (on appelle cela un Cast), alors le nom doit commencer par "to".

 

-----> Une méthode retourne une valeur, alors le mot-clef return évoqué dans le chapitre sur les boucles doit servir à cela non? ^_^

 

En effet, vous avez déjà rencontré le mot-clef qui entre ici en jeux! Il s'agit bien de return ! Commençons par le plus simple, les méthodes void. Dans ces méthodes, return n'est pas obligatoire. Cependant, si vous voulez arrêter la méthode à un certain endroit suivant une condition, vous pouvez l'utiliser au même titre que break !

 

Maintenant, voyons pour les méthodes retournant une valeur. Il est strictement obligatoire ! De la même manière que break, il stoppe la méthode, les instructions qui le suivent ne seront pas exécutées.

int getInt() {
     return 1;
}

 

Cette méthode est vraiment inutile mais elle est un bon exemple pour le point où nous en sommes. Cette méthode retournera 1 à chaque appel.

Petite précision: il est possible de mettre une variable à la place d'une valeur directe comme 1 ici, du moment qu'elle est du même type que celui spécifié dans la déclaration de la méthode.

 

Ainsi on peut stocker le retour de notre méthode dans une variable :

int i = getInt(); //i sera alors égal à 1 car getInt() retourne 1.

 

Sachez que les méthodes retournant une valeur peuvent être appelées comme les void, mais la valeur retournée sera perdue.

int getInt() {
     System.out.println("Bonjour!");
     return 1;
}

 

Une telle méthode pourra à la fois donner une valeur à une variable et écrire "Bonjour!" dans la console si elle est appelée de la même manière que juste au dessus! Cependant si on l'appelle "dans le vide", la valeur 1 sera perdue mais "Bonjour!" sera quand même écrit dans la console.

public static void main(String args[]) {
	getInt();
}
//Ce programme sera parfaitement fonctionnel!

 

Cependant s'il n'y a aucune utilité à ce que la méthode retourne une valeur, optez plutôt pour un void.

 

Afin qu'une méthode soit plus "modulable" et fonctionnelle pour plusieurs valeurs, nous ajouterons des paramètres en arguments. Un paramètre est une valeur à donner à la méthode lors de son appel. Cette dernière s'en servira alors pour effectuer ce qu'on lui demande. Exemple :

int multiplier(int nombre1 , int nombre2) {
	return nombre1 * nombre2;
}

 

La méthode multiplier contient ici deux paramètres. Lors de son appel, on pourra alors multiplier n'importe quels nombres entre eux !

int produit = multiplier( 5 , 2 ); //produit sera égal à 10

 

Nous prenons ici l'exemple des int mais sachez qu'une méthode peut être d'absolument n'importe quel type, qu'il soit primitif ou non !

 

Note: Deux méthodes (ou plus) peuvent porter le même nom à condition qu'elles aient des paramètres différents. On appelle cela la surcharge de méthode.

int methode(int nombre1 , int nombre2) {
	return nombre1 * nombre2;
}

double methode(int nombre1 , int nombre2) { //Cette méthode ne sera pas valable!
	return nombre1 * nombre2;           //Même si elle a un type différent, ses paramètres sont les mêmes
}                                           //que la méthode juste au dessus!

void methode() {
	System.out.println("Une méthode");
}

void methode(String message) {
	System.out.println(message);
}

 

L'utilisation des méthodes est très utile par exemple pour de longues suites d'actions répétées régulièrement et afin d’optimiser et de clarifier son code. Ainsi une simple ligne suffit à en exécuter de nombreuses autres !

 

Pour revenir aux points noirs du chapitre précédent: vous savez donc ce que vous avez fait en appelant la méthode nextLine() de votre variable sc de type Scanner !

 

Je vous offre en cadeau cette semaine un "pack" de méthodes permettant de coder une calculatrice ! ;)

double additionner(double i , double j) {
	return i + j;
}
	
double soustraire(double i , double j) {
	return i - j;
}
	
double multiplier(double i , double j) {
	return i * j;
}
	
double diviser(double i , double j) {
	return i / j;
}
	
double racine(double i) {
	return Math.sqrt(i);
}
	
double sinus(double i) {
	return Math.sin(i);
}
	
double cosinus(double i) {
	return Math.cos(i);
}
	
double tangente(double i) {
	return Math.tan(i);
}

 

Spoiler

Je suis de bonne humeur donc je vous ai concocté un petit (enfin gros) code cadeau supplémentaire! Voici une calculatrice pleinement fonctionnelle! ;) Je vous invite à tester ce code sur votre IDE. :)

 

import java.util.Scanner;

public class Main {
    
    private static final String CALCULS[] = { "Additionner: 1",
            "Soustraire: 2", "Multiplier: 3", "Diviser: 4", "Racine: 5",
            "Cosinus: 6", "Sinus: 7", "Tangente: 8" };

    private static Scanner sc = new Scanner(System.in);
    private static double[] list;
    private static char ch = 'O';

    public static void main(String args[]) {
        double i;
        while (ch == 'O') {
            System.out.println("Quel calcul voulez-vous effectuer?");
            for (String s : CALCULS)
                System.out.println(s);

            int c = sc.nextInt();

            switch (c) {
            case 1:
                list = ask();
                System.out.println("Résultat: " + additionner(list[0], list[1]));
                break;
            case 2:
                list = ask();
                System.out.println("Résultat: " + soustraire(list[0], list[1]));
                break;
            case 3:
                list = ask();
                System.out.println("Résultat: " + multiplier(list[0], list[1]));
                break;
            case 4:
                list = ask();
                System.out.println("Résultat: " + diviser(list[0], list[1]));
                break;
            case 5:
                System.out.println("Veuillez insérer le nombre:");
                i = sc.nextDouble();
                System.out.println("Résultat: " + racine(i));
                break;
            case 6:
                System.out.println("Veuillez insérer le nombre:");
                i = sc.nextDouble();
                System.out.println("Résultat: " + cosinus(i));
                break;
            case 7:
                System.out.println("Veuillez insérer le nombre:");
                i = sc.nextDouble();
                System.out.println("Résultat: " + sinus(i));
                break;
            case 8:
                System.out.println("Veuillez insérer le nombre:");
                i = sc.nextDouble();
                System.out.println("Résultat: " + tangente(i));
                break;
            default:
                System.out.println("Ce nombre n'est pas valable! Veuillez en entrer un compris entre 0 et 9");
                continue;
            }
            System.out.println("Voulez(vous effectuer un autre calcul? (O/N)");
            sc = new Scanner(System.in);
            String carac = sc.nextLine();
            ch = carac.charAt(0);
            if (ch != 'O') {
                System.out.println("Merci d'avoir utilisé ma calculatrice! Au revoir!");
                sc.close();
                System.exit(1);
            }
        }
        sc.close();
    }

    private static double additionner(double i, double j) {
        return i + j;
    }

    private static double soustraire(double i, double j) {
        return i - j;
    }

    private static double multiplier(double i, double j) {
        return i * j;
    }

    private static double diviser(double i, double j) {
        return i / j;
    }

    private static double racine(double i) {
        return Math.sqrt(i);
    }

    private static double sinus(double i) {
        return Math.sin(i);
    }

    private static double cosinus(double i) {
        return Math.cos(i);
    }

    private static double tangente(double i) {
        return Math.tan(i);
    }

    private static double[] ask() {
        double i;
        double j;
        System.out.println("Veuillez insérer le premier nombre:");
        i = sc.nextDouble();
        System.out.println("Veuillez insérer le deuxième nombre:");
        j = sc.nextDouble();
        return new double[] {i , j};

    }
}

 

 

Résumé:

 

  • Une méthode est une série d'actions s'effectuant lors de l'appel de cette dernière.
  • Une méthode retourne une valeur du type spécifié dans sa déclaration.
  • Cette valeur est retournée à l'aide du mot-clef return (obligatoire!).
  • Les méthodes de type void ne retournent rien.
  • Les valeurs retournées par une méthode peuvent être stockées dans une variable directement lors de l'appel de cette méthode.
  • Une méthode peut avoir des paramètres. Ce sont des valeurs requises à l'exécution de la méthode.
  • Plusieurs méthodes peuvent porter le même nom à condition qu'elles aient des paramètres différents.
  • De nombreuses conventions sont appliquées aux méthodes. Veillez à vous en souvenir!
  • Les méthodes optimisent et clarifient votre code.

 

 

JeremGamer

 

 

2cp1ED5.pngtNIpRtq.png

SystemGlitch

blog-0867624001431870718.png

Jusque là, vos programmes n'étaient pas interactifs, et donc sans grand intérêt... Réjouissez-vous, nous abordons enfin la communication avec l'utilisateur !

 

Comme nous ne travaillons que sur console pour l'instant, je vous apprendrais à communiquer via du texte, tel un analyseur syntaxique.

 

Sachez que c'est très simple ! Il n'y a que quelques petites choses qui vous seront inconnues mais qui ne tarderons pas à être expliquées dans les chapitres à venir.

 

Nous allons utiliser une classe de l'API Java qui nous permet donc "d'établir la communication": la classe Scanner!

Je vous invite donc dès maintenant à ajouter ceci à votre méthode principale :

Scanner sc = new Scanner(System.in);

 

Vous venez de déclarer une variable de type Scanner. Nous ne somme plus dans les types primitifs! Cette variable est ce que l'on appelle un Objet.

 

Remarque : C'est la première fois que nous utilisons une classe, et c'est un autre développeur qui l'a concoctée pour nous ! :)Nous allons devoir donc importer les ressources nécessaires dans notre code. Déclarez votre variable puis utilisez le raccourci clavier CTRL+SHIFT+O pour qu'Eclipse s'occupe de gérer automatiquement les imports. ;) Sinon passez votre curseur sur le mot Scanner qui devrait être indiqué comme une erreur et votre IDE devrait vous proposer "Import 'Scanner' (java.util)".

 

Et maintenant, commençons à parler un peu avec notre utilisateur :

public static void main(String args[]) {
		
	Scanner sc = new Scanner(System.in);
	
	System.out.println("Comment vous appelez-vous?");
		
	String nom = sc.nextLine();
	System.out.println("Bonjour " + nom + "!");
	sc.close();
		
}

 

Ce programme va donc demander son nom à l'utilisateur puis le lui retransmettre. Le résultat Devrait ressembler à ça :

Comment vous appelez-vous?
Robert <-------------- Entrée de l'utilisateur
Bonjour Robert!

 

Il y a plusieurs choses sur lesquelles il faut insister. Tout d'abord, notre objet sc est un flux. Cela signifie que comme un robinet, il faut le fermer après utilisation! C'est la dernière ligne: sc.close();

Tous les flux se ferment de la même manière, avec la méthode close().

 

Ensuite, vous remarquerez que le programme se fige temps que l'utilisateur n'a pas répondu à la question. Cela provient de la ligne sc.nextLine();. C'est elle qui s'occupe de récupérer la réponse de l'utilisateur, que l'on stocke alors dans une variable String. Une fois que l'utilisateur aura validé son entrée, le programme se poursuivra.

 

Ici nous utilisons une méthode permettant de récupérer une String. Sachez qu'il existe aussi des méthodes pour récupérer des types primitifs !

Scanner sc = new Scanner(System.in);
int i = sc.nextInt();
short s = sc.nextShort();
double d = sc.nextDouble();
long l = sc.nextLong();
byte b = sc.nextByte();
float f = sc.nextFloat();
boolean bl = sc.nextBoolean();
//Attention il n'existe pas de méthode pour récupérer les char. Utilisez alors la méthode nextLine().

 

Voila il n'y a rien d'autre à savoir pour l'instant sur l'utilisation de cette classe. Cependant il y a un conseil que j'aimerais vous donner par rapport à la communication avec l'utilisateur:

  • Règle d'or: Ne JAMAIS faire confiance à ce dernier! Effectuez toujours des tests pour voir ce qu'il fait! Par exemple s'il rentre une lettre au lieu d'un chiffre dans une calculatrice, ça ne risque pas de bien fonctionner!

 

En application , soit vous fermez le logiciel, l'utilisateur n'avait qu'a répondre correctement, soit vous utilisez une boucle !

 

Si vous avez bien suivi au cours précédent, je pense que vous saurez quelle boucle utiliser !

 

Ajoutons quelques petites choses à notre programme. Maintenant que nous connaissons le nom de l'utilisateur, nous allons lui redemander et vérifier s'il se paie notre tête ou non.

public static void main(String args[]) {
	
	Scanner sc = new Scanner(System.in);
	
	System.out.println("Comment vous appelez-vous?");
	
	String nom = sc.nextLine();
	System.out.println("Bonjour " + nom + "!");
		
	System.out.println("Pouvez-vous me répéter votre nom s'il vous plait?");
	String confirmation = sc.nextLine();
		
	while(!confirmation.equals(nom)) {
		System.out.println("Vous mentez! Répondez sérieusement!");
		confirmation = sc.nextLine();
	}
		
	System.out.println("Merci! Votre nom est bien " + confirmation + ".");
			
	sc.close();
		
}

 

Vous pouvez tester! N'est-ce pas génial? Si cela vous amuse vous pouvez même ajouter des comportements différents en fonction du nombre de fois que l'utilisateur ne répond pas correctement !

 

Concentrons nous sur la boucle. On y teste si la variable confirmation est différente de nom récupéré juste avant.

Donc tant qu'elles ne seront pas égales, le programme redemandera à l'utilisateur de "répondre sérieusement" !

La boucle se termine enfin quand l'utilisateur réécrit exactement son nom, on peut alors lui dire merci et terminer le programme tout en pensant à bien fermer le flux de sc.

 

 

Résumé :

  • La classe Scanner permet de lire les entrées clavier.
  • Il existe de nombreuses méthodes permettant de récupérer les différents types primitifs et les String.
  • Les Scanners sont des flux, il faut les fermer après utilisation.
  • Ne jamais faire confiance à l'utilisateur !
  • L'utilisation des boucles dans les programmes console est très importante.

 

 

JeremGamer

 

 

2cp1ED5.pngtNIpRtq.png

SystemGlitch

blog-0114930001431870700.png

Vous rencontrerez très souvent un cas de figure: Vous voulez effectuer la même action, ou des actions se ressemblant beaucoup un grand nombre de fois. Pour cela, il existe ce que l'on appelle les boucles ! C'est tout simplement une structure dont le contenu se répétera un nombre de fois donné ou indéterminé.

Je m'explique: Imaginez-vous dans une situation concrète. Votre programme permet de lister tous les patients enregistrés dans les fichiers d'un médecin. Vous ne savez donc pas combien de fois la boucle devra tourner. De plus, le nombre de patients pourra varier, on aura donc un nombre de tours indéterminé. On utilisera une boucle while.

Maintenant, imaginez que votre programme doit effectuer dix fois la même action par patient, à peu de chose près. Le nombre de tours est déterminé et ne changera pas au cours de l'exécution du programme. On utilisera donc une boucle for.

Commençons par la plus simple: la boucle while.

Il n'y a qu'a traduire depuis l'anglais pour comprendre son utilité: "Tant que" l'expression donnée en argument est vraie (une expression booléenne donc), les commandes spécifiées dans son bloc (accolades) seront exécutées en boucle.

Un exemple vaut mieux que de longues phrases :

 
int i = 5;
int j = 10;
while( i < j ) {
      System.out.println(i  + " < " + j);
}
//Bien sûr, de la même manière que les conditions, on peut ne pas utiliser les accolades s'il n'y a qu'une instruction à répéter.

 

Ici, votre boucle écrira que i est inférieur à j tant que ce sera vrai.

Les plus malins auront repéré un gros problème.

En effet, ici, cette boucle tournera indéfiniment! Et oui! i et j ne changent pas de valeur donc i sera toujours inférieur à j et votre console sera remplie à jamais de texte! On appelle cela une boucle infinie. Il faut donc y faire très attention !

Pour éviter ce problème, nous incrémenterons i et constaterons le résultat. La boucle ne tournera que i - j fois soit 10 - 5 fois donc 5 fois. Essayez vous-même !

int i = 5;
int j = 10;
while( i < j ) {
     System.out.println(i  + " < " + j);
     i++;
}

 

Le résultat donnera la figure suivante :

5 < 10
6 < 10
7 < 10
8 < 10
9 < 10

 

Il existe une autre syntaxe pour cette boucle. Son fonctionnement est le même sauf que les instructions à exécuter se trouveront dans un bloc do. Cette forme porte le nom de "do-while".

int i = 5;
int j = 10;

do {
     //Instructions
} while (i < j); //Notez bien qu'il y a un point virgule à la fin!

 

Il y a cependant une petite différence avec sa sœur, la boucle while : la do-while s'exécutera au moins une fois, même si la condition n'est pas remplie !

Passons maintenant à la boucle for. Celle-ci vous donne la possibilité de lui spécifier combien de fois elle devra tourner. En plus de cela, elle a d'autres applications très intéressantes dont nous parlerons très, très bientôt !

Elle se syntaxe ainsi :

for(int i = 0 ; i < 10 ; i++) {
     //Instructions
}

 

Elle est un peu complexe par rapport à ce que nous avons vu jusque là, mais n'ayez crainte, elle est finalement simple d'utilisation. Il suffit de la connaître!

Lisons pas-à-pas cette ligne:

  • Le mot-clef for appelle une boucle.
  • On ouvre les parenthèses pour ajouter les arguments.
  • On déclare une variable avec la valeur choisie. Il est possible de lui assigner la valeur d'une variable déjà existante.
  • Un point virgule? :wacko: C'est là la particularité de cette boucle; les arguments sont séparés par des ";".
  • Un test. Il revient exactement au même que pour la boucle while. Vous pouvez mettre une variable déjà existante en deuxième nombre: i < j par exemple.
  • Et pour finir, l'actualisation. Ici c'est une incrémentation, mais il peut très bien s'agir d'une décrémentation ou d'un calcul classique comme i+=5. Attention cependant, vous ne pouvez utiliser que des opérations puis assignation dans ce champ !

 

-----> Mais alors, cette boucle fait la même chose qu'une boucle while avec une incrémentation de la valeur testée? :huh:

 

On peut dire ça, cependant, elle reste plus pratique et optimisée !

int i = 0;

while( i < 10 ) {
     System.out.println(i);
     i++;
}

//Revient au même que:

for(int i = 0 ; i < 10 ; i++)
     System.out.println(i);

 

La boucle for a une variante: la boucle for each. Elle permet d'utiliser l'intégralité des variables d'un tableau ou d'une collection.

C'est simple, "pour chaque valeur du tableau, effectuer telle action". Elle se syntaxe de la manière suivante :

int t[] = { 1 , 5 , 7 , 9 };

for(int i : t) {
     //Instructions
}

 

Je vais illustrer un peu cette notion plus compliquée à comprendre. Imaginez que chaque valeur du tableau est un client attendant à la caisse d'un supermarché. Chacun passe tour à tour et la caissière ré-exécute le passage des articles à chaque client. Quand il n'y aura plus de client, donc que le tableau aura déjà donné toutes ses valeurs, la caissière cessera de passer les articles.

Essayez quelque chose de très simple :

int t[] = { 1 , 5 , 7 , 9 };

for(int i : t) {
     System.out.println(i);
}

 

Vous constaterez que votre programme s'est contenté d'écrire un par un les nombres faisant partie de votre tableau puis, n'ayant plus rien à écrire, a stoppé la boucle.

Il est possible à tout moment d'utiliser break pour stopper la boucle. Cependant, les instructions qui suivent ce mot-clef dans le bloc ne pourront être effectuées !

while(i < j) {
     System.out.println(i);
     break; //La boucle ne s'exécutera qu'une seule fois car on la "casse" dès le premier tour!
     System.out.println("La boucle est stoppée"); //Erreur! Cette ligne se trouve après le break!
}

 

Vous pouvez utiliser un test :

while(i < j) {
     System.out.println(i);
     if(i == 8)
          break;
     System.out.println("Relancement de la boucle");
}
//Ici on teste si i est égal à 8. Si c'est le cas, on stoppe la boucle sans dire qu'elle se relance!

 

Maintenant, je vais vous expliquer une notion plus complexe.

Il est possible de mettre une boucle dans une boucle, elle-même dans une boucle. Comment gérer toutes ces boucles? :wacko:

Tout d'abord, illustrons la situation :

 

 

tEEq33A.png

 

Nous avons donc trois boucles, les unes dans les autres. Si vous ne souhaitez pas interrompre l'une d'entre elles depuis une autre, aucun souci à se faire. Sinon il va falloir s'accrocher !

Comme vous l'avez vu ici, j'ai identifié chaque boucle par une couleur. On peut faire à peu près la même chose en Java avec ce que l'on appelle les labels ! Il est donc possible de nommer une boucle au même titre qu'une variable !

label: while(i < j) {
     //Instructions
}

 

Dans notre situation, le code ressemblerait à cela (si on utilisait que des boucles for) :

vert : for(int i = 0 ; i < 10 ; i++) {
	//Instructions
	bleu: for(int j = 0 ; j < 10 ; j++) {
		//Instructions
		orange: for(int k = 0 ; k < 10 ; k++) {
			//Instructions
		}
		//Instructions après la boucle orange, dans la boucle bleu
	}
	//Instructions après la boucle bleu, dans la boucle verte
}
//C'est un bel exercice de compréhension des accolades n'est-ce pas?^^

 

A l'intérieur de chacune de ces boucles sera alors possible de stopper sa boucle enfant ou sa boucle parent ! Il suffit d'utiliser le mot-clef break et le label de la boucle à stopper !

break vert;

 

Et cela fonctionne également pour le mot-clef que nous allons voir dès maintenant !

continue permet de passer le reste des instructions de la boucle dans laquelle il se trouve puis de la relancer si le test renvoie toujours vrai, à condition qu'il ne précède aucun label. Par exemple, retournons à notre cabinet de médecine. Vous voulez déterminer combien il y a de femmes dans le total de vos patients. Nous utiliserons un tableau recensant vos patients, faire une boucle for pour tous les vérifier et déclarer une variable que l'on incrémentera si le patient est une femme.

int nombreDeFemmes = 0;
String patients[] = {"homme" , "homme" , "femme" , "homme" , "femme" , "femme" , "femme" , "homme"};
int tours = patients.length;

for(int i = 0 ; i < tours ; i++) {
	if(patients[i].equals("homme"))
		continue;
	nombreDeFemmes++;
}
System.out.println(nombreDeFemmes + " femmes consultent dans votre cabinet.");

 

Votre programme vous retournera alors que 4 femmes consultent dans votre cabinet! On constate donc que l'incrémentation de nombreDeFemmes est passée si le patient est un homme !

Au passage, vous remarquerez que j'ai utilisé la méthode equals et non l'opérateur == pour tester la valeur du String. Je détaillerais ce qu'est une méthode plus tard, mais sachez qu'il est bien plus fiable de faire ceci pour le type String !

Si ce mot-clef est suivi d'un label, il s'occupera alors de passer les instructions de la boucle spécifiée, mais pas de la boucle dans laquelle il est inscrit !

vert: for(int i = 0 ; i < 10 ; i++) {
	while(/*un test*/) {
		if(i == 5)
			continue vert;
	}
	System.out.println(i);
}

 

Ici, notre boucle for aura pour objectif d'écrire la valeur de i à chaque tour. Nous faisons une autre boucle, peu importe le test, dans laquelle nous vérifions si i est égal à 5. Si c'est le cas, on empêchera la boucle "vert" d'écrire sa valeur.

Enfin, le mot-clef return stoppe lui aussi les boucles, mais ce dernier effectuant une action que nous détaillerons plus tard, je ne l'expliquerais pas tout de suite. Pour l'instant, pensez juste qu'il est l'équivalent de break. ^_^

Le code cadeau de cette semaine vous permet de compter le nombre de "p" dans une phrase ! Bien sûr, il est facilement modifiable afin que d'autres lettres puissent être vérifiées !

String phrase = "pourquoi, n'arrêtait pas de pester sa promise, pascal, "
		+ "pourtant propriétaire depuis plusieurs années, partit dès la première"
      		+ " opportunité pour partager sa passion naissante avec son autre petite"
      		+ " copine, un pucelle de perpignan à la plastique parfaite, elle aurait"
      		+ " préféré le stopper plus tôt mais persuadée qu'il ne la planterait pas,"
      		+ " elle n'eut plus que ses yeux pour pleurer, emprisonnée à perpétuité dans ses larmes.";
	      
int max = phrase.length();
int nombreDeP = 0;

for (int i = 0; i < max; i++) {
     if (phrase.charAt(i) != 'p')
          continue;
	            
     nombreDeP++;
}
System.out.println("Il y a " + nombreDeP + " \"p\" dans cette phrase.");

 

Résumé :

  • Les boucles permettent d'effectuer plusieurs fois les mêmes actions, ou des actions quasiment identiques.
  • Les boucles while tournent un nombre de fois indéterminé, tant que leur test retourne vrai, et peuvent aussi s'écrire en do-while.
  • Il faut bien faire attention de ne pas écrire de boucle infinie! Sinon les actions se répéterons à l'infini, jusqu'à l’arrêt forcé du programme.
  • Les boucles for tournent un nombre de fois déterminé et prennent en compte une variable dédiée, un test et une actualisation.
  • Les boucles for each enregistrent dans une variable temporaire les valeurs d'un tableau ou d'une collection, une par une et tournent tant que ce tableau ou cette collection à d'autres éléments à proposer.
  • Le mot-clef break est utilisé pour stopper les boucles, même si le test renvoie toujours vrai.
  • Le mot-clef continue est utilisé pour passer les instructions qui restent avant que la boucle recommence. Il est quasi-identique à break, sauf qu'il relance la boucle.
  • Les labels permettent de nommer ses boucles et de les gérer depuis d'autres boucles avec les mots-clef cités précédemment.
  • Pour vérifier si une String est égal à une autre, utiliser la méthode equals(String).

 

 

JeremGamer

 

2cp1ED5.pngtNIpRtq.png

SystemGlitch

blog-0476194001431870684.png

Il est sûr et certain que vous rencontrerez le cas suivant: vous avez un grand nombre de variables du même type à utiliser. Vous allez donc très certainement n'avoir qu'une idée en tête :

 
int nombre0 = 0;
int nombre1 = 1;
int nombre2 = 2;
int nombre3 = 3;
int nombre4 = 4;
int nombre5 = 5;

 

Vous conviendrez que ça ne semble pas très optimisé, et encore moins pratique. Il existe justement un outil pour faire cela ! C'est bien fait hein? ^_^

 

Cet outil, il s'agit des tableaux. Il permet, à l'aide d'une seule variable, de stocker une grand nombre d'éléments du même type. Nous prendrons ici int pour nos exemple. Mais sachez que les tableaux peuvent contenir absolument n'importe quel type, autant des types primitifs que des Objets (notion que vous verrez plus tard).

 

Vous savez ce qu'est un tableau en général, une multitude de cases alignées en lignes et en colonnes. Et bien ici, c'est pareil ! Dans chaque case, on aura une valeur. Pensez à toujours vous représenter vos tableaux dans votre tête lors de la création de vos programmes. ;)

 

Voyons désormais quelle forme cela prend en Java :

int tableau[];

 

La variable est déclarée mais pas initialisée pour l'instant, vous pouvez cependant le faire directement, nous verrons comment juste après.

Ici vous découvrez de nouveaux signes que sont les crochets. Ils caractérisent un tableau, leur utilité sera développée dans un instant.

 

Un tableau contient un certain nombre de cases, alors pour initialiser un tableau, soit on spécifie le nombre de cases (ces dernières seront alors vides et on pourra les remplir ensuite), soit on rempli directement à l'aide des valeurs choisies, et le nombre de cases sera alors adapté au nombre de valeurs entrées.

int tableau[] = new int[7]; //Tableau de 7 entiers, les cases sont vides pour l'instant

int tableau2[] = { 0 , 1 , 2 , 3 , 4 , 5 , 6 }; //Aussi un tableau de 7 entiers, mais on rempli directement les cases

 

Notez l'utilisation d'un nouvel opérateur: new. Vous en aurez besoin sans arrêt plus tard, ne l'oubliez pas ! ;)

 

Rien de bien compliqué pour la première manière de faire. Pour la seconde, notez bien la syntaxe, le point virgule à la fin à ne pas oublier comme d'habitude, mais surtout les accolades: c'est entre ces dernières que se trouvera le contenu du tableau, chaque case étant séparée par une virgule.

 

La déclaration des valeurs se fait de la même manière que pour des variables classiques :

double tableau[] = { 0.0 , 0.7 , 1.5 , 4.2 };

char tableau2[] = { 'a' , 'b' , 'c' , 'd' };

String tableau3[] = { "BlaBla" , "Toto" , "Tata" , "Titi" };

 

Voici donc la forme de notre tableau dans notre tête :

 

REfdv3y.png

 

Vous allez me dire qu'il n'y a qu'une seule ligne ! Ce n'est pas vraiment un tableau alors! Et bien vous avez demandé à votre programme qu'il n'y ai qu'une seule ligne! En effet, le nombre de dimensions du tableau est définie par le nombre de crochets que l'on met! Ainsi, si vous voulez un tableau avec plusieurs lignes, rien de plus simple :

int tableau[][];

 

On aura désormais deux dimensions! En réalité, il s'agit d'un tableau contenant au minimum deux tableaux, mais cela revient au même. Vous devinerez donc qu'il ne sera pas plus compliqué d'initialiser ce genre de tableaux :

int tableau[][] = new int[7][3]; //Nombre de lignes, puis nombre de colonnes

int tableau2[][] = { { 0  , 1  , 2  , 3  , 4  , 5  , 6  }, //Contenu de la première ligne 
                     { 7  , 8  , 9  , 10 , 11 , 12 , 13 }, //Contenu de la deuxième ligne
                     { 14 , 15 , 16 , 17 , 18 , 19 , 20 } }; //Contenu de la troisième ligne

 

Pensez bien à ne pas oublier les deux accolades englobant la totalité des tableaux. Comme je vous le disais, on voit bien ici qu'il s'agit de plusieurs tableaux, car on retrouve les accolades, et les virgules entre chaque valeur. ;)

Vous devriez aussi apercevoir une espèce de tableau comme vous les connaissez, grâce au fait que j'ai bien aligné tous les nombres. :)

 

Sachez qu'il existe aussi les tableaux tridimensionnels, quadridimensionnels, etc...! Vous pouvez ajouter autant de dimensions que vous le voulez, mais au delà de trois cela devient très très abstrait et demande de bonnes connaissances en mathématiques (étudiées au supérieur).

 

C'est bien joli tout ça, mais vous ne savez toujours pas comment naviguer dans ces tableaux, comment récupérer les valeurs qu'il contient ni comment modifier ces valeurs. Vous naviguerez grâce à des index. Chaque case a sa position, comme pour une bataille navale, mais en ( x , y ) s'il s'agit d'un tableau bidimensionnel.

 

YEGXGoy.png

 

Notez bien que le premier indice est 0 ! En informatique, prenez le réflexe de compter en partant de 0 et non de 1, car c'est bien plus logique non? ^_^

 

Je pense qu'il n'est pas nécessaire de s'attarder plus longtemps sur les index, ce n'est pas bien compliqué à comprendre. Passons plutôt à la partie intéressante: naviguer dans son tableau.

 

Encore une fois ce n'est pas compliqué, les coordonnées seront insérées entre les crochets :

int tableau[] = { 1 , 2 , 3 , 4 , 5 , 6 , 7 };

System.out.println(tableau[3]); //Affichera "4"
//Et oui n'oubliez pas que l'on compte à partir de zéro!
//Vous voyez désormais l'importance de bien faire attention à cela ;)

 

Dans le cas d'un tableau bidimensionnel, c'est le même principe :

int tableau[][] = { { 0  , 1  , 2  , 3  , 4  , 5  , 6  }, 
                    { 7  , 8  , 9  , 10 , 11 , 12 , 13 },
                    { 14 , 15 , 16 , 17 , 18 , 19 , 20 } };

System.out.println(tableau[2][1]); //Ligne puis colonne!
//Affichera "15"

 

Vous avez surement deviné comment modifier une valeur :

tableau[2] = 42; //La troisième valeur du tableau sera égale à 42

tableau2[1][4] = 7; //La cinquième valeur de la seconde ligne sera égale à 7

 

Pour finir, il est possible récupérer la taille d'un tableau, c'est à dire son nombre de cases :

tableau.length;

tableau2[2].length; //Dans le cas d'un tableau bidimensionnel
//Retournera le nombre de cases dans la troisième ligne

tableau2.length; //Retournera donc le nombre de lignes et non le nombre total de cases!

 

Faites bien attention, il s'agit bien du nombre de cases et non de l'index maximum ! D'ailleurs si jamais vous décidez d'aller chercher une valeur à l'aide d'un index plus grand que l'index de la dernière case, que se passera-t-il? :huh:

 

Une erreur, appelée exception sera lancée, il s'agit de ArrayIndexOutOfBoundsException, qui signifie tout simplement que vous allez chercher une valeur dans une case qui n'existe pas !

 

Voici un petit bout de code qui vous fera mijoter un moment ;) Des notions qu'il contient seront expliquées en détails dans le prochain chapitre.

int nombres[][] = { { 0 , 1 , 2 , 3 , 4 },
                    { 1 , 3 , 5 , 7 , 9 }};

 

for(int i = 0; i < 2; i++) {    

  for(int j = 0; j < 5; j++) {

    System.out.print(nombres[i][j]);       

  }

  System.out.println("");     

}

int nombres2[] = { 0 , 1 , 2 , 3 , 4 , 5 , 6 };

for( int k : nombres2 )
     System.out.println(k);

 

Résumé :

  • Un tableau est un série de cases contenant des valeurs du même type.
  • Tous les types sont supportés, autant les types primitifs que les Objets.
  • Les tableaux multidimensionnels sont des tableaux contenant plusieurs tableaux.
  • Chaque valeur se trouve dans une case ayant ses coordonnées, sous forme d'index.
  • Le premier index est l'index 0 !
  • Vous pouvez récupérer le nombre de cases d'un tableau grâce à length.

 

 

JeremGamer

 

 

2cp1ED5.pngtNIpRtq.png

SystemGlitch

blog-0582123001431870590.png

Le bout de code donné à la fin du chapitre précédent vous le criait haut et fort, nous allons parler des structures conditionnelles dans ce chapitre! Enfin nous allons nous attaquer au code ! Préparez vos petits doigts ! ^_^

Tout d'abord, commencez par créer une classe dans votre projet en faisant un clic droit sur "src" et en choisissant "New -> Class" :

 

E046LwC.png

 

 

Une boite de dialogue s'ouvrira, comme quand vous souhaitiez créer un projet. Nommez votre classe "Main" et cliquez sur "Finish" :

 

iYxAt0i.png

 

L'instant Convention :

 

Il est de retour ! ^_^

Cette fois, cela concerne le nommage de vos classes.

  • La première lettre doit être une majuscule.
  • Son nom ne doit pas être intégralement en majuscules.
  • Il est mélangé de majuscules et minuscules, sans caractères spéciaux ni espaces.
  • Comme pour les variables, mettez une majuscule au début de chaque mot composant le nom.
    class NomDeLaClasse

     

  • Évitez les acronymes et donnez des noms simples et descriptifs.

 

Voila ! Votre nouvelle classe toute neuve devrait ressembler à ça :

public class Main {
	
}

 

Nous allons la fournir un peu et faire en sorte que votre programme puisse se démarrer !

Nous allons donc ajouter la méthode principale. Les instructions qu'elle contient seront exécutées lors de chaque démarrage du programme.

Avant cela, je voudrais insister sur un point très important: les accolades.

Sachez que les accolades englobent et forment des sortes de paquets, ici, tout ce qui se trouvera entre les accolades déjà présentes fera partie de la classe "Main". Rien à part les imports ne peuvent se trouver à l'extérieur !

Pour se repérer, voir à quelle accolade ouvrante correspond quelle accolade fermante et inversement, on utilise quelque chose qui s'appelle l'indent. C'est simplement le fait d'aligner les portions de codes se trouvant entre plusieurs accolades. Un exemple vaut bien plus que de longues phrases :

public class Main {
	
	int i = 0;
	
	public static void main(String args[]) {
		System.out.println("Salut les bukkitiens!");
	}
}

 

Vous constatez que plus on "s'enfonce" profondément dans les accolades, plus la ligne est décalée. Eclipse s'en occupe en général, mais sinon faites-le vous même à l'aide de la touche tabulation ! ;)

L'indent n'est pas obligatoire mais c'est juste un moyen pour les développeurs de se retrouver dans leur propre code, sinon ce serait un véritable enfer !

L'argument utilisé ici entre les parenthèses est de type String. Il se syntaxe toujours avec des guillemets. C'est une chaîne de caractère extrêmement utilisée mais elle n'est pas un type primitif! Nous entrerons dans les détails de ce type plutôt spécial plus tard. ;)

Maintenant nous pouvons nous intéresser au contenu de ce code. Recopiez-le dans votre IDE. Vous pouvez retirer la ligne "int i = 0;" car elle ne nous servira pas. Elle était là juste à titre d'exemple pour l'indent.

Vous avez sûrement repéré deux nouveaux mots-clefs et d'autres choses bizarres. Ne vous en souciez pas pour l'instant. La méthode principale, c'est ceci !

Ici, le programme sait juste écrire "Salut les bukkitiens" dans la console. Désormais, vous savez aussi le faire !

Testons notre programme grâce à notre IDE. Cliquez sur la flèche verte dans votre barre d'outils. Eclipse vous demandera de sauvegarder avant d'exécuter, cliquez sur OK.

 

 

NMnx7GK.png

 

Vous pouvez constater que la phrase voulue s'est inscrite dans la console! Votre programme est fonctionnel! Sachez que la ligne "System.out.println()" permet d'imprimer n'importe quel type dans la console ! Aussi bien des variables numériques que des chaines de caractères ou bien plus encore !

Nous allons pouvoir enfin rentrer dans le vif du sujet. Nous allons corser un peu la chose.

Commençons par la structure conditionnelle la plus simple: if/else.

Celle-ci permet tout simplement d'exécuter des instructions en fonction du résultat d'un test. Il est possible d'y ajouter "else" à la fin. C'est littéralement "Si quelque chose, faire ceci, sinon, faire ceci." ! Reprenez le code fourni à la fin du chapitre précédent et vous comprendrez tout de suite.

int nombre1 = 6;
int nombre2 = 2;
boolean isMultiple;

if(nombre1 % nombre2 == 0) {
	isMultiple = true;
} else {
	isMultiple = false;
}

 

Observons pas-à-pas ce qu'il se passe.

  1. On déclare deux variables de type int en leur assignant directement une valeur.
  2. On déclare une variable boolean qui nous permettra de savoir si les deux nombres sont multiples ou non mais on ne lui assigne pas de valeur.
  3. On ajoute un if qui teste si le modulo de nombre1 par nombre2 est égal à 0.
  4. Accolade ouvrante. Tout ce qui se trouvera ici s'exécutera si nombre1 % nombre2 = 0.
  5. Accolade fermante, else puis accolade ouvrante.
  6. Tout ce qui se trouvera ici s'exécutera si nombre1 % nombre2 != 0. (N'est pas égal à 0).

 

Je vous propose de modifier un peu ce code pour qu'il puisse communiquer avec l'utilisateur.

int nombre1 = 6;
int nombre2 = 2; //Nous n'avons plus besoin de la variable boolean

if(nombre1 % nombre2 == 0) {
	System.out.println(nombre1 + " est un multiple de " + nombre2 + ".");
} else {
	System.out.println(nombre1 + " n'est pas un multiple de " + nombre2 + ".");
}

 

Vous suivez toujours? Rien de compliqué n'est-ce pas? :) Juste une chose, pensez à mettre des "+" pour les phrases comme je l'ai fait! C'est ce que l'on appelle la concaténation.

Maintenant, exécutez votre programme de la même manière que je l'ai expliqué plus haut. Vous allez voir que votre programme vous répond bien que 6 est multiple de 2! Désormais, essayer de modifier ces nombres et plus précisément, choisissez des nombres non multiples. Relancez votre programme et constatez que la deuxième option s'est déclenchée! C'est pas beau tout ça?

Il est possible de mettre plus encore de conditions. Repensez au chapitre sur les opérateurs. && et || peuvent être utilisés ici.

Exemple :

if(nombre1 != nombre2 && nombre1 != nombre3)

 

Une autre solution ajoutant de nouvelles possibilités ainsi que de l'optimisation existe: else if

Au lieu d'écrire une autre structure, on fait comme suit :

if(...) {
	//Instructions
} else if (...) {
	//Instructions
} else {
	//Instructions
}

 

Vous pouvez mettre autant de else if que vous le souhaitez.

Ce cas de figure fonctionne ainsi:

  1. Si quelque chose, alors on fait cela (if)
  2. Sinon si quelque chose d'autre, on fait cela (else if)
  3. Sinon, en dernier recourt, on fait cela. (else)

 

Essayez donc cette forme dans votre logiciel, toujours dans la méthode principale !

int nombre1 = 0;

if(nombre1 == 1) {
	System.out.println("Ce nombre est égal à 1");
} else if (nombre1 == 2) {
	System.out.println("Ce nombre est égal à 2");
} else {
	System.out.println("Ce nombre n'est ni égal à 1, ni égal à 2");
}

 

Lancez-le et expérimentez en changeant la valeur de nombre1 ! ;)

Il est aussi possible d'écrire les if "sur une seule ligne". Cependant, il faut bien faire attention car si plusieurs action sont à effectuer lorsqu'une condition est remplie, il faudra utiliser les accolades !

if(nombre1 == 0)
	System.out.println("Ce nombre est égal à 0");
else if(nombre1 == 1)
	System.out.println("Ce nombre est égal à 1");
else
	System.out.println("Ce nombre n'est ni égal à 0, ni égal à 1");

System.out.println("Le nombre a été testé!"); //Cette ligne ne fera pas partie du else! Ici l'indent vous le fait savoir sans même regarder qu'il n'y a pas d'accolades au if!

 

L'optimisation qu'apporte else if est la suivante : Quand une des conditions est remplie, le reste des tests n'est pas lu. Du coup, votre programme est accéléré car il peut directement passer à la suite !

Avouez qu'il serait barbant et très long de mettre plein de if partout quand il y a un grand nombre de deux possibilités. Pour cela, il existe une structure conditionnelle très pratique qui s'appelle switch ! Sa syntaxe est assez spéciale alors lisez bien !

switch(nombre1) {
	case 0: //Instructions
		break; //On passe le reste du switch jusqu'a l'accolade fermante.
	case 1:	//Instructions
		break;
	case 2:	//Instructions
		break;
	default: //Instructions
		break;
}

 

Je vous avait prévenu! Voyons ça en détails.

Tout d'abord, on écrit le mot-clef switch puis entre parenthèses, on met la variable à tester. ATTENTION, cette structure ne fonctionne qu'avec des types primitifs, ainsi que les String depuis Java 7.

Le switch va alors vérifier la valeur de "nombre1". Dans le cas où "nombre1" est égal à la valeur spécifiée après le mot-clef case, on exécute les instructions.

Vous pouvez mettre autant de case vous voulez !

default est différent. Premièrement, pas besoin de spécifier de valeur cette fois-ci car les actions spécifiées s'exécutent si la variable a une valeur différente de tous les case au dessus ! Deuxièmement, il n'est pas obligatoire. Dans le cas où vous ne l'avez pas ajouté, rien ne se passe si aucun case ne correspond à la valeur de la variable choisie.

Prenons un exemple concret qui parlera à tout le monde: manger. :P

Nous allons vérifier à quel moment de la journée l'utilisateur mange et nous allons définir le comportement du programme en fonction de cela !

String moment = "Nuit";

switch(moment) {
	case "Matin":
		System.out.println("Le repas le plus important de la journée! C'est bien!");
		break;
	case "Midi":
		System.out.println("La pause midi est toujours la bienvenue!");
		break;
	case "Soir":
		System.out.println("Ça fait du bien de manger après une dure journée de travail!");
		break;
	default:
		System.out.println("Ce n'est pas bien de grignoter entre les repas!");
		break;
}

 

Les trois grands repas de la journée sont précisés dans les case. Mais si on mange à un autre moment, comme ici, la nuit, le programme nous répondra que c'est mal de grignoter en dehors des repas! Essayez donc ce bout de code dans votre programme. Pensez à vider le contenu de la méthode principale avant.

Un dernier aspect du switch qu'il faut connaître, c'est que l'on peu effectuer une action pour différentes possibilités, de la même manière qu'un if avec plein de "||" :

switch(mois){
	case 1: case 3: case 5:
	case 7: case 8: case 10:
	case 12:
		jours = 31;
		break;
	case 4: case 6:
	case 9: case 11:
		jours = 30;
		break;
	case 2:
		jours=28;
		break;
}

 

Ici, on vérifie de quel mois il s'agit, le nombre étant ceux que vous connaissez soit 1 pour Janvier, 2 pour Février, etc...

Pensez à faire ceci plutôt que de recopier plein de fois la même instruction !

La dernière structure est très peu utilisée mais peu s'avérer utile dans certains cas. Elle porte le nom barbare de condition ternaire.

Le principe est simple en soi, mais la pratique l'est moins si on a pas bien saisi son fonctionnement. Ne vous découragez pas en voyant la tête qu'elle a, vous comprendrez bientôt.

int i = 5;
int j = 10;
int max = (i < j) ? j : i;

 

En réalité, nous faisons un test avant d'assigner une nouvelle valeur à max.

Je m'explique.

Entre les parenthèses, on a le test, exactement comme dans un if !

C'est là qu'intervient un nouvel opérateur! "? :" porte le nom d'opérateur ternaire de condition. Il est spécifique à cette structure, vous ne le verrez nul par ailleurs.

Bref, il s'occupe de faire le test puis de choisir entre les deux valeurs données à côté du ":".

Si le test renvoie true, donc ici que i est bien inférieur à j, alors la variable qui se trouve juste après le "?" sera assignée à max. Sinon ce sera la variable après les ":" qui le sera.

Finalement, faire ceci revient au même mais est beaucoup moins rapide :

int i = 5;
int j = 10;
int max;

if(i < j)
	max = j;
else
	max = i;

 

Résumé :

 

  • Vous savez désormais créer un programme fonctionnel !
  • Les instructions contenues dans la méthode principale seront exécutées lors du démarrage du programme.
  • La commande "System.out.println()" permet d'écrire quelque chose dans la console.
  • L'indent correspond au décalage des lignes et permet de se repérer au sein de son code.
  • Il existe trois types de structures conditionnelles:
    • if(...){...}else if(...){...}else{...}
    • switch(...) {case argument:...}
    • (...) ? ... : ... --> La condition ternaire.
    • Faites bien attention aux accolades. C'est très important de bien les placer!
    • Les tests se font à l'aide de plusieurs variables ainsi que des opérateurs logiques.
    • Faire son choix parmi ces structures conditionnelles est une question de pratique, et parfois d'optimisation.

     

 

JeremGamer

 

2cp1ED5.pngtNIpRtq.png

SystemGlitch

blog-0958393001431870576.png

Ce deuxième chapitre détaillera une notion qui parait évidente et qui pourtant, est plus subtile qu'on ne le pense. Seulement, une fois maîtrisée (très rapidement en général car encore une fois ce n'est que de la théorie), on l'utilise sans même y penser !

Je parle des opérateurs, tous ces signes, bien connu grâce aux mathématiques, qui permettent de "manier" les nombres.

Il en existe plein et vous en découvrirez sûrement certains. Certains sont liés aux notions dont j'ai parlé juste au dessus. Je détaillerais ça dans la seconde partie de ce chapitre.

Sans plus tarder, voici un tableau listant certains d'entre eux :

 

 

 

 

soC1cBd.png

 

Ils ne sont pas tous présents ici. Cependant c'est déjà beaucoup ! :lol: Il est important de les connaître !

J'aimerais insister sur plusieurs points:

  • Incrémenter signifie "ajouter 1". L'incrémentation se fait donc ainsi :
    variable++;//Ou ++variable;

     

  • Décrémenter signifie "retirer 1". La décrémentation se fait ainsi :
    variable--;//Ou --variable;

     

  • Les additions se font ainsi :
    nombre3 = nombre1 + nombre2;

     

  • Les soustractions, les multiplications, divisions et modulo de la même manière.
  • Les opérations puis assignation se font ainsi :
    nombre += nombre2;//Équivalent à:nombre = nombre + nombre2;

     

  • Les tests sont un peu différents, je vais vous détailler la syntaxe, mais vous ne saurez les utiliser que dans le prochain chapitre.
  • Les tests s'emploient dans les structures de condition (prochain chapitre) et renvoient un boolean :
    nombre1 == nombre2; //Renvoie true si nombre1 a la même valeur que nombre2
    nombre1 != nombre2; //Renvoie true si nombre1 n'a pas la même valeur que nombre2
    nombre1 >= nombre2; //Renvoie true si nombre1 est supérieur ou égal à nombre2
    //Etc...

     

  • J'insiste sur la différence entre = et ==! Ces deux opérateurs n'ont absolument rien à voir! Il ne faut absolument pas les confondre! J'y reviendrais très bientôt dans la seconde partie.
  • && et || s'utilisent eux aussi dans les structures de condition et renvoient eux aussi un boolean :
    nombre1 == nombre2 && nombre1 != nombre3 //On pourrait traduire par: "nombre1 est égal à nombre2 ET nombre1 n'est pas égal à nombre3"
    nombre1 == nombre2 || nombre1 < nombre2 //"nombre1 égal à nombre deux OU nombre1 inférieur à nombre2" Dans ce cas là mieux vaut utiliser =<

     

  • && est prioritaire par rapport à ||.
  • Pour prévenir d'éventuels problèmes de lecture, ajoutez des parenthèses autour de vos "paquets", comme en mathématiques. Si il y a plusieurs opérateurs logiques, il faut obligatoirement les mettre !
    (nombre1 == nombre2) && (nombre1 != nombre3) //Cela revient au même que la première condition donnée dans le bloc code juste au dessus. Mais c'est plus sécurisant!
    (nombre1 == nombre2 || nombre1 != nombre3) && (nombre2 > nombre3) //Cette condition est plus complexe//"nombre1 est égal à nombre2 OU nombre1 est différent de nombre3 ET nombre2 est supérieur à nombre3"

     

  • L'opérateur ! ("NON logique") permet de récupérer l'inverse d'un boolean.
    boolean variable = true;
    boolean variable2 = !variable; //variable2 sera égal à false.
    //Attention! J'ai mis "= !variable" et non "!= variable"!

     

  • L'opérateur = est sans doute le plus important et aussi celui qui recèle du plus de choses à savoir ! C'est donc ainsi que j'inaugure la seconde partie de se chapitre !

 

Pensez bien que ce chapitre est théorique et que les 3éme, 4éme et 5éme bouts de code proposés ne sont pas fonctionnels! Ils doivent se trouver dans une structure de condition comme je l'ai précisé!

Prenons l'exemple du dernier bout de code. J'ai bien fait la distinction entre les deux opérateurs = et ==. Et pour cause ! L'un sert à assigner, l'autre à tester! Vous connaissez déjà tout à propos du testeur, alors plongeons-nous dans les détails de l'assignation.

boolean variable = true;

 

Décortiquons ce bout de code... Tout d'abord, on a le type, le nom de la variable, l'opérateur et pour finir, la valeur de la variable. Vous savez déjà comment ça fonctionne. Allons plus loin !

On a bien un opérateur d'assignation ici! En fait, on assigne une valeur à la variable directement lors de sa déclaration !

 

-----> Tiens, ce mot me dit quelque chose ! :)

 

C'est normal! Je l'ai bien fait rentrer dans vos esprits dans le chapitre précédent!

Déclarer une variable, c'est simplement "l'inscrire" dans votre code, lui réserver un emplacement dans le vaste monde de la mémoire ! Car oui chaque variable occupe un petit peu de place dans la mémoire de votre ordinateur !

Sachez qu'il est possible de déclarer une variable sans lui assigner de valeur !

int nombre;

 

Il est toujours possible de changer la valeur d'une variable (non constante) après sa déclaration, du moment que cette dernière est accessible !

Dans ce cas, elle aura une valeur par défaut. Pour les variables numériques, ce sera "1", pour les boolean, ce sera false, et pour les autres types (que vous ne connaissez pas encore), ce sera null.

Hum, un nouveau mot-clef ! :D Vous le rencontrerez souvent, il signifie simplement "rien", le vide absolu. :blink: Quand une variable est null (dites "nulle"), c'est qu'elle n'a en fait aucune valeur, mais son emplacement est quand même réservé.

Il faut y faire attention car il a tendance à générer des erreurs assez facilement si on le manipule mal !

Voila! Ce chapitre est terminé ! Dans le prochain, on s'attaque au code! Vous allez pratiquer alors préparez vos claviers !

Et voici le bout de code promis ! Comme d'habitude, le minimum d'explications, il sera explicité dans le prochain chapitre. ;)

//Ce code vous permet de savoir si un nombre est multiple d'un autre ou non!
nombre1 = 6;
nombre2 = 2;
boolean isMultiple;

if(nombre1 % nombre2 == 0) {
	isMultiple = true;
} else {
	isMultiple = false;
}

 

Résumé :

  • Il existe de nombreux opérateurs permettant de manipuler vos variables numériques.
  • Certains peuvent aussi permettre de modifier des variables d'autres types.
  • Il existe beaucoup d'opérateurs permettant de tester vos variables. Ils retournent un boolean.
  • Il faut tous les connaître !
  • Assigner une valeur à une variable revient à modifier sa valeur. Cela se fait par l'intermédiaire de l'opérateur "=".
  • Déclarer une variable est nécessaire! Grâce à cela, un emplacement de mémoire lui est réservé, le programme connaît son type et son nom.
  • Une variable peut être réassignée après sa déclaration, à n'importe quel moment, il faut seulement qu'elle soit accessible et non constante.
  • Le mot-clef null équivaut à une valeur nulle, vide.

 

 

JeremGamer

 

2cp1ED5.pngtNIpRtq.png

SystemGlitch

blog-0891722001431870558.pngCe premier chapitre sera très théorique. Nous allons parler du plus basique des aspects de n'importe quel langage: les types primitifs.

 

Tous les programmes sont basés sur plusieurs, voire de nombreuses variables. Une variable est donc un objet qui peut changer de valeur. Par exemple, imaginons un programme de type calculatrice. Il nous faudra 4 variables ou plus afin d'effectuer le calcul: le premier et le deuxième nombre, définis par l'utilisateur, le résultat et pour finir, l'opérateur. (+ , - , * , /) Nous reviendrons sur cette notion plus tard car il en existe plein d'autres et ces derniers ne s'utilisent pas comme on le fait en mathématiques! Mais les variables ne sont pas forcément numériques! Cela peut très bien être une chaîne de caractère ou même des objets complètement différents comme un bouton dans une interface graphique! Mais pour l'instant, restons-en au plus simple. -_-

 

Nous nous concentrerons sur les variables dites de type primitif.

 

-----> Mais qu'est-ce donc !? :huh:

 

Il existe 8 types primitifs. Ce sont des types de variables qui auront chacun des propriétés différentes. Ils sont désignés par des mots-clef. Vous allez donc apprendre vos premiers mots-clef par la même occasion !

  • byte : Valeur numérique entière comprise entre -128 et 127 (inclus).
  • short : Valeur numérique entière comprise entre-32 768 et 32767 (inclus).
  • int : Utilisé le plus fréquemment. Valeur numérique entière comprise entre -2 147 483 648 et 2 147 483 647 (inclus).
  • long : Valeur numérique entière comprise entre -9 223 372 036 854 775 et 9 223 372 036 854 775 807 (inclus). Pour les distinguer, ils se terminent par un 'L'. Exemple: 584818823L
  • float : Valeur numérique décimale comprise entre plus ou moins 10-45 et plus ou moins 1038. Pour les distinguer, ils se terminent par un 'F'. Exemple: 16.7F
  • double : Valeur numérique décimale comprise plus ou moins 10-323 et plus ou moins 10308.
  • char : Caractère unicode. Tiens, un intrus! Il se syntaxe de la manière suivante: 'a' ou 'b' ou 'c' etc... Ou alors en utilisant le caractère sous forme de code Unicode: '\u00e9' pour 'é'.
  • boolean : Vous avez peut-être déjà entendu parler de valeur booléenne? C'est exactement ça! Un boolean est sois égal à true ou à false. Littéralement, vrai ou faux. Et toc! Plus deux mots-clef!

 

Tous les mots-clef que vous rencontrerez dans Eclispe seront en gras et violet comme ci-dessus, à moins que vous ne vous aventuriez à changer les couleurs de votre IDE...

Je préfère tout de suite soulever un poids qui pèse sur vous: aucun besoin de retenir par cœur les valeurs minimales et maximales des types primitifs ci-dessus! Souvenez-vous en juste approximativement car s'ils sont si nombreux, ce n'est pas pour rien !

 

 

L'instant Convention

 

Avant de passer à la suite, j'introduis ces petits paragraphes qui seront présents un peu partout dans les différents chapitres. Il pourraient être renommés en "L'instant Barbant" mais ils sont importants et ne doivent pas être négligés!

Les développeurs, pour pouvoir se comprendre à travers leur code, et que les librairies soient facilement utilisables, ont mis en place de nombreuses conventions de nommage. Vous vous devez de les respecter !

 

Par convention, il est interdit de nommer une variable ou une méthode (vous verrez bientôt de quoi il s'agit) par un mot-clef. Les noms des variables et des méthodes doivent aussi commencer par une minuscule. Si ceux-ci sont composés de plusieurs mots, pas d'espaces! Mettez une majuscule sur la première lettre de chaque mot, hormis le premier. Utilisez des noms simple et descriptifs. Et pour finir, n'utilisez pas de caractères spéciaux, que des lettre de a à z, de A à Z ainsi que des nombres. (Les noms ne doivent cependant pas débuter par un nombre). Quand aux constantes, notion que nous aborderons dans quelques instants, elles doivent être nommées avec des majuscules et des "_" (underscore).

 

 

Revenons à nos variables.

 

-----> Comment choisir? :unsure:

 

Procédons par ordre. Certains sont évidents. Il serait étrange de déclarer une variable de type int alors que sa valeur est censée être un caractère! :wacko: Pour cela, le choix se fait sans réfléchir: un char.

 

Si vous êtes sûr et certain que votre variable n'aura que deux valeurs possibles, optez pour un boolean.

 

Si vous cherchez à représenter une valeur décimale, donc assez précise, deux choix seulement s'offrent à vous: un float ou un double? Vous choisirez en général le double car c'est le plus utilisé. Cette variable pourra donc être plus facilement utilisée. Les float seront utilisés dans certains cas, mais vous les rencontrerez plus rarement.

 

Pour les entier, il y a plus de choix. Les int seront privilégiés pour les valeurs inconnues car ils offrent une intervalle de valeurs plutôt grande sans utiliser trop de mémoire. C'est le type le plus utilisé.

Les short sont un bon compromis entre optimisation et largeur d'intervalle. Ils pèsent moins que les int mais ont une intervalle de valeurs possibles plus modérée.

Pour finir, les byte seront utilisés dans les cas où l'économie de mémoire est de mise. Par exemple lors de l'utilisation de flux pour copier un fichier.

 

Vous développerez grâce à de nombreux "bouts de code" prêts à l'utilisation et gentiment fournis par les créateurs de Java. Ces "bouts de code" font partie de ce que l'on appelle le JDK (Java Development Kit), autrement dit, l'API Java.

Vous serez alors contraints d'utiliser les mêmes types que ceux qu'utilisent ces "bouts de codes". C'est souvent comme ça que seront définis les ceux que vous utiliserez.

 

 

Maintenant, je vais vous parler d'une variante. Les constantes. Ce sont des variables ayant une valeur constante, qui ne peut pas changer. On pourrait dire qu'elle ne peuvent être que "lues". Elles sont désignées par le mot-clef final lors de leur déclaration.

 

 

J'introduis une nouvelle fois un concept de ce cours. A la fin de chaque chapitre, nous vous fournirons du code brut et sans commentaire en lien avec ce que nous avons dis précédemment. Vous serez en mesure de le comprendre si vous avez bien suivi. Il pourraient peut-être vous être utile alors gardez-les précieusement! ;)

 

Celui qui vous sera fourni pour ce chapitre théorique sera donc un aperçu du code en Java et en même temps, il permettra d'introduire le prochain chapitre. Je vous laisse mijoter là-dessus :

final byte MIN_BYTE = -128;
final byte MAX_BYTE = 127;
byte b = 42;
final short MIN_SHORT = -32768;
final short MAX_SHORT = 32767;
short s = 15680;
final int MIN_INT = -2147483648;
final int MAX_INT = 2147483647;
int i = 48198401;
final long MIN_LONG = -9223372036854775808L;
final long MAX_LONG = 9223372036854775807L;
long l = 490000857471L;
final float MIN_FLOAT = Float.MIN_VALUE;
final float MAX_FLOAT = Float.MAX_VALUE;
float f = 14.2F;
final double MIN_DOUBLE = Double.MIN_VALUE;
final double MAX_DOUBLE = Double.MAX_VALUE;
double d = 5.8498118998;
char c = 'c';
boolean vrai = true;
boolean faux = false;

 

Essayez de modifier les valeurs des variables déclarées final. Ajoutez 1 aux variables "max" et retirez 1 aux variables "min". Vous constaterez que Eclipse s'empresse de vous annoncer une erreur. En effet, ce sont bien les limites de chaque type qui sont présentes ici!

Votre première erreur! C'est un grand pas! Car dans le domaine du développement, on apprend beaucoup de ses erreurs et c'est souvent à tâtons que l'on arrive à ses fins!

 

 

Résumé :

  • Des variables sont des objets ayant une valeur associée pouvant changer.
  • Ces variables peuvent être de plein de types différents. Nous avons vu les types primitifs, il y en a huit: byte, short, int, long, float, double, char et boolean.
  • On les choisi en fonction de leur propriétés et des possibilités qu'ils offrent.
  • Les constantes sont des variables ne pouvant changer de valeur.
  • Elles sont déclarées grâce au mot-clef final.

 

 

JeremGamer

 

2cp1ED5.pngtNIpRtq.png