java-io流
Java-I/O流
读取和数据的一种解决方案。
1.1什么是IO流
I指的是in,O是out,也就是输入输出,指的是程序和外部之间的数据传递,通过流的方式。
那什么是流呢?
这是一种抽象的概念,一系列字节以队列的方式,先进先出,我们知道,程序运行在内存当中,而想要持久化记忆数据,就需要我们在内存和存储设备之间搭建一个数据传输的通道。
1.2流的分类
根据不同的分类方式,我们可以将流分为不同的方式:
- 方向:输入流和输出流 (这里的输入输出的对象相对于内存来收,也就是运行的程序。)
- 单位:字节流和字符流 (字节流可以处理所有类型的文件,而字符流只能处理纯文本文件(txt,md))
- 功能:节点流和处理流
纯文本文件,用windows自带的记事本打开并且能读懂的文件。
1.3字节流
字节流由两个抽象类InputStream和OutputStream继承于Objections类
1.3.1FileInputStream
接下介绍Fileinputstream常用的方法:
方法 | |
---|---|
read() | 返回输入流中下一字节的数据(0~255),如果是-1.则表示文件结束。 |
read(byte b[ ]) | 从输入流中读取一些字符储存到数组b中,最多读取b.length字节数,没有则返回-1 |
close() | 关闭输入流释放相关系统资源 |
readAllBytes() | 读取输入流中的所有字节,返回字节数组 |
available() | 返回输入流中利用读取的字节数 |
public long skip(long n) | 跳过输入流中的指定字节数,不读取这些字节 |
案例:通过Fileinputstream打印数据
public static void main(String[] args) {try (InputStream fis = new FileInputStream("input.txt")) {long skip = fis.skip(2);//跳过两个字节,skip=2(txt实际大小大于2,否则返回0或1.)int content;while ((content = fis.read()) != -1) {System.out.print((char) content);}}catch (IOException e) {e.printStackTrace();}}
注意
创建对象时:
- 文件不存在时就直接报错。
写入数据时:
- 一次读一个字节,读出来的数据在ASCII对应的数字,注意编码和解码方式。
- 读到文件末尾,read方法返回-1。
释放资源:
每次使用完流之后都要释放资源。
1.3.2FileOutputStream
介绍FileOutputStream常用的方法:
方法名 | 描述 |
---|---|
void write(int b) | 将指定的字节写入此文件输出流中。 |
void write(byte[] b) | 将指定的字节数组全部写入此文件输出流中。 |
void write(byte[] b, int off, int len) | 从指定的字节数组写入一部分数据(从偏移量off 开始,长度为len )到此文件输出流中。 |
void flush() | 刷新此输出流并强制任何缓冲的输出字节被写出。通常不需要调用,除非使用了带有缓冲的装饰器如BufferedOutputStream 。 |
void close() | 关闭此文件输出流并释放与此流有关的所有系统资源。 |
final FileChannel getChannel() | 返回与此文件输出流关联的唯一 FileChannel 对象。 |
FileDescriptor getFD() | 返回与该流相关的文件描述符对象。 |
可以把程序中的数据写到本地文件中,是字节流的基本流,书写分为三部,创建对象,写入数据,释放资源
当我们创建FileOutputStream对象时,具体流程如下:
FileOutputStream fos = new FileOutputStream("C:\\output.txt"); //创建对象,里面可以是路径也可以file对象
fos.write(97);//写入一个字节
fos.close();//关闭输出流
注意
创建对象时:
- 参数可以是字符串表示的路径或者是file对象。
- 如果文件不存在会创建一个新的文件,要保证父级路径存在。
- 如果文件存在,则会清空文件。可以在创建是添加append属性。
写入数据时:
-
write方法的参数是整数,写入本地文件是对应的ASCII对应的字符。
-
'值’直接传入数据也可。
释放资源:
每次使用完流之后都要释放资源。
拓展
换行
输入换行的时候,不同操作系统对应不同换行符
windows:\r\n
Linux"\n
MAc:\r
在window操作系统中,Java对换行符做了优化,\r或\n都可,做了补全处理,但是建议补全。
案例:换行符的使用
package io;import java.io.FileOutputStream;
import java.io.IOException;public class demo1 {public static void main(String[] args) throws IOException {try (FileOutputStream fos = new FileOutputStream("a.txt")) {String str = "hello world1";byte[] bytes = str.getBytes();fos.write(bytes);String wrap="\r\n";fos.write(wrap.getBytes());String str2 = "hello world2";byte[] bytes2 = str2.getBytes();fos.write(bytes2); }//自动会执行fos.close();}
}
1.3.3练习-拷贝文件
要求:把某个文件拷贝到当前目录下(文件不要太大)
FileInputStream fis=new FileInputStream("a.txt");FileOutputStream fos=new FileOutputStream("b.txt");int i;while((i=fis.read())!=-1){fos.write(i);}fis.close();fos.close();
当我们通过拷贝的时候,发现一次循环只能读取一个字节那么如果是拷贝视频,那么是不是要循环次数将会很多,有没有解决方法呢?换句话说,在一次的拷贝过程中,能不能拷贝多个字节。
解决方案,通过read,并传入字节数组,我们人为开辟一个空间,将字符存入,这样子增加了效率,减少了时间。
FileInputStream fis=new FileInputStream("a.txt");FileOutputStream fos=new FileOutputStream("b.txt");byte[] b=new byte[1024];//每次读多少个字节数据,和开辟的数组大小有关int len; //read返回值是返回本次读到多少个字节数据while((len=fis.read(b))!=-1){fos.write(b,0,len);//将读到的数据覆盖到原数组}fis.close();fos.close();
1.3.4try…catch 异常处理机制(了解即可)
到我们使用字节流是,他会让我们抛出异常,我们可以throws IOException 或者使用try…catch包裹。
try{FileOutputStream fos=new FileOutputStream("b.txt");fos.write(97)
}
catch(IOException){e.printStackTrace();
}finally{fos.close(); //不管有无异常,都执行
}
在版本更新呢过程中,关于资源的释放存在三种机制
如果我们用jdk9的方式来写拷贝案例的话,那么代码如下,在未来我们会直接抛出异常。
FileInputStream fis=null;FileOutputStream fos=null;try {fis=new FileInputStream("a.txt");fos=new FileOutputStream("b.txt");int len;byte[] b=new byte[1024];while ((len= fis.read(b))!=-1){fos.write(b,0,len);}}catch (IOException e){e.printStackTrace();}finally {if (fos!=null){try {fos.close();}catch (IOException e){e.printStackTrace();}if (fis!=null){try {fos.close();}catch (IOException e){e.printStackTrace();}} }}
1.4字符流
1.4.1编码
在了解字符流之前,我们需要对编码和乱码等有初步的了解。
在计算机中,,任意数据都是以二进制的方式来存储的,存储单位是一个字节,在ASCII中,一个英文占一个字符,而windows(简体中文),默认使用GBK,以存储英文为例,a字符查询ASCII表对应的是110 0001,用先导0补齐,0110 0001。
字符流原理解析
- 创建字符输入流对象
底层:关联文件,创建缓冲区(长度8192的字节数组bb,通过断点测试也可以得出)。
- 读取数据
底层:1. 缓冲区中是否有数据可以读取
缓冲区没有数据:就从文件中读取数据,装到缓冲区中,每次尽可能的将缓冲区填满,如果文件中没有数据,则返回-1。
缓冲区存在是数据:就从缓冲区中读取数据。
空惨的read方法:一次读取一个字符,遇到中文一次读多个字节(根据不同的编码),把字节解码并转成十进制返回。
有参的read方法:把读取字节,解码和强转三步和并了,强转之后的字符放在数组中。避免返回的是十进制,更加易懂。
1.5字节缓冲流
1.5.1基本使用
学完四个基本流,接下来学习高级流,在基本流的基础进行封装,基本流的效率普遍的较低,那么前面我们采用缓冲区的方法,Java当中也为我们封装好了。当然除了缓冲流,还有转换流,序列化流,打印流等。
底层自带了长度为8192的缓冲区提高性能,但是本质上还是对基本流进行包装。
方法名称 | 说明 |
---|---|
public BufferedInputStream(InputStream is) | 把基本流进行封装,提高读取性能 |
public BufferedOutputStream(OutputStream io) | 把基本流进行封装,提高写性能 |
案例:拷贝(也可以通过创建字节数字一次操作多个字节)
package io;import java.io.*;public class demo1 {public static void main(String[] args) throws IOException {BufferedInputStream bis = new BufferedInputStream(new FileInputStream("a.txt"));BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("b.txt"));int c;while ((c = bis.read()) != -1) {bos.write(c);}bis.close();//释放代码,只需要关闭高级流,在底层会给我们关闭基本流bos.close();}
}
1.5.2原理
创建缓冲流的时候,会在内存中开辟两个区域,存放大小为8192的字节数组,创建一个变量b,将两个缓冲区的数据循环传递,那一次还是一个字节的搬运,为什么提高了效率,他其实是减少与存储设备打交道的时间,内存的运行效率其实快得多。
1.6字符缓冲流
案例:利用字符缓冲流写入数据
BufferedWriter bw = new BufferedWriter(new FileWriter("b.txt"));bw.write("<UNK>");bw.newLine();bw.write("<UNK>");bw.newLine();bw.close();
1.7转换流
减少与存储设备打交道的时间,内存的运行效率其实快得多。
[外链图片转存中…(img-WRbUKpJE-1752158721772)]
1.6字符缓冲流
案例:利用字符缓冲流写入数据
BufferedWriter bw = new BufferedWriter(new FileWriter("b.txt"));bw.write("<UNK>");bw.newLine();bw.write("<UNK>");bw.newLine();bw.close();
[外链图片转存中…(img-btyyCVHQ-1752158721773)]