JAVA基础学习-IO流

本文最后更新于:December 3, 2021 pm

Java的核心库java.io提供了全面的IO接口。包括:文件读写、标准设备输出等。Java中IO是以流为基础进行输入输出的,所有数据被串行化写入输出流,或者从输入流读入。在整个Java.io包中最重要的就是5个类和一个接口。5个类指的是File、OutputStream、InputStream、Writer、Reader;一个接口指的是Serializable。

目录

1.IO流

1.1 分类

  1. 按方向
  • 输入流:将 存储设备 中的内容读入到 内存 中。

  • 输出流:将 内存 中的内容写入到 存储设备 中。

  1. 按单位
  • 字节流:以字节为单位,可以读写所有数据 。

  • 字符流:以字符为单位,只能读写文本数据。

  1. 按功能
  • 节点流:具有实际传输数据的读写功能。

  • 过滤流:在节点流的基础之上增强功能。

2.字节流

字节流的父类为抽象类。

2.1 字节输入流(InputStream)

InputStream就是Java标准库提供的最基本的输入流。它位于java.io这个包里。java.io包提供了所有同步IO的功能。InputStream并不是一个接口,而是一个抽象类,它是所有输入流的超类。这个抽象类定义的一个最重要的方法就是int read()。这个方法会读取输入流的下一个字节,并返回字节表示的int值(0~255)。如果已读到末尾,返回-1表示不能继续读取了。

  • int read():从该输入流读取一个字节的数据。返回字节的ASCII码。

  • int read(byte[] b):从该输入流读取最多 b.length个字节的数据为字节数组。返回读取的字节数。

  • int read(byte[] b, int off, int len):从该输入流读取最多 len字节的数据为字节数组。

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
//===================================================
//单个字节读取
public static void main(String[] args) throws Exception{
InputStream is = new FileInputStream("src/main/java/com/IO/dataTeas.txt");
int mi = 0;
while((mi=is.read())!=-1){
System.out.print(mi);
System.out.println((char)mi);
}
is.close();
}

//输出结果
113q
101e
119w
114r



//===================================================
//一次读取多个字节
public static void main(String[] args) throws Exception{
InputStream is = new FileInputStream("src/main/java/com/IO/dataTeas.txt");
byte[] by = new byte[3];
int cnt = is.read(by);
System.out.println(cnt);
System.out.println(new String(by));

cnt = is.read(by);
System.out.println(cnt);
System.out.println(new String(by));

cnt = is.read(by);
System.out.println(cnt);
System.out.println(new String(by));

/*
cnt = is.read(by);
System.out.println(cnt);
System.out.println(new String(by,0,cnt));
*/
cnt = is.read(by);
System.out.println(cnt);
System.out.println(new String(by));

is.close();
}

//输出结果
3
qwe
3
rty
3
uio
/*
1
p
*/
1
pio


//简写
public static void main(String[] args) throws Exception{
InputStream is = new FileInputStream("src/main/java/com/IO/dataTeas.txt");
byte[] by = new byte[3];
int cnt = 0;
while(( cnt = is.read(by)) != -1 ){
System.out.println(cnt);
System.out.println(new String(by,0,cnt));
}

is.close();
}

//输出结果
3
qwe
3
rty
3
uio
1
p

2.2 字节输出流(OutputStream)

  • FileOutputStream(String name):创建文件输出流以指定的名称写入文件。

  • FileOutputStream(String name, boolean append):创建文件输出流以指定的名称写入文件,append表示是否追加。

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
//===================================================
//写入单个字符
OutputStream os = new FileOutputStream("src/main/java/com/IO/dataTest.txt");
os.write(98);
os.write('c');
os.write('q');
os.close();

//写入结果
bcq



//===================================================
//写入字符串
OutputStream os = new FileOutputStream("src/main/java/com/IO/dataTest.txt");
String tt = "qwertruyiop";
os.write(tt.getBytes()); //获取字符串的字节数组
os.close();

//写入结果
qwertruyiop


//===================================================
//追加写入
//再次运行上述代码
OutputStream os = new FileOutputStream("src/main/java/com/IO/dataTest.txt",true); //注意区别
String tt = "qwertruyiop";
os.write(tt.getBytes());
os.close();

//写入结果
qwertruyiopqwertruyiop

2.3 演示示例(复制功能)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public static void main(String[] args) throws Exception{

FileInputStream fis = new FileInputStream("src/main/java/com/IO/binary.jpg"); //读入源文件
FileOutputStream fos = new FileOutputStream("src/main/java/com/IO/binary2.jpg"); //写入新文件
byte[] by = new byte[1024];
int cnt = 0 ;
//边读入边写入
while((cnt = fis.read(by))!=-1){
fos.write(by,0,cnt);
}
fis.close();
fos.close();
System.out.println("OK");
}

3.字节缓冲流

  1. BufferedInputStream
1
2
3
4
5
6
7
8
9
10
11
12
13
public static void main(String[] args) throws Exception{

FileInputStream fis = new FileInputStream("src/main/java/com/IO/dataTest.txt"); //输入节点流
BufferedInputStream bis = new BufferedInputStream(fis); //缓冲流
int cnt = 0 ;
while((cnt = bis.read())!=-1){
System.out.print((char)cnt);
}
bis.close(); //可以不再写fis.close()了,bis.close()内部已经有了
}

//输出结果
qwertruyiopqwertruyiop
  1. BufferedOutputStream
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public static void main(String[] args) throws Exception{
FileOutputStream fos = new FileOutputStream("src/main/java/com/IO/dataTest3.txt");
BufferedOutputStream bos = new BufferedOutputStream(fos);
String s = "abcdef\r\n";
for(int i = 1 ; i <= 3 ; ++i ){
bos.write(s.getBytes());
bos.flush();//如果不写flush(),则写入数据在没有超过缓冲区大小的情况下,文本中是没有数据的,因为都先存在了缓冲区中。而flush()就是用来刷新缓冲区的。
}
bos.close(); //其实,上面的flush(),也可以不写,因为bos.close()内部已经有了flush()。但在上面写可以保证数据不丢失。在这里的flush()是在全部存入后再写入文本。
}

//dataTest3.txt
abcdef
abcdef
abcdef

4.序列化与反序列化

序列化是指把一个Java对象(类)变成二进制内容,本质上就是一个byte[]数组。反序列化,即把一个二进制内容(也就是byte[]数组)变回Java对象。

一个Java对象要能序列化,必须实现一个特殊的java.io.Serializable接口。Serializable接口没有定义任何方法,它是一个空接口。我们把这样的空接口称为“标记接口”(Marker Interface),实现了标记接口的类仅仅是给自身贴了个“标记”,表明可以序列化,其并没有增加任何方法。

4.1 序列化

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
//========================== Person.class
package com.Serializable;

import java.io.Serializable;

/**
* @Author DragonOne
* @Date 2021/8/6 19:13
*/
public class Person implements Serializable { //必须实现接口,否则会报错
public String name ;
public int age ; //序列化类中的对象属性也要实现Serializable接口,比如是其他类时

public Person(String name, int age) {
this.name = name;
this.age = age;
}

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

public int getAge() {
return age;
}

public void setAge(int age) {
this.age = age;
}

@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}


//================================= 主函数
public static void main(String[] args) throws Exception{
FileOutputStream fos = new FileOutputStream("src/main/java/com/Serializable/test.bin");
ObjectOutputStream oos = new ObjectOutputStream(fos);
//序列化(写入操作)
Person pp = new Person("zhangsan",23);
oos.writeObject(pp);

oos.close(); //包含flush()操作
}

4.2 反序列化

1
2
3
4
5
6
7
8
9
10
11
12
public static void main(String[] args) throws Exception{
FileInputStream fis = new FileInputStream("src/main/java/com/Serializable/test.bin");
ObjectInputStream ois = new ObjectInputStream(fis);
//反序列化(读取)
Person pp = (Person) ois.readObject();
//Person pp1 = (Person) ois.readObject(); //如果再一次执行本句,则会报错
ois.close();
System.out.println(pp.toString());
}

//输出结果
Person{name='zhangsan', age=23}

注意事项:

    1. 序列化类必须要实现Serializable接口。
    1. 序列化类中对象属性要求实现Serializable接口。
    1. 序列化版本号ID,保证序列化的类和反序列化的类是同一个类。
    1. 使用transient(瞬间)修饰属性,这个属性不能序列化。即不会保存。
    1. 静态属性不能序列化。即static修饰的。
    1. 序列化多个对象时,可以借助集合实现。

