概述

- I/O表示Input/Output,即數據傳輸過程中的輸入/輸出,并且輸入和輸出都是相對于內存來講
- Java IO(輸入/輸出)流是Java用于處理數據讀取和寫入的關鍵組件
- 常見的I|O介質包括
- 文件(輸入|輸出)
- 網絡(輸入|輸出)
- 鍵盤(輸出)
- 顯示器(輸出)
- 使用場景
- 文件拷貝(File)
- 文件上傳下載
- Excel導入導出
- 網絡程序中數據傳輸(聊天工具)
分類
概述
Java中幾乎所有的IO操作都需要使用java.io包;流可以通過如下方式進行分類
- 按流向分(輸入輸出過程通常都是站在程序角度考慮)
- 輸入流(Input)
- 輸出流(Output)
- 按流的處理類型分
- 字節流(byte): 字節是計算機存儲容量的基本單位(Byte),1B=8b,二進制中占8位
- 字符流(char): 字符是文字或符號的統稱
????????注意:字節流對于什么類型的文件都可以讀取,如二進制類型的文件(圖片,視頻,音頻,壓縮? ? ? ? ? ? ? ? ? ? 文件等),而字符流用于讀取文本類型文件
- 按流的功能來分
- 節點流(直接跟輸入輸出源交互)
- 處理流(對其他流包裝的流:包裝流)
字節流(InputStream && OutputStream)


日常開發過程中常用的字節流:
FileInputStream && FileOutputStream:?常用來實現文件復制/拷貝
BufferedInputStream && BufferedOutputStream:?為了減少IO次數,提高讀取效率
PrintStream:源自OutputStream,標準字節的打印輸出流(日志框架的實現原理)
ZipOutputStream && ZipInputStream:用來進行文件壓縮/文件解壓
字符流(Reader && Writer)


日常開發過程中常用的字符流:
FileReader&&FileWriter:作用同FileInputStream && FileOutputStream
BufferedReader&&BufferedWriter:作用同BufferedInputStream && BufferedOutputStream,同時BufferedReader提供了按行讀取文本的方法,方便文本處理
擴展: 我們知道字節流可以讀取任意文件,為什么還要設計出字符流呢?
- 對于字符文件,先作為字節傳輸->再轉成字符,比較耗時
- 對于字符文件,如果其中為中文,則容易亂碼
設計模式
在IO流中使用了多種設計模式,包括如下:
適配器模式
適配器模式使得原本由于接口不兼容而不能一起工作的那些類可以一起工作。
Java IO中為了實現字符流和字節流之間的相互轉換,設計了兩個適配器的類,
InputStreamReader和OutputStreamWriter
InputStreamReader isr = new InputStreamReader(new FileInputStream(fileName), "UTF-8");
BufferedReader bufferedReader = new BufferedReader(isr);
裝飾器模式
裝飾器模式可以將新功能動態地附加于現有對象而不改變現有對象的功能。InputStream的子類FilterInputStream,OutputStream 的子類 FilterOutputStream,Reader 的子類 BufferedReader 以及 FilterReader,還有Writer的子類BufferedWriter、FilterWriter 以及 PrintWriter等,它們都是抽象裝飾類。增強了子類對象的功能。
實踐
ZipOutputStream&&FileOutputStream&&FileInputStream實現文件壓縮
/*** 功能: 通過ZipOutputStream壓縮文件,最后返回壓縮包* @param files* @param fileName* @return*/
public File zipFiles(File[] files,String fileName) {File zipFile = null;FileOutputStream fosZipFile = null;ZipOutputStream zosZipFile = null; //壓縮文件輸出流try {zipFile = downloadAttachmentService.createFile("", fileName); //創建一個空的文件目錄fosZipFile = new FileOutputStream(zipFile); //以文件流從內存中輸出zosZipFile = new ZipOutputStream(fosZipFile); //以壓縮流從內存中輸出for (File file : files) {FileInputStream fis = new FileInputStream(file); //對每個文件創建輸入流,讀取文件到內存ZipEntry zipEntry = new ZipEntry(file.getName()); //ZipEntry用來創建壓縮文件zosZipFile.putNextEntry(zipEntry); //加入需要壓縮的文件byte[] bytes = new byte[1024];int length;while((length = fis.read(bytes)) >= 0) { //讀取文件到內存zosZipFile.write(bytes, 0, length); //文件寫入壓縮流}fis.close();}} catch (IOException e) {e.printStackTrace();} finally { //關閉流try {zosZipFile.close();fosZipFile.close();} catch (IOException e) {e.printStackTrace();}}return zipFile; //返回壓縮包
}
/*** @Title: createFile* @Description: 創建下載目錄文件* @author Bierce* @param rootPath* @param filename* @return* @throws IOException*/
public File createFile(String rootPath, String filename) throws IOException {// Default root pathif (rootPath.isEmpty()) {rootPath = "download-cache";}File fRoot = new File(rootPath);if (!fRoot.exists() || !fRoot.isDirectory()) {fRoot.mkdirs();}// job sub pathString uuid = UUID.randomUUID().toString();String directoryJob = rootPath + File.separator + getClass().getSimpleName() + File.separator + uuid;//文件名稱隨機生成保證唯一File dirJob = new File(directoryJob);if (!dirJob.exists() || !dirJob.isDirectory()) {dirJob.mkdirs();}String filePath = directoryJob + File.separator + filename;File file = new File(filePath);if (!file.exists()) {file.createNewFile();}return file;
}
//-----------------擴展方法-文件名去重保證唯一-----------------
/*** @Title: snFileName_noUIID* @Description: 去除sn文件UUID以及解決sn文件名重復問題* @author Bierce* @return file*/
public File snFileName_noUIID(String fileParentPath,String snFileName,File file){//snFileName:完整文件名 sn-xx..UUID..xx.xlsx//snFileName_delUIID: sn.xlsx//snFileName_prefix: sn//suffix:xlsx//文件名:如sn.xlsxString snFileName_delUIID = snFileName.substring(0,snFileName.length() - 42) + ".xlsx";//42是固定長度:UUID+.xlsxString snFileName_prefix = snFileName.substring(0,snFileName.length() - 42);//文件前綴String suffix = snFileName.substring(snFileName.lastIndexOf("."));//文件后綴:.xlsxtry {file = new File(fileParentPath + snFileName_delUIID);//設置sn文件所在目錄為計劃交接文件目錄下int i = 1;//對于同名SN文件情況重新命名while(file.exists()) {//保證文件夾下不存在同名文件String newFileName = snFileName_prefix + "(" + i + ")" + suffix;String parentPath = file.getParent();file = new File(parentPath + File.separator + newFileName);i++;}file.createNewFile();//new File 只是創建了一個File對象,還需要調用createNewFile()方法才能實現文件的成功創建} catch (Exception e) {}return file;
}