目录
1、什么是流(流、输入流、输出流的定义)
* 流是一个抽象的概念,是对输入输出设备的抽象定义。输入流可以看做一个输入通道,输出流则可以看做一个输出通道。
* 输入流的“输入”是相对于程序而言的,外部设备传送数据给程序需要借助输入流。
* 输出流的“输出”也是相对于程序而言的,程序将数据传送给外部设备需要借助输出流。
2、字节流和字符流的概念与比较
程序中所有的数据都是以流的形式进行传输或保存的,而所有的流在硬盘中保存或传输时都是以字节的形式存在,包括图片、音频等都是以字节的形式存储的。
字节流主要用来处理二进制数据,但在实际应用中,很多数据是都是文本,所以提出了字符流的概念。一个字符由多个字节组成,即字节是更小的储存单位(字符流处理的单元为2个字节的Unicode字符 ,可直接操作字符、字符串或字符数组;而字节流处理单元为1个字节,操作字节或字节数组)。字符流是按照JVM的encode来处理的,也就是要进行字符集的转化。二者之间通过InputStreamReader和OutputStreamWriter来关联,本质上是通过byte[]和String来关联。实际开发中碰到的中文乱码问题大多都是字节流和字符流之间转化的不统一而造成的。
字节流:即传输过程中,传输数据最基本的单位是字节。在对文件的读写操作中,除了字节缓冲输入流BufferedInputStream和字节缓冲输出流BufferedOutputStream之外,其他字节流类本身不会用到缓冲区(即内存),直接对文件进行操作。字节流可用于任何类型的对象,但主要操作byte类型数据,以byte数组为准,所以注意将数据转化为byte数组。
字符流:即传输过程中,传输数据最基本的单位是字符。在对文件的读写操作中,所有字符流类都要通过缓冲区来操作,即不能直接操作文件。同时,字符流的操作中,在输出到文件之前,所有数据会暂时保存在内存缓冲区中。字符流只能处理字符型数据。
字节流示例:
public class WriterDemo1 {
public static void main(String[] args) throws IOException{
// TODO Auto-generated method stub
File f = new File("text1.txt");
FileOutputStream fop = new FileOutputStream(f);
String str = "我爱 China";
//因为fop是字节流类的实例对象,所以要将字符串转化为字节数组,然后对字节数组进行输出
byte[] bytes = str.getBytes();
fop.write(bytes);
//因为字节流操作文件不用缓冲区,所以就算不关闭字节流,也会输出数据到文件当中
fop.close();
}
}
字符流示例:
public class WriterDemo2 {
public static void main(String[] args) throws IOException{
// TODO Auto-generated method stub
File f = new File("text2.txt");
FileWriter fw = new FileWriter(f);
String str = "你好,China";
//因为fw是字符流类的实例对象,所以可以直接用write方法输出字符串到文件中
fw.write(str);
/*
* 因为字符流操作时将数据保存在缓冲区中,只有关闭字符流后才会将缓冲区中的内容输出,
* 所以下面一句若注释掉,则不会输出“world”到文件当中
*/
fw.close();
//如果想在不关闭字符流的情况下输出缓冲区中的数据,可以使用Writer类中的flush方法强制输出数据
//fw.flush();
}
}
3、字节流中常用的类
* 字节流的类通常以Stream结尾。
(1)常用的字节输入流:
* InputStream
* FileInputStream
* BufferedInputStream
[1] InputStream:
是字节输入流的抽象基类,不能用来实例化。其定义的常用方法如下:
read(byte[] b):从流中读取字节数组b的长度个字节的数据存储到字节数组b中,返回结果是读取到的字节个数(常将返回结果与-1比较确定是否到达结尾,若为-1说明到达结尾,不能再读取到数据)。
read(byte[] b,int off,int len):从流中off位置开始读取len个字节的数据存储到字节数组b中,返回结果是实际读取到的字节个数(同理,常将返回结果与-1比较确定是否到达结尾,若为-1说明到达结尾,不能再读取到数据)。
close():关闭流,释放资源。
[2] FileInputStream:
主要用来读取文件中的数据,所以需要一个文件对象来实例化,这个文件可以是一个File对象,也可以是文件名路径串。例子中采用前者。除了可以使用基类中的所有方法外,它还重写了基类的read方法(无参的):
read():从流中读取一个字节的数据,返回结果是一个int型数据
例:
public static void main(String[] args) throws IOException{
// TODO Auto-generated method stub
File f = new File("text1.txt");
FileInputStream fis = new FileInputStream(f);
byte[] bytes = new byte[1024];
int length;
while((length = fis.read(bytes)) != -1) {
//fip是字节流类的实例对象,读取的是字节数组byte,要在控制台显示则要转换成String类型
System.out.println(new String(bytes,0,length));
}
fis.close();
}
[3] BufferedInputStream:
这个流是字节流中唯二要使用缓冲区的流类之一,它提前将要读取的数据保存到内存缓冲区中,因为内存中操作数据速度较快,所以这个流的效率更高。通常,它会封装别的流对象以提高效率,即它的初始化需要一个InputStream基类的输入流对象。
例:
public static void main(String[] args) throws IOException{
// TODO Auto-generated method stub
File f = new File("text1.txt");
FileInputStream fis = new FileInputStream(f);
BufferedInputStream bis = new BufferedInputStream(fis);
byte[] bytes = new byte[1024];
int length;
while((length = bis.read(bytes)) != -1) {
//字节流类读取的是字节型数据,要转化成String类型才能在控制台显示
System.out.println(new String(bytes,0,length));
}
}
(2)常用的字节输出流:
* OutputStream
* FileOutputStream
*BufferedOutputStream
[1] OutputStream:
是字节输出流的抽象基类,不能用来实例化。其定义的常用方法如下:
write(byte[] b):将字节数组b的长度个字节数据写到输出流中。注意参数是字节型,所以如果要输出字符串型的数据,要用getBytes()方法将字符串转化为byte数组再输出。
write(byte[] b,int off,int len):从字节数组b的off位置开始,获取len个字节数据,写出到输出流中。
flush():刷新输出流,将缓冲区中的数据立即写到输出流中,通常用在不关闭流的情况下降缓冲区数据写出。
close():关闭流,释放系统资源。
[2] FileOutputStream:
主要用来将数据写入文件,所以它需要一个文件对象作为实例化参数,这个文件可以是File对象,也可以是文件路径字符串(若文件不存在,则自动创建文件)。(注:FileOutputStream实例化时可以给定第二个参数,用于说明是否将数据追加写入,参数值为true时代表在原有内容后面追加写入,不带这个参数时默认为false)
例:
public static void main(String[] args) throws IOException{
// TODO Auto-generated method stub
FileOutputStream fos = new FileOutputStream("text2.txt");
String str = "你好 china";
//因为fos是字节流类的实例对象,write方法的参数是字节,所以要将字符串转化为字节数组才行
fos.write(str.getBytes());
//因为fos的类不需要缓冲区,所以不关闭流也能将数据输出到文件中
fos.close();
}
[3] BufferedFileOutputStream:
同上面的字节缓冲输入流一样,字节缓冲输出流是字节流中另一个需要缓冲区的流类,先将数据保存到缓冲区中,等关闭流之后才会将数据输出到文件中。若想在不关闭流的情况下输出数据,可以使用flush()方法刷新缓冲区,从而使数据输出到文件。它需要一个输出流作为实例化参数。
例:
public static void main(String[] args) throws IOException{
// TODO Auto-generated method stub
File f = new File("text1.txt");
FileOutputStream fop = new FileOutputStream(f);
BufferedOutputStream bos = new BufferedOutputStream(fop);
String str = "我爱 China";
//因为fop是字节流类的实例对象,所以要将字符串转化为字节数组,然后对字节数组进行输出
byte[] bytes = str.getBytes();
bos.write(bytes);
//因为字节缓冲输出流BufferedOutputStream操作文件需要缓冲区,所以要么关闭字节流才能将
//数据输出到文件当中,要么不关闭流使用flush()刷新缓冲区输出数据
bos.close();
fop.close();
//法二:去掉bos.close(),用bos.flush()
bos.flush();
}
4、字符流中常用的类
* 字符流的类通常以Reader或Writer结尾
(1)常用的字符输入流:
* Reader
* InputStreamReader
* FileReader
* BufferedReader
[1] Reader:
是字符输入流的抽象基类,不能实例化。其定义的常用方法如下:
read():每次读取单个字符,返回结果是一个int型数据,需要转化成char类型才能将文本内容显示在控制台。到达流的末尾时,返回-1,可以用while循环判断是否到末尾从而读取多个字符。
read(char[] cbuf):读取cbuf的长度个字符到cbuf数组中,需要转化成String类型才能将字符数组中的文本内容显示在控制台,返回结果是读取到的字符数,到达流末尾时,返回-1。
close():关闭流,释放资源。
[2] InputStreamReader:
可以把InputStream中的字节数据流根据字符编码方式转换成字符数据流(即InputStreamReader是字节流通向字符流的桥梁,将字节流转化为字符流),它需要一个字节输入流对象作为实例化参数,还可以指定第二个参数,作为字符编码方式,该参数可以是字符编码的字符串形式(即双引号形式),也可以是一个字符集对象。
例:
public static void main(String[] args) throws IOException{
// TODO Auto-generated method stub
FileInputStream fis = new FileInputStream("text1.txt");
//以InputStream的子类FileInputStream的对象作为参数,第二个参数gbk可以去掉
InputStreamReader isr = new InputStreamReader(fis,"gbk");
char[] ch = new char[1024];
int len;
while((len = isr.read(ch)) != -1) {
System.out.print((new String(ch,0,len)));
}
isr.close();
fis.close();
}
[3] FileReader:
可以读取文件中的数据,需要一个文件对象作为实例化参数,可以是File类对象,也可以是文件的路径字符串。此外,它还可以把FileInputStream中的字节数据根据编码方式转换成字符数据。
例:
public static void main(String[] args) throws IOException{
// TODO Auto-generated method stub
File f= new File("text1.txt");
FileReader fr = new FileReader(f);
int ch;
while((ch = fr.read()) != -1) {
System.out.print((char)ch);
}
fr.close();
}
[4] BufferedReader:
字符缓冲输入流,可以将数据保存到缓冲区中,提高读取效率。它需要一个字符输入流对象作为实例化参数(注意不能是文件对象),通常,也用BufferedReader封装别的流对象以提高效率。除了可以使用基类定义的方法外,它自身还定义了如下方法:
readLine():读取一个文本行,以行结束符作为末尾,返回结果是读取的字符串,所以可直接输出显示在控制台。如果已到达流末尾,则返回null。
例:
public static void main(String[] args) throws IOException{
// TODO Auto-generated method stub
File f= new File("text1.txt");
FileReader fr = new FileReader(f);
BufferedReader br = new BufferedReader(fr);
String line;
while((line = br.readLine()) != null) {
System.out.println(line);
}
br.close();
fr.close();
}
注:
BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
通常作为推荐的控制台获取标准输入的写法。
(2)常用的字符输出流:
* Writer
* OutputStreamWriter
* FileWriter
* BufferedWriter
[1] Writer:
是字符输出流的抽象基类,不能实例化,其定义了以下常用函数:
write(char[] cbuf):往输出流写入一个字符数组
write(int c):往输出流写入一个字符
write(String str):往输出流写入一个字符串
write(String str,int off,int len):往输出流写入字符串的一部分
close():关闭流,释放资源
flush():刷新输出流,将缓冲区中的数据立即写入文件(只在BufferedWriter中用)
[2] OutputStreamWriter:
可以直接往输出流中写入字符串数据(直接调用基类定义的write(String str)方法),它可以帮我们根据字符编码方式来把字符数据转换成字节数据再写入输出流(即它相当于字符流通向字节流的桥梁,将字符流转化为字节流)。它需要一个输出流对象作为实例化参数。
例:
public static void main(String[] args) throws IOException{
// TODO Auto-generated method stub
OutputStream ops = new FileOutputStream("text.txt");
OutputStreamWriter opsw = new OutputStreamWriter(ops);
opsw.write("你好 中国");
opsw.close();
}
[3] FileWriter:
也可以直接往流中写入字符数据,FileWriter内部会根据字节编码方式来把字符数据转换成字节数据再写给输出流。它需要一个文件对象来实例化,可以是File类对象,也可以是文件的路径字符串。
例:
public static void main(String[] args) throws IOException{
// TODO Auto-generated method stub
FileWriter fw = new FileWriter("text.txt");
fw.write("你好 小红果");
fw.close();
}
[4] BufferedWriter:
字符缓冲输出流,可以将数据写到缓冲区中,利用缓冲区来提高写的效率。它需要一个字符输出流对象作为实例化参数。通常,也用BufferedWriter封装别的流对象以提高效率。
例:
public static void main(String[] args) throws IOException{
// TODO Auto-generated method stub
FileWriter fw = new FileWriter("text.txt");
BufferedWriter bw = new BufferedWriter(fw);
bw.write("你好 吴奕杭");
bw.close();
fw.close();
}
注:
BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(System.out));
通常作为推荐的标准输出的方式。
5、总结
(1)java读写文件,实际上分别是将文件中的字节流形式的内容转换成字符流输出到屏幕和将字符流形式的内容转换成字节流形式存储到文件中。
Reader用于读取文件内容,所以InputSteamReader用于将字节流转化为字符流;
Writer用于将字符写入到文件中,所以OutputStreamWriter用于将字符流转化为字节流。
(2)字节流类中的BufferedInputStream、BufferedOutputStream以及所有字符流类都需要用到缓冲区,这些类中如果是输出文件流类,则只有在刷新流(调用flush方法)或关闭流(调用close方法)之后才会将数据输出到文件中。
(3)close()方法也会刷新缓冲区,从而将数据输出到文件中。它与flush()方法的区别在于close方法执行之后流就关闭了,而flush方法执行之后流还开着。
(4)仅针对输出流来说,字符流类的输出方法write(String)可以直接输出字符串;而字节流类需要调用getBytes()方法将字符串转化为字节形式,然后用输出方法write(byte[] bytes)进行输出。
(5)