Retrieving Network Content by Format in Java
Part 4 of 13 in Mastering Java Network Programming
Java’s java.net.URL class can fetch resources over many protocols — HTTP, HTTPS, file, and more. But it offers three different ways to read that content, and each one behaves differently depending on the protocol.
The three approaches are:
url.openConnection().getInputStream()— always returns raw bytes, works for every protocol.url.getContent()— returns an object whose type depends on the protocol’s ContentHandler; you must check withinstanceofbefore casting.url.getContent(Class[])— requests a specific type; if no handler can produce it, it returnsnull.
We’ll see all three in action against both an HTTP URL and a local file URL.
1. getInputStream() — universal byte access
getInputStream() is the most straightforward approach: it always gives you raw bytes wrapped in an InputStream, regardless of protocol. You control the encoding by wrapping it yourself.
import java.io.InputStream;
import java.net.URL;
import java.util.Scanner;
try (InputStream in = url.openConnection().getInputStream();
Scanner sc = new Scanner(in, "UTF-8")) {
while (sc.hasNextLine()) {
System.out.println(sc.nextLine());
}
}
The trade-off is that you get bytes — not a String, not a Reader. If the content is text, you must specify the encoding explicitly.
Both HTTPS and file URLs produce identical output structure here. The only difference is what each protocol’s handler reads off the wire (the HTTP response body) or from disk (the local file).
2. getContent() — handler-dependent return types
Calling url.getContent() delegates to the URL’s ContentHandler, which decides the return type. There is no contract guaranteeing a specific class.
Object content = url.getContent();
System.out.println(content.getClass().getName());
In practice, for HTTP URLs the handler returns HttpURLConnection$HttpInputStream, and for plain text files it returns PlainTextInputStream. Both are subclasses of InputStream — but nothing in the API says that must always hold.
The correct approach is a chain of instanceof checks:
if (content instanceof byte[]) { ... }
else if (content instanceof String) { ... }
else if (content instanceof InputStream) {
try (InputStream in = (InputStream) content) { /* ... */ }
}
else if (content instanceof java.io.Reader) { ... }
Notice the runtime class names differ between protocols: sun.net.www.protocol.http.HttpURLConnection$HttpInputStream for HTTPS versus sun.net.www.content.text.PlainTextInputStream for a file. Both satisfy instanceof InputStream, which is why the safe pattern works.
3. getContent(Class[]) — requesting a specific format
You can pass a desired type to getContent():
Object content = url.getContent(new Class<?>[]{String.class});
If a ContentHandler exists that can produce that type for the given protocol, you get it back. Otherwise, it returns null — silently.
Java’s standard library does not ship with String or Reader ContentHandlers for HTTP or file URLs. So getContent(String.class), getContent(Reader.class), and even getContent(byte[].class) all return null in practice on these protocols:
The safe cast pattern — the one that actually works — remains instanceof InputStream. The ContentHandler chain falls back to returning an InputStream when no specialized handler is available.
Takeaway
Reach for getInputStream() when you want reliable byte-level access across protocols. Use getContent() only when you need its type-conversion features (which are limited), and always guard with instanceof. Don’t count on getContent(Class[]) producing a String or Reader — it returns null by default, and the InputStream fallback is what actually carries the content.