JavaIO流中對數據的操作尤為重要,掌握了基本的拷貝操作,才能將各種數據源的操作聯系起來。
先來看看對文件夾的拷貝吧:
1 /** 2 * 利用遞歸實現文件夾的拷貝操作 3 * 分析:判斷 4 * 是文件:調用拷貝文件的方法fileCopy(...) 5 * 是文件夾:創建文件夾,並使用遞歸實現子文件夾/子文件的判斷及操作 6 * @param src:要拷貝的文件夾源頭 7 * @param dest:要拷貝到的文件夾源頭 8 */ 9 public static void dirCopy(File src,File dest) { 10 if(src.isFile()) { //是文件 11 fileCopy(src, dest); 12 }else { //是文件夾 13 dest.mkdirs(); 14 for(File subSrc:src.listFiles()) { //遍歷子文件夾/子文件 15 dirCopy(subSrc, new File(dest,subSrc.getName())); 16 } 17 } 18 }
對文件的拷貝,我們可以這樣寫:
1 /** 2 * 實現文件的拷貝 3 * 輸入流讀取的同時輸出流進行寫出 4 * @param src:要拷貝的文件源頭 5 * @param dest:要拷貝到的文件源頭 6 */ 7 public static void fileCopy(File src,File dest) { 8 //1.創建源 9 //2.選擇流 10 InputStream is = null; 11 OutputStream os = null; 12 try { 13 is = new FileInputStream(src); 14 os = new FileOutputStream(dest); 15 //3.操作:分段讀取並寫出 16 int len; //接收長度 17 byte[] flush = new byte[1024]; //緩沖容器,一次讀寫1k 18 while((len=is.read(flush))!=-1) { 19 os.write(flush, 0, len); 20 } 21 os.flush(); //寫完手動刷新,避免數據在緩沖容器中(當然當流關閉時會自動刷新) 22 }catch(FileNotFoundException e) { 23 e.printStackTrace(); 24 }catch(IOException e) { 25 e.printStackTrace(); 26 }finally { 27 //4.關閉流,分別關閉,先打開的后關閉 28 try { 29 if(os!=null) { //判斷是否為空,避免空指針異常 30 os.close(); 31 } 32 }catch(IOException e) { 33 e.printStackTrace(); 34 } 35 try { 36 if(is!=null) { //判斷是否為空,避免空指針異常 37 is.close(); 38 } 39 }catch(IOException e) { 40 e.printStackTrace(); 41 } 42 } 43 }
以上代碼只能實現對文件的拷貝操作,當然適合於拷貝任何格式的數據文件,包括視頻、音頻、圖片等等。但是如果我想將一張圖片拷貝到字節數組中呢(這里的字節數組相當於內存),也就是說從文件到字節數組,或着是從字節數組到文件。那么以上代碼就具有局限性了,也不易於擴展,來看以下代碼:
1 /** 2 * 對接流 3 * @param is:輸入流 4 * @param os:輸出流 5 */ 6 public static void copy(InputStream is,OutputStream os) { 7 //1.創建源 8 //2.選擇流 9 try { 10 //3.操作 11 byte[] flush = new byte[1024]; //緩沖容器 12 int len; //接收長度 13 while((len=is.read(flush))!=-1) { 14 os.write(flush, 0, len); 15 } 16 os.flush(); 17 }catch(FileNotFoundException e) { 18 e.printStackTrace(); 19 }catch(IOException e) { 20 e.printStackTrace(); 21 }finally { 22 try { 23 if(os!=null) { 24 os.close(); 25 } 26 }catch(IOException e) { 27 e.printStackTrace(); 28 } 29 try { 30 if(is!=null) { 31 is.close(); 32 } 33 }catch(IOException e) { 34 e.printStackTrace(); 35 } 36 } 37 }
嗯,這樣就可以實現以上要求了,但是我們發現:關閉資源的操作一直在,而且都一樣,我們可以封裝一下,這樣在finally中就可以直接調用了。
1 /** 2 * 關閉的方法 3 * @param is:輸入流 4 * @param os:輸出流 5 */ 6 public static void close(InputStream is,OutputStream os) { 7 try { 8 if(os!=null) { 9 os.close(); 10 } 11 }catch(IOException e) { 12 e.printStackTrace(); 13 } 14 try { 15 if(is!=null) { 16 is.close(); 17 } 18 }catch(IOException e) { 19 e.printStackTrace(); 20 } 21 }
好了,看看封裝的代碼,幸虧只有兩個流,要是流很多咋辦,形參太多,但是我們發現輸入流InputStream和輸出流OutputStream都實現了同一個接口:Closeable。嗯,這樣,我們可以試試JDK1.5的新特性:可變參數。
1 /** 2 * 封裝的關閉方法 3 * @param ios:要關閉的流 4 */ 5 public static void close(Closeable... ios) { 6 for(Closeable io:ios) { 7 try { 8 if(io!=null) { 9 io.close(); 10 } 11 }catch(IOException e) { 12 e.printStackTrace(); 13 } 14 } 15 }
現在看似完美了,但我還不太滿意,有時候,我覺得手動關閉資源太麻煩了。別急,來看看JDK1.7的新特性:try...with...resources(自動關閉資源)。
1 /** 2 * JDK1.7之后的新特性 try...with...resources:自動關閉資源 3 * 文件的拷貝 4 * @param srcPath:要拷貝的源頭 5 * @param destPath:要拷貝到的目的地 6 */ 7 public static void copy1(String srcPath,String destPath) { 8 //1.創建源 9 File src = new File(srcPath); 10 File dest = new File(destPath); 11 //2.選擇流 12 try(InputStream is = new FileInputStream(src); 13 OutputStream os = new FileOutputStream(dest)) { 14 //3.操作 15 byte[] flush = new byte[1024]; //緩沖容器 16 int len; //接收長度 17 while((len=is.read(flush))!=-1) { 18 os.write(flush, 0, len); 19 } 20 os.flush(); 21 }catch(FileNotFoundException e) { 22 e.printStackTrace(); 23 }catch(IOException e) { 24 e.printStackTrace(); 25 } 26 }
大家發現:try里面寫的好繁瑣,別急,看看JDK1.9的改進版(不過要求你所要關閉的資源是final或等效於final的變量)。
1 /** 2 * JDK1.9之后對 try...with...resources的改進 3 * 對接流 4 * @param is:輸入流 5 * @param os:輸出流 6 */ 7 public static void copy2(InputStream is,OutputStream os) { 8 //1.創建源 9 //2.選擇流 10 try(is;os) { 11 //3.操作 12 byte[] flush = new byte[1024]; //緩沖容器 13 int len; //接收長度 14 while((len=is.read(flush))!=-1) { 15 os.write(flush, 0, len); 16 } 17 os.flush(); 18 }catch(FileNotFoundException e) { 19 e.printStackTrace(); 20 }catch(IOException e) { 21 e.printStackTrace(); 22 } 23 }
哈哈,看上去是不是簡潔很多。對的,隨着JDK的新版本發布,越來越多的新技術,也使得代碼看起來越簡潔,不過對我們的要求也只會越來越高。前段時間JDK12已經出來了,還沒用,不過我相信肯定會有好多的新特性,期待,也看好Java,加油。