IO流相关类的关系结构图
Java的输入输出功能来自java.io 包中的InputStream类、OutputStream类、Reader类和Writer类以及继承它们的各种子类。

2.流的分类:

根据数据的流向可以分为:输入流和输出流
- 输入流:把数据从存储设备上读取到内存中的流。
- 输出流:把数据从内存 中写出到存储设备上的流。
根据数据的类型分为:字节流 和 字符流
- 字节流 :以字节为单位,读写数据的流。
- 字符流 :以字符为单位,读写数据的流。
字符流和字节流:
字符流的由来: 因为数据编码的不同,而有了对字符进行高效操作的流对象。本质其实就是基于字节流读取时,去查了指定的码表。
字节流和字符流的区别:
- 读写单位不同:字节流以字节(8bit)为单位,字符流以字符为单位,根据码表映射字符,一次可能读多个字节。
- 处理对象不同:字节流能处理所有类型的数据(如图片、avi等),而字符流只能处理字符类型的数据。
- 字节流:一次读入或读出是8位二进制。
- 字符流:一次读入或读出是16位二进制。
设备上的数据无论是图片或者视频,文字,它们都以二进制存储的。二进制的最终都是以一个8位为数据单元进行体现,所以计算机中的最小数据单元就是字节。意味着,字节流可以处理设备上的所有数据,所以字节流一样可以处理字符数据。
结论:只要是处理纯文本数据,就优先考虑使用字符流。 除此之外都使用字节流。
字节的输入输出流:
字节流:InputStream和OutputStream是java中可以按照最小字节单位读取的流,即每次读写一个字节,字节流是直接连接到输入源的流。
1.输入流:
InputStream 是所有的输入字节流的父类,它是一个抽象类。
ByteArrayInputStream、StringBufferInputStream、FileInputStream 是三种基本的介质流,它们分别从Byte 数组、StringBuffer、和本地文件中读取数据。
PipedInputStream 是从与其它线程共用的管道中读取数据,与Piped 相关的知识后续单独介绍。
ObjectInputStream 和所有FilterInputStream 的子类都是装饰流(装饰器模式的主角)。
输入流InputStream抽象类的方法:



public class InputStream {
public static void main(String[] args) throws IOException {
//创建FileInputStream对象
java.io.InputStream inputStream=new FileInputStream("F:\\test.txt");
byte[] byteData = new byte[200];
//将文件中的数据读入字节数组中
inputStream.read(byteData);
System.out.println(new String(byteData));
inputStream.close();
}
}
2.输出流:
OutputStream 是所有的输出字节流的父类,它是一个抽象类。
ByteArrayOutputStream、FileOutputStream 是两种基本的介质流,它们分别向Byte 数组、和本地文件中写入数据。
PipedOutputStream 是向与其它线程共用的管道中写入数据。
ObjectOutputStream 和所有FilterOutputStream 的子类都是装饰流。
输出流OutputStream 抽象类的方法:



FileOutputStream:
OutputStream有很多子类,我们从最简单的一个子类FileOutputStream开始。看名字就知道是文件输出流,用于将数据写出到文件
FileOutputStream构造方法:
1、 public FileOutputStream(File file):根据File对象为参数创建对象。
2、 public FileOutputStream(String name): 根据名称字符串为参数创建对象。
推荐第二种构造方法【开发常用】:
FileOutputStream outputStream = new FileOutputStream("abc.txt");
就以上面这句代码来讲,类似这样创建字节输出流对象都做了三件事情:
1、调用系统功能去创建文件【输出流对象才会自动创建】
2、创建outputStream对象
3、把foutputStream对象指向这个文件
public class OutputStream {
public static void main(String[] args) throws IOException {
// 第1步:使用File类找到一个文件
File f = new File("f:" + File.separator + "test2.txt");// 声明File 对象
FileOutputStream outputStream=new FileOutputStream(f);
String s = "你好啊 码农";
byte[] bytes = s.getBytes();
outputStream.write(bytes);
}
}
字符流
1.字符输入流:Reader类
字符输入流类都是Reader的子类,Reader类是一个抽象类,方法与InputStream类类似;



