java中表示一个文件的File类型详解

前言

从本篇文章开始,我们将开启对 Java IO 系统的学习,本质上就是对文件的读写操作,听上去简单,其实并不容易。Java 的 IO 系统一直在完善和改进,设计了大量的类,也只有理解了这些类型被设计出来的意义以及各自的应用场景,才能提升文件 IO 的理解。

那么,第一步就是要解决如何表示一个文件的问题,Java 世界中「万物皆对象」,如何将一个实际磁盘文件或目录对应到一个 Java 对象则是我们首要的问题。

Java 中使用 File 来抽象一个文件,无论是普通文件或是目录,都可对应于一个 File 对象。我觉得大家对于 File 这个类型的定位一定要准确:它只是抽象的代表了磁盘上的某个文件或目录,内部实际上是依赖一个平台无关的本地文件系统类,并且 File 无法对其所表示文件内容进行任何读写操作(那是流做的事情)。

构建一个 File 实例

在实际介绍 File 实例构造方法之前,我们得先看看它的几种重要的属性成员。

private static final FileSystem fs = DefaultFileSystem.getFileSystem();

这是 File 类中最核心的成员,它表示为当前系统的文件系统 API,所有向磁盘发出的操作都是基于这个属性的。

private final String path;

path 代表了当前实例的完整路径名称,如果当前的 File 实例表示的是目录的话,那么 path 的值就是这个完整的目录名称,如果表示的是纯文件的话,那么这个 path 的值等于该文件的完整路径 + 文件名称。

public static final char separatorChar = fs.getSeparator();
public static final char pathSeparatorChar = fs.getPathSeparator();

separatorChar 表示的是目录间的分隔符,pathSeparatorChar 表示的是不同路径下的分隔符,这两个值在不同的系统平台下不尽相同。例如 Windows 下这两者的值分别为:「」 和 「;」,其中封号用于分隔多个不同路径。

File 类提供了四种不同的构造器用于实例化一个 File 对象,但较为常用的只有三个,我们也着重学习前三个构造器。

public File(String pathname)

这是最普遍的实例化一个 File 对象的方法,pathname 的值可以是一个目录,也可以是一个纯文件的名称。例如:

File file = new File("C:\\Users\\yanga\\Desktop");

File file1 = new File("C:\\Users\\yanga\\Desktop\\a.txt");

File file2 = new File("a.txt");

当然也可以显式指定一个父路径:

public File(String parent, String child)

在构造器的内部,程序会为我们拼接出一个完整的文件路径,例如:

File file = new File("C:\\Users\\yanga\\Desktop","a.txt");

File file1 = new File("C:\\Users\\yanga\\Desktop","java");

第三种构造器其实本质上和第二种是一样的,只不过增加了一个父类 File 实例的封装过程:

public File(File parent, String child)

类似的情况,不再举例说明了。我们这里并没有深究这些构造器的内部具体实现情况,并不是说它简单,而是 File 过度依赖本地文件系统,很多方法的实现情况都不得直接看到,所以对于 File 的学习,定位为熟练掌握即可,具体实现暂时没法深入学习。

文件名称或路径相关信息获取

getName 方法可以用于获取文件名称:

public String getName() {
 int index = path.lastIndexOf(separatorChar);
 if (index < prefixLength) return path.substring(prefixLength);
 return path.substring(index + 1);
}

还记得我们的 separatorChar 表示的是什么了吗?

它表示为路径分隔符,Windows 中为符号「」,path 属性存储的当前 File 实例的完整路径名称,所以最后一次出现的位置后面所有的字符必然是我们的文件名称。

当然你一定发现了,对于纯文件来说,该方法能够返回文件的简单名称,而对于一个目录而言,返回值将会是最近的目录名。例如:

File file = new File("C:\\Users\\yanga\\Desktop\\a.txt");
System.out.println(file.getName());

File file1 = new File("C:\\Users\\yanga\\Desktop");
System.out.println(file1.getName());

输出结果不会出乎你的意料:

a.txt
Desktop

getParent 方法用于返回当前文件的父级目录,无论你是纯文件或是目录,你终有你的父目录(当然,虚拟机生成的临时文件自然不是)。

public String getParent() {
 int index = path.lastIndexOf(separatorChar);
 if (index < prefixLength) {
 if ((prefixLength > 0) && (path.length() > prefixLength))
 return path.substring(0, prefixLength);
 return null;
 }
 return path.substring(0, index);
}

方法的实现很简单,不再赘述。

getPath 方法可以返回当前 File 实例的完整文件名称:

public String getPath() {
 return path;
}

以下是一些有关目录的相关操作,实现比较简单,此处简单罗列了:

  • public boolean isAbsolute():是否为绝对路径
  • public String getAbsolutePath():获取当前 File 实例的绝对路径
  • public String getCanonicalPath():返回当前 File 实例的标准路径

这里我们需要对 getCanonicalPath 做一点解释,什么叫标准路径,和绝对路径有区别吗?

一般而言,「../」表示源文件所在目录的上一级目录,「../../」表示源文件所在目录的上上级目录,并以此类推。getAbsolutePath 方法不会做这种转换的操作,而 getCanonicalPath 方法则会将这些特殊字符进行识别并取合适的语义。

例如:

File file = new File("..\\a.txt");
System.out.println(file.getAbsolutePath());
System.out.println(file.getCanonicalPath());

输出结果:

C:\Users\yanga\Desktop\Java\workspace2017\TestFile\..\a.txt
C:\Users\yanga\Desktop\Java\workspace2017\a.txt

前者会将「..\a.txt」作为文件路径名称的一部分,而后者却能够识别「..\a.txt」表示的是「a.txt」位于当前目录的上级目录中。这就是两者最大的不同之处,适合不同的情境。

文件的属性信息获取

这部分的文件操作其实很简单,无非是一些文件权限的问题,是否可读,是否可写,是否为隐藏文件等。下面我们具体看看这些方法:

  • public boolean canRead():该抽象的 File 实例对应的文件是否可读
  • public boolean canWrite():该抽象的 File 实例对应的文件是否可写
  • public boolean exists():该抽象的 File 实例对应的文件是否实际存在
  • public boolean isDirectory():该抽象的 File 实例对应的文件是否是一个目录
  • public boolean isFile():该抽象的 File 实例对应的文件是否是一个纯文件
  • public boolean isHidden():该抽象的 File 实例对应的文件是否是一个隐藏文件
  • public long length():文件内容所占的字节数

需要说明一点的是,length 方法对于纯文件来说,可以正确返回该文件的字节总数,但是对于一个目录而言,返回值将会是一个「unspecified」的数值,既不是目录下所有文件的总字节数,也不是零,只是一个未被说明的数值,没有意义。

文件的操作

文件的操作无外乎「增删改查」,下面我们一起来看看。

  • public boolean createNewFile():根据抽象的 File 对象创建一个实际存在的磁盘文件
  • public boolean delete():删除该 File 对象对应的磁盘文件,删除失败会返回 false

当然,处理上述两个简单的新建和删除操作,File 类还提供了所谓「查询」操作,这个我们要好好学习一下。例如:

public String[] list() {
 SecurityManager security = System.getSecurityManager();
 if (security != null) {
 security.checkRead(path);
 }
 if (isInvalid()) {
 return null;
 }
 return fs.list(this);
}

这个方法会检索出当前实例所代表的目录下所有的「纯文件」和「目录」简单名称集合。例如:

File file = new File("C:\\Users\\yanga\\Desktop");
String[] list = file.list();
for (String str : list){
 System.out.println(str);
}

程序的输出结果会打印我电脑桌面目录下所有的文件的简单名称,就不给大家看了。

需要注意一点,如果我们的 File 实例对应的不是一个目录,而是一个纯文件,那么 list 将返回 null。

接着,我们再看一个检索目录文件的方法:

 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()]);
}

这个方法其实是 list 的重载版本,它允许传入一个过滤器用于检索目录时只筛选我们需要的文件及目录。

而这个 FilenameFilter 接口的定义却是如此简单:

public interface FilenameFilter {
 boolean accept(File dir, String name);
}

只需要重写这个 accept 方法即可,list 的 for 循环每获取一个文件或目录就会尝试着先调用这个过滤方法,如果通过筛选,才会将当前文件的简单名称添加进返回集合中。

所以这个 accept 方法的重写就决定着哪些文件能够通过筛选,哪些则不能。我们看个例子:

我的桌面上 test 文件夹下文件情况如下:

File file = new File("C:\\Users\\yanga\\Desktop\\test");
 String[] list = file.list(
 new FilenameFilter() {
  @Override
  public boolean accept(File dir, String name) {
  // dir 代表的当前 File 对象
  //name 是当前遍历的文件项的简单名称
  if (!name.endsWith(".txt"))
   return false;
  else
   return true;
  }
 }
 );
 for (String str : list){
 System.out.println(str);
 }

这里呢,我们使用匿名内部类创建一个 FilenameFilter 的子类实例,然后实现了它的 accept 方法,具体的实现很简单,过滤掉所有的目录并取出所有纯文件的简单名称。

最后输出结果如下:

3.txt
4.txt

当然,File 类中还提供了两个「变种」list 方法,例如:

  • public File[] listFiles()
  • public File[] listFiles(FilenameFilter filter)

它们不再返回目标目录下的「纯文件」和「目录」的简单名称,而返回它们所对应的 File 对象,其实也没什么,目标目录 + 简单名称 即可构建出这些 File 实例了。

