1. 程式人生 > >Java 知識點整理-17.IO流 其他流 序列流+序列化+反序列化+記憶體輸出流+物件操作流+列印流+標準輸入輸出流+鍵盤錄入方式+隨機訪問流+資料輸入輸出流+Properties

Java 知識點整理-17.IO流 其他流 序列流+序列化+反序列化+記憶體輸出流+物件操作流+列印流+標準輸入輸出流+鍵盤錄入方式+隨機訪問流+資料輸入輸出流+Properties

目錄

序列流

記憶體輸出流

記憶體輸出流的面試題

物件操作流ObjectOutputStream

物件操作流ObjectInputStream

物件操作流優化

序列化加上id號

列印流的概述和特點

標準輸入輸出流概述和輸出語句

修改標準輸入輸出流拷貝圖片

兩種方式實現鍵盤錄入

隨機訪問流概述和讀寫資料

資料輸入輸出流

Properties的概述和作為Map集合的使用

Properties的特殊功能使用

Properties的load()和store()功能


序列流

什麼是序列流?

序列流可以把多個位元組輸入流整合成一個。從序列流中讀取資料時,將從被整合的第一個流開始讀,讀完一個之後繼續讀第二個,以此類推。

SequenceInputStream類概述

public class SequenceInputStream extends InputStream,SequenceInputStream表示其他輸入流的邏輯串聯。它從輸入流的有序集合開始,並從第一個輸入流開始讀取,直到到達檔案末尾,接著從第二個輸入流讀取,依次類推,直到到達包含的最後一個輸入流的檔案末尾為止。

SequenceInputStream類構造方法

SequenceInputStream(Enumeration<?extends InputStream> e) 通過記住引數來初始化新建立的SequenceInputStream,該引數必須是生成執行時型別為InputStream物件的Enumeration型引數。

SequenceInputStream(InputStream s1, InputStream s2) 通過記住這兩個引數來初始化新建立的SequenceInputStream(將按順序讀取這兩個引數,先讀取s1,然後讀取s2,),以提供從此SequenceInputStream讀取的位元組。

使用方式:

整合兩個輸入流

SequenceInputStream類構造方法

SequenceInputStream(InputStream s1, InputStream s2) 通過記住這兩個引數來初始化新建立的SequenceInputStream(將按順序讀取這兩個引數,先讀取s1,然後讀取s2,),以提供從此SequenceInputStream讀取的位元組。InputStream是抽象的,所以傳其子類物件

演示:

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.SequenceInputStream;

public class Demo1_SequenceInputStream {
	public static void main(String[] args) throws IOException {
//		demo1_olddata();
		FileInputStream fis1 = new FileInputStream("a.txt");
		FileInputStream fis2 = new FileInputStream("b.txt");
		//對fis1和fis2進行封裝,封裝成SequenceInputStream。其實也是一種裝飾設計模式,使功能更加強大。
		SequenceInputStream sis = new SequenceInputStream(fis1, fis2);
		FileOutputStream fos = new FileOutputStream("c.txt");
		
		int b;
		while((b = sis.read()) != -1) {
			fos.write(b);
		}
		
		sis.close();		//sis在關閉的時候,會將構造方法中傳入的流物件也都關閉。
		fos.close();
		//SequenceInputStream底層原始碼
/*		InputStream in;		//in就是我們傳的InputStream。當我們傳fis1,fis2的時候,相當於對in賦值了。
		
		public void close() throws IOException {
	        do {
	            nextStream();
	        } while (in != null);
	    }
		
		final void nextStream() throws IOException {
	        if (in != null) {
	            in.close();
	        }

	        if (e.hasMoreElements()) {
	            in = (InputStream) e.nextElement();
	            if (in == null)
	                throw new NullPointerException();
	        }
	        else in = null;

	    }
*/		
	}

	public static void demo1_olddata() throws FileNotFoundException, IOException {
		//程式碼重複性高,複用性差
		FileInputStream fis1 = new FileInputStream("a.txt");	//建立位元組輸入流,關聯a.txt
		FileOutputStream fos = new FileOutputStream("c.txt");	//建立位元組輸出流,關聯c.txt。如果c.txt存在,是在建立物件的時候清空的。
		
		int b1;
		while((b1 = fis1.read()) != -1) {	 //不斷的在a.txt上讀取位元組
			fos.write(b1);		//將讀取的位元組寫到c.txt上
		}
		fis1.close();		//關閉位元組輸入流
		
		FileInputStream fis2 = new FileInputStream("b.txt");	//建立位元組輸入流,關聯b.txt
		
		int b2;
		while((b2 = fis2.read()) != -1) {	 //不斷的在b.txt上讀取位元組
			fos.write(b2);		//沒關流,所以會繼續將讀取的位元組寫到c.txt上,不會先清空掉檔案,這不是在建立輸出流物件。
		}
		fis2.close();		//關閉位元組輸入流
		fos.close();		//關閉位元組輸出流
	}
}

整合多個輸入流:

SequenceInputStream類構造方法

SequenceInputStream(Enumeration<?extends InputStream> e) 通過記住引數來初始化新建立的SequenceInputStream,該引數必須是生成執行時型別為InputStream物件的Enumeration型引數。InputStream是抽象的,所以傳其子類物件

Enumeration<E>介面概述

