黑馬程序員_【總結】_IO知識梳理3_(end)

IO知識梳理3


---------- android培訓 java培訓、期待與您交流! ---------- 

---------------------------------------------------------------------------------------------------------------------------------------------
1、在IO中設計到集合的是; SepenceInputStream
  在IO中涉及到多線程的是:管道流
2、隨機訪問流 RandomAccessFile 
new RandomAccessFile("E:\\Demo\\test\\a.txt","rw");
該類只能操作文件。其模式: r 讀--- rw  讀寫
1、模式 
2、直接寫入數據基本類型 
3、通過seek 方法改變指針的位置來進行指定位置的數據讀取和寫入。【重點】
3、基本數據流   能直接的操作基本數據類型:
DataInputStream---DataOutputStream
指定編碼表
new DataOutputStream(new FileOutputStream("E:\\Demo\\utfdate.txt"));
4、管道流
PipendInputStram --PipendOutputStream  
輸入輸出可以直接進行連接,通過結合線程使用。
管道流的特點::當多線程時進行讀寫功能時,
不論是先執行讀,還是寫 都不重要,最後終究會先執行寫,然後再打印
5、字節數組流
用於操作字節數組的流對象:
ByteArrayInputStream在構造時,需要接收數據源,而且數據源是一個字節數組
ByteArrayOutputStream 在構造時,不用定義數據目的,因爲該對象中已經在內部封裝
了可變長度的字節數組,這就是數據目的。
6、字節數組流  因爲這兩個流對象 都操作的是數組,並沒有使用系統資源,
所以不用進行 close 關閉。
7、【操作【字節】數組】 、【操作【字符】數組】、【操作字符串】 都基本相同。
【字節】數組:ByteArrayInputStream ---   ByteArrayOutputStream
【字符】數組: CharArrayReader------  CharArrayWriter
【字符串】 StringReader ----    StringWriter 
8、操作流 、序列化
ObjectInputStream--ObjectOutputStream
Serializable
1、被操作的對象必須實現 Serializable 接口
這接口太爽了,只需要實現,其他什麼也不用寫
2、自定義序列號
static final long serialVersionUID = 42L;  42L爲自定義序列號
3、靜態成員變量不能被序列號
(靜態在方法區,能被序列的都在堆內存中)
4、 普通成員變量想要不被序列號必須加關鍵字:   
這樣該成員不被序列號,但存在與堆內存中。
4讓 普通成員變量也不被序列化呢?
java 提供了一個關鍵字:   transizent    
5、給類設定一個固定序列號,以保證唯一性
static final long serialVersionUID = 42L; 
9、
編碼: 字符串 變 字節數組。
解碼: 字節數組 變 字符串。
String --> byte[] :str.getBytes(charsetName);    
[charsetName  指定編碼表(不指定用默認GBK編碼表)]
byte   -->  String:newString(byte[],charsetName);
10、“聯通”是 GBK 編碼 但是 產生的二進制形式 居然和 UTF-8  一致
這就是爲什麼 會出現亂碼了,因爲識別的時候,爲上列第2中
然後去查UTF-8碼錶。自然就錯了。
---------------------------------------------------------------------
【6】
隨機訪問流
【重點】 
1、模式 
2、直接寫入數據基本類型 
3、通過seek 方法改變指針的位置來進行指定位置的數據讀取和寫入。【重點】

RandomAccessFile
自身具備讀寫方法   達到隨機訪問。
skipBytes(int i)
seek(int i) 
skipBytes(int i) 跳過幾個字節  只能往下跳不能往回   瞭解該方法即可。
1、模式
2、寫入方法
3、seek 指定指針位置。

---------------------------------------------------------------------
該類不算是IO 體系中的子類  (後綴沒有父類)
而直接繼承自Object

但是它是 IO 包中成員,因爲它具備讀和寫功能。
內部封裝了一個 byte 數組 ,而通過指針對數組的元素進行操作。
可以通過geitFilePointer 獲取指針位置。

