Java基礎——I/O流

桃華月禪發表於2014-12-08

I/O流

IO流(資料流):對儲存裝置上的資料進行操作

硬碟磁頭:複製檔案時這裡複製一次,磁頭在這邊複製一次;寫入的那邊,寫一次磁頭移動到那邊又寫一次。硬體移動花費時間量大,所以複製時要定義足夠大的緩衝區來一次性複製大量資料並寫入,大大增加效率

I/O流輸入輸出裝置區分:

源裝置:記憶體System.in、硬碟FileStream、鍵盤ArrayStream

目的裝置:記憶體System.out、硬碟File Stream、控制檯ArrayStream

字元流結構圖:


位元組流結構圖:


I/O流其他結構圖:


1.編碼表

1.碼錶分類

(1)ACCII碼錶:美國資訊標準交換碼,用二進位制數字表示英文字元

(2)GB2312碼錶:中國碼錶,用二進位制陣列表示中國漢字

(3)GBK碼錶:前者擴容後得到的GBK碼錶,現在為GBK18030。GBK中每個中文用2個位元組表示

(4)UNICODE碼錶:世界碼錶,國際標準化組織用二進位制數字表示全世界所有文字。Java預設編碼形式,char型別資料的編碼

(5)UTF-8碼錶:前者優化得來的,主要優化了任何字元都在UNICODE碼錶中佔用了個位元組的問題。UTF-8中每個中文用3個位元組表示。有自己的標識頭,資料最開始有標識,讀取資料方便

(6)IOS8859-1:拉丁碼錶,歐洲碼錶,伺服器Tomcat用的就是該碼錶,當用get方法傳遞資料是必須手動轉碼,post方法可以用內建物件解決


2.知識點

2.1 編碼:字串變為位元組陣列。資料儲存在硬碟上是進行了編碼操作

 byte[] getBytes(Charset charset) 
          使用給定的 charset 將此 String 編碼到 byte 序列,並將結果儲存到新的 byte 陣列。
 byte[] getBytes(String charsetName) 
          使用指定的字符集將此 String 編碼為 byte 序列,並將結果儲存到一個新的 byte 陣列中。

2.2 解碼:字元陣列變為字串。用不同軟體開啟硬碟上的檔案都是在做著解碼的操作

String(byte[] bytes, Charset charset) 
          通過使用指定的 charset 解碼指定的 byte 陣列,構造一個新的 String
String(byte[] bytes, String charsetName) 
          通過使用指定的 charset 解碼指定的 byte 陣列,構造一個新的 String

2.3 具體例子

(1)字串String str = "你好",當使用getBytes("utf-8")形式編碼時,卻用new String(,"gbk")形式解碼,結果是:浣犲ソ。錯誤編碼無法解決,因為底層資料錯亂了

(2)字串String str = "你好",當使用getBytes("gbk")形式編碼時,卻用new String(,"ISO8859-1")形式解碼,結果是:???。錯誤解碼可以解決,再次按照錯誤的碼錶編碼=>getBytes("ISO8859-1"),然後按照正確的碼錶解碼即可new String("gbk")。因為位元組陣列中的資料從頭至尾都是正確的沒有改變過。過程如下圖:

注意:如果是按照“utf-8形式解碼,不能用這種方法解決,因為再次編碼時改變源資料。問題的產生是因為【gbk】與【utf-8】都識別中文


===================================================================================================================

(3)問題:聯通(記事本txt中只存入“聯通”字元,不存入任何其他字元,儲存後再開啟會發現解碼錯誤,顯示亂碼)

           闡述:自動按照utf-8碼錶形式解碼。這裡解決問題先要闡述一下utf-8碼錶,這個碼錶中有2個位元組代表1字元,也有3個位元組代表1字元的,這樣如何判斷是讀取2個位元組還是3個位元組來查表?其實utf-8碼錶對每個位元組都新增了一個標示頭資訊,通過該標示頭資訊就可以知道是讀取1個或者2個還是位元組3個位元組來查表。這時我們可以獲取“聯通”字串的4個位元組的二進位制表現形式,你會發現這2個字元正好符合utf-8定義的標識頭,所以記事本沒有按照gbk碼錶解碼,而是用utf-8碼錶解碼的。

         解決:在聯通前邊加入任意其他中文字元即可正常解碼,使用gbk解碼,顯示正常

特:utf-8的標示頭對照表如下:

\u0001' 到 '\u007F' 範圍內的所有字元都是用單個位元組表示的:

  位值
位元組 1
0
位 6-0

null 字元 '\u0000' 以及從 '\u0080' 到 '\u07FF' 的範圍內的字元用兩個位元組表示:

  位值
位元組 1
1
1
0
位 10-6
位元組 2
1
0
位 5-0

'\u0800' 到 '\uFFFF' 範圍內的 char 值用三個位元組表示:
  位值
位元組 1
1
1
1
0
位 15-12
位元組 2
1
0
位 11-6
位元組 3
1
0
位 5-0

===================================================================================================================

2.字元流

特點:主要用於操作字元資料。基於位元組流,解決了編碼問題,因為內部融合了編碼表,查位元組對碼錶。可以自定義指定所需要的碼錶

注意:應用系統不同換行符不同,windows為 \r\n, Linux為 \n

1.Writer抽象類

注意:寫入的過程都是在呼叫系統中的資源在寫入資料,所以使用完畢後一定要釋放資源

(1)異常的處理方法:

      <1>.IO類中的方法都會丟擲IOException(),所以要注意處理方式

      <2>.處理方法如下:
import java.io.*;

class java
{
	public static void main(String[] args){
		FileWriter fw = null;//定義引用

		try{
			//建立檔案寫入流
			fw = new FileWriter("d:\\a.txt");
			
			//寫入資料
			fw.write("鄭天成是個SB!");

		}catch(IOException e){//處理異常
			e.printStackTrace();
		}finally{
			if(fw!=null)//判斷是否為空
				try{
					fw.close();//關資源
				}catch(IOException e){
					e.printStackTrace();
				}
		}
	}
}
方法摘要
abstract  void close() 
          關閉此流,但要先重新整理它。(會重新整理流一次)(因為給個子類實現方式是不一樣的,所以抽象)
abstract  void flush() 
          重新整理該流的緩衝。(清空緩衝區,把其中的資料傳送到指定檔案中)
 void write(char[] cbuf) 
          寫入字元陣列。
abstract  void write(char[] cbuf, int off, int len) 
          寫入字元陣列的某一部分。
 void write(int c) 
          寫入單個字元。
 void write(String str) 
          寫入字串。(寫的資料不是直接寫入檔案中,而是寫入到了流的緩衝(記憶體)裡面,需要flush重新整理一下才能寫入檔案)
 void write(String str, int off, int len) 
          寫入字串的某一部分。


