DataOutputStream.writeInt: примеры (JAVA)

Метод DataOutputStream.writeInt: назначение и сценарии
Раздел: Потоки ввода-вывода (Streams) байтовые
DataOutputStream.writeInt(int v): void

Описание метода

Метод DataOutputStream.writeInt записывает 32-битное целое значение (тип int) в поток в порядке байт big-endian (старший байт первым). Метод реализует контракт интерфейса DataOutput и возвращаемого значения не имеет (возвращает void), но может выбросить исключение при ошибке ввода-вывода.

Сигнатура метода в Java:

public final void writeInt(int v) throws IOException

Аргументы:

  • v - значение типа int, которое будет разбито на четыре байта и записано в поток.

Возвращаемое значение:

  • Метод не возвращает значения; в случае успешной записи управление возвращается вызывающему коду.

Исключения и поведение:

  • IOException - при ошибках ввода-вывода (например, поток закрыт или проблемы с носителем).

Особенности:

  • Запись всегда в порядке big-endian, независимо от эндianness платформы.
  • Метод записывает ровно 4 байта для каждого вызова.
  • Для потоков, требующих позиционирования (перезаписи заголовков), DataOutputStream сам по себе позиционирования не предоставляет; в таких сценариях используются другие API (например, RandomAccessFile или FileChannel).

Краткие примеры использования

Запись в массив байтов с выводом результата в шестнадцатеричном виде.

import java.io.*;

ByteArrayOutputStream baos = new ByteArrayOutputStream();
DataOutputStream dos = new DataOutputStream(baos);
try {
    dos.writeInt(0x01020304);
    dos.flush();
} finally {
    dos.close();
}
byte[] bytes = baos.toByteArray();
for (byte b : bytes) System.out.printf("%02X ", b);
01 02 03 04 

Запись нескольких целых в файл и чтение назад с помощью DataInputStream.

import java.io.*;

try (DataOutputStream dos = new DataOutputStream(new FileOutputStream("ints.bin"))) {
    dos.writeInt(123456789);
    dos.writeInt(-1);
}

try (DataInputStream dis = new DataInputStream(new FileInputStream("ints.bin"))) {
    System.out.println(dis.readInt());
    System.out.println(dis.readInt());
}
123456789
-1

Использование поверх сокета (передача 32-битного кода команды).

import java.net.*;
import java.io.*;

Socket s = new Socket("example.com", 12345);
try (DataOutputStream dos = new DataOutputStream(s.getOutputStream())) {
    dos.writeInt(42); // отправка кода
}
(нет вывода в стандартный поток; байты отправлены по сети)

Похожие средства в Java

Несколько альтернатив и смежных API:

  • ByteBuffer.putInt (java.nio) - обеспечивает контроль порядка байт через order(ByteOrder). Предпочтительнее для NIO и работы с буферами/мэппингом.
  • RandomAccessFile.writeInt - похожий метод с поддержкой произвольного позиционирования в файле; удобен при необходимости перезаписи заголовков.
  • DataOutputStream.writeLong / writeShort - для других размеров целых; используются при записи 64- или 16-битных значений.
  • ObjectOutputStream - для сериализации объектов; не подходит для простого бинарного протокола из-за добавочных метаданных.

Выбор зависит от требований: для простых последовательных записей в поток подходит DataOutputStream; для контроля порядка байт и работы с памятью подойдут ByteBuffer и FileChannel; для перезаписи данных - RandomAccessFile.

Аналоги в других языках

Короткие примеры записи 32-битного целого в big-endian и отличия.

PHP (pack, big-endian):

$bin = pack('N', 16909060); // 0x01020304
echo bin2hex($bin);
01020304

JavaScript в браузере (ArrayBuffer / DataView):

const buf = new ArrayBuffer(4);
const dv = new DataView(buf);
dv.setInt32(0, 0x01020304, false); // false = big-endian
const arr = new Uint8Array(buf);
console.log(Array.from(arr).map(b => b.toString(16).padStart(2,'0')).join(' '));
01 02 03 04

