Java內容梳理(18)API學習(6)I/O流
目錄:
1、I/O流的分類
2、常用的I/O流
3、物件的序列化和反序列化
4、物件克隆
5、圖片操作
1、I/O流的分類
(1)介紹
流說明了Java中讀寫資料的方式:
順序讀寫資料:從左到右,從上到下依此讀取資料
資料的流動方向:單向
參照物:程式記憶體
(2)分類
1.按照方向分:
輸入流:InputStream父類/Reader父類
輸出流:OutputStream父類/Writer父類
2.按照單位分:
位元組流:InputStream父類/OutputStream父類-------以位元組為單位進行操作
字元流:Reader父類/Writer父類-------以字元為單位進行操作
3.按照功能分:
節點流(功能流):提供核心基礎讀寫資料能力的流
識別節點流:看類名字首,若是一種明確的資料存放地方,那個流就是節點流。
過濾流(裝備流):在節點流提供的基礎能力之上進行功能加強
4、流的父型別
InputStream --- 位元組輸入流
OutputStream --- 位元組輸出流
Reader --- 字元輸入流
Writer --- 字元輸出流
2、常用的I/O流
(1)節點流
FileInputStream類:檔案位元組輸入流,能實現按位元組讀取檔案
構造方法:為欲指定的檔案建立輸入管道
FileInputStream( String filepath )
讀入資料:read( byte[] buf ) 返回值是int,表示讀入的位元組個數。
每次從硬碟上最大讀滿buf陣列個位元組,返回值為-1時,表示沒有讀到資料,可以通過迴圈讀完整個檔案。
FileOutputStream類:檔案位元組輸出流,能實現將位元組寫入檔案(追加方式和覆蓋方式兩種)
構造方法:與指定檔案建立輸出管道(預設是替換模式)
FileOutputStream( File file )
FileOutputStream( String filePath )
按指定的模式與指定的檔案建立輸出管道(true為追加)
FileOutputStream( File file ,boolean isAppendModel)
FileOutputStream( String filePath ,boolean isAppendModel)
寫入資料:
write( byte[] buf ):每次向指定的檔案寫出整個buf陣列個位元組
write( byte[] buf,int offset,int len ):從buf陣列的offset位置開始,寫出len長度的位元組
注意:
1.當輸出的目標檔案不存在時,會自動將其建立
2.輸出流預設採用替換方式,不會追加
3.當檔案是文字檔案時,可以使用字元輸入流(FileReader),字元輸出流(FileWriter),效率更高。
(2)緩衝過濾流
核心:與磁碟互動的過程是由JVM自動管理,我們都從緩衝區讀寫資料,一般在字元輸入,輸出流的基礎上使用
BufferedInputStream類 和 BufferedOutputStream類 不常用
BufferedReader類: 帶緩衝的字元輸入流(將檔案資料讀取到記憶體)
構造方法:要依賴於另外一個流來建立
BufferedReader( Reader in ) 在指定的字元輸入流上建立一個緩衝流(預設8k)
BufferedReader( Reader in ,int size) 在指定的字元輸入流上建立一個size大小的緩衝流
讀方法:
String readLine(): 讀取一行資料(換行符或回車符表示一行讀完)
過程:
BufferedWriter類:帶緩衝的字元輸出流(將記憶體資料寫到檔案中)
不常用,通常情況下我們會使用PrintWriter流替代這個類;
呼叫寫方法時,並不直接向檔案輸出,而是寫到流的緩衝區中;
當緩衝區滿了時JVM才會將緩衝區中的資料一次性提交到檔案中;
當我們呼叫close()方法時,JVM在關流之前會將緩衝區中的資料提交到檔案;
不關流的情況下可以使用flush()方法,強制將緩衝區中的資料提交一次。
(3)轉換流(也是一種過濾流,提升資料處理單位)
InputStreamReader類 作用:InputStream ---> Reader
OuputStreamReader類 不常用,被PrintWriter代替
如下:is是InputStream位元組輸入流,由InputStreamReader轉換成了BufferedReader緩衝字元輸入流
(4)特殊流
PrintWriter類,既可以當作節點流,也可以當作過濾流,還可以當作轉化流來使用
舉例:用PrintWriter去實現文字檔案的複製
package stream;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
public class PrintWriterStream {
public static void main(String[] args) {
File file = new File("src/a.txt");
try {
/**
* 先用字元緩衝輸入流BufferedReader將檔案和記憶體建立通道,需要用該流讀取檔案資料
* BufferedReader物件的建立,要依賴另一個字元流建立
* 構造方法有2個:BufferedReader( Reader in ); BufferedReader( Reader in ,int size)
*/
BufferedReader reader = new BufferedReader(
new InputStreamReader( new FileInputStream(file) )
);
//測試檔案是否找到
System.out.println(file.exists());
//建立記憶體和b.txt檔案的輸出通道,需要用PrintWriter流寫資料到b.txt中,若沒有b.txt會自動新建一個
PrintWriter writer = new PrintWriter("src/b.txt");
String str = reader.readLine();
while(str != null) {
//注意writer.println(line);在輸出到bbb之後,會多一個換行
writer.println(str); //用PrintWriter流寫資料到b.txt中
str = reader.readLine();
}
writer.flush();//強制提交一次,確保提交
reader.close();//關流
writer.close();//關流
} catch ( IOException e) {
e.printStackTrace();
}
}
}
3、物件的序列化和反序列化
前提:
該物件的型別必須實現Serializable介面;否則報NotSerializableException異常
物件的序列化:(記憶體物件輸出(寫)到檔案)
ObjectOutputStream類 writeObject方法 將記憶體中的物件輸出
物件的反序列化:(讀取檔案資料,解析讀取出來的物件)
ObjectInputStream類 readObject方法
transient關鍵字:
當前進行物件序列化,將不會對transient修飾的屬性進行序列化
private static final long serialVersionUID = 1L;
反序列化是否成功主要依據serialVersionUID的值
舉例:將自定義物件寫到檔案中,再從檔案中讀取出來,輸出到控制檯上
第一步:將物件從記憶體寫到檔案
public class Student implements Serializable{
private static final long serialVersionUID = 1L;
private String name;
private int age;
public Student(String name, int age) {
super();
this.name = name;
this.age = age;
}
//...省略get和set方法
}
package stream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;
public class Run {
public static void main(String[] args) {
Student stu1 = new Student("Jack",18);
Student stu2 = new Student("Rose",13);
try {
/*建立記憶體和檔案的輸出通道*/
ObjectOutputStream oos = new ObjectOutputStream(
//true表示追加,後一個寫進檔案的資料不會覆蓋前一個
new FileOutputStream("src/c.txt",true)
);
/*將兩個Student物件寫(輸出)到檔案c.txt中去*/
oos.writeObject(stu1);
oos.writeObject(stu2);
/*關流*/
oos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
第二步:從檔案中讀取資料到記憶體,並輸出到控制檯上;為了程式碼完整,在上面的程式碼上新增
package stream;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
public class Run {
public static void main(String[] args) {
Student stu1 = new Student("Jack",18);
Student stu2 = new Student("Rose",13);
try {
/*建立記憶體和檔案的輸出通道*/
ObjectOutputStream oos = new ObjectOutputStream(
//true表示追加,後一個寫進檔案的資料不會覆蓋前一個
new FileOutputStream("src/c.txt",true)
);
/*將兩個Student物件寫(輸出)到檔案c.txt中去*/
oos.writeObject(stu1);
oos.writeObject(stu2);
/*關流*/
oos.close();
ObjectInputStream ois = new ObjectInputStream(
new FileInputStream("src/c.txt")
);
Student s1 = (Student)ois.readObject();
System.out.println(s1);
Student s2 = (Student)ois.readObject();
System.out.println(s2);
ois.close();
} catch (IOException | ClassNotFoundException e) {
e.printStackTrace();
}
}
}
4、物件克隆
含義:
複製一個副本,並不是同一個物件
淺克隆:
能被淺克隆的前提是:
型別必須實現Clonable介面,沒有任何抽象方法,稱為標識介面
僅複製一層 Object中的clone方法是淺克隆
深克隆:
所有層次均複製一個副本
利用物件的序列化和反序列化來完成深克隆:
ByteArrayInputStream類
ByteArrayOutputStream類
來完成記憶體到記憶體,再到記憶體的一個深克隆的過程,把物件轉化成Byte陣列,再轉化成物件
淺克隆舉例:
package clone;
public class Resume implements Cloneable{
private String name;
private int age;
private WorkExperience work;
public Resume(String name, int age, WorkExperience work) {
super();
this.name = name;
this.age = age;
this.work = work;
}
public WorkExperience getWork() {
return work;
}
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;
}
public void setWork(WorkExperience work) {
this.work = work;
}
//Object中clone()方法是被宣告為protected
//protected被修飾的方法只能在本類,同一個包中的類,或它的子類中使用;
public Resume Clone() {
try {
return (Resume) this.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
return null;
}
@Override
public String toString() {
return "Resume [name=" + name + ", age=" + age + ", work=" + work + "]";
}
}
package clone;
public class WorkExperience {
private String timeArea;
private String company;
public WorkExperience(String timeArea, String company) {
super();
this.timeArea = timeArea;
this.company = company;
}
@Override
public String toString() {
return "WorkExperience [timeArea=" + timeArea + ", company=" + company + "]";
}
public String getTimeArea() {
return timeArea;
}
public void setTimeArea(String timeArea) {
this.timeArea = timeArea;
}
public String getCompany() {
return company;
}
public void setCompany(String company) {
this.company = company;
}
}
package clone;
public class Run {
public static void main(String[] args) {
WorkExperience work1 = new WorkExperience("1999-2008", "中國航空公司");
Resume r1 = new Resume("小明", 12, work1) ;
System.out.println(r1);
Resume r2 =(Resume) r1.Clone();
System.out.println(r2);
System.out.println(r1 == r2);//顯示false,表示克隆出來的物件和原來的物件不是一個
System.out.println(r1.getWork() == r2.getWork());//顯示true,表示它們內部的WorkExperience物件其實是一個
//上面的測試表明,淺克隆其實只複製了一層物件,物件中的物件並沒有克隆,String物件是特殊物件,也被複制了一份
//再測試:如修改work1的內容兩個Resume的WorkExperience都會改變;但r2改變name不會影響r1
work1.setCompany("美國白宮");
r2.setName("小紅");
System.out.println(r1);
System.out.println(r2);
}
}
舉例:深克隆:利用物件序列化實現深克隆;A物件被序列化,而B是A下的屬性也會被序列化,C在B類下,也會被序列化;序列化完成再反序列化成物件;賦值給新的A物件,就實現了深克隆。
5、圖片操作
1、圖片類: java.awt.Image類
用來表示圖片資料 是一個抽象類,是所有圖片類的父類
BufferedImage類 是Image類的實現類,封裝了圖片資料
2、圖片讀寫: 主要用的是BufferedImage類
當程式需要使用一張圖片時(或者需要使用到BufferedImage類物件時),我們可以利用ImageIO來讀寫圖片。
ImageIO的用法(瞭解):
BufferedImage read( File file ): 載入指定的圖片檔案到記憶體(通常用來載入本地檔案)
BufferedImage read( URL url ) 載入指定url位置上的圖片檔案到記憶體(通常用來載入網路圖片)
BufferedImage read( InputStream input ) 將圖片資料在網路上傳輸
write( File file )
write( OutputStream output )