IO流(字节流)
基本概念
IO流用来处理设备之间的数据传输,Java对数据的操作是通过流的方式,Java用于操作流的类都在IO包中,流按照流向分为:输入流、输出流
按照操作类型分类可分为:
1.字节流:字节流可以操作任何字符,因为在计算机中任何数据都是以字节的形式存储的
2.字符流只能操作纯字符数据,比较方便
IO流的抽象父类
1.字节流的抽象父类
InputStream
OutputStream
2.字符流的抽象父类
Reader
Writer
字节流
输入流FileInputStream
输出流FileOutputStream
基础示例e.g.
FileInputStream fis = new FileInputStream("test.txt");
FileOutputStream fos = new FileOutputStream("bbb.txt", true); //这里可以传入true参数,表示向文件内追加内容,而默认是false先删除文件再写入文件
int b; //定义变量,记录每次读到的字节
while((b = fis.read()) != -1) {
fos.write(b); //虽然写出的是一个int数,但是在写出的时候会将前面的24个0去掉,所以写出的一个byte
}
fis.close();
fos.close();
Note:需要注意的是,FileInputStream是字节流,那为什么定义一个int型的b变量来接收读取的字节数据,这是因为字节输入流可以操作任意类型的文件,比如图片音频等,这些文件底层都是以二进制形式的存储的,如果每次读取都返回byte,有可能在读到中间的时候遇到111111111,那么这11111111是byte类型的-1,我们的程序是遇到-1就会停止不读了,后面的数据就读不到了,所以在读取的时候用int类型接收,如果11111111会在其前面补上24个0凑足4个字节,那么byte类型的-1就变成int类型的255了这样可以保证整个数据读完,而结束标记的-1就是int类型
上例中每次读写一个字节,效率很低,那么可以使用字节数组优化
e.g.
FileInputStream fis = new FileInputStream(“test.txt”);
FileOutputStream fos = new FileOutputStream(“bbb.txt”);
byte[] arr = new byte[1024 * 8]; //定义一个1024 * 8大小的字节数组
while((len = fis.read(arr)) != -1) { //一次读取一个字节数组大小的字节数据
fos.write(arr, 0, len);
}
fis.close();
fos.close();
Note: 可以看到上例中定义了一个1024 * 8大小的字节数组,一次读取这么多个字节然后一次写入,这样大大降低了操作磁盘的次数从而提高了效率
使用 BufferedInputStream 和 BufferedOutputStream
这个名字一看就是引入了一个缓冲区的概念,基本思想是:
字节流一次读写一个数组的速度明显比一次读写一个字节的速度快很多,这是加入了数组这样的缓冲区效果,java本身在设计的时候,
使用了装饰设计模式的设计思想,所以提供了字节缓冲区流
1.BufferedInputStream
BufferedInputStream内置了一个缓冲区(数组)
从BufferedInputStream中读取一个字节时
BufferedInputStream会一次性从文件中读取8192个, 存在缓冲区中, 返回给程序一个
程序再次读取时, 就不用找文件了, 直接从缓冲区中获取
直到缓冲区中所有的都被使用过, 才重新从文件中读取8192个
2.C.BufferedOutputStream
BufferedOutputStream也内置了一个缓冲区(数组)
程序向流中写出字节时, 不会直接写到文件, 先写到缓冲区中
直到缓冲区写满, BufferedOutputStream才会把缓冲区中的数据一次性写到文件里
示例e.g.
BufferedInputStream bis = new BufferedInputStream(new FileInputStream(“test.txt”));
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(“bbb.txt”));
//使用了装饰设计模式将原生的FileInputStream和FileOutputStream进行装饰加入缓冲区
int b;
while((b = bis.read()) != -1) {
bos.write(b);
}
bis.close();
bos.close();
flush和close的方法介绍
flush()方法,用来刷新缓冲区的,刷新后可以再次写出
close()方法,用来关闭流释放资源的的,如果是带缓冲区的流对象的close()方法,不但会关闭流,还会再关闭流之前刷新缓冲区,关闭后不能再写出
字节流读写中文的问题
因为字节流在读写的时候操作的单位是字节,那么在读取中文是会出现读到半个中文而导致乱码
在单独写出中文字符串的时候,必须要先用getBytes()方法将中文字符串转换成字节数组在写出,否则也会乱码
IO流标准的异常处理代码
e.g.
FileInputStream fis = null;
FileOutputStream fos = null;
try {
fis = new FileInputStream(“aaa.txt”);
fos = new FileOutputStream(“bbb.txt”);
int b;
while((b = fis.read()) != -1) {
fos.write(b);
}
} finally {
try {
if(fis != null)
fis.close();
}finally {
if(fos != null)
fos.close();
}
}
在jdk1.7版本之后,可以使用下面这种跟简单的方式
e.g.
try(
FileInputStream fis = new FileInputStream(“aaa.txt”);
FileOutputStream fos = new FileOutputStream(“bbb.txt”);
MyClose mc = new MyClose();
){
int b;
while((b = fis.read()) != -1) {
fos.write(b);
}
}
Note: 原理是在try()中创建的对象必须实现了AutoCloseable这个接口,如果实现了,在try后面的{}的代码执行完之后就会自动调用close掉
使用字节流读写简单的加密
e.g.
BufferedInputStream bis = new BufferedInputStream(new FileInputStream(“a.jpg”));
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(“b.jpg”));
int b;
while((b = bis.read()) != -1) {
bos.write(b ^ 123);
}
bis.close();
bos.close();
Note: 在写入的时候将读取到的字节进行^异或运算,下次读取加密后的文件是再次进行异或运算即可