Bad practice in JDK's try-with-resources example? [duplicate] - java

This question already has answers here:
Correct idiom for managing multiple chained resources in try-with-resources block?
(8 answers)
Closed 5 years ago.
I found this example in the try-with-resources documentation for Java:
static String readFirstLineFromFile(String path) throws IOException {
try (BufferedReader br = new BufferedReader(new FileReader(path))) {
return br.readLine();
}
}
If the constructor for BufferedReader throws an exception, then the resources held by the FileReader won't be released. So isn't this a bad practice to write it like that instead of:
static String readFirstLineFromFile(String path) throws IOException {
try (FileReader fr = new FileReader(path);
BufferedReader br = new BufferedReader(fr)) {
return br.readLine();
}
}

Indeed, just gave it a quick try:
public class MyFileReader extends FileReader {
public MyFileReader(String fileName) throws FileNotFoundException {
super(fileName);
}
#Override
public void close() throws IOException {
System.out.println("Closing MyFileReader");
super.close();
}
}
public class MyBufferedReader extends BufferedReader {
public MyBufferedReader(Reader in) {
super(in);
throw new RuntimeException();
}
#Override
public void close() throws IOException {
System.out.println("Closing MyBufferedReader");
super.close();
}
}
public String test(String path) throws IOException {
try (BufferedReader br = new MyBufferedReader(new MyFileReader(path))) {
return br.readLine();
}
}
None of MyFileReader nor MyBufferedReader is closed... Good catch!
While with:
public String test(String path) throws IOException {
try (FileReader fr = new MyFileReader(path); BufferedReader br = new MyBufferedReader(fr)) {
return br.readLine();
}
}
MyFileReader is closed.
BufferedReader constructor can indeed throw exceptions, see BufferedReader(Reader in, int sz) constructor (although not when coming from BufferedReader(Reader in) constructor, but the doc you linked should still alert on this possible issue IMHO).
Looks like you won the right to raise an issue :)

Unfortunately, you are right.
The below example shows this behaviour - instance of Internal is never closed.
public class Test {
public static void main(String[] args) {
try (External external = new External(new Internal())) {
}
}
}
class External implements Closeable {
private Internal internal;
public External(Internal internal) {
this.internal = internal;
throw new RuntimeException("boom");
}
#Override
public void close() {
System.out.println("External.close()");
internal.close();
}
}
class Internal implements Closeable {
#Override
public void close() {
System.out.println("Internal.close()");
}
}

Related

Do I need to keep variables for all writers/readers to close them all manually?

Consider this example:
public class FileAppender implements AutoCloseable {
private final FileWriter fileWriter;
private final BufferedWriter bufferedWriter;
public FileAppender(String fileName) throws IOException {
fileWriter = new FileWriter(fileName, true)
bufferedWriter = new BufferedWriter(fileWriter);
}
public void appendLine(String line) throws IOException {
bufferedWriter.write(line + "\n");
}
#Override
public void close() throws Exception {
bufferedWriter.close();
fileWriter.close();
}
}
Here we keep an unused FileWriter as a member of the class, just so we can manually close it later. There are many tutorials in the web which show examples like this where multiple streams are closed manually.
We could instead implement the same class more concisely:
public class FileAppender implements AutoCloseable {
private final BufferedWriter writer;
public FileAppender(String fileName) throws IOException {
writer = new BufferedWriter(new FileWriter(fileName, true));
}
public void appendLine(String line) throws IOException {
writer.write(line + "\n");
}
#Override
public void close() throws Exception {
writer.close();
}
}
Same applies to the usage of FileReader and BufferedReader.
Would there be any difference between the above two implementations?
With BufferedWriter, specifically, you can just use close on the BufferedWriter and it will call close on the underlying FileWriter.
But, as far as I'm aware, that's not documented, and not required of Writer implementations that wrap other Writers (and similarly, streams). I'm paranoid, and tend to close things explicitly (in reverse order of opening them).
You can see the close operation in the BufferedWriter source (this example is from JDK 11.0.1), though you have to look fairly closely:
public void close() throws IOException {
synchronized (lock) {
if (out == null) {
return;
}
try (Writer w = out) {
flushBuffer();
} finally {
out = null;
cb = null;
}
}
}
Note the use of try-with-resources to auto-close out (via w).

Which close() runs first?

