33.JAVA编程思想——JAVA IO File类

来源:互联网 发布:凉凉网络用语 编辑:程序博客网 时间:2024/06/05 03:22

33.JAVA编程思想——JAVA IO File类

RandomAccessFile用于包含了已知长度记录的文件,以便我们能用 seek()从一条记录移至另一条;然后读取或修改那些记录。各记录的长度并不一定相同;只要知道它们有多大以及置于文件何处即可。

首先,我们有点难以相信RandomAccessFile 不属于InputStream 或者OutputStream 分层结构的一部分。除了恰巧实现了DataInput 以及DataOutput(这两者亦由 DataInputStream和DataOutputStream实现)接口之外,它们与那些分层结构并无什么关系。它甚至没有用到现有InputStream 或OutputStream 类的功能——采用的是一个完全不相干的类。该类属于全新的设计,含有自己的全部(大多数为固有)方法。之所以要这样做,是因为RandomAccessFile 拥有与其他 IO类型完全不同的行为,因为我们可在一个文件里向前或向后移动。不管在哪种情况下,它都是独立运作的,作为Object 的一个“直接继承人”使用。

从根本上说,RandomAccessFile 类似DataInputStream和 DataOutputStream的联合使用。其中,getFilePointer()用于了解当前在文件的什么地方,seek()用于移至文件内的一个新地点,而 length()用于判断文件的最大长度。此外,构建器要求使用另一个自变量(与C 的fopen()完全一样),指出自己只是随机读("r"),还是读写兼施("rw")。这里没有提供对“只写文件”的支持。也就是说,假如是从DataInputStream继承的,那么 RandomAccessFile也有可能能很好地工作。

还有更难对付的。很容易想象我们有时要在其他类型的数据流中搜索,比如一个ByteArrayInputStream,但搜索方法只有RandomAccessFile 才会提供。而后者只能针对文件才能操作,不能针对数据流操作。此时,BufferedInputStream 确实允许我们标记一个位置(使用mark(),它的值容纳于单个内部变量中),并用reset()重设那个位置。但这些做法都存在限制,并不是特别有用。

1.  File类

File 类有一个欺骗性的名字——通常会认为它对付的是一个文件,但实情并非如此。它既代表一个特定文件的名字,也代表目录内一系列文件的名字。若代表一个文件集,便可用list()方法查询这个集,返回的是一个字串数组。之所以要返回一个数组,而非某个灵活的集合类,是因为元素的数量是固定的。而且若想得到

一个不同的目录列表,只需创建一个不同的File 对象即可。事实上,“FilePath”(文件路径)似乎是一个更好的名字。

1.1             目录列表器

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

例子的代码:

l  代码如下

import java.io.*;

publicclassDirList {

    publicstatic void main(String[]args) {

        try {

            Filepath=newFile(".");

            String[]list;

            if (args.length == 0)

                list = path.list();

            else

                list = path.list(new DirFilter(args[0]));

            for (inti = 0; i < list.length; i++)

                System.out.println(list[i]);

        }catch(Exceptione) {

            e.printStackTrace();

        }

    }

}

class DirFilterimplements FilenameFilter {

    Stringafn;

    DirFilter(Stringafn){

        this.afn =afn;

    }

    publicboolean accept(File dir, String name) {

        // Strip path information:

        Stringf =new File(name).getName();

        returnf.indexOf(afn) != -1;

    }

} /// :~

l  执行

.classpath

.project

.settings

bin

src

DirFilter 类“实现”了interface FilenameFilter。下面让我们看看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.2             匿名内部类

l  代码如下

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

import java.io.*;

publicclassDirList2 {

    publicstatic FilenameFilter filter(final Stringafn) {

        // Creation of anonymous inner class:

        returnnew FilenameFilter() {

            Stringfn =afn;

            publicboolean accept(File dir, String n) {

                // Strip path information:

                Stringf =new File(n).getName();

                returnf.indexOf(fn) != -1;

            }

        };// End of anonymous inner class

    }

    publicstatic void main(String[]args) {

        try {

            Filepath=newFile(".");

            String[]list;

            if (args.length == 0)

                list = path.list();

            else

                list = path.list(filter(args[0]));

            for (inti = 0; i < list.length; i++)

                System.out.println(list[i]);

        }catch(Exceptione) {

            e.printStackTrace();

        }

    }

} /// :~

 

