文章目录
IO流概述
- 什么是IO流
I:input
O:output
通过IO就可以完成硬盘文件的读和写
硬盘中的文件加载到内存就是输入(Input)、
内存将使用或者修改过的文件再返回给硬盘就是输出(Output) - IO流分类?
有两种划分方式,一种是按照流的方向进行划分:
输入流:以内存为参照物,往内存中去,叫输入(Input
),或者叫做读(Read
)
输出流:以内存为参照物,从内存中出,叫输出(Output
),或者叫做写(Write
)
另一种方式是按照字符读取数据方式的不同进行分类:
字节流:按照字节的方式读取数据,一次读取1个字节byte
就是一次读取8个二进制,这种流非常万能,能读取任何类型的文件.包括:文本文件,图片,声音文件等…
字符流:按照字符方式进行读取数据的,一次读取一个字符,这种流是为了方便读取普通文本文件而存在的,这种流不能读取:图片、声音、视频等文件。只能读取纯文本文件,连word文件都无法读取.
Java是站在了前人的肩膀上向前眺望.更多的是应用层面的,对底层的东西了解的不多.可能C++多一些
Java中提供了哪些流?每个流的特点是什么?每个流对象上的常用方法有哪些??
- java IO流的四大家族:
四个家族首领:
java.io.InputStream 字节输入流
java.io.OutputStream 字节输出流
java.io.Reader 字符输入流
java.io.Writer 字符输出流
以Stream结尾都是字节流,以Reader/Wrider结尾都是字符流.
这四个首领全是抽象类(abstract class) - 所有流都实现了java.io.Closeable接口,都是可关闭的.
都有close方法
流就像是内存和硬盘之间的管道,用完之后一定要关闭,不然会耗费(占用)很多的资源.用完流之后一定要关闭 - 所有的输出流都实现了java.io.Flushable接口,都是可刷新的,都有flush()方法.
输出流在最终输出之后,必须刷新一下。
这个刷新就是将通道/管道当中剩余未输出的数据强行输出完(清空管道)
如果没有flush()可能会导致丢失数据。 - java.io包下需要掌握的16个流总结(部分了解即可):
文件专属:
java.io.FileInputStream(重点
)
java.io.FileOutputStream(重点
)
java.io.FileReader
java.io.FileWriter
转换流:(将字节流转换成字符流)
java.io.InputStreamReader
java.io.OutputStreamWriter
缓冲流专属:
java.io.BufferedReader
java.io.BufferedWriter
java.io.BufferedInputStream
java.io.BufferedOutputStream
数据流专属:
java.io.DataInputStream
java.io.DataOutputStream
标准输出流:
java.io.PrintWriter
java.io.PrintStream (重点
)
对象专属流:
java.io.ObjectInputStream (重点
)
java.io.ObjectOutputStream (重点
)
利用FileInputStream读取文件
- FileInputStream是文件字节输入流,万能,任何类型的文件都可以采用这个流来读
- 字节的方式,完成输入操作,完成读取操作(硬盘–>文件)
逐个字节读取
public static void main(String[] args)throws Exception{
FileInputStream file = new FileInputStream("C:\\Test\\temp");
int readData=file.read();
System.out.println(readData);//第一次读取到第一个字节
readData=file.read();
System.out.println(readData);//第二次读取到第二个字节
readData=file.read();
System.out.println(readData);//没有内容会读取到-1
}
直接将异常在main方法中抛出非常不规范,一般是try catch,如下面
public static void main(String[] args){
//如果在main方法中有编译异常,一般会try catch 不会throws抛出
//创建文件字节输入流对象
FileInputStream file= null;//文件路径在写入时,IDEA会自动把\变成\\
try {
file = new FileInputStream("C:\\Test\\temp");//我们在temp文件中提前写入内容:两个字母ab
//开始读取数据
int readData=file.read();//这个方法的返回值是:读取到的“字节”本身
System.out.println(readData);
readData=file.read();
System.out.println(readData);
//当读取到文件的末尾时,没有数据了会返回-1 继续读 还是返回-1
readData=file.read();
System.out.println(readData);
} catch (FileNotFoundException e) {
e.printStackTrace();
}catch (IOException e){
e.printStackTrace();
}finally{
//在finally语句块中确保流一定关闭
if (file != null) {
//关闭流的前提是,流不是空,流是null没必要关闭
try {
file.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
用循环逐个读
public static void main(String[] args) throws Exception {
FileInputStream file=new FileInputStream("C:\\Test\\temp");
int readData=0;
while((readData=file.read())!=-1){
System.out.println(readData);
}
file.close();
}
一次读取N个字节
public static void main(String[] args)throws Exception{
//IDEA的默认当前路径是Project这个工程的根目录
//从根目录开始,如果文件放在IDEA中,每一层都是说在哪
//比如我在untitled这个工程的src包的main包下的resources中放了一个文件,
// 那么这个文件的相对路径就是..\\untitled\src\main\resources\xxx文件
FileInputStream file=new FileInputStream("..\\untitled\\src\\main\\resources\\tempd");
byte[] bytes=new byte[4];
//用字节数组读,一次读取n个字节,n是数组长度
int readCount=file.read(bytes);//方法返回值是:读取到的字节数量(不是字节本身)
//如果文件只有5个字节,而数组长度是6 那么读取的时候只能读取到5个,所以会返回5个
//如果文件有6个字节,分别a,b,c,d,e,f 数组长度是4 第一读会读取到4个字节,是a,b,c,d ,返回4 第二次读会读取到两个字节,并且会覆盖已经读取的字节,数组中变成
// e,f,c,d 返回2 再次读取 文件中没有字节了,会返回-1
System.out.println(readCount);
System.out.println(new String(bytes,0,readCount));
readCount=file.read(bytes);
System.out.println(readCount);
System.out.println(new String(bytes,0,readCount));
file.close();
}
FileInputStream主流用法
public static void main(String[] args) throws Exception{
FileInputStream file=new FileInputStream("..\\untitled\\src\\main\\resources\\tempd");
byte[] bytes=new byte[4];
int readCount=0;
while((readCount=file.read(bytes))!=-1){
//把数组转换成字符串,读多少个,转换多少个。
System.out.println(new String(bytes,0,readCount));
}
file.close();
}
FileInputStream类中其他常用方法
- int available():返回流当中剩余的没有读取到的字节数量
public static void main(String[] args) throws Exception{
FileInputStream file=new FileInputStream("..\\untitled\\src\\main\\resources\\tempd");
int readData=file.read();
System.out.println("还剩多少个字节没有读:"+file.available());
file.close();
}
还剩多少个字节没有读:4
这个方法一般用来查看文件总字节数量,然后将这个数量设置为数组长度,便于一次读取文件。但是只适合比较小的文件,因为数组长度不能太大
public static void main(String[] args) throws Exception{
FileInputStream file=new FileInputStream("..\\untitled\\src\\main\\resources\\tempd");
System.out.println("总字节数量:"+file.available());
byte[] bytes=new byte[file.available()];
int readCount=file.read(bytes);
System.out.println(new String(bytes));
file.close();
}
总字节数量:5
abcde
- long skip(long n):跳过N个字节不读
public static void main(String[] args) throws Exception {
FileInputStream file=new FileInputStream("..\\untitled\\src\\main\\resources\\tempd");
file.skip(2);
System.out.println(file.read());
}
文件内容是abcde,跳过两个是c 而c的ASCII码值是99 所以返回99
返回:99
利用FileOutputStream写入文件
- FileOutputStream是文件字节输出流,负责写文件
- 从内存到硬盘,如果文件不存在会自动建立
- 每次写完需要刷新一下,用flush方法
两种创建方法
//如果myfile不存在,会自动建立。如果myfile已经存在,会清空这个文件,然后重新写入
FileOutputStream file=new FileOutputStream("myfile");
//不清空源文件,以追加的方式写入,末尾加个true
FileOutputStream fos=new FileOutputStream("myfile",true);
用write方法写入
public static void main01(String[] args) throws Exception{
FileOutputStream file=new FileOutputStream("myfile");//写文件具体在哪个位置,不写默认项目根目录
//开始写
byte[] bytes={97,98,99,100};
//将整个数组全部写出
file.write(bytes);
//向文件中写入数组的的一部分
file.write(bytes,0,2);
file.flush();
}
利用流完成文件复制
- 一个盘中的文件要复制到另一个盘,需要先经过内存
- 先将文件读取到内存,然后从内存输入到另一个盘中
- 一边读、一边写
使用FileInputStream+FileOutputStream完成文件的拷贝,这个对文件的拷贝比较万能,类型没有限制,什么文件都可以
public static void main(String[] args) throws Exception {
FileInputStream file=new FileInputStream("C:\\Test\\temp");
FileOutputStream fos=new FileOutputStream("C:\\Test\\afile");
byte[] bytes=new byte[1024*1024];//每次最多复制1MB
int readCount=0;
while((readCount=file.read(bytes))!=-1){
fos.write(bytes,0,readCount);
}
fos.flush();
file.close();
fos.close();
}
FileReader、FileWriter的使用
- FileReader、FileWriter也是读取文件的输入输入流,只不过按照字符来进行读取.
- 使用方式和字节流大同小异,字节流读取n个字节时用byte数组,而字符流读取n个字符时用char数组
//以字符方式进行读取,读取文本内容比较方便
public static void main(String[] args) throws Exception{
FileReader reader=new FileReader("C:\\Test\\temp");
//开始读
char[] chars=new char[4];
int readCount=0;
while((readCount=reader.read(chars))!=-1){
System.out.println(new String(chars,0,readCount));
}
reader.close();
}
- FileWriter文件输出流,用来写文件
- 只能写普通文本,图片、声音、视频等等都不可以
- 能用记事本进行编辑的都是普通文本文件,和后缀无关,不一定非得是.txt
//以字符方式进行读取,读取文本内容比较方便
public static void main(String[] args)throws Exception{
FileWriter file=new FileWriter("C:\\Test\\temp",true);
char[] chars={'a','b','k','9'};
file.write(chars);//写入这个数组的全部
file.write(chars,0,3);//写入数组的部分
file.write("直接写入字符串");
file.close();
}
自带缓冲区的字符流使用
- 自带缓冲意思是读写文件的时候不需要再用char或者byte数组
BufferedReader
- readLine()方法读取一行文本,但是不带换行符,读取不到内容时会返回null
- 构造时,需要传入一个字符流
public static void main(String[] args) throws Exception{
FileReader reader=new FileReader("C:\\Test\\temp");
/*当一个流里面有另一个流的时候,里面的流叫节点流,外面的流叫包装流/处理流
当前这个程序里,FileReader就是一个节点流。BufferedReader就是包装流
*/
BufferedReader br=new BufferedReader(reader);
/*String line=br.readLine();
System.out.println(line);
line=br.readLine();
System.out.println(line);*/ //一次读一行太慢,可以用循环
String s=null;
while((s=br.readLine())!=null){
System.out.println(s);
}
//关闭流
//对于多个流包装在一起,只需要关闭最外面的流就可以,里面的节点流会自动关闭
br.close();
}
- 缓冲流构造时,只能传入字符流.如果想要传入字节流必须用转换流进行转换
public static void main(String[] args)throws Exception {
//用字节流读取文件
FileInputStream in=new FileInputStream("C:\\Test\\temp");
//用转换流将字节流转换成字符流
InputStreamReader reader=new InputStreamReader(in);
//缓冲流的构造方法要求只能传入字符流,不能传入字节流
//reader是节点流,br包装流
BufferedReader br=new BufferedReader(reader);
//合并写法
BufferedReader bs=new BufferedReader(new InputStreamReader(new FileInputStream("C:\\Test\\temp")));
String s=null;
while((s=br.readLine())!=null){
System.out.println(s);
}
br.close();
bs.close();
}
BufferedWriter
- 构造时同样需要传入字符流
- 写文件完成后需要清理管道,调用flush方法
- 写入文件时,会默认清空文件,如果不想清空,构造时设置true
public static void main(String[] args) throws Exception{
//带有缓冲区的字符输出流
BufferedWriter out=new BufferedWriter(new FileWriter("C:\\Test\\temp",true));
//开始写
out.write("String String ");
out.write("\n");
out.write("helllo world");
out.write("写文件完成后需要调用刷新方法");
//刷新
out.flush();
//关闭最外层
out.close();
}
数据专属流了解
DataOutputStream
- 可以将数据和数据类型一并写入文件
- 这个文件不是普通文本文件,记事本打不开
- 可以看成是一种加密规则,写入的文件只能用DataInputStream才能打开,还需要知道数据写入规则
public static void main(String[] args) throws Exception{
//数据专属的字节输出流 构造时需要传入字节流
DataOutputStream dos=new DataOutputStream(new FileOutputStream("data"));
//写数据
byte b=100;
short s=200;
int i=300;
long l=400L;
float f=3.0F;
double d=3.14;
boolean flg=false;
char c='a';
//数据写入
dos.writeByte(b);
dos.writeShort(s);
dos.writeInt(i);
dos.writeLong(l);
dos.writeFloat(f);
dos.writeDouble(d);
dos.writeBoolean(flg);
dos.writeChar(c);
//刷新
dos.flush();
//关闭资源
dos.close();
}
DataInputStream
public static void main(String[] args) throws Exception{
DataInputStream dis=new DataInputStream(new FileInputStream("data"));
//开始读
byte b=dis.readByte();
short s=dis.readShort();
int i=dis.readInt();
long l=dis.readLong();
float f=dis.readFloat();
double d=dis.readDouble();
boolean flg=dis.readBoolean();
char c=dis.readChar();
//随意输出
System.out.println(b);
System.out.println(s);
System.out.println(i+1000);
System.out.println(l);
System.out.println(f);
System.out.println(d);
System.out.println(flg);
System.out.println(c);
dis.close();
}
标准输入流PrintStream使用
- 默认输出到控制台
- 标准输出流不需要手动关闭
- 可以改变输出方向,比如不再输出到控制台,而是输出到一个日志文件中
public static void main(String[] args) throws Exception{
//联合起来写
System.out.println("hello");
//分开写
PrintStream ps=System.out;
ps.println("345");
ps.println("234");
ps.println("789");
//对于标准输出流,不需要手动close关闭
PrintStream printStream=new PrintStream(new FileOutputStream("log"));
System.setOut(printStream);
//再输出
System.out.println("hello world");
}