Java-IO 体系学习笔记
Java IO 体系这个话题,一是因为平时使用IO时有点手忙脚乱,IO类数量太多,总是有点迷糊,所谓好记性不如烂笔头,系统学习一遍
本文参考书籍包括
- 《Java 编程思想》-机械工业出版社,第四版
- 《Head First设计模式》-中国电力出版社
- 《Java-API 文档》
输入/输出-引入[I/O的概念]
- 流:表示任何有能力产出数据的数据源对象或者有能力接收数据的数据端对象,流屏蔽了实际的IO设备中处理数据的细节;
- Java 体系中的I/O 类氛围输入和输出两部分;
- 任何自InputStream或者Reader派生而来的类都有read() 方法,用于读取单个字节或者字节数组;
- 任何自OutPutStream或者Writer派生而来的类都有write() 方法,用于写单个字节或者字节数组;
- Java I/O体系类的设计,通过装饰者设计模式,对外提供各种所需的功能,但是另一方面,我们创建单一的流对象,却要通过叠加多个对象来提供所期望的功能,这也是让开发者对I/O体系类感到迷惑的原因;但是你只要了解了装饰者模式,稍加分析,就能快速理清晰I/O类的架构设计
InputStream 类 & OutputStream 类
- InputStream :输入流,可以理解为从各种数据源往程序中读入的接口类
// 输入流可以这么写
String ioTestStr = "我是一个测试字符串";
// 获取字符串字节数组
byte[] data = ioTestStr.getBytes();
// 将字节数组载入流
ByteArrayInputStream bai = new ByteArrayInputStream(data);
- OutputStream :输出流,可以理解为将数据从程序写出到数据源的接口类
// 输出流示例
String ioTestStr = "我是一个测试字符串";
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
byte[] dataByte = ioTestStr.getBytes();
// 主要看这句:dataByte 为输出流操作的数据
// 执行write方法,将字节数组写入到outputStream中
byteArrayOutputStream.write(dataByte);
byte[] bytes = byteArrayOutputStream.toByteArray();
logger.info("result:{}",new String(bytes));
- 如上两点中提及的数据源包括:字节数组、String对象、文件、管道、其他数据源泪如Internet连接等
- 每一种数据源都有相对应的InputStream子类,用于数据操作,这里节省篇幅,不列举相关接口,可以直接参考java-api文档
- 给一个结合输入,输出流操作数据的完整实例
// 这个例子是Java代码中发送Http-post请求的一个实例,使用输出流将数据写出去,使用输入流从网络中读取数据
private static String sendPost(String url, String param) {
PrintWriter out = null;
BufferedReader in = null;
String result = "";
HttpURLConnection conn = null;
try {
String urlNameString = url;
URL realUrl = new URL(url);
// 打开和URL之间的连接
conn = (HttpURLConnection) realUrl.openConnection();
// 打开和URL之间的连接
if (conn instanceof HttpsURLConnection) {
((HttpsURLConnection) conn).setSSLSocketFactory(getSSLContext().getSocketFactory());
}
// 设置通用的请求属性
conn.setRequestProperty("Content-Type", "application/json;charset=UTF-8");
conn.setRequestProperty("accept", "*/*");
conn.setRequestProperty("connection", "Keep-Alive");
conn.setRequestProperty("user-agent", "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1;SV1)");
// 发送POST请求必须设置如下两行
conn.setDoOutput(true);
conn.setDoInput(true);
// 这里获取输出流(包装流),用于数据操作
// 获取URLConnection对象对应的输出流
out = new PrintWriter(conn.getOutputStream());
// 发送请求参数
out.print(param);
// flush输出流的缓冲
out.flush();
// 定义BufferedReader输入流来读取URL的响应
in = new BufferedReader(
new InputStreamReader(conn.getInputStream()));
String line;
while ((line = in.readLine()) != null) {
result += line;
}
} catch (Exception e) {
logger.error("请求异常" + e.getMessage());
throw new YeepayRuntimeException("请求异常", e);
}
//使用finally块来关闭输出流、输入流
finally {
try {
if (out != null) {
out.close();
}
if (in != null) {
in.close();
}
if (conn != null){
conn.disconnect();
}
} catch (IOException e) {
logger.error("执行请求完之后关闭资源异常" + e.getMessage());
throw new RuntimeException("执行请求完之后关闭资源异常", e);
}
}
logger.error("DATA FROM SERVER" + result);
return result;
}
使用装饰者模式,组合I/O类库
- java I/O类库需要操作多种数据源,需要多种不同的功能的组合,这里恰好是装饰者模式一个很好的应用,如上的实例中,获取PrintWriter实例:new PrintWriter(conn.getOutputStream()); PrintWriter 构造器接收一个OutputStream对象,从而获取一个扩展自OutputStream的对象
- I/O类库的类图
输入流类图(部分)
输出流类图(部分)
- 可以发现,输入流类图架构就是装饰者模式典型的例子,输出流类图类似,所以,在实际应用中,按照装饰者模式,理解IO类架构核心类,也就不那么模糊了,多一点实践,掌握IO也就不那么困难了!