字節流轉換為字符流,進階15 IO流+字節字符輸入輸出+IO異常處理+屬性集+緩沖流+各種編碼+序列化

 2023-11-30 阅读 27 评论 0

摘要:IO概述 什么是IO 生活中,你肯定經歷過這樣的場景。當你編輯一個文本文件,忘記了ctrl+s ,可能文件就白白編輯了。當你電腦上插入一個U盤,可以把一個視頻,拷貝到你的電腦硬盤里。那么數據都是在哪些設備上的呢?鍵盤、內存、硬盤、

IO概述

什么是IO

生活中,你肯定經歷過這樣的場景。當你編輯一個文本文件,忘記了ctrl+s ,可能文件就白白編輯了。當你電腦上插入一個U盤,可以把一個視頻,拷貝到你的電腦硬盤里。那么數據都是在哪些設備上的呢?鍵盤、內存、硬盤、外接設備等等。
我們把這種數據的傳輸,可以看做是一種數據的流動,按照流動的方向,以內存為基準,分為輸入input 和輸出output ,即流向內存是輸入流,流出內存的輸出流。
Java中I/O操作主要是指使用java.io包下的內容,進行輸入、輸出操作。輸入也叫做讀取數據,輸出也叫做作寫出數據。

IO的分類

根據數據的流向分為:輸入流和輸出流。
輸入流 :把數據從其他設備上讀取到內存中的流。
輸出流 :把數據從內存 中寫出到其他設備上的流。
格局數據的類型分為:字節流和字符流。

字節流 :以字節為單位,讀寫數據的流。
字符流 :以字符為單位,讀寫數據的流。
在這里插入圖片描述

頂級父類們:

InputStream 字節輸入流
OutputStream 字節輸出流
Reader 字符輸入流
Writer 字符輸出流
在這里插入圖片描述

字節流

一切皆為字節
一切文件數據(文本、圖片、視頻等)在存儲時,都是以二進制數字的形式保存,都一個一個的字節,那么傳輸時一樣如此。所以,**字節流可以傳輸任意文件數據。**在操作流的時候,我們要時刻明確,無論使用什么樣的流對象,底層傳輸的始終為二進制數據。

字節輸出流【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) :將指定的字節輸出流。

java.io.FileOutputStream extends OutputStream

FileOutputStream:文件字節輸出流

作用:把內存中的數據寫入到硬盤的文件中

構造方法:

FileOutputStream(String name)創建一個向具有指定名稱的文件中寫入數據的輸出文件流。
FileOutputStream(File file) 創建一個向指定 File 對象表示的文件中寫入數據的文件輸出流。
   參數:寫入數據的目的String name:目的地是一個文件的路徑File file:目的地是一個文件,直接往文件中寫數據構造方法的作用:1.創建一個FileOutputStream對象2.會根據構造方法中傳遞的文件/文件路徑,創建一個空的文件3.會把FileOutputStream對象指向創建好的文件

寫入數據的原理(內存–>硬盤)
java程序–>JVM(java虛擬機)–>OS(操作系統)–>OS調用寫數據的方法–>把數據寫入到文件中

字符串整體輸入輸出,字節輸出流的使用步驟(重點):

   1.創建一個FileOutputStream對象,構造方法中傳遞寫入數據的目的地2.調用FileOutputStream對象中的方法write,把數據寫入到文件中3.釋放資源(流使用會占用一定的內存,使用完畢要把內存清空,提供程序的效率)
public static void main(String[] args) throws IOException {//1.創建一個FileOutputStream對象,構造方法中傳遞寫入數據的目的地FileOutputStream fos = new FileOutputStream("C:\\Users\\asus\\Desktop\\test\\hello.txt");//2.調用FileOutputStream對象中的方法write,把數據寫入到文件中//public abstract void write(int b) :將指定的字節輸出流。fos.write(97);//3.釋放資源(流使用會占用一定的內存,使用完畢要把內存清空,提供程序的效率)fos.close();
}

文件存儲原理和記事本打開文件原理:

在這里插入圖片描述

創建對象,并且根據路徑創建一個空的文件夾,將對象指向文件夾進行寫入硬盤,硬盤中存儲的內容都是字節,寫入數據時會轉化成二進制字節存到硬盤,用記事本打開的時候會自動轉成十進制字符

