1、字节流与字符流
1.1 流的概念
在程序中所有的数据都是以流的方式进行传输或保存的,程序需要数据的时候要使用输入流读取数据,而当程序需要将一些数据保存起来的时候,就要使用输出流完成。程序中的输入输出都是以流的形式保存的,流中保存的实际上全都是字节文件。
1.2 字节流与字符流
在Java.io包中操作文件内容的主要有两大类:字节流、字符流,两类都分为输入和输出操作。
在字节流中输出数据主要是使用OutputStream完成,输入使的是InputStream;
在字符流中输出主要是使用Writer类完成,输入流主要使用Reader类完成。(这四个都是抽象类)
java中提供了专用于输入输出功能的包Java.io,其中包括:
InputStream,OutputStream,Reader,Writer
//InputStream 和OutputStream两个是为字节流设计的,主要用来处理字节或二进制对象
//Reader和 Writer两个是为字符流(一个字符占两个字节)设计的,主要用来处理字符或字符串
2、字节流和字符流的不同
实际上字节流在操作时本身不会用到缓冲区(内存),是文件本身直接操作的,而字符流在操作时使用了缓冲区,通过缓冲区再操作文件,如图所示。
3、代码比较
3.1 使用字节流,操作完成之后都不关闭输出流
import java.io.File;
import java.io.FileOutputStream;
import java.io.OutputStream;
public class OutputStreamTest{
public static void main(String[] args) throws Exception {
// 第1步:使用File类找到一个文件,声明File 对象
File f = new File("test.txt");
// 第2步:通过子类实例化父类对象,并准备好一个输出的对象
OutputStream out = new FileOutputStream(f);
// 通过对象多态性进行实例化
// 第3步:进行写操作
String str = "Hello World!!!";
// 准备一个字符串
byte b[] = str.getBytes();
// 字符串转byte数组
out.write(b);
// 将内容输出
// 第4步:关闭输出流
// out.close();
// 此时没有关闭
}
}
程序运行结果:
Hello World!!!
在test.txt文件中生成了相应内容。此时没有关闭字节流操作,但是文件中也依然存在了输出的内容,证明字节流是直接操作文件本身的。
3.1 使用字符流,操作完成之后都不关闭输出流
import java.io.File;
import java.io.FileWriter;
import java.io.Writer;
public class WriterTest {
public static void main(String[] args) throws Exception {
// 第1步:使用File类找到一个文件
File f = new File("test.txt");
// 第2步:通过子类实例化父类对象,并准备好一个输出的对象
Writer out = new FileWriter(f);
// 通过对象多态性进行实例化
// 第3步:进行写操作
String str = "Hello World!!!";
// 准备一个字符串
out.write(str);
// 将内容输出
// 第4步:关闭输出流
// out.close();
// 此时没有关闭
}
}
程序运行结果:
在test.txt文件中没有生成相应内容。程序运行后会发现文件中没有任何内容,这是因为字符流操作时使用了缓冲区,而 在关闭字符流时会强制性地将缓冲区中的内容进行输出,但是如果程序没有关闭,则缓冲区中的内容是无法输出的,所以得出结论:字符流使用了缓冲区,而字节流没有使用缓冲区。
4、缓冲区
缓冲区可以简单地理解为一段内存区域。可以简单地把缓冲区理解为一段特殊的内存。
某些情况下,如果一个程序频繁地操作一个资源(如文件或数据库),则性能会很低,此时为了提升性能,就可以将一部分数据暂时读入到内存的一块区域之中,以后直接从此区域中读取数据即可,因为读取内存速度会比较快,这样可以提升程序的性能。
在字符流的操作中,所有的字符都是在内存中形成的,在输出前会将所有的内容暂时保存在内存之中,所以使用了缓冲区暂存数据。如果想在不关闭时也可以将字符流的内容全部输出,则可以使用Writer类中的flush()方法完成。
5、强制性清空缓冲区
import java.io.File;
import java.io.FileWriter;
import java.io.Writer;
public class WriterTest{
public static void main(String[] args) throws Exception {
// 第1步:使用File类找到一个文件
File f = new File("test.txt");
// 第2步:通过子类实例化父类对象
Writer out = null;
// 准备好一个输出的对象
out = new FileWriter(f);
// 通过对象多态性进行实例化
// 第3步:进行写操作
String str = "Hello World!!!";
// 准备一个字符串
out.write(str);
// 将内容输出
out.flush();
// 强制性清空缓冲区中的内容
// 第4步:关闭输出流
// out.close();
// 此时没有关闭
}
}
程序运行结果:
Hello World!!!
在test.txt文件中生成了相应内容。此时,文件中已经存在了内容,更进一步证明内容是保存在缓冲区的。
5、流分类
5.1.Java的字节流
InputStream是所有字节输入流的祖先,而OutputStream是所有字节输出流的祖先。
5.2.Java的字符流
Reader是所有读取字符串输入流的祖先,而writer是所有输出字符串的祖先。
InputStream,OutputStream,Reader,writer都是抽象类。所以不能直接new。
6、拓展
字节流是最基本的,所有的InputStream和OutputStream的子类都是,主要用在处理二进制数据,它是按字节来处理的。但实际中很多的数据是文本,又提出了字符流的概念,它是按虚拟机的encode来处理,也就是要进行字符集的转化。
这两个之间通过 InputStreamReader,OutputStreamWriter来关联,实际上是通过byte[]和String来关联。在实际开发中出现的汉字问题实际上都是在字符流和字节流之间转化不统一而造成的。
在从字节流转化为字符流时,实际上就是byte[]转化为String时,public String(byte bytes[], String charsetName),有一个关键的参数字符集编码,通常我们都省略了,那系统就用操作系统的lang。
而在字符流转化为字节流时,实际上是String转化为byte[]时,String.getBytes(String charsetName),也是一样的道理。