Java FilterInputStream Class
Last modified: April 16, 2025
The java.io.FilterInputStream
class is a base class for input stream
filters. It wraps another input stream and transforms data or provides additional
functionality. This class serves as the superclass for all filtered input streams.
FilterInputStream
itself simply overrides all methods of
InputStream
with versions that pass requests to the wrapped stream.
Subclasses should override some methods to provide filtering behavior. This class
is rarely used directly.
FilterInputStream Class Overview
FilterInputStream
extends InputStream
and maintains a
reference to an underlying input stream. All operations are delegated to this
stream. Subclasses can modify the data or behavior during these operations.
public class FilterInputStream extends InputStream { protected volatile InputStream in; protected FilterInputStream(InputStream in); public int read(); public int read(byte[] b); public int read(byte[] b, int off, int len); public long skip(long n); public int available(); public void close(); public synchronized void mark(int readlimit); public synchronized void reset(); public boolean markSupported(); }
The code above shows the structure of FilterInputStream
. The
protected in
field holds the wrapped stream. All methods delegate
to this stream. Subclasses typically override some methods to add functionality.
Creating a FilterInputStream
FilterInputStream
is abstract and meant to be subclassed. You
typically create concrete subclasses like BufferedInputStream
.
The constructor is protected to prevent direct instantiation.
import java.io.FileInputStream; import java.io.FilterInputStream; import java.io.IOException; import java.io.InputStream; public class Main { static class UpperCaseFilter extends FilterInputStream { public UpperCaseFilter(InputStream in) { super(in); } @Override public int read() throws IOException { int c = super.read(); return (c == -1) ? c : Character.toUpperCase(c); } } public static void main(String[] args) { try { InputStream fileStream = new FileInputStream("data.txt"); FilterInputStream filterStream = new UpperCaseFilter(fileStream); int c; while ((c = filterStream.read()) != -1) { System.out.print((char) c); } filterStream.close(); } catch (IOException e) { e.printStackTrace(); } } }
This example creates a custom UpperCaseFilter
that converts all
characters to uppercase. The filter extends FilterInputStream
and
overrides the read
method. The constructor passes the underlying
stream to the superclass.
Reading Data with FilterInputStream
FilterInputStream delegates all read operations to the underlying stream. Subclasses can intercept these calls to modify data. The basic read methods include single-byte and array-based reading.
import java.io.FilterInputStream; import java.io.ByteArrayInputStream; import java.io.IOException; public class Main { static class Rot13Filter extends FilterInputStream { public Rot13Filter(InputStream in) { super(in); } @Override public int read() throws IOException { int c = super.read(); if (c == -1) return c; if (c >= 'a' && c <= 'z') { return 'a' + ((c - 'a' + 13) % 26); } if (c >= 'A' && c <= 'Z') { return 'A' + ((c - 'A' + 13) % 26); } return c; } } public static void main(String[] args) { String data = "Hello World!"; ByteArrayInputStream bais = new ByteArrayInputStream(data.getBytes()); try (FilterInputStream fis = new Rot13Filter(bais)) { int c; while ((c = fis.read()) != -1) { System.out.print((char) c); } } catch (IOException e) { e.printStackTrace(); } } }
This example implements a ROT13 cipher filter. The read
method
applies the ROT13 transformation to alphabetic characters. Non-alphabetic
characters pass through unchanged. The filter demonstrates data transformation.
Reading Bytes into an Array
FilterInputStream provides bulk read operations that transfer multiple bytes at once. These methods are more efficient than single-byte reads. Subclasses can override them for specialized behavior.
import java.io.FilterInputStream; import java.io.ByteArrayInputStream; import java.io.IOException; public class Main { static class ReverseFilter extends FilterInputStream { public ReverseFilter(InputStream in) { super(in); } @Override public int read(byte[] b, int off, int len) throws IOException { int result = super.read(b, off, len); if (result != -1) { for (int i = off; i < off + result / 2; i++) { byte temp = b[i]; b[i] = b[off + result - 1 - (i - off)]; b[off + result - 1 - (i - off)] = temp; } } return result; } } public static void main(String[] args) { String data = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; ByteArrayInputStream bais = new ByteArrayInputStream(data.getBytes()); try (FilterInputStream fis = new ReverseFilter(bais)) { byte[] buffer = new byte[10]; int bytesRead; while ((bytesRead = fis.read(buffer)) != -1) { System.out.println(new String(buffer, 0, bytesRead)); } } catch (IOException e) { e.printStackTrace(); } } }
This example creates a filter that reverses byte arrays during reading. The
read(byte[], int, int)
method is overridden to reverse the bytes
after reading them from the underlying stream. The filter processes data in
chunks.
Mark and Reset Functionality
FilterInputStream delegates mark and reset operations to the underlying stream. These methods allow returning to previously marked positions. Not all streams support this functionality.
import java.io.FilterInputStream; import java.io.ByteArrayInputStream; import java.io.IOException; public class Main { static class CountingFilter extends FilterInputStream { private int count = 0; public CountingFilter(InputStream in) { super(in); } @Override public int read() throws IOException { count++; return super.read(); } @Override public int read(byte[] b, int off, int len) throws IOException { int result = super.read(b, off, len); if (result != -1) count += result; return result; } public int getCount() { return count; } } public static void main(String[] args) { String data = "Mark and reset demonstration"; ByteArrayInputStream bais = new ByteArrayInputStream(data.getBytes()); try (CountingFilter fis = new CountingFilter(bais)) { System.out.println("Mark supported: " + fis.markSupported()); // Read first 5 bytes byte[] buffer = new byte[5]; fis.read(buffer); System.out.println("First 5: " + new String(buffer)); // Mark position fis.mark(10); System.out.println("Count after mark: " + fis.getCount()); // Read next 5 bytes fis.read(buffer); System.out.println("Next 5: " + new String(buffer)); // Reset to mark fis.reset(); System.out.println("Count after reset: " + fis.getCount()); // Read again from mark fis.read(buffer); System.out.println("After reset: " + new String(buffer)); } catch (IOException e) { e.printStackTrace(); } } }
This example demonstrates mark/reset functionality through a counting filter. The filter tracks bytes read while preserving the underlying stream's mark/reset capability. The count shows the effect of resetting the stream position.
Skipping Bytes in the Stream
The skip
method allows bypassing bytes in the stream without reading
them. FilterInputStream delegates this to the underlying stream. The actual
number of bytes skipped may be less than requested.
import java.io.FilterInputStream; import java.io.ByteArrayInputStream; import java.io.IOException; public class Main { static class LoggingFilter extends FilterInputStream { public LoggingFilter(InputStream in) { super(in); } @Override public long skip(long n) throws IOException { System.out.println("Attempting to skip " + n + " bytes"); long skipped = super.skip(n); System.out.println("Actually skipped " + skipped + " bytes"); return skipped; } } public static void main(String[] args) { String data = "1234567890ABCDEFGHIJKLMNOPQRSTUVWXYZ"; ByteArrayInputStream bais = new ByteArrayInputStream(data.getBytes()); try (LoggingFilter fis = new LoggingFilter(bais)) { // Skip first 10 bytes fis.skip(10); // Read next 5 bytes byte[] buffer = new byte[5]; fis.read(buffer); System.out.println("After skip: " + new String(buffer)); // Try to skip beyond end fis.skip(30); System.out.println("Available: " + fis.available()); } catch (IOException e) { e.printStackTrace(); } } }
This example creates a logging filter that tracks skip operations. The overridden
skip
method logs both the requested and actual bytes skipped. The
example shows skipping within and beyond the stream's bounds.
Available Bytes and Closing
The available
method estimates bytes that can be read without
blocking. close
releases resources. Both delegate to the underlying
stream. Custom filters can add behavior to these operations.
import java.io.FilterInputStream; import java.io.ByteArrayInputStream; import java.io.IOException; public class Main { static class StatusFilter extends FilterInputStream { public StatusFilter(InputStream in) { super(in); } @Override public int available() throws IOException { int avail = super.available(); System.out.println("Available bytes: " + avail); return avail; } @Override public void close() throws IOException { System.out.println("Closing filter stream"); super.close(); } } public static void main(String[] args) { String data = "Stream status monitoring example"; ByteArrayInputStream bais = new ByteArrayInputStream(data.getBytes()); try (StatusFilter fis = new StatusFilter(bais)) { System.out.println("Initial status:"); fis.available(); // Read some data byte[] buffer = new byte[10]; fis.read(buffer); System.out.println("After reading 10 bytes:"); fis.available(); // Read remaining while (fis.available() > 0) { fis.read(buffer); } System.out.println("After reading all:"); fis.available(); } catch (IOException e) { e.printStackTrace(); } } }
This example demonstrates stream status monitoring through a custom filter. The
available
method is overridden to log available bytes. The
close
method logs stream closure. The try-with-resources statement
ensures proper cleanup.
Source
Java FilterInputStream Class Documentation
In this article, we've covered the essential methods and features of the Java FilterInputStream class. Understanding these concepts is crucial for creating custom input stream filters in Java applications.
Author
List all Java tutorials.