一、概述:
1.流的分类:
按照流向的不同分为:输入流 输出流
按照处理数据单位的不通分为:字节流 字符流(处理的文本文件)
按照角色的不通分为 节点流(直接作用于文件的)处理流
关于处理流的说明:(处理流作用在节点流上)
2.IO的体系:
抽象基类 | 节点流(文件流) | 缓冲流 |
InputStream(字节流) | FileInputStream | BufferedInputStream |
OutputStream(字节流) | FileOutputStream | BufferedOutputStream |
Reader(字符流) | FileReader | BufferedReader |
Writer(字符流) | FileWriter | BufferedWriter |
3.如何使用
- 对于文本文件(.txt,.java,.c,.cpp)使用字符流处理
- 对于非文本文件,使用字节流处理
二、节点流
1、FileReaderd
1.1步骤:
- 创建File类对象,指明要操作的文件;
- 提供具体的流
- 数据的处理
- 关闭应用的流
demo1:从硬盘存在的一个文件中,读取其内容到程序中,使用FileInputStream,显示在控制台上
1 @Test
2
3 public void testStream1() {
4
5 // 2.提供具体的流
6 FileReader fileReader = null;
7 try {
8
9 // 1.创建一个File类的对象,指明要操作的文件
10
11 File file1 = new File("hello.txt");// 文件位置为当前工程下面,不是当前类下
12 fileReader = new FileReader(file1);
13 // 3.数据的读入
14 // read() 返回读入的一个字符,文件达到末尾,则返回-1
15 int data = fileReader.read();
16 while (data != -1) {
17 System.out.print((char) data);
18 data = fileReader.read();
19 }
20 } catch (FileNotFoundException e) {
21 // TODO Auto-generated catch block
22 e.printStackTrace();
23 } catch (IOException e) {
24 // TODO Auto-generated catch block
25 e.printStackTrace();
26 } finally {
27 // 4.关闭相应的流
28 if (fileReader != null) {
29 try {
30 fileReader.close();
31 } catch (IOException e) {
32 e.printStackTrace();
33 }
34 }
35
36 }
37
38 }
注意:要读取的文件一定要存在,否则空指针异常
说明:
- read()的理解:返回读入的一直字符,如果到底文件末尾则返回-1;
- 关于异常的处理,为了保证保证流资源的关闭,这里使用了try catch finally 来处理,这里看到了顺序没有从上到下来,是因为最后做了异常处理导致的;
1.2.关于read方法的详细说明
1.2.1read()方法错误版(只为说明用)
1 /
2 reader 方法升级版(错误版)
3 4 @Description
5 @author lixiuming
6 @date 2021年5月5日上午11:22:37
7 @throws IOException
8 9 /
10 @Test
11 public void testReaderUpgrade2() throws IOException {
12 // 1.File 类的实例化
13 File file1 = new File("hello.txt");// 文件位置为当前工程下面,不是当前类下
14 // 2.流的实例化
15 FileReader fileReader = new FileReader(file1);
16 // 3.流的写出
17 char[] charArr = new char[5];// 一次读入多个数据来提高效率
18 int len = fileReader.read(charArr);// 一次读入多个数据,返回每次读入的charArr数组中字符的个数,如果达到文件末尾,返回-1
19 while (len != -1) {
20 for (int i = 0; i < charArr.length; i++) {
21 System.out.print((char) charArr[i]);
22 }
23 len = fileReader.read(charArr);
24 }
25 // 4.流的操作
26 fileReader.close();
27
28 }
这里的charArr.length;有问题,在文件中,如果hello.txt中的内容是:hello word!! hello lixiuming!!123,那么此时的输出结果就是 hello word!! hello lixiuming!!123!!,多了两个“!!”;
若最后取的数组长度不足5,那么前一个的数组是:
i | n | g | ! | ! |
最后一组数组则是:
1 | 3 | 2 | ! | ! |
没有覆盖掉原来的数据;
1.2.2read()的升级版本正确写法:
1 /
2 reader 方法的升级版(正确版)
3 4 @Description
5 @author lixiuming
6 @throws IOException
7 @date 2021年5月5日上午10:55:53
8 9 /
10 @Test
11 public void testReaderUpgrade() throws IOException {
12 // 1.File 类的实例化
13 File file1 = new File("hello.txt");// 文件位置为当前工程下面,不是当前类下
14 // 2.流的实例化
15 FileReader fileReader = new FileReader(file1);
16 // 3.流的写出
17 char[] charArr = new char[5];// 一次读入多个数据来提高效率
18 int len = fileReader.read(charArr);// 一次读入多个数据,返回每次读入的charArr数组中字符的个数,如果达到文件末尾,返回-1
19 while (len != -1) {
20 for (int i = 0; i < len; i++) {
21 System.out.print((char) charArr[i]);
22
23 }
24 len = fileReader.read(charArr);
25 }
26 // 4.流的操作
27 fileReader.close();
28
29 }
2.FileWrite
步骤:
- 创建File类对象,指明要操作的文件;
- 提供具体的流
- 数据的处理
- 关闭应用的流
demo:从内存中写出数据到硬盘的文件里的简单说明
说明:
输出操作,对应File可以不存在,并不会报异常;
如不存在,在输出过程中自动穿件文件
如果存在, a.如果流构造器使用的是FileWriter(file,false)/FileWriter(file),对象会对原有的文件进行覆盖;
b.如果流构造器使用的是FileWriter(file,true) 不会对原有的文件覆盖,而是在原有的文件内追加;
3.文件的复制(FileReader和FileWriter同时使用)
3.1JUNIT测试版:
1 /
2 文件的复制
3 4 @Description
5 @author lixiuming
6 @throws IOException
7 @date 2021年5月5日上午10:19:44
8 9 /
10 @Test
11 public void testFileCopy() {
12 // 2.创建输入和出入流的对象
13 FileReader fr = null;
14 FileWriter fw = null;
15 try {
16 // 1.创建File 类的对象,指明读入和写出的文件
17 File copy = new File("hello.txt");
18 File copyTo = new File("hello2.txt");
19 fr = new FileReader(copy);
20 fw = new FileWriter(copyTo);
21 // 3.数据的读入和写出
22 char[] charArr = new char[5];
23 int len = fr.read(charArr);
24 while (len != -1) {
25 for (int i = 0; i < len; i++) {
26 char data = (char) charArr[i];
27 fw.write(data);
28 }
29 len = fr.read(charArr);
30 }
31 } catch (Exception e) {
32 } finally {
33 // 4.关闭流资源
34 try {
35 if (fw != null) {
36 fw.close();
37 }
38
39 } catch (IOException e) {
40 e.printStackTrace();
41 } finally {
42 if (fr != null) {
43 try {
44 fr.close();
45 } catch (IOException e) {
46 e.printStackTrace();
47 }
48 }
49 }
50
51 }
52
53 }
4、FileInputStream/FileOutputStream
*4.1步骤:
- 创建File类对象,指明要操作的文件;
- 提供具体的流
- 数据的处理
- 关闭应用的流
说明:用字节流 读取文本文件(中文 数字 英文):控制台中文会有乱码,如果单纯的复制文件没有问题;
使用demo:用字节流读取文本文件
1 @Test
2 public void testFileInputString() {
3 // 2.造流
4 FileInputStream fo = null;
5 try {
6 // 1.造文件
7 File file = new File("hello.txt");
8 fo = new FileInputStream(file);
9 // 3.文件读出
10 byte[] arr = new byte[5];
11 int len = fo.read(arr);
12 while (len != -1) {
13 System.out.print(new String(arr, 0, len));
14 len = fo.read(arr);
15 }
16 } catch (Exception e) {
17 // TODO: handle exception
18 } finally {
19 // 4、流关闭
20 try {
21 if (fo != null)
22 fo.close();
23 } catch (IOException e) {
24 // TODO Auto-generated catch block
25 e.printStackTrace();
26 }
27 }
28
29 }
4.2实现图片的复制
*4.3对文件复制的通用方法:
说明:不能使用字符流复制照片,但是 字节流可以复制文本文件,但是,如果在复制的过程中,想在控制台查看文件,可能会有中文乱码;
1 /
2 复制文件通用方法
3 4 @Description
5 @author lixiuming
6 @date 2021年5月5日下午2:34:36
7 @param srcStr 复制文件地址(文件名)
8 @param destStr 复制到文件的地址(文件名)
9 10 /
11 public void testCopFile(String srcStr, String destStr) {
12 // 2.造流
13 FileInputStream fi = null;
14 FileOutputStream fo = null;
15 try {
16 // 1.造文件
17 File copyFile = new File(srcStr);
18 File copytoFile = new File(destStr);
19 fi = new FileInputStream(copyFile);
20 fo = new FileOutputStream(copytoFile);
21 // 3.文件读出
22 byte[] arr = new byte[1024];// 一般使用1024
23 int len = fi.read(arr);
24 while (len != -1) {
25 for (int i = 0; i < len; i++) {
26 fo.write(arr[i]);
27 }
28 len = fi.read(arr);
29
30 }
31 } catch (Exception e) {
32 // TODO: handle exception
33 } finally {
34 // 4、流关闭
35 try {
36 if (fo != null)
37 fo.close();
38 } catch (IOException e) {
39 // TODO Auto-generated catch block
40 e.printStackTrace();
41 }
42 try {
43 if (fi != null)
44 fi.close();
45 } catch (IOException e) {
46 // TODO Auto-generated catch block
47 e.printStackTrace();
48 }
49 }
50
51 }
*三、缓冲流
1.1缓冲流的作用:可以提供效率
*1.2使用和步骤
- 创建File类对象,指明要操作的文件;
- 提供具体的流:节点流和缓冲流;
- 数据的处理
- 关闭应用的流
demo:使用缓冲流实现文件的复制:(BufferedInputStream/BufferedOutputStream)
1 /
2 缓冲流实现的文件复制
3 4 @Description
5 @author lixiuming
6 @date 2021年5月5日下午2:58:38
7 8 /
9 @Test
10 public void testCopyFileByBuffer() {
11 BufferedInputStream bi = null;
12 BufferedOutputStream bo = null;
13 // 3.复制
14 byte[] arr;
15 int len;
16 try {
17 // 1.造文件
18 File copy = new File("hello.txt");
19 File copyTo = new File("hello3.txt");
20 // 2.造流
21 FileInputStream fi = new FileInputStream(copy);
22 FileOutputStream fo = new FileOutputStream(copyTo);
23 bi = new BufferedInputStream(fi);
24 bo = new BufferedOutputStream(fo);
25 arr = new byte[1024];
26 len = bi.read(arr);
27 while (len != -1) {
28 for (int i = 0; i < len; i++) {
29 bo.write(arr[i]);
30 }
31 len = bi.read(arr);
32 }
33 } catch (Exception e) {
34 } finally {
35 // 4.关闭资源
36 try {
37 if (bo != null) {
38 bo.close();
39 }
40 } catch (IOException e) {
41 e.printStackTrace();
42 }
43 try {
44 if (bi != null) {
45 bi.close();
46 }
47 } catch (IOException e) {
48 e.printStackTrace();
49 }
50 }
51
52 }
说明,这里的造流除了 节点流还有缓冲流,因为缓冲流作用在节点流之上;
*1.3 缓冲流复制文件的通用方法
1 public void testCopyFileByBuffer(String srcStr, String destStr) {
2 BufferedInputStream bi = null;
3 BufferedOutputStream bo = null;
4 // 3.复制
5 byte[] arr;
6 int len;
7 try {
8 // 1.造文件
9 File copy = new File(srcStr);
10 File copyTo = new File(destStr);
11 // 2.造流
12 // 2.1造节点流
13 FileInputStream fi = new FileInputStream(copy);
14 FileOutputStream fo = new FileOutputStream(copyTo);
15 // 2.2造缓冲流
16 bi = new BufferedInputStream(fi);
17 bo = new BufferedOutputStream(fo);
18 arr = new byte[1024];
19 len = bi.read(arr);
20 while (len != -1) {
21 for (int i = 0; i < len; i++) {
22 bo.write(arr[i]);
23 }
24 len = bi.read(arr);
25 }
26 } catch (Exception e) {
27 } finally {
28 // 4.关闭资源
29 // 说明,关闭外层流,内层流自动关闭;所以 file的节点流在此处自动关闭
30 try {
31 if (bo != null) {
32 bo.close();
33 }
34 } catch (IOException e) {
35 e.printStackTrace();
36 }
37 try {
38 if (bi != null) {
39 bi.close();
40 }
41 } catch (IOException e) {
42 e.printStackTrace();
43 }
44 }
45
46 }
1.4BufferReader BufferedWriter 实现文本文件的复制
junit测试版
封装版:
四、其他流
1.转换流(属于字符流 ):
- InputStreamReader:将字节的输入流 转 字符的输入流;
- OutputStreamWriter; 将字符的输出流,转字节的输出流
作用:提供字节流和字符流的转换;
通途:可以编码 和 解码;
demo:使用utf-8读取文件,使用gbk写文件;
1 @Test
2 public void testReaderWriter() {
3 InputStreamReader isr = null;
4 OutputStreamWriter osw = null;
5 try {
6 //1.造文件
7 File strFile = new File("hello.txt");
8 File destFile = new File("hello_gbk.txt");
9 //2.造流
10 FileInputStream fi = new FileInputStream(strFile);
11 FileOutputStream fo = new FileOutputStream(destFile);
12 isr = new InputStreamReader(fi, "utf-8");
13 osw = new OutputStreamWriter(fo, "gbk");
14 //3.处理
15 char[] arr = new char[1024];
16 int len = isr.read(arr);
17 while (len != -1) {
18 for (int i = 0; i < len; i++) {
19 osw.write(arr[i]);
20 }
21 len = isr.read(arr);
22 }
23 } catch (Exception e) {
24 // TODO: handle exception
25 }finally {
26 //4.关闭流
27 try {
28 if(isr!=null) {
29 isr.close();
30 }
31 } catch (IOException e) {
32 // TODO Auto-generated catch block
33 e.printStackTrace();
34 }
35 try {
36 if(osw!=null) {
37 osw.close();
38 }
39 } catch (IOException e) {
40 // TODO Auto-generated catch block
41 e.printStackTrace();
42 }
43 }
44
45 }
2.数据流:处理基本数据类型和String数据;
DataInputStream / DataOutputStream
用于读取或写出基本数据类型的变量或者字符串
readBoolean()
readChar()
readLone()
String readUTF()
readByte()
readFloat()
readShort()
readInt()
void readFully(byte[] b)
demo1:将基本类型数据写出到文件
demo2:将文件 读入到内存
注意:读入内存过程中,读入的顺序需要和写入时的顺序一致
3.打印流:
字节流:PrintStream
字符流:printWriter
提供了一系列重载的print() 和println()
改变输出位置
4. 标准的输入,输出流
- System.in 标准的输入流,默认键盘输入
- System.out 标准的输出流,默认从控制台输出
- System.setIn()/System.setOut()可以冲洗指定输入和输出;
demo:把输入内容转化成大写,输出,当直接输入“e”或者“exit”时,退出程序
5.对象流
①作用:用于存储和读取基本数据类型数据或者对象的处理流;
序列化:用ObjectOutputStream 类保存基本类型数据或对象的机制
反序列化:用ObjectInputStream 类读取基本类型数据或对象
②对象的序列化机制:允许把内存中的Java对象转换成与平台无关的二进制流,从而允许把这种二进制流持久的保存到磁盘上,
或者通过网络将这种二进制流传输到另一个网络节点,当其他程序获取了这种二进制流,就可以恢复成原来的java对象;
③序列化的好处:可以将任意实现了Serializable接口的对象转换为字节数据,使其在保存和传输时可被还原;
序列化是RMI过程的传输和返回值都必须实现的机制,而RMI是java EE的基础,因此序列化是javaEE平台的基础
④注意:
如果需要让某个对象支持序列化机制:则必须让对象所属的类及其属性是可序列化的,为了让某个类是可序列化的,该类必须实现如下两个接口之一 否则会抛出
NoSerializableException的异常
- Sericalizable
- Externalizable
ObjectInputStream 和 ObjectOutputStream 不能序列化static和transient修饰的成员;
⑤demo示例(只是为了展示基本使用步骤)
升级版:
1 /
2 自定义类的序列化
3
4 @Description
5 @author lixiuming
6 @date 2021年5月15日下午3:23:28
7
8 /
9 @Test
10 public void test1() {
11 ObjectOutputStream obs = null;
12 try {
13 File file = new File("Person.dat");
14 FileOutputStream fo = new FileOutputStream(file);
15 obs = new ObjectOutputStream(fo);
16 Persion persion = new Persion();
17 persion.setAge(11);
18 persion.setName("李秀明");
19 obs.writeObject(persion);
20 } catch (Exception e) {
21 // TODO: handle exception
22 } finally {
23 try {
24 if (obs != null) {
25
26 obs.close();
27 }
28 } catch (IOException e) {
29 // TODO Auto-generated catch block
30 e.printStackTrace();
31 }
32 }
33 }
34
35 /
36 自定义饭序列化
37
38 @Description
39 @author lixiuming
40 @date 2021年5月15日下午3:44:38
41
42 /
43 @Test
44 public void test2() {
45 ObjectInputStream bis = null;
46 try {
47 File file = new File("Person.dat");
48 FileInputStream fi = new FileInputStream(file);
49 bis = new ObjectInputStream(fi);
50 Persion persion = (Persion) bis.readObject();
51 System.out.println(persion.toString());
52 } catch (Exception e) {
53 // TODO: handle exception
54 } finally {
55 try {
56 if (bis != null) {
57 bis.close();
58 }
59 } catch (IOException e) {
60 // TODO Auto-generated catch block
61 e.printStackTrace();
62 }
63 }
64
65 }
涉及到的类:
注意:对类的要求:
- 需要实现接口:serializable
- 当前类提供一个全局常量:serializableUID
- 除了当前类需要实现serializable接口外,还必须保证其内部所有属性也必须是可序列话的 默认情况下,基本数据类型是可序列化的;
补充:ObjectOutputStream 和 ObjectInputStream不能序列化static和transient修饰的成员
6.RandomAccessFile
说明:
1.RdomAccessFile直接继承于java.lang.Object类,实现了DataInput和DataOutput接口
2.RandomAccessFile既可以作为输入流,又可以作为输出流
RandomAccessFile的访问模式:
- r:以只读的方式打开
- rw:打开以后可以读取和写入
- rwd:打开以后可以读取和写入,同步文件内容的更新
- rws:打开以后可以读取和写入,同步文件内容和元数据的更新;
3.如果RandomAccessFile作为输出流时,写出到的文件如果不存在,则在执行过程中自动创建,如果存在,则会对原有文件进行覆盖(默认情况下从头覆盖);
4.可以通过相关操作,可以实现对文件的“插入”数据的效果;
(应用场景,文件断点续传)
demo1以照片赋值为例
demo2关于文件操作
五、使用第三方jar包实现数据读写
(commons-io-2.8.0.jar)
demo(复制文件)
1 package io; 2 3 import java.io.File; 4 import java.io.IOException; 5 6 import org.apache.commons.io.FileUtils; 7 import org.junit.Test; 8 public class FileUtilsTest { 9 @Test 10 public void test() { 11 File srcFile = new File("hello.text"); 12 File destFile = new File("fileUtilsTest.txt"); 13 try { 14 FileUtils.copyFile(srcFile, destFile); 15 } catch (IOException e) { 16 // TODO Auto-generated catch block 17 e.printStackTrace(); 18 } 19 20 } 21 22 23 }
六、NIO.2中Path、Paths、Files类的使用
说明:早期File类的功能比较有限,不会提供异常信息;NIO2为了补补不足,引入了Paht接口,Path可以看成是File类的升级版,实际引用的资源也可以不存在;
1 package io; 2 3 import org.junit.Test; 4 5 import java.io.File; 6 import java.nio.file.Path; 7 import java.nio.file.Paths; 8 9 10 /** 11 * 1. jdk 7.0 时,引入了 Path、Paths、Files三个类。 12 * 2.此三个类声明在:java.nio.file包下。 13 * 3.Path可以看做是java.io.File类的升级版本。也可以表示文件或文件目录,与平台无关 14 * <p> 15 * 4.如何实例化Path:使用Paths. 16 * static Path get(String first, String … more) : 用于将多个字符串串连成路径 17 * static Path get(URI uri): 返回指定uri对应的Path路径 18 * @Description 19 * @author lixiuming 20 * @version 21 * @date 2021年5月15日下午8:02:08 22 * 23 */ 24 public class PathTest { 25 26 //如何使用Paths实例化Path 27 @Test 28 public void test1() { 29 Path path1 = Paths.get("d:\nio\hello.txt");//new File(String filepath) 30 31 Path path2 = Paths.get("d:\", "nio\hello.txt");//new File(String parent,String filename); 32 33 System.out.println(path1); 34 System.out.println(path2); 35 36 Path path3 = Paths.get("d:\", "nio"); 37 System.out.println(path3); 38 } 39 40 //Path中的常用方法 41 @Test 42 public void test2() { 43 Path path1 = Paths.get("d:\", "nio\nio1\nio2\hello.txt"); 44 Path path2 = Paths.get("hello.txt"); 45 46 // String toString() : 返回调用 Path 对象的字符串表示形式 47 System.out.println(path1); 48 49 // boolean startsWith(String path) : 判断是否以 path 路径开始 50 System.out.println(path1.startsWith("d:\nio")); 51 // boolean endsWith(String path) : 判断是否以 path 路径结束 52 System.out.println(path1.endsWith("hello.txt")); 53 // boolean isAbsolute() : 判断是否是绝对路径 54 System.out.println(path1.isAbsolute() + "~"); 55 System.out.println(path2.isAbsolute() + "~"); 56 // Path getParent() :返回Path对象包含整个路径,不包含 Path 对象指定的文件路径 57 System.out.println(path1.getParent()); 58 System.out.println(path2.getParent()); 59 // Path getRoot() :返回调用 Path 对象的根路径 60 System.out.println(path1.getRoot()); 61 System.out.println(path2.getRoot()); 62 // Path getFileName() : 返回与调用 Path 对象关联的文件名 63 System.out.println(path1.getFileName() + "~"); 64 System.out.println(path2.getFileName() + "~"); 65 // int getNameCount() : 返回Path 根目录后面元素的数量 66 // Path getName(int idx) : 返回指定索引位置 idx 的路径名称 67 for (int i = 0; i < path1.getNameCount(); i++) { 68 System.out.println(path1.getName(i) + "*****"); 69 } 70 71 // Path toAbsolutePath() : 作为绝对路径返回调用 Path 对象 72 System.out.println(path1.toAbsolutePath()); 73 System.out.println(path2.toAbsolutePath()); 74 // Path resolve(Path p) :合并两个路径,返回合并后的路径对应的Path对象 75 Path path3 = Paths.get("d:\", "nio"); 76 Path path4 = Paths.get("nioo\hi.txt"); 77 path3 = path3.resolve(path4); 78 System.out.println(path3); 79 80 // File toFile(): 将Path转化为File类的对象 81 File file = path1.toFile();//Path--->File的转换 82 83 Path newPath = file.toPath();//File--->Path的转换 84 85 } 86 87 88 }