L’essentiel à retenir avant de l’utiliser
- PrintWriter écrit du texte, pas des octets bruts.
- Ses méthodes d’écriture ne lancent pas d’
IOExceptionà l’exécution, donc il faut surveiller les erreurs autrement. -
autoFlushne s’active que surprintln,printfetformat. - Un encodage explicite, souvent UTF-8, évite les surprises entre machines et serveurs.
-
try-with-resourcesreste le réflexe le plus fiable pour fermer et vider le flux.
Ce que fait vraiment PrintWriter
PrintWriter est une sous-classe de Writer. En pratique, cela signifie qu’elle travaille sur des caractères et non sur des octets, ce qui la rend adaptée à tout ce qui ressemble à du texte humain: fichiers, exports, journaux applicatifs, réponses textuelles ou sorties de diagnostic.
Je la choisis quand je veux des appels simples comme print, println, printf, format ou append. Le confort est réel, mais il a un prix: la documentation Oracle rappelle que les écritures de cette classe ne lèvent pas d’IOException directement. C’est pratique pour écrire vite, moins pour détecter un problème d’I/O au millimètre près.
Autre détail qui compte: println utilise le séparateur de ligne de la plateforme, pas un simple caractère \n imposé partout. C’est exactement le genre de comportement qui simplifie la portabilité d’un fichier texte sur Windows, Linux ou macOS. Une fois ce socle compris, le vrai sujet devient le choix du bon constructeur et du bon encodage.
Choisir le bon constructeur et l’encodage
Le bon constructeur dépend surtout de la destination. Si je pars d’un fichier ou d’un flux déjà préparé, je préfère une surcharge qui prend un Charset explicite. Sur un JDK récent, c’est la manière la plus saine d’éviter que la machine s’appuie sur un charset par défaut différent de celui attendu.
| Constructeur | Usage pratique | Point d’attention |
|---|---|---|
PrintWriter(Writer out) |
Encapsuler un writer déjà configuré | Pas d’auto-flush, donc rien ne sort tant que le flux n’est pas fermé ou vidé |
PrintWriter(Writer out, boolean autoFlush) |
Envoyer du texte en mode confortable | L’auto-flush ne s’active que sur println, printf et format
|
PrintWriter(String fileName, Charset charset) |
Écrire un fichier texte avec encodage explicite | Le fichier est tronqué s’il existe déjà |
PrintWriter(OutputStream out, boolean autoFlush, Charset charset) |
Convertir un flux d’octets en sortie texte | Utile surtout quand une API fournit déjà un OutputStream
|
En 2026, je déconseille les surcharges qui reposent sur le charset par défaut, sauf cas très contrôlé. Pour un projet européen ou multilingue, UTF-8 reste le choix le plus robuste. Ce n’est pas une préférence de confort, c’est une façon simple d’éviter des fichiers illisibles sur une autre machine.
Une fois le constructeur bien choisi, il faut surtout écrire le texte proprement, sans supposer que toutes les méthodes se comportent de la même manière.