所以,本质上说,list 方法并不会遍历出目标目录下的所有文件,即目标目录的子目录中的文件并不会被访问遍历。

所以你应当思考如何完成目标目录下所有文件的遍历,包含一级子目录下的深层次文件的遍历。文末将给出答案。

接下来的两个方法和文件夹的创建有关:

  • public boolean mkdir()
  • public boolean mkdirs()

两者都是依据的当前 File 实例创建文件夹,关于它们的不同点,我们先看一段代码:

File file = new File("C:\\Users\\yanga\\Desktop\\test2");
System.out.println(file.mkdir());

File file2 = new File("C:\\Users\\yanga\\Desktop\\test3\\hello");
System.out.println(file2.mkdir());

其中,test2 和 test3 在程序执行之前都不存在。

输出结果如下:

true
false

为什么后者创建失败了?

这源于 mkdir 方法一次只能创建一个文件夹,倘若给定的目录的父级或更上层目录存在未被创建的目录,那么将导致创建失败。

而 mkdirs 方法就是用于解决这种情境的,它会创建目标路径上所有未创建的目录,看代码:

File file3 = new File("C:\\Users\\yanga\\Desktop\\test3\\hello\\231");
System.out.println(file3.mkdirs());

即便我们 test3 文件夹就不存在,程序运行之后,test3、hello、231 这三个文件夹都会被创建出来。

除此之外,File 还有一类创建临时文件的方法,所谓临时文件即:运行期存在,虚拟机关闭时销毁。大家可以自行研究,使用上还是比较简单的,这里不再赘述了。

至此,有关 File 这个文件类型,我们大致学习了一下,想必大家都会或多或少的感觉到将纯文件和目录使用同一个类型进行表示的设计似乎有些混乱不合理。知道 jdk1.7 sun 推出了 Files 和 Path 分离了文件和目录,我们后续文章会详细学习一下。

文章中的所有代码、图片、文件都云存储在我的 GitHub 上:

