前言

b站java课程学习笔记整理。

b站视频: 黑马程序员全套Java教程_Java基础入门视频教程,零基础小白自学Java必备教程

279.IO流概述和分类

  • IO是输入和输出。流时一种抽象的概念,指的是对数据传输的总称。
  • IO流就是用来处理设备间数据传输问题的(例如文件复制,上传和下载)。

把数据从硬盘上加载到内存上,这个动作就是对应的输入:读数据。

把内存中的内容输出到硬盘上,这个动作对应的是写数据。 一般来说,IO流分类是按照数据类型来分的。

分为字符流和字节流:

如果数据可以通过记事本打开,那么就可以使用字节流,如果打不开,那么就使用字符流。默认使用字节流。

280.字节流写数据

字节流抽象类:

  • InputSteam: 这个抽象类是表示子节输入流的所有类的超类。
  • OutputStream: 这个抽象类是表示字节输出流的所有类的超类。
  • 子类名特点:子类名称都是以其父类名作为子类名的后缀。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
package FileOutputStreamDemo;

import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;

public class FileOutputStreamDemo {
public static void main(String[] args) throws IOException {
FileOutputStream fos = new FileOutputStream("idea_test\\FileOutputStreamDemo.txt");
/* 做了三件事:
1.调用系统功能创建了文件。
2.创建了字节输出流对象。
3.让字节输出流对象指向创建好的文件。*/
//写入ASCII码为97的字符‘a’
fos.write(97);
// 写入字符9和7
fos.write(57);
fos.write(55);

//最后释放资源
fos.close();
}
}

总结:

  1. 创建字节输出流对象
  2. 写入
  3. 释放资源

281.字节流写数据的三种方式

方法名 说明
void write(int b) 将指定的子节写入此文件输出流。一次写入一个字节数据
void write(byte[] b) 将b.length子节从指定的字节数组写入此文件输出流,一次写一个字节数组数据
void write(byte[] b, int off, int len) 从off位置开始,写入长度为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
package FileOutputStreamDemo;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.charset.StandardCharsets;

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

// 两种构造方法
FileOutputStream fos = new FileOutputStream("idea_test\\FileOutputStreamDemo.txt");

File file = new File("idea_test\\FileOutputStreamDemo2.txt");
FileOutputStream fos2 = new FileOutputStream(file);

// 输出
fos.write(97);
fos.write(98);
fos.write(99);
fos.write(100);

byte[] bytes = {97,98,99,100};
//fos2.write(bytes);

String line = "abcdefg";
byte[] byte2 = line.getBytes(StandardCharsets.UTF_8);
// fos2.write(byte2);

fos2.write(byte2,1,3);


// 释放资源
fos.close();
fos2.close();

}
}

282.字节流写数据的换行和追加写入

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
package FileOutputStreamDemo;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.charset.StandardCharsets;

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

// 两种构造方法
FileOutputStream fos = new FileOutputStream("idea_test\\FileOutputStreamDemo.txt");
for(int i = 0; i < 10; i++){
fos.write("hello".getBytes(StandardCharsets.UTF_8));
fos.write("\n".getBytes(StandardCharsets.UTF_8));
}

fos.close();

//可以追加写入
FileOutputStream fos2 = new FileOutputStream("idea_test\\FileOutputStreamDemo2.txt",true);
for(int i = 0; i < 10; i++){
fos2.write("hello".getBytes(StandardCharsets.UTF_8));
fos2.write("\n".getBytes(StandardCharsets.UTF_8));
}

fos2.close();
}
}


老师说windows的记事本是/r/n,linux是/n,mac是/r,但是我windows也可以识别/n……

283.字节流写数据加异常处理

finally:在异常处理时会提供finally块来执行所有清除操作,比如IO流中的释放资源。

特点: 被finally控制的语句一定会执行,除非JVM退出。

标准格式:

1
2
3
4
5
6
7
try{
可能出现异常的代码
}catch(异常类名 变量名){
异常的处理代码;
}finally{
执行所有清除操作;
}

示例代码

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
package FileOutputStreamDemo;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.charset.StandardCharsets;

public class FileOutputStreamDemo {
public static void main(String[] args) {

// 两种构造方法
FileOutputStream fos = null;
try {
//fos = new FileOutputStream("Z:\\idea_test\\FileOutputStreamDemo.txt");
fos = new FileOutputStream("idea_test\\FileOutputStreamDemo.txt");
fos.write("hello".getBytes(StandardCharsets.UTF_8));
fos.write("\n".getBytes(StandardCharsets.UTF_8));

} catch (IOException e) {
e.printStackTrace();
} finally{
if(fos!= null){
try {
fos.close();
} catch (IOException e) {
e.printStackTrace();
}
}


}



}
}

284.字节流读数据

需求:把txt文件内容读取出来在控制台

和写数据流程一样,都是三步。

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
package FileInputStreamDemo;

import java.io.FileInputStream;
import java.io.IOException;

public class FileInputStreamDemo {
public static void main(String[] args) throws IOException {
FileInputStream fis = new FileInputStream("idea_test\\FileOutputStreamDemo.txt");

// 第一次读取一个数据
int read = fis.read();
System.out.println((char)read);

// 第二次读取一个数据
read = fis.read();
System.out.println((char)read);

System.out.println("-------------------");

// 读取所有的数据,文件到达末尾,返回值为-1
/* int by = fis.read();
while (by != -1){
System.out.println((char)by);
by = fis.read();
}*/

// 优化上面的程序
int by;
while((by = fis.read())!=-1){
System.out.println((char)by);
}


fis.close();
}
}

这个是可以读换行的。

285.字节流复制文本文件

需求:把rookie文件夹下的rookie.txt复制到theshy文件夹下。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
package 复制文本文件;

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;

