一.IO概述
数据源:提供原始数据媒介
流:一连串连续动态的集合
流的分类
按照流的方向
- 输入流:以InputStream, Reader结尾的流
- 输出流:以OutputStream, Writer结尾的流
按照处理数据单位
- 字节流:以字节为单位获取,命名上以Stream结尾,抽象基类是InputStream,OutputStream,字节流能处理几乎所有的文件
- 字符流:一字符为单位获取数据,命名上以Reader/Writer结尾,抽象基类是Reader,Writer,而字符流只能处理部分像txt, 等
按照功能
-
节点流:程序用于直接操作数据源所对应的类叫节点流,位于IO操作的一线,所有的其他操作基于它
-
处理流:对节点流进行包装,提高程序灵活性
- 一个简单的比喻
把数据源比作水井,地下水;
程序比作水龙头;
节点流就是水库;
处理流就是自来水厂,甚至水塔
IO流体系结构
-
所有的流都实现了:java.io.Closeable接口,都是可关闭的,都有close()方法。
流毕竟是一个管道,这个是内存和硬盘之间的通道,用完之后一定要关闭,
不然会耗费(占用)很多资源。养成好习惯,用完流一定要关闭。 -
java.IO流这块有四大家族:
java.io.InputStream 输入字节流
java.io.OutputStream 输出字节流java.io.Reader 输入字符流
java.io.Writer 输出字符流四大家族都是抽象类(Abstract class)。
二,IO流使用
使用IO流基本步骤
-
创建输入输出流
File file1 = new File("D:\\data.txt"); File file2 = new File("D:\\io.txt"); InputStream fis = new FileInputStream(file1); OutputStream fos = new FileOutputStream(file2);
file1,file2是数据源,和目的的的文件流对象
点开FileOutputStream的源码,后面还可以跟一个boolean的参数
表示追加
OutputStream fos = new FileOutputStream(file2, true);表示在file2的问价对象里追加,而不是把文件清空,再写
public FileOutputStream(File file) throws FileNotFoundException { this(file, false); }
-
定义一个中转站,因为IO是外存,内存的操作是从外存到内存,内存到外存的操作,中转站就是一块内存,用来存放外存到内存的信息
-
使用输入输出流完成文件复制
-
关闭输入输出流
输入输出流常用方法
常用方法输入流:
-
public int read();
从数据源一次读一个字节或者一个字符,针对你使用的是字符流对象还是字节流对象,下同,
返回值是读取的字节或者字符,,返回-1表示读到了文件末尾
-
public int read( b[])
从数据源一次读很b数组大小字节或者字符到b数组,返回的是读取到的文件长度,文件长度不一定的等于数组长度,比如数组大小1024, 文件还剩23未读取, 返回的长度就是23
-
待
常见方法输出流
-
void write(int b)
向目的一次写入一个字节或者字符
-
void write(byte b[], int off, int len);
向目的一次写入数组b off开始的len长度的字节或者字符
-
void write(byte b[])
向目的一次写入b数组
文件流
File类的基本使用
-
file代表文件和文件夹,作用获取文件或者文件夹属性,实现对文件,文件夹的创建删除等操作。
-
创建文件流
File file = new File("D:\\data.txt");
D:\data.txt 是文件对应的绝对路径,
如果是\ 用两次, 如果是/用一次" -
基本使用语法,方法
通过创建test01类来说明用法
public class test1 { public static void main(String[] args) { File file = new File("D:\\data.txt");//"如果是\ 用两次, 如果是/用一次" // File file = new File("D:/data.txt"); System.out.println("文件名 "+file.getName());//文件名 System.out.println("长度/大小 "+file.length());//长度 字节为单位 System.out.println("路径 "+file.getPath());//路径 System.out.println("文件存在? "+file.exists());//文件存在? System.out.println("==============="); System.out.println("可读? "+file.canRead());//可读? System.out.println("可写? "+file.canWrite());//可写? System.out.println("可执行? "+file.canExecute());//可执行? System.out.println("==============="); System.out.println("文件? "+file.isFile());//文件? System.out.println("目录? "+file.isDirectory());//目录? //File[] list = file.listFiles();;如果是文件夹,获取文件夹下文件流 File fileDir = new File("D:\\桌面\\压缩包"); File[] lists = fileDir.listFiles(); for (File list : lists) { System.out.println(list.getName()); } } }
-
实现对文件,文件夹的创建删除
public class test2 { public static void main(String[] args) throws IOException { File file = new File("D:\\io.txt"); //如果文件存在,就删除,否则创建 if(file.exists()){ file.delete(); }else { file.createNewFile();//新建文件夹 //新建文件夹一级的文件夹 file.mkdir(); //新建文件夹多级的文件夹 file.mkdirs(); } } }
实现对文件的复制
文件字节流(FileInputStream,OutputStream)
1.通过一次读取一个字节
public class test3_copy {
public static void main(String[] args) throws IOException {
//1.创建输入输出流
File file1 = new File("D:\\data.txt");
File file2 = new File("D:\\io.txt");
InputStream fis = new FileInputStream(file1);
OutputStream fos = new FileOutputStream(file2);
//OutputStream fos = new FileOutputStream(file2, true);追加
//精简化操作 InputStream fis = new FileInputStream(new File("D:\\data.txt"));
//2.使用输入输出流完成文件复制
int n;//定义一个中转站
// long s = System.currentTimeMillis();
n = fis.read();//读一个字节
while(n != -1){//读到了末尾
fos.write(n);//写入一个字节
n = fis.read();
}
//long e = System.currentTimeMillis();
// System.out.println("时间 " + (e - s) + "ms");
//3.关闭输入输出流
fos.close();
fis.close();
}
}
2.通过一次读取一个数组的字节(会更快一点)
package com.jiu.file;
import java.io.*;
public class test04_copy2 {
public static void main(String[] args) throws IOException {
//1.创建输入输出流
File file1 = new File("D:\\data.txt");
File file2 = new File("D:\\io.txt");
InputStream fis = new FileInputStream(file1);
OutputStream fos = new FileOutputStream(file2);
//2.使用输入输出流完成文件复制
long s = System.currentTimeMillis();
byte [] bs = new byte[1024];//定义一个中转站
int len = fis.read(bs);//读很多个字节
while(len != -1){//读到了末尾
fos.write(bs, 0, len);//一次写入很多字节
len = fis.read(bs);
}
long e = System.currentTimeMillis();
System.out.println("时间 " + (e - s) + "ms");
//3.关闭输入输出流
fos.close();
fis.close();
}
}
注意:while里面使用的是 void write(byte b[], int off, int len);方法因为剩余文件长度不一定的等于数组长度
文件字符流(FileReader, FileWriter)
-
字符流的本质是字节流,源码是这样的
-
public FileReader(File file) throws FileNotFoundException { super(new FileInputStream(file)); }
1.一次读入一个字符
public class test6 {
public static void main(String[] args) {
//1.创建输入输出流
Reader fr = null;
Writer fw = null;
try {
fr = new FileReader(new File("D:\\桌面\\视屏\\bookBackBg.mp4"));
fw = new FileWriter(new File("D:\\桌面\\视屏\\文件liu.mp4"));
//2.使用输入输出流完成文件复制
long s = System.currentTimeMillis();
int n;//定义一个中转站,一个字符
n = fr.read();//读一个字节
while(n != -1){//读到了末尾
fw.write(n);//一次写入一个字节
n = fr.read();
}
long e = System.currentTimeMillis();
System.out.println("时间 " + (e - s) + "ms");
} catch (IOException e) {
e.printStackTrace();
} finally {
//3.关闭输入输出流
try {
if(fr != null) fr.close();
} catch (IOException e) {
e.printStackTrace();
}
try {
if(fw != null) fw.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
2.一次读入一个字符数组(会更快一点)
package com.jiu.file;
import java.io.*;
public class test7 {
public static void main(String[] args) {
//1.创建输入输出流
Reader fr = null;
Writer fw = null;
try {
fr = new FileReader(new File("D:\\桌面\\视屏\\快上试听(第四章第一节).mp4"));
fw = new FileWriter(new File("D:\\桌面\\视屏\\缓冲流.mp4"));
//2.使用输入输出流完成文件复制
long s = System.currentTimeMillis();
char str[] = new char[1024];//定义一个中转站,一个字符数组
int len = fr.read(str);//读一个字节
while(len != -1){//读到了末尾
fw.write(str, 0, len);//一次写入一个字节
len = fr.read(str);
}
long e = System.currentTimeMillis();
System.out.println("时间 " + (e - s) + "ms");
} catch (IOException e) {
e.printStackTrace();
} finally {
//3.关闭输入输出流
try {
if(fr != null) fr.close();
} catch (IOException e) {
e.printStackTrace();
}
try {
if(fw != null) fw.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
异常整合
- 如果输入输出流任何一个创建失败,我们都应停止程序,所以放到一个try里面
- 对输入输出流的关闭,即使输出流关闭失败,我们也不能停止对输入流的关闭
public class test5_copy_exc {
public static void main(String[] args) {
//1.创建输入输出流
File file1 = new File("D:\\data.txt");
File file2 = new File("D:\\io.txt");
InputStream fis = null;
OutputStream fos = null;
try {
//如果输入输出流任何一个创建失败,我们都应停止程序,所以放到一个try里面
fos = new FileOutputStream(file2);
fis = new FileInputStream(file1);
//2.使用输入输出流完成文件复制
long s = System.currentTimeMillis();
byte [] bs = new byte[1024];//定义一个中转站
int len = fis.read(bs);//读很多个字节
while(len != -1){//读到了末尾
fos.write(bs, 0, len);//一次写入很多字节
len = fis.read(bs);
}
long e = System.currentTimeMillis();
System.out.println("时间 " + (e - s) + "ms");
} catch (IOException e) {
e.printStackTrace();
} finally {
//3.关闭输入输出流
//对输入输出流的关闭,即使输出流关闭失败,我们也不能停止对输入流的关闭
try {
if(fos != null) fos.close();
} catch (IOException e) {
e.printStackTrace();
}
try {
if(fis != null) fos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
按行读取
readLine():字节流没有这个方法
返回的是读取一行的内容,String 类型
package com.jiu.buffer;
import java.io.*;
public class readLine {
public static void main(String[] args) {
BufferedReader bis = null;
BufferedWriter bos = null;
try {
long s = System.currentTimeMillis();
bis = new BufferedReader(new FileReader(new File("D:\\data.txt")));
bos = new BufferedWriter(new FileWriter(new File("D:\\io.txt")));
// br = new BufferedReader(new FileReader(new File("D:\\data.txt")));
// bw = new BufferedWriter(new FileWriter(new File("D:\\io.txt")));
String str = "";
str = bis.readLine();
while(str != null){
bos.write(str);
bos.write('\n');
str = bis.readLine();
}
long e = System.currentTimeMillis();
System.out.println("时间 " + (e - s) + "ms");
} catch (IOException e) {
e.printStackTrace();
} finally {
//3.关闭输入输出流
try {
if(bos != null) bos.close();
} catch (IOException e) {
e.printStackTrace();
}
try {
if(bos != null) bos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
缓冲流
缓冲流概述
1.缓冲流可以提高读写速度
2.只需要关闭高层流就可以
3.缓冲流提高速度原因
-
IO是外存到内存,内存到外存的操作,计算机内存之间的访问速度还是很快的,主要消耗在外内的访问,既然如此我们从数据源读取到内存,或者从从内存写入到目的,我们能不能一次性就多读入一点或者多写入一点呢,这样我们IO内外存之间交换频数就会降低就会减少时间,提高效率
-
所以在内村中设置了BufferInputStream/BufferReader(输入缓冲区), BufferOutputStream/BufferWriter(输出缓冲区).另外我们在操作过程中设置了一个中转站,当中专站没有内容时,我们就从输入缓冲区里面读,如果输入缓冲区读完了,就会从外村读取数据源到输入缓冲区,同理输出时,向输出缓冲区写,写满了,就吧输出缓冲区写到目的地
-
缓冲区的大小,8192字节
public BufferedInputStream(InputStream in) { this(in, DEFAULT_BUFFER_SIZE); }
class BufferedInputStream extends FilterInputStream { private static int DEFAULT_BUFFER_SIZE = 8192;
-
简单使用
public class test1 { public static void main(String[] args) { //1.创建输入输出流 InputStream fis = null; OutputStream fos = null; try { long s = System.currentTimeMillis(); fis = new BufferedInputStream(new FileInputStream(new File("D:\\桌面\\视屏\\快上试听(第四章第一节).mp4"))); fos = new BufferedOutputStream(new FileOutputStream(new File("D:\\桌面\\视屏\\缓冲流.mp4"))); // br = new BufferedReader(new FileReader(new File("D:\\data.txt"))); // bw = new BufferedWriter(new FileWriter(new File("D:\\io.txt"))); byte str[] = new byte[1024]; int len = fis.read(str); while(len != -1){ fos.write(str, 0, len); len = fis.read(str); } long e = System.currentTimeMillis(); System.out.println("时间 " + (e - s) + "ms"); } catch (IOException e) { e.printStackTrace(); } finally { //3.关闭输入输出流 try { if(fis != null) fis.close(); } catch (IOException e) { e.printStackTrace(); } try { if(fos != null) fos.close(); } catch (IOException e) { e.printStackTrace(); } } } }
数据流和对象流
介绍
-
文件流,缓冲流读取文件只能按照字节,数组方式读取,最方便也是按行读取,但不能很方便的解决各种基本数据类型和应用类型的读写,并保留本省的数据类型,数据流和对象流可以很好的解决这个问题
-
他们只有子节流,没有字符流,都是处理流,不是节点流
-
写入到文件的是二进制数据,记事本查看就按不出所以然
-
写入的数据需要对应的输入流读取
-
数据流(DataInputStream, DataOutputStream):没有Datawriter, Datareader,希望将各种基本的数据类型和引用数据类型写入到文件中方便的读取出来,比如10 3.14 ‘a’ true “sdjf” … 写入文件,并读取
-
对象流(ObjectInputStream, ObjectOutputStream):不仅能处理基本数据类型和引用类型,还包括对象
-
最大的优点:简单方便
使用
- 基本方法,以下面程序为例,可以体会
数据流的使用
public class test1 {
public static void witer() throws IOException {
//创建输出流
OutputStream fis = new FileOutputStream("D:\\data.txt");//节点流
BufferedOutputStream bos = new BufferedOutputStream(fis);//提高速度
DataOutputStream dos = new DataOutputStream(bos);
//使用输出流写基本的数据类型和字符串
dos.writeInt(10);
// dos.writeInt(15);
dos.writeDouble(3.14);
dos.writeChar('a');
dos.writeUTF("jz");
//关闭流
dos.close();
}
public static void read() throws IOException {
InputStream fos = new FileInputStream("D:\\data.txt");
BufferedInputStream bis = new BufferedInputStream(fos);//提高速度
DataInputStream dis = new DataInputStream(bis);
System.out.println(dis.readInt());
System.out.println(dis.readDouble());
System.out.println(dis.readChar());
System.out.println(dis.readUTF());
//关闭流
dis.close();
}
public static void main(String[] args) throws IOException {
witer();
read();
}
}
- 注意:
这里是有严格的对应关系的,按照顺序来对应来读取,否则会报错,
比如上面文件只有一个int,如果我读取两个int或者不按照顺序读取
不仅读入数据出错,程序也会报错
System.out.println(dis.readInt());
//多谢一个
System.out.println(dis.readInt());
System.out.println(dis.readDouble());
System.out.println(dis.readChar());
System.out.println(dis.readUTF());
对象流使用
public class ObjectS {
public static void witer() throws IOException {
//创建输出流
OutputStream fis = new FileOutputStream("D:\\data.txt");//节点流
BufferedOutputStream bos = new BufferedOutputStream(fis);//提高速度
ObjectOutputStream oos = new ObjectOutputStream(bos);
//使用输出流写基本的数据类型和字符串
oos.writeInt(10);
oos.writeDouble(3.14);
oos.writeChar('a');
oos.writeUTF("jz");
oos.writeObject(new Date());
Student student = new Student(20, "jz", 52.5);
// oos.writeObject(student);
//关闭流,
oos.close();
}
public static void read() throws IOException, ClassNotFoundException {
InputStream fos = new FileInputStream("D:\\data.txt");
BufferedInputStream bis = new BufferedInputStream(fos);//提高速度
ObjectInputStream ois = new ObjectInputStream(bis);
System.out.println(ois.readInt());
// System.out.println(dis.readInt());
System.out.println(ois.readDouble());
System.out.println(ois.readChar());
System.out.println(ois.readUTF());
System.out.println((Date)ois.readObject());
// System.out.println((Student)ois.readObject());
//关闭流
ois.close();
}
public static void main(String[] args) throws IOException, ClassNotFoundException {
witer();
read();
}
}
-
Date()对象能输出
-
我们自定义类看能不能成功
package com.jiu.data; import java.io.*; import java.util.Date; public class ObjectS { public static void witer() throws IOException { //创建输出流 OutputStream fis = new FileOutputStream("D:\\data.txt");//节点流 BufferedOutputStream bos = new BufferedOutputStream(fis);//提高速度 ObjectOutputStream oos = new ObjectOutputStream(bos); //使用输出流写基本的数据类型和字符串 oos.writeInt(10); oos.writeDouble(3.14); oos.writeChar('a'); oos.writeUTF("jz"); oos.writeObject(new Date()); Student student = new Student(20, "jz", 52.5); oos.writeObject(student); //关闭流, oos.close(); } public static void read() throws IOException, ClassNotFoundException { InputStream fos = new FileInputStream("D:\\data.txt"); BufferedInputStream bis = new BufferedInputStream(fos);//提高速度 ObjectInputStream ois = new ObjectInputStream(bis); System.out.println(ois.readInt()); // System.out.println(dis.readInt()); System.out.println(ois.readDouble()); System.out.println(ois.readChar()); System.out.println(ois.readUTF()); System.out.println((Date)ois.readObject()); System.out.println((Student)ois.readObject()); //关闭流 ois.close(); } public static void main(String[] args) throws IOException, ClassNotFoundException { witer(); read(); } } class Student{ private int age; private String name; private double weight; public Student() { } public Student(int age, String name, double weight) { this.age = age; this.name = name; this.weight = weight; } @Override public String toString() { return "Student{" + "age=" + age + ", name='" + name + '\'' + ", weight=" + weight + '}'; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } public String getName() { return name; } public void setName(String name) { this.name = name; } public double getWeight() { return weight; } public void setWeight(double weight) { this.weight = weight; } }
-
发现抱错了,具体是没有序列化,为什么Date没有报错呢?
因为它实现了序列化的接口
解决方法:下面
序列化和反序列化
-
所以需要在student上实现Serializable接口,这个接口没有方法
class Student implements Serializable{
1.什么是序列化和反序列化
序列化(Serialization):将对象信息转换为可以存储或者传输的形式的过程,
对象(内存)---->字节数组,字节序列(外存, 网络)
反序列化(DeSerialization):字节数组,字节序列(外存, 网络)---->对象(内存)
2.什么时候需要序列化和反序列化
存储或者传输,比如存储到外存,传输到网络
3.实现序列和反序列化
- 序列化(writeObject())
别忘了类,implements Serializable
OutputStream fis = new FileOutputStream("D:\\data.txt");//节点流
BufferedOutputStream bos = new BufferedOutputStream(fis);//提高速度
ObjectOutputStream oos = new ObjectOutputStream(bos);
oos.writeObject(student);//这就代表序列化了
-
反序列化(readObject())
InputStream fos = new FileInputStream("D:\\data.txt"); BufferedInputStream bis = new BufferedInputStream(fos);//提高速度 ObjectInputStream ois = new ObjectInputStream(bis); Student student = (Student) ois.readObject();//反序列化,强转
5.注意
-
序列化接口一个方法也没有
-
static 属性不会被序列化
-
如果不想被序列化,又不想用static, static共用一个.transient,(临时的)
private transient String name;
-
写入到文件的序列化对象,如果类对象发生了细微改变,比如序列化时name属性是private,序列化后我们吧源文件改成public, 那么我们读取的时候就读取不出来
异常信息:改动前后序列号不一样 -3266437649961960535, 4078769611778896526
Exception in thread "main" java.io.InvalidClassException: com.jiu.data.Student; local class incompatible: stream classdesc serialVersionUID = -3266437649961960535, local class serialVersionUID = 4078769611778896526
解决方法:在Student 类中加入序列化ID,idea中加入需要配置一下, 我们打开百度,搜索 idea 序列化
打开Settings
找到这个
把它勾选了然后开始使用 光标移到类名上 alt + enter
大功告成码字不易,有错误欢迎指出来,喜欢记得点赞!!