Java.io包

在整个Java.io包中最重要的就是5个类3个接口

说明
File 文件类
InputStream 字节输入流
OutputStream 字节输出流
Reader 字符输入流
Writer 字符输出流
Closeable 关闭流接口
Flushable 刷新流接口
Serializable 序列化接口

流的分类

按流的方向分类

输入/输出流的划分是相对程序而言的,

  • 从外面进来程序的,称为输入流
  • 从程序出去外面的,称为输出流

按处理的数据单元分类

  • 字节流:以字节为单位获取数据,命名上以Stream结尾的流一般是字节流,如FileInputStream、FileOutputStream。
  • 字符流:以字符为单位获取数据,命名上以Reader/Writer结尾的流一般是字符流,如FileReader、FileWriter。

按处理对象不同分类

  • 节点流:可以直接从数据源或目的地读写数据,如FileInputStream、FileReader、DataInputStream等。
  • 处理流:不直接连接到数据源或目的地,是处理流的流。通过对其他流的处理提高程序的性能,如BufferedInputStream、BufferedReader等。处理流也叫包装流。

.png

常用流

  1. InputStream/OutputStream:字节流的抽象类。
  2. Reader/Writer:字符流的抽象类。
  3. FileInputStream/FileOutputStream:节点流:以字节为单位直接操作“文件”
  4. ByteArrayInputStream/ByteArrayOutputStream:节点流:以字节为单位直接操作“字节数组对象”。
  5. ObjectInputStream/ObjectOutputStream:处理流:以字节为单位直接操作“对象”。
  6. DataInputStream/DataOutputStream:处理流:以字节为单位直接操作“基本数据类型与字符串类型”。
  7. FileReader/FileWriter:节点流:以字符为单位直接操作“文本文件”(注意:只能读写文本文件)。
  8. BufferedReader/BufferedWriter:处理流:将Reader/Writer对象进行包装,增加缓存功能,提高读写效率。
  9. BufferedInputStream/BufferedOutputStream:处理流:将InputStream/OutputStream对象进行包装,增加缓存功能,提高 读写效率。
  10. InputStreamReader/OutputStreamWriter:处理流:将字节流对象转化成字符流对象。
  11. PrintStream:处理流:将OutputStream进行包装,可以方便地输出字符,更加灵活。

File类

Java是不能直接与文件交互的,Java能做的就是指导操作系统其打开,处理文件

所以File对象可能是文件,可能是目录,还可能根本不存在

构造方法

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
import java.io.File;

public class student4 {
public static void main(String[] args) {
String path = "F:/Java/feng.jpg";

// 1.构建File对象
File src = new File(path);
System.out.println(src.length());

// 2.组合构建File对象
src = new File("F:/Java","feng.jpg");
src = new File("F:","Java/feng.jpg");
System.out.println(src.length());

// 3.使用File对象创建File对象
src = new File(new File("F:/Java"),"feng.jpg");
System.out.println(src.length());

// 4.创建不存在的文件
src = new File("F:/不存在的目录/不存在的文件.txt");
System.out.println(src.getAbsolutePath()); // F:\不存在的目录\不存在的文件.txt

System.out.println(System.getProperty("user.dir"));
}
}

文件常用方法

方法 说明
getName() getPath() getAbsolutePath() getParent() 文件名,路径名
exists() isFile() isDirectory() 判断状态
length() 文件长度
createNewFile() delete() 创建新文件 删除文件

相对路径是相对谁的路径?相对的是当前目录,也就是user.dir

1
System.out.println(System.getProperty("user.dir"));
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
import java.io.File;

public class student4 {
public static void main(String[] args) {
String path = "F:/Java/feng.jpg";
File src = new File(path);

System.out.println(src.getName()); // feng.jpg
System.out.println(src.getParent()); // F:\Java
System.out.println(src.getPath()); // F:\Java\feng.jpg
System.out.println(src.getAbsolutePath()); // F:\Java\feng.jpg

System.out.println(src.exists()); // true
System.out.println(src.isFile()); // true
System.out.println(src.isDirectory()); // false

System.out.println(src.length()); // 72213
}
}
1
2
3
4
5
6
7
8
9
public class student4 {
public static void main(String[] args) throws IOException {
String path = "F:/Java/feng.txt";
File src = new File(path);

boolean is_succeed = src.createNewFile();
System.out.println(is_succeed);
}
}

