Skip to content
Java io 7 min read

DataInputStream

When you need to read typed data — integers, doubles, booleans, strings — from a binary file or network socket, casting raw bytes manually is tedious and error-prone. DataInputStream gives you named readInt(), readDouble(), readUTF(), and other methods that handle the byte-to-type conversion for you, always in a portable big-endian format.

What DataInputStream Does

DataInputStream is part of java.io and extends FilterInputStream, so it wraps any existing InputStream and layers a typed reading API on top. It implements the DataInput interface, which defines the contract shared with RandomAccessFile.

Its primary design goal is to be the read counterpart to DataOutputStream. Data written with DataOutputStream is guaranteed to be readable by DataInputStream, regardless of the operating system or CPU architecture, because both use big-endian byte order (most significant byte first).

Note: DataInputStream is strictly for binary data written in Java’s portable format. It is not suitable for reading arbitrary binary files produced by C programs or other languages, which may use little-endian order or different struct layouts.

Constructors

There is only one constructor:

DataInputStream(InputStream in)

You pass any InputStream — a FileInputStream, a socket’s input stream, a ByteArrayInputStream, or anything else. For file-based I/O, wrapping with a BufferedInputStream first is a good idea to reduce system calls:

DataInputStream dis = new DataInputStream(
        new BufferedInputStream(new FileInputStream("data.bin")));

Key Read Methods

MethodReturnsBytes Read
readBoolean()boolean1
readByte()byte1
readShort()short2
readChar()char2
readInt()int4
readLong()long8
readFloat()float4
readDouble()double8
readUTF()Stringvariable
readFully(byte[])voidbyte[].length

All methods throw EOFException (a subclass of IOException) if the stream ends before enough bytes are available — so you always know the difference between “end of data” and a genuine read error.

Basic Example: Write and Read Primitives

The most common pattern is to pair DataOutputStream with DataInputStream. Here is a self-contained example that writes several primitive values to a byte array in memory and then reads them back:

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;

public class DataStreamDemo {
    public static void main(String[] args) throws IOException {
        // --- Write ---
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        try (DataOutputStream dos = new DataOutputStream(baos)) {
            dos.writeInt(42);
            dos.writeDouble(3.14);
            dos.writeBoolean(true);
            dos.writeUTF("Hello, DataInputStream!");
        }

        // --- Read ---
        byte[] bytes = baos.toByteArray();
        try (DataInputStream dis = new DataInputStream(
                new ByteArrayInputStream(bytes))) {

            int    i = dis.readInt();
            double d = dis.readDouble();
            boolean b = dis.readBoolean();
            String s = dis.readUTF();

            System.out.println("int:     " + i);
            System.out.println("double:  " + d);
            System.out.println("boolean: " + b);
            System.out.println("String:  " + s);
        }
    }
}

Output:

int:     42
double:  3.14
boolean: true
String:  Hello, DataInputStream!

Warning: You must read values back in exactly the same order they were written. If you call readDouble() where an int was written, you will get garbage data with no error — the stream has no type metadata, only raw bytes.

Reading from a File

In practice, you often write a binary file once and read it many times. Here is a pattern for reading an existing binary file that was created by DataOutputStream:

import java.io.BufferedInputStream;
import java.io.DataInputStream;
import java.io.FileInputStream;
import java.io.IOException;

public class ReadBinaryFile {
    public static void main(String[] args) throws IOException {
        try (DataInputStream dis = new DataInputStream(
                new BufferedInputStream(
                        new FileInputStream("scores.dat")))) {

            // Assume the file contains: player name (UTF), score (int), time (double)
            while (dis.available() > 0) {
                String name  = dis.readUTF();
                int    score = dis.readInt();
                double time  = dis.readDouble();
                System.out.printf("%-15s %5d  %.2fs%n", name, score, time);
            }
        }
    }
}

Tip: available() tells you how many bytes can be read without blocking, but it is not a reliable end-of-stream check for all stream types (especially network sockets). For files, it works well. For robust code, catch EOFException instead and treat it as normal termination.

readFully() — Guaranteed Reads

Ordinary read(byte[]) is not guaranteed to fill the entire array in one call (the OS may return fewer bytes). readFully(byte[] b) keeps reading until the array is completely filled or throws EOFException:

import java.io.DataInputStream;
import java.io.FileInputStream;
import java.io.IOException;

