一、 概念及分类
- 概念
IO流用来处理设备之间的数据传输- Java对数据的操作是通过流的方式
- Java用于操作流的类都在IO包中
- 流按流向分为两种:输入流,输出流。
- 流按操作类型分为两种:
- 字节流 : 字节流可以操作任何数据,因为在计算机中任何数据都是以字节的形式存储的
- 字符流 : 字符流只能操作纯字符数据,比较方便。
以下篇章都是介绍IO流的字节流和字符流
- IO流常用父类
- 字节流的抽象父类:
InputStream
OutputStream - 字符流的抽象父类:
Reader
Writer
- IO程序书写
- 使用前,导入IO包中的类
- 使用时,进行IO异常处理
- 使用后,释放资源
以下一定要理解好,我们是相对于内存而言进行输入和输出,并不是站在使用者的角度,也就是说FileInputStream进行输入。是内存要从我们写的文档中,获取元素。输入到自己的缓冲区中,也就是需要进行读(read) 操作。而我们从java缓冲区中想要输出一些东西,也就是进行OutputStream,从缓冲区中将我们的想要的东西上写(Write)出来。
以下篇章介绍字节流
二、 FileInputStream(进行输入)
因为InputStream为一个抽象类,故不能自己生成对象。所以学习它的第一个子类FileInputStream。
FileInputSteam下的read()方法,首先我们要在本工程下,新建一个txt文件,并且取名XXX.txt。在里面书写abc三个字母
案例:
FileInputStream fileInputStream =new FileInputStream("xxx.txt");
int x =fileInputStream.read(); //创建流对象
System.out.println(x); //从硬盘上读取一个字节
int y =fileInputStream.read();
System.out.println(y);
int z =fileInputStream.read();
System.out.println(z);
int b =fileInputStream.read();
System.out.println(b);
fileInputStream.close(); //关闭释放资源
效果如下:
我们发现,我们每执行一个read方法,只输出一个字母所对应的码表对应的数字。但是当我们读完最后一个数字发现 返回值为 -1。所以我们可以进行优化
FileInputStream fileInputStream =new FileInputStream("xxx.txt");
int b;
while ((b=fileInputStream.read())!=-1) {
System.out.println(b);
}
fileInputStream.close();
三、 read()方法返回值为什么是int
read()方法读取的是一个字节,为什么返回是int,而不是byte
因为字节输入流可以操作任意类型的文件,比如图片音频等,这些文件底层都是以二进制形式的存储的,如果每次读取都返回byte,因为byte占一个字节,一个字节有8个二进制数组成。有可能在读到中间的时候遇到111111111(-1的补码)
那么这11111111是byte类型的-1,我们的程序是遇到-1就会停止不读了,后面的数据就读不到了,所以在读取的时候用int类型接收,如果11111111会在其前面补上24个0凑足4个字节,那么byte类型的-1就变成int类型的255(00000000 00000000 00000000 11111111)这样可以保证整个数据读完,而结束标记的-1(等于-1即可不用转)就是int类型。
四、 FileOutputStream(进行输出)
FileOutputStream fileOutputStream =new FileOutputStream("yyy.txt");
fileOutputStream.write(97);
//虽然写出的是一个int数。但是到文件上的是一个字节 会自动去除前三个8位
fileOutputStream.write(98);
fileOutputStream.write(99);
fileOutputStream.close();
此时我们会看到项目路径下新建了一个yyy.txt。并且写了一些内容
效果如下:
五、 FileOutputStream追加
如果我们注释前面写的内容,会发现后面书写的内容进行修改。
如果想要输出,abcd。表示我们需要进行追加。这样文件的内容就不会进行修改。
FileOutputStream fileOutputStream =new FileOutputStream("yyy.txt",true);
fileOutputStream.write(97);
//虽然写出的是一个int数。但是到文件上的是一个字节 会自动去除前三个8位
fileOutputStream.write(98);
fileOutputStream.write(99);
fileOutputStream.write(100);
fileOutputStream.close();
效果如下:
该功能应用场景,我们可以在查找聊天记录的时候,聊天记录的内容就是进行追加。
六、 拷贝图片(第一种拷贝方式)
首先我们需要一张图片并且把图片放到该项目下。改行代码是IO流的主流代码。如果我们拷贝mp3,只需要改变括号里面的值即可。但是该效率过慢,我们需要一个字节一个字节的去读。
FileInputStream fileInputStream =new FileInputStream("6.jpg");//创建输入流对象
FileOutputStream fileOutputStream =new FileOutputStream("copy.jpg");//创建输出流对象
int b;
while((b=fileInputStream.read())!=-1) {
//在不断地读取每一个字节
fileOutputStream.write(b);
//将每一个字节写出
}
fileOutputStream.close();
fileInputStream.close();
此时,我们的图片会被拷贝过去。但是该效率过慢,我们需要一个字节一个字节的去读。
所以我们引入整个文件读的方法。也就是available()方法
七、 字节数组拷贝之available()方法(第二种拷贝方式)
FileInputStream fileInputStream =new FileInputStream("6.jpg");
FileOutputStream fileOutputStream =new FileOutputStream("copy.jpg");
int len =fileInputStream.available();
System.out.println(len);
fileInputStream.close();
fileOutputStream.close();
效果如下:
该数字就是文件所占用的字节值,bytes。
所以,我们可以进行升级。
FileInputStream fileInputStream =new FileInputStream("6.jpg");
FileOutputStream fileOutputStream =new FileOutputStream("copy.jpg");
byte[] arr=new byte[fileInputStream.available()];
//创建与文件一样大小的字节数组
fileInputStream.read(arr);
//将文件上的字节读取到内存中
fileOutputStream.write(arr);
//将字节数组中的字节数据写到文件上
fileInputStream.close();
fileOutputStream.close();
效果如下:
在开发中,不建议使用该方法数组进行copy文件,当文件过大的时候,可能会导致内存溢出。
八、 定义小组数(第三种拷贝方式)
FileInputStream fileInputStream =new FileInputStream("xxx.txt");
byte[] arr=new byte[2];
int a =fileInputStream.read(arr);
System.out.println(a);
for (byte b : arr) {
System.out.println(b);
}
System.out.println("--------------");
int c=fileInputStream.read(arr);
System.out.println(c);
for (byte b : arr) {
System.out.println(b);
}
fileInputStream.close();
效果如下:
此时我们发现,我们只能读取两个数字。当我们第一次读取的时候,我们会有限读取ab(97 98),随后,再次读取两个数的时候c会占去a的位置,所以会读取出99 98 。
所以我们在读写的时候,会进行如下操作
FileInputStream fileInputStream =new FileInputStream("xxx.txt");
FileOutputStream fileOutputStream =new FileOutputStream("yyy.txt");
byte []arr =new byte[2];
int len;
while((len=fileInputStream.read(arr))!=-1) {
fileOutputStream.write(arr, 0, len);
}
fileInputStream.close();
fileOutputStream.close();
效果如下:
标准写法:因为我们一般读取数据不会两个两个去读,而是会1024的倍数取读,
所以进行升级:
FileInputStream fileInputStream =new FileInputStream("xxx.txt");
FileOutputStream fileOutputStream =new FileOutputStream("yyy.txt");
byte[] arr=new byte[1024*8];
int len;
while((len=fileInputStream.read(arr))!=-1) {
//如果忘记加arr,返回的就不是读取的字节个数,而是字节的码表值
fileOutputStream.write(b, 0, len);
}
效果相同,但是效率会进行翻倍。
九、 BufferedInputStream和BufferOutputStream拷贝(第四种拷贝方式)
缓冲思想
- 字节流一次读写一个数组的速度明显比一次读写一个字节的速度快很多,
- 这是加入了数组这样的缓冲区效果,java本身在设计的时候,
- 也考虑到了这样的设计思想(装饰设计模式后面讲解),所以提供了字节缓冲区流
BufferedInputStream
- BufferedInputStream内置了一个缓冲区(数组)
- 从BufferedInputStream中读取一个字节时
- BufferedInputStream会一次性从文件中读取8192个, 存在缓冲区中, 返回给程序一个
- 程序再次读取时, 就不用找文件了, 直接从缓冲区中获取
- 直到缓冲区中所有的都被使用过, 才重新从文件中读取8192个
BufferedOutputStream
BufferedOutputStream也内置了一个缓冲区(数组)
- 程序向流中写出字节时, 不会直接写到文件, 先写到缓冲区中
- 直到缓冲区写满, BufferedOutputStream才会把缓冲区中的数据一次性写到文件里。
FileInputStream fileInputStream =new FileInputStream("6.jpg");
//创建输入流对象6.jpg
FileOutputStream fileOutputStream =
new FileOutputStream("copy.jpg");
//创建输出流对象copy.jpg
BufferedInputStream bufferedInputStream =
new BufferedInputStream(fileInputStream);
//创建缓冲区对象,对输入流进行包装让其更加强大
BufferedOutputStream bufferedOutputStream =new BufferedOutputStream(fileOutputStream);
//创建缓冲区对象,对输出流进行包装让其更加强大
int b;
while ((b=bufferedInputStream.read())!=-1) {
bufferedOutputStream.write(b);
}
bufferedInputStream.close();
bufferedOutputStream.close();
- 小数组的读写和带Buffered的读取哪个更快?
- 定义小数组如果是8192个字节大小和Buffered比较的话
- 定义小数组会略胜一筹,因为读和写操作的是同一个数组
- 而Buffered操作的是两个数组
十、 flush和close方法的区别
flush()方法
- 用来刷新缓冲区的,刷新后可以再次写出
close()方法
- 用来关闭流释放资源的的,如果是带缓冲区的流对象的close()方法,不但会关闭流,还会再关闭流之前刷新缓冲区,关闭后不能再写出
- 简单来说,它具有刷新功能,在关闭之前,会再刷新一次缓冲区,万一缓冲区中还多有一个多余8192个字节,可能500个字节,会最后一次刷新,并将缓冲区的字节全都刷新再文件上,再进行关闭
十一、 字节流读写中文
字节流读取中文的问题:字节流在读中文的时候有可能会读到半个中文,造成乱码
字节流写出中文的问题:字节流直接操作的字节,所以写出的中文必须将字符串转换位字节输出。写出回车换行。write(“\r\n”.getBytes())
FileInputStream fileInputStream =new FileInputStream("yyy.txt");
byte [] arr=new byte[3];
int len;
while ((len=fileInputStream.read(arr))!=-1) {
System.out.println(new String(arr,0,len));
}
fileInputStream.close();
效果如下:
因为中文中的一个字是两个字节,而我们读取的数组长度是三个字节,会将一个字拆分一半去读取导致识别不成功。
FileOutputStream fileOutputStream =new FileOutputStream("yyy.txt");
fileOutputStream.write("我在学习字节流".getBytes()); //把字节数组写出去
fileOutputStream.close();
效果如下:
因为我们把写的中文给直接存到数组里面,这样并不会出现问题
十二、 IO流的try-catch-finally写法
jdk1.6及以上写法:
public static void demo1() throws FileNotFoundException, IOException {
FileInputStream fileInputStream =null;
FileOutputStream fileOutputStream =null;
int b;
try {
fileInputStream =new FileInputStream("xxx.txt");
fileOutputStream =new FileOutputStream("yyy.txt");
while ((b=fileInputStream.read())!=-1) {
fileOutputStream.write(b);
}
}finally {
try {
if (fileInputStream!=null)
fileInputStream.close();
}finally {
if (fileOutputStream!=null) {
fileOutputStream.close();
}
}
}
}
jdk1.7版本及以上
try(
FileInputStream fileInputStream = new FileInputStream("xxx.txt");
FileOutputStream fileOutputStream =new FileOutputStream("yyy.txt");
){
int b;
while ((b=fileInputStream.read())!=-1) {
fileOutputStream.write(b);
}
}
因为在jdk1.7版本中,try先用小括号(),进行输入输出流的操作,因为他们会默认调用AutoCloseable的接口。自动进行输入输出流的关闭。如果我们想要自行写关闭方法,必须使用该接口