1.1 OutputStreamWriter類

構造方法摘要
OutputStreamWriter(OutputStream out) 
          建立使用預設字元編碼的 OutputStreamWriter。
OutputStreamWriter(OutputStream out, CharsetEncoder enc) 
          建立使用給定字符集編碼器的 OutputStreamWriter。
OutputStreamWriter(OutputStream out, String charsetName) 
          建立使用指定字符集的 OutputStreamWriter。

方法摘要
 String getEncoding() 
          返回此流使用的字元編碼的名稱。

1.2 FileWriter類

FileWriter字符集:採用平臺預設字元編碼

構造方法摘要
FileWriter(File file) 
          根據給定的 File 物件構造一個 FileWriter 物件。
FileWriter(File file, boolean append) 
          根據給定的 File 物件構造一個 FileWriter 物件。
FileWriter(String fileName) 
          根據給定的檔名構造一個 FileWriter 物件。(該物件一初始化就必須明確要操作檔案,並將該檔案會在指定目錄下被建立,而且會覆蓋同名檔案)
FileWriter(String fileName, boolean append) 
          根據給定的檔名以及指示是否附加寫入資料的 boolean 值來構造 FileWriter 物件。(true代表不覆蓋已有檔案)

2.Reader抽象類

注意:windows每個檔案結尾處都有一個被系統識別的結束標識

