day08【File类、递归】

day08【File类、递归】

主要内容

  • File类
  • 递归
  • 过滤器

教学目标

  • 能够说出File对象的创建方式
  • 能够说出File类获取名称的方法名称
  • 能够说出File类获取绝对路径的方法名称
  • 能够说出File类获取文件大小的方法名称
  • 能够说出File类判断是否是文件的方法名称
  • 能够说出File类判断是否是文件夹的方法名称
  • 能够辨别相对路径和绝对路径
  • 能够遍历文件夹
  • 能够解释递归的含义
  • 能够使用递归的方式计算5的阶乘
  • 能够说出使用递归会内存溢出隐患的原因

第一章 File类

1.1 概述

目标

  • 能够理解File类的具体作用。

生活中计算机中包含了文件和文件夹这两类事物,Java有对应的类来对计算机中的文件和文件夹进行操作处理。

  • java.io.File :文件和目录路径名的抽象表示,主要用于文件和目录的创建、查找和删除等操作。

    创建一个文件或文件夹
    删除一个文件或文件夹
    获取一个文件或文件夹
    获取文件的大小
    判断文件或文件夹是否存在
    对文件夹进行遍历

  • 重点记住3个单词:

    • file:文件相关的操作。
    • directory:文件夹相关的操作。
    • path:路径相关的操作。

小结

File类主要是对持久设备上的文件和文件夹进行创建、删除、遍历等操作。它不能去操作文件中的数据。当我们需要操作持久设备上的文件或文件夹时就直接找File类完成,如果要操作文件中的数据只能找后面需要学习的IO技术搞定。

1.2 成员变量

目标

  • 能够明确File类的成员变量的作用,和了解计算机中的路径。
  • 成员变量:
    • static String pathSeparator 获取系统相关的路径分隔符字符,为方便操作,表示为字符串。
    • static char pathSeparatorChar 获取与系统相关的路径分隔符,表示为字符。
    • static String separator 获取系统相关的文件名称分隔符,以方便的方式表示为字符串。
    • static char separatorChar 获取系统相关的文件名称分隔符,表示为字符。

步骤

  1. 调用File类的静态成员获取对应的路径和文件名称分隔符,然后输出打印查看结果。

实现

/*  
    演示File类的静态成员变量
    static String pathSeparator 与系统相关的路径分隔符字符,为方便起见,表示为字符串。
    static char pathSeparatorChar 与系统相关的路径分隔符。
        第一天学习的path变量配置中使用的,用来分隔不同的路径配置。

    static String separator 与系统相关的默认名称 - 分隔符字符,以方便的方式表示为字符串。
    static char separatorChar 与系统相关的默认名称分隔符。
 */
public class FileFiledsDemo {
    public static void main(String[] args) {
        // 获取路径分隔符
        String pathSeparator = File.pathSeparator;
        System.out.println("pathSeparator = " + pathSeparator); // ;
        
        // 获取文件名称分隔符
        String separator = File.separator;
        System.out.println("separator = " + separator); // \
    }
}

小结

File类的成员变量主要是获取系统相关的路径分隔符 和 文件名称分隔符。而不同的 操作系统路径和目录的分隔符会有所不同。所以一般我们不会将分隔符的符号写死。而是通过File类的成员变量获取。 参考代码

/*
  路径分隔符:Windows系统使用 ";"    Linux系统使用  ":"
  文件名称分隔符:Windows系统使用 "\"    Linux系统使用  "/"
*/
public static void main(String[] args) {
  // 获系统分隔符
  "%SystemRoot%;%JAVA_HOME%\bin";
  "%SystemRoot%" + File.pathSeparator + "%JAVA_HOME%" + File.separator + "bin";
}

路径介绍

在日常生活中指的是道路。从起点到终点经过的所有途径。

在网络中,路径指的是从起点到终点的全程路由。比如需要在电脑中查找某个文件,查找的过程中经历的每一个目录的组合,就是当前文件的路径。

在计算机中,路径分为绝对路径 和 相对路径

  • 绝对路径:指的是一个完整的路径。它是以盘符(C: D:)开始的路径。
    C:\aaa\bbb\ccc\1.txt
  • 相对路径:是一个简化的路径。相对指的是相对于当前项目的根目录。

如果使用当前项目的根目录,路径可以简化书写:

E:\\ideawork\\javase_XX期\\就业班\\dayXX ----> dayXX(可以省略项目的根目录)
  • 路径使用注意事项:
    1. 路径的书写格式是不区分大小写的
      2. 路径中文件名称分隔符Windows使用右斜杠\,反斜杠在java中表示转义符,两个反斜杠代表一个 普通的反斜杠。

1.3 构造方法

目标

  • 能够使用File的构造方法创建File对象。

成员方法:

  • public File( String pathname ) :通过将给定的路径名字符串转换为抽象路径名来创建新的 File实例。
  • public File( String parent, String child ) :从父路径名字符串和子路径名字符串创建新的 File实例。
  • public File( File parent, String child ) :从父抽象路径名和子路径名字符串创建新的 File实例。

