#IO
概述
-
IO流介紹
- IO:輸入/輸出(Input/Output)
- 流:是一種抽象概念,是對數據傳輸的總稱.也就是說數據在設備間的傳輸稱為流,流的本質是數據傳輸
- IO流就是用來處理設備間數據傳輸問題的.常見的應用: 文件復制; 文件上傳; 文件下載
- 參照物:內存 內存在讀,內存在寫
-
IO流的分類

-
按照數據的流向
- 輸入流:讀數據
- 輸出流:寫數據
-
按照數據類型來分
- 字節流
- 字節輸入流
- 字節輸出流
- 字符流
- 字符輸入流
- 字符輸出流
- 字節流
-
IO流的使用場景
- 如果操作的是純文本文件,優先使用字符流
- 如果操作的是圖片、視頻、音頻等二進制文件,優先使用字節流
- 如果不確定文件類型,優先使用字節流.字節流是萬能的流
純文本文件?
用windows記事本打開能讀懂的文件就是純文本文件
純文本:字符流,否則:字節流
字節流
字節流寫數據
-
字節流抽象基類
- InputStream:這個抽象類是表示字節輸入流的所有類的超類
- OutputStream:這個抽象類是表示字節輸出流的所有類的超類
- 子類名特點:子類名稱都是以其父類名作為子類名的后綴
-
字節輸出流
- FileOutputStream(String name):創建文件輸出流以指定的名稱寫入文件
-
使用字節輸出流寫數據的步驟
- 創建字節輸出流對象(調用系統功能創建了文件,創建字節輸出流對象,讓字節輸出流對象指向文件)
- 調用字節輸出流對象的寫數據方法
- 釋放資源(關閉此文件輸出流並釋放與此流相關聯的任何系統資源)
-
示例代碼
public class OutPutDemo {
public static void main(String[] args) throws IOException {
//1.創建字節輸出流對象--- 告訴虛擬機我要往哪個文件中寫數據 下面兩個方法一樣的我們用第一個簡單的就行
FileOutputStream fos = new FileOutputStream("F:\\itheima\\a.txt");
FileOutputStream fs = new FileOutputStream(new File("F:\\itheima\\a.txt"));
//2.寫數據
fos.write(97);
//3.釋放資源
fos.close();
}
}
注意事項:
-
創建字節輸出流對象
1.如果文件不存在,會幫我們創建
2.如果文件存在,會把文件清空 -
寫數據
傳遞一個整數時,那么實際上寫到文件中的,是這個整數在碼表中對應的字符
-
釋放資源
告訴操作系統,我現在已經不需要再用這個文件了
每次使用完流必須要釋放資源
字節流寫數據的三種方式
-
寫數據的方法分類
方法名 說明 void write(int b) 將指定的字節寫入此文件輸出流 一次寫一個字節數據 void write(byte[] b) 將 b.length字節從指定的字節數組寫入此文件輸出流 一次寫一個字節數組數據 void write(byte[] b, int off, int len) 將 len字節從指定的字節數組開始,從偏移量off開始寫入此文件輸出流 一次寫一個字節數組的部分數據 -
示例代碼
一次寫一個字節
public class OutPutDemo1 {
public static void main(String[] args) throws IOException {
FileOutputStream fos = new FileOutputStream("F:\\itheima\\a.txt");
fos.write(97);
fos.write(98);
fos.write(99);
fos.close();
}
}
一次寫一個字節數組
public class OutPutDemo2 {
public static void main(String[] args) throws IOException {
FileOutputStream fos = new FileOutputStream("F:\\itheima\\a.txt");
byte[] arr = {97,98,99};
fos.write(arr);
fos.close();
}
}
一次寫一個字節數組的一部分
public class OutPutDemo3 {
public static void main(String[] args) throws IOException {
FileOutputStream fos = new FileOutputStream("F:\\itheima\\a.txt");
byte[] arr = {97,98,99,100,101,102,103,104};
fos.write(arr,1,3);
fos.close();//bcd
}
}
字節流寫數據的兩個小問題
-
字節流寫數據如何實現換行
- windows:\r\n
- linux:\n
- mac:\r
-
示例代碼
public class OutPutDemo4 {
public static void main(String[] args) throws IOException {
FileOutputStream fos = new FileOutputStream("F:\\itheima\\a.txt");
fos.write(97);
fos.write("\r\n".getBytes());
fos.write(98);
fos.write("\r\n".getBytes());
fos.write(99);
fos.write("\r\n".getBytes());
fos.write(100);
fos.write("\r\n".getBytes());
fos.write(101);
fos.write("\r\n".getBytes());
fos.close();
}
}
字節流寫數據如何實現追加寫入
- public FileOutputStream(String name,boolean append)
- 創建文件輸出流以指定的名稱寫入文件。如果第二個參數為true ,則字節將寫入文件的末尾而不是開頭
- 示例代碼
public class OutPutDemo4 {
public static void main(String[] args) throws IOException {
FileOutputStream fos = new FileOutputStream("F:\\itheima\\a.txt",true);
fos.write(69);
fos.write("\r\n".getBytes());
fos.write(70);
fos.write("\r\n".getBytes());
fos.close();
}
}
第二個參數是續寫開關,如果不寫就是false,表示不打開續寫功能會清空文件內容
字節流寫數據加異常處理
-
異常處理格式
-
try-catch-finally
try{ 可能出現異常的代碼; }catch(異常類名 變量名){ 異常的處理代碼; }finally{ 執行所有清除操作; } -
finally特點
- 被finally控制的語句一定會執行,除非JVM退出
-
-
示例代碼
public class OutPutDemo5 {
public static void main(String[] args) {
FileOutputStream fos = null;
try {
fos = new FileOutputStream("F:\\itheima\\a.txt", true);
fos.write(69);
fos.write("\r\n".getBytes());
fos.write(70);
fos.write("\r\n".getBytes());
} catch (IOException e) {
e.printStackTrace();
} finally {
if (fos != null) {
try {
fos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
字節流讀數據
字節流讀取數據方式
一次讀取一個字節

-
字節輸入流
- FileInputStream(String name):通過打開與實際文件的連接來創建一個FileInputStream,該文件由文件系統中的路徑名name命名
-
字節輸入流讀取數據的步驟
-
創建字節輸入流對象
如果文件存在,那么正常
如果文件不存在會報FileNotFoundException
-
調用字節輸入流對象的讀數據方法
一次讀取一個字節,返回值就是本次讀到的那個字節數據
也就是字符在碼表中的數字,如果先要看到字符數據,那么一定要強轉為char
-
釋放資源
-
-
示例代碼
public class OutPutDemo6 {
public static void main(String[] args) throws IOException {
FileInputStream fis = new FileInputStream("F:\\itheima\\a.txt");
int read = fis.read();
System.out.println(read);
fis.close();//a
}
}
讀多個字節
public class OutPutDemo7 {
public static void main(String[] args) throws IOException {
FileInputStream fis = new FileInputStream("F:\\itheima\\a.txt");
//文件中多個字節怎么辦
int b;
while ((b = fis.read()) != -1){
System.out.println((char)b);
}
fis.close();
}
}
復制文件
-
案例需求
把“E:\itcast\窗里窗外.txt”復制到模塊目錄下的“窗里窗外.txt” (文件可以是任意文件)
-
實現步驟
-
復制文本文件,其實就把文本文件的內容從一個文件中讀取出來(數據源),然后寫入到另一個文件中(目的地)
-
數據源:
E:\itcast\窗里窗外.txt --- 讀數據 --- InputStream --- FileInputStream
-
目的地:
myByteStream\窗里窗外.txt --- 寫數據 --- OutputStream --- FileOutputStream
-
-
代碼實現
一次讀一個字節數組

-
案例需求
把“E:\itcast\mn.jpg”復制到模塊目錄下的“mn.jpg” (文件可以是任意文件去)
-
實現步驟
- 根據數據源創建字節輸入流對象
- 根據目的地創建字節輸出流對象
- 讀寫數據,復制圖片(一次讀取一個字節數組,一次寫入一個字節數組)
- 釋放資源
-
代碼實現
public class OutPutDemo9 {
public static void main(String[] args) throws IOException {
//准備讀文件
FileInputStream fis = new FileInputStream("F:\\itheima\\a.txt");
//准備寫文件
FileOutputStream fos = new FileOutputStream("F:\\setup\\a.txt");
//一次讀一個拷貝速度慢怎么辦
//相當於買雞蛋的籃子 容器
byte[] buf = new byte[1024];
//本次讀到的有效字節個數 -- 這次讀了幾個字節
int len;
while ((len = fis.read(buf)) != -1){
//0索引開始len個字節,這里是文件中字符個數
fos.write(buf, 0, len);
}
fis.close();
fos.close();
}
}
字節緩沖流
-
字節緩沖流介紹
- lBufferOutputStream:該類實現緩沖輸出流.通過設置這樣的輸出流,應用程序可以向底層輸出流寫入字節,而不必為寫入的每個字節導致底層系統的調用
- lBufferedInputStream:創建BufferedInputStream將創建一個內部緩沖區數組.當從流中讀取或跳過字節時,內部緩沖區將根據需要從所包含的輸入流中重新填充,一次很多字節
-
構造方法:
方法名 說明 BufferedOutputStream(OutputStream out) 創建字節緩沖輸出流對象 BufferedInputStream(InputStream in) 創建字節緩沖輸入流對象 -
為什么構造方法需要的是字節流,而不是具體文件或路徑呢?
字節緩沖流僅僅提供緩沖區,而真正讀寫數據還得靠基本的字節流對象進行操作
緩沖流只是為了提高效率而存在的
一次讀取一個字節數據

- 示例代碼
public class OutPutDemo10 {
public static void main(String[] args) throws IOException {
//創建字節緩沖輸入流
BufferedInputStream bis = new BufferedInputStream(new FileInputStream("F:\\itheima\\a.txt"));
//創建字節緩沖輸出流
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("F:\\setup\\a.txt"));
int b;
while ((b = bis.read()) != -1){
bos.write(b);
}
//方法的底層會把字節流給關閉
bis.close();
bos.close();
}
BufferedInputStream、BufferedOutputStream相當於在底層創建了一個長度為8192的字節數組
一次讀寫一個字節數組
public class OutPutDemo11 {
public static void main(String[] args) throws IOException {
//創建字節緩沖輸入流
BufferedInputStream bis = new BufferedInputStream(new FileInputStream("F:\\itheima\\a.txt"));
//創建字節緩沖輸出流
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("F:\\setup\\a.txt"));
byte[] buf = new byte[1024];
int len;
while ((len = bis.read(buf)) != -1){
bos.write(buf, 0, len);
}
bis.close();
bos.close();
}
}
字符流
為什么會出現字符流
-
字符流的介紹
由於字節流操作中文不是特別的方便,所以Java就提供字符流
字符流 = 字節流 + 編碼表
-
中文的字節存儲方式
用字節流復制文本文件時,文本文件也會有中文,但是沒有問題,原因是最終底層操作會自動進行字節拼接成中文,如何識別是中文的呢?
漢字在存儲的時候,無論選擇哪種編碼存儲,第一個字節都是負數

編碼表
-
什么是字符集
是一個系統支持的所有字符的集合,包括各國家文字、標點符號、圖形符號、數字等
l計算機要准確的存儲和識別各種字符集符號,就需要進行字符編碼,一套字符集必然至少有一套字符編碼。常見字符集有ASCII字符集、GBXXX字符集、Unicode字符集等
-
常見的字符集
-
ASCII字符集:
lASCII:是基於拉丁字母的一套電腦編碼系統,用於顯示現代英語,主要包括控制字符(回車鍵、退格、換行鍵等)和可顯示字符(英文大小寫字符、阿拉伯數字和西文符號)
基本的ASCII字符集,使用7位表示一個字符,共128字符。ASCII的擴展字符集使用8位表示一個字符,共256字符,方便支持歐洲常用字符。是一個系統支持的所有字符的集合,包括各國家文字、標點符號、圖形符號、數字等
-
GBXXX字符集:
GBK:最常用的中文碼表。是在GB2312標准基礎上的擴展規范,使用了雙字節編碼方案,共收錄了21003個漢字,完全兼容GB2312標准,同時支持繁體漢字以及日韓漢字等
-
Unicode字符集:
UTF-8編碼:可以用來表示Unicode標准中任意字符,它是電子郵件、網頁及其他存儲或傳送文字的應用 中,優先采用的編碼。互聯網工程工作小組(IETF)要求所有互聯網協議都必須支持UTF-8編碼。它使用一至四個字節為每個字符編碼
編碼規則:
128個US-ASCII字符,只需一個字節編碼
拉丁文等字符,需要二個字節編碼
大部分常用字(含中文),使用三個字節編碼
其他極少使用的Unicode輔助字符,使用四字節編碼
-
字符串中的編碼解碼問題
-
相關方法
方法名 說明 byte[] getBytes() 使用平台的默認字符集將該 String編碼為一系列字節 byte[] getBytes(String charsetName) 使用指定的字符集將該 String編碼為一系列字節 String(byte[] bytes) 使用平台的默認字符集解碼指定的字節數組來創建字符串 String(byte[] bytes, String charsetName) 通過指定的字符集解碼指定的字節數組來創建字符串 -
代碼演示
public class OutPutDemo12 {
public static void main(String[] args) throws UnsupportedEncodingException {
String sf = "大吉大利,今晚吃雞!";
byte[] bytes = sf.getBytes();
//利用idea默認的UTF-8編碼為一系列字節
System.out.println(Arrays.toString(bytes));
byte[] bytes1 = sf.getBytes("GBK");
System.out.println(Arrays.toString(bytes1));
}
}
執行結果
[-27, -92, -89, -27, -112, -119, -27, -92, -89, -27, -120, -87, -17, -68, -116, -28, -69, -118, -26, -103, -102, -27, -112, -125, -23, -72, -95, -17, -68, -127]
[-76, -13, -68, -86, -76, -13, -64, -5, -93, -84, -67, -15, -51, -19, -77, -44, -68, -90, -93, -95]
大吉大利,
大吉大利,今晚吃雞!
字節流為什么會出現亂碼
因為字節流一次讀取一個字節,而不管GBK還是UTF-8一個中文都是多個字節,用字節流每次只能讀取其中一部分內容,所以就會出現亂碼問題。
字符流讀取中文過程
字符流 = 字節流 + 碼表;
基礎知識:
不管在哪張碼表中,中文的第一個字節一定是負數

字符流寫數據
-
介紹
Writer: 用於寫入字符流的抽象父類
FileWriter: 用於寫入字符流的常用子類
-
構造方法
方法名 說明 FileWriter(File file) 根據給定的 File 對象構造一個 FileWriter 對象 FileWriter(File file, boolean append) 根據給定的 File 對象構造一個 FileWriter 對象 FileWriter(String fileName) 根據給定的文件名構造一個 FileWriter 對象 FileWriter(String fileName, boolean append) 根據給定的文件名以及指示是否附加寫入數據的 boolean 值來構造 FileWriter 對象 -
成員方法
方法名 說明 void write(int c) 寫一個字符 void write(char[] cbuf) 寫入一個字符數組 void write(char[] cbuf, int off, int len) 寫入字符數組的一部分 void write(String str) 寫一個字符串 void write(String str, int off, int len) 寫一個字符串的一部分 -
刷新和關閉的方法
方法名 說明 flush() 刷新流,之后還可以繼續寫數據 close() 關閉流,釋放資源,但是在關閉之前會先刷新流。一旦關閉,就不能再寫數據 -
代碼演示
一次寫出一個字符
public class OutPutDemo13 {
public static void main(String[] args) throws IOException {
//創建字符輸出流對象
FileWriter fw = new FileWriter(new File("F:\\itheima\\a.txt"));
//寫出數據
fw.write(97);
fw.write(98);
fw.write(99);
//釋放資源
fw.close();
}
}
一次寫出一個字符數組
public class OutPutDemo13 {
public static void main(String[] args) throws IOException {
//創建字符輸出流對象
FileWriter fw = new FileWriter(new File("F:\\itheima\\a.txt"));
//寫出數據
char [] chars = {97,98,99,100};
fw.write(chars);
//釋放資源
fw.close();
}
}
寫一個字符串(常用)
public class OutPutDemo13 {
public static void main(String[] args) throws IOException {
//創建字符輸出流對象
FileWriter fw = new FileWriter(new File("F:\\itheima\\a.txt"));
//寫出數據
String s = "大吉大利,今晚吃雞";
fw.write(s);
//釋放資源
fw.close();
}
}
注意事項

flush和close
//創建字符輸出流對象
FileWriter fw = new FileWriter(new File("F:\\itheima\\a.txt"));
//寫出數據
String s = "大吉大利,今晚吃雞";
fw.write(s);
//把數據刷新到本地文件中
fw.flush();
//釋放資源
fw.close();
使用flush()刷新數據到本地后,還可以繼續錄入
close()也會刷新數據到本地,但是就關流了,之后不能再輸入
字符流讀數據
一次讀取一個字符
public class OutPutDemo14 {
public static void main(String[] args) throws IOException {
//創建字符輸出流對象
FileReader fr = new FileReader(new File("F:\\itheima\\a.txt"));
//讀數據
//一次讀取一個字符
int ch;
while ((ch = fr.read()) != -1) {
System.out.println((char)ch);
}
//釋放資源
fr.close();
}
}
一次讀取多個字符
public class OutPutDemo15 {
public static void main(String[] args) throws IOException {
//創建字符輸出流對象
FileReader fr = new FileReader(new File("F:\\itheima\\a.txt"));
//讀數據
//創建一個字符數組
//一次讀取多個字符
char [] chars = new char[1024];
int len;
while ((len = fr.read(chars)) != -1) {
System.out.println(new String(chars, 0, len));
}
//釋放資源
fr.close();
}
}
小練習
-
案例需求
將鍵盤錄入的用戶名和密碼保存到本地實現永久化存儲
-
實現步驟
- 獲取用戶輸入的用戶名和密碼
- 將用戶輸入的用戶名和密碼寫入到本地文件中
- 關流,釋放資源
-
代碼實現
public class OutPutDemo16 {
public static void main(String[] args) throws IOException {
//需求
//將鍵盤錄入的用戶名和密碼保存到本地實現永久化存儲
//分析
//1.實現鍵盤錄入,把用戶名密碼錄入進來
Scanner sc = new Scanner(System.in);
System.out.println("請錄入用戶名:");
String username = sc.next();
System.out.println("請錄入密碼:");
String password = sc.next();
//2.分別把用戶名密碼寫入到本地
//創建字符輸出流對象
FileWriter fw = new FileWriter("F:\\itheima\\b.txt");
//把用戶名密碼寫入到本地
fw.write(username);
//表示一個回車換行符
fw.write("\r\n");
fw.write(password);
//刷新流
fw.flush();
//釋放資源
fw.close();
}
}
字符緩沖流
-
字符緩沖流介紹
-
BufferedWriter:將文本寫入字符輸出流,緩沖字符,以提供單個字符,數組和字符串的高效寫入,可以指定緩沖區大小,或者可以接受默認大小。默認值足夠大,可用於大多數用途
-
BufferedReader:從字符輸入流讀取文本,緩沖字符,以提供字符,數組和行的高效讀取,可以指定緩沖區大小,或者可以使用默認大小。 默認值足夠大,可用於大多數用途
-
-
構造方法
方法名 說明 BufferedWriter(Writer out) 創建字符緩沖輸出流對象 BufferedReader(Reader in) 創建字符緩沖輸入流對象 -
代碼演示
public class OutPutDemo17 {
public static void main(String[] args) throws IOException {
//創建字符輸出流對象
BufferedReader br = new BufferedReader(new FileReader(new File("F:\\itheima\\a.txt")));
//讀數據
//創建一個字符數組
//一次讀取多個字符
char [] chars = new char[1024];
int len;
while ((len = br.read(chars)) != -1) {
System.out.println(new String(chars, 0, len));
}
//釋放資源
br.close();
}
}
字符緩沖輸出流
public class OutPutDemo18 {
public static void main(String[] args) throws IOException {
//字符緩沖輸出流
BufferedWriter bw = new BufferedWriter(new FileWriter("F:\\itheima\\c.txt"));
//寫出數據
bw.write(97);
bw.write(98);
bw.write(99);
bw.write(100);
bw.write("\r\n");
char [] chars = {
101,102,103,104,105
};
bw.write(chars);
bw.write("\r\n");
bw.write(chars,2,2);
bw.write("\r\n");
bw.write("大吉大利");
bw.write("\r\n");
bw.flush();
bw.close();
}
}
字符緩沖流特有功能
-
方法介紹
BufferedWriter:
方法名 說明 void newLine() 寫一行行分隔符,行分隔符字符串由系統屬性定義 BufferedReader:
方法名 說明 String readLine() 讀一行文字。 結果包含行的內容的字符串,不包括任何行終止字符如果流的結尾已經到達,則為null -
代碼演示
public class OutPutDemo19 {
public static void main(String[] args) throws IOException {
BufferedWriter bw = new BufferedWriter(new FileWriter("F:\\itheima\\d.txt"));
//寫出數據
bw.write("大吉大利,今晚吃雞");
bw.newLine();
bw.write("abcdefg");
bw.newLine();
bw.flush();
bw.close();
}
}
public class OutPutDemo20 {
public static void main(String[] args) throws IOException {
BufferedReader br = new BufferedReader(new FileReader("F:\\itheima\\d.txt"));
//讀出數據
String s = br.readLine();
String l = br.readLine();
String r = br.readLine();
System.out.println(s);
System.out.println(l);
System.out.println(r);
br.close();
}
}
使用循環改進讀取
public class OutPutDemo21 {
public static void main(String[] args) throws IOException {
BufferedReader br = new BufferedReader(new FileReader("F:\\itheima\\d.txt"));
//讀出數據
String line;
while ((line = br.readLine()) != null){
System.out.println(line);
}
//釋放資源
br.close();
}
}
readline為什么可以讀取一整行?
可以讀取一整行數據,一直讀直到回車換行為止
但是它不會讀取回車換行符
字符緩沖流操作文件中數據排序案例
-
案例需求
使用字符緩沖流讀取文件中的數據,排序后再次寫到本地文件
-
實現步驟
- 將文件中的數據讀取到程序中
- 對讀取到的數據進行處理
- 將處理后的數據添加到集合中
- 對集合中的數據進行排序
- 將排序后的集合中的數據寫入到文件中
-
代碼實現
public class OutPutDemo22 {
public static void main(String[] args) throws IOException {
//1.需要將文件中的數據讀進來
BufferedReader br = new BufferedReader(new FileReader("F:\\itheima\\f.txt"));
//讀出數據
int [] ints = {};
String line;
while ((line = br.readLine()) != null){
String[] s = line.split(" ");
//2.排序
ints = sortList(s);
}
System.out.println(Arrays.toString(ints));
//3.把排序后的結果寫出
BufferedWriter bw = new BufferedWriter(new FileWriter("F:\\itheima\\g.txt"));
bw.write(Arrays.toString(ints));
//釋放資源
br.close();
bw.close();
}
//數組冒泡排序方法
private static int[] sortList(String[] s) {
//定義外層循環
int[] ints = StringToInt(s);
for (int i = 0; i < ints.length - 1; i++) {
//定義內層循環
for (int j = 0; j < ints.length - i -1; j++) {
if (ints[j] > ints[j + 1]){
int temp = ints[j];
ints[j] = ints[j+1];
ints[j+1] = temp;
}
}
}
return ints;
}
//String數組轉為int
public static int[] StringToInt(String[] arr){
int[] array = new int[arr.length];
for (int i = 0; i < arr.length; i++) {
array[i] = Integer.parseInt(arr[i]);
}
return array;
}
}
轉換流

-
InputStreamReader:是從字節流到字符流的橋梁,父類是Reader
它讀取字節,並使用指定的編碼將其解碼為字符
它使用的字符集可以由名稱指定,也可以被明確指定,或者可以接受平台的默認字符集
-
OutputStreamWriter:是從字符流到字節流的橋梁,父類是Writer
是從字符流到字節流的橋梁,使用指定的編碼將寫入的字符編碼為字節
它使用的字符集可以由名稱指定,也可以被明確指定,或者可以接受平台的默認字符集
轉換流讀寫數據
-
構造方法
方法名 說明 InputStreamReader(InputStream in) 使用默認字符編碼創建InputStreamReader對象 InputStreamReader(InputStream in,String chatset) 使用指定的字符編碼創建InputStreamReader對象 OutputStreamWriter(OutputStream out) 使用默認字符編碼創建OutputStreamWriter對象 OutputStreamWriter(OutputStream out,String charset) 使用指定的字符編碼創建OutputStreamWriter對象

- 代碼演示
package com.cloudcore.iotest;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
/**
* @Author: hepeng
* @Date: 2021/10/18 19:45
*/
public class OutPutDemo23 {
public static void main(String[] args) throws IOException {
//會產生亂碼
method1();
//如何解決亂碼?
method2();
}
private static void method2() throws IOException {
InputStreamReader is = new InputStreamReader(new FileInputStream("F:\\itheima\\a.txt"),"UTF-8");
int ch;
while ((ch = is.read()) != -1){
System.out.println((char)ch);
}
is.close();
OutputStreamWriter out = new OutputStreamWriter(new FileOutputStream("F:\\itheima\\aa.txt"),"UTF-8");
out.write("我愛學習誰也別打擾我");
out.close();
}
private static void method1() throws IOException {
FileReader fr = new FileReader("F:\\itheima\\a.txt");
int ch;
while ((ch = fr.read()) != -1){
System.out.println((char)ch);
}
fr.close();
}
}
對象操作流
對象序列化流
-
對象序列化介紹
- 對象序列化:就是將對象保存到磁盤中,或者在網絡中傳輸對象
- 這種機制就是使用一個字節序列表示一個對象,該字節序列包含:對象的類型、對象的數據和對象中存儲的屬性等信息
- 字節序列寫到文件之后,相當於文件中持久保存了一個對象的信息
- 反之,該字節序列還可以從文件中讀取回來,重構對象,對它進行反序列化
-
對象序列化流: ObjectOutputStream
- 將Java對象的原始數據類型和圖形寫入OutputStream。 可以使用ObjectInputStream讀取(重構)對象。 可以通過使用流的文件來實現對象的持久存儲。 如果流是網絡套接字流,則可以在另一個主機上或另一個進程中重構對象
-
構造方法
方法名 說明 ObjectOutputStream(OutputStream out) 創建一個寫入指定的OutputStream的ObjectOutputStream -
序列化對象的方法
方法名 說明 void writeObject(Object obj) 將指定的對象寫入ObjectOutputStream -
示例代碼
學生類
public class ObjectOutputStreamDemo {
public static void main(String[] args) throws IOException {
//ObjectOutputStream(OutputStream out):創建一個寫入指定的OutputStream的ObjectOutputStream
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("myOtherStream\\oos.txt"));
//創建對象
Student s = new Student("佟麗婭",30);
//void writeObject(Object obj):將指定的對象寫入ObjectOutputStream
oos.writeObject(s);
//釋放資源
oos.close();
}
}
注意事項
- 一個對象要想被序列化,該對象所屬的類必須必須實現Serializable 接口
- Serializable是一個標記接口,實現該接口,不需要重寫任何方法
對象反序列化流
-
對象反序列化流: ObjectInputStream
- ObjectInputStream反序列化先前使用ObjectOutputStream編寫的原始數據和對象
-
構造方法
方法名 說明 ObjectInputStream(InputStream in) 創建從指定的InputStream讀取的ObjectInputStream -
反序列化對象的方法
方法名 說明 Object readObject() 從ObjectInputStream讀取一個對象 -
示例代碼
public class ObjectInputStreamDemo {
public static void main(String[] args) throws IOException, ClassNotFoundException {
//ObjectInputStream(InputStream in):創建從指定的InputStream讀取的ObjectInputStream
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("myOtherStream\\oos.txt"));
//Object readObject():從ObjectInputStream讀取一個對象
Object obj = ois.readObject();
Student s = (Student) obj;
System.out.println(s.getName() + "," + s.getAge());
ois.close();
}
}
serialVersionUID&transient
-
serialVersionUID
- 用對象序列化流序列化了一個對象后,假如我們修改了對象所屬的類文件,讀取數據會不會出問題呢?
- 會出問題,會拋出InvalidClassException異常
- 如果出問題了,如何解決呢?
- 重新序列化
- 給對象所屬的類加一個serialVersionUID
- private static final long serialVersionUID = 42L;
- 用對象序列化流序列化了一個對象后,假如我們修改了對象所屬的類文件,讀取數據會不會出問題呢?
-
transient
- 如果一個對象中的某個成員變量的值不想被序列化,又該如何實現呢?
- 給該成員變量加transient關鍵字修飾,該關鍵字標記的成員變量不參與序列化過程
- 如果一個對象中的某個成員變量的值不想被序列化,又該如何實現呢?
-
示例代碼
學生類
public class Student implements Serializable {
private static final long serialVersionUID = 42L;
private String name;
// private int age;
private transient int age;
public Student() {
}
public Student(String name, int age) {
this.name = name;
this.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;
}
}
Properties集合
-
Properties介紹
- 是一個Map體系的集合類
- Properties可以保存到流中或從流中加載
- 屬性列表中的每個鍵及其對應的值都是一個字符串
-
Properties基本使用
package com.cloudcore.iotest;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
/**
* @Author: hepeng
* @Date: 2021/10/19 0:01
*/
public class properties {
public static void main(String[] args) {
Properties prop = new Properties();
//增
prop.put("小龍女","尹志平");
prop.put("郭襄","楊過");
prop.put("黃蓉","歐陽克");
System.out.println(prop);
//刪
prop.remove("小龍女");
System.out.println(prop);
//改
prop.put("黃蓉","小沈陽");
System.out.println(prop);
//查
Object value = prop.get("郭襄");
System.out.println(value);
//遍歷
Set<Object> keys = prop.keySet();
for (Object key : keys) {
Object o = prop.get(key);
System.out.println(key + "=" + o);
}
Set<Map.Entry<Object, Object>> entries = prop.entrySet();
for (Map.Entry<Object, Object> entry : entries) {
Object key = entry.getKey();
Object value1 = entry.getValue();
System.out.println(key + "=" + value1);
}
}
}
Properties作為Map集合的特有方法
package com.cloudcore.iotest;
import java.util.Properties;
import java.util.Set;
/**
* @Author: hepeng
* @Date: 2021/10/19 0:08
*/
public class propertiesTest {
public static void main(String[] args) {
Properties prop = new Properties();
prop.setProperty("山東","濟南");
prop.setProperty("浙江","杭州");
prop.setProperty("山西","太原");
prop.setProperty("安徽","合肥");
System.out.println(prop);
String key = prop.getProperty("浙江");
System.out.println(key);
Set<String> keys = prop.stringPropertyNames();
for (String s : keys) {
Object value = prop.get(s);
System.out.println(s + "=" + value);
}
}
}
