JavaTips

Un article de Agora2ia.


Vous trouverez ici des conseils pour utiliser Java dans différents domaines...

Des exemples sur des cas précis d'utilisations de classes Java sur ExampleDepot


Sommaire

Notions de base

Les Java Beans

Un JavaBean est une classe Java "toute simple", d'où la métaphore du bean qui est en anglais un grain.

Les classes relatives à l'API JavaBeans se trouvent dans le package java.beans.


Cette notion va de paire avec celle de propriété ou property. Une property est un attribut membre qui possède les deux accesseurs, à savoir les méthodes getXxx et setXxx qui permettent respectivement d'accéder et de postionner la valeur de la property "Xxx". Si l'on prend l'exemple du "nom" comme property d'une classe, on obtient le getter public String getName() et le setter public void setName(String name).

Les properties peuvent être de 2 types : régulières ou indexées. Notre exemple du "nom" est une property régulière. Alors qu'une property indexée, comme son nom l'inique est accessible via un indexe, ce qui peut donner :

public void setName(int index, String name)

public String getName(int index)

public void setName(String[] names)

public String[] getName() 

Une propriété fort intéressante de ces properties est que l'utilisateur d'un bean peut être averti du changement de valeur d'une de ses properties. Pour cela il faut enregistrer un PropertyChangeListener via la méthode addPropertyChangeListener() du bean. Le listener est alors notifier d'un changement de valeur par l'envoi d'un PropertyChangeEvent ou d'un IndexedPropertyChangeEvent. Une propriété qui génère l'envoi d'un PropertyChangeEvent est appelée bound property. Le listener doit implémenter la méthode

public void propertyChange(PropertyChangeEvent pce)

Pour savoir si il s'agit vraiment d'un PropertyChangeEvent ou bien d'un IndexedPropertyChangeEvent, comme le second hérite du premier, il suffit d'utiliser un instanceof :

public void propertyChange(PropertyChangeEvent pce) {
    String name = pce.getPropertyName();
    if (pce instanceof IndexedPropertyChangeEvent) {
        IndexedPropertyChangeEvent ipce = (IndexedPropertyChangeEvent) pce;
        int index = ipce.getIndex();
        System.out.println("Property: " + name + "; index: " + index);
    } else {
        System.out.println("Property: " + name);
    }
    System.out.println("; value: " + pce.getNewValue());
}


Pour plus d'information sur les JavaBeans, voir le tutorial Sun sur les JavaBeans, voire la page sur l'API Enhancements to the JavaBeansTM Component API in J2SE 5.0.


Les archives Java

Compresser et déconpresser un WAR

Pour décompresser un fichier WAR :

jar -xvf monFichier.war


Et pour le recompresser une fois le contenu modifié (et le monFichier.war original supprimé !!!) :

jar -cvf ../nomDuFichier.war *

Jouer avec les types

Les chaînes de caractères

  • Une classe utile est le StringTokenizer (Voir "Comment lire un fichier CSV" sur cette page).
  • La classe StringUtils d'Apache rend également beaucoup de services.


Les tableaux : Array et Arrays

Afficher le contenu d'un tableau

java.util.Arrays.Arrays.toString(arguments)

ou arguments est un tableau.


Convertir une liste en tableau

return (Plugin[])result.toArray(new Plugin[result.size()]);

Le paramètre de la méthode toArray() permet de faire fonctionner le cast (Plugin[]), qui ne passe pas sinon !!!


Convertir un tableau en liste

Arrays.asList(arguments)

Se qui peut donner avec un tableau de String :

asList(new String[] { "countryCode", "dueDate" })


Résumé de l'API java.util.Arrays

Arrays.toString

Arrays.asList


Les Dates

Convertir une chaîne de charactère en date :

// import java.text.SimpleDateFormat
Date date = new SimpleDateFormat("yyyy-MM-dd").parse("2006-04-13");


Afficher la date courante :

// import java.text.SimpleDateFormat
String now = new SimpleDateFormat("yyyy-MM-dd HH.mm.ss.SSS").format(new java.util.Date());