public interface Enumeration<E> 實現Enumeration介面的物件,它生產一系列元素,一次生成一個。連續呼叫nextElement方法將返回一系列的連續元素。例如,要輸出Vector<E> v的所有元素,可使用以下方法:for(Enumeration<E> e = v.elements(); e.hasMoreElements();) System.out.println(e.nextElement());這些方法主要通過向量的元素、雜湊表的鍵以及雜湊表中的值進行列舉。列舉也用於將輸入流指定到SequenceInputStream中。

注:此介面的功能與Iterator介面的功能是重複的。此外,Iterator介面添加了一個可選的移除操作,並使用較短的方法名。新的實現應該優先考慮使用Iterator介面而不是Enumeration介面。

Enumeration<E>介面方法

public boolean hasMoreElements() 測試此列舉是否包含更多的元素。返回:當且僅當此列舉物件至少還包含一個可提供的元素時,才返回true;否則返回false。

public E nextElement() 如果此列舉物件至少還有一個可提供的元素,則返回此列舉的下一個元素。

Vector類中的方法:

public Enumeration<E> elements() 返回此向量元件的列舉。返回值型別就是Enumeration。

將流物件都裝到Vector集合中,通過該elements方法就拿到了他的列舉。再把列舉賦給SequenceInputStream(Enumeration<?extends InputStream> e) 的構造即可。

 

序列流應用:多首mp3整合做歌曲串燒等

演示:

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.SequenceInputStream;

public class Demo1_SequenceInputStream {
	public static void main(String[] args) throws IOException {
//		demo1_olddata();
		FileInputStream fis1 = new FileInputStream("a.txt");
		FileInputStream fis2 = new FileInputStream("b.txt");
		//對fis1和fis2進行封裝,封裝成SequenceInputStream。其實也是一種裝飾設計模式,使功能更加強大。
		SequenceInputStream sis = new SequenceInputStream(fis1, fis2);
		FileOutputStream fos = new FileOutputStream("c.txt");
		
		int b;
		while((b = sis.read()) != -1) {
			fos.write(b);
		}
		
		sis.close();		//sis在關閉的時候,會將構造方法中傳入的流物件也都關閉。
		fos.close();
		//SequenceInputStream底層原始碼
/*		InputStream in;		//in就是我們傳的InputStream。當我們傳fis1,fis2的時候,相當於對in賦值了。
		
		public void close() throws IOException {
	        do {
	            nextStream();
	        } while (in != null);
	    }
		
		final void nextStream() throws IOException {
	        if (in != null) {
	            in.close();
	        }

	        if (e.hasMoreElements()) {
	            in = (InputStream) e.nextElement();
	            if (in == null)
	                throw new NullPointerException();
	        }
	        else in = null;

	    }
*/		
	}

	public static void demo1_olddata() throws FileNotFoundException, IOException {
		//程式碼重複性高,複用性差
		FileInputStream fis1 = new FileInputStream("a.txt");	//建立位元組輸入流,關聯a.txt
		FileOutputStream fos = new FileOutputStream("c.txt");	//建立位元組輸出流,關聯c.txt。如果c.txt存在,是在建立物件的時候清空的。
		
		int b1;
		while((b1 = fis1.read()) != -1) {	 //不斷的在a.txt上讀取位元組
			fos.write(b1);		//將讀取的位元組寫到c.txt上
		}
		fis1.close();		//關閉位元組輸入流
		
		FileInputStream fis2 = new FileInputStream("b.txt");	//建立位元組輸入流,關聯b.txt
		
		int b2;
		while((b2 = fis2.read()) != -1) {	 //不斷的在b.txt上讀取位元組
			fos.write(b2);		//沒關流,所以會繼續將讀取的位元組寫到c.txt上,不會先清空掉檔案,這不是在建立輸出流物件。
		}
		fis2.close();		//關閉位元組輸入流
		fos.close();		//關閉位元組輸出流
	}
}

記憶體輸出流

什麼是記憶體輸出流?

該輸出流可以向記憶體中寫資料,把記憶體當作一個緩衝區,寫出之後可以一次性獲取出所有資料。

ByteArrayOutputStream類概述

public class ByteArrayOutputStream extends OutputStream,此類實現了一個輸出流,其中的資料被寫入一個byte陣列。緩衝區會隨著資料的不斷寫入而自動增長。可使用toByteArray()和toString()獲取資料。(自動增長:其實是建立了一個新的更大的位元組陣列,並把原陣列的內容拷過來,之後再繼續新增新內容)

關閉ByteArrayOutputStream無效。此類中的方法在關閉此流後仍可被呼叫,而不會產生任何IOException。

ByteArrayOutputStream類的構造方法

ByteArrayOutputStream() 建立一個新的byte陣列輸出流。

ByteArrayOutputStream(int size) 建立一個新的byte陣列輸出流,它具有指定的大小的緩衝區容量(以位元組為單位)。

ByteArrayOutputStream類的成員方法

public void write(int b) 將指定的位元組寫入此byte陣列輸出流。

public void write(byte[] b, int off, int len) 將指定byte陣列中從偏移量off開始的len個位元組寫入此byte陣列輸出流。

public byte[] toByteArray() 建立一個新分配的byte陣列。

