激情久久久_欧美视频区_成人av免费_不卡视频一二三区_欧美精品在欧美一区二区少妇_欧美一区二区三区的

服務器之家:專注于服務器技術及軟件下載分享
分類導航

PHP教程|ASP.NET教程|JAVA教程|ASP教程|

服務器之家 - 編程語言 - JAVA教程 - Java使用ByteArrayOutputStream 和 ByteArrayInputStream 避免重復讀取配置文件的方法

Java使用ByteArrayOutputStream 和 ByteArrayInputStream 避免重復讀取配置文件的方法

2020-03-08 15:47digdeep JAVA教程

這篇文章主要介紹了Java使用ByteArrayOutputStream 和 ByteArrayInputStream 避免重復讀取配置文件的方法,需要的朋友可以參考下

ByteArrayOutputStream類是在創建它的實例時,程序內部創建一個byte型別數組的緩沖區,然后利用ByteArrayOutputStream和ByteArrayInputStream的實例向數組中寫入或讀出byte型數據。在網絡傳輸中我們往往要傳輸很多變量,我們可以利用ByteArrayOutputStream把所有的變量收集到一起,然后一次性把數據發送出去。具體用法如下:

ByteArrayOutputStream:    可以捕獲內存緩沖區的數據,轉換成字節數組。

ByteArrayInputStream: 可以將字節數組轉化為輸入流

ByteArrayInputStream類有兩個默認的構造函數:

ByteArrayInputStream(byte[] b): 使用一個字節數組當中所有的數據做為數據源,程序可以像輸入流方式一樣讀取字節,可以看做一個虛擬的文件,用文件的方式去讀取它里面的數據。

ByteArrayInputStream(byte[] b,int offset,int length): 從數組當中的第offset開始,一直取出length個這個字節做為數據源。

ByteArrayOutputStream類也有兩個默認的構造函數:

ByteArrayOutputStream(): 創建一個32個字節的緩沖區
ByteArrayOutputStream(int): 根據參數指定大小創建緩沖區

最近參與了github上的一個開源項目 Mycat,是一個mysql的分庫分表的中間件。發現其中讀取配置文件的代碼,存在頻繁多次重復打開,讀取,關閉的問題,代碼寫的很初級,稍微看過一些框架源碼的人,是不會犯這樣的錯誤的。于是對其進行了一些優化。

優化之前的代碼如下所示:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
private static Element loadRoot() {
  InputStream dtd = null;
  InputStream xml = null;
  Element root = null;
  try {
    dtd = ConfigFactory.class.getResourceAsStream("/mycat.dtd");
    xml = ConfigFactory.class.getResourceAsStream("/mycat.xml");
    root = ConfigUtil.getDocument(dtd, xml).getDocumentElement();
  } catch (ConfigException e) {
    throw e;
  } catch (Exception e) {
    throw new ConfigException(e);
  } finally {
    if (dtd != null) {
      try {
        dtd.close();
      } catch (IOException e) { }
    }
    if (xml != null) {
      try {
        xml.close();
      } catch (IOException e) { }
    }
  }
  return root;
}

然后其它方法頻繁調用 loadRoot():

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
@Override
public UserConfig getUserConfig(String user) {
  Element root = loadRoot();
  loadUsers(root);
  return this.users.get(user);
}
@Override
public Map<String, UserConfig> getUserConfigs() {
  Element root = loadRoot();
  loadUsers(root);
  return users;
}
@Override
public SystemConfig getSystemConfig() {
  Element root = loadRoot();
  loadSystem(root);
  return system;
}
// ... ...

ConfigUtil.getDocument(dtd, xml) 方法如下:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
public static Document getDocument(final InputStream dtd, InputStream xml) throws ParserConfigurationException,
      SAXException, IOException {
    DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
//factory.setValidating(false);
    factory.setNamespaceAware(false);
    DocumentBuilder builder = factory.newDocumentBuilder();
    builder.setEntityResolver(new EntityResolver() {
      @Override
      public InputSource resolveEntity(String publicId, String systemId) {
        return new InputSource(dtd);
      }
    });
    builder.setErrorHandler(new ErrorHandler() {
      @Override
      public void warning(SAXParseException e) {
      }
      @Override
      public void error(SAXParseException e) throws SAXException {
        throw e;
      }
      @Override
      public void fatalError(SAXParseException e) throws SAXException {
        throw e;
      }
    });
    return builder.parse(xml);
  }

顯然這不是很好的處理方式。因為會多次重復配置文件。

1. 第一次優化:

