天天看點

IO流知識IO流知識

IO流知識

ByteArrayOutputStream 與ByteArrayInputStream流關閉問題(記憶體流)

ByteArrayOutPutStream和ByteArrayInputStream内部封裝了緩沖區(記憶體流),他們是記憶體讀寫流,不同于指向磁盤的流,不涉及底層資源的使用,并且底層用的是byte數組并不是調用了磁盤,這個位元組數組是它成員變量,當數組不再使用變成垃圾的時候Java的垃圾回收機制會将它回收,關閉流是為了防止底層資源的洩露(記憶體),并且他自己的close方法并沒有實作,如下所示:

/** 底層建立流
 * Creates a new byte array output stream, with a buffer capacity of
 * the specified size, in bytes.
 *
 * @param   size   the initial size.
 * @exception  IllegalArgumentException if size is negative.
 */
public ByteArrayOutputStream(int size) {
    if (size < 0) {
        throw new IllegalArgumentException("Negative initial size: "
                                           + size);
    }
    buf = new byte[size];
}
           
/** 底層關閉流
 * Closing a <tt>ByteArrayOutputStream</tt> has no effect. The methods in
 * this class can be called after the stream has been closed without
 * generating an <tt>IOException</tt>.
 */
public void close() throws IOException {
}
           

FileOutputStream與FileInputStream流需要關閉問題

需要關閉的

easyPOI導出Excel報錯easyPOI導出Excel報org.apache.poi.ss.usermodel.CellStyle.setAlignment(S)V

版本依賴問題,将版本更新到4.1.3版本即可解決問題

耗時 easyPOI與easyExcel與Apache POI導出Excel耗時比較問題

1萬條資料(這裡的測試不是從庫裡導出來測試的填充Excel耗時):

easyPOI:1219ms

easyExcel:2298ms

Apache POI:1001(導出Excel不是從庫裡導出來的)

jdk泛型反射:6319ms

easyPOI 與easyExcel 與ApachePOI導出Excel特點

easyPOI:預設導出的資料的列值是全部上下居中的,預設列的寬度是10 個機關,不能設定全局自适應列的寬度,隻能指定每個列的寬度,并且對數字的導出,對數字的類型沒有要求,不損失精度,可以直接導出數值,如果需要對數字轉義成中文可以直接使用replace進行轉義,也可以自己實作IExcelExportStyler接口指定表格的全局樣式,對于Date和String類型的時間可以使用注解進行時間格式指定,功能非常齊全,基本實作了全注解式開發,但對開發者對easyPOI基本屬性熟練度要求較高,隻是百萬級别資料,也是分批次導出的和easyExcel導出原理一樣。

easyExcel:1.0方法較少,2.0以後,方法較多,注解支援自定義導出屬性值的轉換器,2.1.6版本較為穩定,它需要自己指定表格的樣式,對值的上下居中及各種屬性需要自己設定,支援鍊式操作,導出方法較為簡單,但導出的數值類型有要求,對float和double會損失精度,需要自己寫轉換器進行轉為字元串進行導出,導出的數值類型較為單一,但可以自己實作設定全局表格列的自适應,隻需要自定義表格的攔截器即可,快速、簡單避免OOM的java處理Excel工具!。

Apache POI:實作較為繁瑣,對開發者要求較高,實作列自适應,需要每次對每列進行設定,很是繁瑣,并且每導出一次表格都要重複寫很多代碼,很是麻煩,如果使用jdk反射雖然降低代碼備援度,但會降低系統的性能,或者ReflectASM架構反射進行反射提高反射性能,總體來說,Apache POI導出表格性能很差,代碼備援度也很高,非常耗記憶體,嚴重時會導緻記憶體溢出。

總結:總體來說,easyPOI較為簡單,并且性能較高,對百萬級别的資料也支援,其次是easyExcel,最後是Apache POI。

IO包裝流如何關閉

  • 一般情況下是:先打開的後關閉,後打開的先關閉;
  • 另一種情況:看依賴關系,如果流a依賴流b,應該先關閉流a,再關閉流b
  • 例如處理流a依賴節點流b,應該先關閉處理流a,再關閉節點流b
  • 當然完全可以隻關閉處理流,不用關閉節點流。處理流關閉的時候,會調用其處理的節點流的關閉方法
  • 如果将節點流關閉以後再關閉處理流,會抛出IO異常
  • 在循環中建立流,在循環内關閉每個流

判斷一個流是否被關閉,是沒有提供直接的

API

方法判别的。這裡要辟個謠,有人說可以通過判斷比如

inputStream == null

來确認流是否被關閉,這是行不通的。可根據

try {
            inputStream.read();
        } catch (IOException e) {
            e.printStackTrace();
            log.error("InputStream 流已經被關閉了");
        }
           

FileOutputstream與ObjectOutputstream嵌套關閉

FileOutputStream out1 = new FileOutputStream("D:\\SingleTon.txt");
        ObjectOutputStream out2 = new ObjectOutputStream(out1);
        out1.close();//是否需要關閉内層的IO流?
        out2.close();
           

