黑馬程序員_Java中的IO

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

IO流總結


一、IO流
IO流用來處理設備之間的數據傳輸
Java中的數據全都是用流來操作的,java所有的流都放在IO包中。
分類:流按操作對象分爲兩種:字節流與字符流。
 流按流向分爲:輸入流,輸出流。輸入輸出流一般是對應的,有輸入流就有對應的輸出流。
  輸出流中,指定的目的文件不存在時會新建,存在時會覆蓋。
IO流中的阻塞方法:沒有讀到數據就等。read 
二、字符流和字節流
字符流的抽象基類:Reader , Writer。
字節流的抽象基類:InputStream ,OutputStream。
這四個類的子類命名:這四個父類名作爲子類的後綴名,可以看出屬於這四種流中的哪一種。前綴名錶示流的功能。


字符流和字節流區別:1.字符流向文件存數據時,必定會 用讀到的數據去查找編碼表。所以,用字符流copy圖片時,圖片不能看。
2.字節流寫數據write時,會直接將字節數據寫到目的地。字節流在指定緩衝區(BufferedInputStream)時才刷新。
字符流寫數據write時,數據會先緩衝起來,需要刷新到目的地。原理:字符流內部封裝了字節流,由於它操作的是文本文件,
例如讀一個漢字(2個字節),將讀到的第一個字節緩衝起來,讀完第二個字節後,再去查編碼表,經過刷新將其對應的漢字寫到目的文件。
三、字符流和字節流體系
字符流
Reader抽象類
|---InputStreamReader:字節流轉換爲字符流的橋樑。在創建該流實例時,就要傳入一個要轉換的字節流。構造方法可以指定編碼表。
|---FileReader:文件讀取流。操作文本文件
|---BufferedReader:字符流緩衝區
|---LineNumberReader:跟蹤行號的緩衝字符輸入流。用setLineNumber(int)和getLineNumber()設置和獲取行號。
|---PipedReader管道輸入流,應該連接到管道輸出流;不建議對這兩個對象嘗試使用單個線程,因爲這樣可能死鎖線程。
|---CharArrayReader
Writer:該類中的write方法,只能將數據寫到流中,不能寫到目的地,要寫到目的地,必須進行刷新。
|--OutputStreamWriter:字符流轉換爲字節流的橋樑。最重要的是,構造方法可以指定編碼表,並且只有轉換流能指定編碼表
|---FileWriter:文件寫入流。構造方法使用 默認編碼表GBK
|---BufferedWriter
|---PipedWriter
|---CharArrayWriter
|---PrintWriter 構造方法中要傳入目的,即文件或者輸出流,可以是字節輸出流或者字符輸出流。特點:可以設置自動刷新。
Socket s=new Socket(); PrintWriter out=new PrintWriter(s.getOutputStream(),true);  out.println("nihao");//println方法並沒有向流中寫入結束標記。
字節流
InputStream抽象類(System.in)
|---FileInputStream:操作非文本文件,如圖片、視頻、音樂。
|---FilterInputStream
|---BufferedInputStream
|---DataInputStream
|---ObjectInputStream只有字節流
|---PipedInputStream字節流字符流都有
|---ByteArrayInputStream
OutputStream:該類寫數據時,可以直接將數據寫到目的地,不用刷新,只有用到緩衝區BufferedOutputStream時,才需要刷新。
|---FileOutputStream:文件輸出流,將數據寫入 File 或 輸出流
|---FilterOutputStream
|---BufferedOutputStream
|---PrintStream(System.out)
|---DataOutputStream
|---ObjectOutputStream
|---PipedOutputStream
|---ByteArrayOutputStream
四、方法
1.超類方法
Writer中的方法:
void write(char[] cbuf) 將字符數組cbuf寫入到輸出流(經刷新再寫入目的地)。 
abstract  void write(char[] cbuf, int off, int len) 寫入字符數組的某一部分。 
void write(int c) 寫入單個字符。寫入的是c的低16位,16個高位被忽略。 
void write(String str) 寫入字符串。 
void write(String str, int off, int len) 寫入字符串的某一部分。

abstract  void close() 關閉此流,但要先刷新它。 
abstract  void flush() 刷新該流的緩衝。 
Reader中的方法:
int read() 一次讀一個字符。而且會自動往下讀。返回的是,字符對應的整數,讀完時返回-1 
int read(char[] cbuf) 將數據讀入到字符數組cbuf中,返回的是讀到的字符個數 
abstract  int read(char[] cbuf, int off, int len) 將數據讀入到數組的某一部分。  

