Java笔记整理六(File类,递归,字节流IO,字符流IO,流中的异常处理,属性集Properties,缓冲流,转换流,序列化,打印流)
1.File類
java.io.File 類是文件和目錄路徑名的抽象表示,主要用于文件和目錄的創建、查找和刪除等操作。
文件和目錄路徑名的抽象表示
java把文件和文件夾封裝位為一個File類,我們可以用File類的對文件和文件夾進行操作
使用File類我們可以
- 創建一個文件夾/文件
- 刪除文件/文件夾
- 獲取文件/文件夾
- 判斷文件/文件夾是否存在
- 獲取文件大小
File是一個與系統無關的類,任何系統都可以使用這個類中的方法。
重點:
file:文件
directory:目錄
path:路徑
static String pathSeparator:路徑分隔符
static char pathSeparator:路徑分隔符
static String separator:默認名稱分隔符
static char separator:默認名稱分隔符
路徑不能寫死了
“c:“+File.separator+”develop“+File.separator+a.txt”
絕對路徑,相對路徑
絕對路徑:完整路徑c:\a.txt
相對路徑:簡化路徑,相對當前項目的根目錄
注意:
File中的構造方法
-
public File(String pathname) :通過將給定的路徑名字符串轉換為抽象路徑名來創建新的 File實例。
可以是以文件/文件夾結尾。可以相對路徑也可以是絕對路徑,可以是存在的也可以不存在。創建File對象,只把字符串路徑封裝給File對象,不考慮路徑對象 它重寫了File的toString方法。 -
public File(String parent, String child) :從父路徑名字符串和子路徑名字符串創建新的 File實例。
父路徑和子路徑,可以單獨書寫,使用靈活 -
public File(File parent, String child) :從父抽象路徑名和子路徑名字符串創建新的 File實例。
父路徑和子路徑,可以單獨書寫,使用靈活,父路徑是File類型,可以使用File的方法對路徑進行一些操作,再使用路徑創建對象
獲取功能的方法
-
public String getAbsolutePath() :返回此File的絕對路徑名字符串。
-
public String getPath() :將此File轉換為路徑名字符串。
-
public String getName() :返回由此File表示的文件或目錄的名稱。
-
public long length() :返回由此File表示的文件的長度。
方法演示,代碼如下:
判斷功能的方法
- public boolean exists() :此File表示的文件或目錄是否實際存在。
- public boolean isDirectory() :此File表示的是否為目錄。
- public boolean isFile() :此File表示的是否為文件。
方法演示,代碼如下:
public class FileIs {public static void main(String[] args) {File f = new File("d:\\aaa\\bbb.java");File f2 = new File("d:\\aaa");// 判斷是否存在System.out.println("d:\\aaa\\bbb.java 是否存在:"+f.exists());System.out.println("d:\\aaa 是否存在:"+f2.exists());// 判斷是文件還是目錄System.out.println("d:\\aaa 文件?:"+f2.isFile());System.out.println("d:\\aaa 目錄?:"+f2.isDirectory());} } 輸出結果: d:\aaa\bbb.java 是否存在:true d:\aaa 是否存在:true d:\aaa 文件?:false d:\aaa 目錄?:true創建刪除功能的方法
-
public boolean createNewFile() :當且僅當具有該名稱的文件尚不存在時,創建一個新的空文件。 只能創建文件。不能創建文件夾。需要處理異常,必須路徑存在,否則會拋出io異常。
-
public boolean delete() :刪除由此File表示的文件或目錄。 不走回收站
-
文件/文件夾刪除成功,返回true
-
文件夾有內容不回刪除,返回false,構造方法中不存在也返回false
- public boolean mkdir() :創建由此File表示的目錄。只能創建單集空文件夾
- public boolean mkdirs() :創建由此File表示的目錄,包括任何必需但不存在的父目錄。可以創建單級文件夾也可以創建多級空文件夾。
- 返回值:true:文件夾不存在,創建文件夾,返回true
返回false:文件夾存在,不會創建,構造方法中給出的路徑不存在也返回false
只能創建文件夾不能創建文件
創建文件的路徑必須存在,否則會拋出異常
方法演示,代碼如下:
public class FileCreateDelete {public static void main(String[] args) throws IOException {// 文件的創建File f = new File("aaa.txt");System.out.println("是否存在:"+f.exists()); // falseSystem.out.println("是否創建:"+f.createNewFile()); // trueSystem.out.println("是否存在:"+f.exists()); // true// 目錄的創建File f2= new File("newDir"); System.out.println("是否存在:"+f2.exists());// falseSystem.out.println("是否創建:"+f2.mkdir()); // trueSystem.out.println("是否存在:"+f2.exists());// true// 創建多級目錄File f3= new File("newDira\\newDirb");System.out.println(f3.mkdir());// falseFile f4= new File("newDira\\newDirb");System.out.println(f4.mkdirs());// true// 文件的刪除System.out.println(f.delete());// true// 目錄的刪除System.out.println(f2.delete());// trueSystem.out.println(f4.delete());// false} }API中說明:delete方法,如果此File表示目錄,則目錄必須為空才能刪除。
目錄的遍歷
-
public String[] list() :返回一個String數組,表示該File目錄中的所有子文件或目錄。把獲取到的多個名稱存儲到一個String類型的數組中。
-
public File[] listFiles() :返回一個File數組,表示該File目錄中的所有的子文件或目錄。遍歷構造方法中給出的目錄,獲取所有文件或文件夾的名稱,把獲取到的多個名稱存儲到一個String類型的數組中
list和listFiles方法遍歷的是構造方法中給出的目錄
如果走早方法中給出路徑不存在,會拋出空指針異常
如果構造方法中給出的路徑不是一個目錄,會拋出空指針異常
小貼士:
調用listFiles方法的File對象,表示的必須是實際存在的目錄,否則返回null,無法進行遍歷。
遞歸
遞歸:指再當前方法體調用自己
分為直接遞歸和間接遞歸
注意事項:
- 遞歸一定要有條件限制,保證遞歸能夠停止
- 遞歸次數不能太多,否則可能會發生棧內存溢出
- 構造方法禁止遞歸
遞歸方法使用前提:
當調用方法的時候,方法主體不變,每次調用方法的參數不同,可以使用遞歸
使用遞歸計算1–n的和
使用遞歸必須明確:
實現代碼:
public class DiGuiDemo {public static void main(String[] args) {//計算1~num的和,使用遞歸完成int num = 5;// 調用求和的方法int sum = getSum(num);// 輸出結果System.out.println(sum);}/*通過遞歸算法實現.參數列表:int 返回值類型: int */public static int getSum(int num) {/* num為1時,方法返回1,相當于是方法的出口,num總有是1的情況*/if(num == 1){return 1;}/*num不為1時,方法返回 num +(num-1)的累和遞歸調用getSum方法*/return num + getSum(num-1);} }遞歸求階乘
階乘:所有小于及等于該數的正整數的積。
n的階乘:n! = n * (n-1) *...* 3 * 2 * 1 推理得出:n! = n * (n-1)!代碼實現:
public class DiGuiDemo {//計算n的階乘,使用遞歸完成public static void main(String[] args) {int n = 3;// 調用求階乘的方法int value = getValue(n);// 輸出結果System.out.println("階乘為:"+ value);}/*通過遞歸算法實現.參數列表:int 返回值類型: int */public static int getValue(int n) {// 1的階乘為1if (n == 1) {return 1;}/*n不為1時,方法返回 n! = n*(n-1)!遞歸調用getValue方法*/return n * getValue(n - 1);} }遞歸打印多級目錄
分析:多級目錄的打印,就是當目錄的嵌套。遍歷之前,無從知道到底有多少級目錄,所以我們還是要使用遞歸實現。
代碼實現:
public class DiGuiDemo2 {public static void main(String[] args) {// 創建File對象File dir = new File("D:\\aaa");// 調用打印目錄方法printDir(dir);}public static void printDir(File dir) {// 獲取子文件和目錄File[] files = dir.listFiles();// 循環打印/*判斷:當是文件時,打印絕對路徑.當是目錄時,繼續調用打印目錄的方法,形成遞歸調用.*/for (File file : files) {// 判斷if (file.isFile()) {// 是文件,輸出文件絕對路徑System.out.println("文件名:"+ file.getAbsolutePath());} else {// 是目錄,輸出目錄絕對路徑System.out.println("目錄:"+file.getAbsolutePath());// 繼續遍歷,調用printDir,形成遞歸printDir(file);}}} }文件搜索
搜索D:\aaa 目錄中的.java 文件。
分析:
代碼實現:
public class DiGuiDemo3 {public static void main(String[] args) {// 創建File對象File dir = new File("D:\\aaa");// 調用打印目錄方法printDir(dir);}public static void printDir(File dir) {// 獲取子文件和目錄File[] files = dir.listFiles();// 循環打印for (File file : files) {if (file.isFile()) {// 是文件,判斷文件名并輸出文件絕對路徑if (file.getName().endsWith(".java")) {System.out.println("文件名:" + file.getAbsolutePath());}} else {// 是目錄,繼續遍歷,形成遞歸printDir(file);}}} }文件過濾器優化
listFiles(FileFilter fileter)
File類中有兩個和ListFiles重載的方法,方法參數就是過濾器
java.io.FileFilter是一個接口,是File的過濾器。 該接口的對象可以傳遞給File類的
用于抽象路徑名(File對象)的過濾器,用來過濾文件
boolean accept(File pathname) :測試pathname是否應該包含在當前File目錄中,符合則返回true。
listFiles(FilenameFilter fileter) :
java.io.FilenameFilter接口:實現此接口的類實例可用于過濾器文件名
用于過濾文件名稱
boolean accept(File dir ,String name) :測試文件是否包含在某一文件夾中
File dir:構造方法中傳遞的被遍歷的目錄
String name::使用ListFiles方法遍歷目錄,獲取的每一個文件/文件夾的名稱
注意:兩個過濾器接口是沒有實現類的,需要我們自己寫實現類,重寫過濾方法accep,在方法中自己定義過濾器規則。
分析:
代碼實現:
public class DiGuiDemo4 {public static void main(String[] args) {File dir = new File("D:\\aaa");printDir2(dir);}public static void printDir2(File dir) {// 匿名內部類方式,創建過濾器子類對象File[] files = dir.listFiles(new FileFilter() {@Overridepublic boolean accept(File pathname) {return pathname.getName().endsWith(".java")||pathname.isDirectory();}});// 循環打印for (File file : files) {if (file.isFile()) {System.out.println("文件名:" + file.getAbsolutePath());} else {printDir2(file);}}} }FileFilter過濾器的原理和使用
必須明確兩件事情:
ListFiles方法一共做了三件事情:
accept方法返回的是一個boolean值
true: 會把傳遞過去的File對象保存到File數組中
false:就不會把傳遞過去的File對象保存到File數組中
因此,過濾的規則:
在accept方法中,判斷File對象
Lambda優化
分析:FileFilter是只有一個方法的接口,因此可以用lambda表達式簡寫。
lambda格式:
()->{ }代碼實現:
public static void printDir3(File dir) {// lambda的改寫File[] files = dir.listFiles(f ->{ return f.getName().endsWith(".java") || f.isDirectory(); });// 循環打印for (File file : files) {if (file.isFile()) {System.out.println("文件名:" + file.getAbsolutePath());} else {printDir3(file);}} }IO
IO的分類
根據數據的流向分為:輸入流和輸出流。
- 輸入流 :把數據從其他設備上讀取到內存中的流。
- 輸出流 :把數據從內存 中寫出到其他設備上的流。
格局數據的類型分為:字節流和字符流。
- 字節流 :以字節為單位,讀寫數據的流。
- 字符流 :以字符為單位,讀寫數據的流。
流:數據(字符,字節)一個字符=兩個字節
一個字節=八個二進制(八位)
| 字節流 | 字節輸入流 InputStream | 字節輸出流 OutputStream |
| 字符流 | 字符輸入流 Reader | 字符輸出流 Writer |
IO字節流
以二進制數字的形式保存,都一個一個的字節,那么傳輸時一樣如此
字節輸出流OutputStream
java.io.OutputStream抽象類是表示字節輸出流的所有類的超類,將指定的字節信息寫出到目的地。它定義了字節輸出流的基本共性功能方法。
- public void close() :關閉此輸出流并釋放與此流相關聯的任何系統資源。
- public void flush() :刷新此輸出流并強制任何緩沖的輸出字節被寫出。
- public void write(byte[] b):將 b.length字節從指定的字節數組寫入此輸出流。
- public void write(byte[] b, int off, int len) :從指定的字節數組寫入 len字節,從偏移量 off開始輸出到此輸出流。
- public abstract void write(int b) :將指定的字節輸出流。
close方法,當完成流的操作時,必須調用此方法,釋放系統資源。
FileOutputStream類:文件輸出流
java.io.FileOutputStream類是文件輸出流,用于將內存數據寫出到硬盤的文件中。
構造方法
- public FileOutputStream(File file):創建文件輸出流以寫入由指定的 File對象表示的文件。
- public FileOutputStream(String name): 創建文件輸出流以指定的名稱寫入文件。
參數:寫入數據的目的地。
String name文件路徑
File file:目的地是一個文件
構造方法地作用:
1.創建一個 FileOutputStream對象
2.會根據構造方法中傳遞的文件/文件路徑,創建一個空的文件
3.會把 FileOutputStream對象指向創建好的問文件
字節流寫入數據到文件
寫出字節
寫入數據的原理:(內存–>硬盤)
java程序—>JVM(Java虛擬機)—>Os(操作系統)—>OS調用寫數據的方法—>把數據寫入到文件中
字節輸出流的使用步驟:
需要拋出異常
public class FOSWrite {public static void main(String[] args) throws IOException {// 使用文件名稱創建流對象FileOutputStream fos = new FileOutputStream("fos.txt"); // 寫出數據fos.write(97); // 寫出第1個字節fos.write(98); // 寫出第2個字節fos.write(99); // 寫出第3個字節// 關閉資源fos.close();} } 輸出結果: abc寫數據的時候,會把10進制的整數轉換為二進制整數97
當你創建一個流對象時,必須傳入一個文件路徑。該路徑下,如果沒有這個文件,會創建該文件。如果有這個文件,會清空這個文件的數據。
寫出字節數組:write(byte[] b),
一次寫多個字節
寫出指定長度字節數組:write(byte[] b, int off, int len) ,每次寫出從off索引開始,len個字節。把字節數組的一部寫入到文件中
.getBytes()把字符串轉換為字節數組
arrays.toString(字節數組)轉換為十進制數組
數據追加續寫
** 追加寫**:使用兩個參數的構造方法
-
public FileOutputStream(File file, boolean append): 創建文件輸出流以寫入由指定的 File對象表示的文件。
-創建一個向指定File對象表示的文件中寫入數據的文件輸出流 -
public FileOutputStream(String name, boolean append): 創建文件輸出流以指定的名稱寫入文件
-
創建一個向具有指定name的文件中寫入數據的輸出文件流
String 那么,File file:寫入數據的目的地
boolean append:追加寫開關
true:創建對象不回覆蓋原文件,繼續在文件的末尾追加寫數據
false:創建一個新文件,覆蓋原文件
寫換行:寫換行符號
windows:\r\n
linux: /n
mac:/r
字節輸入流InputStream
java.io.InputStream抽象類是表示字節輸入流的所有類的超類,可以讀取字節信息到內存中。它定義了字節輸入流的基本共性功能方法。
所有子類共性的方法:
-
public void close() :關閉此輸入流并釋放與此流相關聯的任何系統資源。
-
public abstract int read(): 從輸入流讀取數據的下一個字節。
-
public int read(byte[] b): 從輸入流中讀取一些字節數,并將它們存儲到字節數組 b中 。
close方法,當完成流的操作時,必須調用此方法,釋放系統資源。
FileInputStream類
作用:把硬盤中的文件數據,讀取到內存中使用
構造方法:
-
FileInputStream(File file): 通過打開與實際文件的連接來創建一個 FileInputStream ,該文件由文件系統中的 File對象 file命名。
-
FileInputStream(String name): 通過打開與實際文件的連接來創建一個 FileInputStream ,該文件由文件系統中的路徑名 name命名。
當你創建一個流對象時,必須傳入一個文件路徑。該路徑下,如果沒有該文件,會拋出FileNotFoundException 。
參數:讀取文件的數據源
String name:文件路徑
File file:文件
構造方法的作用:
1.會創建一個FileInputStream對象
2.會把FileInputStream對象指定給構造方法中讀取的文件
讀取數據的原理(硬盤–>內存)
java程序 -->JVM -->OS–>OS讀取數據的方法–>讀取文件
自己輸入流的使用步驟(重點):
讀取字節數據
每次讀取一個字節
public class FISRead {public static void main(String[] args) throws IOException{// 使用文件名稱創建流對象FileInputStream fis = new FileInputStream("read.txt");// 讀取數據,返回一個字節int read = fis.read();System.out.println((char) read);read = fis.read();System.out.println((char) read);read = fis.read();System.out.println((char) read);read = fis.read();System.out.println((char) read);read = fis.read();System.out.println((char) read);// 讀取到末尾,返回-1read = fis.read();System.out.println( read);// 關閉資源fis.close();} } 輸出結果: a b c d e -1循環改進讀取方式,代碼使用演示:
不知道文件有多少字節,使用while循環
結束條件:讀取到-1的時候
. 使用字節數組讀取:read(byte[] b),每次讀取b的長度個字節到數組中,返回讀取到的有效字節個數,讀取到末尾時,返回-1 ,:
public class FISRead {public static void main(String[] args) throws IOException{// 使用文件名稱創建流對象.FileInputStream fis = new FileInputStream("read.txt"); // 文件中為abcde// 定義變量,作為有效個數int len ;// 定義字節數組,作為裝字節數據的容器 byte[] b = new byte[2];// 循環讀取while (( len= fis.read(b))!=-1) {// 每次讀取后,把數組的有效字節部分,變成字符串打印System.out.println(new String(b,0,len));// len 每次讀取的有效字節個數}// 關閉資源fis.close();} }輸出結果: ab cd e一次讀取多個字節
原理:
1.創建一個流對象,并把它指向要讀取的文件
2.創建一個byte數組
3.讀取數據
- 讀取數組長度大小的數據
- 返回有效讀取字節個數
數組起到緩沖作用,存儲讀取到的多個字節 一般定義為1024的整數倍
字節流練習:圖片復制
文件復制:一讀一寫
明確:
數據源
數據的目的地:
字符流
當使用字節流讀取文本文件時,可能會有一個小問題。就是遇到中文字符時,可能不會顯示完整的字符,那是因為一個中文字符可能占用多個字節存儲。所以Java提供一些字符流類,以字符為單位讀寫數據,專門用于處理文本文件。
GBK:占用兩個字節
utf-8:占用三個字節
字符輸入流【Reader】
java.io.Reader抽象類是表示用于讀取字符流的所有類的超類,可以讀取字符信息到內存中。它定義了字符輸入流的基本共性功能方法。
共有成員方法
- public int read(): 從輸入流讀取一個字符。
- public int read(char[] cbuf): 從輸入流中讀取多個字符,并將它們存儲到字符數組 cbuf中 。
- public void close() :關閉此流并釋放與此流相關聯的任何系統資源。
FileReader類 文件字符輸入流
java.io.FileReaderextend InputStreamReader··extendReader類
是讀取字符文件的便利類。構造時使用系統默認的字符編碼和默認字節緩沖區。
作用:把硬盤中的數據以字符的方式讀取到內存中
構造方法:
- FileReader(File file): 創建一個新的 FileReader ,給定要讀取的File對象。
- FileReader(String fileName): 創建一個新的 FileReader ,給定要讀取的文件的名稱。
參數:讀取的數據源
String fileName:文件的路徑
File file:文件
FileReader構造方法中的作用:
1.創建一個FileReader的對象
2.會把FileReader對象指向要讀取的文件
當你創建一個流對象時,必須傳入一個文件路徑。類似于FileInputStream 。
字符編碼:字節與字符的對應規則。Windows系統的中文編碼默認是GBK編碼表。
idea中UTF-8
字節緩沖區:一個字節數組,用來臨時存儲字節數據。
字符輸入流的使用步驟?
1.創建FileReader對象,構造方法中要綁定要讀取的數據源
2.使用FileReader對象中的方法Read讀取文件
3.釋放資源
讀取字符數據
使用字符數組讀取:read(char[] cbuf),每次讀取b的長度個字符到數組中,返回讀取到的有效字符個數,讀取到末尾時,返回-1 ,代碼使用演示:
String類構造方法:
String(char[] value):把字符數組轉換為字符串
String(char[] value,int offset ,int count):把字符數組一部分轉換為字符串
獲取有效的字符改進,代碼使用演示:
public class FISRead {public static void main(String[] args) throws IOException {// 使用文件名稱創建流對象FileReader fr = new FileReader("read.txt");// 定義變量,保存有效字符個數int len ;// 定義字符數組,作為裝字符數據的容器char[] cbuf = new char[2];// 循環讀取while ((len = fr.read(cbuf))!=-1) {System.out.println(new String(cbuf,0,len));}// 關閉資源fr.close();} }輸出結果: 黑馬 程序 員字符輸出流【Writer】
java.io.Writer抽象類是表示用于寫出字符流的所有類的超類,將指定的字符信息寫出到目的地。它定義了字節輸出流的基本共性功能方法。
- void write(int c) 寫入單個字符。
- void write(char[] cbuf)寫入字符數組。
- abstract void write(char[] cbuf, int off, int len)寫入字符數組的某一部分,off數組的開始索引,len寫的字符個數。
- void write(String str)寫入字符串。
- void write(String str, int off, int len) 寫入字符串的某一部分,off字符串的開始索引,len寫的字符個數。
- void flush()刷新該流的緩沖。
- void close() 關閉此流,但要先刷新它。
FileWriter類 文件字符輸出流
作用: 把內存中的字符數數劇寫入到文件中
java.io.FileWriterextends OutputStreamWrite extends··Write
java.io.FileWriter類是寫出字符到文件的便利類。構造時使用系統默認的字符編碼和默認字節緩沖區。
構造方法:
- FileWriter(File file): 創建一個新的 FileWriter,給定要讀取的File對象。
- FileWriter(String fileName): 創建一個新的 FileWriter,給定要讀取的文件的名稱。
當你創建一個流對象時,必須傳入一個文件路徑,類似于FileOutputStream。
參數:寫入文件的目的地
String fileName:文件的路徑
File file:文件
構造方法中的作用:
字符輸出流的使用步驟:
基本寫出數據
寫出單個字符:write(int b) 方法,每次可以寫出一個字符數據,代碼使用演示:
public class FWWrite {public static void main(String[] args) throws IOException {// 使用文件名稱創建流對象FileWriter fw = new FileWriter("fw.txt"); // 寫出數據fw.write(97); // 寫出第1個字符fw.write('b'); // 寫出第2個字符fw.write('C'); // 寫出第3個字符fw.write(30000); // 寫出第4個字符,中文編碼表中30000對應一個漢字。/*【注意】關閉資源時,與FileOutputStream不同。如果不關閉,數據只是保存到緩沖區,并未保存到文件。*/// fw.close();} } 輸出結果: abC田關閉和刷新
因為內置緩沖區的原因,如果不關閉輸出流,無法寫出字符到文件中。但是關閉的流對象,是無法繼續寫出數據的。如果我們既想寫出數據,又想繼續使用流,就需要flush 方法了。
- flush :刷新緩沖區,流對象可以繼續使用。
- close:先刷新緩沖區,然后通知系統釋放資源。流對象不可以再被使用了。
寫出其他數據
使用兩個參數的構造方法:
FileWriter(String fileName,Boolean append)
FileWriter(File file,Boolean append)
Boolean append:
true:可以續寫,不回創建新的文件覆蓋源文件
false:創建新的文件覆蓋源文件
流中的異常處理
jdk1.7之前使用try...catch...finally處理流中的異常
public class HandleException1 {public static void main(String[] args) {// 聲明變量,提高變量作用域,聲明時可以沒有值,使用時必須有值FileWriter fw = null;try {//可能會出現異常的代碼創建流對象fw = new FileWriter("fw.txt");// 寫出數據fw.write("黑馬程序員"); //黑馬程序員} catch (IOException e) {//異常的處理邏輯e.printStackTrace();} finally {try { //若創建對象失敗,fw默認值時null,null不能調用方法,會拋出nullponitException,需要增加一個判斷不是null,則釋放資源if (fw != null) {fw.close();}} catch (IOException e) {e.printStackTrace();}}} }JDK7的新特性:
在try之后增加一個();
在括號中可以定義流對象,那么這個流對象的作用域就在try中有效,try中代碼執行完畢,會自動把流對象釋放掉,不用寫finally,
格式如下:
代碼使用演示:
public class HandleException2 {public static void main(String[] args) {// 創建流對象try ( FileWriter fw = new FileWriter("fw.txt"); ) {// 寫出數據fw.write("黑馬程序員"); //黑馬程序員} catch (IOException e) {e.printStackTrace();}} }JDK9的改進(擴展知識點了解內容)
try的前邊可以定義流對象
在try后邊()中可以直接引入流對象的名稱(變量名)
在try代碼執行完畢之后流對象也可以釋放掉,不用寫finally
JDK9中try-with-resource 的改進,對于引入對象的方式,支持的更加簡潔。被引入的對象,同樣可以自動關閉,無需手動close,我們來了解一下格式。
A a =new A();
B b=new B();
try(a,b){
可能會產出異常的代碼,
} catch(異常類變量 變量名){
異常的處理邏輯
}
屬性集
概述
java.util.Properties 繼承于Hashtable ,來表示一個持久的屬性集。它使用鍵值結構存儲數據,每個鍵及其對應值都是一個字符串。該類也被許多Java類使用,比如獲取系統屬性時,System.getProperties 方法就是返回一個Properties對象。
java.util.Properties extends hashtable<K,v>implements Map<K,v>
Properties類
表示了一個持久的屬性集Properties可保存在流中或從流中加載。
Properties集合是唯一一個和IO流相結合的集合
可以使用Properties集合中的臨時數據,持久化寫入到硬盤中存儲、
可以使用Properties集合中的方法load,把硬盤中保存的文件(鍵值對),讀取到集合中使用
Properties屬性列表中每個鍵及其對對應的值都是一個字符串
Properties是一個雙列集合,key和value默認都是字符串
構造方法
- public Properties() :創建一個空的屬性列表。
基本的存儲方法
使用Properties結合存儲數據,遍歷取出Properties集合中的數據
是一個雙列集合,Key和value都是字符串
- public Object setProperty(String key, String value) : 保存一對屬性。 調用hashtable的方法put
- public String getProperty(String key) :使用此屬性列表中指定的鍵搜索屬性值。通過key找到value值,此方法相當于map集合中的get(key)方法
- public Set<String> stringPropertyNames() :所有鍵的名稱的集合。返回此屬性列表中的鍵集,其中該鍵及其對應值是字符串,此方法相當于Map集合中的KeySet方法
與流相關的方法
可以使用properties集合中的方法store,把集合中的臨時數據,持久化寫入到硬盤中存儲
void store (OutputStream out,String comments)
void store(Write write,String comments)
參數:
OutputStream out:不可以寫入中文
Write write:字符輸出流,可以寫入中文
String comments:注釋,用來解釋說明保存的文件是做什么用的,不能使用中文,會產生亂碼默認unicide編碼,一般使用“空字符串”
使用步驟:
可以使用Properties集合中的方法load,把硬盤中保存的文件(鍵值對),讀取到集合中使用
- public void load(InputStream inStream): 從字節輸入流中讀取鍵值對。
- public void load(Reader reader):
參數:
(InputStream inStream):字節輸入流,不能讀取含有中文的鍵值對,
Reader reader:字符輸入流,可以讀取含有中文的鍵值對
通過流對象,可以關聯到某文件上,這樣就能夠加載文本中的數據了。文本數據格式:
使用步驟:
注意:
加載代碼演示:
一般使用字符流
小貼士:文本中的數據,必須是鍵值對形式,可以使用空格、等號、冒號等符號分隔。
緩沖流,也叫高效流,是對4個基本的FileXxx 流的增強,所以也是4個流,按照數據類型分類:
緩沖流
緩沖流的基本原理,是在創建流對象時,會創建一個內置的默認大小的緩沖區數組,通過緩沖區讀寫,減少系統IO次數,從而提高讀寫的效率。
字節緩沖流
BufferedInputStream,字節緩沖輸入流
所有子類共性的方法:
-
public void close() :關閉此輸入流并釋放與此流相關聯的任何系統資源。
-
public abstract int read(): 從輸入流讀取數據的下一個字節。
-
public int read(byte[] b): 從輸入流中讀取一些字節數,并將它們存儲到字節數組 b中 。
close方法,當完成流的操作時,必須調用此方法,釋放系統資源。
BufferedOutputStream 字節緩沖輸出流
共性方法:
- public void close() :關閉此輸出流并釋放與此流相關聯的任何系統資源。
- public void flush() :刷新此輸出流并強制任何緩沖的輸出字節被寫出。
- public void write(byte[] b):將 b.length字節從指定的字節數組寫入此輸出流。
- public void write(byte[] b, int off, int len) :從指定的字節數組寫入 len字節,從偏移量 off開始輸出到此輸出流。
- public abstract void write(int b) :將指定的字節輸出流。
close方法,當完成流的操作時,必須調用此方法,釋放系統資源。
字節緩沖輸入流
構造方法
- public BufferedInputStream(InputStream in) :創建一個 新的緩沖輸入流。 ,保存其參數,即輸入流in,以便將來使用
- public BufferedInputStream(InputStream in,int size ) :創建一個 具有指定緩沖區大小的緩沖輸入流,并保存其參數
參數:
InputStream in字節輸入流:我們可以傳遞FileInputStream,增加一個緩沖區,提高FileInputStream的讀取效率
int size:指定緩沖流內部緩沖區大小,不指定默認
使用步驟
1.創建FileInputStream對象,構造方法中綁定要讀取的數據源
2.創建BufferedInputStream對象,構造方法中傳遞FileInputStream對象,提高FileInputStream對象效率
3.使用BufferedInputStream對象中的方法read ,讀取文件
4.釋放資源(會調用Flue方法刷新數據)
字節緩沖輸出流
-
public BufferedOutputStream(OutputStream out): 創建一個新的緩沖輸出流。以將數據寫入指定的底層輸出流
-
public BufferedOutputStream(OutputStream out,int size): 創建一個新的緩沖輸出流。以將數據寫入指定的底層輸出流
參數:
OutputStream out:字節輸出流
我們可以傳遞FileOutputStream,緩沖流會給FileOutoutStream增加一個緩沖區,提高FileOutputStream 的寫入效率
使用步驟:
1.創建FileOutputStream對象,構造方法中綁定要輸出目的地
2.創建BufferedOutputStream對象,構造方法中傳遞FileOutputStream對象 對象,提高FileOutputStream對象效率
3.使用BufferedOutputStream對象中的方法write,把數據寫入到內部緩沖區,
4.使用BufferedOutputStream對象中的方法flush把內部緩沖區中的數據,刷新到文件中
5.釋放資源(會調用Flue方法刷新數據)
構造舉例,代碼如下:
// 創建字節緩沖輸入流 BufferedInputStream bis = new BufferedInputStream(new FileInputStream("bis.txt")); // 創建字節緩沖輸出流 BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("bos.txt"));關閉緩沖流,會自動關閉基本流
效率測試
如何更快呢?
使用數組的方式,代碼如下:
public class BufferedDemo {public static void main(String[] args) throws FileNotFoundException {// 記錄開始時間long start = System.currentTimeMillis();// 創建流對象try (BufferedInputStream bis = new BufferedInputStream(new FileInputStream("jdk9.exe"));BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("copy.exe"));){// 讀寫數據int len;byte[] bytes = new byte[8*1024];while ((len = bis.read(bytes)) != -1) {bos.write(bytes, 0 , len);}} catch (IOException e) {e.printStackTrace();}// 記錄結束時間long end = System.currentTimeMillis();System.out.println("緩沖流使用數組復制時間:"+(end - start)+" 毫秒");} } 緩沖流使用數組復制時間:666 毫秒字符緩沖流
共性方法
- void write(int c) 寫入單個字符。
- void write(char[] cbuf)寫入字符數組。
- abstract void write(char[] cbuf, int off, int len)寫入字符數組的某一部分,off數組的開始索引,len寫的字符個數。
- void write(String str)寫入字符串。
- void write(String str, int off, int len) 寫入字符串的某一部分,off字符串的開始索引,len寫的字符個數。
- void flush()刷新該流的緩沖。
- void close() 關閉此流,但要先刷新它。
BufferedWriter字符緩沖輸出流
構造方法
BufferedWriter(writer out):創建一個使用默認大小輸出緩沖區的緩沖輸出流
BufferedWriter(writer out,int sz):創建一個使用給定大小輸出緩沖區的新緩沖字符輸出流
參數:
writer out:字符輸出流
我們可以傳遞一個FileWriter,緩沖流會給FileWriter增加一個緩沖區,提高FileWrite的寫入效率
int sz:指定緩沖區的大小,不寫默認大小
特有成員方法:
Void newLine():寫入一個分隔符,會根據不同的操作系統,獲取不同的行分隔符
換行符號:
windows:\r\n
linux:/n
mac:/r
使用步驟:
1.創建字符緩沖輸出流對象,構造方法中傳遞字符輸出流
2.調用字符緩沖流中的方法,write,把數據寫入到內存緩沖區中
3.調用字符緩沖流中的方法flush,把內存緩沖區中的數據,刷新到文件中
4.釋放資源
BufferedReader,extends Reader 字符緩沖輸入流
- 共有成員方法
- public int read(): 從輸入流讀取一個字符。
- public int read(char[] cbuf): 從輸入流中讀取多個字符,并將它們存儲到字符數組 cbuf中 。
- public void close() :關閉此流并釋放與此流相關聯的任何系統資源。
構造方法
BufferedReader(Reader in):創建一個使用默認大小的輸出緩沖的緩沖字符輸入流
BufferedReader(Reader in,int sz):創建一個使用指定大小的緩沖區的緩沖字符輸入流
參數
Reader in:字符輸入流我們可以傳遞FileReader,緩沖流會給FileReader增加一個緩沖區,提高FileReader的讀取效率
int sz:
特有成員方法
String readLine():讀取一個文本行 。讀一行數據,行的終止符號:通過列字符之一即可認為某行已終止,換行\n 回車\r,回車后跟著換行(\r\n)
返回值:包含該行內容字符串,不包含任何行終止符,如果已到達流末尾,則返回null
使用步驟
readLine方法演示,代碼如下:
1.4 練習:文本排序
練習:
對文本的內容進行排序
案例分析
1.創建一個HashMap集合對象,可以存儲每行文本的序號,value存儲每行的文本
2.創建字符緩沖輸入流對象,構造方法綁定字符輸入流
3.創建字符緩沖輸出流對象,構造方法中綁定字符輸出流
4.使用字符緩沖輸入流的方法readLine,逐行讀取文本
5.對讀取到的文本 進行切割,獲取行中的序號和文本內容
6.把切割好的序號和文本的內容存儲到Hashmap集合中(key序號是有序的,會自動排序)
7.遍歷HashMap集合,獲取每一個鍵值對
8.把每一個鍵值對拼接為一個文本行
9.把拼接好的文本,使用字符緩沖輸出流中的方法write,寫入到文件中
10.釋放資源
案例實現
public class BufferedTest {public static void main(String[] args) throws IOException {//1. 創建map集合,保存文本數據,鍵為序號,值為文字HashMap<String, String> lineMap = new HashMap<>();// 2.創建字符緩沖輸入流對象,構造方法中綁定字符輸入流BufferedReader br = new BufferedReader(new FileReader("in.txt"));//3.創建一個字符緩沖輸出流對象,構造方法中綁定字符串輸出流BufferedWriter bw = new BufferedWriter(new FileWriter("out.txt")); //4.使用字符緩沖輸出流中的方法readLine逐行讀取文本// 讀取數據String line = null;while ((line = br.readLine())!=null) {// 解析文本String[] split = line.split("\\.");// 保存到集合lineMap.put(split[0],split[1]);}// 釋放資源br.close();// 遍歷map集合for (int i = 1; i <= lineMap.size(); i++) {String key = String.valueOf(i);// 獲取map中文本String value = lineMap.get(key);// 寫出拼接文本bw.write(key+"."+value);// 寫出換行bw.newLine();}// 釋放資源bw.close();} }轉換流
字符編碼和字符集
按照某種規則,將字符存儲到計算機中,稱為編碼 。反之,將存儲在計算機中的二進制數按照某種規則解析顯示出來,稱為解碼 。
字符編碼:就是一套自然語音字符與二進制數之間的對應規則
字符集:也叫編碼表,是一個系統支持所有字符的集合
Idea默認編碼utf-8
FileReader可以讀取IO默認編碼格式UTF-8的文件
FileReader讀取系統默認編碼GBK中文 會產生亂碼
InputStreamReader類
轉換流java.io.InputStreamReader,是Reader的子類,是從字節流到字符流的橋梁。
它讀取字節,并使用指定的字符集將其解碼為字符。它的字符集可以由名稱指定,也可以接受平臺的默認字符集。
構造方法
- InputStreamReader(InputStream in): 創建一個使用默認字符集的字符流。
- InputStreamReader(InputStream in, String charsetName): 創建一個指定字符集的字符流。
參數:
InputSrteam in:字節輸入流,用來讀取文件中保存的字節
String charsetName:指定的編碼表名稱,不區分大小寫,可以實Utf-8,Gbk。。。。不指定默認使用UTF-8
使用步驟:
注意事項:
構造方法中指定的編碼表民晨光要和文件的編碼相同,否則會發生亂碼
OutputStreamWriter類
轉換流java.io.OutputStreamWriter ,是Writer的子類,是從字符流到字節流的橋梁。使用指定的字符集將字符編碼為字節。它的字符集可以由名稱指定,也可以接受平臺的默認字符集。
構造方法
- OutputStreamWriter(OutputStream in): 創建一個使用默認字符集的字符流。
- OutputStreamWriter(OutputStream in, String charsetName): 創建一個指定字符集的字符流。
參數
OutputStream out:字節輸出流,用來寫轉換之后的字節到文件中
String charsetName:指定的編碼表名稱,不區分大小寫,可以是Utf-8,GBK…,不指定默認使用Utf-8
使用步驟
指定編碼寫出
public class OutputDemo {public static void main(String[] args) throws IOException {// 定義文件路徑String FileName = "E:\\out.txt";// 創建流對象,默認UTF8編碼OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream(FileName));// 寫出數據osw.write("你好"); // 保存為6個字節osw.close();// 定義文件路徑String FileName2 = "E:\\out2.txt";// 創建流對象,指定GBK編碼OutputStreamWriter osw2 = new OutputStreamWriter(new FileOutputStream(FileName2),"GBK");// 寫出數據osw2.write("你好");// 保存為4個字節osw2.close();} }練習:轉換文件編碼
將GBK編碼的文本文件,轉換為UTF-8編碼的文本文件。
案例分析
1.創建InputStreanReader對象,構造方法中傳遞字節輸入流和指定的編碼表名稱GBK
2.創建OutputStreamWrite對象,構造方法中傳遞字節輸出流和指定的編碼表utf-8
3.使用InputSteamReader對象中的方法read讀取文件
4.使用OutputStreamWrite方法write,把讀取的數據寫入到文件中
5.釋放資源
案例實現
public class TransDemo {public static void main(String[] args) { // 1.定義文件路徑String srcFile = "file_gbk.txt";String destFile = "file_utf8.txt";// 2.創建流對象// 2.1 轉換輸入流,指定GBK編碼InputStreamReader isr = new InputStreamReader(new FileInputStream(srcFile) , "GBK");// 2.2 轉換輸出流,默認utf8編碼OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream(destFile));// 3.讀寫數據// 3.1 定義數組char[] cbuf = new char[1024];// 3.2 定義長度int len;// 3.3 循環讀取while ((len = isr.read(cbuf))!=-1) {// 循環寫出osw.write(cbuf,0,len);}// 4.釋放資源osw.close();isr.close();} }序列化
概述
Java 提供了一種對象序列化的機制。用一個字節序列可以表示一個對象,該字節序列包含該對象的數據、對象的類型和對象中存儲的屬性等信息。字節序列寫出到文件之后,相當于文件中持久保存了一個對象的信息。
反之,該字節序列還可以從文件中讀取回來,重構對象,對它進行反序列化。對象的數據、對象的類型和對象中存儲的數據信息,都可以用來在內存中創建對象。
把對象以流的方式,寫入到文件中保存,叫寫對象,也可以叫對象的序列化
對象中包含的不僅僅是字符使用字節流
ObjectOutputStream:對象的序列化writeObject(p)
把文件中保存的對象,以流的方式讀取出來,叫做讀對象,也叫對象的反序列化
讀取的文件保存的都是字節使用字節流
ObjectInputStream:對象的反序列化流
readObject()
ObjectOutputStream類
作用:把對象以流的方式寫入到文件中保存
java.io.ObjectOutputStreamextends OutputStream 類,將Java對象的原始數據類型寫出到文件,實現對象的持久存儲。
構造方法
- public ObjectOutputStream(OutputStream out): 創建一個指定OutputStream的ObjectOutputStream。
參數:
OutputStream out:字節輸出流
構造舉例,代碼如下:
特有的成員方法:
- public final void writeObject (Object obj) : 將指定的對象寫出。
使用步驟,需要序列化對象實現類
1.創建ObjectOutputStream對象,構造方法中傳遞字節輸出流
2.使用ObjectOutputStream對象中的方法writeObject,把對象寫入到文件中
3.釋放資源
序列化操作
1.一個對象要想序列化,必須滿足兩個條件:
-
該類必須 實現java.io.Serializable 接口來啟用序列化功能,Serializable 是一個標記接口,不實現此接口的類將不會使任何狀態序列化或反序列化,會拋出NotSerializableException 。實現Serializable接口,會給類添加一個標記,當序列化和反序列化的時候,就會檢測類上是否有這個標記
-
該類的所有屬性必須是可序列化的。如果有一個屬性不需要可序列化的,則該屬性必須注明是瞬態的,使用transient 關鍵字修飾。
-
static關鍵字:靜態關鍵字
靜態優先于非靜態加載到內存中(靜態優先于對象進入到內存中)被static修飾的成員變量不能被序列化
- transient 瞬態關鍵字: 被其修飾,不能被序列化
2.寫出對象方法
- public final void writeObject (Object obj) : 將指定的對象寫出。
ObjectInputStream類
ObjectInputStream反序列化流,將之前使用ObjectOutputStream序列化的原始數據恢復為對象。
作用:把文件中保存的對象,以流的方式讀取出來使用
構造方法
- public ObjectInputStream(InputStream in): 創建一個指定InputStream的ObjectInputStream。
參數InputStream in:字節輸入流
特有成員方法:
- public final Object readObject () : 從ObjectInputStream讀取一個對象。
使用步驟:
1.創建一個ObjectInputStream對象,構造方法中傳遞字節輸入流
2.使用ObjectInputStream對象中的方法readObject讀取保存對象的文件
3.釋放資源
4.使用讀取出來的對象
反序列化操作1
如果能找到一個對象的class文件,我們可以進行反序列化操作,調用ObjectInputStream讀取對象的方法:
- public final Object readObject () : 讀取一個對象。
readObject方法聲明拋出了ClassNotFoundException(class文件找不到異常)當不存在對象的class文件時拋出異常
反序列化前提:
對于JVM可以反序列化對象,它必須是能夠找到class文件的類。如果找不到該類的class文件,則拋出一個 ClassNotFoundException 異常。
反序列化操作2
**另外,當JVM反序列化對象時,能找到class文件,但是class文件在序列化對象之后發生了修改,那么反序列化操作也會失敗,拋出一個InvalidClassException異常。**發生這個異常的原因如下:
- 該類的序列版本號與從流中讀取的類描述符的版本號不匹配
- 該類包含未知數據類型
- 該類沒有可訪問的無參數構造方法
Serializable 接口給需要序列化的類,提供了一個序列版本號。serialVersionUID 該版本號的目的在于驗證序列化的對象和對應類是否版本匹配。
問題:
每次修改類的定義,都會給class文件生成一個新得序列號
解決方法:
無論是否對類的定義進行修改,都不生成新的序列號
可以手動給類添加一個序列號
格式在Serializable 接口規定:
可序列化類可以通過聲明為serialVersionUID的字段(該字段必須是static)最終final的long星字段,顯示聲明其自己的serialVersionUID
static final long serialVersionUID=42L
public class Employee implements java.io.Serializable {// 加入序列版本號private static final long serialVersionUID = 1L;public String name;public String address;// 添加新的屬性 ,重新編譯, 可以反序列化,該屬性賦為默認值.public int eid; public void addressCheck() {System.out.println("Address check : " + name + " -- " + address);} }練習:序列化集合
練習:
當我們想在文件中保存多個對象的時候
可以把多個對象存儲到一個集合中
對象進行序列化和反序列化
分析:
定義一個存儲Person對象的Array List集合
往ArrayList集合中存儲Person對象
創建一個序列化流ObjectOutputStream
使用ObjectOutputStream對象中的方法writeObject,對集合進行序列化
創建一個反序列化Object InputStream對象
使用ObjectInputStream對象中的方法readObject讀取文件中保存的集合
把Object類型的集合轉換為ArraysList類型
遍歷ArrayList集合
釋放資源
案例分析
案例實現
public class SerTest {public static void main(String[] args) throws Exception {// 創建 學生對象Student student = new Student("老王", "laow");Student student2 = new Student("老張", "laoz");Student student3 = new Student("老李", "laol");ArrayList<Student> arrayList = new ArrayList<>();arrayList.add(student);arrayList.add(student2);arrayList.add(student3);// 序列化操作// serializ(arrayList);// 反序列化 ObjectInputStream ois = new ObjectInputStream(new FileInputStream("list.txt"));// 讀取對象,強轉為ArrayList類型ArrayList<Student> list = (ArrayList<Student>)ois.readObject();for (int i = 0; i < list.size(); i++ ){Student s = list.get(i);System.out.println(s.getName()+"--"+ s.getPwd());}}private static void serializ(ArrayList<Student> arrayList) throws Exception {// 創建 序列化流 ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("list.txt"));// 寫出對象oos.writeObject(arrayList);// 釋放資源oos.close();} }打印流
概述
平時我們在控制臺打印輸出,是調用print方法和println方法完成的,這兩個方法都來自于java.io.PrintStream類,該類能夠方便地打印各種數據類型的值,是一種便捷的輸出方式。
PrintStream類extends OutputStream
為其他輸出流添加了功能,是他們能夠方便的打印各種數據值表示形式
特點:
構造方法
-
public PrintStream(String fileName): 使用指定的文件名創建一個新的打印流。輸出目的地是一個文件路徑
-
public PrintStream(File file): 使用指定的文件創建一個新的打印流。輸出目的地是一個文件
-
public PrintStream(Output Stream): 使用指定的輸出流創建一個新的打印流。輸出目的地是一個字節輸出流
構造舉例,代碼如下:
PrintStream ps = new PrintStream("ps.txt");繼承自父類的成員方法
- public void close() :關閉此輸出流并釋放與此流相關聯的任何系統資源。
- public void flush() :刷新此輸出流并強制任何緩沖的輸出字節被寫出。
- public void write(byte[] b):將 b.length字節從指定的字節數組寫入此輸出流。
- public void write(byte[] b, int off, int len) :從指定的字節數組寫入 len字節,從偏移量 off開始輸出到此輸出流。
- public abstract void write(int b) :將指定的字節輸出流。
注意事項
如果使用繼承自父類的write方法寫數據,那么查看數據的時候會查詢編碼表
如果使用自己特有的print/println方法寫數據,寫的數據會原樣輸出
使用步驟
改變打印流向
System.out就是PrintStream類型的,只不過它的流向是系統規定的,打印在控制臺上。
可以改變輸出語句的目的地(打印流的流向)
輸出語句,默認在控制臺輸出
使用System.setOut方法改變輸出語句的目的地改為參數傳遞的打印流的目的地
static void setOut(PrintStream out )
重新分配“標準”輸出流
總結
以上是生活随笔為你收集整理的Java笔记整理六(File类,递归,字节流IO,字符流IO,流中的异常处理,属性集Properties,缓冲流,转换流,序列化,打印流)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: JAVA SSL数字证书
- 下一篇: Java 支付宝手机网站支付下单 支付回