1. Чтение файла целиком
Чтение всех байтов
Иногда нужно вытащить из файла всё содержимое разом — как есть, без разбора, будь то картинка, архив или какой-нибудь бинарный формат. Для этого в Java есть метод Files.readAllBytes(path). Он возвращает массив байт (byte[]), то есть просто сырые данные файла, которые вы дальше можете использовать как угодно.
import java.nio.file.*;
public class ReadBytesExample {
public static void main(String[] args) throws Exception {
Path path = Paths.get("example.txt");
byte[] allBytes = Files.readAllBytes(path);
System.out.println("Длина файла в байтах: " + allBytes.length);
}
}
Примечание: Если файл большой (гигабайты), такой подход может привести к проблемам с памятью — ведь всё содержимое файла загружается сразу в оперативку.
Чтение всех строк
Для текстовых файлов есть более человечный способ: Files.readAllLines(path). Он возвращает список строк (List<String>), где каждая строка — это одна строка из файла (разделители строк автоматически учитываются).
import java.nio.file.*;
import java.util.List;
public class ReadLinesExample {
public static void main(String[] args) throws Exception {
Path path = Paths.get("example.txt");
List<String> lines = Files.readAllLines(path);
for (String line : lines) {
System.out.println(line);
}
}
}
Важно: По умолчанию используется системная кодировка. Если вы хотите явно указать кодировку (например, UTF-8), используйте перегруженную версию метода:
List<String> lines = Files.readAllLines(path, java.nio.charset.StandardCharsets.UTF_8);
Пример: читаем файл и выводим на экран
Давайте добавим это в наше мини-приложение (например, «блокнот» или «список задач»):
import java.nio.file.*;
import java.util.List;
public class TodoReader {
public static void main(String[] args) throws Exception {
Path todoPath = Paths.get("todo.txt");
if (Files.exists(todoPath)) {
List<String> tasks = Files.readAllLines(todoPath);
System.out.println("Ваши задачи на сегодня:");
for (String task : tasks) {
System.out.println("- " + task);
}
} else {
System.out.println("Файл todo.txt не найден. Создайте список задач!");
}
}
}
2. Запись в файл
Запись массива байт
Если у вас есть массив байт (например, результат сериализации, изображение, аудио), используйте Files.write(path, bytes). Этот метод создаёт файл, если его нет, или перезаписывает существующий.
import java.nio.file.*;
public class WriteBytesExample {
public static void main(String[] args) throws Exception {
Path path = Paths.get("output.bin");
byte[] data = {1, 2, 3, 4, 5};
Files.write(path, data);
System.out.println("Байты записаны в файл output.bin");
}
}
Запись списка строк
Для текстовых файлов удобнее использовать список строк — каждая строка будет записана отдельно, с переводом строки.
import java.nio.file.*;
import java.util.Arrays;
import java.util.List;
public class WriteLinesExample {
public static void main(String[] args) throws Exception {
Path path = Paths.get("todo.txt");
List<String> tasks = Arrays.asList(
"Купить молоко",
"Позвонить бабушке",
"Сделать домашку по Java"
);
Files.write(path, tasks);
System.out.println("Список задач записан в todo.txt");
}
}
Внимание: По умолчанию файл будет перезаписан! Если он уже существовал, старые задачи исчезнут. (Как не потерять старое — расскажем чуть позже.)
Запись строки в файл
Если хочется записать одну строку — просто создайте список из одной строки или используйте Files.write(path, string.getBytes()):
import java.nio.file.*;
public class WriteStringExample {
public static void main(String[] args) throws Exception {
Path path = Paths.get("hello.txt");
String message = "Привет, Java!";
Files.write(path, message.getBytes());
System.out.println("Строка записана в hello.txt");
}
}
Явное указание кодировки
Чтобы избежать «кракозябр» при работе с русским (и не только) текстом, всегда указывайте кодировку:
import java.nio.charset.StandardCharsets;
import java.nio.file.*;
import java.util.List;
public class WriteLinesUtf8Example {
public static void main(String[] args) throws Exception {
Path path = Paths.get("hello.txt");
List<String> lines = List.of("Привет, мир!", "Это Java.");
Files.write(path, lines, StandardCharsets.UTF_8);
}
}
3. Обработка ошибок
При работе с файлами Java может выбросить исключение IOException. Это общий сигнал о том, что «что-то пошло не так» на уровне ввода-вывода. Причин может быть масса: файла просто нет, прав доступа не хватает, диск переполнен или занят, а иногда даже флешку выдернули в самый неподходящий момент.
Поэтому методы чтения и записи файлов всегда требуют от нас готовности обработать такие ситуации. Делается это через try-catch:
import java.nio.file.*;
import java.util.List;
import java.io.IOException;
public class SafeReadExample {
public static void main(String[] args) {
Path path = Paths.get("todo.txt");
try {
List<String> lines = Files.readAllLines(path);
System.out.println("Содержимое файла:");
for (String line : lines) {
System.out.println(line);
}
} catch (IOException ex) {
System.out.println("Ошибка при чтении файла: " + ex.getMessage());
}
}
}
Зачем всё это?
try-catch превращает потенциальный крах программы в контролируемую ситуацию. Если файла нет — мы спокойно сообщим пользователю. Если система не даёт доступ — укажем на проблему. А если диск исчезнет посреди операции, программа не рухнет с жутким стек-трейсом, а хотя бы предупредит нормальным сообщением. Иначе говоря: IOException — это не враг, а просто способ Java сказать нам: «эй, тут снаружи что-то пошло не так, придумай, что делать дальше».
4. Практические примеры
Прочитать файл и вывести содержимое
import java.nio.file.*;
import java.util.List;
import java.io.IOException;
public class PrintFileExample {
public static void main(String[] args) {
Path path = Paths.get("notes.txt");
try {
if (!Files.exists(path)) {
System.out.println("Файл notes.txt не найден.");
return;
}
List<String> lines = Files.readAllLines(path, java.nio.charset.StandardCharsets.UTF_8);
System.out.println("Содержимое файла notes.txt:");
for (String line : lines) {
System.out.println(line);
}
} catch (IOException e) {
System.out.println("Ошибка при чтении файла: " + e.getMessage());
}
}
}
Записать строку в новый файл
import java.nio.file.*;
import java.nio.charset.StandardCharsets;
import java.io.IOException;
public class WriteFileExample {
public static void main(String[] args) {
Path path = Paths.get("greeting.txt");
String content = "Добро пожаловать в мир Java IO!";
try {
Files.write(path, content.getBytes(StandardCharsets.UTF_8));
System.out.println("Строка записана в файл greeting.txt");
} catch (IOException e) {
System.out.println("Ошибка при записи файла: " + e.getMessage());
}
}
}
Записать список строк в файл
import java.nio.file.*;
import java.nio.charset.StandardCharsets;
import java.util.List;
import java.io.IOException;
public class WriteListExample {
public static void main(String[] args) {
Path path = Paths.get("shopping.txt");
List<String> items = List.of("Хлеб", "Молоко", "Сыр");
try {
Files.write(path, items, StandardCharsets.UTF_8);
System.out.println("Список покупок записан в shopping.txt");
} catch (IOException e) {
System.out.println("Ошибка при записи файла: " + e.getMessage());
}
}
}
5. Кратко о потоках
Методы Files.readAllBytes, Files.readAllLines, Files.write — это «всё и сразу»: либо весь файл в память, либо всё содержимое сразу на диск. Для небольших файлов это удобно, а для больших — не очень (можно получить OutOfMemoryError или просто «зависнуть» на чтении гигабайтных логов).
Для работы с большими файлами или построчного чтения используют потоки:
- Для текста: BufferedReader, BufferedWriter
- Для байтов: InputStream, OutputStream
Подробнее о потоках мы поговорим в следующих лекциях, но вот маленький тизер:
import java.nio.file.*;
import java.io.*;
public class BufferedReaderExample {
public static void main(String[] args) {
Path path = Paths.get("bigfile.txt");
try (BufferedReader reader = Files.newBufferedReader(path)) {
String line;
while ((line = reader.readLine()) != null) {
// Обрабатываем строку
System.out.println(line);
}
} catch (IOException e) {
System.out.println("Ошибка при чтении файла: " + e.getMessage());
}
}
}
6. Полезные нюансы
- Перезапись файла: Все методы Files.write по умолчанию перезаписывают файл. Если нужно добавить в конец, используйте дополнительные опции (об этом — в следующей лекции).
- Кодировка: Всегда указывайте кодировку явно, если работаете с не-ASCII текстом. Это избавит от «кракозябр» на выходе. Хороший выбор — StandardCharsets.UTF_8.
- Пути: Используйте Paths.get(...) для кроссплатформенности. Никогда не хардкодьте слэши (/ или \) вручную.
- Имена файлов: Не используйте в имени файла запрещённые символы (?, *, :, <, >, | и т.д.), особенно если ваш код должен работать на Windows.
7. Типичные ошибки при чтении и записи файлов
Ошибка №1: Не обрабатывается IOException. Очень часто новички пишут Files.readAllLines(path) без try-catch, и при первой же проблеме (файл не найден, нет прав, повреждён диск) программа падает. Всегда обрабатывайте исключения!
Ошибка №2: Работа с большими файлами через readAllBytes/readAllLines. Если файл весит сотни мегабайт или гигабайты, попытка загрузить его целиком может «убить» вашу программу. Для таких случаев используйте потоки (BufferedReader).
Ошибка №3: Не указана кодировка при записи/чтении текста. Если не указать кодировку, на разных компьютерах и ОС результат может отличаться. Особенно часто проблема проявляется с кириллицей. Используйте StandardCharsets.UTF_8 или нужную вам кодировку явно.
Ошибка №4: Ожидание, что File/Path — это сам файл. Класс File или Path — это только «ярлык» на файл, а не сам файл. Создав объект, вы не создаёте файл на диске. Чтобы создать файл — используйте методы Files.createFile, Files.write и так далее.
Ошибка №5: Не закрывается поток (если используете потоки). Если вы используете потоки вручную (например, BufferedReader), обязательно закрывайте их (лучше через try-with-resources). Иначе файл может остаться «залоченным» и недоступным для других программ.
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