5.字符流

5.1 输入流(Reader)

FileReader是Reader的一个子类,它可以打开文件并获取Reader。
FileReader默认的编码与系统相关。例如,Windows系统的默认编码可能是GBK,打开一个UTF-8编码的文本文件就会出现乱码。若要避免乱码问题,我们需要在创建FileReader时指定编码:Reader reader = new FileReader(“src/readme.txt”, StandardCharsets.UTF_8);

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
//dataTest.txt
/*
你好世界helloworld!
*/


//单个字符读取
public static void main(String[] args) throws Exception{
FileReader fr = new FileReader("src/main/java/com/IO/dataTest.txt");
int cnt = 0;
while((cnt = fr.read())!=-1){
System.out.print((char)cnt);
}
System.out.println();
fr.close();
}


//利用缓冲区读取
public static void main(String[] args) throws Exception{
FileReader fr = new FileReader("src/main/java/com/IO/dataTest.txt");
char[] ca = new char[1024];
int cnt = 0;
while((cnt = fr.read(ca))!=-1){
System.out.print(new String(ca,0,cnt));
}
System.out.println();
fr.close();
}



//输出结果均为
你好世界helloworld!

5.2 输出流(Writer)

FileWriter就是向文件中写入字符流的Writer。它的使用方法和FileReader类似。

1
2
3
4
5
6
7
8
9
10
11
12
public static void main(String[] args) throws Exception{
FileWriter fw = new FileWriter("src/main/java/com/IO/wdata.txt");
for ( int i = 1 ; i < 3 ; ++i){
fw.write("这是一个写入样例");
fw.flush();
}
System.out.println("OK");
fw.close();
}

//wdata.txt
这是一个写入样例这是一个写入样例

5.3 演示示例(复制功能)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
//不能复制图片或二进制文件,因为是字符
public static void main(String[] args) throws Exception{
FileReader fr = new FileReader("src/main/java/com/IO/wdata.txt");
FileWriter fw = new FileWriter("src/main/java/com/IO/wdata77.txt");
int cnt = 0;
while((cnt = fr.read()) != -1){
fw.write(cnt);
fw.flush();
}
fw.close();
System.out.println("OK");
}

//wdata.txt
这是一个写入样例这是一个写入样例
//wdata77.txt
这是一个写入样例这是一个写入样例

6.字符缓冲流

1
2
3
//wdata.txt
这是一个写入样例
这是一个写入样例
  1. BufferedReader
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
//方法一
public static void main(String[] args) throws Exception{
FileReader fr = new FileReader("src/main/java/com/IO/wdata.txt");
BufferedReader br = new BufferedReader(fr);

char[] ca = new char[1024];
int cnt = 0;
while((cnt = br.read(ca))!=-1){
System.out.println(new String(ca,0,cnt));
}

br.close();
}



//=============================================
//方法二 一行一行读取

public static void main(String[] args) throws Exception{
FileReader fr = new FileReader("src/main/java/com/IO/wdata.txt");
BufferedReader br = new BufferedReader(fr);

String st = null;
while((st = br.readLine())!=null){
System.out.println(st);
}

br.close();
}



//输出均为
这是一个写入样例
这是一个写入样例
  1. BufferedWriter
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public static void main(String[] args) throws Exception{
FileWriter fw = new FileWriter("src/main/java/com/IO/wwdata.txt");
BufferedWriter bw = new BufferedWriter(fw);
for(int i = 1 ; i < 5 ; ++i ){
bw.write("好好学习,天天向上"); //可以手动加上换行符,但不推荐
bw.newLine();//写入一个换行,兼容性更好。根据不同的系统自动写入不同的换行符。
bw.flush();
}
bw.close();
}

//wwdata.txt
好好学习,天天向上
好好学习,天天向上
好好学习,天天向上
好好学习,天天向上

7.打印流

PrintWriter 与 PrintStream 同样的用法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public static void main(String[] args) throws Exception{
PrintWriter pw = new PrintWriter("src/main/java/com/IO/wwdata.txt");
pw.println(23);
pw.println('v');
pw.println(true);
pw.println(3.243);
pw.close();
}

//wwdata.txt
23
v
true
3.243

本文作者: 墨水记忆
本文链接: https://tothefor.com/DragonOne/179604172.html
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!