abstract  void close() 關閉該流並釋放與之關聯的所有資源。
---------------------------------------------------------------------------------------------
OutputStream中的方法:
void write(byte[] b) 將字節數組b寫入此輸出流,不用刷新可直接到目的地。 
void write(byte[] b, int off, int len) 
abstract  void write(int b) 寫入單個字節。寫入的是b的低8位,24個高位被忽略。注意:將read方法提升後的整數,強轉了。

void close()  
void flush(); 
InputStream中的方法:與字符流Reader的區別就是:將字符換成字節。讀取一個字符換成讀取一個字節,讀取一個字符數組換成讀取一個字節數組。
abstract  int read() 從輸入流中讀取數據的下一個字節。返回 0 到 255 範圍內的 int 字節值。
即,將讀取到的字節轉換成int返回,當讀取完時返回-1.注意:read方法在做提升。這樣做可以避免讀取到的字節與判斷讀取結束的標記-1相撞。
提升原理:在8個二進制位前面補24個0,即將讀取到的字節 與上整數255。
11111111 讀取的字節正好爲-1
11111111-11111111-11111111-11111111 提升成int:-1
 & 00000000-00000000-00000000-11111111 &255
00000000-00000000-00000000-11111111 要返回的數,從-1成爲了255.
int read(byte[] b) 從輸入流中讀取一定數量的字節,並將其存儲在緩衝區數組 b 中,返回讀取的字節個數。 
int read(byte[] b, int off, int len)

int available();返回的是要讀取的文件的字節數,即文件的大小,包括換行符。 
   2.子類方法
2.1 FileWriter中的方法:都繼承自父類
在創建一個文件寫入流對象時,就要傳入一個目的文件。當文件已存在時,就覆蓋;文件不存在時,就在指定目錄下新建。
當傳入的文件路徑錯誤時,會拋出IOException
 FileReader中的方法:都繼承自父類
在創建一個文件讀取流對象時,同樣要傳入一個源文件。如果源文件不存在,會拋出異常 FileNotFoundException
常用代碼:char[] buf=new char[1024];
 int num=0;
 while((num=fileReader.read(buf))!=-1){
  fileWriter.write(buf,0,num);
 }
 fileWriter.close();
 fileReader.close();
使用完記得關閉流。
2.2 緩衝區。
 特點:1.在創建緩衝區之前,必須要先有流對象。因爲緩衝區的出現就是爲了提高流的讀寫效率。
   2.關閉了緩衝區,就是在關閉緩衝區中的流對象。就不用再關閉流對象。原理:緩衝區的close方法,調用了關閉流的代碼
   3.只要用到BufferedWriter緩衝區,就必須刷新
   注意:數組只是起到緩衝的作用,並不是緩衝區。
 字符流緩衝區:  
 BufferedWrite的方法: void newLine();跨平臺的換行方法
 BufferedReader的方法:String readLine(); 讀取一行。不包含任何行終止符。返回的是讀取到的數據,當讀取完畢時,返回null。
 字節流緩衝區。
   BufferedOutputStream:繼承自OutputStream
   BufferedInputStream: 繼承自InputStream
五、流操作的基本規律
流對象有很多,該選用哪一個?
通過三個明確來完成:
1,明確源和目的。
源:輸入流。InputStream  Reader
目的:輸出流。OutputStream  Writer。
2,操作的數據是否是純文本。
是:字符流。 打開文件能看懂的 是純文本。如.txt、.java、鍵盤錄入的數據、打印到控制檯的數據等 
不是:字節流。 打開文件看不懂的 不是純文本的:圖片、音樂、視頻
3,當體系明確後,在明確要使用哪個具體的對象。
通過設備來進行區分:
源設備:內存,硬盤。鍵盤(字節流 System.in,但讀取的數據是能看懂的純文本,所以要進行字節流轉換成字符流)
目的設備:內存,硬盤,控制檯(字節流 System.out)。
注意:有必要時,要進行流轉換。
例如:將鍵盤錄入的數據保存到文件中。
源:1.InputStream  Reader 2.是能看懂的純文本,字符流。
3.鍵盤 字節流,此時爲了方便操作純文本數據,應將字節流轉換爲字符流。BufferedReader br=new BufferedReader(new InputStreamReader(System.in));
擴展一下,將鍵盤錄入的數據按照指定的編碼表(utf-8),將數據存到文件中。
目的:OutputStream  Writer
是否是存文本?是!Writer。
設備:硬盤。一個文件。使用 FileWriter。
但是FileWriter是使用的默認編碼表。GBK.

但是存儲時,需要加入指定編碼表utf-8。而指定的編碼表只有轉換流可以指定。
所以要使用的對象是OutputStreamWriter。
而該轉換流對象要接收一個字節輸出流。而且還可以操作的文件的字節輸出流。FileOutputStream

OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("d.txt"),"UTF-8");

需要高效嗎?需要。
BufferedWriter bufw = new BufferedWriter(osw);


所以,記住。轉換流什麼使用?字符和字節之間的橋樑。
通常,涉及到字符編碼轉換時,需要用到轉換流。
六、IO的異常處理
凡是與設備數據有關係的數據都會發生IO異常。
public static void copyFile_Exception(){
BufferedReader br=null;
BufferedWriter bw=null;
try{
br=new BufferedReader(new FileReader("f:\\a.txt"));
bw=new BufferedWriter(new FileWriter("f:\\aa.txt"));
String line=null;
while((line=br.readLine())!=null){
bw.write(line);
bw.newLine();
bw.flush();
}
}catch(Exception e){
e.printStackTrace();
}
finally{
//關閉多個流時,要分別進處理
try{
br.close();
}
catch(Exception e){
e.printStackTrace();
}
try{
bw.close();
}catch(Exception e){
e.printStackTrace();
}
}
}
七、其它類
1.Properties容器:獲取配置文件內容時使用
特點:1.是Map體系中Hashtable的子類,具備map集合的特點
 2.存儲的鍵值對都是字符串。
 3.集合中和IO技術相結合的集合容器。
 4.用於鍵值對形式的配置文件。可以通過讀取流直接將配置文件中的鍵值對直接加載到內存中。那麼在加載數據時,需要數據有固定格式:鍵=值。
   
方法:
 添加: Object setProperty(String key, String value) ;向集合中添加鍵值對。
 獲取: String getProperty(String key)根據鍵獲取值。如果在此屬性列表中未找到該鍵,則接着遞歸檢查默認屬性列表及其默認值。如果未找到屬性,則此方法返回 null。
  void load(InputStream inStream) 將文件中的鍵值對(屬性列表)通過流直接加載到內存的集合當中。
  void load(Reader reader)  
  void store(OutputStream out,String comments)與load方法相反。將集合當中的鍵值對通過流寫入到文件(屬性列表)當中。
  comments - 對屬性列表的描述。前面加了個#,是不會被加載到集合當中的。
void store(Writer writer, String comments) 
  Set<String> stringPropertyNames() 返回此屬性列表中的鍵集,其中該鍵及其對應值是字符串,如果在主屬性列表中未找到同名的鍵,則還包括默認屬性列表中不同的鍵。  
方法練習:
//將屬性列表中的鍵值對加載到集合當中
FileReader fr = new FileReader("f:\\屬性列表.txt");
Properties prop=new Properties();
prop.load(fr);
prop.list(System.out);
//將集合中的鍵值對寫入到屬性列表中
prop.setProperty("zhangsan", "20");
FileWriter fw = new FileWriter("f:\\屬性列表.txt");
prop.store(fw, "描述信息");
2.ObjectOutputStream和ObjectInputStream:操作對象的字節流。只有字節流沒有字符流。
 ObjectOutputStream:
  構造方法 :ObjectOutputStream(OutputStream out) 
  方法:void writeObject(Object obj) 將對象obj寫入到關聯的OutputStream指定的文件當中。
   方法當中含有寫入8中基本數據類型的方法:writeInt(int val)(寫入一個大於255的數據時用此方法)、writeFloat(float val)...
 ObjectInputStream:
  構造方法 :ObjectInputStream(InputStream in) 
  方法: Object readObject() 讀取在文件當中存儲的對象。方法當中含有讀取8中基本數據類型的方法:
 注意:要被操作的對象必須被序列化,即所屬的類  要繼承Serializable接口。
  序列化特點:1.靜態成員變量 不能序列化。
  2.非靜態成員變量 不想參加序列化時,可以在前面加上transient。
     3.在類中加入代碼 public static final long serialVersionUID = 42L;時,就是自己在給類編序列號。
       如果沒有該代碼時,Java會自動編號。此時,如果對參加序列化的成員進行修改,那麼文件中保存的對象讀取出來時就不能使用了。
 
 
  存取對象演示:
  ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("obj.txt"));
  oos.writeObject(new Person("lisi0",399,"kr"));  
  oos.close();
  ObjectInputStream ois = new ObjectInputStream(new FileInputStream("obj.txt"));
Person p = (Person)ois.readObject();
System.out.println(p);
ois.close();

