0%

Java的IO流学习笔记

前言

这个是个人在学习Java的IO流知识时所记录的笔记,涉及IO流的基础知识;

1. File类

1.1 File类基本概念

  1. File类的一个对象,代表一个文件或一个文件目录;
  2. File类声明在java.io包下;
  3. 创建实例的三种方式:
    • File(String filePath)
      ①相对路径:相较于某个路径下指明的路径
      ②绝对路径:包含盘符在内的文件或文件目录的路径
    • File(String parentPath, String childPath)
    • File(File parentPath, String childPath)
  4. File类的对象常会作为参数传递到流的构造器中,指明读取或写入的终点;

注意:涉及读取或写入文件内容必须使用IO流来完成;

1.2 File类常用方法

获取功能:

  1. String getAbsolutePath():获取绝对路径
  2. String getPath():获取路径
  3. String getName():获取名称
  4. String getParent():获取上层文件目录路径,若没有就返回null
  5. Long length():获取文件长度,不能获取目录的长度
  6. Long lastModified():获取最后一次的修改时间(ms)
  7. String[] list():获取指定目录下的所有文件或文件目录的名称数组
  8. File[] listFiles():获取指定目录下的所有文件或文件目录的File数组

重命名功能:

  1. boolean renameTo(File dest):把文件重命名为指定的文件路径

    1
    2
    Boolean renameTo = file1.renameTo(file2)
    // 注意:要想返回true,需要file1在硬盘中必须存在,且file2不能在硬盘中存在;

判断功能:

  1. boolean isDirectory():判断是否是文件目录
  2. boolean isFile():判断是否是文件
  3. boolean exists():判断是否存在
  4. boolean canRead():判断是否可读
  5. boolean canWrite():判断是否可写
  6. boolean isHidden():判断是否隐藏

创建和删除功能:

  1. boolean createNewFile():创建文件,若文件已存在返回false
  2. boolean mkdir():创建文件目录,上层目录不存在就不创建
  3. boolean mkdirs():创建文件目录,上层目录不存在就一并创建
  4. boolean delete():删除文件或文件夹

2. 节点流(文件流)

注意:字符流不能用来处理图片等字节数据,处理字节数据要用字节流;

总结:字节流和字符流的读写操作基本步骤一致,都是分为4步;

2.1 字节流

2.1.1 字节输入流:FileInputStream

FileInputStream读取非文本文件基本操作:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
1.实例化File类的对象,指明要操作的文件:
File file = new File(String filePath);

2.FileInputStream流的实例化,提供具体的流:
FileInputStream fis = new FileInputStream(file);

3.数据的读入操作
byte[] buffer = new byte[int];
int len; // 记录每次读取的字节的个数
while ((len = fr.read(buffer)) != -1) {
String str = new String(buffer, 0, len);
System.out.print(str);
}

4.流的关闭操作
if (fis != null) {
fis.close( );
}

// 注意:
1.对于文本文件(.txt, .java, .c, .cpp等)建议使用字符流处理,字节流处理可能出现乱码;
2.对于非文本文件(图片、视频等)必须使用字节流处理;

2.1.2 字节输出流:FileOutputStream

使用FileInputStream和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
1.实例化File类的对象,指明读入和写出的文件: 
File srcFile = new File(String filePath);
File destFile = new File(String filePath);

2.创建输入流和输出流的对象:
FileInputStream fis = new FileInputStream(srcFile);
FileOutputStream fos = new FileOutputStream(destFile);

3.数据的读入和写出操作 (复制过程)
byte[] buffer = new byte[int];
// 记录每次读入到buffer数组中的字符的个数
int len = fis.read(buffer);
while (len != -1) {
//每次写出len个字符,从0开始
fos.write(buffer, 0, len);
}

4.流资源的关闭操作
if (fis != null) {
fis.close( );
}
if (fos != null) {
fos.close( );
}

2.2 字符流

2.2.1 字符输入流:FileReader

FileReader读入数据基本操作步骤:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
1.实例化File类的对象,指明要操作的文件:
FileReader fr = null
File file = new File(String filePath);
//相对地址相较于当前Module

2.Filereader流的实例化,提供具体的流:
fr = new FileReader(file);
//读入的文件一定要存在,否则就会报FileNotFoundException

3.数据的读入操作
int data = fr.read( );
while (data != -1) {
System.out.println((char)data);
data = fr.read();
}
//read( ):返回读入的一个字符,如果到达文件末尾返回-1

4.流的关闭操作
if (fr != null) {
fr.close( );
}

//注意:为了保证流资源一定可以执行关闭操作,需要使用try-catch-finally处理,把close()放在finally块中

使用read的重载方法read(char[ ] cbuf)进行读入操作:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
char[] cbuf = new char[int];
int len = fr.read(cbuf);
while (len != -1) {
// 方式一:遍历char[]数组,注意此处i<len不可以写成 i<cbuf.length
for (int i=0; i<len; i++) {
System.out.print(cbuf[i]);
}

// 方式二:将char[]数组转换为str再遍历字符串,注意不可以只写cbuf,要指定范围
String str = new String(cbuf, 0, len);
System.out.print(str);
}

//read(char[ ] cbuf):返回每次读入cbuf数组中字符的个数,如果到达文件末尾返回-1

2.2.2 字符输出流:FileWriter

FileWriter写出数据基本操作步骤:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
1.实例化File类的对象,指明写出到的文件: 
FileWriter fw = null
File file = new File(String filePath);

2.FileWriter流的实例化,用于数据写出:
fw = new FileWriter(file);

3.数据的写出操作
fw.write("String");

4.流资源的关闭操作
if (fw != null) {
fw.close( );
}

// 说明:
1.输出操作,对应的File如果不存在,在输出的过程中会自动创建此文件;
2.对应的File如果存在,根据构造器不同:
FileWriter fw = new FileWriter(file)会对原有文件进行覆盖
FileWriter fw = new FileWriter(file, true)不会对原有文件进行覆盖,而是在原有文件基础上追加内容

使用FileReader和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
1.实例化File类的对象,指明读入和写出的文件: 
File srcFile = new File(String filePath);
File destFile = new File(String filePath);

2.创建输入流和输出流的对象:
FileReader fr = new FileReader(srcFile);
FileWriter fw = new FileWriter(destFile);

3.数据的读入和写出操作 (复制过程)
char[] cbuf = new char[int];
// 记录每次读入到cbuf数组中的字符的个数
int len = fr.read(cbuf);
while (len != -1) {
//每次写出len个字符,从0开始
fw.write(cbuf, 0, len);
}

4.流资源的关闭操作
if (fr != null) {
fr.close( );
}
if (fw != null) {
fw.close( );
}

3. 处理流

3.1 缓冲流

作用:内部提供了一个缓冲区,提高流的读取和写入的速度;(因此缓冲流读写速度快于文件流)

3.1.1 字节型

作用在InputStream上:BufferedInputStream

作用在OutputStream上:BufferedOutputStream

使用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
1.实例化File类的对象,指明读入和写出的文件:(造文件)
File srcFile = new File(String filePath);
File destFile = new File(String filePath);

2.创建输入流和输出流的对象:(造流)
// 先造内层的节点流
FileInputStream fis = new FileInputStream(srcFile);
FileOutputStream fos = new FileInputStream(destFile);
// 再造外层的缓冲流
BufferedInputStream bis = new BufferedInputStream(fis);
BufferedOutputStream bos = new BufferedOutputStream(fos);

3.数据的读入和写出操作 :(复制过程)
byte[] buffer = new byte[int];
int len;
while ((len = bis.read(buffer)) != -1) {
bos.write(buffer, 0, len);
}

4.流资源的关闭操作
// 要求:先关闭外层的流,再关闭内层的流(即造流先内后外,关流先外后内)
bos.close( );
bis.close( );
// 说明:关闭外层流的同时,内层流也会自动地进行关闭,关于内层流的关闭操作其实可以省略
fos.close( );
fis.close( );

3.1.2 字符型

作用在FileReader上:BufferedReader

作用在FileWriter上:BufferedWriter

使用BufferedReader和BufferedWriter实现文本文件复制:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
1.实例化File类的对象,指明读入和写出的文件:(造文件) 
File srcFile = new File(String filePath);
File destFile = new File(String filePath);

2.创建输入流和输出流的对象:(造流)
// 先造内层的节点流
FileReader fr = new FileReader(srcFile);
FileWriter fw = new FileWriter(destFile);
// 再造外层的缓冲流
BufferedReader br = new BufferedReader(fr);
BufferedWriter bw = new BufferedWriter(fw);

3.数据的读入和写出操作 :(复制过程)
char[] cbuf = new char[int];
int len;
while ((len = br.read(cbuf)) != -1) {
bw.write(cbuf, 0, len);
}

4.流资源的关闭操作
// 内层文件流会在外层缓冲流关闭时自动关闭,因此可以省略文件流的关闭步骤
br.close( );
bw.close( );

3.2 转换流

作用:提供了在字节流和字符流之间的转换,本身属于字符流;

将一个字节的输入流转换为字符的输入流(解码):InputStreamReader

1
2
3
4
5
6
7
8
9
10
11
12
13
// 造文件、造流
FileInputStream fis = new FileInputStream(new File(String filepath));

//参数2指明字符集,具体使用哪个字符集取决于文件保存时使用的字符集
InputStreamReader isr = new InputStreamReader(fis, "UTF-8")

char[] cbuf = new char[int];
int len;
while ((len = isr.read(cbuf)) != -1) {
System.out.print(new String(cbuf, 0, len));
}

isr.close();

将一个字符的输出流转换为字节的输出流(编码):OutputStreamWriter

