原創轉載請注明出處:https://www.cnblogs.com/agilestyle/p/11444284.html
利用java.io類庫,直接為源文件構建一個FileInputStream讀取,然后再為目標文件構建一個FileOutputStream,完成寫入工作。
1 public static void copyFileByStream(File source, File dest) throws IOException { 2 try (InputStream is = new FileInputStream(source); 3 OutputStream os = new FileOutputStream(dest)) { 4 byte[] buffer = new byte[1024]; 5 int length; 6 7 while ((length = is.read(buffer)) > 0) { 8 os.write(buffer, 0, length); 9 } 10 } 11 }
利用java.nio類庫提供的transferTo或transferFrom方法實現
1 public static void copyFileByChannel(File source, File dest) throws IOException { 2 try (FileChannel sourceChannel = new FileInputStream(source).getChannel(); 3 FileChannel targetChannel = new FileOutputStream(dest).getChannel()) { 4 5 for (long count = sourceChannel.size(); count > 0; ) { 6 long transferred = sourceChannel.transferTo(sourceChannel.position(), count, targetChannel); 7 8 sourceChannel.position(sourceChannel.position() + transferred); 9 10 count -= transferred; 11 } 12 } 13 }
Java標准類庫提供的幾種Files.copy的實現

對於Copy的效率,這個其實與操作系統和配置等情況相關,總體上來說,NIO transferTo/From的方式可能更快,因為它更能利用現代操作系統底層機制,避免不必要拷貝和上下文切換。
拷貝實現機制分析
先來理解一下,前面實現的不同拷貝方法,本質上有什么明顯的區別。
首先,需要理解用戶態空間(User Space)和內核態空間(Kernel Space),這是操作系統層面的基本概念,操作系統內核、硬件驅動等運行在內核態空間,具有相對高的特權;而用戶態空間,則是給普通應用和服務使用。可以參考:https://en.wikipedia.org/wiki/User_space。
當使用輸入輸出流進行讀寫時,實際上是進行了多次上下文切換,比如應用讀取數據時,先在內核態將數據從磁盤讀取到內核緩存,再切換到用戶態將數據從內核緩存讀取到用戶緩存。
寫入操作也是類似,僅僅是步驟相反,可以參考下面這張圖。

所以,這種方式會帶來一定的額外開銷,可能會降低IO效率。
而基於NIO transferTo的實現方式,在Linux和Unix上,則會使用到零拷貝技術,數據傳輸並不需要用戶態參與,省去了上下文切換的開銷和不必要的內存拷貝,進而可能提高應用拷貝性能。注意,transferTo不僅僅是可以用在文件拷貝中,與其類似的,例如讀取磁盤文件,然后進行Socket發送,同樣可以享受這種機制帶來的性能和擴展性提高。
transferTo的傳輸過程是:

