2015-11-05 13 views
19

System.inは、ユーザ入力データを供給する「標準」入力ストリームです。一旦閉鎖されると、この流れは再び開くことができない。その一例は次のようにユーザ入力を読み取るようにスキャナを用いた場合であり、この例ではクローズド(標準)ストリームを再度開くことができないのはなぜですか?

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

、タイプScannerのインスタンスが作成され、ユーザからの一連の数字を読み取るために使用される(ください他の詳細は無視してください。このコードはこの例の範囲を超えています。私はスキャナを作成してループの外側で閉じなければなりません)。数字がユーザ入力から検索された後、このScannerのインスタンス(すなわち、入力ストリーム)は閉じられる。ただし、別の番号がユーザーから要求され、新しいインスタンスが作成されると、入力ストリームを再び開くことができません。この例の場合、無限ループを作成します。

質問です:なぜ、閉じたストリームを再開することができないのですか?

+0

そのリソースはすでにオペレーティングシステムにリリースされているためです。なぜあなたはスキャナを開き、ループの外でそれを閉じていないのですか? – RealSkeptic

+0

私はあなたの[前の質問](http://stackoverflow.com/questions/33552505/scanner-continuous-loop)にリンクします。両方の質問は重複していません。なぜなら、最初の質問は「各繰り返し後にスキャナがブロックされない理由」だったからです。しかし、今後の読者には、なぜ「クローズドストリームを再開できないのか」という質問についての有益なヒントや説明があります。 – Frakcool

答えて

23

なぜJavaで閉じられたストリームを再開することができないのですか?

これは、Javaストリームが表す基本的なオペレーティングシステム構成の性質です。ストリームは基本的にデータコンジットです。一度閉じると、もう存在しません。同じエンドポイントの間に新しいものを作成することはできますが、基本的に異なるストリームが生成されます。私たちは、バッファリングやストリームの位置付けなどの実装上の考慮事項に入ることができますが、それは実際には副次的な問題です。

また、標準ストリームについても具体的に質問しました。これは再作成できない場合のいくつかです。オペレーティングシステムは、各プロセスに一連の標準ストリームを提供します。それらが閉じられると、等価物を得る方法はありません。別のストリームを配置することはできますが、元のエンドポイントに接続することはできません。

+0

一度ストリームを閉じると、それはもはや存在しないという事実に同意することができます。ただし、ユーザーまたはJavaプロセスには、使用できる専用ストリームが必要です(ここでは「専用」は重すぎます)。エンドポイントに基づいて、ユーザーはストリームを再作成できるはずです。十分に妥当と思われ、典型的なクライアント - サーバーアーキテクチャで発生します。私はWebページを読み込み、Ctrl + Rキーで指を貼り付けても、サーバーは依然としてクライアントからの新しい要求を受け入れます。私はエンドポイントを持っている、私はストリームを再作成する必要があります。私は何が欠けていますか? – Andrei

+2

私が言ったように、もしあなたがストリームのエンドポイントを持っていれば、それらのエンドポイント間で代替ストリームを作ることができるかもしれません。しかし、標準ストリームでは、自分のプログラムであるエンドポイントしか知りません。または、Webサーバーの例を取っ​​てください。*サーバー*は、クライアントが終了するとクライアントとのストリームを再確立できません。クライアントでさえ、そのことについては確信できません。サーバーが負荷分散されたサーバーファームの一部であるとします。クライアントが新しい要求を発行すると、同じ物理サーバーに移動しない可能性があります。 –

+0

より一般的には、標準ストリームを含むIPCに関係するすべてのストリームは、関係するプロセスによって協調的に確立されなければなりません。そのようなストリームがいずれかの側で閉じられると、それを一方的に再作成することはできません。 –

0

Javaの標準ライブラリはInputStreamに「標準化」のアプローチを選択しました。あなたが合法的に論理的に再開閉可能な、このような入力コンソールからの入力データとして、いくつかのストリームを、感じることがあってもそれらの多くは自分のことであり、すべての可能なInputStream Sを、カバーすることを意図しているとして、InputStreamは、一般的なアプローチを表し、自然は再オープンできません。 @ JohnBollingerの答えに完全に記述されています。

3

ストリームは無制限なので、必要に応じてストリームから値を確認します。完了したら、それを閉じます。ストリームはメモリ内のすべてのデータを保持しません。ストリームは、メモリに保持できない比較的大量のデータを処理するように設計されています。ストリームをもう一度開くことはできません。すでにストリームをループしてすべてのデータを使い果たしたからです。ストリームはこれらのデータをメモリに保持しないため。彼らは単に失われているので、それを再開することはできません。既存のストリームを再オープンするよりも新しいストリームを作成する方が良いでしょう。

8

あなたが標準入力ストリーム閉じるとき:あなたの入力がパイプで提供されていた場合

  • を、パイプの他端が通知されます。終了し、データの送信を停止します。間違いを犯したことを伝える方法はなく、もう一度送信を開始する必要があります。あなたの入力がファイルによって提供されていた場合

  • 、OSはファイルへの参照を削除し、完全にあなたがそれを使用していたことを忘れてしまいます。標準入力を再開して読み続ける方法はありません。

  • あなたの入力がコンソールによって提供されていた場合、それはパイプで動作します。コンソールに通知され、パイプの終わりを閉じてデータの送信を停止します。

標準入力を再開する方法はありません。

でも、理由はありません閉じる標準入力ですので、やめてください!

フォローする良好なパターンがある:ファイルを開く

  • コードやクラスがそれを閉じるための責任があります。

  • InputStreamを読み取る別のメソッドに渡すと、そのメソッドはでなく、になります。それを開いたコードにそのままにしておきます。ストリームの所有者のようです。

  • 同様に、OutputStreamを書き込む別のメソッドに渡すと、そのメソッドはでなく、になります。それを所有するコードに任せてください。しかし、ストリームをいくつかのデータをバッファリングする可能性のあるクラスにラップする場合do呼び出すにはすべて.flush()を呼び出してください。

  • InputStreamとOutputStreamについて独自のラッパークラスを作成している場合は、ファイナライザでデリゲートストリームを閉じないでください。ストリームがGC中にクリーンアップされる必要がある場合は、そのストリームを処理する必要があります。

このコードでは、そのスキャナを閉じないでください。あなたは標準入力を開かなかったので、それを閉じる必要はありません。

+0

再オープンすると、ストリームを作成することを意味します。私はすべての回答から、エンドポイントへの最初の参照を失うため、再オープンすることはできないことを理解しています。しかし、上記のサンプルコードを見ると、ループが実行されるたびに新しいストリームが作成されるはずです。あなたのブラウザとインターネットのページを考えてみましょう。そのページを一度ロードすると、そのページをリフレッシュすることができますか? – Andrei

+0

違いは、ブラウザにページの取得方法を示すURLがあることです。プロセスには、標準入力ファイルが何を参照しているかについての情報はありません。基礎となるファイルは、実際に親プロセス(親プロセスを作成するプロセス)によって開かれ、開いたファイルのハンドルを取得するだけです。これは、OSのファイルを識別する番号です。これは、URLのようなストリームを作成するための一連の命令ではありません。 –

関連する問題