走进JDK(四)------InputStream、OutputStream、Reader、Writer
InputStream
InputStream是java中的输入流,下面基于java8来分析下InputStream源码
一、类定义
public abstract class InputStream implements Closeable
Closeable接口定义了close()方法,流在使用完之后需要关闭,并且放在finally块中操作比较好。
二、变量
// 该变量用于确定在skip方法中使用的最大缓存数组大小。
private static final int MAX_SKIP_BUFFER_SIZE = 2048;
三、主要方法
1、read()
//从输入流中读取下一字节数据。返回的字节值为一个范围在0-255之间的int数。若由于到达流的尾部而没有字节可获取,则返回-1.直到数据可达,检测到流的末尾或者抛出一个异常,该方法才停止。由每个子类自己实现。
public abstract int read() throws IOException;
//将数据放入到字节数组中,内部调用的也是read(b, 0, b.length)
public int read(byte b[]) throws IOException {
return read(b, 0, b.length);
}
//byte[] b代表存放数据的字节数组,off代表将读取的数据写入数组b的起始偏移地址。len默认则是b的长度
public int read(byte b[], int off, int len) throws IOException {
if (b == null) {
throw new NullPointerException();
} else if (off < 0 || len < 0 || len > b.length - off) {
throw new IndexOutOfBoundsException();
} else if (len == 0) {
return 0;
} //这地方有个很有意思的问题,为啥不用byte作为接收值,需要int,此方法的返回值的范围为0-255,如果使用byte,则byte会将255转成-1,这样就会与read()方法的约定混淆,因为返回-1时,会认为流中已经没有数据,但此时是有数据的。
//那么java内部byte是怎么转成int的呢?例如对于-1这个值,在java中,负数都是以补码的形式存在(可以参考本人另外一篇文章)。-1的补码就是11111111,因为byte是1个字节8位。而int是4个字节32位,当byte->int时,所有的高位都会补0,
//-1对应的int则为00000000 00000000 00000000 11111111,这个数在int中则为255,也就是说byte的-1转成就是int的255.所以在下面(byte)c就可以将数据还原成byte,这种做法既可以保证读取到数据为128-255时不会出错,也能保证byte->int->byte
//读取的是原始数据
int c = read();
if (c == -1) {
return -1;
}
b[off] = (byte)c; int i = 1;
try {
for (; i < len ; i++) {
c = read();
if (c == -1) {
break;
}
b[off + i] = (byte)c;
}
} catch (IOException ee) {
}
return i;
}
2、available()
可以在读写操作前先得知数据流里有多少个字节可以读取。需要注意的是,如果这个方法用在从本地文件读取数据时,一般不会遇到问题,但如果是用于网络操作,就经常会遇到一些麻烦。比如,Socket通讯时,对方明明发来了1000个字节,
但是自己的程序调用available()方法却只得到900,或者100,甚至是0,感觉有点莫名其妙,怎么也找不到原因。其实,这是因为网络通讯往往是间断性的,一串字节往往分几批进行发送。
本地程序调用available()方法有时得到0,这可能是对方还没有响应,也可能是对方已经响应了,但是数据还没有送达本地。对方发送了1000个字节给你,也许分成3批到达,这你就要调用3次available()方法才能将数据总数全部得到。
InputStream中的此方法一直返回的0,能否使用取决于实现了InputStream这个抽象类的具体子类中有没有实现available这个方法。
public int available() throws IOException {
return 0;
}
很多小伙伴在读取流之前喜欢使用available()方法来判断有多少字节,写法如下:
int count = in.available();
byte[] b = new byte[count];
in.read(b);
这样在网络延迟的情况下就会有问题,应改成如下这种(不过这种也有问题,当流没数据一直循环):
int count = 0;
while (count == 0) {
count = in.available();
}
byte[] b = new byte[count];
in.read(b);
3、close()
//关闭输入流并释放与其相关的系统资源。一般放在finally中操作
public void close() throws IOException {}
OutputStream
一、类定义
public abstract class OutputStream implements Closeable, Flushable
flushable接口则定义flush()
二、主要方法
1、write()
//将指定的一个字节写入此输出流。int 值为 4 个字节,此方法丢弃 int 类型高位的 3 个字节,只保留低位的 1 个字节写入(对字节来说,转为 int 其 3 个高位字节都是全 0 的,所以可以丢弃)。此方法是抽象方法,子类必须要进行实现
public abstract void write(int b) throws IOException;
//将 b.length 个字节从指定的 byte 数组写入此输出流。调用下边的 write 方法。
public void write(byte b[]) throws IOException {
write(b, 0, b.length);
}
//将指定 byte 数组中从偏移量 off 开始的 len 个字节写入此输出流。
public void write(byte b[], int off, int len) throws IOException {
if (b == null) {
throw new NullPointerException();
} else if ((off < 0) || (off > b.length) || (len < 0) ||
((off + len) > b.length) || ((off + len) < 0)) {
throw new IndexOutOfBoundsException();
} else if (len == 0) {
return;
}
for (int i = 0 ; i < len ; i++) {
write(b[off + i]);
}
}
2、flush()
//刷新此输出流并强制写出所有缓冲的输出字节。此类未实现具体行为,子类应该复写此方法。
public void flush() throws IOException {}
3、close()
//关闭此输出流并释放与此流有关的所有系统资源,此类未实现具体行为,子类应该复写此方法。
public void close() throws IOException {}
Reader
一、类定义
public abstract class Reader implements Readable, Closeable
Readable:Readable 接口表示尝试将字符读取到指定的缓冲中。
Closeable:Closeable 接口表示 Reader 可以被close。
二、变量
//最大跳过缓冲的大小
private static final int maxSkipBufferSize = 8192;
//是一个 char[] 类型,表示跳过缓冲
private char skipBuffer[] = null;
//是 Reader 的锁,用于实现同步
protected Object lock;
三、构造函数
protected Reader() {
this.lock = this;
}
protected Reader(Object lock) {
if (lock == null) {
throw new NullPointerException();
}
this.lock = lock;
}
四、主要方法
1、read()
//所有 read 方法最终都会调用这个抽象方法,提供给子类处理逻辑的实现。它传入的三个参数,字符数组cbuf、偏移量off和数组长度。
public abstract int read(char cbuf[], int off, int len) throws IOException;
//无参的 read 方法其实是默认读一个字符,new 一个 char 对象然后调用子类实现进行读取,最后返回读到的字符
public int read() throws IOException {
char cb[] = new char[1];
if (read(cb, 0, 1) == -1)
return -1;
else
return cb[0];
}
//假如 read 方法传入的参数为 char 数组时,则直接调用子类实现进行读取
public int read(char cbuf[]) throws IOException {
return read(cbuf, 0, cbuf.length);
}
//最后一个 read 方法其实是 Readable 接口定义的方法,用于读取字符到指定的 CharBuffer 对象中,逻辑是先得到 CharBuffer 对象剩余长度,根据该长度实例化 char 数组,然后调用子类实现完成读取,最后将读取到的字符放进 CharBuffer 对象
public int read(java.nio.CharBuffer target) throws IOException {
int len = target.remaining();
char[] cbuf = new char[len];
int n = read(cbuf, 0, len);
if (n > 0)
target.put(cbuf, 0, n);
return n;
}
2、close()
abstract public void close() throws IOException;
Writer
一、类定义
public abstract class Writer implements Appendable, Closeable, Flushable
二、变量
//字符缓存数组。用于临时存放要写入字符输出流中的字符
private char[] writeBuffer;
//字符缓存数组的默认大小
private static final int WRITE_BUFFER_SIZE = 1024;
//用于同步针对此流的操作的对象
protected Object lock;
三、构造函数
protected Writer() {
this.lock = this;
}
protected Writer(Object lock) {
if (lock == null) {
throw new NullPointerException();
}
this.lock = lock;
}
四、主要方法
1、write()
//写入单个字符。要写入的字符包含在给定整数值的16个低位中,16高位被忽略。
public void write(int c) throws IOException {
synchronized (lock) {
if (writeBuffer == null){
writeBuffer = new char[WRITE_BUFFER_SIZE];
}
writeBuffer[0] = (char) c;
write(writeBuffer, 0, 1);
}
}
//将一个字符数组写入到writerBuffer中
public void write(char cbuf[]) throws IOException {
write(cbuf, 0, cbuf.length);
}
//试图将字符数组中从off开始的len个字符写入输出流中。尽量写入len个字符,但写入的字节数可能少于len个,也可能为零。
abstract public void write(char cbuf[], int off, int len) throws IOException;
//写入字符串
public void write(String str) throws IOException {
write(str, 0, str.length());
}
//试图将字符串中从off开始的len个字符写入输出流中。尽量写入len个字符,但写入的字节数可能少于len个,也可能为零。
public void write(String str, int off, int len) throws IOException {
synchronized (lock) {
char cbuf[];
if (len <= WRITE_BUFFER_SIZE) {
if (writeBuffer == null) {
writeBuffer = new char[WRITE_BUFFER_SIZE];
}
cbuf = writeBuffer;
} else { // Don't permanently allocate very large buffers.
cbuf = new char[len];
}
str.getChars(off, (off + len), cbuf, 0);
write(cbuf, 0, len);
}
}
2、append()
/**
* 添加字符序列
*/
public Writer append(CharSequence csq) throws IOException {
if (csq == null)
write("null");
else
write(csq.toString());
return this;
} /**
* 添加字符序列的一部分
*/
public Writer append(CharSequence csq, int start, int end) throws IOException {
CharSequence cs = (csq == null ? "null" : csq);
write(cs.subSequence(start, end).toString());
return this;
} /**
* 添加指定字符
*/
public Writer append(char c) throws IOException {
write(c);
return this;
}
3、flush()
/**
* 刷新该流的缓冲。
*/
abstract public void flush() throws IOException;
4、close()
/**
* 关闭此流,但要先刷新它。
*/
abstract public void close() throws IOException;
走进JDK(四)------InputStream、OutputStream、Reader、Writer的更多相关文章
- Java I/O流-总结(InputStream,OutputStream,Reader,Writer)
Java流总结 一. 流的分类 • 按数据流动方向 – 输入流:只能从中读取字节数据,而不能向其写出数据 – 输出流:只能向其写入字节数据,而不能从中读取数据 • 按照流所处理的数据类型 – 字节流: ...
- 02_IO操作的基本规律(InputStream,OutputStream,Reader,Writer,FileReader,FileWriter,BufferedReader,BufferedWri
模拟BufferedInputStream,编写一个类 package toto.IO; import java.io.IOException; import java.io.InputStre ...
- InputStream和Reader区别
InputStream,OutputStream 前者为字节输入流,后者为字节输出流.Reader Writer 前者为字符输入流,后者为字符输出流. 四个均为抽象类.fileInputStr ...
- Stream,Reader/Writer,Buffered的区别(1)
Stream: 是字节流形式,exe文件,图片,视频等.支持8位的字符,用于 ASCII 字符和二进制数据. Reader/Writer: 是字符流,文本文件,XML,txt等,用于16位字符,也就是 ...
- java13 InputStream,Reader
流的方向: .输入流:数据源到程序(InputStream,Reader读进来). .输出流:程序到目的地(OutPutStream,Writer写出来). 处理数据单元: 字节流:按照字节读取数据( ...
- 字节流InputStream/OutputStream
字节流InputStream/OutputStream 本篇将对JAVA I/O流中的字节流InputStream/OutputStream做个简单的概括: 总得来说,每个字节流类都有一个对应的用途, ...
- ExtJS4.2学习(7)——基础知识之Reader&Writer篇
Reader: 主要用于将proxy数据代理读取的数据按照不同的规则进行解析,将解析好的数据保存到Modle中. 结构图 Ext.data.reader.Reader 读取器的根类(很少直接实例化这个 ...
- 系统学习 Java IO (十五)----字符读写 Reader/Writer 其他子类
目录:系统学习 Java IO---- 目录,概览 跟踪行号的缓冲字符输入流 LineNumberReader LineNumberReader 类是一个 BufferedReader ,用于跟踪读取 ...
- 调试过程中发现按f5无法走进jdk源码
debug 模式 ,在fis=new FileInputStream(file); 行打断点 调试过程中发现按f5无法走进jdk源码 package com.lzl.spring.test; impo ...
随机推荐
- Error Code: 1786 Statement violates GTID consistency: CREATE TABLE ... SELECT.
1.错误描述 1 queries executed, 0 success, 1 errors, 0 warnings 查询:call account_check_main('20180511') 错误 ...
- Linux 上利用Nginx代理uWSGI处理Flask web应用
一.介绍 最近开发要用一个测试环境,是这样的Nginx+uwsgi+flask 的一个结构.下面是一些记录,在Centos 系统上使用Flask 架构部署一个简单的Python应用.然后使用Nginx ...
- 7-安装Spark
1.Apache Hadoop2.7中的YARN与JAVA8有冲突,如果想要使用spark on yarn,首先需要在yarn-site.xml中配置如下项: <property> < ...
- vlan划分
1.vlan:虚拟局域网: 作用:划分广播域,抑制广播风暴: 2.vlan技术的优点: 有效控制广播域范围: 增强局域网的安全性: 灵活构建虚拟工作组: 3.vlan划分的方式: 基于端口: 基于MA ...
- 学习笔记 requests + BeautifulSoup
第一步:requests get请求 # -*- coding:utf-8 -*- # 日期:2018/5/15 17:46 # Author:小鼠标 import requests url = &q ...
- Java 性能调优工具
CPU使用率工具: vmstat 检查应用性能时,应该首先审查CPU时间.代码优化的目的是提升而不是降低(更短时间段内的)CPU的使用率.在试图深入优化应用前,应该先弄清楚为何CPU使用率低.磁盘使用 ...
- activemq 无法消费! consumers are alive when the messages are stuck !
我的微服务中, activemq 消费 一条消息的时候, 出了错, 结果导致了 那条消息就一直处于pending 状态, queue.user.545c2ed5-fee7-482a-bb59-564b ...
- Python模块定义和使用
Python中所谓的模块就是一个Python文件,一个abc.py的文件就是一个名字叫abc的模块,一个xyz.py的文件就是一个名字叫xyz的模块.模块由代码.函数或类组成.编程中使用模块不仅可以提 ...
- go相关知识点
后续开发go相关, 环境搭建 go env //查看环境所有 go只有三种引用类型 slice(切片). map(字典). channel(管道): go的类型的浅记忆 4仲类型bool,字符串,数字 ...
- 为datagrid、treegrid增加右键表头菜单,用于显示或隐藏列,注意:冻结列不在此菜单中
var createGridHeaderContextMenu = function(e, field) { e.preventDefault(); var grid = $(this);/* gri ...