OutputStream.write: примеры (JAVA)
OutputStream.write(byte[] b): voidОбщее описание метода
В Java метод OutputStream.write(byte[] b) принадлежит классу java.io.OutputStream и служит для записи всего содержимого переданного массива байт в целевой поток. Метод объявлен как public void write(byte[] b) throws IOException. Внутренняя реализация по умолчанию вызывает write(b, 0, b.length), а последняя в базовой реализации выполняет побайтовую запись через write(int b), поэтому конкретные подклассы часто переопределяют варианты с массивом для эффективности.
Аргументы и возвращаемое значение
Аргументы:
- byte[] b - массив байт, данные из которого будут записаны. Передача
nullприводит кNullPointerException.
Возвращаемое значение: метод возвращает void. Индикация ошибок производится через исключения.
Исключения и поведение
- IOException - общая ошибка ввода-вывода при записи в поток (например, закрытый поток, ошибка устройства, сетевое прерывание).
- NullPointerException - если передан
nullвместо массива.
Когда применяется
Метод применяется, когда требуется записать последовательность байт целиком: запись в файл, сетевой сокет, буфер в памяти (ByteArrayOutputStream) и т. п. В случаях, когда требуется записать только часть массива, используется перегруженный метод write(byte[] b, int off, int len). Для одиночного байта доступен write(int b).
Короткие примеры использования
Примеры показывают разные варианты и ожидаемые результаты.
1) Запись в файл через FileOutputStream
import java.io.*;
public class Example1 {
public static void main(String[] args) throws IOException {
byte[] data = "Привет".getBytes("UTF-8");
try (OutputStream os = new FileOutputStream("out1.txt")) {
os.write(data);
}
}
}
Результат: в файле out1.txt появится текст "Привет" в кодировке UTF-8.
2) Запись в ByteArrayOutputStream (в памяти)
import java.io.*;
public class Example2 {
public static void main(String[] args) throws IOException {
byte[] data = {65, 66, 67}; // A B C
try (ByteArrayOutputStream baos = new ByteArrayOutputStream()) {
baos.write(data);
byte[] result = baos.toByteArray();
System.out.println(java.util.Arrays.toString(result));
}
}
}
Результат: [65, 66, 67]
3) Использование с сокетом (синхронная блокирующая запись)
import java.net.*;
import java.io.*;
// Отправка простого сообщения на локальный эхо-сервер
try (Socket s = new Socket("localhost", 7);
OutputStream os = s.getOutputStream()) {
os.write("ping".getBytes());
}
Результат: отправлены байты 'p','i','n','g' в сокет; в случае эхо-сервера будет получен ответ.
4) Ошибка при передаче null
byte[] arr = null;
OutputStream os = new ByteArrayOutputStream();
os.write(arr); // вызовет NullPointerException
Результат: java.lang.NullPointerException
Похожие методы в Java
- write(byte[] b, int off, int len) - запись части массива. Предпочтительнее для частичного вывода без копирования в новый массив.
- write(int b) - запись одного байта. Подходит для побайтовой обработки, но медленнее для больших объемов.
- DataOutputStream.writeBytes(String s) и похожие методы класса DataOutputStream - для записи примитивных типов с контролем формата; используются при необходимости писать типизированные данные.
- BufferedOutputStream (обёртка) - увеличивает производительность при частых вызовах записи за счет буферизации. Рекомендуется, когда поток медленный (файл, сеть).
Выбор зависит от задач: для больших блоков предпочтительна запись массива (или переопределённый метод), для частичных фрагментов - write(b, off, len), для формализованных бинарных форматов - DataOutputStream, для повышения скорости - обёртки-буферы.
Аналоги в других языках и отличия
Краткие эквиваленты с примерами и результатами.
PHP - fwrite / file_put_contents
// fwrite
$fp = fopen('out_php.txt', 'wb');
fwrite($fp, "Привет");
fclose($fp);
// file_put_contents
file_put_contents('out2.txt', "Hello");
Результат: файлы out_php.txt и out2.txt содержат записанные байты.
JavaScript (Node.js) - fs.write / fs.writeSync / stream.write
const fs = require('fs');
fs.writeFileSync('out_js.txt', Buffer.from('Hello'));
// поток
const ws = fs.createWriteStream('out_js2.txt');
ws.write(Buffer.from([65,66,67]));
ws.end();
Результат: созданные файлы содержат соответствующие байты.
Python - file.write / io.BufferedWriter.write
# бинарная запись
with open('out_py.bin', 'wb') as f:
f.write(b'Hello')
# BytesIO
import io
b = io.BytesIO()
b.write(b'ABC')
print(b.getvalue())
Результат: out_py.bin содержит биты 'Hello'; вывод: b'ABC'
C# - Stream.Write(byte[], int, int)
using (var fs = new FileStream("out_cs.bin", FileMode.Create))
{
byte[] data = new byte[] {65,66,67};
fs.Write(data, 0, data.Length);
}
Результат: файл out_cs.bin содержит ABC в байтах.
Go - io.Writer.Write([]byte)
package main
import (
"os"
)
func main() {
os.WriteFile("out_go.txt", []byte("Hello"), 0644)
}
Результат: out_go.txt с текстом Hello.
Lua - io.write (обычно работает со строками)
local f = io.open('out_lua.txt','wb')
f:write('Hello')
f:close()
Результат: файл out_lua.txt с текстом Hello.
Kotlin - использует те же потоки, что и Java
val data = "Привет".toByteArray()
File("out_kt.txt").writeBytes(data)
Результат: файл out_kt.txt с UTF-8 представлением строки.
Отличия от Java: в большинстве языков запись возвращает количество записанных байт или бросает исключение; в Go возвращается (int, error), в Java метод возвращает void и сигнализирует только исключением. В некоторых языках (PHP, Node.js) доступны асинхронные варианты записи по умолчанию.
Типичные ошибки при использовании
- NullPointerException: вызов
write(null). Пример:
OutputStream os = new ByteArrayOutputStream();
byte[] arr = null;
os.write(arr); // NullPointerException
Результат: java.lang.NullPointerException
- IOException при закрытом или разорванном потоке. Пример: запись в сокет после закрытия.
Socket s = new Socket("example.com", 80);
OutputStream os = s.getOutputStream();
s.close();
os.write(new byte[]{1,2,3}); // IOException: ошибка записи
Результат: java.io.IOException: Socket is closed (или подобная ошибка)
- Непонимание буферизации и flush: данные могут остаться в буфере BufferedOutputStream или в протоколах; отсутствие явного flush/close приводит к задержкам отдачи.
try (BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("f.txt"))) {
bos.write("Hi".getBytes());
// без bos.flush() или закрытия некоторое содержимое может не попасть в файл немедленно
}
Результат: при нормальном завершении ресурса данные попадут в файл, но при аварийном завершении возможна потеря данных.
- Производительность при побайтовой записи: использование базовой реализации write(byte[]) может быть медленным, если подкласс не переопределил метод - запись будет вызывать write(int) в цикле.
Рекомендации: проверять на null, обрабатывать IOException, использовать буферизацию и переопределённые методы потоков для больших передач.
Изменения в реализации метода
Метод write(byte[] b) присутствует в классе OutputStream с ранних версий JDK (с самого начала API ввода-вывода). За последние релизы Java существенных изменений в сигнатуре или семантике данного метода не происходило. Важный нюанс: базовая реализация вызывает write(b, 0, b.length), а write(b, off, len) в своей стандартной реализации перебирает байты и вызывает write(int), поэтому оптимизация остаётся за подклассами (например, FileOutputStream, ByteArrayOutputStream и сетевые потоки переопределяют методы для эффективности).
В новых версиях рекомендуется по-прежнему использовать буферизированные или специализированные реализации для повышения производительности; API метода не изменялся.
Расширенные и нестандартные примеры
Несколько подробных сценариев применения с пояснениями.
1) Буферизация и частичная запись (с контролем оффсета)
import java.io.*;
public class Adv1 {
public static void main(String[] args) throws Exception {
byte[] big = new byte[1024];
for (int i = 0; i < big.length; i++) big[i] = (byte) (i % 256);
try (OutputStream fos = new BufferedOutputStream(new FileOutputStream("adv1.bin"))) {
// Записать только половину массива
fos.write(big, 0, 512);
fos.flush();
}
}
}
Результат: файл adv1.bin длиной 512 байт, содержащий первые 512 значений последовательности.
2) Запись с компрессией (GZIPOutputStream)
import java.io.*;
import java.util.zip.GZIPOutputStream;
public class Adv2 {
public static void main(String[] args) throws Exception {
byte[] data = "Очень длинный текст...".getBytes("UTF-8");
try (OutputStream os = new GZIPOutputStream(new FileOutputStream("adv2.gz"))) {
os.write(data);
}
}
}
Результат: adv2.gz содержит сжатую версию данных; декомпрессия возвращает исходные байты.
3) Запись в канал NIO через адаптер OutputStream
import java.io.*;
import java.nio.channels.Channels;
import java.nio.channels.WritableByteChannel;
public class Adv3 {
public static void main(String[] args) throws Exception {
byte[] data = "Data via channel".getBytes();
try (FileOutputStream fos = new FileOutputStream("adv3.txt")) {
WritableByteChannel channel = Channels.newChannel(fos);
// канал имеет другой API, но можно использовать адаптеры для совместимости
fos.write(data);
}
}
}
Результат: adv3.txt с текстом "Data via channel". Примечание: для высокопроизводительных сценариев предпочтительнее работать напрямую с каналами и ByteBuffer.
4) Асинхронная отправка с контролем блокировки (в сетевых клиентах)
// Пример демонстрирует идею: проверка доступности записи и управление таймаутом
Socket socket = new Socket();
socket.connect(new java.net.InetSocketAddress("example.com", 80), 2000);
socket.setSoTimeout(3000);
try (OutputStream os = socket.getOutputStream()) {
byte[] payload = new byte[1_000_000];
// возможно блокирование при записи большого массива; при необходимости разбить на чанки
int chunk = 64 * 1024;
for (int i = 0; i < payload.length; i += chunk) {
int len = Math.min(chunk, payload.length - i);
os.write(payload, i, len);
// можно добавить паузу или проверку состояния
}
}
Результат: данные отправляются пакетами; подход помогает избежать долгой блокирующей операции при одном крупном write.
5) Запись в RandomAccessFile через получение OutputStream (через FileDescriptor)
import java.io.*;
public class Adv5 {
public static void main(String[] args) throws Exception {
RandomAccessFile raf = new RandomAccessFile("adv5.bin", "rw");
raf.seek(100); // смещение записи
raf.write(new byte[]{1,2,3,4});
raf.close();
}
}
Результат: в файле adv5.bin четыре байта записаны начиная с позиции 100.
Комментарий: в сложных сценариях рекомендуется выбирать подходящий примитив (буфер, канал, компрессия, фрагментация) для контроля производительности и надежности. Переопределение методов записи в собственных реализациях OutputStream позволяет добиться лучшей эффективности, чем базовая побайтовая реализация.