注意事项:

  • File类的构造方法创建File对象时,JVM不会检测当前路径是否存在。File类有专门判断路径是否存在的方法。
  • File类的构造方法中,指定的路径可以是绝对路径,也可以是相对路径。

步骤

  1. 使用File类的各个构造方法创建File对象。

  2. 通过指定各种不同类型的路径名称创建File对象。

  3. 打印输出File对象,查看数据变化。

    import java.io.File;
    /*
    演示File类的构造方法:
    File(String pathname)
    创建一个指定字符串路径的文件对象。
    File(String parent, String child)
    将指定的字符串父目录和字符串子目录合并,创建一个文件对象。
    File(File parent, String child)
    将指定的文件父目录和字符串子目录合并,创建一个文件对象。

         在创建File对象时,不判断路径是否存在,即使不存在也可以正常创建。
             在File中有方法来完成文件路径是否存在的判断。
             路径可以以文件结尾,也可以以文件夹结尾。
             路径可以是绝对路径,也可以是相对路径。
             路径的格式:
                 盘符:/路径/文件;
                 盘符:\\路径\\文件;
    

    */
    public class FileConstructorDemo {
    public static void main(String[] args) {
    // File(String pathname) 创建一个指定字符串路径的文件对象。
    constructor01();

         // File(String parent, String child)
         constructor02( "C:\\abc\\","a.txt");
         constructor02( "D:\\abc\\","d.txt");
    
         // File(File parent, String child)
         constructor03();
     }
     /*
         File(String pathname)  创建一个指定字符串路径的文件对象。
     */
     public static void constructor01(){
    
         String pathname = "D:/aaa.txt";
         File file = new File( pathname );
         System.out.println("file = " + file);
     }
     /*
         File(String parent, String child)
             将指定的字符串父目录和字符串子目录合并,创建一个文件对象。
         参数:将路径参数分为两部分
             String parent;  父路径
             String child;   子路径
         好处:父路径和子路径,都可以单独书写,使用起来非常灵活,子父路径都可以变化
      */
     public static void constructor02( String parent, String child ) {
         File file = new File(parent, child);
         System.out.println(file);
     }
    
     /*
         File(File parent, String child)
         将指定的文件父目录和字符串子目录合并,创建一个文件对象。
         参数:将路径参数分为两部分
             File parent;    父路径
             String child;   子路径
         好处:
             1、父路径和子路径,都可以单独书写,使用起来非常灵活,子父路径都可以变化
             2、父路径是File类型,可以使用File类的方法对路径进行一些操作,然后在使用路径创建对象
     */
     public static void constructor03() {
         // 创建父路径File对象
         File parent = new File("D:\\abc");
    
         File file = new File(parent,"HelloWorld.java");
         System.out.println("file = " + file);
     }
    

    }

小结

File的构造方法主要是用于创建File对象。一个File对象代表硬盘中实际存在的一个文件或者目录。无论该路径下是否存在文件或者目录,都不影响File对象的创建。构造方法中指定的路径可以是绝对路径或是相对路径。

1.4 常用方法

1.4.1 获取功能的方法

目标

  • 能够使用File类中获取功能的常用方法。

  • public String getAbsolutePath() :返回此File的绝对路径名字符串。

    • 获取的是当前File对象的绝对路径,即使指定的路径为相对路径。
  • public String getPath() :获取File对象构造方法中指定的路径。

  • public String getName() :获取File对象构造方法中指定的最后一级目录。

    • 最后一级目录可以是文件或是文件夹。
    • 文件夹是没有大小概念的,所以不能获取文件夹的大小。
    • 如果构造方法中指定的路径是不存在的,则返回的字节数为0。
  • public long length() :获取File对象的字节数。

    • 文件夹是没有大小概念的,所以不能获取文件夹的大小。
    • 如果构造方法中指定的路径是不存在的,则返回的字节数为0。

步骤

  1. 创建File文件对象
  2. 分别调用常用的获取功能的方法
  3. 输出打印返回的数据结果

实现

import java.io.File;
// 演示File类获取的常用方法
public class FileGetMethodDemo {
    /*
        public String getAbsolutePath() :返回此File的绝对路径名字符串。
        public String getPath() :将此File转换为路径名字符串。
        public String getName()  :返回由此File表示的文件或目录的名称。
        public long length()  :返回由此File表示的文件的长度。
     */
    public static void main(String[] args) {
        method4();
    }
    /*
        public long length()  :返回由此File表示的文件的长度。
        获取构造方法中传递的文件的大小,以字节为单位
        注意:
            1、文件夹是没有大小概念的,不能获取文件夹的大小
            2、如果构造方法中传递的路径不存在,则length()方法返回0
     */
    private static void method4() {
        File f1 = new File("E:\\ideawork\\javase_XX期\\就业班\\dayXX\\demo\\abc.txt");
        long length1 = f1.length();
        System.out.println("length1 = " + length1); // 894

        File f2 = new File("E:\\ideawork\\javase_XX期\\就业班\\dayXX\\demo\\123.txt");
        long length2 = f2.length();
        System.out.println("length2 = " + length2); // 0
    }

    /*
        public String getName()  :返回由此File表示的文件或目录的名称。
        返回构造方法中传递的路径的最后一级目录。
        不管最后一级目录是文件夹 还是 文件都正常返回
     */
    private static void method3() {
        File f1 = new File("E:\\ideawork\\javase_XX期\\就业班\\dayXX\\1.txt");
        String name1 = f1.getName();
        System.out.println("name1 = " + name1); // 1.txt
        
        File f2 = new File("E:\\ideawork\\javase_XX期\\就业班\\dayXX");
        String name2 = f2.getName();
        System.out.println("name2 = " + name2); // day08
    }

    /*
        public String getPath() :将此File转换为路径名字符串。
        返回构造方法中传递的路径
        如果构造方法中传递的是绝对路径,则返回绝对路径
        如果构造方法中传递的是相对路径,则返回相对路径
     */
    private static void method2() {
        File f1 = new File("E:\\ideawork\\javase_XX期\\就业班\\dayXX\\1.txt");
        String path1 = f1.getPath();
        System.out.println("path1 = " + path1);
        
        File f2 = new File("1.txt");
        String path2 = f2.getPath();
        System.out.println("path2 = " + path2); // 1.txt
    }

    /*
        public String getAbsolutePath() :返回此File的绝对路径名字符串。
        返回构造方法中传递的路径
        不管传递的是绝对路径还是相等路径,最终返回的都是绝对路径
     */
    private static void method1() {
        File f1 = new File("E:\\ideawork\\javase_XX期\\就业班\\dayXX\\1.txt");
        String absolutePath1 = f1.getAbsolutePath();
        System.out.println("absolutePath1 = " + absolutePath1); 

        File f2 = new File("1.txt");
        String absolutePath2 = f2.getAbsolutePath();
        System.out.println("absolutePath2 = " + absolutePath2); 
    }
}

小结

File类中的获取方法,主要是获取File对象的各类信息。其他的获取方法请自行查阅API。

1.4.2 判断功能的方法

目标

  • 能够使用File类中的判断功能的方法。
  • public boolean exists() :此File表示的文件或目录是否实际存在。
    • true:表示当前File对象表示的路径是存在的。
    • false:表示当前File对象表示的路径是不存在的。
  • public boolean isDirectory() :此File表示的是否为目录。
    • true:表示当前File对象表示的路径是文件夹。
    • false:表示当前File对象表示的路径是文件,或当前路径不存在。
  • public boolean isFile() :此File表示的是否为文件。
    • true:表示当前File对象表示的路径是文件。
    • false:表示当前File对象表示的路径是文件夹,或当前路径不存在。
  • 注意事项:
    • 在判断某个路径是否为文件或文件夹之前,首先需要判断当前路径是否存在。
    • isFile() 和 isDirectory()两个方法为互斥的方法,一个路径的指向,不是文件就是文件夹。

步骤

  1. 创建File对象,分别指定存在或不存在的路径。
  2. 调用判断的方法,获取结果并输出打印。

实现

import java.io.File;
// 演示File类判断的常用方法
public class FileMethodDemo {
    /*
        public boolean exists() :此File表示的文件或目录是否实际存在。
        public boolean isDirectory() :此File表示的是否为目录。
        public boolean isFile() :此File表示的是否为文件。
     */
    public static void main(String[] args) {
        method2();
    }

    /*
        public boolean isDirectory() :此File表示的是否为目录。
            判断传递的文件是否为文件夹。
            是:true
            不是: false
        public boolean isFile() :此File表示的是否为文件。
            判断传递的文件是否为文件。
            是:true
            不是: false
        注意:
            1、电脑的硬盘上只有文件 或 文件夹 ,两个方法互斥。
            2、两个方法的使用前提是路径必须是真实存在的,否则都返回false
            所以在判断是否为文件或文件夹之前,一般要先判断路径是否存在
     */
    private static void method2() {
        File f1 = new File("E:\\ideawork\\javase_XX\\就业班\\dayXX");
        if( f1.exists() ) {
            System.out.println( f1.isDirectory()); // true
            System.out.println( f1.isFile()); // false
        }

        File f2 = new File("E:\\ideawork\\javase_XX期\\就业班\\dayXX\\dayXX.iml");
        if( f2.exists() ) {
            System.out.println( f2.isDirectory() ); // false
            System.out.println( f2.isFile()); // true
        }
    }

    /*
        public boolean exists() :此File表示的文件或目录是否实际存在。
        判断构造方法中传递的路径是否存在。
        存在:true
        不存在:false
     */
    private static void method1() {
        File f1 = new File("E:\\ideawork\\javase_XX期\\就业班\\dayXX");
        boolean exists1 = f1.exists();
        System.out.println("exists1 = " + exists1); // true
        
        File f2 = new File("E:\\123.txt");
        boolean exists2 = f2.exists();
        System.out.println("exists2 = " + exists2); // false
    }
}

小结

