.Net轉Java自學之路—基礎鞏固篇十五(IO)
IO:
IO流按操作分為:位元組流、字元流。
所有的資料都是以位元組體現的,後期產生了字元流。因為字元資料涉及到了編碼問題。所以在字元流物件中加入的編碼機制。如果處理的資料都是字元資料,那麼可以使用字元流物件來完成。
IO流按流分為:輸入流、輸出流。
字元流:
字元流操作檔案資料,讀取/寫入時需要匹配編碼表。
字元流的抽象基類:Reader、Writer
//簡單例項:字串寫入檔案 FileWriter fw=new FileWriter(path);//替換指定檔案 new FileWriter(path,true);續寫指定檔案 fw.write(內容);//將資料寫入快取fw.flush();//重新整理快取中的資料到指定檔案 fw.close();//先重新整理快取中的資料到檔案,然後釋放資源
寫入資料換行符:\r\n(windows);\n(LINUX)
IO異常(IOException)處理方式
try{ }catch(IOException ex){ }finally{ }
例項:FileReader、FileWriter
//讀取檔案 FileReader fr=new FileReader("path"); int ch=0; while((ch=fr.read())!=-1){ char c=(charView Code)ch; } fr.close(); //或 char[] buf=new char[1024];//緩衝區的長度; int len=0; while((len=fr.read(buf))!=-1){ new String(buf,0,len); } //複製文字檔案:先讀後寫 FileReader fr=new FileReader(path); FileWriter fw=new FileWriter(corpy-path); char[] buf=new char[1024]; int len=0; while((len=fr.read(buf))!=-1){ fw.write(buf,0,len); } fw.close(); fr.close();
例項:BufferedReader、BufferedWriter 緩衝技術
//BufferedWriter 緩衝區寫入 FileWriter fw=new FileWriter(path); BufferedWriter bw=new BufferedWriter(fw); bw.write(內容); bw.newLine();//寫入換行符。不同平臺的換行符操作 bw.flush(); bw.close(); //BufferedReader 緩衝區讀取 FileReader fr=new FileReader(path); BufferedReader br=new BufferedReader(fr); String strline=null; while((strline=br.readLine())!=null){//readLine() 一次讀取一行 System.out.println(strline); } //緩衝區複製 FileReader fr=new FileReader(path); FileWriter fw=new FileWriter(corp-path); BufferedReader br=new BufferedReader(fr); BufferedWriter bw=new BufferedWriter(fw); string strline=null; while((strline=br.readLine())!=null){ bw.write(strline); bw.newLine(); bw.flush(); } bw.close(); bw.close();View Code
裝飾設計模式:
字元流的緩衝區時為了提高效率而存在。緩衝區的出現提供了比以前流物件功能更強的函式。故、當對類的功能進行增強時,可稱為對該類的裝飾。
同時,裝飾類的出現具備靈活性。
裝飾和繼承的區別:
裝飾:裝飾設計模式是一種解決某一類問題的思想,該類問題的有效解決方案。解決給該類提供增強功能的問題。
繼承:是面向物件的特徵之一。
繼承會讓體系變的臃腫,而裝飾更為靈活。
裝飾設計模式的出現可以對一組類進行功能的增強;而裝飾類本身也是該體系的一個子類。
程式碼體現:通常情況下,裝飾類一般不單獨存在,都是通過建構函式接收被裝飾的物件。基於被裝飾的物件的功能,並對外提供增強型的功能。
在IO中有很多裝飾設計模式的體現。如:BufferedReader、BufferedWriter
//例項:裝飾ClassName 物件的類,稱為裝飾類,只為增強ClassName的功能而出現 class ClassName{ void method(){ //Code... } } class NewClassName{ private ClassName cn; NewClassName(ClassName cn){ this.cn=cn; } void newMethod(){ cn.method(); //Code... } }
LineNumberReader類:
FileReader fr=new FileReader(path); LineNumberReader lnr=new LineNumberReader(fr); String strline=null; lnr.setLineNumber(int);//設定開始行號 while((strline=lnr.readLine())!=null){ lnr.getLineNumber();//讀取行號 System.out.println(strline); }
凡是直接操作資料的流物件,或操作檔案的流物件,都是常用的流物件。因為檔案就是資料體現形式,而操作流就是提高效率,所以帶緩衝的流物件,也是常用物件。
位元組流:
位元組流的抽象基類:InputStream、OutputStream
例項:寫入、讀取
//寫入 FileOutputStream fos=new FileOutputStream(path); byte[] bytes="內容".getBytes();//字串編碼 fos.write(bytes);//位元組流寫入指定檔案。因為位元組流物件中不存在緩衝區。 fos.close(); //讀取 FileInputStream fis=new FileInputStream(path); byte[] bytes=new byte[1024]; int len=0; while((len=fis.read(bytes))!=-1){ new String(bytes,0,len); } fis.close(); //複製 FileInputStream fis=new FileInputStream(path); FileOutputStream fos=new FileOutputStream(corp-path); byte[] bytes=new byte[1024]; int len=0; while((len=fis.read(bytes))!=-1){ fos.write(bytes,0,len); } //或 byte[] bytes=new byte[fis.available()];//獲取和流相關聯的位元組數。操作小檔案。 fis.read(bytes); fos.read(bytes); fos.close(); fis.close();View Code
例項:緩衝區
//複製 FileInputStream fis=new FileInputStream(path); BufferedInputStream bis=new BufferedInputStream(fis); FileOutputStream fos=new FileOutputStream(copy-path); BufferedOutputStream bos=new BufferedOutputStream(fos); int len=0; while((len=bis.read())!=-1){ bos.write(len); } bos.close(); bis.close();View Code
例項:鍵盤讀取
InputStream ins=System.in;//獲取鍵盤輸入位元組流 int len=ins.read();//讀取鍵盤錄入
轉換流:
InputStreamReader:位元組流 通 字元流
讀取鍵盤資料:一次讀取一行
BufferedReader br=new BufferedReader(new InputStreamReader(System.in));
OutputStreamWriter:字元流 通 位元組流
輸出控制檯
BufferedWriter bw=new BufferedWriter(new OutputStreamWriter(System.out));
例項:
InputStream ins=System.in;//獲取鍵盤的輸入位元組流 InputStreamReader isr=new InputStreamReader(ins);//將位元組流轉字元流 BufferedReader br=new BufferedReader(br);//用緩衝區提高效率 //或 簡化 BufferedReader br=new BufferedReader(new InputStreamReader(System.in)); String line=null; while((line=br.readLine())!=null){ if(line.equals("over"))//自己搞的結束標記 break; System.out.println(line); } OutputStream os=System.out; OutputStreamWriter osw=new OutputStreamWriter(os); BufferedWriter bw=new BufferedWriter(osw); //或 簡化 BufferedWriter bw=new BufferedWriter(new OutputStreamWriter(System.out)); String line=null; while((line=br.readLine())!=null){ if(line.equals("over")); bw.write(line); bw.newLine(); bw.flush(); } bw.close(); br.close();View Code
轉換流的編碼操作
FileReader fr=new FileReader(path); OutputStreamWriter osw=new OutputStreamWriter(new FileOutputStream(new-path),"utf-8/gbk"); char[] buf=new char[1024]; int len=0; while((len=fr.read(buf))!=-1){ osw.write(buf,0,len); } osw.close(); fr.close();
流的操作規律:
明確物件使用:
1、明確 資料來源、資料目的(資料匯)
資料來源:InputStream、Reader
資料匯:OutputStream、Writer
2、明確資料內容是否屬於文字
資料來源-是:Reader
資料匯-是:Writer
否、則:InputStream、OutputStream
若不能明確只能使用位元組流。
3、明確具體裝置
常見的資料來源裝置:鍵盤(System.in)、記憶體(陣列)、硬碟(File開頭流物件)
常見的資料匯裝置:控制檯(System.out)、記憶體(陣列)、硬碟(File開頭流物件)
4、明確是否需要提高效率
是:使用帶 Buffer 物件
File類:
路徑分隔符:File.separator
目錄分隔符:File.pathSeparator Windows(;) LINUX(:)
File常見基本功能
//建立: boolean createNewFile(); File createTempFile();//建立臨時檔案 boolean mkdir();//建立資料夾。若存在,不建立 boolean kmirs();//建立多級資料夾,可以在已存在目錄中繼續建立 //刪除 boolean delete();//刪除檔案/目錄,刪除目錄時,目錄中內容要先刪除 void deleteOnExit();//程式退出刪除 //判斷 boolean isFile();//是否為檔案 boolean isDirectory();//是否為目錄 boolean isAbsolute();//是否為絕對路徑 boolean exists();//檔案是否存在 boolean canExecute();//檔案是否為可執行檔案 boolean isHidden();//檔案是否為隱藏檔案 //獲取 String getAbsolutePath();//獲取絕對路徑 String getPath();//獲取相對路徑 String getParent();//獲取父目錄 String getName();//獲取檔名 long length();//檔案長度;位元組數。不針對資料夾 long lastModified();//檔案最後一次修改時間,毫秒值 //重新命名 removeTo();//重新命名;也可以進行檔案的移動(剪下) //檔案過濾 list();//目錄中的檔案和資料夾列表 list(FilenameFilter file);//過濾檔案
遞迴:
自身呼叫自身
當一個功能在被重複使用時,該功能的引數在隨著功能變化,這時可以使用遞迴完成。
注:
1、控制遞迴次數,不要過大
2、必須要有條件,否則,會棧記憶體溢位
例項:遞迴、刪除帶內容目錄
void deletedir(File dir){ File[] files=dir.listFiles(); for(int f=0;f<files.length;f++){ if(files[f].isDirectory()){ deleteDir(files[f]) } else { files[f].delete(); } } dir.delete(); }
列印流:
PrintWriter(字元流)、PrintStream(位元組流)
PrintStream:
用不會丟擲IO異常IOException
位元組流中的列印流,可以直接操作裝置的流物件。
建構函式的 引數 可接收:1、字串路徑。2、File物件。3、位元組輸出流
PrintWriter:
字元流中的列印流。
建構函式的 引數 可接收:1、字串路徑。2、File物件。3、位元組輸出流。4、字元輸出流。
列印流可以直接操作檔案,是比較常用的流物件
列印流的特點:在於提供了N多的print方法。可以列印任何資料型別。
管道流:
PipedInputStream、PipedOutputStream
管道流是需要和多執行緒相結合流物件。讀取流和寫入流可以進行連線;但是需要被多執行緒操作。因為read()是阻塞式方法。容易引發死鎖。
PipedInputStream in=new PipedInputStream(); PipedOutputStream out=new PipedOutputStream(); in.connect(out);//管道輸入流於管道輸出流連線 new Thread(new Input(in)).start(); new Thread(new Output(out)).start(); class Input implements Runnable{ private PipedInputStream in; Input(PipedInputStream in){ this.in=in } public void run(){ try{ byte[] butes=new byte[1024]; int len=0; while((len=in.read(bytes))!=-1){ new String(bytes,0,len); in.close(); } } catch(IOException ex){ throw new RuntimeException("提示資訊"); } } } class Output implements Runnable{ private PipedOutputStream out; Output(PipedOutputStream out){ this.out=out; } public void run(){ try{ out.write("管道流資料".getBytes()); out.close(); } catch(IOException ex){ throw new RuntimeException("提示資訊"); } } }View Code
Properties類:
集合中的 Properties ( Map>>>>HashTable>>>>Properties ) 儲存的鍵和值都是字串型別的資料,通常用配置檔案的定義。
Properties pro=new Properties(); pro.setProperty("key","value");//新增元素 Set<String> keySet=pro.stringPropertyNames();//獲取鍵的Set集合 for(String key:keySet){ System.out.println(key); String value=pro.getProperty(key); } //Properties 特有的list() pro.list(System.out);//列印到控制檯,只要引數是System.out /* Properties 類的list()和列印流PrintStream相結合使用,會直接將Properties 集合中的元素寫入檔案中。 注:需丟擲IO異常 */ PrintStream psOut=new PrintStream(path); pro.list(psOut); pro.close();
//獲取系統屬性集 Properties pro=System.getProperties(); pro.list(System.out或new PrintStream(path));
//硬碟中的鍵值對載入到集合Properties 中 Properties pro=new Properties(); FileInputStream fis=new FileInputStream(path); pro.load(fis);//使用集合的特有方法load(),將流的特有規則(鍵值對)資訊儲存到集合中;注:流中資訊的規則必須是鍵值對,用“=”號隔開。 pro.setProperties("k","v"); FileOutputStream fos=new FileOutputStream(path); pro.store(fos,"非中文註釋資訊");//將載入到集合中的資料改變後重新載入到檔案中
例項:將指定檔案型別的路徑儲存到一個檔案中
//全稱無try異常處理,只簡述方法 void main(String[] args)throws IOException{ File dir=new File(目錄-path); File destfile=new File(dir,目的-path); File[] files=getFileList(dir,"副檔名"); pathToFile(files,destfile); } //對目錄進行遞迴 void getDir(File dir,List<File> list,String exte){ File[] files=dir.listFiles(); for(File files:files){ if(files.isDirectory()){ getDir(files,list,exte); } else{ if(files.getName().endsWith(exte)) list.add(files);//滿足條件,載入到集合中 } } } //獲取符合條件的物件集合 List<File>/File[] getFileList(File dir,String exte){ List<File> list=new ArrayList<File>(); getDir(dir,list,exte); return list; //或 List<File> list=new ArrayList<File>(); getDir(dir,list,exte); File[] files=list.toArray(new File[list.size()]); return files; } //目錄儲存檔案 void pathToFile(File[] files) throws IOException{ BufferedWriter bw=new BufferedWriter(new FileWriter(path)); for(File files:files){ bw.write(file.getAbsolutePath());//取並寫絕對路徑 bw.newLine(); bw.flush(); } bw.close(); }View Code
序列流:
SequenceInputStream:位元組讀取流,對多個流進行合併。每個流之間用-1結尾。
Vector<FileInputStream> v=new Vector<FileInputStream>(); //或 ArrayList<FileInputStream> v=new ArrayList<FileInputStream>(); v.add(new FileInputStream(path1)); v.add(new FileInputStream(path2)); v.add(new FileInputStream(path3)); Enumeration<FileInputStream> en=v.elements(); //或 /* ArrayList無法直接獲取列舉物件,所以通過匿名內部類定義該介面的子類物件。 因為列舉和迭代的功能重複。故、通過ArrayList獲取迭代;列舉使用迭代即可完成。 */ final Iterator<FileInputStream> iter=v.iterator(); Enumertion<FileInputStream> en=new Enumertion<FileInputStream>(){ public boolean hasMoreElements(){ return iter.hasNext(); } public FileInputStream nextElement(){ return iter.next(); } } SequenceInputStream sis=new SequenceInputStream(en); byte[] bytes=new byte[1024]; int len=0; while((len=sis.read(bytes))!=-1){ new String(bytes,0,len); } sis.close(); //合併檔案 /* ......省略的Code */ FileOutputStream fos=new FileOutputStream(new-path); byte[] bytes=new byte[1024]; int len=0; while((len=sis.read(bytes))!=-1){ fos.write(bytes,0,len); } fos.close(); sis.close();
例項:檔案切割
//檔案切割 FileInputStream fis=new FileInputStream(path); FileOutputStream fos=null; byte[] bytes=new byte[1024]; int len=0; int item=0; File dir=new File(save-path); File file=null; while((len=fis.read(bytes))!=-1){ file=new File(dir,"自定義的檔名稱+副檔名"); fos=new FileOutputStream(file); fos.write(bytes,0,len); item++; fos.close(); } //=========分隔檔案完畢=↑↑↑↑↑↑↑ //=========分隔檔案的配置檔案 ↓↓ File configfile=new File(dir,"自定義名稱.properties"); Properties pro=new Properties(); pro.setProperty("filename","path".getName()); pro.setProperty("partcount",item); fos=new FileOutputStream(configfile); pro.stroe(fos,"非中文註釋資訊"); fos.close(); fis.close();View Code
RandomAccessFile類:
隨機訪問檔案,自身具備讀寫方法。內部封裝了byte型別的陣列。故、操作位元組資料。
getFilePointer();獲取指標位置
seek();設定指標位置
該物件只能操作檔案,也就是源和目的都是一個檔案;並通過建構函式的另一個引數來確定訪問方式。
該引數有如下四個值:
r:以只讀方式開啟指定檔案。如果試圖對該RandomAccessFile指定的檔案執行寫入方法則會丟擲IOException
rw:以讀取、寫入方式開啟指定檔案。如果該檔案不存在,則嘗試建立檔案
rws:以讀取、寫入方式開啟指定檔案。相對於rw模式,還要求對檔案的內容或元資料的每個更新都同步寫入到底層儲存裝置,預設情形下(rw模式下),是使用buffer的,只有cache滿的或者使用RandomAccessFile.close()關閉流的時候兒才真正的寫到檔案
rwd:與rws類似,只是僅對檔案的內容同步更新到磁碟,而不修改檔案的元資料
持久化儲存:
ObjectStream
也就是將物件中封裝資料儲存到持久化的裝置上。
那麼其他應用程式都需要建立該物件,直接讀取裝置上的物件即可。
操作流的物件:ObjectInputStream、ObjectOutputStream
物件中的靜態化資料不會被持久化。若非靜態資料不需要被持久化,那麼就用transient修飾即可。
// 寫入物件 writeObject(); ObjectOutputStream oos=new ObjectOutputStream(new FileOutputStream(path)); oos.writeObject(new Person("內容","內容"));//如果一個物件要被寫入,該物件必須具備序列化功能;也就是說需要要實現 Serializable 介面 oos.close(); // 讀取物件 readObject(); ObjectInputStream ois=new ObjectInputStream(new FileInputStream(path)); Person p=(Person)ois.readObject(); ois.close(); import java.io.*; class Person implements Serializable{ //Code... }
Serializable:
用於給類檔案加UID。就是一個序列號。
當類中的成員發生大的改動時類會重新編譯,生成帶有新的UID的序列號。這樣就和增儲存的原來的類生成的物件的序列號不匹配。這樣就可以讓使用者必須重新對新類產生的物件進行儲存。避免新類接收老物件出現安全隱患,這就是序列號的功能所在。
若、類中成員沒有大的改動,只是有個別的修改和已儲存的物件沒有太大影響。就需要重新進行儲存。同時可以用新的類接收讀到的老物件。這時,可以定義類時指定序號列,而不讓jvm自動算該序列號。
基本資料型別:
DataInputStream、DataOutputStream
操作基本資料型別的流物件。如用DataOutputStream 寫入的資料,必須要用與之對應的DataInputStream來讀取。否則讀取不出來。
//寫入 DataOutputStream dos=new DataOutputStream(new FileOutputStream(path)); dos.writeUTF(內容); dos.close(); //讀取 DataInputStream dis=new DataInputStream(new FileInputStream(path)); dis.readUTF(); dis.close();
運算元組的流物件:
ByteArrayInputStream、ByteArrayOutputStream
運算元組流物件,它對應的裝置就是記憶體。
ByteArrayOutputStream:內部封裝了一個可變長度的字串陣列。關閉無效,是因該物件沒有呼叫過底層資源。可通過toByteArray()、toString()獲取陣列中的資料。
ByteArrayInputStream:複製資料來源在初始化的時候必須有資料來源內容。因操作的是陣列;故、源就是一個位元組陣列。該物件不會出現異常發生,是因沒有呼叫過底層資源。
字元編碼:
字元流=位元組流+編碼表;
能指定編碼表的是轉換流。內部默認了編碼表的轉換流的子類FileReader、FileWriter 。預設的是本機編碼表。
OutputStreamWriter osw=new OutputStreamWriter(new FileOutputStream(path)); OutputStreamWriter osw=new OutputStreamWriter(new FileOutputStream(path),"GBK"); FileWriter fw=new FileWriter(path); //以上三種都是使用預設編碼表GBK
//寫入 OutputStreamWriter osw=new OutputStreamWriter(new FileOutputStream(path),"GBK"); osw.writer(內容); osw.close(); //讀取 InputStreamReader isr=new InputStreamReader(new FileInputStream(path),"GBK"); char[] ch=new char[1024]; int len=0; while((len=isr.read(ch))!=-1){ new String(ch,0,len); } isr.close();