Java學習IO篇


        來吧,同志們,為復習網絡編程做准備……

一.理論准備

        流是個抽象的概念,是對輸入輸出設備的抽象,Java程序中,對於數據的輸入/輸出操作都是以“流”的方式進行,設備可以是文件、網絡、內存等。流具有方向性,至於是輸入流還是輸出流則是一個相對的概念,一般以程序(小馬哥說的是機器)為參考,如果數據的流向是程序至設備,我們成為輸出流,反之我們稱為輸入流,可以將流想象成一個“水流管道”(很多資料都這么講的),自然就出現了方向的概念。

        流把I/O設備內部的具體操作給隱藏起來了。所有InputStream和Reader的派生類都有一個基本的,繼承下來的,能讀取單個或byte數組的read( )方法。

        Java分為字節流(Stream結尾)和字符流(Reader、Write結尾),再分為輸入流(InputStream、Reader)和輸出流(OutputStream、Write),輸入輸出相對於內存而言。在讀字符的時候用字符流,如文本文件、XML(我想xml明明是字母字符組成的,屬於ASCII文件,為何不用stream讀取呢?)等。在讀二進制文件時候用字節流,如RAR、EXE等不是文本以外的文件(圖片)。Buffered開頭的流只是加了緩沖區,為了讀寫提高效率。字符流不能直接輸出,需要轉換成字節流才能輸出(這個確實是剛知道的)!

        Java 2 SDK中有三種基本類型的節點:文件(file)、內存(memory)、管道(pipe)。

下面來看鄭莉教材上IO章節的那個經典圖片。

        繼承自InputStream/OutputStream的流都是用於向程序中輸入/輸出數據,且數據的單位都是字節(byte=8bit),如圖,深色的為節點流,淺色的為處理流。

1_thumb2

          繼承自Reader/Writer的流都是用於向程序中輸入/輸出數據,且數據的單位都是字符(2byte=16bit),如圖,深色的為節點流,淺色的為處理流。

2_thumb2

二.用法分析

        Java IO的一般使用原則(部分來自百度文庫):
        (1) 按數據來源(去向)分類:

  1. 是文件: FileInputStream, FileOutputStream, FileReader, FileWriter
  2. 是byte[]:ByteArrayInputStream, ByteArrayOutputStream
  3. 是Char[]: CharArrayReader, CharArrayWriter
  4. 是String: StringBufferInputStream, StringReader, StringWriter
  5. 網絡數據流:InputStream, OutputStream, Reader, Writer
    (2) 按是否格式化輸出分:
    要格式化輸出:PrintStream, PrintWriter
    (3) 按是否要緩沖分:
    要緩沖:BufferedInputStream, BufferedOutputStream, BufferedReader, BufferedWriter。

       (4) 按數據格式分:

  1. 二進制格式(只要不能確定是純文本的): InputStream, OutputStream及其所有帶Stream結束的子類
  2. 純文本格式(含純英文與漢字或其他編碼方式);Reader, Writer及其所有帶Reader, Writer的子類
    (5) 按輸入輸出分:
    輸入:Reader, InputStream類型的子類;輸出:Writer, OutputStream類型的子類
    (6) 特殊需要:

  1. 從Stream到Reader,Writer的轉換類:InputStreamReader, OutputStreamWriter
  2. 對象輸入輸出:ObjectInputStream, ObjectOutputStream
  3. 進程間通信:PipeInputStream, PipeOutputStream, PipeReader, PipeWriter
  4. 合並輸入:SequenceInputStream
  5. 更特殊的需要:PushbackInputStream, PushbackReader, LineNumberInputStream, LineNumberReader

       (7) 決定使用哪個類以及它的構造進程的一般准則如下(不考慮特殊需要):
考慮最原始的數據格式是什么:是否為文本?是輸入還是輸出?是否需要轉換流:InputStreamReader, OutputStreamWriter?數據來源(去向)是什么:文件?內存?網絡?是否要緩沖:bufferedReader (特別注明:一定要注意的是readLine()是否有定義,有什么比read, write更特殊的輸入或輸出方法)是否要格式化輸出:print。

三.若干實例

         還是寒假時候寫的,權當復習了,折疊代碼的插件找不到了,先看着吧。

         1.System.in

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
/*
 * System.in是InputStream static final的,包含了多態,叫同步式或者阻塞式
 * 讀取ASCII和二進制文件(圖片),而字母就是ASCII字符(個人理解)。
 */
public class TestSystemIn {
	public static void main(String[] args) {
		InputStreamReader isr = new InputStreamReader(System.in);
		BufferedReader br = new BufferedReader(isr);//有readline
		String s = null;
		try {
			s = br.readLine();
			while(s!=null) {
				if(s.equalsIgnoreCase("exit")) {
					break;
				}
				System.out.println(s.toUpperCase());
				s = br.readLine();
			}
			br.close();
		}catch (IOException e) {
			e.printStackTrace();
		}
	}
}

           2.buffer

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
public class TestBuffer {
	public static void main(String[] args) {
		try {
			//查看修改日就可以判斷文件是否是新建的了
			BufferedWriter bw = new BufferedWriter(new FileWriter("d:/java.txt"));
			BufferedReader br = new BufferedReader(new FileReader("d:/java.txt"));
			String s = null;
			for(int i=1; i<100; i++) {
				s = String.valueOf(Math.random());
				bw.write(s);
				bw.newLine();//換行
			}
			//刷新該流的緩沖,br沒有該方法
			bw.flush();
			while((s=br.readLine())!=null) {
				System.out.println(s);
			}
			bw.close();
			br.close();
		}catch (IOException e) {
			e.printStackTrace();
		}
	}
}

             3.FileInputStream