File类中判断方法,主要用于判断当前文件路径是否存在,当前路径是文件还是文件夹。其他的判断方法请自行查阅API。

1.4.3 创建删除功能的方法

目标

  • 能够使用File类中的创建和删除的方法。
  • public boolean createNewFile() :当File对象指定的文件不存在时,创建一个新的空文件。
    • 当前方法只能用于文件的创建,不能创建文件夹,也不能操作文件中的数据。
  • public boolean delete() :删除由此File表示的文件或目录。
    • true:删除成功。
    • false:删除失败,或当前路径不存在。或删除的文件夹中包含内容。
    • 删除不走回收站,使用需谨慎。
  • public boolean mkdir() :创建由此File表示的单级目录。如果文件存在则创建失败。
  • public boolean mkdirs() :创建由此File表示的多级目录。如果文件存在则创建失败。
    • 当前方法只能创建文件夹,不能创建文件。

步骤

  1. 创建File对象,分别指定存在或不存在的路径

  2. 调用File类的创建和删除方法

  3. 运行程序查看结果

    import java.io.File;
    import java.io.IOException;

    // 演示File类创建和删除的方法
    public class FileMethodDemo {
    /*
    public boolean createNewFile() :当且仅当具有该名称的文件尚不存在时,创建一个新的空文件。
    public boolean delete() :删除由此File表示的文件或目录。
    public boolean mkdir() :创建由此File表示的目录。
    public boolean mkdirs() :创建由此File表示的目录,包括任何必需但不存在的父目录。
    /
    public static void main(String[] args) throws IOException {
    method3();
    }
    /

    public boolean delete() :删除由此File表示的文件或目录。
    删除构造方法中指定路径的文件 或 文件夹
    返回值:
    true:文件或文件夹删除成功,返回true
    false:文件夹中有内容,不会删除返回false;给定的路径不存在返回false。
    注意:
    delete方法是从硬盘上直接删除文件或文件夹,不走回收站,删除需谨慎。
    */
    private static void method3() {
    File f1 = new File(“E:\demo\1.txt”);
    boolean d1 = f1.delete();
    System.out.println("d1 = " + d1);

         // 删除的文件夹有内容,无法删除
         File f2 = new File("e:\\demo\\aaa");
         boolean d2 = f2.delete();
         System.out.println("d2 = " + d2);
         
         // 删除的路径不存在无法删除
         File f3 = new File("e:\\abc");
         boolean d3 = f3.delete();
         System.out.println("d3 = " + d3);
     }
    
     /*
         public boolean mkdir() :只能创建单级目录。
         public boolean mkdirs() :既可以创建单级目录,也可以创建多级目录。
         返回值:
             true: 路径文件夹不存在,创建文件夹返回true
             false:路径文件夹存在,不会创建返回false
         注意:
             此方法只能创建文件,不能创建文件夹
      */
     private static void method2() {
         // 创建单级目录
         File f1 = new File("E:\\demo\\aaa");
         boolean mkdir1 = f1.mkdir();
         System.out.println("mkdir1 = " + mkdir1);
         
         // 创建多级目录
         File f2 = new File("E:\\demo\\aaa\\bbb\\ccc\\ddd");
         boolean mkdir2 = f2.mkdirs();
         System.out.println("mkdir2 = " + mkdir2);
    
         // 创建有后缀名的文件夹。
         File f3 = new File("E:\\demo\\aaa.txt");
         boolean mkdir3 = f3.mkdirs();
         System.out.println("mkdir3 = " + mkdir3);
     }
    
     /*
         public boolean createNewFile() :创建一个新的空文件。
         根据构造方法指定的路径,创建出一个空的文件
         返回值:
             true:路径文本不存在,创建文件,返回true
             false: 文件存在,不会创建,返回false
         注意:
             1、此方法只能用来创建文件,不能创建文件夹
             2、创建文件的路径必须存在,否则抛出异常
     */
     private static void method1() throws IOException {
         // 路径存在
         File f1 = new File("E:\\demo\\1.txt");
         boolean boo1 = f1.createNewFile();
         System.out.println("boo1 = " + boo1);
    
         // 路径不存在
         File f2 = new File("E:\\demo\\123\\1.txt");
         //boolean boo2 = f2.createNewFile();
         //System.out.println("boo2 = " + boo2);
    
         // 不带后缀名的文件,创建的是文件不是文件夹
         File f3 = new File("E:\\demo\\新建文件");
         boolean boo3 = f3.createNewFile();
         System.out.println("boo3 = " + boo3);
     }
    

    }

小结

File类中的创建和删除的方法主要是用于创建文件和文件夹,以及删除文件或文件夹。需要注意的是创建文件的方法只能创建文件,而创建文件夹的方法则只能创建文件夹。

1.5 目录的遍历

目标

  • 能够完成指定路径的文件夹的遍历。

  • public String[] list() :获取文件夹中每个子文件和子文件夹的字符串名称,保存到String数组中。

  • public File[] listFiles() :获取文件夹中每个子文件和文件夹的File对象,保存到File数组中。

  • 特别注意:

    • list() 和 listFiles()都是用来遍历目的方法,只是返回的类型不一致。
    • 如果指定的文件路径不存在,则会抛出空指针异常。
    • 如果指定的文件路径不是文件夹,则会抛出空指针异常。