其實,完成讀寫原理就是內部封裝了 字節寫入流 和輸出流

通過構造函數可以看出,該類只能操作文件。
而操作文件的模式: r 讀--- rw  讀寫

如果模式爲只讀: r 不會創建文件,會去讀取一個已存在的文件,如果文件不存在
則會出現異常。
如果模式:rw操作的文件不存在,會自動創建,如果存在則不會覆蓋。
---------------------------------------------------------------------
1、通過指針的設定達到隨意性的訪問。
2、數據是有規律的。

1、能夠進行數據的分段寫入,比如下載,就是設計了多個線程,分別在寫入數據
通過該方法給每個線程不同的段落,不會有任何衝突。
用一般流寫數據因爲是從頭到尾,會導致數據都存完了,最後讀出來是錯誤的,解碼錯誤。

//問題:如何把名字搞爲16的字節格式??
class $3RandomAccessFile {
	public static void main(String[] args) throws IOException{
		method_3();
		//method_2();
		//method_1();
	}
	public static void method_3()throws IOException{
		RandomAccessFile raf =new RandomAccessFile("E:\\Demo\\test\\a.txt","rw");
		raf.seek(8*4);//指針直接把位置指向8個字節的 第4個。
		raf.write("德吉".getBytes());
		raf.writeInt(99);//直接寫 基本數據類型
		raf.close();
	}
	public static void method_2()throws IOException{
		RandomAccessFile raf =new RandomAccessFile("E:\\Demo\\test\\a.txt","rw");

		byte [] by = new byte [4];
		raf.seek(8*2);// 在數據規律的情況下*0表示第一個,*1第二個。。。
		raf.read(by);
		String name = new String(by);		
		int age = raf.readInt();
		System.out.println(name+">>"+age);
		raf.close();
	}
	public static void method_1() throws IOException{
		RandomAccessFile raf = new RandomAccessFile("E:\\Demo\\test\\a.txt","rw");
		raf.write("張三".getBytes());
		raf.writeInt(90);	
		raf.write("薑末".getBytes());
		raf.writeInt(99);
		raf.write("上官".getBytes());
		raf.writeInt(97);
		raf.close();
	}
}
【7】
基本數據流
操作基本數據類型:   (使用頻率比較高。)
DataInputStream---DataOutputStream

主要用於操作基本數據類型的對象。  [專業操作數據]
(ObjectInputStream、ObjectOutputStream 中 有很多相同方法 不過更專注於操作對象)

構造方法
DataInputStream(InputStream in) 
DataOutputStream(OutputStream out) 
import java.io.*;
class $4DataStream{
	public static void main(String[] args) throws IOException{
		dataRead();//
		dataWrite();
		dReadUTF();//
		dWriteUTF();
		yibanliu();
	}
	public static void yibanliu() throws IOException{
		OutputStreamWriter  osw =
			//new OutputStreamWriter(new FileOutputStream("E:\\Demo\\utf.txt"),"utf-8");
			new OutputStreamWriter(new FileOutputStream("E:\\Demo\\gbk.txt"),"gbk");
		osw.write("你好");
		osw.close();
		//創建 tuf-8 和 GBK 編碼,需要轉換流纔可以辦到
		//DataStream 可以修改版 utf-8  但是必須對應讀取,
		//【a】 處讀取 其他編碼格式,報錯
		//修改版 utf-8      8個字節  
		//	   utf-8    6字節
		//	   Gbk      4字節
	}
	public static void dWriteUTF() throws IOException{
		DataOutputStream dos = new DataOutputStream(new FileOutputStream("E:\\Demo\\utfdate.txt"));
		dos.writeUTF("你好");
		dos.close();
	}
	public static void dReadUTF() throws IOException{
		DataInputStream dos = 
			new DataInputStream(new FileInputStream("E:\\Demo\\utfdate.txt"));
			//new DataInputStream(new FileInputStream("E:\\Demo\\gbk.txt"));//【a】
		String s = dos.readUTF();
		System.out.println(s);
		dos.close();
	}
	public static void dataWrite() throws IOException{
		DataOutputStream dos = new DataOutputStream(new FileOutputStream("E:\\Demo\\987.txt"));
		dos.writeInt(223);
		dos.writeBoolean(true);
		dos.writeDouble(9293.234);//各種直接操作 基本數據類型
		System.out.println("Is ok!~");
		dos.close();
	}
	public static void dataRead() throws IOException{
		DataInputStream dos = new DataInputStream(new FileInputStream("E:\\Demo\\987.txt"));
		int i = dos.readInt();
		boolean b = dos.readBoolean();
		double d = dos.readDouble();//輸出的時候, 也是直接操作即可。
		System.out.println("int:"+i);
		System.out.println("bool:"+b);
		System.out.println("dou:"+d);
		dos.close();
	}
}
能夠直接對基本數據類型進行操作。很簡便

