OutputStream.write: примеры (JAVA)

Примеры и детали метода записи в OutputStream
Раздел: Потоки ввода-вывода (Streams) байтовые
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) Буферизация и частичная запись (с контролем оффсета)

Пример java
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)

Пример java
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

Пример java
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) Асинхронная отправка с контролем блокировки (в сетевых клиентах)

Пример java
// Пример демонстрирует идею: проверка доступности записи и управление таймаутом
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)

Пример java
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 позволяет добиться лучшей эффективности, чем базовая побайтовая реализация.

джава OutputStream.write function comments

En
OutputStream.write Writes b.length bytes from the specified byte array to this output stream