步骤

  1. 创建File对象,指定文件夹路径
  2. 分别调用list() 和 listFiles()方法,获取对应的数据。
  3. 遍历数组,查看具体的结果

实现

import java.io.File;
import java.io.IOException;

// 演示File类遍历(列举)方法
public class FileMethodDemo {
    /*
          public String[] list() :返回一个String数组,表示该File目录中的所有子文件或目录。
          public File[] listFiles() :返回一个File数组,表示该File目录中的所有的子文件或目录。
          注意:
            1、list 和 listFiles方法都想用来遍历文件对象的目录 , 返回的数据不同
            2、如果当前文件路径不存在,则会抛出空指针异常
            3、如果当前文件不是文件夹,也会抛出空指针异常

     */
    public static void main(String[] args) throws IOException {
        method2();
    }
    /*
        public File[] listFiles() :返回一个File数组,表示该File目录中的所有的子文件或目录。
        遍历文件路径下的所有文件,将获取到的文件或文件夹再次封装成文件对象,保存到File数组中并返回
     */
    private static void method2() {
        File f1 = new File("e:\\demo");
        File[] files = f1.listFiles();
        for (File file : files) {
            System.out.println( file );
        }
    }
    /*
        public String[] list() :返回一个String数组,表示该File目录中的所有子文件或目录。
        遍历文件路径下的所有文件,获取文件的字符串名称,保存到String数组中并返回
     */
    private static void method1() {
        File f1 = new File("e:\\demo");
        String[] dir = f1.list();
        for( String fileName : dir ) {
            System.out.println(fileName);
        }

        // 文件对象不是文件夹 或 路径不存在 抛空指针异常
        File f2 = new File("e:\\demo\\0.jpg");
        String[] dir2 = f2.list();
        for (String s : dir2) {
            System.out.println(s);
        }
    }
}

小结

调用list() 或 listFiles()方法的File对象,表示的必须是实际存在的目录,否则返回null,无法进行遍历。并且指定的目录也必须是文件夹。

第二章 递归

2.1 概述

目标

  • 能够理解递归的概念。

在前面学习方法时,我们知道可以通过一个方法调用另外一个方法。而方法本身也可以调用它自己本身。我们将这种现象称为:递归。

  • 递归:指在当前方法内调用自己的这种现象。递归分为两种: 直接递归和间接递归。

    • 直接递归:表现为方法自身调用自己。
    • 间接递归:表现为方法A调用方法B,方法B调用方法A
  • 递归注意事项

    • 递归一定要有结束的条件限定,保证递归能够停止下来,否则会发生栈内存溢出。
    • 即使在有结束条件的限定下,递归的次数也不宜过多,否则也会发生栈内存溢出。
    • 构造方法禁止递归。
  • 递归的使用前提
    当调用方法的时候,方法的主体不变,但是每次调用方法时的参数不同,可以使用递归。

提示

当一个方法调用另外一个方法的时候,被调用的方法如果没有执行完毕,当前方法就会一直等待被调用的方法执行完毕之后,才会继续执行。

步骤

递归演示

  1. 创建a()方法,在方法内部调用自己本身。
  2. 执行程序,查看结果。
  3. 创建a( int i )方法
  4. 方法内部打印i,然后判断i是否==20000,是则结束程序
  5. 如果i!=20000,则方法内部调用a(++i);
  6. 运行程序,查看结果。

实现

// 演示递归异常
public class FileMethodDemo {
    /*
          递归一定要有结束的条件限定,保证递归能够停止下来,否则会发生栈内存溢出。
          即使在有结束条件的限定下,递归的次数也不宜过多,否则也会发生栈内存溢出。
          构造方法禁止递归。
          递归的使用前提
            当调用方法的时候,方法的主体不变,但是每次调用方法时的参数不同,可以使用递归。
     */
    public static void main(String[] args) throws IOException {
        //a();
        a(1);
    }
    /*
        即使在有结束条件的限定下,递归的次数也不宜过多,否则也会发生栈内存溢出。
     */
    private static void a( int i ) {
        System.out.println( i );
        if( i == 20000 ) {
            return;
        }
        a(++i);
    }

    /*
        递归一定要有结束的条件限定,保证递归能够停止下来,否则会发生栈内存溢出。
        原因是a方法在栈内存中出现的次数过多,导致栈内存容纳不下。
     */
    private static void a() {
        a();
    }
}

小结

递归指的是方法调用自己本身,分为直接和间接递归。递归在使用时,要有结束的条件。并且递归的次数不能过多。否则都会造成内存的溢出。

2.2 递归累和

目标

  • 能够使用递归计算1-n的和值。

步骤

分析:

计算的公式:1 + 2 + 3 ... + n;   也可以被推导为下列公式: n + ( n - 1 ) + ( n - 2 ) + ... + 1;