public String toString() 使用平臺預設的字符集,通過解碼位元組將緩衝區內容轉換為字串。

public String toString(String charsetName) 使用指定的charsetName,通過解碼位元組將緩衝區內容轉換為字串。

使用方式:

Ⅰ.建立物件:new ByteArrayOutputStream()

Ⅱ.寫出資料:write(int), write(byte[])

Ⅲ.獲取資料:toByteArray()

演示:

import java.io.ByteArrayOutputStream;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;

public class Demo3_ByteArrayOutputStream {
	public static void main(String[] args) throws IOException {
		//FileInputStream讀取中文的時候出現了亂碼。
/*		FileInputStream fis = new FileInputStream("e.txt");
		byte[] arr = new byte[3];
		
		int len;
		while((len = fis.read(arr)) != -1) {
			System.out.println(new String(arr, 0, len));
		}
		
		fis.close();
*/		
		/*
		 * 解決方案:
		 * 1.字元流讀取
		 * 2.ByteArrayOutputStream
		 */
		FileInputStream fis = new FileInputStream("e.txt");
		ByteArrayOutputStream baos = new ByteArrayOutputStream();		//建立該類的物件就相當於在記憶體中建立了一個可以增長的位元組陣列,把我們的內容存進來,所以他是可以沒有構造的。
		
		int b;
		while((b = fis.read()) != -1) {
			baos.write(b);		//將讀取到的資料逐個寫到記憶體中
		}
		
/*		byte[] arr = baos.toByteArray();		//將緩衝區的資料全部獲取出來,並賦值給arr陣列
		System.out.println(new String(arr));
*/	
		System.out.println(baos.toString()); 	//效果一致,將緩衝區的內容轉換為字串,在輸出語句中可以省略呼叫toString方法,列印索引系統預設呼叫toString()
		fis.close();
	}
}

記憶體輸出流的面試題

定義一個檔案輸入流,呼叫read(byte[] b)方法,將a.txt檔案中的內容打印出來(byte陣列大小限制為5)。

import java.io.ByteArrayOutputStream;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;

/**
 * 定義一個檔案輸入流,呼叫read(byte[] b)方法,將a.txt檔案中的內容打印出來(byte陣列大小限制為5)。
 * 分析:
 * 1.read(byte[] b)是位元組輸入流的方法,所以我們應該建立的是FileInputStream,關聯a.txt
 * 2.建立記憶體輸出流,將獨到的資料寫到記憶體輸出流中。
 * 3.建立一個位元組陣列,長度為5。
 * 4.將記憶體輸出流的資料全部轉換為字串列印。
 * 5.關閉輸入流。
 */
public class Test1 {
	public static void main(String[] args) throws IOException {
		//1.read(byte[] b)是位元組輸入流的方法,所以我們應該建立的是FileInputStream,關聯a.txt
		FileInputStream fis = new FileInputStream("a.txt");
		//2.建立記憶體輸出流,將獨到的資料寫到記憶體輸出流中。
		ByteArrayOutputStream baos = new ByteArrayOutputStream();
		//3.建立一個位元組陣列,長度為5。
		byte[] arr = new byte[5];
		int len;
		
		while((len = fis.read(arr)) != -1) {
			baos.write(arr, 0, len);
		}
		//4.將記憶體輸出流的資料全部轉換為字串列印。
		System.out.println(baos);		//底層預設幫我們呼叫toString()
		//5.關閉輸入流。
		fis.close();
	}
}

物件操作流ObjectOutputStream

什麼是物件操作流

該流可以將一個物件寫出,或者讀取一個物件到程式中。也就是執行了序列化和反序列化的操作。將物件從記憶體中按照某種規定寫到文件上去這個動作叫做序列化,通俗來說,就像玩遊戲的存檔。讀檔這個動作叫做反序列化。反序列化就是把那些寫到文件中的位元組資料翻譯成對應的物件,再去使用。

ObjectOutputStream類概述

public class ObjectOutputSteam extends OutputStream implements ObjectOutput, ObjectStreamConstants,ObjectOutputStream將Java物件的原始資料型別和圖形寫入OutputStream。可以使用ObjectInputStream讀取(重構)物件。 可以通過使用流的檔案來實現物件的持久儲存。如果流是網路套接字流,則可以在另一個主機上或另一個程序中重構物件。

只有支援java.io.Serializable介面的物件才能寫入流中。每個可序列化物件的類被編碼,包括類的類名和簽名,物件的欄位和陣列的值以及從初始物件引用的任何其他物件的關閉。

方法writeObject用於將一個物件寫入流中。任何物件,包括字串和陣列,都是用writeObject編寫的。多個物件或原語可以寫入流。 必須從對應的ObjectInputstream讀取物件,其型別和寫入次序相同。

writeObject方法負責為其特定的類編寫物件的狀態,以便相應的readObject方法可以恢復它。該方法不需要關心屬於物件的超類或子類的狀態。 通過使用writeObject方法或通過使用DataOutput支援的原始資料型別的方法將各個欄位寫入ObjectOutputStream來儲存狀態。

序列化不會寫出任何不實現java.io.Serializable介面的物件的欄位。不可序列化的物件的子類可以是可序列化的。在這種情況下,非可序列化類必須有一個無引數建構函式,以允許其欄位被初始化。在這種情況下,子類有責任儲存並恢復不可序列化類的狀態。通常情況下,該類的欄位是可訪問的(public,package或protected),或者可以使用get和set方法來恢復狀態。

