2012-01-20 21 views
18

Estaba ayudando a un amigo a escribir código Java, que no sabe mucho sobre Java. Así que le escribí algunas funciones auxiliares para hacer fácilmente cosas que son un poco peculiares en sus ojos. Una de ellas es una función que escribe una cadena en OutputStream. Eche un vistazo:System.out cerrado? ¿Puedo volver a abrirlo?

public void write(String txt, OutputStream out) { 
    PrintWriter printer = new PrintWriter(out); 
    printer.print(txt); 
    printer.close(); 
} 

Ahora, puede usarlo fácilmente de diferentes maneras para escribir donde desee. Por ejemplo, podría hacer eso:

(new StreamHelper()).write("Hello Test", System.out); 

Hacer eso descubrí que después System.out.println() no escribe nada a la cáscara más. Así que creo que tal vez printer.close() también cerró automáticamente System.out y me pregunto cómo reactivarlo para poder usarlo después de que esta función haya terminado nuevamente.

¿Mi suposición es correcta? (¿Cómo podría haberlo descubierto sin preguntar aquí?)

¿Cómo puedo seguir usando System.out después de una llamada a la función write()?

¿Hay mejores formas de escribir tal función auxiliar?

+3

I cree, puede usar 'flush' en lugar de' close() '. –

+1

Puede hacer que el método 'write' sea estático' para evitar la llamada fea' (new StreamHelper()) write (...) '. – adarshr

+2

@Stas Kurilin: Lo intenté y funciona. – erikbwork

Respuesta

19

El contrato general para OutputStream 's cercanos:

public void close() throws IOException Cierra esta corriente de salida y libera los recursos del sistema asociados a esta corriente. El contrato general de cierre es que cierra el flujo de salida. Una secuencia cerrada no puede realizar operaciones de salida y no se puede volver a abrir.

PrintStream 's

public void close() Cierra la corriente. Esto se hace al enjuagar la secuencia y luego cerrando la corriente de salida subyacente.

El único consejo que puedo dar es que no se debe escribir asymmetrical code, es decir, no delegar el cierre de los recursos de su código ha creado a otro lugar.

Incluso si en su caso podría parecer sensato cerrar la secuencia contenedora, el hecho es que no debería hacerlo porque está cerrando una secuencia abierta en otro lugar.

En resumen:

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(); 
} 

Ah, y por cierto, es posible que desee cambiar el nombre de la función a print, en lugar de write.

+0

Creo que contiene una respuesta y también una solución al problema. Gracias por la ayuda de todos! – erikbwork

3

System.out es un PrintStream, por lo tanto el código que usted proporciona arriba no tiene literalmente ninguna ventaja sobre simplemente llamar al System.out.print directamente. La razón por la que ya no está escribiendo es que close sí, de hecho, cerró System.out.

Si esto es para iniciar sesión, aprenda log4j para su amigo o ayúdelo a aprenderlo. Log4j maneja situaciones donde necesita escribir en una secuencia de archivos, salida estándar, etc. simultáneamente bastante bien.

+2

No del todo. Tiene la ventaja de la abstracción, ya que puedes usar System.out y cualquier otra transmisión. –

+2

A menos que tenga un caso de uso donde esa abstracción podría ser realmente necesaria, como el registro, no tiene sentido. –

3

Puede verificar si la entrada out que se está pasando es System.out y selectivamente decide no cerrar.

La llamada flush() es necesaria. Tenga en cuenta que estoy haciendo el control == ya que las referencias serán idénticas si invocó este método write con un argumento System.out.

public void write(String txt, OutputStream out) { 
    PrintWriter printer = new PrintWriter(out); 
    printer.print(txt); 
    printer.flush(); 

    if(out != System.out) { 
     printer.close(); 
    } 
} 

Pero, honestamente, me quedaría con un método diferente para el cierre o llamar a este método writeAndClose con el fin de evitar la confusión.

Si desea mantener la abstracción (como lo indica @Urs), haga lo siguiente. Pero no veo el punto de este lugar sobre-ingeniería

public void write(String txt, OutputStream out) { 
    PrintWriter printer = new PrintWriter(out); 
    printer.print(txt); 
    printer.flush(); 
} 

public void close(OutputStream out) { 
    out.close(); 
} 
+0

Rompe con la abstracción que creó la solución original. –

2

Veamos esto desde el punto de vista de la persona que llama.

La persona que llama tiene un OutputStream de algún tipo, y llama a un método llamado write(). Una vez que se completa la llamada, la persona que llama descubre que la transmisión se ha cerrado.

En mi opinión, su método write() simplemente no debe llamar al printer.close(). Este último cierra el flujo proporcionado por la persona que llama y probablemente no sea lo que espera la persona que llama.

Si necesita enjuagar la corriente, puede usar flush().

2

Haz lo que sugiere Stas Kurilin.

En general, las secuencias deben ser cerradas por la parte que las abrió/creó.

En su método, simplemente descargue la corriente. Ciérrelo donde sea que se haya abierto cuando ya no sea necesario.

+0

+1 para promocionar mi nombre) –

9

Como han dicho los demás, un flujo debe estar cerrado donde se abrió. Sin embargo, se podría escribir una fachada para proteger un flujo de conseguir cerrado así:

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(); 
    } 
} 

y utilizar de esta manera:

new StreamHelper().write("Hello Test", new UnclosableOutputStream(System.out)); 

puede ser útil para los escenarios de prueba, etc.

Cuestiones relacionadas