Ajouter des jour(s), mois, année(s) à une date :

public static Date increaseDate(final Date date, int days, int months, int years) {
    // import java.util.GregorianCalendar;
    GregorianCalendar gregorianCalendar = new GregorianCalendar();
    gregorianCalendar.setTime(date);
    gregorianCalendar.add(GregorianCalendar.DATE, days);
    gregorianCalendar.add(GregorianCalendar.MONTH, months);
    gregorianCalendar.add(GregorianCalendar.YEAR, years);
    return gregorianCalendar.getTime();
}


Et pouvoir consulter ces jour(s), mois, année(s) :

GregorianCalendar gregorianCalendar = new GregorianCalendar();
final Date today = new Date();
gregorianCalendar.setTime(today);

// gregorianCalendar.get(GregorianCalendar.DAY_OF_WEEK); 
System.out.println(gregorianCalendar.get(GregorianCalendar.DATE));
System.out.println(gregorianCalendar.get(GregorianCalendar.MONTH));
System.out.println(gregorianCalendar.get(GregorianCalendar.YEAR));


Pour lire ou modifier les heures, minutes, secondes :

// POUR LIRE
Calendar calendar = Calendar.getInstance();
calendar.setTime(date);
int heure = calendar.get(Calendar.HOUR_OF_DAY);
int minute = calendar.get(Calendar.MINUTE);
int seconde = calendar.get(Calendar.SECOND);

// POUR MODIFIER
Calendar calendar = Calendar.getInstance();
calendar.setTime(date);
calendar.set(Calendar.HOUR_OF_DAY, heure);
calendar.set(Calendar.MINUTE, minute);
calendar.set(Calendar.SECOND, seconde);
Date newDate = calendar.getTime();


Créer une date à l'aide des années/mois/jours :

Ne surtout pas ré-utiliser un calendar pour créer une date :

private Date createTomorrowMorning() {
     final Calendar calendar = Calendar.getInstance();
     calendar.setTime(new Date());
     calendar.set(
         calendar.get(Calendar.YEAR), 
         calendar.get(Calendar.MONTH), 
         calendar.get(Calendar.DATE) + 1);
     return calendar.getTime();
}

Il faut initialiser une fois chaque instance :

private static Date createTonight() {
    final Calendar calendar = Calendar.getInstance();
    calendar.setTime(new Date());
    calendar.set(Calendar.HOUR_OF_DAY, 23);
    calendar.set(Calendar.MINUTE, 59);
    calendar.set(Calendar.SECOND, 59);
    calendar.set(Calendar.MILLISECOND, 999);
    return calendar.getTime();
}


Pour info sur les constantes de Calendar :

// For the date fields: 
YEAR + MONTH + DAY_OF_MONTH
YEAR + MONTH + WEEK_OF_MONTH + DAY_OF_WEEK
YEAR + MONTH + DAY_OF_WEEK_IN_MONTH + DAY_OF_WEEK
YEAR + DAY_OF_YEAR
YEAR + DAY_OF_WEEK + WEEK_OF_YEAR

// For the time of day fields: 
HOUR_OF_DAY
AM_PM + HOUR

Les décimales

DecimalFormat df = new DecimalFormat("########.00");
System.out.println(df.format(mon_nombre));


Avec les java.math.BigDecimal :

import java.math.BigDecimal;
// ...
BigDecimal amount = new BigDecimal(100.2222);
amount = amount.setScale(2, BigDecimal.ROUND_HALF_EVEN); // == 100.22


Trier une Collection

Des objets quelconques

Pour trier une Collection il faut appeler la méthode Collections.sort en lui passant :

  1. en premier paramètre la Collection à trier,
  2. en second paramètre une instance de Comparator où votre implémentation de la méthode Comparator#compare(Object o1, Object o2) doit retourner :
    1. une valeur négative si son premier paramètre est plus petit que le second,
    2. 0 (zéro) si les deux paramètres sont égaux,
    3. une valeur positive si son premier paramètre est plus grand que le second,


