Java-IO流

发布时间 2023-11-15 20:04:16作者: 索静丨LLH

第十二章 IO流

File类

引入

【1】文件,目录:

文件: 

内存中存放的数据在计算机关机后就会消失。要长久保存数据,就要使用硬盘、光盘、U 盘等设备。为了便于数据的管理和检索,引入了“文件”的概念。一篇文章、一段视频、一个可执行程序,都可以被保存为一个文件,并赋予一个文件名。操作系统以文件为单位管理磁盘中的数据。

 

一般来说,文件可分为文本文件、视频文件、音频文件、图像文件、可执行文件等多种类别,这是从文件的功能进行分类的。从数据存储的角度来说,所有的文件本质上都是一样的,都是由一个个字节组成的,归根到底都是 0、1 比特串。不同的文件呈现出不同的形态(有的是文本,有的是视频等等)



文件夹(目录):

成千上万个文件如果不加分类放在一起,用户使用起来显然非常不便,因此又引入了树形目录(目录也叫文件夹)的机制,可以把文件放在不同的文件夹中,文件夹中还可以嵌套文件夹,这就便于用户对文件进行管理和使用



【2】查看文件/目录的信息:

右键-属性

 

 

【3】在java程序中操纵 文件/目录 ?怎么办?

java程序,最典型的特点,面向对象,java程序最擅长的就是操作对象,盘符上的文件/目录,将它的各种信息进行了封装,封装为一个对象,

java程序最擅长的就是操纵对象,这个对象属于 ---》File类

 

盘符上的文件---》封装为对象---》对象属于File类的对象--》有了这个对象,我们程序就可以直接操纵这个对象,通过这个对象获取文件的各种信息,还可以对文件进行创建 ,删除。

 

 

对文件进行操作

 1 package com.llh;
 2 
 3 import java.io.File;
 4 import java.io.IOException;
 5 
 6 public class Test01 {
 7     //这是一个main方法,是程序的入口:
 8     public static void main(String[] args) throws IOException {
 9         //将文件封装为一个File类的对象:
10         File f = new File("d:\\test.txt");
11         File f1 = new File("d:\\test.txt");
12         File f2 = new File("d:/test.txt");
13         //File.separator属性帮我们获取当前操作系统的路径拼接符号
14         //在windows,dos下,系统默认用“\”作为路径分隔符 ,在unix,url中,使用“/”作为路径分隔符。
15         File f3 = new File("d:"+File.separator+"test.txt");//建议使用这种
16         //常用方法:
17         System.out.println("文件是否可读:"+f.canRead());
18         System.out.println("文件是否可写:"+f.canWrite());
19         System.out.println("文件的名字:"+f.getName());
20         System.out.println("上级目录:"+f.getParent());
21         System.out.println("是否是一个目录:"+f.isDirectory());
22         System.out.println("是否是一个文件:"+f.isFile());
23         System.out.println("是否隐藏:"+f.isHidden());
24         System.out.println("文件的大小:"+f.length());
25         System.out.println("是否存在:"+f.exists());
26         /*if(f.exists()){//如果文件存在,将文件删除操作
27             f.delete();
28         }else{//如果不存在,就创建这个文件
29             f.createNewFile();
30         }*/
31         System.out.println(f == f1);//比较两个对象的地址
32         System.out.println(f.equals(f1));//比较两个对象对应的文件的路径
33         //跟路径相关的:
34         System.out.println("绝对路径:"+f.getAbsolutePath());
35         System.out.println("相对路径:"+f.getPath());
36         System.out.println("toString:"+f.toString());
37         System.out.println("----------------------");
38         File f5 = new File("demo.txt");
39         if(!f5.exists()){
40             f5.createNewFile();
41         }
42         //绝对路径指的就是:真实的一个精准的,完整的路径
43         System.out.println("绝对路径:"+f5.getAbsolutePath());
44         //相对路径:有一个参照物,相对这个参照物的路径。
45         //在main方法中,相对位置指的就是:D:\IDEA_workspace\TestJavaSE
46         //在junit的测试方法中,相对路径指的就是模块位置
47         System.out.println("相对路径:"+f5.getPath());
48         //toString的效果永远是  相对路径
49         System.out.println("toString:"+f5.toString());
50         File f6 = new File("a/b/c/demo.txt");
51         if(!f5.exists()){
52             f5.createNewFile();
53         }
54         System.out.println("绝对路径:"+f6.getAbsolutePath());
55         System.out.println("相对路径:"+f6.getPath());
56     }
57 }

 

