文章目录
输入输出流的定义
输入流:只能从中读取数据,而不能向其写入数据.
输出流:只能向其写出数据,而不能从中读取数据.
角度很重要,我们作为Java工程师应该站在程序运行所在内存来考虑的.
字节流和字符流
小技巧
- 字节流的类一定是以Stream结尾的.
- 字符流的输入流以Reader结尾,
- 字符流的输出流以Writer结尾.
区别
字节流操作的最小数据单元是8位(8bit)的字节(byte),而字符流操作的最小数据单元是16位的字符(2byte->char).
节点流和处理流
按照流的角色分,可以分为节点流和处理流.
具体点就是,
可以从或者向一个特定的IO设备读写数据的流(不管你是输入还是输出流,只要与IO设备相关),称为节点流(也被称为低级流).
当一个流对一个已经存在的流进行连接或封装,并通过操作后的流来实现数据读写功能,这个流就可以被称为高级流.
处理流存在的意义
只要使用相同的处理流(字节流或者字符流),程序就可以采用完全相同的输入/输出代码来访问不同的数据源,随着处理流所包装节点流的改变,程序实际所访问的数据源也相应发生改变.
这意味着什么?
我们可以通过使用处理流包装节点流完成装饰器模式的设计,在原节点流的基础上新增一些方法或重写一些方法.
I/O流的基类方法一览(爸爸会的儿子也会)
InputStream和Reader提供的方法一样
区别只是inputStream按字节读取,Reader按字符读取.
方法 | 作用 |
---|---|
int read() | 从输入流中读取单个字节 |
int read(byte[] b) | 从输入流中读取最多b.length个字节的数据,并将其存储在字节数组b中,返回实际读取的字节数 |
int read(byte[] b,int off,int len) | 从输入流中读取最多len字节数据,并将其存储在数组b中,放入b数组中时,并不是从数组起点开始,而是从offset位置开始,返回实际读取的字节数 |
void mark(int readAheadLimit) | 在记录指针当前位置记录一个标记mark |
boolean markSupported() | 判断此输入流是否支持mark()操作,即是否支持记录标记 |
void reset() | 将此流的记录指针重新定位到上一次记录标记mark的位置 |
long skip(long n) | 记录指针向前移动n个字节/字符 |
OutputStream和Writer
读取数据输出后发现乱码的问题
当我们创建较小长度的字节数组时,程序运行时在输出中文注释时可能出现乱码,这是因为本文件保存时采用的是GBK的编码方式.在这种方式下,每个中文字符占2个字节,如果在结束read()后只读到半个中文字符就会发生乱码.
谈谈处理流的具体用法
使用处理流包装节点流,程序通过处理流来执行输入输出.
下面展示一个PrintStream处理流来包装OutputStream节点流的例子
import java.io.FileOutputStream;
import java.io.PrintStream;
public class PrintStreamTest {
public static void main(String[] args) {
//声明处理流
PrintStream pStream=null;
try {
//创建一个节点输出流:FileOutputStream
FileOutputStream fileOutputStream=new FileOutputStream("text.txt");
//用处理流包装节点流
pStream=new PrintStream(fileOutputStream);
pStream.print("普通字符串");
pStream.println(new PrintStreamTest());
} catch (Exception e) {
// TODO: handle exception
}finally {
pStream.close();
}
}
}
标准I/O流体系一览
关于字节/字符流的转换
java提供了两个转换流:InputStreamReader和OutputStreamWriter
这两个类把字符流转为对应的字节流.
一个使用的例子
将System.in包装成BufferReader.
import java.io.BufferedReader;
import java.io.InputStreamReader;
public class KeyinTest {
public static void main(String[] args) {
BufferedReader bReader=null;
try {
InputStreamReader reader=new InputStreamReader(System.in);
bReader=new BufferedReader(reader);
String buffer=null;
while((buffer=bReader.readLine())!=null) {
if (buffer.equals("exit")) {
System.exit(1);
}
System.out.println("输入内容为:"+buffer);
}
} catch (Exception e) {
// TODO: handle exception
}
}
神奇的推回输入流
推回字节流:PushbackInputStream
推回字符流:PushbackReader
方法:
方法 | 作用 |
---|---|
void unread(byte[]/char[] buf) | 将一个字节/字符数组内容推回到缓冲区里,从而允许重复读取刚才读取的内容 |
void unread(byte[] b,int off,int len) | 将一个字节/字符数组里从off开始,长度为len字节/字符的内容推回到缓冲区里 |
void unread(int b) | 将一个字节/字符推回到缓冲区里 |
这三个方法一一对应Read,怎么读进来的就怎么推回去(参数一样).
重定向标准输入/输出
System类中重定向的方法
方法 | 作用 |
---|---|
static void setErr(PrintStream err) | 重定向"标准"错误输出流 |
static void setIn(InputStream in) | 重定向标准输入流 |
static void setOut(printStream out) | 重定向标准输出流 |
操作文件必须会的类:RandomAccessFile
RandomAccessFile可以自由的访问文件的任意位置,所以如果我们希望只访问文件部分内容,而不是把文件从头到尾读取,就使用RandomAccessFile.
优点:可以任意移动记录指针mark,方法如下
方法 | 作用 |
---|---|
long getFilePointer | 返回文件记录指针的当前位置 |
void seek(long pos) | 将文件记录指针定位到pos处 |
关于创建RandomAccessFile的mode参数
r:只读
w:只写
s:内容或元数据每次更新同步到存储设备
d:内容每次更新同步到存储设备
内容插入问题
RandomAccessFile不能向文件的指定位置插入内容,会覆盖后面的内容.
如果要插入内容,则必须先把插入点后面所有的内容读取拷贝(一般放在缓冲区),等把要插入的内容写入后再追加拷贝的内容.