16.IO、文件、NIO【草案二】

(这一个章节将讲到Java里面比较重要的一个章节,这里说一句抱歉,因为最近换工作的原因,一直没有时间继续书写教程,不过接下来我会一直坚持写下去的哈,希望大家能够支持。这个章节主要涉及到常用的文件读写,包括高级的文件IO内容——java.nio,因为这些内容在如今的一些项目里面也属于相当常见的一部分,如果有什么遗漏或者笔误的话,希望读者来Email告知:[email protected],谢谢!这一部分篇幅可能比前边章节长很多,也是为了保证能够将Java里面IO和文件操作部分内能写的都写入,如果有遗漏希望读者来Email,概念上有混淆的地方请告知,里面有些内容参考了一些原文数据进行了翻译以及思考注解。)
本章目录:

1.IO类相关内容
2.文件和目录
3.文件高级操作

2.文件和目录

  i.文件处理类:
  Java针对文件处理的时候提供了专用的文件类:File、FileDescriptor、FilePermission、RandomAccessFile,然后配合上边的IO部分的类:FileInputStream、FileOutputStream、FileReader、FileWriter来进行文件的操作,这几个类构成了Java里面常用的文件处理的结构,如果有遗漏的请读者来Email说明: [email protected] ;Java和C#不一样的有一点就是文件和目录的抽象表示形式并没有分离,C#里面针对文件和目录有单独的类来进行描述,而Java里面统一使用抽象的File表示文件和目录。
  [1]File类说明:
  该类的定义如下:
