黑马程序员-Java I/O流(一)-IO流概述、字符流、字节流、流操作规律

时间:2023-02-17 18:00:58

——Java培训、Android培训、iOS培训、.Net培训、期待与您交流! ——-

1. 概述
流:可以理解数据的流动,就是一个数据流。Java中与IO相关的类有很多,都集中在java.io中,都是以流的形式操作的,它的本质是传输数据。
IO流分类:
(1)根据数据类型的不同可以分为字节流和字符流。
(2)根据流向的不同可以分为输入流和输出流。

Java 中所有IO流的关系如下图:
黑马程序员-Java I/O流(一)-IO流概述、字符流、字节流、流操作规律

主要的操作流程如下:
1使用File类打开一个文件
2 通过字节流或字符流的子类,指定输出的位置
3 进行读/写操作
4 关闭输入/输出

2. 字符流 Reader Writer
以Unicode字符为导向的stream,表示以Unicode字符为单位从stream中读取或往stream 中写入信息。
Reader/Writer 为abstact类

Reader:以Unicode字符为导向的流,以Unicode字符为单位从流中读取,是一个抽象类。子类必须实现的方法只有 read(char[], int, int) 和 close()。
Reader的常用读取操作:
1.int read() 读取单个字符。
2.int read(char[] cbuf) 将字符读入数组。
3.abstract int read(char[] cbuf, int off, int len) 将字符读入数组的某一部分。

在使用Reader进行数据流的读入操作时是不会直接创建Reader对象的,一般都是使用其子类,如FileReader,示例代码如下:

import java.io.*;
class FileReaderDemo {
public static void main(String[] args) throws IOException {
//创建可以读取文本文件的流对象,FileReader让创建好的流对象和指定的文件相关联。

//读取demo.txt数据,一次读一个字符,返回字符的十进制形式。
FileReader fr = new FileReader("demo.txt");
int ch = 0;
while((ch = fr.read())!= -1) { //条件是没有读到结尾
System.out.println((char)ch); //调用读取流的read方法,读取一个字符。
}
fr.close();
}
}

读取的第二种形式是添加一个自定义缓冲区,这种方法比较高效。

import java.io.*;
class FileReaderDemo {
public static void main(String[] args) throws IOException {
//读取demo.txt数据。
FileReader fr = new FileReader("demo.txt");
//设定一个字符数组为缓冲区,使用read(char[])方法,将读取到字符存入数组,字符数组的长度都是1024的整数倍。
char[] buf = new char[1024];//缓冲区
int len = 0;
while(( len=fr.read(buf)) != -1) {
System.out.println(new String(buf,0,len));
}
fr.close();
}
}

上面代码需要注意是,不管是流的创建,打开,写入,读取,刷新,关闭等操作,一般都会抛出IOException,因为对磁盘进行操作(一般都是)都有可能产生错误,如磁盘满了,文件被占用等等,所以必须对其进行捕获,并处理。流的关闭一般放在try中的finally中,原因是为防止出错后无法及时释放资源。

Writer : 以Unicode字符为导向的流,以Unicode字符为单位往流中写入,是一个抽象类。子类必须实现的方法仅有 write(char[], int, int)、flush() 和 close()。
Writer的常用写入操作:
1.abstract void flush() 刷新该流的缓冲。
2.void write(char[] cbuf) 写入字符数组。
3.void write(int c) 写入单个字符。
4.void write(String str) 写入字符串。
5.void write(String str, int off, int len) 写入字符串的某一部分。

在使用Writer进行数据流的读入操作时是不会直接创建Writer对象的,一般都是使用其子类,如FileWriter,示例代码如下:

import java.io.*;
class FileDWriterdemo {

public static void main(String[] args) {
/* 创建一个FileWriter对象,该对象一被初始化就必须要明确被操作的文件。
而且该文件会被创建到指定目下, 其实该步骤就是在明确数据要存放的目的地。 */

FileWriter fw = null;
try {
fw = new FileWriter("demo.txt",true);//传递一个true参数,代表不覆盖已有的文件,并在已有文件的末尾处进行数据续写。
fw.write("aaa");//调用write()方法,将字符串写入到流中。
}
catch (IOException e ){
System.out.println(e.toString()+"....");
}
finally{
if(fw!=null)
try{
fw.close();
}
catch (IOException e){
System.out.println("close:"+e.toString());
}
}
}

需要注意的是:流的关闭一般放在try中的finally中,原因是为防止出错后无法及时释放资源。在进行数据的写入时,当执行完write方法后,数据可能不会立即被写入到目的地,这时可以使用flush功能进行立即完成写入功能,也可以在close时自定完成内容的写入。

字符缓冲流BufferedWriter和BufferedReader:
为了提程序效率,java设置了Writer和Reader对应的类:BufferedReader和BufferedWriter,下面是一个示例:

import java.io.*;
class BufferedDemo {
public static void main(String[] args) {
BufferedWriter bw = null;
try {
//创建一个写入流对象,并且用缓冲流对象指定该写入流
bw = new BufferedWriter(new FileWriter("file.txt"));

bw.write("Test");//将"test"写入
bw.newLine();//写入换行标志
bw.flush();
} catch (IOException e) {
// IO异常在这里处理
} finally {
try {
// 在finally中关闭流,并判断是否为null
if(bw != null) {
bw.close();
}
} catch (IOException e) {}
}
}
}

3. 字节流 InputStream OutputStream

字符流操作的是纯文本内容,而字节流则是所有二进制文件都可以操作,如图片,视频,当然文件文件也是可以的。与字符流中读出和写入的类型为char型相对应,字节流读出和写入的是byte类型。字节流的两个基本输入输出流为InputStream和OutputStream,其功能与字符流的功能类似。对于文件的操作的流是相应的FileInputStream和FileOutputStream,下面通过一个图片拷贝功能作为字节流的一个示例代码:

import java.io.*;
class putStreamDemo {
public static void main(String[] args) throws IOException {

FileInputStream fin = new FileInputStream("pic1.png");
FileOutputStream fout = new FileOutputStream("pic2.png");

// 与Reader不同的是这里使用的是byte类型
byte[] buf = new byte[1024];
int n = 0;

while((n=fin.read(buf)) != -1) {
fout.write(buf, 0, n);//字节流写入
}

// 关闭流
fin.close();
fout.close();
}
}

我们注意到字节流不能直接写入,需要转化成字节数组进行写入和输出。

字节缓冲流BufferedInputStream和BufferedOutputStream:

import java.io.*;
class Demo {
public static void main(String[] args) throws IOException {

BufferedInputStream bi =
new BufferedInputStream(new FileInputStream("pic1.png"));
BufferedOutputStream bo =
new BufferedOutputStream(new FileOutputStream("pic2.png"));

int data = 0;

while((data = bi.read()) != -1) {
bo.write(data);
}

bi.close();
bo.close();
}
}

未加入缓冲机制时候,每次read()都会调用系统底层读取磁盘的操作,读取一个字节,非常耗时;而加入缓冲机制后,系统会一次将很多内容读取到内存,而调用read()时,只需要从内存中返回数据内容即可,大大减少了系统底层访问磁盘的次数,所以速度会加快很多。

读取转换流和写入转换流 InputStreamReader和OutputStreamWriter:
为了方便了字符流与字节流之间的操作,当我们读取键盘(System.in)数据时,便可以使用InputStreamReader,当我们将数据输出到控制台(System.out),便可以使用OutputStreamWriter,示例代码如下:

import java.io.*;
class Demo {
public static void main(String[] args) throws IOException {
// 键盘的最常见写法。
BufferedReader bfr =
new BufferedReader(new InputStreamReader(System.in));

// 使用字符输出方式到控制台
BufferedWriter bfw =
new BufferedWriter(new OutputStreamWriter(System.out));

String line = null;
while((line=bfr.readLine()) != null) {
bfw.write(line.toUpperCase());//将输入的字符转成大写字符输出
bfw.newLine();
bfw.flush();
}
bfr.close();
bfw.close();
}
}

4. 流操作规律 :
(1)明确源和目的。
数据源:就是需要读取,可以使用两个体系:InputStream、Reader;
数据汇:就是需要写入,可以使用两个体系:OutputStream、Writer;
(2)操作的数据是否是纯文本数据?
如果是:数据源:Reader
数据汇:Writer
如果不是:数据源:InputStream
数据汇:OutputStream
(3)虽然确定了一个体系,但是该体系中有太多的对象,到底用哪个呢?
明确操作的数据设备。
数据源对应的设备:硬盘(File),内存(数组),键盘(System.in)
数据汇对应的设备:硬盘(File),内存(数组),控制台(System.out)。
(4)需要在基本操作上附加其他功能吗?比如缓冲。
如果需要就进行装饰。