public class CopyDemo {
public static void main(String[] args) throws IOException {
String source = "E:\\Javacode\\JavaSE Code\\rookie\\rookie.txt";
String destination = "E:\\Javacode\\JavaSE Code\\theshy\\theshy.txt";

FileInputStream fis = new FileInputStream(source);
FileOutputStream fos = new FileOutputStream(destination);

int by;
while((by=fis.read())!=-1){
fos.write(by);
}
fis.close();
fos.close();
}
}

注意:用字节流时目标路径一定要指向文件,而不是文件夹。否则会抛出拒绝访问的报错。

286.字节流读数据(一次读一个字节数组的数据)

现在有文本文档内容为:
目标文本文档

如果执行如下代码:

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
package 字节流读取字节数组数据;

import java.io.FileInputStream;
import java.io.IOException;

public class FileOutputStreamRead {

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

FileInputStream fis = new FileInputStream("idea_test\\FileOutputStreamDemo.txt");

byte[] bys = new byte[5];

// 第一次读取
int len = fis.read(bys);
System.out.println(len);
System.out.println(new String(bys));

// 第二次读取
len = fis.read(bys);
System.out.println(len);
System.out.println(new String(bys));

// 但三次读取
len = fis.read(bys);
System.out.println(len);
System.out.println(new String(bys));
fis.close();

}


}

返回的是一串很奇怪的东西:
控制台输出

原理:
文本文档的真实内容是

1
2
hello\n
world\n\n

len是实际读取的fis的数据个数。
定义的数组长度为5。可以理解为 [$ $ $ $ $] ;

第一次读取,len = 5,即读取到了五个字符,得到的字符数组为[h e l l o];

第二次读取,len = 5, 即读取到了5个字符,得到的字符数组为[\n w o r l];

第三次读取, len = 3, 即读取到了3个字符,得到的字符数组为[d \n \n r l];

第三次只读到了d和两个 \n ,只能替换之前[\n w o r l]里面的前三个字符,就变成了这样。

改进:

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
package 字节流读取字节数组数据;

import java.io.FileInputStream;
import java.io.IOException;

public class FileOutputStreamRead {

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

FileInputStream fis = new FileInputStream("idea_test\\FileOutputStreamDemo.txt");

byte[] bys = new byte[5];

// 第一次读取
int len = fis.read(bys);
System.out.println(len);
System.out.println(new String(bys,0,len));

// 第二次读取
len = fis.read(bys);
System.out.println(len);
System.out.println(new String(bys,0,len));

// 但三次读取
len = fis.read(bys);
System.out.println(len);
System.out.println(new String(bys,0,len));
fis.close();

}


}

使用String方法,读取几个,转成几个字符串;

使用循环的终极改进:

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
package 字节流读取字节数组数据;

import java.io.FileInputStream;
import java.io.IOException;

public class FileOutputStreamRead {

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

FileInputStream fis = new FileInputStream("idea_test\\FileOutputStreamDemo.txt");

// 一般给1024及其整数倍
byte[] bys = new byte[1024];
int len;
while ((len = fis.read(bys))!=-1){
System.out.println(new String(bys, 0, len));
}
fis.close();


}


}

0是offset,也就是读取的偏移值。

byte数组一般定义为1024或者1024的整数倍长度。

287.字节流复制图片

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
package 字节流复制图片;

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;

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

String source = "E:\\Javacode\\JavaSE Code\\rookie\\rookie.jpg";
String destination = "E:\\Javacode\\JavaSE Code\\theshy\\rookie.jpg";


FileInputStream fis = new FileInputStream(source);
FileOutputStream fos = new FileOutputStream(destination);

byte[] bys = new byte[1024];
int len;
while((len = fis.read(bys))!=-1){
fos.write(bys,0,len);
}

fis.close();
fos.close();
}
}

所以文本文档用字节来复制,图片用字节数组来复制。

288.字节缓冲流

为字节流提供一个缓冲区,说人话就是”凑够一车人再发车“,可以有效地”省油“。

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
package 字节缓冲流;

import java.io.*;
import java.nio.charset.StandardCharsets;

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

FileOutputStream fos = new FileOutputStream("idea_test\\FileOutputStreamDemo.txt");
// 直接封装了一个大小为8192字节的数组。
BufferedOutputStream bos = new BufferedOutputStream(fos);

bos.write("hello\r\n".getBytes(StandardCharsets.UTF_8));
bos.write("world\r\n".getBytes(StandardCharsets.UTF_8));

//fos.close();
bos.close();


FileInputStream fis = new FileInputStream("idea_test\\FileOutputStreamDemo.txt");
// 直接封装了一个大小为8192字节的数组。
BufferedInputStream bis = new BufferedInputStream(fis);
/* int by;
while((by = bis.read())!=-1){
System.out.println((char)by);
}*/
//fis.close();
//bis.close();

byte[] bys = new byte[1024];
int len;
while((len = bis.read(bys))!=-1){
System.out.println(new String(bys,0,len));
}
//bis.close();

}
}

方法和之前的非缓冲区一样。这个的作用是可以更快的读取和写入。

289.字节流复制视频

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
package 字节流复制视频;

import java.io.*;

public class CopyVideo {
public static void main(String[] args) throws IOException {
String source = "E:\\Javacode\\JavaSE Code\\rookie\\沙皇一推五.webm";
String destination = "E:\\Javacode\\JavaSE Code\\theshy\\沙皇一推五.webm";

BufferedInputStream bis = new BufferedInputStream(new FileInputStream(source));
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(destination));

byte[] bys = new byte[1042];
int len;
while( (len = bis.read(bys))!=-1){
bos.write(bys,0,len);
}

bis.close();
bos.close();

}
}

这种方式在复制大型文件(例如视频)时快很多。