考慮一下,像這樣的嵌套IO流,是否應該從内到外依次關閉呢?

答案是不需要!這些IO類都是JDK自帶的,調用了最外層的close方法,其實是一層一層向内調用了最内層的IO類的close方法,這也就是裝飾者模式。

當然你肯定想問,為什麼我之前自内向外逐層關閉也不會抛出異常?

因為就算你對某個流重複關閉多次,也不會抛出異常

Java如何正确的使用try catch finally關閉檔案流的總結

/**
 * 測試正确關閉檔案流
 */
private static void testCloseFileStream() {
    final Logger LOG = LoggerFactory.getLogger(Cmshome.class);
    String fileName = "";
    InputStream inputStream = null;//聲明個引用,因為這個new對象的時候也是會異常的
    try {
        //這裡就會異常,如果檔案名不存在的話。
        inputStream = new FileInputStream(fileName);
    } catch (IOException e) {
        //這個主要是把出現的異常給人看見,不然就算異常了,看不到就找不到問題所在。
        LOG.debug("loadProperties IOException:" + e.getMessage());
    } finally {
        if (inputStream != null) {
            try {
                inputStream.close(); // 關閉流
            } catch (IOException e) {
                LOG.debug("inputStream close IOException:" + e.getMessage());
            }
        }
    }
}
           
//錯誤的關閉檔案的方式的解釋:
Properties properties = new Properties();
try {
	//這要是異常,直接就到catch語句,下面的close就不會執行啦,關閉就沒用啦
	InputStream wrongWay = new FileInputStream(fileName);
	properties.load(wrongWay);
	wrongWay.close(); // 關閉流
} catch (IOException e) {
	e.printStackTrace();
}
 
//下面是new檔案流和關閉檔案流的源碼,有抛異常動作。
public FileInputStream(String name) throws FileNotFoundException {
    this(name != null ? new File(name) : null);
}
 
//這個是抽象類(abstract class)裡面的方法,是以沒有具體實作過程。
public void close() throws IOException {}
           

java - 我應該使用close或closeQuietly來關閉輸出流嗎?

當我們需要關閉輸出流時,我們有兩個選擇。

安靜地關閉意味着沒有例外地關閉一條小溪。

try {
	close(out)
} catch(IOException e){
}
           

關閉

try {
	close(out)
} catch(IOException e) {
 throw a Exception;
}
           

衆所周知,在關閉時,輸出流會在檔案末尾寫入一個或多個字元,如果這些寫入錯誤,檔案也無法正确打開,例如zipoutputstream。

如果我使用第一個,我會有一些失敗關閉的風險。

如果我使用第二個,它會讓我的代碼不友好。

有人能給我一些建議嗎?

很抱歉把這個問題描述得不清楚。

我是說如何安全地進行IO操作。如果資源的釋放失敗,它會讓調用者知道。

謝謝你的回答。特别感謝“唐·羅比”給了我一個連結,其中包含了“法比安·巴尼”的最佳答案

最佳答案

由于Java 7

IOUtils.closeQuietly

已經過時,唯一合理的解決方案是

try-with-resources

它自動關閉資源。

try (InputStream is = new FileInputStream(file)) {
    ...
}
           

注意,它還解決了正确打開/關閉多個資源的問題

try (InputStream is = new FileInputStream(infile); OutputStream out = new FileOutputStream(outfile)) {
   ...          
}
           

它也不能抑制

close()

可能抛出的ioexception,這正是

closeQuietly

所做的。

windows 系統導出Excel,檔案名不支援冒号時間戳

easyPOI會将冒号時間檔案名轉為中劃線,easyExcel和Apache POI則不會轉直接報錯

檔案名中文亂碼問題

//将檔案名按如下進行設定,根據系統自動編碼實作
String fileName = new String(("李玉傑在" +time +"上傳"+"return_order.xls").getBytes("GBK"), StandardCharsets.ISO_8859_1);//涉及到中文問題 根據系統實際編碼改變
           

com.alibaba.excel.exception.ExcelAnalysisException: java.lang.NoClassDefFoundError: org/apache/poi/p

解決方案:

1.檢查:檢視poi版本是否沖突

2.檢查是否缺少依賴版本

3.檢查poi的版本要大于等于3.17版本

紅框的是必須引入的依賴 其他幾個看需求引入 版本最好大于等于3.17 并且統一

<dependency>
    <groupId>org.apache.poi</groupId>
    <artifactId>poi</artifactId>
    <version>3.17</version>
</dependency>
<dependency>
    <groupId>org.apache.poi</groupId>
    <artifactId>poi-ooxml</artifactId>
    <version>3.17</version>
</dependency>
           

postman測試檔案上傳接口方法

headers 裡頭資訊:

Content-Type:multipart/form-data

description:""

enabled:true

body 選擇:form-data ,key填寫requestparm參數,與接口保持一緻