IO流學習
流的分類
- 根據操作單位不同分為位元組流和字符流
- 根據流向不同分為輸入流和輸出流
- 根據角色不同分為節點流和處理流
以下四個類是IO流中最基礎的類,都是抽象類,其他流都是繼承他們的,
| 分類 | 位元組流 | 字符流 |
|---|---|---|
| 輸入流 | InputStream | Reader |
| 輸出流 | OutputStream | Writer |
位元組流:操作的物件是位元組陣列
字符流:操作的物件是字符陣列
節點流:作用在檔案上的流,直接對檔案進行讀寫操作
處理流:作用在節點流上的流,可以加速流的傳輸
輸入流和輸入流是相對的,主要看你站在那個角度來看待,如果站在程式的角度,讀入資料較輸入流,寫出資料較輸出流,
IO流體系結構
| 分類 | 位元組輸入流 | 位元組輸出流 | 字符輸入流 | 字符輸出流 |
|---|---|---|---|---|
| 抽象基類 | InputStream | OutputStream | Reader | Writer |
| 訪問檔案 | FileInputStream | FileOutputStream | FileReader | FileWriter |
| 訪問陣列 | ByteArrayInputStream | ByteArrayOutputStream | CharArrayReader | CharArayWriter |
| 訪問管道 | PipedInputStream | PipedOutputStream | PipedReader | PipedWriter |
| 訪問字串 | StringReader | StringWriter | ||
| 緩沖流 | BufferedInputStream | BufferedOutputStream | BufferedReader | BufferedWriter |
| 轉換流 | InputStreamReader | OutputStreamWriter | ||
| 物件流 | ObjectInputStream | ObjectOutputStream | ||
| 過濾流 | FilterInputStream | FileOutputStream | FilterReader | FileWriter |
| 列印流 | PrintStream | PrintWriter | ||
| 推回輸入流 | PushbackInputStream | PushbackReader | ||
| 特殊流 | DataInputStream | DataOutputStream |
訪問檔案的流都是節點流,其他除了抽象基類外都是處理流
File類
想要進行IO操作,肯定離不來檔案,所以學習IO流的前提就是要學會File類的操作,
File類的介紹
File是用來操作檔案或目錄的,但是不能對檔案的內容進行操作,主要是用于檔案和目錄的創建、檔案的洗掉、檔案的查找,
File類構造方法
File類表示的是硬碟中實際存在的目錄或檔案,可以通過以下三種方式創建一個File類的物件,
File(String pathname)//通過將給定的路徑名字串轉換為抽象路徑名來創建新的 File實體, 常用
File(File parent, String child) //從父抽象路徑名和子路徑名字串創建新的 File實體,常用
File(String parent, String child) //從父路徑名字串和子路徑名字串創建新的 File實體, 常用
File(URI uri) //通過將給定的 file: URI轉換為抽象路徑名來創建新的 File實體,
代碼舉例:
@Test
public void test(){
//根據路徑創建
String path = "d:\\file.txt";
File file = new File(path);
//根據父檔案和子類路徑創建
File parentFile = new File( "d:\\io");
File file1 = new File(parentFile,"file1.txt");
//根據父路徑和子路徑創建
String parentPath = "d:\\io";
String childPath = "file2.txt";
File file2 = new File(parentPath,childPath);
}
注意:在Windows中路徑分割使用‘\’,在Linux中使用‘/’,為了方便區分在File類中有一個separator 屬性,可以用來代替分隔符,
在Java中 \ 是用來轉義的,所以這里需要使用兩個 \ 字符
路徑問題
相對路徑:相對于當前的專案路徑,不帶盤符,如: "hello.txt"
絕對路徑:相對于硬碟的物理路徑,是一個帶有盤符的完整路徑,如:"d:\hi.txt"
如代碼所示,目前檔案只是存在于記憶體中,還沒有在硬碟上創建實際的檔案,需要使用一些方法來創建,下面介紹File類中的常用方法,
File類常用方法
1、獲取的方法
public String getAbsolutePath() //絕對路徑名字串,
public String getPath() //將此抽象路徑名轉換為路徑名字串,
public String getName() //回傳由此抽象路徑名表示的檔案或目錄的名稱,
public String getParent() //回傳上層檔案目錄路徑,沒有則回傳null,
public long length() //回傳由此抽象路徑名表示的檔案的長度, 長度指的是檔案的位元組數,不能獲取目錄的長度,
public String[] list() //獲取指定目錄下的所有檔案或檔案目錄的名稱陣列
public File[] listFiles() //獲取指定目錄下的所有檔案或檔案目錄的File陣列
@Test
public void test3(){
File file = new File("f:\\io\\hello.txt");
System.out.println("絕對路徑:"+file.getAbsolutePath());
System.out.println("路徑:"+file.getPath());
System.out.println("名字:"+file.getName());
System.out.println("上層路徑:"+file.getParent());
System.out.println("長度"+file.length());
System.out.println("檔案目錄:"+file.list());
System.out.println("檔案目錄"+file.listFiles());
}
/*
運行結果:
絕對路徑:f:\io\hello.txt
路徑:f:\io\hello.txt
名字:hello.txt
上層路徑:f:\io
長度0 -->因為檔案中沒有內容,所以是0
檔案目錄:null --> 因為當前是一個檔案,所以沒有目錄,如果是一個目錄就會有
檔案目錄null --> 因為當前是一個檔案,所以沒有目錄,如果是一個目錄就會有
*/
進行判斷的方法
public boolean exists()//判斷檔案或目錄是否存在
public boolean isDirectory()//判斷是否是目錄
public boolean isFile()//判斷是否是檔案
@Test
public void test5(){
File file = new File("hello.txt");//該檔案沒建過
System.out.println("檔案是否存在"+file.exists());//false
System.out.println("是否是目錄"+file.isDirectory());//false
System.out.println("是否是檔案"+file.isFile());//false,因為連檔案都沒有,所以是false
File file1 = new File("hello.txt");//該檔案我已經創建過了
System.out.println("檔案是否存在"+file1.exists());//true
System.out.println("是否是目錄"+file1.isDirectory());//false
System.out.println("是否是檔案"+file1.isFile());//true
}
進行創建的方法
public boolean createNewFile() //如果檔案不存在則創建一個新的空檔案回傳true,如果存在,不創建,回傳false;
public boolean mkdir()//創建檔案目錄,如果檔案目錄存在,不創建,如果檔案的上層目錄不存在,也不創建;
public boolean mkdirs()//創建檔案目錄,如果上層檔案目錄不存在,也一起創建
@Test
public void test4() throws IOException {
File file = new File("test.txt");
System.out.println(file.createNewFile());//true
File file1 = new File("f:\\test\\hello");
file1.mkdir();//test和hello目錄都不存在,不會創建
File file2 = new File("f:\\test\\hello\\java");
file2.mkdirs();//test和hello目錄都不存在,會全部創建
}
進行洗掉的方法
public boolean delete()//洗掉檔案或目錄
File類練習
列印指定檔案目錄下的所有檔案-遞回實作
//遞回列印檔案
@Test
public void test6(){
File file = new File("f:\\io");
printFile(file);
}
public void printFile(File file){
File[] files = file.listFiles();
for (File f : files) {
if(f.isDirectory()) printFile(f);
System.out.println(f.getName());
}
}
字符流
純文本檔案都用字符流來處理
字符輸入流
FileReader
1、構造方法
FileReader(File file) //根據file創建一個物件
FileReader(String fileName)//根據檔案目錄創建一個物件
2、常用方法
public int read()//從檔案中讀取一個字符,如果檔案中沒有字符了回傳-1
public int read(char[] cbuf)//從檔案中讀取多個字符,如果檔案中沒有字符了回傳-1,如果有回傳讀了多少個字符
public void close();//關閉流
使用FileReader讀取hello.txt中的內容
hello.txt

