Текущая директория в Java в 2025: 3 способа и ловушки

Онлайн-тренажер Python для начинающих

Изучайте Python легко и без перегрузки теорией. Решайте практические задачи с автоматической проверкой, получайте подсказки на русском языке и пишите код прямо в браузере — без необходимости что-либо устанавливать.

Начать курс

Где я? Или как не заблудиться в файловой системе Java-приложения

Каждый Java-разработчик хотя бы раз в жизни задавался вопросом: «А где, собственно, выполняется мой код?» Вроде бы запускаешь программу из IDE, всё работает. Но стоит перенести её на сервер или в Docker-контейнер — и файлы, которые должны были лежать рядом с jar-файлом, оказываются где-то в недрах /opt/app или /tmp. Знакомая ситуация?

Вопрос «How to get the current working directory in Java?» — один из самых популярных на Stack Overflow. И неспроста: ответ кажется очевидным, но на практике таит множество подводных камней. В этой статье мы разберём лучшие практики 2025 года: как надёжно получить current working directory, когда можно ему доверять, а когда лучше искать альтернативы.

Дисклеймер: статья написана для Java 17+ (LTS), но примеры совместимы с Java 11 и новее. Если вы всё ещё на Java 8 — срочно обновляйтесь, но код тоже будет работать.

Классический способ: System.getProperty("user.dir")

Самый простой и широко используемый метод — обратиться к системному свойству user.dir. Это то, что вы увидите в 90% ответов на Stack Overflow:

public class WorkingDirectoryExample {
    public static void main(String[] args) {
        String cwd = System.getProperty("user.dir");
        System.out.println("Текущая директория: " + cwd);
    }
}

Что вы получите на выходе? Путь к папке, из которой была запущена JVM. Если запускаете из корня проекта — это будет /Users/you/projects/my-app. Если из target/ — будет target/.

Почему это не всегда надёжно?

Проблема №1: IDE и билд-системы обманывают. IntelliJ IDEA по умолчанию устанавливает working directory равным корню модуля. Maven — корню проекта. Gradle — корню модуля. Но это можно переопределить в конфигурации запуска. В результате на локальной машине код работает, а на сервере — нет.

Проблема №2: Unit-тесты. При запуске через JUnit или TestNG working directory может быть корнем проекта, а может — папкой билда. Зависит от настроек раннера.

Проблема №3: Веб-приложения и микросервисы. В контейнерах (Tomcat, Jetty) working directory часто равен $CATALINA_HOME/bin или /. Никакого отношения к вашему WAR-файлу это не имеет.

Современный подход: java.nio.file.Paths

Начиная с Java 7, у нас есть более «джавовый» способ — через NIO.2 API:

import java.nio.file.Paths;

public class NioWorkingDirectory {
    public static void main(String[] args) {
        String cwd = Paths.get("").toAbsolutePath().toString();
        System.out.println("Текущая директория (NIO): " + cwd);
    }
}

Что здесь происходит? Paths.get("") создаёт ссылку на пустой путь, который интерпретируется как относительный путь к текущей директории. Метод toAbsolutePath() разрешает его в абсолютный. Результат — тот же user.dir, но с более строгой типизацией.

Плюсы:

  • Работает с объектами Path, а не со строками (меньше ошибок с разделителями)
  • Можно сразу использовать в современных API
  • Кроссплатформенность (Windows vs Linux)

Минусы: всё те же — это по-прежнему working directory процесса.

Альтернатива: File("")

Для тех, кто не хочет переходить на NIO (хотя зря):

import java.io.File;

public class FileWorkingDirectory {
    public static void main(String[] args) {
        String cwd = new File("").getAbsolutePath();
        System.out.println("Текущая директория (File): " + cwd);
    }
}

Абсолютно то же самое, что и Paths.get("").toAbsolutePath(), но с использованием старого java.io.File. Работает, но не даёт преимуществ NIO.

Когда working directory — зло? Разбор реальных кейсов

Кейс 1: Чтение конфигурационного файла

Допустим, у вас есть config.properties в корне проекта. Вы пишете:

// ПЛОХОЙ ПРИМЕР
File configFile = new File("config.properties");

Это сработает только если JVM запущена из корня проекта. В production окружении файл не найдётся. Правильное решение — класть конфиги в classpath:

// ХОРОШИЙ ПРИМЕР
InputStream input = getClass().getClassLoader()
    .getResourceAsStream("config.properties");

Кейс 2: Создание временных файлов

Никогда не создавайте временные файлы в working directory! Используйте System.getProperty("java.io.tmpdir") или Files.createTempFile():

Path tempFile = Files.createTempFile("myapp-", ".tmp");
System.out.println("Временный файл: " + tempFile);

Кейс 3: Логирование путей

Если вам нужно залогировать, откуда запущено приложение — user.dir подойдёт. Но не используйте его для бизнес-логики.

Лучшие практики на 2025 год

После тысяч прочитанных issue на GitHub и багов в продакшене я выработал несколько железных правил:

  1. Не полагайтесь на working directory. Если вам нужен файл, который лежит рядом с jar — используйте ProtectionDomain или getCodeSource().
  2. Используйте Paths.get("") только для отладки. В продакшене — явно передавайте пути через конфигурацию или переменные окружения.
  3. Для веб-приложений — получайте реальный путь через ServletContext.getRealPath().
  4. Для unit-тестов — не используйте working directory. Используйте временные папки через @TempDir (JUnit 5).

Пример: как правильно получить путь к jar-файлу

import java.net.URL;
import java.security.ProtectionDomain;

public class JarLocation {
    public static void main(String[] args) {
        ProtectionDomain pd = JarLocation.class.getProtectionDomain();
        URL location = pd.getCodeSource().getLocation();
        System.out.println("JAR находится здесь: " + location);
    }
}

Этот код покажет, откуда физически загружен ваш класс. Если вы запускаете из IDE — это будет папка target/classes. Если из jar — путь к jar-фай

Блоги

Рекомендаций по книгам