可以通過實現丟擲NotSerializableException的writeObject和readObject方法來防止物件的序列化。 異常將被ObjectOutputStream捕獲並中止序列化過程。

ObjectOutputStream類的成員方法

public voice writeObject(Object obj) 將指定的物件寫入ObjectOutPutStream。寫入物件的類,類的簽名以及類的非瞬態和非靜態欄位的值以及其所有超型別。可以使用writeObject和readObject方法覆蓋類的預設序列化。

Serializable介面概述

public interface Serializable,類通過實現java.io.Serializable介面以啟用其序列化功能。未實現此介面的類將無法使其任何狀態序列化或反序列化。可序列化的所有子型別本身都是可序列化的。序列化介面沒有方法或欄位,僅用於標識可序列化的語義。未實現序列化系統會報NotSerializableException。

使用方式:

寫出:new ObjectOutputStream(OutputStream), writeObject()

演示:

import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;

import com.bean.Person;

public class Demo4_ObjectOutputStream {
	public static void main(String[] args) throws IOException {
		Person p1 = new Person("張三", 23);
		Person p2 = new Person("李四", 24);
		//序列化:將物件寫到檔案上。
		ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("e.txt"));
		oos.writeObject(p1);		//NotSerializableException 無法序列化異常,需要讓自定義類實現Serializable介面。
		oos.writeObject(p2);		//Person物件自動型別提升為Object
		
		oos.close();
	}
}

物件操作流ObjectInputStream

ObjectInputStream類概述

public class ObjectInputStream extends InputStream implements ObjectInput, ObjectStreamConstants,ObjectInputStream反序列化先前使用ObjectOutputStream編寫的原始資料和物件。

ObjectOutputStream和ObjectInputStream可以分別為與FileOutputStream和FileInputStream一起使用的物件圖提供永續性儲存的應用程式。ObjectInputStream用於恢復先前序列化的物件。

ObjectInputStream確保從流中建立的圖中的所有物件的型別與Java虛擬機器中存在的類匹配。 根據需要使用標準機制載入類。

只能從流中讀取支援java.io.Serializable或java.io.Externalizable介面的物件。

方法readObject用於從流中讀取物件。在Java中,字串和陣列是物件,在序列化過程中被視為物件。讀取時,需要將其轉換為預期型別。

可以使用DataInput上的適當方法從流中讀取原始資料型別。

物件的預設反序列化機制將每個欄位的內容恢復為寫入時的值和型別。宣告為瞬態或靜態的欄位被反序列化過程忽略。對其他物件的引用導致根據需要從流中讀取這些物件。使用參考共享機制正確恢復物件的圖形。反序列化時總是分配新物件,這樣可以防止現有物件被覆蓋。

讀取物件類似於執行新物件的建構函式。為物件分配記憶體,並初始化為零(NULL)。對非可序列化類呼叫無索引建構函式,然後從最接近java.lang.object的可序列化類開始,從串中還原可序列化類的欄位,並使用物件的最特定類完成。

類通過實現java.io.Serializable或java.io.Externalizable介面來控制它們是如何序列化的。

實現Serializable介面允許物件序列化儲存和恢復物件的整個狀態,並允許類在流被寫入的時間和讀取時間之間演變。它自動遍歷物件之間的引用,儲存和恢復整個圖形。

readObject方法負責使用通過相應的writeObject方法寫入流的資料來讀取和恢復其特定類的物件的狀態。該方法不需要關注屬於其超類或子類的狀態。通過從ObjectInputStream讀取各個欄位的資料並對物件的相應欄位進行分配來恢復狀態。DataInput支援讀取原始資料型別。

任何嘗試讀取超過相應writeObject方法寫入的自定義資料邊界的物件資料將導致使用eof欄位值為true的丟擲OptionalDataException。超過分配資料結束的非物件讀取將以與指示流結尾相同的方式反映資料的結尾:Bytewise讀取將返回-1作為位元組讀取或讀取的位元組數,並且原語讀取將丟擲EOFExceptions。如果沒有相應的writeObject方法,則預設序列化資料的結尾標記分配的資料的結尾。

序列化不會讀取或賦值任何不實現java.io.Serializable介面的物件的值。不可序列化的物件的子類可以是可序列化的。在這種情況下,非可序列化類必須有一個無引數建構函式,以允許其欄位被初始化。在這種情況下,子類有責任儲存並恢復不可序列化類的狀態。通常情況下,該類的欄位是可訪問的(public,package或protected),或者可以使用get和set方法來恢復狀態。

反序列化物件時發生的任何異常都將被ObjectInputStream捕獲並中止讀取過程。

使用方式:

讀取:new ObjectInputStream(InputStream), readObject()

演示:

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.ObjectInputStream;

import javax.print.attribute.standard.PresentationDirection;

import com.bean.Person;

public class Demo5_ObjectInputStream {
	public static void main(String[] args) throws IOException, ClassNotFoundException {
		//ObjectInputStream物件輸入流,反序列化
		ObjectInputStream ois = new ObjectInputStream(new FileInputStream("e.txt"));
		
		Person p1 = (Person) ois.readObject();		//Object強制型別轉換成Person
		Person p2 = (Person) ois.readObject();		
//		Person p3 = (Person) ois.readObject();		//EOFException檔案到末尾異常 寫倆讀三
		
		System.out.println(p1);
		System.out.println(p2);
		
		ois.close();
	}
}

物件操作流優化

將物件儲存在集合中寫出,讀取到的是一個集合物件。

演示:

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.util.ArrayList;

import com.bean.Person;

public class Demo6_ObjectOperationStream {
	public static void main(String[] args) throws IOException, ClassNotFoundException {
//		demo_write();
		ObjectInputStream ois = new ObjectInputStream(new FileInputStream("e.txt"));
		//讀取到的是一個集合物件。
		ArrayList<Person> list = (ArrayList<Person>) ois.readObject();		//將集合物件一次讀取
		for (Person person : list) {
			System.out.println(person);
		}
		
		ois.close();
	}

	public static void demo_write() throws IOException, FileNotFoundException {
		Person p1 = new Person("張三", 23);
		Person p2 = new Person("李四", 24);
		Person p3 = new Person("王五", 25);
		Person p4 = new Person("趙六", 26);
		//將物件儲存在集合中寫出
		ArrayList<Person> list = new ArrayList<>();
		list.add(p1);
		list.add(p2);
		list.add(p3);
		list.add(p4);
		
		ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("e.txt"));
		oos.writeObject(list);		//把整個集合物件一次寫出
		oos.close();
	}
}

序列化加上id號

注意

要寫出的物件必須實現Serializable接口才能被序列化。

不用必須加id號。不加系統會隨機生成一串很長的id,加上id的作用是當忘記存檔就讀檔時方便我們檢視錯誤。


列印流的概述和特點

什麼是列印流?

該流可以很方便的將物件的toString()結果輸出,並且自動加上換行,而且可以使用自動刷出的模式。

System.out就是一個PrintStream,其預設向控制檯輸出資訊。

System類概述

public final class System extends Object,System類包含一些有用的類欄位和方法。它不能被例項化。

在System類提供的設施中,有標準輸入、標準輸出和錯誤輸出流;對外部定義的屬性和 環境變數的訪問;載入檔案和庫的方法;還有快速複製陣列的一部分的使用方法。

System類的成員變數:

public static PrintStream out,“標準”輸出流。

public static InputStream in,”標準“輸入流。

public static PrintStream err,“標準”錯誤輸出流。

PrintStream類概述

public class PrintStream extends FilterOutputStream implements Appendable, Closeable,PrintStream為其他輸出流添加了功能,使他們能夠方便地列印各種資料值表示形式。他還提供其他兩項功能。與其他輸出流不同,PrintStream永遠不會丟擲IOException;而是,異常情況僅設定可通過的checkError方法測試的內部標誌。另外,為了自動重新整理,可以建立一個PrintStream;這意味著可在寫入byte陣列之後自動呼叫flush方法,可呼叫其中一個println方法,或寫入一個換行符或位元組('\n')。

PrintStream列印的所有字元都使用平臺的預設字元編碼轉換為位元組。在需要寫入字元而不是位元組的情況下,應該使用PrintWriter類。

print方法和println方法都屬於PrintStream類,具體請查閱api。

演示:

import java.io.PrintStream;

import com.bean.Person;

public class Demo7_PrintStream {
	public static void main(String[] args) {
		System.out.println("aaa");		//平時這麼用輸出語句就行,不用先獲取輸出流,再列印
		PrintStream ps = System.out;	//獲取標準輸出流
		ps.println(97);		//97 底層通過Integer.toString()將97轉換成對應的字串並列印
		ps.write(97);		//a 查詢碼錶,找到對應的a並列印
		
		//println底層原始碼 println→print→valueof→toString
/*		public void println(int x) {
	        synchronized (this) {
	            print(x);
	            newLine();
	        }
	    }
		
		public void print(int i) {
	        write(String.valueOf(i));
	    }
		
		public static String valueOf(int i) {
	        return Integer.toString(i);
	    }
		
		 public static String toString(int i) {  //傳進一個整數,經過一系列變化轉成字串返回
	        if (i == Integer.MIN_VALUE)
	            return "-2147483648";
	        int size = (i < 0) ? stringSize(-i) + 1 : stringSize(i);
	        char[] buf = new char[size];
	        getChars(i, size, buf);
	        return new String(buf, true);
	    }
*/		 
		Person p1 = new Person("張三", 23);	//預設呼叫p1的toString方法
		ps.println(p1);
		
		Person p2 = null;	//列印引用資料型別,如果是null就列印null;如果不是null就列印物件的toString方法。
		ps.println(p2);
		ps.close();
		//println底層原始碼 println→valueof
/*		public void println(Object x) {
	        String s = String.valueOf(x);
	        synchronized (this) {
	            print(s);
	            newLine();
	        }
	    }
		 
		public static String valueOf(Object obj) {
	        return (obj == null) ? "null" : obj.toString();		//不為空,就列印該物件的toString();為空,則列印null
	    }
*/		
	}
}

使用方式:

列印:print(), println()

自動刷出:PrintWriter(OutputStream out, boolean autoFlush, String encoding)  可以自動重新整理。

PrintWriter類概述

public class PrintWriter extends Writer 向文字輸出流列印物件的格式化表示形式。此類實現在PrintStream中的所有print方法。它不包含用於寫入原始位元組的方法,對於這些位元組,程式應該使用未編碼的位元組流進行寫入。

與PrintStream類不同,如果啟用了自動重新整理,則只有在呼叫println、printf或format的其中一個方法時才可能完成此操作,而不是每當正好輸出換行符時才完成。這些方法使用平臺自有的行分隔符概念,而不是換行符。

此類中的方法不會丟擲I/O異常,儘管其某些構造方法可能丟擲異常。客戶端可能會查詢呼叫checkError()是否出現錯誤。

PrintWriter類構造方法

PrintWriter(File file) 使用指定檔案建立不具有自動行重新整理的新PrintWriter。

PrintWriter(File file, String csn) 建立具有指定檔案和字符集且不帶自動行重新整理的新PrintWriter。

PrintWriter(OutputStream out) 根據現有的OutputStream建立不帶自動行重新整理的新PrintWriter。

PrintWriter(OutputStream out, boolean autoFlush) 通過現有的OutputStream建立新的PrintWriter。

PrintWriter(String fileName) 建立具有指定檔名稱且不帶自動行重新整理的新PrintWriter。

PrintWriter(String fileName, String csn) 建立具有指定檔名稱和字符集且不帶自動行重新整理的新PrintWriter。

PrintWriter(Writer out) 建立不帶自動行重新整理的新PrintWriter。

PrintWriter(Writer out, boolean autoFlush) 建立新的PrintWriter。

演示:

import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.PrintWriter;

public class Demo8_PrintWriter {
	public static void main(String[] args) throws IOException {
		PrintWriter pw = new PrintWriter(new FileOutputStream("f.txt"), true);
//		pw.println(97);		//97	自動刷出功能只針對println方法
		pw.write(97);		//a		其他方法不行,print都不行
		pw.println();		//		但把println放最後就都能刷新出來
		pw.close();			//      關流也重新整理,其實沒啥用
	}
}

PrintStream和PrintWriter分別是列印的位元組流和字元流。

列印流只操作 資料目的(記憶體從硬碟讀取資料的檔案叫資料來源,記憶體寫到硬碟資料的檔案叫資料目的)。


標準輸入輸出流概述和輸出語句

什麼是標準輸入輸出流?

System.in是InputStream,標準輸入流,預設可以從鍵盤輸入讀取位元組資料。

System.out是PrintStream,標準輸出流,預設可以向Console中輸出字元和位元組資料。

修改標準輸入輸出流

修改輸入流:System.setIn(InputStream)

修改輸出流:System.setOut(PrintStream)

演示:

import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintStream;

public class Demo9_SystemInOut {
	public static void main(String[] args) throws IOException {
//		demo_systemIn();
		//可以改變輸入、輸出流
		System.setIn(new FileInputStream("a.txt"));	//改變標準輸入流
		System.setOut(new PrintStream("b.txt"));	//改變標準輸出流
		
		InputStream is = System.in;		//獲取標準的鍵盤輸入流,預設指向鍵盤,改變後指向檔案。
		PrintStream ps = System.out;	//獲取標準輸出流,預設指向控制檯,改變後指向檔案。
		
		int b;
		while((b = is.read()) != -1) {
			ps.write(b);
		}
		System.out.println();	//System.out也是一個PrintStream標準輸出流,如果沒有和硬碟上的檔案產生關聯的管道的話,是不用關的
		is.close();
		ps.close();
	}

	public static void demo_systemIn() throws IOException {
		InputStream is = System.in;
		int x = is.read();		//只讀第一個位元組
		System.out.println(x);
		
		is.close();
		//輸入流只有一個,即使重新建立也是原來那個。之前關閉了流,再想開就開不了了。而且這個流其實是不用關的,因為並沒有讓記憶體和硬碟檔案產生關聯。
		InputStream is2 = System.in;
		int y = is.read();		
		System.out.println(x);
	}
}

修改標準輸入輸出流拷貝圖片

演示:

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintStream;

public class Test2 {
	public static void main(String[] args) throws IOException {
		//修改標準輸入輸出流拷貝圖片,但開發中不推薦這麼用
		System.setIn(new FileInputStream("demo.jpg"));	//改變標準輸入流
		System.setOut(new PrintStream("copy.jpg"));		//改變標準輸出流
		
		InputStream is = System.in;
		PrintStream ps = System.out;
		
		byte[] arr = new byte[1024];
		int len;
		
		while((len = is.read(arr)) != -1) {
			ps.write(arr, 0, len);
		}
		
		is.close();
		ps.close();
	}
}

兩種方式實現鍵盤錄入

BufferedReader的readLine方法。

BufferedReader br = new BufferedReader(new InputStreamReader(System.in));

Scanner。

演示:

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.Scanner;

public class Demo10_SystemIn {
	public static void main(String[] args) throws IOException {
//		demo_bufferedReader();
		//Scanner進行鍵盤錄入,更推薦,功能更加強大。
		Scanner sc = new Scanner(System.in);	//Scanner也可以用來讀某個檔案
		String line = sc.nextLine();
		System.out.println(line);
		sc.close();		//Scanner是對流進行封裝,所以他也有關閉的功能。但傳System.in的時候可以是不關的,因為沒有和硬碟上某個檔案發生關聯。
	}

	public static void demo_bufferedReader() throws IOException {
		//BufferedReader進行鍵盤錄入讀取一行
		BufferedReader br = new BufferedReader(new InputStreamReader(System.in));	//中間InputStreamReader是轉換流,將位元組流轉換成字元流。  對System.in進行包裝,通過轉換流轉換為了字元流,再通過BufferedReader使其功能更加強大。
		String line = br.readLine();
		System.out.println(line);
		br.close();
	}
}

隨機訪問流概述和讀寫資料

隨機訪問流概述

RandomAccessFile類概述

public class RandomAccessFile extends Object implements DataOutput, DataInput, Closeable,此類的例項支援對隨機訪問檔案的讀取和寫入。隨機訪問檔案的行為類似儲存在檔案系統中的一個大型byte陣列。存在指向該隱含陣列的游標或索引,稱為檔案指標;輸入操作從檔案指標開始讀取位元組,並隨著對位元組的讀取而前移此檔案指標。如果隨機訪問檔案以讀取/寫入模式建立,則輸出操作也可用;輸出操作從檔案指標開始寫入位元組,並隨著對位元組的寫入而前移此檔案指標。寫入隱含陣列的當前末尾之後的輸出操作導致該陣列拓展。該檔案指標可以通過getFilePointer方法讀取,並通過seek方法設定。

通常,如果此類中的所有讀取例程在讀取所需數量的位元組之前已到達檔案末尾,則丟擲EOFException(是一種IOException)。如果由於某些原因無法讀取任何位元組,而不是在讀取所需數量的位元組之前已到達檔案末尾,則丟擲IOException,而不是EOFException。需要指出的是,如果流已被關閉,則可能丟擲IOException。

RandomAccessFile類不屬於流,是Object類的子類。但它融合了InputStream和OutputStream的功能。即可以讀也可以寫。java.io包下,使用需要導包。

支援對隨機訪問檔案的讀取和寫入。

RandomAccessFile類的構造方法

RandomAccessFile(File file, String mode) 建立從中讀取和其中寫入(可選)的隨機訪問檔案流,該檔案由File引數指定。mode引數指定用以開啟檔案的訪問模式。允許的值及其含義為:

含義

"r"

以只讀方式開啟。呼叫結果物件的任何write方法都將導致IOException。

"rw"

開啟以便讀取和寫入。如果該檔案尚不存在,則嘗試建立該檔案。

"rws"

開啟以便讀取和寫入,對於“rw”,還要求對檔案的內容或元資料的每個更新都同步寫入到底層儲存裝置。同步更新:邊寫變儲存。元資料:即檔案屬性中的詳細資訊。

"rwd"

開啟以便讀取和寫入,對於“rw”,還要求對檔案內容的每個更新都同步寫入到底層儲存裝置。

 

RandomAccessFile(String name, String mode) 建立從中讀取和向其中寫入(可選)的隨機訪問檔案流,該檔案具有指定名稱。

RandomAccessFile類的成員方法

public void seek(long pos) 設定檔案指標偏移,從該檔案的開頭測量,發生下一次讀取或寫入。 偏移可以設定為超出檔案的末尾。設定超出檔案末尾的偏移量不會更改檔案長度。檔案長度只有在偏移設定超出檔案結尾之後才會通過寫入進行更改。

引數:pos - 從檔案開頭測量的偏移位置,用於設定檔案指標。

異常:IOException - 如果 pos小於 0或發生I / O錯誤。 

演示:

import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.RandomAccessFile;

public class Demo11_RandomAccessFile {
	public static void main(String[] args) throws IOException {
		RandomAccessFile raf = new  RandomAccessFile("g.txt", "rw");
//		raf.write(97);		//寫
/*		int x = raf.read();	//讀
		System.out.println(x);		//97
*/		
		raf.seek(10);		//在指定位置設定指標。可以進行讀寫操作,且不會清空檔案。
		raf.write(98);
		raf.close();
	}
}

指定位置讀寫的好處,可以進行多執行緒下載。提高效率。


資料輸入輸出流

什麼是資料輸入輸出流

DataInputStream,DataOutputStream可以按照基本資料型別大小讀寫資料。

例如按Long大小寫出一個數字,寫出時該資料佔8位元組。讀取的時候也可以按照Long型別讀取,一次讀取8個位元組。

DataInputStream類概述

public class DataInputStream extends FilterInputStream implements DataInput,資料輸入流允許應用程式以獨立於機器的方式從底層輸入流讀取原始Java資料型別。應用程式使用資料輸出流來寫入稍後可以被資料輸入流讀取的資料。DataInputStream對於多執行緒訪問來說不一定是安全的。

DataInputStream類的構造方法

public DataInputStream(InputStream in) 建立使用指定的底層InputStream的DataInputStream。

DataInputStream類詳細成員方法見api。

DataOutputStream類概述

public class DataOutputStream extends FilterOutputStream implements DataOutput 資料輸出流使應用程式以行動式方式將原始Java資料型別寫入輸出流。然後應用程式可以使用資料輸入流來讀取資料。

DataOutputStream類的構造方法

public DataOutputStream(OutputStream out) 建立一個新的資料輸出流,以將資料寫入指定的底層輸出流。計數器written設定為零。