@Test
public void test1() {
FileReader reader = null;
try {
File file = new File("hello.txt");
reader = new FileReader(file);
char[] cbuf = new char[5];
int len = 0;
// 方式一,每次讀一個字符
while((len = reader.read()) != -1){//每次讀一個字符
System.out.print((char)len);
}
/*方式二,每次讀多個字符
while ((len = reader.read(cbuf)) != -1){
for (int i = 0; i < len; i++) {//這里不能使用cbuf.length;因為在最后可能字符不夠5個,所以使用len
System.out.print(cbuf[i]);
}
}
*/
} catch (IOException e) {
e.printStackTrace();
} finally {
if(reader != null) {
try {
reader.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
/*
運行結果:
hellojava
*/
使用IO流都有可能會出現例外,所以在這里需要使用try\catch\finally來處理例外
字符輸出流
FileWriter
1、構造方法
FileWriter(File file) //根據file創建一個物件
FileWriter(File file, boolean append) // 根據file創建一個物件,并根據append判斷在多次寫入資料是否覆寫,默認false,選擇覆寫
FileWriter(String fileName) // 根據字串創建一個物件
FileWriter(String fileName, boolean append) //同上,true表示不覆寫,false表示覆寫,默認false
2、常用方法
public void wirte(int c)//寫入一個字符
public void wirte(char[] cbuf)//寫入多個字符
public void wirte(char[] cbuf, int off, int len)//寫入字符的長度由len決定,off表示從那開始,
public void wirte(String str)//寫入字串
public void wirte(String str, int off, int len)//寫入部分字串,有off和len決定,off表示開始的下標,len表示長度
public void flush()//重繪快取區,如果沒有關閉流則資料不會寫入檔案
public void close()//關閉流,關閉流之后會自動重繪快取
使用FileWriter向hello.txt中寫入資料
@Test
public void test8(){
FileWriter fw = null;
try {
File file = new File("hello.txt");
fw = new FileWriter(file);
String str = "i love java";
fw.write(str);
} catch (IOException e) {
e.printStackTrace();
} finally {
if(fw != null) {
try {
fw.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
結果:

注意:字符流不能處理非文本的資料
位元組流
非純文本檔案使用位元組流來處理
位元組輸入流
FileInputStream
1、構造方法
FileInputStream(File file)//根據file創建一個位元組輸入流物件
FileInputStream(String name)//根據檔案路徑創建一個位元組輸入流物件
2、常用方法
public int read()//從檔案中讀取一個位元組,如果檔案中沒有位元組了回傳-1
public int read(b[] b)//從檔案中讀取多個位元組,如果檔案中沒有位元組了回傳-1,如果有回傳讀了多少個位元組
public void close();//關閉流
使用FileInputStream讀取hello.txt中的內容
@Test
public void test9(){
FileInputStream fis = null;
try {
File file = new File("hello.txt");
fis = new FileInputStream(file);
int len;
while((len = fis.read()) != -1){//每次讀一個位元組
System.out.print((char)len);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
if(fis != null){
try {
fis.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
位元組輸出流
FileOutputStream
1、構造方法
FileOutputStream(File file)//根據file創建一個位元組輸出流物件
FileOutputStream(File file, boolean append)//通過append判斷是否覆寫,默認為false,覆寫,true為不覆寫
FileOutputStream(String name) //根據檔案路徑創建一個位元組輸出流物件
FileOutputStream(String name, boolean append)//同上
2、常用方法
public void wirte(int c)//寫入一個位元組
public void wirte(byte[] b)//寫入多個位元組
public void wirte([] b, int off, int len)//寫入位元組的長度由len決定,off表示從那開始,
public void close()//關閉流,
使用FileOutputStream向hello.txt中寫入資料
@Test
public void test10(){
FileOutputStream fos = null;
try {
fos = new FileOutputStream(new File("hello.txt"));
String str = "hellojava";
fos.write(str.getBytes());
} catch (IOException e) {
e.printStackTrace();
} finally {
if(fos != null){
try {
fos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
運行結果:

因為我都選擇默認不append,所以每次都是將之前的覆寫掉
緩沖流
緩沖流的主要目的就是提高之前的流的讀取或寫入的速度,
緩沖流的使用方法和字符流或位元組流一樣,這里就介紹一下如何宣告一個緩沖流,
字符緩沖流
1、構造方法
BufferedReader(Reader in)
BufferedWriter(Writer out)
注意:構造方法的引數是一個抽象類,在傳參的時候應該傳該抽象類的實作類
使用
BufferedReader br = new BufferedReader(new FileReader(new File("hello.txt")));
BufferedWriter bw = new BufferedWriter(new FileWriter(new File("hello.txt")));
2、特有方法
public String readLine()//讀一行
public void newLine()//換行
位元組緩沖流
1、構造方法
BufferedInputStream(InputStream in)
BufferedOutputStream(OutputStream out)
注意:構造方法的引數是一個抽象類,在傳參的時候應該傳該抽象類的實作類
使用
BuffededInputStream bis = new BufferedInputStream(new FileInputStream("hello.txt"));
BufferedOutputStream bos = new BufferedOutputStream(new FileInputStream("hello.txt"));
轉換流
轉換流就是將位元組流轉換為字符流或將字符流轉換為位元組流,
InputStreamReader(將位元組流轉換為字符流)
1、構造方法
InputStreamReader(InputStream in) //創建一個默認字符集的轉換流
InputStreamReader(InputStream in, String charsetName) //創建一個指定字符集的轉換流
2、使用
@Test
public void test11(){
InputStreamReader isr = null;
InputStreamReader isr1 = null;
try {
isr = new InputStreamReader(new FileInputStream("hello.txt"));//默認字符集
isr1 = new InputStreamReader(new FileInputStream("hello.txt"),"GBK");//默認GBK字符集,會出現亂碼
int len = 0;
while((len = isr.read()) != -1){
System.out.print((char)len);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
if(isr != null) {
try {
isr.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
/*
運行結果:
hellojava
*/
OutputStreamWriter(將字符流轉換為位元組流)
1、構造方法
OutputStreamWriter(OutputStream out)//創建一個默認字符集的轉換流
OutputStreamWriter(OutputStream out, String charsetName) //創建一個指定字符集的轉換流
2、使用
@Test
public void test12(){
OutputStreamWriter osw = null;
try {
osw = new OutputStreamWriter(new FileOutputStream("hello.txt",true));
String str = "hellojava";
osw.write(str);
} catch (IOException e) {
e.printStackTrace();
} finally {
if(osw != null) {
try {
osw.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
運行結果:

序列化流
序列化流分為:序列化和反序列化
序列化:就是將自己寫的一個物件持久化到硬碟上,
反序列化:將序列化到硬碟上的物件取出來
要求:序列化的類必須實作Serializable介面,而且這個類的屬性也必須是可序列化的,8個基本型別是可序列化的,
序列化的類需要提供一個常量,用來表示序列版本號
如果沒有這個版本號,會出錯!!出什么錯呢,比如你先序列化了一個物件到一個檔案中,然后你對序列化的類進行了修改,然后從檔案中吧物件反序列化出來就會出錯,
因為在你實作了Serializable介面的時候,你當前類會有一個默認的序列版本號,你修改類的時候,你的序列版本號就有可能會修改,然后反序列化的時候就會找不到物件,
重點:被static和transient修飾的屬性是不會被序列化的,
序列化-ObjectOutputStream
1、構造方法
ObjectOutputStream(OutputStream out) //引數是一個抽象類,需要傳一個它的子類
2、使用
@Test
public void test13(){
ObjectOutputStream oos = null;
try {
oos = new ObjectOutputStream(new FileOutputStream("hello.txt"));
oos.writeObject(new User("Tom",3));
} catch (IOException e) {
e.printStackTrace();
} finally {
if(oos != null) {
try {
oos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
運行結果:

報錯了,為什么呢?因為User類沒有實作Serializable介面,當實作了介面后就能正常的序列化了,
ObjectInputStream-反序列化
1、構造方法
ObjectInputStream(InputStream in)
2、使用
@Test
public void test14(){
ObjectInputStream ois = null;
try {
ois = new ObjectInputStream(new FileInputStream("hello.txt"));
User user = (User) ois.readObject();
System.out.println(user);
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
} finally {
if(ois != null){
try {
ois.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
運行結果:

反序列化后,得到了剛才序列化的內容,
寫在最后
花了一天時間,總算是自己把IO的部分總結了一下,怎么說呢,總結的肯定是不夠完整和細致的,以后在實踐中慢慢的在完善吧!
自己還是太菜了,需要學習的地方還很多!不學習就找不到作業!!!!!
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/190351.html
標籤:Java
上一篇:docker 常用命令
