Чтобы сократить разрыв между ответами aioobe и Bozho, я бы также советовал не устанавливать параметр line.separator
при запуске JVM, поскольку это потенциально нарушает многие фундаментальные предположения, которые JVM и код библиотеки делают в отношении среды, в которой выполняется работа. Например, Если библиотека, от которой вы зависите, использует line.separator
для хранения конфигурационного файла кроссплатформенным способом, вы просто нарушили это поведение. Да, это крайний случай, но это делает его еще более гнусным, когда спустя годы проблема действительно возникает, и теперь весь ваш код зависит от наличия этой настройки, в то время как ваши библиотеки (правильно) предполагают ее нет.
Тем не менее, иногда эти вещи находятся вне вашего контроля, например, когда библиотека полагается на line.separator
и не дает вам возможности явно переопределить это поведение. В таком случае вы застреваете, переопределяя значение, или что-то более болезненное, например, повторная реализация или исправление кода вручную.
Для этих ограниченных случаев допустимо переопределить line.separator
, но мы должны следовать двум правилам:
- Свести к минимуму область переопределения
- Отменить отмену независимо от того, что
Оба эти требования хорошо выполняются синтаксисом AutoCloseable
и try-with-resources , поэтому я реализовал класс PropertiesModifier
, который обеспечивает чистое и то, и другое.
/**
* Class which enables temporary modifications to the System properties,
* via an AutoCloseable. Wrap the behavior that needs your modification
* in a try-with-resources block in order to have your properties
* apply only to code within that block. Generally, alternatives
* such as explicitly passing in the value you need, rather than pulling
* it from System.getProperties(), should be preferred to using this class.
*/
public class PropertiesModifier implements AutoCloseable {
private final String original;
public PropertiesModifier(String key, String value) {
this(ImmutableMap.of(key, value));
}
public PropertiesModifier(Map<String, String> map) {
StringWriter sw = new StringWriter();
try {
System.getProperties().store(sw, "");
} catch (IOException e) {
throw new AssertionError("Impossible with StringWriter", e);
}
original = sw.toString();
for(Map.Entry<String, String> e : map.entrySet()) {
System.setProperty(e.getKey(), e.getValue());
}
}
@Override
public void close() {
Properties set = new Properties();
try {
set.load(new StringReader(original));
} catch (IOException e) {
throw new AssertionError("Impossible with StringWriter", e);
}
System.setProperties(set);
}
}
Мой вариант использования был с Files.write()
, который является очень удобным методом, за исключением того, что он явно основан на line.separator
. Обратив вызов в Files.write()
, я могу безошибочно указать разделитель строк, который хочу использовать, не рискуя подвергнуть его воздействию каких-либо других частей моего приложения (обратите внимание, конечно, что он по-прежнему не ориентирован на многопоточность).
try(PropertiesModifier pm = new PropertiesModifier("line.separator", "\n")) {
Files.write(file, ImmutableList.of(line), Charsets.UTF_8);
}