已知最大值为: n ,最小值为: 1
  1. 递归的结束条件,就是获取到1的时候结束
    2. 递归的目的,就是获取下一个需要被加的数字( n - 1 )

实现步骤:

  1. 定义sum( int n )方法。
  2. 判断 n == 1。满足条件则直接return 1;
  3. 如果 n != 1。 return n + sum( n - 1 );
  4. 调用方法,传递3并获取返回值运行查看结果。

实现

// 演示递归计算1-n之间的和值。
public class Demo {
    public static void main(String[] args) {
        
        int sum = sum(3);
        System.out.println("sum = " + sum);
    }
    // 使用递归计算 1 - n之间的和值
    public static int sum( int n ){
        // 当获取到1的时候,结束程序
        if( n == 1 ) {
            return 1;
        }
        // 当num不为1时,调用sum方法本身,获取下一个被加的数字( n - 1 )
        return n + sum(n - 1);
    }
}

小结

遇到需求,如果通过其他方式能够完成功能的实现,建议不要使用递归。运行效率比较低。

2.2.1 递归求阶乘

  • 阶乘:所有小于及等于该数的正整数的积。

    n的阶乘:n! = n * (n-1) 3 * 2 * 1

分析:这与累和类似,只不过换成了乘法运算,学员可以自己练习,需要注意阶乘值符合int类型的范围。

推理得出:n! = n * (n-1)!

代码实现:

public class DiGuiDemo {
  	//计算n的阶乘,使用递归完成
    public static void main(String[] args) {
        int n = 3;
      	// 调用求阶乘的方法
        int value = getValue(n);
      	// 输出结果
        System.out.println("阶乘为:"+ value);
    }
	/*
  	  通过递归算法实现.
  	  参数列表:int 
  	  返回值类型: int 
  	*/
    public static int getValue(int n) {
      	// 1的阶乘为1
        if (n == 1) {
            return 1;
        }
      	/*
      	  n不为1时,方法返回 n! = n*(n-1)!
          递归调用getValue方法
      	*/
        return n * getValue(n - 1);
    }
}

2.3 递归打印多级目录

目标

  • 能够使用递归完成多级目录的打印。

分析:多级目录的打印,就是当目录的嵌套。需要将指定路径中的所有文件获取。如果获取到的是文件则直接打印,如果获取到的是文件夹则需要继续遍历。

步骤

  1. 定义printDir( File file )方法。参数为需要遍历的文件对象。
  2. 调用listFiles()方法获取指定文件中的所有的File对象。
  3. 遍历获取的File对象数组,取出每个文件对象。
  4. 调用isDirectory()方法,判断当前获取的文件对象是否为文件夹对象
  5. 如果是则继续调用printDir()方法,将新获取的文件夹对象传递继续遍历
  6. 如果不是文件夹对象,则表示一定是文件对象,直接打印当前文件对象。

实现

// 演示递归打印多级目录
public class Demo {
    public static void main(String[] args) {
        // 创建文件对象
        File file = new File("e:\\demo");
        printDir( file );
    }
    // 使用递归的方式,遍历文件目录下的所有文件和子文件,子子文件....
    public static void printDir( File file  ){
        // 获取文件对象内的所有子文件对象
        File[] files = file.listFiles();
        // 遍历获取文件内的所有子文件对象
        for( File dirs : files ) {
            // 对获取的文件对象进行判断,如果是文件就打印输出
            if( dirs.isFile() ) {
                System.out.println(dirs);
            } else {
                // 进入else说明当前文件对象是文件夹,是文件夹就继续遍历。
                printDir( dirs );
            }
        }
    }
}

小结

通过递归可以完成指定文件路径的遍历,需要注意的是,如果获取到的文件对象是文件夹对象,需要重新遍历的是新获取的文件夹对象,而不是原来是文件对象。

第三章 综合案例

3.1 文件搜索

目标

  • 能够使用递归获取指定目录中的指定类型的文件。

搜索E:\demo 目录中的.txt 文件。

步骤

  1. 复制使用前面的案例代码进行扩展。
  2. 如果判断的结果为文件对象,则继续对文件对象进行处理。
  3. 获取文件对象的名称
  4. 因为后缀名不区分大小写,所以将获取的文件名称全部改为小写。以免漏到大写后缀的文件。
  5. 对小写之后的文件名做判断,是否是以.txt结尾的。如果是则打印。

实现

// 演示递归打印指定类型的文件
public class Demo {
    public static void main(String[] args) {
        // 创建文件对象
        File file = new File("e:\\demo");
        printDir( file );
    }
    // 递归打印指定类型的文件
    public static void printDir( File file  ){
        // 获取文件对象内的所有子文件对象
        File[] files = file.listFiles();
        // 遍历获取文件内的所有子文件对象
        for( File dirs : files ) {
            // 对获取的文件对象进行判断,如果是文件就打印输出
            if( dirs.isFile() ) { // 判断文件对象是否是文件
               /* // 先获取文件对象的String类型的名称
                String name = dirs.getName();
                //String name1 = dirs.getAbsolutePath();
                //String name2 = dirs.getPath();

                // 为了防止有的后缀名为大写,导致无法获取,先将文件名称改为全部小写
                String s = name.toLowerCase();

                // 判断文件名称是否以.txt为结尾
                if( s.endsWith(".txt") ) {
                    System.out.println(dirs);
                }*/
               // 使用链式编程一步完成
               if( dirs.getPath().toLowerCase().endsWith(".txt") ) {
                   System.out.println(dirs);
               }
            } else {
                // 进入else说明当前文件对象是文件夹,是文件夹就继续遍历。
                printDir( dirs );
            }
        }
    }
}