方法摘要
abstract  void close() 
          關閉該流並釋放與之關聯的所有資源。(因為給個子類實現方式是不一樣的,所以抽象)
 int read() 
          讀取單個字元。(讀取方式1:此方法返回的是一個字元,當讀取到檔案末尾時返回-1
 int read(char[] cbuf) 
          將字元讀入陣列。(讀取方式2:此方法返回的是讀到的字元個數,cbuf是自定義的用來儲存讀取到的字元的陣列,長度最好定義為1024的整數倍,1kb,字元轉成字串必須用new String(char[],offset,length)這種形式,因為不這樣定義最後一次讀取時可能讀取到上次重疊的元素。換行不同自己去新增,java會讀取到windows中每行的換行符)
abstract  int read(char[] cbuf, int off, int len) 
          將字元讀入陣列的某一部分。
 long skip(long n) 
          跳過字元。

2.1 InputStreamReader類

構造方法摘要
InputStreamReader(InputStream in) 
          建立一個使用預設字符集的 InputStreamReader。
InputStreamReader(InputStream in, Charset cs) 
          建立使用給定字符集的 InputStreamReader。
InputStreamReader(InputStream in, String charsetName) 
          建立使用指定字符集的 InputStreamReader。

方法摘要
 String getEncoding() 
          返回此流使用的字元編碼的名稱。

2.2 FileReader類

FileWriter字符集:採用平臺預設字元編碼

構造方法摘要
FileReader(File file) 
          在給定從中讀取資料的 File 的情況下建立一個新 FileReader
FileReader(String fileName) 
          在給定從中讀取資料的檔名的情況下建立一個新 FileReader(如果檔案不存在則丟擲FileNotFoundException())

(1)程式碼演示:(將一個檔案讀取並列印在控制檯上)

import java.io.*;

/**
*將一個檔案讀取並列印在控制檯上
*/
class java
{
	public static void main(String[] args){
		FileReader fr = null;

		try{
			fr = new FileReader("t03.java");//指定讀取的檔案

			char[] chr = new char[1024];//定義儲存資料的陣列
			int length = 0;//定義判斷標記,陣列可用資料長度

			while((length=fr.read(chr))!=-1){
				System.out.print(String.valueOf(chr,0,length));
			}		
		}catch(IOException e){
			System.out.println(e.toString());
		}finally{//保證一定關閉資源
			if(fr!=null)//防止拋異常後空指標的出現
				try{
					fr.close();//關閉資源
				}catch(IOException e){
					System.out.println(e.toString());
				}
		}
	}
}

(2)程式碼演示:(將D盤的一個文字檔案拷貝到E盤)

/**
*將D盤一個文字檔案複製到E盤
*/
class java
{
	public static void main(String[] args){
		FileReader fr = null;
		FileWriter fw = null;

		try{
			fr = new FileReader("t03.java");//指定讀取的檔案
			fw = new FileWriter("E:\\java複製的檔案.txt");//指定複製的地址與檔名

			char[] chr = new char[1024];//定義儲存資料的陣列
			int length = 0;//定義判斷標記,陣列可用資料長度

			while((length=fr.read(chr))!=-1){
				fw.write(String.valueOf(chr,0,length));
				fw.flush();
			}		
		}catch(IOException e){
			System.out.println(e.toString());
		}finally{//保證一定關閉資源
			if(fr!=null)//防止拋異常後空指標的出現
				try{
					fr.close();//關閉資源
				}catch(IOException e){
					System.out.println(e.toString());
				}
			if(fw!=null)
				try{
					fw.close();//關閉資源
				}catch(IOException e){
					System.out.println(e.toString());
				}
		}
	}
}

3.緩衝區

意義:緩衝區的出現是為了增加流運算元據的效率的,所有在建立緩衝區之前,一定要有流物件。

知識:下載工具中就有緩衝區的存在,表示先把資料放在一塊設定好大小的記憶體當中——緩衝區,然後當緩衝區填滿後再傳輸進硬碟

3.1 BufferedWriter類

構造方法摘要
BufferedWriter(Writer out) 
          建立一個使用預設大小輸出緩衝區的緩衝字元輸出流。
BufferedWriter(Writer out, int sz) 
          建立一個使用給定大小輸出緩衝區的新緩衝字元輸出流。

方法摘要
 void newLine() 
          寫入一個行分隔符。(跨平臺的換行符)

3.2 BufferedReader類

構造方法摘要
BufferedReader(Reader in) 
          建立一個使用預設大小輸入緩衝區的緩衝字元輸入流。
BufferedReader(Reader in, int sz) 
          建立一個使用指定大小輸入緩衝區的緩衝字元輸入流。

方法摘要
 String readLine() 
          讀取一個文字行。(讀取方式3:能夠讀取一行的方法,讀到末尾處返回null)


(1)自定義一個BufferedReader(注意最後一行沒有換行符時,單單隻判斷結束標記會導致最後一行無法返回)

class MyBufferedReader
{
	private FileReader fr;
	MyBufferedReader(FileReader fr){
		this.fr = fr;
	}

	//可以每次讀取一行的方法
	public String myReadLine()throws IOException{
		//原緩衝區是用的字元陣列,這裡為了方便用了StringBuilder
		StringBuilder sb = new StringBuilder();

		int ch;

		while((ch=fr.read())!=-1){
			if(ch=='\r')
				continue;
			else if(ch=='\n')
				return sb.toString();
			else
				sb.append((char)ch);
		}

		//當最後一行沒有回車時不加下邊語句,最後一行不會返回
		if(sb.length()!=0)
			return sb.toString();
		return null;
	}

	public void myClose()throws IOException{
		fr.close();
	}
}

3.2.1 LineNumberReader類

1.知識點:

(1)是BufferedReader的子類,增加了修改和新增行號兩個方法

2.功能簡介:

方法摘要
 int getLineNumber()
          獲得當前行號。
void setLineNumber(int lineNumber)
          設定當前行號。

3.位元組流

特點:當操作的資料不是本文資料,比如多媒體資料,應該用位元組流操作

要點:(底層

問題:當讀取到一個多媒體檔案時,由於多媒體檔案資料排列不一,很可能出現其中1位元組的資料是1111-1111,因為1111-1111對應的十進位制數字是-1,這樣讀到的這一位元組返回值就是-1。沒有讀到檔案結尾處就返回了-1

解決:你會發現API中read方法返回值為int型,這是為什麼?

返回的byte型-1二級制形式為1111-1111,正常轉換為int型後應該是1111-1111 1111-1111 1111-1111 1111-1111。這樣還是不能解決問題。

不過可以通過int型-1=>1111-1111 1111-1111 1111-1111 1111-1111 和 255=>0000-0000 0000-0000 0000-0000 1111-1111做"&"的運算。

結果為255,二級製表現形式為 0000-0000 0000-0000 0000-0000 1111-1111,你會發現在這樣處理既保證後設資料沒有變化,又避免了-1這種情況的草出現。這也是為什麼返回值不是byte型,而是int型的原因。返回的結果都在做著 &255的操作

read()方法讀取到的資料是int型別,是原來byte型別大小的四倍;而wirte()方法又在做著強轉的過程,保證了寫入的資料是byte型別,只往出寫資料的最低8位。(記憶體中真正的過程)

1.OutputStream抽象類

方法摘要
 void close() 
          關閉此輸出流並釋放與此流有關的所有系統資源。
 void flush() 
          重新整理此輸出流並強制寫出所有緩衝的輸出位元組。()
 void write(byte[] b) 
          將 b.length 個位元組從指定的 byte 陣列寫入此輸出流。
 void write(byte[] b, int off, int len) 
          將指定 byte 陣列中從偏移量 off 開始的 len 個位元組寫入此輸出流。
abstract  void write(int b) 
          將指定的位元組寫入此輸出流。

2.1 FileOutputStream類

注意:此類寫資料時不用重新整理,因為此類沒用到緩衝區,這是對最小單位位元組的操作,直接往目的地裡存即可,中間過程不涉及任何轉換,所以用不到緩衝區,也就不會用到重新整理操作

構造方法摘要
FileOutputStream(File file)
          建立一個向指定 File 物件表示的檔案中寫入資料的檔案輸出流。
FileOutputStream(File file, boolean append)
          建立一個向指定 File 物件表示的檔案中寫入資料的檔案輸出流。
FileOutputStream(String name)
          建立一個向具有指定名稱的檔案中寫入資料的輸出檔案流。
FileOutputStream(String name, boolean append)
          建立一個向具有指定 name 的檔案中寫入資料的輸出檔案流。

1.InputStream抽象類

方法摘要
 int available() 
          返回此輸入流下一個方法呼叫可以不受阻塞地從此輸入流讀取(或跳過)的估計位元組數。(獲取目標檔案中的位元組數量,從此方法的位元組陣列長度剛好,可以不定義迴圈,獲取檔案中的全部資料。不過虛擬機器啟動後分配的大小為64M,用此種解決方式容易出現記憶體溢位。所以此方法要慎用)
 void close() 
          關閉此輸入流並釋放與該流關聯的所有系統資源。
abstract  int read() 
          從輸入流中讀取資料的下一個位元組。
 int read(byte[] b) 
          從輸入流中讀取一定數量的位元組,並將其儲存在緩衝區陣列 b 中。
 int read(byte[] b, int off, int len) 
          將輸入流中最多 len 個資料位元組讀入 byte 陣列。
 long skip(long n) 
          跳過和丟棄此輸入流中資料的 n 個位元組。
/**
*鍵盤錄入:定義輸入裝置,鍵盤錄入,然後列印輸入文字的大寫形式,輸入over結束本功能
*/
class java
{
	public static void main(String[] args)throws IOException{
		InputStream is = System.in;//鍵盤錄入讀取流
		StringBuilder sb = new StringBuilder();//緩衝區
		
		while(true){
			int ch = is.read();//記住讀到的字元
			if(ch=='\r')
				continue;
			if(ch=='\n'){
				String str = sb.toString();
				if("over".equals(str))//輸入over結束的實現
					break;
				System.out.println(str.toUpperCase());//輸出大寫形式
				sb.delete(0,sb.length());//清空緩衝區
			}
			else
				sb.append((char)ch);//存入緩衝區

		}
	}
}

1.1 FileInputStream類

構造方法摘要
FileInputStream(File file) 
          通過開啟一個到實際檔案的連線來建立一個 FileInputStream,該檔案通過檔案系統中的 File 物件file 指定。
FileInputStream(String name) 
          通過開啟一個到實際檔案的連線來建立一個 FileInputStream,該檔案通過檔案系統中的路徑名 name 指定。 

(1)程式碼演示:(複製一張圖片)

/**
*複製一張圖片到指定位置
*/
class java
{
	public static void main(String[] args){
		InputStream fis = null;//位元組輸入流
		OutputStream fos = null;//位元組輸出流
		try{
			fis = new FileInputStream("MIKU.jpg");//目標檔案
			fos = new  FileOutputStream("我的圖片.jpg");//目的地
			
			byte[] ch = new byte[1024];//緩衝區陣列
			int len;//陣列長度
			while((len=fis.read(ch))!=-1)
				fos.write(ch,0,len);//寫資料
		}catch(IOException e){
			throw new RuntimeException(e);//異常傳遞	
		}finally{
			if(fis!=null)
				try{
					fis.close();//關閉資源
				}catch(IOException e){
					throw new RuntimeException(e);
				}
			if(fos!=null)
				try{
					fos.close();//關閉資源
				}catch(IOException e){
					throw new RuntimeException(e);
				}
		}
	}
}

3.緩衝區

注意:緩衝區一定要用read(byte[])的方法,不然不但不增加效率,而且減少效率,還不如在FileInputStream中自定義new byte[1024]這種來的有效率

特點:緩衝區就是自定義一個陣列接收臨時資料,存多了一起存數儲存介質中

3.1 BufferedOutputStream類

構造方法摘要
BufferedOutputStream(OutputStream out) 
          建立一個新的緩衝輸出流,以將資料寫入指定的底層輸出流。
BufferedOutputStream(OutputStream out, int size) 
          建立一個新的緩衝輸出流,以將具有指定緩衝區大小的資料寫入指定的底層輸出流。

方法摘要
 void flush() 
          重新整理此緩衝的輸出流。

3.2 BufferedInputStream類

構造方法摘要
BufferedInputStream(InputStream in) 
          建立一個 BufferedInputStream 並儲存其引數,即輸入流 in,以便將來使用。
BufferedInputStream(InputStream in, int size) 
          建立具有指定緩衝區大小的 BufferedInputStream 並儲存其引數,即輸入流 in,以便將來使用。

(1)緩衝區效率上的體現(三個函式分別採用三種方式複製檔案)

import java.io.*;

/**
*比較緩衝區相比正常檔案流效率上的優化
*/
class java
{
	public static void main(String[] args)throws Exception{
		long start = System.currentTimeMillis();
		copy_3();
		long end = System.currentTimeMillis();
		System.out.println((end-start)+"毫秒");
	}

	public static void copy_1(){
		InputStream fis = null;//位元組輸入流
		OutputStream fos = null;//位元組輸出流
		try{
			fis = new FileInputStream("笨蛋、測試、召喚獸第一季-OP.m4a");//目標檔案
			fos = new  FileOutputStream("01.m4a");//目的地
			
			byte[] ch = new byte[1024];//緩衝區陣列
			int len;//陣列長度
			while((len=fis.read(ch))!=-1)
				fos.write(ch,0,len);//寫資料
		}catch(IOException e){
			throw new RuntimeException(e);//異常傳遞	
		}finally{
			if(fis!=null)
				try{
					fis.close();//關閉資源
				}catch(IOException e){
					throw new RuntimeException(e);
				}
			if(fos!=null)
				try{
					fos.close();//關閉資源
				}catch(IOException e){
					throw new RuntimeException(e);
				}
		}
	}

	public static void copy_2()throws IOException{
		BufferedInputStream bufis = new BufferedInputStream(new FileInputStream("笨蛋、測試、召喚獸第一季-OP.m4a"));
		BufferedOutputStream bufos = new BufferedOutputStream(new FileOutputStream("01.m4a"));

		int ch;
		while((ch=bufis.read())!=-1)
			bufos.write(ch);

		bufis.close();
		bufos.close();
	}

	public static void copy_3()throws IOException{
		BufferedInputStream bufis = new BufferedInputStream(new FileInputStream("笨蛋、測試、召喚獸第一季-OP.m4a"));
		BufferedOutputStream bufos = new BufferedOutputStream(new FileOutputStream("01.m4a"));

		byte[] buf = new byte[1024];
		int ch;
		while((ch=bufis.read(buf))!=-1)
			bufos.write(buf,0,ch);

		bufis.close();
		bufos.close();
	}
}

4.轉換流

注意:下邊兩個類的使用方法請在標題2中去查詢

檔案字元流其實就是封裝了本地字符集(GBK)的轉換流,做了簡單的封裝。所以不是GBK就用轉換流就好

1.OutputStreamWriter類

意義:此轉換流是位元組流通向字元流的橋樑,實際應用時就是為了用字元流的特殊方法操作位元組流接收的資料

2.InpuStreamReader類

意義:此轉換流是位元組流通向字元流的橋樑,實際應用時就是為了用字元流的特殊方法操作位元組流接收的資料

(1)程式碼實際操作

/**
*鍵盤錄入:定義輸入裝置,鍵盤錄入,然後列印輸入文字的大寫形式,輸入over就是本功能
*/
class java
{
	public static void main(String[] args)throws IOException{
		//獲取鍵盤錄入,通過轉換流來實現用字元緩衝讀取流操作
		BufferedReader bufr = new BufferedReader(new InputStreamReader(System.in));	

		//獲取列印控制檯的流,通過轉換流老實現字元緩衝寫入流操作
		BufferedWriter bufw = new BufferedWriter(new OutputStreamWriter(System.out));

		String line = null;

		while((line=bufr.readLine())!=null){
			if("over".equals(line))//定義結束標記
				break;
			bufw.write(line.toUpperCase());//大寫輸出
			bufw.newLine();//跨平臺換行
			bufw.flush();//重新整理
		}

		bufr.close();
		bufw.close();
	}
}

5.File類

(1)用來將檔案或資料夾封裝成物件,可以操作檔案或資料夾的屬性資訊

構造方法摘要
File(File parent, String child) 
          根據 parent 抽象路徑名和 child 路徑名字串建立一個新 File 例項。
File(String pathname) 
          通過將給定路徑名字串轉換為抽象路徑名來建立一個新 File 例項。
File(String parent, String child) 
          根據 parent 路徑名字串和 child 路徑名字串建立一個新 File 例項。

欄位摘要
static String pathSeparator 
          與系統有關的路徑分隔符,為了方便,它被表示為一個字串。
static char pathSeparatorChar 
          與系統有關的路徑分隔符。
static String separator 
          與系統有關的預設名稱分隔符,為了方便,它被表示為一個字串。
static char separatorChar 
          與系統有關的預設名稱分隔符。

1 功能簡介:

1.1 建立

方法摘要
boolean createNewFile() 
          當且僅當不存在具有此抽象路徑名指定名稱的檔案時,不可分地建立一個新的空檔案。(檔案已存在時則不重新建立)
static File createTempFile(String prefix, String suffix) 
          在預設臨時檔案目錄中建立一個空檔案,使用給定字首和字尾生成其名稱。(建立臨時檔案)
static File createTempFile(String prefix, String suffix, File directory) 
           在指定目錄中建立一個新的空檔案,使用給定的字首和字尾字串生成其名稱。(建立臨時檔案)
 boolean mkdir() 
          建立此抽象路徑名指定的目錄。(只能建立一級目錄)
 boolean mkdirs() 
          建立此抽象路徑名指定的目錄,包括所有必需但不存在的父目錄。(可以建立多級目錄)

1.2 刪除

方法摘要
 boolean delete() 
          刪除此抽象路徑名錶示的檔案或目錄。(刪除失敗返回false,可能檔案正在執行無法刪除,當程式出現異常也可能無法刪除
 void deleteOnExit() 
          在虛擬機器終止時,請求刪除此抽象路徑名錶示的檔案或目錄。

1.3 判斷

方法摘要
 boolean canExecute() 
          測試應用程式是否可以執行此抽象路徑名錶示的檔案。
 boolean canRead() 
          測試應用程式是否可以讀取此抽象路徑名錶示的檔案。
 boolean canWrite() 
          測試應用程式是否可以修改此抽象路徑名錶示的檔案。
 int compareTo(File pathname) 
          按字母順序比較兩個抽象路徑名。
 boolean equals(bject obj) 
          測試此抽象路徑名與給定物件是否相等。
 boolean exists() 
          測試此抽象路徑名錶示的檔案或目錄是否存在。
 boolean isAbsolute() 
          測試此抽象路徑名是否為絕對路徑名。
 boolean isDirectory() 
          測試此抽象路徑名錶示的檔案是否是一個目錄。(在判斷是否是目錄時要先判斷目錄是否存在)
 boolean isFile() 
          測試此抽象路徑名錶示的檔案是否是一個標準檔案。(在判斷是否是檔案時要先判斷檔案是否存在)
 boolean isHidden() 
          測試此抽象路徑名指定的檔案是否是一個隱藏檔案。

1.4 獲取資訊

方法摘要
 String getName() 
          返回由此抽象路徑名錶示的檔案或目錄的名稱。
 String getParent() 
          返回此抽象路徑名父目錄的路徑名字串;如果此路徑名沒有指定父目錄,則返回 null
 File getParentFile() 
          返回此抽象路徑名父目錄的抽象路徑名;如果此路徑名沒有指定父目錄,則返回 null
 String getPath() 
          將此抽象路徑名轉換為一個路徑名字串。(封裝了什麼路徑就返回什麼路徑)
 File getAbsoluteFile() 
          返回此抽象路徑名的絕對路徑名形式。
 String getAbsolutePath() 
          返回此抽象路徑名的絕對路徑名字串。(只返回絕對路徑)
 long lastModified() 
          返回此抽象路徑名錶示的檔案最後一次被修改的時間。
 long length() 
          返回由此抽象路徑名錶示的檔案的長度。
static File[] listRoots() 
          列出可用的檔案系統根。獲取系統中全部有效碟符
 String[] list() 
          返回一個字串陣列,這些字串指定此抽象路徑名錶示的目錄中的檔案和目錄。(獲取指定目錄下全部檔案和資料夾的名稱)
 String[] list(FilenameFilter filter) 
          返回一個字串陣列,這些字串指定此抽象路徑名錶示的目錄中滿足指定過濾器的檔案和目錄。
 File[] listFiles() 
          返回一個抽象路徑名陣列,這些路徑名錶示此抽象路徑名錶示的目錄中的檔案。(獲取一個目錄中全部的檔案物件)
 File[] listFiles(FileFilter filter) 
          返回抽象路徑名陣列,這些路徑名錶示此抽象路徑名錶示的目錄中滿足指定過濾器的檔案和目錄。
 File[] listFiles(FilenameFilter filter) 
          返回抽象路徑名陣列,這些路徑名錶示此抽象路徑名錶示的目錄中滿足指定過濾器的檔案和目錄。

1.5 移動

方法摘要
 boolean renameTo(File dest) 
          重新命名此抽象路徑名錶示的檔案。

2.FilenameFilter介面

意義:這是檔名過濾器

方法摘要
 boolean accept(File dir, String name) 
          測試指定檔案是否應該包含在某一檔案列表中。

3.FileFilter介面

意義:這是檔名過濾器

方法摘要
 boolean accept(File pathname) 
          測試指定抽象路徑名是否應該包含在某個路徑名列表中。

import java.io.*;
/**
*檔名過濾器
*/
class java
{
	public static void main(String[] args)throws Exception{
		File mainDir = new File("F:\\我妹妹最近有點怪");

		File[] arr =  mainDir.listFiles(new FileFilter(){//傳入檔名過濾器
			public boolean accept(File pathname){
				if(pathname.getName().endsWith(".mkv"))//判斷是否是mkv格式
					return true;
				return false;
			}
		});

		for(File f : arr){
			System.out.println(f);//獲取檔案資訊列印在控制檯上
		}
	}
}

4.遞迴

(1)事實:就是函式自身執行的過程中呼叫自身。遞迴需要限定條件才能實現,不然就是無限迴圈

(2)程式碼演示:(最簡單的遞迴)

/**
*最簡單的遞迴,遞迴就是函式自身執行時呼叫自身
*/

class java
{
	public static void main(String[] args){
		getContent(new File("E:\\新建資料夾"));
	}

	public static void getContent(File dir){
		File[] files = dir.listFiles();

		for(File file : files){
			if(file.isDirectory())
				getContent(file);
			else
				System.out.println(file);
		}
	}
}

(3)底層運算樣圖(以把6轉換為二進位制遞迴為例)


(4)遞迴與迴圈的優劣

<1>.遞迴演算法: 
優點:程式碼簡潔、清晰,並且容易驗證正確性。(如果你真的理解了演算法的話,否則你更暈) 
缺點:它的執行需要較多次數的函式呼叫,如果呼叫層數比較深,需要增加額外的堆疊處理,比如引數傳遞需要壓棧等操作,會對執行效率有一定影響。但是,對於某些問題,如果不使用遞迴,那將是極端難看的程式碼。 遞迴次數太多,超出棧內寸的空間,記憶體溢位

<2>.迴圈演算法:  
優點:速度快,結構簡單。
缺點:並不能解決所有的問題。有的問題適合使用遞迴而不是迴圈。如果使用迴圈並不困難的話,最好使用迴圈。

遞迴演算法 和迴圈演算法總結  
1.   一般遞迴呼叫可以處理的演算法,也通過迴圈去解決常需要額外的低效處理 。
2.   現在的編譯器在優化後,對於多次呼叫的函式處理會有非常好的效率優化,效率未必低於迴圈。

(5)程式碼演示:(帶層級的列出資料夾下的所有內容)

import java.io.*;

class java
{
	public static void main(String[] args){
		getContent(new File("E:\\動漫MAD"),0);
	}

	//新增層級字串
	public static String getLevel(int level){
		StringBuilder sb = new StringBuilder();
		sb.append("|--");
		for(int x=0;x<level;x++){
			//sb.append("|--");
			sb.insert(0,"  ");
		}
		return sb.toString();
	}

	//遞迴得到目錄下的全部內容
	public static void getContent(File dir,int level){
		File[] files = dir.listFiles();
		level++;

		for(File file : files){
			if(!file.isHidden() && file.isDirectory())
				getContent(file,level);
			else
				System.out.println(getLevel(level)+file);
		}
	}
}

6 列印流

(1)注意:列印的流全是輸出流,只是分為字元和位元組兩種

(2)列印流的好處:可以對基本資料型別直接操作,保證資料的原樣性將資料列印

(3)列印流非常常用,必須會用。列印流特點方法 => println

1.PrintStream類

構造方法摘要
PrintStream(File file) 
          建立具有指定檔案且不帶自動行重新整理的新列印流。 File物件
PrintStream(File file, String csn) 
          建立具有指定檔名稱和字符集且不帶自動行重新整理的新列印流。File物件
PrintStream(OutputStream out) 
          建立新的列印流。位元組輸出流
PrintStream(OutputStream out, boolean autoFlush) 
          建立新的列印流。位元組輸出流,如果標記為true,println、printf、format方法將自動重新整理
PrintStream(String fileName) 
          建立具有指定檔名稱且不帶自動行重新整理的新列印流。字串路徑

2.PrintWriter類

構造方法摘要
PrintWriter(File file) 
          使用指定檔案建立不具有自動行重新整理的新 PrintWriter。File物件
PrintWriter(File file, String csn) 
          建立具有指定檔案和字符集且不帶自動刷行新的新 PrintWriter。File物件
PrintWriter(OutputStream out) 
          根據現有的 OutputStream 建立不帶自動行重新整理的新 PrintWriter。位元組輸出流
PrintWriter(OutputStream out, boolean autoFlush) 
          通過現有的 OutputStream 建立新的 PrintWriter。位元組輸出流,如果標記為true,println、printf、format方法將自動重新整理
PrintWriter(String fileName) 
          建立具有指定檔名稱且不帶自動行重新整理的新 PrintWriter。字串路徑
PrintWriter(String fileName, String csn) 
          建立具有指定檔名稱和字符集且不帶自動行重新整理的新 PrintWriter。字串路徑
PrintWriter(Writer out) 
          建立不帶自動行重新整理的新 PrintWriter。字元輸出流
PrintWriter(Writer out, boolean autoFlush) 
          建立新 PrintWriter。字元輸出流,如果標記為true,println、printf、format方法將自動重新整理
import java.io.*;
/**
*列印流
*/
class java
{
	public static void main(String[] args)throws Exception{
		BufferedReader bufr = new BufferedReader(new InputStreamReader(System.in));

		String line = null;

		PrintWriter pw = new PrintWriter(new FileWriter("Upper.txt"),true);//自動重新整理

		while((line=bufr.readLine())!=null){
			if("over".equals(line))//結束標記
				break;
			pw.println(line.toUpperCase());//列印流不用重新整理和換行
		}

		bufr.close();
		pw.close();
	}
}

7.合併流

1.SequenceInputStream類

(1)意義:合併多個流為一個大流,從第一個開始一直讀出最後一個的資料。可以同時讀取多個檔案的資料合併到一個檔案中

構造方法摘要
SequenceInputStream(Enumeration<? extends InputStream> e) 
          通過記住引數來初始化新建立的 SequenceInputStream,該引數必須是生成執行時型別為 InputStream 物件的 Enumeration 型引數。
SequenceInputStream(InputStream s1, InputStream s2) 
          通過記住這兩個引數來初始化新建立的 SequenceInputStream(將按順序讀取這兩個引數,先讀取 s1,然後讀取 s2),以提供從此SequenceInputStream 讀取的位元組。

2.切割檔案

(1)切割的原理:通過定義一個固定大小的byte[]型陣列,來知道每次寫入資料的具體大小

(2)切割檔案與合併檔案程式碼實現:

import java.io.*;
import java.util.*;
/**
*分割與合併多個檔案
*/

class java
{
	public static void main(String[] args)throws Exception{
		File file = new File("d:\\銀魂第二季-ED1.m4a");
		spitFile(file);
		combineFile();
	}

	//分割檔案
	public static void spitFile(File file)throws Exception{
		BufferedInputStream bufis = new BufferedInputStream(new FileInputStream(file));
		BufferedOutputStream bufos = new BufferedOutputStream(new FileOutputStream("d:\\分割檔案\\1.part"));;

		byte[] buf = new byte[1024*1024];
		
		for(int countFile=2,countSize=0,len;(len=bufis.read(buf))!=-1;countSize++){
			if(countSize==5){//限制每個檔案大小為5M
				bufos = new BufferedOutputStream(new FileOutputStream("d:\\分割檔案\\"+countFile+".part"));
				countFile++;
				countSize = 0;
			}
			bufos.write(buf,0,len);//寫入1M資料
		}

		bufis.close();
		bufos.close();
	}

	//合併檔案
	public static void combineFile()throws Exception{
		Vector<InputStream> list = new Vector<InputStream>();
		list.add(new FileInputStream("d:\\分割檔案\\1.part"));
		list.add(new FileInputStream("d:\\分割檔案\\2.part"));

		Enumeration<InputStream> en = list.elements();
		
		BufferedInputStream bufis = new BufferedInputStream(new SequenceInputStream(en));//合併多個流

		byte[] buf = new byte[1024];
		int len;
		PrintStream ps = new PrintStream("d:\\我的銀魂.m4a");
		while((len=bufis.read(buf))!=-1)
			ps.write(buf,0,len);

		bufis.close();
		ps.close();
	}
}

8.操作物件的流

(1)意義:對堆記憶體中的物件進行持久化儲存,也稱為物件的序列化

1.ObjectOutputStream類

構造方法摘要
  ObjectOutputStream(OutputStream out) 
          建立寫入指定 OutputStream 的 ObjectOutputStream。

方法摘要
 void writeObject(Object obj) 
          將指定的物件寫入 ObjectOutputStream。
 void writeUTF(String str) 
          以 UTF-8 修改版格式寫入此 String 的基本資料。(這裡使用的是修改版的UTF-8,中文佔用4個位元組)

2.ObjectInputStream類

構造方法摘要
  ObjectInputStream(InputStream in) 
          建立從指定 InputStream 讀取的 ObjectInputStream。

方法摘要
 Object readObject() 
          從 ObjectInputStream 讀取物件。
 String readUTF() 
          讀取 UTF-8 修改版格式的 String。(這裡使用的是修改版的UTF-8)

3.Serializable介面

(1)說明:你會發現這個介面沒有成員,這種介面一般被稱為標記介面,就等於給實現它的類做了標記

(2)原理:ANY-ACCESS-MODIFIER static final long serialVersionUID = 42L;每個類都有一個serialVersionUID號,這樣類就被ID所標識,其所生成的物件也被這個ID鎖標識,這個ID一般被編譯器所用。當類改變時,其ID也會隨之改變,持久化儲存的物件會因為ID號與新的類不同而產生不匹配的結果。這個UID是通過類中的所有成員的數字標識(數字簽名)所得出的

(3)如果不想讓系統自動根據成員生成UID,可由我們自動固定UID,建立子類成員變數——public static final long serialVersionUID = 42L;來讓UID固定不變

(4)只有堆記憶體中的資料能夠序列化,方法區中的靜態成員無法序列化

(5)如果你不想讓非靜態成員序列化,那麼可以用transient關鍵字修飾

(6)物件序列化儲存的本地檔案命名格式可以為:類名.object

4.transient關鍵字

import java.io.*;
/**
*物件的序列化
*/
class java
{
	public static void main(String[] args)throws Exception{
		//write(new Person[]{new Person("張天成",25),new Person("張天",5)});//傳入多個物件
		read(new File("d:\\save.object"));
	}

	//讀取檔案中的物件
	public static void read(File file)throws Exception{
		//讀取序列化物件
		ObjectInputStream ois = new ObjectInputStream(new BufferedInputStream(new FileInputStream(file)));

		Person[] p = (Person[])ois.readObject();//獲取物件陣列

		for(Person per : p){//解析出陣列中的全部物件取出物件
			per.get();	
		}
	}

	public static <T> void write(T t)throws IOException{
		//序列化物件
		ObjectOutputStream oos = new ObjectOutputStream(new BufferedOutputStream(new FileOutputStream("d:\\save.object")));

		oos.writeObject(t);

		oos.close();
	}
}

class Person implements Serializable//實現標記介面
{
	private String name;
	private int age;

	Person(String name,int age){
		this.name = name;
		this.age = age;
	}

	public void get(){
		System.out.println(name+"..."+age);
	}
}

9.管道流

(1)意義:一般輸出流與輸入流同時操作要提供一箇中轉站——一個陣列、字串等,而管道流能直接連線上,不用建立中轉站

(2)注意:建議通過多執行緒的wait、nitify來控制,單執行緒可能會造成死鎖

1.PipedInputStream類

構造方法摘要
PipedInputStream() 
          建立尚未連線的 PipedInputStream
PipedInputStream(int pipeSize) 
          建立一個尚未連線的 PipedInputStream,並對管道緩衝區使用指定的管道大小。
PipedInputStream(PipedOutputStream src) 
          建立 PipedInputStream,使其連線到管道輸出流 src

方法摘要
 void connect(PipedOutputStream src) 
          使此管道輸入流連線到管道輸出流 src

2.PipedOutputStream類

構造方法摘要
PipedOutputStream() 
          建立尚未連線到管道輸入流的管道輸出流。
PipedOutputStream(PipedInputStream snk) 
          建立連線到指定管道輸入流的管道輸出流。

方法摘要
 void connect(PipedInputStream snk) 
          將此管道輸出流連線到接收者。

10.操作集中資料型別的流

1.操作基本資料型別

1.1 DataOutputStream類

(1)此流的功能與RundomAccessFile流中寫基本資料型別的方法相同,不在一一舉出

構造方法摘要
DataOutputStream(OutputStream out) 
          建立一個新的資料輸出流,將資料寫入指定基礎輸出流。

1.2 DataIntputStream類

構造方法摘要
DataInputStream(InputStream in) 
          使用指定的底層 InputStream 建立一個 DataInputStream。

2.操作位元組陣列中資料

意義:對陣列的操作無非是設定和獲取,對應的IO就是寫和讀,此流就是在用流的思想在運算元組

2.1 ByteArrayOutputStream類

(1)這個方法是把資料儲存到內部封裝的一個可變長度陣列中,沒有呼叫到底層資源,依然不會丟擲IOException
(2)可用此類來遍歷獲取陣列中的值

構造方法摘要
ByteArrayOutputStream() 
          建立一個新的 byte 陣列輸出流。
ByteArrayOutputStream(int size) 
          建立一個新的 byte 陣列輸出流,它具有指定大小的緩衝區容量(以位元組為單位)。

方法摘要
 int size() 
          返回緩衝區的當前大小。
 byte[] toByteArray() 
          建立一個新分配的 byte 陣列。
 String toString() 
          使用平臺預設的字符集,通過解碼位元組將緩衝區內容轉換為字串。
String toString(String charsetName) 
          使用指定的 charsetName,通過解碼位元組將緩衝區內容轉換為字串。
 void writeTo(OutputStream out) 
          將此 byte 陣列輸出流的全部內容寫入到指定的輸出流引數中,這與使用 out.write(buf, 0, count) 呼叫該輸出流的 write 方法效果一樣。

2.2 ByteArrayInputStream類

(1)這是一個運算元組的流物件,不會呼叫底層資源,所以不會丟擲IOException,也因此根本用不到close()方法,關閉流無效
(2)可當此類為存數字節資料的可變長度陣列

構造方法摘要
ByteArrayInputStream(byte[] buf) 
          建立一個 ByteArrayInputStream,使用 buf 作為其緩衝區陣列。
ByteArrayInputStream(byte[] buf, int offset, int length) 
          建立 ByteArrayInputStream,使用 buf 作為其緩衝區陣列。

方法摘要
 void close() 
          關閉 ByteArrayInputStream 無效。

3.操作字元陣列中資料

意義:同位元組陣列流

3.1 CharArrayWriter類

構造方法摘要
CharArrayWriter() 
          建立一個新的 CharArrayWriter。
CharArrayWriter(int initialSize) 
          建立一個具有指定初始大小的新 CharArrayWriter。

方法摘要
 char[] toCharArray() 
          返回輸入資料的副本。
 String toString() 
          將輸入資料轉換為字串。

3.2 CharArrayReader類

注意:此類會丟擲異常,請處理,雖說不知道為什麼

構造方法摘要
CharArrayReader(char[] buf) 
          根據指定的 char 陣列建立一個 CharArrayReader。
CharArrayReader(char[] buf, int offset, int length) 
          根據指定的 char 陣列建立一個 CharArrayReader。

4.操作字串中資料

4.1 StringWriter類

構造方法摘要
StringWriter() 
          使用預設初始字串緩衝區大小建立一個新字串 writer。
StringWriter(int initialSize) 
          使用指定初始字串緩衝區大小建立一個新字串 writer。

方法摘要
StringBuffer getBuffer() 
          返回該字串緩衝區本身。
 String toString() 
          以字串的形式返回該緩衝區的當前值。

4.2 StringReader類

注意:此類會丟擲異常,請處理,雖說不知道為什麼

構造方法摘要
StringReader(String s) 
          建立一個新字串 reader。

11.RandomAccessFile類

(1)意義:此類例項支援對隨機訪問檔案的讀取和寫入,物件內部封裝了陣列和指標,通過指標來運算元據

(2)特點:不算是IO體系中的子類,直接繼承自Object

(3)重點:能夠實現隨機讀寫資料——就是讀取某具體位置的資料或者在某個具體位置寫入資料。基於這個原理可以實現資料的分段寫入,並用多執行緒操作不同資料段來增加效率。如果讓多執行緒操作其他的流,寫完資料後就會發現寫入的資料是錯亂的。這就是隨機讀寫訪問,下載就是基於這個原理

(4)注意:此流不會覆蓋原始檔,而是在其基礎上覆寫檔案

構造方法摘要
RandomAccessFile(File file, String mode) 
          建立從中讀取和向其中寫入(可選)的隨機訪問檔案流,該檔案由 File 引數指定。(只能操作檔案)
RandomAccessFile(String name, String mode) 
          建立從中讀取和向其中寫入(可選)的隨機訪問檔案流,該檔案具有指定名稱。(mode有四種格式:"r"、“rw"這兩種較常用)

"r"以只讀方式開啟。呼叫結果物件的任何 write 方法都將導致丟擲 IOException

"rw":開啟以便讀取和寫入。如果該檔案尚不存在,則嘗試建立該檔案

方法摘要
 int read() 
          從此檔案中讀取一個資料位元組。
 int read(byte[] b) 
          將最多 b.length 個資料位元組從此檔案讀入 byte 陣列。
 int read(byte[] b, int off, int len) 
          將最多 len 個資料位元組從此檔案讀入 byte 陣列。
 boolean readBoolean() 
          從此檔案讀取一個 boolean
 byte readByte() 
          從此檔案讀取一個有符號的八位值。
 char readChar() 
          從此檔案讀取一個字元。
 double readDouble() 
          從此檔案讀取一個 double
 float readFloat() 
          從此檔案讀取一個 float
 int readInt() 
          從此檔案讀取一個有符號的 32 位整數。
 String readLine() 
          從此檔案讀取文字的下一行。
 long readLong() 
          從此檔案讀取一個有符號的 64 位整數。
 short readShort() 
          從此檔案讀取一個有符號的 16 位數。
 String readUTF() 
          從此檔案讀取一個字串。
 void write(byte[] b) 
          將 b.length 個位元組從指定 byte 陣列寫入到此檔案,並從當前檔案指標開始。
 void write(byte[] b, int off, int len) 
          將 len 個位元組從指定 byte 陣列寫入到此檔案,並從偏移量 off 處開始。
 void write(int b) 
          向此檔案寫入指定的位元組。
 void writeBoolean(boolean v) 
          按單位元組值將 boolean 寫入該檔案。
 void writeByte(int v) 
          按單位元組值將 byte 寫入該檔案。
 void writeBytes(String s) 
          按位元組序列將該字串寫入該檔案。
 void writeChar(int v) 
          按雙位元組值將 char 寫入該檔案,先寫高位元組。
 void writeChars(String s) 
          按字元序列將一個字串寫入該檔案。
 void writeDouble(double v) 
          使用 Double 類中的 doubleToLongBits 方法將雙精度引數轉換為一個 long,然後按八位元組數量將該 long 值寫入該檔案,先定高位元組。
 void writeFloat(float v) 
          使用 Float 類中的 floatToIntBits 方法將浮點引數轉換為一個 int,然後按四位元組數量將該 int 值寫入該檔案,先寫高位元組。
 void writeInt(int v) 
          按四個位元組將 int 寫入該檔案,先寫高位元組。
 void writeLong(long v) 
          按八個位元組將 long 寫入該檔案,先寫高位元組。
 void writeShort(int v) 
          按兩個位元組將 short 寫入該檔案,先寫高位元組。
 void writeUTF(String str) 
          使用 modified UTF-8 編碼以與機器無關的方式將一個字串寫入該檔案。(這裡使用的是修改版的UTF-8)
 void seek(long pos) 
          設定到此檔案開頭測量到的檔案指標偏移量,在該位置發生下一個讀取或寫入操作。(pos是位元組數,可以通過此方法把資料寫到指定位置,不管前邊寫沒寫;或者覆蓋此位置之前的資料)(這是此類最重要的方法)
 int skipBytes(int n) 
          嘗試跳過輸入的 n 個位元組以丟棄跳過的位元組。(跳過指定位元組數,相對的)
import java.io.*;
import java.util.*;

/*
需求:有五個學生,每個學生有3門課的成績,從鍵盤輸入以上資料(包括姓名,三門課成績)
	  輸入格式:如:張三 30, 40, 60計算出總成績,並把學生的資訊和計算出來的總分數高低順序高低順序存放在磁碟檔案"stud.txt"
思路:1.首先鍵盤錄入是可以這樣表示
*/

class java
{
	public static void main(String[] args) 
	{
		new StudentIn().inputInfo();
	}
}

class Student implements Comparable<Student>
{
	private String name;
	private int math;
	private int en;
	private int chis;
	private int bio;
	
	//初始化成員
	Student(String name,int math,int en, int chis,int bio){
		this.name = name;
		this.math = math;
		this.en = en;
		this.chis = chis;
		this.bio = bio;
	}

	//獲取成員
	public int getMath(){
		return math;
	}

	public int getEn(){
		return en;
	}

	public int getChis(){
		return chis;
	}

	public int getBio(){
		return bio;
	}

	public int getGrade(){
		return math+en+chis+bio;
	}

	public String getName(){
		return this.name;
	}

	//複寫hashCode方法
	public int hashCode(){
		return name.hashCode()+math*6+en*2+chis*9+bio*7;
	}

	//複習equal方法
	public boolean equals(Object o){
		if(!(o instanceof Student))
			throw new ClassCastException();
		Student s = (Student)o;

		return name.equals(s.getName()) && getGrade() == s.getGrade();
	}

	//判斷大小
	public int compareTo(Student s){
		if(getGrade()==s.getGrade())
			return name.compareTo(s.getName());
		else
			return new Integer(getGrade()).compareTo(s.getGrade());
	}
}

class StudentIn
{

	public void inputInfo(){
		File file = new File("studentInfo.text");//獲取資料儲存檔案地址

		BufferedReader bufr = null;
		PrintWriter pw = null;

		//儲存資料的集合,對學生成績總分進行排序,按照從大到小的順序
		TreeSet<Student> ts = new TreeSet<Student>(Collections.reverseOrder());
		try{
			bufr = new BufferedReader(new InputStreamReader(System.in));
			pw = new PrintWriter(new BufferedWriter(new FileWriter(file)),true);

			for(int x=0;x<3;x++){//存入三個學生
				System.out.print("請輸入學生名稱:");
				String name = bufr.readLine();
				System.out.print("請輸入數學成績:");
				int math = Integer.parseInt(bufr.readLine());
				System.out.print("請輸入英語成績:");
				int en = Integer.parseInt(bufr.readLine());
				System.out.print("請輸入語文成績:");
				int chis = Integer.parseInt(bufr.readLine());
				System.out.print("請輸入生物成績:");
				int bio = Integer.parseInt(bufr.readLine());
				System.out.println("======================");
				
				Student s = new Student(name,math,en,chis,bio);

				ts.add(s);//存入TreeSet集合,並進行排序
			}
			
			//寫入資料到檔案
			for(Student s : ts){
				pw.println("姓名:"+s.getName());
				pw.println("數學:"+s.getMath());
				pw.println("英語:"+s.getEn());
				pw.println("語文:"+s.getChis());
				pw.println("生物:"+s.getBio());
				pw.println("總分:"+s.getGrade());
				pw.println();
			}

			pw.close();
			bufr.close();//便於觀看,未新增到finally程式碼塊
			
		}catch(IOException e){
			throw new RuntimeException("存入資料出錯",e);
		}
		System.out.print("========資訊存入成功========");
	}

}

相關文章