所有知識體系文章,GitHub已收錄,歡迎Star!再次感謝,願你早日進入大廠!
GitHub位址: https://github.com/Ziphtracks/JavaLearningmanual
搜尋關注微信公衆号“碼出Offer”,送你學習福利資源!
目錄
一、流的概念
二、流的分類
三、位元組流(案例詳解)
四、編碼方式(編碼與解碼轉換案例)
五、字元流(案例詳解)
六、File類(案例詳解)
思維導圖參考:【十四】Java IO架構章節思維導圖
【習題與詳解】
1、利用IO輸入、輸出流來複制圖檔,你試過嗎?
2、(File類、遞歸)Java寫一個查找盤符内指定檔案的代碼,你可以試試!
3、IO流小練習
4、(位元組流, Buffered Reader,HashMap)讀入該檔案的基礎上,讓使用者輸入一個年份,輸出該年的世界杯冠軍。如果該年沒有舉辦世界杯
5、(File類、位元組流、橋轉換流、字元流)從指令行中讀入一個檔案名,判斷該檔案是否存在。如果該檔案存在,則在原檔案相同路徑下建立一個文 件名為“copy_原檔案名”的新檔案,該檔案内容為原檔案的拷貝
一、流的概念
什麼是流
- 概念: 記憶體與儲存設備之間傳輸資料的通道
- 比如: 水借助管道傳輸;資料借助流傳輸
【十四】Java IO架構(輕言巧辯IO流)目錄
二、流的分類
流的分類
- 按方向【重點】
- 輸入流: 将<儲存設備>中的内容讀入到<記憶體>中
- 輸出流: 将<記憶體>中的内容寫入到<儲存設備>中
【十四】Java IO架構(輕言巧辯IO流)目錄
- 按機關
- 位元組流: 以位元組為機關,可以讀寫所有資料
- 字元流: 以字元為機關,可以讀寫文本資料
- 按功能
- 節點流: 具有實際傳輸資料的讀寫功能
- 過濾流: 在節點流的基礎上增強功能
流 | 分類和功能 |
---|---|
ObjectInputStream | 位元組流、讀八種基本類型、讀對象、緩沖 |
ObjectOutputStream | 位元組流、寫八種基本類型、寫對象、緩沖 |
BufferedInputStream | 位元組流、緩沖 |
BufferedOutputStream | 位元組流、緩沖 |
DataInputStream | 位元組流、讀八種基本類型 |
DataOutputStream | 位元組流、寫八種基本類型 |
PrintWriter | 字元流、寫八種基本類型、字元串并換行、緩沖功能、寫對象 |
PrintStream | 位元組流、寫八種基本類型、字元串并換行、緩沖功能、寫對象 |
BufferedReader | 字元流、緩沖、讀入一行文本 |
三、位元組流
位元組流
- 位元組流的父類(抽象類)
- InputStream: 位元組輸入流
- public int read() {}
- public int read(byte[] b) {}
- public int read(byte[] b, int off, int len) {}
- OutputStream: 位元組輸出流
- public void write() {}
- public int write(byte[] b) {}
- public int write(byte[] b, int off, int len) {}
- InputStream: 位元組輸入流
位元組節點流
- FileOutputStream:
- public void write(byte[] b) //一次寫多個位元組,将b數組中所有位元組,寫入輸出流
import java.io.FileOutputStream;
import java.io.IOException;
/**
*1.輸出位元組流 OutputStream
*2.輸出位元組節點流 具有實際傳輸資料的功能 (檔案路徑,boolean append) true代表追加,不覆寫
*3.IOException異常是FileNotFoundException異常的父類,是以隻需要抛出父類異常即可
*特别注意:FileOutputStream是不會自動建立檔案夾以及多級檔案夾的,它隻會給你建立檔案,而你是需要提供路徑的,假如不提供路徑預設建立在根目錄下!
*/
public class TestOutputStream {
public static void main(String[] args) throws IOException {
/**
* 注意:路徑正确但是檔案(.txt)不存在,則會自動建立一個!
* 傳入的參數是絕對路徑,那你的電腦必須有路徑存在,否則報錯!
* Exception in thread "main" java.io.FileNotFoundException: D:\hello\temp.txt (系統找不到指定的路徑。)
*/
// FileOutputStream fileOutputStream = new FileOutputStream("D:\\hello\\temp.txt");
//預設建立在根目錄下
// FileOutputStream fileOutputStream = new FileOutputStream("world.txt");
//表示可以追加
FileOutputStream fileOutputStream = new FileOutputStream("D:\\hello\\temp.txt", true);
fileOutputStream.write(65);//此時會在檔案中出現AA
/**
* 注意:Files檔案是自己建立,text檔案可以自動生成!
* 傳入的參數是相對路徑與上一種形勢等價,相對在目前項目路徑下,尋找該路徑和檔案
* 如果沒有找到的話,它會在你項目的根目錄下建立temp.txt
*/
FileOutputStream fileOutputStream1 = new FileOutputStream("Files\\temp.txt");
fileOutputStream1.write(65);
fileOutputStream1.write('B');//直接寫入字元B
String s = "你好";
byte[] bytes = s.getBytes();//擷取位元組存入位元組數組中
System.out.println(s.length());//查找出兩個長度:2
System.out.println(bytes[0] + "\t" + bytes[1]);//查找出兩個位元組數:-28、-67
fileOutputStream1.write(bytes);//直接寫入bytes位元組數組,這時候在檔案中就可以看到:你好
byte[] bytes1 = new byte[] {65, 66, 67, 68, 'E'};//如果我們想寫入多個位元組,就可以用數組去寫入了
fileOutputStream1.write(bytes1);//直接寫入整個位元組數組的所有位元組
/**
* 第一個參數:寫入的位元組數組
* 第二個參數:起始下标,從哪個下标的位元組開始寫入
* 第三個參數:長度,從起始下标開始寫入幾個下标長度的位元組
*/
fileOutputStream1.write(bytes1, 1, 3);//我這裡寫入的是66、67、68,ASCII編碼對應的BCD
}
}
我把我寫出來的檔案以截圖的形式貼出來!
- FileInputStream:
- public int read(byte[] b) //從流中讀取多個位元組,将讀到的内容存入b數組,傳回實際讀到的位元組數;如果達到檔案的尾部,則傳回-1
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
public class TestFileInputStream {
public static void main(String[] args) throws IOException {
FileOutputStream fileOutputStream = new FileOutputStream("Files\\target.txt");
//寫入一個A
fileOutputStream.write('A');
FileInputStream fileInputStream = new FileInputStream("Files\\target.txt");
//讀出一個A,如果不做強轉的話,會出現列印的位元組數65
System.out.println((char) fileInputStream.read());//A
//這裡我們在檔案中多寫入幾個值
fileOutputStream.write('B');
fileOutputStream.write('C');
fileOutputStream.write('D');
fileOutputStream.write('E');
/*
//用普通的方法讀隻能一個一個的讀,這裡寫一個死循環讀一下試試
while (true) {
int n = fileInputStream.read();//一次讀一個位元組
if (n == -1) {//在有效位元組讀完後,讀到無效位元組或者空會傳回-1,也就是檔案的末尾
break;//停止讀的操作
}
System.out.print((char) n + " ");//B C D E (因為上邊讀出了第一個位元組A下一次從第二個開始讀)
}
*/
//如果我們用數組存儲位元組來讀檔案怎麼樣呢?
byte[] bytes = new byte[4];//該數組作為讀時的存儲,數組長度為4
while (true) {
int count = fileInputStream.read(bytes);//每次讀出數組中有效位元組數
if (count == -1) {//停止讀的條件
break;
}
/**
* 讀多少個列印多少個
* 假如讀數組長度的haul會出現小問題
* 我們從檔案中讀出是按數組長度一組組的讀出來的
* 假如最後一個位元組是數組長度中的第一個,而後面的三個就會被上面一個數組列印出來的結果的緩沖區影響
* 進而多讀出了3個上一次讀出數組的緩沖區數字,不信你可以去試試,畢竟實踐出真知嘛!
*/
for (int i = 0; i < count; i++) {//讀有效個數,也就是從count
System.out.print((char) bytes[i] + " ");//B C D E(因為上一個讀操作會對這一次的讀操作産生影響,是以我就把上一個操作注掉了!)
}
}
}
}
位元組過濾流
- 緩沖區: BufferedOutputStream/BufferInputStream
- 提高IO效率,減少通路磁盤的次數
- 資料存儲在緩沖區中,flush是将緩存區的内容寫入檔案中,也可以直接close
【十四】Java IO架構(輕言巧辯IO流)目錄
import java.io.*;
public class TestBufferedOutput {
public static void main(String[] args) throws IOException {
//有參構造需要一個位元組流輸出節點流
//先建立一個輸出節點流
FileOutputStream fileOutputStream = new FileOutputStream("Files\\buff.txt");
//增強節點流(類似用增強的工具做了包裝)
BufferedOutputStream bufferedOutputStream = new BufferedOutputStream(fileOutputStream);
//過濾流的write方法,是先寫入到緩沖區數組裡!
bufferedOutputStream.write('A');
bufferedOutputStream.write('B');
bufferedOutputStream.write('C');
bufferedOutputStream.write('D');
bufferedOutputStream.write('E');
//重新整理緩沖區(将緩沖區的數組,一次性寫入到檔案中,并清空目前緩沖區)
//如果不重新整理的話,我們的檔案中将會是空的!
bufferedOutputStream.flush();
bufferedOutputStream.write('F');
bufferedOutputStream.write('G');
bufferedOutputStream.flush();
//級聯的執行了flush()釋放資源的同時
//将緩沖區的資料重新整理了,也就是将緩沖區的資料一次性寫入到檔案裡
//就省略使用了flush()方法去重新整理緩沖區了(更友善)
bufferedOutputStream.close();
FileInputStream fileInputStream = new FileInputStream("Files\\buff.txt");
//增強節點流(方法和上限的輸出流一緻,這個是輸入流,這裡我就不再寫一遍了)
BufferedInputStream bufferedInputStream = new BufferedInputStream(fileInputStream);
byte[] bytes = new byte[100];
fileInputStream.read(bytes);
for (int i = 0; i < bytes.length; i++) {
System.out.print((char) bytes[i] + " ");//A B C D E F G(其實還多列印了很多多餘長度的空格)
}
}
}
位元組過濾流
- 對象流: ObjectOutputStream/ObjectInputStream
- 增強了緩沖區功能
- 增強了讀寫8種基本資料類型和字元串功能
- 增強了讀寫對象的功能:
- readObject() 從流中讀取一個對象
- writeObject(Object obj) 從流中寫入一個對象
注意:使用流傳輸對象的過程稱為序列化、反序列化
對象序列化
- 對象序列化的細節:
- 必須實作Serializable接口
- 必須保證其所有屬性均可序列化
- transient修飾為臨時屬性,不參會序列化
- 讀取到檔案尾部的标志: java.io.EOFException
import java.io.*;
//注意:static 依舊是屬于類的,會影響序列化(因為static是全局公用一個,随着修改而修改,則讀的時候會出現全都是最後一個對象的資訊!)
public class TestObjectStream {
public static void main(String[] args) throws IOException, ClassNotFoundException {
OutputStream outputStream = new FileOutputStream("Files\\obj.txt");
ObjectOutputStream objectOutputStream = new ObjectOutputStream(outputStream);
Student student = new Student("Ziph", 18, "男", 100D, new Address("河北", "100000"));
Student student1 = new Student("Join", 22, "男", 88D, new Address("上海", "100000"));
objectOutputStream.writeObject(student);//寫入對象資訊
objectOutputStream.writeObject(student1);
objectOutputStream.flush();//重新整理緩沖區
InputStream inputStream = new FileInputStream("Files\\obj.txt");
ObjectInputStream objectInputStream = new ObjectInputStream(inputStream);
// Object object = objectInputStream.readObject();//讀寫入檔案中的對象資訊
// Student s = (Student) object;//将Object對象強轉為學生對象
//注意:沒有序列化的位址資訊為null
//這是讀一個Student對象資訊
// System.out.println(object);//讀出:Student{name='Ziph', age=18, sex='男', score=100.0, address=null}
//利用循環讀所有Student對象資訊
while (true) {
try {
Object obj = objectInputStream.readObject();
System.out.println(obj.toString());
} catch (Exception e) {
//到達檔案末尾,獲得EOFException異常就停止
break;//自定義處理異常
}
}
/**
*列印結果為:Student{name='Ziph', age=18, sex='男', score=100.0, address=null}
*Student{name='Join', age=22, sex='男', score=88.0, address=null}
*/
}
}
//對象自身要支援序列化
//屬性也要支援序列化
class Student implements Serializable {
String name; //姓名
Integer age; //年齡
String sex; //性别
Double score; //分數
//修飾的屬性不參與序列化
//注意:數組,基本資料類型可以序列化
//引用資料類型的數組,數組中的類型要支援序列化
transient Address address; //位址資訊
public Student(String name, Integer age, String sex, Double score, Address address) {
this.name = name;
this.age = age;
this.sex = sex;
this.score = score;
this.address = address;
}
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
", sex='" + sex + '\'' +
", score=" + score +
", address=" + address +
'}';
}
}
class Address implements Serializable {
String position;
String zipCode;
public Address(String position, String zipCode) {
this.position = position;
this.zipCode = zipCode;
}
public String toString() {
return "Address{" +
"position='" + position + '\'' +
", zipCode='" + zipCode + '\'' +
'}';
}
}
四、編碼方式
字元編碼 | 字元編碼介紹 |
---|---|
ISO-8856-1 | 收錄ASCII外,還包括西歐、希臘語、泰語、阿拉伯語、希伯來語對應的文字元号 |
UTF-8 | 針對Unicode的可變長度字元編碼 |
GB2312 | 簡體中文 |
GBK | 簡體中文、擴充 |
BIG5 | 台灣,繁體中文 |
注意: | 當編碼方式和解碼方式不一緻時,會出現亂碼 |
編碼和解碼轉換
import java.io.UnsupportedEncodingException;
public class TestEncoding {
public static void main(String[] args) throws UnsupportedEncodingException {
/**
* 注意:假如傳入字元集參數的位置,你不傳入的話,預設你的開發環境編碼!
*/
String str = "你好,先生!";//文本内容
//編碼 文本-->二進制
byte[] bytes = str.getBytes("UTF-8");//獲得字元串的二進制表現形式
for (int i = 0; i < bytes.length; i++) {
System.out.print(bytes[i] + " ");//-28 -67 -96 -27 -91 -67 -17 -68 -116 -27 -123 -120 -25 -108 -97 -17 -68 -127
}
//解碼 二進制-->文本
String str1 = new String(bytes, "BIG5");
System.out.println(str1);//雿�憟踝�����嚗�
//如果想再把它轉回去那是不可能的看一下操作和列印結果
byte[] bytes1 = str1.getBytes("BIG5");
String str2 = new String(bytes1, "UTF-8");
System.out.println(str2);//�?好�?????�?(?的話就是在UTF-8的編碼中沒有對應BIG5的編碼)
//正确做法:需要編碼和解碼一緻
String s = "哈喽!Ziph!";
//編碼 字元集是一緻的
byte[] b = s.getBytes("UTF-8");
//解碼 字元集是一緻的
String ss = new String(b, "UTF-8");
System.out.println(ss);//哈喽!Ziph!
}
}
五、字元流
字元流
- 字元流的父類(抽象類)
- Reader: 字元輸入流
- public int read() {}
- public int read(char c) {}
- public int read(char c, int off, int len) {}
- Writer: 字元輸出流
- public void write(int n) {}
- public void write(String str) {}
- public void write(char[] c) {}
- Reader: 字元輸入流
字元節點流
- FileWriter:
- public void write(String str) //一次寫多個字元,将b數組的所有字元,寫入輸出流
- FileReader:
- public int read(char[] c) //從流中讀取多個字元,将讀到記憶體存入c數組,傳回實際讀到的字元數;如果達到檔案的尾部,則傳回-1
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
public class TestWriter {
public static void main(String[] args) throws IOException {
FileWriter fileWriter = new FileWriter("Files\\chars.txt");
fileWriter.write("哈喽!Ziph!");
char[] chars = new char[] {'見', '到', '你', '很', '高', '興'};
fileWriter.write(chars);
fileWriter.flush();//檔案中寫入的結果:哈喽!Ziph!見到你很高興
FileReader fileReader = new FileReader("Files\\chars.txt");
//數組列印
char[] cache = new char[3];//每次讀3個字元
while (true) {
int n = fileReader.read(cache);
if (n == -1) {
break;
}
for (int i = 0; i < n; i++) {
System.out.print(cache[i]);//列印結果即是:三個字元一換行的形式列印到結尾
}
System.out.println();
}
//普通列印(如果讀的話兩個選一個,另一個得注釋掉!)
while (true) {
int n1 = fileReader.read();
if (n1 == -1) {
break;
}
for (int i = 0; i < n1; i++) {
System.out.print(n1);
}
}
}
}
字元過濾流
- 緩沖流: BufferedWriter/BufferedReader
- 支援輸入換行符
- 可一次寫一行、讀一行
- PrintWrite:
- 封裝了print() / println() 方法,支援寫入後換行
import java.io.*;
public class TestBuffered {
public static void main(String[] args) throws IOException {
FileWriter fileWriter = new FileWriter("Files\\buf.txt");
/**
* 注意:寫入文本時,輸入\n換行,在原生的記事本中是不識别此操作的!(進階記事本可以)
* 由此我們需要借助特殊的換行處理進行換行
* 如下:
*/
//newLine操作的換行形式,不友善
/*
BufferedWriter bufferedWriter = new BufferedWriter(fileWriter);//過濾流
// bufferedWriter.write("Hello!\n");
bufferedWriter.write("Hello!");
bufferedWriter.newLine();//根據平台提供換行符
bufferedWriter.write("World!");
bufferedWriter.close();//檔案中寫入的是Hello!(換行)World!
*/
//這種方式更友善(常用)
PrintWriter printWriter = new PrintWriter(fileWriter);
printWriter.println("哈哈哈!");
printWriter.println("嘻嘻嘻!");
printWriter.close();//此時文本寫入的内容即:哈哈哈!(換行)嘻嘻嘻!
//字元輸入過濾流讀操作
FileReader fileReader = new FileReader("Files\\buf.txt");
BufferedReader bufferedReader = new BufferedReader(fileReader);
// BufferedReader.readLine(); //檔案末尾傳回的是null
while (true) {
String str = bufferedReader.readLine();
if (str == null) {
break;
}
System.out.println(str);//控制台列印結果即是:哈哈哈!(換行)嘻嘻嘻!
}
}
}
字元節點流
- 橋轉換流: InputStreamReader/OutputStreamWriter
- 可将位元組流轉換為字元流
- 可設定字元的編碼格式
使用步驟
- 建立節點流
- [建立過濾流 設定字元編碼集]
- 封裝過濾流
- 讀寫資料
- 關閉流
import java.io.*;
/**
* 注意:
* 編碼和解碼的編碼類型必須保持一緻!
* 看似很複雜,實際就是增加了功能,多了兩層包裝而已,關閉流的時候關閉外層即可!
*/
public class TestConvertStream {
public static void main(String[] args) throws IOException {
//1.位元組輸出流
OutputStream outputStream = new FileOutputStream("Files\\convert.txt");
//2.增加編碼的類型功能——轉換為字元輸出流
OutputStreamWriter outputStreamWriter = new OutputStreamWriter(outputStream, "UTF-8");
//3.增加換行功能——包裝字元過濾流
PrintWriter printWriter = new PrintWriter(outputStreamWriter);
//4.在檔案内寫入内容
printWriter.println("hello!你好呀!");
printWriter.println("見到你很高興");
printWriter.println("最近記得戴口罩!");
//5.重新整理并關閉流
printWriter.close();
/**
* 檔案寫入後的内容:
* hello!你好呀!
* 見到你很高興
* 最近記得戴口罩!
*/
//1.位元組輸入流
InputStream inputStream = new FileInputStream("Files\\convert.txt");
//2.解碼——轉換為字元輸入流
InputStreamReader inputStreamReader = new InputStreamReader(inputStream, "UTF-8");
//3.包裝字元過濾流
BufferedReader bufferedReader = new BufferedReader(inputStreamReader);
//4.列印檔案内資訊
while (true) {
String s = bufferedReader.readLine();
if (s == null) {
break;
}
System.out.println(s);
}
//5.重新整理并關閉流
bufferedReader.close();
/**
* 列印結果:
* hello!你好呀!
* 見到你很高興
* 最近記得戴口罩!
*/
}
}
六、File類
File類
- 概念: 代表實體盤符中的一個檔案或者檔案夾
- 方法:
- createNewFile() //建立一個新檔案
- Mkdir() //建立一個新目錄
- Delete() //删除檔案或空目錄
- Exists() //判斷File對象代表的檔案類型檔案是否存在
- getName() //取得名字
- getParent() //擷取檔案/目錄所在的目錄
- isDirectory() //是否是目錄
- isFile() //是否是檔案
- length() //擷取檔案的長度
- listFiles() //列出目錄中的所有内容
- renameTo() //修改檔案名為
import java.io.File;
import java.io.IOException;
/**
* 因為有些方法之間互相影響,是以就沒有注釋掉!
* 自己測試的時候記得注意互相影響的方法!
*/
public class TestFiles {
public static void main(String[] args) throws IOException {
File file = new File("Files\\test\\target.txt");
// File file = new File("Files\\test\\newFile");
System.out.println(file.canExecute());//所有可以打開的檔案或檔案夾
System.out.println(file.canWrite());//能不能修改檔案
System.out.println(file.canRead());//能不能執行檔案
System.out.println(file.createNewFile());//檔案不存在則建立一個檔案
System.out.println(file.delete());//如果檔案存在,則删除,傳回true
file.deleteOnExit();//JVM終止時,執行删除删除檔案
System.out.println(file.exists());//測試此抽象路徑名表示的檔案或目錄是否存在
System.out.println(file.getAbsolutePath());//獲得絕對路徑
System.out.println(file.getPath());//獲得相對路徑
System.out.println(file.getName());//獲得檔案名(檔案名.字尾)
System.out.println(file.getFreeSpace() / 1024 / 1024 / 1024);//獲得磁盤空閑空間容量
System.out.println(file.getTotalSpace() / 1024 / 1024 / 1024);//獲得磁盤總空間容量
System.out.println(file.getParent());//指定檔案的上一級目錄
System.out.println(file.isDirectory());//判斷是否為檔案夾
System.out.println(file.isFile());//判斷是否為檔案
System.out.println(file.isHidden());//判斷是否為隐藏檔案
System.out.println((System.currentTimeMillis() - file.lastModified()) / 1000 / 60);//擷取檔案最後一次修改時間
System.out.println(file.length());//檔案内容的位元組
file.mkdirs();//建立一個新目錄
}
}
FileFilter接口
- public interface FileFilter
- boolean accept(File pathname)
- 當調用File類中listFiles()方法時,支援傳入FileFilter接口接口實作類,對擷取檔案進行過濾,隻有滿足條件的才可出現在listFiles() 的傳回值中
import java.io.File;
import java.io.FileFilter;
//
public class TestListFile {
public static void main(String[] args) {
File file = new File("D:\\Code");
// String[] fileNames = file.list();//擷取檔案夾中的所有檔案(包括檔案夾的名字)
// for (String name : fileNames) {
// System.out.println(name);
// }
File[] files = file.listFiles(new MyFilter());//擷取檔案夾中的所有檔案(包括檔案夾)的對象
System.out.println(files.length);
for (File f : files) {
System.out.println(f.getName());
}
}
}
class MyFilter implements FileFilter {
public boolean accept(File file) {
if (file.isFile()) {
//找到該檔案所有.class字尾的檔案,過濾其他檔案
if (file.getName().endsWith(".class")) {//true儲存、false就過濾檔案
return true;//儲存檔案
}
}
return false;//過濾
}
}
所有知識體系文章,GitHub已收錄,歡迎Star!再次感謝,願你早日進入大廠!
GitHub位址: https://github.com/Ziphtracks/JavaLearningmanual
搜尋關注微信公衆号“碼出Offer”,送你學習福利資源!