返回介绍

10.4.1 目录列表器

发布于 2024-10-15 23:56:23 字数 5931 浏览 0 评论 0 收藏 0

现在假设我们想观看一个目录列表。可用两种方式列出 File 对象。若在不含自变量(参数)的情况下调用 list(),会获得 File 对象包含的一个完整列表。然而,若想对这个列表进行某些限制,就需要使用一个“目录过滤器”,该类的作用是指出应如何选择 File 对象来完成显示。

下面是用于这个例子的代码(或在执行该程序时遇到困难,请参考第 3 章 3.1.2 小节“赋值”):

//: DirList.java
// Displays directory listing
package c10;
import java.io.*;

public class DirList {
  public static void main(String[] args) {
    try {
      File path = new File(".");
      String[] list;
      if(args.length == 0)
        list = path.list();
      else 
        list = path.list(new DirFilter(args[0]));
      for(int i = 0; i < list.length; i++)
        System.out.println(list[i]);
    } catch(Exception e) {
      e.printStackTrace();
    }
  }
}

class DirFilter implements FilenameFilter {
  String afn;
  DirFilter(String afn) { this.afn = afn; }
  public boolean accept(File dir, String name) {
    // Strip path information:
    String f = new File(name).getName();
    return f.indexOf(afn) != -1;
  }
} ///:~

DirFilter 类“实现”了 interface FilenameFilter(关于接口的问题,已在第 7 章进行了详述)。下面让我们看看 FilenameFilter 接口有多么简单:

public interface FilenameFilter {

boolean accept(文件目录,字串名);

}

它指出这种类型的所有对象都提供了一个名为 accept() 的方法。之所以要创建这样的一个类,背后的全部原因就是把 accept() 方法提供给 list() 方法,使 list() 能够“回调”accept(),从而判断应将哪些文件名包括到列表中。因此,通常将这种技术称为“回调”,有时也称为“算子”(也就是说,DirFilter 是一个算子,因为它唯一的作用就是容纳一个方法)。由于 list() 采用一个 FilenameFilter 对象作为自己的自变量使用,所以我们能传递实现了 FilenameFilter 的任何类的一个对象,用它决定(甚至在运行期)list() 方法的行为方式。回调的目的是在代码的行为上提供更大的灵活性。

通过 DirFilter,我们看出尽管一个“接口”只包含了一系列方法,但并不局限于只能写那些方法(但是,至少必须提供一个接口内所有方法的定义。在这种情况下,DirFilter 构建器也会创建)。

accept() 方法必须接纳一个 File 对象,用它指示用于寻找一个特定文件的目录;并接纳一个 String,其中包含了要寻找之文件的名字。可决定使用或忽略这两个参数之一,但有时至少要使用文件名。记住 list() 方法准备为目录对象中的每个文件名调用 accept(),核实哪个应包含在内——具体由 accept() 返回的“布尔”结果决定。

为确定我们操作的只是文件名,其中没有包含路径信息,必须采用 String 对象,并在它的外部创建一个 File 对象。然后调用 getName(),它的作用是去除所有路径信息(采用与平台无关的方式)。随后,accept() 用 String 类的 indexOf() 方法检查文件名内部是否存在搜索字串"afn"。若在字串内找到 afn,那么返回值就是 afn 的起点索引;但假如没有找到,返回值就是-1。注意这只是一个简单的字串搜索例子,未使用常见的表达式“通配符”方案,比如"fo?.b?r*";这种方案更难实现。

list() 方法返回的是一个数组。可查询这个数组的长度,然后在其中遍历,选定数组元素。与 C 和 C++的类似行为相比,这种于方法内外方便游历数组的行为无疑是一个显著的进步。

1. 匿名内部类

下例用一个匿名内部类(已在第 7 章讲述)来重写显得非常理想。首先创建了一个 filter() 方法,它返回指向 FilenameFilter 的一个句柄:

//: DirList2.java
// Uses Java 1.1 anonymous inner classes
import java.io.*;

