IO流分类:

1. 输入流(读)和输出流(写)。

2. 因为处理的数据不同,分为字节流和字符流。

close()和flush()的区别:

  • flush():将缓冲区的数据刷到目的地中后,流可以使用。
  • close():将缓冲区的数据刷到目的地中后,流就关闭了,该方法主要用于结束调用的底层资源。这个动作一定做。

流的操作规律

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


字节流: InputStream OutputStream

适合操作非文本文件

处理字节数据的流对象。设备上的数据无论是图片或者dvd,文字,它们都以二进制存储的。二进制的最终都是以一个8位为数据单元进行体现,所以计算机中的最小数据单元就是字节。意味着,字节流可以处理设备上的所有数据,所以字节流一样可以处理字符数据。

输入流:

基类:InputStream(抽象)

实现类: FileInputStream
fi
1
2
3
4
5
6
>##### 读取:
```java
int i;
while((i=fi.read())!=-1){
System.out.print((char)i);
}

输出流:

基类:OutputStream(抽象)

实现类: FileOutputStream

fo
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34

#### 字符流读写文件:
```java
private static void method2(String src, String dest) throws IOException {

//1,指定数据源

FileInputStream in = new FileInputStream(src);

//2,指定目的地

FileOutputStream out = new FileOutputStream(dest);

//3,读数据

byte[] buffer = new byte[1024];

int len = -1;

while ( (len=in.read(buffer)) != -1) {

//4,写数据

out.write(buffer, 0, len);

}

//5,关闭流

in.close();

out.close();

}

【注】

  1. 路径: C:/a.txt -> C:\\a.txt
  2. false: 不覆盖文件,true:更新覆盖原文件
  3. 原文件不存在则新建
  4. 路径可由 File 对象代替


字符流: Reader Writer

适合操作文本文件

字符每个国家都不一样,所以涉及到了字符编码问题,那么GBK编码的中文用unicode编码解析是有问题的,所以需要获取中文字节数据的同时+ 指定的编码表才可以解析正确数据。为了方便于文字的解析,所以将字节流和编码表封装成对象,这个对象就是字符流。只要操作字符数据,优先考虑使用字符流体系。

输入流:Reader

实现类:FileReader

1
2
3
4
5
6
7
8
9
10
11
12
13
14
//自定义缓冲区
import java.io.*;
class FileReaderDemo2 {
public static void main(String[] args) throws IOException {
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();
}
}

输出流:Writer

实现类:FileWriter



缓冲流: Buffer

字节缓冲流:

BufferedInputStream

BufferedOutputStream

字符缓冲流 高效 读取文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
private static void method4(String src, String dest) throws IOException {

//1,指定数据源

BufferedInputStream in = new BufferedInputStream(new FileInputStream(src));

//2,指定目的地

BufferedOutputStream out = new BufferedOutputStream(new FileOutputStream(dest));

//3,读数据

byte[] buffer = new byte[1024];

int len = -1;

while ( (len = in.read(buffer)) != -1) {

//4,写数据

out.write(buffer, 0, len);

}
//5,关闭流

in.close();

out.close();

}

字符缓冲流:

BufferedReader(读)

1
2
3
4
5
6
7
FileReader fr = new FileReader("bufdemo.txt");
BufferedReader bufr = new BufferedReader(fr);
String line = null;
while((line=bufr.readLine())!=null){ //readLine方法返回的时候是不带换行符的。
System.out.println(line);
}
bufr.close();

BufferedWriter(写)

1
2
3
4
5
6
7
8
FileWriter fw = new FileWriter("bufdemo.txt");
BufferedWriter bufw = new BufferedWriter(fw);//让缓冲区和指定流相关联。
for(int x=0; x<4; x++){
bufw.write(x+"abc");
bufw.newLine(); //写入一个换行符,这个换行符可以依据平台的不同写入不同的换行符。
bufw.flush();//对缓冲区进行刷新,可以让数据到目的地中。
}
bufw.close();//关闭缓冲区,其实就是在关闭具体的流。

字符缓冲流读写文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
public class CopyTextFile {

public static void main(String[] args) throws IOException {

//1,指定数据源, 是数据源中读数据,采用输入流

BufferedReader in = new BufferedReader(new FileReader("file.txt"));

//2,指定目的地,是把数据写入目的地,采用输出流

BufferedWriter out = new BufferedWriter(new FileWriter("copyFile.txt"));

//3,读数据

String line = null;

while ( (line = in.readLine()) != null ) {

out.write(line);//4,写数据

out.newLine();//写入换行符号

}

//5,关闭流

out.close();

in.close();

}

}


