目录
1、什么是IO流
io流是程序对文件进行的输入输出的操作,Input,Output。
简单的说:输入流Reader,InputStream。输出流Writer,OutputStream。是程序与外部设备之间的数据传递过程。而输入流/输出流都是抽象的基类,无法直接创建实列。
2、IO流重点字节流和字符流
2.1 字节流
字节流就是InoutStream, OutputStream,在输入输出的基础上加上了Stream“流”。
字节流、字符流处理数据的单位:
- 字节流:一次读或写1字节的数据,也就是8位二进制,常见的有:视频、音频、图片、PDF、Excel
- 字符流:一次读或写1个字符,也就是至少1字节的数据(因为汉字是有占2-3字节的),也就是大于8位的二进制。常见的有文本.txt
- 图片
字节流、字符流改变过程:
在进行网络数据传输、硬盘数据保存所支持的数据类型只有:字节。
而所有磁盘中的数据必须先读取到内存后才能进行操作,这时候内存会帮助我们把字节变成字符。而字符更容易处理中文。
两者的区别:
- 字节流和字符流操作的本质区别只有一个:字节流是原生的操作,字符流是经过处理后的操作。
- 字节流和字符流的区别在于数据处理单位的不同。一个是1字节一个2字节。
按照流向方向划分为:输入流、输出流。
- 输入流:硬盘数据读入程序中。相对程序而言的,外部传入数据给程序需要借助输入流。
- 输出流:程序写出到硬盘上,相对程序而言的,程序把数据传输到外部需要借助输出流。
2.2 字节输入流
思路:InputStream这个类是表示输入字节流的所有类的超类,掌握这个就基本掌握其他类了。
按找功能不同。
输入流可划分为
节点流和包装流。
节点流:我可以向某一个特定的地方(也称作为节点)读取内部数据。
包装流也叫做(处理流),简单的说,包装流就是用来包装节点流的。
它的作用就是对节点流进行包装处理,然后也增加了一些功能,是一种典型的装饰器设计模式。
处理流:它也叫做高级流或包装流,处理流对一个已存在的流进行连接,通过封装后的流来进行读写。
图片:
节点流直接通过程序操作数据源
包装流通过包装后,在通过节点流操作数据
图片参考:JAVA IO流 - 屌丝迷途 - 博客园 (cnblogs.com)
节点流的常用类:
FileInputStream、ByteArrayInputStream基本都是父类的方法,只是某些使用场景略有不同。
文件:FileInputStream,读取文件内容的适合使用。
字节数组:ByteArrayInputStream,读取字节数组的时候使用。
包装流的常用类:
SequenceInputStream:序列流。
ObjectInputStream:对象反序列化流。
什么是序列化、反序列化?
序列化:将Java对象保存到硬盘上。
反序列化:从硬盘上转化成Java对象。
参考:java IO笔记(序列化与ObjectInputStream、ObjectOutputStream) - moonfish - 博客园 (cnblogs.com)
BufferedInputStream:缓冲输入流。
这个缓冲流为另一个输入流添加了功能,即缓冲输入和支持mark和reset方法的功能。
DataInoutStream:数据流。
它提供了可以直接传输功能,基本数据类型的功能,不需要提前转换,可以直接读取到数据内容。
使用数据输入流“包装”“文件输入流”获取数据,可以直接读取到指定的类型数据。
2.3 字符流
和上面的字节流差不多,但是它使用的常见不太一样。
输入流(Reader)
节点流:
在字符流中,需要注意的是,没有字节数组流(ByteArrayInputStream)只有字符数组流(CharArrayReader)
FileReader是节点流,但是它的父类InputStreamReader是包装流
InputStreamReader:字符输入流(字节流到字符流的桥梁)
FileReader;文件流:在文件字符输入读取时使用。
CharArrayReader:字符数组流;用于字符数组的读取。
包装流:
StringReader:字符串流;读取字符串时使用。
InputStreamReader:字符输入流(字节流到字符流的桥梁)
输出流(Writer)
FileWriter是节点流,父类OutputStreamWriter是包装流。
BufferedWriter:将对象的格式表示打印到文本输出流。(缓冲流)
PrintWriter:将对象的格式表示打印到文本输出流。和PrintStream类似,但是他不会自动刷新
StringWriter:在字符串缓冲区中收集其输出的字符流,然后可以用于构造字符串。(读取字符串使用)
OutputStreamWriter:字符输出流(字节流到字符流的桥梁)
FileWriter:文件字符输写出。
3、IO流的总体结构图
操作的是字节:InputStream,OutputStream。
操作的是文本:Reader,Writer。
图上:
4、IO流的索引头 (File)
File文件类,因为io的引介都是围绕着文件的,所有File文件类就是必不可少的了。
常用的三个File类的构造方法。
Constructor and Description File(File parent, String child) // 从父抽象路径名和子路径名字符串创建新的 File实例。
File(String pathname) // 通过将给定的路径名字符串转换为抽象路径名来创建新的 File实例。
File(String parent, String child) // 从父路径名字符串和子路径名字符串创建新的 File实例。
File文件类-从文件出发,到文件结束,而文件创建的意义就是;不存在才创建,存在后才创建成功。
File src = new File("C:/Users/lei/IdeaProjects/项目名/文件存放的位置/包名/类名");
boolean flag = src.createNewFile(); System.out.println(flag); src.delete(); // 删除已创建的相关文件
boolean createNewFile() //当且仅当具有该名称的文件尚不存在时,原子地创建一个由该抽象路径名命名的新的空文件。 bollean delete(); //删除已创建的相关文件。
文件的长度计算,是通过字节数来计算的。
File src = new File("C:/Users/lei/IdeaProjects/IO多网/src/io流/PathIo.java");
System.out.println("长度:"+src.length()); // length 文件的字节数
src = new File("C:/Users/lei/IdeaProjects/IO多网/");
System.out.println("长度:"+src.length());
long length();返回由此抽象路径名表示的文件的长度。
File的使用:
-
exists();是否存在
-
isFile();是否是文件
-
isDirectory();是否是文件夹
File src = new File("src/io流/PathIo.java"); System.out.println("是否存在:"+src.exists()); System.out.println("是否文件:"+src.isFile()); System.out.println("是否文件夹:"+src.isDirectory());
-
getName(); 返回名称
-
getPath(); 返回一个相对路径
-
getAbsolutePath(); 返回一个绝对路径
-
getParent(); 返回他的上一级
File src = new File("C:/Users/lei/IdeaProjects/IO多网/src/io流/PathIo.java"); System.out.println("名称:"+src.getName()); // 返回名称 System.out.println("路径:"+src.getPath()); // 返回一个相对路径 System.out.println("绝对路径:"+src.getAbsolutePath()); // 返回一个绝对路径 System.out.println("父路径:"+src.getParent()); // 返回他的上一级
5、解决编码乱码的问题
常用的编码有(ASKLL ,GBK ,UTF-8等)
如何解决编码乱码的问题呢,首先我们要知道编码乱的原因。
乱码产生的原因分两种,一是字数不够了,这样程序运行的时候就会出现少一个字节问题,从而产生乱码的问题,二是字符集不同,比如JVM要的是UTF-8的编码,你给了GBK的编码,也会产生乱码问题。
// 乱码 字数不够
msg = new String(datas, 0, datas.length - 1, "UTF8");
System.out.println(msg)**;
msg = new String(datas, 0, datas.length - 2, "UTF8");
System.out.println(msg);
// 字符集不统一。产生乱码
msg = new String(datas, 0, datas.length - 2, "gbk");
6、IO流的操作步骤
Reader和InputStream输入流,Writer和OutputStream输出流都是类的模板,知道了操作步骤,剩下的流就容易理解了:
- 创建源。
- 选择要使用的流。
- 操作或(读取字节的数组)。
- 释放资源。
1)使用方法
int read(); //从该输入流读取一个字节的数据。
void close(); //关闭此文件输入流并释放与流相关联的任何系统资源。
public static void main(String[] args) {
File src = new File("abc.txt"); // 创建源
InputStream is = null; // 选择流
try {
is = new FileInputStream(src); // 操作(读取字节的数据)
int a;
while ((a = is.read()) != -1) {
System.out.print((char) a);
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
is.close(); // 释放资源
} catch (IOException e) {
e.printStackTrace();
}
}
}
7、文件字节流
FileInputStream (通过字节的方式读取文件,适合读取所有类型的文件(图像,视频等)全字符考虑FileReader)。
构造方法:
protected FilterInputStream(InputStream in) 通过将参数 in到字段 this.in创建一个 FilterInputStream ,以便将其记住以供以后使用。
FileoutputStream (通过字节的方式写出或追加数据到文件,适合所有类型的文件(图像,视频等),全字符考虑FileWriter)。
构造方法
- FileOutputStream(File file); 创建文件输出流以写入由指定的 File对象表示的文件。
- FileOutputStream(File file, boolean append); 创建文件输出流以写入由指定的 File对象表示的文件。
- FileOutputStream(FileDescriptor fdObj); 创建文件输出流以写入指定的文件描述符,表示与文件系统中实际文件的现有连接。
- FileOutputStream(String name); 创建文件输出流以指定的名称写入文件。
- FileOutputStream(String name, boolean append);创建文件输出流以指定的名称写入文件。
使用方法
void flush(); // 刷新此输出流,并强制将任何缓冲的输出字节写入流。
void write(byte[] b); // 将 b.length个字节写入此输出流。
File dest = new File("cbc.txt");
OutputStream os = null;
try {
os = new FileOutputStream(dest, true);
String msg = "I have a wantPeople\r\n"; // 通过字节的方式写出或追加数据到文件
byte[] flush = msg.getBytes();
os.write(flush, 0, flush.length);
os.flush();
8、文件流
文件字符输入流;
构造方法
- FileReader(File file); 创建一个新的 FileReader ,给出 File读取。 FileReader(FileDescriptor fd); 创建一个新的 FileReader ,给定 FileDescriptor读取。 FileReader(String fileName); 创建一个新的 FileReader ,给定要读取的文件的名称。
File src = new File("cbc.txt"); Reader reader = null; try { reader = new FileReader(src); char[] car = new char[2]; // 缓冲容器 int len = -1; //接收长度 int temp; while ((len = reader.read(car)) != -1) { // 字符数组--->字符串 String ctr = new String(car, 0, len); System.out.println(ctr);
- 用Reader把文件中的信息读取出来。要注意读取的长度。
9、文件字符输出流
构造方法:
FileWriter(File file); 给一个File对象构造一个FileWriter对象。
FileWriter(File file, boolean append); 给一个File对象构造一个FileWriter对象。
FileWriter(FileDescriptor fd); 构造与文件描述符关联的FileWriter对象。
FileWriter(String fileName); 构造一个给定文件名的FileWriter对象。
FileWriter(String fileName, boolean append); 构造一个FileWriter对象,给出一个带有布尔值的文件名,表示是否附加写入的数据
。
File src = new File("cbc.txt");
Writer writer = null;
try {
writer = new FileWriter(src, true);
String msg = "IO so easy\r\n掌握自己的命运";
char[] srcs = msg.toCharArray();
writer.write(srcs, 0, srcs.length);
writer.flush(); // 刷新
用Writer把字符内容写入到文件中,其中可加入\r\n进行换行处理。在这边还可以使用一个append方法,简约代码。
writer.append("IO so easy").append("\n掌握自己的命运");
writer.flush();
writer写出 . append方法写入字符内容 . append方法 ; 结尾。最后刷新一下一下程序就可以了。
10、数组流
数组可以看成另外的一种内存,它可以不用向之前的那种流一样,需要关闭资源。
- 代码,数组输入
构造方法:
ByteArrayInputStream(byte[] buf) // 创建一个 ByteArrayInputStream ,使其使用 buf作为其缓冲区数组。
ByteArrayInputStream(byte[] buf, int offset, int length) // 创建 ByteArrayInputStream使用 buf作为其缓冲器阵列。
byte[] src = "io is so easy".getBytes(); // 使用平台的默认字符集将该String编码为字节序列,并将结果储存到一个新的字节数组中
InputStream is = null;
try {
is = new ByteArrayInputStream(src);
byte[] flush = new byte[5];
int len = -1;
while ((len = is.read(flush)) != -1) {
String str = new String(flush, 0, len);
System.out.println(str);
}
} catch (IOException e) {
e.printStackTrace();
}
2. 代码,数组输出
构造方法
ByteArrayOutputStream() // 创建一个新的字节数组输出流。
ByteArrayOutputStream(int size) // 创建一个新的字节数组输出流,具有指定大小的缓冲区容量(以字节为单位)。
byte[] dest = null;
ByteArrayOutputStream baos = null;
try {
baos = new ByteArrayOutputStream();
String str = "io is so easy";
byte[] datas = str.getBytes();
baos.write(datas, 0, datas.length);
baos.flush();
//获取数据
dest = baos.toByteArray();
System.out.println(dest.length+"--->"+new String(dest,0, baos.size()));
} catch (IOException e) {
e.printStackTrace();
}
11、缓冲流
BufferadReader&BufferedWriter(字节缓冲流)
// 使用缓冲输入流“包装”“文件输入流”提升读取效率。
is = new BufferedInputStream(new FileInputStream(src));
os = new BufferedInputStream(new FileOnputStream(src));
12、转换流
以字符流的形式操作字节流。
1)使用方法。
String readLine(); 读一行文字。
void newLine(); 写一行行分隔符
// 字符流转换为字节流
try (BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(System.out));) {
// 循环获取键盘的输入(exit退出),输出此内容
String msg = "";
while (!msg.equals("exit")) {
msg = reader.readLine(); // 循环读取
writer.write(msg); // 循环写出
writer.newLine();
writer.flush(); // 强制刷新
}
} catch (IOException e) {
System.out.println("操作异常");
}
2) InputStreamReadder&OutputStreamWriter:是字节流与字符流之间的桥梁,能将字节流转化为字符流,并且能为字节流指定字符集,可处理一个个的字符。
13、数据流
数据流是方便处理基本类型和八大基本类型,以及字符串的。
// 写出
ByteArrayOutputStream bos = new ByteArrayOutputStream();
DataOutputStream dos = new DataOutputStream(new BufferedOutputStream(bos));
// 操作数据类型+数据
dos.writeUTF("一二三四五六");
dos.writeInt(18);
dos.writeBoolean(false);
dos.writeChar('a');
dos.flush();
byte[] datas = bos.toByteArray();
// 读取
DataInputStream dis = new DataInputStream(new BufferedInputStream(new ByteArrayInputStream(datas)));
// 顺序与写出一致
String msg = dis.readUTF();
int age = dis.readInt();
boolean flag = dis.readBoolean();
char ch = dis.readChar();
System.out.println(age);
14、对象流和序列化
ByteArrayOutputStream bos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(new BufferedOutputStream(bos));
// 操作数据类型+数据
oos.writeUTF("一二三四五");
oos.writeInt(18);
将对象数据存储到数组中
序列化==文件的Copy
一个对象要想满足序列化就必须实现Serializable接口。
还有该类的属性必须是可序列化的。
还原序列化对象
// 对象的数据还原
Object str = ois.readObject();
Object date = ois.readObject();
Object Employee = ois.readObject();
读取 —反序列化
ObjectInputStream ois = new ObjectInputStream(new BufferedInputStream(new ByteArrayInputStream(datas)));
顺序写出一致
String msg = ois.readUTF();
int age = ois.readInt();
对象数据的还原
Object str = ois.readObject();
Object date = ois.readObject();
Object Employee = ois.readObject();
参考;Java 8 中文版 - 在线API中文手册 - 码工具 (matools.com)
图片参考;JAVA IO流 - 屌丝迷途 - 博客园 (cnblogs.com)