【8】
管道流
PipendInputStram --PipendOutputStream  
輸入輸出可以直接進行連接,通過結合線程使用。

、、、、、、、、、、、、、
定義2個線程,我們知道,他們運行的時候隨機性非常強
誰都有可能先執行。
通過在寫入前設定寫入sleep(6000)  秒 ,多次運行
我們發現,不論是先執行讀,還是寫 都不重要,最後終究會先執行寫,然後再打印

這就是管道流的特點。
----------
1、創建兩個線程,初始化連接管道流
2、創建管道流,並進行連接。並傳遞給 線程
3、啓動線程。
//讀取線程
class read implements Runnable  // 多線程{
	private PipedInputStream in;
	read(PipedInputStream in ){//3、 初始化連接到 輸出管道流
		this.in = in;
	}
	//1、實現 Runnable 成爲多線程  2、覆蓋 run 方法
	public void run(){
		//4、記住只能try 不能 throws
		try{
			//、讀取方法就不用在多說了,因爲示例,數據短,就沒用while
			byte [] by = new byte[1024];
			int len = 0;
			System.out.println("讀取前:");
			len = in.read(by);
			//System.out.println("讀取後:");
			String s = new String(by,0,len);
			System.out.println(":"+s);
			in.close();
		}
		catch (IOException e){
			throw new RuntimeException("沒有讀取到信息");
		}
	}
}
// 寫入線程  --基本步奏同上。
class write implements Runnable{
	private PipedOutputStream out;
	write(PipedOutputStream out){
		this.out = out;
	}
	public void run(){
		try{
			System.out.println("6秒後開始寫入:");
			Thread.sleep(6000);//爲了演示多態性讓等待5秒後在寫
			out.write("wo yao fei de geng gao".getBytes());
			out.close();
		}
		catch (Exception e){
			throw new RuntimeException("沒有寫入信息");
		}
	}
}
class $2PipedStream {
	public static void main(String[] args) throws Exception{
		PipedInputStream in = new PipedInputStream();
		PipedOutputStream out = new PipedOutputStream();
		in.connect(out);//管道連接
		read r = new read(in);
		write w =  new write(out);
		//啓動線程
		  //全寫方法:
		//Thread t1 =new Thread(r);
		//t1.start();
		//Thread t2 = new Thread(w);
		//t2.start();  
		//	簡寫--內部類寫法:
		new Thread(r).start();
		new Thread(w).start();
	}
}

在IO中設計到集合的是;SepenceInputStream
在IO中涉及到多線程的是:管道流
【9】
字節數組流
用於操作字節數組的流對象:
ByteArrayInputStream 在構造時,需要接收數據源,而且數據源是一個字節數組