切换字符集实现文件读入与写出:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
FileInputStream fis = new FileInputStream(new File(String path));
FileOutputStream fos = new FileOutputStream(new File(String path));


// InputStream -> Reader(字节流转换为字符流)
InputStreamReader isr = new InputStreamReader(fis, "UTF-8")
// Writer -> OutputStream(字符流转换为字节流)
OutputStreamWriter osw = new OutputStreamWriter(fos, "gbk")


char[] cbuf = new char[int];
int len;
while ((len = isr.read(cbuf)) != -1) {
osw.write(cbuf, 0, len);
}


isr.close();
osw.close();

3.3 对象流

作用:用于存储和读取基本数据类型数据或对象的处理流,可以把Java中的对象写入到数据源中,也能把对象从数据源中还原回来;

对象序列化机制:允许把内存中任何实现了Serializable接口的对象转换成与平台无关的二进制流,从而允许把这种二进制流持久地保存在磁盘上,或通过网络将这种二进制流传输到另一个网络节点。当其它程序获取了这种二进制流,就可以恢复成原来的Java对象;

ObjectOutputStream序列化:对象写入到数据源

1
2
3
4
5
6
7
8
9
10
11
1.造文件、造节点流和处理流
File file =new File("object.data")
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(file))

2.写出对象
oos.writeObject(new String("a example"));
//刷新操作
oos.flush( );

3.关闭流资源
oos.close( );

ObjectInputStream反序列化:从数据源还原对象

1
2
3
4
5
6
7
8
9
10
11
12
// 反序列化:将磁盘文件中的对象还原为内存中的一个Java对象

1.造文件、造节点流和处理流
File file =new File("object.data")
ObjectInputStream ois = new ObjectInputStream(new FileInputStream(file))

2.读取对象
String str = (String) ois.readObject( );
System.out.println(str);

3.关闭流资源
ois.close( );

注意:ObjectOutputStream和ObjectInputStream不能序列化static和transient修饰的成员变量

3.4 其他流

3.4.1 标准输入流、输出流

  • System.in:标准的输入流,默认从键盘输入

  • System.out:标准的输出流,默认从控制台输出

    注意:可以通过System类的setIn(InputStream is) / setOut(PrintStream ps)方式重新指定输入和输出的流;

3.4.2 打印流

作用:将基本数据类型的数据格式转化为字符串输出;

  • 字节输出流:PrintStream
  • 字符输出流:PrintWriter

3.4.3 数据流

作用:用于读取或写出基本数据类型的变量或字符串;

  • DateInputStream:将文件中存储的基本数据类型变量和字符串读取到内存中,保存在变量中
  • DateOutputStream:将内存中的字符串、基本数据类型的变量写出到文件中

注意:读取不同类型的数据的顺序要与当初写入文件时保存的数据的顺序一致;

3.4.4 随机存取文件流

  • RandomAccessFile直接继承于java.lang.Object类,实现了DataInput和DataOutput接口,既可作为输入流,也可作为输出流;
  • RandomAccessFile支持随机访问方式,程序可以直接跳到文件的任意地方读、写文件(由于其对象包含一个记录指针用以标示当前读写处的位置),支持只访问文件的部分内容,或者向已存在的文件后追加内容;
  • 如果RandomAccessFile作为输出流时,写出到的文件如果不存在则会在执行过程中自动创建,如果已经存在则会对原有文件内容从头覆盖;

RandomAccessFile实现数据的读写操作

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
//创建两个对象,一个用于写入,一个用于读取
/*
mode参数指定RandomAccessFile的访问模式:
访问模式有以下4种:
r:以只读方式打开
rw:打开以便读取和写入
rwd:打开以便读取和写入,并同步文件内容的更新
rws:打开以便读取和写入,并同步文件内容和元数据的更新
*/
RandomAccessFile raf1 = new RandomAccessFile(new File(String pathname, mode))
RandomAccessFile raf2 = new RandomAccessFile(new File(String pathname, mode))


byte[ ] buffer = new byte[int];
int len;
while ((len = raf1.read(buffer)) != -1) {
raf2.write(buffer, 0, len);
}


raf1.close();
raf2.close();

RandomAccessFile实现数据的插入操作

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
方法:void seek(long pos):将文件记录指针定位到角标为pos位置

如何实现插入新的数据而不会覆盖后面数据的效果?

//将指针调到角标为pos的位置
raf.seek(pos);
//保存指针pos后面的所有数据到StringBuilder中
StringBuilder builder = new StringBuilder((int) new File().length);

byte[ ] buffer = new byte[int];
int len;
while((len = raf.read(buffer)) != -1) {
builder.append(new String(buffer, 0, len));
}
//回调指针到pos,写入要插入到pos后的内容
raf.seek(pos);
raf.write("String".getBytes())
//将StringBuilder中保存的数据重新写入到文件中
raf.write(builder.toString().getBytes())