Java 平臺的基礎 I/O 類。它首先關註 I/O Streams,這是一個強大的概念, 可以大大簡化 I/O 操作。該課程還可以看到序列化,這使得程式可以將整個對象寫入流並再次讀取它們。 然後,該課程將查看 文件 I/O 和文件系統操作,包括隨機訪問文件。 I/O Streams 大多數都是講... ...
Java I/O流講解
每博一文案
誰讓你讀了這麼多書,又知道了雙水村以外還有一個大世界,如果從小你就在這個天地里,日出而作,日落而息。
那你現在就會和眾鄉親抱同一理想:經過幾年的辛勞,像大哥一樣娶個滿意的媳婦,生個胖兒子,加上你的體魄,
會成為一名出色的莊稼人。不幸的是,你知道的太多了,思考的太多了,因此才有了,這種不能為周圍人所理解的苦惱。
—————— 《平凡的世界》
人生是這樣的不可預測,沒有永恆的痛苦,也沒有永恆的幸福,生活就像流水一般,
有時是那麼平展,有時又是那麼曲折。
世界上有些人因為忙而感到生活的沉重,也有些人因為閑而活得壓抑,人啊,都有自己一本難念的經;
可是不同處境的人又很難理解別人的苦處。
細想過來,每個人的生活也同樣是一個世界,即使是最平方的人,也要為他那個世界的存在而戰鬥。
—————— 《平凡的世界》
@
目錄- Java I/O流講解
1. File 類
java.io.File
類:文件和文件目錄路徑的抽象表示形式,與平臺無關。
File 能新建,刪除,重命名文件和目錄,但File 不能訪問文件內容本身。如果需要訪問文件內容本身,則需要使用 輸入/輸出 流。
想要在Java 程式中表示一個真實存在的文件或目錄,那麼必須有一個 File 對象,但是 Java 程式中的一個 File 對象,可能沒有一個真實存在的文件或目錄。
File 對象可以作為參數傳遞給流的構造器。
1.1 File 類中:構造器
public File(String pathname); // 過將給定路徑名字元串轉換為抽象路徑名來創建一個新 File 實例。如果給定字元串是空字元串,那麼結果是空抽象路徑名
public File(String parent,String child); // 根據 parent 路徑名字元串和 child 路徑名字元串創建一個新 File 實例。以parent為父路徑,child為子路徑創建File對象
public File(File parent, String child); // 根據 parent 抽象路徑名和 child 路徑名字元串創建一個新 File 實例。根據一個父File對象和子文件路徑創建File對象
// 路徑可以是絕對路徑,也可以是相對路徑
- 絕對路徑: 是一個固定的路徑,從盤符開始。
- 相對路徑: 是相對於某個位置開始。IDEA中預設相對路徑是從
**Project**
項目(路徑)下和同級的**src**
的路徑開始的,註意不是模塊開始的**Module**
的 。如下圖所示:src 和 Module 模塊是同級的。
1.2 File 類中:路徑分隔符
路徑中的每級目錄之間用一個路徑分隔符隔開。
路徑分隔符和系統有關:
- windows和DOS系統預設使用
“\”
來表示,需要註意的是在 java 中**"\"**
具有轉義的意思,所以想要表示真正的 “\” 需要兩個**"\\"**
來轉義回來表示一個斜桿。 - UNIX和URL使用
“/”
來表示。
Java程式支持跨平臺運行,因此路徑分隔符要慎用。
為瞭解決這個隱患,File類提供了一個常量:
public static final String separator。// 根據操作系統,動態的提供分隔符。
File file1 = new File("E:\\Test\\info.txt");
File file2 = new File("E:" + File.separator + "Test" + File.separator + "info.txt");
File file3 = new File("E:/Test");
// 這三者表示的路徑是一樣的。只是表示方式不同而已。
舉例:
package blogs.blog9;
import java.io.File;
public class FileTest {
/**
* File 構造器的使用
*/
public static void main(String[] args) {
// 構造器一:
// 絕對路徑: 帶盤符
File file = new File("E:\\Java\\JavaRebuilt\\src\\blogs\\blog9"); // 雙右斜桿表示一個 \ (轉義)
// 相對路徑: IDEA預設是Project的根目錄,不是模塊Module的根目錄,
// 也可以使用:左斜桿表示路徑分隔符
System.out.println(file);
File file2 = new File("src/blogs/blog9"); // 這裡是在src的包下(src 和 Module 模塊是同級的)
System.out.println(file2);
// 構造器二:
// 第一個參數是第二個參數的父路徑,第二個參數是子路徑
File file3 = new File("E:\\Java\\JavaRebuilt\\src\\blogs","blog9");
System.out.println(file3);
// 構造器三:
// 第一個參數是 File 類對象(這裡是第二個參數的父路徑的File對象),第二個參數是子路徑
File file4 = new File(file3,"blog9");
System.out.println(file4);
}
}
1.3 File 類中:常用方法
獲取文件屬性的信息的方法:
- getAbsoluteFile() : 返回此File對象中路徑的絕對路徑。返回的是 File 對象
public File getAbsoluteFile(); // 返回此抽象路徑名的絕對路徑名形式。
- getAbsolutePath() : 返回此抽象路徑名的絕對路徑名字元串
public String getAbsolutePath(); // 返回此抽象路徑名的絕對路徑名字元串
- getPath() : 返回此抽象路徑名以字元串的形式。
public String getPath(); // 返回此抽象路徑名
- getName() : 獲取名稱
public String getName(); // 返回由此抽象路徑名錶示的文件或目錄的名稱。該名稱是路徑名名稱序列中的最後一個名稱。如果路徑名名稱序列為空,則返回空字元串
- getParent() : 獲取上層文件目錄路徑(也就是父路徑)。若無,返回null
public String getParent(); // 返回此抽象路徑名父目錄的路徑名字元串;如果此路徑名沒有指定父目錄,則返回 null。
- length() : 返迴文件長度即(位元組數)
public long length(); // 獲取文件長度(即:位元組數)。不能獲取目錄的長度
- lastModified() : 獲取最後一次的修改時間,毫秒值。
public long lastModified(); // 獲取最後一次的修改時間,毫秒值
- list() : 獲取指定目錄下的所有文件或者文件目錄的名稱數組
public String[] list(); // 返回一個字元串數組,這些字元串指定此抽象路徑名錶示的目錄中的文件和目錄。
- listFiles() : 獲取指定目錄下的所有文件或者文件目錄的 File 數組
public File[] listFiles(); // 返回一個抽象路徑名數組,這些路徑名錶示此抽象路徑名錶示的目錄中的文件。
舉例:
import java.io.File;
import java.text.SimpleDateFormat;
import java.util.Date;
public class FileTest {
public static void main(String[] args) {
File file = new File("src\\blog9\\hello.txt"); // 註意轉義以及文件尾碼
File file2 = new File("src/blog9/hello3.txt"); // 左斜桿也是可以的
String absolutePath = file.getAbsolutePath(); // 返回絕對路徑,以String的形式返回
System.out.println(absolutePath);
File absoluteFile = file.getAbsoluteFile(); // 返回絕對路徑,以File 對象的形式返回
System.out.println();
System.out.println(file.getPath()); // 返回此路徑名/目錄名
System.out.println(file.getName()); // 返回該文件名/目錄名
System.out.println(file.getParent()); // 返回該上層文件/目錄名稱
System.out.println(file.length()); // 返迴文件長度即文件的大小(位元組)
long l = file.lastModified(); // 返回該文件的最後一次修改的時間值(毫秒值)時間戳
// 將時間戳轉換為Date,再轉換為 指定格式的字元串
Date date = new Date(l);
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd hh:ss:mm SSS");
String format = simpleDateFormat.format(date);
System.out.println(format);
}
}
舉例:
import java.io.File;
import java.text.SimpleDateFormat;
import java.util.Date;
public class FileTest {
/**
* File 文件目錄
*/
public static void main(String[] args) {
File file = new File("src/blogs/blog9"); // 也可以使用 左斜桿
String[] list = file.list(); // 返回獲取指定目錄下的所有文件或者文件目錄的名稱數組
for (String s : list) {
System.out.println(s);
}
File[] files = file.listFiles();
for(File f : files) {
System.out.println(f);
}
}
}
File類的重命名功能
- renameTo(File dest) : 把文件重命名為指定的文件路徑。換句話說:就是剪切附加對文件的重命名 的意思。
public boolean renameTo(File dest); // 重新命名此抽象路徑名錶示的文件。
註意: 這裡的剪切效果,有一定的要求:就是比如:file.renameTo(dest)
想要將 file 剪切到 dest 位置路徑上。要保證 file 剪切的文件實際在硬碟中存在,並且 dest 不能在硬碟文件中存在(僅僅當一個路徑)。如果不滿足會失敗,返回 false
舉例:
import java.io.File;
public class FileTest {
public static void main(String[] args) {
File file = new File("src\\blogs\\blog9\\hello.txt");
File dest = new File("E:\\臨時文件\\temp\\test.txt");
boolean b = file.renameTo(dest); // 將file文件剪切到 dest 中並重命名
System.out.println(b);
}
}
失敗:原因是:dest 中的 test.txt 是在硬碟中實際存在的。
將test.txt去了就沒事了 ,再重新剪切,就可以了。
File 類的判斷功能
- isDirectory() : 判斷是否是文件目錄
public boolean isDirectory(); // 測試此抽象路徑名錶示的文件是否是一個目錄。
- isFile() :判斷是否是文件
public boolean isFile(); // 當且僅當此抽象路徑名錶示的文件存在且 是一個標準文件時,返回 true;否則返回 false
- exists() : 判斷該文件/目錄是否存在
public boolean exists(); // 測試此抽象路徑名錶示的文件或目錄是否存在
- canRead() : 判斷該文件是否可讀的
public boolean canRead(); // 當且僅當此抽象路徑名指定的文件存在且 可被應用程式讀取時,返回 true;否則返回 false
- canWrite() : 判斷該文件是否可寫的
public boolean canWrite(); // 當且僅當文件系統實際包含此抽象路徑名錶示的文件且 允許應用程式對該文件進行寫入時,返回 true;否則返回 false.
- isHidden() : 判斷該文件是否隱藏的
public boolean isHidden(); // 當且僅當此抽象路徑名錶示的文件根據底層平臺約定是隱藏文件時,返回 true
舉例:
import java.io.File;
public class FileTest {
public static void main(String[] args) {
File file = new File("src\\blogs\\blog9\\hello.txt"); // 註意轉義以及文件尾碼(該文件實際存在的)
File file2 = new File("src/blogs/blog9/hello3.txt"); // 左斜桿也是可以的 (該文件不存在的)
System.out.println(file.isDirectory()); // 判斷是否是文件目錄
System.out.println(file.isFile()); // 判斷是否為文件
System.out.println(file.exists()); // 判斷該文件/目錄是否實際存在
System.out.println(file.canRead()); // 判斷該我呢見是否可讀的
System.out.println(file.canWrite()); // 判斷該文件是否是可寫的
System.out.println(file.isHidden()); // 判斷該文件是否隱藏的
System.out.println("*************************** file2 *************************");
System.out.println(file2.isDirectory()); // 判斷是否是文件目錄
System.out.println(file2.isFile()); // 判斷是否為文件
System.out.println(file2.exists()); // 判斷該文件/目錄是否實際存在
System.out.println(file2.canRead()); // 判斷該我呢見是否可讀的
System.out.println(file2.canWrite()); // 判斷該文件是否是可寫的
System.out.println(file2.isHidden()); // 判斷該文件是否隱藏的
}
}
File 類的創建功能
- **createNewFile() ** : 創建文件。若文件存在,則不創建,返回false
public boolean createNewFile() throws IOException // 如果指定的文件不存在併成功地創建,則返回 true;如果指定的文件已經存在,則返回 false
- mkdirs() : 創建文件目錄。創建文件目錄。如果上層文件目錄不存在,一併創建
public boolean mkdirs(); // 創建此抽象路徑名指定的目錄,包括所有必需但不存在的父目錄。註意,此操作失敗時也可能已經成功地創建了一部分必需的父目錄。
- mkdir() : 創建文件目錄。如果此文件目錄存在,就不創建了。如果此文件目錄的上層目錄不存在,也不創建。
public boolean mkdir(); // 創建此抽象路徑名指定的目錄。
註意事項:如果你創建文件或者文件目錄沒有寫盤符路徑,那麼,預設在項目路徑下。
舉例:
import java.io.File;
import java.io.IOException;
public class FileTest {
public static void main(String[] args) {
File file = new File("src/blogs/blog9/hello.txt");
boolean b = false;
try {
b = file.createNewFile(); // 文件存在不創建,不存在文件創建
} catch (IOException e) {
e.printStackTrace();
}
System.out.println(b);
}
}
創建目錄: 使用 mkdir
import java.io.File;
public class FileTest {
public static void main(String[] args) {
File file = new File("src/blogs/blog9/test/test2/");
boolean b = file.mkdir(); // 如果對應的 test2目錄的上級目錄test不存在,則目錄都不創建
System.out.println(b);
}
}
使用 mkdirs()
import java.io.File;
import java.io.IOException;
public class FileTest {
public static void main(String[] args) {
File file = new File("src/blogs/blog9/test/test2/");
boolean b = file.mkdirs(); // 如果對應的 test2目錄的上級目錄test不存在,則目錄一併都創建
System.out.println(b);
}
}
File類的刪除功能:
- delete() : 刪除文件或者文件夾
public boolean delete(); // 刪除此抽象路徑名錶示的文件或目錄。如果此路徑名錶示一個目錄,則該目錄必須為空才能刪除。
刪除註意事項:Java中的刪除不走回收站。要刪除一個文件目錄,請註意該文件目錄內不能包含文件或者文件目錄。如果含有無法刪除的。
舉例:
import java.io.File;
public class FileTest {
public static void main(String[] args) {
File file = new File("src/blogs/blog9/test");
boolean b = file.delete(); // test 目錄下不能有文件/目錄,有的話無法刪除
System.out.println(b);
}
}
小結 :
1.4 實用案例:
判斷指定目錄下是否有尾碼名為.jpg的文件,如果有,就輸出該文件名稱
package com.atguigu.exer2;
import java.io.File;
import java.io.FileFilter;
import java.io.FilenameFilter;
import org.junit.Test;
/**
判斷指定目錄下是否有尾碼名為.jpg的文件,如果有,就輸出該文件名稱
*/
public class FindJPGFileTest {
@Test
public void test1(){
File srcFile = new File("d:\\code");
String[] fileNames = srcFile.list();
for(String fileName : fileNames){
if(fileName.endsWith(".jpg")){
System.out.println(fileName);
}
}
}
@Test
public void test2(){
File srcFile = new File("d:\\code");
File[] listFiles = srcFile.listFiles();
for(File file : listFiles){
if(file.getName().endsWith(".jpg")){
System.out.println(file.getAbsolutePath());
}
}
}
/*
* File類提供了兩個文件過濾器方法
* public String[] list(FilenameFilter filter)
* public File[] listFiles(FileFilter filter)
*/
@Test
public void test3(){
File srcFile = new File("d:\\code");
File[] subFiles = srcFile.listFiles(new FilenameFilter() {
@Override
public boolean accept(File dir, String name) {
return name.endsWith(".jpg");
}
});
for(File file : subFiles){
System.out.println(file.getAbsolutePath());
}
}
}
遍歷指定目錄所有文件名稱,包括子文件目錄中的文件。
拓展1:並計算指定目錄占用空間的大小
拓展2:刪除指定文件目錄及其下的所有文件
package com.atguigu.exer2;
import java.io.File;
/**
* 3. 遍歷指定目錄所有文件名稱,包括子文件目錄中的文件。
拓展1:並計算指定目錄占用空間的大小
拓展2:刪除指定文件目錄及其下的所有文件
* @author shkstart 郵箱:[email protected]
* @version 創建時間:2019年2月23日 上午1:55:31
*
*/
public class ListFilesTest {
public static void main(String[] args) {
// 遞歸:文件目錄
/** 列印出指定目錄所有文件名稱,包括子文件目錄中的文件 */
// 1.創建目錄對象
File dir = new File("E:\\teach\\01_javaSE\\_尚矽谷Java編程語言\\3_軟體");
// 2.列印目錄的子文件
printSubFile(dir);
}
public static void printSubFile(File dir) {
// 列印目錄的子文件
File[] subfiles = dir.listFiles();
for (File f : subfiles) {
if (f.isDirectory()) {// 文件目錄
printSubFile(f);
} else {// 文件
System.out.println(f.getAbsolutePath());
}
}
}
// 方式二:迴圈實現
// 列出file目錄的下級內容,僅列出一級的話
// 使用File類的String[] list()比較簡單
public void listSubFiles(File file) {
if (file.isDirectory()) {
String[] all = file.list();
for (String s : all) {
System.out.println(s);
}
} else {
System.out.println(file + "是文件!");
}
}
// 列出file目錄的下級,如果它的下級還是目錄,接著列出下級的下級,依次類推
// 建議使用File類的File[] listFiles()
public void listAllSubFiles(File file) {
if (file.isFile()) {
System.out.println(file);
} else {
File[] all = file.listFiles();
// 如果all[i]是文件,直接列印
// 如果all[i]是目錄,接著再獲取它的下一級
for (File f : all) {
listAllSubFiles(f);// 遞歸調用:自己調用自己就叫遞歸
}
}
}
// 拓展1:求指定目錄所在空間的大小
// 求任意一個目錄的總大小
public long getDirectorySize(File file) {
// file是文件,那麼直接返回file.length()
// file是目錄,把它的下一級的所有大小加起來就是它的總大小
long size = 0;
if (file.isFile()) {
size += file.length();
} else {
File[] all = file.listFiles();// 獲取file的下一級
// 累加all[i]的大小
for (File f : all) {
size += getDirectorySize(f);// f的大小;
}
}
return size;
}
// 拓展2:刪除指定的目錄
public void deleteDirectory(File file) {
// 如果file是文件,直接delete
// 如果file是目錄,先把它的下一級幹掉,然後刪除自己
if (file.isDirectory()) {
File[] all = file.listFiles();
// 迴圈刪除的是file的下一級
for (File f : all) {// f代表file的每一個下級
deleteDirectory(f);
}
}
// 刪除自己
file.delete();
}
}
2. I/O 流的概述
一個 I / O流 代表輸入源或輸出目的地。流可以表示許多不同種類的源和目的地,包括磁碟文件,設備,其他程式和存儲器陣列。
流支持許多不同類型的數據,包括簡單位元組,原始數據類型,本地化字元和對象。一些流簡單地傳遞數據; 其他人以有用的方式操縱和轉換數據。
I/O 其中的 I
是 Input 的縮寫,O
是 Output 的縮寫。I/O 技術是非常實用的技術,用於處理設備之間的數據傳輸。如讀/寫文件,網路通訊等。
Java 程式中,對於數據的輸入/輸出操作以 流(stream)
的方式進行。
java.io
包下提供了各種 “流”類和介面,用以獲取不同種類的數據,並通過方法輸入或輸出數據。
無論內部工作如何,所有流都會使用與使用它們的程式相同的簡單模型:
流是一系列數據。程式使用 輸入流 從源中讀取數據:**input**
輸入流以記憶體為參考對象(將文件中的數據內容寫入到記憶體當中) 以及 **Read**
(以文件為參考對象,將讀取文件中的數據到記憶體當中)。這兩個都是將意思都是一樣的將文件中的數據讀取出來寫入到記憶體當中。
程式使用 輸出流 將數據寫入目的地。**Output**
輸出流以記憶體為參考對象(將記憶體中的數據內容輸出到硬碟文件當中) 以及 **Write**
(以文件為參考對象,將記憶體中的數據到寫入到硬碟文件當中)。這兩個都是將意思都是一樣的:將記憶體中的數據輸出到硬碟文件當中。
2.1 I/O的分類和體繫結構
按照不同的分類方式, 可以將流分為不同的類型。
2.1.1 輸入流 和 輸出流
按照流的流向來分, 可以分為輸入流和輸出流:
輸入流: 只能從中讀取數據, 而不能向其寫入數據。
輸出流: 只能向其寫入數據, 而不能從中讀取數據。
此處的輸入、 輸出涉及一個方向問題, 對於如圖 1 所示的數據流向, 數據從記憶體到硬碟, 通常稱為輸出流——也就是說, 這裡的輸入、 輸出都是從程式運行所在記憶體的角度來劃分的。
對於如圖 2 所示的數據流向, 數據從伺服器通過網路流向客戶端, 在這種情況下, Server 端的記憶體負責將數據輸出到網路里, 因此 Server 端的程式使用輸出流; Client 端的記憶體負責從網路里讀取數據, 因此 Client 端的程式應該使用輸入流。
2.1.2 位元組流 和 字元流
按操作數據單位不同分為:位元組流(8 bit),字元流(16 bit)。
位元組流和字元流的用法幾乎完全一樣, 區別在於位元組流和字元流操作的數據單元的不同:位元組流是 8 位的位元組, 而字元流操作的數據單元是 16 位的字元。其中還有一點不同的就是:
- 字元流:只能讀取操作文本文件,因為字元流讀取的是文件中的 char 字元信息。
.c,.java,.c++,.txt
等等這些都是文本文件不僅僅只是 txt文件,而特別註意的是 :.wrod 不是文本文件,wrod中的文字是經過特殊處理的存在一定的規範格式,不是純文本文件。 - 位元組流:可以操作任何的文件,因為位元組流讀取的是二進位信息,讀取1個位元組byte,等同於一次讀取8個二進位,這種流是萬能的,什麼類型的文件都可以讀取到,因為文件都是有二進位組成的。包括: 文本文件,圖片,聲音文件。
2.1.3 節點流 和 處理流(包裝流)
按流的角色的不同分為:節點流,處理流。
- 節點流: 所謂的節點流:就是最基本的一個流到底目的的流向,其中的流沒有被其它的流所包含住。直接從數據源或目的地讀寫數據。如下圖所示
- 處理流: 處理流又稱為包裝流 ,處理流對一個己存在的流進行連接或封裝/包裝, 通過封裝後的流來實現數據讀/寫功能。不直接連接到數據源或目的地,而是“連接”在已存在的流(節點流或處理流)之上,通過對數據的處理為程式提 供更為強大的讀寫功能。
- 如下圖所示:
註意: 節點流和包裝流是相對的,有時候,相對於不同的流的,一個節點流變成了是另一個流的包裝流。一個包裝流變成了另一個流的節點流 。如下圖所示:
區分一個流是節點流還是包裝流:大家可以:以誰包裝誰作為參考,被包裝的流就是節點流,包裝了其它的流的就是包裝流,當然註意這是相對的。
2.1.4 流的概念模型
Java 把所有設備里的有序數據抽象成流模型, 簡化了輸入/輸出處理, 理解了流的概念模型也就瞭解了Java IO。
通過使用處理流, Java 程式無須理會輸入/輸出節點是磁碟、 網路還是其他的輸入/輸出設備, 程式只要將這些節點流包裝成處理流, 就可以使用相同的輸入/輸出代碼來讀寫不同的輸入/輸出設備的數據。
2.1.5 I/O 的體繫結構
- Java的IO流共涉及40多個類,實際上非常規則,都是從如下 Java Io 流四大家族 個 抽象基類派生的。
- 由以下這四個類派生出來的子類。其名稱都是以其父類名作為子類名尾碼。這樣用於我們辨認。
- Java IO流四大家族:
- java.io.InputStream 位元組輸入流,類名是以
"stream"
結尾的。
- java.io.InputStream 位元組輸入流,類名是以
public abstract class InputStream implements Closeable {}
- java.io.OutputStream 位元組輸出流,類名是以
"stream"
結尾的。
public abstract class OutputStream implements Closeable, Flushable {}
- java.io.Reader 字元輸入流,類名是以
"Reader/Writer"
結尾的。
public abstract class Reader implements Readable, Closeable {}
- java.io.Writer 字元輸出流,類名是以以
"Reader/Writer"
結尾的。
public abstract class Writer implements Appendable, Closeable, Flushable {}
- 上述四大家族的首領都是抽象類
abstract class
。這四個類都實現了java.io.Closeable
介面,都是可以關閉的,都有close()
方法。流畢竟是一個管道,這個記憶體和硬碟之間的通道,用完之後一定要關閉。不然會耗費很多資源(因為Java打開的資源是有限的,當你打開過多的資源超過限制時就無法打開其它的資源了)。養成好習慣,用完之後一定要關閉(必須關閉)。
- 這四個類都實現了
java.io.Flushable
介面,都是可刷新 的,都是有 flush() 方法的,養成一個好習慣,輸出流(將記憶體當中的數據輸出到硬碟文件當中)在最終(輸出完)之後,一定要記得 flush() ,刷新一下,這個刷新的作用就是清空管道(強制將記憶體當中的數據輸出到文件中)。為什麼要清空管道呢:因為:如果沒有 flush() 可以會導致記憶體中一部分的數據並沒有全部輸出到硬碟當中,從而導致一部分的數據丟失。 - 註意:在Java中只要 類名是以
**"stream"**
結尾的都是位元組流,以**"Reader/Writer"**
結尾的都是字元流。
java.io包下需要掌握的流有 16個
文件專屬:
java.io.FileInputStream
java.io.FileOutputStream 位元組流無法讀取到: 文件中的空格的
java.io.FileReader
java.io.FileWriter 字元流可以讀取到:文件中的空格的
轉換流: (將位元組流轉換字元流)
java.io.InputStreamReader
java.io.OutputStreamWriter
緩衝流專屬:
java.io.BufferedReader
java.io.BufferedWriter
java.io.BufferedInputStream
java.io.BufferedOutputStream
java.io.BufferedOutputStream
數據流專屬:
java.io.DataInputStream
java.io.DataOutputStream
標準輸出流:
java.io.PrintWtiter
java.io.PrinStream
對象專屬流:
java.io.ObjectInputStream
java.io.ObjectOutputStream
2.2 字元流
2.2.1 java.io.FileReader 字元輸入流
關於字元輸入流的類都是繼承了:java.io.Writer(字元輸出流)/ java.io.Reader (字元輸入流) 來使用的,但是這個兩個類是抽象類,是無法 new 對象來使用的。所以我們就需要使用其實現的子類:對於文件字元輸入流比較常用的就是: java.io.FileReader 這個子類了。
字元流: 只能讀取文本文件,不能讀取其它格式的文件,文本文件不僅僅是 .txt
尾碼的文件,.c,.java,.c++
都是文本文件,註意 : word 不是文本文件,因為 word 中的文本字元是有一個規範格式設置的。不是純的文本文件。字元流操作字元,只能操作普通文本文件。最常見的文本文件:.txt,.java,.c,.cpp 等語言的源代碼。尤其註意.doc,excel,ppt這些不是文本文件。
在讀取文件時,必須保證該文件已存在,否則報異常。
其中繼承的 InputStreamReader 是個轉換流,繼承了 Reader 抽象類的。
FileReader的構造器
public FileReader(File file) throws FileNotFoundException; // 在給定從中讀取數據的 File 的情況下創建一個新 FileReader對象
public FileReader(String fileName) throws FileNotFoundException; // 根據文件的相對路徑名/絕對路徑創建一個新 FileReader對象
舉例:
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
public class FileReaderTest {
public static void main(String[] args) {
File file = new File("E:\\Java\\JavaRebuilt\\src\\blogs\\blog9\\hello.txt"); // 絕對路徑
try {
FileReader fileReader = new FileReader(file);
} catch (FileNotFoundException e) {
e.printStackTrace();
}
try {
FileReader fileReader2 = new FileReader("src/blogs/blog9/hello.txt"); // 相對路徑
} catch (FileNotFoundException e) {
e.printStackTrace();
}
}
}
如下是 FileReader 繼承 java.io.InputStreamReader** 繼承的方法**
- read() : 讀取單個字元。作為整數讀取的字元,範圍在 0 到 65535 之間 (0x00-0xffff)(2個位元組的Unicode碼),如果已到達流的末尾,則返回 -1。
public int read() throws IOException; // 讀取單個字元。
- int read(char[] cbuf) : 將字元讀入數組。如果已到達流的末尾,則返回 -1。否則返回本次讀取的字元數。
public int read(char[] cbuf) throws IOException; // 將文件中的數據讀取到char[] cbuf的字元數組當中,返回讀取到的個數。
- int read(char[] cbuf,int off,int len) : 將字元讀入數組的某一部分。存到數組cbuf中,從off處開始存儲,最多讀len個字 符。如果已到達流的末尾,則返回 -1。否則返回本次讀取的字元數。
public int read(char[] cbuf,int offset, int length) throws IOException; // 將字元讀入數組中的某一部分.
- public void close() throws IOException :關閉此輸入流並釋放與該流關聯的所有系統資源。
public void close() throws IOException; // 關閉此輸入流並釋放與該流關聯的所有系統資源。
需要明白:對於 read() 讀取文件內容的方式是,一個一個字元的讀取的,每調用一次 read()對於的文件中的游標就會往後移動一下。對於特殊的 read(char[ ] cbuf) 讀取的個數是 char[] 數組的長度,往後移動游標的位置也是 char[] 數組的長度。以及返回的是對於字元的編碼值。
設文件 file1.txt ,採用字元流的話是這樣讀的:
a中國bo張三
第一次讀: ‘a’字元
第二次讀: ‘中’字元
舉例:
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
public class FileReaderTest {
public static void main(String[] args) {
FileReader fileReader = null; // 相對路徑
try {
fileReader = new FileReader("src/blogs/blog9/hello.txt");
int read = fileReader.read(); // 返回的是編碼值
System.out.println(read);
read = fileReader.read();
System.out.println(read);
read = fileReader.read();
System.out.println(read);
read = fileReader.read();
System.out.println(read);
read = fileReader.read();
System.out.println(read);
read = fileReader.read();
System.out.println(read);
read = fileReader.read();
System.out.println(read);
} catch (IOException e) {
e.printStackTrace();
} finally {
// fileReader 防止 null引用
if(fileReader != null) {
try {
fileReader.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
舉例: 使用 while() 迴圈處理
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
public class FileReaderTest {
public static void main(String[] args) {
FileReader fileReader = null; // 相對路徑
try {
fileReader = new FileReader("src/blogs/blog9/hello.txt");
int len = 0;
// 當read()讀取到 文件末尾返回 -1,跳出迴圈
while((len = fileReader.read()) != -1) {
System.out.println(len);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
// fileReader 防止 null引用
if(fileReader != null) {
try {
fileReader.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
舉例: 使用 int read(char[] cbuf) : 將字元讀入數組。如果已到達流的末尾,則返回 -1。否則返回本次讀取的字元數
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
public class FileReaderTest {
public static void main(String[] args) {
FileReader fileReader = null; // 相對路徑
try {
fileReader = new FileReader("src/blogs/blog9/hello.txt");
int len = 0;
char [] chars = new char[4];
// read(chars) 一次性讀取數組長度個字元,返回讀取到的字元個數。到達文件末尾返回-1
while((len = fileReader.read(chars)) != -1) {
// 將char[] 數組轉換為字元串
System.out.println(new String(chars));
}
} catch (IOException e) {
e.printStackTrace();
} finally {
// fileReader 防止 null引用
if(fileReader != null) {
try {
fileReader.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
read(char[]) 讀取數據時的覆蓋效果的講解
舉例: 去除 read(char[] cduf) 的覆蓋效果,我們讀取多到了多少個字元,就 new String 轉換多少個字元
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
public class FileReaderTest {
public static void main(String[] args) {
FileReader fileReader = null; // 相對路徑
try {
fileReader = new FileReader("src/blogs/blog9/hello.txt");
int len = 0;
char [] chars = new char[4];
// read(chars) 一次性讀取數組長度個字元,返回讀取到的字元個數。到達文件末尾返回-1
while((len = fileReader.read(chars)) != -1) {
// 將char[] 數組轉換為字元串
// 這裡我們 讀取到了多少個字元,就將 chars數組中的前多少個轉換為字元串
System.out.println(new String(chars,0,len));
}
} catch (IOException e) {
e.printStackTrace();
} finally {
// fileReader 防止 null引用
if(fileReader != null) {
try {
fileReader.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
2.2.2 java.io.FileWriter 字元輸出流
於字元流輸出的類都是繼承了:java.io.Writer(字元輸出流)/ java.io.Reader (字元輸入流) 來使用的,但是這個兩個類是抽象類,是無法 new 對象來使用的。所以我們就需要使用其實現的子類:對於文件字元輸出流比較常用的就是: java.io.FileWriter 這個子類了。
其中繼承的 OutputStreamWriter 是個轉換流,繼承了 Writer 抽象類的。
OutputStreamWriter 的構造器:
public FileWriter(File file) throws IOException; // 根據給定的 File 對象構造一個 FileWriter 對象
public FileWriter(String fileName) throws IOException; // 根據給定的文件名構造一個 FileWriter 對象。
public FileWriter(File file,boolean append) throws IOException; // 根據給定的 File 對象構造一個 FileWriter 對象。如果第二個參數為 true,則將位元組寫入文件末尾處,而不是寫入文件開始處。
// file - 要寫入數據的 File 對象
// append - 如果為 true,則將位元組寫入文件末尾處,而不是寫入文件開始處 ,預設是 false,不寫的話也是 false
舉例:
package blogs.blog9;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
public class FileWriterTest {
public static void main(String[] args) {
File file = new File("E:\\Java\\JavaRebuilt\\src\\blogs\\blog9\\hello.txt"); // 絕對路徑
try {
FileWriter fileWriter = new FileWriter(file);
} catch (IOException e) {
e.printStackTrace();
}
try {
FileWriter fileWriter2 = new FileWriter("src/blogs/blog9/hello.txt"); // 相對路徑
} catch (IOException e) {
e.printStackTrace();
}
try {
FileWriter fileWriter3 = new FileWriter("src/blogs/blog9/hello.txt",true);
} catch (IOException e) {
e.printStackTrace();
}
}
}
如下是 FileWriter 繼承 java.io.OutputStreamWriter繼承的方法
- void write(int c) : 寫入單個字元。要寫入的字元包含在給定整數值的 16 個低位中,16 高位被忽略。 即寫入0 到 65535 之間的Unicode碼。
public void write(int c) throws IOException; // 寫入單個字元。
- void write(char[] cbuf) : 將字元數組的內容,寫入到對應的硬碟文件中去
public void write(char[] cbuf) throws IOException; // 將字元數組的內容,寫到文件中
- void write(char[] cbuf,int off,int len) : 寫入字元數組的某一部分。從off開始,寫入len個字元。
public void write(char[] cbuf,int off,int len) throws IOException // 寫入字元數組的某一部分。
- void write(String str) :將字元串的內容,寫入到對應的硬碟文件中去
public void write(Stirng str) throws IOException
- void write(String str,int off,int len) :寫入字元串的某一部分。從off開始,寫入len個結束
public void write(String str,int off,int len) throws IOException
- void flush() :刷新該流的緩衝,立即記憶體中的數據寫入預期目標硬碟文件中去。
public void flush() throws IOException; // 刷新該流的緩衝。
- public void close() :關閉此輸出流並釋放與該流關聯的所有系統資源
public void close() throws IOException; // 關閉此流,但要先刷新它。在關閉該流之後,再調用 write() 或 flush() 將導致拋出 IOException。關閉以前關閉的流無效。
註意: 如果寫入到的文件不存在,是會自動創建的。如果文件已經存在了,在創建FileWriter 對象時沒有設置為 true 的話,是會將原本文件中已經存在的內容覆蓋的,寫入新的內容。
舉例: 文件不存在,自動創建。
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
public class FileReaderTest {
public static void main(String[] args) {
FileReader fileReader = null; // 相對路徑
try {
fileReader = new FileReader("src/blogs/blog9/hello.txt");
int len = 0;
char [] chars = new char[4];
// read(chars) 一次性讀取數組長度個字元,返回讀取到的字元個數。到達文件末尾返回-1
while((len = fileReader.read(chars)) != -1) {
// 將char[] 數組轉換為字元串
// 這裡我們 讀取到了多少個字元,就將 chars數組中的前多少個轉換為字元串
System.out.println(new String(chars,0,len));
}
} catch (IOException e) {
e.printStackTrace();
} finally {
// fileReader 防止 null引用
if(fileReader != null) {
try {
fileReader.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
舉例: 文件已經存在,寫入的信息覆蓋原本文件的全部內容
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
public class FileWriterTest {
public static void main(String[] args) {
FileWriter fileWriter = null; // 相對路徑
try {
// 1. 創建輸出流對象: FileWriter
fileWriter = new FileWriter("src/blogs/blog9/hello2.txt");
// 2. 將記憶體當中的內容寫入到文件中
fileWriter.write("你好世界");
// 3. 刷新:將記憶體中沒有輸出到文件中的內容,強制全部寫入到文件中
fileWriter.flush();
} catch (IOException e) {
e.printStackTrace();
} finally {
// 防止 null 引用
if(fileWriter != null) {
// 4. 關閉IO資源
try {
fileWriter.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
舉例: 創建 **FileWriter ** 對象時,設置 true ,將記憶體當中的信息寫入到文件的末尾去,不會覆蓋原本文件中的內容
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
public class FileWriterTest {
public static void main(String[] args) {
FileWriter fileWriter = null; // 相對路徑
try {
// 1. 創建輸出流對象: FileWriter,並設置 true 將寫入的內容追加到文件的末尾中去
fileWriter = new FileWriter("src/blogs/blog9/hello2.txt",true);
// 2. 將記憶體當中的內容寫入到文件中
fileWriter.write("\n"); // 換行
fileWriter.write("Hello World");
// 3. 刷新:將記憶體中沒有輸出到文件中的內容,強制全部寫入到文件中
fileWriter.flush();
} catch (IOException e) {
e.printStackTrace();
} finally {
// 防止 null 引用
if(fileWriter != null) {
// 4. 關閉IO資源
try {
fileWriter.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
2.2.3 實例文本文件的拷貝
將同目錄中的 hello.txt 文件的內容拷貝到 同目錄中的 hello2.txt 中去
思路:
import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
public class FileWriterTest {
public static void main(String[] args) {
FileWriter descFile = null;
FileReader srcFile = null; // 註意文件尾碼
try {
// 1. 創建hello.txt 文件的字元輸入流對象,以及 hello2.txt文件的字元輸出流對象
descFile = new FileWriter("src/blogs/blog9/hello2.txt");
srcFile = new FileReader("src/blogs/blog9/hello.txt");
// 2. 一邊讀,一邊寫
int len = 0;
char[] chars = new char[10];
// 讀取hello.txt的數據信息
while((len = srcFile.read(chars)) != -1) {
// 將讀取到的內容寫入到hello2.txt文件中
descFile.write(chars,0,len);
}
// 3. 刷新:將記憶體中遺留沒有寫入到文件中的信息,全部強制寫入到文件中去
descFile.flush();
} catch (IOException e) {
e.printStackTrace();
} finally {
// 5. 關閉IO資源
// 分開 try,如果兩個一起try的話其中一個出現異常了,後面的一個IO就無法關閉了
if(srcFile != null) { // 防止 null引用
try {
srcFile.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if(descFile != null) { // 防止 null引用
try {
descFile.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
2.3 位元組流
2.3.1 FileInputStream 位元組輸入流
關於位元組輸入流的類都是繼承了:InputStream(位元組輸入流) 來使用的,但是個類是抽象類,是無法 new 對象來使用的。所以我們就需要使用其實現的子類:對於文件字元輸入流比較常用的就是: **java.io.FileInputStream ** 這個子類了。
位元組流: 可以操作任何的文件,因為位元組流讀取的是二進位信息,讀取1個位元組byte,等同於一次讀取8個二進位,這種流是萬能的,什麼類型的文件都可以讀取到,因為文件都是有二進位組成的。包括: 文本文件,圖片,聲音文件。再比如:比如:.mp3,.avi,.rmvb,mp4,.jpg,.doc,.ppt等文件。
<