public class DirList2 {
  public static FilenameFilter 
  filter(final String afn) {
    // Creation of anonymous inner class:
    return new FilenameFilter() {
      String fn = afn;
      public boolean accept(File dir, String n) {
        // Strip path information:
        String f = new File(n).getName();
        return f.indexOf(fn) != -1;
      }
    }; // End of anonymous inner class
  }
  public static void main(String[] args) {
    try {
      File path = new File(".");
      String[] list;
      if(args.length == 0)
        list = path.list();
      else 
        list = path.list(filter(args[0]));
      for(int i = 0; i < list.length; i++)
        System.out.println(list[i]);
    } catch(Exception e) {
      e.printStackTrace();
    }
  }
} ///:~

注意 filter() 的自变量必须是 final。这一点是匿名内部类要求的,使其能使用来自本身作用域以外的一个对象。

之所以认为这样做更好,是由于 FilenameFilter 类现在同 DirList2 紧密地结合在一起。然而,我们可采取进一步的操作,将匿名内部类定义成 list() 的一个参数,使其显得更加精简。如下所示:

//: DirList3.java
// Building the anonymous inner class "in-place"
import java.io.*;

public class DirList3 {
  public static void main(final String[] args) {
    try {
      File path = new File(".");
      String[] list;
      if(args.length == 0)
        list = path.list();
      else 
        list = path.list(
          new FilenameFilter() {
            public boolean 
            accept(File dir, String n) {
              String f = new File(n).getName();
              return f.indexOf(args[0]) != -1;
            }
          });
      for(int i = 0; i < list.length; i++)
        System.out.println(list[i]);
    } catch(Exception e) {
      e.printStackTrace();
    }
  }
} ///:~

main() 现在的自变量是 final,因为匿名内部类直接使用 args[0]。

这展示了如何利用匿名内部类快速创建精简的类,以便解决一些复杂的问题。由于 Java 中的所有东西都与类有关,所以它无疑是一种相当有用的编码技术。它的一个好处是将特定的问题隔离在一个地方统一解决。但在另一方面,这样生成的代码不是十分容易阅读,所以使用时必须慎重。

2. 顺序目录列表

经常都需要文件名以排好序的方式提供。由于 Java 1.0 和 Java 1.1 都没有提供对排序的支持(从 Java 1.2 开始提供),所以必须用第 8 章创建的 SortVector 将这一能力直接加入自己的程序。就象下面这样:

//: SortedDirList.java
// Displays sorted directory listing
import java.io.*;
import c08.*;

public class SortedDirList {
  private File path;
  private String[] list;
  public SortedDirList(final String afn) {
    path = new File(".");
    if(afn == null)
      list = path.list();
    else
      list = path.list(
          new FilenameFilter() {
            public boolean 
            accept(File dir, String n) {
              String f = new File(n).getName();
              return f.indexOf(afn) != -1;
            }
          });
    sort();
  }
  void print() {
    for(int i = 0; i < list.length; i++)
      System.out.println(list[i]);
  }
  private void sort() {
    StrSortVector sv = new StrSortVector();
    for(int i = 0; i < list.length; i++)
      sv.addElement(list[i]);
    // The first time an element is pulled from
    // the StrSortVector the list is sorted:
    for(int i = 0; i < list.length; i++)
      list[i] = sv.elementAt(i);
  }
  // Test it:
  public static void main(String[] args) {
    SortedDirList sd;
    if(args.length == 0)
      sd = new SortedDirList(null);
    else
      sd = new SortedDirList(args[0]);
    sd.print();
  }
} ///:~

这里进行了另外少许改进。不再是将 path(路径)和 list(列表)创建为 main() 的本地变量,它们变成了类的成员,使它们的值能在对象“生存”期间方便地访问。事实上,main() 现在只是对类进行测试的一种方式。大家可以看到,一旦列表创建完毕,类的构建器就会自动开始对列表进行排序。

这种排序不要求区分大小写,所以最终不会得到一组全部单词都以大写字母开头的列表,跟着是全部以小写字母开头的列表。然而,我们注意到在以相同字母开头的一组文件名中,大写字母是排在前面的——这对标准的排序来说仍是一种不合格的行为。Java 1.2 已成功解决了这个问题。

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据
    我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
    原文