1. IO概述

  • IO流用来处理设备之间的数据传输
  • Java对数据的操作是通过流的方式
  • Java用于操作流的对象都在IO包中
  • 流按操作对象分为两种:字节流与字符流,字节流可以操作任何数据,字符流只能操作纯字符数据比较方便。
  • 流按流向分为:输入流,输出流。

2. IO流常用基类

2.1 字节流的抽象基类:

InputStream ,OutputStream

2.2 字符流的抽象基类

Reader,Writer

2.3 由这四个类派生出来的子类名称都是以其父类名作为子类名的后缀。

  • 如:InputStream的子类FileInputStream。
  • 如:Reader的子类FileReader。
  • InputStreamReader是Reader的子类

3. 字符流的操作

3.1 读取文件

1
2
3
4
5
6
//定义字符流关联指定文件
FileReader reader = new FileReader("Test.txt");
//读取一个字符,返回int,该字符的码表值
int ch = reader.read();
//关闭流,释放资源
reader.close();

3.2 写出文件

1
2
3
4
5
6
//定义字符输出流关联指定文件
FileWriter writer = new FileWriter("Test.txt");
//写出一个字符,接收int码表值
writer.write(97);
//关闭流,释放资源
writer.close();

3.3 注意事项

  • 文件路径
    • 定义文件路径时Windows中的目录符号为“\”,但这个符号在Java中是特殊字符,需要转义。可以用“\”或“/”表示。
  • 读取文件
    • 读取文件时必须保证文件存在,否则将抛出FileNotFoundException。
  • 写出文件
    • 写出时文件如不存在时程序会创建新文件,如文件已存在则会清空原文件内容重新写入。
    • 如需追加内容可调用FileWriter构造函数FileWriter(String fileName, boolean append)

4. 字符流缓冲区的读写操作

4.1 自定义缓冲区读写

  • 为什么定义缓冲区
    • 由于单个字符读写需要频繁操作文件,所以效率非常低。我们可以定义缓冲区将要读取或写出的数据缓存,减少操作文件次数。
  • 缓冲区读取
    • 先定义一个数组,然后调用FileReader读取一个数组的方法。int read(char[] cbuf)
  • 缓冲区写出
    • 将要写出的数据存放在数组中,调用FileWriter方法,一次写出一个数组。void write(char[] cbuf, int off, int len)

4.2 内置缓冲区的BufferedReader和BufferedWriter

  • Java提供了带缓冲功能的Reader和Writer类:BufferedReader,BufferedWriter
  • 这两个类都是提供包装功能,需要提供其他流来使用,给其他流增加缓冲功能
  • 当我们调用BufferedReader读取数据时,程序会从文件中一次读取8192个字符用来缓冲
  • 当我们调用BufferedWriter写出数据时,程序会先将数据写出到缓冲数组,直到写满8192个才一次性刷出到文件
  • 特有方法 newLine(), readLine()

注意:BufferedReader 读取时 不会读取换行符,所以写入时会出现乱序,在写入后加newLine();可以解决

==Eg1.==
需求:在硬盘上,创建一个文件并写入一些文字数据。找到一个专门用于操作文件的Writer子类对象。FileWriter。 后缀名是父类名。 前缀名是该流对象的功能。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
import java.io.*;
class FileWriterDemo
{
public static void main(String[] args) throws IOException
{
//创建一个FileWriter对象。该对象一被初始化就必须要明确被操作的文件。
//而且该文件会被创建到指定目录下。如果该目录下已有同名文件,将被覆盖。
//其实该步就是在明确数据要存放的目的地。
FileWriter fw = new FileWriter("demo.txt");

//调用write方法,将字符串写入到流中。
fw.write("abcde");

//刷新流对象中的缓冲中的数据。
//将数据刷到目的地中。
//fw.flush();


//关闭流资源,但是关闭之前会刷新一次内部的缓冲中的数据。
//将数据刷到目的地中。
//和flush区别:flush刷新后,流可以继续使用,close刷新后,会将流关闭。
fw.close();
}
}

==Eg2.==

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
/*
演示对已有文件的数据续写。
*/
import java.io.*;
class FileWriterDemo3
{
public static void main(String[] args) throws IOException
{

//传递一个true参数,代表不覆盖已有的文件。并在已有文件的末尾处进行数据续写。
FileWriter fw = new FileWriter("demo.txt",true);

fw.write("nihao\r\nxiexie");

fw.close();
}
}

