W3docs

Класс Properties в Java

Загрузка и хранение пар ключ-значение в Java с помощью класса Properties, включая файлы .properties.

Properties — это контейнер JDK для строковой конфигурации — настроек приложения, переопределений среды, локализованных сообщений, параметров подключения JDBC. Он расширяет Hashtable<Object, Object> (историческое решение, о котором сейчас сожалеют, но которое уже не изменить) и добавляет три вещи сверху: формат файла .properties с читателем и писателем, XML-читатель и XML-писатель, а также концепцию объекта свойств по умолчанию, который используется, когда ключ не найден локально.

System.getProperties() возвращает Properties. Каждый вызов System.getProperty("user.home") проходит через него. Как только вы познакомитесь с этим классом, вы будете встречать его повсюду.

Контракт: строки с обеих сторон

Хотя класс наследует метод put(Object, Object) от Hashtable, единственным безопасным API является пара с типом string:

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

Если вы обойдёте setProperty и вызовете put("server.port", 8080) с Integer, запись всё равно попадёт в таблицу, но вы заложите мину: stringPropertyNames() молча отфильтрует её, а методы записи файлов (store, storeToXML) выбросят ClassCastException, как только попытаются привести этот Integer к String. Относитесь к Properties как к Properties<String, String>, даже если generics этого не говорят.

Формат файла .properties

Обычный текст, построчный формат, key=value. Пробелы вокруг = допустимы. Строки, начинающиеся с # или !, являются комментариями. Обратная косая черта в конце строки продолжает значение на следующей строке. Поддерживаются Unicode-экранирования (\uXXXX), но начиная с Java 9 перегрузка load(Reader) читает UTF-8 нативно, поэтому они редко нужны.

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

load и store работают с этим форматом. loadFromXML и storeToXML работают с эквивалентным XML-форматом, определённым в properties.dtd — он существует, иногда полезен, но почти никогда не предпочтителен перед текстовой формой.

Загрузка и сохранение

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
}

Метод store записывает комментарий с временной меткой после пользовательского комментария, ничего не сортирует (записи попадают в порядке итерации Hashtable) и экранирует специальные символы (=, :, #, начальные пробелы) для правильного восстановления. Вывод переносим между JVM.

Для ресурсов, включённых в ваше приложение, загружайте из classpath вместо файловой системы:

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

load(InputStream) — историческая перегрузка, использующая ISO-8859-1 (Latin-1) с \u-экранированиями. load(Reader) использует кодировку, с которой был открыт читатель. Предпочитайте форму с читателем, когда контролируете кодировку.

Свойства по умолчанию: многоуровневая конфигурация

Двухаргументный getProperty(key, default) возвращает запасное значение, когда ключ отсутствует. Конструктор Properties(Properties defaults) делает то же самое, но на уровне объекта — второй Properties используется, когда первый не содержит ключ:

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)

Это стандартный паттерн для "умолчаний, поставляемых с приложением, которые пользователь может переопределить для каждой среды". Два уровня — наиболее распространённый случай; можно строить цепочки длиннее.

Свойства System и флаги -D

JVM имеет глобальный экземпляр Properties, доступный через System.getProperties() и System.getProperty(key). Стандартные ключи включают java.version, user.home, user.dir, os.name, file.separator и line.separator. Флаг -Dkey=value в командной строке JVM добавляет значения до запуска main:

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

Это простейшая "командная строчная конфигурация", которую можно дать Java-программе. Для более крупных конфигураций принято использовать файл .properties, поставляемый с приложением и объединяемый при запуске со свойствами системы (которые действуют как переопределения).

Чем Properties не является

  • Не map произвольных типов. Только строки. Разбирайте Integer.parseInt(config.getProperty("port")) самостоятельно.
  • Не иерархический. Ключи вроде db.primary.host — просто строки; точки — это соглашение, а не структура. Если нужна настоящая иерархия, используйте библиотеку конфигурации YAML/JSON.
  • Не потокобезопасный для составных операций. Каждый метод синхронизирован (унаследован от Hashtable), но операции "проверить, затем выполнить" всё равно могут гонять потоки. Та же оговорка, что и у родительского класса.
  • Не замена ResourceBundle для i18n. PropertyResourceBundle — это ResourceBundle, основанный на файле .properties, который добавляет поиск по локали; это правильный инструмент для переведённых строк.

Рабочий пример: загрузка умолчаний, переопределение по среде, запись обратно

Программа ниже строит многоуровневую конфигурацию (умолчания внутри JAR, файл среды снаружи), читает системное свойство как переопределение -D, сглаживает результат для записи обратно в буфер .properties для проверки и демонстрирует ловушку с нестроковым store.

Одна тонкость, которую пример намеренно обрабатывает: store записывает только собственные записи объекта Properties — он никогда не обходит унаследованную цепочку defaults. Поэтому, чтобы получить полный, переносимый файл, мы копируем каждый разрешённый ключ в плоский Properties перед сохранением, а не вызываем store на объекте, который опирается на родителя defaults.

java— editable, runs on the server

Что можно вынести из результата выполнения:

  • Трёхуровневая конфигурация (умолчания → файл среды → переопределения -D) разрешается правильно. Умолчания заполняют то, что никто не переопределил; файл среды изменяет log.level и feature.beta; флаг -D побеждает для server.port.
  • store создал переносимый текст .properties с комментарием и временной меткой вверху, содержащий все четыре разрешённых ключа. Вы могли бы подать этот файл обратно в load и получить ту же карту — потому что мы предварительно сгладили уровни.
  • setProperty("age", 30) не скомпилируется (требуется String). put("age", 30) компилируется, запись попадает в таблицу, и stringPropertyNames фильтрует её — но store не пропускает её молча: он выбрасывает ClassCastException, как только пытается привести Integer к String. Урок: никогда не используйте put для нестрок в Properties — всегда используйте setProperty.

Что дальше

Properties был последней главой о "структурах данных" в этой части. Оставшиеся главы посвящены операциям над коллекциями: обход (Iterators и ListIterator), сравнение элементов (Comparable и Comparator) и статические утилиты для сортировки, поиска и обёртывания (класс Collections). Следующая глава начинается с основ — интерфейса Iterator, который тайно использует каждый цикл for-each.

Практика

Практика
Вы пишете `props.put('port', 8080)` (Integer) на объекте `Properties`, а затем вызываете `props.store(out, null)`. Что произойдёт?
Вы пишете `props.put('port', 8080)` (Integer) на объекте `Properties`, а затем вызываете `props.store(out, null)`. Что произойдёт?
Was this page helpful?