小结

获取到文件对象之后,可以对获取的文件对象进行继续的各类操作。针对于获取指定类型的文件案例,需要特别注意:后缀名是不区分大小写的。所以在获取到文件名之后,需要将文件名全部改为大小或小写,在进行判断。

3.2 文件过滤器优化

  • 过滤器:就是把不需要的过滤出去,留下需要的内容。
  • 文件或文件夹过滤器:过滤掉不需要的文件或文件夹,留下需要的文件或文件夹。

3.2.1 FileFilter文件过滤器

目标

  • 能够使用文件过滤器,过滤指定类型的文件。

在File类中,listFiles()有一个重载的方法,重载方法中传递的参数就是文件过滤器对象。

  • java.io.FileFilter: 抽象路径名的文件过滤器。 用来过滤文件。过滤的对象是文件对象。
  • 抽象方法:
    • boolean accept(File pathname) :用来过滤文件的方法。结果为true则保留。
      • File pathname:指定遍历的文件对象中的所有子文件对象。

步骤

  1. 使用接口实现类的方式完成文件的过滤。
  2. 创建一个FileFilter接口的实现类。
  3. 在实现类中重写accept()方法。
  4. 对参数File pathname进行判断:
    1. 判断当前文件对象是否为文件夹,如果是则保留继续遍历。
    2. 如果当前文件对象是文件,则判断是否为指定后缀名的文件。是则打印输出。
  5. 定义printDir(File file)方法,用于递归。
  6. 调用listFiles()方法,并传递过滤器实现类对象。
  7. 对listFiles()返回的File数组进行遍历。是文件夹继续遍历,是文件直接打印输出。
  8. 分别在使用匿名内部类 和 Lambda表达式实现。

实现

使用自定义实现类代码演示:

// 自定义实现类对象
public class MyFileFilter implements FileFilter {
   /*
        重写过滤的方法
        pathname就是需要过滤的文件对象内的所有子文件对象
        如果拿到的是文件对象,需要将后缀名为.txt的文件留下,其他类型的文件过滤掉
        如果拿到的是文件夹对象,也需要留下来继续遍历,取出其中的子文件和子子文件
    */
    public boolean accept(File pathname) {
        // 判断是否为文件夹,如果是则过滤下来,继续遍历
        if( pathname.isDirectory() ) {
            return true;
        }
        // 遍历是否是以.txt后缀结尾的文件,如果是过滤下来直接打印
        return pathname.getPath().toLowerCase().endsWith(".txt");
    }
}



// 演示递归打印指定类型的文件
public class FileFilterDemo {
    public static void main(String[] args) {
        // 创建文件对象
        File file = new File("e:\\demo");
        printDir( file );
    }
    // 递归打印指定类型的文件
    public static void printDir( File file  ){
        // 获取文件对象内的所有子文件对象
        File[] files = file.listFiles( new MyFileFilter() );
        // 遍历获取文件内的所有子文件对象
        for( File dirs : files ) {
            // 判断文件对象是否是文件
            if( dirs.isFile() ) {
                System.out.println(dirs);
            } else {
                // 进入else说明当前文件对象是文件夹,是文件夹就继续遍历。
                printDir( dirs );
            }
        }
    }
}    

使用匿名内部类代码演示

public class FileFilterDemo {
    public static void main(String[] args) {
        // 创建文件对象
        File file = new File("e:\\demo");
        printDir( file );
    }
    // 递归打印指定类型的文件
    public static void printDir( File file  ){
        // 获取文件对象内的所有子文件对象
        File[] files = file.listFiles( new FileFilter(){
            public boolean accept(File pathname) {
                /*
                     1、判断是否为文件夹,如果是则过滤下来,继续遍历
                     2、判断是否是以.txt后缀结尾的文件,如果是过滤下来直接打印
                  */
                return pathname.isDirectory() || pathname.getPath().toLowerCase().endsWith(".txt");
            }
        } );
        // 遍历获取文件内的所有子文件对象
        for( File dirs : files ) {
            // 判断文件对象是否是文件
            if( dirs.isFile() ) {
                System.out.println( dirs );
            } else {
                // 进入else说明当前文件对象是文件夹,是文件夹就继续遍历。
                printDir( dirs );
            }
        }
    }
}

使用Lambda表达式代码演示

分析:FileFilter是只有一个方法的接口,因此可以用lambda表达式简写。