public class ReadTest {
public static void main(String[] args) {
File file = new File("F:\\test.txt");
reader(file);
}
/**
* 读取文件内容
* @param file
*/
public static void reader(File file) {
Reader read = null; //声明Reader对象
char[] c = new char[(int)file.length()]; //根据文件大小定义数组
try {
read = new FileReader(file); //使用FileReader实例化Reader
read.read(c);//读取内容到数组
read.close();
}catch(IOException e) {
System.out.println("读取失败");
e.printStackTrace();
}
System.out.println(c);
}
}
2.字符输出流:Writer类
Writer类和OutputStream类,在功能上是一致的,但两者前者写入的是字符,后者写入的是字节,Writer本身也是一个抽象类:


/**
* 向文件写入内容
* @param file
*/
public static void writer(File file) {
String str = "java";
char[] c = str.toCharArray(); //将字符串转换为char数组
try {
Writer writer = new FileWriter(file); //实例化对象
writer.write(c); //写入数据
writer.close();
System.out.println("写入成功");
}catch(IOException e) {
System.out.println("写入失败");
e.printStackTrace();
}
}
public class FWWrite {
public static void main(String[] args) throws IOException {
// 使用文件名称创建流对象
FileWriter fw = new FileWriter("F:\\test5.txt");
// 写出数据
fw.write(97); // 写出第1个字符
fw.write('b'); // 写出第2个字符
fw.write('C'); // 写出第3个字符
//关闭资源时,与FileOutputStream不同。 如果不关闭,数据只是保存到缓冲区,并未保存到文件。
fw.close();
}
}
流的定义
1.流的概念:什么是流?
流是用来读写数据的,是内存与存储设备之间传输数据的通道,Java中的流实际上是一个对象,真正的文件是在硬盘上的一块空间,在这个文件里面存放着各种各样的数据。而文件的数据通过以流为载体来进行数据的读写。这里的水桶相当于硬盘的文件等,水管相当于流,接水桶相当于程序猿操作的对象或JVM,水桶和接水桶之间通过流来进行数据传输。
如图:



关于流的理解的具体图:

举个栗子:



io类的关联的具体图解:

1.输入流与输出流:
个人见解:所谓输入输出针对程序而言。输入流就是外部(存储设备)读入程序(内存)中,输出就是程序(内存)输出到外部(存储设备)

2.字节流与字符流:
字节流和字符流的用法几乎完成全一样,区别在于字节流和字符流所操作的数据单元不同,字节流操作的单元是数据单元是8位的字节,字符流操作的是数据单元为16位的字符
为什么要有字符流?
Java中字符是采用Unicode标准,Unicode 编码中,一个英文为一个字节,一个中文为两个字节。

而在UTF-8编码中,一个中文字符是3个字节。例如下面图中,“云深不知处”5个中文对应的是15个字节:-28-70-111-26-73-79-28-72-115-25-97-91-27-92-124
那么问题来了,如果使用字节流处理中文,如果一次读写一个字符对应的字节数就不会有问题,一旦将一个字符对应的字节分裂开来,就会出现乱码了。为了更方便地处理中文这些字符,Java就推出了字符流。
字节流和字符流的其他区别:
字节流一般用来处理图像、视频、音频、PPT、Word等类型的文件。字符流一般用于处理纯文本类型的文件,如TXT文件等,但不能处理图像视频等非文本文件。用一句话说就是:字节流可以处理一切文件,而字符流只能处理纯文本文件。
字节流本身没有缓冲区,缓冲字节流相对于字节流,效率提升非常高。而字符流本身就带有缓冲区,缓冲字符流相对于字符流效率提升就不是那么大了。
3.节点流和处理流:
节点流:直接操作数据读写的流类,比如FileInputStream
处理流:对一个已存在的流的链接和封装,通过对数据进行处理为程序提供功能强大、灵活的读写功能,例如BufferedInputStream(缓冲字节流)
处理流和节点流应用了Java的装饰者设计模式。
下图就很形象地描绘了节点流和处理流,处理流是对节点流的封装,最终的数据处理还是由节点流完成的。

