Try with resources, first introduced in Java 7, is a Loan Pattern implementation baked in the language which enables us to borrow a AutoCloseable resource, mess with it as we wish and in the meanwhile, not to worry about freeing the resource; because it’s automatically being taken care of by the compiler. Before this feature being rolled out, the common idiom to use a resource was kinda like the following:

InetSocketAddress birthPort = new InetSocketAddress(1989);
DatagramChannel udpServer = DatagramChannel.open().bind(birthPort);
try {
    udpServer.receive(buffer);
    // Process the packet
} catch (Exception e) {
    // handle the exception
} finally {
    if (udpServer != null) {
        try {
            udpServer.close();
        } catch (Exception ignored) {}
    }
}

Try with resources reduces the preceding code to:

InetSocketAddress birthPort = new InetSocketAddress(1989);
try(DatagramChannel udpServer = DatagramChannel.open().bind(birthPort)) {
    udpServer.receive(buffer);
    // Process the packet
} catch (Exception e) {
    // handle the exception
}

There is no need to explicitly free the borrowed resource, which is nice, but the resource declaration is a bit ugly and it’s even more uglier if we try to manage multiple resources:

InetSocketAddress birthPort = new InetSocketAddress(1989);
try (DatagramChannel udpServer = DatagramChannel.open().bind(birthPort);
     Selector selector = Selector.open()) {
    // go non-blocking and register channel with selector
    udpServer.receive(buffer);
    // Process the packet
} catch (Exception e) {
    // handle the exception
}

The problem is, In Java 7, the resources to be managed by a try-with-resources must be fresh variables declared like:

try (Resource resource = ...)

In Java 9, the try-with-resources statement refined in a way that it can accept final and effectively final variables. So in Java 9 you can write:

InetSocketAddress birthPort = new InetSocketAddress(1989);
DatagramChannel udpServer = DatagramChannel.open().bind(birthPort);
Selector selector = Selector.open();

try (udpServer;selector) {
    // go non-blocking and register channel with selector
    udpServer.receive(buffer);
    // Process the packet
} catch (Exception e) {
    // handle the exception
}