l  执行

.classpath

.project

.settings

bin

src

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

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

l  代码2

import java.io.*;

 

publicclassDirList3 {

    publicstatic void main(final String[]args) {

        try {

            Filepath=newFile(".");

            String[]list;

            if (args.length == 0)

                list = path.list();

            else

                list = path.list(new FilenameFilter() {

                    publicboolean accept(File dir, String n) {

                        Stringf =new File(n).getName();

                        returnf.indexOf(args[0]) != -1;

                    }

                });

            for (inti = 0; i < list.length; i++)

                System.out.println(list[i]);

        }catch(Exceptione) {

            e.printStackTrace();

        }

    }

} /// :~

l  执行

.classpath

.project

.settings

bin

src

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

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

1.3             顺序目录列表

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

l  代码

import java.io.*;

publicclassSortedDirList {

    private Filepath;

    private String[]list;

    public SortedDirList(final Stringafn) {

        path = new File(".");

        if (afn ==null)

            list = path.list();

        else

            list = path.list(new FilenameFilter() {

                publicboolean accept(File dir, String n) {

                    Stringf =new File(n).getName();

                    returnf.indexOf(afn) != -1;

                }

            });

        sort();

    }

    void print() {

        for (inti = 0; i < list.length; i++)

            System.out.println(list[i]);

    }

    privatevoid sort() {

        StrSortVectorsv =new StrSortVector();

        for (inti = 0; i < list.length; i++)

            sv.addElement(list[i]);

        // The first time an element is pulled from

        // the StrSortVector the list is sorted:

        for (inti = 0; i < list.length; i++)

            list[i] = sv.elementAt(i);

    }

    // Test it:

    publicstatic void main(String[]args) {

        SortedDirListsd;

        if (args.length == 0)

            sd = new SortedDirList(null);

        else

            sd = new SortedDirList(args[0]);

        sd.print();

    }

} /// :~

l  执行

.classpath

.project

.settings

bin

src

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

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

1.4             检查与创建目录

File 类并不仅仅是对现有目录路径、文件或者文件组的一个表示。亦可用一个 File 对象新建一个目录,甚至创建一个完整的目录路径——假如它尚不存在的话。亦可用它了解文件的属性(长度、上一次修改日期、读/写属性等),检查一个File 对象到底代表一个文件还是一个目录,以及删除一个文件等等。下列程序完整展示了如何运用File 类剩下的这些方法:

l  代码

import java.io.*;

 

publicclassMakeDirectories {

    privatefinal static Stringusage = "Usage:MakeDirectories path1 ...\n" + "Creates each path\n"

            +"Usage:MakeDirectories -d path1...\n" +"Deleteseach path\n" + "Usage:MakeDirectories -r path1 path2\n"

            +"Renames from path1 to path2\n";

    privatestatic void usage() {

        System.err.println(usage);

        System.exit(1);

    }

    privatestatic void fileData(Filef) {

        System.out.println("Absolute path: "+f.getAbsolutePath()+ "\n Can read: " + f.canRead() + "\n Can write: "

                +f.canWrite() +"\n getName: "+ f.getName() +"\n getParent: "+ f.getParent() + "\n getPath: "

                +f.getPath() +"\n length: "+ f.length() +"\n lastModified: "+ f.lastModified());

        if (f.isFile())

            System.out.println("it's a file");

        elseif (f.isDirectory())

            System.out.println("it's a directory");

    }

 

    publicstatic void main(String[]args) {

        if (args.length < 1)

            usage();

        if (args[0].equals("-r")) {

            if (args.length != 3)

                usage();

            Fileold=newFile(args[1]),rname=newFile(args[2]);

            old.renameTo(rname);

            fileData(old);

            fileData(rname);

            return;// Exit main

        }

        intcount = 0;

        booleandel = false;

        if (args[0].equals("-d")) {

            count++;

            del = true;

        }

        for (;count < args.length;count++) {

            Filef =new File(args[count]);

            if (f.exists()) {

                System.out.println(f +" exists");

                if (del) {

                    System.out.println("deleting..."+f);

                    f.delete();

                }

            }else{// Doesn't exist

                if (!del) {

                    f.mkdirs();

                    System.out.println("created "+f);

                }

            }

            fileData(f);

        }

    }

} /// :~