Écrire un fichier proprement sans pièges
Voici le genre de structure que je recommande le plus souvent quand je veux produire un fichier texte propre, lisible et portable en Java:
import java.io.PrintWriter;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.Locale;
public class ExportTexte {
public static void main(String[] args) throws IOException {
try (PrintWriter out = new PrintWriter("rapport.txt", StandardCharsets.UTF_8)) {
out.println("Rapport d'exécution");
out.printf(Locale.FRANCE, "Score : %.2f%n", 42.75);
out.write("Ligne finale");
}
}
}
Dans cet exemple, println ajoute un retour à la ligne portable, printf formate une valeur avec la locale française, et write n’ajoute rien du tout. Cette différence paraît évidente sur le papier, mais c’est souvent là que les erreurs se glissent dans les petits scripts et les exports métier.
Je préfère aussi le try-with-resources: le writer se ferme automatiquement, le buffer est vidé à la fin du bloc, et le code reste lisible. Si vous écrivez un document ou un export destiné à être ouvert juste après la génération, ce point devient vite non négociable.
Quand le texte doit être formaté pour un humain, je passe volontiers par printf ou format. En revanche, pour une écriture brute et séquentielle, je reste prudent: la suite du sujet, c’est justement la gestion du flush et des erreurs silencieuses.
Flush, close et erreurs silencieuses
Le piège classique avec PrintWriter, c’est de croire qu’il signale les problèmes au moment où ils se produisent. En réalité, il accumule les erreurs et vous laisse les vérifier avec checkError(). Si vous écrivez vers un fichier local, cela passe souvent inaperçu. Si vous écrivez vers une réponse réseau, un tube ou un flux dont la disponibilité varie, ce silence peut coûter cher.
-
flush()pousse le contenu du buffer vers la destination sans fermer le flux. -
close()ferme définitivement le flux et libère les ressources associées. -
autoFlushne réagit qu’àprintln,printfetformat, pas à un simplewriteou à unprintsuivi d’un retour manuel. -
checkError()devient utile quand la sortie est critique et que vous devez réagir à un échec réel.
Je garde un autre réflexe en tête: si j’utilise format sans locale explicite, c’est la locale par défaut de la machine qui s’applique. Pour un service en français, je préfère souvent passer Locale.FRANCE dès que le rendu est destiné à un humain, afin d’éviter des séparateurs numériques inattendus.
Dans les faits, je flush manuellement surtout quand je veux que la sortie soit visible avant la fin du bloc, par exemple dans un flux interactif ou une séquence de diagnostic. Sinon, je laisse le close() du try-with-resources faire le travail, parce que c’est plus lisible et généralement suffisant.
Cette manière de raisonner aide aussi à comparer PrintWriter avec les autres classes d’écriture de texte.
PrintWriter face à BufferedWriter et PrintStream
Je vois souvent PrintWriter choisi par réflexe alors que l’objectif réel est différent. Si vous cherchez surtout une sortie texte simple et confortable, il est très bien. Si vous voulez une chaîne d’I/O plus stricte ou une remontée d’erreurs plus directe, d’autres classes sont parfois plus cohérentes.
| Classe | Forces | Limites | Quand la choisir |
|---|---|---|---|
PrintWriter |
API très pratique, println/printf/format, fermeture facile |
Erreurs silencieuses, encodage à cadrer | Export texte simple, génération de fichiers lisibles, sortie textuelle rapide |
BufferedWriter |
Buffering explicite, contrôle plus bas niveau, exceptions I/O normales | Moins ergonomique pour le formatting | Gros volumes de texte, besoin de remonter les erreurs immédiatement |
PrintStream |
Très pratique pour la console et les flux orientés octets | Modèle centré sur les bytes, pas sur le texte strictement typé |
System.out, intégration avec un flux binaire, sortie standard |
Mon critère est simple: si je veux écrire vite du texte lisible, je prends PrintWriter. Si je veux une écriture plus explicite sur les erreurs et un comportement moins permissif, je tends vers BufferedWriter. Et si je travaille avec la console ou un flux binaire, PrintStream reste la classe à regarder.
Le point important n’est pas de trouver la classe “idéale”, mais celle dont les compromis correspondent au cas réel.
Les réflexes que j’applique en production
Quand j’intègre PrintWriter dans un projet, je reviens presque toujours aux mêmes règles. Elles ne sont pas spectaculaires, mais elles évitent la majorité des bugs bêtes:
- UTF-8 explicite pour éviter les écarts entre environnements.
-
try-with-resourcespour fermer proprement et vider le buffer. -
printlnouprintfquand je veux un flush automatique, pas un simplewrite. -
checkError()si la sortie a une valeur métier ou technique. - Locale explicite si le texte formaté doit rester stable dans un contexte français.
Au fond, le bon usage de PrintWriter tient en une phrase: je l’emploie pour rendre l’écriture de texte confortable, mais je ne lui confie pas aveuglément la fiabilité du flux. Cette nuance fait la différence entre un code pratique et un code fragile.