public classFileextendsObjectimplementsSerializable,Comparable<File>
  该类是Java里面针对文件和目录的抽象表示形式,这里列举它常用的方法列表以方便读者查阅:
  • 判断操作:
    booleancanExecute():测试某个应用程序是否可以执行,也就是某个文件是否可以针对平台进行执行,该应用程序是一个抽象路径名表示的文件,Linux平台因为我没有用过这个类,估计是.bin、.rpm或者其他格式文件,Windows一般.exe、.bat和.com文件
    booleancanRead():测试某个应用程序是否可以进行读取,也就是某个文件是否可读
    booleancanWrite():测试某个应用程序或文件是否可写,这种情况下当文件设置了只读属性过后,若不更改就会返回false
    booleanisAbsolute():判断该文件的抽象路径名是为一个绝对路径还是相对路径
    booleanisDirectory():测试该抽象路径表示的文件是否为一个目录【这里的目录可以理解为常用的语言:文件夹】
    booleanisFile():测试该抽象路径表示的文件是否为一个标准文件
    booleanisHidden():测试该抽象路径表示的文件是否是一个隐藏文件或者隐藏目录
    booleanexists():测试该抽象路径表示的文件或者目录是否存在
  • 枚举操作:
    String[] list():返回一个字符串数组,这些字符串指定了此抽象路径表示的目录下边所有的文件和目录
    String[] list(FilenameFilter filter):和上边方法同义,添加了一个过滤器,满足条件是满足所有过滤器定义的
    File[] listFiles():返回一个抽象路径数组,该方法操作的结果是枚举目录下边所有的文件列表的抽象路径表示
    File[] listFiles(FileFilter filter):返回抽象路径名数组,这些路径名表示此抽象路径名表示的目录中满足指定过滤器的文件和目录。
    File[] listFiles(FilenameFilter filter):返回抽象路径名数组,这些路径名表示此抽象路径名表示的目录中满足指定过滤器的文件和目录,和上边方法一模一样
    staticFile[] listRoots():列出可用的文件系统根目录
      这里说明两点:
    >第一:listRoots()使用该方法可以列举所有的根目录,其实所有存在于系统的根目录一般情况就是盘符,Linux可能不一样,Windows调用该方法的时候最终的输出列表就是一个盘符名的数组,因为该方法是静态方法,那么这个方法针对的就是所有的文件,针对每一个安装了Java平台的操作系统而言,就是列举文件的根路径清单。读者可以在自己的平台尝试一下自己写一段简单的程序测试测试。
    >第二:关于接口FilenameFilterFileFilter
    FileFilter接口
    public interfaceFileFilter(1.2):该接口用于抽象路径的过滤器,只包含了一个方法:booleanaccept(File pathname)——该方法用于测试抽象路径名是否包含在某个路径名的列表中
    FilenameFilter接口
    public interfaceFilenameFilter(1.0):实现该接口的类实例可用于过滤器文件名,该接口也只包含了一个方法:booleanaccept(File dir,Stringname)——测试指定文件是否包含在某一个文件列表中
  • 修改操作:
    booleancreateNewFile():当且仅当不存在该文件名的时候直接创建一个新的空文件,成功返回true,失败返回false
    staticFile createTempFile(Stringprefix,Stringsuffix):在默认的临时目录中创建一个空文件,使用给定的前缀和后缀的生成名称
    staticFile createTempFile(Stringprefix,Stringsuffix,File directory):在指定的目录中创建一个新的空文件,使用给定的前缀和后缀的生成文件名
    booleandelete():删除此抽象路径表示的文件或目录,删除成功返回true,失败返回false
    voiddeleteOnExit():在虚拟机终止的时候,请求删除此抽象路径名表示的目录或文件
    booleanmkdir()创建此抽象路径名指定的目录
    booleanmkdirs():创建此抽象路径名指定的目录,包括所有必须而且不存在的父目录
    booleanrenameTo(File dest):重新命名该抽象路径表示的文件名
  • 属性的设置和获取:
    获取属性函数:
    File getAbsoluteFile():返回此抽象路径名的绝对路径名形式
    StringgetAbsolutePath():返回此抽象路径名表示的文件或者目录的绝对路径字符串
    File getCanonicalFile():返回此抽象路径名的规范形式
    StringgetCanonicalPath():返回此抽象路径名的规范形式字符串
    longgetFreeSpace():返回此抽象路径名指定分区中未分配的字节
    StringgetName():返回此抽象路径名表示的文件或者目录的名称
    StringgetParent():返回此抽象路径名父目录的路径名字符串,若没有指定父目录则返回null
    File getParentFile():返回此抽象路径父母路的路径名;如果没有指定父目录则返回null
    StringgetPath():将此抽象路径名转换为一个路径名字符串
    longgetTotalSpace():返回此抽象路径名指定的分区大小
    longgetUsableSpace():返回此抽象路径指定分区用于此虚拟机的字节数
    longlastModified():返回此抽象路径名表示的文件最后一次被修改的时间
    longlength():返回此抽象路径表示的文件的长度
    设置属性函数:
    booleansetExecutable(booleanexecutable):设置此抽象路径所有者执行权限的一个便捷方法
    booleansetExecutable(booleanexecutable,booleanownerOnly):设置此抽象路径名所有者或用户的执行权限
    booleansetLastModified(longtime):设置最后一次修改的时间
    booleansetReadable(booleanreadable):设置此文件为所有者的读权限的一个便捷方法
    booleansetReadable(booleanreadable,booleanownerOnly):设置此抽象路径名的所有者或所有用户的读权限。
    booleansetReadOnly():标记此抽象路径名指定的文件或目录,从而只能对其进行读操作。
    booleansetWritable(booleanwritable):设置此抽象路径名所有者写权限的一个便捷方法。
    booleansetWritable(booleanwritable,booleanownerOnly):设置此抽象路径名的所有者或所有用户的写权限。
  【*:这里还没有列举的方法请读者去查看Java的API文档!】
  [2]FileDescriptor类:
  文件描述符的实例用作与基础机器有关的某种结构的不透明句柄,该结构表示开放文件、开放套接字或者字节的另一个源或接受者,文件描述符的主要实际用途是创建一个包含该结构的FileInputStreamFileOutputStream,应用程序不应该创建自己的文件描述符。该类构造的时候构造的是一个无效的对象,真正要使用的话还需要斟酌,这里提供一个网上摘录文章的片段【自己翻译】:
  其实Sun公司在设计这个类的时候是不希望我们去用它的,参阅官方文档可以知道这个类没有办法构造一个合法的实例对象,也就是说在应用程序里面没有办法创建一个合法的FileDescriptor类的实例。文档里面有显示声明:“Applications should not create their own file descriptors”。
  这里针对File descriptor(文件描述符:可能这里翻译的不是很妥当)做个说明:在计算机编程里面,一个文件描述符是去操作这个文件的一个抽象的键,这个用法以前是使用在POSIX结构的操作系统上的,在Windows上边,如果要实现它使用的则是标准C的输入输出库,“file handle”文件句柄。在POSIX系统上边,文件描述符实际上是一个int类型的值,标准C里面就是一个int。在每一进程里面,文件描述符有三个标准的POSIX平台的规范:
标准输入(stdin):值是0
标准输出(stdout):值是1
错误输出(stderr):值是2
  其结构如下图:
  一般来讲,文件描述符在打开文件过程中是系统内核的实体里面整个文件结构的一个索引,它描述了文件内部的数据结构的细节。
