W3docs

Classe Properties en Java

Chargez et stockez des paires clé-valeur de chaînes en Java avec la classe Properties, y compris les fichiers .properties.

Properties est le conteneur du JDK pour la configuration chaîne-à-chaîne — paramètres d'application, substitutions d'environnement, messages localisés, paramètres de connexion JDBC. Elle étend Hashtable<Object, Object> (une décision historique que nous regrettons, mais avec laquelle nous sommes coincés) et ajoute trois choses par-dessus : un format de fichier .properties avec un lecteur et un écrivain, un lecteur et un écrivain XML, et le concept d'un objet Properties par défaut consulté lorsqu'une clé n'est pas trouvée localement.

System.getProperties() retourne un Properties. Chaque appel à System.getProperty("user.home") passe par lui. Une fois que vous avez vu la classe, vous la voyez partout.

Le contrat : des chaînes des deux côtés

Bien que la classe hérite d'une méthode put(Object, Object) de Hashtable, la seule API sûre est la paire typée en chaîne :

Properties config = new Properties();
config.setProperty("server.port", "8080");
config.setProperty("server.host", "localhost");
String port = config.getProperty("server.port");          // "8080"
String log  = config.getProperty("log.level", "INFO");    // default fallback

Si vous contournez setProperty et appelez put("server.port", 8080) avec un Integer, l'entrée atterrit quand même dans la table, mais vous avez posé une mine : stringPropertyNames() la filtre silencieusement, et les méthodes d'écriture de fichier (store, storeToXML) lancent une ClassCastException au moment où elles tentent de convertir cet Integer en String. Traitez Properties comme Properties<String, String> même si les génériques ne le disent pas.

Le format de fichier .properties

Texte brut, orienté ligne, key=value. Les espaces autour de = sont autorisés. Les lignes commençant par # ou ! sont des commentaires. Un antislash en fin de ligne continue la valeur sur la ligne suivante. Les échappements Unicode (\uXXXX) sont pris en charge, mais depuis Java 9 la surcharge load(Reader) lit nativement en UTF-8, donc vous en avez rarement besoin.

# server.properties — last edited 2026-05-12
server.host = localhost
server.port = 8080
server.path = /api/v1
greeting    = Welcome, \
              user!

load et store gèrent ce format. loadFromXML et storeToXML gèrent le format XML équivalent défini par properties.dtd — il existe, occasionnellement utile, presque jamais préféré au format texte.

Chargement et stockage

Properties config = new Properties();
try (var in = Files.newBufferedReader(Path.of("server.properties"))) {
  config.load(in);              // UTF-8 text
}

config.setProperty("server.port", "9090");

try (var out = Files.newBufferedWriter(Path.of("server.properties"))) {
  config.store(out, "edited by setup script");   // comment becomes the first line
}

