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也有可能能非常好地工作。
还有更难对付的。非常easy想象我们有时要在其它类型的数据流中搜索,比方一个ByteArrayInputStream。但搜索方法仅仅有RandomAccessFile 才会提供。而后者仅仅能针对文件才干操作,不能针对数据流操作。此时。BufferedInputStream 确实同意我们标记一个位置(使用mark(),它的值容纳于单个内部变量中)。并用reset()重设那个位置。但这些做法都存在限制。并非特别实用。
1. File类
File 类有一个欺骗性的名字——一般会觉得它对付的是一个文件。但实情并非如此。它既代表一个特定文件的名字,也代表文件夹内一系列文件的名字。若代表一个文件集。便可用list()方法查询这个集,返回的是一个字串数组。之所以要返回一个数组,而非某个灵活的集合类。是由于元素的数量是固定的。并且若想得到
一个不同的文件夹列表。仅仅需创建一个不同的File 对象就可以。其实,“FilePath”(文件路径)似乎是一个更好的名字。
1.1 文件夹列表器
如果想观看一个文件夹列表。可用两种方式列出File 对象。若在不含自变量(參数)的情况下调用list(),会获得 File 对象包括的一个完整列表。
然而。若想对这个列表进行某些限制,就须要使用一个“文件夹过滤器”,该类的作用是指出应怎样选择File 对象来完毕显示。
样例的代码:
l 代码例如以下
import java.io.*;
public classDirList {
public static 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 (int i = 0; i < list.length; i++)
System.out.println(list[i]);
}catch(Exception e) {
e.printStackTrace();
}
}
}
class DirFilter implements FilenameFilter {
Stringafn;
DirFilter(Stringafn){
this.afn = afn;
}
public boolean accept(File dir, String name) {
// Strip path information:
Stringf = new File(name).getName();
return f.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.*;
public classDirList2 {
public static FilenameFilter filter(final String afn) {
// Creation of anonymous inner class:
return new FilenameFilter() {
Stringfn = afn;
public boolean accept(File dir, String n) {
// Strip path information:
Stringf = new File(n).getName();
return f.indexOf(fn) != -1;
}
};// End of anonymous inner class
}
public static void main(String[] args) {
try {
Filepath= newFile(".");
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();
}
}
} /// :~
l 运行
.classpath
.project
.settings
bin
src
注意filter()的自变量必须是final。
这一点是匿名内部类要求的,使其能使用来自本身作用域以外的一个对象。
之所以觉得这样做更好,是由于FilenameFilter 类如今同DirList2 紧密地结合在一起。然而,我们可採取进一步的操作,将匿名内部类定义成list()的一个參数,使其显得更加精简。
l 代码2
import java.io.*;
public classDirList3 {
public static void main(final String[] args) {
try {
Filepath= newFile(".");
String[]list;
if (args.length == 0)
list = path.list();
else
list = path.list(new FilenameFilter() {
public boolean accept(File dir, String n) {
Stringf = 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();
}
}
} /// :~
l 运行
.classpath
.project
.settings
bin
src
main()如今的自变量是 final。由于匿名内部类直接使用args[0]。
这展示了怎样利用匿名内部类高速创建精简的类。以便解决一些复杂的问题。由于Java 中的全部东西都与类有关,所以它无疑是一种相当实用的编码技术。它的一个优点是将特定的问题隔离在一个地方统一解决。但在还有一方面,这样生成的代码不是十分easy阅读,所以使用时必须谨慎。
1.3 顺序文件夹列表
常常都须要文件名称以排好序的方式提供。由于 Java 1.0 和Java 1.1 都没有提供对排序的支持(从 Java 1.2開始提供),用创建的 SortVector将这一能力直接添加自己的程序。
l 代码
import java.io.*;
public classSortedDirList {
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) {
Stringf = 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() {
StrSortVectorsv = 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) {
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.*;
public classMakeDirectories {
private final static String usage = "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";
private static void usage() {
System.err.println(usage);
System.exit(1);
}
private static void fileData(File f) {
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");
else if (f.isDirectory())
System.out.println("it's a directory");
}
public static 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
}
int count = 0;
boolean del = 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.*;
public classSortVector extends Vector {
private Compare compare; // To hold the callback
public SortVector(Compare comp) {
compare = comp;
}
public void sort() {
quickSort(0,size() - 1);
}
private void quickSort(int left, int right) {
if (right > left) {
Objecto1 = elementAt(right);
int i = left - 1;
int j = 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);
}
}
private void swap(int loc1, int loc2) {
Objecttmp= elementAt(loc1);
setElementAt(elementAt(loc2), loc1);
setElementAt(tmp, loc2);
}
} /// :~
3. 附StrSortVector.java
import java.util.*;
interface Compare {
boolean lessThan(Object lhs, Object rhs);
boolean lessThanOrEqual(Object lhs, Object rhs);
} /// :~
public classStrSortVector{
private SortVector v = new SortVector(
// Anonymous inner class:
new Compare() {
public boolean lessThan(Object l, Object r) {
return ((String) l).toLowerCase().compareTo(((String)r).toLowerCase())< 0;
}
public boolean lessThanOrEqual(Object l, Object r) {
return ((String) l).toLowerCase().compareTo(((String)r).toLowerCase())<= 0;
}
});
private boolean sorted = false;
public void addElement(String s) {
v.addElement(s);
sorted = false;
}
public String elementAt(int index) {
if (!sorted) {
v.sort();
sorted = true;
}
return (String) v.elementAt(index);
}
public Enumerationelements() {
if (!sorted) {
v.sort();
sorted = true;
}
return v.elements();
}
// Test it:
public static void main(String[] args) {
StrSortVector sv = new StrSortVector();
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());
}
} /// :~