(https://github.com/SingleYam/overview_java)

大家也可以选择通过本地下载。

总结

以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,如果有疑问大家可以留言交流,谢谢大家对我们的支持。

(0)

相关推荐

  • java中File类的构造函数及其方法

    1.IO流(Input Output) IO流技术主要的作用是解决设备与设备之间的数据传输问题,比如:硬盘的数据--读取到-->内存中 内存的数据--读取到-->硬盘中 键盘上的数据--读取到-->内存中 2.IO流技术的运用场景 导出报表.上传大头照.下载.解析xml文件.....等等 (数据保存到硬盘,该数据可以做到永久性保存.数据一般以文件的形式保存到硬盘上.sun使用一个File类来描述文件或者文件夹) 3.File类的构造函数(方法) File(String pathname)

  • 基于java Files类和Paths类的用法(详解)

    Java7中文件IO发生了很大的变化,专门引入了很多新的类: import java.nio.file.DirectoryStream; import java.nio.file.FileSystem; import java.nio.file.FileSystems; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; import java.nio.file.attribute.

  • Java中File类中常用方法详解

    java.io包下的File类用于描述和创建一个文件或文件夹对象,只能对文件或文件夹做一些简单操作,不能修改文件的内容,功能比较有限.下面是对于File类中常用方法的程序演示. [1] 演示程序一 package pack01; import java.io.*; import java.sql.Date; public class FileTest { public static void main(String[] args) { File file1 = new File("d:/TEST

  • 详谈java中File类getPath()、getAbsolutePath()、getCanonical的区别

    简单看一下描述,例子最重要. 1.getPath(): 返回定义时的路径,(就是你写什么路径,他就返回什么路径) 2.getAbsolutePath(): 返回绝对路径,但不会处理"."和".."的情况 3.getCanonicalPath(): 返回的是规范化的绝对路径,相当于将getAbsolutePath()中的"."和".."解析成对应的正确的路径 第一个例子:(使用:".\\src\\test.txt&qu

  • Java文件操作工具类fileUtil实例【文件增删改,复制等】

    本文实例讲述了Java文件操作工具类fileUtil.分享给大家供大家参考,具体如下: package com.gcloud.common; import java.io.*; import java.net.MalformedURLException; import java.net.URL; /** * 文件工具类 * Created by charlin on 2017/9/8. */ public class FileUtil { /** * 读取文件内容 * * @param is *

  • java中表示一个文件的File类型详解

    前言 从本篇文章开始,我们将开启对 Java IO 系统的学习,本质上就是对文件的读写操作,听上去简单,其实并不容易.Java 的 IO 系统一直在完善和改进,设计了大量的类,也只有理解了这些类型被设计出来的意义以及各自的应用场景,才能提升文件 IO 的理解. 那么,第一步就是要解决如何表示一个文件的问题,Java 世界中「万物皆对象」,如何将一个实际磁盘文件或目录对应到一个 Java 对象则是我们首要的问题. Java 中使用 File 来抽象一个文件,无论是普通文件或是目录,都可对应于一个

  • Java 中HttpURLConnection附件上传的实例详解

    Java 中HttpURLConnection附件上传的实例详解 整合了一个自己写的采用Http做附件上传的工具,分享一下! 示例代码: /** * 以Http协议传输文件 * * @author mingxue.zhang@163.com * */ public class HttpPostUtil { private final static char[] MULTIPART_CHARS = "-_1234567890abcdefghijklmnopqrstuvwxyzABCDEFGHIJK

  • Java 中 Class Path 和 Package的使用详解

    目录 一. 类路径 (class path) 二. 包 (package) 三. jar 文件 一. 类路径 (class path) 当你满怀着希望安装好了 java, 然后兴冲冲地写了个 hello world,然后编译,运行, 就等着那两个美好的单词出现在眼前, 可是不幸的是, 只看到了 Can't find class HelloWorld 或者 Exception in thread "main" java.lang.NoSuchMethodError : maain.为什么

  • Java中对List集合的常用操作详解

    目录: 1.list中添加,获取,删除元素: 2.list中是否包含某个元素: 3.list中根据索引将元素数值改变(替换): 4.list中查看(判断)元素的索引: 5.根据元素索引位置进行的判断: 6.利用list中索引位置重新生成一个新的list(截取集合): 7.对比两个list中的所有元素: 8.判断list是否为空: 9.返回Iterator集合对象: 10.将集合转换为字符串: 11.将集合转换为数组: 12.集合类型转换: 备注:内容中代码具有关联性. 1.list中添加,获取,

  • Java中JDBC实现动态查询的实例详解

    一 概述 1.什么是动态查询? 从多个查询条件中随机选择若干个组合成一个DQL语句进行查询,这一过程叫做动态查询. 2.动态查询的难点 可供选择的查询条件多,组合情况多,难以一一列举. 3.最终查询语句的构成 一旦用户向查询条件中输入数据,该查询条件就成为最终条件的一部分. 二 基本原理 1.SQL基本框架 无论查询条件如何,查询字段与数据库是固定不变的,这些固定不变的内容构成SQL语句的基本框架,如 select column... from table. 2.StringBuilder形成D

  • java 中 String format 和Math类实例详解

    java 中 String format 和Math类实例详解 java字符串格式化输出 @Test public void test() { // TODO Auto-generated method stub //可用printf(); System.out.println(String.format("I am %s", "jj")); //%s字符串 System.out.println(String.format("首字母是 %c",

  • java中 String和StringBuffer的区别实例详解

    java中 String和StringBuffer的区别实例详解 String: 是对象不是原始类型.            为不可变对象,一旦被创建,就不能修改它的值.            对于已经存在的String对象的修改都是重新创建一个新的对象,然后把新的值保存进去.            String 是final类,即不能被继承. StringBuffer: 是一个可变对象,当对他进行修改的时候不会像String那样重新建立对象            它只能通过构造函数来建立,  

  • java中functional interface的分类和使用详解

    java 8引入了lambda表达式,lambda表达式实际上表示的就是一个匿名的function. 在java 8之前,如果需要使用到匿名function需要new一个类的实现,但是有了lambda表达式之后,一切都变的非常简介. 我们看一个之前讲线程池的时候的一个例子: //ExecutorService using class ExecutorService executorService = Executors.newSingleThreadExecutor(); executorSer

  • java中关于return返回值的用法详解

    我们输入一个条件时,系统就会对这个条件进行判断,然后给出一个返回时的结论,我们把这个结果看做是返回值.在java里可以使用return语句来进行返回,从字面意思就能很好的理解它的用法了.下面我们就return的有无返回值进行分类展示,同时带来代码的实例分享. 1.定义 return语句可以使其从当前方法中退出,返回到调用该方法的语句处,继续程序的执行. 2.返回语句两种格式 有返回值: (1)return 返回值: (2)return 0 代表程序正常退出, (3)return 1 代表程序异常

  • Java中的重要核心知识点之继承详解

    目录 一.继承 1.概念 2.语法 3.父类成员的访问 (1)子类中访问父类成员变量 (2)子类中访问父类成员方法 4.super关键字 5.子类构造方法 6.super和this 7.代码块执行顺序 8.父类成员在子类中的可见性 9.继承方式 10.final关键字 11.组合 一.继承 1.概念 继承(inheritance)机制:是面向对象程序设计使代码可以复用的最重要的手段,它允许程序员在保持原有类特 性的基础上进行扩展,增加新功能,这样产生新的类,称派生类.继承呈现了面向对象程序设计的

随机推荐