——在POSIX结构的操作系统上,这个数据结构又称为文件描述符表,每一个运行进程也有它自己内置的文件描述符表,当用户运行某个应用程序的时候,就是传递一个抽象的Key也就是这个文件描述符给系统内核来实现系统调用,然后内核就通过该系统应用程序的识别和判断来返回一个信号给应用程序告诉它它是否可以操作该文件,这些运行都是基于文件描述符进行的,而应用程序本身是不能实现针对文件描述符表的直接读写操作的。
——在Unix体系结构的操作系统里面,文件描述符可以直接引用文件、目录、块和字符设备(也称为“特殊文件”)、端口、FIFOs(也称为命名管道),而FILE *文件句柄在C的标准库里面就是一个被routines库引用的指针用来管理文件的数据结构而存在,其中这个数据结构往往包括了低级文件描述符用来访问Unix操作系统底层的对象。
——在Windows操作系统里面,同样使用文件句柄来因引用许多底层构造,和POSIX系统的文件描述符一样,微软的C库里面同样提供了兼容功能兼容运行POSIX上边支持的整数值。
  【*:好了,不扯远了,否则就没法继续了!】
  [3]FilePermission类:
  该类的定义为:
public final classFilePermissionextendsPermissionimplementsSerializable
  此类表示对文件和目录的访问,该类的实例由路径名和对该路径名有效的操作组合而成,路径名是授予指定操作的文件或目录的路径名。以"/*"(其中"/"是文件分隔符字符,即 File.separatorChar)结尾的路径名指示包含在该目录中的所有文件和目录。以"/-"结尾的路径名(递归地)指示包含在该目录中的所有文件和子目录。由特殊标记"<<ALL FILES>>"组成的路径名可匹配任何文件。注:由单个"*"组成的路径名指示当前目录中的所有文件,而由单个"-"组成的路径名指示当前目录中的所有文件,并(递归地)指示包含在当前目录中的所有文件和子目录。将所要授予的操作以字符串的形式传递给构造方法,该字符串包含由一个或多个用逗号分隔的关键字组成的列表。可能的关键字有"read"、"write"、"execute""delete"。其含义定义如下:
read读权限
write写权限
execute执行权限。允许调用 Runtime.exec。对应于 SecurityManager.checkExec。
delete删除权限。允许调用 File.delete。对应于 SecurityManager.checkDelete。
  处理前会将操作字符串转换为小写字母。在授予 FilePermission 权限时要小心。在对各种文件和目录授予读访问权和(尤其是)写访问权时,一定要慎重。对写操作授予"<<ALL FILES>>"权限特别危险。这允许对整个文件系统进行写操作。事实上它甚至允许对系统中的二进制文件(包括 JVM 运行时环境)进行替换。
  [4]RandomAccessFile类:
  该类的定义为:
public classRandomAccessFileextendsObjectimplementsDataOutput,DataInput,Closeable
  该类的实例支持对随机访问文件的读取写入,随机访问文件的行为类似存储在文件系统中的一个大型的byte数组,存在指向该隐含数组的光标或索引,称为文件指针;输入操作从文件指针开始读取字节,并随着对字节的读取而前移此文件指针,如果随机访问文件以读取/写入模式创建,则输出操作也可用;输出操作从文件指针开始写入字节,并随着对字节的写入而前移该指针。写入隐含数组的当前末尾之后的输出操作导致该数组的扩展,该文件可以通过getFilePointer方法读取,并且通过seek方法设置。通常,此类中所有的读取例程在读取所需数量的字节之前已经到达了文件末尾,则抛出EOFException,若有些原因无法读取任何字节,而不是在读取所需数量的字节之前已到达文件末尾则抛出IOException,而不是EOFException,特别指出,如果流被关闭了也可能抛出IOException
  该对象的文件位置指针遵循下边几个规律:
  • 新建RandomAccessFile对象的文件位置指针位于文件的开头处;
  • 每次读写操作之后,文件位置的指针都相应后移到读写的字节处;
  • 可通过getFilePointer()方法来获得文件位置指针的位置,通过seek方法来设置文件指针的位置。
  ——[$]随机的IO访问——
packageorg.susan.java.io;

importjava.io.IOException;
importjava.io.RandomAccessFile;

public classRandomIOApp {
public static voidmain(Stringargs[])throwsIOException{
RandomAccessFile file =newRandomAccessFile("D:/test.txt","rw");
file.writeBoolean(true);
file.writeInt(123456);
file.writeChar('j');
file.writeDouble(1234.56);
file.seek(1);
System.out.println(file.readInt());
System.out.println(file.readChar());
System.out.println(file.readDouble());
file.seek(0);
System.out.println(file.readBoolean());
file.close();
}
}
  这段代码的输出为:
123456
j
1234.56
true
  不仅仅如此,test.txt文件里面的内容也被写入了,我在书写该程序的时候犯了个小错误,就是第一个file.seek(1)没有调用,所以第一次运行的时候抛了一个EOFException,因为文件的光标没有移动到前边,所以默认是为没有任何字节可以读取了。下边再提供一个比较简单的例子来说明这个类的使用。【*:这里给读者道个歉,因为不知道怎么描述这个类,只能用简单的使用来操作,这是我个人水平的问题,因为平时有时候只是使用到这个类,并没有用得很多。】
  ——[$]另外简单的用法——
packageorg.susan.java.io;

