To read a properties file in Java, we create a java.util.Properties object, pass a Reader for the file to load(), and get each value with getProperty(); to write one, we call setProperty() and store(). A properties file is a plain text file with one key=value pair per line.
We use properties files for settings that change between environments without a rebuild, such as a page size, a feature flag or a server URL, and for message texts in several languages.
The following example reads the settings of a recipe box app from app.properties, adds a value and writes the file back on Java 25.
Path file = Files.createTempFile("app", ".properties");
Files.writeString(file, "app.name=recipe-box\napp.pageSize=20\n");
Properties props = new Properties();
try (Reader reader = Files.newBufferedReader(file, StandardCharsets.UTF_8)) {
props.load(reader);
}
String name = props.getProperty("app.name"); // "recipe-box"
String theme = props.getProperty("app.theme", "light"); // "light"
props.setProperty("app.theme", "dark");
try (Writer writer = Files.newBufferedWriter(file, StandardCharsets.UTF_8)) {
props.store(writer, "Recipe box settings");
}
List<String> saved = Files.readAllLines(file).stream().filter(line -> !line.startsWith("#")).toList(); // [app.name=recipe-box, app.pageSize=20, app.theme=dark]
Notice that a missing key returns the default we pass, and that store() writes the keys in sorted order. We go through the file format, loading from disk and from the classpath, defaults and typed values, writing the file safely, and the encoding rules that break non-English text.
1. The Properties File Format
The format that Properties.load() reads is small and line-based. Most files use only key=value lines and # comments, but the parser accepts a few more forms, and they explain values that look wrong after loading.
- The separator is =, : or whitespace, and spaces around the separator are ignored.
- Lines that start with # or ! are comments, and blank lines are skipped.
- A backslash at the end of a line continues the value on the next line, and the leading spaces of that next line are dropped.
- Escapes such as \n, \t, \\ and \u00e9 are decoded, so a file can hold any character in plain ASCII.
- Trailing spaces belong to the value, which is a common source of bugs when we parse numbers.
The following example loads text that uses each form and checks what Properties makes of it.
String text = """
# Recipe box settings
app.name = recipe-box
app.pageSize: 20
app.greeting Bon app\\u00e9tit
app.categories = breakfast, lunch, \\
dinner
""";
Properties props = new Properties();
props.load(new StringReader(text));
String pageSize = props.getProperty("app.pageSize"); // "20"
boolean accented = props.getProperty("app.greeting").equals("Bon app\u00e9tit"); // true
String categories = props.getProperty("app.categories"); // "breakfast, lunch, dinner"
int count = props.size(); // 4

