輸入/輸出(一)
File類
訪問檔案和目錄
不管是檔案還是目錄都是使用File來操作的,File能新建、刪除、重新命名檔案和目錄,File不能訪問檔案內容本身。如果需要訪問檔案內容本身,則需要使用輸入/輸出流。
File類提供了很多方法來操作檔案和目錄:
流的分類
1. 輸入流和輸出流
- 輸入流:只能從中讀取資料,而不能向其寫入資料;
- 輸出流:只能向其寫入資料,而不能從中讀取資料;
Java的輸入流主要由InputStream和Reader作為基類,而輸出流則主要由OuputStream和Writer作為基類。它們都是一些抽象基類,無法直接建立例項。
2. 位元組流和字元流
- 位元組流:操作的資料單元是8位的位元組;
- 字元流:操作的資料單元是16位的字元;
位元組流主要由InputStream和OuputStream作為基類,而字元流主要由Reader和Writer作為基類。
3. 節點流和處理流
- 節點流:可以從/向一個特定的IO裝置(如磁碟、網路)讀/寫資料的流,節點流也被稱為低階流;
- 處理流:用於對一個已存在的流進行連線或封裝,通過封裝後的流來實現資料讀/寫功能,處理流也被稱為高階流;
當使用處理流進行輸入/輸出時,程式並不會直接連線到實際的資料來源,沒有和實際的輸入/輸出節點連線。
流的概念模型
Java的IO流40多個類都是從如下4個抽象基類派生的:
- InputStream/Reader:所有輸入流的基類,前者是位元組輸入流,後者是字元輸入流;
- OuputStream/Writer:所有輸出流的基類,前者是位元組輸出流,後者是字元輸出流;
對於InputStream和Reader而言,它們把輸入裝置抽象成一個水管,這個水管裡的每個水滴依次排列:
位元組流和字元流的處理方式其實非常相似,只是它們處理的輸入/輸出單位不同而已。輸入流使用隱式的記錄指標來表示當前正準備從哪個“水滴”開始讀取,每當程式從InputStream/Reader裡取出一個或多個“水滴”後,記錄指標自動向後移動。除此之外InputStream和Reader裡都提供一些方法來控制記錄指標的移動。
對於OuputStream和Writer而言,它們同樣把輸出裝置抽象成一個水管,只是這個水管裡沒有任何水滴:
當執行輸出時,程式相當於依次把水滴放入到輸出流的水管中。輸出流同樣採用隱式的記錄指標來標識當前水滴即將放入的位置,每當程式OuputStream和Writer裡輸出一個或多個水滴後,記錄指標自動向後移動。
處理流可以嫁接在任何已存在的流的基礎之上,這就允許Java應用程式採用相同的程式碼、透明的方式來訪問不同的輸入/輸出裝置的資料流:
InputStream和Reader
InputStream和Reader是所有輸入流的抽象基類,本身不能建立例項來執行輸入,但它們將成為所有輸入流的模板:
FileInputStreamTest.java
public class FileInputStreamTest
{
public static void main(String[] args) throws IOException
{
//建立位元組輸入流
FileInputStream fis = new FileInputStream("FileInputStreamTest.java");
//建立一個長度為1024的“竹筒”
byte[] bbuf = new byte[1024];
//用於儲存實際讀取的位元組數
int hasRead = 0;
//使用迴圈來重複“取水”過程
while ((hasRead = fis.read(bbuf)) > 0 )
{
//取出“竹筒”中水滴(位元組),將位元組陣列轉換成字串輸入!
System.out.print(new String(bbuf , 0 , hasRead ));
}
fis.close();
}
}
控制檯輸出:
FileReaderTest.java
//建立字元輸入流
FileReader fr = new FileReader("FileReaderTest.java");
//建立一個長度為32的“竹筒”
char[] cbuf = new char[32];
與前面FileInputStreamTest沒有什麼太大區別;
OuputStream和Writer
FileOutputStreamTest.java
public class FileOutputStreamTest
{
public static void main(String[] args) throws IOException
{
FileInputStream fis = null;
FileOutputStream fos = null;
try
{
//建立位元組輸入流
fis = new FileInputStream("FileOutputStreamTest.java");
//建立位元組輸入流
fos = new FileOutputStream("newFile.txt");
byte[] bbuf = new byte[32];
int hasRead = 0;
//迴圈從輸入流中取出資料
while ((hasRead = fis.read(bbuf)) > 0 )
{
//每讀取一次,即寫入檔案輸出流,讀了多少,就寫多少。
fos.write(bbuf , 0 , hasRead);
}
}
catch (IOException ioe)
{
ioe.printStackTrace();
}
}
}
執行,將看到系統當前路徑下多了一個檔案:newFile.txt。內容與FileOutputStreamTest.java一樣;
FileWriterTest.java
public class FileWriterTest
{
public static void main(String[] args) throws IOException
{
//建立字元輸出流
try(FileWriter fw = new FileWriter("poem.txt")) //帶資源的try,java會對try塊引數中宣告的物件自動呼叫close()
{
fw.write("錦瑟 - 李商隱\r\n");
fw.write("錦瑟無端五十弦,一弦一柱思華年。\r\n"); //\r\n這是Windows平臺的換行符
fw.write("莊生曉夢迷蝴蝶,望帝春心託杜鵑。\r\n");
fw.write("滄海月明珠有淚,藍田日暖玉生煙。\r\n");
fw.write("此情可待成追憶,只是當時已惘然。\r\n");
}
catch (IOException ioe)
{
ioe.printStackTrace();
}
}
}
處理流的用法
使用處理流的典型思路是,使用處理流來包裝節點流,程式通過處理流來執行輸入/輸出功能,讓節點流與底層的I/O裝置、檔案互動。
實際識別處理流非常簡單,只要流的構造器引數不是一個物理節點,而是已經存在的流,那麼這種流就一定是處理流;而所有節點流都是直接以物理IO節點作為構造器引數的。
PrintStreamTest.java
public class PrintStreamTest
{
public static void main(String[] args)throws IOException
{
try(FileOutputStream fos = new FileOutputStream("test.txt");//建立一個節點輸出流:FileOutputStream
PrintStream ps = new PrintStream(fos))//以PrintStream來包裝FileOutputStream輸出流
{
//使用PrintStream執行輸出
ps.println("普通字串");
ps.println(new PrintStreamTest());
}
catch (IOException ioe)
{
ioe.printStackTrace();
}
}
}
程式使用處理流非常簡單,通常只需要在建立處理流時傳入一個節點流作為構造器引數即可;
輸入/輸出流體系
如果進行輸入/輸出的內容是文字內容,則應該考慮使用字元流;如果進行輸入/輸出的內容是二進位制內容,則應該考慮使用節點流;
轉換流
輸入/輸出流體系中還提供了兩個轉換流,這兩個轉換流用於實現將位元組流轉換成字元流:
- InputStreamReader:將位元組輸入流轉換為字元輸入流;
- OutputStreamWriter:將位元組輸出流轉換為字元輸出流;
為什麼沒有把字元流轉換成位元組流的轉換流呢?
因為字元流比位元組流操作更加方便,所以如果有一個流已經是字元流了,是一個用起來更加方便的流了,那麼為什麼要轉換成位元組流呢?
KeyinTest.java
public class KeyinTest
{
public static void main(String[] args)
{
try(InputStreamReader reader = new InputStreamReader(System.in);//將Sytem.in物件轉換成Reader物件
BufferedReader br = new BufferedReader(reader); )//將普通Reader包裝成BufferedReader
//BufferedReader流具有緩衝功能,它可以一次讀取一行文字----以換行符為標誌,如果沒有讀到換行符,則程式阻塞,等到讀到換行符為止
{
String buffer = null;
//採用迴圈方式來一行一行的讀取
while ((buffer = br.readLine()) != null)
{
//如果讀取的字串為"exit",程式退出
if (buffer.equals("exit"))
{
System.exit(1);
}
//列印讀取的內容
System.out.println("輸入內容為:" + buffer);
}
}
catch (IOException ioe)
{
ioe.printStackTrace();
}
}
}
以上只是學習所做的筆記,不做任何用途(其實就是照著書抄啦)!!!
書籍:瘋狂Java講義