在诸多处理流中,有一个非常重要,那就是缓冲流。
我们知道,程序与磁盘的交互相对于内存运算是很慢的,容易成为程序的性能瓶颈。减少程序与磁盘的交互,是提升程序效率一种有效手段。缓冲流,就应用这种思路:普通流每次读写一个字节,而缓冲流在内存中设置一个缓存区,缓冲区先存储足够的待操作数据后,再与内存或磁盘进行交互。这样,在总数据量不变的情况下,通过提高每次交互的数据量,减少了交互次数。

联想一下生活中的例子,我们搬砖的时候,一块一块地往车上装肯定是很低效的。我们可以使用一个小推车,先把砖装到小推车上,再把这小推车推到车前,把砖装到车上。这个例子中,小推车可以视为缓冲区,小推车的存在,减少了我们装车次数,从而提高了效率。
处理流:
处理流和节点流一块使用,在节点流的基础上,再套接一层,套接在节点流上的就是处理流。如BufferedReader.处理流的构造方法总是要带一个其他的流对象做参数。一个流对象经过其他流的多次包装,称为流的链接。

常用的处理流
- 缓冲流:BufferedInputStrean 、BufferedOutputStream、 BufferedReader、 BufferedWriter 增加缓冲功能,避免频繁读写硬盘。
- 转换流:InputStreamReader 、OutputStreamReader实现字节流和字符流之间的转换。
- 数据流: DataInputStream 、DataOutputStream 等-提供将基础数据类型写入到文件中,或者读取出来。
构造函数:
InputStreamReader(InputStream); //通过构造函数初始化,使用的是本系统默认的编码表GBK。
InputStreamReader(InputStream,String charSet); //通过该构造函数初始化,可以指定编码表。
OutputStreamWriter(OutputStream); //通过该构造函数初始化,使用的是本系统默认的编码表GBK。
OutputStreamwriter(OutputStream,String charSet); //通过该构造函数初始化,可以指定编码表。
注意:
1.在实际的项目中,所有的IO操作都应该放到子线程中操作,避免堵住主线程。
2.FileInputStream在读取文件内容的时候,我们传入文件的路径(“D:/abc.txt”), 如果这个路径下的文件不存在,那么在执行readFile()方法时会报FileNotFoundException异常。
3.FileOutputStream在写入文件的时候,我们传入文件的路径(“D:/abc.txt”), 如果这个路径下的文件不存在,那么在执行writeFile()方法时, 会默认给我们创建一个新的文件。还有重要的一点,不会报异常。
节点流处理流分类图:

转换流:

字符编码与解码:
众所周知,计算机中储存的信息都是用二进制数表示的,而我们在屏幕上看到的数字、英文、标点符号、汉字等字符是二进制数转换之后的结果。按照某种规则,将字符存储到计算机中,称为编码 。反之,将存储在计算机中的二进制数按照某种规则解析显示出来,称为解码 。比如说,按照A规则存储,同样按照A规则解析,那么就能显示正确的文本符号。反之,按照A规则存储,再按照B规则解析,就会导致乱码现象。
简单一点说:
编码:字符(能看懂的)–字节(看不懂的)
解码:字节(看不懂的)–>字符(能看懂的)
代码解释:
String(byte[] bytes, String charsetName):通过指定的字符集解码字节数组
byte[] getBytes(String charsetName):使用指定的字符集合把字符串编码为字节数组
编码:把看得懂的变成看不懂的
String -- byte[]
解码:把看不懂的变成看得懂的
byte[] -- String
从另一角度来讲:字符流=字节流+编码表