If I have multiple resources, in a try catch, which one gets closed called on first?
public class TestRes {
public static void main(String[] args) {
TestRes tr = new TestRes();
tr.test();
}
public void test() {
try (MyResource1 r1 = new MyResource1(); MyResource2 r2 = new MyResource2(); ) {
System.out.print("T ");
} catch (IOException ioe) {
System.out.print("IOE ");
} finally {
System.out.print("F ");
}
}
class MyResource1 implements AutoCloseable {
public void close() throws IOException {
System.out.print("1 ");
}
}
class MyResource2 implements Closeable {
public void close() throws IOException {
throw new IOException();
}
}
}
This sample outputs:
T 1 IOE F
If I change the order so...
public class TestRes {
public static void main(String[] args) {
TestRes tr = new TestRes();
tr.test();
}
public void test() {
try (MyResource2 r2 = new MyResource2(); MyResource1 r1 = new MyResource1();) {
System.out.print("T ");
} catch (IOException ioe) {
System.out.print("IOE ");
} finally {
System.out.print("F ");
}
}
class MyResource1 implements AutoCloseable {
public void close() throws IOException {
System.out.print("1 ");
}
}
class MyResource2 implements Closeable {
public void close() throws IOException {
throw new IOException();
}
}
}
I get the same output - why?
It seems that you believe an exception from a close() method will prevent other close() methods from being called. That is wrong.
The Java Language Specification, section 14.20.3. try-with-resources, says:
Resources are closed in the reverse order from that in which they were initialized. A resource is closed only if it initialized to a non-null value. An exception from the closing of one resource does not prevent the closing of other resources. Such an exception is suppressed if an exception was thrown previously by an initializer, the try block, or the closing of a resource.
Which means that the close() method printing 1 will always be executed, and the first part answers your "Which close() runs first?" question.

Writing to file in netBeans IDE in java

public class FileWriterClass {
/**
* #param args the command line arguments
* #throws java.io.IOException
*/
public static void main(String[] args) throws IOException {
// TODO code application logic here
FileWriter fr = new FileWriter("hello.txt");
BufferedWriter br = new BufferedWriter(fr);
br.write("helllllllllllllllll");
}
}
its not writing into file.please help me out
You should always call the close() once you finish writing the data.
public class FileWriterClass {
/**
* #param args the command line arguments
* #throws java.io.IOException
*/
public static void main(String[] args) throws IOException {
// TODO code application logic here
FileWriter fr = new FileWriter("hello.txt");
BufferedWriter br = new BufferedWriter(fr);
br.write("helllllllllllllllll");
br.close();
fr.close();
}
}
You need to make sure the writers are closed, which will flush the buffer to disk. You need to do this because Java makes no guarentees that the objects will be finalised and closed automatically.
To make life easier for yourself, you can make use of the The try-with-resources Statement, which will close the writers automatically, even if there is an exception during the write process
public class FileWriterClass {
public static void main(String[] args) throws IOException {
try (FileWriter fr = new FileWriter("hello.txt");
BufferedWriter br = new BufferedWriter(fr)) {
br.write("helllllllllllllllll");
}
}
}
Since you are not Closing the Resources That's why it write Continuous.
and must have to put risky code inside Try catch block. must have to use close and flush the File Operation
Updated Code : -
public class FileWriterClass {
public static void main(String[] args){
try{
FileWriter fr = new FileWriter("hello.txt");
BufferedWriter br = new BufferedWriter(fr);
br.write("helllllllllllllllll");
br.close();
}catch(IOException i){
i.printStackTrace();
}}}
Try to close() the BufferedWriter and FileWriter like this:
br.write("helllllllllllllllll");
br.close();
fr.close();
You could use the utility methods of java.lang.nio.file.Files. Which removes the need to write to much boilerplate code.
public class FileWriterClass {
public static void main(String[] args) throws IOException {
// path to the file to write, in this case the file
// will be created in the current directory
Path path = Paths.get("hello.txt");
// the character set of the file
Charset fileCharset = StandardCharsets.UTF_8;
// the needed code to write to the file
try (BufferedWriter bw = Files.newBufferedWriter(path, fileCharset)) {
bw.write("hello NIO 2");
}
}
}

If it safe to return an InputStream from try-with-resource [duplicate]