import java.io.*;
public class TestFileInputStream {
	public static void main(String[] args) {
		FileInputStream in = null;
		try {
			in = new FileInputStream("e:/1.txt");
		}catch(FileNotFoundException e) {
			System.out.println("找不到文件");
			System.exit(-1);
		}
		//下面表示找到了文件
		int tag = 0;
		try {
			long num = 0;
			while((tag = in.read())!=-1) {
				//read是字節流,若是有漢字就顯示不正常了,使用reader就解決了
				System.out.print((char)tag);
				num++;
			}
			in.close();
			System.out.println();
			System.out.println("共讀取了" + num + "字符");
		}catch(IOException e1) {//read和close都會拋出IOException
			System.out.println("文件讀取錯誤");
			System.exit(-1);
		}
	}
}

              4.FileOutputStream實現復制功能

import java.io.*;
/*
 * 實現復制功能
 */
public class TestFileOutputStream {
	public static void main(String[] args) {
		int b = 0;
		FileInputStream in = null;
		FileOutputStream out = null;
		try {
			in = new FileInputStream("d:/java.txt");
			//下面的若是不存在的話會自動建立
			out = new FileOutputStream("d:/my_java.txt");
			while((b=in.read())!=-1) {
				out.write(b);
			}
			in.close();
			out.close();
		}catch(FileNotFoundException e) {
			System.out.println("找不到指定文件");
			System.exit(-1);
		}catch(IOException e1) {
			System.out.println("文件復制錯誤");
			System.exit(-1);
		
		}
		System.out.println("文件已復制");	
	}
}

              5.ObjectOutputStream與Serializable

import java.io.*;
/*
 * transient(透明的),可以用來修飾成員變量,
 * 當進行序列化時不予考慮,修飾int 的話,不管原來的值是多少
 * 輸出的就是0
 */
public class TestObjectIO {
	public static void main(String[] args) throws Exception {
		T t = new T();
		t.k = 8;
		FileOutputStream fos = new FileOutputStream("d:/1.txt");
		ObjectOutputStream oos = new ObjectOutputStream(fos);
		oos.writeObject(t);
		oos.flush();
		oos.close();
		
		FileInputStream fis = new FileInputStream("d:/1.txt");
		ObjectInputStream ois = new ObjectInputStream(fis);
		T tRead = (T)ois.readObject();
		System.out.println(tRead.i + " " + tRead.j + " " + tRead.k);
	}
}
class T implements Serializable {
	int i = 10;
	int j = 9;
	double d = 2.3;
	int k = 15;
}

             6.轉換編碼方式

import java.io.*;
/*
 * 中文windows默認GBK編碼方式
 * 追加的內容顯示為問號,不知道咋回事
 */
public class TestTransForm {
	public static void main(String[] args) {
		try {
			OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("d:/java.txt"));
			osw.write("你好123");//可以直接寫入字符串,包括中文,因為外邊的是字符流
			System.out.println("編碼方式:" + osw.getEncoding());//ISO8859_1是西歐語言,又叫latin-1,此時未考慮東方人,國標(ISO)為Unicode
			osw.close();
			osw = new OutputStreamWriter(new FileOutputStream("d:/java.txt",true),"ISO8859_1");//true表示追加
			osw.write("這是追加的內容");
			System.out.println("編碼方式:" + osw.getEncoding());
			osw.close();
		}catch(IOException e) {
			e.printStackTrace();
		}
	}
}

               7.輸出重定向

import java.io.*;
/*
 * Print流屬於輸出流,提供了重載的方法很多,
 * PrintWriter和PrintStream不會拋異常,用戶通過檢測錯誤狀態獲取信息,
 * 包含自動flush功能,有什么用呢,在jsp里也要輸出一些東西,
 * 但不必每次拋異常。
 */
public class TestPrintStream {
	public static void main(String[] args) {
		PrintStream ps = null;
		try {
			FileOutputStream fos = new FileOutputStream("d:/java.txt");
			ps = new PrintStream(fos);
			
		}catch (IOException e) {
			e.printStackTrace();
		}
		if(ps!=null) {
			System.setOut(ps);//輸出重定向
		}
		int ln = 0;
		for(char c=0; c<65535; c++) {
			System.out.print(c + " ");
			if(ln++>100) {
				System.out.println();
				ln = 0; 
			}
		}
	}
}

              8.DataStream

import java.io.*;
public class TestDataStream {
	public static void main(String[] args) {
	//先在內存里分配一個字節數組,再有一個	OutputStream,再加上一個數據流
		ByteArrayOutputStream baos = new ByteArrayOutputStream();
		DataOutputStream dos = new DataOutputStream(baos);
		try {//寫出讀入
			dos.writeDouble(Math.random());
			dos.writeBoolean(true);
			ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
			System.out.println(bais.available());//共幾個字節可用
			DataInputStream dis = new DataInputStream(bais);
			////先寫的先讀(隊列),下面這兩個輸出不可以調換,否則就先輸出了double里的一個字節
			System.out.println(dis.readDouble());
			System.out.println(dis.readBoolean());
			dos.close();
			dis.close();
		}catch (IOException e) {
			e.printStackTrace();	
		}
	}
}

四.小問題

         為什么Writer/Reader不繼承自Stream呢?字符最終也要轉換成二進制呀。Writer/Readre繼承OutputStream/InputStream,這樣的繼承層次不是更好,為什么要單獨做一個呢,而且Stream也有些子類能夠實現字符串的讀寫。大神回答:單一職責。太牽強了。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM