File類:
程序中操作文件和目錄都可以使用File類來完成即不管是文件還是目錄都是使用File類來操作的,File能新建,刪除,重命名文件和目錄,但File不能訪問文件內容本身,如果需要訪問文件本身,則需要使用輸入/輸出流,該類是位於java.io包下的
輸入與輸出IO:
輸入流:只能從中讀取數據,而不能向其中寫入數據(由InputStream(字節流)和Reader(字符流)作為基類)
輸出流:只能向其寫入數據,而不能從中讀取數據(由OutputStream(字節流)和Writer(字符流)作為基類)
java的io總共涉及40多個類,但都是從這四個抽象基類中派生出來的
InputStream最重要的三個read方法:
Reader中的read方法:
從這兩個抽象類提供的方法就可以看出其實功能基本是一樣的,只是操作的數據單元不一樣而已
由於InputStream與Reader都是抽象類,是不能進行實例化的,我們只能用他們的子類來創建實例,它們分別提供了一個子類用於讀取文件的輸入流:FileInputStream和
FileReader,這兩個子類都是節點流(與處理流相對)-----會直接與指定的文件關聯而無包裝。下面代碼演示FileInputStream使用read(byte[] b):
package xidian.sl.io; import java.io.FileInputStream; import java.io.FileReader; public class InputStreamTest { /** * 使用FileInputStream讀取該類本身 * */ public static void FileInputStreamTest() throws Exception{ FileInputStream fis = null; try{ //創建字節輸入流 fis = new FileInputStream("src/xidian/sl/io/InputStreamTest.java"); //創建一個長度為1024的字節數組來存取 byte[] bbuf = new byte[1024]; //用於保存實際讀取的字節數 int hasRead = 0; //使用循環來進行重復讀取 while((hasRead = fis.read(bbuf))> 0){ //取出字節,將字節數組轉化為字符串輸出 System.out.println(new String(bbuf, 0 , hasRead)); } }finally{ //關閉文件輸入流 fis.close(); } } /** * 使用FileReader讀取該類本身 * */ public static void FileReaderTest() throws Exception{ FileReader fr = null; try{ //創建字節輸入流 fr = new FileReader("src/xidian/sl/io/InputStreamTest.java"); //創建一個長度為1024的字節數組來存取 char[] bbuf = new char[40]; //用於保存實際讀取的字節數 int hasRead = 0; //使用循環來進行重復讀取 while((hasRead = fr.read(bbuf))> 0){ //取出字節,將字節數組轉化為字符串輸出 System.out.println(new String(bbuf, 0 , hasRead)); } }finally{ //關閉文件輸入流 fr.close(); } } public static void main(String[] args) throws Exception{ InputStreamTest.FileInputStreamTest(); InputStreamTest.FileReaderTest(); } }
可以看到這兩個子類的使用方式可以說是完全一樣的,不過這里要注意一個問題:字節流FileInputStream是根據字節來讀取的,而一個中文是占兩個字節的,如果包含很多中文的文件被字節流分多次進行讀取,可能會造成亂碼,因為有可能會導致剛好將一個中文分兩次讀取,這樣就會亂碼了,因此如果中文包含多的話還是使用字符流FileReader比較好,
在字節流與字符流之間選擇的規律:如果需要進行輸入/輸出的內容是文本內容,則應該考慮使用字符流,如果需要進行輸入/輸出的是二進制內容,則應該考慮使用字節流,因為字節流的功能比字符流強大,計算機中所有的數據都是二進制的,而字節流可以處理所有的二進制文件;
OutputStream中最重要的write方法:
Writer中最重要的write方法:
Writer類中多了兩個對字符串的操作類,因此如果是直接輸出字符串就選用Writer會比較的方便;
與輸入流一樣,輸出流也有兩個文件操作的子類:FileOutputStream和FileWrite
package xidian.sl.io; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.FileWriter; public class OutputStreamTest { /** * 使用字節流輸出 * */ public static void FileOutputStreamTest() throws Exception{ FileInputStream fis = null; FileOutputStream fos = null; try{ //創建字節輸入流 fis = new FileInputStream("src/xidian/sl/io/InputStreamTest.java"); //創建字節輸出流 fos = new FileOutputStream("src/xidian/sl/io/Output.txt"); byte[] bt = new byte[40]; int hasRead = 0; //循環從輸入流中讀取數據 while((hasRead = fis.read(bt))> 0){ //每讀取一個,即寫入文件輸出流,讀了多少就寫多少 fos.write(bt, 0, hasRead); } }catch (Exception e) { e.printStackTrace(); }finally{ /** * 流在關閉時會自動執行flash,將緩沖中的數據flush到物理節點中 * 所以關閉時很重要的 * */ if(fis != null){ fis.close(); } if(fos != null){ fos.close(); } } } /** * 使用字符流輸出字符串會顯得比較的方便 * */ public static void FileWriteTest() throws Exception{ FileWriter fw = null; try{ //創建字節輸出流 fw = new FileWriter("src/xidian/sl/io/Output.txt"); fw.write("溫州醫學院\r\n"); fw.write("信息與管理專業\r\n"); fw.write("溫州醫學院\r\n"); fw.write("溫州醫學院\n"); fw.write("溫州醫學院"); }catch (Exception e) { e.printStackTrace(); }finally{ if(fw != null){ fw.close(); } } } public static void main(String[] args) throws Exception{ OutputStreamTest.FileOutputStreamTest(); OutputStreamTest.FileWriteTest(); } }
上面是節點流的基本使用,下面將了解處理流的使用,處理流會顯得更加的高效
區分節點流於處理流的方法是:只要流的構造器的參數不是一個物理節點,而是已存在的流,那這個流一定是處理流,因為所有的節點流都是直接以物理io節點作為構造器的參數、
(如file)。
舉例:PrintStream處理流來封裝FileOutputStream節點流,進行輸出,由於PrintStream類的輸出功能非常的強大,因此我們需要輸出文本內容一般都會將輸出流包裝成PrintStream后輸出
package xidian.sl.io; import java.io.FileOutputStream; import java.io.PrintStream; public class PrintStreamTest { public static void main(String[] args){ PrintStream ps = null; try{ //創建一個節點輸出流 FileOutputStream fos = new FileOutputStream("src/xidian/sl/io/Output.txt"); //以PrintStream處理流來包裝FileOutputStream節點流 ps = new PrintStream(fos); ps.println("普通字符串"); ps.println(new PrintStreamTest()); }catch (Exception e) { e.printStackTrace(); }finally{ ps.close(); } } }
其實我們一直使用的標准輸出System.out的類型都是PrintStream:
從上面的實例就可以看出將節點流封裝成處理流很簡單,只需調用處理流的構造方法來傳入節點流就可以了;而且看到上面流的關閉只是關閉了處理流而未去關閉節點流,這樣做是完全正確的,以后我們在關閉流的時候只需要關閉最上層的處理流即可;
字符流中有兩個特別的流來處理字符串的:StringReader和StringWriter,
可以看到使用時實例化只需要傳入一個字符串即可:例子:
package xidian.sl.io; import java.io.StringReader; import java.io.StringWriter; public class StringNodeTest { /** * @param args */ public static void main(String[] args) { // TODO Auto-generated method stub String src = "你是個神"; StringReader sr = new StringReader(src); char[] chars = new char[40]; int hasRead = 0; try{ //采用循環的方式 while((hasRead = sr.read(chars))>0){ System.out.println(new String(chars, 0, hasRead)); } }catch (Exception e) { e.printStackTrace(); }finally{ sr.close(); } //創建StringWriter StringWriter sw = new StringWriter(40); sw.write("你是一個大神"); sw.write("你也是一個大神"); System.out.println(sw.toString()); } }
io系統提供的兩個轉換流:InputStreamReader和OutputStreamWriter,都是將字節流轉化為字符流
在java中是使用System.in來提供鍵盤輸入的,但這個標准輸入流是InputStream類的實例:
而前面講到了當處理的是文本內容時,使用字符流會顯得比較方便,正好鍵盤輸入就是文本的操作,因此我們有必須將System.in轉換為字符流:
package xidian.sl.io; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; public class KeyinTest { public static void main(String[] args){ BufferedReader br = null; try{ //將System.in對象轉化為Reader對象 InputStreamReader isr = new InputStreamReader(System.in); //將節點流包裝為處理流 br = new BufferedReader(isr); String buffer = null; //采用循環的方式一行一行讀取 while((buffer = br.readLine()) != null){ System.out.print("輸入的內容 = "+ buffer); } }catch (Exception e) { e.printStackTrace(); }finally{ try { br.close(); } catch (IOException e) { e.printStackTrace(); } } } }
PrintStream是有強大的輸出功能,而BufferReader有強大的輸入(即讀取),因此在操作讀取文本內容時盡可能將其轉化為BufferReader,可以方便的使用readLine()方法
接下來最為強大的文件操作類RandomAccessFile來了,這個類既可以向文件輸入數據,也可以輸出數據,並且他與不同的流最大的不同就是“支持文件任意位置的訪問”,即程序可以控制讀取文件哪個位置的內容;
從構造方法上可以看出,除了提供一個文件或文件名外還需要提供一個String參數mode,mode規定了RandomAccessFile類訪問文件的模式:
1. “r”:以只讀的方式打開指定文件
2. “rw”:以讀取,寫入方式打開指定文件,並且文件不存在會自動進行創建
3.“rws”與“rwd”:與“rw”類似,只是要求文件內容或元數據的每個更新都同步寫入底層存儲設備