在Java中IO操作是有相应步骤的,以文件操作为例,主要的操作流程如下:
- 使用File类打开一个文件;
- 通过字节流或字符流的子类指定输出的位置;
- 进行读/写操作
- 关闭输入/输出
字节流和字符流
区别:
- 读写单位:顾名思义,字节流以字节(byte)为读写单位,而字符流以字符为读写单位,根据码表映射字符,一次可能读入多个字符。
- 处理对象:字节流可以处理所有类型的数据(包括图片等),而字符流只能处理字符类型的纯文本数据。
- 字节流:一次写入或读出8位二进制。
- 字符流:一次写入或读出至少8位二进制。不同的字符所占的字节是不同的。
ASCII码:
一个英文字母(不分大小写)占一个字节的空间,一个中文汉字占两个字节的空间。一个二进制数字序列,在计算机中作为一个数字单元,一般为8位二进制数,换算为十进制。最小值0,最大值255。如一个ASCII码就是一个字节。
UTF-8编码:
一个英文字符等于一个字节,一个中文(含繁体)等于三个字节。
Unicode编码:
一个英文等于两个字节,一个中文(含繁体)等于两个字节。
1、字节流
字节输出流OutputStream
OutputStream是整个IO包中字节输出流的最大父类,作为一个抽象类,如果要使用此类,则必须通过子类实例化对象。
OutputStream类的常用方法:
方法 | 返回类型 | 描述 | 备注 |
---|---|---|---|
close() | void | 关闭输出流 | |
flush() | void | 刷新缓冲区 | |
write(byte[] b) | void | 将一个byte数组写入数据流 | |
write(byte[] b, int off, int len) | void | 将一个指定范围的byte数组写入数据流 | |
write(int b) | void | 将一个字节数据写入数据流 | 抽象方法abstract |
现在要操作的是一个文件,可以使用FileOutputStream类,通过向上转型后,可以为OutputStream实例化。
字节输入流InputStream
InputStream与OutputStream类一样,也是一个抽象类,必须依靠其子类。
InputStream类的常用方法:
方法 | 返回类型 | 描述 | 备注 |
---|---|---|---|
available() | int | 可以取得输入文件的大小 | |
close() | void | 关闭输入流 | |
read(byte[] b) | int | 将内容读到byte数组中,同时返回读入的个数 | |
read() | int | 读取内容,以数字的方式读取 | 抽象方法abstract |
现在要操作的是一个文件,可以使用FileInputStream类,通过向上转型后,可以为InputStream实例化。
实例
注意:由于在 windows 下,文件的路径使用的是“\”方式,但是反斜杠在字符串中被解析为转义符,所以一般来讲我们需要用“/”来替代“\”。当然,File.separator 是一样的效果。
如 在 windows 下的地址 F:\大学,我们就可以等价为 F:/大学,或 F:\\大学,或 F: + File.separator + 大学。
1、将数据写到TXT中
// 1. 使用File类打开一个文件;
File file = new File("d:" + File.separator + "test.txt"); //声明File对象
// 2. 通过字节流或字符流的子类指定输出的位置
OutputStream fos = new FileOutputStream(file); // 若是追加文件内容,则(file, true)
// 3. 进行写操作
String str = "你好,世界";
fos.write(str.getBytes("UTF-8")); //将字符串变成字节byte数组,使用UTF-8编码
// 4. 关闭输出
fos.close();
2、将TXT导入到内存
// 1. 使用File类打开一个文件;
File file = new File("d:" + File.separator + "test.txt");
// 2. 通过字节流或字符流的子类指定输出的位置
InputStream fis = new FileInputStream(file);
// 3. 进行读操作
byte[] buf = new byte[1024]; // 所有的内容读到此数组中临时存放
int len; //用于记录读取的数据个数
String myStr = "";
while((len = fis.read(buf)) != -1) { //将内容读到byte数组中,同时返回个数,若为-1,则内容读到底
myStr += new String(buf, 0, len, "UTF-8");
}
// 4. 关闭输出
fis.close();
System.out.println(myStr);
2、字符流
缓冲区:可以简单地理解为一段内存区域。
某些情况下,如果一个程序频繁地操作一个资源(如 文件或数据库),则性能会很低,此时为了提升性能,就可以将一部分数据暂时读入到内存的一块区域中,以后直接从此区域中读取数据即可,因为读取内存速度会比较快,这样可以提升程序的性能。
注意:字符流使用了缓冲区,而字节流没有使用缓冲区。
在字符流的操作中,所有的字符都是在内存中形成的,在输出前会将所有的内容暂时保存在内存之中,所以使用了缓冲区暂存数据。
字符输出流Writer
Writer是一个字符流的输出类,作为一个抽象类,如果要使用此类,则肯定要使用其子类。
Writer类的常用方法:
方法 | 返回类型 | 描述 | 备注 |
---|---|---|---|
close() | void | 关闭输出流 | abstract |
write(String str) | void | 将字符串输出 | |
write(char[] cbuf) | void | 将字符数组(缓冲区)输出 | |
flush() | void | 强制性清空缓存 | abstract |
此时如果是向文件中写入内容,应该使用FileWriter子类。
字符输入流Reader
Reader是使用字符的方式从文件中取出数据,作为一个抽象类,如果要使用此类,则肯定要使用其子类。
Reader类的常用方法:
方法 | 返回类型 | 描述 | 备注 |
---|---|---|---|
close() | void | 关闭输入流 | abstract |
read() | int | 读取单个字符 | |
read(char[] cbuf) | int | 将内容读到字符数组中,返回读入的长度 |
此时如果是向文件中读取内容,应该使用FileReader子类。
实例
1、将数据写到TXT中
// 1. 使用File类打开一个文件;
File file = new File("d:" + File.separator + "test.txt");
// 2. 通过字节流或字符流的子类指定输出的位置
Writer fw = new FileWriter(file);
// 3. 进行写操作
fw.write(str);
// 4. 关闭输出
fw.close();
2、将TXT导入内存中
// 1. 使用File类打开一个文件;
File file = new File("d:" + File.separator + "test.txt");
// 2. 通过字节流或字符流的子类指定输出的位置
Reader fr = new FileReader(file);
// 3. 进行读操作
char[] buf2 = new char[1024]; // 所有的内容读到此数组中临时存放
int len2;
String myStr2 = "";
while((len2 = fr.read(buf)) != -1) {
myStr2 += new String(buf2, 0, len2); //将缓冲区buf2数组中的0到len2字符串读出
}
// 4. 关闭输入
fw.close();
System.out.println(myStr2);
3、转换流
整个IO包实际上分为字节流和字符流,但是除了这两个流之外,还存在一组字节流-字符流的转换类。
- OutputStreamWriter:是Writer的子类,将输出的字符流变为字节流(字符流→字节流),即 将一个字符流的输出对象变为字节流的输出对象。
- InputStreamReader:是Reader的子类,将输入的字节流变为字符流,(字节流→字符流),即 将一个字节流的输入对象变为字符流的输入对象。
提示:
FileOutputStream 是 OutputStream 的直接子类
FileInputStream 也是 OutputStream 的直接子类
而 FileWriter 并不直接是 Writer 的子类,而是 OutputStreamWriter 的子类
而 FileReader 并不直接是 Reader 的子类,而是 InputStreamReader 的子类
所以,不管是使用字节流还是字符流实际上最终都是以字节的形式操作输入/输出流的。
PS : 所有的文件在硬盘或在传输时都是以字节的方式进行的,包括图片等都是按字节的方式存储的,而字符是只有在内存中才会形成,所以在开发中,字节流使用较为广泛。
结论:只要是处理纯文本数据,就优先考虑使用字符流。除此之外都使用字节流。