对目录进行操作

 1 package com.llh;
 2 
 3 import java.io.File;
 4 
 5 public class Test02 {
 6     //这是一个main方法,是程序的入口:
 7     public static void main(String[] args) {
 8         //将目录封装为File类的对象:
 9         File f = new File("D:\\IDEA_workspace");
10         System.out.println("文件是否可读:"+f.canRead());
11         System.out.println("文件是否可写:"+f.canWrite());
12         System.out.println("文件的名字:"+f.getName());
13         System.out.println("上级目录:"+f.getParent());
14         System.out.println("是否是一个目录:"+f.isDirectory());
15         System.out.println("是否是一个文件:"+f.isFile());
16         System.out.println("是否隐藏:"+f.isHidden());
17         System.out.println("文件的大小:"+f.length());
18         System.out.println("是否存在:"+f.exists());
19         System.out.println("绝对路径:"+f.getAbsolutePath());
20         System.out.println("相对路径:"+f.getPath());
21         System.out.println("toString:"+f.toString());
22         //跟目录相关的方法:
23         File f2 = new File("D:\\a\\b\\c");
24         //创建目录:
25         //f2.mkdir();//创建单层目录
26         //f2.mkdirs();//创建多层目录
27         //删除:如果是删除目录的话,只会删除一层,并且前提:这层目录是空的,里面没有内容,如果内容就不会被删除
28         f2.delete();
29         //查看:
30         String[] list = f.list();//文件夹下目录/文件对应的名字的数组
31         for(String s:list){
32             System.out.println(s);
33         }
34         System.out.println("=========================");
35         File[] files = f.listFiles();//作用更加广泛
36         for(File file:files){
37             System.out.println(file.getName()+","+file.getAbsolutePath());
38         }
39     }
40 }

 

IO流

引入

【1】File类:封装文件/目录的各种信息,对目录/文件进行操作,但是我们不可以获取到文件/目录中的内容。

【2】引入:IO流:

I/O : Input/Output的缩写,用于处理设备之间的数据的传输。

【3】形象理解:IO流 当做一根 “管”:

 

 

【4】IO流的体系结构: 

 

案例:通过java程序完成文件的复制操作

 

功能分解1:文件--》程序:FileReader

一个字符一个字符的将文件中的内容读取到程序中了:

 1 package com.llh;
 2 
 3 import java.io.File;
 4 import java.io.FileNotFoundException;
 5 import java.io.FileReader;
 6 import java.io.IOException;
 7 
 8 public class Test01 {
 9     //这是一个main方法,是程序的入口:
10     public static void main(String[] args) throws IOException {
11         //文件--》程序:
12         //1.有一个文件:----》创建一个File类的对象
13         File f = new File("d:\\Test.txt");
14         //2.利用FileReader这个流,这个“管”怼到源文件上去   ---》创建一个FileReader的流的对象
15         FileReader fr = new FileReader(f);
16         //3.进行操作“吸”的动作  ---》读取动作
17         /*下面的代码我们验证了:如果到了文件的结尾处,那么读取的内容为-1
18         int n1 = fr.read();
19         int n2 = fr.read();
20         int n3 = fr.read();
21         int n4 = fr.read();
22         int n5 = fr.read();
23         int n6 = fr.read();
24         System.out.println(n1);
25         System.out.println(n2);
26         System.out.println(n3);
27         System.out.println(n4);
28         System.out.println(n5);
29         System.out.println(n6);*/
30         //方式1:
31         /*int n = fr.read();
32         while(n!=-1){
33             System.out.println(n);
34             n = fr.read();
35         }*/
36         //方式2:
37         int n;
38         while((n = fr.read())!=-1){
39             System.out.println((char)n);
40         }
41         //4.“管”不用了,就要关闭  ---》关闭流
42         //流,数据库,网络资源,靠jvm本身没有办法帮我们关闭,此时必须程序员手动关闭:
43         fr.close();
44     }
45 }

 

想一次性读取五个字符,不够的话下次再读五个字符:

 1 package com.llh;
 2 
 3 import java.io.File;
 4 import java.io.FileReader;
 5 import java.io.IOException;
 6 
 7 public class Test02 {
 8     //这是一个main方法,是程序的入口:
 9     public static void main(String[] args) throws IOException {
10         //文件--》程序:
11         //1.创建一个File类的对象
12         File f = new File("d:\\Test.txt");
13         //2.创建一个FileReader的流的对象
14         FileReader fr = new FileReader(f);
15         //3.读取动作
16         //引入一个“快递员的小车”,这个“小车”一次拉5个快递:
17         char[] ch = new char[5];//缓冲数组
18         int len = fr.read(ch);//一次读取五个:返回值是这个数组中 的有效长度
19         while(len!=-1){
20             //System.out.println(len);
21             //错误方式:
22             /*for (int i = 0 ;i < ch.length;i++){
23                 System.out.println(ch[i]);
24             }*/
25             //正确方式:
26             /*for (int i = 0 ;i < len;i++){
27                 System.out.println(ch[i]);
28             }*/
29             //正确方式2:将数组转为String:
30             String str = new String(ch,0,len);
31             System.out.print(str);
32             len = fr.read(ch);
33         }
34         //4.关闭流
35         fr.close();
36     }
37 }

 

功能分解2:程序--》文件:FileWriter

一个字符一个字符的向外输出:

 1 import java.io.File;
 2 import java.io.FileWriter;
 3 import java.io.IOException;
 4 /**
 5  * @author : msb-zhaoss
 6  */
 7 public class Test03 {
 8     //这是一个main方法,是程序的入口:
 9     public static void main(String[] args) throws IOException {
10         //1.有个目标文件:
11         File f = new File("d:\\demo.txt");
12         //2.FileWriter管怼到文件上去:
13         FileWriter fw = new FileWriter(f);
14         //3.开始动作:输出动作:
15         //一个字符一个字符的往外输出:
16         String str = "hello你好";
17         for (int i = 0 ;i < str.length();i++){
18             fw.write(str.charAt(i));
19         }
20         //4.关闭流:
21         fw.close();
22     }
23 }

 

