Java Datei-E/A

Zweipunktnull

Commander
Registriert
Dez. 2004
Beiträge
2.546
Hallo,

ich habe mal eine Frage zu E/A von Java. Ich versuche, die Ströme in finally-Blocks zu schließen, damit sie auf jeden Fall geschlossen werden. Dazu habe ich sogar ein Beispiel von Sun gefunden.

Code:
finally {
    if (out != null) { 
        System.out.println("Closing PrintWriter");
        out.close(); 
    } else { 
        System.out.println("PrintWriter not open");
    } 
}

Das Problem ist, dass das so nicht immer funktioniert. Wenn ich folgendes versuche zu kompilieren, dann gibts eine Fehlermeldung:

Code:
        BufferedWriter out = null;
        try {
            out = new BufferedWriter(new FileWriter(new File("c:\\Directory\\File.txt")));
            out.write("String");
            out.newLine();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (out != null) {
                out.close();
            }
        }

Und zwar wäre bei dem out.close() eine unbehandelte IOException. Stimmt ja auch soweit, close() kann schließlich eine werfen. Nur wie löst man das Problem dann in der Praxis? In dem finally-Block nochmals ein weiteres try-catch-finally-Konstrukt schachteln? Das kommt mir irgendwie ziemlich dämlich vor. Das Sun-Beispiel funktioniert, weil die einen PrintWriter einsetzen und dessen close() irgendwie keine IOException wirft. Nun will (muss) ich aber einen BufferedWriter und keinen PrintWriter einsetzen. Muss man dort echt noch innerhalb des finally-Blocks neue try-catch-Konstrukte machen?
 
wieso closed du nich im try block? (vllt isses acuh stuss bin noch prog noob)
aber du willst ja immer closen und dann wärs durch catch gedeckt oder?

edit: ach dann wird bei ner exception nich mehr geclosed :D wie gesagt noob und so;)
 
Zuletzt bearbeitet:
In der Java Doku steht:

close

public abstract void close()
throws IOException

Closes the stream, flushing it first. Once the stream has been closed, further write() or flush() invocations will cause an IOException to be thrown. Closing a previously closed stream has no effect.

Specified by:
close in interface Closeable

Throws:
IOException - If an I/O error occurs

Ich interpretiere das so, dass eine IOException nur auftritt, wenn der Stream geschlossen wurde und dann ein write() oder flush() darauf ausgeführt wird. Von daher stellt sich die Frage inwiefern es sinnvoll ist an der Stelle eine IOException abzufangen.

Falls an einer anderen Stelle (anderer Thread z.B) nicht sicher ist ob der Stream bereits geschlossen wurde würde ich an deren Stelle entsprechende Exceptions abfangen.
 
Seh ich genauso wie Bender.

Einfachste Weg, aber vermutlich nicht der "schönste" wäre mit Annotations hier zu arbeiten. Also über der Methode ein "@throws IOException" in Javadoc zu tippen:
/**
* @throws IOException
*/
 
Bender86 schrieb:
Ich interpretiere das so, dass eine IOException nur auftritt, wenn der Stream geschlossen wurde und dann ein write() oder flush() darauf ausgeführt wird.

Das sehe ich nicht so. Das kann ja gar nicht so funktionieren. Wenn ich einen Stream schließe und danach, also zu einem späteren Zeitpunkt, z.B. write() aufruft, dann ist es natürlich die Methode write(), die dann ggf. die entsprechende IOException wirft. Das hat dann ja mit close() nichts mehr zu tun, da diese Methode ja schon längst abgearbeitet wurde. Es ist die Methode write(), die dann wirft, wenn man sie auf einem geschlossenen Stream aufruft.

Bender86 schrieb:
Von daher stellt sich die Frage inwiefern es sinnvoll ist an der Stelle eine IOException abzufangen.

Ich weiß ja noch immer nicht, wann diese IOException überhaupt auftreten kann. Aber selbst, wenn es unsinnig wäre, so muss ich sie trotz allem abfangen, da Exceptions nun mal behandelt werden müssen und weiterreichen an dieser Stelle nicht sinnvoll wäre, da das ganze direkt in einer main-Methode steht.


Firestorm- schrieb:
Einfachste Weg, aber vermutlich nicht der "schönste" wäre mit Annotations hier zu arbeiten. Also über der Methode ein "@throws IOException" in Javadoc zu tippen:

Inwiefern das helfen soll, verstehe ich nicht. Es ist dem Compiler ja recht egal, was ich für (javadoc-)Kommentare in meinen Quelltext schreibe. Die Exception ist und bleibt unbehandelt.

EDIT: Ich schätze, ich muss tatsächlich zwei try-catchs dafür schachteln. Sah für mich halt sehr unschön aus, als müsse es quasi eine bessere Lösung geben. Aber es scheint ja nicht so zu sein...
 
