"Amigo, ten-hut!"

"I'm happy to learn Java, Captain!"

"At ease, Amigo. Today we have a super interesting topic. We will talk about how a Java program interacts with external resources and we'll study one very interesting Java statement. Better not cover your ears."

"I'm all ears."

"As a Java program runs, sometimes it interacts with entities outside the Java machine. For example, with files on disk. These entities are usually called external resources."

"Then what are considered internal resources?"

"Internal resources are the objects created inside the Java machine. Typically, the interaction follows this scheme:

Try-with-resources statement

"The operating system rigorously keeps track of the resources available, and also controls shared access to them from different programs. For example, if one program changes a file, then another program cannot change (or delete) that file. This principle isn't limited to files, but they provide the most readily understandable example.

"The operating system has functions (APIs) that allow a program to acquire and/or release resources. If a resource is busy, then only the program that acquired it can work with it. If a resource is free, then any program can acquire it.

"Imagine that an office has shared coffee mugs. If someone takes a mug, then other people can no longer take it. But once the mug is used, washed, and put back in its place, then anyone can take it again."

"Got it. It's like seats on the subway or other public transport. If a seat is free, then anyone can take it. If a seat is occupied, then it is controlled by the person who took it."

"That's right. And now let's talk about acquiring external resources. Every time your Java program starts working with a file on disk, the Java machine asks the operating system for exclusive access to it. If the resource is free, then the Java machine acquires it.

"But after you've finished working with the file, this resource (file) must be released, i.e. you need to notify the operating system that you no longer need it. If you do not do this, then the resource will continue to be held by your program."

"That sounds fair."

"To keep it that way, the operating system maintains a list of resources occupied by each running program. If your program exceeds the assigned resource limit, then the operating system will no longer give you new resources.

"It's like programs that can eat up all the memory..."

"Something like that. The good news is that if your program terminates, all resources are automatically released (the operating system itself does this)."

"If that's the good news, does that mean there is bad news?"

"Precisely so. The bad news is that if you are writing a server application..."

"But do I write such applications?"

"A lot of server applications are written in Java, so most likely you will write them for work. As I was saying, if you are writing a server application, then your server needs to run non-stop for days, weeks, months, etc."

"In other words, the program doesn't terminate, and that means the memory is not automatically released."

"Exactly. And if you open 100 files a day and don't close them, then in a couple of weeks your application will reach its resource limit and crash."

"That's falling far short of months of stable work! What can be done?"

"Classes that use external resources have a special method for releasing them: close().

"Here's an example of a program that writes something to a file and then closes the file when it is done, i.e. it frees up the operating system's resources. It looks roughly like this:

Code Note
String path = "c:\\projects\\log.txt";
FileOutputStream output = new FileOutputStream(path);
output.write(1);
output.close();
The path to the file.
Get the file object: acquire the resource.
Write to the file
Close the file - release the resource

"Ah... So, after working with a file (or other external resources), I have to call the close() method on the object linked to the external resource."

"Yes. It all seems simple. But exceptions can occur as a program runs, and the external resource won't be released."

"And that is very bad. What to do?"

"To ensure that the close() method is always called, we need to wrap our code in a try-catch-finally block and add the close() method to the finally block. It will look something like this:

try
{
   FileOutputStream output = new FileOutputStream(path);
   output.write(1);
   output.close();
}
catch (IOException e)
{
   e.printStackTrace();
}
finally
{
   output.close();
}

"Hmm... Something is wrong here?"

"Right. This code will not compile, because the output variable is declared inside the try{} block, and therefore is not visible in the finally block.

Let's fix it:

FileOutputStream output = new FileOutputStream(path);

try
{
   output.write(1);
   output.close();
}
catch (IOException e)
{
   e.printStackTrace();
}
finally
{
   output.close();
}

"Is everything okay now?"

"It's okay, but it won't work if an error occurs when we create the FileOutputStream object, and this could happen quite easily.

Let's fix it:

FileOutputStream output = null;

