文章目錄
零、本講學習目標
1、瞭解字節流的概念及其分類
2、學會使用字節流實現讀寫操作
3、學會使用字節流實現文件拷貝
一、I/O流概述
1、I/O流定義
I/O(Input/Output)流,即輸入/輸出流,是Java中實現輸入/輸出的基礎,它可以方便地實現數據的輸入/輸出操作。
2、I/O流分類
分類 | 分類依據 |
---|---|
字節流與字符流 | 根據流操作的數據單位的不同,可以分爲字節流和字符流 |
輸入流與輸出流 | 根據流傳輸方向的不同,可以分爲輸入流和輸出流 |
節點流與處理流 | 根據流的功能不同,可以分爲節點流和處理流 |
3、I/O流層次結構
Java中的I/O流主要定義在java.io包中,該包下定義了很多類,其中有4個類爲流的頂級類,分別爲InputStream和OutputStream,Reader和Writer。
- InputStream和OutPutStream是字節流,而Reader和Writer是字符流
- InputStream和Reader是輸入流,而OutPutStream和Writer是輸出流
- 注意圖中的4個頂級類都是抽象類,並且是所有流類型的父類
4、數據的輸出
數據從程序到文件,叫做輸出,引入流的概念,那就叫輸出流,這個流是以字節爲單位,叫做字節輸出流(OutputStream)。如果是針對文本文件來進行操作,一般是以字符爲單位,叫做字符輸出流(Writer)。
5、數據的輸入
數據從文件到程序,叫做輸入,引入流的概念,那就叫輸入流,這個流是以字節爲單位,叫做字節輸入流(InputStream)。如果是針對文本文件來進行操作,一般是以字符爲單位,叫做字符輸入流(Reader)。
6、聲明I/O流的四個頂級抽象類
可以看到I/O流的類都在java.io包裏,程序裏要使用,必須要導入。
二、字節流概述
1、字節流定義
在計算機中,無論是文本、圖片、音頻還是視頻,所有文件都是以二進制(字節)形式存在的,I/O流中針對字節的輸入/輸出提供了一系列的流,統稱爲字節流。
2、字節流說明
字節流是程序中最常用的流。在JDK中,所有的字節輸入流都繼承自InputStream,所有的字節輸出流都繼承自OutputStream。
3、字節輸入流與字節輸出流示意圖
- InputStream被看成一個輸入管道,OutputStream被看成一個輸出管道,數據通過InputStream從源設備輸入到程序,通過OutputStream從程序輸出到目標設備,從而實現數據的傳輸。
4、字節輸入流
(1)常用方法
方法聲明 | 功能描述 |
---|---|
int read() | 從輸入流讀取一個8位的字節,把它轉換爲0~255之間的整數,並返回這一整數。當沒有可用字節時,將返回-1 |
int read(byte[] b) | 從輸入流讀取若干字節,把它們保存到參數b指定的字節數組中,返回的整數表示讀取字節的數目 |
int read(byte[] b,int off,int len) | 從輸入流讀取若干字節,把它們保存到參數b指定的字節數組中,off指定字節數組開始保存數據的起始下標,len表示讀取的字節數目 |
void close() | 關閉此輸入流並釋放與該流關聯的所有系統資源 |
- 前三個read()方法都是用來讀數據的,分按字節讀取和按字節數組讀取。
- 進行I/O流操作時,應該調用close()方法關閉流,從而釋放當前I/O流所佔的系統資源。
(2)繼承關係圖
5、字節輸出流
(1)常用方法
方法聲明 | 功能描述 |
---|---|
void write(int b) | 向輸出流寫入一個字節 |
void write(byte[] b) | 把參數b指定的字節數組的所有字節寫到輸出流 |
void write(byte[] b,int off,int len) | 將指定byte數組中從偏移量off開始的len個字節寫入輸出流 |
void flush() | 刷新此輸出流並強制寫出所有緩衝的輸出字節 |
void close() | 關閉此輸出流並釋放與此流相關的所有系統資源 |
- 前三個write()方法都是用來寫數據的,分按字節讀取和按字節數組寫入。
- flush()方法用來將當前輸出流緩衝區(通常是字節數組)中的數據強制寫入目標設備,此過程稱爲刷新。
- close()方法是用來關閉流並釋放與當前IO流相關的系統資源。
(2)繼承關係圖
三、利用字節流輸入輸出數據
1、DataInputStream繼承關係圖
2、DataOutputStream繼承關係圖
3、案例演示:利用DataInputStream與DataOutputStream輸入輸出整數
- 創建Example2402
package net.hw.lesson24;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
/**
* 功能:利用DataInputStream與DataOutputStream輸入輸出整數
* 作者:華衛
* 日期:2020年05月25日
*/
public class Example2402 {
public static void main(String[] args) throws IOException {
int a;
// 定義數據字節輸入流
DataInputStream dis = new DataInputStream(System.in);
// 定義數據字節輸出流
DataOutputStream dos = new DataOutputStream(System.out);
System.out.print("a = ");
a = dis.readInt(); // 從數據字節輸入流讀取整數
System.out.print("a = ");
dos.writeInt(a); // 將整數寫入數據字節輸出流
}
}
-
運行程序,查看結果
-
輸入a的之後,修改a的值
-
運行程序,查看結果
-
說明:改變a的值之後,再利用數據字節輸出流對象輸出a的值,竟然是空,也沒有報異常。那麼我們應該如何操作,才能讓輸入的數據參與運算之後還能輸出正確的結果呢?
-
注意:下面這個案例用到了《Java講課筆記25講:緩衝流、字符流和轉換流》裏的轉換流。
4、案例演示:行讀取鍵盤輸入的數據
- 創建Example2403
package net.hw.lesson24;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
/**
* 功能:行讀取鍵盤輸入的數據
* 作者:華衛
* 日期:2020年05月25日
*/
public class Example2403 {
public static void main(String[] args) throws IOException {
int a;
/**
* System.in 標準字節輸入流,代表的是鍵盤
* InputStreamReader 轉換流:將字節流轉換成字符流
* BufferedReader:緩衝字符輸入流,提供高效的行讀取方法
*/
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
System.out.print("a = ");
a = Integer.parseInt(br.readLine()); // 讀取的字符串解析成整數
a = a + 150;
System.out.println("a + 150 = " + a);
}
}
-運行程序,查看結果
轉換過程:標準字節輸入流——>字符流——>緩衝字符輸入流(提供了效率比較高的行讀取方法)
5、課堂練習:利用緩衝字符輸入流輸入學生基本信息(學號、姓名和年齡)
五、利用字節流讀寫文件
針對文件的讀寫操作,JDK專門提供了兩個類,分別是FileInputStream和FileOutputStream。
(一)利用FileInputStream讀取文件
1、讀取文件思路
FileInputStream是InputStream的子類,它是操作文件的字節輸入流,專門用於讀取文件中的數據。從文件讀取數據是重複的操作,因此需要通過循環語句來實現數據的持續讀取。可以按字節讀取文件,也可以按字節數組來讀取文件。
2、案例演示:利用文件字節輸入流讀取文本文件
- 在當前包裏創建文本文件in.txt,輸入兩行字符內容
- 創建Example2404
- 文件字節輸入流的read()方法,返回值爲-1時,表明讀到文件尾。
package net.hw.lesson24;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
/**
* 功能:利用FileInputStream讀取文件
* 作者:華衛
* 日期:2020年05月25日
*/
public class Example2404 {
public static void main(String[] args) {
try {
// 創建文件字節輸入流對象
FileInputStream fis = new FileInputStream("in.txt");
// 通過while循環按字節讀取文件
int c = 0;
while ((c = fis.read()) != -1){
System.out.print(c);
}
// 關閉文件字節輸入流
fis.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}
- 運行程序,查看結果
- 說明:FileInputStream(“in.txt”)是在項目根目錄去找“in.txt”文件,顯然找不到,因爲“in.txt”在當前包裏,路徑爲
src/net/hw/lesson24/in.txt
,因此拋出FileNotFoundException異常。 - 修改程序,如下所示:
FileInputStream fis = new FileInputStream("src/net/hw/lesson24/in.txt");
- 運行程序,查看結果
- 說明:文件輸入字節流read()方法返回是讀取的字符對應的ASCII碼值,要輸出字符內容,很簡單,只需強轉成字符型輸出即可。
- 運行程序,查看效果
(二)利用FileOutputStream寫入文件
1、寫入文件思路
- 通過FileOutputStream向一個不存在的文件中寫入數據,會自動創建文件。可將要寫入的字符串轉換成字節數組,再調用文件字節輸出流的write()方法將其寫入指定文件。
- 通過FileOutputStream向一個已經存在的文件中寫入數據,該文件中的數據首先會被清空,再寫入新的數據。若希望在已存在的文件內容之後追加新內容,則可使用構造函數FileOutputStream(String fileName, boolean append)來創建文件輸出流對象,並把append參數的值設置爲true。
2、案例演示
(1)利用文件字節輸出流將數據寫入指定文件,非追加模式
- 創建Example2405
package net.hw.lesson24;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
/**
* 功能:非追加模式寫入文件
* 作者:華衛
* 日期:2020年05月25日
*/
public class Example2405 {
public static void main(String[] args) {
try {
// 創建文件字節輸出流
FileOutputStream fos = new FileOutputStream("src/net/hw/lesson24/out.txt");
// 定義字符串
String text = "Java功能強大\n我們一起學Java\n";
// 將字符串轉成字節數組寫入指定文件
fos.write(text.getBytes());
// 關閉文件字節輸出流
fos.close();
// 提示用戶寫入文件成功
System.out.println("數據成功寫入指定文件!");
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}
- 運行程序,查看結果
- 在當前包裏查看生成的輸出文件out.txt
- 再次執行程序,查看輸出文件,你會發現,內容依然是一樣的,因爲寫入已存在的文件時,會先清空文件原有的內容,再寫入新的內容。
(2)利用文件字節輸出流將數據寫入指定文件,追加模式
- 創建Example2406
package net.hw.lesson24;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
/**
* 功能:追加模式寫入文件
* 作者:華衛
* 日期:2020年05月25日
*/
public class Example2406 {
public static void main(String[] args) {
try {
// 創建文件字節輸出流
FileOutputStream fos = new FileOutputStream("src/net/hw/lesson24/out.txt", true);
// 定義字符串
String text = "我們永不放棄\n堅持就是勝利\n";
// 將字符串轉成字節數組寫入指定文件
fos.write(text.getBytes());
// 關閉文件字節輸出流
fos.close();
// 提示用戶寫入文件成功
System.out.println("數據成功寫入指定文件!");
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}
- 運行程序,查看結果
- 查看輸出文件out.txt
- 再執行一次程序,查看輸出文件out.txt
(三)字節流關閉語句最好放在finally塊裏
I/O流在進行數據讀寫操作時會出現異常,爲了保證I/O流的close()方法一定執行來釋放佔用的系統資源,通常會將關閉流的操作寫在finally代碼塊中,否則遇到I/O異常,放在try代碼塊的關閉語句就無法執行,流對象所佔用的系統資源將不能釋放。
- 創建Example2407,將字節流關閉語句放到finally代碼塊裏
package net.hw.lesson24;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
/**
* 功能:追加模式寫入文件
* 將字節流關閉語句寫在finally代碼塊裏
* 作者:華衛
* 日期:2020年05月25日
*/
public class Example2407 {
public static void main(String[] args) {
// 聲明文件字節輸出流
FileOutputStream fos = null;
try {
// 創建文件字節輸出流
fos = new FileOutputStream("src/net/hw/lesson24/out.txt", true);
// 定義字符串
String text = "我們永不放棄\n堅持就是勝利\n";
// 將字符串轉成字節數組寫入指定文件
fos.write(text.getBytes());
// 提示用戶寫入文件成功
System.out.println("數據成功寫入指定文件!");
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
// 判斷文件字節輸出流是否非空
if (fos != null) {
try {
// 關閉文件字節輸出流
fos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
六、利用字節流實現文件拷貝
1、文件拷貝實現思路
I/O流通常都是成對出現的,即輸入流和輸出流一起使用。例如文件的拷貝就需要通過輸入流來讀取源文件中的數據,並通過輸出流將數據寫入新文件。
2、案例演示:利用文件字節流拷貝圖片文件
- 將源圖source.png拷貝當前包net.hw.lesson24裏
- 創建Example2408
package net.hw.lesson24;
import java.io.FileInputStream;
import java.io.FileOutputStream;
/**
* 功能:利用文件字節流拷貝圖片文件
* 作者:華衛
* 日期:2020年05月25日
*/
public class Example2408 {
public static void main(String[] args) throws Exception {
// 創建文件字節輸入流
FileInputStream fis = new FileInputStream("src/net/hw/lesson24/source.png");
// 創建文件字節輸出流
FileOutputStream fos = new FileOutputStream("src/net/hw/lesson24/target.png");
// 獲取開始拷貝文件的系統時間
long startTime = System.currentTimeMillis();
// 讀取源文件,寫入目標文件
int len = 0;
while ((len = fis.read()) != -1) {
fos.write(len);
}
// 獲取結束拷貝文件的系統時間
long endTime = System.currentTimeMillis();
// 輸出拷貝文件耗費時間
System.out.println("拷貝文件耗費時間:" + (endTime - startTime) + "毫秒");
}
}
- 運行程序,查看結果
- 在當前包裏查看拷貝生成的目標文件target.png
一個4.6MB的圖片文件,拷貝用了大約9秒多,實在是太慢了,效率不高,下一講,我們會採用字節流緩衝區和緩衝字節流的方法來提高文件拷貝的效率。
七、課後作業
任務、利用文件字節流完成文件讀寫工作
- 創建文件字節輸出流,指定輸出文件爲“D:\love.txt”
- 將字符串“I love you more than I can say”寫入指定文件
- 關閉文件字節輸出流
- 創建文件字節輸出流,指定輸入文件爲“D:\love.txt”
- 讀取文件內容,顯示在控制檯窗口
- 關閉文件字節輸入流