希望您在看這篇部落格之前,知道Java IO技術,至少寫過IO操作的Demo!
另外建議您看看: Java IO:IO流中的flush方法
這篇部落格,主要使用Socket執行個體(很簡單的小例子)來引出問題,然後在探討問題。
1. Socket服務端
package mark.zhang;import java.io.BufferedReader;import java.io.InputStream;import java.io.InputStreamReader;import java.net.ServerSocket;import java.net.Socket;public class Server {public static void main(String[] args) throws Exception {ServerSocket server = new ServerSocket(3000);Socket socket = server.accept();InputStream is = socket.getInputStream();BufferedReader br = new BufferedReader(new InputStreamReader(is));String info = br.readLine();System.out.println("server receive info: " + info);}}
2. Socket用戶端
package mark.zhang;import java.io.OutputStream;import java.io.PrintWriter;import java.net.Socket;public class Client {public static void main(String[] args) throws Exception {Socket client = new Socket("127.0.0.1", 3000);OutputStream os = client.getOutputStream();PrintWriter pw = new PrintWriter(os, true);pw.print("Hello,Server");pw.close();// pw.println("Hello,Server");}}
代碼很簡單,主要是用戶端向服務端發送一個字串,服務端將接收到的資訊列印出來。
上面代碼,運行(先運行Server.java後運行Client.java)之後,一切正常!
經過Debug,我發現,調用層次:
PrintWriter的 close()方法 ---> BufferedWriter的close()方法 ---> OutputStreamWriter的close()方法 ---> StreamEncoder的close()方法
BufferedWriter的close()方法源碼:
public void close() throws IOException {synchronized (lock) { if (out == null) {return; } try { flushBuffer(); } finally { out.close(); out = null; cb = null; }}
OutputStreamWriter的close()方法源碼:
public void close() throws IOException {se.close();}
StreamEncoder的close()方法源碼可以參看http://blog.csdn.net/androidbluetooth/article/details/6460726這篇文章的代碼。
那麼,我們做點調整,改變這些正常反應吧!修改用戶端代碼(注釋掉pw.close()):
package mark.zhang;import java.io.OutputStream;import java.io.PrintWriter;import java.net.Socket;public class Client {public static void main(String[] args) throws Exception {Socket client = new Socket("127.0.0.1", 3000);OutputStream os = client.getOutputStream();PrintWriter pw = new PrintWriter(os, true);pw.print("Hello,Server");// pw.close();// pw.println("Hello,Server");}}
服務端列印結果:
server receive info: null
也就是說,沒有接收到任何資訊。也就是說,這種方式在服務端無法接收到資訊。
惡作劇一把之後,我們再次修改代碼,如下:
package mark.zhang;import java.io.OutputStream;import java.io.PrintWriter;import java.net.Socket;public class Client {public static void main(String[] args) throws Exception {Socket client = new Socket("127.0.0.1", 3000);OutputStream os = client.getOutputStream();PrintWriter pw = new PrintWriter(os, true);//pw.print("Hello,Server");//pw.close();pw.println("Hello,Server");}}
ok,服務端可以接收到資訊。看看PrintWriter類的println方法源碼:
public void println(String x) {synchronized (lock) { print(x); println();}
public void print(String s) {if (s == null) { s = "null";}write(s);}
public void println() {newLine();}
newLine()就是加入一個斷行符號符:
private void newLine() {try { synchronized (lock) {ensureOpen();out.write(lineSeparator);if (autoFlush) out.flush(); }}catch (InterruptedIOException x) { Thread.currentThread().interrupt();}catch (IOException x) { trouble = true;} }
lineSeparator定義:
lineSeparator = (String) java.security.AccessController.doPrivileged( new sun.security.action.GetPropertyAction("line.separator"));
服務端的readLine()方法,遇到斷行符號符就讀一次資料(一行一行的讀)。所以服務端可以收到資訊。
3. 研究一下readLine
再次修改Client.java代碼:
package mark.zhang;import java.io.DataOutputStream;import java.io.OutputStream;import java.net.Socket;public class Client {public static void main(String[] args) throws Exception {Socket client = new Socket("127.0.0.1", 3000);OutputStream os = client.getOutputStream();DataOutputStream dos = new DataOutputStream(os);dos.writeBytes("Hello,Server1");dos.writeBytes("\n");dos.writeBytes("Hello,Server2");//dos.close();}}
服務端接收資訊:
server receive info: Hello,Server1
說明,沒有接收到Hello,Server2。這就說明,readLine遇到斷行符號就算讀完資訊。
關於readLine()方法的其它用法,見下代碼:
package csdn.zhwx;import java.io.BufferedReader;import java.io.BufferedWriter;import java.io.File;import java.io.FileInputStream;import java.io.FileOutputStream;import java.io.IOException;import java.io.InputStreamReader;import java.io.OutputStreamWriter;import java.nio.charset.Charset;public class BufferedReaderTest {public static void getFile(String readFilePath, String readFileName,String writeFilePath, String writeFileName) {File reaFile = null;FileInputStream fis = null;BufferedReader bReader = null;File writeFile = null;FileOutputStream fos = null;BufferedWriter bWriter = null;try {if (!(readFileName == null || readFileName.length() <= 0)) {reaFile = new File(readFilePath + readFileName);fis = new FileInputStream(reaFile);// bReader = new BufferedReader(new InputStreamReader(fis,"GBK"));//指定GBK編碼,一般用在漢化windows上。// bReader = new BufferedReader(new InputStreamReader(fis));//預設編碼,和系統編碼一致。linux為UTF-8bReader = new BufferedReader(new InputStreamReader(fis, "UTF-8"));// 指定UTF-8編碼writeFile = new File(writeFilePath, writeFileName);fos = new FileOutputStream(writeFile);// bWriter = new BufferedWriter(new OutputStreamWriter(fos));//預設編碼,和系統編碼一致。linux為UTF-8bWriter = new BufferedWriter(new OutputStreamWriter(fos,"UTF-8"));// 指定UTF-8編碼String str = null;while ((str = bReader.readLine()) != null) {// 該方法不會將讀取到的換行\n寫到檔案中// System.out.println(str);// 要自己手動加\nbWriter.write(str + "\n");/*bWriter.write(str);bWriter.newLine();*/// 不重新整理的話,檔案不會有內容bWriter.flush();}}} catch (Exception e) {e.printStackTrace();try {fis.close();bReader.close();fos.close();bWriter.close();} catch (IOException e1) {e1.printStackTrace();}} finally {try {fis.close();bReader.close();fos.close();bWriter.close();} catch (IOException e1) {e1.printStackTrace();}}}public static void main(String[] args) {getFile("/home/zhihui/Downloads/", "hello.txt","/home/zhihui/Downloads/", "copyFormHello.txt");//linux下測試/*當前JRE:1.6.0_23當前JVM的預設字元集:UTF-8*/System.out.println("當前JRE:" + System.getProperty("java.version")); System.out.println("當前JVM的預設字元集:" + Charset.defaultCharset()); }}