发现:

如果目标文件不存在的话,那么会自动创建此文件。

如果目标文件存在的话:

new FileWriter(f)   相当于对原文件进行覆盖操作。

new FileWriter(f,false)  相当于对源文件进行覆盖操作。不是追加。

 new FileWriter(f,true)   对原来的文件进行追加,而不是覆盖。

 

利用缓冲数组:向外输出(利用缓冲数组:)

 1 package com.llh;
 2 
 3 import java.io.File;
 4 import java.io.FileWriter;
 5 import java.io.IOException;
 6 
 7 
 8 public class Test03 {
 9     //这是一个main方法,是程序的入口:
10     public static void main(String[] args) throws IOException {
11         //1.有个目标文件:
12         File f = new File("d:\\demo.txt");
13         //2.FileWriter管怼到文件上去:
14         FileWriter fw = new FileWriter(f,true);
15         //3.开始动作:输出动作:
16         //一个字符一个字符的往外输出:
17         String str = "你好中国";
18         char[] chars = str.toCharArray();
19         fw.write(chars);
20         //4.关闭流:
21         fw.close();
22     }
23 }
 

功能分解3:利用FileReader,FileWriter文件复制

 1 package com.llh;
 2 
 3 import java.io.*;
 4 
 5 
 6 public class Test04 {
 7     //这是一个main方法,是程序的入口:
 8     public static void main(String[] args) throws IOException {
 9         //1.有一个源文件
10         File f1 = new File("d:\\Test.txt");
11         //2.有一个目标文件:
12         File f2 = new File("d:\\Demo.txt");
13         //3.搞一个输入的管 怼到源文件上:
14         FileReader fr = new FileReader(f1);
15         //4.搞一个输出的管,怼到目标文件上:
16         FileWriter fw = new FileWriter(f2);
17         //5.开始动作:
18         //方式1:一个字符一个字符的复制:
19         /*int n = fr.read();
20         while(n!=-1){
21             fw.write(n);
22             n = fr.read();
23         }*/
24         //方式2:利用缓冲字符数组:
25         /*char[] ch = new char[5];
26         int len = fr.read(ch);
27         while(len!=-1){
28             fw.write(ch,0,len);//将缓冲数组中有效长度写出
29             len = fr.read(ch);
30         }*/
31         //方式3:利用缓冲字符数组,将数组转为String写出。
32         char[] ch = new char[5];
33         int len = fr.read(ch);
34         while(len!=-1){
35             String s = new String(ch,0,len);
36             fw.write(s);
37             len = fr.read(ch);
38         }
39         //6.关闭流:(关闭流的时候,倒着关闭,后用先关)
40         fw.close();
41         fr.close();
42     }
43 }

 

警告:不要用字符流去操作非文本文件

文本文件:.txt   .java  .c  .cpp  ---》建议使用字符流操作

非文本文件:.jpg,  .mp3  ,   .mp4 , .doc  , .ppt  ---》建议使用字节流操作

利用try-catch-finally处理异常方式

 

 1 package com.llh;
 2 
 3 import java.io.*;
 4 
 5 
 6 public class Test04 {
 7     //这是一个main方法,是程序的入口:
 8     public static void main(String[] args)  {
 9         //1.有一个源文件
10         File f1 = new File("d:\\Test.txt");
11         //2.有一个目标文件:
12         File f2 = new File("d:\\Demo.txt");
13         //3.搞一个输入的管 怼到源文件上:
14         FileReader fr = null;
15         FileWriter fw = null;
16         try {
17             fr = new FileReader(f1);
18             //4.搞一个输出的管,怼到目标文件上:
19             fw = new FileWriter(f2);
20             //5.开始动作:
21             char[] ch = new char[5];
22             int len = fr.read(ch);
23             while(len!=-1){
24                 String s = new String(ch,0,len);
25                 fw.write(s);
26                 len = fr.read(ch);
27             }
28         } catch (FileNotFoundException e) {
29             e.printStackTrace();
30         } catch (IOException e) {
31             e.printStackTrace();
32         } finally {
33             //6.关闭流:(关闭流的时候,倒着关闭,后用先关)
34             try {
35                 if(fw!=null){//防止空指针异常
36                     fw.close();
37                 }
38             } catch (IOException e) {
39                 e.printStackTrace();
40             }
41             try {
42                 if(fr!=null){
43                     fr.close();
44                 }
45             } catch (IOException e) {
46                 e.printStackTrace();
47             }
48         }
49     }
50 }

 

FileInputStream读取文件中内容

