杰瑞科技汇

Java InputStream in,如何正确使用与关闭?

Of course! The java.io.InputStream is a fundamental and cornerstone class in Java's I/O (Input/Output) library. Let's break it down comprehensively, from what it is to how you use it.

Java InputStream in,如何正确使用与关闭?-图1
(图片来源网络,侵删)

What is InputStream?

At its core, an InputStream is an abstract class that represents an input stream of bytes. It's a fundamental abstraction in Java for reading data from a source.

Think of it like a one-way pipe. You can pull bytes out of one end, but you can't push bytes back in. The source of these bytes can be almost anything:

  • A file on your hard drive (FileInputStream)
  • A network connection (Socket.getInputStream())
  • A block of memory in your program (ByteArrayInputStream)
  • A string (StringInputStream - though less common now)
  • The system's standard input (usually your keyboard, System.in)

Because it's an abstract class, you can't create an instance of InputStream directly. You must use one of its many concrete subclasses.


Key Characteristics

  • Byte-Oriented: It reads data in raw bytes (8 bits). This means it's not directly aware of characters, numbers, or objects. It just reads byte values.
  • Low-Level: It's a low-level API. You are responsible for managing the "cursor" or pointer through the stream. You read a byte, and the pointer moves to the next byte.
  • Sequential Access: You read data sequentially, from the beginning to the end. You can't easily jump around (though some subclasses like BufferedInputStream and FileInputStream support this via their skip() or reset() methods).
  • Resource Management: This is CRITICAL. Any stream that opens a resource (like a file or network connection) must be closed properly to release that resource. Failure to do so can lead to resource leaks (e.g., files being locked, network ports remaining open).

Core Methods (The API)

Here are the most important methods you'll use with any InputStream:

Java InputStream in,如何正确使用与关闭?-图2
(图片来源网络,侵删)
Method Signature Description
int read() Reads one byte from the stream and returns it as an integer (0-255). If the end of the stream is reached, it returns -1.
int read(byte[] b) Reads up to b.length bytes from the stream into the byte array b. It returns the number of bytes actually read, or -1 if the end of the stream is reached.
int read(byte[] b, int off, int len) A more flexible version of the above. Reads up to len bytes into the sub-array of b starting at index off. Returns the number of bytes read or -1.
long skip(long n) Skips over and discards n bytes from the input stream. Returns the actual number of bytes skipped.
int available() Returns the estimated number of bytes that can be read without blocking. Useful for non-blocking I/O.
void close() Closes the stream and releases any system resources associated with it (like a file handle). This must be called in a finally block or try-with-resources.
synchronized void mark(int readlimit) Marks the current position in the stream. A subsequent call to reset() will reposition the stream to this point. The readlimit argument tells the stream how many bytes it can read before the mark becomes invalid.
boolean markSupported() Tests if the input stream supports the mark() and reset() methods. FileInputStream does not, but BufferedInputStream does.
synchronized void reset() Repositions the stream to the position last marked by mark().
byte[] readAllBytes() (Java 9+) Reads all remaining bytes from the stream into a byte array.
byte[] readNBytes(int len) (Java 9+) Reads up to len bytes from the stream into a byte array.

Hierarchy of Common Subclasses

Understanding the hierarchy is key to knowing which stream to use for which job.

java.lang.Object
   java.io.InputStream
      java.io.ByteArrayInputStream
      java.io.FileInputStream
      java.io.FilterInputStream
         java.io.BufferedInputStream
         java.io.DataInputStream
         java.io.LineNumberInputStream
         java.io.PushbackInputStream
      java.io.ObjectInputStream
      java.io.PipedInputStream
      java.io.SequenceInputStream
      java.io.StringBufferInputStream (Deprecated)

Key Subclasses Explained:

  • FileInputStream: Reads raw bytes from a file.
  • BufferedInputStream: A decorator/wrapper class. It doesn't read from a source directly. Instead, it wraps another InputStream (like a FileInputStream) and adds a buffer. This makes reading much more efficient because it reads large chunks of data from the underlying stream into memory, so subsequent requests are served from the fast memory buffer instead of the slow disk/network.
  • DataInputStream: Another decorator. It wraps another InputStream and allows you to read Java primitive data types (like int, double, boolean) and UTF-8 strings in a machine-independent way. This is crucial for reading binary files created by other programs or network protocols.
  • ByteArrayInputStream: Reads bytes from a byte array in memory. Useful for testing or when data is already in memory.
  • ObjectInputStream: Used with ObjectOutputStream to read objects that have been serialized into a byte stream.

How to Use It: Code Examples

There are two primary ways to handle streams: the traditional try-finally block and the modern try-with-resources statement.

Example 1: Reading a File (The Old Way - try-finally)

This is the classic way, but it's verbose and error-prone if you're not careful.

import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
public class InputStreamOldWay {
    public static void main(String[] args) {
        InputStream inputStream = null; // Declare outside the try block
        try {
            // 1. Create the stream
            inputStream = new FileInputStream("my-file.txt");
            int data;
            // 2. Read one byte at a time
            // read() returns -1 when the end of the stream is reached
            while ((data = inputStream.read()) != -1) {
                // Cast the int to a char to print it
                System.out.print((char) data);
            }
        } catch (IOException e) {
            System.err.println("An error occurred: " + e.getMessage());
        } finally {
            // 3. ALWAYS close the stream in the finally block
            if (inputStream != null) {
                try {
                    inputStream.close();
                } catch (IOException e) {
                    System.err.println("Error closing the stream: " + e.getMessage());
                }
            }
        }
    }
}

Example 2: Reading a File (The Modern Way - try-with-resources)

This is the recommended approach since Java 7. It automatically closes the resource when the try block is exited, even if an exception occurs. It's cleaner and safer.

Java InputStream in,如何正确使用与关闭?-图3
(图片来源网络,侵删)
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
public class InputStreamModernWay {
    public static void main(String[] args) {
        // The try-with-resources statement automatically closes the InputStream
        try (InputStream inputStream = new FileInputStream("my-file.txt")) {
            int data;
            while ((data = inputStream.read()) != -1) {
                System.out.print((char) data);
            }
        } catch (IOException e) {
            // If an error occurs, the stream is still closed automatically
            System.err.println("An error occurred while reading the file: " + e.getMessage());
        }
        // No need for a finally block or inputStream.close() here!
    }
}

Example 3: Using a BufferedInputStream for Efficiency

This is a very common and practical pattern. You wrap a low-level stream like FileInputStream with a high-performance one like BufferedInputStream.

import java.io.BufferedInputStream;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
public class BufferedExample {
    public static void main(String[] args) {
        // The try-with-resources statement can manage multiple resources
        try (
            // 1. Create the low-level stream to the file
            InputStream fileStream = new FileInputStream("large-file.txt");
            // 2. Wrap it in a buffered stream for performance
            InputStream bufferedStream = new BufferedInputStream(fileStream)
        ) {
            byte[] buffer = new byte[1024]; // A 1KB buffer
            int bytesRead;
            // 3. Read in chunks, not one byte at a time
            while ((bytesRead = bufferedStream.read(buffer)) != -1) {
                // Process the chunk of data (e.g., write it to another stream)
                System.out.write(buffer, 0, bytesRead);
            }
        } catch (IOException e) {
            System.err.println("An error occurred: " + e.getMessage());
        }
分享:
扫描分享到社交APP
上一篇
下一篇