ByteArrayOutputStream 在構造時,不用定義數據目的,因爲該對象中已經在內部封裝
了可變長度的字節數組,這就是數據目的。
因爲這兩個流對象 都操作的是數組,並沒有使用系統資源,
所以不用進行 close 關閉。
-------------------------
在流操作規律講解時:
源設備:
鍵盤 System.in  
硬盤 FileStream
內存 ArrayStream
目的設備:
控制檯 System.out
硬盤   FileStream
內存ArrayStream
------------------------------------
發現:【操作【字節】數組】 、【操作【字符】數組】、【操作字符串】 都基本相同。
---------------------------------------------------------------------------------------------------
【字節】數組: ByteArrayInputStream---   ByteArrayOutputStream
---------------------------------------------------------------------------------------------------
【字符】數組:CharArrayReader  ------  CharArrayWriter
---------------------------------------------------------------------------------------------------
【字符串】 StringReader ----    StringWriter 
---------------------------------------------------------------------------------------------------

import java.io.*;
class $5ByteArrayStream {
	public static void main(String[] args) throws IOException{
		byteArray();
		charArray();
		string();
	}
	//【操作【字節】數組】
	public static void byteArray() throws IOException{
		ByteArrayInputStream bais = new ByteArrayInputStream("afdfgfdgdfgd".getBytes());
		ByteArrayOutputStreambaos = new ByteArrayOutputStream();
		int len = 0;
		while((len = bais.read())!=-1){
			baos.write(len);
		}
		System.out.println(baos.size());
		System.out.println(baos.toString());
	}
	//【操作【字符】數組】
	public static void charArray() throws IOException{
		CharArrayReader car = new CharArrayReader("fdafergr".toCharArray());
		CharArrayWriter caw = new CharArrayWriter();		
		int len = 0 ;
		while((len = car.read())!=-1){
			caw.write(len);
		}
		System.out.println(caw.size());
		System.out.println(caw.toString());
	}
	//【操作字符串】
	public static void string() throws IOException{
		StringReader sr = new StringReader("ccccccccccccccccMMMM");
		StringWriter sw = new StringWriter();
		int len = 0 ;
		while((len = sr.read())!=-1){
			sw.write(len);
		}
		System.out.println(sw.toString());
		//可以讀取一行。
		//StringWriter sw2 = new StringWriter();
		//char [] ch = new char [1024];
		//while((len = sr.read(ch))!=-1){
		//	sw2.write(ch,0,len);
		//}
		//System.out.println("SW2:"+sw.toString());
		
	}
}
【10】
操作流 、序列化
ObjectInputStream--ObjectOutputStream
Serializable
操作流 、序列化
(對象被產生後存在於堆裏面,而一旦使用完之後,就會消失,如何將對象存儲到硬盤中呢)
ObjectInputStream--ObjectOutputStream  相互對應一個存,一個取
void writeObject(Object obj)--Object readObject()  方法也是對應的。
注意:
1、被操作的對象必須實現 Serializable 接口
這接口太爽了,只需要實現,其他什麼也不用寫
2、自定義序列號
static final long serialVersionUID = 42L;  42L爲自定義序列號
3、靜態成員變量不能被序列號
(靜態在方法區,能被序列的都在堆內存中)
   普通成員變量想要不被序列號必須加關鍵字:   
這樣該成員不被序列號,但存在與堆內存中。

結合 $1Person 完成 操作流 、序列化 的演示

比如,添加一個靜態成員變量
(這個時候,在 $1Person 添加靜態成員,並初始化到構造方法中 運行發現 是一個null )
爲什麼呢
因爲 序列號是根據 成員變量生成的,是在堆內存中

而靜態成員變量是在方法區中所以不可被序列化   
那麼,可不可以讓 普通成員變量也不被序列化呢?
java 提供了一個關鍵字:   transizent    
 
(給年紀 用該關鍵字修飾  發現  打印的年紀 爲0    )

發現,當 $1Person 設定完成後,進行操作流生成文件並能閱讀
而一旦, $1Person 發生變化時,該文件不可被讀取了,