importjava.io.RandomAccessFile;

public classRandomAccessFileMain {
public static voidmain(Stringargs[])throwsException{
RandomAccessFile file =newRandomAccessFile("D:/test.html","rw");
for(inti = 0; i <= 6; i++ ){
System.out.println(file.readLine());
}
longcurrent = file.getFilePointer();
file.seek(current + 0);
file.write("34".getBytes());
for(inti = 0; i <= 6; i++ ){
System.out.println(file.readLine());
}
current = file.getFilePointer();
file.seek(current + 6);
file.write("27".getBytes());
file.close();
}
}
  这里就不介绍这段代码的输入输出了,对了,如果这个地方出现了乱码问题就牵涉到本身读写的系统字符集相关问题,这个在后边的编码章节慢慢解释
  ——[$]翻译字符——
packageorg.susan.java.io;

importjava.io.File;
importjava.io.RandomAccessFile;
importjava.nio.ByteBuffer;
importjava.nio.CharBuffer;
importjava.nio.MappedByteBuffer;
importjava.nio.channels.FileChannel;
importjava.nio.charset.Charset;
importjava.nio.charset.CharsetDecoder;
importjava.nio.charset.CharsetEncoder;

public classTranslationApp {
public static voidmain(Stringargs[])throwsException {
File infile =newFile("D:/test.txt");
File outfile =newFile("D:/data2.txt");

RandomAccessFile inraf =newRandomAccessFile(infile,"r");
RandomAccessFile outraf =newRandomAccessFile(outfile,"rw");
FileChannel finc = inraf.getChannel();
FileChannel foutc = outraf.getChannel();
MappedByteBuffer inmbb = finc.map(FileChannel.MapMode.READ_ONLY,0,
(int) infile.length());

Charset inCharset = Charset.forName("ISO8859-1");
Charset outCharset = Charset.forName("utf8");
CharsetDecoder decoder = inCharset.newDecoder();
CharsetEncoder encoder = outCharset.newEncoder();
CharBuffer cb = decoder.decode(inmbb);
ByteBuffer outbb = encoder.encode(cb);

foutc.write(outbb);
inraf.close();
outraf.close();
}
}
  这段代码使用了后边将会提及到的java.nio包里面的内容,暂时在这里做一个简单的铺垫
  ii.目录处理:
  【*:接下来进入文件处理的常用方法里面,首先是针对目录的操作,其次就是处理各种文件,这里仅仅局限于文本文件的处理,更加深入的内容后边会说明,这两部分直接用代码说明。】
  ——[$]目录的创建、删除——
packageorg.susan.java.io;

importjava.io.File;
importjava.io.IOException;

public classDirCreateDelete {
public static voidmain(Stringargs[])throwsIOException{
File file =newFile("D:/test");
if(!file.exists()){
file.mkdir();
}
booleandelResult = file.delete();
if( delResult){
System.out.println("Delete Success!");
}else{
System.out.println("Delete Failure!");
}
}
}
  我这里进行了这样的测试,如果D盘下边什么东西都没有这段代码只是会输出Delete Success!,然后运行结束过后D盘下边没有任何内容,表面上看起来好像该程序没有起到任何作用,实际上这个程序已经演示了两个操作,第一个操作是创建了一个名为test的目录,也就是文件夹,其次把这个文件目录删除掉了。【*:当然前提条件是应用程序拥有这个权限进行这两步操作】;然后把后半部分删除目录注释掉单独运行,然后就会发现D盘下边会多一个目录,名称就为test,这样一个目录就创建好了,然后放一个文件到这个目录下,任何文件都可以,再重新取消掉注释——运行,就会发现会输出Delete Failure。这里需要注意的一点就是——使用程序进行File类的delete操作的时候,若File类对象是一个目录,这个目录必须是空目录delete才能成功执行即使打开了该目录但是程序执行了,这个目录还是会被删除掉。读者可以根据需要设置不同的情况测试上边的程序就能更加理解目录的创建和删除等操作了。
  执行下边这段程序之前先做一个简单操作:
创建一个目录以及它的子目录,其结构如下:
root
|—a
|—a1
|—a2
|—a21.txt
|—a3
|—a4.txt
|—b
|—b1
|—b11.txt
|—b2.txt
|—b3
|—c.txt
  并且隐藏c.txt,将b11.txt设置成为只读属性
  ——[$]递归枚举以及属性获取——
packageorg.susan.java.io;

importjava.io.File;
importjava.io.IOException;

public classListFilesTester {
public static voidmain(Stringargs[])throws<span cl
分享到:
评论
shangjava
  • 浏览: 297995 次
  • 性别: Icon_minigender_1
  • 来自: 北京
文章分类
社区版块
存档分类
最新评论

你可能感兴趣的:(数据结构,linux,windows,虚拟机,unix)