Node.js Buffer:

const buf = Buffer.alloc(4);
buf.writeInt32BE(0x01020304, 0);
console.log(buf.toString('hex'));
01020304

Python (struct):

import struct
b = struct.pack('>i', 0x01020304)
print(b.hex())
01020304

C# (BinaryWriter) - по умолчанию little-endian; для big-endian требуется переворот:

using (var ms = new MemoryStream()) {
  using (var bw = new BinaryWriter(ms)) {
    int v = 0x01020304;
    var bytes = BitConverter.GetBytes(v);
    if (BitConverter.IsLittleEndian) Array.Reverse(bytes);
    bw.Write(bytes);
    Console.WriteLine(BitConverter.ToString(ms.ToArray()).Replace("-"," "));
  }
}
01 02 03 04

Go (encoding/binary):

import (
  "bytes"
  "encoding/binary"
  "fmt"
)

buf := new(bytes.Buffer)
_ = binary.Write(buf, binary.BigEndian, int32(0x01020304))
fmt.Printf("% X\n", buf.Bytes())
01 02 03 04

Lua 5.3+ (string.pack):

s = string.pack('>i4', 0x01020304)
for i = 1, #s do io.write(string.format('%02X ', s:byte(i))) end
01 02 03 04

Отличия от Java:

  • Некоторые API по умолчанию используют порядок байт платформы (например, BinaryWriter в C#), поэтому может потребоваться явный переворот.
  • В языках с встроенной упаковкой (Python, PHP, Go) управление порядком байт реализовано прямо в функциях, что упрощает переносимость.
  • В Java DataOutputStream фиксированно использует big-endian; для little-endian требуется дополнительная обёртка или ByteBuffer с соответствующим порядком.

Типичные ошибки при использовании

Наиболее распространённые ошибки и их проявления.

  • Запись в закрытый поток: после закрытия поток бросит IOException.
ByteArrayOutputStream baos = new ByteArrayOutputStream();
DataOutputStream dos = new DataOutputStream(baos);
dos.close();
dos.writeInt(1); // IOException
java.io.IOException: Stream closed
  • Неправильный порядок байт: если принимающая сторона ожидает little-endian, а записано big-endian, данные интерпретируются неправильно.
// Записано big-endian
DataOutputStream dos = new DataOutputStream(baos);
dos.writeInt(1); // 00 00 00 01
// Чтение с little-endian интерпретацией даст 16777216
(расхождение значений при чтении)
  • Потеря данных при неправильном приводе типов: попытка записать значение большего типа через приведение может привести к обрезанию.
long big = 0x1_0000_0000L; // 4294967296
dos.writeInt((int) big); // потеря старших бит
записаны только младшие 32 бита
  • Отсутствие буферизации: множественные мелкие вызовы записи в поток без буфера могут снизить производительность; применение BufferedOutputStream улучшает ситуацию.

Рекомендации по отладке ошибок: проверять порядок байт, не вызывать запись после закрытия, использовать try-with-resources и при необходимости проверять размер записанных данных.

Изменения за версии Java

Метод DataOutputStream.writeInt существует с ранних версий Java (Java 1.0) и не претерпевал изменений в сигнатуре или семантике. В более новых релизах происходили общие улучшения платформы ввода-вывода и оптимизации, но сам метод остаётся стабильным и не помечен как deprecated. Для функциональных расширений используются соседние API: java.nio, FileChannel, MappedByteBuffer.

Расширенные и нетипичные примеры

1) Перезапись заголовка с длиной записи: сначала резервирование места, запись блока, затем возврат и корректировка заголовка с помощью RandomAccessFile.

Пример java
import java.io.*;