public class FileFilterDemo {
    public static void main(String[] args) {
        // 创建文件对象
        File file = new File("e:\\demo");
        printDir( file );
    }
    // 递归打印指定类型的文件
    public static void printDir( File file  ){
        // 获取文件对象内的所有子文件对象
        File[] files = file.listFiles( pathname -> 
                pathname.isDirectory() || pathname.getPath().toLowerCase().endsWith(".txt"));
        // 遍历获取文件内的所有子文件对象
        for( File dirs : files ) {
            // 判断文件对象是否是文件
            if( dirs.isFile() ) {
                System.out.println(dirs);
            } else {
                // 进入else说明当前文件对象是文件夹,是文件夹就继续遍历。
                printDir( dirs );
            }
        }
    }
}

小结

FileFilter是用来过滤指定文件对象的过滤器。在抽象方法accept()方法中规定需要保留的文件即可。

3.2.2 FilenameFilter文件名过滤器

目标

  • 能够使用FilenameFilter文件名过滤器过滤指定类型的文件。
  • java.io.FilenameFilter:抽象路径名的文件名滤器。 过滤的对象是文件的名称。
  • 抽象方法:
    • accept(File dir, String name):用来过滤指定文件名的抽象方法。
      • File dir:被list()方法过滤的那个文件对象。
      • String name:文件对象下,所有子文件的字符串名称。

步骤

  1. 定义printDir(File file),用于递归遍历。
  2. 调用listFiles()方法,使用Lambda表示式传递FilenameFilter接口对象,重写accept( File dir, String name )方法。
  3. 因为获取的是需要遍历的文件对象和其所有的子文件的名称,因此需要将其先封装成以个完成的File对象。
  4. 对封装的完成的File对象进行判断是否为文件夹 和 是否为指定类型的文件。
  5. 遍历listFiles()返回的File数组,如果是文件则直接打印,如果是文件夹则继续遍历。

实现

public class FileFilterDemo {
    public static void main(String[] args) {
        // 创建文件对象
        File file = new File("e:\\demo");
        printDir( file );
    }
    // 递归打印指定类型的文件
    public static void printDir( File file  ){
        // 获取文件对象内的所有子文件对象
        File[] files = file.listFiles( (File dir, String name) -> 
                /*
                    File dir:遍历的文件目录
                    String name: 文件目录下的所有文件名称。
                    1、先将文件对象和文件名称封装成File对象
                    2、判断封装的文件对象是文件还是文件夹
                        如果是文件判断:判断是否以.txt为后缀名的文件
                        如果是文件夹,则保留继续遍历
                 */
                new File(dir,name).isDirectory() || name.toLowerCase().endsWith(".txt")
            
        );
        // 遍历获取文件内的所有子文件对象
        for( File dirs : files ) {
            // 判断文件对象是否是文件
            if( dirs.isFile() ) {
                System.out.println(dirs);
            } else {
                // 进入else说明当前文件对象是文件夹,是文件夹就继续遍历。
                printDir( dirs );
            }
        }
    }
}

小结

FilenameFilter是文件的名称过滤器,它获取的是文件的名称,在操作上没有文件过滤器方便。

3.3 获取电脑中的所有的.java文件

目标

  • 能够遍历计算机中所有的目录,并获取后缀名为.java的文件。
  • static File[] listRoots() 列出可用的文件系统根。 即获取电脑中的各个盘符。

步骤

  1. 定义方法printDir( File file ),用于递归遍历。
  2. 调用listFiles()方法,并使用Lambda表达式传递FileFilter文件过滤器对象。
  3. 重写accept()方法,过滤需要保留的文件对象
  4. 对listFiles()方法返回的数组进行遍历。如果是文件直接打印,如果是文件夹则继续遍历。
  5. 在main方法中使用File类的静态方法listRoots()获取所有的根盘符
  6. 遍历获取的所有根盘符,传递给printDir()方法进行遍历。

实现

public class Demo {
    public static void main(String[] args) {
        // 获取所有的根目录
        File[] files = File.listRoots();
        // 遍历根目录
        for( File dir : files ) {
            printDir(dir);
        }
    }

    public static void printDir( File file ) {
        // 调用listFiles方法,获取文件对象中的所有子文件对象
        File[] files = file.listFiles( pathname-> pathname.isDirectory() || pathname.getName().toLowerCase().endsWith(".java")  );
        /*
            因为在系统中,有很多受保护的文件。使用windows系统都无法访问,通过File类是依然
            无法访问的,因此会获取到空文件。所以在遍历之前,要确保获取的对象不为null。
            在File对象不为null的情况下载进行遍历
         */
        if( files != null ) {
            // 遍历获取的文件对象
            for (File dir : files) {
                // 判断获取的文件对象是否为文件夹
                if (dir.isDirectory()) {
                    // 继续遍历
                    printDir(dir);
                } else {
                    // 是文件直接打印
                    System.out.println(dir);
                }
            }
        }
    }
}

小结

在对通过遍历方法获取的File[]遍历之前,要先判断数组中是否存在null值,否则会抛出空指针异常。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值