【1】读取文本文件:

 1 package com.llh;
 2 
 3 import java.io.File;
 4 import java.io.FileInputStream;
 5 import java.io.FileNotFoundException;
 6 import java.io.IOException;
 7 
 8 
 9 public class Test01 {
10     //这是一个main方法,是程序的入口:
11     public static void main(String[] args) throws IOException {
12         //功能:利用字节流将文件中内容读到程序中来:
13         //1.有一个源文件:
14         File f = new File("D:\\Test.txt");
15         //2.将一个字节流这个管 怼  到 源文件上:
16         FileInputStream fis = new FileInputStream(f);
17         //3.开始读取动作
18         /*
19         细节1:
20         文件是utf-8进行存储的,所以英文字符 底层实际占用1个字节
21         但是中文字符,底层实际占用3个字节。
22         细节2:
23         如果文件是文本文件,那么就不要使用字节流读取了,建议使用字符流。
24         细节3:
25         read()读取一个字节,但是你有没有发现返回值是 int类型,而不是byte类型?
26         read方法底层做了处理,让返回的数据都是“正数”
27         就是为了避免如果字节返回的是-1的话,那到底是读入的字节,还是到文件结尾呢。
28          */
29         int n = fis.read();
30         while(n!=-1){
31             System.out.println(n);
32             n = fis.read();
33         }
34         //4.关闭流:
35         fis.close();
36     }
37 }

 

【2】利用字节流读取非文本文件:(以图片为案例:)--》一个字节一个字节的读取:

 1 package com.llh;
 2 
 3 import java.io.File;
 4 import java.io.FileInputStream;
 5 import java.io.IOException;
 6 
 7 
 8 public class Test02 {
 9     //这是一个main方法,是程序的入口:
10     public static void main(String[] args) throws IOException {
11         //功能:利用字节流将文件中内容读到程序中来:
12         //1.有一个源文件:
13         File f = new File("D:\\LOL.jpg");
14         //2.将一个字节流这个管 怼  到 源文件上:
15         FileInputStream fis = new FileInputStream(f);
16         //3.开始读取动作
17         int count = 0;//定义一个计数器,用来计读入的字节的个数
18         int n = fis.read();
19         while(n!=-1){
20             count++;
21             System.out.println(n);
22             n = fis.read();
23         }
24         System.out.println("count="+count);
25         //4.关闭流:
26         fis.close();
27     }
28 }

 

【3】利用字节类型的缓冲数组:

 1 import java.io.File;
 2 import java.io.FileInputStream;
 3 import java.io.IOException;
 4 /**
 5  * @author : msb-zhaoss
 6  */
 7 public class Test03 {
 8     //这是一个main方法,是程序的入口:
 9     public static void main(String[] args) throws IOException {
10         //功能:利用字节流将文件中内容读到程序中来:
11         //1.有一个源文件:
12         File f = new File("D:\\LOL.jpg");
13         //2.将一个字节流这个管 怼  到 源文件上:
14         FileInputStream fis = new FileInputStream(f);
15         //3.开始读取动作
16         //利用缓冲数组:(快递员的小车)
17         byte[] b = new byte[1024*6];
18         int len = fis.read(b);//len指的就是读取的数组中的有效长度
19         while(len!=-1){
20             //System.out.println(len);
21             for(int i = 0;i<len;i++){
22                 System.out.println(b[i]);
23             }
24             len = fis.read(b);
25         }
26         //4.关闭流:
27         fis.close();
28     }
29 }

 

FileInputStream,FileOutputStream完成非文本文件的复制

【1】读入一个字节,写出一个字节:

 1 package com.llh;
 2 
 3 import java.io.*;
 4 
 5 
 6 public class Test04 {
 7     //这是一个main方法,是程序的入口:
 8     public static void main(String[] args) throws IOException {
 9         //功能:完成图片的复制:
10         //1.有一个源图片
11         File f1 = new File("d:\\LOL.jpg");
12         //2.有一个目标图片:
13         File f2 = new File("d:\\LOL2.jpg");
14         //3.有一个输入的管道 怼 到 源文件:
15         FileInputStream fis = new FileInputStream(f1);
16         //4.有一个输出的管道 怼到  目标文件上:
17         FileOutputStream fos = new FileOutputStream(f2);
18         //5.开始复制:(边读边写)
19         int n = fis.read();
20         while(n!=-1){
21             fos.write(n);
22             n = fis.read();
23         }
24         //6.关闭流:(倒着关闭流,先用后关)
25         fos.close();
26         fis.close();
27     }
28 }

 

【2】利用缓冲字节数组:

 1 package com.llh;
 2 
 3 import java.io.File;
 4 import java.io.FileInputStream;
 5 import java.io.FileOutputStream;
 6 import java.io.IOException;
 7 
 8 
 9 public class Test05 {
10     //这是一个main方法,是程序的入口:
11     public static void main(String[] args) throws IOException {
12         //功能:完成图片的复制:
13         //1.有一个源图片
14         File f1 = new File("d:\\LOL.jpg");
15         //2.有一个目标图片:
16         File f2 = new File("d:\\LOL2.jpg");
17         //3.有一个输入的管道 怼 到 源文件:
18         FileInputStream fis = new FileInputStream(f1);
19         //4.有一个输出的管道 怼到  目标文件上:
20         FileOutputStream fos = new FileOutputStream(f2);
21         //5.开始复制:(边读边写)
22         //利用缓冲数组:
23         byte[] b = new byte[1024*8];
24         int len = fis.read(b);
25         while(len!=-1){
26             fos.write(b,0,len);
27             len = fis.read(b);
28         }
29         //6.关闭流:(倒着关闭流,先用后关)
30         fos.close();
31         fis.close();
32     }
33 }

 

缓冲字节流(处理流)-BufferedInputStream ,BufferedOutputStream

【1】读入一个字节,写出一个字节:

 

