
FAQ JavaConsultez toutes les FAQ
Nombre d'auteurs : 53, nombre de questions : 231, dernière mise à jour : 7 juin 2009
Sommaire→Astuces et divers- Comment utiliser une police True Type n'importe où en Java ?
- Comment mettre mon application dans le 'systray' ?
- Comment gérer facilement les préférences de l'utilisateur ?
- Où trouver de l'aide sur JavaMail ?
- Comment lire et générer des documents aux formats spécifiques (pdf, rtf, html, doc, etc.) ?
- Comment créer son propre Listener ?
- Comment faire en sorte que le paramètre int de ma méthode soit modifié en retour ?
- Comment gérer les fermetures accidentelles ( Contrôle-C, kill -2, kill -15) ?
- Comment forcer le passage du 'Garbage collector' ?
- Comment faire une énumération en Java ?
- Comment annuler la construction d'un objet lors de la construction ?
- Comment recharger dynamiquement une classe ?
- Qu'est-ce qu'un singleton ?
- Qu'est ce que le double-check locking ?
- Comment avoir un Singleton sûr en environnement multithread ?
- Comment afficher la réference d'un objet redéfinissant la méthode toString()?
- Comment déterminer le temps écoulé entre deux points d'un programme ?
- Comment connaitre la mémoire utilisée par notre application pendant son exécution ?
- Pourquoi et comment redéfinir la méthode equals() ?
- Pourquoi et comment redéfinir la méthode hashCode() ?
- Pourquoi est-ce que mon application se bloque à l'éxécution d'un programme externe ?
- Comment intercepter tous les elements Throwable ?
- Comment vérifier la validité d'une URL ?
- Comment placer des commentaires javadoc ou des annotations au niveau du package ?
- Quelle est la liste des paramètres (options/arguments) de la JVM ?
1. On récupère un InputStream sur le fichier TTF
InputStream ttf;
- depuis une servlet, le fichier TTF est dans le répertoire /fonts/ du war :
ttf = getServletContext().getResourceAsStream("fonts/ARIALN.TTF");
- depuis le système de fichiers :
ttf = new FileInputStream("/aaa/bbb/ccc/ARIALN.TTF");
2. On crée une Font :
Font arialNarrow;
try {
arialNarrow = Font.createFont(Font.TRUETYPE_FONT, ttf);
}
catch (Exception e) { //Une solution de rechange, au cas où...
arialNarrow = new Font("Arial", Font.PLAIN, 1);
}
finally {
try {
ttf.close();
}
catch (Exception ignore) {}
}
3. La Font obtenue est de taille 1. Ce qui n'est pas terrible. On utilise alors deriveFont pour en créer des dérivées. Par exemple, on obtient du arial narrow grassouillet avec :
Font arialNarrowBold = arialNarrow.deriveFont(Font.BOLD);
Si besoin, créer une version avec ITALIC... Maintenant avec nos polices de bases, il suffit de modifier la taille.
BufferedImage bi = ImageIO.read("fichier.jpg");
Graphics2D g2d = bi.createGraphics();
//Antialiasing
g2d.setRenderingHint(
RenderingHints.KEY_TEXT_ANTIALIASING,
RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
g2d.setFont(arialNarrow.deriveFont(48f));
//et maintenant on peut écrire en Arial Narrow de taille 48
g2d.setFont(arialNarrowBold.deriveFont(24f));
//et là du Arial Narrow gras taille 24
Le petit f, c'est pour float, deriveFont accepte un int pour BOLD, ITALIC, PLAIN (qui sont des int), ou un float pour modifier la taille.
Depuis Java SE 6, il est possible de placer des icônes dans le "system tray" du bureau. Toutefois cette fonctionnalité étant fortement dépendante du système d'exploitation et de son bureau, elle n'est pas forcément supporté dans tous les cas.
Voici un exemple de code permettant d'ajouter une icône dans le "systray" :
// On vérifie que le "systray" est supporté
// par la JVM pour ce système d'exploitation
if (SystemTray.isSupported()) {
// On Crée un TrayIcon qui représentera notre icône :
TrayIcon trayIcon = new TrayIcon(
image, // L'image qui sera utilisé comme icône
tooltip, // Le texte affiché lors du survol de la souris
popup // Le PopupMenu qui s'affichera lors du clic droit
);
// On active le redimensionnement automatique de
// l'icône, afin qu'elle s'adapte au système
// (sinon l'icône peut être tronqué ou disproportionné)
trayIcon.setImageAutoSize(true);
// On récupère l'instance du SystemTray
SystemTray systemTray = SystemTray.getSystemTray();
// Et on ajoute notre TrayIcon dans le system tray
systemTray.add(trayIcon);
}
A noter qu'une fois que l'icône est présente dans le systray, on peut l'utiliser pour afficher une infobulle :
trayIcon.displayMessage(
"Titre", // Titre de la bulle
"Message", // Contenu du message
MessageType.INFO // Style de l'icone
);
Pour les versions antérieurs à Java SE 6, plusieurs API (plus ou moins portables), utilisant JNI, existent pour pallier à ce manque :
- SysTray for Java : fonctionne sur les systèmes Windows et KDE.
- Java System Tray Manager : fonctionne uniquement sur Windows.
- JDIC (JDesktop Integration Components) : fonctionne sous Windows, Linux et Solaris
Lien : Les classes Desktop et SystemTray, des alternatives à JDIC, par Baptiste Wicht
La classe abstraite Preferences du package java.util.prefs permet d'enregistrer facilement certaines données. Les implémentations dépendent du système (registres, SGBD, fichiers, etc.), mais ceci est invisible pour l'utilisateur. Plusieurs niveaux de préférences peuvent être définis. Ceux-ci sont accessibles grâce aux méthodes statiques de la classe Preferences.
- Preferences.systemRoot() : préférences globales du système.
- Preferences.systemNodeForPackage(Class c) : préférences du système pour une classe donnée.
- Preferences.userRoot() : préférences globales de l'utilisateur.
- Preferences.userNodeForPackage(Class c) : préférences globales de l'utilisateur pour une classe donnée.
Les Preferences associent à une clef (String) une valeur. On peut stocker dans les Preferences tout type primitif. Voici les méthodes correspondantes à :
Preferences prefs = Preferences.systemRoot();
String unString = prefs.get("uneClef","une valeur par défaut si il n'y a pas de valeur");
// on peut aussi récuperer d'autres types primitifs
int unInt = prefs.getInt("clefInt",123);
// on doit toujours spécifier une valeur par défaut
Preferences prefs = Preferences.systemRoot();
prefs.put("clef","valeur");
prefs.putInt("clefInt",456);
Pour un exemple plus complet, regardez le fichier ci-dessous. Il consiste en une fenêtre capable de "mémoriser" sa position, sa taille et son état (maximisée, iconifiée, etc.).
Téléchargement : Exemple
Lien : http://developer.java.sun.com/developer/onlineTraining/JavaMail/contents.html
De nombreux formats de fichiers textes peuvent être utilisés avec Java. Malheureusement, tous ne sont pas utilisables grâce à l'API standard. Voici quelques formats et les API qui peuvent les utiliser :
| Format | API | Remarques |
|---|---|---|
| HTML | API Standard | Cf. HTMLEditorKit, JEditorPane et JTextPane |
| RTF | API Standard | Cf. RTFEditorKit, JEditorPane et JTextPane |
| XML | API Standard (SAX, DOM) | Il existe de nombreuses librairies facilitant le travail avec ce type de documents |
| iText, FOP | Ces deux librairies sont utilisées seulement pour la génération | |
| Excel | POI, JExcelAPI | |
| Word | POI |
Cas de figure :
Un composant donné a besoin d'informer un ou plusieurs composants externes que certains événements se sont produits.
Un petit exemple :
Le composant MonBouton a besoin d'informer une liste d'objets que le bouton gauche de la souris est appuyé et que le bouton de la souris a été relaché.
Définir une nouvelle interface :
public interface MonBoutonMouseListener extends EventListener {
public void boutonGauchePresse (EventObject e);
public void boutonGaucheRelache (EventObject e);
}
class monBoutton :
/** import nécessaire : */
import javax.swing.event.EventListenerList;
/** attribut : */
protected EventListenerList listenerList;
/** instanciation nécessaire : (à faire dans son constructeur
* ou dans une méthode appelée par le constructeur)
*/
this.listenerList = new EventListenerList ();
/** méthode à faire :
* permet d'ajouter un composant dans la liste de ceux qui veulent
* être informés de l'évènement
*/
public void addMonBoutonMouseListener (MonBoutonMouseListener l) {
this.listenerList.add (MonBoutonMouseListener.class, l);
}
/** enlève un objet qui est actuellement écouteur de l'évènement */
public void removeMonBoutonMouseListener (MonBoutonMouseListener l) {
this.listenerList.remove (MonBoutonMouseListener.class, l);
}
/** lance un évènement à tous les objets écouteurs
* (== appelle la méthode boutonGauchePresse sur tous les objets
* écouteurs de la liste)
*/
protected void fireboutonGauchePresse () {
MonBoutonMouseListener [] listeners = (MonBoutonMouseListener [])
listenerList.getListeners( MonBoutonMouseListener.class);
EventObject e = new EventObject (this);
for (int i = listeners.length-1; i>=0; i--) {
listeners [i].boutonGauchePresse (e);
}
}
Il suffit ensuite d'appeler cette méthode à chaque fois que l'évènement approprié est généré.
Téléchargement : Exemple d'implémentation.
En Java les types de base sont passés aux méthodes par valeur, ils ne peuvent pas être modifiés. Il va falloir utiliser une instance de classe "encapsulant" un entier pour effectuer la modification. Attention la classe fournie Integer ne permet pas la modification et est donc inutile dans ce cas.
class MonEntier {
/** champ privé : */
private int value;
/** mise à jour de la valeur */
public void setValue(int newValue) {
value = newValue;
}
/** Acces à la valeur */
public int getValue() {
return( value);
}
}
public void maMethode (MonEntier i) {
i.setValue(maNouvelleValeur);
}
Contrôle-C, kill -2, kill -15 et autres sont des méthodes permettant d'arrêter l'exécution du programme sans lui demander son avis. Pourtant, il est parfois nécessaire de faire au moins quelques instructions capitales telles que la fermeture de flux de fichier, d'une connexion réseaux ou une sauvegarde rapide. Pour se faire il est possible de définir un 'ShutdownHook' ce qui se traduit par un thread exécuté à la réception du signal de fin d'exécution.
Cette astuce ne marche malheureusement pas pour le code kill -9.
Téléchargement : Exemple d'implémentation.
Le garbage Collector (rammasse miette) est la partie de la JVM qui s'occupe de la récupération des zones mémoires. Les phases de libération de la mémoire sont gérées de façon autonome, il est inutile de lui dire quand passer.
Il existe néanmoins la fonction gc de la classe java.lang.System qui permet de faire un appel explicite au garbage collector. Cet appel ne garantit pas que tous les objets inutiles seront supprimés de la mémoire.
System.gc ();
//Appel explicite au Garbage collector
Néanmoins, cette méthode ne garantit par le passage du GC. Elle permet seulement d'augmenter la propabilité que le GC se déclenche. Il est préférable de mieux gérer ses objets plutôt que d'utiliser cette méthode.
Voici trois solutions :
public class MonEnum {
public final static int Val1=0;
public final static int Val2=1;
public final static int Val3=2;
}
On peut ensuite utiliser MonEnum.Val1, MonEnum.Val2 etc, ou mieux on peut faire une interface plutôt qu'une classe afin que les classes l'implémentant puissent directement appeler Val1, Val2...
Le problème reste le typage des éléments, rien n'empêche de passer un objet d'une énumération à la place d'une autre.
public final class MonEnum1 {
private MonEnum1() {
/** Rien à faire */
}
/** liste des valeurs */
public final static MonEnum1 Val1 = new MonEnum1();
public final static MonEnum1 Val2 = new MonEnum1();
public final static MonEnum1 Val3 = new MonEnum1();
}
Ainsi un éventuel objet de type MonEnum2 ne pourra jamais être utilisé à la place d'un objet de type Enum1 (Enum1.Val1 Enum1.Val2 ...).
public final static Enum1 val1 = null;
De cette manière il devient totalement impossible de passé un Enum1 non reconnu !
enum Align{
LEFT,
RIGHT,
CENTER,
JUSTIFIED
};
Vous pouvez accéder aux valeurs ainsi :
Align.LEFT;
Align.RIGHT;
Lien : http://jakarta.apache.org/commons/lang/api/org/apache/commons/lang/enum/package-summary.html
Lien : Plus d'infos sur les enums
Si les paramètres passés au constructeur sont incohérents ou que la construction provoque une erreur, la création de l'objet est impossible et doit être annulée. Comme les méthodes, les constructeurs peuvent lever des exceptions, c'est la façon la plus simple d'annuler l'exécution du constructeur. Techniquement, l'objet est créer en mémoire (l'allocation de la mémoire a lieu au moment de l'appel du constructeur) mais l'appelant étant obligé de traiter l'exception, le pointeur vers cette objet est perdu et sera donc recupéré par le ramasse-miettes.
Une solution plus élégante consiste à utiliser par une méthode statique qui verifie les paramètres et ne fait l'appel au constructeur que si ceux-ci sont corrects.
public class Test {
/** Le constructeur est déclaré protected
* pour interdire son utilisation par les clients de la classe.
*/
protected Test(int val) {
}
public static Test createTest( int val ) {
if( val<100 ) {
return( new Test(val) );
} else {
return( null);
/** Ou alors on lève une exception... */
}
}
}
En utilisant la méthode:
Class c = Class.forName("com.developpez.MaClasse");
On utilise le ClassLoader du système. Ce ClassLoader cache l'information et ne rechargera pas la classe lors d'un nouvel appel a forName, et ce même si le fichier ".class" a changé depuis le dernier chargement.
Il y a deux solutions :
1 - La classe n'est pas présente dans le CLASSPATH C'est le cas le plus simple, il suffit d'utiliser un nouveau URLClassLoader a chaque fois pour recharger une nouvelle version de la classe.
// le chemin ou trouver la classe a recharger
URL chemins[] = { new URL("file:/C:/MesClasses/") };
URLClassLoader loader = new URLClassLoader(chemins);
Class c = loader.loadClass("com.developpez.MaClasse");
Attention, il faut créer a chaque fois un nouvel URLClassLoader, sinon la classe est cachée par le loader.
Si la classe est présente dans le CLASSPATH c'est le ClassLoader du système qui la charge (et la met dans son cache).
2 - La classe est présente dans le CLASSPATH
Dans ce cas, on ne peut pas utiliser l'URLClassLoader. Il faut créer son propre ClassLoader qui hérite de la classe ClassLoader
import java.io.InputStream;
public class MonLoader extends ClassLoader
{
public Class loadNewClass(String aName) throws Exception
{
InputStream is = getClass().getResourceAsStream("/" + aName);
if (null == is)
{
return null;
}
byte buffer[] = new byte[is.available()];
is.read(buffer);
Class c = defineClass(aName, buffer, 0, buffer.length);
resolveClass(c);
return c;
}
}
Dans ce code, nous avons créé une nouvelle méthode "loadNewClass" qui recharge le fichier ".class" a chaque fois.
Le fait de ne pas avoir redéfini la méthode loadClass permet a ce nouveau ClassLoader d'utiliser le ClassLoader du système pour charger d'eventuelles classes mères ou interfaces.
Si la méthode loadClass est redéfinie il faut qu'elle puisse aussi trouver les super classes et interfaces des classes qui seront chargées, et ce sans recharger celles déjà présentes dans le système.
Un singleton est un objet qui ne peut être instancié qu'une seule et unique fois dans le programme.
Pour transformer une classe en singleton, il faut passer les constructeurs en accès private et ajouter au code une instance et un accesseur statiques :
public class Singleton {
private static Singleton instance = new Singleton();
public static Singleton getInstance() {
return instance;
}
private Singleton() {
//constructeur
}
//reste de la classe
}
Il existe une variante :
public class Singleton {
private static Singleton instance = null;
public static Singleton getInstance() {
if(instance == null)
instance = new Singleton();
return instance;
}
private Singleton() {
//constructeur
}
//reste de la classe
}
Enfin, pour accéder au singleton, il suffit d'appeler :
Singleton s = Singleton.getInstance();
Il existe encore une dernière variante consistant à utiliser une classe interne statique comme conteneur du singleton :
public class Something{
private Something() {
}
private static class LazyHolder {
private static final Something something = new Something();
}
public static Something getInstance(){
return LazyHolder.something;
}
}
Attention, toutes ces méthodes sont uniquement adaptées à un environnement mono-thread. Dans le cas où vous avez plusieurs processus, dirigez-vous vers cet article.
Le double-check locking est un idiome de programmation censé assurer la sécurité du Singleton en environnement multithread.
Attention, ce pattern ne marche pas !
Il peut etre écrit comme suit :
public static Singleton getInstance(){
if (instance==null)
{
synchronized (Singleton.class){
if(instance==null)
instance=new Singleton();
}
}
return instance;
}
Bien qu'encore recommandé sur le web, son utilisation est fortement déconseillée.
Pour plus de détails : Le Singleton en environnement Multithread
Il existe trois solutions pour sécuriser un Singleton dans un programme multithread :
- Synchroniser toute la méthode getInstance() et payer le coût de la synchronisation à chaque appel
- Utiliser ThreadLocal dont l'implémentation n'est pas plus performante que la synchronisation
- Abandonner la synchronisation et utiliser un initialiseur static quand la construction du Singleton ne nécessite pas de paramètres particuliers.
Pour plus d'informations : Le Singleton en environnement Multithread
La méthode toString() de la classe Object affiche le nom de l'objet suivi de la réference de l'objet dans la machine virtuelle (une sorte de pointeur).
Ceci peut s'avérer utile parfois lors d'une phase de deboguage, mais comment faire lorsqu'une classe (comme la classe String par exemple) redéfinie la méthode toString() ?
Il faut passer par la méthode System.identityHashCode(Object).
Le code suivant retourne la même chose que le toString() d'Object pour n'importe quel type d'objet:
public String getReference(Object anObject){
return anObject.getClass().getName() + "@" + Integer.toHexString(System.identityHashCode(anObject));
}
System.currentTimeMillis()
En utilisant la méthode statique currentTimeMillis() de la classe System. Cette méthode renvoie un long représentant la différence de temps entre le 1er Janvier 1970 à minuit et le temps système à l'exécution de l'instruction. Cette différence s'exprime en fonction de l'unité de temps de l'OS (la milliseconde, voire quelques dizaines de milliseconde).
long start = System.currentTimeMillis();
// ... instructions à chronométrer
long duree = System.currentTimeMillis() - start;
System.out.println(duree);
System.nanoTime()
En utilisant la méthode statique nanoTime() de la classe System. Cette méthode n'est disponible que depuis le JDK 5.0. Elle renvoie un long représentant la valeur du timer en nanosecondes.
long start = System.nanoTime();
// ... instructions à chronométrer
long duree = System.nanoTime() - start;
System.out.println(duree);
Ce temps n'est pas lié à un référentiel de temps universel, aussi on ne doit-on l'employer que pour mesurer une différence de temps. La précision est de l'ordre de la nanoseconde.
Il faut aussi noter que ce n'est pas exactement le temps utilisée par votre application, mais le temps qui s'est écoulée depuis le début à la fin de votre code. Pendant ce temps, d'autres applications tournent sur votre ordinateur et utilisent ce qu'on appelle du temps CPU. Pour connaître exactement le temps CPU nécessaire, il faut utiliser la méthode getCurrentThreadCpuTime() de l'interface java.lang.management.ThreadMXBean à la place de System.nanoTime() ou currentTimeMillis() et vous aurez le temps CPU.
Avec Java 5.0, on peut utiliser la classe MemoryMXBean de la nouvelle API de management pour obtenir des informations sur l'état de la mémoire. On récupère une instance de MemoryMXBean via le code suivant :
MemoryMXBean memoryBean = ManagementFactory.getMemoryMXBean();
Ensuite, il suffit d'utiliser la méthode getHeapMemoryUsage() qui retournera un objet MemoryUsage contenant le détail de la mémoire utilisé par votre application. Il contient ainsi les informations suivantes :
- init : La taille initiale de la mémoire utilisé par l'application (généralement 0, sauf si l'on a utilisé l'option -Xms de la JVM).
- used : La quantité de mémoire qui est réellement utilisée par votre application.
- committed : La quantité de mémoire qui a été réservée auprès du système d'exploitation.
- max : La quantité maximale que la JVM est authorisée à réserver auprès du système d'exploitation (modifiable avec l'option -Xmx de la JVM). Si l'application utilise plus de mémoire cela générera une OutOfMemoryError.
L'objet MemoryUsage possède bien entendu toutes les méthodes accésseurs pour accéder à ces éléments, mais également une redéfinition de la méthode toString() afin de les afficher plus simplement. Ainsi le code suivant :
System.out.println( memoryBean.getHeapMemoryUsage() );
Donnera un résultat de le forme suivante :
init = 0(0K) used = 436912(426K) committed = 2031616(1984K) max = 530907136(518464K)
A noter également qu'il est possible de surveiller la mémoire utilisé pour la JVM elle-même (et non pas par votre application) via la méthode getNonHeapMemoryUsage().
Pour les versions précédentes à Java 5.0, on peut utiliser la classe Runtime et ses méthodes maxMemory(), totalMemory() et freeMemory() :
- maxMemory() représente la quantité maximale que la JVM est authorisée à réserver auprès du système d'exploitation (équivalent de l'attribut max).
- totalMemory() représente la quantité de mémoire qui a été réservée auprès du système d'exploitation (équivalent de l'attribut committed).
- freeMemory() représente la quantité de mémoire libre utilisable avant que la JVM n'alloue encore de la mémoire auprès du système (équivalent à la différence entre les attributs committed et used)
Ainsi pour obtenir un résultat similaire, on peut utiliser le code suivant :
Runtime runtime = Runtime.getRuntime();
System.out.print( "used : " + ( runtime.totalMemory()-runtime.freeMemory() ) );
System.out.print( " committed : " + runtime.totalMemory() );
System.out.println( " max : " + runtime.maxMemory() );
Qui donnerait ceci :
used : 446256 committed : 2031616 max : 530907136
Lien : Gestion du Garbage Collector
Lien : MemoryMXBean
Lien : Runtime
La méthode equals() permet d'indiquer qu'un objet est égal à un autre. L'implémentation par défaut hérité de Object se contente de renvoyer true lorsqu'il s'agit du même objet en mémoire et ne vérifie pas les valeurs des attributs de la classe (donc x.equals(y) équivaut à x==y).
Dès lors que l'on a besoin de tester l'égalité des instances d'une classe, il faut que cette dernière redéfinissent la méthode equals(). C'est également une condition pour le bon fonctionnement de certaines méthodes des Collections de Java.
La méthode equals() doit donc renvoyer true lorsque deux objets sont identiques, et false dans le cas inverse. Enfin il faut noter que cette méthode ne devraient pas renvoyer d'exception, même si son paramètre vaut null.
En général la méthode equals() se décompose en trois étapes :
- Vérification de l'égalité des références : il est inutile de comparer les valeurs si les références sont identiques.
- Vérification du type du paramètre : on ne peut pas comparer n'importe quel type.
- Vérification des valeurs des attributs utiles des deux objets (c'est à dire des attributs représentatifs de la valeur de l'objet).
Ainsi, cela pourrait donner :
class TestClass {
private int attribut1;
private String attribut2;
private boolean visible; // Attribut non-représentatif et donc ignoré
@Override
public boolean equals(Object obj) {
// Vérification de l'égalité des références
if (obj==this) {
return true;
}
// Vérification du type du paramètre
if (obj instanceof TestClass) {
// Vérification des valeurs des attributs
TestClass other = (TestClass) obj;
// Pour les attributs de type primitif
// on compare directement les valeurs :
if (this.attribut1 != other.attribut1) {
return false; // les attributs sont différents
}
// Pour les attributs de type objets
// on compare dans un premier temps les références
if (this.attribut2 != other.attribut2) {
// Si les références ne sont pas identiques
// on doit en plus utiliser equals()
if (this.attribut2 == null || !this.attribut2.equals(other.attribut2)) {
return false; // les attributs sont différents
}
}
// Si on arrive ici c'est que tous les attributs sont égaux :
return true;
}
return false;
}
}
Il faut prendre en compte les règles suivantes lors de la redéfinition de la méthode equals() :
- Réflection : x.equals(x) devrait toujours retourner true.
- Symétrie : Si x.equals(y) retourne true, alors y.equals(x) retournera true.
- Transitivité : Si x.equals(y) retourne true et y.equals(z) retourne true, alors x.equals(z) retourne true.
- Consistance : Pour deux référence x et y, tous les appels à la méthode x.equals(y) devront toujours donner le même résultat.
Toutefois ces règles peuvent être difficilement réalisable en cas d'héritage lorsque de nouveaux attributs sont pris en compte.
Enfin, la librairie Jakarta Common Lang propose une classe EqualsBuilder qui facilite et simplifie l'écriture des méthodes equals() en se chargeant de la comparaison des attributs, ce qui pourrait donner dans ce cas :
public boolean equals(Object obj) {
// Vérification de l'égalité des références
if (obj==this) {
return true;
}
// Vérification du type du paramètre
if (obj instanceof TestClass) {
// Vérification des valeurs des attributs
TestClass other = (TestClass) obj;
return new EqualsBuilder()
.append(this.attribut1, other.attribut1)
.append(this.attribut2, other.attribut2)
.isEquals();
}
return false;
}
Enfin, il faut noter qu'il est conseillé de redéfinir également la méthode hashCode() afin de respecter le contrat de cette dernière (qui est fortement lié à equals()).
Lien : Pourquoi et comment redéfinir la méthode hashCode() ?
La méthode hashCode() a pour objectif de fournir un code de hashage, afin d'optimiser le traitements des collections de type Map, qui se basent sur ce hashCode pour classer les données. Il est ainsi nécessaire de redéfinir la méthode hashCode() dès que l'on souhaire utiliser un objet en tant que clef d'une Map.
Mais plus généralement, il est souhaitable de redéfinir la méthode hashCode() lorsque l'on redéfinit la méthode equals(), afin de conserver une certaine cohérence.
En effet, ces deux méthodes sont très liées, puisque les hashCode de deux objets égaux doivent être égaux, ainsi l'on doit vérifier la condition suivante :
Si x.equals(y), alors x.hashCode() == y.hashCode()
Par contre l'inverse n'est pas forcément vrai. Ainsi deux objets différents peuvent avoir le même hashCode(). Toutefois ceci est à éviter dans la mesure du possible, car cela peut détériorer les performances des Map.
Mais le calcul du hashCode() doit rester raisonnables et peu complexe, afin d'éviter des temps de calcul trop important.
La solution les plus courantes est de calculer un hashCode() selon la valeurs des attributs utilisés dans la méthode equals() afin de conserver la cohérence entre les deux méthodes.
Pour le calcul du hashCode(), on peut raisonnablement suivre la règle suivante :
- On choisit deux nombres impairs différents de zéro. Un de ces nombres servira comme valeur de départ pour le calcul du hashCode. Le second servira de "multiplieur" à chaque "ajout" du hashCode d'un attribut. Il est préférable d'utiliser des nombres premiers par sécurité.
En effet, si à chaque clé x , correspond h(x) l'endroit où se trouve x dans la table de hachage. L'expression de la fonction de Hachage est :
h(x)=[x(1)*B^(l-1) + x(2)*B^(l-2)....+x(l)] mod N
Prenons B = 128, et prenons une taille N égale a B, on obtient après calcul :
h(x)=x(l) mod N
car B mod N serait égale a 0
Une seule valeur a donc été rangée dans la table, voilà pourquoi il est très important de limiter au maximum les risques de diviseur commun car certaines valeurs disparaîtraient alors des tables.
NB : le choix de B comme d'une puissance de deux au lieu d'un nombre premier peut s'avérer judicieux pour des raisons de vitesse d'exécution. En effet une multiplication par deux est un simple décalage d'un bit à gauche pour l'ordinateur. Malgré tout, cela augmente les risques de diviseur commun et donc de disparition de donnée.
- Calcul du hashCode de chaque attribut utile (ceux utilisés dans la méthode equals()), ce qui revient à faire (soit 'a' l'attribut) :
- Pour un boolean, renvoyer 0 ou 1 (a ? 0 : 1)
- Pour type entier (byte, char, short ou int) il suffit de prendre la valeur entière (avec un cast éventuel) : (int)a;
- Pour un type long, le convertir en int en déplacant les bits : (int) (a^(a>>>32))
- Pour le type float, on le convertit en int avec la méthode Float.floatToIntBits(a).
- Pour le type double, on le convertit en long avec la méthode Double.doubleToLongBits(a), puis on effectue le même traitement que pour les long.
- Pour les objets, on se contentera d'utiliser la méthode hashCode(), ou d'utiliser zéro si la référence est null : (object==null ? 0, object.hashCode())
- Pour un tableau, on traitera tous les éléments de ce dernier en respectant les règles ci dessus.
Et enfin on ajoute chacun des hashCodes calculés au résultat, après l'avoir multiplié par le nombre "multiplieur".
Ce qui pourrait donner (pour la classe en exemple dans la question précédente) :
public int hashCode() {
// On choisit les deux nombres impairs
int result = 7;
final int multiplier = 17;
// Pour chaque attribut, on calcule le hashcode
// que l'on ajoute au résultat après l'avoir multiplié
// par le nombre "multiplieur" :
result = multiplier*result + attribut1;
result = multiplier*result + (attribut2==null ? 0 : attribut2.hashCode());
// On retourne le résultat :
return result;
}
Les nombres 7 et 17 ont été choisit de manière totalement arbitraire. Il est juste préférable de ne pas utiliser les mêmes pour toutes les classes.
Enfin, la librairie Jakarta Common Lang propose également une classe HashCodeBuilder qui facilite et simplifie l'écriture des méthodes hashCode() en se chargeant du calcul des hashCode des attributs selon leurs types, ce qui pourrait donner dans ce cas :
public int hashCode() {
return new HashCodeBuilder(17, 37)
.append(this.attribut1)
.append(this.attribut2)
.toHashCode();
}
Il faut consommer tous les flux dans des threads différents.
La JVM utilise deux (petit) buffer pour lire la sortie stdout et stderr du programme appellé, mais lorsque ces buffers sont plein, le programme appellé reste bloqué sur la méthode d'écriture tant que le buffer de la JVM n'est pas vidé.
Voici un exemple complet :
Runtime runtime = Runtime.getRuntime();
final Process process = runtime.exec("monappli");
// Consommation de la sortie standard de l'application externe dans un Thread separe
new Thread() {
public void run() {
try {
BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()));
String line = "";
try {
while((line = reader.readLine()) != null) {
// Traitement du flux de sortie de l'application si besoin est
}
} finally {
reader.close();
}
} catch(IOException ioe) {
ioe.printStackTrace();
}
}
}.start();
// Consommation de la sortie d'erreur de l'application externe dans un Thread separe
new Thread() {
public void run() {
try {
BufferedReader reader = new BufferedReader(new InputStreamReader(process.getErrorStream()));
String line = "";
try {
while((line = reader.readLine()) != null) {
// Traitement du flux d'erreur de l'application si besoin est
}
} finally {
reader.close();
}
} catch(IOException ioe) {
ioe.printStackTrace();
}
}
}.start();
Lien : Comment exécuter une application externe ?
Lien : Exécuter une application externe en Java
Il suffit de positionner la propriété systeme sun.awt.exception.handler soit avec l'option -D de java soit avec un System.setProperty().
Cette propriété doit contenir le nom complet d'une classe contenant une méthode :
public void handle (Throwable t);
A chaque exception non interceptée, une nouvelle instance de cette classe est créée (il faut un constructeur vide) et la méthode handle() est appelée.
Attention : Cela ne fonctionne pas avec Java 1.5.
Dans le cadre de Java 1.5 on peut utiliser les UncaughtExceptionHandler avec la méthode Thread.setDefaultUncaughtExceptionHandler() (voir avec setUncaughtExceptionHandler() afin de l'effectuer thread par thread...)
Cette solution est nettement plus propre mais malheureusement elle neccessite une JVM 1.5.
Il faut se connecter à l'URL et vérifier le code de réponse. On voit ainsi si l'URL est valide.
import java.io.*;
import java.net.*;
publique boolean testUrl(String url) {
try {
HttpURLConnection conn = (HttpURLConnection) new URL(url).openConnection();
conn.connect();
return conn.getResponseCode() == HttpURLConnection.HTTP_OK;
} catch (MalformedURLException e) {
return false;
} catch (IOException e) {
return false;
}
}
Pour définir les commentaires javadoc au niveau du package, il faut créer un fichier HTML nommé "package.html" dans le répertoire du package. Ce fichier sera lu par l'outil javadoc pour générer la page de documentation du package, et son contenu (entre les balises <body> et </body>) sera alors utilisé comme commentaire pour le package, par exemple :
<HTML>
<BODY>
Documentation du package <b>com.masociete.monpack</b>.
@since 1.0
</BODY>
</HTML>
A partir de Java 5.0, le fichier "package.html" peut être remplacé par un fichier source java spécial nommé "package-info.java". Ce fichier ne doit contenir que la ligne de déclaration du package, avec ses commentaires "javadoc" et surtout ses éventuelles annotations. Par exemple :
/**
* Documentation du package <b>com.masociete.monpack</b>.
* @since 1.0
*/
@MonAnnotation
package com.masociete.monpack;
Lien : The Java API Documentation Generator - Package Comment Files
La liste de toutes les options pour les versions 1.3.1 à 1.6.0 de la Java HotSpot VM (plateforme SPARC/Solaris) sont disponibles sur le site de Sun Microsystems : A Collection of JVM Options
D'autres éléments relatifs aux options de la JVM sont disponibles aux adresses suivantes:
Il est à noter que cette liste d'options est utilisable avec la plupart des outils livrés avec le JDK tels que:
- %JAVA_HOME%\bin\javac.exe
- %JAVA_HOME%\bin\javap.exe
- %JAVA_HOME%\bin\jconsole.exe
- %JAVA_HOME%\bin\keytool.exe
- %JAVA_HOME%\bin\rmic.exe
- etc.
Il suffit pour cela d'utiliser l'option -J qui passera directement les paramètres à la Runtime Java sous-jacente.
Exemple:
C:\Program Files\Java\jdk1.6.0_02\bin>javadoc.exe -J-version
java version "1.6.0_02"
Java(TM) SE Runtime Environment (build 1.6.0_02-b06)
Java HotSpot(TM) Client VM (build 1.6.0_02-b06, mixed mode, sharing)

