try (RandomAccessFile raf = new RandomAccessFile("packet.bin", "rw")) {
    raf.writeInt(0); // место для длины
    long start = raf.getFilePointer();
    try (DataOutputStream dos = new DataOutputStream(new BufferedOutputStream(new FileOutputStream(raf.getFD())))) {
        dos.writeInt(0xDEADBEEF);
        dos.writeInt(42);
        dos.flush();
    }
    long end = raf.length();
    int length = (int)(end - start);
    raf.seek(0);
    raf.writeInt(length);
}

// Чтение для проверки
try (DataInputStream dis = new DataInputStream(new FileInputStream("packet.bin"))) {
    System.out.println(dis.readInt()); // длина блока
    System.out.printf("%08X\n", dis.readInt());
    System.out.println(dis.readInt());
}
8
DEADBEEF
42

2) Запись в little-endian через ByteBuffer и вывод результата (DataOutputStream лишь big-endian).

Пример java
import java.nio.*;

ByteBuffer bb = ByteBuffer.allocate(4).order(ByteOrder.LITTLE_ENDIAN);
bb.putInt(0x01020304);
byte[] arr = bb.array();
for (byte b: arr) System.out.printf("%02X ", b);
04 03 02 01 

3) Использование MappedByteBuffer для высокопроизводительной записи большого числа целых (memory-mapped файл).

Пример java
import java.io.*;
import java.nio.*;
import java.nio.channels.*;

try (RandomAccessFile raf = new RandomAccessFile("map.bin", "rw");
     FileChannel fc = raf.getChannel()) {
    int count = 1_000_000;
    raf.setLength((long)count * 4);
    MappedByteBuffer mb = fc.map(FileChannel.MapMode.READ_WRITE, 0, (long)count * 4);
    mb.order(ByteOrder.BIG_ENDIAN);
    for (int i = 0; i < count; i++) mb.putInt(i);
}
System.out.println("done");
done

4) Сравнение производительности: DataOutputStream vs ByteBuffer bulk - пример сбора в буфер и запись одним write.

Пример java
// Сбор в ByteBuffer для уменьшения количества системных вызовов
import java.io.*;
import java.nio.*;

int n = 100_000;
ByteBuffer bb = ByteBuffer.allocate(n * 4).order(ByteOrder.BIG_ENDIAN);
for (int i = 0; i < n; i++) bb.putInt(i);
try (FileOutputStream fos = new FileOutputStream("bulk.bin")) {
    fos.write(bb.array());
}
(файл записан быстрее при уменьшении числа write-вызовов)

5) Протокол обмена: упаковка заголовка с контрольной суммой (CRC32) и несколькими полями.

Пример java
import java.io.*;
import java.util.zip.CRC32;

ByteArrayOutputStream baos = new ByteArrayOutputStream();
try (DataOutputStream dos = new DataOutputStream(baos)) {
    dos.writeInt(0xABCD); // тип
    dos.writeInt(0); // место для длины
    dos.writeInt(12345); // payload field
    dos.flush();
}
byte[] payload = baos.toByteArray();
CRC32 crc = new CRC32();
crc.update(payload, 8, payload.length - 8); // вычислить по payload
ByteArrayOutputStream packet = new ByteArrayOutputStream();
try (DataOutputStream dos2 = new DataOutputStream(packet)) {
    dos2.writeInt(0xABCD);
    dos2.writeInt(payload.length - 8);
    dos2.write(payload, 8, payload.length - 8);
    dos2.writeInt((int) crc.getValue());
}
System.out.println(Integer.toHexString(((packet.toByteArray())[0] & 0xFF)));
(пакет собран; пример показывает способ вычисления и вставки контрольной суммы)

Комментарии к примерам: приведённые расширенные сценарии демонстрируют техники для реальных задач: предварительное резервирование длины, работа с маппингом, изменение порядка байт, уменьшение числа системных вызовов и добавление контрольных сумм.

джава DataOutputStream.writeInt function comments

En
DataOutputStream.writeInt Writes an int value to the output stream