try
{
   output = new FileOutputStream(path);
   output.write(1);
   output.close();
}
catch (IOException e)
{
   e.printStackTrace();
}
finally
{
   output.close();
}

"And does everything work now?"

"There are still a few criticisms. First, if an error occurs when creating the FileOutputStream object, then the output variable will be null. This possibility must be accounted for in the finally block.

"Second, the close() method is always called in the finally block, which means that it is not necessary in the try block. The final code will look like this:

FileOutputStream output = null;

try
{
   output = new FileOutputStream(path);
   output.write(1);
}
catch (IOException e)
{
   e.printStackTrace();
}
finally
{
   if (output!=null)
      output.close();
}

"Even if we don't consider the catch block, which can be omitted, then our 3 lines of code become 10. But we basically just opened the file and wrote 1."

"Phew... It's a good thing that concludes the matter. Relatively understandable, but somewhat tedious, isn't it?"

"So it is. That's why Java's creators helped us by adding some syntactic sugar. Now let's move on to the highlight of the program, or rather, this lesson:

try-with-resources

"Starting with its 7th version, Java has a new try-with-resources statement.

"It was created precisely to solve the problem with the mandatory call to the close() method."

"It sounds promising!"

"The general case looks quite simple:

try (ClassName name = new ClassName())
{
   Code that works with the name variable
}

"So this is another variation of the try statement?"

"Yes. You need to add parentheses after the try keyword, and then create objects with external resources inside the parentheses. For each object in the parentheses, the compiler adds a finally section and a call to the close() method.

"Below are two equivalent examples:

Long code Code with try-with-resources
FileOutputStream output = null;

try
{
   output = new FileOutputStream(path);
   output.write(1);
}
finally
{
   if (output!=null)
   output.close();
}
try(FileOutputStream output = new FileOutputStream(path))
{
   output.write(1);
}

"Cool! The code using try-with-resources is much shorter and easier to read. And the less code we have, the less chance of making a typo or other error."

"I'm glad you like it. By the way, we can add catch and finally blocks to the try-with-resources statement. Or you can not add them if they aren't needed.

16
Task
New Java Syntax,  level 16lesson 3
Locked
Shortening try
The program reads a string from the console and displays it in all lowercase letters. Without changing the functionality of the program, rewrite the code using a try-with-resources statement. Put the creation of objects that use external resources inside the parentheses. Remember to remove the unnec
16
Task
New Java Syntax,  level 16lesson 3
Locked
Back to basics
The program reads a string from the console and displays it in all uppercase letters. As it turns out, it doesn't work in older versions of Java (older than the 7th version). Without changing the functionality of the program, rewrite the try-with-resources block as an ordinary try-catch statement. D

Several variables at the same time

"You may often encounter a situation when you need to open several files at the same time. Let's say you are copying a file, so you need two objects: the file from which you are copying data and the file to which you are copying data.

"In this case, the try-with-resources statement lets you create one but several objects in it. The code that creates the objects must be separated by semicolons. Here's the general appearance of this statement:

try (ClassName name = new ClassName(); ClassName2 name2 = new ClassName2())
{
   Code that works with the name and name2 variables
}

Example of copying files:

Short code Long code
String src = "c:\\projects\\log.txt";
String dest = "c:\\projects\\copy.txt";

try(FileInputStream input = new FileInputStream(src);

FileOutputStream output = new FileOutputStream(dest))
{
   byte[] buffer = input.readAllBytes();
   output.write(buffer);
}
String src = "c:\\projects\\log.txt";
String dest = "c:\\projects\\copy.txt";

FileInputStream input = null;
FileOutputStream output = null;

try
{
   input = new FileInputStream(src);
   output = new FileOutputStream(dest);

   byte[] buffer = input.readAllBytes();
   output.write(buffer);
}
finally
{
   if (input!=null)
      input.close();
   if (output!=null)
      output.close();
}

"Well, what can we say here? try-with-resources is a wonderful thing!"

"What we can say is that we should use it."