为了达到最高效率,可以考虑在 BufferedReader 内包装 InputStreamReader
BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
IO的实例操作
1、FileInputStream、FileOutputStream(字节流)
字节流的方式效率较低,不建议使用
public class InputOuputTest {
public static void main(String[] args) throws IOException {
File file=new File("F:\\test3.txt");
writeFile(file);
read(file);
}
//写文件
public static void writeFile(File file) throws IOException {
OutputStream outputStream=new FileOutputStream(file);
String niuBPlus="各位看官 细听分说";
byte[] bytes = niuBPlus.getBytes();
outputStream.write(bytes);
// outputStream.close(); 注释了也能完成效果不知为何
}
//读文件
public static void read(File file) throws IOException {
InputStream inputStream=new FileInputStream(file);
byte[] bytes = new byte[200];
inputStream.read(bytes);
System.out.println("不多bb,各位大佬看结果:"+new String(bytes));
}
}
在开发中一般强烈推荐使用数组读取文件,代码如下:
package io;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
public class input2 {
public static void main(String args[]){
FileInputStream inputStream = null;
try {
inputStream = new FileInputStream("a.txt");
int len = 0 ;
byte[] bys = new byte[1024];
while ((len = inputStream.read(bys)) != -1) {
System.out.println(new String(bys,0,len));
}
} catch (IOException e) {
e.printStackTrace();
}finally {
try {
inputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
输出结果:
ab
cd
e
2.BufferedInputStream、BufferedOutputStream(缓冲字节流)
缓冲字节流是为高效率而设计的,真正的读写操作还是靠FileOutputStream和FileInputStream,所以其构造方法入参是这两个类的对象也就不奇怪了。
public class IOTest {
public static void main(String[] args) throws IOException {
File file=new File("F:\\test4.txt");
write(file);
String read = read(file);
System.out.println(read);
}
public static void write(File file) throws IOException {
// 缓冲字节流,提高了效率
BufferedOutputStream bis = new BufferedOutputStream(new FileOutputStream(file, true));
// 要写入的字符串
String string = "各位看官 细听分说";
// 写入文件
bis.write(string.getBytes());
// 关闭流
bis.close();
}
public static String read(File file) throws IOException {
BufferedInputStream fis = new BufferedInputStream(new FileInputStream(file));
// 一次性取多少个字节
byte[] bytes = new byte[1024];
//方法1
// 用来接收读取的字节数组
// StringBuilder sb = new StringBuilder();
// 读取到的字节数组长度,为-1时表示没有数据
// int length = 0;
// 循环取数据
// while ((length = fis.read(bytes)) != -1) {
// // 将读取的内容转换成字符串
// sb.append(new String(bytes, 0, length));
// }
//方法2
fis.read(bytes);
// 关闭流
fis.close();
// return sb.toString();
return new String(bytes);
}
}
3.InputStreamReader、OutputStreamWriter(字符流)
字符流适用于文本文件的读写,OutputStreamWriter类其实也是借助FileOutputStream类实现的,故其构造方法是FileOutputStream的对象
public class IOTest {
public static void write(File file) throws IOException {
// OutputStreamWriter可以显示指定字符集,否则使用默认字符集
OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream(file, true), "UTF-8");
// 要写入的字符串
String string = "各位看官 细听分说";
osw.write(string);
osw.close();
}
public static String read(File file) throws IOException {
InputStreamReader isr = new InputStreamReader(new FileInputStream(file), "UTF-8");
// 字符数组:一次读取多少个字符
char[] chars = new char[1024];
// 每次读取的字符数组先append到StringBuilder中
StringBuilder sb = new StringBuilder();
// 读取到的字符数组长度,为-1时表示没有数据
int length;
// 循环取数据
while ((length = isr.read(chars)) != -1) {
// 将读取的内容转换成字符串
sb.append(chars, 0, length);
}
// 关闭流
isr.close();
return sb.toString()
}
}
如何写好IO程序
(1)明确要操作的数据是数据源还是数据目的(也就是要读还是要写)
(2)明确要操作的设备上的数据是字节还是文本
(3)明确数据所在的具体设备
(4)明确是否需要额外功能(比如是否需要转换流、高效流等)
Java的IO流包括输入流和输出流,字节流和字符流。字节流处理所有类型数据,字符流专用于处理字符。字符流由Reader和Writer类构成,解决字节流处理字符编码问题。字节流的InputStream和OutputStream为基础类,处理流如BufferedInputStream和BufferedOutputStream提供缓冲功能。在实际开发中,通常使用字符流的BufferedReader和BufferedWriter提高效率,并结合InputStreamReader和OutputStreamWriter处理编码。

114

被折叠的 条评论
为什么被折叠?



