对于程序语言的设计者来讲,建立一个好的输入、输出(I/O)系统是一个项艰难的任务。算法
在本章学习笔记中,我将会记录关于File、I/O流的相关学习笔记。编程
File类这个名字有必定的误导性;咱们会认为它代指一个文件,实际上不是这样。FilePath对这个类来讲是更好的名字,由于它既能表明一个文件,也能够表明一个文件集(文件夹),咱们能够经过调用list()方法来获取文件集中的文件(集)。数组
FilenameFilter至关于一个过滤器,建立扩展于该接口的类的目的在于把accept()方法提供给list()使用,使list()能够回调accept()方法,进而决定哪些文件将包含在这个数组中。演示以下:多线程
/** * Created by Mr.W on 2017/11/8. */ public class FileClass { public static void main(String[] args) { // "."表示项目根目录 File file = new File("./src/com/stupidzhe/jdklearning/io/file-test"); System.out.println("FileName: " + file.getName()); System.out.println("isDirectory: " + file.isDirectory()); // 经过lambda表达式来实例化FilenameFilter的匿名类 String[] fileNameArray = file.list((dir, name) -> Pattern.matches("^[file].*", name)); if (null == fileNameArray) { return; } for (String fileName : fileNameArray) { System.out.println("fileName: " + fileName + " " + fileName.hashCode()); } // 这里经过hashCode大小来排序 Arrays.sort(fileNameArray, ((o1, o2) -> (o1.hashCode() > o2.hashCode()?-1:1))); for (String fileName : fileNameArray) { System.out.println("fileName: " + fileName + " " + fileName.hashCode()); } } } --------------output-------------- FileName: file-test isDirectory: true fileName: file1.c -855046422 fileName: file2.c -855045461 fileName: file3.c -855044500 fileName: file4.c -855043539 ----------after sort-------- fileName: file4.c -855043539 fileName: file3.c -855044500 fileName: file2.c -855045461 fileName: file1.c -855046422 -----------------------------------
这种结构经常被称为“回调”。更具体地说,这是一个策略模式的例子,由于list()实现了基本的功能,并且按照FilenameFilter的形式提供了这个策略,以完善list()在提供服务时锁需的算法。编程语言
策略的目的就是提供了代码行为的灵活性。学习
由于File类不只仅只表明存在的文件或目录。也能够用File对象来建立新的目录或尚不存在的这个目录路径。咱们还能够查看文件的特性(如:大小,最后修改日期,读/写),检查某个File对象表明的是一个文件仍是一个目录,并能够删除文件。下面是例子:this
public class FileClass { public static void main(String[] args) { File file = new File("./src/com/stupidzhe/jdklearning/io/file-test/t"); if (!file.exists()) { System.out.println("this is not a file"); return; } System.out.println("is Directory: " + file.isDirectory()); System.out.println("is File: " + file.isFile()); System.out.println("its parent: " + file.getParent()); System.out.println("can write: " + file.canWrite()); System.out.println("can read: " + file.canRead()); System.out.println("absolute path: " + file.getAbsolutePath()); // 返回最近一次修改的时间戳 System.out.println("last modify: " + file.lastModified()); if (!file.delete()) { System.out.println("delete file fail"); return; } file = new File("./src/com/stupidzhe/jdklearning/io/file-test/t"); if (file.exists()) { return; } try { Thread.sleep(2000L); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("create dir: " + file.mkdir()); // 能够对文件(集)的位置和名称进行修改 //file.renameTo(new File("./src/com/stupidzhe/jdklearning/io/file-test/k")); } } --------------output----------- is Directory: true is File: false its parent: ./src/com/stupidzhe/jdklearning/io/file-test can write: true can read: true absolute path: /Users/x72/Downloads/jdk-test/./src/com/stupidzhe/jdklearning/io/file-test/t last modify: 1510140825000 create dir: true -------------------------------
编程语言的I/O类库中常使用流这个抽象概念,它表明任何有能力产出数据的数据源对象或是有能力接收数据的接收端对象。“流”屏蔽了实际的I/O设备中处理数据的细节。线程
Java类库中的I/O类分红输入和输出两部分,能够在JDK文档里的类层次中查看到。经过继承,任何自InputStream或Reader派生而来的类都含有名为read()的基本方法,用于读取单个字节或者字节数组。一样,任何自OutputStream或Write派生而来的类都含有名为write()的基本方法,用于写单个字节或者字节数组。可是,咱们一般不会用到这些方法,它们之因此存在是由于别的类可使用它们,以便提供更有用的接口。所以,咱们不多使用单一的类来建立流对象。设计
InputStream的做用是用来表示那些从不一样数据源产生输入的类,这些数据包括:code
每一种数据源都有相应的InputStream子类,另外,FilterInputStream也属于一种InputStream,为装饰器类提供基类,其中,装饰类能够把属性或有用的接口与输入流链接起来。
下面的表格是InputStream类型的实现:
<table> <thead> <tr> <th>类</th> <th align="left">功能</th> <th align="left">构造器参数</th> <th align="left">如何使用</th> </tr> </thead> <tbody><tr> <td>ByteArrayInputStream</td> <td align="left">容许将内存的缓冲区看成InputStream使用</td> <td align="left">缓冲区,字节将从中取出</td> <td align="left">做为一种数据源:将其与FilterInputStream对象相连以提供有用接口</td> </tr> <tr> <td>StringBufferInputStream</td> <td align="left">将String转换成InputStream</td> <td align="left">字符串。底层实现实际使用StringBuffer</td> <td align="left">做为一种数据源:将其与FilterInputStream对象相连以提供有用接口</td> </tr> <tr> <td>FileInputStream</td> <td align="left">用于从文件读取信息</td> <td align="left">字符串,表示文件名、文件或FileDescriptor对象</td> <td align="left"></td> </tr> <tr> <td>PipedInputStream</td> <td align="left">产生用于写入相关PipedOutputStream的数据。实现“管道化”概念</td> <td align="left">PipedOutputStream</td> <td align="left">做为多线程中的数据源:将其与FilterInputStream对象相连以提供有用接口</td> </tr> <tr> <td>SequenceInputStream</td> <td align="left">将两个或多个InputStream对象转换成单一InputStream</td> <td align="left">两个InputStream对象或一个容纳InputStream对象的容器Enumeration</td> <td align="left">做为一种数据源:将其与FilterInputStream对象相连以提供有用接口</td> </tr> <tr> <td>FilterInputStream</td> <td align="left">抽象类,做为“装饰器”的接口。其中,“装饰器”为其余的InputStream类提供有用功能。</td> <td align="left"></td> <td align="left"></td> </tr> </tbody></table>
该类别的类决定了输出所要去往的目标:字节数组、文件、管道。
另外,FilterOutputStream为“装饰器”类提供了一个基类,“装饰器”类把属性或者有用的接口和输出流链接了起来。
下面是继承OutputStream的类型:
<table> <thead> <tr> <th>类</th> <th align="left">功能</th> <th align="left">构造器参数</th> <th align="left">如何使用</th> </tr> </thead> <tbody><tr> <td>ByteArrayOutputStream</td> <td align="left">在内存中建立缓冲区。全部送往“流”的数据都要放置在此缓冲区</td> <td align="left">缓冲区初始化尺寸(可选的)</td> <td align="left">用于指定数据的目的地:将其与FilterOutputStream对象相连以提供有用接口</td> </tr> <tr> <td>FileOutputStream</td> <td align="left">用于将信息写至文件</td> <td align="left">字符串,表示文件名、文件或者FileDescriptor对象</td> <td align="left">指定数据的目的地:将其与FilterOutputStream对象相连以提供有用接口</td> </tr> <tr> <td>PipedOutputStream</td> <td align="left">任何写入其中的信息都会自动做为相关PipedInputStream的输出。实现“管道化”概念</td> <td align="left">PipedInputStream</td> <td align="left">指定用于多线程的数据的目的地:将其与FilterOutputStream对象相连以提供有用接口</td> </tr> <tr> <td>FilterOutputStream</td> <td align="left">抽象类,做为“装饰器”的接口。其中,“装饰器”为其余的InputStream类提供有用功能。</td> <td align="left"></td> <td align="left"></td> </tr> </tbody> </table>
FilterInputStream类可以完成两件彻底不一样的事情,其中,DataInputStream容许咱们读取不一样的基本类型数据以及String对象(全部方法都以“read”开头,例如readByte()、readFloat()等等)。搭配相应的FilterOutputStream,咱们能够经过数据“流”将基本类型的数据从一个地方迁移到另外一个地方。具体是那些“地方”是由InputStream类型的实现的表格决定。
其余FilterInputStream类则在内部修改InputStream的行为方式:是否缓冲,是否保留它所读过的行(容许咱们查询行数或设置行数),以及是否把单一字符推回输入流等等。
下面是FilterInputStream的实现类:
<table> <thead> <tr> <th>类</th> <th align="left">功能</th> <th align="left">构造器参数</th> <th align="left">如何使用</th> </tr> </thead> <tbody><tr> <td>DataInputStream</td> <td align="left">与DataOutputStream搭配使用,所以咱们能够按照可移植方式从流读取基本数据类型(int,char,long等)</td> <td align="left">InputStream</td> <td align="left">包含用于读取基本类型数据的所有接口</td> </tr> <tr> <td>BufferedInputStream</td> <td align="left">使用它能够防止每次读取时都得进行实际写操做。表明“使用缓冲区”</td> <td align="left">InputStream,能够指定缓冲区大小(可选的)</td> <td align="left">本质上不提供接口,只不过是向进程中添加缓冲区所必须的。与接口对象搭配</td> </tr> <tr> <td>LineNumberInputStream</td> <td align="left">跟踪输入流中的行号;可调用getLineNumber()和setLineNumber(int)</td> <td align="left">InputStream</td> <td align="left">仅增长了行号,所以可能要与接口对象搭配使用</td> </tr> <tr> <td>PushbackInputStream</td> <td align="left">具备“能弹出一个字节的缓冲区”。所以能够将读到的最后一个字符回退</td> <td align="left">InputStream</td> <td align="left">一般做为编译器的扫描器,之因此包含在内是由于Java编译器的须要,咱们基本上用不到</td> </tr> </tbody></table>
与DataInputStream对应的是DataOutputStream,它能够将各类基本数据类型以及String对象格式化输出到流中;这样一来,任何机器的任何DataInputStream都能读取他们。因此方法都以“write”开头。
PrintStream最初的目的即是为了以可视化格式打印全部的基本数据类型以及String对象。这和DataOutputStream不一样,后者的目的是将数据元素置入“流”中,使DataInputStream可以可移植地重构他们。
PrintStream内有两个重要的方法:print()、println()。对他们进行了重载,以即可以打印出各类数据类型。
DataInputStream的实现类:
<table> <thead> <tr> <th>类</th> <th align="left">功能</th> <th align="left">构造器参数</th> <th align="left">如何使用</th> </tr> </thead> <tbody><tr> <td>DataOutputStream</td> <td align="left">与DatInputStream搭配使用,所以咱们能够按照可移植方式从流读取基本数据类型(int,char,long等)</td> <td align="left">OutputStream</td> <td align="left">包含用于写入基本类型数据的所有接口</td> </tr> <tr> <td>PrintStream</td> <td align="left">用于产生格式化输出。其中OutputStream处理数据的存储,PrintStream处理显示</td> <td align="left">OutputStream,能够用boolean值指示是否每次换行时状况缓冲区(可选的)应该是对OutputStream对象的“final”封装。可能会常用到它</td> <td align="left"></td> </tr> <tr> <td>BufferedOutputStream</td> <td align="left">使用它以免每次发送数据时都要进行实际的写操做。表明“使用缓冲区”。能够调用flush()清空缓冲区</td> <td align="left">OutputStream,能够指定缓冲区大小(可选的)</td> <td align="left">本质上并不提供接口,只不过向进程中添加缓冲区所必需的。与接口对象搭配</td> </tr> </tbody></table>
当咱们初次看到Reader和Writer时,可能会觉得这是两个用来代替InputStream和OutputStream的类;但实际上并不是如此。
Reader和Writer则提供兼容Unicode与面向字符的I/O功能, 另外:
有时咱们必须把来自“字节”层次结构中的类和“字符”层次结构中的类结合起来使用。为了这个目的,要用到适配器类:InputStreamReader能够把InputStream转化为Reader,OutputStreamReader能够把OutputStream转化为Writer。