為什么不讀取一次,然后緩存起來呢?然后其它方法在調用 loadRoot() 時,就直接使用緩存中的就行了。但是遇到一個問題,InputStream 是不能被緩存,然后重復讀取的,因為 InputStream 一旦被讀取之后,其 pos 指針,等等都會發生變化,無法進行重復讀取。所以只能將配置文件的內容讀取處理,放入  byte[] 中緩存起來,然后配合 ByteArrayOutputStream,就可以重復讀取 byte[] 緩存中的內容了。然后利用 ByteArrayOutputStream 來構造 InputStream 就達到了讀取配置文件一次,然后重復構造 InputStream 進行重復讀取,相關代碼如下:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// 為了避免原代碼中頻繁調用 loadRoot 去頻繁讀取 /mycat.dtd 和 /mycat.xml,所以將兩個文件進行緩存,
// 注意這里并不會一直緩存在內存中,隨著 LocalLoader 對象的回收,緩存占用的內存自然也會被回收。
private static byte[] xmlBuffer = null;
private static byte[] dtdBuffer = null;
private static ByteArrayOutputStream xmlBaos = null;
private static ByteArrayOutputStream dtdBaos = null;
static {
  InputStream input = ConfigFactory.class.getResourceAsStream("/mycat.dtd");
  if(input != null){
    dtdBuffer = new byte[1024 * 512];
    dtdBaos = new ByteArrayOutputStream();
    bufferFileStream(input, dtdBuffer, dtdBaos);
  }
  input = ConfigFactory.class.getResourceAsStream("/mycat.xml");
  if(input != null){
    xmlBuffer = new byte[1024 * 512];
    xmlBaos = new ByteArrayOutputStream();
    bufferFileStream(input, xmlBuffer, xmlBaos);
  }
}

bufferFileStream 方法:

?
1
2
3
4
5
6
7
8
9
10
11
12
private static void bufferFileStream(InputStream input, byte[] buffer, ByteArrayOutputStream baos){
  int len = -1;
  try {
    while ((len = input.read(buffer)) > -1 ) {
      baos.write(buffer, 0, len);
    }
    baos.flush();
  } catch (IOException e) {
    e.printStackTrace();
    logger.error(" bufferFileStream error: " + e.getMessage());
  }
}

 loadRoat 優化之后如下:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
private static Element loadRoot() {
  Element root = null;
  InputStream mycatXml = null;
  InputStream mycatDtd = null;
  if(xmlBaos != null)
    mycatXml = new ByteArrayInputStream(xmlBaos.toByteArray());
  if(dtdBaos != null)
    mycatDtd = new ByteArrayInputStream(dtdBaos.toByteArray());
  try {
  root = ConfigUtil.getDocument(mycatDtd, mycatXml).getDocumentElement();
  } catch (ParserConfigurationException | SAXException | IOException e1) {
    e1.printStackTrace();
    logger.error("loadRoot error: " + e1.getMessage());
  }finally{
    if(mycatXml != null){
      try { mycatXml.close(); } catch (IOException e) {}
    }
    if(mycatDtd != null){
      try { mycatDtd.close(); } catch (IOException e) {}
    }
  }
  return root;
}

這樣優化之后,即使有很多方法頻繁調用 loadRoot() 方法,也不會重復讀取配置文件了,而是使用 byte[] 內容,重復構造 InputStream 而已。

其實其原理,就是利用 byte[] 作為一個中間容器,對byte進行緩存,ByteArrayOutputStream 將 InputStream 讀取的 byte 存放如 byte[]容器,然后利用 ByteArrayInputStream 從 byte[]容器中讀取內容,構造 InputStream,只要 byte[] 這個緩存容器存在,就可以多次重復構造出 InputStream。 于是達到了讀取一次配置文件,而重復構造出InputStream,避免了每構造一次InputStream,就讀取一次配置文件的問題。

2. 第二次優化:

可能你會想到更好的方法,比如:

