先放總結
如果是對圖片、音樂、視頻、壓縮包等文件的拷貝,推薦使用 緩衝字節輸入/輸出流(BufferedOutputStream)+字節數組 (第4種)
如果是對文本文件的讀取和拷貝,推薦使用 字符緩存輸入/輸出流(BufferedWriter) + 按行讀取 (第9種)
對於特大文件,比如 1 個 G 以上的,多線程操作可以使用 NIO (第 10 種)。
一、字節流
1、FileInputStrem 按字節流 逐字節讀寫(速度最慢)
/**
* FileInputStream 方式拷貝文件
* @param srcFileName
* @param descFileName
* @throws IOException
*/
public static void copyFile(String srcFileName, String descFileName) throws IOException {
Long startTime = System.currentTimeMillis();
FileInputStream fis = new FileInputStream(srcFileName);
FileOutputStream fos = new FileOutputStream(descFileName);
int len = 0;
while ((len = fis.read()) != -1) {//讀取
//寫入另一個文件
fos.write(len);
}
Long endTime = System.currentTimeMillis();
System.out.println("總共耗時:" + (endTime - startTime) + "毫秒");//2937
}
public static void main(String args[]) throws IOException {
String srcFileName = "/Users/liuyanzhao/Desktop/temp/三國演義.txt";
String descFileName = "/Users/liuyanzhao/Desktop/temp/三國演義的拷貝.txt";
copyFile(srcFileName, descFileName);
}
總共耗時:2937毫秒
2、FileInputStream 構造一個緩衝數組進行讀寫(速度提升很多)
/**
* FileInputStream 方式拷貝文件,添加緩存數組
* @param srcFileName
* @param descFileName
* @throws IOException
*/
public static void copyFile(String srcFileName, String descFileName) throws IOException {
Long startTime = System.currentTimeMillis();
FileInputStream fis = new FileInputStream(srcFileName);
FileOutputStream fos = new FileOutputStream(descFileName);
byte[] buffer = new byte[1024];//構造一個長度爲1024的字節數組
int len = 0;
while ((len = fis.read(buffer)) != -1) {//讀取
//寫入另一個文件
fos.write(buffer);
}
Long endTime = System.currentTimeMillis();
System.out.println("總共耗時:" + (endTime - startTime) + "毫秒");// 6
}
public static void main(String args[]) throws IOException {
String srcFileName = "/Users/liuyanzhao/Desktop/temp/三國演義.txt";
String descFileName = "/Users/liuyanzhao/Desktop/temp/三國演義的拷貝.txt";
copyFile(srcFileName, descFileName);
}
總共耗時:6毫秒
3、利用字節緩衝區流 BufferedInputStream 和 BufferedOutputStream來直接逐字節讀寫(比第1個快很多,比第2個慢)
/**
* 使用 BufferedInputStream 和 BufferedOutputStream 通過字符拷貝
* @param srcFileName
* @param descFileName
* @throws IOException
*/
public static void copyFile(String srcFileName, String descFileName) throws IOException {
Long startTime = System.currentTimeMillis();
//緩衝區字節輸入流
BufferedInputStream bis = new BufferedInputStream(new FileInputStream(srcFileName));
//緩衝區字節輸出流
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(descFileName));
int len = 0;
while ((len = bis.read()) != -1) {//讀取
//寫入另一個文件
bos.write(len);
}
Long endTime = System.currentTimeMillis();
System.out.println("總共耗時:" + (endTime - startTime) + "毫秒");// 60
}
public static void main(String args[]) throws IOException {
String srcFileName = "/Users/liuyanzhao/Desktop/temp/三國演義.txt";
String descFileName = "/Users/liuyanzhao/Desktop/temp/三國演義的拷貝.txt";
copyFile(srcFileName, descFileName);
}
總共耗時:60毫秒
4、利用字節緩衝區流BufferedInputStream和BufferedOutputStream通過構造一個緩衝數組進行讀寫(速度最快)
/**
* 使用 BufferedInputStream 和 BufferedOutputStream 通過一個緩衝數組 字符拷貝
* @param srcFileName
* @param descFileName
* @throws IOException
*/
public static void copyFile(String srcFileName, String descFileName) throws IOException {
Long startTime = System.currentTimeMillis();
//緩衝區字節輸入流
BufferedInputStream bis = new BufferedInputStream(new FileInputStream(srcFileName));
//緩衝區字節輸出流
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(descFileName));
byte[] buffer = new byte[1024];//構造一個長度爲1024的字節數組
int len = 0;
while ((len = bis.read(buffer)) != -1) {//讀取
//寫入另一個文件
bos.write(buffer);
}
Long endTime = System.currentTimeMillis();
System.out.println("總共耗時:" + (endTime - startTime) + "毫秒");// 4
}
public static void main(String args[]) throws IOException {
String srcFileName = "/Users/liuyanzhao/Desktop/temp/三國演義.txt";
String descFileName = "/Users/liuyanzhao/Desktop/temp/三國演義的拷貝.txt";
copyFile(srcFileName, descFileName);
}
總共耗時:4 毫秒
二、字符流
5、利用字符流InputStreamWriter和 OutputStreamWriter直接按字節讀取
/**
* 使用 InputStreamWriter和 OutputStreamWriter直接按字節讀取拷貝
* @param srcFileName
* @param descFileName
* @throws IOException
*/
public static void copyFile(String srcFileName, String descFileName) throws IOException {
Long startTime = System.currentTimeMillis();
//緩衝區字符輸入流
InputStreamReader isr = new InputStreamReader(new FileInputStream(srcFileName));
//緩衝區字符輸出流
OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream(descFileName));
int len = 0;
while ((len = isr.read()) != -1) {//直接按字節讀取
//寫入另一個文件
osw.write(len);
}
Long endTime = System.currentTimeMillis();
System.out.println("總共耗時:" + (endTime - startTime) + "毫秒");// 586
}
public static void main(String args[]) throws IOException {
String srcFileName = "/Users/liuyanzhao/Desktop/temp/三國演義.txt";
String descFileName = "/Users/liuyanzhao/Desktop/temp/三國演義的拷貝.txt";
copyFile(srcFileName, descFileName);
}
總共耗時:586毫秒
會亂碼
6、字符流InputStreamWriter和 OutputStreamWriter直接用緩衝區數組讀寫
/**
* 使用 InputStreamWriter和 OutputStreamWriter直接按字節讀取拷貝
* @param srcFileName
* @param descFileName
* @throws IOException
*/
public static void copyFile(String srcFileName, String descFileName) throws IOException {
Long startTime = System.currentTimeMillis();
//緩衝區字符輸入流
InputStreamReader isr = new InputStreamReader(new FileInputStream(srcFileName));
//緩衝區字符輸出流
OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream(descFileName));
char[] buffer = new char[1024];//構造一個長度爲1024的字節數組
int len = 0;
while ((len = isr.read(buffer)) != -1) {//直接按字節讀取
//寫入另一個文件
osw.write(buffer);
}
Long endTime = System.currentTimeMillis();
System.out.println("總共耗時:" + (endTime - startTime) + "毫秒");// 385
}
public static void main(String args[]) throws IOException {
String srcFileName = "/Users/liuyanzhao/Desktop/temp/三國演義.txt";
String descFileName = "/Users/liuyanzhao/Desktop/temp/三國演義的拷貝.txt";
copyFile(srcFileName, descFileName);
}
總共耗時:385毫秒
7、字符緩衝流BufferedWriter和BufferedReader直接逐字節讀寫
/**
* 使用 BufferedWriter和BufferedReader直接逐字節讀寫
* @param srcFileName
* @param descFileName
* @throws IOException
*/
public static void copyFile(String srcFileName, String descFileName) throws IOException {
Long startTime = System.currentTimeMillis();
//緩衝區字符輸入流
BufferedReader bf = new BufferedReader(new FileReader(srcFileName));
//緩衝區字符輸出流
BufferedWriter bw = new BufferedWriter(new FileWriter(descFileName));
int len = 0;
while ((len = bf.read()) != -1) {//直接按字節讀取
//寫入另一個文件
bw.write(len);
}
Long endTime = System.currentTimeMillis();
System.out.println("總共耗時:" + (endTime - startTime) + "毫秒");// 412
}
public static void main(String args[]) throws IOException {
String srcFileName = "/Users/liuyanzhao/Desktop/temp/三國演義.txt";
String descFileName = "/Users/liuyanzhao/Desktop/temp/三國演義的拷貝.txt";
copyFile(srcFileName, descFileName);
}
總共耗時:433毫秒
8、字符緩衝流 BufferedWriter 和 BufferedReader 按照數組大小逐塊讀寫
/**
* 使用 BufferedWriter和BufferedReader直接逐字節讀寫
* @param srcFileName
* @param descFileName
* @throws IOException
*/
public static void copyFile(String srcFileName, String descFileName) throws IOException {
Long startTime = System.currentTimeMillis();
//緩衝區字符輸入流
BufferedReader bf = new BufferedReader(new FileReader(srcFileName));
//緩衝區字符輸出流
BufferedWriter bw = new BufferedWriter(new FileWriter(descFileName));
int len = 0;
char[] buffer = new char[1024];
while ((len = bf.read(buffer)) != -1) {//直接按字節讀取
//寫入另一個文件
bw.write(buffer);
}
Long endTime = System.currentTimeMillis();
System.out.println("總共耗時:" + (endTime - startTime) + "毫秒");// 387
}
public static void main(String args[]) throws IOException {
String srcFileName = "/Users/liuyanzhao/Desktop/temp/三國演義.txt";
String descFileName = "/Users/liuyanzhao/Desktop/temp/三國演義的拷貝.txt";
copyFile(srcFileName, descFileName);
}
總共耗時:381毫秒
9、字符緩衝流BufferedWriter和BufferedReader按逐行讀寫(應用於文本讀寫)
/**
* 使用 BufferedWriter和BufferedReader直接逐字節讀寫
* @param srcFileName
* @param descFileName
* @throws IOException
*/
public static void copyFile(String srcFileName, String descFileName) throws IOException {
Long startTime = System.currentTimeMillis();
//緩衝區字符輸入流
BufferedReader bf = new BufferedReader(new FileReader(srcFileName));
//緩衝區字符輸出流
BufferedWriter bw = new BufferedWriter(new FileWriter(descFileName));
String str;
while ((str = bf.readLine()) != null) {//直接按字節讀取
//寫入另一個文件
bw.write(str);
}
Long endTime = System.currentTimeMillis();
System.out.println("總共耗時:" + (endTime - startTime) + "毫秒");// 353
}
public static void main(String args[]) throws IOException {
String srcFileName = "/Users/liuyanzhao/Desktop/temp/三國演義.txt";
String descFileName = "/Users/liuyanzhao/Desktop/temp/三國演義的拷貝.txt";
copyFile(srcFileName, descFileName);
}
總共耗時:353 毫秒
注意:
後面的幾種字符流的均會出現中文字符亂碼的情況。
需要指定字符集
- InputStreamReader isr = new InputStreamReader(new FileInputStream(srcFileName),"GBK")
- BufferedReader br = new BufferedReader(new InputStreamReader(new FileInputStream(srcFileName),"GBK"));
以上只是通過一個 1MB 的文件的複製測試,並不能說明問題。
總結:
複製文本文件有幾種方式?
9種
複製圖片文件有幾種方式?
4種
他們各自的優缺點是什麼?
字符流按字符處理數據,速度較之於同原理的字節流快,但是使用範圍較小,僅限文本文件;
字節流處理範圍廣,文本,音頻,視頻都可以處理。
三、FileInputStream 與BufferedInputStream區別:
FileInputStream是字節流,BufferedInputStream是字節緩衝流,使用BufferedInputStream讀資源比FileInputStream讀取資源的效率高(BufferedInputStream的read方法會讀取儘可能多的字節),且FileInputStream對象的read方法會出現阻塞;
- 瞭解“堵塞”的意思吧!
- 假設一個文件的長度是100個字節,要將之讀取到內存中,再假設您每次只讀取10個字節,那麼讀完整個文件是不是讀取10次的呀?
- 假設老闆讓你完成100件事情,老闆說,你每天只完成10件就可以了,難道你非得等到第十天才完成第100件事情嗎?有一天您在中午下班前就完成了10件事情,下午您不妨多幹一點,那麼也許在第9天的時候就完成了100件事情
- 同理,BufferedInputStream有可能會讀取比您規定的更多的東西到內存,以減少訪問IO的次數,
- 總之您要記住一句話,訪問IO的次數越少,性能就越高,原因就在於CPU和內存的速度》》》》遠大於硬盤或其他外部設備的速度。
- 換一個不太恰當的例子來說,您和您的朋友一起去登山,你朋友太不給力了,走一會兒就要休息,而您呢,您的體力比他要好的多,根本不需要休息,所以每當他休息的時候,您得等着他,您那時候什麼也幹不了,這就叫堵塞,堵塞就是說您有能力幹某事,但是迫於某種原因您什麼也幹不了,只能乾等。所以您朋友休息的次數越少,你們兩個到達山頂所花費的時間就越少。CPU訪問硬盤的次數越少,程序就越快。BufferedInputStream在小型文件中的性能優勢無法體現出來,假設您將以個2G大小的文件從D盤完全複製到E盤,性能之優勢便展露無疑!
四、FileInputStream與FileReader區別:
兩個類的構造函數的形式和參數都是相同的,參數爲 File 對象或者表示路徑的 String ,它們到底有何區別呢?
FileInputStream :以字節流方式讀取;
FileReader :把文件轉換爲字符流讀入;
InputStream提供的是字節流的讀取,而非文本讀取,這是和Reader類的根本區別。用Reader讀取出來的是char數組或者String ,使用InputStream讀取出來的是byte數組。
Reader類及其子類提供的字符流的讀取char,inputStream及其子類提供字節流的讀取byte,所以FileReader類是將文件按字符流的方式讀取,FileInputStream則按字節流的方式讀取文件;InputStreamReader可以將讀如stream轉換成字符流方式,是reader和stream之間的橋樑
最初Java是不支持對文本文件的處理的,爲了彌補這個缺憾而引入了Reader和Writer兩個類。
FileInputStream 類以二進制輸入 / 輸出, I/O 速度快且效率搞,但是它的 read ()方法讀到的是一個字節,很不利於人們閱讀。 而 FileReader 類彌補了這個缺陷,可以以文本格式輸入/ 輸出,非常方便;比如可以使用 while((ch = filereader.read())!=-1 ) 循環來讀取文件;可以使用BufferedReader 的 readLine() 方法一行一行的讀取文本。 當我們讀寫文本文件的時候,採用 Reader 是非常方便的,
比如 FileReader , InputStreamReader 和 BufferedReader 。其中最重要的類是 InputStreamReader ,它是字節轉換爲字符的橋樑。 你可以在構造器中指定編碼的方式,如果不指定的話將採用底層操作系統的默認編碼方式,例如 GBK 等。 FileReader 與 InputStreamReader 涉及編碼轉換 ( 指定編碼方式或者採用 os 默認編碼 ) ,可能在不同的平臺上出現亂碼現象!而 FileInputStream 以二進制方式處理,不會出現亂碼現象 .
如果處理純文本文件,建議使用 FileReader ,因爲更方便,也更適合閱讀;但是要注意編碼問題!
其他情況(處理非純文本文件),FileInputStream是唯一的選擇;FileInputStream是進Socket通訊時會用到很多,如將文件流是Stream的方式傳向服務器!
五、NIO
10、使用 NIO 複製
/**
* NIO
* @param sourcePath
* @param targetPath
* @throws IOException
*/
public static void copyFile(String sourcePath, String targetPath) throws IOException {
FileInputStream in = new FileInputStream(sourcePath);
FileOutputStream out = new FileOutputStream(targetPath);
FileChannel inChannel = in.getChannel();
FileChannel outChannel = out.getChannel();
ByteBuffer buffer = ByteBuffer.allocate(4 * 1024);
long startTime = System.currentTimeMillis();
while (inChannel.read(buffer) != -1) {
buffer.flip();
outChannel.write(buffer);
buffer.clear();
}
long endTime = System.currentTimeMillis();
System.out.println("總共耗時" + (endTime - startTime) + "ms");
}
public static void main(String args[]) throws IOException {
String sourcePath = "/Users/liuyanzhao/Desktop/temp/三國演義.txt";
String targetPath = "/Users/liuyanzhao/Desktop/temp/三國演義的拷貝.txt";
copyFile(sourcePath, targetPath);//13ms
}
IO是阻塞的。NIO是非阻塞的,在多線程的時候效果比較明顯。