2015-11-05 2 views
19

System.in ist der "Standard" -Eingabestrom, der Benutzereingabedaten liefert. Nach dem Schließen kann dieser Stream nicht mehr geöffnet werden. Ein solches Beispiel ist in dem Fall der Verwendung eines Scanners die Benutzereingabe zu lesen wie folgt:Warum ist es nicht möglich, einen geschlossenen (Standard) Stream wieder zu öffnen?

public class Test { 
    public static void main(String[] args) { 

     boolean finished; 

     do { 
      Scanner inputScanner = new Scanner(System.in); 
      finished = inputScanner.hasNext("exit"); 
      boolean validNumber = inputScanner.hasNextDouble(); 
      if (validNumber) { 
       double number = inputScanner.nextDouble(); 

       System.out.print(number); 
      } else if (!finished) { 
       System.out.println("Please try again."); 
      } 
      inputScanner.close(); 
     } while (!finished); 
    } 
} 

In diesem Beispiel wird eine Instanz vom Typ Scanner erstellt und verwendet, um eine Reihe von Zahlen vom Benutzer zu lesen (bitte ignoriere andere Details mit diesem Code, die über den Rahmen dieses Beispiels hinausgehen. Ich weiß, dass der Scanner außerhalb der Schleife erstellt und geschlossen werden sollte. Nachdem eine Nummer von der Benutzereingabe abgerufen wurde, wird die Instanz dieses Scanner (d. H. Der Eingabestrom) geschlossen. Wenn jedoch eine andere Nummer vom Benutzer angefordert wird und eine neue Instanz erstellt wird, kann der Eingabestream nicht erneut geöffnet werden. Im Fall dieses Beispiels wird eine Endlosschleife erstellt.

Die Frage ist: Warum ist es nicht möglich, einen geschlossenen Strom zu öffnen?

+0

Da seine Ressourcen bereits wieder auf das Betriebssystem freigegeben wurden. Warum öffnen Sie den Scanner nicht und schließen ihn außerhalb der Schleife? – RealSkeptic

+0

Ich werde nur Ihre [vorherige Frage] (http://stackoverflow.com/questions/33552505/scanner-continuous-loop) verknüpfen. Beide Fragen sind keine Duplikate, denn auf der ersten Frage stand "Warum wird mein Scanner nicht nach jeder Iteration blockiert". Aber es gibt einige nützliche Tipps und Erklärungen, die für zukünftige Leser nützlich sein könnten, wenn es um die Frage geht, "warum Sie einen geschlossenen Stream nicht wieder öffnen können". – Frakcool

Antwort

23

Warum ist es nicht möglich, einen geschlossenen Stream in Java wieder zu öffnen?

Das ist einfach die Art der zugrunde liegenden Betriebssystemkonstrukte, die Java-Streams darstellen. Ein Stream ist im Wesentlichen ein Datenkanal. Sobald Sie es schließen, existiert es nicht mehr. Sie können möglicherweise einen neuen zwischen denselben Endpunkten erstellen, der jedoch einen grundlegend anderen Stream ergibt. Wir könnten auf Implementierungsüberlegungen wie Pufferung und Stream-Positionierung eingehen, aber das sind wirklich Nebenprobleme.

Sie fragte auch speziell über die Standard-Streams. Dies sind einige der Fälle, die Sie nicht neu erstellen können. Das Betriebssystem stellt jedem Prozess seine Standard-Streams zur Verfügung. Sobald sie geschlossen sind, gibt es keine Möglichkeit, Äquivalente zu erhalten. Sie können verschiedene Streams an ihre Stelle setzen, aber Sie können sie nicht mit den ursprünglichen Endpunkten verbinden.

+0

Ich kann der Tatsache zustimmen, dass, sobald Sie einen Stream schließen, es nicht mehr existiert. Der Benutzer oder der Java-Prozess sollte jedoch einen dedizierten Stream haben, den er verwenden kann oder nicht ("dediziert" ist hier zu schwer). Basierend auf Endpunkten sollte der Benutzer in der Lage sein, den Stream neu zu erstellen. Scheint vernünftig genug, und es passiert in typischen Client-Server-Architektur. Ich kann eine Webseite laden und meine Finger an STRG + R Tasten kleben, und der Server wird immer noch neue Anfragen von Clients annehmen. Ich habe die Endpunkte, ich sollte den Stream neu erstellen können. Was vermisse ich? – Andrei

+2

Wie gesagt: Wenn Sie die Endpunkte eines Streams haben, dann * können * Sie möglicherweise einen Ersatzstrom zwischen denselben Endpunkten erzeugen. Aber mit den Standard-Streams kennen Sie nur einen Endpunkt: Ihr eigenes Programm. Oder nehmen Sie das Beispiel Ihres Webservers: Der * Server * kann einen Stream mit einem Client nicht wiederherstellen, sobald beide Enden ihn schließen. Selbst der Klient kann sich dessen nicht sicher sein. Angenommen, der Server ist Teil einer Serverfarm mit Lastenausgleich. Wenn der Client eine neue Anforderung ausgibt, wird möglicherweise nicht auf denselben physischen Server zugegriffen. –

+0

Allgemeiner gesagt, jeder Strom, der an IPC beteiligt ist, der die Standardströme die meiste Zeit umfasst, muss kooperativ durch die beteiligten Prozesse eingerichtet werden. Sobald ein solcher Strom auf beiden Seiten geschlossen ist, kann er nicht mehr einseitig wiederhergestellt werden. –

0

Die Java-Standardbibliothek hat einen "standardisierten" Ansatz für InputStream gewählt. Selbst wenn Sie einige Datenströme, wie z. B. Daten, die von der Eingabekonsole eingehen, als logisch wieder zu öffnen anerkennen, stellt der InputStream einen generischen Ansatz dar, da er alle möglichen von ihnen abdecken soll Natur nicht wieder zu öffnen. Wie in @ JohnBollingers Antwort perfekt beschrieben.

3

Da Streams unbegrenzt sind. Sie sehen Werte aus Streams nach Bedarf. Wenn Sie fertig sind, schließen Sie es einfach. Streams enthält nicht alle Daten im Speicher. Streams wurden entwickelt, um relativ große Datenmengen zu verarbeiten, die nicht im Speicher gehalten werden können. Sie können also einen Stream nicht einfach wieder öffnen, weil Sie bereits eine Schleife darüber gemacht haben und alle Daten erschöpft haben. Da der Stream diese Daten nicht im Speicher hält. Sie sind einfach verloren und deshalb können Sie es nicht wieder öffnen. Je besser Sie einen neuen Stream erstellen, als einen vorhandenen zu öffnen.

8

Wenn Sie den Standardeingabestream schließen:

  • Wenn Sie Ihre Eingabe durch ein Rohr zur Verfügung gestellt wurde, das andere Ende der Leitung mitgeteilt wird. Es schließt sein Ende und hört auf, Daten zu senden. Es gibt keine Möglichkeit zu sagen, dass du einen Fehler gemacht hast, und es sollte erneut gesendet werden.

  • Wenn Ihre Eingabe von einer Datei bereitgestellt wurde, löscht das Betriebssystem den Verweis auf die Datei und vergisst vollständig, dass Sie es verwendet haben.Es gibt einfach keine Möglichkeit, die Standardeingabe erneut zu öffnen und weiter zu lesen.

  • Wenn Ihre Eingabe von der Konsole bereitgestellt wurde, funktioniert sie mit einer Pipe. Die Konsole wird benachrichtigt, schließt das Ende der Pipe und hört auf, Ihnen Daten zu senden.

Es gibt also keine Möglichkeit, die Standardeingabe wieder zu öffnen.

ABER ... es gibt auch keinen Grund zu zu schließen Standard-Eingang, also tun Sie das nicht!

Ein gutes Muster zu folgen ist:

  • Der Code oder Klasse, die öffnet eine Datei zum Schließen sie verantwortlich ist.

  • Wenn Sie einen InputStream an eine andere Methode übergeben, die davon liest, sollte diese Methode nicht schließen. Überlassen Sie das dem Code, der es geöffnet hat. Es ist wie die Ströme Besitzer.

  • Wenn Sie einen OutputStream an eine andere Methode übergeben, die darauf schreibt, sollte die Methode nicht schließen. Überlassen Sie das dem Code, der es besitzt. ABER wenn Sie den Stream in andere Klassen, die einige Daten puffern können do Anruf .flush() auf ihnen um sicherzustellen, dass alles herauskommt!

  • Wenn Sie Ihre eigenen Wrapper-Klassen um InputStream und OutputStream schreiben, schließen Sie nicht den Delegate-Stream in Ihrem Finalizer. Wenn ein Stream während des GC bereinigt werden muss, sollte dies selbst erledigt werden.

In Ihrem Beispielcode, schließen Sie nicht diesen Scanner. Sie haben die Standardeingabe nicht geöffnet, daher sollten Sie sie nicht schließen.

+0

Mit re-open möchte ich einen Stream erstellen. Ich verstehe aus allen Antworten, dass es nicht möglich ist, wieder zu öffnen, weil Sie die ursprünglichen Verweise auf die Endpunkte verlieren. Wenn wir uns jedoch den obigen Beispielcode ansehen, sollte jedes Mal, wenn die Schleife ausgeführt wird, ein neuer Stream erstellt werden. Denken Sie an Ihren Browser und eine Internetseite. Wenn Sie diese Seite einmal laden, was hält Sie davon ab, die Seite zu aktualisieren? – Andrei

+0

Der Unterschied ist, dass Ihr Browser eine URL hat, die ihm sagt, wie er auf die Seite gelangt. Ihr Prozess hat keine Informationen darüber, auf was sich die Standardeingabedatei bezieht. Die zugrunde liegende Datei wird tatsächlich vom übergeordneten Prozess (demjenigen, der Ihren erstellt) geöffnet, und Sie erhalten nur einen Handle für die geöffnete Datei. Das ist eine Nummer, die die Datei im Betriebssystem identifiziert. Es sind keine Anweisungen zum Erstellen eines Streams, wie es bei einer URL der Fall ist. –

Verwandte Themen