• 【Java进阶篇】第六章 IO流


    一、IO流的概述

    1、流

    以内存为参照,读进内存为输入,反之为输出
    流

    2、流的分类

    🍁以流的方向进行分类:

    • 以内存为参照物,往内存中写,称为输入、Input、读
    • 从内存中出来,叫输出、Output、写

    🍁按照读取数据方式不同进行分类

    • 有的流按照字节的方式读取数据,一次读取一个字节byte,即8位bit,这种流是万能的,可读取任何类型的文件,如文本、图片、声音文件、视频文件。称字节流
    • 有的流按照字符的方式读取数据,一次读取一个字符,这种流方便了对普通文本的读取,不能读取图片、声音、视频等文件,只能读取纯文本,Word文件都无法读取。称字符流

    如:file.txt文件,内容:a中国bcdef,a在Windows中占一个字节,中占两个字节,则:

    字符流: a–>中–>国…
    字节流:a–>中字符的一半–>中的另一半

    🍺Java中的IO流已经写好了,都在java.io.*下

    3、Java IO流的四大块

    类名是否抽象类
    java.io.InputStream字节输入流
    java.io.OutputStream字节输出流
    java.io.Reader字符输入流
    java.io.Writer字符输出流

    在Java中,只要类名以Stream结尾,都是字节流。只要类名以Reader/Writer结尾的都是字符流。 如InputStreamReader即字符流

    4、流的两大特性

    🍁

    • 所有流都实现了java.io.Closeable接口,都是可关闭的,都有close()方法,流就像一个管道,是内存和硬盘之间的通道,用完一定要关掉,不然会占用很多资源
    • 所有的输出流都是实现了java.io.Flushable接口,都是可刷新的,都有flush()方法。输出流在最终输出后,一定要用flush()刷新下,这个刷新表示将管道/通道中剩余未输出的数据强行输出完,清空管道,没flush()可能会导致丢数据

    5、java.io包下的16个常用流

    常用流

    二、文件专属流

    1、java.io.FileInputStream

    文件字节输入流,万能的,可读任何类型的文件(硬盘–>内存)

    • 通过构造方法创建文件字节输入流对象
    //注意路径在IDEA中\变\\,前面那个\表示转义
    //你直接手输G:/JAVA/old files也行
    
    //直接new报错,因为有异常未处理
    FileInputStream fileInputStream = new FileInputStream("G:\\JAVA\\old files");
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • read()方法

    read()

    try{
        fileInputStream = new FileInputStream("G:\\JAVA\\old files");
        while(true){
            int readData = fileInputStream.read();
            //读完以后返回-1
            if(readData == -1){
                break;
            }
            System.out.println(readData);
        }
    }catch(FileNotFoundException e) {
       e.printStackTrace();
    //处理read方法的异常
    }catch(IOException e){
       e.printStackTrace();
    }....
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    以上使用while(true) + if–break可以优化为:

    int readData = 0;
    
    while( (readData = fileInputStream.read() ) != -1){
        System.out.println(readData);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    以上,使用read()方法从输入流中一次读取一个字节,这样内存和硬盘之间的交互太频繁,耗费不必要的资源。

    • read(byte[ ] b)方法

    从输入流中将最多b.length个字节的数据先读入一个byte数组中

    try{
       fileInputStream = new FileInputStream("G:\\JAVA\\test.txt");
       byte[] bytes = new byte[4];
       //注意传入数组时read返回的是读取到的字节数量
       //不是字节本身
       int readCount1 = fileInputStream.read(bytes);
       //4
       System.out.println(readCount1);
       //abcd
       System.out.println(new String(bytes));
       int readCount2 = fileInputStream.read(bytes);
       //2
       System.out.println(readCount2);
       //efcd
       System.out.println(new String(bytes));
       int readCount3 = fileInputStream.read(bytes);
       //-1,表示一个都没有读到
       System.out.println(readCount3);
       System.out.println(new String(bytes));
       ...
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    过程分析:
    分析

    运行结果:
    run
    程序优化:

    //利用String的构造方法,传入数组,转为String
    System.out.println(new String(bytes));
    
    //改为用另一个构造方法,传入启示下标和长度
    //使用readCount,即可“读到多少个,转多少个”
    System.out.println(new String(bytes,0,readCount));
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    FileInputStream最终版代码:

    import java.io.FileInputStream;
    import java.io.FileNotFoundException;
    import java.io.IOException;
    
    public class FileInputStreamTest {
        public static void main(String[] args) {
        	//try域中的fileInputStream变量在finally域中识别不到
        	//所以在外面定义
            FileInputStream fis = null;
            try {
                fis = new FileInputStream("G:\\JAVA\\test.txt");
                byte[] bytes = new byte[6];
                int readCount = 0;
                while((readCount = fis.read(bytes)) != -1){
                    System.out.println(new String(bytes,0,readCount));
                }
            }catch(FileNotFoundException e){
                e.printStackTrace();
            }catch(IOException e){
                e.printStackTrace();
            //在finally语句中确保流一定被关闭
            }finally{
            	//关闭的前提是流不为空
                if(fis != null){
                    try{
                        fis.close();
                    //这里处理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
    • FileInputStream类中的其他常用方法

    🍁int available()方法

    返回流当中剩余的没有读到的字节数量

    System.out.println("流中的总字节数:" + fileInputStream.available());
    //读一个
    int data = fileInputStream.read();
    //流中剩余的字节数
    System.out.println(fileInputStream.available());
    
    • 1
    • 2
    • 3
    • 4
    • 5

    有了总字节数,我们就可以创建一个对应长度的byte数组,这样直接一次性拿完,不用再根据read的返回值来写循环了

    byte[] byte1 = new byte[fileInputStream.available()];
    int readCount = fileInputStream.read(byte1);
    System.out.println(new String(byte1));
    
    //但byte数组不能太大,所以以上不适用于大文件。
    
    • 1
    • 2
    • 3
    • 4
    • 5

    🍁long skip()方法

    跳过几个字节不取

    fis = new FileInputStream("G:\\JAVA\\test.txt");
    fis.skip(2);
    
    • 1
    • 2

    2、java.io.FileOutputStream

    文件字节输出流,负责写,从内存到硬盘。

    通过构造方法创建文件字节输出流对象

    加不加true传参,就像Linux中的 > 和 >>的区别:

    //tempFile如果不存在,运行程序会自动创建
    
    //不加参数true,调用write方法会清空文件原来的内容
    fos = new FileOutputStream("tempFile");
    
    //append参数传true,则write方法是追加而不是清空重写
    fos = new FileOutputStream("tempFile",true);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    write()方法

    FileOutputStream fos = null;
    //即abcd
    byte[] bytes = {97,98,99,100};
    try{
        fos = new FileOutputStream("tempFile",true);
        //将数组中的内容全部写到tempFile
        fos.write(bytes);
        //只要bytes数组中的前两位
        fos.write(bytes,0,2);
        fos.flush();
    }catch(FileNotFoundException e){
        e.printStackTrace();
    }catch(IOException e){
        e.printStackTrace();
    }finally{
        try{
            if(fos != null){
    
                fos.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
    String s = "这是个字符串";
    //String转byte数组
    byte[] bs = s.getBytes();
    fos.write(bs);
    fos.flush();
    
    • 1
    • 2
    • 3
    • 4
    • 5

    🍺🍺🍺综合练习:复制D盘文件1.avi到C盘
    复制文件

    import java.io.FileInputStream;
    import java.io.FileNotFoundException;
    import java.io.FileOutputStream;
    import java.io.IOException;
    
    public class Copy {
        public static void main(String[] args) {
            FileInputStream fis = null;
            FileOutputStream fos = null;
            try {
                fis = new FileInputStream("D:\\course\\1.avi");
                fos = new FileOutputStream("C:\\1.avi");
                //一次读1M
                byte[] bytes = new byte[1024*1024];
                int readCount = 0;
                while ((readCount = fis.read(bytes)) != -1) {
                    //在循环条件中读,在循环体中写
                    fos.write(bytes,0,readCount);
                }
                //输出流最后要刷新
                fos.flush();
    
    
            } catch (FileNotFoundException e) {
                e.printStackTrace();
            } catch (IOException e) {
                e.printStackTrace();
            } finally {
                try {
                    if (fis != null) {
                        fis.close();
                    }
                } catch (IOException e) {
                    e.printStackTrace();
                }
                //注意这里fis和fos分开try
                try {
                    if (fos != null) {
                        fos.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
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49

    注意最后:

    √ 如果把fis和fos的关闭流分支写在一起:

    ...
     } finally {
          try {
              if (fis != null) {
                  fis.close();
              }
              if (fos != null) {
                  fos.close();
              }
          } catch (IOException e) {
              e.printStackTrace();
          }
          ...
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    当fis出现异常的时候,fos的关闭流代码就执行不了了,所以分开try…catch

    3、java.io.FileReader

    文件字符输入流,只能读取普通文本。

    //用法和类中的方法,类比FileInputStream
    
    FileReader reader = null;
    try{
        reader = new FileReader("D:\\1.txt");
        char[] chars = new char[4];
        int readerCount = 0;
        while( (readerCount = reader.read(chars)) != -1){
            System.out.println(new String(chars,0,readerCount));
        }
    }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

    4、java.io.FileWriter

    文件字符输出流,将文本从内存写到磁盘,只能传输普通文本,word文件不是普通文本。

    与字节输出流不同的是:字符输出流的write方法可以直接传字符串,也能写成功

    FileWriter fw = null;
        try{
            fw = new FileWriter("temp.txt");
            char[] chars = {'字','符','输','出'};
            fw.write(chars);
            fw.write(chars,0,2);
            //write(String str)
            fw.write("可以直接写字符串了");
        }catch(IOException e){
            e.printStackTrace();
        }finally{
        ....
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    🍺🍺🍺综合练习:拷贝普通文本文件

    import java.io.FileReader;
    import java.io.FileWriter;
    import java.io.IOException;
    
    public class TxtCopy {
        public static void main(String[] args) {
            FileReader fr = null;
            FileWriter fw = null;
            try{
                fr = new FileReader("D:\\course\\HelloWorld.java");
                fw = new FileWriter("E:\\HelloWorld_copy.java");
                int charCount = 0;
                //循环一次1M
                char[] chars = new char[1024*512];
                while((charCount = fr.read(chars)) != -1){
                    fw.write(chars,0,charCount);
                }
            }catch(IOException e){
                e.printStackTrace();
            }finally {
                try{
                    if(fr != null){
                        fr.close();
                    }
    
                }catch(IOException e){
                    e.printStackTrace();
                }
                try{
                    if(fw != null){
                        fw.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
    • 38
    • 39

    注意:
    能用记事本编辑的都是普通文本文件,如xx.java,并不是单指txt文件

    三、缓冲流与转换流

    Buffered

    1、java.io.BufferedReader

    BufferedReader类的构造方法:

    带有缓冲区的字符输入流,使用这个流不用自定义char数组,自带缓冲

    FileReader fileReader = new FileReader("D:\\1.txt");
    BufferedReader br = new BufferedReader(fileReader);
    //注意,根据源码,BufferedReader只能传字符流,不能传字节流,对应的
    
    • 1
    • 2
    • 3

    当一个流的构造方法中需要传入另一个流的时候,这个被传进来的流称为节点流,如上面的FileReader,外部负责包装这个流的,称包装流,也称处理流。如上面的BufferedReader

    🍁

    br.close();
    
    • 1

    对于包装流来说,只需要关闭最外层的流就好,里面的节点流有底层源码去自动关闭。

    readLine()方法

    String firstLine = br.readLine();
    //readLine方法不带换行,所以这里选择println
    System.out.println(firstLine);
    String s =null;
    //读全部
    while((s = br.readLine()) != null){
        System.out.println(s);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    2、java.io.BufferedWriter

    带有缓冲的字符输出流

    FileWriter fileWriter = new FileWriter("temp.txt");
    BufferedWriter bw = new BufferedWriter(fileWriter);
    bw.write("hello world!");
    bw.write("\n");
    bw.flush();
    //只关闭最外层
    bw.close()
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    传入FileOutputStream时,用转换流

    3、java.io.InputStreamReader

    前面提到:BufferedReader的构造方法只能传字符流,不能传字节流,可通过转换流转字节流为字符流:

    FileInputStream in = new FileInputStream("D:\\course\\1.txt");
    //这里reader是包装流
    //字节流转为字符流
    InputStreamReader reader = new InputStreamReader(in);
    BufferedReader bfReader = new BufferedReader(reader);
    
    • 1
    • 2
    • 3
    • 4
    • 5

    合并代码:

    BufferedReader bfReader = new BufferedReader(new InputStreamReader(new FileInputStream("D:\\course\\1.txt")));
    
    • 1

    四、数据流

    1、java.io.DataOutputStream

    数据专属流,这个流可以将数据连同数据的类型一并写入文件(那么这个文件就不是普通的文本文件了,用记事本打开就会乱码)

    //注意DataOutputStream的构造方法中传入一个字节流做为节点流
    DataOutputStream dos = new DataOutputStream(new FileOutputStream("tempData"));
    
    byte b = 100;
    int i = 300;
    char c = 'a';
    //把数据类型一并写入文件中
    dos.writeByte(b);
    dos.writeInt(i);
    dos.writeChar(c);
    dos.flush();
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    2、java.io.DataInputStream

    数据字输入流,DataOutputStream写的文件,只能使用DataInputStream去读,且读的时候需要提前知道写入的顺序(读的顺序和写的顺序一致,才能正常取出数据)

    DataInputStream dis = new DataInputStream(new FileInputStream("tempData"));
    //注意和写的顺序一致
    byte b1 = dis.readByte();
    int i1 = dis.readInt();
    
    • 1
    • 2
    • 3
    • 4

    五、标准输出流

    标准输出流

    1、java.io.PrintStream

    标准的字节输出流,默认输出到控制台,标准输出流不需要close()

    //System.out.println
    PrintStream ps = System.out;
    ps.println();
    
    • 1
    • 2
    • 3

    总结回顾之前System类中的方法

    • System.gc()
    • System.currentTimeMills()
    • System.exit(0);
    • System.arrayCopy()
    //传入一个文件字节输出流做为节点流
    PrintStream printStream = new PrintStream(new FileOutputStream("log"));
    //标准输出流不再指向控制台,修改成了log文件
    System.setOut(printStream);
    //再输出
    System.out.println("hello");
    System.out.println("log");
    //这时候就输出到文件中去了
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    以上也是日志输出的一个实现思路:

    /**
     * 日志记录
     */
    public class Logger {
        public static void main(String[] args) {
          Logger.log("System is ready!");
        }
        public static void log(String msg){
            try {
                PrintStream printStream = new PrintStream(new FileOutputStream("log.txt",true));
                System.setOut(printStream);
                //日期
                Date nowTime = new Date();
                SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss SSS");
                String strTime = sdf.format(nowTime);
                System.out.println(strTime + ":" + msg);
    
            } catch (FileNotFoundException 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

    2、java.io.PrintWriter

    标准输出流之字符流,用法参照PrintStream

    六、对象专属流与File类

    1、File类

    • java.io.File类的父类是java.lang.Object
    • File对象是文件和目录路径名的抽象表现形式。如C:\course是一个File对象,也可能是文件,也可能是目录
    • File类和四大流没有关系,所以File类不能完成文件的读和写

    2、File类的常用方法

    🍁 exists()方法

    //判断文件是否存在
    File f1 = new File("D:\\file");
    System.out.println(f1.exists());
    
    • 1
    • 2
    • 3

    🍁 createNewFile()和mkdir()

    //若D:\\file不存在,则以文件的形式创建
    if(!f1.exists()){
       f1.createNewFile();
    }
    
    //以目录的形式创建
    if(!f1.exists()){
       f1.mkdir();
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    注意:

    当f1对象中的路径是多重目录时,mkdir方法变为mkdirs()

    🍁getParent()方法

    //获取文件的父路径
    File f2 = new File("D:\\course\\src\\1.txt");
    // D:\course\src
    String parentPath = f2.getParent();
    File parentPathFile = f2.getParentFile();
    
    • 1
    • 2
    • 3
    • 4
    • 5

    相对应的,有个getAbsolutePath() 方法:

    //获取绝对路径
    
    File f3 = new File("FileTest.java");
    System.out.println(f3.getAbsoluteFile());
    System.out.println(f3.getAbsolutePath());
    
    • 1
    • 2
    • 3
    • 4
    • 5

    🍁getName()方法

    //获取文件名
    
    File f2 = new File("D:\\course\\src\\1.txt");
    //1.txt
    System.out.println(f2.getName());
    
    • 1
    • 2
    • 3
    • 4
    • 5

    🍁isDirectory()和isFile()方法

    //判断是文件/目录
    File f2 = new File("D:\\course\\src\\1.txt");
    //false
    System.out.println(f2.isDirectory());
    
    • 1
    • 2
    • 3
    • 4

    🍁lastModified()方法

    File f2 = new File("D:\\course\\src\\1.txt");
    //从1970年0:0到现在的总毫秒数
    Long times = f2.lastModified();
    
    Date time = new Date(times);
    SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss SSS");
    String strTime = sdf.format(time);
    System.out.println(strTime);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    🍁length()方法

    //获取文件大小
    Long size = f2.length();
    
    • 1
    • 2

    🍁File[ ] listFiles()方法

    //获取当前目录下的所有子文件,返回一个File[]数组
    File f = new File("D:\\course");
    File[] files = f.listFiles();
    
    //增强for循环获取子文件的绝对路径
    for(File fObj:files){
        System.out.println(fObj.getAbsoluteFile());
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    ❀❀❀练习–目录拷贝

    /**
     * 业务功能:实现任意两个目录间文件的拷贝
     */
    
    import java.io.*;
    
    public class CopyAll {
        public static void main(String[] args) {
            File src = new File("D:\\course");
            File dest = new File("E:\\copy");
            copyDir(src,dest);
    
        }
        public static void copyDir(File srcFile,File destFile){
            /**
             * 源文件是一个文件
             */
            if(srcFile.isFile()){
                FileInputStream in = null;
                FileOutputStream out = null;
                try {
                    in = new FileInputStream(srcFile);
                    /**
                     * 三目运算符,判断目标路径结尾是不是\
                     * 不是则加\,并截取源目录除盘符以外的目录拼接给它
                     */
                    String path = destFile.getAbsolutePath().endsWith("\\") ?
                            destFile.getAbsolutePath():
                            destFile.getAbsolutePath() + "\\" + srcFile.getAbsolutePath().substring(3);
                    if(!new File(path).exists()){
                        new File(path).getParentFile().mkdirs();
                    }
                    out = new FileOutputStream(path);
                    byte[] bytes = new byte[1024*1024];
                    int readCount = 0;
                    while( (readCount = in.read(bytes)) != -1 ){
                        out.write(bytes,0,readCount);
                    }
                    out.flush();
                } catch(FileNotFoundException e) {
                    e.printStackTrace();
                }catch(IOException e){
                    e.printStackTrace();
                }finally{
                    try{
                        if(in != null){
                            in.close();
                        }
                    }catch(IOException e){
                        e.printStackTrace();
                    }
                    try{
                        if(out != null){
                            out.close();
                        }
                    }catch(IOException e){
                        e.printStackTrace();
                    }
                }
                System.out.println("拷贝完成,进度100%!");
                //如果源文件是文件,则一定不是目录,下面的目录复制
                //下面的目录复制不必再执行
                return;
            }
            /**
             * 能执行到这儿说明源文件是一个目录
             * 这里要是再加if分支判断是否为目录,则上面的return就不必了
             * 但要么文件要么目录,所以直接上面return了
             */
            //获取源目录下的所有子文件
            File[] files = srcFile.listFiles();
            for(File file:files){
                if(file.isDirectory()){
                    String destPath = (destFile.getAbsolutePath().endsWith("\\") ?
                            destFile.getAbsolutePath():
                            destFile.getAbsolutePath() + "\\" + srcFile.getAbsolutePath().substring(3));
                    File newFile = new File(destPath);
                    if(!newFile.exists()){
                        newFile.mkdirs();
                    }
    
                }
                //执行到这里,说明源目录的子文件是一个文件,不是目录
                // 递归调用copy就行
                copyDir(file,destFile);
            }
            System.out.println("拷贝完成,进度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
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91

    在刚开始调代码的时候遇到一个error:

    error:
    java.io.FileNotFoundException:.\xxx\xxx.txt (系统找不到指定的路径。)
    java.io.FileNotFoundException: E:\xx\xx (拒绝访问。)
    
    • 1
    • 2
    • 3

    分析:

    在构造一个File对象时,指定的文件路径是什么都可以,就算不存在也能够构造File对象,但是,现在你要对文件进行输入输出操作,也就是InputStream和OutputStream操作时,如果填写的路径不存在,那么就会报系统找不到指定路径,如果指定的是xxx\,就会报拒绝访问异常。

    具体参考【BUG】

    3、对象流

    序列化与反序列化
    🍁序列化:
    Serialize,Java对象存储到文件中,将Java对象的状态保存下来的过程,用ObjectOutputStream实现。

    🍁反序列化:
    DeSerialize,将硬盘上的数据重新恢复到内存中,恢复成Java对象

    public class Students implements Serializable{
    
    	private int age;
    	.....
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    参与序列化和反序列化的对象,必须实现Serializable接口 ,否则报错java.io.NotSerializableException

    //Serializabl接口的源码就这两行
    public interface Serializable {
    }
    
    • 1
    • 2
    • 3

    Serializable接口中没有任何方法,是一个标志性接口。标志性接口,起到标识作用,Java虚拟机看到这个类实现了这个接口,可能会对这个类进行特殊待遇,即为该类自动生成一个序列化版本号。

    //定义实现序列化接口的Students类
    class Students implements Serializable{
        int no;
        String name;
        public Students(){
    
        }
        public Students(int no,String name){
            this.no = no;
            this.name = name;
        }
    
    }
    
    Students s = new Students(111,"llg");
    //序列化,将对象存到硬盘,文件名称students
    ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("students"));
    oos.writeObject(s);
    oos.flush();
    
    
    //将硬盘中的文件students反序列化成对象,读进内存
    ObjectInputStream ois = new ObjectInputStream(new FileInputStream("students"));
    Object obj = ois.readObject();
    System.out.println(obj);
    
    • 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

    对象序列化后的文件,用txt查看乱码:
    序列化
    🍁序列化多个对象和反序列化多个对象:

    把对象放到集合中,writeObject方法中传入一个集合

    List<Students> studentsList = new ArrayList<>();
    studentsList.add(new Students(02,"A"));
    studentsList.add(new Students(03,"B"));
    ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("studentsList"));
    //传入了一个List集合
    //ArrayList类已经实现了Serializable
    oos.writeObject(studentsList);
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    反序列化多个对象:

    ObjectInputStream ois = new ObjectInputStream(new FileInputStream("studentsList"));
    //readObject方法返回的是Object类型,更细分析这里是一个List,故强转
    List<Students> objList = (List<Students>) ois.readObject();
    for(Students objStudents:objList){
       System.out.println(objStudents);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    🍁transient关键字

    transient关键字表示游离的,不参与序列化。

    private transient String name;
    
    • 1

    即对象的name属性序列化的时候不会再序列化到文件中,则此后再反序列化,需要new对象时name就无值,出现默认值null

    🍁IDEA生成序列化版本号
    IDEA

    private static final Long serialVersionUID = -7876415467956846468432L;
    
    • 1

    ❀自动生成序列化版本号的缺陷:

    一旦代码后续修改,重新编译会生成全新的序列化版本号。而序列化版本号是用来区分类的,故JVM会认为这是一个全新的类。再反序列化的时候就会报错。

    因此:实现Serializable后,手动在代码中写一个固定的序列化版本号,别自动生成(自动生成的不显示,且代码修改编译后,序列化版本号会改变)。

    4、IO与Properties联合使用

    //有文件userinfo.txt
    username=root
    password=123qweASD
    
    • 1
    • 2
    • 3

    将userinfo.txt文件中的数据加载到Properties对象中(温习:Properties是一个Map集合,其key和value都是String类型)

    FileReader reader = new FileReader("userinfo.txt");
    Properties pro = new Properties();
    //Properties对象的load方法,将文件中的数据加载到Map集合中
    //其中,等号左边为key,右边为value
    pro.load(reader);
    //由key取value
    String value = pro.getProperty("username");
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    对于经常变动的数据,可以单独写到一个文件中(即配置文件),使用程序动态读取。后续修改只需改配置文件,不用改代码,不用重新编译。

    当配置文件的内容格式是key=value的时候,称为属性配置文件,文件名常以.properties结尾。=也可用:且=左右两边最好别加空格。

    Properies是专门存放属性配置文件内容的一个类。

  • 相关阅读:
    天然气销售企业人工智能技术应用研究
    FPGA项目开发之同步信号和亚稳态
    关于ssh的使用
    [Vue3]Vue3基本概述
    Java代码中如何向一个HashMap中添加元素呢?
    【LINUX】1-移植NXP提供的源码
    在Linux上安装部署JDK和Tomcat(超级详细)
    Kubernetes新增节点
    【Matlab】状态空间模型的最小化实现 minreal() 函数
    HCIA HYBRID端口
  • 原文地址:https://blog.csdn.net/llg___/article/details/128029093