因爲 Serializable 根據 $1Person 的成員變量,生成了一個 序列號
當 成員變量發生變化時候,序列號會隨着變化。 所以不能被閱讀

爲了避免這種情況,可以給類設定一個固定序列號,以保證唯一性:
在  $1Person 設定靜態成員變量
static final long serialVersionUID = 42L;

最後,一般文件都存爲  .Object  (因爲文件無法閱讀,都是亂碼 )
import java.io.*;
class ObjectStream {
	public static void main(String[] args)  throws Exception{
		//writrObj();
		readerObj();
	}
	public static void writrObj() throws IOException{
		ObjectOutputStream oos = 
			new ObjectOutputStream(new FileOutputStream("E:\\Demo\\obj\\Person.Object"));
		
		oos.writeObject(new Person("liuh",19,"ch"));
		oos.close();
	}
	public static void readerObj() throws Exception{
		ObjectInputStream ois = 
			new ObjectInputStream(new FileInputStream("E:\\Demo\\obj\\Person.Object"));
		Person p =(Person) ois.readObject();
		System.out.println(p);
	}
}
class Person implements Serializable{
	String name="111";
	transient nt age=22;   //不序列化關鍵字
	static String guo="bbq";
	Person(String name, int age,String guo){
		this.name = name;
		this.age = age;
		this.guo = guo;
	}
	public String toString(){
		return name+">>"+age+":"+guo;
	}
}
【11】
字符編碼表
1/
-----------------------------
字符流的出現爲了方便操作字符
更重要的是加入了編碼轉換
通過子類轉換流完成
InputStreamReader
OutoutStreamWriter
在兩個對象進行構造時,可以加入字符集也就是編碼表
----------------------------------------------------------
什麼是編碼表?
1、計算機只能識別二進制數據,早期由來是電信號
2、爲了方便應用計算機,讓它可以識別各個國家的文字
3、於是將各個國家的文字用數字來表示,並一一對應,形成一張表。
這就是編碼表。
----------------------------------------------------------
常見編碼表:
ASCII 美國標準信息交換碼
用一個字節的7位表示
ISO8859-1 拉丁碼錶。歐洲碼錶
用一個字節的8位表示
GB2312 中國的中文編碼表
GBK 中國的文字編碼表升級,融合了更多的中文文字符號
Unicode 國際標準碼,融合了多種文字
所有文字都是用兩個字節表示
Java語言使用的就是該編碼表
UTF-8 最多用三個字節來表示一個字符
。。。。
----------------------------------------------------------
2/
編碼: 字符串 變 字節數組。
解碼: 字節數組 變 字符串。
String --> byte[] : str.getBytes(charsetName);    
[charsetName  指定編碼表(不指定用默認GBK編碼表)]
byte   -->  String: newString(byte[],charsetName);
-----------------------------------------------
遇到不知道什麼編碼的情況下
可以用“你好” 編碼解碼根據結果:

返回:??     編碼GBK  ----> 解碼UTF-8 
返回:浣犲ソ     編碼UTF-8----> 解碼 GBK