【2】利用缓冲字节数组: 

 

 

【3】利用缓冲区:

 

想要完成上面的效果,单纯的靠FileInputStream,FileOutputStream是不可以完成的,这个时候就需要功能的加强,

这个加强就需要引入新的流(在FileInputStream,FileOutputStream外面再套一层流):BufferedInputStream ,BufferedOutputStream. ----->处理流

 

代码:

 1 package com.llh;
 2 
 3 import java.io.*;
 4 
 5 
 6 public class Test06 {
 7     //这是一个main方法,是程序的入口:
 8     public static void main(String[] args) throws IOException {
 9         //1.有一个源图片
10         File f1 = new File("d:\\LOL.jpg");
11         //2.有一个目标图片:
12         File f2 = new File("d:\\LOL2.jpg");
13         //3.有一个输入的管道 怼 到 源文件:
14         FileInputStream fis = new FileInputStream(f1);
15         //4.有一个输出的管道 怼到  目标文件上:
16         FileOutputStream fos = new FileOutputStream(f2);
17         //5.功能加强,在FileInputStream外面套一个管:BufferedInputStream:
18         BufferedInputStream bis = new BufferedInputStream(fis);
19         //6.功能加强,在FileOutputStream外面套一个管:BufferedOutputStream:
20         BufferedOutputStream bos = new BufferedOutputStream(fos);
21         //7.开始动作 :
22         byte[] b = new byte[1024*6];
23         int len = bis.read(b);
24         while(len!=-1){
25             bos.write(b,0,len);
26             /* bos.flush(); 底层已经帮我们做了刷新缓冲区的操作,不用我们手动完成:底层调用flushBuffer()*/
27             len = bis.read(b);
28         }
29         //8.关闭流:
30         //倒着关:
31         //如果处理流包裹着节点流的话,那么其实只要关闭高级流(处理流),那么里面的字节流也会随之被关闭。
32         bos.close();
33         bis.close();
34         /*fos.close();
35         fis.close();*/
36     }
37 }

 

比对非文本文件复制的三种方法的效率

【1】读入一个字节,写出一个字节:

  

【2】利用缓冲字节数组:

  

【3】利用缓冲区:

 

 

代码:

 1 package com.llh;
 2 
 3 import java.io.*;
 4 
 5 
 6 public class Test06 {
 7     //这是一个main方法,是程序的入口:
 8     public static void main(String[] args) throws IOException {
 9         //1.有一个源图片
10         File f1 = new File("d:\\LOL.jpg");
11         //2.有一个目标图片:
12         File f2 = new File("d:\\LOL2.jpg");
13         //3.有一个输入的管道 怼 到 源文件:
14         FileInputStream fis = new FileInputStream(f1);
15         //4.有一个输出的管道 怼到  目标文件上:
16         FileOutputStream fos = new FileOutputStream(f2);
17         //5.功能加强,在FileInputStream外面套一个管:BufferedInputStream:
18         BufferedInputStream bis = new BufferedInputStream(fis);
19         //6.功能加强,在FileOutputStream外面套一个管:BufferedOutputStream:
20         BufferedOutputStream bos = new BufferedOutputStream(fos);
21         //7.开始动作 :
22         long startTime = System.currentTimeMillis();
23         byte[] b = new byte[1024];
24         int len = bis.read(b);
25         while(len!=-1){
26             bos.write(b,0,len);
27             /* bos.flush(); 底层已经帮我们做了刷新缓冲区的操作,不用我们手动完成:底层调用flushBuffer()*/
28             len = bis.read(b);
29         }
30         long endTime = System.currentTimeMillis();
31         System.out.println("复制完成的时间为:"+(endTime-startTime));
32         //8.关闭流:
33         //倒着关:
34         //如果处理流包裹着节点流的话,那么其实只要关闭高级流(处理流),那么里面的字节流也会随之被关闭。
35         bos.close();
36         bis.close();
37         /*fos.close();
38         fis.close();*/
39     }
40 }
 

缓冲字符流(处理流)-BufferedReader,BufferedWriter完成文本文件的复制

 1 package com.llh;
 2 
 3 import java.io.*;
 4 
 5 
 6 public class Test07 {
 7     //这是一个main方法,是程序的入口:
 8     public static void main(String[] args) throws IOException {
 9         //1.有一个源文件:
10         File f1 = new File("d:\\Test.txt");
11         //2.有一个目标文件:
12         File f2 = new File("d:\\Demo.txt");
13         //3.需要一个管 怼到 源文件:
14         FileReader fr = new FileReader(f1);
15         //4.需要一根管怼到目标文件:
16         FileWriter fw = new FileWriter(f2);
17         //5.套一根管在输入字符流外面:
18         BufferedReader br = new BufferedReader(fr);
19         //6.套一根管在输出字符流外面:
20         BufferedWriter bw = new BufferedWriter(fw);
21         //7.开始动作:
22         //方式1:读取一个字符,输出一个字符:
23         /*int n = br.read();
24         while(n!=-1){
25             bw.write(n);
26             n = br.read();
27         }*/
28         //方式2:利用缓冲数组:
29         /*char[] ch = new char[30];
30         int len = br.read(ch);
31         while(len!=-1){
32             bw.write(ch,0,len);
33             len = br.read(ch);
34         }*/
35         //方式3:读取String:
36         String str = br.readLine();//每次读取文本文件中一行,返回字符串
37         while(str!=null){
38             bw.write(str);
39             //在文本文件中应该再写出一个换行:
40             bw.newLine();//新起一行
41             str = br.readLine();
42         }
43         //8.关闭流
44         bw.close();
45         br.close();
46     }
47 }

