Mémos, Astuces & Best Practice en Java

Tiens un mémo !

A chaque titre correspond une astuce ou un truc utile  dont j’aurais surement besoin de me rappeler plus tard !

 

Mon nextLine() est ignoré et la ligne suivante éxécutée ?

Une des best practice pour éviter ce genre de choses, qui arrive lorsque l’on saisi un entier, avant de saisir un String :

Exemple :

Scanner hScan = new Scanner(System.in);
int entier = hScan.nextInt();
System.out.println(entier);
String vide="";
// remplir vide
vide = hScan.nextLine();
 // dans cet exemple, il ne nous laisse pas saisir de valeur et affiche directement vide
System.out.println(vide);

Donc des gens proposent des solutions sur le net comme le fameux :

int selection = Integer.parseInt(hScan.nextLine());

Ou encore plus affreux, il posent un deuxième nextLine() juste après leur nextInt() pour ignorer une ligne inutile (qui sert de fusible en gros), mais c’est vraiment pas top.

Solution :

Le mieux reste d’utiliser un objet Scanner pour les entier et un pour les chaines de caractère. En effet, le nextInt() lis seulement le chiffre et pas la fin de la ligne, et comme le nextLine() vient après, il finit de lire la ligne que l’on a tapée avant avec le nextInt() (vous me suivez ? :p ).

Alors hop, soit on fait deux méthodes de saisie, une pour les String et une pour les Int, soit on instancie deux Scanners.

public class Saisie {

    public int saisieInt() {
	int nb = 0;
	Scanner hRead = new Scanner(System.in);
	try {
	    nb = hRead.nextInt();
	} catch (Exception e) {
	    System.out.println("Type saisi invalide (integer attendu)");
	    System.exit(-1);
	}
	return nb;
    }

    public String saisieString() {
	String line = "";
	Scanner hRead = new Scanner(System.in);
	try {
	    line = hRead.nextLine();
	} catch (Exception e) {
	    System.out.println("Type saisi invalide (string attendu)");
	    System.exit(-1);
	}
	return line;
    }
}

 Optimisation du code :

Quelques règles générales d’optimisation du code Java :

  • Dans les méthodes d’une classe, utiliser le moins de getter possible, il est préférable de ne l’appeler qu’une fois, et de stocker sont résultat en local.
  • Toujours éviter la déclaration de variable dans une boucle quand on peut.
  • Si on est amené à faire beaucoup de vérification de contenu contains() sur des objets de type List, préférez l’utilisation de Map, plus optimisé pour ce genre de recherches (avec l’id associé)
  • La déclaration des variables doivent êtres faites avec le type le plus haut, et typée, la construction doit par contre être faite avec le type le moins haut. Exemple :

Mauvaises déclaration :

// non typé et mal déclaré
HashMap mapObjet = new HashMap();

 

// non typé mais bien déclaré
Map mapObjet = new HashMap();

 Bonne déclaration :

//typé et bien déclaré (index : integer, objet à stocker : monObjet
Map<Integer, monObjet> mapObjet = new HashMap<Integer, monObjet>();

 Concaténation de String :

La concaténation de String classique en utilisant les opérateurs « += » utilise beaucoup de ressources, en multipliant les tableaux temporaires, donc le code ci-dessous est MAUVAIS (Définition : Beurk!) :

String line = "";
for (Personnes p : personnes) {
    line += ", " + p.getName();
}
// ici on vire la première virgule
line = line.substring(2);

Voici un code plus élégant permettant la concaténation avec une économie de garbage et d’opérations :

StringBuilder hBuild = new StringBuilder(personnes.size() * 16);
for (Personnes p : personnes) {
    if (hBuild.length() > 0) hBuild.append(", ");
    hBuild.append(p.getName());
}

 Pas de chemins dépendants de la plateforme :

Ne jamais coder en dur les chemins dans un programme, toujours laisser une constantes modifiable par fichier de configuration,  ou utiliser des chemins temporaires avec l’API, bien penser à personnaliser ses séparateurs dans les chemin, par exemple « / » pour Linux et « \ » pour Windows.

File tmp = File.createTempFile("monfichier","tmp");
File nom = new File("log.txt");
File repertoire = new File(path);
File f = new File(repertoire, nomfichier);

 Bien parcourir les Maps ou les Lists :

On utiliseras les « enhanced for loops » les boucles for améliorées en Français, afin de faire un parcours propre de nos objets Lists ou Maps. Il existe différentes méthodes qui peuvent être utilisées dépendamment de la valeur que l’on veut récupérer dans nos Maps par exemple:

Pour une Map avec un entier et un objet Personne par exemple, on procède tout simplement comme cela, pour récupérer l’id des objets :

for(Map<Integer, Personnes> : current)
{
    int tmp = current.keySet();
}

Ou si on veut récupérer seulement les objets, indépendamment de notre id :

for(Map<Integer, Personnes> : current)
{
    int tmp = current.value();
}

Autant de petits détails importants, qui nous évite la récupération d’éléments inutiles dans une Map ou une List.

 

Etre prudent avec les types non-primitif

J’ai récemment découvert à mes dépend qu’une variable de type Long (pas ‘long’, mais ‘Long’) est mise en cache si elle contient une valeur entre -128 et 127. Si elle dépasse cette valeur, elle n’est pas mise en cache.

Dans le cache, c’est la valeur qui est stockée et en dehors du cache c’est l’adresse.

Donc lorsque je faisais :

Long myId = 1050;
Long newId = 1050;

if(myId != newId)
   throw new CustomException("IDs différents !");

Mon exception était déclenchée ! J’ai pas mal tourné en rond avant de trouver le problème. Si maintenant on remplace ces valeurs par une valeur entre -128 et 127, et bien aucune exception n’est déclenchée !! Essayez, vous allez voir !

Du coup, une solution est d’utiliser la méthode equals() du type Long pour comparer les valeurs.

Laisser un commentaire