• File类和IO流的相关面试(二)


    一.IO流

    1.什么是IO?

    I:Input,数据的读取,数据的输入

            数据的输入,可以是从键盘输入,从文件读取,从网络接收...

    O:Output,写数据,数据的输出

            数据的输出,可以是输出到控制台,写到文件,发生到网络中...

    2.在java中要进行IO操作需要使用IO流类来完成

    现在说的是传统的IO,阻塞式IO。

    后面还会有NIO(非阻塞式IO)和AIO(异步IO)

    基础阶段的IO流类:

    (1)按照方向分

    输入流类:

            InputStream,Reader系列

    输出流类:

            OutputStream,Writer系列

    (2)按照操作数据的方式,单位分:

    字节流:

            InputStream,OutputStream系列

    字符流:

            Reader,Writer

    (3)按照角色不同

    节点流:

            和数据的源头/目的地连接的IO流:

                    FileInputStream,FileOutputStream,FileReader,FileWriter:读写文件

                    ByteArrayInputStream,ByteArrayOutputStream,CharArrayReader,CharArrayWriter:读写数组

                    ....

    处理流/包装流/装饰流:

            它是在其他IO流基础上,增加功能用的

                    BufferedInputStream,BufferedOutputStream,BufferedReader,BufferedWriter,给其他IO流增加缓冲功能。

                    InputStreamReader,OutputStreamReader,给其他IO流转换类型用的,或者给其他IO编码,解码用的

                    ...

    区分它们的依据是创建它们的对象的方式,如果是处理流,创建调用它们的构造器创建对象时,必须传入另一个IO流对象。

            FileInputStream(String name):节点流,参数不是IO流类型

            BufferedInputStream(InputStream in):处理流,参数是IO流类型

    3.IO流类的四个基类,四个超类,四个父类,其他的IO流都是从它们演变过来,是它们的子类。

            字节输入流:InputStream

            字节输出流:OutputStream

            字符输入流:Reader

            字符输出流:Writer

    代码如下:

    1. public class TestIO {
    2. @Test
    3. public void test02(){
    4. Scanner input = new Scanner(System.in);//从键盘输入
    5. System.out.print("输入一个姓名:");
    6. String name = input.next();
    7. System.out.println("name = " + name);
    8. input.close();
    9. }
    10. @Test
    11. public void test01(){
    12. System.out.println("hello");//这个也是IO操作,把数据输出到控制台
    13. }
    14. }

    4.文件IO流(输入流)

    (1)FileInputStream类

            如果文件不存在,会报错java.io.FileNotFoundException:xxx(系统找不到指定的文件)

    父类:InputStream类(字节输入流)

    public int read():从输入流读取一个字节。返回读取的字节值。虽然读取了一个字节,但是会自动提升为int类型。如果已经到达流末尾,没有数据可读,则返回-1

    public int read(byte[] b):从输入流中读取一些字节数,并将它们存储到字节数组 b 中。每次最多读取b.length个字节。返回实际读取的字节个数。如果已经到达流末尾,没有数据可读,则返回-1.

    public int read(byte[] b,int off,int len):从输入流中读取一些字节数,并将它们存储到字节数组 b 中,从b[off]开始存储,返回实际读取的字节个数。如果已经到达流末尾,没有数据可读,则返回-1

    public void close():关闭此输入流并释放与此流相关联的任何系统资源。

    (2)FileOutputStream类

            如果文件不存在,会自动创建

            如果文件已存在,会覆盖原来的内容

            如果要追加,在创建FileOutputStream类对象时,加一个参数true

    它的父类是OutputStream类,字节输出流

    public void write(int b):将指定的字节输出。虽然参数为int类型四个字节,但是只会保留一个字节的信息写出。

    public void write(byte[] b):将b.length字节从指定的字节数组写入此输出流。

    public void write(byte[] b,int off,int len):从指定的字节数组写入 len 字节,从偏移量 off 开始输出到此输出流。

    public void close():关闭此输入流并释放与此流相关联的任何系统资源。

    (3)FileReader类

    父类 Reader类:

    public int read(): 从输入流读取一个字符。 虽然读取了一个字符,但是会自动提升为int类型。返回该字符的Unicode编码值。如果已经到达流末尾了,则返回-1。

    public int read(char[] cbuf): 从输入流中读取一些字符,并将它们存储到字符数组 cbuf中 。每次最多读取cbuf.length个字符。返回实际读取的字符个数。如果已经到达流末尾,没有数据可读,则返回-1。

    public int read(char[] cbuf,int off,int len):从输入流中读取一些字符,并将它们存储到字符数组 cbuf中,从cbuf[off]开始的位置存储。每次最多读取len个字符。返回实际读取的字符个数。如果已经到达流末尾,没有数据可读,则返回-1。

    public void close():关闭此流并释放与此流相关联的任何系统资源。

    (4)FileWriter类

    父类Writer类

    public void write(int c):写入单个字符。

    public void write(char[] cbuf):写入字符数组。

    public void write(char[] cbuf, int off, int len):写入字符数组的某一部分,off数组的开始索引,len写的字符个数。

    public void write(String str):写入字符串。

    public void write(String str, int off, int len):写入字符串的某一部分,off字符串的开始索引,len写的字符个数。

    public void flush():刷新该流的缓冲。

    public void close():关闭此流,但要先刷新它。

    5.IO流操作的步骤:

    ① 先选择合适的IO流类,创建它的对象

    ② 读或写操作

    ③ 关闭IO流

            因为IO流类的对象会在JVM内存中开辟空间。这个部分的内存,可以有JVM的GC自动回收

            但是IO流的操作还会在JVM内存的外部开辟空间,因为IO操作需要用到操作系统的一些函数,这些内存GC是无法回收的。

            那么要通过调用close方法,通知操作系统释放对应的内存。

    代码如下:

    FileInputStream类

    1. public class TestFileInputStream {
    2. @Test
    3. public void test04() throws IOException {
    4. /*
    5. 要以字节的方式读取一个文件:当前模块下的文件1.txt
    6. */
    7. FileInputStream fis = new FileInputStream("1.txt");//相对路径
    8. byte[] data = new byte[2];
    9. while(true) {
    10. int len = fis.read(data);//数据读取到data字节数组中
    11. if(len == -1){
    12. break;
    13. }
    14. System.out.println("len = " + len);
    15. // System.out.println(Arrays.toString(data));//[97,98]
    16. System.out.println(new String(data,0,len));//本次读取了几个字节,就用几个字节构造字符串
    17. }
    18. fis.close();
    19. }
    20. @Test
    21. public void test03() throws IOException {
    22. /*
    23. 要以字节的方式读取一个文件:当前模块下的文件1.txt
    24. */
    25. FileInputStream fis = new FileInputStream("1.txt");//相对路径
    26. while(true){
    27. int read = fis.read();
    28. if(read == -1){
    29. break;
    30. }
    31. System.out.println(read);
    32. }
    33. fis.close();
    34. }
    35. @Test
    36. public void test02() throws IOException {
    37. /*
    38. 要以字节的方式读取一个文件:当前模块下的文件1.txt
    39. */
    40. FileInputStream fis = new FileInputStream("1.txt");//相对路径
    41. byte[] data = new byte[2];
    42. int len = fis.read(data);//数据读取到data字节数组中
    43. System.out.println("len = " + len);//2
    44. System.out.println(Arrays.toString(data));//[97,98]
    45. len = fis.read(data);
    46. System.out.println("len = " + len);//1
    47. System.out.println(Arrays.toString(data));//[99, 98] 这个98是上次读取的,本次只读取了一个字节
    48. len = fis.read(data);
    49. System.out.println("len = " + len);//-1
    50. System.out.println(Arrays.toString(data));//[99, 98] 这个99,98是上次读取的,本次只读取了一个字节
    51. fis.close();
    52. }
    53. @Test
    54. public void test01() throws IOException {
    55. /*
    56. 要以字节的方式读取一个文件:当前模块下的文件1.txt
    57. */
    58. FileInputStream fis = new FileInputStream("1.txt");//相对路径
    59. System.out.println(fis.read());
    60. System.out.println(fis.read());
    61. System.out.println(fis.read());
    62. System.out.println(fis.read());
    63. fis.close();
    64. }
    65. }

    FileOutputStream类(代码演示)

    1. public class TestFileOutputStream {
    2. @Test
    3. public void test03() throws IOException {
    4. //输出hello到1.txt,接着原来内容写
    5. FileOutputStream fos = new FileOutputStream("1.txt",true);//已存在 true表示追加
    6. //把字符串转为字节数组
    7. fos.write("hello".getBytes());
    8. fos.close();
    9. }
    10. @Test
    11. public void test02() throws IOException {
    12. //输出houxiaosun到1.txt
    13. FileOutputStream fos = new FileOutputStream("1.txt");//已存在
    14. //把字符串转为字节数组
    15. fos.write("houxiaosun".getBytes());
    16. fos.close();
    17. }
    18. @Test
    19. public void test01() throws IOException {
    20. //输出chai到1.txt
    21. FileOutputStream fos = new FileOutputStream("1.txt");
    22. //把字符串转为字节数组
    23. fos.write("chai".getBytes());
    24. fos.close();
    25. }
    26. }

    FileReader类

    1. public class TestFileReader {
    2. @Test
    3. public void test02()throws IOException{
    4. /*
    5. 要以字符的方式读取一个文件:当前模块下的文件1.txt
    6. */
    7. FileReader fr = new FileReader("1.txt");
    8. char[] data = new char[2];
    9. int len;
    10. while((len = fr.read(data)) != -1){
    11. System.out.println(new String(data,0,len));
    12. }
    13. fr.close();
    14. }
    15. @Test
    16. public void test01()throws IOException {
    17. /*
    18. 要以字节的方式读取一个文件:当前模块下的文件1.txt
    19. */
    20. FileInputStream fis = new FileInputStream("1.txt");//相对路径
    21. byte[] data = new byte[3];
    22. while(true) {
    23. int len = fis.read(data);//数据读取到data字节数组中
    24. if(len == -1){
    25. break;
    26. }
    27. System.out.println(new String(data,0,len));//本次读取了几个字节,就用几个字节构造字符串
    28. //new String就是解码操作,但是发现乱码了
    29. }
    30. fis.close();
    31. }
    32. }

    FileWriter类

    1. public class TestFileWriter {
    2. @Test
    3. public void test()throws IOException {
    4. //输出一句话到1.txt
    5. FileWriter fw = new FileWriter("1.txt",true);//true表示追加
    6. fw.write("我爱你");
    7. fw.close();
    8. }
    9. }

    练习题:使用文件字节流复制一个视频文件

    例如:D:\atguigu\javaee\JavaSE20220106\video\day0107_01video\day0107_01JavaSE阶段学什么.avi

    复制到当前工程的根目录

    复制文件:其实就是读一个文件,然后把读取的数据写到另一个文件

    1. public class Exercise3 {
    2. @Test
    3. public void test()throws IOException{
    4. FileInputStream fis = new FileInputStream("D:\\atguigu\\javaee\\JavaSE20220106\\video\\day0107_01video\\day0107_01JavaSE阶段学什么.avi");
    5. //要求上面这个文件必须存在,否则会报错
    6. FileOutputStream fos = new FileOutputStream("day0107_01JavaSE阶段学什么1.avi");
    7. //下面这个文件可以不存在
    8. byte[] data = new byte[1024];//一次读取1KB
    9. /* while(fis.read(data) != -1){//错误,因为这个循环会导致每次循环读了两次,一次用于判断,一次用于输出
    10. fos.write(fis.read(data));
    11. }*/
    12. int len;
    13. while((len= fis.read(data)) !=-1){
    14. fos.write(data,0,len);
    15. }
    16. fis.close();
    17. fos.close();
    18. }
    19. public static void main(String[] args) throws IOException {
    20. FileInputStream fis = new FileInputStream("D:\\atguigu\\javaee\\JavaSE20220106\\video\\day0107_01video\\day0107_01JavaSE阶段学什么.avi");
    21. //要求上面这个文件必须存在,否则会报错
    22. FileOutputStream fos = new FileOutputStream("day0107_01JavaSE阶段学什么.avi");
    23. //下面这个文件可以不存在
    24. byte[] data = new byte[1024];//一次读取1KB
    25. while(true){
    26. int len = fis.read(data);//从源文件读取len个字节
    27. if(len == -1){
    28. break;
    29. }
    30. fos.write(data,0,len);//把本次读取的len个字节写到目标文件中
    31. }
    32. fis.close();
    33. fos.close();
    34. }
    35. }

    6.IO流的问题

    (1)关于直接基于"文件夹/目录"创建IO流对象是错误的

    会报java.io.FileNotFoundException:d:\download(拒绝访问)

    (2)输出流如果不能及时关闭(因为可能后面还要用),但是又想要数据及时输出,可以调用flush方法,但是如果后面不用啦,一定要close。因为close不仅仅是刷数据的问题,还涉及到内存回收问题。

    1. public class TestProblem {
    2. @Test
    3. public void test02()throws IOException{
    4. FileWriter fw = new FileWriter("1.txt");
    5. fw.write("柴林燕");
    6. /*
    7. 像FileWriter等很多的输出流的内部有自己的一个小小缓冲区,
    8. 它在调用write方法时,会先将数据写到缓冲区。
    9. 当缓冲区满的时候,会自动“溢出”到文件。
    10. 当缓冲区没满的时候,会close方法执行时,把缓冲区的数据“清空”输出。
    11. */
    12. //如果希望数据及时写出,但是暂时还不close
    13. fw.flush();//刷新
    14. fw.write("是Java讲师");
    15. fw.flush();
    16. fw.close();
    17. fw.write("很棒");//java.io.IOException: Stream closed
    18. fw.close();//这个就是错误的,因为已经close过了
    19. }
    20. @Test
    21. public void test01()throws IOException {
    22. // FileInputStream fis = new FileInputStream("d:\\download");
    23. //java.io.FileNotFoundException: d:\download (拒绝访问。)
    24. // 文件IO必须基于文件路径创建对象
    25. //"d:\\download"这个是一个文件夹的路径,不是一个文件的路径
    26. // FileOutputStream fos = new FileOutputStream("d:\\download");
    27. //java.io.FileNotFoundException: d:\download (拒绝访问。)
    28. //就算是让 FileOutputStream帮你自动创建文件,也必须在IO流的构造器中,指明文件的名称。
    29. }
    30. }

    7.缓冲流:

            作用是给其他IO流增加缓冲区,提高效率

    缓冲流的使用必须基于其他的IO流,也就是说,缓冲流它只能是装修(包装)别的流。

    BufferedInputStream:只能包装InputStream系列的IO流

    BufferedOutputStream:只能包装OutputStream系列的IO流

    BufferedReader:只能包装Reader系列的IO流

    BufferedWriter:只能包装Writer系列的IO流

    原理:所有的缓存流在内部会开辟一块更大的缓存区,默认大小是8192字节/字符(本质就是一个8192长度的byte/char数组)

            另外,对于BufferedReader和BufferedWriter来说,它除了提高效率之外,

    还可以使用下面两个方法,使得读写更方便:

    BufferedReader类的  String readLine() 读取一整行

    BufferedWriter类的void newLine()  写入一整行

    代码演示:

    1. public class TestBuffered {
    2. @Test
    3. public void test05()throws IOException{
    4. //一行一行的写数据
    5. FileWriter fw = new FileWriter("1.txt");
    6. BufferedWriter bw = new BufferedWriter(fw);
    7. bw.write("hello");
    8. bw.newLine();//一行一行的写数据
    9. bw.write("java");
    10. bw.newLine();
    11. bw.write("world");
    12. bw.newLine();
    13. bw.write("aa");
    14. bw.close();
    15. fw.close();
    16. }
    17. @Test
    18. public void test04()throws IOException{
    19. //一行一行的写数据
    20. FileWriter fw = new FileWriter("1.txt");
    21. fw.write("hello");
    22. fw.write("\r\n");
    23. fw.write("java");
    24. fw.write("\r\n");
    25. fw.write("world");
    26. fw.write("\r\n");
    27. fw.write("aa");
    28. fw.close();
    29. }
    30. @Test
    31. public void test03()throws IOException {
    32. FileReader fr = new FileReader("1.txt");
    33. BufferedReader br = new BufferedReader(fr);//包装fr
    34. String str;
    35. while((str=br.readLine())!=null){//一行一行的读数据
    36. System.out.println(str);
    37. }
    38. br.close();
    39. fr.close();
    40. }
    41. @Test
    42. public void test02()throws IOException {
    43. long start = System.currentTimeMillis();
    44. /*
    45. 演示 BufferedInputStream包装FileInputStream
    46. BufferedOutputStream包装FileOutputStream
    47. 来完成复制文件。
    48. D:\software\IntelliJ IDEA\ideaIU-2020.3.1.win.zip
    49. */
    50. FileInputStream fis = new FileInputStream("D:\\software\\IntelliJ IDEA\\ideaIU-2020.3.1.win.zip");
    51. BufferedInputStream bis = new BufferedInputStream(fis);//BufferedInputStream包装FileInputStream的对象
    52. FileOutputStream fos = new FileOutputStream("D:\\idea2.zip");
    53. BufferedOutputStream bos = new BufferedOutputStream(fos);
    54. byte[] data = new byte[1024];
    55. int len;
    56. while((len = bis.read(data)) != -1){
    57. bos.write(data,0,len);
    58. }
    59. bis.close();
    60. fis.close();
    61. bos.close();
    62. fos.close();
    63. long end = System.currentTimeMillis();
    64. System.out.println("耗时:" + (end-start));//耗时:6164
    65. }
    66. @Test
    67. public void test01()throws IOException {
    68. long start = System.currentTimeMillis();
    69. /*
    70. 演示 BufferedInputStream包装FileInputStream
    71. BufferedOutputStream包装FileOutputStream
    72. 来完成复制文件。
    73. D:\software\IntelliJ IDEA\ideaIU-2020.3.1.win.zip
    74. */
    75. FileInputStream fis = new FileInputStream("D:\\software\\IntelliJ IDEA\\ideaIU-2020.3.1.win.zip");
    76. FileOutputStream fos = new FileOutputStream("D:\\idea3.zip");
    77. byte[] data = new byte[1024];
    78. int len;
    79. while((len = fis.read(data)) != -1){
    80. fos.write(data,0,len);
    81. }
    82. fis.close();
    83. fos.close();
    84. long end = System.currentTimeMillis();
    85. System.out.println("耗时:" + (end-start));//42063
    86. }
    87. }

    有无缓冲区的不同:

    8.IO流的关闭顺序:

    IO流有依赖关系。外层的包装流依赖于内存的被包装流。
            比如:BufferedInputStream 依赖于 FileInputStream
                       BufferedOutputStream 依赖于 FileOutputStream
     
                 如果把内层的IO先关闭了,外层IO流就失去了依赖,就会报错。
             比喻:我坐在凳子上,我依赖于凳子,如果把凳子先撤了,我就会摔倒。
             先让我站起来,然后撤凳子。
     
             关闭的顺序是:先关闭外层的包装流,再关闭内层的被包装流。
     
             本质:
                这段代码数据的流向: 源文件 -> fis -> bis -> data数组  -> bos -> fos ->目标文件.
                BufferedOutputStream和FileOutputStream,如果把fos先关了,
                bos中的缓冲的数据没有办法完全写出。因为close时,会清空缓冲区。
     
            好记:
                fis被包装在里面的,比喻成内衣,先穿内衣,再穿外衣。
                bis相当于外套。
     
                fos是内衣,bos是外套。
     
                关闭好比是脱衣服。
                先脱外套,再脱内衣。
     
               或者,先new的后关。
     
               fis和bis有顺序关系
               fos和bos有顺序关系
     
                fis和bis 、 fos和bos是没有顺序关系。

    演示代码:

    1. public class TestClose {
    2. @Test
    3. public void test02()throws IOException {
    4. FileInputStream fis = new FileInputStream("D:\\atguigu\\javaee\\JavaSE20220106\\video\\day0107_01video\\day0107_01JavaSE阶段学什么.avi");
    5. BufferedInputStream bis = new BufferedInputStream(fis);//BufferedInputStream包装FileInputStream的对象
    6. FileOutputStream fos = new FileOutputStream("JavaSE.avi");
    7. BufferedOutputStream bos = new BufferedOutputStream(fos);
    8. /*
    9. 这段代码数据的流向: 源文件 -> fis -> bis -> data数组 -> bos -> fos ->目标文件
    10. */
    11. byte[] data = new byte[1024];
    12. int len;
    13. while((len = bis.read(data)) != -1){
    14. bos.write(data,0,len);
    15. }
    16. fis.close();
    17. fos.close();
    18. bis.close();
    19. bos.close();//java.io.IOException: Stream Closed
    20. //应该先关闭外层,再关闭内层
    21. }
    22. }

     9.编码与解码问题

    使用FileReader和FileWriter去操作和当前程序编码不一致的文件时,就会出现乱码的问题,怎么解决?

    使用InputStreamReader:输入流

            OutputStreamReader:输出流

    构造方法摘要:

    InputStreamReader(InputStream in):创建一个使用默认字符集的 InputStreamReader。

    InputStreamReader(InputStream in,Charset cs):创建使用给定字符集的 InputStreamReader。

    InputStreamReader(InputStream in,CharsetDecoder dec):创建使用给定字符集解码器的 InputStreamReader。

    InputStreamReader(InputStream in,String charsetName):创建使用指定字符集的 InputStreamReader。

    代码演示:

    1. public class TestInputStreamReader {
    2. @Test
    3. public void test04()throws IOException{
    4. //输出尚硅谷太棒了到GBK编码的1.txt文件中
    5. //加入缓冲流提高效率
    6. FileOutputStream fos = new FileOutputStream("1.txt");
    7. //(1)可以加入这个位置
    8. BufferedOutputStream bos = new BufferedOutputStream(fos);//因为被包装的fos是字节流
    9. OutputStreamWriter osw = new OutputStreamWriter(bos,"GBK");//把字符数据用GBK编码转为字节数据输出
    10. //(2)也可以加入这儿
    11. BufferedWriter bw = new BufferedWriter(osw);//因为osw是字符流
    12. bw.write("尚硅谷太棒了");
    13. bw.close();
    14. osw.close();
    15. bos.close();
    16. fos.close();
    17. }
    18. @Test
    19. public void test03()throws IOException{
    20. //输出尚硅谷太棒了到GBK编码的1.txt文件中
    21. FileOutputStream fos = new FileOutputStream("1.txt");
    22. OutputStreamWriter osw = new OutputStreamWriter(fos,"GBK");//把字符数据用GBK编码转为字节数据输出
    23. osw.write("尚硅谷太棒了");
    24. osw.close();
    25. fos.close();
    26. }
    27. @Test
    28. public void test02()throws IOException {
    29. FileInputStream fis = new FileInputStream("1.txt");
    30. InputStreamReader isr = new InputStreamReader(fis,"GBK");//文件的编码是GBK,用GBK解从文件读取的数据
    31. char[] data = new char[10];
    32. int len;
    33. while((len = isr.read(data)) != -1){
    34. System.out.println(new String(data,0,len));
    35. }
    36. isr.close();
    37. fis.close();
    38. }
    39. @Test
    40. public void test01()throws IOException {
    41. FileReader fr = new FileReader("1.txt");//1.txt的编码是GBK
    42. char[] data = new char[10];
    43. int len;
    44. while((len = fr.read(data)) != -1){
    45. System.out.println(new String(data,0,len));
    46. }
    47. fr.close();
    48. }
    49. }

    10.对象序列化和反序列化

    IO流:

            字节流,可以读/写  图片,视频,文本等。

            字符流,只能读/写  文本(字符串,char)

    如果要处理Java程序中的各种数据类型的数据,就需要用

    DataInputStream:读Java各种基本数据类型的数据+String。

    DataOutputStream:写Java各种基本数据类型的数据+String。

    ObjectInputStream:读Java任意类型,包括对象。

    ObjectOutputStream:写Java任意类型,包括对象。

    序列化:把Java对象直接转化为字节序列过程,这个过程称为序列化,要求这个对象的类型必须实现java.io.Serializable接口,否则无法支持序列化。

    反序列化:把字节序列转为Java对象的过程。

    反序列化失败问题:

            当数据已经序列化好之后,对Student类做了修改,导致反序列化的代码运行失败;报错:        java.io.InvalidClassException(无效的类异常): com.atguigu.object.Student;

    local(本地) class incompatible(不相容的;矛盾的;):stream(流) classdesc(类描述) serialVersionUID(序列化版本ID) = -3979294235569238736,local(本地) class serialVersionUID(序列化版本ID) = 5212389082514962991

            如果在类声明并实现java.io.Serializable接口时,如果没有固定serialVersionUID(序列化版本ID)值,那么会在每一次编译类的时候,会产生一个新的serialVersionUID(序列化版本ID)值。

            修改Student类,就会导致类重新编译。

    如何解决这个问题?

            在类声明并实现java.io.Serializable接口时,固定 serialVersionUID(序列化版本ID)值。

    不参与序列化的字段:

    ① transient修饰的属性字段

    ② static修饰的属性字段

    Student类代码:

    1. public class Student implements Serializable {
    2. private static final long serialVersionUID = -3979294235569238736L;
    3. private static int total = 1;
    4. private int id;
    5. private String name;
    6. private int age;
    7. private transient int score;//transient瞬时的,临时,序列化时遇到transient标记的字段,会忽略它
    8. private String tel;
    9. public Student(String name, int age, int score) {
    10. id=total++;
    11. this.name = name;
    12. this.age = age;
    13. this.score = score;
    14. }
    15. public String getName() {
    16. return name;
    17. }
    18. public void setName(String name) {
    19. this.name = name;
    20. }
    21. public int getAge() {
    22. return age;
    23. }
    24. public void setAge(int age) {
    25. this.age = age;
    26. }
    27. public int getScore() {
    28. return score;
    29. }
    30. public void setScore(int score) {
    31. this.score = score;
    32. }
    33. public static int getTotal() {
    34. return total;
    35. }
    36. public static void setTotal(int total) {
    37. Student.total = total;
    38. }
    39. @Override
    40. public String toString() {
    41. return "Student{" +
    42. "id=" + id +
    43. ", name='" + name + '\'' +
    44. ", age=" + age +
    45. ", score=" + score +
    46. ", tel='" + tel + '\'' +
    47. '}';
    48. }
    49. }

    测试代码:TestObjectIO类:

    1. public class TestObjectIO {
    2. @Test
    3. public void test04()throws Exception{
    4. //在stu.dat文件中增加一个对象
    5. FileInputStream fis = new FileInputStream("stu.dat");
    6. ObjectInputStream ois = new ObjectInputStream(fis);
    7. ArrayList list = (ArrayList) ois.readObject();
    8. list.add(new Student("沈彦",22,100));
    9. System.out.println(list);
    10. ois.close();
    11. fis.close();
    12. FileOutputStream fos = new FileOutputStream("stu.dat");
    13. ObjectOutputStream oos = new ObjectOutputStream(fos);
    14. oos.writeObject(list);
    15. oos.close();
    16. fos.close();
    17. }
    18. @Test
    19. public void test03()throws Exception{
    20. Student s1 = new Student("李四",24,88);
    21. Student s2= new Student("王五",24,90);
    22. Student s3 = new Student("赵六",24,100);
    23. ArrayList list = new ArrayList<>();
    24. list.add(s1);
    25. list.add(s2);
    26. list.add(s3);
    27. //同时序列化多个对象时,怎么办?
    28. //放到集合里面。
    29. FileOutputStream fos = new FileOutputStream("stu.dat");
    30. ObjectOutputStream oos = new ObjectOutputStream(fos);
    31. oos.writeObject(list);
    32. oos.close();
    33. fos.close();
    34. }
    35. @Test
    36. public void test02() throws IOException, ClassNotFoundException {
    37. Student s1 = new Student("李四",24,0);
    38. Student s2= new Student("王五",24,0);
    39. Student s3 = new Student("赵六",24,0);
    40. System.out.println(Student.getTotal());
    41. FileInputStream fis = new FileInputStream("game.dat");
    42. ObjectInputStream ois = new ObjectInputStream(fis);
    43. //读的顺序要与写的顺序一致
    44. String name = ois.readUTF();
    45. int age = ois.readInt();
    46. char gender = ois.readChar();
    47. int energy = ois.readInt();
    48. double price = ois.readDouble();
    49. boolean relive = ois.readBoolean();
    50. Object object = ois.readObject();
    51. System.out.println("name = " + name);
    52. System.out.println("age = " + age);
    53. System.out.println("gender = " + gender);
    54. System.out.println("energy = " + energy);
    55. System.out.println("price = " + price);
    56. System.out.println("relive = " + relive);
    57. System.out.println("object = " + object);
    58. System.out.println(Student.getTotal());
    59. ois.close();
    60. fis.close();
    61. }
    62. @Test
    63. public void test01()throws IOException {
    64. String name ="巫师";
    65. int age = 300;
    66. char gender ='男';
    67. int energy = 5000;
    68. double price = 75.5;
    69. boolean relive = true;
    70. Student stu = new Student("张三",23,89);
    71. //输出到文件
    72. FileOutputStream fos = new FileOutputStream("game.dat");
    73. ObjectOutputStream oos = new ObjectOutputStream(fos);
    74. oos.writeUTF(name);
    75. oos.writeInt(age);
    76. oos.writeChar(gender);
    77. oos.writeInt(energy);
    78. oos.writeDouble(price);
    79. oos.writeBoolean(relive);
    80. oos.writeObject(stu);
    81. //java.io.NotSerializableException: com.atguigu.object.Student
    82. System.out.println(Student.getTotal());
    83. oos.close();
    84. fos.close();
    85. }
    86. }

    11.Scanner类与IO流

    java.util.Scanner类,不是只能用来从键盘输入数据,它可以从任意流中扫描数据。

    如果没有修改过System.in,那么Scanner  input = new Scanner(System.in);默认是从键盘输入

    在创建Scanner对象时,指定了其他的IO流,会从其他IO流中读取文本数据。

    示例代码:

    1. public class TestScanner {
    2. @Test
    3. public void test01()throws Exception{
    4. //从2.txt文件中获取数据
    5. Scanner input = new Scanner(new FileInputStream("2.txt"));
    6. while(input.hasNextLine()){
    7. String line = input.nextLine();
    8. System.out.println(line);
    9. }
    10. input.close();
    11. }

    12.System类与IO流

    System类中有三个常量对象:

    ① public final static InputStream in = null;

    ② public final static PrintStream out = null;

    ③ public final static PrintStream err = null;

    常量对象正常是以大写字母开头,但System的三个常量对象是以小写字母开头,而且用 final 修饰的变量是不可变的,但是System类的底层有set方法,可以设置in,out,err的值,即下面方法:

    ① public static void setIn(InputStream in)

    ② public static void setOut(PrintStream out)

    ③ public static void setErr(PrintStream err)

    底层是用native修饰的,来更改in,out,err的值。

    private static native void setIn0(InputStream in);

    private static native void setOut0(PrintStream out);

    private static native void setErr0(PrintStream err);

    示例代码如下:

    1. public class TestSystem {
    2. @Test
    3. public void test02()throws IOException {
    4. System.setIn(new FileInputStream("2.txt"));
    5. //此时默认是从 2.txt 文件中获取数据
    6. Scanner input = new Scanner(System.in);
    7. while(input.hasNextLine()){
    8. String line = input.nextLine();
    9. System.out.println(line);
    10. }
    11. input.close();
    12. }
    13. @Test
    14. public void test01(){
    15. InputStream in = System.in;
    16. PrintStream out = System.out;
    17. PrintStream err = System.err;
    18. out.println("hello");
    19. err.println("world");
    20. }
    21. }

    13.PrintStream类

    PrintStream打印流:

            ① 可以支持各种数据类型的打印

            ② 可以支持输出换行,输出不换行,还支持格式化输出

    print(xxx);输出不换行

    println(xxx);输出换行

    printf(xx);格式化输出

            ③ 输出可以输出到各种其他IO流中,也可以直接输出到文件,也可以输出到控制台。

    1. public class TestPrintStream {
    2. @Test
    3. public void test01()throws IOException {
    4. //以 UTF-8 的编码形式,输入到 3.txt 文件中
    5. PrintStream ps = new PrintStream("3.txt","UTF-8");
    6. ps.println("hello");
    7. ps.println("world");
    8. ps.close();
    9. }
    10. }

    14.try...catch新语法自动关闭IO流

    JDK1.7版本之前:

    代码如下:

    1. public class TestTryCatch {
    2. @Test
    3. public void test01(){
    4. FileInputStream fis = null;
    5. InputStreamReader isr = null;
    6. BufferedReader br = null;
    7. FileOutputStream fos = null;
    8. OutputStreamWriter osw = null;
    9. BufferedWriter bw = null;
    10. try {
    11. //GBK编码的1.txt,复制UTF-8的2.txt文件
    12. fis = new FileInputStream("1.txt");
    13. isr = new InputStreamReader(fis,"GBK");
    14. br = new BufferedReader(isr);
    15. fos = new FileOutputStream("2.txt");
    16. osw = new OutputStreamWriter(fos, "UTF-8");
    17. bw = new BufferedWriter(osw);
    18. String line;
    19. while((line = br.readLine()) != null){
    20. bw.write(line);
    21. bw.newLine();
    22. }
    23. } catch (IOException e) {
    24. e.printStackTrace();
    25. }finally{
    26. /* try {
    27. bw.close();
    28. osw.close();
    29. fos.close();
    30. br.close();
    31. isr.close();
    32. fis.close();
    33. } catch (IOException e) {
    34. e.printStackTrace();
    35. }*/
    36. try {
    37. if(bw!=null) {
    38. bw.close();
    39. }
    40. } catch (IOException e) {
    41. e.printStackTrace();
    42. }
    43. try {
    44. if(osw != null) {
    45. osw.close();
    46. }
    47. } catch (IOException e) {
    48. e.printStackTrace();
    49. }
    50. try {
    51. if(fos != null){
    52. fos.close();
    53. }
    54. } catch (IOException e) {
    55. e.printStackTrace();
    56. }
    57. try {
    58. if(br != null){
    59. br.close();
    60. }
    61. } catch (IOException e) {
    62. e.printStackTrace();
    63. }
    64. try {
    65. if(isr != null) {
    66. isr.close();
    67. }
    68. } catch (IOException e) {
    69. e.printStackTrace();
    70. }
    71. try {
    72. if(fis != null) {
    73. fis.close();
    74. }
    75. } catch (IOException e) {
    76. e.printStackTrace();
    77. }
    78. }
    79. }
    80. }

    这种try...catch非常麻烦,finally里面如果有异常的话,需要一个一个来try...catch很麻烦,所以JDK1.7之后,就有了新的方法

    try(需要自动关闭的资源的对象的声明和创建){

            需要异常检查的业务逻辑代码

    }catch(异常类型 e){

            异常处理代码

    }finally{

            这里写的是其他的必须执行的代码,但是不是资源关闭的代码。

    }

    try()中不是所有类型的对象的声明和创建都可以放进去的,只能放实现了 AutoClose 接口的类型

    只有在try()中声明的才会自动关闭。不在这里声明的不会自动关闭。

    在try()中声明的变量,会自动加 final

    1. public class TestTryCatch {
    2. @Test
    3. public void test02(){
    4. //GBK编码的1.txt,复制UTF-8的2.txt文件
    5. try(
    6. FileInputStream fis = new FileInputStream("1.txt");
    7. InputStreamReader isr = new InputStreamReader(fis,"GBK");
    8. BufferedReader br = new BufferedReader(isr);
    9. FileOutputStream fos = new FileOutputStream("2.txt");
    10. OutputStreamWriter osw = new OutputStreamWriter(fos, "UTF-8");
    11. BufferedWriter bw = new BufferedWriter(osw);
    12. ) {
    13. String line;
    14. while ((line = br.readLine()) != null) {
    15. bw.write(line);
    16. bw.newLine();
    17. }
    18. }catch(IOException e){
    19. e.printStackTrace();
    20. }
    21. //自动关闭,不需要写了
    22. /* bw.close();
    23. osw.close();
    24. fos.close();
    25. br.close();
    26. isr.close();
    27. fis.close();*/
    28. }
    29. }
  • 相关阅读:
    NSS [西湖论剑 2022]real_ez_node
    OpenHarmony鸿蒙南向开发案例:【智能门铃】
    灰度级形态学 - 灰度膨胀和灰度腐蚀
    操作系统备考学习 day3 (2.1.1 - 2.1.6)
    基于Yolov8的野外烟雾检测(1)
    利用STM32CubeMX软件生成USB_HOST_HID连接鼠标和键盘扫码枪
    记录PyTorch中半精度amp训练出现Nan的排查过程
    设计模式乱记
    JUC系列(九) CAS 与锁的理解
    《联邦学习实战—杨强》之使用Python从零开始实现一个简单的横向联邦学习模型
  • 原文地址:https://blog.csdn.net/weixin_52222660/article/details/130865282