转换流-InputStreamReader,OutputStreamWriter

【1】转换流:作用:将字节流和字符流进行转换。

【2】转换流  属于 字节流还是字符流?属于字符流

InputStreamReader  :字节输入流 ---》字符的输入流

OutputStreamWriter  : 字符输出流 --》字节的输出流

 【3】图解: 

【4】将输入的字节流转换为输入的字符流,然后完成文件--》程序 :

 1 package com.llh;
 2 
 3 import java.io.*;
 4 
 5 
 6 public class Test01 {
 7     //这是一个main方法,是程序的入口:
 8     public static void main(String[] args) throws IOException {
 9         //文件---》程序:
10         //1.有一个源文件:
11         File f = new File("d:\\Test.txt");
12         //2.需要一个输入的字节流接触文件:
13         FileInputStream fis = new FileInputStream(f);
14         //3.加入一个转换流,将字节流转换为字符流:(转换流属于一个处理流)
15         //将字节转换为字符的时候,需要指定一个编码,这个编码跟文件本身的编码格式统一
16         //如果编码格式不统一的话,那么在控制台上展示的效果就会出现乱码
17         //InputStreamReader isr = new InputStreamReader(fis,"utf-8");
18         //获取程序本身的编码--》utf-8
19         InputStreamReader isr = new InputStreamReader(fis);
20         //4.开始动作,将文件中内容显示在控制台:
21         char[] ch = new char[20];
22         int len = isr.read(ch);
23         while(len!=-1){
24             //将缓冲数组转为字符串在控制台上打印出来
25             System.out.print(new String(ch,0,len));
26             len = isr.read(ch);
27         }
28         //5.关闭流:
29         isr.close();
30     }
31 }

转换流-InputStreamReader,OutputStreamWriter实现文本文件的复制

 1 package com.llh;
 2 
 3 import java.io.*;
 4 
 5 
 6 public class Test02 {
 7     //这是一个main方法,是程序的入口:
 8     public static void main(String[] args) throws IOException {
 9         //1.有一个源文件
10         File f1 = new File("d:\\Test.txt");
11         //2.有一个目标文件:
12         File f2 = new File("d:\\Demo.txt");
13         //3.输入方向:
14         FileInputStream fis = new FileInputStream(f1);
15         InputStreamReader isr = new InputStreamReader(fis,"utf-8");
16         //4.输出方向:
17         FileOutputStream fos = new FileOutputStream(f2);
18         OutputStreamWriter osw = new OutputStreamWriter(fos,"gbk");
19         //5.开始动作:
20         char[] ch = new char[20];
21         int len = isr.read(ch);
22         while(len!=-1){
23             osw.write(ch,0,len);
24             len = isr.read(ch);
25         }
26         //6.关闭流:
27         osw.close();
28         isr.close();
29     }
30 }

 

System类对IO流的支持

 

【1】System的属性:

System.in  : “标准”输入流。---》默认情况下  从键盘输入

System.out  :“标准”输出流。 ---》默认情况下,输出到控制台。

 

【2】System.in :“标准”输入流。---》默认情况下  从键盘输入

 1 public class Test01 {
 2     //这是一个main方法,是程序的入口:
 3     public static void main(String[] args) throws IOException {
 4         //得到的是标准的输入流:--》从键盘输入:
 5         //InputStream in = System.in;
 6         //调用方法:
 7         //int n = in.read();//read方法等待键盘的录入,所以这个方法是一个阻塞方法。
 8         //System.out.println(n);
 9         //以前案例:从键盘录入一个int类型的数据:
10         //从上面的代码证明,键盘录入实际上是:System.in
11         //形象的理解:System.in管,这个管怼到键盘上去了,所以你从键盘录入的话,就从这个管到程序中了
12         //Scanner的作用:扫描器:起扫描作用的,扫键盘的从这根管出来的数据
13         /*Scanner sc = new Scanner(System.in);
14         int i = sc.nextInt();
15         System.out.println(i);*/
16         //既然Scanner是扫描的作用,不一定非得扫 System.in进来的东西,还可以扫描其他管的内容:
17         Scanner sc = new Scanner(new FileInputStream(new File("d:\\Test.txt")));
18         while(sc.hasNext()){
19             System.out.println(sc.next());
20         }
21     }
22 }

 

【3】System.out  : 返回的输出流 、 打印流(PrintStream)

 1 package com.llh;
 2 
 3 import java.io.PrintStream;
 4 
 5 
 6 public class Test02 {
 7     //这是一个main方法,是程序的入口:
 8     public static void main(String[] args) {
 9         //写到控制台:
10         PrintStream out = System.out;
11         //调用方法:
12         out.print("你好1");//直接在控制台写出,但是不换行
13         out.print("你好2");
14         out.print("你好3");
15         out.print("你好4");
16         out.println("我是中国人1");//直接在控制台写出,并且换行操作
17         out.println("我是中国人2");
18         out.println("我是中国人3");
19         out.println("我是中国人4");
20         System.out.println("你是");
21         System.out.print("中国人");
22     }
23 }

 

 

