02-字节流读写

image-20230316140530530

1. 字节输入输出类

image-20230316140547448

2. 抽象父类:InputStream & OutputStream

InputStream字节输入流:

1
2
3
public abstract class InputStream
extends Object
implements Closeable

这个抽象类是表示输入字节流的所有类的超类。需要定义InputStream子类的应用InputStream必须始终提供一种返回输入的下一个字节的方法。

1
2
3
4
5
6
abstract int read()
从输入流读取数据的下一个字节。
int read(byte[] b)
从输入流读取一些字节数,并将它们存储到缓冲区 b 。
int read(byte[] b, int off, int len)
从输入流读取最多 len字节的数据到一个字节数组。

OutputStream字节输出流:

1
2
3
public abstract class OutputStream
extends Object
implements Closeable, Flushable

这个抽象类是表示字节输出流的所有类的超类。 输出流接收输出字节并将其发送到某个接收器。需要定义OutputStream子类的应用OutputStream必须至少提供一个写入一个字节输出的方法。

1
2
3
4
5
6
void write(byte[] b)
将 b.length字节从指定的字节数组写入此输出流。
void write(byte[] b, int off, int len)
从指定的字节数组写入 len个字节,从偏移 off开始输出到此输出流。
abstract void write(int b)
将指定的字节写入此输出流。

2.1 实现类:字节节点流 FileOutputStream & FileInputStream

  • FileOutputStream类(字节节点输出流):
1
2
public class FileOutputStream
extends OutputStream

核心方法:

1
2
3
4
5
6
void write(int b)
将指定的字节写入此文件输出流。
void write(byte[] b)
将 b.length个字节从指定的字节数组写入此文件输出流。
void write(byte[] b, int off, int len)
将 len字节从位于偏移量 off的指定字节数组写入此文件输出流。

构造方法:

1
2
3
4
5
6
7
8
9
10
FileOutputStream(File file)
创建文件输出流以写入由指定的 File对象表示的文件。
FileOutputStream(File file, boolean append)
创建文件输出流以写入由指定的 File对象表示的文件,append==true追加,不覆盖。
FileOutputStream(FileDescriptor fdObj)
创建文件输出流以写入指定的文件描述符,表示与文件系统中实际文件的现有连接。
FileOutputStream(String name)
创建文件输出流以指定的名称写入文件。
FileOutputStream(String name, boolean 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
public class TestFileOutputStream {
public static void main(String[] args) throws IOException {
// 1.输出字节流 OutputStream
// 2.输出字节节点流,具有实际传输数据的功能,boolean append == true不覆盖原文件内容,追加写入
// 3.路径正确,文件不存在,会自动创建文件
// 4.使用相对路径,相对于当前项目的路径,寻找路径和文件
FileOutputStream fos = new FileOutputStream(".\\files\\target.txt", true);

try {
String s = "你";
fos.write(s.getBytes()); // 写入中文

fos.write(65); // 写入字符
fos.write(66);
fos.write(67);
fos.write('D');
byte[] bs = new byte[] {65, 66, 67, 68, 69, 'Z'}; // 写入字符数组
fos.write(bs);
System.out.println("OK");
} catch (IOException e) {
e.getStackTrace();
} finally {
fos.close();
}
}
}
  • FileInputStream类(字节节点输入流):
1
2
public class FileInputStream
extends InputStream

核心方法:

1
2
3
4
5
6
int read()
从该输入流读取一个字节的数据。
int read(byte[] b)
从流中读取多个字节,将读到内容存入b数组,返回实际读到的字节数;如果达到文件尾部,返回-1
int read(byte[] b, int off, int len)
从该输入流读取最多 len字节的数据为字节数组。

构造方法:

1
2
3
4
5
6
FileInputStream(File file)
通过打开与实际文件的连接创建一个 FileInputStream ,该文件由文件系统中的 File对象 file命名。
FileInputStream(FileDescriptor fdObj)
创建 FileInputStream通过使用文件描述符 fdObj ,其表示在文件系统中的现有连接到一个实际的文件。
FileInputStream(String name)
通过打开与实际文件的连接来创建一个 FileInputStream ,该文件由文件系统中的路径名 name命名。

示例代码:

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 class TestFileInputStream {
public static void main(String[] args) throws IOException {
FileInputStream fis = new FileInputStream(".\\files\\target.txt");
try {
// 逐个字节读入
// while (true) {
// int n = fis.read();
// if (n < 0) { // -1, 文件结尾EOF
// break;
// }
// System.out.println((char)n);
// }

// 按照数组读入
byte[] bs = new byte[4];
while(true) {
int count = fis.read(bs); // count:每次读取到的有效字节个数
if (count < 0) { // -1, 文件结尾EOF
break;
}
// 读多少个,输出多少个
for (int i = 0; i < count; i++) {
System.out.print((char)bs[i]);
}
System.out.println();
}

//read(bs, off, len); // 读入bs数组中,从off下标开始,每次读入len

} catch (Exception e) {
e.printStackTrace();
} finally {
fis.close();
}
}
}

2.2 实现类:字节过滤流(缓冲流)BufferedOutputStream & BufferedInputStream

特点:

  • 提高IO效率,减少访问磁盘次数;
  • 数据存储在缓冲区中,flush是讲缓存区的内容写入文件中,也可以直接close;
1
2
3
4
5
public class BufferedOutputStream
extends FilterOutputStream

public class BufferedInputStream
extends FilterInputStream

示例演示:

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 class TestBufferredOutput {
public static void main(String[] args) throws IOException {
// 输出节点流
FileOutputStream fos = new FileOutputStream("files\\buffer.txt");
// 增强节点流:有参构造需要一个字节输出节点流
BufferedOutputStream bos = new BufferedOutputStream(fos);
try {
// 过滤流的write方法,是先写入到缓冲区数组里
bos.write('A');
bos.write('B');
bos.write('C');
// 刷新缓冲区:将缓冲区的数据,或一次性写入文件中,并清空当前缓冲区
bos.flush();
bos.write('E');
// 每次均需刷新缓冲区
//bos.flush();
} catch (Exception e) {
e.printStackTrace();
} finally {
// .close() 级联的执行了flush();释放了资源的同时,将缓冲器的数据一次性写入到文件中
bos.close();
System.out.println("写入OK");
}

// 输入字节流 - 普通的即可
FileInputStream fis = new FileInputStream("files\\buffer.txt");
//BufferedInputStream bis = new BufferedInputStream(fis);
byte[] b = new byte[100];
fis.read(b);
for (int i = 0; i < b.length; i++) {
System.out.println((char)b[i]);
}

fis.close();
}
}

2.3 实现类:字节过滤流(对象流)ObjectOutputStream & ObjectInputStream - 【最佳】

1
2
3
4
5
6
7
public class ObjectOutputStream
extends OutputStream
implements ObjectOutput, ObjectStreamConstants

public class ObjectInputStream
extends InputStream
implements ObjectInput, ObjectStreamConstants

特点:

  • 增强了缓冲区功能
  • 增强了读写8种基本数据类型和字符串功能
  • 增强了读写对象的功能:
    1. Object readObject() // 从流中读取一个对象
    2. void writeObject(Object obj) // 向流中写入一个对象
  • 对象序列化:
    使用流传输对象的过程称为序列化、反序列化。
  1. 必须实现Serializable接口
  2. 对象自身和类中属性都必须序列化(即实现Serializable这个空接口即可,基本数据类型数组可不序列化,引用数据类型必须序列化);
  3. transient 关键字修饰为临时属性,不参与序列化;
  4. 对象的默认序列化机制写入对象的类、类签名和所有非瞬态和非静态字段的值,因此属性不能使用static修饰,否则取的都是最后一次的值(static属于类本身,会影响序列化)。
  5. 序列化对象流读取到文件尾,会抛出 EOFException 异常 - 捕获后停止读取文件
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
public class TestObjectStream {
public static void main(String[] args) throws IOException, ClassNotFoundException {
// 写
OutputStream os = new FileOutputStream("files\\target.txt");
ObjectOutputStream oos = new ObjectOutputStream(os);
Student stu = new Student("小明", 12, "男", 100D);
Student stu1 = new Student("小黑", 13, "男", 60D);
oos.writeObject(stu); // NotSerializableException: 类实现Serializable接口即可
oos.writeObject(stu1);
oos.flush();
oos.close();
// 读
InputStream is = new FileInputStream("files\\target.txt");
ObjectInputStream ois = new ObjectInputStream(is);
while (true) {
try {
Object obj = ois.readObject();
System.out.println(obj);
} catch (EOFException e) {
// 到达文件尾,抛出EOFException文件尾异常
break;
}
}
ois.close();
}
}
// 对象需要实现序列化
// 属性也需要实现序列化
class Student implements Serializable {
private static final long serialVersionUID = 1L;
/*static*/String name; // static为类所有,会影响该属性值变为一个相同的值
Integer age;
String sex;
transient Double score; // transient修饰的属性不参与序列化,均为null
public Student() {
super();
}
public Student(String name, Integer age, String sex, Double score) {
super();
this.name = name;
this.age = age;
this.sex = sex;
this.score = score;
}
@Override
public String toString() {
return "Student [name=" + name + ", age=" + age + ", sex=" + sex + ", score=" + score + "]";
}
}

3. 读写文件

1
2
3
4
5
6
7
BufferedInputStream bis = new BufferedInputStream(srcfile.getInputStream());
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(dstPath));
byte[] bs = new byte[8*1024];
int size;
while ((size = bis.read(bs)) != -1) {
bos.write(bs, 0, size);
}

image-20230316140616372

4. 拷贝图片

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public class TestFileIOcopy {
public static void main(String[] args) throws IOException {
// 拷贝一张图片
FileInputStream fis = new FileInputStream("C:\\Users\\Administrator\\Desktop\\test.jpg");
FileOutputStream fos = new FileOutputStream("files\\new.jpg");
int data = 0;

try {
// 无参的read()返回的是数据
while ((data = fis.read()) >= 0) {
System.out.println(data);
fos.write(data);
}
} finally {
fis.close();
fos.close();
}
}
}

02-字节流读写
https://janycode.github.io/2016/04/28/02_编程语言/01_Java/01_JavaSE/06_文件和流/02-字节流读写/
作者
Jerry(姜源)
发布于
2016年4月28日
许可协议