目录常用方法

API 说明
mkdir() mkdirs() 创建目录,如果父目录不存在则取消创建
创建目录,如果父目录不存在一同创建
list() 下级名称
listFiles() 下级File对象
listRoots() 根路径
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
import java.io.File;
import java.io.IOException;

public class student4 {
public static void main(String[] args) throws IOException {
String path = "F:/java";
File src = new File(path);

/*创建目录*/
boolean flag = src.mkdir();
System.out.println(flag);

/*以String形式展示目录下的文件和子目录*/
String[] subName = src.list();
for(String each : subName){
System.out.println(each);
}

/*以File形式展示目录下的文件和子目录*/
File[] subName2 = src.listFiles();
for(File each : subName2){
System.out.println(each);
}

/*展示所有的盘符*/
File[] roots = src.listRoots();
for(File each : roots){
System.out.println(each.getAbsoluteFile());
}
}
}

递归使用:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
import java.io.File;

public class student4 {
public static void main(String[] args) {
String path = "F:/乱七八糟/360安全浏览器下载/chrome/下载/messing/小说";
File src = new File(path);
ListAllFile(src,1);
}

public static void ListAllFile(File f ,int level) {
for(File sub : f.listFiles()){
if (sub.isDirectory()){
ListAllFile(sub,level+1);
}else{
for(int i=0 ; i < level ; i++){
System.out.print("-");
}
System.out.println(sub.getAbsoluteFile());
}
}

}
}

编码与解码

  • 编码 : getBytes(String CharsetName) : 返回字节数组
  • 解码 : String(byte[] bytes , int offset , int length , String CharsetName)

getBytes() : 默认使用工程的字符集。

1
2
3
4
5
6
7
8
9
10
11
12
import java.io.UnsupportedEncodingException;

public class student4 {
public static void main(String[] args) throws UnsupportedEncodingException {
String msg = "AAA";
byte[] datas = msg.getBytes();
System.out.println(datas.length);

String w = new String(datas,0,datas.length,"utf-8");
System.out.println(w);
}
}

出现乱码的原因:

  • 字节数不够
  • 字符集不统一

四大IO抽象类

InputStream/OutputStreamReader/writer类是所有IO流类的抽象父类

抽象类 说明 常用方法
InputStream 字节输入流的父类,数据单位为字节。 int read() void close()
OutputStream 字节输出流的父类,数据单位为字节。 void write(int) void flush() void close()
Reader 字符输入流的父类,数据单位为字符。 int read() void close()
Writer 字符输出流的父类,数据单位为字符。 void write(String) void flush() void close()

具体IO流

文件字节输入流FileInputStream和文件字节输出流FileOutputStream