Si l'on souhaite trier une liste de Project par le nom de ces derniers, on peut écrire :

List projects = new ArrayList();
projects.add(new Project("First project name"));
projects.add(new Project("Second project name"));

// ...

Collections.sort(projects,
   new Comparator() {
      public int compare(Object o1, Object o2) {
         Project p1 = (Project)o1;
         Project p2 = (Project)o2;
         return p1.getName().compareTo(p2.getName());
      }
   });

// La liste 'projects' est maintenant triée...


Des objets Comparable

Un exemple d'objets Comparable est la classe String. Ainsi, pour trier simplement une liste de String :

final List<String> labels = new ArrayList<String>();
// ...
Collections.sort(labels);

En fait cela fonctionna car la classe String implémente l'interface Comparable.

Si l'on souhaite définir sur notre objet des critères de comparaison généraux, on peut lui faire :

  1. Implémenter l'interface Comparable
  2. Et définir la méthode compareTo(java.lang.Object)

Ainsi, si l'on souhaite trier des Train en fonction de leur nom, on pourra écrire :

public class Train implements Comparable<Train> {

    // ...

    /**
    * @see java.lang.Comparable#compareTo(java.lang.Object)
    */
    public int compareTo(Train other) {
        if (other == null) {
            return -1;
        }
        if (getName() == null) {
            if (other.getName() == null) {
                return 0;
            } else {
                return 1;
            }
        }
        if (other.getName() == null) {
            return -1;
        }
        return getName().compareTo(other.getName());
    }

    // ...

}

Puis les trier comme les String :

final List<Train> trains = new ArrayList<train>();
// ...
Collections.sort(trains);

Les entree/sortie : IO

La classe File

The API says that the class File is "An abstract representation of file and directory pathnames." The File class isn't used to actually read or write data; it's used to work at a higher level,

  • making new empty files,
  • searching for files,
  • deleting files,
  • making directories,
  • and working with paths.


Obtenir le répertoire temporaire

Afin de ne pas être dépendant du système d'exploitation, il faut utiliser la property Java nommée java.io.tmpdir. Ce qui peut donner l'exemple suivant :

File temporaryDirectory = new File(System.getProperty("java.io.tmpdir"));


File.toURL

La méthode File.toURL() ne doit plus être utilisée. Dans la version 6 de Java, Standard Edition (Java SE, anciennement désignée comme "J2SE"), cette méthode deviendra deprecated. Elle n'échappe pas correctement les caractèes illegaux ndas les URLs. Instead, if you need a URL, first get the URI, then convert the URI to a URL, as in :

fileVariable.toURI().toURL()


Récupérer la sortie d'une éxécution console

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;

// ...

Process process = null;
String commandLine = "dir";
try {
   process = Runtime.getRuntime().exec(commandLine);
   
   InputStream inputStream = process.getInputStream();
   InputStreamReader inputStreamReader = new InputStreamReader(inputStream);
   BufferedReader bufferedReader = new BufferedReader(inputStreamReader);
   String line = bufferedReader.readLine();
   
   while (line != null) {
      System.out.println(line);
      line = bufferedReader.readLine();
   }
   bufferedReader.close();
   inputStreamReader.close();
   inputStream.close();
} catch (IOException exception) {
   exception.printStackTrace();
}


Lire un fichier texte

import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;

// ...

private void printFile(String filePath) throws IOException {
    FileReader fileReader = new FileReader(filePath);
    BufferedReader bufferedReader = new BufferedReader(fileReader); // throws FileNotFoundException
    String ligne;
    while ((ligne = bufferedReader.readLine()) != null) { // throws IOException
        System.out.println(ligne);
    }
    bufferedReader.close(); // throws IOException
}


Lire un fichier tabulaire (CSV)

