Когда использовать подкласс Writer в Java; общие практики
Я всегда немного путался с количеством различных реализаций ввода-вывода в Java, и теперь, когда я полностью застрял в разработке моего проекта, я не торопился читать полезные полезные материалы.
Я понял, что нет дружественного к новичкам сравнения (кроме краткого объяснения в API для класса Writer) между различными подклассами Writer
класс. Поэтому я решил, что увожу вопрос, для чего нужны разные подклассы?
Например, я обычно использую FileWriter
, завернутый с BufferedWriter
для моих выходов в файлы, но меня всегда раздражало то, что нет метода println()
, и нужно использовать newLine()
каждую вторую строку (чтобы сделать вывод доступным для человека). PrintWriter
имеет метод println()
, но конструктор, поддерживающий добавление, однако...
Я бы очень признателен, если бы вы могли дать мне свои два цента из вашего опыта или хороший путеводитель/как-то, возможно, наткнулся на вас.
EDIT: Спасибо за ответы всем, я очень ценю информацию, переданную здесь. Немного досадно, что вся вещь append()
оказалась в фокусе, это просто означало это как пример. Мой вопрос в основном касался необходимости и использования всех различных реализаций, о которых, как я думаю, несколько упоминалось в нескольких ответах.
Невозможно выбрать один ответ, как принято, поскольку есть три действительно твердых ответа, каждый из которых внес свой вклад в мое понимание проблемы. Мне нужно пойти с Аноном, на этот раз, когда он получил наименьшее количество репутации. (я полагаю, что он новичок в SO). Он ответил, что некоторые из них действительно хорошо сформулированы и заданы 0 вопросов. Хороший вклад я бы сказал, и это стоит поощрять.
Сказав это, ColinD и Jay также предоставили действительно хорошие ответы и указали интересные идеи. Особенно комментарий Jay о Java, автоматически обертывающий BufferedWriter
, стоит отметить. Еще раз спасибо, ребята, очень оценили!
Ответы
Ответ 1
Классы java.io
обычно следуют шаблону Decorator. Итак, в то время как PrintWriter
не имеет конкретного конструктора, который вам может понадобиться, он имеет конструктор, который принимает еще один Writer
, поэтому вы можете сделать что-то вроде следующего:
FileOutputStream fos = null;
try
{
fos = new FileOutputStream("foo.txt");
PrintWriter out = new PrintWriter(
new BufferedWriter(
new OutputStreamWriter(fos, "UTF-8")));
// do what you want to do
out.flush();
out.close();
}
finally
{
// quietly close the FileOutputStream (see Jakarta Commons IOUtils)
}
В качестве общего примечания об использовании вы всегда хотите обернуть низкоуровневый Writer (например, FileWriter
или OutputStreamWriter
) в BufferedWriter
, чтобы минимизировать фактические операции ввода-вывода. Однако это означает, что вам нужно явно скрывать и закрывать самого внешнего Writer, чтобы убедиться, что все содержимое записано.
И тогда вам нужно закрыть низкоуровневый Writer в блоке finally
, чтобы убедиться, что вы не утечка ресурсов.
Edit
Глядя на ответ MForster, я снова взглянул на API для FileWriter. И я понял, что он не принимает явный набор символов, который является очень плохим. Поэтому я отредактировал фрагмент кода, чтобы использовать FileOutputStream
, завернутый в OutputStreamWriter
, который принимает явный набор символов.
Ответ 2
FileWriter
обычно не является приемлемым классом. Он не позволяет вам указать Charset
для записи, что означает, что вы застряли в том, что происходит с кодировкой по умолчанию на платформе, на которой вы работаете. Излишне говорить, что это не позволяет последовательно использовать одну и ту же кодировку для чтения и записи текстовых файлов и может привести к повреждению данных.
Вместо использования FileWriter
вы должны обернуть FileOutputStream
в OutputStreamWriter
. OutputStreamWriter
позволяет вам указать кодировку:
File file = ...
OutputStream fileOut = new FileOutputStream(file);
Writer writer = new BufferedWriter(new OutputStreamWriter(fileOut, "UTF-8"));
Чтобы использовать PrintWriter
с приведенным выше, просто оберните BufferedWriter
в PrintWriter
:
PrintWriter printWriter = new PrintWriter(writer);
Вы также можете использовать конструктор PrintWriter
, который принимает File
и имя кодировки:
PrintWriter printWriter = new PrintWriter(file, "UTF-8");
Это отлично подходит для вашей конкретной ситуации и на самом деле делает то же самое, что и код выше, но хорошо знать, как его построить, обернув различные части.
Другие типы Writer
в основном предназначены для специализированного использования:
-
StringWriter
- это просто Writer
, который можно использовать для создания String
. CharArrayWriter
то же самое для char[]
.
-
PipedWriter
для трубопровода до PipedReader
.
Edit:
Я заметил, что вы прокомментировали еще один ответ о многословии создания автора таким образом. Обратите внимание, что существуют библиотеки, такие как Guava, которые помогают уменьшить объемность общих операций. Возьмем, например, запись файла String
в файл в определенной кодировке. С Guava вы можете просто написать:
Files.write(text, file, Charsets.UTF_8);
Вы также можете создать BufferedWriter
следующим образом:
BufferedWriter writer = Files.newWriter(file, Charsets.UTF_8);
Ответ 3
PrintWriter не имеет конструктора, который принимает параметр "добавить", но FileWriter делает это. И мне кажется логичным, что там, где он принадлежит. PrintWriter не знает, записываете ли вы файл, сокет, консоль, строку и т.д. Что это значит "добавить" к записи в сокет?
Итак, правильный способ сделать то, что вы хотите, просто:
PrintWriter out=new PrintWriter(new BufferedWriter(new FileWriter(myfile, append)));
Интересная заметка: если вы завершаете OutputStream в PrintWriter, Java автоматически добавляет BufferedWriter посередине. Но если вы обернете Writer в PrintWriter, это не так. Поэтому ничего не получается, говоря:
PrintWriter out=new PrintWriter(new BufferedWriter(new OutputStreamWriter(new FileOutputStream(myfile))));
Просто оставьте BufferedWriter и OutputStreamWriter, вы получите их бесплатно в любом случае. Я не знаю, есть ли веские причины для несогласованности.
Верно, что вы не можете указывать кодировку символов в FileWriter как заметки ColinD. Я не знаю, что это делает его "неприемлемым". Я почти всегда с удовольствием принимаю кодировку по умолчанию. Возможно, если вы используете язык, отличный от английского, это проблема.
Необходимость обертывания Writers или OutputStreams в слоях сбивала меня с толку, когда я впервые начал использовать Java. Но как только вы получите это, это не имеет большого значения. Вам просто нужно свести свой ум в рамки записи. У каждого автора есть функция. Подумайте об этом, я хочу напечатать файл, поэтому мне нужно обернуть FileWriter в PrintWriter. Или я хочу преобразовать выходной поток в запись, поэтому мне нужен OutputStreamWriter. Etc.
Или, может быть, вы просто привыкаете к тем, кого используете все время. Объясните это один раз и вспомните, как вы это сделали.
Ответ 4
Вы можете создать добавление PrintWriter
следующим образом:
OutputStream os = new FileOutputStream("/tmp/out", true);
PrintWriter writer = new PrintWriter(os);
Изменить: Сообщение анонимного права относительно использования BufferedWriter
между ними и указания кодировки.