转换流: InputStreamReader OutputStreamWriter

转换流特有功能

  • 转换流可以将字节转成字符,原因在于,将获取到的字节通过查编码表获取到指定对应字符。

==凡是操作设备上的文本数据,涉及编码转换,必须使用转换流。==

转换流 InputStreamReader

【注意】:在读取指定的编码的文件时,一定要指定编码格式,否则就会发生解码错误,而发生乱码现象。

代码演示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
public class InputStreamReaderDemo {

public static void main(String[] args) throws IOException {

//演示字节转字符流的转换流

readCN();

}

public static void readCN() throws IOException{

//创建读取文件的字节流对象

InputStream in = new FileInputStream("c:\\cn8.txt");

//创建转换流对象

//InputStreamReader isr = new InputStreamReader(in);这样创建对象,会用本地默认码表读取,将会发生错误解码。

InputStreamReader isr = new InputStreamReader(in,"utf-8");

//使用转换流去读字节流中的字节

int ch = 0;

while((ch = isr.read())!=-1){

System.out.println((char)ch);

}

//关闭流

isr.close();

}

}

转换流 OutputStreamWriter

【注意】:在写出指定的编码的文件时,一定要指定编码格式,否则读取时编码格式不同可能发生乱码现象。

将字符串按照指定的编码表转成字节,在使用字节流将这些字节写出去。

代码演示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public static void writeCN() throws Exception {

//创建与文件关联的字节输出流对象

FileOutputStream fos = new FileOutputStream("c:\\cn8.txt");

//创建可以把字符转成字节的转换流对象,并指定编码

OutputStreamWriter osw = new OutputStreamWriter(fos,"utf-8");

//调用转换流,把文字写出去,其实是写到转换流的缓冲区中

osw.write("你好");//写入缓冲区。

osw.close();

}


打印流

PrintWriter

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
package com.qfedu.bhy.test_printwriter;

import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;
import java.text.SimpleDateFormat;
import java.util.Date;
/**
* 打印流输出异常日志信息
* @author bhy
*
*/
public class MyException extends Exception{

//设置异常信息
public String getMessage() {

return "异常信息--打印异常日志";
}
//定义异常时执行的方法
public void printException() {

PrintWriter pw = null;
FileWriter fw = null;
try {
fw = new FileWriter("F:/print.txt",true);//true:文件不覆盖
pw = new PrintWriter(fw);
} catch (IOException e) {
e.printStackTrace();
}finally {

this.printStackTrace(pw);//this.printStackTrace(PrintWriter s); 写出当前异常信息到指定打印流中
pw.print(new SimpleDateFormat("yyyy-MM-dd hh:mm:ss")
.format(new Date())+"\r\n");//追加时间信息到打印流中
//关闭流
if(fw!=null) {
try {
fw.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if(pw!=null) {
pw.close();
}
}
}

}

附上自定义异常使用代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
package com.qfedu.bhy.test_printwriter;

public class TestPrintWriter {

public static void main(String[] args){

try { /捕获异常
print();
} catch (MyException e) { //处理异常
e.printException();//调用自定义异常的异常处理方法
}
}
public static int print()throws MyException{//声明异常

int[] arr = new int[] {1,4,0,3};
int n = 0 ;

for (int i = 0; i < arr.length+1; i++) {
if(i > arr.length-1 || arr[i] == 0) {
throw new MyException();//抛出自定义异常对象
}else {
n = arr[i+1]/arr[i];
}
}
return n;
}

}


对象流

  • ObjectInputStream
  • ObjectOutputStream

对象流–实现序列化:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
import java.io.*;
class ObjectStreamDemo {
public static void main(String[] args) throws Exception{
writeObj();
readObj();
}
public static void readObj()throws Exception{
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("obj.txt"));
Object obj = ois.readObject();//读取一个对象。
System.out.println(obj.toString());
}
public static void writeObj()throws IOException{
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("obj.txt"));
oos.writeObject(new Person("lisi",25)); //写入一个对象。
oos.close();
}
}
class Person implements Serializable{
private static final long serialVersionUID = 42L;//生成一个序列化ID号
private transient String name;//用transient修饰后name将不会进行序列化
public int age;
Person(String name,int age){
this.name = name;
this.age = age;
}
public String toString(){
return name+"::"+age;
}
}