IO流
IO流概述
一.概念
1.IO流用来处理设备之间的数据传输
2.Java对数据的操作是通过流的方式
3.Java用于操作流的类都在IO包中
4.流按流向分为两种:输入流,输出流
5.流按操作类型分为两种:
- 字节流:字节流可以操作任何数据,因为在计算机中任何数据都是以字节的形式存储的
- 字符流:字符流只能操作纯字符数据,比较方便
二.IO流常用父类
1.字节流的抽象父类
- InputStream
- OutputStream
2.字符流的抽象父类
- Reader
- Writer
三.IO程序书写
- 使用前,导入IO包中的类
- 使用时,进行IO异常处理
- 使用后,释放资源
FileInputStream
- Inputstream抽象类的子类
- 可以使用Inputstream类中的read(),close()方法
read()方法读取的是一个字节,为什么返回的是int,而不是byte?
因为字节输入流可以操作任意类型的文件,比如图片音频,这些文件底层都是以二进制形式存储的,如果每次读取都返回byte,有可能在读到中间的时候遇到11111111,那么这11111111是byte类型的-1,我们的程序是遇到-1就会停止不读了,后面的数据就读不到了,所以在读取的时候用int类型接收,如果11111111会在其前面补上24个0凑足4个字节,那么byte类型的-1就变成了int类型的255,这样可以保证整个数据读完,而结束标志的-1就是int类型
案例演示
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
public class Demo1_FileInputStream {
public static void main(String[] args) throws IOException {
//demo1();
FileInputStream fis = new FileInputStream("xxx.txt");//创建流对象
int b;
while ((b = fis.read()) != -1) {
System.out.println(b);//97 98 99
}
fis.close();
}
public static void demo1() throws FileNotFoundException, IOException {
FileInputStream fis = new FileInputStream("xxx.txt");//创建流对象
int x = fis.read(); //从硬盘上读取一个字节 a 97
System.out.println(x);//97
int y = fis.read(); //第二次指针向下走一次读到 b 98
System.out.println(y);//98
int z = fis.read(); //第三次指针向下走一次读到 c 99
System.out.println(z);//98
int end = fis.read(); //第三次指针向下走一次读到 c 99
System.out.println(end);// -1 结束标志
fis.close(); //关流释放资源
}
}
FileOutputStream
一.FileOutputStream的构造方法如何实现数据的写入
- write()方法:向指定文件中写入数据
FileOutpuitStream在创建对象的时候时如果没有这个文件会帮我创建出来
如果有这个文件就会先将文件清空
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
public class Demo2_FileOutputStream {
public static void main(String[] args) throws IOException {
FileOutputStream fos = new FileOutputStream("yyy.txt");//创建字节输出流对象,如果没有就创建一个
fos.write(97);//虽然写出的是一个int数,但是到文件上的是一个字节,会自动去除前三个8位
fos.write(98);
fos.write(99);
fos.close();
}
}
二.FileOutputStream的构造方法如何实现数据的追加写入
- FileOutputStream fos = new FileOutputStream(“yyy.txt”,true);
如果想续写就在第二个参数传入true
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
public class Demo2_FileOutputStream {
public static void main(String[] args) throws IOException {
//demo1();
FileOutputStream fos = new FileOutputStream("yyy.txt",true);//如果想续写就在第二个参数传入true
fos.write(97);
fos.write(98);
fos.write(99);
fos.close();
}
}
文件拷贝
一.字节流读取并写出
弊端:
- 字节流一次读写一个字节复制音频,效率太慢(开发中不推荐)
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
public class Demo3_Copy {
public static void main(String[] args) throws IOException {
//demo1();
//拷贝音频文件
//字节流一次读写一个字节复制音频,效率太慢
FileInputStream fis = new FileInputStream("Todd Li - 小宇.mp3");
FileOutputStream fos = new FileOutputStream("copy1.mp3");
int b;
while ((b = fis.read()) != -1) {
fos.write(b);
}
fis.close();
fos.close();
}
public static void demo1() throws FileNotFoundException, IOException {
//拷贝图片
FileInputStream fis = new FileInputStream("8482331.jpg");
FileOutputStream fos = new FileOutputStream("copy.jpg");
int b;
while ((b = fis.read()) != -1) {
fos.write(b);
}
fis.close();
fos.close();
}
}
二.字节数组拷贝之available()方法
- available()方法:获取文件字节数
- 开辟字节数组,一次存储,一次写出
优点:
- 拷贝速度快,但是有可能会导致内存溢出(开发中不推荐)
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
public class Demo3_Copy {
public static void main(String[] args) throws IOException {
//第二种拷贝(开发中不推荐,因为有可能会导致内存溢出)
FileInputStream fis = new FileInputStream("Todd Li - 小宇.mp3");
FileOutputStream fos = new FileOutputStream("copy1.mp3");
//int len = fis.available();
//System.out.println(len);//文件字节数
byte[] arr = new byte[fis.available()]; //创建与文件一样大小的字节数组
fis.read(arr); //将文件上的字节读取到字节数组
fos.write(arr); //将字节数组中的字节数据写到文件上
fis.close();
fos.close();
}
}
三.定义小数组
- read(byte[] arr):返回数组读到的有效字节个数
- write(byte[] arr,int off,int len):off数组中的索引,len写入的长度
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
public class Demo4_ArrayCopy {
public static void main(String[] args) throws IOException {
FileInputStream fis = new FileInputStream("xxx.txt");
FileOutputStream fos = new FileOutputStream("yyy.txt");
byte[] arr = new byte[1024 * 8];//一般为1024的整数倍
int len;
while ((len = fis.read(arr)) != -1) {//len为读到的有效字节个数,len为-1时读取文件结束
//如果忘记加arr,返回的就不是读取的字节个数,而是字符的码表值
fos.write(arr,0,len); //只写读到的有效字节
}
fis.close();
fos.close();
}
}
四.BufferedInputStream和BufferedOutputStream拷贝
1.拷贝思想
- 字节流一次读写一个数组的速度明显比一次读写一个字节的速度快很多
- 这是加入了数组这样的缓冲区效果,java本身在设计的时候也考虑到了这样的设计思想
(装饰设计模式后面讲),所以提供了字节缓冲流
2.BufferedInputStream
- BufferedInputStream内置了一个缓冲区(数组)
- 从BufferedInputStream中读取一个字节时,BufferedInputStream会一次性
从文件中读取8192个,存在缓冲区中,返回给程序的时候是一个,程序再次读取时,
就不用找文件了,直接从缓冲区中获取,直到缓冲区中所有的都被使用过,才重新从文件中读取8192个
3.BufferedOutputStream
- BufferedOutputStream也内置了一个缓冲区(数组)
- 程序向流中写出字节时,不会直接写到文件,会先写到缓冲区中,直到缓冲区写满,
BufferedOutputStream才会把缓冲区的数据一次性写到文件中
4.案例演示:拷贝音频文件
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
public class Demo5_BufferCopy {
public static void main(String[] args) throws IOException {
FileInputStream fis = new FileInputStream("Todd Li - 小宇.mp3"); //创建文件输入流对象,关联Todd Li - 小宇.mp3
FileOutputStream fos = new FileOutputStream("copy1.mp3"); //创建输出流对象,关联copy1.mp3
BufferedInputStream bis = new BufferedInputStream(fis); //创建缓冲区对fis装饰
BufferedOutputStream bos = new BufferedOutputStream(fos); //创建缓冲区对fos装饰
int b;
while ((b = bis.read()) != -1) {
bos.write(b);
}
bis.close(); //只关闭装饰后的对象
bos.close();
}
}
原理图
5.小数组的读写和带Buffered的读写那个更快
- 定义小数组如果是8192个字节大小和buffered比较的话,定义小数组会略胜一筹,因为读和写操作的是同一个数组,而Buffered操作的是两个数组
五.flush和close方法的区别
1.close
- 具备刷新的功能,在关闭流之前,就会先刷新一次,将缓冲区的字节全部刷新到文件中,然后关闭流,close刷新完之后就不能写了
2.flush
- 具备刷新的功能,刷新完之后还可以继续写
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
public class Demo5_BufferCopy {
public static void main(String[] args) throws IOException {
BufferedInputStream bis = new BufferedInputStream(new FileInputStream("Todd Li - 小宇.mp3"));
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("copy1.mp3"));
int b;
while ((b = bis.read()) != -1) {
bos.write(b);
//bos.close();报错
//bos.flush();每次读到缓冲区都刷新,但没必要,因为每次只有读取8192个字节才会才会向文件中写
}
bos.flush();
//bis.close();
//bos.close();
}
}
六.字节流读写中文的问题
1.字节流读取中文的问题
- 字节流在读取中文的时候有可能会读到半个中文,造成乱码
2.字节流写中文的问题
- 字节流直接操作的字节,所以写出中文必须将字符转成字节数组
- 写出回车换行write("\r\n".getBytes());
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
public class Demo6_Chinese {、
public static void main(String[] args) throws IOException {
//demo1();
//字节流写中文的问题
FileOutputStream fos = new FileOutputStream("zzz.txt");
fos.write("我读书少,你不要骗我".getBytes());
fos.write("\r\n".getBytes());
fos.close();
}
public static void demo1() throws FileNotFoundException, IOException {
//字节流读取中文的问题
FileInputStream fis = new FileInputStream("yyy.txt");
byte[] arr = new byte[4];//一个汉字两个字节,一次存两个字
int len;
while ((len = fis.read(arr)) != -1) {
System.out.println(new String(arr,0,len));
}
fis.close();
}
}
流的标准处理异常代码
一.1.6版本及其以前
- try finally的嵌套,目的是能关一个尽量关一个
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
public class Demo7_TryFinally {
public static void main(String[] args) throws IOException {
//流的标准处理异常代码1.6版本及其以前
FileInputStream fis = null;
FileOutputStream fos = null;
try {
fis = new FileInputStream("xxx.txt");
fos = new FileOutputStream("yyy.txt");
int b;
while ((b = fis.read()) != -1) {
fos.write(b);
}
} finally {
try {
if (fis != null)
fis.close();
} finally { //try finally的嵌套,目的是能关一个尽量关一个
if (fos != null)
fos.close();
}
}
}
}
二.1.7版本
1.原理
- 在try()中创建的流对象必须实现AutoCloseable接口,如果实现了,在try后面的{}(读写代码)执行后就会自动调用流对象的close方法将流关闭
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
public class Demo7_TryFinally {
public static void main(String[] args) throws IOException {
//demo1();
try (//不需要关闭流,因为当文件对象在try()中时,执行完读写文件的代码系统会自动调用关闭流的方法,因为FileInputStream,FileOutputStream
//的父类实现了AutoCloseable接口
FileInputStream fis = new FileInputStream("xxx.txt");
FileOutputStream fos = new FileOutputStream("yyy.txt");
Myclose mc = new Myclose();//只有实现了AutoCloseable接口的类可以在这里创建对象
) {
int b;
while ((b = fis.read()) != -1) {
fos.write(b);
}
}
}
class Myclose implements AutoCloseable {//实现了AutoCloseable接口的类
public void close() {
System.out.println("我关了");
}
}