一、流的分類
按輸入輸出分類
- 輸入流:將外設中的數據讀取到內存中。
- 輸出流:將內存中的數據寫到外設中。
按字節,字符分類
- 字節流:傳輸過程中,傳輸數據的最基本單位是字節的流。
- 字符流:傳輸過程中,傳輸數據的最基本單位是字符的流。
二、字符流
由來
字節流讀取字節數據後,先不操作數據,而是去查找指定的編碼表,然後再對文字進行操作。簡單來說,字符流=字節流+編碼表。
操作文本的字符流
向文件中寫數據
FileWriter
主要構造方法:
- new FileWriter(String path):如果此文件不存在,則會自動創建此文件;如果,此文件已經存在,則會覆蓋此文件;下同。
- new FileWriter(String path,boolean append):如果append爲true,那麼表示可以追加數據(也就是多次使用write方法,不會把以前寫入文本的數據覆蓋掉,可以在文本後面追加數據),下同。
- new FileWriter(File file)
- new FileWriter(File file,boolean append)
實用方法:
- write(String str):把字符串str寫入到緩衝區中
- write(char []ch):把字符數組ch寫入到緩衝區中
- write(char ch[],int start,int offset):把字符數組ch中從start開始的長度爲offset的字符寫入到緩衝區中
- write(int c):把字符c寫入緩衝區中
- flush():把緩衝區中的數據刷新到磁盤中
- close():關閉流,關閉資源。在關閉之前要先刷新數據(close會自動調用flush方法)
換行:
- 因爲在不同的操作系統中,換行符是不一樣的,所以需要適配操作系統。Java中已經爲我們解決了該問題。
- 換行符的獲取:final String line=System.getProperty(“line.separator”);
IO異常的處理:
- 標準寫法:
FileWriter fw = null;
try {
fw = new FileWriter("G:/xxx.txt", true);
fw.write("窗外的麻雀,在電線杆上多嘴,你說這一句,很有夏天的感覺");
} catch (IOException e) {
System.out.println(e.getMessage());
} finally {
// 只有當fw不爲空的時候,才能關閉資源,否則會有空指針異常
if (fw != null) {
try {
fw.close();
} catch (IOException e) {
throw new RuntimeException("老鐵,關閉資源失敗!");
}
}
}
讀取文件中的數據
FileReader
構造方法:
- new FileReader(String path)
- new FileReader(File file)
實用方法:
- int read():每次讀取一個字符,返回讀到的字符(int類型);如果返回的是-1,那麼就表示已經讀到了流的末尾。
- int read(char ch[]):每次返回讀取到的字符數,即讀到了幾個字符
該方法原理圖:
- int read(char ch[],int start,int len)
- close():關閉資源。
小練習(複製並粘貼一個文本文件)
- 思路:使用FileReader不斷地把文本從源文件中讀取出來,並把讀到的字符(或字符數組)用FileWriter寫入到另一個文件。
- 注意點:複製的過程中可能會產生中文亂碼,這是因爲編碼方式不一致導致的:FileReader讀取字符以及FileWriter寫字符時的編碼方式是根據操作系統的編碼方式來的,而txt文本的編碼方式可能會與上述編碼方式不一致,所以會產生亂碼。
- 文本文件複製原理圖:
- 實現代碼:
public void test01() {
FileReader fr = null;
FileWriter fw = null;
try {
fr = new FileReader("G:\\系統屬性.txt");
fw = new FileWriter("F:\\系統屬性.txt", true);
char ch[] = new char[1024];
int len = -1;
while ((len = fr.read(ch)) != -1) {
fw.write(ch, 0, len);
fw.flush();
}
} catch (IOException e) {
e.printStackTrace();
} finally {
if (fr != null) {
try {
fr.close();
} catch (IOException e) {
throw new RuntimeException("異常......");
}
}
if (fw != null) {
try {
fw.close();
} catch (IOException e) {
throw new RuntimeException("異常......");
}
}
}
}
緩衝區
BufferedWriter
構造方法:
- new BufferedWriter(Writer writer):創建BufferedWriter時必須把字符輸出流傳入,這相當於把資源傳入。緩衝區大小爲默認。
- new BufferedWriter(Writer writer,int size):指定緩衝區大小爲size。
實用方法:
- write(String str)
- write(int c)
- write(char ch[])
- write(char ch[],int start,int len)
- newLine():換行。
- flush():刷新,最好每使用緩衝流寫一次數據就刷新一次。
- close():關閉資源。注意: 關閉BufferedWriter後,傳入的字符輸出流也已經被關閉了
BufferedReader
構造方法:
- new BufferedReader(Reader reader):創建BufferedReader,並把Reader傳入。緩衝區大小爲默認。
- new BufferedReader(Reader reader,int size):指定緩衝區大小爲size。
實用方法:
- int read()
- int read(char ch[]):讀取字符,並存入字符數組,讀取規律同Reader。
- int read(char ch[],int start,int len)。
- String readlLine():讀取一行文本,返回讀取到的字符串,如果讀取到流的末尾,則返回空。
read()以及readLine()方法原理圖:
LineNumberReader
介紹:LineNumberReader是BufferedReader的子類,LineNumberReader記錄了當前行號,也可以獲取/設置行號。
構造方法:
- new LineNumberReader(Reader reader)
實用方法:
- String readLine()
- read(char ch[])
- read(char[],int start,int len)
- int getLineNumber():獲取讀取的當前行(是第幾行)
- void setLineNumber():行數默認是0,此時就從第一行開始讀取;如果自己設置行爲n,那麼就從第n+1行開始讀取。
自定義緩衝區
代碼如下:
//自定義BufferedReader
public class MyBufferedReader {
private FileReader fr;
private char ch[] = new char[10]; //緩衝區
private int pos = -1; //緩衝區中指針的位置
private int count = 0; //緩衝區中元素個數
//構造方法
public MyBufferedReader(FileReader fr) {
this.fr = fr;
}
//自定義read方法
//br中的read方法讀取數據時從緩衝區中讀取
public int myRead() throws IOException {
//緩衝區中元素個數爲0
if (this.count == 0) {
pos = -1; //角標賦值爲-1
//從數據源中讀取數據存入緩衝區
int len = fr.read(this.ch);
if (len == -1)
return -1;
this.count = len;
//從緩衝區中讀取一個字符
--this.count; //緩衝區中的字符減少一個
return (int) ch[++pos];
} else {
//緩衝區有數據
--this.count;
return (int) ch[++pos];
}
}
//讀取一行文本
public String myReadLine() throws IOException {
StringBuffer sb = new StringBuffer();
int len = 0;
while ((len = this.myRead()) != -1) {
char c = (char) len;
if (c == '\r')
continue; //遇到'\r'繼續讀取
if (c == '\n')
//直接就return,
// 因爲如果跳出了循環,那麼就沒法分清是讀取到了流末尾出的循環,
// 還是用break跳出的循環
return sb.toString();
sb.append(c);
}
return null; //返回null表示上面的循環結束,即爲讀到了流末尾
}
public void close() throws IOException {
fr.close();
}
}
裝幀設計模式
- 介紹:裝幀設計模式是爲了使類的功能更加強大。
- 實現原理:
-請看如下代碼:
public class Person {
public void eat() {
System.out.println("恰飯!!!");
}
}
public class NewPerson {
private Person p;
public NewPerson(Person p) {
this.p = p;
}
public void eat() {
System.out.println("飯前喝湯");
p.eat();
System.out.println("飯後甜點");
}
}
- 裝幀設計模式與繼承的區別:
- 裝飾類比繼承更加精煉
三、字符流
FileInputStream
構造方法:
- new FileInputStream(String path)
- new FileInputStream(File file)
實用方法:
- int read():讀取一個字節的數據。若返回-1表示讀取到了流的末尾。
- int read(byte b[]):讀取數組b的長度的字節數據進入b數組中,返回讀取到的字節數,返回-1表示讀到了流的末尾。詳細的讀取數據到數組b中的過程同上,此處不再贅述。
- int read(byte b[],int off,int len)
- int available():返回文件中可以讀取的數據的估計值(返回字節大小);本方法可以在創建字節數組時定義數組的大小,不過,當文件很大的時候,不能用此方法。
FileOutputStream
構造方法:
- new FileOutputStream(String path)
- new FileOutputStream(File file)
- new FileOutputStream(String path,boolean flag)
- new FileOutputStream(File file,boolean flag)
實用方法
- void write(byte b[])
- void write(byte b[],int off,int len)
- void write(int b):寫入一個字符(char),只不過是以int來表示。
BufferedInputStream
構造方法:
- new BufferedInputStream(InputStream fis)
- new BufferedInputStream(InputStream fis, int size)
實用方法:
- int read()
- int read(byte []b)
- int read(byte []b,int off,int len)
- close()
BufferedOutputStream
構造方法:
- new BufferedOutputStream(OutputStream fos)
- new BufferedOutputStream(OutputStream fos,int size)
實用方法:
- void write(byte []b)
- void write(byte []b,int off,int len)
- void write(int ch)
四、轉換流
解釋
- 看不懂:磁盤中的存儲的數據都是“看不懂”的,也就是以字節形式存儲的。
- 看得懂:內存中存儲的數據都是“看得懂”的,也就是以字符形式存儲的。
InputStreamReader
- new InputStreamReader(InputStream in):
- new InputStreamReader(InputStream in,String charsetName):需要傳入一個InputStream對象,採用當前平臺(操作系統)的解碼方式來解碼。
OutputStreamWriter:使用指定的解碼方式來解碼。
- 把“看得懂”的數據轉換成"看不懂"的數據,即把字符流編碼成字節流。
- new OutputStreamWriter(OutputStream out):需要傳入一個OutputStream對象,使用當前平臺(操作系統)的編碼方式來編碼。
- new OutputStreamWriter(OutputStream out,String charsetName):使用指定的編碼方式來編碼。
轉換流原理圖
以指定編碼方式來讀取某文本
遇到這種需求時,就需要用到轉換流,因爲只有轉換流才能指定編解碼格式。GBK把一個漢字解析成兩個字節,UTF-8把一個漢字解析成三個字節。所以,如果寫入文本用的編碼格式與讀取文本用的解碼格式不一樣的話,就會讀取到亂碼(也可能是問號?)。
代碼如下:
@Test
//使用指定編碼方式寫入一個字符串到文本文件中
public void test03() throws IOException {
OutputStreamWriter outputStreamWriter =
new OutputStreamWriter(new FileOutputStream("G:\\轉換流.txt"), "GBK");
outputStreamWriter.write("你好,大兄弟!!");
outputStreamWriter.flush();
}
@Test
//使用指定的解碼格式讀取文本文件中的字符串
public void test04() throws IOException {
InputStreamReader inputStreamReader =
new InputStreamReader(new FileInputStream("G:\\轉換流.txt"), "UTF-8");
char[] ch = new char[10];
int len = -1;
while ((len = inputStreamReader.read(ch)) != -1) {
System.out.println(new String(ch, 0, len));
}
}
五、File
構造方法:
- new File(String path):根據路徑path把該文件(夾)封裝成一個對象。
- new File(String parentPath,String childPath):根據兩個字符串的路徑,把該文件(夾)封裝成對象。這樣做的好處是可以靈活填寫路徑。
- new File(File f,String path):根據文件對象f以及路徑path把對應的文件(夾)封裝成對象。
File裏面的字段(final類型):
- 與系統有關的默認名稱分隔符
- File.separator:String類型。Windows下是"",Linux下是"/"。
- File.separatorChar:char類型。
- 與系統有關的路徑分隔符
- File.pathSeparator: 此字符用於分隔以路徑列表形式給定的文件序列中的文件名,在 UNIX 系統上此字段爲:,在 Microsoft Windows 系統上,它爲;。
- File.pathSeparatorChar:char類型。
常用方法:
- 獲取
- 文件(夾)名字:String getName()
- 文件(夾)路徑
- 相對路徑:String getPath();是相對於項目所處的路徑,File構造方法裏面傳入什麼路徑,getPath就獲取什麼路徑。因爲路徑裏面不寫磁盤的話,那就一定是相對路徑。寫了磁盤就是絕對路徑。
- 絕對路徑:String getAbsolutePath()
- 文件(夾)大小:long length();返回字節大小。
- 文件(夾)最後修改時間:long lastModified(),返回距離1970年1月1日的毫秒數。
- 系統目錄及其容量
- static File [] listRoots():獲取系統目錄(可以理解爲磁盤目錄),返回一個File數組。
- long getFreeSpace():獲取該目錄(磁盤)的可用空間,返回字節數(long類型)。
- long getTotalSpace():獲取該目錄(磁盤)的總空間,返回字節數(long類型)。
- long getUsableSpace():獲取該目錄(磁盤)可以被虛擬機使用的空間,返回字節數(long類型)。
- 當前文件夾裏面的文件或文件夾名字
- String [] list()
- String [] list(FilenameFilter filter):傳入一個過濾器(通過文件名字來過濾),過濾器的解釋請看下文。
- 注意:File封裝的必須是一個文件夾(目錄),如果封裝的是一個文件,則會拋空指針異常。
- 當前文件夾裏面的文件(夾)
- File [] listFiles():獲取某個文件夾裏面的文件(夾),返回File數組,可以用來操作文件,而不僅僅是獲取文件名字。
- File [] listFiles(FilenameFilter filter):傳入一個過濾器(通過文件名字來過濾)。
- File [] listFiles(FileFilter filter):傳入一個過濾器(通過文件來過濾)。
- 過濾器解釋
- 過濾器分爲兩種,一種是FilenameFilter(通過文件名字來過濾,比如可以過濾掉非.txt後綴的文件),另一種是FileFilter(通過文件來過濾,比如可以過濾掉隱藏文件)。
- FilenameFilter:FilenameFilter是一個接口,它裏面有一個抽象方法boolean accept(File f,String name),文件的過濾就是通過該方法來過濾的。自定義過濾器實現該接口後,實現accept()方法,如果該方法返回true,那麼就留下當前遍歷到的文件,否則就不留下。
- FileFilter:FileFilter過濾器的原理與FilenameFilter的原理是一樣的,只不過該接口裏面的抽象方法是boolean accept(File f),方法參數裏面只有一個File對象,並沒有文件名。因爲該過濾器是通過File來過濾的,與文件名無關。
- 過濾器原理圖
- 創建與刪除
- 文件
- 創建:boolean createNewFile();根據文件的路徑創建一個文件,如果該文件存在,則不創建並返回false;否則創建並返回true。下同,自己體會。(與IO流那邊是不同的,那邊每一次都是直接覆蓋)。
- 刪除:boolean delete();刪除該文件。刪除成功返回true,刪除失敗返回false。下同,自己體會。如果某個文件裏面有內容,比如File f=new File(“G:/a”),而a文件裏面有其他的文件或文件夾,那麼f.delete()就會失敗。Windows刪除文件是從裏面往外面刪的。
- 文件
- 創建:
- boolean mkdir():創建單個文件夾,例如只創建一個a文件夾。
- boolean mkdirs():創建多層文件夾,例如創建a/b/c/d/e/f。
- 刪除:boolean delete();刪除該文件。如果File f=new File(“G:/a/b/c/d/e/f”),那麼此時定位的文件夾是f,故f.delete()刪除的是f文件夾,其餘文件夾不會被刪除。
- 創建:
- 文件
- 判斷
- boolean exists():判斷文件(夾)是否存在,存在則返回true,否則返回false。
- boolean isFile():判斷是否爲文件。
- boolean isHidden():判斷文件(夾)是否爲隱藏文件。
- boolean isDirectory():判斷是否爲文件夾。
- boolean canRead():判斷該文件(夾)是否可讀。
- boolean canWrite():判斷該文件(夾)是否可寫。
- boolean canExecute():判斷該文件(夾)是否可運行。
- 重命名
- boolean renameTo(File des):如果源文件src和目標文件des在同一個磁盤下,那麼src.renameTo(des)則會以des的名字來重命名src。如果src和des不在同一個磁盤裏,那麼src.renameTo(des)則會把src文件剪切到des所在的磁盤,並以des的名字重命名src。