2. Read a Properties File in Java from the File System
For a file that operations staff edit on the server, such as /etc/recipe-box/app.properties, we open it with Files.newBufferedReader() and a UTF-8 charset inside try-with-resources. The method load() does not close the reader, so try-with-resources must do it.
static Properties loadSettings(Path file) throws IOException {
Properties props = new Properties();
try (Reader reader = Files.newBufferedReader(file, StandardCharsets.UTF_8)) {
props.load(reader);
}
return props;
}
Path config = Files.writeString(Files.createTempFile("app", ".properties"), "app.name=recipe-box\n");
String appName = loadSettings(config).getProperty("app.name"); // "recipe-box"
Properties none = loadSettings(Path.of("/etc/recipe-box/missing.properties")); // NoSuchFileException: /etc/recipe-box/missing.properties
The NoSuchFileException names the file, which is the message we want in the startup log. When the file is optional, we check Files.exists(file) first and fall back to the defaults from section 4.
3. Loading a Properties File from the Classpath
Default settings that ship with the app belong in src/main/resources, and the build copies them to the classpath. We load them with getResourceAsStream(), check for null and wrap the stream in an InputStreamReader with UTF-8, as the resources folder guide explains.
Properties defaults = new Properties();
try (InputStream in = Objects.requireNonNull(AppSettings.class.getResourceAsStream("/config/app.properties"), "config/app.properties");
Reader reader = new InputStreamReader(in, StandardCharsets.UTF_8)) {
defaults.load(reader);
}
String bundledName = defaults.getProperty("app.name"); // "recipe-box"
String bundledSize = defaults.getProperty("app.pageSize"); // "20"
A file on the classpath is read-only at runtime, because in production it sits inside the JAR. Code that changes settings writes them to a file on disk, never back into the resources folder. The rules for class loader names are in the classpath guide.
4. Defaults, Typed Values and a Settings Cache
The method getProperty(key) returns null for a missing key, and getProperty(key, defaultValue) returns the default instead. For a whole set of defaults, we pass a second Properties object to the constructor. A lookup checks the main object first and the defaults second, so the file on disk overrides the bundled values key by key.
Properties bundled = new Properties();
bundled.setProperty("app.pageSize", "20");
bundled.setProperty("app.theme", "light");
Properties settings = new Properties(bundled);
settings.setProperty("app.theme", "dark");
String theme = settings.getProperty("app.theme"); // "dark"
String size = settings.getProperty("app.pageSize"); // "20"
Set<String> keys = new TreeSet<>(settings.stringPropertyNames()); // [app.pageSize, app.theme]
int ownEntries = settings.size(); // 1
Note that size() counts only the entries of the main object, whereas stringPropertyNames() includes the defaults. Every value is a String, so numbers and flags need parsing.
Say the recipe box app shows 20 recipes per page, and the operations team raises the value in app.properties during a sale. If someone types app.pageSize=25 with a trailing space or app.pageSize=twenty, a plain Integer.parseInt() throws NumberFormatException and the app does not start. Most apps read the settings once at startup, so the parsing belongs in one small class that also keeps the loaded values.
final class AppSettings {
private static final Properties PROPS = load();
private AppSettings() {
}
private static Properties load() {
Properties props = new Properties();
try (InputStream in = AppSettings.class.getResourceAsStream("/config/app.properties")) {
if (in == null) {
throw new IllegalStateException("config/app.properties is not on the classpath");
}
props.load(new InputStreamReader(in, StandardCharsets.UTF_8));
} catch (IOException e) {
throw new UncheckedIOException(e);
}
return props;
}
static String get(String key, String defaultValue) {
return PROPS.getProperty(key, defaultValue);
}
static int getInt(String key, int defaultValue) {
String value = PROPS.getProperty(key);
if (value == null) {
return defaultValue;
}
try {
return Integer.parseInt(value.strip());
} catch (NumberFormatException e) {
return defaultValue;
}
}
}
int pageSize = AppSettings.getInt("app.pageSize", 10); // 20
int timeout = AppSettings.getInt("app.timeoutSeconds", 30); // 30
String appName = AppSettings.get("app.name", "unknown"); // "recipe-box"
The JVM initializes the static field PROPS once, on the first call, and class initialization is thread-safe, so the file is read one time even when many threads ask for settings. This is the same idea as the holder idiom of the singleton pattern. Failing fast with a clear exception when the file is missing beats a NullPointerException at the first lookup.
5. Writing and Updating a Properties File
To change a file, we load it, call setProperty() or remove(), and call store() with a Writer. The method writes our comment, a date line and every entry, escaping characters such as = and : in values with a backslash.
#Recipe box settings
#Sat Oct 10 11:26:04 IST 2026
app.name=recipe-box
app.pageSize=20
app.theme=dark
app.url=https\://example.com/a\=b
Since Java 18, store() writes the keys in their natural sort order, so the output no longer changes from run to run. The system property java.properties.date, set on the command line, replaces the date line with fixed text, which keeps the file identical between builds.
The method store() writes a new file from the map, so the comments, blank lines and key order of the original file are lost. When people maintain the file by hand, we keep it read-only for the app and write the values that the app changes to a second file that we load on top.
A crash in the middle of store() leaves a half-written file. The safe way is to write to a temporary file in the same folder and move it over the original in one step.
static void saveSettings(Properties props, Path file) throws IOException {
Path tmp = Files.createTempFile(file.toAbsolutePath().getParent(), "app", ".tmp");
try (Writer writer = Files.newBufferedWriter(tmp, StandardCharsets.UTF_8)) {
props.store(writer, "Recipe box settings");
}
Files.move(tmp, file, StandardCopyOption.REPLACE_EXISTING, StandardCopyOption.ATOMIC_MOVE);
}
Path file = Files.writeString(Files.createTempFile("app", ".properties"), "app.pageSize=20\n");
Properties props = loadSettings(file);
props.setProperty("app.pageSize", "25");
props.setProperty("app.theme", "dark");
saveSettings(props, file);
String reloaded = loadSettings(file).getProperty("app.pageSize"); // "25"
The move either replaces the whole file or fails, so a reader never sees a partial file. The move a file guide covers ATOMIC_MOVE and its limits across file systems.
6. Properties File Encoding: ISO-8859-1 vs UTF-8
The two overloads of load() decode text differently. The method load(InputStream) always reads ISO-8859-1, and the UTF-8 default of Java 18 (JEP 400) does not change that. A UTF-8 file with an accented letter, saved by any modern editor, turns into two wrong characters.
Path file = Files.writeString(Files.createTempFile("messages", ".properties"), "greeting=Bon app\u00e9tit\n", StandardCharsets.UTF_8);
Properties bytes = new Properties();
try (InputStream in = Files.newInputStream(file)) {
bytes.load(in);
}
boolean brokenText = bytes.getProperty("greeting").equals("Bon app\u00e9tit"); // false (read as ISO-8859-1)
Properties chars = new Properties();
try (Reader reader = Files.newBufferedReader(file, StandardCharsets.UTF_8)) {
chars.load(reader);
}
boolean correctText = chars.getProperty("greeting").equals("Bon app\u00e9tit"); // true
The same split applies to writing. The method store(OutputStream, …) writes ISO-8859-1 and turns every character above \u007E into an escape such as \u00E9, which every reader decodes correctly, whereas store(Writer, …) writes the characters in the charset of the writer. So we read and write with a UTF-8 Reader and Writer, or keep files in ASCII with escapes when old tools also read them.
| Method | Charset | Non-ASCII characters |
|---|---|---|
| load(InputStream) | ISO-8859-1 | Correct only as \uXXXX escapes |
| load(Reader) | Charset of the reader | Correct in UTF-8 with a UTF-8 reader |
| store(OutputStream, comment) | ISO-8859-1 | Written as \uXXXX escapes |
| store(Writer, comment) | Charset of the writer | Written as characters |
| PropertyResourceBundle (Java 9+) | UTF-8, falls back to ISO-8859-1 | Correct in UTF-8 |
Message bundles loaded with ResourceBundle.getBundle() read UTF-8 since Java 9 (JEP 226), so messages_fr.properties can contain accented text as is. The UTF-8 file guide covers charsets in general.
7. Properties File FAQs
Properties files raise new questions once the first version works and other people start editing the file.
7.1. Is the Properties class thread-safe?
Yes. Several threads can share one Properties object without external synchronization. The iterators of its views are not fail-fast, so a loop over the entries while another thread changes them does not throw, but it may or may not see those changes. For settings that change at runtime, we build a new Properties object and swap the reference.
7.2. Why does getProperty() return null for a value we added with put()?
Because getProperty() returns only String values. Properties extends Hashtable<Object, Object>, so put(“app.pageSize”, 20) compiles, but getProperty(“app.pageSize”) returns null and store() throws ClassCastException. We always call setProperty(), which takes two strings.
Properties props = new Properties();
props.put("app.pageSize", 20);
String size = props.getProperty("app.pageSize"); // null
7.3. How do we iterate over all properties?
We loop over stringPropertyNames(), which includes the defaults and skips non-String entries. Sorting the names into a TreeSet gives a stable order for logs. The forEach() method of the map view works too, but it skips the defaults.
7.4. How do we read properties in a Spring Boot application?
Spring Boot loads application.properties for us, so we inject values with @Value or bind a group of keys to a class with @ConfigurationProperties, as the Spring Boot properties guide shows. We use java.util.Properties on its own only for files that Spring does not manage.
7.5. Can a properties file be stored as XML?
Yes. The methods storeToXML() and loadFromXML() write and read the same keys and values in an XML format that is UTF-8 by default. Few tools expect it, so plain .properties files remain the common choice.
8. Conclusion
We read a properties file by passing a UTF-8 Reader to Properties.load() inside try-with-resources, whether the file comes from disk through Files.newBufferedReader() or from the classpath through getResourceAsStream(). The method getProperty() takes a default, a second Properties object supplies defaults for a whole file, and numbers need parsing with a fallback.
To write the file, we call setProperty() and store() with a UTF-8 Writer, preferably into a temporary file that we move over the original. Since Java 18 the keys come out sorted, while comments and the original order are lost, and load(InputStream) still reads ISO-8859-1 on every Java version. More file topics are in the Java IO tutorial.
9. References
- Properties Javadoc (Java 25)
- JDK 18 Release Notes
- JEP 400, UTF-8 by Default
- JEP 226, UTF-8 Property Resource Bundles
- Properties (The Java Tutorials)
Happy Learning !!
Please find the code snippets for normal property file updation
public class ReadingProp { public static void main(String args[]) { ReadingProp readingProp = new ReadingProp(); readingProp.ReadProperties(); } public void ReadProperties() { Properties properties = new Properties(); try (final InputStream inputStream = this.getClass().getResourceAsStream("xyz.properties")) { properties.load(inputStream); System.out.println(properties.getProperty("rama")); properties.setProperty("rama", "lakshmana"); String path= this.getClass().getResource("jaanu.properties").getPath(); OutputStream output = new FileOutputStream(path); properties.store(output,"comments are there"); output.close(); inputStream.close(); System.out.println(properties.getProperty("rama")); } catch (IOException e) { e.printStackTrace(); } } }We have to save the property file after setting the property, otherwise the actual property file still has old values.
Hi
Iam working on rest ssured. i am stuck at how to write/replace attribute in property file. i am looking for solution/suggestion
where should we place the prorperty file
Hi friend, thanks for your helpful topic but for the 2nd section “write to Property File Example”, I tested but it not write new or update value to properties file? Please take a look about it!
I have ExceptionInInitializerError
Hi Lokesh,
Firstly, I would like to thank you for writing articles on java which are easy to understand with great examples.
Secondly, I have a question, what if I have to process multiple properties files in my project.
One vague option I can think of is creating separate class per properties file. Which doesn’t sound good to me
Thirdly, if I need to update a value or introduce a new pair in my properties file then I will have to restart my application, which I see as a small draw back in making a singleton instance. What do you think?
Thanks,
A
Hi Aman, Managing one property file from one class OR multiple properties files from one class is a matter of situation in hand. The decision shoul be made logically. If it’s logical to group some of properties file, then manage them from one class file, if not logical then create separate files. I usually do not prefer more than 3-4 properties file in a application (for configuration purposes; excluding i18n). And chances are that you will need to read/write only a certain file in very specific scenarios; rest are usually directly feed to frameworks e.g. spring or struts.
Regarding singleton, they are supposed to be immutable as best practice. But it’s not mandatory. No need to restart the application.
Hi,
I want to add a hyperlink in the value (of key , value pair )in properties file, which should redirect me to my desired page of website. How can i do that
Yes, store the escaped URL e.g. url=http://www.example.org/test. Read URL and redirect using framework you are using.
It’s crystal clear concept, but can you tell me how to apply that concept in configure hibernate.cfg.xml file . I don’t know how to configure the hibernate properties vai using properties file ?
Hi, Lokesh, your solution for properties loading is very clean and nice. The only question I have is, you are not handling the IOException that may be thrown during loading the properties. How can we handle, can you please share?
There is an IOException catch block
Hi sir can u tell me how to use property file while reading a BLOB file from mysql database using jdbc?and save it.thanks.
Really best way to read properties file .. but one clarification needed is is .. where should i Keep properties file..Is it good enough if its Class Path?
Absolutely, I do not see any harm in doing so.