La méthode store écrit un commentaire d'horodatage après le commentaire utilisateur, ne trie rien (les entrées s'insèrent dans l'ordre d'itération de Hashtable), et échappe les caractères spéciaux (=, :, #, les espaces en début de ligne) pour un aller-retour fidèle. La sortie est portable entre les JVM.

Pour les ressources regroupées avec votre application, chargez depuis le classpath au lieu du système de fichiers :

try (var in = MyApp.class.getResourceAsStream("/app.properties")) {
  config.load(in);              // load(InputStream) defaults to ISO-8859-1
}

load(InputStream) est la surcharge historique et utilise ISO-8859-1 (Latin-1) avec des échappements \u. load(Reader) utilise le jeu de caractères avec lequel le lecteur a été ouvert. Préférez la forme avec lecteur lorsque vous contrôlez l'encodage.

Propriétés par défaut : configuration en couches

Le getProperty(key, default) à deux arguments retourne une valeur de repli lorsque la clé est absente. Le constructeur Properties(Properties defaults) fait la même chose mais au niveau de l'objet — le second Properties est consulté lorsque le premier ne contient pas la clé :

Properties base = new Properties();
base.setProperty("server.port", "8080");
base.setProperty("log.level",   "INFO");

Properties override = new Properties(base);   // base is the defaults
override.setProperty("log.level", "DEBUG");   // override wins

override.getProperty("server.port");          // "8080"  (from base)
override.getProperty("log.level");            // "DEBUG" (from override)

C'est le modèle standard pour « des valeurs par défaut livrées avec l'application, l'utilisateur peut les remplacer par environnement ». Deux couches est le cas courant ; vous pouvez en chaîner davantage.

Propriétés System et flags -D

La JVM possède une instance globale de Properties accessible via System.getProperties() et System.getProperty(key). Les clés standard comprennent java.version, user.home, user.dir, os.name, file.separator et line.separator. Le flag -Dkey=value sur la ligne de commande JVM l'enrichit avant que main ne s'exécute :

java -Dserver.port=9090 -Dlog.level=DEBUG -jar app.jar
String port = System.getProperty("server.port", "8080");

C'est la « configuration en ligne de commande » la plus simple que vous puissiez donner à un programme Java. Pour des configurations plus importantes, la convention est un fichier .properties livré avec l'application, fusionné au démarrage avec les propriétés système (qui jouent le rôle de substitutions).

Ce que Properties n'est pas

  • Pas une map de types arbitraires. Uniquement des chaînes. Parsez vous-même avec Integer.parseInt(config.getProperty("port")).
  • Pas hiérarchique. Des clés comme db.primary.host ne sont que des chaînes ; les points sont conventionnels, pas structurels. Si vous avez besoin d'une vraie hiérarchie, utilisez une bibliothèque de configuration YAML/JSON.
  • Pas thread-safe pour les opérations composées. Chaque méthode est synchronisée (héritée de Hashtable), mais vérifier-puis-agir reste sujet aux races. Même mise en garde que la classe parente.
  • Pas un remplacement de ResourceBundle pour l'i18n. PropertyResourceBundle est un ResourceBundle soutenu par un fichier .properties et ajoute la recherche locale ; c'est le bon outil pour les chaînes traduites.

Un exemple concret : charger les valeurs par défaut, substituer par environnement, réécrire

Le programme ci-dessous construit une configuration en couches (valeurs par défaut à l'intérieur du JAR, fichier d'environnement à l'extérieur), lit une propriété système pour servir de substitution -D, aplatit le résultat afin qu'il puisse être réécrit dans un buffer .properties pour inspection, et démontre le piège des non-chaînes avec store.

Une subtilité que l'exemple gère délibérément : store n'écrit que les entrées propres d'un objet Properties — il ne parcourt jamais la chaîne defaults héritée. Donc pour obtenir un fichier complet et sérialisable en aller-retour, nous copions chaque clé résolue dans un Properties plat avant de stocker, plutôt que d'appeler store sur un objet qui repose sur son parent de valeurs par défaut.

java— editable, runs on the server

Ce qu'il faut retenir de l'exécution :

  • La configuration à trois couches (valeurs par défaut → fichier d'environnement → substitutions -D) se résout correctement. Les valeurs par défaut remplissent ce que personne n'a substitué ; le fichier d'environnement modifie log.level et feature.beta ; le flag -D l'emporte pour server.port.
  • store a produit un texte .properties portable avec un commentaire et un horodatage en haut, contenant les quatre clés résolues. Vous pourriez réinjecter ce fichier directement dans load et obtenir la même map — parce que nous avons d'abord aplati les couches.
  • setProperty("age", 30) ne compilerait pas (il exige une String). put("age", 30) compile bien, l'entrée atterrit dans la table, et stringPropertyNames la filtre — mais store ne la saute pas silencieusement : il lance une ClassCastException dès qu'il essaie de convertir l'Integer en String. La leçon : ne jamais utiliser put avec une non-chaîne sur un Properties — utilisez toujours setProperty.

La suite

Properties était le dernier chapitre sur les « structures de données » dans cette partie. Les chapitres restants portent sur les opérations sur les collections : les parcourir (Iterators et ListIterator), comparer les éléments (Comparable et Comparator), et les utilitaires statiques pour trier, rechercher et encapsuler (la classe Collections). Le prochain chapitre commence par la base — l'interface Iterator que chaque boucle for-each utilise secrètement.

Pratique

Pratique
Vous écrivez `props.put('port', 8080)` (un `Integer`) sur un objet `Properties` et appelez ensuite `props.store(out, null)`. Que se passe-t-il ?
Vous écrivez `props.put('port', 8080)` (un `Integer`) sur un objet `Properties` et appelez ensuite `props.store(out, null)`. Que se passe-t-il ?
Was this page helpful?