==Eg.3==

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
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
//将C盘一个文本文件复制到D盘。

/*
复制的原理:
其实就是将C盘下的文件数据存储到D盘的一个文件中。

步骤:
1,在D盘创建一个文件。用于存储C盘文件中的数据。
2,定义读取流和C盘文件关联。
3,通过不断的读写完成数据存储。
4,关闭资源。
*/

import java.io.*;

class CopyText
{
public static void main(String[] args) throws IOException
{
copy_2();
}


public static void copy_2()
{
FileWriter fw = null;
FileReader fr = null;
try
{
fw = new FileWriter("SystemDemo_copy.txt");
fr = new FileReader("SystemDemo.java");

char[] buf = new char[1024];

int len = 0;
while((len=fr.read(buf))!=-1)
{
fw.write(buf,0,len);
}
}
catch (IOException e)
{
throw new RuntimeException("读写失败");

}
finally
{
if(fr!=null)
try
{
fr.close();
}
catch (IOException e)
{
}
if(fw!=null)
try
{
fw.close();
}
catch (IOException e)
{
}
}
}
//从C盘读一个字符,就往D盘写一个字符。
public static void copy_1()throws IOException
{
//创建目的地。
FileWriter fw = new FileWriter("RuntimeDemo_copy.txt");

//与已有文件关联。
FileReader fr = new FileReader("RuntimeDemo.java");

int ch = 0;

while((ch=fr.read())!=-1)
{
fw.write(ch);
}

fw.close();
fr.close();

}
}

5. 装饰设计模式

5.1 什么情况下使用装饰设计模式

当我们需要对一个类的功能进行改进、增强的时候

5.2 装饰模式的基本格式。

  • 含有被装饰类的引用
  • 通过构造函数传入被装饰类对象
  • 和被装饰类含有同样的方法,其中调用被装饰类的方法,对其进行改进、增强
  • 和被装饰类继承同一个类或实现同一个接口,可以当做被装饰类来使用

5.3 了解BufferedReader、BufferedWriter的原理。

BufferedReaderBufferedWriter都是装饰类,他们可以装饰一个ReaderWriter,给被装饰的Reader和Writer提供缓冲的功能。就像我们用BufferedReaderBufferedWriter装饰FileReaderFileWriter,使用的读写功能还是FileReaderFileWriter的,但给这两个类的读写添加了缓冲功能。

6 字节流

基本操作与字符流相同,字节流可以操作任意类型数据

6.1 自定义缓冲区读写

  • 原理和字符流相同,都是为了提高效率
  • 定义数组缓冲数据,一次读取一个数组,一次写出一个数组,减少操作文件的次数

6.2 BufferedInputStream、BufferedOutputStream

  • BufferedReaderBufferedWriter原理相同,都是包装类
  • BufferedInputStreamBufferedOutputStream包装InputStream``和OutputStream提供缓冲功能

6.3 FileOutputStream

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
public class IoDemo03 {
public static void main(String[] args) {
FileOutputStream outputStream=null;
try {
//创建源
//选择流
outputStream = new FileOutputStream("test.txt",true);
//操作
String s="i love you hunan";
byte[] bytes=s.getBytes(); //字符==>字节 编码
outputStream.write(bytes,0,bytes.length);
FileInputStream inputStream = new FileInputStream("test02.txt");
byte[] bytes1=new byte[1024];
int read ;
while ((read=inputStream.read(bytes1))!=-1) {
String str=new String(bytes1,0,read); //字节==>字符 解码
System.out.print(str );
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
if (outputStream!=null){
try {
outputStream.close();
} catch (IOException e) {
e.printStackTrace();
}

}
}
}
}

6.4 FileInputStream

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
public class IoDemo01 {
public static void main(String[] args) {
//1.确定源 file 或者 字节数组流
File file=new File("test.txt");
//System.out.println(file.getAbsolutePath());
//2.选择流
InputStream inputStream=null;
try {
inputStream=new FileInputStream(file);
//3.操作
int temp=0;
while((temp=inputStream.read())!=-1){
System.out.print((char) temp);
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}finally{
if(inputStream!=null){
try {
inputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
//4.释放资源
}
}
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
public class IoDemo02 {
public static void main(String[] args) {
//1.确定源
File file =new File("test.txt");
//2.选择流
FileInputStream inputStream=null;
try {
//3.操作
inputStream = new FileInputStream(file);
byte[] bytes = new byte[1024]; //缓冲区
int temp;
while((temp=inputStream.read(bytes))!=-1){
System.out.print(new String(bytes));
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
//4.释放资源
}finally{
try {
inputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}

7. 转换流

字符流与字节流之间的桥梁,方便了字符流与字节流之间的操作,字节流中的数据都是字符时,转成字符流操作更高效
==注==:转换流的输出流可以指定编码表
InputStreamReader,OutputStreamWriter

8. 标准输入,输出

System类中的成员变量:inout

  1. 它们各代表了系统标准的输入和输出设备。
  2. 默认输入设备是键盘,输出设备是显示器。
  3. System.in的类型是InputStream.
  4. System.out的类型是PrintStream是OutputStream的子类FilterOutputStream 的子类.

9. 流操作小结


通过三个明确来完成。
1,明确源和目的。
​ 源:输入流。InputStream Reader
​ 目的:输出流。OutputStream Writer。
2,操作的数据是否是纯文本。
​ 是:字符流。
​ 不是:字节流。

3,当体系明确后,在明确要使用哪个具体的对象。
​ 通过设备来进行区分:
​ 源设备:内存,硬盘。键盘
​ 目的设备:内存,硬盘,控制台。

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
Eg.
1,将一个文本文件中数据存储到另一个文件中。复制文件。
源:因为是源,所以使用读取流。InputStream Reader
是不是操作文本文件。
是!这时就可以选择Reader
这样体系就明确了。

接下来明确要使用该体系中的哪个对象。
明确设备:硬盘。上一个文件。
Reader体系中可以操作文件的对象是 FileReader

是否需要提高效率:是!。加入Reader体系中缓冲区 BufferedReader.


FileReader fr = new FileReader("a.txt");
BufferedReader bufr = new BufferedReader(fr);


目的:OutputStream Writer
是否是纯文本。
是!Writer。
设备:硬盘,一个文件。
Writer体系中可以操作文件的对象FileWriter。
是否需要提高效率:是!。加入Writer体系中缓冲区 BufferedWriter

FileWriter fw = new FileWriter("b.txt");
BufferedWriter bufw = new BufferedWriter(fw);
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
练习:将一个图片文件中数据存储到另一个文件中。复制文件。要按照以上格式自己完成三个明确。


---------------------------------------

2,需求:将键盘录入的数据保存到一个文件中。
这个需求中有源和目的都存在。
那么分别分析
源:InputStream Reader
是不是纯文本?是!Reader

设备:键盘。对应的对象是System.in.
不是选择Reader吗?System.in对应的不是字节流吗?
为了操作键盘的文本数据方便。转成字符流按照字符串操作是最方便的。
所以既然明确了Reader,那么就将System.in转换成Reader。
用了Reader体系中转换流,InputStreamReader

InputStreamReader isr = new InputStreamReader(System.in);

需要提高效率吗?需要!BufferedReader
BufferedReader bufr = new BufferedReader(isr);

目的:OutputStream Writer
是否是存文本?是!Writer。
设备:硬盘。一个文件。使用 FileWriter。
FileWriter fw = new FileWriter("c.txt");
需要提高效率吗?需要。
BufferedWriter bufw = new BufferedWriter(fw);


**************
扩展一下,想要把录入的数据按照指定的编码表(utf-8),将数据存到文件中。

目的:OutputStream Writer
是否是存文本?是!Writer。
设备:硬盘。一个文件。使用 FileWriter。
但是FileWriter是使用的默认编码表。GBK.

但是存储时,需要加入指定编码表utf-8。而指定的编码表只有转换流可以指定。
所以要使用的对象是OutputStreamWriter。
而该转换流对象要接收一个字节输出流。而且还可以操作的文件的字节输出流。FileOutputStream

OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("d.txt"),"UTF-8");

需要高效吗?需要。
BufferedWriter bufw = new BufferedWriter(osw);

所以,记住。转换流什么使用。字符和字节之间的桥梁,通常,涉及到字符编码转换时,
需要用到转换流。