Il faut être vigilent dans le cas ou le fichier peut contenir des « trous », ce qui se matérialise dans le fichier par 2 séparateurs successifs, accolés. Dans ce cas, une instance par défaut du StringTokenizer passera directement à la valeur suivante. Ainsi, deux lignes ayant le même nombre de colonnes, mais avec certaines colonnes nulles, n’auront par forcément le même nombre de token. Pour y remédier, il faut instancier le Tokenizer avec le troisième paramètre, booléen, à true (paramètre en commentaire dans la portion de code suivante)…


public void load(String filePath, String separator) throws IOException {
  BufferedReader reader = new BufferedReader(new FileReader(filePath));
  String line;
  while ((line = reader.readLine()) != null) {
    StringTokenizer tokenizer = new StringTokenizer(line, separator /*, true*/);
    while (tokenizer.hasMoreTokens()) {
      String value = tokenizer.nextToken();
      System.out.print("value=" + value);
    }
    System.out.println();
  }
  reader.close();
}

Ecrire dans un fichier

import java.io.File;
import java.io.PrintWriter;
import java.io.FileOutputStream;
import java.io.IOException;

// ...

File outputFile = new File(System.getProperty("java.io.tmpdir"), "outputFile.txt");
try {
    outputFile.createNewFile();
}
catch (IOException e) {}

PrintWriter printWriter = new PrintWriter(new FileOutputStream(outputFile));
printWriter.println("This is the first line !");
printWriter.flush();
printWriter.close();


Copier un fichier texte

BufferedReader bufferedReader = new BufferedReader(new FileReader(fileName));
BufferedWriter bufferedWriter = new BufferedWriter(new FileWriter(targetFilePath));
String line = bufferedReader.readLine();
while (line != null) {
   bufferedWriter.write(line + "\n");
   line = bufferedReader.readLine();
}
bufferedWriter.flush();
bufferedWriter.close();
bufferedReader.close();

Jouer avec du XML

APIs diverses


L'API XML DOM de SUN


Pour l'ensemble des exemples de ce paragraphe, il faut les librairies :

<dependency>
    <id>xml-apis</id><version>1.0.b2</version>
</dependency>
<dependency>
    <id>xerces</id><version>2.4.0</version>
</dependency>
<dependency>
    <id>xalan</id><version>2.4.1</version>
</dependency>


Lire un fichier XML

Considérons l'exemple d'un fichier XML contenant notamment une liste de liens au format HTML, à savoir de la forme <a href="url">bla bla</a>. Le code suivant permet d'afficher la liste de ces liens.

import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.DocumentBuilder;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;
import java.io.File;

// ...

DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();
DocumentBuilder documentBuilder = documentBuilderFactory.newDocumentBuilder();
Document document = documentBuilder.parse(new File("myFile.xml"));
Element root = document.getDocumentElement();

System.out.println("Liste des liens :");

NodeList links = root.getElementsByTagName("a");
for (int i = 0; i < links.getLength(); i++) {
   Element link = (Element)links.item(i);
   String href = link.getAttribute("href");
   String label = link.getFirstChild().getNodeValue();
   System.out.println("link:" + href + "  " + "label:" + label);
}


Ecrire un 'document' dans un fichier

import java.io.IOException;
import java.io.FileOutputStream;
import org.w3c.dom.Document;
import org.apache.xml.serialize.OutputFormat;
import org.apache.xml.serialize.XMLSerializer;

// ...

private void serialize(Document document, String filename) throws IOException {
    FileOutputStream fileOutputStream = new FileOutputStream(filename);

    //// XERCES 1 or 2 additionnal classes.
    OutputFormat outputFormat = new OutputFormat("XML", "ISO-8859-1", true);
    outputFormat.setIndent(1);
    outputFormat.setIndenting(true);
    //outputFormat.setDoctype(null,"users.dtd");
    XMLSerializer serializer = new XMLSerializer(fileOutputStream, outputFormat);

    //// As a DOM Serializer
    serializer.asDOMSerializer();
    serializer.serialize(document.getDocumentElement());
    fileOutputStream.close();
}


Appliquer un XSL à un XML

