「这是我参与11月更文挑战的第7天,活动详情查看:2021最后一次更文挑战」
基本概念
Reader是一个抽象类,它是以字符为单位的输入流的公共父类。
构造函数及方法
protected Reader() //创建一个新的字符流 reader,其重要部分将同步其自身的 reader。
protected Reader(Object lock) //创建一个新的字符流 reader,其重要部分将同步给定的对象。
abstract void close() //关闭该流并释放与之关联的所有资源。
void mark(int readAheadLimit) //标记流中的当前位置。
boolean markSupported() //判断此流是否支持 mark() 操作。
int read() //读取单个字符。
int read(char[] cbuf) //将字符读入数组。
abstract int read(char[] cbuf, int off, int len) //将字符读入数组的某一部分。
int read(CharBuffer target) //试图将字符读入指定的字符缓冲区。
boolean ready() //判断是否准备读取此流。
void reset() //重置该流。
long skip(long n) //跳过字符。
Reader类图
CharArrayReader是字符数组输入流,用于读取字符数组。
PipedReader 是字符管道输入流,它继承于Writer。
FileReader 方便读取字符文件的。
BufferedReader 从流里面读取文本,通过缓存的方式提高效率,读取的内容包括字符、数组和行。缓存的大小可以指定,也可以用默认的大小,大部分情况下,默认大小就够了。
InputStreamReader 把字节翻译成字符的,可以处理乱码问题。
CharArrayReader类
CharArrayReader是字符数组输入流,用于读取字符数组,继承于Reader操作的数据是以字符为单位。
CharArrayReader实际上是通过“字符数组”去保存数据。
源码分析
public class CharArrayReader extends Reader {
// 字符数组缓冲
protected char buf[];
// 下一个被获取的字符的位置
protected int pos;
// 被标记的位置
protected int markedPos = 0;
// 字符缓冲的长度
protected int count;
// 构造函数
public CharArrayReader(char buf[]) {
this.buf = buf;
this.pos = 0;
this.count = buf.length;
}
// 构造函数
public CharArrayReader(char buf[], int offset, int length) {
if ((offset < 0) || (offset > buf.length) || (length < 0) ||
((offset + length) < 0)) {
throw new IllegalArgumentException();
}
this.buf = buf;
this.pos = offset;
this.count = Math.min(offset + length, buf.length);
this.markedPos = offset;
}
// 判断“CharArrayReader是否有效”。
// 若字符缓冲为null,则认为其无效。
private void ensureOpen() throws IOException {
if (buf == null)
throw new IOException("Stream closed");
}
// 读取下一个字符。即返回字符缓冲区中下一位置的值。
// 注意:读取的是字符!
public int read() throws IOException {
synchronized (lock) {
ensureOpen();
if (pos >= count)
return -1;
else
return buf[pos++];
}
}
// 读取数据,并保存到字符数组b中从off开始的位置中,len是读取长度。
public int read(char b[], int off, int len) throws IOException {
synchronized (lock) {
ensureOpen();
if ((off < 0) || (off > b.length) || (len < 0) ||
((off + len) > b.length) || ((off + len) < 0)) {
throw new IndexOutOfBoundsException();
} else if (len == 0) {
return 0;
}
if (pos >= count) {
return -1;
}
if (pos + len > count) {
len = count - pos;
}
if (len <= 0) {
return 0;
}
System.arraycopy(buf, pos, b, off, len);
pos += len;
return len;
}
}
// 跳过n个字符
public long skip(long n) throws IOException {
synchronized (lock) {
ensureOpen();
if (pos + n > count) {
n = count - pos;
}
if (n < 0) {
return 0;
}
pos += n;
return n;
}
}
// 判断“是否能读取下一个字符”。能的话,返回true。
public boolean ready() throws IOException {
synchronized (lock) {
ensureOpen();
return (count - pos) > 0;
}
}
public boolean markSupported() {
return true;
}
// 保存当前位置。readAheadLimit在此处没有任何实际意义
// mark()必须和reset()配合使用才有意义!
public void mark(int readAheadLimit) throws IOException {
synchronized (lock) {
ensureOpen();
markedPos = pos;
}
}
// 重置“下一个读取位置”为“mark所标记的位置”
public void reset() throws IOException {
synchronized (lock) {
ensureOpen();
pos = markedPos;
}
}
public void close() {
buf = null;
}
}
PipedReader类
PipedReader 是字符管道输入流,它继承于Writer。它用于读取对应绑定的管道字符输出流写入其内置字符缓存数组buffer中的字符、借此来实现线程之间的通信、PipedReader中专门有两个方法供PipedWriter调用、receive(char c)、receive(char[] b, int off, intlen)、使得PipedWriter可以将字符或者字符数组写入PipedReader的buffer中。
源码分析
public class PipedReader extends Reader {
// “PipedWriter”是否关闭的标记
boolean closedByWriter = false;
// “PipedReader”是否关闭的标记
boolean closedByReader = false;
// “PipedReader”与“PipedWriter”是否连接的标记
// 它在PipedWriter的connect()连接函数中被设置为true
boolean connected = false;
Thread readSide; // 读取“管道”数据的线程
Thread writeSide; // 向“管道”写入数据的线程
// “管道”的默认大小
private static final int DEFAULT_PIPE_SIZE = 1024;
// 缓冲区
char buffer[];
//下一个写入字符的位置。in==out代表满,说明“写入的数据”全部被读取了。
int in = -1;
//下一个读取字符的位置。in==out代表满,说明“写入的数据”全部被读取了。
int out = 0;
// 构造函数:指定与“PipedReader”关联的“PipedWriter”
public PipedReader(PipedWriter src) throws IOException {
this(src, DEFAULT_PIPE_SIZE);
}
// 构造函数:指定与“PipedReader”关联的“PipedWriter”,以及“缓冲区大小”
public PipedReader(PipedWriter src, int pipeSize) throws IOException {
initPipe(pipeSize);
connect(src);
}
// 构造函数:默认缓冲区大小是1024字符
public PipedReader() {
initPipe(DEFAULT_PIPE_SIZE);
}
// 构造函数:指定缓冲区大小是pipeSize
public PipedReader(int pipeSize) {
initPipe(pipeSize);
}
// 初始化“管道”:新建缓冲区大小
private void initPipe(int pipeSize) {
if (pipeSize <= 0) {
throw new IllegalArgumentException("Pipe size <= 0");
}
buffer = new char[pipeSize];
}
// 将“PipedReader”和“PipedWriter”绑定。
// 实际上,这里调用的是PipedWriter的connect()函数
public void connect(PipedWriter src) throws IOException {
src.connect(this);
}
// 接收int类型的数据b。
// 它只会在PipedWriter的write(int b)中会被调用
synchronized void receive(int c) throws IOException {
// 检查管道状态
if (!connected) {
throw new IOException("Pipe not connected");
} else if (closedByWriter || closedByReader) {
throw new IOException("Pipe closed");
} else if (readSide != null && !readSide.isAlive()) {
throw new IOException("Read end dead");
}
// 获取“写入管道”的线程
writeSide = Thread.currentThread();
// 如果“管道中被读取的数据,等于写入管道的数据”时,
// 则每隔1000ms检查“管道状态”,并唤醒管道操作:若有“读取管道数据线程被阻塞”,则唤醒该线程。
while (in == out) {
if ((readSide != null) && !readSide.isAlive()) {
throw new IOException("Pipe broken");
}
/* full: kick any waiting readers */
notifyAll();
try {
wait(1000);
} catch (InterruptedException ex) {
throw new java.io.InterruptedIOException();
}
}
if (in < 0) {
in = 0;
out = 0;
}
buffer[in++] = (char) c;
if (in >= buffer.length) {
in = 0;
}
}
// 接收字符数组b。
synchronized void receive(char c[], int off, int len) throws IOException {
while (--len >= 0) {
receive(c[off++]);
}
}
// 当PipedWriter被关闭时,被调用
synchronized void receivedLast() {
closedByWriter = true;
notifyAll();
}
// 从管道(的缓冲)中读取一个字符,并将其转换成int类型
public synchronized int read() throws IOException {
if (!connected) {
throw new IOException("Pipe not connected");
} else if (closedByReader) {
throw new IOException("Pipe closed");
} else if (writeSide != null && !writeSide.isAlive()
&& !closedByWriter && (in < 0)) {
throw new IOException("Write end dead");
}
readSide = Thread.currentThread();
int trials = 2;
while (in < 0) {
if (closedByWriter) {
/* closed by writer, return EOF */
return -1;
}
if ((writeSide != null) && (!writeSide.isAlive()) && (--trials < 0)) {
throw new IOException("Pipe broken");
}
/* might be a writer waiting */
notifyAll();
try {
wait(1000);
} catch (InterruptedException ex) {
throw new java.io.InterruptedIOException();
}
}
int ret = buffer[out++];
if (out >= buffer.length) {
out = 0;
}
if (in == out) {
/* now empty */
in = -1;
}
return ret;
}
// 从管道(的缓冲)中读取数据,并将其存入到数组b中
public synchronized int read(char cbuf[], int off, int len) throws IOException {
if (!connected) {
throw new IOException("Pipe not connected");
} else if (closedByReader) {
throw new IOException("Pipe closed");
} else if (writeSide != null && !writeSide.isAlive()
&& !closedByWriter && (in < 0)) {
throw new IOException("Write end dead");
}
if ((off < 0) || (off > cbuf.length) || (len < 0) ||
((off + len) > cbuf.length) || ((off + len) < 0)) {
throw new IndexOutOfBoundsException();
} else if (len == 0) {
return 0;
}
/* possibly wait on the first character */
int c = read();
if (c < 0) {
return -1;
}
cbuf[off] = (char)c;
int rlen = 1;
while ((in >= 0) && (--len > 0)) {
cbuf[off + rlen] = buffer[out++];
rlen++;
if (out >= buffer.length) {
out = 0;
}
if (in == out) {
/* now empty */
in = -1;
}
}
return rlen;
}
// 是否能从管道中读取下一个数据
public synchronized boolean ready() throws IOException {
if (!connected) {
throw new IOException("Pipe not connected");
} else if (closedByReader) {
throw new IOException("Pipe closed");
} else if (writeSide != null && !writeSide.isAlive()
&& !closedByWriter && (in < 0)) {
throw new IOException("Write end dead");
}
if (in < 0) {
return false;
} else {
return true;
}
}
// 关闭PipedReader
public void close() throws IOException {
in = -1;
closedByReader = true;
}
}
FileReader类
FilterReader是实现自定义过滤输入字符流的基类,本事是一个抽象类、为所有装饰类提供一个标准、只是简单重写了父类Reader的所有方法、要求子类必须重写核心方法、和提供具有自己特色的方法、这里没有像字节流那样有很多的子类来实现不同的功能、可能是因为字符流本来就是字节流的一种装饰、所以在这里没有必要再对其进行装饰、只是提供一个扩展的接口而已。
源码分析
/**
* 本身是一个抽象类、只简单重写了父类Reader的一些方法、作用是为所有装饰类提供一个标准、
* 子类要重写父类的核心方法和扩展具有自己特色的方法、目前没有子类、作用是为以后的扩展提供一个接口
*/
public abstract class FilterReader extends Reader {
/**底层字符输入流*/
protected Reader in;
/** 使用传入的底层字符输入流创建FilterReader*/
protected FilterReader(Reader in) {
super(in);
this.in = in;
}
/**读取一个字符*/
public int read() throws IOException {
return in.read();
}
/**将字符读取到字符数组cbuf中*/
public int read(char cbuf[], int off, int len) throws IOException {
return in.read(cbuf, off, len);
}
/**跳过底层输入流中的n个字符*/
public long skip(long n) throws IOException {
return in.skip(n);
}
/**检测此流是否可以读取 */
public boolean ready() throws IOException {
return in.ready();
}
/**检测此流是否支持mark*/
public boolean markSupported() {
return in.markSupported();
}
/**标记此流*/
public void mark(int readAheadLimit) throws IOException {
in.mark(readAheadLimit);
}
/**重置最后一次mark的位置 */
public void reset() throws IOException {
in.reset();
}
/**关闭此流 */
public void close() throws IOException {
in.close();
}
}
BufferedReader类
BufferedReader能为字符输入流提供缓冲区,可以提高许多IO处理的速度。你可以一次读取一大块的数据,而不需要每次从网络或者磁盘中一次读取一个字节。特别是在访问大量磁盘数据时,缓冲通常会让IO快上许多。
源码分析
public class BufferedReader extends Reader {
private Reader in;
// 字符缓冲区
private char cb[];
// nChars 是cb缓冲区中字符的总的个数
// nextChar 是下一个要读取的字符在cb缓冲区中的位置
private int nChars, nextChar;
// 表示“标记无效”。它与UNMARKED的区别是:
// (01) UNMARKED 是压根就没有设置过标记。
// (02) 而INVALIDATED是设置了标记,但是被标记位置太长,导致标记无效!
private static final int INVALIDATED = -2;
// 表示没有设置“标记”
private static final int UNMARKED = -1;
// “标记”
private int markedChar = UNMARKED;
// “标记”能标记位置的最大长度
private int readAheadLimit = 0; /* Valid only when markedChar > 0 */
// skipLF(即skip Line Feed)是“是否忽略换行符”标记
private boolean skipLF = false;
// 设置“标记”时,保存的skipLF的值
private boolean markedSkipLF = false;
// 默认字符缓冲区大小
private static int defaultCharBufferSize = 8192;
// 默认每一行的字符个数
private static int defaultExpectedLineLength = 80;
// 创建“Reader”对应的BufferedReader对象,sz是BufferedReader的缓冲区大小
public BufferedReader(Reader in, int sz) {
super(in);
if (sz <= 0)
throw new IllegalArgumentException("Buffer size <= 0");
this.in = in;
cb = new char[sz];
nextChar = nChars = 0;
}
// 创建“Reader”对应的BufferedReader对象,默认的BufferedReader缓冲区大小是8k
public BufferedReader(Reader in) {
this(in, defaultCharBufferSize);
}
// 确保“BufferedReader”是打开状态
private void ensureOpen() throws IOException {
if (in == null)
throw new IOException("Stream closed");
}
// 填充缓冲区函数。有以下两种情况被调用:
// (01) 缓冲区没有数据时,通过fill()可以向缓冲区填充数据。
// (02) 缓冲区数据被读完,需更新时,通过fill()可以更新缓冲区的数据。
private void fill() throws IOException {
// dst表示“cb中填充数据的起始位置”。
int dst;
if (markedChar <= UNMARKED) {
// 没有标记的情况,则设dst=0。
dst = 0;
} else {
// delta表示“当前标记的长度”,它等于“下一个被读取字符的位置”减去“标记的位置”的差值;
int delta = nextChar - markedChar;
if (delta >= readAheadLimit) {
// 若“当前标记的长度”超过了“标记上限(readAheadLimit)”,
// 则丢弃标记!
markedChar = INVALIDATED;
readAheadLimit = 0;
dst = 0;
} else {
if (readAheadLimit <= cb.length) {
// 若“当前标记的长度”没有超过了“标记上限(readAheadLimit)”,
// 并且“标记上限(readAheadLimit)”小于/等于“缓冲的长度”;
// 则先将“下一个要被读取的位置,距离我们标记的置符的距离”间的字符保存到cb中。
System.arraycopy(cb, markedChar, cb, 0, delta);
markedChar = 0;
dst = delta;
} else {
// 若“当前标记的长度”没有超过了“标记上限(readAheadLimit)”,
// 并且“标记上限(readAheadLimit)”大于“缓冲的长度”;
// 则重新设置缓冲区大小,并将“下一个要被读取的位置,距离我们标记的置符的距离”间的字符保存到cb中。
char ncb[] = new char[readAheadLimit];
System.arraycopy(cb, markedChar, ncb, 0, delta);
cb = ncb;
markedChar = 0;
dst = delta;
}
// 更新nextChar和nChars
nextChar = nChars = delta;
}
}
int n;
do {
// 从“in”中读取数据,并存储到字符数组cb中;
// 从cb的dst位置开始存储,读取的字符个数是cb.length - dst
// n是实际读取的字符个数;若n==0(即一个也没读到),则继续读取!
n = in.read(cb, dst, cb.length - dst);
} while (n == 0);
// 如果从“in”中读到了数据,则设置nChars(cb中字符的数目)=dst+n,
// 并且nextChar(下一个被读取的字符的位置)=dst。
if (n > 0) {
nChars = dst + n;
nextChar = dst;
}
}
// 从BufferedReader中读取一个字符,该字符以int的方式返回
public int read() throws IOException {
synchronized (lock) {
ensureOpen();
for (;;) {
// 若“缓冲区的数据已经被读完”,
// 则先通过fill()更新缓冲区数据
if (nextChar >= nChars) {
fill();
if (nextChar >= nChars)
return -1;
}
// 若要“忽略换行符”,
// 则对下一个字符是否是换行符进行处理。
if (skipLF) {
skipLF = false;
if (cb[nextChar] == '\n') {
nextChar++;
continue;
}
}
// 返回下一个字符
return cb[nextChar++];
}
}
}
// 将缓冲区中的数据写入到数组cbuf中。off是数组cbuf中的写入起始位置,len是写入长度
private int read1(char[] cbuf, int off, int len) throws IOException {
// 若“缓冲区的数据已经被读完”,则更新缓冲区数据。
if (nextChar >= nChars) {
if (len >= cb.length && markedChar <= UNMARKED && !skipLF) {
return in.read(cbuf, off, len);
}
fill();
}
// 若更新数据之后,没有任何变化;则退出。
if (nextChar >= nChars) return -1;
// 若要“忽略换行符”,则进行相应处理
if (skipLF) {
skipLF = false;
if (cb[nextChar] == '\n') {
nextChar++;
if (nextChar >= nChars)
fill();
if (nextChar >= nChars)
return -1;
}
}
// 拷贝字符操作
int n = Math.min(len, nChars - nextChar);
System.arraycopy(cb, nextChar, cbuf, off, n);
nextChar += n;
return n;
}
// 对read1()的封装,添加了“同步处理”和“阻塞式读取”等功能
public int read(char cbuf[], int off, int len) throws IOException {
synchronized (lock) {
ensureOpen();
if ((off < 0) || (off > cbuf.length) || (len < 0) ||
((off + len) > cbuf.length) || ((off + len) < 0)) {
throw new IndexOutOfBoundsException();
} else if (len == 0) {
return 0;
}
int n = read1(cbuf, off, len);
if (n <= 0) return n;
while ((n < len) && in.ready()) {
int n1 = read1(cbuf, off + n, len - n);
if (n1 <= 0) break;
n += n1;
}
return n;
}
}
// 读取一行数据。ignoreLF是“是否忽略换行符”
String readLine(boolean ignoreLF) throws IOException {
StringBuffer s = null;
int startChar;
synchronized (lock) {
ensureOpen();
boolean omitLF = ignoreLF || skipLF;
bufferLoop:
for (;;) {
if (nextChar >= nChars)
fill();
if (nextChar >= nChars) { /* EOF */
if (s != null && s.length() > 0)
return s.toString();
else
return null;
}
boolean eol = false;
char c = 0;
int i;
/* Skip a leftover '\n', if necessary */
if (omitLF && (cb[nextChar] == '\n'))
nextChar++;
skipLF = false;
omitLF = false;
charLoop:
for (i = nextChar; i < nChars; i++) {
c = cb[i];
if ((c == '\n') || (c == '\r')) {
eol = true;
break charLoop;
}
}
startChar = nextChar;
nextChar = i;
if (eol) {
String str;
if (s == null) {
str = new String(cb, startChar, i - startChar);
} else {
s.append(cb, startChar, i - startChar);
str = s.toString();
}
nextChar++;
if (c == '\r') {
skipLF = true;
}
return str;
}
if (s == null)
s = new StringBuffer(defaultExpectedLineLength);
s.append(cb, startChar, i - startChar);
}
}
}
// 读取一行数据。不忽略换行符
public String readLine() throws IOException {
return readLine(false);
}
// 跳过n个字符
public long skip(long n) throws IOException {
if (n < 0L) {
throw new IllegalArgumentException("skip value is negative");
}
synchronized (lock) {
ensureOpen();
long r = n;
while (r > 0) {
if (nextChar >= nChars)
fill();
if (nextChar >= nChars) /* EOF */
break;
if (skipLF) {
skipLF = false;
if (cb[nextChar] == '\n') {
nextChar++;
}
}
long d = nChars - nextChar;
if (r <= d) {
nextChar += r;
r = 0;
break;
}
else {
r -= d;
nextChar = nChars;
}
}
return n - r;
}
}
// “下一个字符”是否可读
public boolean ready() throws IOException {
synchronized (lock) {
ensureOpen();
// 若忽略换行符为true;
// 则判断下一个符号是否是换行符,若是的话,则忽略
if (skipLF) {
if (nextChar >= nChars && in.ready()) {
fill();
}
if (nextChar < nChars) {
if (cb[nextChar] == '\n')
nextChar++;
skipLF = false;
}
}
return (nextChar < nChars) || in.ready();
}
}
// 始终返回true。因为BufferedReader支持mark(), reset()
public boolean markSupported() {
return true;
}
// 标记当前BufferedReader的下一个要读取位置。关于readAheadLimit的作用,参考后面的说明。
public void mark(int readAheadLimit) throws IOException {
if (readAheadLimit < 0) {
throw new IllegalArgumentException("Read-ahead limit < 0");
}
synchronized (lock) {
ensureOpen();
// 设置readAheadLimit
this.readAheadLimit = readAheadLimit;
// 保存下一个要读取的位置
markedChar = nextChar;
// 保存“是否忽略换行符”标记
markedSkipLF = skipLF;
}
}
// 重置BufferedReader的下一个要读取位置,
// 将其还原到mark()中所保存的位置。
public void reset() throws IOException {
synchronized (lock) {
ensureOpen();
if (markedChar < 0)
throw new IOException((markedChar == INVALIDATED)
? "Mark invalid"
: "Stream not marked");
nextChar = markedChar;
skipLF = markedSkipLF;
}
}
public void close() throws IOException {
synchronized (lock) {
if (in == null)
return;
in.close();
in = null;
cb = null;
}
}
}
InputStreamReader类
InputStreamReader 将字节流转换为字符流。是字节流通向字符流的桥梁,能将字节流输出为字符流,并且能为字节流指定字符集,可输出一个个的字符。如果不指定字符集编码,该解码过程将使用平台默认的字符编码,如:GBK。
源码分析
public class InputStreamReader extends Reader {
// 流解码类,所有的调用都是交给它完成。
private final StreamDecoder sd;
// 使用默认的字符集名来创建实例
public InputStreamReader(InputStream in) {
super(in);
try {
sd = StreamDecoder.forInputStreamReader(in, this, (String)null); // ## check lock object
} catch (UnsupportedEncodingException e) {
// The default encoding should always be available
throw new Error(e);
}
}
// 根据指定的字符集名来创建实例
public InputStreamReader(InputStream in, String charsetName)
throws UnsupportedEncodingException
{
super(in);
if (charsetName == null)
throw new NullPointerException("charsetName");
sd = StreamDecoder.forInputStreamReader(in, this, charsetName);
}
// 根据指定的字符集来创建实例
public InputStreamReader(InputStream in, Charset cs) {
super(in);
if (cs == null)
throw new NullPointerException("charset");
sd = StreamDecoder.forInputStreamReader(in, this, cs);
}
// 根据指定的字符集解码器来创建实例
public InputStreamReader(InputStream in, CharsetDecoder dec) {
super(in);
if (dec == null)
throw new NullPointerException("charset decoder");
sd = StreamDecoder.forInputStreamReader(in, this, dec);
}
// 获取该流使用的字符编码名
public String getEncoding() {
return sd.getEncoding();
}
// 读取一个字符
public int read() throws IOException {
return sd.read();
}
// 读取一串字符到字符数组中
public int read(char cbuf[], int offset, int length) throws IOException {
return sd.read(cbuf, offset, length);
}
// 查看流是否准备好用于读取。
public boolean ready() throws IOException {
return sd.ready();
}
// 关闭Reader
public void close() throws IOException {
sd.close();
}
}
例子
public static void main(String[] args) throws IOException {
bufferReader();
fileReader();
inputStreamReader();
}
private static void bufferReader()throws IOException{
BufferedReader buf = null ; // 声明对象
buf = new BufferedReader(new InputStreamReader(System.in)) ; // 将字节流变为字符流
String str = null ; // 接收输入内容
System.out.print("请输入内容:") ;
try{
str = buf.readLine() ; // 读取一行数据
}catch(IOException e){
e.printStackTrace() ; // 输出信息
}
System.out.println("输入的内容为:" + str) ;
}
private static void fileReader() throws IOException {
char[] buf = new char[1024]; //字符数组
FileReader in = new FileReader("D:\\hello.txt");
int len = in.read(buf); //此时的read方法可以读取一个字符或几个字符,len代表实际读取到的字符的个数。
System.out.println(new String(buf,0,1024)); //String构造函数把字符数组转化为字符串。
in.close();
}
public static void inputStreamReader() throws IOException {
//读取字节流
//InputStream in = System.in;//读取键盘的输入。
InputStream in = new FileInputStream("D:\\hello.txt");//读取文件的数据。
//将字节流向字符流的转换。要启用从字节到字符的有效转换,
InputStreamReader isr = new InputStreamReader(in);//读取
//InputStreamReader isr = new InputStreamReader(
//new FileInputStream("D:\\demo.txt"));
char []cha = new char[1024];
int len = isr.read(cha);
System.out.println(new String(cha,0,len));
isr.close();
}