练习:键盘录入内容输出到文件中

【1】解决思路: 

【2】代码:

 1 package com.llh;
 2 
 3 import java.io.*;
 4 
 5 public class Test03 {
 6     //这是一个main方法,是程序的入口:
 7     public static void main(String[] args) throws IOException {
 8         //1.先准备输入方向:
 9         //键盘录入:
10         InputStream in = System.in;//属于字节流
11         //字节流--》字符流:
12         InputStreamReader isr = new InputStreamReader(in);
13         //在isr外面再套一个缓冲流:
14         BufferedReader br = new BufferedReader(isr);
15         //2.再准备输出方向:
16         //准备目标文件
17         File f = new File("d:\\Demo1.txt");
18         FileWriter fw = new FileWriter(f);
19         BufferedWriter bw = new BufferedWriter(fw);
20         //3.开始动作:
21         String s = br.readLine();
22         while(!s.equals("exit")){
23             bw.write(s);
24             bw.newLine();//文件中换行
25             s = br.readLine();
26         }
27         //4.关闭流:
28         bw.close();
29         br.close();
30     }
31 }

 

数据流-DataInputStream,DataOutputStream

【1】数据流:用来操作基本数据类型和字符串的

【2】

DataInputStream:将文件中存储的基本数据类型和字符串  写入  内存的变量中

DataOutputStream:  将内存中的基本数据类型和字符串的变量 写出  文件中

【3】代码:

利用DataOutputStream向外写出变量:

 1 public class Test01 {
 2     //这是一个main方法,是程序的入口:
 3     public static void main(String[] args) throws IOException {
 4         //DataOutputStream:  将内存中的基本数据类型和字符串的变量 写出  文件中
 5         /*File f = new File("d:\\Demo2.txt");
 6         FileOutputStream fos = new FileOutputStream(f);
 7         DataOutputStream dos = new DataOutputStream(fos);*/
 8         DataOutputStream dos = new DataOutputStream(new FileOutputStream(new File("d:\\Demo2.txt")));
 9         //向外将变量写到文件中去:
10         dos.writeUTF("你好");
11         dos.writeBoolean(false);
12         dos.writeDouble(6.9);
13         dos.writeInt(82);
14         //关闭流:
15         dos.close();
16     }
17 }

在Demo2.txt文件中,我们看到: 

发现:这个内容我们看不懂,是给程序看的

 

所以下面我们开始读取的程序:

 1 package com.llh;
 2 
 3 import java.io.*;
 4 
 5 
 6 public class Test02 {
 7     //这是一个main方法,是程序的入口:
 8     public static void main(String[] args) throws IOException {
 9         //DataInputStream:将文件中存储的基本数据类型和字符串  写入  内存的变量中
10         DataInputStream dis = new DataInputStream(new FileInputStream(new File("d:\\Demo2.txt")));
11         //将文件中内容读取到程序中来:
12         System.out.println(dis.readUTF());
13         System.out.println(dis.readBoolean());
14         System.out.println(dis.readDouble());
15         System.out.println(dis.readInt());
16         //关闭流:
17         dis.close();
18     }
19 }

 

结果: 

 

验证:那个文件,我们看不懂,程序看得懂

要求:

写出的类型跟读入的类型 必须 要匹配!

 

对象流-ObjectInputStream,ObjectOutputStream

【1】对象流:ObjectInputStream,ObjectInputStream

用于存储和读取基本数据类型数据或对象的处理流。

它的强大之处就是可以把Java中的对象写入到数据源中,也能把对象从数据源中还原回来。

 

【2】序列化和反序列化:

ObjectOutputStream 类 : 把内存中的Java对象转换成平台无关的二进制数据,从而允许把这种二进制数据持久地保存在磁盘上,或通过网络将这种二进制数据传输到另一个网络节点。----》序列化

用ObjectInputStream类 : 当其它程序获取了这种二进制数据,就可以恢复成原来的Java对象。----》反序列化

 

【3】代码:操作字符串对象:

首先将一个字符串对象写到文件中去:----》序列化

 1 public class Test01 {
 2     //这是一个main方法,是程序的入口:
 3     public static void main(String[] args) throws IOException {
 4         ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(new File("d:\\Demo3.txt")));
 5         //将内存中的字符串写出到文件中:
 6         oos.writeObject("你好");
 7         //关闭流:
 8         oos.close();
 9     }
10 }

 

查看文件: 

 

我们看不懂文件的内容,但是程序是可以看懂的,所以可以写一个程序读文件中内容:----》反序列化

 1 public class Test02 {
 2     //这是一个main方法,是程序的入口:
 3     public static void main(String[] args) throws IOException, ClassNotFoundException {
 4         //将文件中保存的字符串 读入到 内存:
 5         ObjectInputStream ois = new ObjectInputStream(new FileInputStream(new File("d:\\Demo3.txt")));
 6         //读取:
 7         String s = (String)(ois.readObject());
 8         System.out.println(s);
 9         //关闭流:
10         ois.close();
11     }
12 }

 

控制台: 

  

【4】代码:操作自定义类的对象:

自定义的Person类:

 1 public class Person {
 2     private String name;
 3     private int age;
 4     public String getName() {
 5         return name;
 6     }
 7     public void setName(String name) {
 8         this.name = name;
 9     }
10     public int getAge() {
11         return age;
12     }
13     public void setAge(int age) {
14         this.age = age;
15     }
16     public Person() {
17     }
18     public Person(String name, int age) {
19         this.name = name;
20         this.age = age;
21     }
22 }

 

测试类:

 1 public class Test01 {
 2     //这是一个main方法,是程序的入口:
 3     public static void main(String[] args) throws IOException {
 4         //序列化:将内存中对象 ---》 文件:
 5         //有一个对象:
 6         Person p = new Person("lili",19);
 7         //有对象流:
 8         ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(new File("d:\\Demo4.txt")));
 9         //向外写:
10         oos.writeObject(p);
11         //关闭流:
12         oos.close();
13     }
14 }

 

运行的时候发现出现异常:

 

出现异常的原因:

你想要序列化的那个对象对应的类,必须要实现一个接口:

 

接口内部,什么都没有,这种接口叫 标识接口。

起到标识作用,标识什么呢?只要实现这个接口的类的对象才能序列化,否则不可以。

 

解决办法:将Person 实现这个标识接口就可以:

 1 public class Person implements Serializable {
 2     private String name;
 3     private int age;
 4     public String getName() {
 5         return name;
 6     }
 7     public void setName(String name) {
 8         this.name = name;
 9     }
10     public int getAge() {
11         return age;
12     }
13     public void setAge(int age) {
14         this.age = age;
15     }
16     public Person() {
17     }
18     public Person(String name, int age) {
19         this.name = name;
20         this.age = age;
21     }
22 }

测试:发现序列化成功,Person具备了序列化的能力。

 

这个二进制数据我们看不懂,但是程序可以看懂,所以我们可以用程序实现 反序列化操作:

将这个对象 恢复到内存中来:

 1 public class Test02 {
 2     //这是一个main方法,是程序的入口:
 3     public static void main(String[] args) throws IOException, ClassNotFoundException {
 4         ObjectInputStream ois = new ObjectInputStream(new FileInputStream(new File("d:\\Demo4.txt")));
 5         //读入内存:
 6         Person p = (Person)(ois.readObject());
 7         System.out.println(p/*.toString()*/);
 8         //关闭流:
 9         ois.close();
10     }
11 }

结果:

因为我们没有重写toString方法,所以结果为:

 

证明了反序列化成功:   将二进制数据 --》内存

 

【5】serialVersionUID:

凡是实现Serializable接口(标识接口)的类都有一个表示序列化版本标识符的静态常量:

➢private static final long serialVersionUID;

➢serialVersionUID用来表明类的不同版本间的兼容性。简言之,其目的是以序列化对象进行版本控制,有关各版本反序加化时是否兼容。

➢如果类没有显示定义这个静态变量,它的值是Java运行时环境根据类的内部细节自动生成的。若类的实例变量做了修改,serialVersionUID 可能发生变化。故建议,显式声明。

➢简单来说,Java的序列化机制是通过在运行时判断类的serialVersionUID来验证版本一致性的。在进行反序列化时,JVM会把传来的字节流中的serialVersionUID与本地相应实体类的serialVersionUID进行比较,如果相同就认为是一致的,可以进行反序列化,否则就会出现序列化版本不一致的异常。(InvalidCastException)

 

我现在在Person类中加入toString方法:

 1 public class Person implements Serializable {
 2     private String name;
 3     private int age;
 4     public String getName() {
 5         return name;
 6     }
 7     public void setName(String name) {
 8         this.name = name;
 9     }
10     public int getAge() {
11         return age;
12     }
13     public void setAge(int age) {
14         this.age = age;
15     }
16     public Person() {
17     }
18     public Person(String name, int age) {
19         this.name = name;
20         this.age = age;
21     }
22     @Override
23     public String toString() {
24         return "Person{" +
25                 "name='" + name + '\'' +
26                 ", age=" + age +
27                 '}';
28     }
29 }

 

再次运行测试类:

出现异常:

 

出现异常的原因:

 

解决:给这个类 加入一个 序列号:serialVersionUID

 

【6】IDEA中配置序列化版本号:

 

在Person类上:alt+enter:

  

回车即可生成

  

【7】序列化细节:

(1)被序列化的类的内部的所有属性,必须是可序列化的 (基本数据类型都是可序列化的)

 

 

(2)static,transient修饰的属性 不可以被序列化。

 

 1 public class Person implements Serializable {
 2     private static final long serialVersionUID = 8027651838638826533L;
 3     private transient String name;
 4     private static int age;
 5     private Famaily f = new Famaily();
 6     public String getName() {
 7         return name;
 8     }
 9     public void setName(String name) {
10         this.name = name;
11     }
12     public int getAge() {
13         return age;
14     }
15     public void setAge(int age) {
16         this.age = age;
17     }
18     public Person() {
19     }
20     @Override
21     public String toString() {
22         return "Person{" +
23                 "name='" + name + '\'' +
24                 ", f=" + f + ",age=" + age +
25                 '}';
26     }
27 }

 

结果:

以上就是Java IO流的介绍。