l  执行

Usage:MakeDirectories path1 ...

Creates each path

Usage:MakeDirectories -d path1 ...

Deletes each path

Usage:MakeDirectories -r path1 path2

Renames from path1 to path2

加上执行参数:-rtest1.txt test2.txt

然后在目录中增加test1.txt文件,执行后

Absolute path: F:\java_test\test1.txt

 Can read: false

 Can write: false

 getName: test1.txt

 getParent: null

 getPath: test1.txt

 length: 0

 lastModified: 0

Absolute path: F:\java_test\test2.txt

 Can read: true

 Can write: true

 getName: test2.txt

 getParent: null

 getPath: test2.txt

 length: 0

 lastModified: 1460890496589

it's a file

 

在fileData()中,可看到应用了各种文件调查方法来显示与文件或目录路径有关的信息。

main()应用的第一个方法是 renameTo(),利用它可以重命名(或移动)一个文件至一个全新的路径(该路径由参数决定),它属于另一个File 对象。这也适用于任何长度的目录。

若试验上述程序,就可发现自己能制作任意复杂程度的一个目录路径,因为mkdirs()会帮我们完成所有工作。在Java 1.0 中,-d标志报告目录虽然已被删除,但它依然存在;但在 Java 1.1 中,目录会被实际删除。

2.  附SortVector.java

import java.util.*;

publicclassSortVector extendsVector {

    private Comparecompare; // To hold the callback

    public SortVector(Comparecomp) {

        compare = comp;

    }

    publicvoid sort() {

        quickSort(0,size() - 1);

    }

    privatevoid quickSort(intleft, intright) {

        if (right >left) {

            Objecto1 = elementAt(right);

            inti = left - 1;

            intj = right;

            while (true) {

                while (compare.lessThan(elementAt(++i),o1))

                    ;

                while (j > 0)

                    if (compare.lessThanOrEqual(elementAt(--j),o1))

                        break;// out of while

                if (i >=j)

                    break;

                swap(i,j);

            }

            swap(i,right);

            quickSort(left,i - 1);

            quickSort(i + 1,right);

        }

    }

    privatevoid swap(intloc1, intloc2) {

        Objecttmp= elementAt(loc1);

        setElementAt(elementAt(loc2),loc1);

        setElementAt(tmp,loc2);

    }

} /// :~

3.  附StrSortVector.java

import java.util.*;

interface Compare {

    boolean lessThan(Objectlhs, Object rhs);

 

    boolean lessThanOrEqual(Objectlhs, Object rhs);

} /// :~

publicclassStrSortVector{

    private SortVectorv = new SortVector(

            // Anonymous inner class:

            new Compare() {

                publicboolean lessThan(Object l, Object r) {

                    return ((String)l).toLowerCase().compareTo(((String)r).toLowerCase())< 0;

                }

                publicboolean lessThanOrEqual(Object l, Object r) {

                    return ((String)l).toLowerCase().compareTo(((String)r).toLowerCase())<= 0;

                }

            });

    privateboolean sorted =false;

    publicvoid addElement(String s) {

        v.addElement(s);

        sorted = false;

    }

    public String elementAt(intindex) {

        if (!sorted) {

            v.sort();

            sorted = true;

        }

        return (String)v.elementAt(index);

    }

    publicEnumerationelements() {

        if (!sorted) {

            v.sort();

            sorted = true;

        }

        returnv.elements();

    }

    // Test it:

    publicstatic void main(String[]args) {

        StrSortVector sv = newStrSortVector();

        sv.addElement("d");

        sv.addElement("A");

        sv.addElement("C");

        sv.addElement("c");

        sv.addElement("b");

        sv.addElement("B");

        sv.addElement("D");

        sv.addElement("a");

        Enumeratione =sv.elements();

        while (e.hasMoreElements())

            System.out.println(e.nextElement());

    }

} /// :~

 

 

0 0
原创粉丝点击