為什么我們不將 private static Element root = null; 作為類屬性,緩存起來,這樣就不需要重復打開和關閉配置文件了,修改如下:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
public class LocalLoader implements ConfigLoader {
  private static final Logger logger = LoggerFactory.getLogger("LocalLoader");
  // ... .. 
  private static Element root = null;
  // 然后 loadRoot 方法改為:
  private static Element loadRoot() {
    InputStream dtd = null;
    InputStream xml = null;
//    Element root = null;
    if(root == null){
      try {
        dtd = ConfigFactory.class.getResourceAsStream("/mycat.dtd");
        xml = ConfigFactory.class.getResourceAsStream("/mycat.xml");
        root = ConfigUtil.getDocument(dtd, xml).getDocumentElement();
      } catch (ConfigException e) {
        throw e;
      } catch (Exception e) {
        throw new ConfigException(e);
      } finally {
        if (dtd != null) {
          try {
            dtd.close();
          } catch (IOException e) { }
        }
        if (xml != null) {
          try {
            xml.close();
          } catch (IOException e) { }
        }
      }
    }
    return root;
  }

這樣就不需要也不會重復 打開和關閉配置文件了。只要 root 屬性沒有被回收,那么 root 引入的 Document 對象也會在緩存中。這樣顯然比第一次優化要好很多,因為第一次優化,還是要從 byte[] 重復構造 InputStream, 然后重復 build 出 Document 對象。

3. 第三次優化

上面是將 private static Element root = null; 作為一個屬性進行緩存,避免重復讀取。那么我們干嘛不直接將 Document 對象作為一個屬性,進行緩存呢。而且具有更好的語義,代碼更好理解。代碼如下:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
public class LocalLoader implements ConfigLoader {
  private static final Logger logger = LoggerFactory.getLogger("LocalLoader");
  // ... ...
  // 為了避免原代碼中頻繁調用 loadRoot 去頻繁讀取 /mycat.dtd 和 /mycat.xml,所以將 Document 進行緩存,
  private static Document document = null;
  private static Element loadRoot() {
    InputStream dtd = null;
    InputStream xml = null;   
    if(document == null){
      try {
        dtd = ConfigFactory.class.getResourceAsStream("/mycat.dtd");
        xml = ConfigFactory.class.getResourceAsStream("/mycat.xml");
        document = ConfigUtil.getDocument(dtd, xml);
        return document.getDocumentElement();
      } catch (Exception e) {
        logger.error(" loadRoot error: " + e.getMessage());
        throw new ConfigException(e);
      } finally {
        if (dtd != null) {
          try { dtd.close(); } catch (IOException e) { }
        }
        if (xml != null) {
          try { xml.close(); } catch (IOException e) { }
        }
      }
    }   
    return document.getDocumentElement();
  }

這樣才是比較合格的實現。anyway, 第一種優化,學習到了 ByteArrayOutputStream 和 ByteArrayInputStream 同 byte[] 配合使用的方法。

---------------------分割線------------------------------------

參考文章:http://blog.csdn.net/it_magician/article/details/9240727 原文如下:

有時候我們需要對同一個InputStream對象使用多次。比如,客戶端從服務器獲取數據 ,利用HttpURLConnection的getInputStream()方法獲得Stream對象,這時既要把數據顯示到前臺(第一次讀取),又想把數據寫進文件緩存到本地(第二次讀取)。

但第一次讀取InputStream對象后,第二次再讀取時可能已經到Stream的結尾了(EOFException)或者Stream已經close掉了。

而InputStream對象本身不能復制,因為它沒有實現Cloneable接口。此時,可以先把InputStream轉化成ByteArrayOutputStream,后面要使用InputStream對象時,再從ByteArrayOutputStream轉化回來就好了。代碼實現如下:

?
1
2
3
4
5
6
7
8
9
10
11
12
InputStream input = httpconn.getInputStream();
ByteArrayOutputStream baos = new ByteArrayOutputStream();
byte[] buffer = new byte[1024];
int len;
while ((len = input.read(buffer)) > -1 ) {
  baos.write(buffer, 0, len);
}
baos.flush();      
InputStream stream1 = new ByteArrayInputStream(baos.toByteArray());
//TODO:顯示到前臺
InputStream stream2 = new ByteArrayInputStream(baos.toByteArray());
//TODO:本地緩存

java中ByteArrayInputStream和ByteArrayOutputStream類用法

ByteArrayInputStream和ByteArrayOutputStream,用于以IO流的方式來完成對字節數組內容的讀寫,來支持類似內存虛擬文件或者內存映射文件的功能

實例:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
import java.io.*;
public class ByteArrayStreamTest {
  public static void main(String [] args) {
    String str = "abcdef";
    ByteArrayInputStream in = new ByteArrayInputStream(str.getBytes());
    ByteArrayOutputStream out = new ByteArrayOutputStream();
    transform(in, out);
    byte[] result = out.toByteArray();
    System.out.println(out);
    System.out.println(new String(result));
    transform(System.in, System.out); // 從鍵盤讀,輸出到顯示器
  }
  public static void transform(InputStream in, OutputStream out) {
    int ch = 0;
    try {
      while ((ch = in.read()) != -1) {
        int upperChar = Character.toUpperCase((char)ch);
        out.write(upperChar);
      } // close while
    } catch (Except

延伸 · 閱讀

精彩推薦
主站蜘蛛池模板: 国产日韩精品欧美一区视频 | 黄色视频一级毛片 | 毛片小网站 | www.guochanav.com| 精品国产96亚洲一区二区三区 | 国产午夜精品久久久久 | 狠狠干91| www.91成人| 日本网站一区二区三区 | 精品一区二区在线播放 | 一级电影免费 | 免费国产a| 日本高清无遮挡 | 伊人yinren22综合网色 | av成人免费在线观看 | 欧美69free性videos | 一级毛片在线观看视频 | 欧美国产精品久久 | 国产小视频在线观看 | 日韩视频在线一区二区三区 | 99爱视频 | www.17c亚洲蜜桃 | 一区二区美女视频 | 久草在线资源福利站 | 久久精品综合视频 | 欧美色视 | 西川av在线一区二区三区 | 羞羞视频.www在线观看 | 手机av在线电影 | 国产成人高清在线观看 | 国产做爰全免费的视频黑人 | 全黄毛片 | 美女扒开胸罩给男生看视频 | 女人久久久www免费人成看片 | 久久99精品久久久久久236 | 欧美成人一区二区三区电影 | av大全在线免费观看 | 午夜精品成人 | 免费香蕉成视频成人网 | 国产精品成人av片免费看最爱 | 高清国产福利 |