Zuletzt bearbeitet:
So ginge es auch noch:

PHP:
	public void myFunction() throws IOException{
		BufferedWriter out = null;
		try{
			out = (...)
		} catch (final IOException e){
			e.printStackTrace();
		} finally{
			if( out != null ){
				out.close();
			}
		}
	}
 
Zweipunktnull schrieb:
Inwiefern das helfen soll, verstehe ich nicht. Es ist dem Compiler ja recht egal, was ich für (javadoc-)Kommentare in meinen Quelltext schreibe. Die Exception ist und bleibt unbehandelt.

Du hast das scheinbar nicht verstanden. Eine Annotation @throws IOException sagt nichts anderes als dass deine Methode eine IOException schmeissen kann. D.h. es wird direkt nach deinem Funktionsnamen ein throws IOException hinten angesetzt.

Kleiner Auszug zu dem was ich meine aus den Java Tutorials:
PHP:
To specify that writeList can throw two exceptions, add a throws clause to the method declaration for the writeList method. 
The throws clause comprises the throws keyword followed by a comma-separated list of all the exceptions thrown by that method. 
The clause goes after the method name and argument list and before the brace that defines the scope of the method; here's an example.

    public void writeList() throws IOException,
                                   ArrayIndexOutOfBoundsException {
 
Natürlich kann eine Exception nicht nur in der betreffenden Methode behandelt, sondern auch einfach an den Aufrufer weitergeleitet werden. Dazu muss einfach im Kopf der betreffenden Methode eine throws-Klausel eingeführt werden. Das halt aber nichts mit Annotations oder gar javadoc-Kommentaren zu tun. Das einzige, worauf es ankommt, ist, dass die besagte throws-Klausel im betreffenden Methodenkopf steht - ob dort noch irgendwo ein javadoc-Kommentar stehen, ist ausschließlich für die Dokumentation interessant, hat aber nichts mit dem eigentlichen Programm zu tun. Bei folgendem Beispiel würde der Compiler noch immer eine Fehlermeldung ausgeben, da der javadoc-Kommentar eben keinen Einfluss auf das eigentliche Progamm hat. Es ist nun mal ausschließlich ein reiner Dokumentationskommentar.

Code:
    /**
     * @throws IOException
     */
    public void schreibe() {
        BufferedWriter out = null;
        try {
            out = new BufferedWriter(new FileWriter(new File("c:\\Directory\\File.txt")));
            out.write("String");
            out.newLine();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (out != null) {
                out.close();
            }
        }
    }

Wohingegen das folgende Beispiel sich kompilieren lassen würde, da dort die besagte throws-Kausel im Kopf der betreffenden Methode zu finden ist. Annotations oder ein javadoc-Kommentar fehlt hier, was ja aber auch absolut gleichgültig ist, da dieser nur für die Dokumentation interessant ist.

Code:
    public void schreibe() throws IOException {
        BufferedWriter out = null;
        try {
            out = new BufferedWriter(new FileWriter(new File("c:\\Directory\\File.txt")));
            out.write("String");
            out.newLine();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (out != null) {
                out.close();
            }
        }
    }

Daher konnte ich mit deinem Post nicht viel anfangen, da ein javadoc-Kommentar nichts am Problem ändert. Eine throws-Klausel im Methodenkopf ist natürlich möglich, scheidet aber wie bereits gesagt aus, da das Ganze in einer main-Methode steht.
 
Hm ich dachte das versteht sich von selbst, hätte es aber wohl scheinbar hinschreiben sollen.
 
Javadoc schrieb:
Closes the stream, flushing it first. Once the stream has been closed, further write() or flush() invocations will cause an IOException to be thrown. Closing a previously closed stream has no effect.

Bender86 schrieb:
Ich interpretiere das so, dass eine IOException nur auftritt, wenn der Stream geschlossen wurde und dann ein write() oder flush() darauf ausgeführt wird.

Zweipunktnull schrieb:
Ich weiß ja noch immer nicht, wann diese IOException überhaupt auftreten kann

Also ich vermute ja, dass die Java-Doku etwas missverständlich geschrieben wurde und es bedeuten soll, dass write() oder flush() nach close() ein Fall ist, wo eine IOException geworfen wird, aber diese wird natürlich dann von write() oder flush() geworfen... und es gibt noch andere Szenarios, in denen close() eine IOException wirft, die aber nicht in deiser Doku genannt werden. Ich weiß es auch nicht genau, aber vielleicht tritt das auf, wenn der File Descriptor aus irgendeinem (internen) Fehlergrund (Hardware? out of memory?) abbricht, bevor man selber aktiv close() aufruft. Gerade bei Netzwerkstreams macht das auf jeden Fall Sinn.
 
Zurück
Oben