Thinking in java读书笔记-I/O部分(一):File类的用法

一:File类的常规用法
File(文件)类,这个名字具有一定的误导性,我们可能认为它是一个文件,其实它并非如此。其实它既可以代表一个文件的名称,又可以代表一个目录下一组文件的名称。如果它是一个文件集,那么可以对其采用list()方法,从而返回一个字符数组。

package test;
import java.io.File;

public class FileTest {
    public static void main(String args[]){
        File f_1 = new File(".\\src\\test\\CategoryDao.java");
        File f_2 = new File(".\\src\\test");
        System.out.println(f_1.list() == null);
        System.out.println(f_2.list() == null);
        System.out.println("..............");
        for(String name : f_2.list()){
            System.out.println(name);
        }
        System.out.println("..............");
        System.out.println(f_1.getName());
        System.out.println("..............");
        System.out.println(f_2.getName());
        System.out.println(f_2.getParent());
    }
}

此时实际的目录结构为:
这里写图片描述
输出结果为:

.
true
false
…………..
CategoryDao.java
DirList.java
FileTest.java
…………..
CategoryDao.java
…………..
test
.\src

由此可以得到几点信息:

  • File file = new File(“.”);,这里指代的是当前项目的目录(System.getProperty(“user.dir”) 返回的目录地址)。在本示例中是指E:\workspace\test目录。

  • 对于一个指代目录的的File对象,对其调用list()方法返回不为null;对于一个指代特定文件(如CategoryDao.java) ,其调用list()方法返回null。

我们可以依据上面的测试得到以下结论:这个File类其实更应该理解为文件路径。

接下来我们从此章中第一个示例出发进行分析(依据我的文件目录结作了一丢丢的改变):

package test;
import java.io.*;
import java.util.Arrays;
import java.util.regex.Pattern;

public class DirList {
    public static void main(String args[]){
        File path = new File(".\\src\\test");
        String[] list;
        if(args.length == 0){
            list = path.list();
        }else{
            list = path.list(new DirList().new DirFilter(args[0]));
            Arrays.sort(list, String.CASE_INSENSITIVE_ORDER);
            for(String dirItem:list){
                System.out.println(dirItem);
            }
        }
    }

    class DirFilter implements FilenameFilter{
        private Pattern pattern;
        public DirFilter(String regex){
            pattern = Pattern.compile(regex);
        }

        public boolean accept(File dir, String name){
            return pattern.matcher(name).matches();
        }
    }
}

经过上面的分析,这个程序其实已经有一部分很好理解了。最不好理解的应该是DirFilter的部分了。

先从java环境下的正则表达式入手:

概念

正则表达式是对字符串操作的一种逻辑公式,就是用事先定义好的一些特定字符、及这些特定字符的组合,组成一个“规则字符串”,这个“规则字符串”用来表达对字符串的一种过滤逻辑。

java中的正则表达式及用法:

正则表达式java api主要封装在java.util.regex的包中, 其主要包括以下三个类:
Pattern 类:
pattern 对象是一个正则表达式的编译表示。Pattern 类没有公共构造方法。要创建一个 Pattern 对象,你必须首先调用其公共静态编译方法,它返回一个 Pattern 对象。该方法接受一个正则表达式作为它的第一个参数。
Matcher 类:
Matcher 对象是对输入字符串进行解释和匹配操作的引擎。与Pattern 类一样,Matcher 也没有公共构造方法。你需要调用 Pattern 对象的 matcher 方法来获得一个 Matcher 对象。
PatternSyntaxException:
PatternSyntaxException 是一个非强制异常类,它表示一个正则表达式模式中的语法错误。

正则表达式主要是用来匹配的。现列出几种常见的匹配格式:

\
将下一字符标记为特殊字符、文本、反向引用或八进制转义符。例如,”n”匹配字符”n”。”\n”匹配换行符。序列”\\”匹配”\”,”\(“匹配”(“。

^
匹配输入字符串开始的位置。如果设置了 RegExp 对象的 Multiline 属性,^ 还会与”\n”或”\r”之后的位置匹配。

*
零次或多次匹配前面的字符或子表达式。例如,zo* 匹配”z”和”zoo”。* 等效于 {0,}。

+
一次或多次匹配前面的字符或子表达式。例如,”zo+”与”zo”和”zoo”匹配,但与”z”不匹配。+ 等效于 {1,}。

?
零次或一次匹配前面的字符或子表达式。例如,”do(es)?”匹配”do”或”does”中的”do”。? 等效于 {0,1}。

{n}
n 是非负整数。正好匹配 n 次。例如,”o{2}”与”Bob”中的”o”不匹配,但与”food”中的两个”o”匹配。

[a-z]
字符范围。匹配指定范围内的任何字符。例如,”[a-z]”匹配”a”到”z”范围内的任何小写字母。

\s
匹配任何空白字符,包括空格、制表符、换页符等。与 [ \f\n\r\t\v] 等效。

更多的就不一一列举了。需要用的时候再去查就ok了。

class DirFilter implements FilenameFilter{
        private Pattern pattern;
        public DirFilter(String regex){
            pattern = Pattern.compile(regex);
        }

        public boolean accept(File dir, String name){
            return pattern.matcher(name).matches();
        }
    }

这里的类DirFilter对象 在初始化的时候就生成了一个Pattern对象。

pattern = Pattern.compile(regex);

为了更好的理解这一段流程,可以看一下File.list(FilenameFilter filter)函数的源码:

    public String[] list(FilenameFilter filter) {
        String names[] = list();
        if ((names == null) || (filter == null)) {
            return names;
        }
        List<String> v = new ArrayList<>();
        for (int i = 0 ; i < names.length ; i++) {
            if (filter.accept(this, names[i])) {
                v.add(names[i]);
            }
        }
        return v.toArray(new String[v.size()]);
    }

由此可知,这个DirFilter将File.list()返回的字符串列中的项与正则表达式进行匹配,如果匹配成功,则返回。
因此可以这样进行测试:
(1)匹配以java结尾的文件
这里写图片描述
执行结果为:

CategoryDao.java
DirList.java
FileTest.java

(2)匹配以t.java结尾的文件
这里写图片描述

执行结果为:

DirList.java
FileTest.java

因此达到了所需要的效果。

由File.list(FilenameFilter filter)方法源码可知,File类为此方法注册了一个FilenameFilter接口,并在内部调用了FilenameFilter的accept()方法。所以DirFilter存在的目的就是为了创建表示正则匹配的accept()方法并提供给list()使用。

这种先注册一个接口,再实例化调用的结构可以称之为回调。或者更具体的说,这是一个策略模式的例子。

为了保证本博客的结构性。这一设计模式就不在此处细讲了。以后会专门开一篇博客讲解这部分的内容。

为了得到结构性更好的代码(不定义新的类,以防扰乱视线),通常我们会使用匿名内部类来完成一样的功能,代码如下:

package test;
import java.io.*;
import java.util.Arrays;
import java.util.regex.Pattern;

public class DirList2 {
    public static void main(String args[]){
        File path = new File(".\\src\\test");
        String[] list;
        if(args.length == 0){
            list = path.list();
        }else{
            list = path.list(new FilenameFilter(){
                private Pattern pattern = Pattern.compile(args[0]);
                public boolean accept(File dir, String name){
                    return pattern.matcher(name).matches();
                }
            });
        }
        Arrays.sort(list, String.CASE_INSENSITIVE_ORDER);
        for(String dirItem:list){
            System.out.println(dirItem);
        }

    }

}

二:File类作为工具类生成一个目录下的结构:

package test;

import java.util.regex.*;
import java.io.*;
import java.util.*;

public final class Directory {
    public static File[] local(File dir, final String regex){
        return dir.listFiles(new FilenameFilter(){
            private Pattern pattern = Pattern.compile(regex);
            public boolean accept(File die, String name){
                return pattern.matcher(new File(name).getName()).matches();
            }
        });
    }

    public static File[] local(String path, final String regex){
            return local(new File(path), regex);
    }

    public static class TreeInfo implements Iterable<File>{
        public List<File> files = new ArrayList<File>();
        public List<File> dirs = new ArrayList<File>();

        public Iterator<File> iterator(){
            return files.iterator();
        }

        void addAll(TreeInfo other){
            files.addAll(other.files);
            dirs.addAll(other.dirs);
        }

        public String toString(){
            return "dirs: " + PPrint.pformat(dirs) + "\n\nfiles: " + PPrint.pformat(files);
        }
    }

    public static TreeInfo walk(String start, String regex){//Begin recursion
        return recurseDirs(new File(start), regex);
    }

    public static TreeInfo walk(File start, String regex){
        return recurseDirs(start, regex);
    }

    public static TreeInfo walk(File start){
        return recurseDirs(start, ".*");
    }

    public static TreeInfo walk(String start){
        return recurseDirs(new File(start), ".*");
    }

    static TreeInfo recurseDirs(File startDir, String regex){
        TreeInfo result = new TreeInfo();
        for(File item: startDir.listFiles()){
            if(item.isDirectory()){
                result.dirs.add(item);
                result.addAll(recurseDirs(item, regex));
            }else{
                if(item.getName().matches(regex)){
                    result.files.add(item);
                }
            }
        }
        return result;
    }

    public static void main(String args[]){
        if(args.length == 0){
            System.out.println(walk("."));
        }else{
            for(String arg:args){
                System.out.println(walk(arg));
            }
        }
    }

}

其中PPrint是自定义的用以格式化打印的工具类:

package test;
import java.util.*;
public class PPrint {
    public static String pformat(Collection<?> c){
        if(c.size() == 0){
            return "[]";
        }
        StringBuilder result = new StringBuilder("[");
        for(Object elem : c){
            if(c.size() != 1){
                result.append("\n ");
            }
                result.append(elem);
        }
        if(c.size() != 1){
            result.append("\n");
        }
        result.append("]");
        return result.toString();
    }

    public static void pprint(Collection<?> c){
        System.out.println(pformat(c));
    }

    public static void pprint(Object[] c){
        System.out.println(Arrays.asList(c));
    }
}

在本目录下运行的结果为:

dirs: [
 .\.settings
 .\bin
 .\bin\template
 .\bin\test
 .\src
 .\src\template
 .\src\test
]

files: [
 .\.classpath
 .\.project
 .\.settings\org.eclipse.jdt.core.prefs
 .\bin\test\CategoryDao.class
 .\bin\test\Directory$1.class
 .\bin\test\Directory$TreeInfo.class
 .\bin\test\Directory.class
 .\bin\test\DirList$DirFilter.class
 .\bin\test\DirList.class
 .\bin\test\DirList2$1.class
 .\bin\test\DirList2.class
 .\bin\test\DirList3$1.class
 .\bin\test\DirList3.class
 .\bin\test\FileTest.class
 .\bin\test\PPrint.class
 .\src\test\CategoryDao.java
 .\src\test\Directory.java
 .\src\test\DirList.java
 .\src\test\DirList2.java
 .\src\test\DirList3.java
 .\src\test\FileTest.java
 .\src\test\PPrint.java
]

因此可以看出,此工具类Directory成功实现了打印本工作目录下所有目录和文件的功能。

上述工具类重要的部分为:

public static class TreeInfo implements Iterable<File>{
        public List<File> files = new ArrayList<File>();
        public List<File> dirs = new ArrayList<File>();

        public Iterator<File> iterator(){
            return files.iterator();
        }

        void addAll(TreeInfo other){
            files.addAll(other.files);
            dirs.addAll(other.dirs);
        }

        public String toString(){
            return "dirs: " + PPrint.pformat(dirs) + "\n\nfiles: " + PPrint.pformat(files);
        }
    }

    static TreeInfo recurseDirs(File startDir, String regex){
        TreeInfo result = new TreeInfo();
        for(File item: startDir.listFiles()){
            if(item.isDirectory()){
                result.dirs.add(item);
                result.addAll(recurseDirs(item, regex));//如果File是目录,则递归调用
            }else{
                if(item.getName().matches(regex)){
                    result.files.add(item);
                }
            }
        }
        return result;
    }

因此此工具类可以将目录下的目录和文件全部打印出来。

三:File对象创建新的文件

package test;

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

public class NewFileTest {
    public static void main(String args[]) throws IOException{
        File f = new File(".\\src\\test\\Director.java");

        System.out.println(f.createNewFile());


        System.out.println("f is a directory" + f.isFile());
    }
}

经过检查可以发现在.\src\test目录下生成了Director.java文件。

注意,如果.\src\test\目录不存在,会报错IOException。这时候需要调用File.mkdirs()来创建父目录。这里就不赘述了。

本来想花一天看完I/O部分的全部内容的,结果发现才大致弄清楚了File类的内容。感觉《Thinking in java》第四版还是没有第二版好懂啊。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
《Thinking in Java》是一本经典的Java编程入门教材,通过阅读该书可以系统地学习Java编程的基础知识和高级概念。阅读过程中,我结合自己的学习体验和实践,进行了详细的学习笔记。 首先,该书从基础语法开始介绍,包括数据型、控制语句、数组等。对于初学者来说,这些基础知识是最基本的,通过书中的示例代码和解析,我能够更好地理解这些概念和语法规则。 其次,书中对面向对象编程进行了深入的讲解。通过学习面向对象的思想,我明白了、对象、继承、多态等概念的含义和使用方法。同时,书中还讲解了接口、内部、异常处理等较为高级的概念,极大地拓宽了我的Java知识面。 另外,该书还介绍了Java的常见库和工具,如字符串操作、集合框架、输入输出、线程等。这些内容对于实际开发非常有用,而且书中的示例代码也能帮助我更好地理解和应用这些库和工具。 此外,该书通过大量的实例和案例,生动地展示了Java编程的实战应用。这些实例涵盖了各种不同的应用场景,从简单的程序到复杂的项目,都有所涉及。通过分析这些实例,我不仅可以学习到实际编程的技巧,还能提高自己的解决问题的能力。 总的来说,读完《Thinking in Java》后,我对Java编程有了更深入的理解和掌握。通过学习笔记的整理,我不仅复习了各个知识点,还加深了对这些概念的理解。希望这份学习笔记能够帮助到其他想要学习Java的同学,让他们能够更快地入门和掌握这门编程语言。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值