FileInputStream

  • FileInputStream通过字节的方式读取文件,适合读取所有类型的文件(图像、视频、文本文件等)。Java也提供了FileReader专门读取文本文件。
  • FileOutputStream 通过字节的方式写数据到文件中,适合所有类型的文件。Java也提供了FileWriter专门写入文本文件。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public class TestIO1 {
public static void main(String[] args) {
try {
//创建输入流
FileInputStream fis = new FileInputStream("d:/a.txt"); // 文件内容是:abc
//一个字节一个字节的读取数据
int s1 = fis.read(); // 打印输入字符a对应的ascii码值97
int s2 = fis.read(); // 打印输入字符b对应的ascii码值98
int s3 = fis.read(); // 打印输入字符c 对应的ascii码值99
int s4 = fis.read(); // 由于文件内容已经读取完毕,返回-1
System.out.println(s1);
System.out.println(s2);
System.out.println(s3);
System.out.println(s4);
// 流对象使用完,必须关闭!不然,总占用系统资源,最终会造成系统崩溃!
fis.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}

常用手法:
每个字符挨个读取

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
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;

public class Testing2 {
public static void main(String[] args) {
// 1.创建源
File f = new File("F:/Java/hyl.txt");
// 2.选择流
FileInputStream is = null;
try {
is = new FileInputStream(f);
// 3. 操作(读取)
int temp;
while ((temp = is.read())!=-1){
System.out.println((char)temp);
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally{
try{
if (is!=null){
// 释放资源
is.close();
}
}catch(IOException e){
e.printStackTrace();
}
}
}
}

分段读取

1
2
3
4
5
6
7
8
9
is = new FileInputSream(src);
byte[] car = new byte[1024]; // 缓冲容器
int len = -1; // 接受长度

while ((len=is.read(car))!=-1){
//字节数组 --> 字符串(解码)
String str = new String(car,0,len);
System.out.println(str);
}

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
35
36
37
38
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;

public class Testing3 {
public static void main(String[] args) {
// 1. 选择源
File dest = new File("dest.txt");
// 2. 选择流
OutputStream os = null;

try {
// true使用add,否则覆盖文件
os = new FileOutputStream(dest,true);
// 3. 操作(写出)
String msg = "hyl is sb";
byte[] datas = msg.getBytes(); // 编码
os.write(datas,0,datas.length);
os.flush();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e){
e.printStackTrace();
} finally{
// 4. 释放资源
try{
if (os!=null){
// 释放资源
os.close();
}
}catch(IOException e){
e.printStackTrace();
}
}
}
}

使用FileInputStreamFileOutputStream实现文件拷贝

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
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;

public class TestFileCopy {
public static void main(String[] args) {
//将a.txt内容拷贝到b.txt
copyFile("d:/a.txt", "d:/b.txt");
}

/**
* 将src文件的内容拷贝到dec文件
* @param src 源文件
* @param dec 目标文件
*/
static void copyFile(String src, String dec) {
FileInputStream fis = null;
FileOutputStream fos = null;
//为了提高效率,设置缓存数组!(读取的字节数据会暂存放到该字节数组中)
byte[] buffer = new byte[1024];
int temp = 0;
try {
fis = new FileInputStream(src);
fos = new FileOutputStream(dec);
//边读边写
//temp指的是本次读取的真实长度,temp等于-1时表示读取结束
while ((temp = fis.read(buffer)) != -1) {
/*将缓存数组中的数据写入文件中,注意:写入的是读取的真实长度;
*如果使用fos.write(buffer)方法,那么写入的长度将会是1024,即缓存
*数组的长度*/
fos.write(buffer, 0, temp);
}
} catch (Exception e) {
e.printStackTrace();
} finally {
//两个流需要分别关闭
try {
if (fos != null) {
fos.close();
}
} catch (IOException e) {
e.printStackTrace();
}
try {
if (fis != null) {
fis.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
}

在使用文件字节流时,我们需要注意以下两点:

  1. 为了减少对硬盘的读写次数,提高效率,通常设置缓存数组。相应地,读取时使用的方法为:read(byte[] b);写入时的方法为:write(byte[ ] b, int off, int length)。
  2. 程序中如果遇到多个流,每个流都要单独关闭,防止其中一个流出现异常后导致其他流无法关闭的情况。

文件字符输入流FileReader与文件字符输出流FileWriter

  • 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
25
26
27
28
29
30
31
32
33
34
35
36
37
38
// FileReader
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;

public class Testing4 {
public static void main(String[] args) {
// 1.创建源
File f = new File("F:/Java/hyl.txt");
// 2.选择流
FileReader reader = null;
try {
reader = new FileReader(f);
// 3. 操作(读取)
char[] buffer = new char[1024];
int len = -1;
while ((len = reader.read(buffer))!=-1){
String str = new String(buffer,0,len);
System.out.println(str);
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally{
try{
if (reader!=null){
// 释放资源
reader.close();
}
}catch(IOException e){
e.printStackTrace();
}
}
}
}
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
// FileWriter
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileWriter;
import java.io.IOException;

public class Testing5 {
public static void main(String[] args) {
// 1. 选择源
File dest = new File("dest.txt");
// 2. 选择流
FileWriter writer = null;

try {
writer = new FileWriter(dest);
// 3. 操作(写出)
String msg = "XXXXX";
char[] datas = msg.toCharArray();
writer.write(datas,0,datas.length);
writer.flush();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e){
e.printStackTrace();
} finally{
// 4. 释放资源
try{
if (writer!=null){
// 释放资源
writer.close();
}
}catch(IOException e){
e.printStackTrace();
}
}
}
}

FileWriter有三种输出方式:
writer = new FileWriter(dest);

1
2
3
String msg = "第一段内容,第二段内容";
char[] datas = msg.toCharArray(); // 字符串 -- > 字符数组
writer.write(datas,0,datas.length);
1
2
3
4
5
6
String msg1 = "第一段内容";
String msg2 = "第二段内容";

writer.write(msg1);
writer.write(msg2);
writer.flush();
1
2
writer.append("第一段内容").append("第二段内容");
writer.flush();

使用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
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
public class TestFileCopy2 {
public static void main(String[] args) {
// 写法和使用Stream基本一样。只不过,读取时是读取的字符。
FileReader fr = null;
FileWriter fw = null;
int len = 0;
try {
fr = new FileReader("d:/a.txt");
fw = new FileWriter("d:/d.txt");
//为了提高效率,创建缓冲用的字符数组
char[] buffer = new char[1024];
//边读边写
while ((len = fr.read(buffer)) != -1) {
fw.write(buffer, 0, len);
}

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

字节数组流ByteArrayInputStream和ByteArrayOutputStream

ByteArrayInputStream和ByteArrayOutputStream经常用在需要流和数组之间转化的情况

说白了,FileInputStream是把文件当做数据源。ByteArrayInputStream则是把内存中的”某个字节数组对象”当做数据源。

因为是使用内存的数据,所以不需要关闭

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.ByteArrayInputStream;
import java.io.File;
import java.io.IOException;

public class Testing2 {
public static void main(String[] args) {
// 1.创建源
byte[] src = "hyl is a sb".getBytes();
// 2.选择流
ByteArrayInputStream is = null;
try {
is = new ByteArrayInputStream(src);
// 3. 操作(分段读取)
byte[] buffer = new byte[5];
int len = -1;
while ((len = is.read(buffer))!=-1){
String str = new String(buffer,0,len);
System.out.println(str);
}
} catch (IOException e)
e.printStackTrace();
}
}
}
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
import java.io.ByteArrayOutputStream;
import java.io.IOException;

public class Testing4 {
public static void main(String[] args) {
// 1.创建源
byte[] dest = null;
// 2.选择流
ByteArrayOutputStream os = null;
try {
os = new ByteArrayOutputStream();
String msg = "ABCDEFG";
byte[] datas = msg.getBytes();

// 3. 操作(写入)
os.write(datas,0,datas.length);
os.flush();

// 4. 获取数据
dest = os.toByteArray();
System.out.println(dest.toString());
} catch (IOException e) {
e.printStackTrace();
}
}
}

使用FileInputStreamFileOutputStreamByteArrayInputStreamByteArrayOutputStream实现图片文件的拷贝

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
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;

public class Testing5 {
public static void main(String[] args) {
byte[] datas = fileToByteArray("F:/Java/hyl.jpg");
System.out.println(datas.length);
byteArrayToFile(datas, "F:/Java/hyl2.jpg");
}
// 将图片读取到字节数组
public static byte[] fileToByteArray(String SrcFilePath) {
// 1. 创建源
File src = new File(SrcFilePath);
// 2. 选择流
FileInputStream is = null;
ByteArrayOutputStream os = null;

try {
is = new FileInputStream(src);
os = new ByteArrayOutputStream();

// 3. 操作(分段读取)
byte[] buffer = new byte[1024*10];
int len = -1;
while ((len = is.read(buffer))!=-1){
os.write(buffer,0,len);
}
os.flush();
return os.toByteArray();

} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e){
e.printStackTrace();
} finally{
// 4. 释放资源
try {
if (null != is){
is.close();
}
} catch (IOException e){
e.printStackTrace();
}
}
return null;
}

// 将字节数组写出到图片
public static void byteArrayToFile(byte[] src , String DestFilePath) {
// 1. 创建源
File dest = new File(DestFilePath);
// 2. 选择流
ByteArrayInputStream is = null;
FileOutputStream os = null;

try {
is = new ByteArrayInputStream(src);
os = new FileOutputStream(dest);
// 3. 操作
byte[] buffer = new byte[5];
int len = -1;
while((len=is.read(buffer))!=-1){
os.write(buffer,0,len);
}
os.flush();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e){
e.printStackTrace();
} finally{
// 4. 释放资源
try {
if (null != is){
is.close();
}
} catch (IOException e){
e.printStackTrace();
}
}
}
}

缓冲字节流BufferedInputStream和BufferedOutputStream

Java缓冲流本身并不具有IO流的读取与写入功能,只是在别的流(节点流或其他处理流)上加上缓冲功能提高效率,就像是把别的流包装起来一样,因此缓冲流是一种处理流(包装流)。

当对文件或者其他数据源进行频繁的读写操作时,效率比较低,这时如果使用缓冲流就能够更高效的读写信息。因为缓冲流是先将数据缓存起来,然后当缓存区存满后或者手动刷新时再一次性的读取到程序或写入目的地。

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
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;

public class student4 {
public static void main(String[] args) {
File f = new File("F:/Java/hyl.txt");
FileInputStream is = null;
BufferedInputStream bis = null;

try {
is = new FileInputStream(f);
bis = new BufferedInputStream(is);
byte[] car = new byte[1024];
int len = -1;

while ((len=is.read(car))!=-1){
String str = new String(car,0,len);
System.out.println(str);
}

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

注意

  1. 在关闭流时,应该先关闭最外层的包装流,即“后开的先关闭”。

  2. 缓存区的大小默认是8192字节(8K),也可以使用其它的构造方法自己指定大小。

FileInputStream :

1
2
3
4
5
6
7
8
9
10
11
// 1.创建源
File f = new File("F:/Java/hyl.txt");
// 2.选择流
FileInputStream is = null;
try {
is = new FileInputStream(f);
// 3. 操作(读取)
int temp;
while ((temp = is.read())!=-1){
System.out.println((char)temp);
}

BufferedInputStream :

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// 1.创建源
File f = new File("F:/Java/hyl.txt");
// 2.选择流
FileInputStream is = null;
// 添加了这一句
BufferedInputStream bis = null;

try {
is = new FileInputStream(f);
// 添加了这一句
bis = new BufferedInputStream(is)
byte[] car = new byte[1024];
int len = -1;

while ((len=is.read(car))!=-1){
String str = new String(car,0,len);
System.out.println(str);
}

我们发现这只是添加了一个装饰器而已

所以,我们可以将

1
is = new FileInputStream(f);

改为

1
is = new BufferedInputStream(new FileInputStream(src));

同理,遇到FileOutputStream的话就可以使用BufferedOutputStream

1
is = new BufferedOutputStream(new FileOutputStream(dest));

最终为:

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
import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;

public class student5 {
public static void main(String[] args) {
File f = new File("F:/Java/hyl.txt");
BufferedInputStream is = null;
try {
// 修改本句
is = new BufferedInputStream(new FileInputStream(f));

byte[] car = new byte[1024];
int len = -1;

while ((len=is.read(car))!=-1){
String str = new String(car,0,len);
System.out.println(str);
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally{
try{
if (is!=null){
is.close();
}
}catch(IOException e){
e.printStackTrace();
}
}
}
}

缓冲字符流BufferedReader和BufferedWriter

  • 增加了缓存机制,大大提高了读写文本文件的效率,
  • 同时,提供了更方便的按行读取的方法:readLine();

同理,我们可以将

1
2
is = new FileReader(src);
is = new FileWriter(dest);

改为

1
2
is = new BufferedReader(new FileWriter(src));
is = new BufferedWriter(new FileWriter(dest));

同时我们还可以使用他的readLine方法:

1
2
3
4
5
6
try {
reader = new BufferedReader(new FileReader(f));
String line = null;
while ((line=reader.readLine())!=null){
System.out.println(line);
}

还要有一个newLine方法:

1
2
writer.append("第一段内容/r/n").append("第二段内容/r/n");
writer.flush();

改为

1
2
3
4
5
writer.append("第一段内容");
writer.newLine();
writer.append("第二段内容");
writer.newLine();
writer.flush();

转换流InputStreamReader和OutputStreamWriter

InputStreamReader/OutputStreamWriter用来实现将字节流转化成字符流。

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.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;

public class Testing3 {
public static void main(String[] args) {
try ( BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(System.out));)
{
// 循环获取键盘的输入(exit退出),输出此内容
String msg = "";
while (! msg.equals("exit")){
msg = reader.readLine();
writer.write(msg);
writer.newLine();
writer.flush();
}
} catch (IOException e) {
System.out.println("操作异常");
}
}
}

数据流DataInputStream和DataOutputStream

数据流将基本数据类型与字符串类型作为数据源,从而允许程序以与机器无关的方式从底层输入输出流中操作Java基本数据类型与字符串类型。

DataInputStream和DataOutputStream提供了可以存取与机器无关的所有Java基础类型数据(如:int、double、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
26
27
28
29
30
31
32
33
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;

public class Testing4 {
public static void main(String[] args) throws IOException {
// 写入
ByteArrayOutputStream baos = new ByteArrayOutputStream();
DataOutputStream dos = new DataOutputStream(new BufferedOutputStream(baos));

// 操作数据类型 + 数据
dos.writeUTF("何应良");
dos.writeInt(18);
dos.writeBoolean(false);
dos.writeChar('a');
dos.flush();
byte[] datas = baos.toByteArray();

// 读取
DataInputStream dis = new DataInputStream(new BufferedInputStream(new ByteArrayInputStream(datas)));

// 顺序与写出一致
String msg = dis.readUTF();
int age = dis.readInt();
boolean flag = dis.readBoolean();
char ch = dis.readChar();
System.out.println(flag);
}
}

对象流ObjectInputStream/ObjectOutputStream

我们前边学到的数据流只能实现对基本数据类型和字符串类型的读写,并不能读取对象(字符串除外),如果要对某个对象进行读写操作,我们需要学习一对新的处理流:ObjectInputStream/ObjectOutputStream。

ObjectInputStream/ObjectOutputStream是以对象为数据源,但是必须将传输的对象进行序列化与反序列化操作。

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
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;

public class Student {
public static void main(String[] args) throws IOException,ClassNotFoundException{
// 写出
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(new BufferedOutputStream(baos));

// 操作数据类型
oos.writeUTF("hyl");
oos.writeInt(18);
oos.writeBoolean(false);
oos.writeChar('a');
// 添加对象
oos.writeObject("字符串也是对象,而且字符串可序列化");
oos.writeObject(new Person());

oos.flush();
byte[] datas = baos.toByteArray();

// 读取
ObjectInputStream ois = new ObjectInputStream(new BufferedInputStream(new ByteArrayInputStream(datas)));

String msg = ois.readUTF();
int age = ois.readInt();
boolean flag = ois.readBoolean();
char ch = ois.readChar();

// 读取对象
Object str = ois.readObject();
Object person = ois.readObject();

if(str instanceof String){
String strObj = (String) str;
System.out.println(strObj);
}
if(person instanceof Person){
Person personObj = (Person) person;
System.out.println(personObj);
}

System.out.println(flag);
}
}
// Person类必须实现Serializable接口
class Person implements Serializable{
private String name;
private int age;
}

对于某些不想被序列化的属性,可以添加一个transient

1
2
3
4
class Person implements Serializable{
private String name;
private int age;
}

改为

1
2
3
4
class Person implements Serializable{
private String name;
private transient int age;
}

注意

  1. 对象流不仅可以读写对象,还可以读写基本数据类型。
  2. 使用对象流读写对象时,该对象必须序列化与反序列化。
  3. 系统提供的类(如Date等)已经实现了序列化接口,自定义类必须手动实现序列化接口。

打印流PrintSream和PrintWriter

Printstream将功能添加到另一个输出流,即能够方便地打印各种数据值的表示。

还提供了另外两个功能。

  1. 不像其他输出流,一个 Printstream从不抛出一个要IOException;相反,异常情况只是设置一个可以通过 checkEr方法测试的内部标志。
  2. 可选地,可以创建 Printstream以便自动刷撕;这意味看1uh方法在少字节数组之后自动调用,其中一个 print1n方法被调用,或换行字符(“\n’)被写入使用平台的认字符编码将 Printstream打印的所换为字节。Printriter类应该在需要写入字符而不是字节的情况下使用
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import java.io.BufferedOutputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.PrintStream;

public class Testing2 {
public static void main(String[] args) throws FileNotFoundException {
PrintStream ps = System.out;
ps.println("打印流");

ps = new PrintStream(new BufferedOutputStream(new FileOutputStream("print.txt")),true);
ps.println(true);

// 重定向输出端
System.setOut(ps);
System.out.println("hyl");
}
}
1
2
3
4
5
6
7
8
9
10
11
12
import java.io.BufferedOutputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.PrintWriter;

public class Testing3 {
public static void main(String[] args) throws FileNotFoundException {
PrintWriter pw = new PrintWriter(new BufferedOutputStream(new FileOutputStream("print.txt")),true);
pw.println("打印流");
pw.close();
}
}

随机流RandomAccessFile

  1. 随机流有点像迅雷下载,随机下载同一个文件的不同的部分
  2. 所以,我们必须在此之前对文件进行分割

RandomAccessFile :
该类的实例支持读取和写入随机访问文件。随机访问文件的行为类似于存储在文件系统中的大量字节。

  • 有一种游标,或索引倒到隐含的数组,称为文件指针
  • 输入操作读取从文件指针开始的字节,并使文件指针超过读取的字节。如果在读/写模式下创建随机访问文件,则出操作也可用;
  • 输出操作从文伴指针开始写入字节,并将文件指针提前到写入的字节。写入隐式数组的当前端的输出操作会导致扩展数组。文件指针可以通过读取getpointer方法由设置seek方法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import java.io.File;
import java.io.IOException;
import java.io.RandomAccessFile;

public class Testing {
public static void main(String[] args) throws IOException {
RandomAccessFile raf = new RandomAccessFile(new File("D:/tasks.json"), "r");
// 随机读取
raf.seek(20);
// 读取
byte[] buffer = new byte[1024]; // 缓冲容器
int len = -1;
while((len = raf.read(buffer))!= -1){
System.out.println(new String(buffer,0,len));
}
raf.close();
}
}

装饰器

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
public class Testing {
public static void main(String[] args) {
Person p = new Person();
p.say();

Amplifier am = new Amplifier(p);
am.say();
}

}


interface Say{
void say();
}

class Person implements Say{
private int voice = 10;
public void say() {
System.out.println(this.getVoice());
}
public int getVoice() {
return voice;
}
public void setVoice(int voice) {
this.voice = voice;
}
}


class Amplifier implements Say{
private Person p;
Amplifier(Person p){
this.p = p;
}
public void say(){
System.out.println(p.getVoice()*100);
}
}
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
// 模拟咖啡
// 抽象组件:需要装饰的抽象对象(接口或抽象父类)
// 具体组件:需要装饰的对象
// 抽象装饰类:包含了对抽象组件的引用以及装饰着共有的方法
// 具体装饰类:被装饰的对象
public class Testing2 {
public static void main(String[] args) {
Drink coffee = new Coffee();

Drink suger = new Suger(coffee);
System.out.println(suger.info() + "-->" + suger.cost());

Drink milk = new Milk(coffee);
System.out.println(milk.info() + "-->" + milk.cost());

milk = new Milk(suger);
System.out.println(milk.info() + "-->" + milk.cost());
}
}

// 抽象组件
interface Drink{
double cost(); // 费用
String info(); // 说明
}

// 具体组件
class Coffee implements Drink{
private String name = "原味咖啡";
public double cost() {
return 10;
}

public String info() {
return name;
}
}

// 抽象装饰类
abstract class Decorate implements Drink{
// 对抽象组件的引用
private Drink drink;
public Decorate(Drink drink){
this.drink = drink;
}
public double cost() {
return this.drink.cost();
}

public String info() {
return this.drink.info();
}
}

// 具体装饰类
class Milk extends Decorate{
public Milk(Drink drink){
super(drink);
}
public double cost() {
return super.cost() * 4;
}

public String info() {
return super.info() + "加入了牛奶";
}
}

// 具体装饰类
class Suger extends Decorate{
public Suger(Drink drink){
super(drink);
}
public double cost() {
return super.cost() * 2;
}

public String info() {
return super.info() + "加入了蔗糖";
}
}