一次寫多個字節的方法:

   - public void write(byte[] b):將 b.length字節從指定的字節數組寫入此輸出流。- public void write(byte[] b, int off, int len) :從指定的字節數組寫入 len字節,從偏移量 off開始輸出到此輸出流。
 public static void main(String[] args) throws IOException {//創建FileOutputStream對象,構造方法中綁定要寫入數據的目的地FileOutputStream fos = new FileOutputStream(new File("C:\\Users\\asus\\Desktop\\test\\hello.txt"));//直接創建file,寫入//調用FileOutputStream對象中的方法write,把數據寫入到文件中//在文件中顯示100,需要寫3個字節fos.write(49);//表示1的ASCII碼fos.write(48);fos.write(48);/*public void write(byte[] b):將 b.length字節從指定的字節數組寫入此輸出流。一次寫多個字節:如果寫的第一個字節是正數(0-127),那么顯示的時候會查詢ASCII表如果寫的第一個字節是負數,那第一個字節會和第二個字節,兩個字節組成一個中文顯示,查詢系統默認碼表(GBK)*///先創建一個數組,然后對象fos會把數組寫入byte[] bytes = {65,66,67,68,69};//ABCDE
//        byte[] bytes = {-65,-66,-67,68,69};//烤紻Efos.write(bytes);/*public void write(byte[] b, int off, int len) :把字節數組的一部分寫入到文件中int off:數組的開始索引int len:寫幾個字節*/fos.write(bytes,1,2);//BC/*寫入字符的方法:可以使用String類中的方法把字符串,轉換為字節數組byte[] getBytes()  把字符串轉換為字節數組*/byte[] bytes2 = "你好".getBytes();//getBytes()方法轉成字節System.out.println(Arrays.toString(bytes2));//[-28, -67, -96, -27, -91, -67] UTF-8三個字節是一個中文,JDK中兩個字節是一個中文fos.write(bytes2);//釋放資源fos.close();}

上述代碼中,如果重復運行相同代碼會在已經創建好的文件下次次覆蓋創建新的文件,接下來描述文件的追加和續寫。

追加寫/續寫:使用兩個參數的構造方法

 FileOutputStream(String name, boolean append)創建一個向具有指定 name 的文件中寫入數據的輸出文件流。FileOutputStream(File file, boolean append) 創建一個向指定 File 對象表示的文件中寫入數據的文件輸出流。
參數:String name,File file:寫入數據的目的地,boolean append:追加寫開關true:創建對象不會覆蓋源文件,繼續在文件的末尾追加寫數據  也就是說文件已經創建好了,現在創建一個輸出流,寫入流false:創建一個新文件,覆蓋源文件

寫換行:寫換行符號
windows:\r\n
linux:/n
mac:/r

public static void main(String[] args) throws IOException {FileOutputStream fos = new FileOutputStream("C:\\Users\\asus\\Desktop\\test\\你好.txt",true);for (int i = 1; i <=10 ; i++) {fos.write("你好".getBytes());fos.write("\r\n".getBytes());}fos.close();
}

字節輸入流【InputStream】

java.io.InputStream:字節輸入流
抽象類是表示字節輸入流的所有類的超類。
抽象類沒辦法創建對象,只能使用其子類創建對象。

一個字符串占幾個字節?定義了所有子類共性的方法:

int read()從輸入流中讀取數據的下一個字節。讀取的是字節,返回值是int
int read(byte[] b) 從輸入流中讀取一定數量的字節,并將其存儲在緩沖區數組 b 中。
void close() 關閉此輸入流并釋放與該流關聯的所有系統資源。

java.io.FileInputStream extends InputStream
FileInputStream:文件字節輸入流,即讀取
作用:把硬盤文件中的數據,讀取到內存中使用

構造方法:

  FileInputStream(String name)FileInputStream(File file)
參數:讀取文件的數據源String name:文件的路徑File file:文件
構造方法的作用:1.會創建一個FileInputStream對象2.會把FileInputStream對象指定構造方法中要讀取的文件

讀取數據的原理(硬盤–>內存)
java程序–>JVM–>OS–>OS讀取數據的方法–>讀取文件

字節輸入流的使用步驟(重點):

1.創建FileInputStream對象,構造方法中綁定要讀取的數據源
2.使用FileInputStream對象中的方法read,讀取文件
3.釋放資源、
public static void main(String[] args) throws IOException {//1.創建FileInputStream對象,構造方法中綁定要讀取的數據源FileInputStream fis = new FileInputStream("C:\\Users\\asus\\Desktop\\test\\hello.txt");//里面是abc//2.使用FileInputStream對象中的方法read,讀取文件//int read()讀取文件中的一個字節并返回,讀取到文件的末尾返回-1/*int len = fis.read();System.out.println(len);//97 alen = fis.read();System.out.println(len);// 98 blen = fis.read();System.out.println(len);//99 clen = fis.read();System.out.println(len);//-1len = fis.read();System.out.println(len);//-1*//*發現以上讀取文件是一個重復的過程,所以可以使用循環優化不知道文件中有多少字節,使用while循環while循環結束條件,讀取到-1的時候結束布爾表達式(len = fis.read())!=-11.fis.read():讀取一個字節2.len = fis.read():把讀取到的字節賦值給變量len3.(len = fis.read())!=-1:判斷變量len是否不等于-1*/int len = 0; //記錄讀取到的字節while ((len =fis.read()) != -1){System.out.println(len);}//3.釋放資源fis.close();
}int read()//返回字節,一次只返回一個,如果有多個字節,只能一個一個返回。

字節流讀取文件原理:
在這里插入圖片描述

一個char幾個字節?一次讀取一個字節:首先創建對象,指向文件(會指向文件的第一個字節,如果字節后面會有一個結束的標志,這個標志本身看不到),從a開始,指針自動向后移動一位,知道讀取到結束的標記,給os–>JVM–>返回-1

一次讀取多個字節:硬盤中存放的文件b.txt,之前和上面類似,創建了2個字節的數組,字節就會進到緩沖區數組中,依次讀取直到讀到結束的標記。如果創建一個數組長度是5,就會一次全部讀出。提高了效率。

字節輸入流一次讀取多個字節的方法:

  int read(byte[] b) 從輸入流中讀取一定數量的字節,并將其存儲在緩沖區數組 b 中。和一次讀一個字節read()方法,參數上標記多了一個數組

明確兩件事情:
1.方法的參數byte[]的作用?
起到緩沖作用,存儲每次讀取到的多個字節
數組的長度一把定義為1024(1kb)或者1024的整數倍

2.方法的返回值int是什么?
每次讀取的有效字節個數

String類的構造方法

String(byte[] bytes) :把字節數組轉換為字符串
String(byte[] bytes, int offset, int length) 把字節數組的一部分轉換為字符串
offset:數組的開始索引 
length:轉換的字節個數
public static void main(String[] args) throws IOException {//創建FileInputStream對象,構造方法中綁定要讀取的數據源FileInputStream fis = new FileInputStream("09_IOAndProperties\\b.txt");//使用FileInputStream對象中的方法read讀取文件//int read(byte[] b) 從輸入流中讀取一定數量的字節,并將其存儲在緩沖區數組 b 中。/*byte[] bytes = new byte[2];int len = fis.read(bytes);System.out.println(len);//2//System.out.println(Arrays.toString(bytes));//[65, 66],//看到字節很不爽,直接用string中的方法System.out.println(new String(bytes));//ABlen = fis.read(bytes);System.out.println(len);//2System.out.println(new String(bytes));//CDlen = fis.read(bytes);System.out.println(len);//1System.out.println(new String(bytes));//EDlen = fis.read(bytes);System.out.println(len);//-1System.out.println(new String(bytes));//ED*//*發現以上讀取時一個重復的過程,可以使用循環優化不知道文件中有多少字節,所以使用while循環while循環結束的條件,讀取到-1結束*/byte[] bytes = new byte[1024];//存儲讀取到的多個字節int len = 0; //記錄每次讀取的有效字節個數,也就是read返回的int值while((len = fis.read(bytes))!=-1){//String(byte[] bytes, int offset, int length) 把字節數組的一部分轉換為字符串 offset:數組的開始索引 length:轉換的字節個數System.out.println(new String(bytes,0,len));}//釋放資源fis.close();
}

字節流練習:圖片復制

在這里插入圖片描述

字符串序列化。自己分析:實現文件的復制,應該需要文件的讀取和寫入,需要創建兩個流。
在這里插入圖片描述

文件復制練習:一讀一寫

明確:
數據源: c:\1.jpg
數據的目的地: d:\1.jpg

文件復制的步驟:

1.創建一個字節輸入流對象,構造方法中綁定要讀取的數據源
2.創建一個字節輸出流對象,構造方法中綁定要寫入的目的地
3.使用字節輸入流對象中的方法read讀取文件
4.使用字節輸出流中的方法write,把讀取到的字節寫入到目的地的文件中
5.釋放資源
public static void main(String[] args) throws IOException {long s = System.currentTimeMillis();//1.創建一個字節輸入流對象,構造方法中綁定要讀取的數據源FileInputStream fis = new FileInputStream("E:\\文件復制文件\\a.txt");//2.創建一個字節輸出流對象,構造方法中綁定要寫入的目的地FileOutputStream fos = new FileOutputStream("F:\\文件寫入\\");//一次讀取一個字節寫入一個字節的方式//3.使用字節輸入流對象中的方法read讀取文件/*int len = 0;while((len = fis.read())!=-1){//4.使用字節輸出流中的方法write,把讀取到的字節寫入到目的地的文件中fos.write(len);}*///使用數組緩沖讀取多個字節,寫入多個字節byte[] bytes = new byte[1024];//3.使用字節輸入流對象中的方法read讀取文件int len = 0;//每次讀取的有效字節個數while((len = fis.read(bytes))!=-1){//4.使用字節輸出流中的方法write,把讀取到的字節寫入到目的地的文件中fos.write(bytes,0,len);}//5.釋放資源(先關寫的,后關閉讀的;如果寫完了,肯定讀取完畢了)fos.close();fis.close();long e = System.currentTimeMillis();System.out.println("復制文件共耗時:"+(e-s)+"毫秒");
}

只要是文件,只用字節流都可以復制。

字符流

當使用字節流讀取文本文件時,可能會有一個小問題。就是遇到中文字符時,可能不會顯示完整的字符,那是因為一個中文字符可能占用多個字節存儲。所以Java提供一些字符流類,以字符為單位讀寫數據,專門用于處理文本文件。

字符輸入流【Reader】

字符與字節,使用字節流讀取中文文件
1個中文
GBK:占用兩個字節
UTF-8:占用3個字節

public static void main(String[] args) throws IOException {FileInputStream fis = new FileInputStream("C:\\Users\\asus\\Desktop\\test\\你好.txt");int len = 0;while((len = fis.read())!=-1){System.out.println((char) len);  //亂碼。使用字節流,每次讀取三分之一個字符}fis.close();
}

java.io.Reader:字符輸入流,是字符輸入流的最頂層的父類,定義了一些共性的成員方法,是一個抽象類

共性的成員方法:

int read() 讀取單個字符并返回。
int read(char[] cbuf)一次讀取多個字符,將字符讀入數組。
void close() 關閉該流并釋放與之關聯的所有資源。

java.io.FileReader extends InputStreamReader extends Reader
FileReader:文件字符輸入流

作用:把硬盤文件中的數據以字符的方式讀取到內存中

構造方法:

FileReader(String fileName)
FileReader(File file)
參數:讀取文件的數據源String fileName:文件的路徑File file:一個文件
FileReader構造方法的作用:1.創建一個FileReader對象2.會把FileReader對象指向要讀取的文件

字符輸入流的使用步驟:

1.創建FileReader對象,構造方法中綁定要讀取的數據源
2.使用FileReader對象中的方法read讀取文件
3.釋放資源
public static void main(String[] args) throws IOException {//1.創建FileReader對象,構造方法中綁定要讀取的數據源FileReader fr = new FileReader("C:\\Users\\asus\\Desktop\\test\\你好.txt");//2.使用FileReader對象中的方法read讀取文件//int read() 讀取單個字符并返回。/*int len = 0;while((len = fr.read())!=-1){System.out.print((char)len);}*///int read(char[] cbuf)一次讀取多個字符,將字符讀入數組。char[] cs = new char[1024];//存儲讀取到的多個字符int len = 0;//記錄的是每次讀取的有效字符個數while((len = fr.read(cs))!=-1){/*String類的構造方法String(char[] value) 把字符數組轉換為字符串String(char[] value, int offset, int count) 把字符數組的一部分轉換為字符串 offset數組的開始索引 count轉換的個數*/System.out.println(new String(cs,0,len));}//3.釋放資源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() 關閉此流,但要先刷新它。

java.io.FileWriter extends OutputStreamWriter extends Writer
FileWriter:文件字符輸出流

作用:把內存中字符數據寫入到文件中

構造方法:

FileWriter(File file)根據給定的 File 對象構造一個 FileWriter 對象。
FileWriter(String fileName) 根據給定的文件名構造一個 FileWriter 對象。
參數:寫入數據的目的地String fileName:文件的路徑File file:是一個文件
構造方法的作用:可以使用Properties集合中的方法load,把硬盤中保存的文件(鍵值對),讀取到集合中使用
void load(InputStream inStream)
void load(Reader reader)

參數:
InputStream inStream:字節輸入流,不能讀取含有中文的鍵值對
Reader reader:字符輸入流,能讀取含有中文的鍵值對
使用步驟:

1.創建Properties集合對象
2.使用Properties集合對象中的方法load讀取保存鍵值對的文件
3.遍歷Properties集合

結構體字符數組初始化,注意:

1.存儲鍵值對的文件中,鍵與值默認的連接符號可以使用=,空格(其他符號)
2.存儲鍵值對的文件中,可以使用#進行注釋,被注釋的鍵值對不會再被讀取
3.存儲鍵值對的文件中,鍵與值默認都是字符串,不用再加引號1.會創建一個FileWriter對象2.會根據構造方法中傳遞的文件/文件的路徑,創建文件3.會把FileWriter對象指向創建好的文件

字符輸出流的使用步驟(重點):

1.創建FileWriter對象,構造方法中綁定要寫入數據的目的地
2.使用FileWriter中的方法write,把數據寫入到內存緩沖區中(字符轉換為字節的過程)
3.使用FileWriter中的方法flush,把內存緩沖區中的數據,刷新到文件中
4.釋放資源(會先把內存緩沖區中的數據刷新到文件中)
public static void main(String[] args) throws IOException {//1.創建FileWriter對象,構造方法中綁定要寫入數據的目的地FileWriter fw = new FileWriter("c:\\users\\asus\\desktop\\test\\你好.txt");//2.使用FileWriter中的方法write,把數據寫入到內存緩沖區中(字符轉換為字節的過程)//void write(int c) 寫入單個字符。fw.write(97);//3.使用FileWriter中的方法flush,把內存緩沖區中的數據,刷新到文件中//fw.flush();//4.釋放資源(會先把內存緩沖區中的數據刷新到文件中)fw.close();
}

flush方法和close方法的區別
- flush :刷新緩沖區,流對象可以繼續使用。
- close: 先刷新緩沖區,然后通知系統釋放資源。流對象不可以再被使用了。

public static void main(String[] args) throws IOException {//1.創建FileWriter對象,構造方法中綁定要寫入數據的目的地FileWriter fw = new FileWriter("C:\\Users\\asus\\Desktop\\test\\你好.txt");//2.使用FileWriter中的方法write,把數據寫入到內存緩沖區中(字符轉換為字節的過程)//void write(int c) 寫入單個字符。fw.write(97);//3.使用FileWriter中的方法flush,把內存緩沖區中的數據,刷新到文件中fw.flush();//刷新之后流可以繼續使用fw.write(98);//4.釋放資源(會先把內存緩沖區中的數據刷新到文件中)fw.close();//close方法之后流已經關閉了,已經從內存中消失了,流就不能再使用了fw.write(99);//IOException: Stream closed
}

字符輸出流寫數據的其他方法

- 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寫的字符個數。
public static void main(String[] args) throws IOException {FileWriter fw = new FileWriter("C:\\Users\\asus\\Desktop\\test\\你好.txt");char[] cs = {'a','b','c','d','e'};//void write(char[] cbuf)寫入字符數組。fw.write(cs);//abcde\//void write(char[] cbuf, int off, int len)寫入字符數組的某一部分,off數組的開始索引,len寫的字符個數。fw.write(cs,1,3);//bcd//void write(String str)寫入字符串。fw.write("你好啊");//你好啊//void write(String str, int off, int len) 寫入字符串的某一部分,off字符串的開始索引,len寫的字符個數。fw.write("張小敬啊",2,2);//靜啊fw.close();
}

續寫和換行
續寫,追加寫:使用兩個參數的構造方法

FileWriter(String fileName, boolean append)
FileWriter(File file, boolean append)
參數:String fileName,File file:寫入數據的目的地boolean append:續寫開關 true:不會創建新的文件覆蓋源文件,可以續寫; false:創建新的文件覆蓋源文件

換行:換行符號
windows:\r\n
linux:/n
mac:/r

public static void main(String[] args) throws IOException {FileWriter fw = new FileWriter("C:\\Users\\asus\\Desktop\\test\\你好.txt",true);fw.write("\r\n"+"我續寫了哦!");for (int i = 0; i<10;i++){fw.write("\r\n"+"我續寫了第"+i+"次了");}fw.write("\r\n"+"寫完吃飯!");fw.close();
}

IO異常的處理

字節是什么?在jdk1.7之前使用try catch finally 處理流中的異常
格式:

 try{可能會產出異常的代碼}catch(異常類變量 變量名){異常的處理邏輯}finally{一定會執行的代碼資源釋放}  
public static void main(String[] args) {//提高變量fw的作用域,讓finally可以使用,如果寫在try中在finally中就不會接收到fw對象//變量在定義的時候,可以沒有值,但是使用的時候必須有值//fw = new FileWriter("09_IOAndProperties\\g.txt",true); 執行失敗,fw沒有值,fw.close會報錯FileWriter fw = null;try{//可能會產出異常的代碼fw = new FileWriter("w:\\09_IOAndProperties\\g.txt",true);for (int i = 0; i <10 ; i++) {fw.write("HelloWorld"+i+"\r\n");}}catch(IOException e){//異常的處理邏輯System.out.println(e);}finally {//一定會指定的代碼//創建對象失敗了,fw的默認值就是null,null是不能調用方法的,會拋出NullPointerException,需要增加一個判斷,不是null在把資源釋放if(fw!=null){try {//fw.close方法聲明拋出了IOException異常對象,所以我們就的處理這個異常對象,要么throws,要么try catchfw.close();} catch (IOException e) {e.printStackTrace();}}}

JDK7的新特性
在try的后邊可以增加一個(),在括號中可以定義流對象
那么這個流對象的作用域就在try中有效

try中的代碼執行完畢,會自動把流對象釋放,不用寫finally
格式:

 try(定義流對象;定義流對象....){可能會產出異常的代碼}catch(異常類變量 變量名){異常的處理邏輯}

注意:小括號之間的對象用分號斷開。

public static void main(String[] args) {try(//1.創建一個字節輸入流對象,構造方法中綁定要讀取的數據源FileInputStream fis = new FileInputStream("c:\\1.jpg");//2.創建一個字節輸出流對象,構造方法中綁定要寫入的目的地FileOutputStream fos = new FileOutputStream("d:\\1.jpg");){//可能會產出異常的代碼//一次讀取一個字節寫入一個字節的方式//3.使用字節輸入流對象中的方法read讀取文件int len = 0;while((len = fis.read())!=-1){//4.使用字節輸出流中的方法write,把讀取到的字節寫入到目的地的文件中fos.write(len);}}catch (IOException e){//異常的處理邏輯System.out.println(e);}}

JDK9新特性
try的前邊可以定義流對象
在try后邊的()中可以直接引入流對象的名稱(變量名)
在try代碼執行完畢之后,流對象也可以釋放掉,不用寫finally

格式:

   A a = new A();B b = new B();try(a,b){可能會產出異常的代碼}catch(異常類變量 變量名){異常的處理邏輯}
public static void main(String[] args) throws IOException {//1.創建一個字節輸入流對象,構造方法中綁定要讀取的數據源FileInputStream fis = new FileInputStream("c:\\1.jpg");//2.創建一個字節輸出流對象,構造方法中綁定要寫入的目的地FileOutputStream fos = new FileOutputStream("d:\\1.jpg");try(fis;fos){//一次讀取一個字節寫入一個字節的方式//3.使用字節輸入流對象中的方法read讀取文件int len = 0;while((len = fis.read())!=-1){//4.使用字節輸出流中的方法write,把讀取到的字節寫入到目的地的文件中fos.write(len);}}catch (IOException e){System.out.println(e);}//fos.write(1);//Stream Closed}

屬性集

java.util.Properties集合 extends Hashtable<k,v> implements Map<k,v>
Hashtable又實現了Map集合

Properties 類表示了一個持久的屬性集。Properties 可保存在流中或從流中加載。
Properties集合是一個唯一和IO流相結合的集合
方法:
可以使用Properties集合中的方法store,把集合中的臨時數據,持久化寫入到硬盤中存儲
可以使用Properties集合中的方法load,把硬盤中保存的文件(鍵值對),讀取到集合中使用

屬性列表中每個鍵及其對應值都是一個字符串。
Properties集合是一個雙列集合,key和value默認都是字符串

c語言輸入單個字符,使用Properties集合存儲數據,遍歷取出Properties集合中的數據
Properties集合是一個雙列集合,key和value默認都是字符串

 Properties集合有一些操作字符串的特有方法Object setProperty(String key, String value) 調用 Hashtable 的方法 put。String getProperty(String key) 通過key找到value值,此方法相當于Map集合中的get(key)方法Set<String> stringPropertyNames()  返回此屬性列表中的鍵集,其中該鍵及其對應值是字符串,此方法相當于Map集合中的keySet方法
private static void show01() {//創建Properties集合對象Properties prop = new Properties();//使用setProperty往集合中添加數據prop.setProperty("趙麗穎","168");prop.setProperty("迪麗熱巴","165");prop.setProperty("古力娜扎","160");//prop.put(1,true);//使用stringPropertyNames把Properties集合中的鍵取出,存儲到一個Set集合中Set<String> set = prop.stringPropertyNames();//遍歷Set集合,取出Properties集合的每一個鍵for (String key : set) {//使用getProperty方法通過key獲取valueString value = prop.getProperty(key);System.out.println(key+"="+value);}
}
可以使用Properties集合中的方法store,把集合中的臨時數據,持久化寫入到硬盤中存儲
void store(OutputStream out, String comments)
void store(Writer writer, String comments)
參數:OutputStream out:字節輸出流,不能寫入中文Writer writer:字符輸出流,可以寫中文String comments:注釋,用來解釋說明保存的文件是做什么用的不能使用中文,會產生亂碼,默認是Unicode編碼一般使用""空字符串使用步驟:1.創建Properties集合對象,添加數據2.創建字節輸出流/字符輸出流對象,構造方法中綁定要輸出的目的地3.使用Properties集合中的方法store,把集合中的臨時數據,持久化寫入到硬盤中存儲4.釋放資源private static void show02() throws IOException {//1.創建Properties集合對象,添加數據Properties prop = new Properties();prop.setProperty("趙麗穎","168");prop.setProperty("迪麗熱巴","165");prop.setProperty("古力娜扎","160");//2.創建字節輸出流/字符輸出流對象,構造方法中綁定要輸出的目的地//FileWriter fw = new FileWriter("09_IOAndProperties\\prop.txt");//3.使用Properties集合中的方法store,把集合中的臨時數據,持久化寫入到硬盤中存儲//prop.store(fw,"save data");//4.釋放資源//fw.close();prop.store(new FileOutputStream("09_IOAndProperties\\prop2.txt"),"");
}

load方法(重點)

可以使用Properties集合中的方法load,把硬盤中保存的文件(鍵值對),讀取到集合中使用

void load(InputStream inStream)
void load(Reader reader)

參數:
InputStream inStream:字節輸入流,不能讀取含有中文的鍵值對
Reader reader:字符輸入流,能讀取含有中文的鍵值對
使用步驟:
1.創建Properties集合對象
2.使用Properties集合對象中的方法load讀取保存鍵值對的文件
3.遍歷Properties集合
注意:
1.存儲鍵值對的文件中,鍵與值默認的連接符號可以使用=,空格(其他符號)
2.存儲鍵值對的文件中,可以使用#進行注釋,被注釋的鍵值對不會再被讀取
3.存儲鍵值對的文件中,鍵與值默認都是字符串,不用再加引號

private static void show03() throws IOException {//1.創建Properties集合對象Properties prop = new Properties();//2.使用Properties集合對象中的方法load讀取保存鍵值對的文件prop.load(new FileReader("C:\\Users\\asus\\Desktop\\test\\你好.txt"));//prop.load(new FileInputStream("09_IOAndProperties\\prop.txt"));//3.遍歷Properties集合Set<String> set = prop.stringPropertyNames();for (String key : set){//通過key來獲取value,set集合里面裝的都是prop的keyString value = prop.getProperty(key);System.out.println(key+"的value是"+value);}}

緩沖流

在這里插入圖片描述

概述

緩沖流,也叫高效流,是對4個基本的FileXxx 流的增強,所以也是4個流,按照數據類型分類:
字節緩沖流:BufferedInputStream,BufferedOutputStream
字符緩沖流:BufferedReader,BufferedWriter

緩沖流的基本原理,是在創建流對象時,會創建一個內置的默認大小的緩沖區數組,通過緩沖區讀寫,減少系統IO次數,從而提高讀寫的效率。

字節緩沖流

字節緩沖輸出流

java.io.BufferedOutputStream extends OutputStream
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) :將指定的字節輸出流。

構造方法:
BufferedOutputStream(OutputStream out) 創建一個新的緩沖輸出流,以將數據寫入指定的底層輸出流。
BufferedOutputStream(OutputStream out, int size) 創建一個新的緩沖輸出流,以將具有指定緩沖區大小的數據寫入指定的底層輸出流。
參數:
OutputStream out:字節輸出流
我們可以傳遞FileOutputStream,緩沖流會給FileOutputStream增加一個緩沖區,提高FileOutputStream的寫入效率
int size:指定緩沖流內部緩沖區的大小,不指定即默認
使用步驟(重點)

1.創建FileOutputStream對象,構造方法中綁定要輸出的目的地
2.創建BufferedOutputStream對象,構造方法中傳遞FileOutputStream對象對象,提高FileOutputStream對象效率
3.使用BufferedOutputStream對象中的方法write,把數據寫入到內部緩沖區中
4.使用BufferedOutputStream對象中的方法flush,把內部緩沖區中的數據,刷新到文件中
5.釋放資源(會先調用flush方法刷新數據,4部可以省略)
public static void main(String[] args) throws IOException {//1.創建FileOutputStream對象,構造方法中綁定要輸出的目的地FileOutputStream fos = new FileOutputStream("C:\\Users\\asus\\Desktop\\test\\你好.txt");//2.創建BufferedOutputStream對象,構造方法中傳遞FileOutputStream對象對象,提高FileOutputStream對象效率BufferedOutputStream bos = new BufferedOutputStream(fos);//3.使用BufferedOutputStream對象中的方法write,把數據寫入到內部緩沖區中bos.write("我寫到了緩沖區".getBytes());//4.使用BufferedOutputStream對象中的方法flush,把內部緩沖區中的數據,刷新到文件中bos.flush();//5.釋放資源(會先調用flush方法刷新數據,第4部可以省略)bos.close();
}

字節緩沖輸入流

java.io.BufferedInputStream extends InputStream
BufferedInputStream:字節緩沖輸入流

繼承自父類的成員方法:

int read()從輸入流中讀取數據的下一個字節。
int read(byte[] b) 從輸入流中讀取一定數量的字節,并將其存儲在緩沖區數組 b 中。
void close() 關閉此輸入流并釋放與該流關聯的所有系統資源。

構造方法:

BufferedInputStream(InputStream in) 創建一個 BufferedInputStream 并保存其參數,即輸入流 in,以便將來使用。
BufferedInputStream(InputStream in, int size) 創建具有指定緩沖區大小的 BufferedInputStream 并保存其參數,即輸入流 in,以便將來使用。
參數:InputStream in:字節輸入流我們可以傳遞FileInputStream,緩沖流會給FileInputStream增加一個緩沖區,提高FileInputStream的讀取效率int size:指定緩沖流內部緩沖區的大小,不指定默認

使用步驟(重點):
1.創建FileInputStream對象,構造方法中綁定要讀取的數據源
2.創建BufferedInputStream對象,構造方法中傳遞FileInputStream對象,提高FileInputStream對象的讀取效率
3.使用BufferedInputStream對象中的方法read,讀取文件
4.釋放資源

public static void main(String[] args) throws IOException {//1.創建FileInputStream對象,構造方法中綁定要讀取的數據源FileInputStream fis = new FileInputStream("C:\\Users\\asus\\Desktop\\test\\你好.txt");//2.創建BufferedInputStream對象,構造方法中傳遞FileInputStream對象,提高FileInputStream對象的讀取效率BufferedInputStream bis = new BufferedInputStream(fis);//3.使用BufferedInputStream對象中的方法read,讀取文件//int read()從輸入流中讀取數據的下一個字節。/*int len = 0;//記錄每次讀取到的字節while((len = bis.read())!=-1){System.out.println(len);}*///int read(byte[] b) 從輸入流中讀取一定數量的字節,并將其存儲在緩沖區數組 b 中。byte[] bytes = new byte[1024];//存儲每次讀取的數據int len = 0; //記錄每次讀取的有效字節個數while((len=bis.read(bytes)) != -1){System.out.println(new String(bytes,0,len));//把字節數組轉化成字符串}//4.釋放資源bis.close();//直接關閉緩沖流即可,關閉緩沖流就會自動關閉基本流
}

普通輸入輸出流和緩沖流的效率比較:

字節流:

c語言字符串數組輸入輸出。文件復制練習:一讀一寫
文件復制的步驟:
1.創建一個字節輸入流對象,構造方法中綁定要讀取的數據源
2.創建一個字節輸出流對象,構造方法中綁定要寫入的目的地
3.使用字節輸入流對象中的方法read讀取文件
4.使用字節輸出流中的方法write,把讀取到的字節寫入到目的地的文件中
5.釋放資源

public static void main(String[] args) throws IOException {long s = System.currentTimeMillis();//1.創建一個字節輸入流對象,構造方法中綁定要讀取的數據源FileInputStream fis = new FileInputStream("C:\\Users\\asus\\Desktop\\JAVA核心知識點整理.pdf");//2.創建一個字節輸出流對象,構造方法中綁定要寫入的目的地FileOutputStream fos = new FileOutputStream("C:\\Users\\asus\\Desktop\\test\\JAVA核心知識點整理.pdf");//一次讀取一個字節寫入一個字節的方式//3.使用字節輸入流對象中的方法read讀取文件/*int len = 0;while((len = fis.read())!=-1){//4.使用字節輸出流中的方法write,把讀取到的字節寫入到目的地的文件中fos.write(len);}*///使用數組緩沖讀取多個字節,寫入多個字節byte[] bytes = new byte[1024];//3.使用字節輸入流對象中的方法read讀取文件int len = 0;//每次讀取的有效字節個數while((len = fis.read(bytes))!=-1){//4.使用字節輸出流中的方法write,把讀取到的字節寫入到目的地的文件中fos.write(bytes,0,len);}//5.釋放資源(先關寫的,后關閉讀的;如果寫完了,肯定讀取完畢了)fos.close();fis.close();long e = System.currentTimeMillis();System.out.println("復制文件共耗時:"+(e-s)+"毫秒");
}
復制文件共耗時:804毫秒

字節緩沖流:

1.創建字節緩沖輸入流對象,構造方法中傳遞字節輸入流
2.創建字節緩沖輸出流對象,構造方法中傳遞字節輸出流
3.使用字節緩沖輸入流對象中的方法read,讀取文件
4.使用字節緩沖輸出流中的方法write,把讀取的數據寫入到內部緩沖區中
5.釋放資源(會先把緩沖區中的數據,刷新到文件中)
public static void main(String[] args) throws IOException {long s = System.currentTimeMillis();//1.創建字節緩沖輸入流對象,構造方法中傳遞字節輸入流BufferedInputStream bis = new BufferedInputStream(new FileInputStream("C:\\Users\\asus\\Desktop\\JAVA核心知識點整理.pdf"));//2.創建字節緩沖輸出流對象,構造方法中傳遞字節輸出流BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("C:\\Users\\asus\\Desktop\\test\\JAVA核心知識點整理.pdf"));//3.使用字節緩沖輸入流對象中的方法read,讀取文件//一次讀取一個字節寫入一個字節的方式/*int len = 0;while((len = bis.read())!=-1){bos.write(len);}*///使用數組緩沖讀取多個字節,寫入多個字節byte[] bytes = new byte[1024];int len = 0;while((len = bis.read(bytes))!=-1){bos.write(bytes,0,len);}bos.close();bis.close();long e = System.currentTimeMillis();System.out.println("復制文件共耗時:"+(e-s)+"毫秒");
}
復制文件共耗時:118毫秒

字節流耗時804毫秒,字節緩沖流耗時118毫秒。

字符緩沖流:

字符緩沖輸出流

java.io.BufferedWriter extends Writer
BufferedWriter:字符緩沖輸出流

繼承自父類的共性成員方法:

- 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(Writer out) 創建一個使用默認大小輸出緩沖區的緩沖字符輸出流。
BufferedWriter(Writer out, int sz) 創建一個使用給定大小輸出緩沖區的新緩沖字符輸出流。
參數:Writer out:字符輸出流我們可以傳遞FileWriter,緩沖流會給FileWriter增加一個緩沖區,提高FileWriter的寫入效率int sz:指定緩沖區的大小,不寫默認大小

特有的成員方法:

void newLine() 寫入一個行分隔符。會根據不同的操作系統,獲取不同的行分隔符
換行:換行符號
windows:\r\n
linux:/n
mac:/r

字符型輸入和輸出用什么表示?使用步驟:
1.創建字符緩沖輸出流對象,構造方法中傳遞字符輸出流
2.調用字符緩沖輸出流中的方法write,把數據寫入到內存緩沖區中
3.調用字符緩沖輸出流中的方法flush,把內存緩沖區中的數據,刷新到文件中
4.釋放資源

public static void main(String[] args) throws IOException {//System.out.println();//1.創建字符緩沖輸出流對象,構造方法中傳遞字符輸出流BufferedWriter bw = new BufferedWriter(new FileWriter("C:\\Users\\asus\\Desktop\\test\\a.txt"));//2.調用字符緩沖輸出流中的方法write,把數據寫入到內存緩沖區中for (int i = 0; i <10 ; i++) {bw.write("我寫進來了");//bw.write("\r\n");bw.newLine();}//3.調用字符緩沖輸出流中的方法flush,把內存緩沖區中的數據,刷新到文件中bw.flush();//4.釋放資源bw.close();
}

字符緩沖輸入流

java.io.BufferedReader extends Reader
BufferedReader:字符緩沖輸入流

繼承自父類的共性成員方法:

int read() 讀取單個字符并返回。
int read(char[] cbuf)一次讀取多個字符,將字符讀入數組。
void close() 關閉該流并釋放與之關聯的所有資源。

構造方法:

BufferedReader(Reader in)  創建一個使用默認大小輸入緩沖區的緩沖字符輸入流。
BufferedReader(Reader in, int sz)     創建一個使用指定大小輸入緩沖區的緩沖字符輸入流。
參數:Reader in:字符輸入流我們可以傳遞FileReader,緩沖流會給FileReader增加一個緩沖區,提高FileReader的讀取效率

特有的成員方法:

 String readLine() 讀取一個文本行。讀取一行數據
    行的終止符號:通過下列字符之一即可認為某行已終止:換行 ('\n')、回車 ('\r') 或回車后直接跟著換行(\r\n)。
返回值:包含該行內容的字符串,不包含任何行終止符,如果已到達流末尾,則返回 null

使用步驟:
1.創建字符緩沖輸入流對象,構造方法中傳遞字符輸入流
2.使用字符緩沖輸入流對象中的方法read/readLine讀取文本
3.釋放資源

public static void main(String[] args) throws IOException {//1.創建字符緩沖輸入流對象,構造方法中傳遞字符輸入流BufferedReader br = new BufferedReader(new FileReader("C:\\Users\\asus\\Desktop\\test\\a.txt"));//2.使用字符緩沖輸入流對象中的方法read/readLine讀取文本/*String line = br.readLine();System.out.println(line);line = br.readLine();System.out.println(line);line = br.readLine();System.out.println(line);line = br.readLine();System.out.println(line);*//*發下以上讀取是一個重復的過程,所以可以使用循環優化不知道文件中有多少行數據,所以使用while循環while的結束條件,讀取到null結束*/String line;while((line = br.readLine())!=null){System.out.println(line);}//3.釋放資源br.close();
}

練習:

對文本的內容進行排序
按照(1,2,3....)順序排序

字節輸入流和字符輸入流,分析:
1.創建一個HashMap集合對象,可以:存儲每行文本的序號(1,2,3,…);value:存儲每行的文本
2.創建字符緩沖輸入流對象,構造方法中綁定字符輸入流
3.創建字符緩沖輸出流對象,構造方法中綁定字符輸出流
4.使用字符緩沖輸入流中的方法readline,逐行讀取文本
5.對讀取到的文本進行切割,獲取行中的序號和文本內容
6.把切割好的序號和文本的內容存儲到HashMap集合中(key序號是有序的,會自動排序1,2,3,4…)
7.遍歷HashMap集合,獲取每一個鍵值對
8.把每一個鍵值對,拼接為一個文本行
9.把拼接好的文本,使用字符緩沖輸出流中的方法write,寫入到文件中
10.釋放資源

public static void main(String[] args) throws IOException {//1.創建一個HashMap集合對象,可以:存儲每行文本的序號(1,2,3,..);value:存儲每行的文本HashMap<String,String> map = new HashMap<>();//2.創建字符緩沖輸入流對象,構造方法中綁定字符輸入流BufferedReader br = new BufferedReader(new FileReader("C:\\Users\\asus\\Desktop\\in.txt"));//3.創建字符緩沖輸出流對象,構造方法中綁定字符輸出流BufferedWriter bw = new BufferedWriter(new FileWriter("C:\\Users\\asus\\Desktop\\test\\in.txt"));//4.使用字符緩沖輸入流中的方法readline,逐行讀取文本String line;while((line = br.readLine())!=null){//5.對讀取到的文本進行切割,獲取行中的序號和文本內容String[] arr = line.split("\\.");//根據.點來切割,是一個轉義符 需要用\\.//6.把切割好的序號和文本的內容存儲到HashMap集合中(key序號是有序的,會自動排序1,2,3,4..)map.put(arr[0],arr[1]);}//7.遍歷HashMap集合,獲取每一個鍵值對for(String key : map.keySet()){String value = map.get(key);//8.把每一個鍵值對,拼接為一個文本行line = key + "." + value;//9.把拼接好的文本,使用字符緩沖輸出流中的方法write,寫入到文件中bw.write(line);bw.newLine();//寫換行}//10.釋放資源bw.close();br.close();
}

小結:對于HashMap傳入數據方法不熟悉,以及分割split,轉義符
map.put()方法,參數,
集合的遍歷方法不熟練。

轉換流

計算機中儲存的信息都是用二進制數表示的,而我們在屏幕上看到的數字、英文、標點符號、漢字等字符是二進制數轉換之后的結果。也就是說圖片查看軟件等都帶有自動轉化功能。
按照某種規則,將字符存儲到計算機中,稱為編碼 。反之,
將存儲在計算機中的二進制數按照某種規則解析顯示出來,稱為解碼
比如說,按照A規則存儲,同樣按照A規則解析,那么就能顯示正確的文本符號。
反之,按照A規則存儲,再按照B規則解析,就會導致亂碼現象。
編碼:字符(能看懂的)–字節(看不懂的)
解碼:字節(看不懂的)–>字符(能看懂的)

  • 字符編碼Character Encoding : 就是一套自然語言的字符與二進制數之間的對應規則。
    編碼表:生活中文字和計算機中二進制的對應規則
  • 字符集 Charset:也叫編碼表。是一個系統支持的所有字符的集合,包括各國家文字、標點符號、圖形符號、數字等。

在這里插入圖片描述

  • ASCII字符集 :
    ASCII(American Standard Code for Information Interchange,美國信息交換標準代碼)是基于拉丁字母的一套電腦編碼系統,用于顯示現代英語,主要包括控制字符(回車鍵、退格、換行鍵等)和可顯示字符(英文大小寫字符、阿拉伯數字和西文符號)。
    基本的ASCII字符集,使用7位(bits)表示一個字符,共128字符。ASCII的擴展字符集使用8位(bits)表示一個字符,共256字符,方便支持歐洲常用字符。
  • ISO-8859-1字符集:
    拉丁碼表,別名Latin-1,用于顯示歐洲使用的語言,包括荷蘭、丹麥、德語、意大利語、西班牙語等。
    ISO-8859-1使用單字節編碼,兼容ASCII編碼。
  • GBxxx字符集:
    GB就是國標的意思,是為了顯示中文而設計的一套字符集。
    GB2312:簡體中文碼表。一個小于127的字符的意義與原來相同。但兩個大于127的字符連在一起時,就表示一個漢字,這樣大約可以組合了包含7000多個簡體漢字,此外數學符號、羅馬希臘的字母、日文的假名們都編進去了,連在ASCII里本來就有的數字、標點、字母都統統重新編了兩個字節長的編碼,這就是常說的"全角"字符,而原來在127號以下的那些就叫"半角"字符了。
    GBK:最常用的中文碼表。是在GB2312標準基礎上的擴展規范,使用了雙字節編碼方案,共收錄了21003個漢字,完全兼容GB2312標準,同時支持繁體漢字以及日韓漢字等。
    GB18030:最新的中文碼表。收錄漢字70244個,采用多字節編碼,每個字可以由1個、2個或4個字節組成。支持中國國內少數民族的文字,同時支持繁體漢字以及日韓漢字等。
  • Unicode字符集 :
    Unicode編碼系統為表達任意語言的任意字符而設計,是業界的一種標準,也稱為統一碼、標準萬國碼。
    它最多使用4個字節的數字來表達每個字母、符號,或者文字。有三種編碼方案,UTF-8、UTF-16和UTF-32。最為常用的UTF-8編碼。
    UTF-8編碼,可以用來表示Unicode標準中任何字符,它是電子郵件、網頁及其他存儲或傳送文字的應用中,優先采用的編碼。互聯網工程工作小組(IETF)要求所有互聯網協議都必須支持UTF-8編碼。所以,我們開發Web應用,也要使用UTF-8編碼。它使用一至四個字節為每個字符編碼,編碼規則:
    128個US-ASCII字符,只需一個字節編碼。
    拉丁文等字符,需要二個字節編碼。
    大部分常用字(含中文),使用三個字節編碼。
    其他極少使用的Unicode輔助字符,使用四字節編碼。

編碼引出的問題

在IDEA中,使用FileReader 讀取項目中的文本文件。由于IDEA的設置,都是默認的UTF-8編碼,所以沒有任何問題。但是,當讀取Windows系統中創建的文本文件時,由于Windows系統的默認是GBK編碼,就會出現亂碼。

在文件中書寫一個UTF-8編碼 內容為 你好 txt,轉換成GBK編碼。字節由6個轉成4個
因為UTF-8中三個字節代表一個中文,GBK兩哥字節代表一個中文。

public static void main(String[] args) throws IOException {FileReader fr = new FileReader("C:\\Users\\asus\\Desktop\\test\\你好.txt");int len = 0;while((len = fr.read())!=-1){System.out.print((char))len);}fr.close();
}
FileReader可以讀取IDE默認編碼格式(UTF-8)的文件
FileReader讀取系統默認編碼(中文GBK)會產生亂碼���

在這里插入圖片描述

上述圖標說明:使用FileReader輸入流只能讀取默認的UTF-8編碼的文件,GBK就會出錯。
使用InputStreamReader可以查詢指定的碼表,寫入的流FileWriter可以查詢默認碼表把字符轉化成字節,實現了編碼(看懂的變成看不懂的)。也可以使用OutputStreamWriter就可以指定碼表進行編碼。使用FileOutStream進行寫入。

OutputStreamWriter類

java.io.OutputStreamWriter extends Writer
OutputStreamWriter: 是字符流通向字節流的橋梁:可使用指定的 charset 將要寫入流中的字符編碼成字節。(編碼:把能看懂的變成看不懂)

繼續自父類的共性成員方法:

- 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() 關閉此流,但要先刷新它。

構造方法:
OutputStreamWriter(OutputStream out)創建使用默認字符編碼的 OutputStreamWriter。
OutputStreamWriter(OutputStream out, String charsetName) 創建使用指定字符集的 OutputStreamWriter。
參數:
OutputStream out:字節輸出流,可以用來寫轉換之后的字節到文件中
String charsetName:指定的編碼表名稱,不區分大小寫,可以是utf-8/UTF-8,gbk/GBK,…不指定默認使用UTF-8
使用步驟:
1.創建OutputStreamWriter對象,構造方法中傳遞字節輸出流和指定的編碼表名稱
2.使用OutputStreamWriter對象中的方法write,把字符轉換為字節存儲緩沖區中(編碼)
3.使用OutputStreamWriter對象中的方法flush,把內存緩沖區中的字節刷新到文件中(使用字節流寫字節的過程)
4.釋放資源

private static void write_gbk() throws IOException {//1.創建OutputStreamWriter對象,構造方法中傳遞字節輸出流和指定的編碼表名稱OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("C:\\Users\\asus\\Desktop\\test\\你好.txt"),"GBK");//2.使用OutputStreamWriter對象中的方法write,把字符轉換為字節存儲緩沖區中(編碼)osw.write("你好");//3.使用OutputStreamWriter對象中的方法flush,把內存緩沖區中的字節刷新到文件中(使用字節流寫字節的過程)osw.flush();//4.釋放資源osw.close();
}/*使用轉換流OutputStreamWriter寫UTF-8格式的文件*/
private static void write_utf_8() throws IOException {//1.創建OutputStreamWriter對象,構造方法中傳遞字節輸出流和指定的編碼表名稱//OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("10_IO\\utf_8.txt"),"utf-8");OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("C:\\Users\\asus\\Desktop\\test\\你好.txt"));//不指定默認使用UTF-8//2.使用OutputStreamWriter對象中的方法write,把字符轉換為字節存儲緩沖區中(編碼)osw.write("你好");//3.使用OutputStreamWriter對象中的方法flush,把內存緩沖區中的字節刷新到文件中(使用字節流寫字節的過程)osw.flush();//4.釋放資源osw.close();
}

InputStreamReader類

java.io.InputStreamReader extends Reader
InputStreamReader:是字節流通向字符流的橋梁:它使用指定的 charset 讀取字節并將其解碼為字符。(解碼:把看不懂的變成能看懂的)

繼承自父類的共性成員方法:

int read() 讀取單個字符并返回。
int read(char[] cbuf)一次讀取多個字符,將字符讀入數組。
void close() 關閉該流并釋放與之關聯的所有資源。

構造方法:
InputStreamReader(InputStream in) 創建一個使用默認字符集的 InputStreamReader。
InputStreamReader(InputStream in, String charsetName) 創建使用指定字符集的 InputStreamReader。
參數:
InputStream in:字節輸入流,用來讀取文件中保存的字節
String charsetName:指定的編碼表名稱,不區分大小寫,可以是utf-8/UTF-8,gbk/GBK,…不指定默認使用UTF-8
使用步驟:
1.創建InputStreamReader對象,構造方法中傳遞字節輸入流和指定的編碼表名稱
2.使用InputStreamReader對象中的方法read讀取文件
3.釋放資源
注意事項:
構造方法中指定的編碼表名稱要和文件的編碼相同,否則會發生亂碼

private static void read_gbk() throws IOException {//1.創建InputStreamReader對象,構造方法中傳遞字節輸入流和指定的編碼表名稱//InputStreamReader isr = new InputStreamReader(new FileInputStream("10_IO\\gbk.txt"),"UTF-8");//???InputStreamReader isr = new InputStreamReader(new FileInputStream("C:\\Users\\asus\\Desktop\\test\\你好.txt"),"gbk");//你好//2.使用InputStreamReader對象中的方法read讀取文件int len = 0;while ((len = isr.read()) != -1){System.out.println((char)len);}//3.釋放資源isr.close();
}/*使用InputStreamReader讀取UTF-8格式的文件*/
private static void read_utf_8() throws IOException {//1.創建InputStreamReader對象,構造方法中傳遞字節輸入流和指定的編碼表名稱//InputStreamReader isr = new InputStreamReader(new FileInputStream("10_IO\\utf_8.txt"),"UTF-8");InputStreamReader isr = new InputStreamReader(new FileInputStream("c:\\users\\asus\\desktop\\test\\哈哈.txt"));//不指定默認使用UTF-8//2.使用InputStreamReader對象中的方法read讀取文件int len = 0;while((len = isr.read())!=-1){System.out.println((char)len);//注意進行強轉}//3.釋放資源isr.close();
}

在這里插入圖片描述

轉換文件編碼

將GBK編碼的文本文件,轉換為UTF-8編碼的文本文件。
練習:轉換文件編碼
將GBK編碼的文本文件,轉換為UTF-8編碼的文本文件。

分析:
1.創建InputStreamReader對象,構造方法中傳遞字節輸入流和指定的編碼表名稱GBK
2.創建OutputStreamWriter對象,構造方法中傳遞字節輸出流和指定的編碼表名稱UTF-8
3.使用InputStreamReader對象中的方法read讀取文件
4.使用OutputStreamWriter對象中的方法write,把讀取的數據寫入到文件中
5.釋放資源

public static void main(String[] args) throws IOException {//1.創建InputStreamReader對象,構造方法中傳遞字節輸入流和指定的編碼表名稱GBKInputStreamReader isr = new InputStreamReader(new FileInputStream("10_IO\\我是GBK格式的文本.txt"),"GBK");//2.創建OutputStreamWriter對象,構造方法中傳遞字節輸出流和指定的編碼表名稱UTF-8OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("10_IO\\我是utf_8格式的文件.txt"),"UTF-8");//3.使用InputStreamReader對象中的方法read讀取文件int len = 0;while((len = isr.read())!=-1){//4.使用OutputStreamWriter對象中的方法write,把讀取的數據寫入到文件中osw.write(len);}//5.釋放資源osw.close();isr.close();
}

序列化

在這里插入圖片描述

把對象p以流的方式寫入到文件中保存,就叫對象的序列化,需要使用字節流。
ObjectOutputStream:對象的序列化流。使用writeObject方法寫入
把文件中保存的對象讀取出來:反序列化
ObjectInputStream:對象的反序列化流。使用readObect方法讀取,使用Object類型返回,任意對象

ObjectOutputStream類

java.io.ObjectOutputStream extends OutputStream
ObjectOutputStream:對象的序列化流
作用:把對象以流的方式寫入到文件中保存

構造方法:
ObjectOutputStream(OutputStream out) 創建寫入指定 OutputStream 的 ObjectOutputStream。
參數:
OutputStream out:字節輸出流
特有的成員方法:

 void writeObject(Object obj) 將指定的對象寫入 ObjectOutputStream。

使用步驟:
1.創建ObjectOutputStream對象,構造方法中傳遞字節輸出流
2.使用ObjectOutputStream對象中的方法writeObject,把對象寫入到文件中
3.釋放資源

public static void main(String[] args) throws IOException {//1.創建ObjectOutputStream對象,構造方法中傳遞字節輸出流ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("10_IO\\person.txt"));//2.使用ObjectOutputStream對象中的方法writeObject,把對象寫入到文件中oos.writeObject(new Person("小美女",18));//3.釋放資源oos.close();
}

序列化和反序列化的時候,會拋出NotSerializableException沒有序列化異常
類通過實現 java.io.Serializable 接口以啟用其序列化功能。

未實現此接口的類將無法使其任何狀態序列化或反序列化。
Serializable接口也叫標記型接口
**要進行序列化和反序列化的類必須實現Serializable接口,就會給類添加一個標記
當我們進行序列化和反序列化的時候,就會檢測類上是否有這個標記有:就可以序列化和反序列化沒有:就會拋出 NotSerializableException異常**

去市場買肉–>肉上有一個藍色章(檢測合格)–>放心購買–>買回來怎么吃隨意

public class Person implements Serializable{private static final long serialVersionUID = 1L;private String name;//private static int age;//private transient int age;public int age;public Person() {}public Person(String name, int age) {this.name = name;this.age = age;}@Overridepublic String toString() {return "Person{" +"name='" + name + '\'' +", age=" + age +'}';}public String getName() {return name;}public void setName(String name) {this.name = name;}public int getAge() {return age;}public void setAge(int age) {this.age = age;}
}

ObjectInputStream類

ObjectInputStream反序列化流,將之前使用ObjectOutputStream序列化的原始數據恢復為對象。

java.io.ObjectInputStream extends InputStream
ObjectInputStream:對象的反序列化流
作用:把文件中保存的對象,以流的方式讀取出來使用

構造方法:
ObjectInputStream(InputStream in) 創建從指定 InputStream 讀取的 ObjectInputStream。
參數:
InputStream in:字節輸入流
特有的成員方法:

Object readObject() 從 ObjectInputStream 讀取對象。

使用步驟:
1.創建ObjectInputStream對象,構造方法中傳遞字節輸入流
2.使用ObjectInputStream對象中的方法readObject讀取保存對象的文件
3.釋放資源
4.使用讀取出來的對象(打印)

public static void main(String[] args) throws IOException, ClassNotFoundException {//1.創建ObjectInputStream對象,構造方法中傳遞字節輸入流ObjectInputStream ois = new ObjectInputStream(new FileInputStream("10_IO\\person.txt"));//2.使用ObjectInputStream對象中的方法readObject讀取保存對象的文件Object o = ois.readObject();//3.釋放資源ois.close();//4.使用讀取出來的對象(打印)System.out.println(o);Person p = (Person)o;//o是object類型,可以進行強轉,轉成Person類型System.out.println(p.getName()+p.getAge());
}

static和transient關鍵字:

static關鍵字:靜態關鍵字
靜態優先于非靜態加載到內存中(靜態優先于對象進入到內存中)
被static修飾的成員變量不能被序列化的,序列化的都是對象
private static int age;
oos.writeObject(new Person(“小美女”,18));
Object o = ois.readObject();
Person{name=‘小美女’, age=0}//讀出age是0,是成員變量的默認值。

transient關鍵字:瞬態關鍵字
被transient修飾成員變量,不能被序列化
private transient int age;
oos.writeObject(new Person(“小美女”,18));
Object o = ois.readObject();
Person{name=‘小美女’, age=0}

readObject方法聲明拋出了ClassNotFoundException(class文件找不到異常)
當不存在對象的class文件時拋出此異常

反序列化的前提:
1.類必須實現Serializable
2.必須存在類對應的class文件

public class Person implements Serializable{
private static final long serialVersionUID = 1L;

上述第二行代碼:無論Person類怎么進行修改,序列號都是1L

原理圖:
在這里插入圖片描述

Person類實現了Serializable接口,就會定義class文件時添加一個序列號,這個序列號類似于身份證,序列化時根據對象發放序列號,反序列化時對比序列號,如果相同就反序列化成功,但是如果Person類進行修改,會重新生成新的序列號,此時反序列化就會失敗。只有進行修改時不生成新的序列號才能解決。手動添加一個序列號。聲明為 serialVersionUID。就不會發生沖突異常。

test:序列化集合

練習:序列化集合
當我們想在文件中保存多個對象的時候
可以把多個對象存儲到一個集合中
對集合進序列化和反序列化
分析:
1.定義一個存儲Person對象的ArrayList集合
2.往ArrayList集合中存儲Person對象
3.創建一個序列化流ObjectOutputStream對象
4.使用ObjectOutputStream對象中的方法writeObject,對集合進行序列化
5.創建一個反序列化ObjectInputStream對象
6.使用ObjectInputStream對象中的方法readObject讀取文件中保存的集合
7.把Object類型的集合轉換為ArrayList類型
8.遍歷ArrayList集合
9.釋放資源

public static void main(String[] args) throws IOException, ClassNotFoundException {//1.定義一個存儲Person對象的ArrayList集合ArrayList<Person> list = new ArrayList<>();//2.往ArrayList集合中存儲Person對象list.add(new Person("張三",18));list.add(new Person("李四",19));list.add(new Person("王五",20));//3.創建一個序列化流ObjectOutputStream對象ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("10_IO\\list.txt"));//4.使用ObjectOutputStream對象中的方法writeObject,對集合進行序列化oos.writeObject(list);//5.創建一個反序列化ObjectInputStream對象ObjectInputStream ois = new ObjectInputStream(new FileInputStream("10_IO\\list.txt"));//6.使用ObjectInputStream對象中的方法readObject讀取文件中保存的集合Object o = ois.readObject();//7.把Object類型的集合轉換為ArrayList類型ArrayList<Person> list2 = (ArrayList<Person>)o;//強轉成對應集合對象//8.遍歷ArrayList集合for (Person p : list2) {//增強for遍歷System.out.println(p);}//9.釋放資源ois.close();oos.close();

小結:新建兩個流,注意寫入讀取方法,以及反序列化后的強轉類型,增強for循環遍歷集合。

打印流

java.io.PrintStream:打印流
PrintStream 為其他輸出流添加了功能,使它們能夠方便地打印各種數據值表示形式。
PrintStream特點:
1.只負責數據的輸出,不負責數據的讀取
2.與其他輸出流不同,PrintStream 永遠不會拋出 IOException
3.有特有的方法,print,println

  void print(任意類型的值)void println(任意類型的值并換行)

構造方法:
PrintStream(File file):輸出的目的地是一個文件
PrintStream(OutputStream out):輸出的目的地是一個字節輸出流
PrintStream(String fileName) :輸出的目的地是一個文件路徑
PrintStream extends 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) :將指定的字節輸出流。

注意:
如果使用繼承自父類的write方法寫數據,那么查看數據的時候會查詢編碼表 97->a
如果使用自己特有的方法print/println方法寫數據,寫的數據原樣輸出 97->97
*/

public static void main(String[] args) throws FileNotFoundException {//System.out.println("HelloWorld");//創建打印流PrintStream對象,構造方法中綁定要輸出的目的地PrintStream ps = new PrintStream("10_IO\\print.txt");//如果使用繼承自父類的write方法寫數據,那么查看數據的時候會查詢編碼表 97->aps.write(97);//a//如果使用自己特有的方法print/println方法寫數據,寫的數據原樣輸出 97->97ps.println(97);ps.println(8.8);ps.println('a');ps.println("HelloWorld");ps.println(true);//釋放資源ps.close();
}

可以改變輸出語句的目的地(打印流的流向)
輸出語句,默認在控制臺輸出
使用System.setOut方法改變輸出語句的目的地改為參數中傳遞的打印流的目的地
static void setOut(PrintStream out)
重新分配“標準”輸出流。

public static void main(String[] args) throws FileNotFoundException {System.out.println("我是在控制臺輸出");//在控制臺輸出//下面不讓在控制臺,改成流向文件中,用system。setOut方法PrintStream ps = new PrintStream("C:\\Users\\asus\\Desktop\\test\\序列化.txt");System.setOut(ps);//把輸出語句的目的地改變為打印流的目的地System.out.println("我在打印流的目的地中輸出");//輸出語句從控制臺到了文件中System.out.println("我又來了");PrintStream ps01 = new PrintStream("C:\\Users\\asus\\Desktop\\test\\序列化01.txt");System.setOut(ps01);System.out.println("我來這里了");ps.close();ps01.close();
}

版权声明:本站所有资料均为网友推荐收集整理而来,仅供学习和研究交流使用。

原文链接:https://hbdhgg.com/2/186167.html

发表评论:

本站为非赢利网站,部分文章来源或改编自互联网及其他公众平台,主要目的在于分享信息,版权归原作者所有,内容仅供读者参考,如有侵权请联系我们删除!

Copyright © 2022 匯編語言學習筆記 Inc. 保留所有权利。

底部版权信息