------------
class $7EncodeStream {
	public static void main(String[] args)  throws Exception{
		toEncode_1();
	}
	public static void toEncode_1()throws Exception{
		String s = "我去";		
		byte [] b1 = s.getBytes("GBK");//默認GBK編碼表
		System.out.println(Arrays.toString(b1));//記住數組變字符串的方法。
		String ss = new String(b1,"UTF-8");
		System.out.println("ss:"+ss);
		//出現解碼錯誤的情況-進行UTF-8編碼
		byte [] b2 =ss.getBytes("UTF-8");
		//進行GBK 解碼
		String s3 = new String(b2,"GBK");
		System.out.println(Arrays.toString(b2));
		System.out.println("ss:"+s3);
	}
	public static void toEncode()throws Exception{
		String s = "你好";		
		byte [] b1 = s.getBytes("GBK");//默認GBK編碼表
		//byte [] b1 = s.getBytes("ISO8859-1");//【1】 編碼錯誤的時候,就不要解碼了。
		System.out.println(Arrays.toString(b1));//記住數組變字符串的方法。
		String ss =// new String(b,"GBK");//默認GBK編碼表
					new String(b1,"UTF-8");
		System.out.println("ss:"+ss);
//---------------
		//出現解碼錯誤的情況
		String s2 = new String(b1,"ISO8859-1");//【2】解碼錯誤的時候該如何處理:  再編碼,在再解碼一次:		
		//對s2 進行ISO8859-1 編碼
		byte [] b2 =s2.getBytes("ISO8859-1");
		//進行GBK 編碼
		String s3 = new String(b2,"GBK");
		System.out.println("ss:"+s3);
	}
}

對於 toEncode 中 出現ISO8859-1 解碼錯誤
如果編碼GBK 解碼UTF-8  出現解碼錯誤 根據前面的經驗
進行 對UTF-8 進行編碼 再用GBK 解碼 會是正確結果嗎?

-----發現錯誤,發現第一次編碼的時候是4個字節,而再次編碼的時候卻是9個字節
他的把前面的三位?號有3個,所以對應是9個字節
爲了驗證,我們把“你好”更改爲“哈哈”----發現?號變成了4個,對應變成了12個字節
----因爲GBK和UTF-8 都識別中文,導致的錯亂。
-----------------------------------------------
3/
聯通
String --> byte[] : str.getBytes(charsetName);    
byte   -->  String: newString(byte[],charsetName);
----------------------------------
聯通:
-63 -86 -51-88
轉換二進制,&255  取八位
-63--> 11000001
-86--> 10101010
-51--> 11001101
-88--> 10101000
這個時候,對這個二進制,解碼的時候,

UTF-8 --  最多3個字節,那麼編譯時如何知道是1個還是2 個3個字節?
1、   讀到開頭 0    把這個1字節 帶去查表
2、   讀到開頭 110 會直接跳到下一行 讀取10  然後把這2個字節 帶去查表
3、   讀到開頭 1110 會跳過 讀取 兩行 10 10  然後把這3個字節 帶去查表
----------------------------------
這個時候我們發現,
“聯通”是 GBK 編碼 但是 產生的二進制形式 居然和 UTF-8  一致

這就是爲什麼 會出現亂碼了,因爲識別的時候,爲上列第2中
然後去查UTF-8碼錶。自然就錯了。

class  $8EncodeStream{
	public static void main(String[] args) throws IOException{
		lianTong_1();
	}
	public static void lianTong_1() throws IOException{
		String s = "好號";
		byte [] by = s.getBytes("GBK");
		sop(Arrays.toString(by));
		sop("--------");
		for(byte b : by){
			sop(b);//
			//sop(Integer.toBinaryString(b));//【記住,轉換二進制】。但是太長,只想要後8位怎麼辦?
			sop(Integer.toBinaryString(b & 255));//【記住,& 255】
		}
		sop("--------");
		String s1 = new String(by,"UTF-8");
		sop(s1);
		sop("--------");
		String s2 = new String(by,"GBK");
		sop(s2);
		sop("--------");
	}
	public static void lianTong() throws IOException
	{
		String s = "聯通";
		byte [] by = s.getBytes("UTF-8");
		sop(Arrays.toString(by));	
		String s1 = new String(by,"UTF-8");
		sop(s1);
		sop("--------");
		String s2 = new String(by,"GBK");
		sop(s2);
	}
	public static void sop(Object obj){
			System.out.println(obj);
	}
}
據說,編碼表是 程序員心中永遠的通。。哈哈,不過還體會不到啊。






---------------------------------------------------------------------------------------------------------------------------------------------
---------- android培訓、 java培訓、期待與您交流!----------


----------------------------------------------
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章