Understanding URLs vs URIs, and What Makes Up a URL

Part 3 of 12 in Mastering Java Network Programming

The terms URI, URL, and URN are often used interchangeably in casual conversation — but they mean different things, and confusing them leads to subtle bugs when parsing or validating identifiers.

A URI (Uniform Resource Identifier) is the superset: any string that identifies a resource. A URL is a URI that also tells you where to find the resource — it encodes a network location and protocol. A URN (Uniform Resource Name) identifies a resource without saying how to locate it; think of it as a persistent name rather than a pointer.

In Java’s java.net package this hierarchy is reflected in two separate classes: URI parses, validates, and exposes the components of any URI string, while URL requires a registered network protocol handler and can only be constructed from strings whose scheme is known to the JVM.

Every URL is also a valid URI, but not every URI is a valid URL. The example below drives this home, then walks through all five URL components.

The code

import java.net.URI;
import java.net.URL;
import java.net.MalformedURLException;

public class UrlDemo {

    private static final String URN_STR = "urn:isbn:978-0321349606";

    private static final String[] URL_STRS = {
        "https://www.example.com:8443/path/to/resource?key=value&foo=bar#section",
        "ftp://[email protected]/pub/docs/readme.txt",
        "http://192.168.1.1/api/v2/status",
        "mailto:[email protected]"
    };

    public static void main(String[] args) throws Exception {
        // Part 1 — URN is a URI but not a URL
        URI uri = new URI(URN_STR);              // succeeds
        try { new URL(URN_STR); }                // MalformedURLException: unknown protocol: urn

        // Part 2 — break down every component for several URLs
        for (String urlStr : URL_STRS) {
            URI u = new URI(urlStr);
            System.out.println(u.getScheme());
            System.out.println(u.getAuthority());
            System.out.println(u.getPath());
            System.out.println(u.getQuery());
            System.out.println(u.getFragment());
        }

        // Part 3 — authority subdivisions: userinfo, host, port
        // Also covers IPv6 brackets and encoded passwords.
    }
}

Running it

The output makes three points clear.

First, the URN urn:isbn:978-0321349606 parses into a valid URI but fails with MalformedURLException: unknown protocol: urn when fed to java.net.URL. This confirms that not every identifier is a locator — the JVM simply doesn’t know how to resolve the urn scheme.

Second, every URL-like string also parses as a valid URI. Java’s URI class handles HTTPS, FTP, HTTP (even with an IP address host), and even mailto: without complaint. All four produce proper five-component breakdowns:

  • The full HTTPS example shows all five components populated: scheme (https), authority (www.example.com:8443), path (/path/to/resource), query (key=value&foo=bar), and fragment (section).
  • The FTP URL reveals the authority splitting into userinfo (anonymous) and host (files.example.com), with no explicit port.
  • The IPv4 host case (http://192.168.1.1/api/v2/status) demonstrates that IP addresses work just fine as hosts — no hostname resolution needed at parse time.
  • mailto:[email protected] is the outlier: despite containing an @, it has no authority because mailto: doesn’t use the authority section; the email address lives in the scheme-specific part instead. This is a common gotcha for anyone assuming every @ marks userinfo.

Third, the authority subdivision examples show that IPv6 addresses must be bracketed ([::1]) and Java’s URI.getHost() strips those brackets correctly. Encoded characters in userinfo (like %40 decoding to @) also parse through without issue — p%40ss becomes the raw string p@ss in the extracted value.

Takeaway

A URI identifies a resource; a URL adds how to reach it. When you need to validate or parse identifiers, use java.net.URI — it handles everything that Java’s URL class can plus URNs and custom schemes without requiring a network protocol handler. The five components of a URL (scheme, authority → userinfo/host/port, path, query, fragment) map directly onto the URI getter methods, but don’t assume every identifier with an @ has an authority — scheme-specific parsing rules apply before the authority section is even considered.