public class ReadFullyDemo {
    public static void main(String[] args) throws IOException {
        try (DataInputStream dis = new DataInputStream(
                new FileInputStream("header.bin"))) {

            byte[] header = new byte[16];
            dis.readFully(header); // guaranteed to fill all 16 bytes
            System.out.println("Header read: " + header.length + " bytes");
        }
    }
}

Output:

Header read: 16 bytes

This is especially useful when you need to parse fixed-size binary headers — for example, image file headers or custom protocol frames.

readUTF() — Modified UTF-8

readUTF() reads a string encoded in Java’s modified UTF-8 format. The encoding stores a 2-byte length prefix (number of bytes, not characters) followed by the encoded characters. This is not standard UTF-8 — it encodes null characters (\u0000) as two bytes instead of one, so the resulting byte sequence is never zero-padded.

// Writing
dos.writeUTF("café");   // writes 2-byte length + encoded bytes

// Reading back
String s = dis.readUTF(); // "café"

You should only use readUTF() to read strings written by writeUTF(). For general UTF-8 decoding from external sources, use InputStreamReader with StandardCharsets.UTF_8.

readUnsignedByte() and readUnsignedShort()

Java does not have unsigned primitive types, but binary protocols often use unsigned values. DataInputStream provides two helpers:

import java.io.DataInputStream;
import java.io.ByteArrayInputStream;
import java.io.IOException;

public class UnsignedDemo {
    public static void main(String[] args) throws IOException {
        byte[] data = {(byte) 200, (byte) 0xFF, (byte) 0x80};
        try (DataInputStream dis = new DataInputStream(
                new ByteArrayInputStream(data))) {

            int ub1 = dis.readUnsignedByte();  // 200  (not -56)
            int ub2 = dis.readUnsignedByte();  // 255  (not -1)
            int ub3 = dis.readUnsignedByte();  // 128  (not -128)

            System.out.println(ub1 + ", " + ub2 + ", " + ub3);
        }
    }
}

Output:

200, 255, 128

readUnsignedByte() returns an int in the range 0–255. readUnsignedShort() returns an int in the range 0–65535. These are pure convenience — they just mask the sign bits that Java’s signed types would otherwise extend.

Under the Hood

Big-Endian Byte Order

When DataInputStream.readInt() executes, it reads exactly 4 bytes from the underlying stream and reassembles them as:

value = (b0 << 24) | (b1 << 16) | (b2 << 8) | b3

This is network byte order (big-endian), consistent with java.net and many binary protocols. If you need little-endian reading (e.g., for Windows BMP or WAV files), use java.nio.ByteBuffer with ByteOrder.LITTLE_ENDIAN instead.

No Buffering of Its Own

DataInputStream performs no buffering. Every call to readInt() triggers four read() calls on the underlying stream. On an unbuffered FileInputStream, those become four separate system calls. That is why wrapping in a BufferedInputStream before passing to DataInputStream is almost always worthwhile:

FileInputStream  →  BufferedInputStream  →  DataInputStream
    (OS layer)         (memory buffer)        (typed API)

FilterInputStream Inheritance

DataInputStream extends FilterInputStream, which stores the wrapped stream in a protected field called in. All read() calls delegate to in.read(...). This is the classic Decorator pattern from the Gang of Four — each wrapper layer adds behaviour without changing the interface.

Thread Safety

DataInputStream is not thread-safe. If multiple threads share one instance, interleaved reads will corrupt the data. Each thread should use its own stream instance, or access must be externally synchronized.

Handling EOFException

When reading a stream of unknown length, the idiomatic Java pattern is to catch EOFException:

import java.io.DataInputStream;
import java.io.EOFException;
import java.io.FileInputStream;
import java.io.IOException;

public class EOFHandling {
    public static void main(String[] args) {
        try (DataInputStream dis = new DataInputStream(
                new FileInputStream("numbers.dat"))) {

            while (true) {
                int value = dis.readInt(); // throws EOFException at end
                System.out.println(value);
            }
        } catch (EOFException e) {
            System.out.println("Finished reading.");
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

EOFException is a subclass of IOException, so you can also let the outer catch handle it — but catching it separately makes the intent clear.

DataInputStream vs Other Stream Classes

FeatureDataInputStreamBufferedInputStreamObjectInputStream
Reads primitivesYesNoYes
Reads objectsNoNoYes
BufferingNo (delegate)YesInternal
Use caseTyped binary dataRaw byte performanceFull object graphs

If you need to read entire serialized Java objects, see Object Streams. For reading text line by line, BufferedReader is the right tool.

Last updated June 13, 2026
Was this helpful?