DataOutputStream類的成員方法

public final void writeInt(int v) 將int寫入底層輸出流作為四位元組值。如果沒有丟擲異常,計數器written遞增4。

DataOutputStream類詳細成員方法見api。

使用方式:

DataOutputStream(OutputStream), writeInt(), writeLong() 

演示:

import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;

public class Demo12_Data {
	public static void main(String[] args) throws IOException {
//		demo_write();
//		demo_read();
		
/*		DataOutputStream dos = new DataOutputStream(new FileOutputStream("h.txt"));
		dos.writeInt(997);
		dos.writeInt(998);
		dos.writeInt(999);
		
		dos.close();
*/
		DataInputStream dis = new DataInputStream(new FileInputStream("h.txt"));
		int x = dis.readInt();
		int y = dis.readInt();
		int z = dis.readInt();
		
		System.out.println(x);  //997
		System.out.println(y);	//998
		System.out.println(z);	//999
		
		dis.close();
	}

	public static void demo_read() throws FileNotFoundException, IOException {
		FileInputStream fis = new FileInputStream("h.txt");
		int x = fis.read();		//00000000 00000000 00000000 11100101‬
		int y = fis.read();
		int z = fis.read();
		System.out.println(x);  //229
		System.out.println(y);	//230
		System.out.println(z);	//231
		
		fis.close();
	}

	public static void demo_write() throws FileNotFoundException, IOException {
		FileOutputStream fos = new FileOutputStream("h.txt");
		fos.write(997);		//int型別997 00000000 00000000 ‭00000011 11100101	寫入時前三個8位被砍去,只剩11100101‬
		fos.write(998);		//寫的時候不能按照基本資料型別大小寫過去,本來想寫四個位元組,結果寫過去的是一個位元組。讀的時候當然也是一個位元組。
		fos.write(999);
		
		fos.close();
	}
}

Properties的概述和作為Map集合的使用

Properties的概述:

public class Properties extends Hashtable<Object, Object> Properties類表示了一個持久的屬性集。Properties可儲存在流中或從流中載入。屬性列表中每個鍵及其對應值都是一個字串。

不是一個流,他可以用流讀取配置檔案。是一個沒有泛型的雙列集合,父類Hashtable。沒有指定泛型,他有一個固定的作用,就是去當做配置檔案,儲存一些資訊,基本都是字串型別的。如果加了泛型,就可以設定任意型別。不加泛型,通過某種方法限制指定你為String型別。

演示:Properties作為Map集合的使用。

import java.util.Properties;

public class Demo13_Properties {
	public static void main(String[] args) {
		Properties prop = new Properties();
		prop.put("abc", 123);
		System.out.println(prop);	//{abc=123}
	}
}	

Properties的特殊功能使用

Properties類的特殊功能:

public Object setProperty(String key, String value) 呼叫Hashtable的方法put。

public String getProperty(String key) 用指定的鍵在此屬性列表中搜索屬性。

public Enumeration<?> PropertyNames() 返回屬性列表中所有鍵的列舉,如果在主屬性列表中未找到同名的鍵,則包括預設屬性列表中不同的鍵。

演示:

import java.util.Enumeration;
import java.util.Properties;

public class Demo14_Properties {
	public static void main(String[] args) {
		Properties prop = new Properties();
		prop.setProperty("name", "張三");
		prop.setProperty("tel", "18912345678");
		
//		System.out.println(prop);
		Enumeration<String> en = (Enumeration<String>) prop.propertyNames();		//沒有加泛型,預設Object,所以要加一次強轉。
		while(en.hasMoreElements()) {
			String key = en.nextElement();			//獲取Properties中的每一個鍵
			String value = prop.getProperty(key);	//根據建獲取值
			System.out.println(key + "=" + value);
		}
	}
}

Properties的load()和store()功能

Properties類的load()和store()功能:

public void load(InputStream inStream) 從輸出流中讀取屬性列表(鍵和元素對)。

public void load(Reader reader) 按簡單的面向行的格式從輸入字元流中讀取屬性列表(鍵和元素對)。

public void store(OutputStream out, String comments) 以適合使用load(InputStream)方法載入到Properties表中的格式,將此Properties表中的屬性列表(鍵和元素對)寫入輸出流。

public void store(Writer writer, String comments) 以適合使用load(Reader)方法的格式,將此Properties表中的屬性列表(鍵和元素對)寫入輸出字元。引數:comments-屬性列表的描述。可以不給,寫個null。如果給了在檔案中會以#註釋作開頭加上我們給的描述。

演示:

準備一個config.properties配置檔案,寫入

username=zhangsan

tel=18912345678

qq=12345

等號、冒號系統都是可以識別的。等號前面是鍵,後面是值。

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.Properties;

public class Demo15_Properties {
	public static void main(String[] args) throws IOException {
		Properties prop = new Properties();
		prop.load(new FileInputStream("config.properties"));	//將檔案上的鍵值對讀取到集合中
		prop.setProperty("tel", "18612345678");		//只改了記憶體的,沒有寫到檔案上去
		prop.store(new FileOutputStream("config.properties"), null);	//第二個引數是對列表引數的描述,可以給值,也可以給null
		System.out.println(prop);
	}
}