import org.w3c.dom.Document;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.Transformer;
import javax.xml.transform.OutputKeys;
import javax.xml.transform.Source;
import javax.xml.transform.TransformerConfigurationException;
import javax.xml.transform.TransformerException;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamSource;
import javax.xml.transform.stream.StreamResult;
import java.io.File;

// ...

private void generateXdocs(Document document, File xslFile) {
    TransformerFactory factory = TransformerFactory.newInstance();
    Transformer transformer = null;
    StreamSource stylesource = new StreamSource(xslFile);
    try {
        transformer = factory.newTransformer(stylesource);
    } catch (TransformerConfigurationException e) {
        e.printStackTrace();  // Todo
    }
    transformer.setOutputProperty(OutputKeys.INDENT, "yes");
    transformer.setOutputProperty(OutputKeys.ENCODING, "ISO-8859-1");
    Source xmlSource = new DOMSource(document);
    try {
        transformer.transform(xmlSource, new StreamResult(System.out));
    } catch (TransformerException e) {
        e.printStackTrace();  // Todo
    }
}


L'EntityResolver

import javax.xml.transform.Transformer;
import javax.xml.transform.URIResolver;
// ...

public class SiteGenerator {
    // ...
    private void generateXdocs() {
        // ...    
        transformer.setURIResolver(new MyUriResolver(rootPath));
        // ...    
    }
    // ...
}
// ...

private static class MyUriResolver implements URIResolver {
    private String releaseTestsRoot;

    UriResolver(String releaseTestsRoot) {
        this.releaseTestsRoot = releaseTestsRoot;
    }

    public Source resolve(String systemId, String base) {
        String resourcePath = releaseTestsRoot + File.separator + systemId;
        return new StreamSource(resourcePath);
    }
}


L'API XML dom4j

Voir la FAQ dom4j


Obtenir un document XML à partir d'un String

import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.DocumentHelper;

public class Foo {

    public Document getDocument() throws DocumentException {
        return DocumentHelper.parseText( 
            "<root> <child id='1'>James</child> </root>"
        );
    }
}


Comparer des fichiers XML avec XMLUnit

Après avoir ajouter les dépencances suivantes à votre projet :

  1. xmlunit-1.0.jar
  2. xercesImpl-2.7.1.jar

vous pourrez comparer 2 fichiers XML en utilisant la classe org.custommonkey.xmlunit.Diff et ses deux méthodes similar() ou identical() (diff.toString() permettant de situer la divergence en cas d'erreur) :

import org.custommonkey.xmlunit.Diff;
import org.xml.sax.InputSource;

// ...

InputSource expected = new InputSource(
    getClass().getResourceAsStream("/transitiveDependencies/cataweb-webapp.iml.xml"));
InputSource actual = new InputSource(new FileReader(ideaModuleFile));

final Diff diff = new Diff(expected, actual);
assertTrue(diff.toString(), diff.similar());
assertTrue(diff.toString(), diff.identical());


XStream

Voir la page XStream...

L'Interface Graphique

Les Renderer dans Swing

Render table cells


Patienter avec classe

Voir l'article Attendre avec style et l'API Swing de Romain Guy.

Lancer une application graphique

Voici le code minimum pour lancer une application graphique Swing (inspiré du tutoriel Sun : How to Make Frames (Main Windows)) :


public class MySwingSpike {

   public static void main(String[] arguments) {
       javax.swing.SwingUtilities.invokeLater(new Runnable() {
           public void run() {
               createAndShowGUI();
           }
       });
   }

   private static void createAndShowGUI() {
       JFrame frame = new JFrame("FrameDemo");
       frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

       frame.getContentPane().add(new JLabel("Hello World !"), BorderLayout.CENTER);

       frame.pack();
       frame.setVisible(true);
   }
}

Les dialog

Thread, synchronisation et programmation concurrente

Web


Livres

  • Java Concurrency in Practice, Addison Wesley Professional (2006)
  • Concurrent Programming in Java: Design Principles and Patterns, Second Edition, Addison Wesley (1999)