1. 程式人生 > >Java內容梳理(18)API學習(6)I/O流

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 )