I was helping a friend to write some Java code, who doesn't know a lot about Java. So I wrote him some helper functions to easily get things done that are a little quirky in his eyes. One of them is a function, that writes a String to an OutputStream
. Have a look:
public void write(String txt, OutputStream out) {
PrintWriter printer = new PrintWriter(out);
printer.print(txt);
printer.close();
}
Now, you can use that easily in different ways to write wherever you want. For example you could do that:
(new StreamHelper()).write("Hello Test", System.out);
Doing that I found out that afterwards System.out.println()
doesn't write anything to the shell anymore. So I think that maybe printer.close()
automatically also closed System.out
and I wonder how to reactivate it that I can use it after this function is finished again.
Is my assumption correct? (How could I have found out without asking here?)
How can I continue to use System.out
after a call to the write()
function?
Are there better ways to write such a helper function?
The general contract for OutputStream
's close:
public void close() throws IOException Closes this output stream and releases any system resources associated with this stream. The general contract of close is that it closes the output stream. A closed stream cannot perform output operations and cannot be reopened.
PrintStream
's
public void close() Close the stream. This is done by flushing the stream and then closing the underlying output stream.
The only advice I can give you is that you should not write asymmetrical code, that is, don't delegate the closing of resources your code has created to somewhere else.
Even if in your case it might seemed sensible to close the wrapper stream, the fact is that you should not because you are closing a stream opened somewhere else.
In short:
public void write(String txt, OutputStream out) {
PrintWriter printer = new PrintWriter(out);
printer.print(txt);
printer.flush();
//it is very unpolite to close someone else's streams!
//printer.close();
}
Oh, and by the way, you may want to change the function name to print
, rather than write
.
Like the others have said, a stream should be closed where it was opened. However, you could write a facade to protect a stream from getting closed like this:
import java.io.FilterOutputStream;
import java.io.IOException;
import java.io.OutputStream;
public class UnclosableOutputStream extends FilterOutputStream {
public UnclosableOutputStream(OutputStream out) {
super(out);
}
@Override
public void close() throws IOException {
out.flush();
}
}
and use it like this:
new StreamHelper().write("Hello Test", new UnclosableOutputStream(System.out));
Might come in handy for test scenarios etc.
System.out
is a PrintStream, therefore the code you provide above has literally no advantage over just calling System.out.print
directly. The reason it is not writing anymore is that close
did, in fact, close System.out
.
If this is for logging, learn log4j for your friend or help him learn it. Log4j handles situations where you need to write to a file stream, standard out, etc. simultaneously quite well.
You can check if the out
input being passed is System.out
and selectively decide not to close.
The flush()
call is necessary. Note that I am doing ==
check since the references will be identical if you were to invoke this write
method with a System.out
argument.
public void write(String txt, OutputStream out) {
PrintWriter printer = new PrintWriter(out);
printer.print(txt);
printer.flush();
if(out != System.out) {
printer.close();
}
}
But honestly, I would keep a different method for closing or call this method writeAndClose
in order to avoid the confusion.
If you want to maintain abstraction (as hinted by @Urs), do the below. But I don't see the point of this rather over-engineering
public void write(String txt, OutputStream out) {
PrintWriter printer = new PrintWriter(out);
printer.print(txt);
printer.flush();
}
public void close(OutputStream out) {
out.close();
}
If you want to try the output on JVM console, you'd better to get a Console instance with System.console()
. And then get the PrintWriter
with console.writer()
method. After using the PrintWriter
, you can close it. Based on the console description of JavaDoc,
Invoking
close()
on the objects returned by thereader()
and thewriter()
will not close the underlying stream of those objects.
You can do this again and again. No need to worrying about reopen the stream.
Do what Stas Kurilin suggests.
In general, streams should be closed by the party that opened/created them.
In your method, just flush the stream. Close it wherever it was opened when it is no longer needed.
Let's look at this from the caller's point of view.
The caller has an OutputStream
of some sort, and calls a method called write()
. After the call completes, the caller discovers that the stream has been closed.
In my view, your write()
method simply should not call printer.close()
. The latter closes the stream provided by the caller and is probably not what the caller expects.
If you need to flush the stream, you could use flush()
.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With