This question already has answers here:
Try-with-resources and return statements in java
(4 answers)
Closed 7 years ago.
Is it safe to return an input stream from a try-with-resource statement to handle the closing of the stream once the caller has consumed it?
public static InputStream example() throws IOException {
...
try (InputStream is = ...) {
return is;
}
}
It's safe, but it will be closed, so I don't think it is particularly useful... (You can't reopen a closed stream.)
See this example:
public static void main(String[] argv) throws Exception {
System.out.println(example());
}
public static InputStream example() throws IOException {
try (InputStream is = Files.newInputStream(Paths.get("test.txt"))) {
System.out.println(is);
return is;
}
}
Output:
sun.nio.ch.ChannelInputStream#1db9742
sun.nio.ch.ChannelInputStream#1db9742
The (same) input stream is returned (same by reference), but it will be closed. By modifying the example to this:
public static void main(String[] argv) throws Exception {
InputStream is = example();
System.out.println(is + " " + is.available());
}
public static InputStream example() throws IOException {
try (InputStream is = Files.newInputStream(Paths.get("test.txt"))) {
System.out.println(is + " " + is.available());
return is;
}
}
Output:
sun.nio.ch.ChannelInputStream#1db9742 1000000
Exception in thread "main" java.nio.channels.ClosedChannelException
at sun.nio.ch.FileChannelImpl.ensureOpen(FileChannelImpl.java:109)
at sun.nio.ch.FileChannelImpl.size(FileChannelImpl.java:299)
at sun.nio.ch.ChannelInputStream.available(ChannelInputStream.java:116)
at sandbox.app.Main.main(Main.java:13)

Java Try and Catch IOException must be caught or declared to be thrown

I am trying to use a bit of code I found at the bottom of this page. Here is the code in a class that I created for it:
import java.io.LineNumberReader;
import java.io.FileReader;
import java.io.IOException;
public class LineCounter {
public static int countLines(String filename) throws IOException {
LineNumberReader reader = new LineNumberReader(new FileReader(filename));
int cnt = 0;
String lineRead = "";
while ((lineRead = reader.readLine()) != null) {}
cnt = reader.getLineNumber();
reader.close();
return cnt;
}
}
My objective is to count the lines of a text file, store that number as an integer, then use that integer in my main class. In my main class I tried a few different ways of making this happen, but (being a new programmer) I am missing something. Here is the first thing I tried:
String sFileName = "MyTextFile.txt";
private int lineCount = LineCounter.countLines(sFileName);
With this attempt I get the error "unreported exception java.io.IOException; must be caught or declared to be thrown." I don't understand why I am getting this because as I can see the exception is declared in my "countLines" method. I tried to use a try catch block right under that last bit of code I posted, but that didn't work either (I don't think I did it right though). Here is my try catch attempt:
String sFileName = "MyTextFile.txt";
private int lineCount;{
try{
LineCounter.countLines(sFileName);
}
catch(IOException ex){
System.out.println (ex.toString());
System.out.println("Could not find file " + sFileName);
}
}
Please show me the way!
Initializer block is just like any bits of code; it's not "attached" to any field/method preceding it. To assign values to fields, you have to explicitly use the field as the lhs of an assignment statement.
private int lineCount; {
try{
lineCount = LineCounter.countLines(sFileName);
/*^^^^^^^*/
}
catch(IOException ex){
System.out.println (ex.toString());
System.out.println("Could not find file " + sFileName);
}
}
Also, your countLines can be made simpler:
public static int countLines(String filename) throws IOException {
LineNumberReader reader = new LineNumberReader(new FileReader(filename));
while (reader.readLine() != null) {}
reader.close();
return reader.getLineNumber();
}
Based on my test, it looks like you can getLineNumber() after close().
The reason you are getting the the IOException is because you are not catching the IOException of your countLines method. You'll want to do something like this:
public static void main(String[] args) {
int lines = 0;
// TODO - Need to get the filename to populate sFileName. Could
// come from the command line arguments.
try {
lines = LineCounter.countLines(sFileName);
}
catch(IOException ex){
System.out.println (ex.toString());
System.out.println("Could not find file " + sFileName);
}
if(lines > 0) {
// Do rest of program.
}
}
Your countLines(String filename) method throws IOException.
You can't use it in a member declaration. You'll need to perform the operation in a main(String[] args) method.
Your main(String[] args) method will get the IOException thrown to it by countLines and it will need to handle or declare it.
Try this to just throw the IOException from main
public class MyClass {
private int lineCount;
public static void main(String[] args) throws IOException {
lineCount = LineCounter.countLines(sFileName);
}
}
or this to handle it and wrap it in an unchecked IllegalArgumentException:
public class MyClass {
private int lineCount;
private String sFileName = "myfile";
public static void main(String[] args) throws IOException {
try {
lineCount = LineCounter.countLines(sFileName);
} catch (IOException e) {
throw new IllegalArgumentException("Unable to load " + sFileName, e);
}
}
}

Categories

Resources