class Person implements Serializable
{
public static final long serialVersionUID = 42L;

private String name;
transient int age;
static String country = "cn";
Person(String name,int age,String country)
{
this.name = name;
this.age = age;
this.country = country;
}
public String toString()
{
return name+":"+age+":"+country;
}
}
3.管道流:PipedWriter、PipedReader和PipedOutputStream、PipedInputStream
 1.用構造方法連接,或connect方法
 2.它的寫方法,是將數據寫到了管道中;讀取數據,是從管道中讀取。
 3.結合線程使用。
 
 PipedReader pr=new PipedReader();
 PipedWriter pw=new PipedWriter(pr);
 /*或者 PipedWriter pw=new PipedWriter(); pw.connect(pr);*/
 class Write implements Runnable{
  private PipedOutputStream out=null;
  PipedOutputStream(PipedOutputStream out){ this.out=out; }
  public void run(){
  try{
  syso("開始寫入數據,等待6秒後。");
  Thread.sleep(6000);
  out.write("piped is coming!".getBytes());
  out.close();
  }
  catch(Exception e){ syso("管道關閉失敗"); }
 
  }
 }
 class Read implements Runnable{
  private PipedInputStream in=null;
  PipedInputStream(PipedInputStream in){ this.in=in; }
  public void run(){
  byte[] buf=new byte[1024];
  int len=0;
  while((len=in.read(buf))!=-1){
  syso(new String(buf,0,len));
  }
  }
 }
 class PipedStreamDemo{
  public static void main(String[] args){
  PipedInputStream in=new PipedInputStream();
  PipedOutputStream out=new PipedOutputStream(in);
 
  Read r=new Read(in);
  Write w=new Write(out);
  new Thread(r).start();
  new Thread(w).start();
  }
 }
   
4.RandomAccessFile隨機訪問文件
 特點:1.該類不是IO體系中子類,而是直接繼承自Object。但是它是IO包中成員。因爲它具備讀和寫功能,原理是內部封裝了字節輸入流和輸出流。
2.內部封裝了一個數組,而且通過指針對數組的元素進行操作。也就是將文件中的內容放入到了數組中,可以隨機訪問數組中的任意一段數據。
 可以通過getFilePointer獲取指針位置,同時可以通過seek改變指針的位置。
3.該類只能操作文件。而且操作文件還有模式:只讀r,,讀寫rw等。
 如果模式爲只讀 r。不會創建文件。會去讀取一個已存在文件,如果該文件不存在,則會出現異常。而普通讀取流,文件不存在時也創建。
 如果模式rw。操作的文件不存在,會自動創建。如果存在則不會覆蓋。
4.可以隨機讀寫文件。在指定的位置寫一段數據,前面可能還沒寫上數據。應用:用多線程,每個線程在指定位置段寫數據,實現多線程下載。    
 方法:1.讀寫基本數據類型的方法:writeInt(int v)、readInt()、writeByte(int v)...
  2. long getFilePointer() 獲取指針當前位置。
    void seek(long pos)改變指針位置。
  3. int skipBytes(int n) 讓指針跳過n個字節。只能向後跳,不能回跳。
5.DataInputStream、DataOutputStream:操作基本數據類型的流。
 特點:在創建實例對象時,就要傳入流。
 方法:1.writeInt(int v)、readInt()、writeByte(int v)...
  2.writeUTF(String s)將字符串s按照utf修改版寫入文件當中。讀取時必須按utf修改版讀取,即String readUTF().
 
 方法演示: DataOutputStream dos=new DataOutputStream(new FileOutputStream("gbk.txt"));
  dos.writeInt(234);
dos.writeBoolean(true);
dos.writeDouble(9887.543);
dos.close();
DataInputStream dis = new DataInputStream(new FileInputStream("data.txt"));
int num = dis.readInt();
boolean b = dis.readBoolean();
double d = dis.readDouble();
System.out.println("num="+num);System.out.println("b="+b); System.out.println("d="+d);
dis.close();
   
    DataOutputStream dos=new DataOutputStream(new FileOutputStream("utf.txt"));
  dos.writeUTF("你好");
  DataInputStream dis = new DataInputStream(new FileInputStream("utf.txt"));
  String s=dis.readUTF();
6.ByteArrayInputStream 、ByteArrayOutputStream:用於操作字節數組的流對象。應用:將文件中的數據存放到內存中的數組當中。
 特點:1.ByteArrayInputStream在構造的時候,需要接收數據源。而且數據源是一個字節數組。
   ByteArrayOutputStream在構造的時候,不用定義數據目的,因爲該對象中已經內部封裝了可變長度的字節數組。這就是數據目的地。
  2.因爲這兩個流對象都操作的數組,並沒有使用系統資源。所以,不用進行close關閉。

在流操作規律講解時:
源設備,
鍵盤 System.in,硬盤 FileStream,內存 ArrayStream。
目的設備:
控制檯 System.out,硬盤FileStream,內存 ArrayStream。

用流的讀寫思想來操作數據。

                                                                  ----------------------詳細請查看:http://edu.csdn.net/heima---------------------------------------

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