File类
一、目录列表器
假设我们想查看一个目录列表,可以有两种方法来使用File对象。如果我们调用不带参数的list()方法,便可以获得此对象包含的全部列表。然而,如果我们想获得一个受限列表,例如,想得到所有扩展名为.java的文件,那么我们就要用到“目录过滤器”,这个类会告诉我们怎样显示符合条件的File对象。
下面是一个示例,注意,通过使用java.util.Arrays.sort()和String.CASE_INSENSITIVE.ORDERComparator,可以很容易地对结果进行排序(按字母排序)
public class DirList{
public static void main(String [] args){
File path = new File(".");
String [] list ;
String [] paras = {"src"};
if(paras.length == 0)
list = path.list();
else
list = path.list(new DirFilter(paras[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类“实现”了FilenameFilter接口。请注意FilenameFilter接口是多么的简单:
public interface FilenameFilter {
/**
* Tests if a specified file should be included in a file list.
*
* @param dir the directory in which the file was found.
* @param name the name of the file.
* @return <code>true</code> if and only if the name should be
* included in the file list; <code>false</code> otherwise.
*/
boolean accept(File dir, String name);
}
创建DirFilter的目的在于把accept()方法提供给list()使用,使list()可以回调accept(),进而决定哪些文件包含在列表中。以下是File.list()及File.list(FilenameFilter filter)的实现。
/**
* Returns an array of strings naming the files and directories in the
* directory denoted by this abstract pathname.
*
* <p> If this abstract pathname does not denote a directory, then this
* method returns <code>null</code>. Otherwise an array of strings is
* returned, one for each file or directory in the directory. Names
* denoting the directory itself and the directory's parent directory are
* not included in the result. Each string is a file name rather than a
* complete path.
*
* <p> There is no guarantee that the name strings in the resulting array
* will appear in any specific order; they are not, in particular,
* guaranteed to appear in alphabetical order.
*
* @return An array of strings naming the files and directories in the
* directory denoted by this abstract pathname. The array will be
* empty if the directory is empty. Returns <code>null</code> if
* this abstract pathname does not denote a directory, or if an
* I/O error occurs.
*
* @throws SecurityException
* If a security manager exists and its <code>{@link
* java.lang.SecurityManager#checkRead(java.lang.String)}</code>
* method denies read access to the directory
*/
public String[] list() {
SecurityManager security = System.getSecurityManager();
if (security != null) {
security.checkRead(path);
}
return fs.list(this);
}
/**
* Returns an array of strings naming the files and directories in the
* directory denoted by this abstract pathname that satisfy the specified
* filter. The behavior of this method is the same as that of the
* <code>{@link #list()}</code> method, except that the strings in the
* returned array must satisfy the filter. If the given
* <code>filter</code> is <code>null</code> then all names are accepted.
* Otherwise, a name satisfies the filter if and only if the value
* <code>true</code> results when the <code>{@link
* FilenameFilter#accept}</code> method of the filter is invoked on this
* abstract pathname and the name of a file or directory in the directory
* that it denotes.
*
* @param filter A filename filter
*
* @return An array of strings naming the files and directories in the
* directory denoted by this abstract pathname that were accepted
* by the given <code>filter</code>. The array will be empty if
* the directory is empty or if no names were accepted by the
* filter. Returns <code>null</code> if this abstract pathname
* does not denote a directory, or if an I/O error occurs.
*
* @throws SecurityException
* If a security manager exists and its <code>{@link
* java.lang.SecurityManager#checkRead(java.lang.String)}</code>
* method denies read access to the directory
*/
public String[] list(FilenameFilter filter) {
String names[] = list();
if ((names == null) || (filter == null)) {
return names;
}
ArrayList v = new ArrayList();
for (int i = 0 ; i < names.length ; i++) {
if (filter.accept(this, names[i])) {
v.add(names[i]);
}
}
return (String[])(v.toArray(new String[v.size()]));
}
因此,这种结构也常常称为回调。更具体的说,这是一个策略模式的例子,因为list()实现了基本的功能,而且按照FilenameFilter的形式提供了这个策略,以便完善list()在提供服务时所需的算法。因为list()接受FilenameFilter对象作为参数,这意味着我们可以传递实现了FilenameFilter接口的任何类的对象,用以选择list() 方法的行为方式。策略的目的就是提供了代码行为的灵活性。
匿名内部类
这个例子很合适用一个匿名内部类进行改写,首先创建一个filter()方法,他会返回一个指向FilenameFilter的引用:
public class DirList {
public static FilenameFilter filter(final String regex) {
return new FilenameFilter() {
private Pattern pattern = Pattern.compile(regex);
public boolean accept(File dir, String name) {
return pattern.matcher(name).matches();
}
};
}
public static void main(String[] args) {
File path = new File(".");
String[] list;
if (args.length == 0) {
list = path.list();
} else {
list = path.list(filter(args[0]));
}
Arrays.sort(list, String.CASE_INSENSITIVE_ORDER);
for (String dirItem : list) {
System.out.println(dirItem);
}
}
}
这个设计有所改进,因为现在FilenameFilter类紧密的和DirList绑定在一起。然而,我们可以进一步修改方法,定义一个作为list()参数的匿名内部类,这样一来程序会变的更小:
public class DirList {
public static void main(final String[] args) {
File path = new File(".");
String[] list;
if (args.length == 0) {
list = path.list();
} else {
list = path.list(new FilenameFilter() {
private Pattern pattern = Pattern.compile(args[0]);
@Override
public boolean accept(File dir, String name) {
// TODO Auto-generated method stub
return pattern.matcher(name).matches();
}
});
}
Arrays.sort(list, String.CASE_INSENSITIVE_ORDER);
for (String dirItem : list) {
System.out.println(dirItem);
}
}
}
这个例子展示了匿名内部类怎样通过创建特定的、一次性的类来解决问题,此方法的一个优点就是将解决特定问题的代码隔离、聚拢与一点。而另一方面,这种方法却不易阅读,因此要谨慎使用。
二、目录实用工具
程序设计中一项常见的任务就是在文件集上执行操作,这些文件要么在本地目录中,要么遍布于整个目录树中。如果有一种工具能够为你产生这个文件及,那么他会非常有用。下面的使用工具类就可以通过使用local()方法产生由本地目录中的文件构成的File对象数组,或者通过使用walk()方法产生给定目录下的由整个目录树中所有文件构成的List<File>
三、目录的检查及创建
File类不仅仅只代表存在的文件或目录。也可以用File对象来创建新的目录或尚不存在的整个目录路径。我们可以查看文件的特性(如:大小,最后修改日期,读写),检查某个File对象代表的是一个文件还是一个目录,并可以删除文件。下面的示例展示了File类的一些其他方法
public class MakeDirctories {
/**
* 提示出错信息
*/
private static void usage() {
System.err.println("Usage:MakeDirectories path1 ...\n"
+ "Creates each path\n"
+ "Usage:MakeDirecotries -d path1 ...\n"
+ "Deletes each path\n"
+ "Usage:MekeDirectories -r path1 path2 \n"
+ "Renames from path1 to path2");
System.exit(1);
}
/**
* 打印文件信息
* @param f
*/
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();
// 输入参数-r
if (args[0].equals("-r")) {
// 如果参数个数不是3个 提示错误信息
if (args.length != 3)
usage();
// 执行修改文件名称
File old = new File(args[1]), rname = new File(args[2]);
old.renameTo(rname);
fileData(old);
fileData(rname);
return;
}
int count = 0;
boolean del = false;
// 输入参数 -d
if (args[0].equals("-d")) {
count++;
del = true;
}
count--;
while (++count < args.length) {
File f = new File(args[count]);
if (f.exists()) {
System.out.println(f + " exists");
if (del) {
System.out.println("deleting ... " + f);
f.delete();
}
} else {
if (!del) {
f.mkdirs();
System.out.println("created " + f);
}
}
fileData(f);
}
}
}
例子里面写了一个mkdirs()方法,我们来研究一下源码:
public boolean mkdirs() {
if (exists()) {
return false;
}
if (mkdir()) {
return true;
}
File canonFile = null;
try {
canonFile = getCanonicalFile();
} catch (IOException e) {
return false;
}
File parent = canonFile.getParentFile();
return (parent != null && (parent.mkdirs() || parent.exists()) &&
canonFile.mkdir());
}
这是一个递归方法。关键在于return,使用当前File对象的parent对象构造文件,如果parent不为空,并且parent调用mkdirs创建成功或者parent已经存在,并且标准文件可以创建,则返回true,否则的话就继续调用mkdirs方法,直到递归到根目录下为止。
以下是getCanonicalFile方法的源码,这个方法返回抽象路径名的规范形式。说白了就是创建一个以规范路径名命名的File对象,他等同于new File(this.getCanonicalPath())。
public File getCanonicalFile() throws IOException {
String canonPath = getCanonicalPath();
return new File(canonPath, fs.prefixLength(canonPath));
}
这里调用了getCanonicalPath方法,这个方法返回抽象路径名的规范路径名字符串。
public String getCanonicalPath() throws IOException {
return fs.canonicalize(fs.resolve(this));
}