1 IO介绍
1.1概述
流是一组有序的数据序列,根据操作类型有输入流和输出流两种。
IO流为数据的传输提供了一个管道。
输入流是指数据源到程序(以InputStream、Reader结尾的流)
输出流是指向数据要到达的目的地,程序通过输出流中写入数据把信息传递到目的地,目标可以是文件,网络、压缩包、控制台等, (以OutPutStream、Writer结尾的流)。
![输入数据](https://img-blog.csdnimg.cn/20200816160634327.png)
![](https://img-blog.csdnimg.cn/20200816160719944.png)
按处理的数据单元分类:
- 字节流:以字节为单位获取数据,命名上以Stream结尾的流一般是字节流,顶级类InputStream、OutputStream。
- 字符流:以字符为单位获取数据,命名上以Reader/Writer结尾的流一般是字符流,顶级类Reader、Writer。
按处理对象不同分类:
- 节点流:可以直接从数据源或目的地读写数据,如FileInputStream、FileReader等。
- 处理流:不直接连接到数据源或目的地,是”处理流的流”。通过对其他流的处理提高程序的性能,如BufferedInputStream、BufferedReader等。处理流也叫包装流。
1.2 O流体系结构
字节流
- InputStream和OutputStream是Java语言中最基本的两个字节输入输出类。其他所有字节输入输出流类都继承自这两个基类。
- 这两个类都是抽象类,不能创建它们的实例,只能使用它们的子类.
- FilterInputStream和FilterOutputStream是所有包装流的父类
![](https://img-blog.csdnimg.cn/20200816161724324.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzM2MTI4NjQ4,size_16,color_FFFFFF,t_70)
![](https://img-blog.csdnimg.cn/20200816161733326.png)
字符流(Reader和Writer)
- Java语言中最基本的两个字符输入输出类。
- 其他所有字符输入输出流类都继承自这两个基类。
- 这两个类都是抽象类,不能创建它们的实例,只能使用它们的子类.
1.3 File类
File类是java.IO包中唯一代表磁盘文件本身的对象,通过调用File类的方法实现创建、删除、重命名等操作。
3.1文件的创建与删除
语法:new File(String pathname); payhmame :路径名称
如果当前目录不存在调用的文件,会调用createNewFile()方法创建一个该文件。
删除用delete()方法
File类的常用方法
【示例】使用File类新建、删除文件和文件夹
public class TestFile2 {
public static void main(String[] args) {
//创建一个File对象
//File file = new File("c:/study/readme.txt");
File file = new File("c:/study/abc/cba/acb/bac/readme.txt");
//如果文件存在就删除,如果不存在就创建
if(file.exists()){
file.delete();
}else{
try {
//判断所在文件夹是否存在,不存在,要先创建文件夹
File dir = file.getParentFile();
if(!dir.exists()){
//dir.mkdir();//make directory
dir.mkdirs();
}
//创建文件
file.createNewFile();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
注意
|
2 文件流
2.1 文件字节流 FileInputStream和FileOutputStream
- FileInputStream和FileOutputStream是字节流,是节点流,数据源和目的地是文件。
- 复制文件需要分别创建一个输入流和输出流完成文件读写
- 需要创建一个中转站,借助循环和中转站完成复制
- 流使用完毕一定要关闭,这和垃圾回收没有关系
【示例2】复制文件(中转站是一个字节)
public class TestFileStream {
public static void main(String[] args) throws IOException {
//1.创建流
File file1 = new File("e:/readme.txt");
File file2 = new File("e:/readme2.txt");
InputStream fis = new FileInputStream(file1);
OutputStream fos = new FileOutputStream(file2);
//2.使用流
//2.1 准备一个中转站(一个字节)
int n;
//2.2 读取一个字节到中转站
n = fis.read();
while(n!=-1){//读到了文件的末尾
//2.3 写一个字节到目的文件
fos.write(n);
//2.4 再读一个
n = fis.read();
}
//3.关闭流
fis.close();
fos.close();
}
}
- 缺点:中转站太小,速度慢,效率低;复制更大文件时效果更明显;可以将中转站由一个字节变为一个字节数组,减少读写硬盘的次数。
【示例3】复制文件(中转站是一个字节数组)
public class TestFileStream2 {
public static void main(String[] args) throws IOException {
//1.创建流
// File file1 = new File("e:/readme.txt");
// File file2 = new File("e:/readme2.txt");
// InputStream fis = new FileInputStream(file1);
// //OutputStream fos = new FileOutputStream(file2);//默认是覆盖文件
// OutputStream fos = new FileOutputStream(file2,true);//这是追加内容
// InputStream fis = new FileInputStream(new File("e:/readme.txt"));
// OutputStream fos = new FileOutputStream(new File("e:/readme2.txt"),true);
InputStream fis = new FileInputStream("e:/readme.txt");
OutputStream fos = new FileOutputStream("e:/readme2.txt",true);
//2.使用流
//2.1 准备一个中转站(一个字节数组)
byte [] buf = new byte[1024];
//2.2 读取一些字节到中转站
int len = fis.read(buf);//读取文件的数据到buf数组,返回真实读取的字节个数赋给len
while(len!=-1){//读到了文件的末尾
//2.3 写一个字节字节数组到目的文件
//fos.write(buf);
fos.write(buf,0,len);
//2.4 再读一些字节到字节数组
len = fis.read(buf);
}
//3.关闭流
fis.close();
fos.close();
}
}
【示例4】JDK7异常处理新特征
public class TestFileStream4 {
public static void main(String[] args) {
try(InputStream fis = new FileInputStream("e:/readme.txt");
OutputStream fos = new FileOutputStream("e:/readme2.txt")){
//1.创建流
//2.使用流
//2.1 准备一个中转站(一个字节数组)
byte [] buf = new byte[1024];
//2.2 读取一些字节到中转站
int len = fis.read(buf);
while(len!=-1){//读到了文件的末尾
//2.3 写一个字节字节数组到目的文件
//fos.write(buf);
fos.write(buf,0,len);
//2.4 再读一些字节到字节数组
len = fis.read(buf);
}
}catch (FileNotFoundException e){
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}
2.2 文件字符流FileReader和FileWriter
- FileReader和FileWriter是字符流,节点流,数据源和目的地是文件。
【示例5】复制文件(中转站是一个字符)
public class TestFileReaderWriter {
public static void main(String[] args) throws IOException {
//1.创建字符流
Reader fr = new FileReader(new File("e:/readme.txt"));
Writer fw = new FileWriter("e:/readme2.txt");
//2.使用字符流
/*
int n = fr.read();//一次读一个字符,不是一个字节。一个汉字一次搞定
while(n!= -1){
//System.out.println((char)n);
fw.write(n);
n = fr.read();
}
*/
int n=0;
while((n = fr.read())!=-1){
fw.write(n);
}
//3.关闭字符流
fr.close();
fw.close();
}
}
【示例6】复制文件(中转站是一个字符数组,并进行异常处理)
public class TestFileReaderWriter2 {
public static void main(String[] args) {
//1.创建字符流
try( Reader fr = new FileReader(new File("e:/readme.txt"));
Writer fw = new FileWriter("e:/readm22.txt");){
//2.使用字符流
char [] cbuf = new char[1024];
int len = fr.read(cbuf);
while(len!= -1){
//System.out.println(cbuf);
fw.write(cbuf,0,len);
len = fr.read(cbuf);
}
}catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}
3 缓冲流
3.1缓冲字节流BufferedInputStream和BufferedOutputStream
缓存是I/O的一种性能优化。缓存流为I/O流增加了内存缓存区,使得再流上执行skip()、mark()、 reset()
BufferedInputStream类对所有InputStream类进行带缓存的包装达到性能的优化。
有两个构造方法
-
BufferedInputStream(InputStream in)
- BufferedInputStream(InputStream in ,int size)
第一种构造一个带有32位字节的缓存流; 第二种可以按指定大小来创建缓存区。
BufferedOutputStream相比OutputStream多了一个fush()方法将缓存区数据强制输出广域完。也是两种构造方法
-
BufferedOutputStream(OutputStream out)
- BufferedOutputStream(OutputStream out ,int size)
【示例7】复制文件(使用缓冲流字节流提高效率)
public class TestCopy5 {
public static void main(String[] args) throws IOException {
//1.创建一个输入流和输出流
InputStream fis = new FileInputStream(new File("e:/JDK_API.CHM"));
OutputStream fos = new FileOutputStream(new File("e:/JDK_API2.CHM"));
//默认输入缓冲区大小8192
BufferedInputStream bis = new BufferedInputStream(fis);
//默认输出缓冲区大小8192
BufferedOutputStream bos = new BufferedOutputStream(fos);
//2.使用输入流和输出流完成文件复制
//2.1准备一个中转站(水杯)
int n;
//2.2先读一个字节
n = bis.read();//读取一个字节,赋给n
while(n != -1){
//2.3再写一个字节
bos.write(n);
//2.4在读一个字节
n = bis.read();
}
//3.关闭输入流和输出流
bis.close();
bos.close();
}
}
缓冲流原理
- 只要关闭高层流即可,底层流不用手动关闭;因为高层的关闭方法就是把底层流关闭
- 如何刷新输出缓冲区(让缓冲区内容写入硬盘,保证一致)
- 满了就自动刷新
- bos.close() 先flush,再关闭
- 手动刷新 flush()
【示例8】复制文件(按行读写)
public class TestCopy6 {
public static void main(String[] args) throws IOException {
//创建两个流
BufferedReader br =
new BufferedReader(new FileReader(new File("e:/sqlnet.log")));
BufferedWriter bw =
new BufferedWriter(new FileWriter("e:/sqlnet2.log"));
//使用两个流完成按行读取的功能
//中转站就是一个字符串,存储一行数据
//先读一行
String str = br.readLine();
while(str != null ){
//再写一行
bw.write(str);
bw.newLine(); //bw.write("\r\n");不同操作系统中换行符是不同的
//再读一行
str = br.readLine();//!!!
}
//关闭两个流
br.close();
bw.close();
}
}
总结1:BufferedReader和BufferedWriter的优点
1.速度快
2.简化编程
总结2:readLine()底层的原理
底层还是一个一个字符的读取,append()放入到StringBuilder(或者char[] )中,遇到换行符 ,将StringBuilder(char[])转换成String并返回
总结3:不同的操作系统中换行符是不同的
Unix系统里,每行结尾只有“<换行>”,即“\n”;
Windows系统里面,每行结尾是“<回车><换行>”,即“\r\n”;
Mac系统里,每行结尾是“<回车>”,即“\r”。
4 数据流和对象流
4.1数据流DataInputStream和DataOutputStream
之前使用文件流、缓冲流读取文件只能按照字节、数组方式读取,最方便的也是按行读取,能否很方便的实现对各种基本类型和引用类型数据的读写,并保留其本身的类型。
数据流DataInputStream和DataOutputStream和对象流ObjectInputStream和ObjectOutputStream可以解决这个问题,最大的优势就是提供了方便操作各种数据类型的方法,直接调用,简单方便。
注意
- 只有字节流,没有字符流
- 都是处理流,不是节点流
- 数据流只能操作基本数据类型和字符串,对象流还可以操作对象
- 写入的是二进制数据,无法直接通过记事本等查看
- 写入的数据需要使用对应的输入流来读取
【示例9】使用数据流读写文件
public class TestDataStream {
public static void main(String[] args) throws Exception {
//write();
read();
}
public static void write() throws Exception{
//创建输出流
OutputStream fos = new FileOutputStream("e:/readme2.txt");
BufferedOutputStream bos = new BufferedOutputStream(fos);
DataOutputStream dos = new DataOutputStream(bos);
//使用输出流
dos.writeInt(123);
dos.writeDouble(3.14);
dos.writeChar('A');
dos.writeBoolean(true);
dos.writeUTF("bjsxt");
//关闭输出流
dos.close();
}
public static void read() throws Exception{
//创建输入流
DataInputStream dis =
new DataInputStream(new BufferedInputStream(
new FileInputStream(
new File("e:/readme2.txt"))));
//使用输入流
System.out.println(dis.readInt());
double d = dis.readDouble();
System.out.println(d);
System.out.println(dis.readChar());
System.out.println(dis.readBoolean());
System.out.println(dis.readUTF());
//System.out.println(dis.readUTF());
//关闭输入流
dis.close();
}
}
4.2 对象流ObjectInputStream和ObjectOutputStream
【示例10】使用对象流读写文件
public class TestObjectStream {
public static void main(String[] args) throws Exception {
//write();
read();
}
public static void write() throws Exception{
//创建输出流
OutputStream fos = new FileOutputStream("e:/readme2.txt");
BufferedOutputStream bos = new BufferedOutputStream(fos);
ObjectOutputStream oos = new ObjectOutputStream(bos);
//使用输出流
oos.writeInt(123);
oos.writeDouble(3.14);
oos.writeChar('A');
oos.writeBoolean(true);
oos.writeUTF("bjsxt");
oos.writeObject(new Date());
oos.writeObject(new Student(1, "111", 22, 333.3));
//关闭输出流
oos.close();
}
public static void read() throws Exception{
//创建输入流
ObjectInputStream ois =
new ObjectInputStream(new BufferedInputStream(
new FileInputStream(
new File("e:/readme2.txt"))));
//使用输入流
System.out.println(ois.readInt());
double d = ois.readDouble();
System.out.println(d);
System.out.println(ois.readChar());
System.out.println(ois.readBoolean());
System.out.println(ois.readUTF());
//System.out.println(dis.readUTF());
Object date = (Date)ois.readObject();
System.out.println(date);
System.out.println(ois.readObject());
//关闭输入流
ois.close();
}
}
5 其他流
1. 打印流:PrintStream和PrintWriter
只有输出流,没有输入流
System.out、System.err是PrintStream的实例变量
2. 转换流:InputStreamReader和OutputStreamWriter
实现字节流到字符流的转换,是适配器设计模式的应用
只能从字节流转换成字符流,可以带来处理字符的便利。没有字符流转换成字节流的转换流,因为没有这种需求。
3. 字节数组流ByteArrayInputStream 和ByteArrayOutputStream
是节点流,数据源是字节数组,可以实现各种基本和引用数据类型与字节数组之间的相互转换
4. Java IO流的设计使用了装饰模式,动态组装流,可以减少子类的数量,是继承的一种替代方案。
OutputStream fos = new FileOutputStream("e:/readme.txt"); //提高速度 //简化操作 DataOutputStream dos = new DataOutputStream(bos); |
【示例11】认识其他IO流
public class Test {
public static void main(String[] args) throws IOException {
//1.打印流 只有输出流,没有输入流
PrintStream ps; //字节流 System.out System.err
PrintWriter pw; //字符流 后面讲解Servlet时会使用
//System.out就是PrintStream的一个引用变量
System.out.println();
//System.err也是PrintStream的一个引用变量
System.err.println();
//println()强大作用:不管什么类型数据,都给你变成字符串,并输出
//2.转换流
//接收键盘的输入一行数据,并输出
//接收一行数据,需要使用BufferedReader(或者Scanner)。接收键盘的输入,
//需要使用System.in;@2
// InputStream is = System.in; //三相插头
//将字节输入流InputStream转换为字符输入流Reader 三相转两相的转换头
// Reader reader = new InputStreamReader(is);
// BufferedReader br = new BufferedReader(reader);//两相的插座
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
BufferedWriter bw = new BufferedWriter(new FileWriter("e:/bjsxt.txt"));
//使用两个流完成按行读取的功能
//中转站就是一个字符串,存储一行数据
//先读一行
String str = br.readLine();
while(!"bye".equals(str) ){
//再写一行
bw.write(str);
//bw.write("\r\n");不同操作系统中换行符是不同的
bw.newLine();
//再读一行
str = br.readLine();//!!!
}
//关闭两个流
br.close();
bw.close();
FileInputStream fis; //节点流 数据源是文件
FileOutputStream fos;//节点流 目的地是文件
//数组流 节点流 数据源和目的地都是数组
ByteArrayInputStream bais;
ByteArrayOutputStream baos;
}
}