IO流概述
IO流概述(Input Output Stream)
IO流的分类
1.按照流的方向来分
2. 按照处理的数据单元分类
3.按处理对象不同来分类
IO流体系结构
字节流
Reader和Writer
File类的使用
示例1:获取文件或者文件夹的属性
示例2:实现对文件或文件夹的创建或者删除操作
文件字节流InputStream和OutputStream
复制文件(中转站是一个字节)
中转站缺点
复制文件(中转站是一个字节数组)
进行异常处理
JDK7异常处理新特征
JDK9出现的新的异常处理机制
文件字符流FileReader和FileWriter
复制文件(中转站是一个字符)
缓冲字节流BufferedInputStream和BufferedOutputStream
复制文件(使用缓冲字节流提高效率)
缓冲字符流BufferedReader和BufferedWriter
缓冲字符流复制文件
总结1:BufferedReader和BufferedWriter的优点
总结2:readLine()底层原理
总结3:不同操作系统下的换行符是不同的。
数据流 DataInputStream和DataOutputStream
使用数据流读写文件
对象流ObjectInputStream和ObjectOutputStream
往期文章回顾
IO体系
IO流概述(Input Output Stream)
在Java程序中,数据的输入与输出都是通过“流”的形式来处理。Java提供了各种各样的“流"类,用以获取不同种类的数据。程序中通过标准的方法输出或者输入数据。
Java的流类型一般位于java.io包中。
「数据源」:提供原始数据的原始媒介,常见的有IO设备、数据库、文件、其他程序、内存、网络连接等
「流」:是一个动态抽象的概念,是一串连续动态的集合。
数据源就像是水箱,流就像是水管中的水流,程序就是我们最终用户。
IO流的分类
1.按照流的方向来分
「输入流」:数据流向是数据源到程序(InputStream、Reader结尾的流) 「输出流」:数据流向是是程序到目的地(OutputStream、Writer结尾的流)
输入输出流的划分是相对于程序而言,而不是数据源
2. 按照处理的数据单元分类
「字节流」:以字节为单位获取数据,命名上以「Stream」结尾的一般是字节流,顶级类:「InputStream」、「OutputStream」 「字符流」:以字符为单位获取数据,命名上以「Reader」或者「Writer」结尾的一般是字符流。顶级类:「Reader」、「Writer」
3.按处理对象不同来分类
「节点流」:可以直接从数据源或者目的地读写数据,如「FileInputStream」或者「FileReader」 「处理流」:不直接连接到数据源或者目的地,是“处理流的流”。通过对其他流的处理提高程序的性能。如「BufferedInputStream」和「BufferedReader」,处理流也叫做包装流
节点流处于IO操作的第一线,所有操作必须通过他们进行。
处理流可以通过对节点流进行包装,提高程序的性能和灵活性
节点流与处理流的关系
IO流体系结构
字节流
InputStream和OutputStream是Java中最基本的两个字节输入输出类,其他所有的字节输入输出流类都要继承这两个基类。 这两个类都是抽象类,不能够创建实例,只能使用他们的子类。 「FilterInputStream」和「FilterOutputStream」是所有「包装流」的父类。
输入字节流
输出字节流
Reader和Writer
Java中两个最基本的字符输入输出类 其他所有的字符输入输出流类都继承自这两个基类。 这两个类都是抽象类,不能够创建他们的子类,只能使用他们的子类。
Reader体系结构
Writer体系结构
File类的使用
File类用来代表「文件」和「文件夹」
主要作用有两个:
获取文件或者文件夹的属性 实现对文件或者文件夹的创建和删除。
文件夹:file folder,目录:directory
示例1:获取文件或者文件夹的属性
public class TestFileGetField {
public static void main(String[] args) {
/*
* 让File类的对象指向一个文件*/
//利用File类来获取/home/JavaIO示例目录下的txt文件 /data/home/lambda/JavaIo文件示例/ReadMe.txt
//绝对路径,根路径
File file=new File("/data/home/lambda/JavaIo文件示例/ReadMe.txt");
//相对路径,用的比较少
File file1=new File("ReadMe.txt");
//文件名
System.out.println(file.getName());
//长度
System.out.println(file.length());
//是否存在
System.out.println(file.exists());
//获取绝对路径的方法,如果采用相对路径的方式创建file,它获取的是该项目所在路径的文件(与该文件同名)
// 。即/home/lambda/idea软件/ReadMe.txt
System.out.println(file.getAbsolutePath());
System.out.println("=================================");
//查看该文件是否可以读
System.out.println(file.canRead());
//查看该文件是否可以写
System.out.println(file.canWrite());
//查看该文件是否可以执行
System.out.println(file.canExecute());
System.out.println("=================================");
//查看file是文件吗?
System.out.println(file.isFile());
//查看file是否是文件夹?
System.out.println(file.isDirectory());
System.out.println("=================================");
/*
* 让File类的对象指向一个文件夹*/
File file2=new File("/data/home/lambada/JavaIo文件示例/JAVAIOExercises" );
//查看file是文件吗?
System.out.println(file2.isFile());
//查看file是否是文件夹?
System.out.println(file2.isDirectory());
/* //查看该目录下所有的文件
var files = file2.listFiles();
System.out.println(files.length());*/
}
}
示例2:实现对文件或文件夹的创建或者删除操作
public class TestFileCreateAndDelete {
public static void main(String[] args) throws IOException {
/**
* 创建或者删除文件
* */
//创建file对象,指向一个文件
File file=new File("/data/home/lambda/JavaIo文件示例/ReadMe.txt");
//如果文件存在就删除,不存在就创建
if (file.exists()){
file.delete();
}else{
file.createNewFile();
}
/**
* 创建或者删除一个目录*/
//创建一个file对象,指向一个文件夹 /data/home/lambda/JavaIo文件示例
File file1=new File(" /data/home/lambda/JavaIo文件示例/JavaIOexercises");
//如果文件夹存在就删除,不存在就创建
if (file1.exists()){
file1.delete();
}else{
//如果上级文件夹不存在,那就创建,此处是存在的
file1.mkdir();
//新建多级目录
file1.mkdirs();
}
}
}
文件流
文件字节流InputStream和OutputStream
InputStream和OutputStream都是字节流,是「节点流」,数据源和目的地都是文件。 复制文件需要分别创建一个输入流和输出流完成文件的读写。 需要创建一个中转站,借助循环和中转站完成文件复制 流使用完毕之后一定要关闭,这和垃圾回收没有关系。
文件字节流操作示意图
复制文件(中转站是一个字节)
public class TestCopyOneWithFileStream {
public static void main(String[] args) throws IOException {
//创建输入流和输出流 /data/home/lambda/JavaIo文件示例
File file1=new File("/data/home/lambda/JavaIo文件示例/ReadMe.txt");
File file2=new File("/data/home/lambda/JavaIo文件示例/ReadMe2.txt");
//此时表示读取file1内的信息,输入流(相对程序而言)
InputStream fis=new FileInputStream(file1);
OutputStream fos=new FileOutputStream(file2);
//使用输入流和输出流,完成文件复制
/** 定义一个中转站,中转站是一个字节,较小。
int n;
读一个字节。返回值为int
n=fis.read();
写入一个字节
fos.write(n);
此处需要不停地进行读写操作。
*/
//定义一个中转站,中转站是一个字节,较小。
int n;
// 读一个字节。返回值为int
n=fis.read();
//此处进行判断,如果n不为-1,则继续写入并再次读取字节,若n为-1则表示该该文件已被完全读取,不需要在写入了。
while (n!=-1){
fos.write(n);
n=fis.read();
}
//关闭输入和输出流
fis.close();
fos.close();
}
}
中转站缺点
中转站太小,速度慢,效率低,复制更大文件时效果更明显 可以将中转站由一个字节转变为一个字节数组,减少对硬盘的读写次数。
复制文件(中转站是一个字节数组)
public class TestCopyTwoWithFileStream {
public static void main(String[] args) throws IOException {
File file1=new File("/data/home/lambda/JavaIo文件示例/ReadMe.txt");
File file2=new File("/data/home/lambda/JavaIo文件示例/ReadMe2.txt");
//此时表示读取file1内的信息,输入流(相对程序而言)
InputStream fis=new FileInputStream(file1);
OutputStream fos=new FileOutputStream(file2);
// OutputStream fos=new FileOutputStream(file2,true);
// 此项操作在第二次运行之后会将第二次及以后的内容都添加到file2文件之中去。
//创建一个字节数组
byte[] buffer=new byte[1024];
//先读内容到一个字节处。
//读取文件的内容放入到字节数组中,返回读取到的字节数。
int len=fis.read(buffer);
while (len!=-1){
//将字节数组的内容写入文件
// fos.write(buffer);它是读写了1024个字节,并没有指定从哪里读到哪里。
fos.write(buffer,0,len);
//再读内容到一个字节数组
len=fis.read(buffer);
}
//关闭输入和输出流
fis.close();
fos.close();
}
}
进行异常处理
public class TestCopyThreeWithFileStream {
public static void main(String[] args) {
//创建输入流和输出流 /data/home/lambda/JavaIo文件示例
File file1=new File("/data/home/lambda/JavaIo文件示例/ReadMe.txt");
File file2=new File("/data/home/lambda/JavaIo文件示例/ReadMe2.txt");
//此时表示读取file1内的信息,输入流(相对程序而言)
InputStream fis= null;
OutputStream fos=null;
try {
fis = new FileInputStream(file1);
fos=new FileOutputStream(file2);
//使用输入流和输出流,完成文件复制
/** 定义一个中转站,中转站是一个字节,较小。
int n;
读一个字节。返回值为int
n=fis.read();
写入一个字节
fos.write(n);
此处需要不停地进行读写操作。
*/
//定义一个中转站,中转站是一个字节,较小。
int n;
// 读一个字节。返回值为int
n=fis.read();
//此处进行判断,如果n不为-1,则继续写入并再次读取字节,若n为-1则表示该该文件已被完全读取,不需要在写入了。
while (n!=-1){
fos.write(n);
n=fis.read();
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}finally {
//关闭输入和输出流
try {
if (fis!=null){
fis.close();}
} catch (IOException e) {
e.printStackTrace();
}
try {
if (fos!=null){
fos.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
异常处理分析:创建、使用一次流要使用try-catch语句,关闭流需要分开进行异常处理/ Java7异常处理新特征:tyr -with-resources:不用显式进行资源关闭,只需要将资源对象放入tye后的括号中。作用范围是当前try语句,执行完毕(正常执行或者是异常情况)都会对资源进行关闭,可以省略finally语句,更加方便高效。
JDK7异常处理新特征
public class TestCopyFourWithFileStream {
public static void main(String[] args) {
//创建输入流和输出流 /data/home/lambda/JavaIo文件示例
File file1=new File("/data/home/lambda/JavaIo文件示例/ReadMe.txt");
File file2=new File("/data/home/lambda/JavaIo文件示例/ReadMe2.txt");
//此时表示读取file1内的信息,输入流(相对程序而言)
try ( InputStream fis=new FileInputStream(file1);
OutputStream fos=new FileOutputStream(file2);
){
//使用输入流和输出流,完成文件复制
/** 定义一个中转站,中转站是一个字节,较小。
int n;
读一个字节。返回值为int
n=fis.read();
写入一个字节
fos.write(n);
此处需要不停地进行读写操作。
*/
//定义一个中转站,中转站是一个字节,较小。
int n;
// 读一个字节。返回值为int
n=fis.read();
//此处进行判断,如果n不为-1,则继续写入并再次读取字节,若n为-1则表示该该文件已被完全读取,不需要在写入了。
while (n!=-1){
fos.write(n);
n=fis.read();
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}
关于JDK9的异常处理新变化:try之前定义好对象,try()引入创建好的对象。如果多个对象使用;分隔。如果try之前定义的对象会抛出异常就不推荐这种方式来处理异常。
JDK9出现的新的异常处理机制
public class TestCopyFiveWithFileStream {
public static void main(String[] args) throws FileNotFoundException {
//创建输入流和输出流 /data/home/lambda/JavaIo文件示例
File file1=new File("/data/home/lambda/JavaIo文件示例/ReadMe.txt");
File file2=new File("/data/home/lambda/JavaIo文件示例/ReadMe2.txt");
//此时表示读取file1内的信息,输入流(相对程序而言)
InputStream fis=new FileInputStream(file1);
OutputStream fos=new FileOutputStream(file2);
try (fis;fos){
//使用输入流和输出流,完成文件复制
/** 定义一个中转站,中转站是一个字节,较小。
int n;
读一个字节。返回值为int
n=fis.read();
写入一个字节
fos.write(n);
此处需要不停地进行读写操作。
*/
//定义一个中转站,中转站是一个字节,较小。
int n;
// 读一个字节。返回值为int
n=fis.read();
//此处进行判断,如果n不为-1,则继续写入并再次读取字节,若n为-1则表示该该文件已被完全读取,不需要在写入了。
while (n!=-1){
fos.write(n);
n=fis.read();
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}
文件字符流FileReader和FileWriter
FileReader和FileWriter是字符流,是节点流,数据源和目的地是「文件」。
复制文件(中转站是一个字符)
/**
* 使用字节流可以读写任意类型的文件,
* 使用字符流只可以读写文本文件。
*
* 使用字符流处理非英文字符比较方便
*
* 其实只有字节流,没有字符流。*/
/**
* @author lambda
*/
public class TestCopyFileTwoWithFileWriterAndFileReader {
public static void main(String[] args) throws IOException {
File file1=new File("/data/home/lambda/JavaIo文件示例/ReadMe.txt");
File file2=new File("/data/home/lambda/JavaIo文件示例/ReadMe3.txt");
//1.创建输入输出流 /data/home/lambda/JavaIo文件示例
// Reader fr=new FileReader("/data/home/lambda/JavaIo文件示例/ReadMe.txt");
// Writer fw=new FileWriter("/data/home/lambda/JavaIo文件示例/ReadMe3.txt");
Reader fr=new FileReader(file1);
Writer fw=new FileWriter(file2);
//2.使用输入流和输出流复制文件
char[] buf=new char[1024];
int len=fr.read(buf);
while (len!=-1){
fw.write(buf,0,len);
// System.out.println(buf);
System.out.println(new String(buf,0,len));
len= fr.read(buf);
}
//3.关闭输入输出流
fr.close();
fw.close();
}
}
其实本质上来说没有字符流,全部都是字节流(字符流的底层还是由字节流来支持的) 并且他们全都是「节点流」:他们都是直接与数据源相连。不是处理流。
缓冲流
缓冲字节流BufferedInputStream和BufferedOutputStream
「文件缓冲字节流工作示意图」
复制文件(使用缓冲字节流提高效率)
import java.io.*;
/**使用缓冲流的好处:
* 1.提高读写速度
* 2.关闭高层流即可,不必关闭底层流
* 3.关闭高层流其实就是关闭底层流
*4.缓冲流可以提高查询速度的原因:
* 引入了缓冲区,大大减少了读写硬盘的次数。
* 5.如何刷新输出缓冲区? * */
/** * @author lambda */
public class TestCopBufferedStream {
public static void main(String[] args) throws IOException {
File file1=new File("/data/home/lambda/JavaIo文件示例/ReadMe.txt");
File file2=new File("/data/home/lambda/JavaIo文件示例/ReadMe3.txt");
//先创建两个字节节点流
InputStream fis=new FileInputStream(file1);
OutputStream fos=new FileOutputStream(file2);
//再将节点流传入两个处理流中
BufferedInputStream bis=new BufferedInputStream(fis);
BufferedOutputStream bos=new BufferedOutputStream(fos);
/**
* 简写:
* BufferedInputStream bis =new BufferedInputStream(new FileInputStream("/data/home/lambda/JavaIo文件示例/ReadMe.txt"));
* BufferedOutputStream bos=new BufferedOutputStream(new FileOutputStream("/data/home/lambda/JavaIo文件示例/ReadMe3.txt"));
*
* */
//此时仍把中转站算作一个字节来传输,但是缓冲流可以提高效率。
int n=bis.read();
while (n!=-1){
bos.write(n);
n=bis.read();
}
//关闭高层流即可(关闭缓冲字节流即可)
bis.close();
bos.close();
}
}
如何刷新输出缓冲区?
满了就自动刷新 bos.close(),先用flush,再关闭 手动刷新flush
缓冲字符流BufferedReader和BufferedWriter
之前的读写方式都是采用一个字节或者一个字节数组的形式操作,对于文本文件而言, Java提供了这两个类来实现「按行读写」。
缓冲字符流复制文件
/** * 使用缓冲流的好处 * 1.可以提高效率,BufferedInputStream和BufferedOutputStream,BufferedReader * 2.可以简化操作:BufferedReader * */
import java.io.*;
/** * @author lambda */
public class TestCopyBufferedReader {
public static void main(String[] args) throws IOException {
//创建输入流和输出流 /data/home/lambda/JavaIo文件示例/ReadMe.txt /data/home/lambda/JavaIo文件示例/ReadMe3.txt
BufferedReader br=new BufferedReader(new FileReader("/data/home/lambda/JavaIo文件示例/ReadMe.txt"));
BufferedWriter bw=new BufferedWriter(new FileWriter("/data/home/lambda/JavaIo文件示例/ReadMe3.txt "));
//使用输入输出流复制文件(按行读取)
String s= br.readLine();
while (s!=null){
bw.write(s);
//采用写入\n来实现换行
// bw.write("\n");
bw.newLine();
s=br.readLine();
}
//关闭输入输出流
br.close();
bw.close(); }}
总结1:BufferedReader和BufferedWriter的优点
速度快 简化编程
总结2:readLine()底层原理
底层本质上还是一个字符一个字符地读取,append()方法放入StringBuilder(或char数组)中
遇到换行符,将StringBuilder或者char数组转换成String并返回。
总结3:不同操作系统下的换行符是不同的。
Unix系统中,每行结尾只有换行,即:“\n” Windows系统中,每行结尾是回车+换行。即“\r\n” Mac系统中每行结尾是回车符号,即:“\r”
数据流和对象流-处理流
数据流 DataInputStream和DataOutputStream
之前使用文件流和缓冲流都只能按照字节、数组的方式来读取。最方便的也就是按行读取。数据流DataInputStream、DataOutputStream和对象流ObjectInputStream、 ObjectOutputStream可以很方便的「实现对各种基本数据类型和引用类型的读写。」最大的优势就是方便操作各种数据类型的方法、直接调用,简单方便。
注意:
只有字节流,没有字符流 都是处理流不是节点流 数据流只能操作基本类型和字符串,对象流还可以操作对象。 写入的是「二进制数据」,无法用记事本直接查看。 写入的数据需要用对应的输入流来读取。
使用数据流读写文件
public class TestDataStream {
public static void main(String[] args) {
try {
writer();
read();
} catch (IOException e) {
e.printStackTrace();
}
}
/** * 创建一个写的方法*/
public static void writer() throws IOException {
//创建一个缓冲写入字节处理流。里面传入写入字节节点流对象
BufferedOutputStream bos=new BufferedOutputStream(new FileOutputStream("/data/home/lambda/JavaIo文件示例/CatchMe.txt"));
//创建一个数据流来继续封装写入处理流(写入处理流封装了文件写入节点流,而处理本身也被数据流封装)
//此时不可以写成OutputStream dos=new DataOutputStream(bos);因为父类的方法较少。
DataOutputStream dos=new DataOutputStream(bos);
dos.writeInt(10);
dos.writeDouble(3.14);
dos.writeUTF("java");
dos.writeChar('a');
dos.writeBoolean(true);
dos.close();
}
/** * 创建一个读的方法*/
public static void read() throws IOException{
BufferedInputStream bis =new BufferedInputStream(new FileInputStream("/data/home/lambda/JavaIo文件示例/CatchMe.txt"));
DataInputStream dis=new DataInputStream(bis);
//直接读取写入的数据,按照顺序来读取。
System.out.println(dis.readInt());
System.out.println(dis.readDouble());
System.out.println(dis.readUTF());
System.out.println(dis.readChar());
System.out.println(dis.readBoolean());
dis.close();
}
}
对象流ObjectInputStream和ObjectOutputStream
使用对象流读写引用数据类型时,需要相应类实现「Serializable」接口,否则会 提示异常,显示没有「序列化」。
public class TestObjectStream {
public static void main(String[] args) throws IOException {
try {
writer();
read();
} catch (Exception e) {
e.printStackTrace();
}
}
public static void writer() throws IOException {
//创建一个缓冲写入字节处理流。里面传入写入字节节点流对象
BufferedOutputStream bos=new BufferedOutputStream(new FileOutputStream("/data/home/lambda/JavaIo文件示例/CatchMe.txt"));
//创建一个数据流来继续封装写入处理流(写入处理流封装了文件写入节点流,而处理本身也被数据流封装)
//此时不可以写成OutputStream dos=new ObjectOutputStream(bos);因为父类的方法较少。
ObjectOutputStream oos=new ObjectOutputStream(bos);
oos.writeInt(10);
oos.writeDouble(3.14);
oos.writeUTF("java");
oos.writeChar('a');
oos.writeBoolean(true);
oos.writeObject(new Date());
oos.close();
}
public static void read() throws IOException, ClassNotFoundException {
BufferedInputStream bis =new BufferedInputStream(new FileInputStream("/data/home/lambda/JavaIo文件示例/CatchMe.txt"));
ObjectInputStream ois=new ObjectInputStream(bis);
//直接读取写入的数据,按照顺序来读取。
System.out.println(ois.readInt());
System.out.println(ois.readDouble());
System.out.println(ois.readUTF());
System.out.println(ois.readChar());
System.out.println(ois.readBoolean());
Date date=(Date) ois.readObject();
System.out.println(date);
ois.close();
}
}