IO流的分类
- 文件流分为字节流和字符流。
- 字节流以字节为传输单位,适用于二进制文件(图片、视频等);
- 字符流以字符为传输单位,根据不同的编码方式字符单位的大小也不同,适用于文件传输;
-
根据数据的流向分为:输入流 和 输出流。
- 输入流 :把数据从
其他设备
上读取到内存
中的流。 - 输出流 :把数据从
内存
中写出到其他设备
上的流。
输入也叫做读取数据,输出也叫做作写出数据
上面这四个类衍生出一系列类,它们是最根本的,但它们都是抽象类,需要其子类来实现其方法。
PS:我们必须明确一点的是,一切文件数据(文本、图片、视频等)在存储时,都是以二进制数字的形式保存,都一个一个的字节,那么传输时一样如此。所以,字节流可以传输任意文件数据。在操作流的时候,我们要时刻明确,无论使用什么样的流对象,底层传输的始终为二进制数据。
字节流
字节输入流(InputStream):
java.io.InputStream
抽象类是表示字节输入流的所有类的父类,可以读取字节信息到内存中。它定义了字节输入流的基本共性功能方法。
文件输入流FileInputStream:
- FileInputStream是继承了InputStream的类,也就表明其是以字节为传输单位
- 其作用是从文件读取数据,输出到控制台
FileInputStream的构造方法
1、
FileInputStream(File file)
: 通过打开与实际文件的连接来创建一个 FileInputStream ,该文件由文件系统中的 File对象 file命名。
2、FileInputStream(String name)
: 通过打开与实际文件的连接来创建一个 FileInputStream ,该文件由文件系统中的路径名name命名。
同样的,推荐使用第二种构造方法:
FileInputStream inputStream = new FileInputStream("D:\code\JavaDemo\a.txt");
当你创建一个流对象时,必须传入一个文件路径。该路径下,如果没有该文件,会抛出FileNotFoundException
。
构造举例,代码如下:
public class FileInputStreamConstructor throws IOException{
public static void main(String[] args) {
// 方法1:使用File对象创建流对象
File file = new File("a.txt");
FileInputStream fos = new FileInputStream(file);
// 方法2:使用文件名称创建流对象(推荐使用)
FileInputStream fos = new FileInputStream("b.txt");
}
}
案例代码(利用字节数组形式):
新建文本,名字为a,路径为D:\\code\\JavaDemo\\a.txt,内容为abcde。
package com.logindemo.IO;
import java.io.FileInputStream;
import java.io.IOException;
public class f1 {
public static void main(String[] args) {
FileInputStream fileInputStream = null;
int readData=0;
byte[] rData = new byte[2];
int len = 0; //记录每一轮实际读取的字节数
try {
//创建字节文件流对象
fileInputStream = new FileInputStream("D:\\code\\JavaDemo\\a.txt");
//循环读取
while ((len = fileInputStream.read(rData)) != -1) {
//设置字节数组,一次最多可读取2字节
//读取结束后,把数组变成字符串打印出来,从下标0开始
System.out.print(new String(rData, 0, len));
}
} catch (Exception e) {
throw new RuntimeException(e);
} finally {
//读取完毕要关闭文件输入流
try {
fileInputStream.close();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
}
运行结果:确实是abcde~~~读取数据成功~
代码解析几点:
(1)读取字节:
read()
: 每次读取一个字节,返回该字节对应的int值,读取到文件末尾返回-1;后面需要转成字符型打印出来。但是这种方式不太好处理中文,一个汉字可能会对应多个字节,所有更推荐数组↓
(2)使用字节数组读取:
public int read(byte[] b)
: 每次读取b的长度个字节到数组中,返回该字节数组的有效数组长度,并将读取到的有效字节存储到字节数组中,后面需要把数组变成字符串打印出来。读取到末尾时,返回-1
- String构造器有一个是将字符数组转化为字符串,参数为
(数组,起点,长度)
- 要设置finally以关闭IO流
补充:为什么需要len?
我们先看没有len的情况,只图中改这一行代码:
运行可以发现,输出的是abcded!多了一个d!显然不对!!为什么会出现这种情况?看图分析!
所以得出结论:要通过len
,获取有效的字节!!!
文件输出流FileOutputStream:
- FileOutputStream是OutputStream的子类,表明其是按字节进行输出
- 其作用是向文件写入数据
FileOutputStream的构造方法:
1、
public FileOutputStream(File file)
:根据File对象为参数创建对象。
2、public FileOutputStream(String name)
: 根据名称字符串为参数创建对象。
推荐第二种构造方法 :
FileOutputStream outputStream = new FileOutputStream("D:\code\JavaDemo\a.txt");
同样的,当你创建一个流对象时,必须传入一个文件路径。该路径下,如果没有该文件,会抛出FileNotFoundException
。
在该路径下,如果没有这个文件,会创建该文件。如果有这个文件,会清空这个文件的数据。
构造举例,代码如下:
public class FileOutputStreamConstructor throws IOException {
public static void main(String[] args) {
// 使用File对象创建流对象
File file = new File("D:\code\JavaDemo\a.txt");
FileOutputStream fos = new FileOutputStream(file);
// 使用文件名称创建流对象
FileOutputStream fos = new FileOutputStream("D:\code\JavaDemo\b.txt");
}
}
案例代码:
新建文本,名字为b,路径为D:\\code\\JavaDemo\\a.txt,内容为abc。
package com.logindemo.IO;
import java.io.FileOutputStream;
import java.io.IOException;
public class f2 {
public static void main(String[] args) {
FileOutputStream fileOutputStream = null;
String path = "D:\\code\\JavaDemo\\b.txt";
try {
//fileOutputStream=new FileOutputStream(path); //这个会清空原有内容,即覆盖
//设置追加而不是覆盖的写法
fileOutputStream = new FileOutputStream(path, true); //不会清空,即追加
//写入单个字符
//fileOutputStream.write('a');
//设置一次添加多个字符
String s1 = "hello";
fileOutputStream.write(s1.getBytes()); //字符串转换为字节数组
} catch (Exception e) {
throw new RuntimeException(e);
} finally {
//关闭输出流
try {
fileOutputStream.close();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
}
运行结果:
代码解析几点:
(1)写出字节:
write(int b)
方法,每次可以写出一个字节数据;(2)写出字节数组:
write(byte[] b)
,每次可以写出数组中的数据;
追加新数据:
true
表示追加数据,false
表示不追加也就是清空原有数据;write方法中接收的是char型字符或者是数组;
s1.getBytes():把字符串“hello”转换为字节数组;
补充知识:
写出指定长度字节数组:write(byte[] b, int off, int len)
,每次写出从off
索引开始,len
个字节
改一下原代码框的位置,加off和len的值,我们要追加从索引2开始的2个字节,hello,即ll,可以发现确实是追加了ll: