侧边栏壁纸
  • 累计撰写 57 篇文章
  • 累计创建 10 个标签
  • 累计收到 2 条评论

目 录CONTENT

文章目录

2、输入与输出

yilee
2023-04-04 / 0 评论 / 0 点赞 / 60 阅读 / 0 字 / 正在检测是否收录...
温馨提示:
本文最后更新于2024-05-31,若内容或图片失效,请留言反馈。 部分素材来自网络,若不小心影响到您的利益,请联系我们删除。

二、输入与输出

2.1 输入/输出流

  1. 读写字节

    • **InputStream ** 类有一个抽象方法 abstract int read() 这个方法将读入一个字节,并返回读入的字节,或者在遇到输入源结尾时返回 -1。

    • **OutputStream **类定义了抽象方法 abstract void write(int b)它可以向某个输出位置写出一个字节。

    • read 和write 方法在执行时都将阻塞,直至字节确实被读入或写出。available 方法使我们可以去检查当前可读入的字节数量。当你完成对输入/ 输出流的读写时,应该通过调用close 方法来关闭它,这个调用会释
      放掉十分有限的操作系统资源。关闭一个输出流的同时还会冲刷用千该输出流的缓冲区,我们还可以用flush 方法来人为地冲刷这些输出。

  2. 完整的流家族

    • java 流家族,包含各种输入流,使用方法来进行划分

      • 处理字节,输入流与输出流的层次结构

      397e518fbaef42eba3dd56e205087d17

      读写字符串和数字就需要功能更强大的子类,DatalnputStream 和 DataOutputStream 可以以二进制格式读写所有的基本Java 类型。

      • 处理字符,Reader 和Writer 的层次结构

      a207d9ad7e5e4bbd9eb66b46ceb6a3d2

      Reader 和Writer 类的基本方法与Input Stream 和OutputStream 中的方法类似。

      abstract int read()
      abstract void write(int c)
      

      read 方法将返回一个Unicode 码元( 一个在0 ~ 6 5 535 之间的整数),或者在碰到文件结尾时返回-1。write 方法在被调用时,需要传递一个Uni code 码元

    • 附加的接口:Closeable 、Flushable 、Readable 和 Appendable

      • Closeable void close() throws IOException
      • Flushable void flush()
    • InputStream 、OutputStream 、Reader 和Writer 都实现了Closeable 接口。

      93764d2663e34e0eb8ff6461bf801b4c

    • Output Stream 和Writer 还实现了Flushable 接口。

    • Readable 接口只有一个方法:int read(CharBuffer cb) , CharBuffer 类拥有按顺序和随机地进行读写访问的方法,它表示一个内存中的缓冲区或者一个内存映像的文件。

    • Appendable 接口有两个用于添加单个字符和字符序列的方法,在流类的家族中,只有Writer 实现了Appendable 。

      Appendable append(char c)
      Appendable append(CharSequence s)
      

      CharSequence 接口描述了一个char 值序列的基本属性, String 、CharBuffer 、StringBuilder 和StringBuffer 都实现了它。

  3. 组合输入/输出流过滤器

    • 所有在 java.io 中的类都将相对路径名解释为以用户工作目录开始,你可以通过调用System . getProperty(“user.dir”) 来荻得这个信息。

    • 某些输入流(例如 FilelnputStream 和由URL 类的 openStream 方法返回的输入流)可以从文件和其他更外部的位置上获取字节,而其他的输入流(例如 DatalnputStream ) 可以将字节组装到更有用的数据类型中。Java 程序员必须对二者进行组,如下所示。

      // 为了从文件中读人数字,首先需要创建一个FilelnputStream , 然后将其传递给DatalnputStream
      FileinputStream fin = new FileinputStream("employee.dat");
      DatainputStream din= new DatainputStream(fin);
      double x = din.readDouble();
      
    • 你可以通过嵌套过滤器来添加多重功能。

      DataInputStream din = new DataInputStream(
      new BufferedInputStream(
      new FileInputStream ("employee. dat")));
      // 代码获取使用带缓冲机制的 DatalnputStream 的方法
      
    • 有时当多个输人流链接在一起时,你需要跟踪各个中介输入流,如预览下一个字节

      PushbacklnputStream pbin = new PushbackInputStream(
      	new BufferedInputStream(
          new FileInputStream("emp1oyee.dat")));
      // 预读下一个字节
      int b = pbin.read();
      // 若它并非你所期望的值时将其推回流中
      if (b != '<') pbin.unread(b);
      
      // 获取一个既是可回推输入流,又是数据输入流的引用。
      DataInputStream din = new DataInputStream(
          new PushbackInputStream(
      	new BufferedInputStream(
          new FileInputStream("emp1oyee.dat"))));
      
    • Java 就有一点麻烦,它必须将多个流过滤器组合起来。但是,这种混合并匹配过滤器类以构建真正有用的输入/ 输出流序列的能力,将带来极大的灵活性。

      // 读取一个 ZIP 压缩文件
      ZipInputStream zin = new ZipInputStream(new FileInputStream("emp1oyee.dat"));
      DataInputStream din = new DataInputStream(zin);
      

2.2 文本输入与输出

  • OutputStreamWriter 类将使用选定的字符编码方式,把Unicode 码元的输出流转换为字节流。而InputStreamReader 类将包含字节(用某种字符编码方式表示的字符) 的输入流转换为可以产生Unicode 码元的读入器。

    让一个输入读入器可以从控制台读入键盘敲击信息,并将其转换为Unicode

    Reader in = new InputStreamReader(System.in);
    
    

    你应该总是在 InputStreamReader 的构造器中选择一种具体的编码方式。

    Reader in = new InputStreamReader(new FileInputStream("data.txt"), StandardCharsets.UTF-8) ;
    
  1. 如何写出文本输出

    • 对于文本输出,可以使用PrintWriter。

      PrintWriter out= new PrintWriter ("employee.txt", "UTF-8");
      // 等价写法
      PrintWriter out = new PrintWriter(new FileOutputStream("employee.txt"), "UTF-8");
      

      为了输出到打印写出器,需要使用与使用System.out 时相同的print 、println 和 printf 方法。

      println 方法在行中添加了对目标系统来说恰当的行结束符( Windows 系统是“\r\n" , UNIX 系统是"\n ” ) ,也就是通过调用 System.getProperty("line.separator”) 而获得的字符串。

    • 通过使用PrintWriter(Writer out , Boolean autoFlush) 来启用或禁用自动冲刷机制:

      PrintWriter out = new PrintWriter(new FileOutputStream("employee.txt"), "UTF-8", true);
      
  2. 如何读入文本输入

    • 最简单的处理任意文本的方式就是使用在卷I 中我们广泛使用的Scanner 类。从任何输入流中构建Scanner 对象。

      // 将短小的文本文件像下面这样读入到一个字符串
      String content = new String(Files.readAllBytes(path), charset);
      // 将这个文件一行行地读入
      List<String> lines= Files.readAlllines(path, charset);
      // 文件太大,那么可以将行惰性处理为一个Stream<String> 对象
      try (Stream<String> lines = Files.lines(path, charset)){
          /// ... 
      }
      
  3. 以文本格式存储对象

    • 将一个Employee 记录数组存储成了一个文本文件,其中每条记录都保存成单独的一行,而实例字段彼此之间使用分隔符分离开,这里我们使用竖线(|)作为分隔符(冒号( : )是另一种流行的选择,假设不会发生在要存储的字符串中存在I 的情况。

      Harry Hacker|35500|1989-10-01
      Carl Cracker|75000|1987-12-15
      Tony Tester|38000|1990-03-15
      
  4. 字符编码方式

    • Java 针对字符使用的是Unicode 标准。每个字符或“编码点”都具有一个21 位的整数。有多种不同的字符编码方式,也就是说,将这些21 位数字包装成字节的方法有多种。

      • UTF-8:将每个Unicode 编码点编码为l 到4 个字节的序列,包含了英语中用到的所有字符
      • UTF-16:将每个Unicode 编码点编码为1 个或2 个16位值,这是一种在Java 字符串中使用的编码方式。
      • ISO 8859-1:它是一种单字节编码,它包含了西欧各种语言中用到的带有重音符号的字符
      • Shift-JlS: 是一种用千日文字符的可变长编码。
    • StandardCharsets 类具有类型为 Charset 的静态变量,用于表示每种Java 虚拟机都必须支持的字符编码方式,为了获得另一种编码方式的 Charset, 可以使用静态的f orName 方法:

      StandardCharsets.UTF-8
      StandardCharsets.UTF-16
      StandardCharsets.UTF-16BE
      StandardCharsets.UTF-16LE
      StandardCharsets.IS0_8859_1
      StandardCharsets.US_ASCII
      // 从字符串加载变量
      Charset shiftJIS = Charset.forName("Shift-JIS");
      

2.3 读写二进制数据

  1. DatalnputDataOutput 接口

    • DataOutput 接口定义了下面用千以二进制格式写数组、字符、boolean 值和字符串的方法。writeInt 总是将一个整数写出为4 字节的二进制数量值,而不管它有多少位,writeDouble 总是将一个double 值写出为8 字节的二进制数量值

      writeChars、writeByte、writeInt、writeShort、writelong
      writeFloat、writeDouble、writeChar、writeBoolean、writeUTF
      

      writeUTF 方法使用修订版的8 位Unicode 转换格式写出字符串。Unicode 码元序列首先用UTF-16 表示,其结果之后使用UTF-8 规则进行编码。应该只在写出用于Java 虚拟机的字符串时才使用writeUTF 方法

      readInt、readShort、readLong、readFloat、readDouble、
      readChar、readBoolean、readUTF
      
    • DatalnputStream 类实现了Data Input 接口,为了从文件中读入二进制数据,可以将DatalnputStream 与某个字节源相组合

      DataInputStream in = new DataInputStream(new FileInputStream("employee.dat"));
      

      写出二进制数据,你可以使用实现了DataOutput 接口的DataOutputStream类

      DataOutputStream out = new DataOutputStream(new FileOutputStream("employee.dat" ));
      
  2. 随机访问文件

    • RandomAccessFile 类可以在文件中的任何位置查找或写入数据。磁盘文件都是随机访问的,但是与网络套接字通信的输入/输出流却不是。

      RandomAccessFile in = new RandomAccessFile("employee.dat" , "r");
      RandomAccessFile inOut = new RandomAccessFile ("employee.dat", "rw");
      

      当你将已有文件作为RandomAccessFile 打开时,这个文件并不会被删除。

      • **seek **: 方法用来将这个文件指针设置到文件中的任意字节位置,它的参数是一个long 类型的整
        数它的值位于0 到文件按照字节来度量的长度之间。

      • getFilePointer :方法返回文件指针的当前位置。

      • **writeFixedString **: 写出从字符串开头开始的指定数量的码元(如果码元过少,该方法将用0 值来补齐字符串)。

      • **readFixedString **:方法从输入流中读入字符,直至读入size 个码元,或者直至遇到具有0 值的字符值,然后跳过输入字段中剩余的0 值。为了提高效率,这个方法使用了StringBuilder 类来读入字符串。

      RandomAccessFile 类同时实现了Data Input 和DataOutput 接口。为了读写随机访问文件,可以使用在前面小节中讨论过的诸如readInt/writeInt 和readChar/writeChar 之类的方法。

  3. ZIP 文档

    ZIP 文档(通常) 以压缩格式存储了一个或多个文件,每个ZIP 文档都有一个头,包含诸如每个文件名字和所使用的压缩方法等信息。

    • 使用ZiplnputStream 来读入ZIP 文档。

      • getNextEntry : 返回一个描述这些项的ZipEntry 类型的对象。向ZiplnputStream 的getlnputStream 方法传递该项可以获取用于读取该项的输入流。

      • closeEntry: 读入下一项。

        ZipInputStream zin = new ZipInputStream(new FileInputStream(zipname));
        ZipEntry entry;
        while ((entry = zin.getNextEntry()) != null){
            InputStream in= zin.getInputStream(entry);
            // read the contents of in
            zin.closeEntry();
        }
        zin.close ();
        
    • 使用ZipOutputStream,写出到ZIP 文件

      1. 对于你希望放入到ZIP 文件中的每一项,创建一个ZipEntry 对象,并将文件名传递给ZipEntry 的构造器,它将设置其他诸如文件日期和解压缩方法等参数。

      2. 调用ZipOutputStream 的putNextEntry 方法来开始写出新文件,并将文件数据发送到ZIP 输出流中。

      3. 当完成时,需要调用 closeEntry。所有你希望存储的文件都重复这个过程。

        FileOutputStream fout = new FileOutputStream("test.zip");
        ZipOutputStream zout = new ZipOutputStream(fout );
        for all files {
            ZipEntry ze = new ZipEntry(fiename);
            zout.putNextEntry(ze);
            // send data to zout
            zout.closeEntry();
        }
        zout.close();
        
    • ZIP 输入流是一个能够展示流的抽象化的强大之处的实例。当你读入以压缩格式存储的数据时,不必担心边请求边解压数据的问题,而且ZIP 格式的字节源并非必须是文件,也可以是来自网络连接的ZIP 数据。

      JAR 文件只是带有一个特殊项的ZIP 文件,这个项称作清单。你可以使用JarlnputStream 和JarOutputStream 类来读写清单项。

2.4 对象输入/输出流与序列化

  1. 保存和加载序列化对象

    • ObjectOutputStream 可以保存对象数据,使用其writeObject 方法即可

      ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("employee.dat"));
      
    • ObjectlnputStream 可以读取保存的对象,用readObject 方法以这些对象被写出时的顺序获得它们

      ObjectInputStream in = new ObjectInputStream(new FileInputStream("employee.dat"));
      

      这些被保存的类应该实现 Serializable 接口,该接口没有任何方法,因此你不需要对这些类做任何改动。

      对于基本类型值,你需要使用诸如writelnt/readlnt 或writeDouble/readDouble 这样的方法。

    • 保存对象网络写入

      • 对你遇到的每一个对象引用都关联一个序列号(如图2-6 所示) 。

      • 对千每个对象,当第一次遇到时,保存其对象数据到输出流中。

      • 如果某个对象之前已经被保存过,那么只写出”与之前保存过的序列号为x 的对象相同” 。

        每个对象都是用一个序列号(serial number) 保存的,这就是这种机制之所以称为对象序列化的原因。

        fc5e3d037156453eafd1273488e8e3a2

    • 保存对象网络读取

      • 对千对象输入流中的对象,在第一次遇到其序列号时,构建它,并使用流中数据来初始化它,然后记录这个顺序号和新对象之间的关联。
      • 当遇到”与之前保存过的序列号为x 的对象相同”标记时, 获取与这个顺序号相关联的对象引用。
  2. 理解对象序列化的文件格式

    • 对象流输出中包含所有对象的类型和数据域。
    • 每个对象都被赋予一个序列号。
    • 相同对象的重复出现将被存储为对这个对象的序列号的引用。
  3. 修改默认的序列化机制

    • 某些数据域是不可以序列化的,如只对本地方法有意义的存储文件句柄或窗口句柄的整数值,防止这种域被序列化,及将它们标记成是transient这些域属千不可序列化的类,你也需要将它们标记成transient 的。瞬时的域在对象被序列化时总是被跳过的。

    • 序列化机制为单个的类提供了一种方式,去向默认的读写行为添加验证或任何其他想要的行为。可序列化的类可以定义具有下列签名的方法,之后,数据域就再也不会被自动序列化,取而代之的是调用这些方法。

      private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException;
      private void writeObject(ObjectOutputStream out) throws IOException
      

      readObject 和writeObject 方法只需要保存和加载它们的数据域,而不需要关心超类数据和任何其他类的信息。

    • 除了让序列化机制来保存和恢复对象数据,类还可以定义它自己的机制。即实现Externalizable 接口,并实现以下方法

      public void readExternal(ObjectinputStream in) throws IOException, ClassNotFoundException;
      public void writeExternal(ObjectOutputStream out) throws IOException;
      

      与readObject 和writeObject 不同,这些方法对包括超类数据在内的整个对象的存储和恢复负全责。

    ==注意:==readObject 和writeObject 方法是私有的,并且只能被序列化机制调用。与此不同的是, readExternal 和writeExternal 方法是公共的。特别是, readExternal还潜在地允许修改现有对象的状态。

  4. 序列化单例和类型安全的枚举

    • 遗留代码

      public class Orientation{
          public static final Orientation HORIZONTAL = new Orientation(1);
          public static final Orientation VERTICAL = new Orientation(2);
          private int value;
          private Orientation(int v) {value= v; }
      }
      
    • 上述即使构造器是私有的,序列化机制也可以创建新的对象,所以序列化上方面对象后,始终会创建一个新对象,此时不能再使用 == 去比较,因为内存地址已经改变。解决方法是定义另外一种称为readResolve 的特殊序列化方法。

      protected Object readResolve() throws ObjectStreamException {
          if (value == 1) return Orientation.HORIZONTAL;
          if (value == 2) return Orientation.VERTICAL;
          throw new ObjectStreamException(); // this shou 1 dn't happen
      }
      

      定义了readResolve 方法后,在对象被序列化之后就会调用它。它必须返回一个对象,而该对象之后会成为readObject 的返回值。

      在上面的情况中, readResolve 方法将检查value 域并返回恰当的枚举常量

    • 请记住向遗留代码中所有类型安全的枚举以及向所有支持单例设计模式的类中添加readResolve 方法。

  5. 版本管理

    • 如果一个类具有名为serialVersionUID 的静态数据成员,它就不再需要人工地计算其指纹, 而只需直接使用这个值。一旦这个静态数据成员被置千某个类的内部,那么序列化系统就可以读入这个类的对象的不同版本。
    • 如果这个类只有方法产生了变化,那么在读入新对象数据时是不会有任何问题的。但是,如果数据域产生了变化,那么就可能会有问题。例如,旧文件对象可能比程序中的对象具有更多或更少的数据域,或者数据域的类型可能有所不同。在这些情况中,对象输入流将尽力将流对象转换成这个类当前的版本。
    • 对象输入流会将这个类当前版本的数据域与被序列化的版本中的数据域进行比较,当然,对象流只会考虑非瞬时和非静态的数据域。如果这两部分数据域之间名字匹配而类型不匹配,那么对象输入流不会尝试将一种类型转换成另一种类型,因为这两个对象不兼容;如果被序列化的对象具有在当前版本中所没有的数据域,那么对象输入流会忽略这些额外的数据;如果当前版本具有在被序列化的对象中所没有的数据域,那么这些新添加的域将被设置成它们的默认值(如果是对象则是null , 如果是数字则为0, 如果是boolean 值则是false ) 。
  6. 为克隆使用序列化

    • 序列化机制提供了一种克隆对象的简便途径。直接将对象序列化到输出流中,然后将其读回。这样产生的新对象是对现有对象的一个深拷贝(deep copy) 。在此过程中,我们不必将对象写出到文件中,因为可以用ByteArrayOutputStream 将数据保存到字节数组中。要想得到clone 方法,只需扩展SerialCloneable 类。

      @Override
      public Object clone() throws CloneNotSupportedException {
          try {
              // save the object to a byte array
              ByteArrayOutputStream bout = new ByteArrayOutputStream();
              try (ObjectOutputStream out = new ObjectOutputStream(bout)) {
                  out.writeObject(this);
              }
              // read a clone of the object from the byte array
              try (InputStream bin = new ByteArrayInputStream(bout.toByteArray())) {
                  ObjectInputStream in = new ObjectInputStream(bin);
                  return in.readObject();
              }
          } catch (IOException | ClassNotFoundException e) {
              CloneNotSupportedException one2 = new CloneNotSupportedException();
              one2.initCause(e);
              throw one2;
          }
      }
      

    我们应该当心这个方法,尽管它很灵巧,但是它通常会比显式地构建新对象并复制或克隆数据域的克隆方法慢得多。

2.5 操作文件

  1. Path

    • Path 表示的是一个目录名序列, 其后还可以跟着一个文件名。路径中的第一个部件可以是根部件,例如/ 或C:\,而允许访问的根部件取决于文件系统。以根部件开始的路径是绝对路径;否则,就是相对路径。

      Path absolute = Paths.get("/home ", "harry");
      // 静态的Paths.get 方法接受一个或多个字符串,并将它们用默认文件系统的路径分隔符连接起来。结果就是一个Path 对象。
      // 如果其表示的不是给定文件系统中的合法路径,那么就抛出InvalidPathException 异常
      Path relative = Paths.get ("myprog ", "conf", "user.properties");
      

      路径不必对应着某个实际存在的文件,它仅仅只是一个抽象的名宇序列。当你想要创建文件时,首先要创建一个路径, 然后才调用方法去创建对应的文件。

    • 组合或解析路径

      • 调用p.resolve(q) 将按照下列规则返回一个路径:

        • 如果q 是绝对路径, 则结果就是q 。
        • 否则,根据文件系统的规则, 将“ p 后面跟着q " 作为结果
      • resolveSibling 它通过解析指定路径的父路径产生其兄弟路径。

      • resolve 的对立面是 relativize,调用 **p.relativize® ** 将产生路径q , 而对q进行解析的结果正是r,例如,以“/home/cay" 为目标对“/home/fred/myprog " 进行相对化操作,会产生”…/fred/myprog" , 其中,我们假设表示文件系统中的父目录。

      • normalize 方法将移除所有冗余的 . 和 … 部件(或者文件系统认为冗余的所有部件)例如,规范化“/home/cay/…/fred/./myprog” 将产生“/home/fred/myprog” 。

      • toAbsolutePath 将产生给定路径的绝对路径,该绝对路径从根部件开始,

      • Path 类有许多有用的方法用来将路径断开。

        Path p = Paths.get("/home", "fred", "myprog. properties");
        Path parent = p.getParent(); // the path /home/fred
        Path file = p.getFileName(); // the path myprog.properties
        Path root = p.getRoot(); // the path /
        
      • 从Path 对象中构建Scanner 对象

        Scanner in = new Scanner(Paths.get("/home/fred/input.txt"));
        
      • Path 与File 相互转化:Path 接口有一个toFile 方法,而File 类有一个toPath 方法。

  2. 读写文件

    • Files 类可以使得普通文件操作变得快捷。仅适用千处理中等长度的文本文件

      // 读取文件的所有内容
      byte[] bytes = Files.readAllBytes(path);
      // 将文件当作字符串读入
      String content= new String(bytes, charset);
      // 将文件当作行序列读入
      List<String> lines = Files.readAllLines(path, charset);
      // 写出一个字符串到文件中
      Files.write(path, content.getBytes(charset));
      // 向指定文件追加内容
      Files.write(path, content.getBytes(charset), StandardOpenOption.APPEND);
      // 将一个行的集合写出到文件
      Files.write(path, lines);
      
    • 处理的文件长度比较大,或者是二进制文件,这些便捷方法可以将你从处理FilelnputStream 、FileOutputStream 、BufferedReader和BufferedWriter 的繁复操作中解脱出来。

      InputStream in = Files.newinputStream(path) ;
      OutputStreamout = Files.newOutputStream(path) ;
      Reader in = Files.newBufferedReader(path, charset);
      Writer out= Files.newBufferedWriter(path, charset);
      
  3. 创建文件和目录

    • 创建目录

      • Files.createDirectory(path); 用于创建新目录 ,注意路径中除最后一个部件外,其他部分都必须是已存在的。
      • Files.createDirectories(path); 用于创建路径中的中间目录
    • Files.createFile(path); 用于创建文件, 如果文件已经存在了,那么这个调用就会抛出异常。检查文件是否存在和创建文件是原子性的,如果文件不存在,该文件就会被创建,并且其他程序在此过程中是无法执行文件创建操作的。

    • 在给定位置或者系统指定位置创建临时文件或临时目录,如下所示,dir 是一个Path 对象, prefix 和suffix 是可以为null 的字符串。例如,调用Files.createTempFile(null ," .txt ") 可能会返回一个像/tmp/1234405522364837194.txt 这样的路径。

      Path newPath = Files.createTempFile(dir, prefix, suffix);
      Path newPath = Files.createTempFile(prefix, suffix);
      Path newPath = Files.createTempDirectory(dir, prefix);
      Path newPath = Files.createTempDirectory(prefi x);
      
  4. 复制、移动和删除文件

    • 将文件从一个位置复制到另一个位置

      Files.copy(fromPath, toPath);
      
    • 移动文件( 即复制并删除原文件),如果目标路径已经存在,那么复制或移动将失败。

      Files.move(fromPath, toPath);
      // 如果想要覆盖已有的目标路径,可以使用REPLACE_EXISTING 选项。如果想要复制所有的文件属性,可以使用COPY_ATTRIBUTES 选项。或者同时使用两者
      Files.copy(fromPath, toPath, StandardCopyOption.REPLACE_EXISTING, StandardCopyOption.COPY_ATTRIBUTES);
      // 将移动操作定义为原子性的,这样就可以保证要么移动操作成功完成,要么源文件继续保持在原来位置。使用ATOMIC_MOVE 选项来实现
      Files.move(fromPath, toPath, StandardCopyOption.ATOMIC_MOVE);
      // 将一个Path 复制到输出流中
      Files.copy(inputStream, toPath);
      Files.copy(fromPath, outputStream);
      // 其他对copy 的调用,可以根据需要提供相应的复制选项。
      
    • 删除文件

      // 如果要删除的文件不存在,这个方法就会抛出异常。
      Files.delete(path);
      // 存在文件则删除,返回文件是否删除,该删除方法还可以用来移除空目录。
      boolean deleted = Files.deleteIfExists(path);
      
    • 文件操作选项

      292946f416c34484a15034efe03378ac

  5. 获取文件信息

    • 下面的静态方法都将返回一个boolean 值,表示检查路径的某个属性的结果

      exists
      isHidden
      isReadable, isWritable, isExecutable
      isRegularFile, isDirectory, isSymboliclink
      
    • size 方法将返回文件的字节数

      long fileSize = Files.size(path);
      
    • getOwner 方法将文件的拥有者作为 **java.nio.file.attribute.UserPrincipal ** 的一个实例返回。

    • 所有的文件系统都会报告一个基本属性集, 它们被封装在BasicFileAttributes 接口中,这些属性与上述信息有部分重叠。

      • 创建文件、最后一次访问以及最后一次修改文件的时间,这些时间都表示成 java.nio.file.attribute.FileTime

      • 文件是常规文件、目录还是符号链接,抑或这三者都不是。

      • 文件尺寸。

      • 文件主键,这是某种类的对象,具体所属类与文件系统相关,有可能是文件的唯一标识符, 也可能不是。

      • 获取属性,若文件系统系统兼容POSIX ,则可以获取一个PosixFileAttributes实例

        BasicFileAttributes attributes = Files.readAttributes(path, BasicFileAttributes.class);
        // POSIX
        PosixFileAttributes attributes = Files.readAttributes(path, PosixFileAttributes.class);
        
  6. 访问目录中的项

    • 静态的Files.list 方法会返回一个可以读取目录中各个项的Stream 对象。目录是被惰性读取的,这使得处理具有大量项的目录可以变得更高效。因为读取目录涉及需要关闭的系统资源,所以应该使用try 块

      try (Stream<Path> entries= Files.list(pathToDirectory)){
          ...
      }
      
    • list 方法不会进入子目录。为了处理目录中的所有子目录,需要使用File.walk 方法。可以通过调用File.walk(pathToRoot , depth) 来限制想要访问的树的深度。walk 方法都具有 FileVisitOption… 的可变长参数,但是你只能提供一种选项: FOLLOW_LINKS , 即跟踪符号链接。

      try (Stream<Path> entries= Files.walk(pathToRoot)){
          ...
      }
      

      如果要过滤walk 返回的路径,并且你的过滤标准涉及与目录存储相关的文件属性,例如尺寸、创建时间和类型(丈件、目录、符号链接),那么应该使用find 方法来替代walk 方法。可以用某个谓词函数来调用这个方法,该函数接受一个路径和一个BasicFileAttributes 对象。这样做唯一的优势就是效率高。因为路径总是会被读入,所以这些属性很容易荻取。

      ==注意:==你无法很容易地使用Files . walk 方法来删除目录树,因为你需要在删除父目录之前必须先删除子目录。

  7. 使用目录流

    • 目录流,使用 File.newDirectoryStream 对象产生一个 DirectoryStream,访问目录中的项并没有具体的顺序。注意, 它不是java.util . stream.Stream 的子接口,而是专门用于目录遍历的接口。它是Iterable 的子接口,因此你可以在增强的for 循环中使用目录流。

      try (DirectoryStream<Path> entries = Files.newDirectoryStream(dir)){
          for (Path entry: entries)
          	// Process ent ri es
      }
      // 用glob 模式来过滤文件:
      try (DirectoryStream<Path> entries = Files.newDirectoryStream(dir, "*.java")) {
          ...
      }
      

      75b0ad17b7074ae0885252c296d335e6

      如果使用Windows 的glob 语法,则必须对反斜杠转义两次:一次为glob 语法转义,一次为Java 宇符串转义:

      Files.newDirectoryStream(dir, "C:\\\\")
      
    • 如果想要访问某个目录的所有子孙成员,可以转而调用walkFileTree 方法,并向其传递一个FileVisitor 类型的对象

      • 在遇到一个文件或目录时: FileVisitResult visitFile(T path , BasicFileAttributes attrs)
      • 在一个目录被处理前: FileVisitResult preVisitDirectory(T dir , IOException ex)
      • 在一个目录被处理后: FileVisitResult postVisitDirectory(T dir, IOException ex)
      • 在试图访问文件或目录时发生错误,例如没有权限打开目录: FileVisitResult visitFileFailed(path , IOException)
    • 对于上述每种情况,都可以指定是否希望执行下面的操作:

      • 继续访问下一个文件: FileVisitResult.CONTINUE
      • 继续访问, 但是不再访问这个目录下的任何项了: FileVisitResult.SKIP_SUBTREE
      • 继续访问,但是不再访问这个文件的兄弟文(和该文件在同一个目录下的文件)了:FileVisitResult. SKIP_SIBLINGS
      • 终止访问: FileVisitResult.TERMINATE
    • 便捷类SimpleFileVisitor 实现了FileVisitor 接口,但是其除vis i tF i1 eF a i 1 ed方法之外的所有方法并不做任何处理而是直接继续访问,而visitFileFailed 方法会抛出由失败导致的异常,并进而终止访问。

      Files.walkFileTree(Paths.get("/"), new SimpleFileVisitor<Path>() {
          @Override
          public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException {
              System.out.println(path);
              return FileVisitResult.CONTINUE;
          }
      
          @Override
          public FileVisitResult visitFileFailed(Path file, IOException exc) throws IOException {
              return FileVisitResult.SKIP_SUBTREE;
          }
      
          @Override
          public FileVisitResult postVisitDirectory(Path dir, IOException exc) throws IOException {
              return FileVisitResult.CONTINUE;
          }
      });
      

      注意: 需要覆盖postVisitDirectory 方法和visitFileFailed 方法,否则,访问会在遇到不允许打开的目录或不允许访问的文件时立即失败。

      • 路径的众多属性是作为preVisitDirectory 和visitFile 方法的参数传递的。访问者不得不通过操作系统调用来获得这些属性,因为它需要区分文件和目录。
    • 删除目录及名下所有文件

      Files.walkFileTree(path, new SimpleFileVisitor<Path>() {
          @Override
          public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
              Files.delete(file);
              return FileVisitResult.CONTINUE;
          }
      
          @Override
          public FileVisitResult postVisitDirectory(Path dir, IOException exc) throws IOException {
              if (exc != null) {
                  throw exc;
              }
              Files.delete(dir);
              return FileVisitResult.CONTINUE;
          }
      });
      
  8. ZIP 文件系统

    • 建立一个ZIP 文件系统,它包含ZIP 文档中的所有文件,它使是多个专门处理ZIP 文档的新类。

      // zipname 是某个ZIP 文件的名字
      FileSystem fs = FileSystems.newFileSystem(Paths.get(zipname), null) ;
      // 如果知道文件名,从ZIP 文档中复制出这个文件
      Files.copy(fs.getPath(sourceName), targetPath);
      // 列出ZIP 文档中的所有文件
      Files.walkFileTree(fs.getPath("/"), new SimpleFileVisitor<Path>() {
          @Override
          public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
              System.out.println(file);
              return FileVisitResult.CONTINUE;
          }
      });
      

2.6 内存映射文件

  1. 内存映射文件的性能

    • 内存映射比使用带缓冲的顺序输入要稍微快一点,但是比使用RandomAccessFile 快很多,但对于中等尺寸文件的顺序读入则没有必要使用内存映射。

    • 使用内存映射

      • 从文件中获得一个通道(channel ) ,通道是用千磁盘文件的一种抽象,它使我们可以访问诸如内存映射、文件加锁机制以及文件间快速数据传递等操作系统特性。

        FileChannel channel = FileChannel.open(path, options);
        
      • 通过调用FileChannel 类的map 方法从这个通道中获得一个ByteBuffer。你可以指定想要映射的文件区域与映射模式,支持的模式有三种:

        • FileChannel.MapMode.READ_ONLY : 所产生的缓冲区是只读的,任何对该缓冲区写入的尝试都会导致ReadOnlyBufferException 异常。
        • FileChannel.MapMode.READ_WRITE : 所产生的缓冲区是可写的,任何修改都会在某个时刻写回到文件中。注意,其他映射同一个文件的程序可能不能立即看到这些修改,多个程序同时进行文件映射的确切行为是依赖千操作系统的。
        • FileChannel.MapMode.PRIVATE : 所产生的缓冲区是可写的,但是任何修改对这个缓冲区来说都是私有的,不会传播到文件中。
      • 缓冲区支持顺序和随机数据访问,它有一个可以通过get 和put 操作来移动的位置。

        // 顺序遍历缓冲区中的所有字节
        while(buffer.hasRemaining()){
            byte b = buffer.get();
            ...
        }
        // 随机访问某些字节
        for (int i = 0; i < buffer.limit(); i++)
        	byte b = buffer.get(i);
        	...
        }
        // 读写字节数组
        get(byte[] bytes)
        get(byte[] bytes, int offset, int length)
        // 更多方法
        getInt、getLong、getShort
        getChar、getFloat、getDouble
        // 以低位在前的排序方式处理包含二进制数字的文件
        buffer.order(ByteOrder.LITTLE_ENDIAN);
        // 要查询缓冲区内当前的字节顺序
        ByteOrder b = buffer.order()
        
        // 向缓冲区写数字
        putInt、putLong、putShort
        putChar、putFloat、putDouble
        // 在恰当的时机,以及当通道关闭时,会将这些修改写回到文件中。
        
    • 计算文件的32 位的循环冗余校验和(CRC32),这个数值就是经常用来判断一个文件是否已损坏的校验和,因为文件损坏极有可能导致校验和改变。

      CRC32 ere = new CRC32();
      while (more bytes)
      	crc.update(next byte)
      long eheeksum = crc.getValue();
      
  2. 缓冲区数据结构

    • 缓冲区是由具有相同类型的数值构成的数组, Buffer 类是一个抽象类,它有众多的具体子类,包括ByteBuffer 、CharBuffer 、DoubleBuffer 、IntBuffer 、LongBuffer 和 ShortBuffer 。注意:StringBuffer 类与这些缓冲区没有关系。

    • 最常用的将是 ByteBuffer 和 CharBuffer 每个缓冲区都有以下的属性,且满足(0 \leq 标记 \leq 位置 \leq 界限 \leq 容量)

      • 一个容量,它永远不能改变。

      • 一个读写位置,下一个值将在此进行读写。

      • 一个界限,超过它进行读写是没有意义的。

      • 一个可选的标记,用于重复一个读入或写出操作。

        e042d7f20202459bb9f1fc4c5f26fb0b

    • 使用缓冲区的主要目的是执行”写,然后读入"循环。

      • 假设我们有一个缓冲区,在一开始,它的位置为0,界限等于容量。我们不断地调用put 将值添加到这个缓冲区中,当我们耗尽所有的数据或者写出的数据量达到容量大小时,就该切换到读入操作了。

      • 这时调用 flip 方法将界限设置到当前位置,并把位置复位到 0。现在在 remaining 方法返回正数时(它返回的值是"界限-位置”),不断地调用get。在我们将缓冲区中所有的值都读入之后,调用clear 使缓冲区为下一次写循环做好准备。clear 方法将位置复位到0,并将界限复位到容量。

      • 重读缓冲区,可以使用rewind 或mark/reset 方法,详细内容请查看API注释。

      • 要获取缓冲区,可以调用诸如 ByteBuffer.allocate 或 ByteBuffer.wrap 这样的静态方法。可以用来自某个通道的数据填充缓冲区,或者将缓冲区的内容写出通道中。

        // 可以替代随机访问文件
        ByteBuffer buffer = ByteBuffer.allocate(RECORD_SIZE);
        channel.read(buffer);
        channel.position(newpos);
        buffer.flip();
        channel.write(buffer);
        
  3. 文件加锁机制

    • 文件锁可以控制对文件或文件中某个范围的字节的访问。要锁定一个文件, 可以调用FileChannel 类的lock 或trylock 方法,下方第一个调用会阻塞直至可获得锁,而第二个调用将立即返回,要么返回锁,要么在锁不可获得的情况下返回null 。这个文件将保待锁定状态,直至这个通道关闭,或者在锁上调用了release 方法。

      FileChannel channel = FileChannel.open(path);
      // 加锁
      FileLock lock = channel.lock();
      // 或
      FileLock lock = channel.trylock();
      
      
    • 调用锁定文件的一部分,如果shared 标志为false , 则锁定文件的目的是读写, 而如果为true , 则这是一个共享锁,它允许多个进程从文件中读入,并阻止任何进程获得独占的锁。并非所有的操作系统都支待共享锁,因此你可能会在请求共享锁的时候得到的是独占的锁。调用Filelock 类的isShared 方法可以查询你所持有的锁的类型。

      FileLock lock(long start, long size, boolean shared)
      // 或
      FileLock tryLock(long start, long size, boolean shared)
      

      如果你锁定了文件的尾部,而这个文件的长度随后增长超过了锁定的部分,那么增长出来的额外区域是未锁定的,要想锁定所有的字节, 可以使用Long.MAX_VALUE 来表示尺寸。

    • 要确保在操作完成时释放锁,与往常一样,最好在一个try 语句中执行释放锁的操作:

      try (FileLock lock = channel.lock()){
      	// access the locked file or segment
      }
      
    • 注意:文件加锁机制是依赖千操作系统的

      • 在某些系统中,文件加锁仅仅是建议性的,如果一个应用未能得到锁,它仍旧可以向被另一个应用并发锁定的文件执行写操作。
      • 在某些系统中,不能在锁定一个文件的同时将其映射到内存中。
      • 文件锁是由整个Java 虚拟机持有的。如果有两个程序是由同一个虚拟机启动的(例如Applet 和应用程序启动器),那么它们不可能每一个都获得一个在同一个文件上的锁。当调用lock 和trylock 方法时,如果虚拟机巳经在同一个文件上持有了另一个重叠的锁,那么这两个方法将抛出OverlappingFi l elockException 。
      • 在一些系统中,关闭一个通道会释放由Java 虚拟机持有的底层文件上的所有锁。因此,在同一个锁定文件上应避免使用多个通道。
      • 在网络文件系统上锁定文件是高度依赖千系统的,因此应该尽量避免。

2.7 正则表达式

  1. 正则表达式(regular expression) 用千指定字符串的模式,你可以在任何需要定位匹配某种特定模式的字符串的情况下使用正则表达式。

  2. 正则语法

    • 字符类(character class) 是一个括在括号中的可选择的字符集,例如,[ Jj] 、[ 0-9] 、[A-Za-z] 或[^0-9] 。这里”-”表示是一个范围(所有Unicode 值落在两个边界范围之内的字符),而^表示补集(除了指定字符之外的所有字符) 。

    • 如果字符类中包含“ -",那么它必须是第一项或最后一项;如果要包含"[" ,那么它必须是第一项;如果要包含"^",那么它可以是除开始位置之外的任何位置。其中,你只需要转义” ["和“ \" 。

    • 有许多预定的字符类,例如\d (数字) 和\p {Sc} (Unicode 货币符号)。

    • 大部分字符都可以与它们自身匹配,例如在前面示例中的ava 字符。

    • 符号可以匹配任何字符(有可能不包括行终止符,这取决于标志的设置) 。

    • 使用\ 作为转义字符,例如, \.匹配句号而\\ 匹配反斜线。

    • ^和$分别匹配一行的开头和结尾。

    • 如果X 和Y 是正则表达式,那么XY 表示“任何X 的匹配后面跟随Y 的匹配”,XIY表示“任何X 或Y 的匹配” 。

    • 你可以将量词运用到表达式X:X+(1 个或多个)、X*(0 个或多个)与X? ( 0 个或l 个) 。

    • 默认情况下,量词要匹配能够使整个匹配成功的最大可能的重复次数。你可以修改这种行为,方法是使用后缀?(使用勉强或吝啬匹配,也就是匹配最小的重复次数)或使用后缀+(使用占有或贪婪匹配,也就是即使让整个匹配失败,也要匹配最大的重复次数) 。

    • 我们使用群组来定义子表达式,其中群组用括号()括起来。例如,([±]?)([ 0-9] +)。然后你可以询问模式匹配器,让其返回每个组的匹配,或者用\n 来引用某个群组,其中n 是群组号(从\1 开始) 。

  3. Pattern 各个标志

    • Pattern.CASE_INSENSITIVE 或r : 匹配字符时忽略字母的大小写,默认情况下,这个标志只考虑US ASCII 字符。
    • Pattern.UNICODE_CASE 或u : 当与CASE_INSENSITIVE 组合使用时,用Unicode 字母的大小写来匹配。
    • Pattern.UNICODE_CHARACTER_CLASS 或U : 选择Unicode 字符类代替POSIX , 其中蕴含了UNICODE _CASE。
    • Pattern.MULTILINE 或m : ^和$匹配行的开头和结尾,而不是整个输入的开头和结尾。
    • Pattern.UNIX_LINES 或d : 在多行模式中匹配^ 和$时,只有 ‘\n’ 被识别成行终止符。
    • Pattern.DOTALL 或S : 当使用这个标志时,.符号匹配所有字符,包括行终止符。
    • Pattern.COMMENTS 或X : 空白字符和注释( 从#到行末尾)将被忽略。
    • Pattern.LITERAL : 该模式将被逐字地采纳,必须精确匹配,因字母大小写而造成的差异除外。
    • Pattern.CANON_EQ : 考虑Unicode 字符规范的等价性,例如, u 后面跟随(分音符号)匹配u 。

API注释

2.1.1 读写字节

java.io.InputStream 1.0

abstract int read()
// 从数据中读入一个字节,并返回该字节。这个read 方法在碰到输入流的结尾时返回-1。
int read(byte[] b)
// 读入一个字节数组,并返回实际读入的字节数,或者在碰到输入流的结尾时返回-1。这个read 方法最多读入b.length 个字节。
int read(byte[] b, int off, int len)
// 读入一个字节数组。这个read 方法返回实际读入的字节数,或者在碰到输入流的结尾时返回 -1。
// 参数: b 数据读入的数组
//		 off 第一个读入字节应该被放置的位置在b 中的偏移量
//		 len 读入字节的最大数量
long skip(long n)
// 在输入流中跳过n 个字节,返回实际跳过的字节数(如果碰到输入流的结尾,则可能小千n) 。
int available()
// 返回在不阻塞的情况下可获取的字节数(回忆一下,阻塞意味着当前线程将失去它对资源的占用) 。
void close()
// 关闭这个输入流。
void mark(int readlimit)
// 在输入流的当前位置打一个标记(并非所有的流都支持这个特性) 。如果从输入流中巳经读入的字节多千 readlimit 个,则这个流允许忽略这个标记。
void reset()
// 返回到最后一个标记,随后对read 的调用将重新读入这些字节。如果当前没有任何标记,则这个流不被重置。
boolean markSupported ()
// 如果这个流支待打标记,则返回true 。

java.io.OutputStream 1.0

abstract void write(int n)
// 写出一个字节的数据。
void write(byte[] b)
void write(byte[] b , int off, int len)
// 写出所有字节或者某个范围的字节到数组b 中。
// 参数: b 数据写出的数组
//		 off 第一个写出字节在b 中的偏移量
//		 len 写出字节的最大数量
void close()
// 冲刷并关闭输出流。
void flush()
// 冲刷输出流,也就是将所有缓冲的数据发送到目的地。

java.io.Closeable 5.0

void close()
// 关闭这个Closeable, 这个方法可能会抛出IOException。

java.io.Flushable 5.0

void flush()
// 冲刷这个Flushable。

java.io.Readable 5.0

int read(CharBuffer cb)
// 尝试着向cb 读入其可持有数量的char 值。返回读入的char 值的数量,或者当从这个Readable 中无法再获得更多的值时返回 -1。

java.io.Appendable 5.0

Append able append(char c)
Append able append(CharSequence cs)
// 向这个Appendable 中追加给定的码元或者给定的序列中的所有码元,返回this 。

java.io.CharSequence 1.4

char charAt(int index)
// 返回给定索引处的码元。
int length()
// 返回在这个序列中的码元的数量。
CharSequence subSequence(int startindex, int endindex)
// 返回由存储在start Index 到endindex-1 处的所有码元构成的CharSequence 。
String toStri ng()
// 返回这个序列中所有码元构成的字符串。

2.1.3 组合输入/输出流过滤器

java.io.FilelnputStream 1.0

FileInputStream(String name)
FileInputStream(File file)
// 使用由name 字符串或file 对象指定路径名的文件创建一个新的文件输入流( File 类在本章结尾处描述) 。非绝对的路径名将按照相对于VM 启动时所设置的工作目录来解析。

java.io.FileOutputStream 1.0

FileOutputStream(String name)
FileOutputStream(String name , boolean append)
FileOutputStream(File file)
FileOutputStream(File file, boolean append)
// 使用由name 字符串或file 对象指定路径名的文件创建一个新的文件输出流( File类在本章结尾处描述) 。如果append 参数为true , 那么数据将被添加到文件尾,而具有相同名字的已有文件不会被删除;否则,这个方法会删除所有具有相同名字的已有文件。

java.io.BufferedInputStream 1.0

BufferedInputStream(InputStream in)
// 创建一个带缓冲区的输入流。带缓冲区的输入流在从流中读入字符时,不会每次都对设备访问。当缓冲区为空时,会向缓冲区中读入一个新的数据块。

java.io.BufferedOutputStream 1.0

BufferedOutputStream(OutputStream out)
// 创建一个带缓冲区的输出流。带缓冲区的输出流在收集要写出的字符时,不会每次都对设备访间。当缓冲区填满或当流被冲刷时,数据就被写出。

java.io.PushbackInputStream 1.0

PushbackInputStream(InputStream in)
PushbackInputStream(InputStream in , int size)
// 构建一个可以预览一个字节或者具有指定尺寸的回推缓冲区的输入流。
void unread(int b)
// 回推一个字节,它可以在下次调用read 时被再次获取。
// 参数: b 要再次读入的字节。

2.2.1 如何写出文本输出

java.io.PrintWriter 1.1

PrintWri ter(Wri ter out)
PrintWriter(Writer writer)
// 创建一个向给定的写出器写出的新的PrintWriter 。
PrintWriter(String filename, String encoding)
PrintWriter(File file, String encoding)
// 创建一个使用给定的编码方式向给定的文件写出的新的PrintWriter 。
void print(Object obj )
// 通过打印从toString 产生的字符串来打印一个对象。
void print(String s)
// 打印一个包含Unicode 码元的字符串。
void println(String s)
// 打印一个字符串,后面紧跟一个行终止符。如果这个流处千自动冲刷模式,那么就会冲刷这个流。
void print(char[] s)
// 打印在给定的字符串中的所有Unicode 码元。
void print(char c)
// 打印一个Unicode 码元。
void print(int i)
void print(long l)
void print(float f)
void print(double d)
void print(boolean b)
// 以文本格式打印给定的值。
void printf(String format, Object... args)
// 按照格式字符串指定的方式打印给定的值。请查看卷I 第3 章以了解格式化字符串的相关规范。
boolean checkError ()
// 如果产生格式化或输出错误,则返回true 。一旦这个流碰到了错误,它就受到了污染,并且所有对checkError 的调用都将返回true 。

2.3 读写二进制数据

java.io.Datalnput 1.0

boolean readBoolean()
byte readByte()
char readChar()
double readDouble()
float readFloat()
int readInt()
long readlong()
short readShort()
// 读入一个给定类型的值。
void readFully(byte[] b)
// 将字节读入到数组b 中,其间阻塞直至所有字节都读入。
// 参数: b 数据读入的缓冲区
void readFully(byte[] b, int off, int len)
// 将字节读入到数组b 中,其间阻塞直至所有字节都读入。
// 参数: b 数据读入的缓冲区
//		 off 数据起始位置的偏移量
//		 len 读入字节的最大数量
String readUTF()
// 读入由“修订过的UTF- 8" 格式的字符构成的字符串。
int skipBytes(int n)
// 跳过n 个字节,其间阻塞直至所有字节都被跳过。
// 参数: n 被跳过的字节数

java.io.DatalOutput 1.0

void writeBoolean(boolean b)
void writeByte(int b)
void writeChar(int c)
void writeDouble(double d)
void writeFloat(float f)
void writeInt(int i)
void writelong(long l)
void writeShort (int s)
// 写出一个给定类型的值。
void writeChars(String s)
// 写出字符串中的所有字符。
void writeUTF(String s)
// 写出由“修订过的UTF- 8" 格式的字符构成的字符串。

2.3.2 随机访问文件

java.io.RandomAccessFile 1.0

RandomAccessFile(String file, String mode)
RandomAccessFile(File file, String mode)
// 参数:file 要打开的文件
// 	    mode "r" 表示只读模式;“rw" 表示读/写模式;“rws" 表示每次更新时,都对数据和元数据的写磁盘操作进行同步的读/写模式; “rwd"表示每次更新时,只对数据的写磁盘操作进行同步的读/写模式
long getFilePointer()
// 返回文件指针的当前位置。
void seek(long pos)
// 将文件指针设置到距文件开头pos 个字节处。
long length()
// 返回文件按照字节来度量的长度。

2.3.3 ZIP 文档

java.util zip.ZiplnputStream 1.1

ZipInputStream(InputStream in)
// 创建一个ZiplnputStream , 使得我们可以从给定的Input Stream 向其中填充数据。
ZipEntrygetNextEntry()
// 为下一项返回ZipEntry对象,或者在没有更多的项时返回null。
void closeEntry()
// 关闭这个ZIP 文件中当前打开的项。之后可以通过使用getNextEn t ry( ) 读入下一项。

java.util.zip.ZipOutputStream 1.1

ZipOutputStream(OutputStream out)
// 创建一个将压缩数据写出到指定的Output Stream 的ZipOutputStream 。
void putNextEntry(ZipEntry ze)
// 将给定的ZipEntry 中的信息写出到输出流中,并定位用千写出数据的流,然后这些数据可以通过write( ) 写出到这个输出流中。
void closeEntry()
// 关闭这个ZIP 文件中当前打开的项。使用putNextEntry 方法可以开始下一项。
void setlevel (int level)
// 设置后续的各个DEFLATED 项的默认压缩级别。这里默认值是Deflater . DEFAULT_COMPRESSION 。如果级别无效,则抛出IllegalArgumentException 。
// 参数:level 压缩级别,从O(NO_COMPRESSION) 到9(BEST_COMPRESSION)
void setMethod(int method)
// 设置用千这个ZipOutputStream 的默认压缩方法,这个压缩方法会作用千所有没有指定压缩方法的项上。
// 参数: method 压缩方法, DEFLATED 或STORED

java.util.zip.ZipEntry 1.1

ZipEntry(String name)
// 用给定的名字构建一个Zip 项。
参// 数: name 这一项的名字
long getCrc()
// 返回用于这个ZipEntry 的CRC32 校验和的值。
String getName()
// 返回这一项的名字。
long getSize()
返// 回这一项未压缩的尺寸,或者在未压缩的尺寸不可知的情况下返回- 1 。
boolean isDirectory()
// 当这一项是目录时返回true 。
void setMethod(int method)
// 参数: method 用千这一项的压缩方法,必须是DEFLATED 或STORED
void setSize(long size)
// 设置这一项的尺寸,只有在压缩方法是STORED 时才是必需的。
// 参数: size 这一项未压缩的尺寸
void setCre(long ere)
// 给这一项设置CRC32 校验和,这个校验和是使用CRC32 类计算的。只有在压缩方法是STORED 时才是必需的。
// 参数: ere 这一项的校验和

java.util.zip.ZipFile 1.1

ZipFile(String name)
ZipFile(File file)
// 创建一个ZipFile, 用于从给定的字符串或File 对象中读入数据。
Enumeration entries()
// 返回一个Enumeration 对象,它枚举了描述这个ZipFile 中各个项的ZipEntry 对象。
ZipEntry getEntry(String name)
// 返回给定名字所对应的项,或者在没有对应项的时候返回null 。
// 参数: name 项名
InputStream getInputStream(ZipEntry ze)
// 返回用千给定项的Input Stream 。
// 参数: ze 这个ZIP 文件中的一个ZipEntry
String getName()
// 返回这个ZIP 文件的路径。

2.4.1 保存和加载序列化对象

java.io.ObjectOutputStream 1.1

ObjectOutputStream(OutputStream out)
// 创建一个ObjectOutputStream 使得你可以将对象写出到指定的OutputStream 。
void writeObject(Object obj)
// 写出指定的对象到ObjectOutputStream , 这个方法将存储指定对象的类、类的签名以及这个类及其超类中所有非静态和非瞬时的域的值。

java.io.ObjectlnputStream 1.1

ObjectInputStream(InputStream in)
// 创建一个ObjectlnputStream 用千从指定的InputStream 中读回对象信息。
Object readObject()
// 从ObjectinputStream 中读入一个对象。特别是,这个方法会读回对象的类、类的签名以及这个类及其超类中所有非静态和非瞬时的域的值。它执行的反序列化允许恢复多个对象引用。

2.5.1 Path

java.nio.file.Paths 7

static Path get(String first, String... more)
通过连接给定的字符串创建一个路径。

java.nio.file.Path 7

Path resolve(Path other)
Path resolve(String other)
// 如果other 是绝对路径,那么就返回other ;否则,返回通过连接this 和other 获得的路径。
Path resolveSibling(Path other)
Path resolveSibling(String other)
// 如果other 是绝对路径,那么就返回other ;否则,返回通过连接this 的父路径和other 获得的路径。
Path relativize(Path other)
// 返回用this 进行解析,相对于other 的相对路径。
Path normalize()
// 移除诸如.和..等冗余的路径元素。
Path toAbsolutePath()
// 返回与该路径等价的绝对路径。
Path getparent()
// 返回父路径,或者在该路径没有父路径时,返回null 。
Path getFileName()
// 返回该路径的最后一个部件,或者在该路径没有任何部件时,返回null 。
Path getRoot()
// 返回该路径的根部件,或者在该路径没有任何根部件时,返回null 。
File toFile()
// 从该路径中创建一个File 对象。

java.io.File.1.0

Path toPath() 7
// 从该文件中创建一个Path 对象。

2.5.2 读写文件

java.nio.file.Files 7

static byte[] readAllBytes(Path path)
static List<String> readAllLines (Path path , Charset charset)
// 读入文件的内容。
static Path write(Path path , byte[] contents , OpenOption... options)
static Path write(Path path , Iterable< ? extends CharSequence>contents, OpenOption options)
// 将给定内容写出到文件中,并返回path 。
static InputStream newlnputStream(Path path , OpenOption... options)
static OutputStream newOutputStream(Path path, OpenOption... options)
static BufferedReader newBufferedReader(Path path, Charset charset)
static BufferedWriter newBufferedWriter(Path path , Charset charset , OpenOption... options)
// 打开一个文件,用于读入或写出。

2.5.3 创建文件和目录

java.nio.file.Files 7

static Path createFile(Path path, FileAttribute< ?>... attrs)
static Path createDirectory(Path path , FileAttribute< ?> ... attrs)
static Path createDirectories(Path path, FileAttribute< ?>... attrs)
// 创建一个文件或目录, createDirectories 方法还会创建路径中所有的中间目录。
static Path createTempFile(String prefix, String suffix, FileAttribute< ?>... attrs)
static Path createTempFile(Path parentDir , String prefix, String suffix , FileAttribute< ?>... attrs)
static Path createTempDirectory(String prefix, FileAttribute< ?>... attrs)
static Path createTempDirectory(Path parentDir, String prefix , FileAttribute< ?>... attrs)
// 在适合临时文件的位置,或者在给定的父目录中,创建一个临时文件或目录。返回所创建的文件或目录的路径。

2.5.4 复制、移动和删除文件

java.nio.file.Files 7

static Path copy(Path from , Path to , CopyOption... options)
static Path move(Path from , Path to , CopyOption... options)
// 将from 复制或移动到给定位置,并返回to 。
static long copy(InputStream from , Path to , CopyOption... options)
static long copy(Path from , OutputStream to , CopyOption... options)
// 从输入流复制到文件中,或者从文件复制到输出流中,返回复制的字节数。
static void delete(Path path)
static boolean deletelfExists(Path path)
// 删除给定文件或空目录。第一个方法在文件或目录不存在情况下抛出异常,而第二个方法在这种情况下会返回false 

2.5.5 获取文件信息

java.nio.file.Files 7

static boolean exists(Path path)
static boolean isHidden(Path path)
static boolean isReadable(Path path)
static boolean isWritable(Path path)
static boolean isExecutable(Path path)
static boolean isRegularFile(Path path)
static boolean isDirectory(Path path)
static boolean isSymboliclink(Path path)
// 检查由路径指定的文件的给定属性。
static long size(Path path)
// 获取文件按字节数度量的尺寸。
A readAttributes(Path path, Class<A> type, LinkOption... options)
// 读取类型为A 的文件属性。

java.nio.file.attribute.BasicFileAttributes 7

FileTime creationTime()
FileTime lastAccessTime()
FileTime lastModifiedTime()
boolean isRegularFile()
boolean isDirectory()
boolean isSymboliclink()
long size()
Object fileKey()
// 获取所请求的属性。

2.5.7 使用目录流

java.nio.file.Files 7

static DirectoryStream<Path> newDirectoryStream(Path path)
static DirectoryStream<Path> newDirectoryStream(Path path , String glob)
// 获取给定目录中可以遍历所有文件和目录的迭代器。第二个方法只接受那些与给定的glob 模式匹配的项。
static Path walkFileTree(Path start , FileVisitor< ? super Path> visitor)
// 遍历给定路径的所有子孙,并将访问器应用于这些子孙之上。

java.nio.file.SimpleFileVisitor 7

static FileVisitResult visitFile(T path, BasicFileAttributes attrs)
// 在访问文件或目录时被调用,返回CONTINUE 、SKIP_SUBTREE 、SKIP_SIBLINGS和TERMINATE 之一,默认实现是不做任何操作而继续访问。
static FileVisitResult preVisitDirectory(T dir, BasicFileAttributes attrs)
static FileVisitResult postVisitDirectory(T dir, BasicFileAttributes attrs)
// 在访问目录之前和之后被调用,默认实现是不做任何操作而继续访问。
static FileVisitResult visitFileFailed(T path, IOException exc)
// 如果在试图获取给定文件的信息时抛出异常,则该方法被调用。默认实现是重新抛出异常,这会导致访问操作以这个异常而终止。如果你想自己访问,可以覆盖这个方法。

2.5.8 ZIP 文件系统

java.nio.file.FileSystems 7

static FileSystem newFileSystem(Path path , Classloader loader)
// 对所安装的文件系统提供者进行迭代,并且如果loader 不为null , 那么就还迭代给定的类加载器能够加载的文件系统,返回由第一个可以接受给定路径的文件系统提供者创建的文件系统。默认情况下,对千ZIP 文件系统是有一个提供者的,它接受名字以. z ip 或. jar 结尾的文件。

java.nio.file.FileSystem 7

static Path getPath(String first, String... more)
// 将给定的字符串连接起来创建一个路径。

2.6.1 内存映射文件的性能

java.io.FilelnputStream 1.0

FileChannel getChannel() 1. 4
// 返回用千访问这个输入流的通道。

java.io.FileOutputStream 1.0

FileChannel getChannel() 1.4
// 返回用于访问这个输出流的通道。

java.io.RandomAccessFile 1.0

FileChannel getChannel() 1.4
// 返回用千访问这个文件的通道。

java.nio.channels. FileChannel 1.4

static FileChannel open(Path path, OpenOption... options) 7
// 打开指定路径的文件通道,默认情况下,通道打开时用千读入。
// 参数: path 打开通道的文件所在的路径
//		 options StandardOpenOpt ion 枚举中的WRITE 、APPEND 、TRUNCATE._EX I STING 、CREATE 值
MappedByteBuffer map(Fi l eChanne l . MapMode mode , long position , long size)
// 将文件的一个区域映射到内存中。
// 参数: mode FileChannel . MapMode 类中的常量READ_ONLY 、READ_WRITE 、或PRIVATE 之一
//		 position 映射区域的起始位置
//		 size 映射区域的大小

java.nio.Buffer 1.4

boolean hasRemaining()
// 如果当前的缓冲区位置没有到达这个缓冲区的界限位置, 则返回true 。
int limit()
// 返回这个缓冲区的界限位置,即没有任何值可用的第一个位置。

java.nio.ByteBuffer 1.4

byte get()
// 从当前位置获得一个字节,并将当前位置移动到下一个字节。
byte get(int index)
// 从指定索引处获得一个字节。
ByteBuffer put (byte b)
// 向当前位置推人一个字节,并将当前位置移动到下一个字节。返回对这个缓冲区的引用。
ByteBuffer put(int index, byte b)
// 向指定索引处推入一个字节。返回对这个缓冲区的引用。
ByteBuffer get(byte[] destination)
ByteBuffer get(byte[] destination, int offset, int length)
// 用缓冲区中的字节来填充字节数组,或者字节数组的某个区域,并将当前位置向前移动读入的字节数个位置。如果缓冲区不够大,那么就不会读入任何字节,并抛BufferUnderflow Exception 。返回对这个缓冲区的引用。
// 参数: destination 要填充的字节数组
//		 offset 要填充区域的偏移量
//		 length 要填充区域的长度
ByteBuffer put(byte[] source)
ByteBuffer put(byte[] source, int offset , int length)
// 将字节数组中的所有字节或者给定区域的字节都推入缓冲区中,并将当前位置向前移动写出的字节数个位置。如果缓冲区不够大,那么就不会读入任何字节,并抛出BufferUnderflow Exception 。返回对这个缓冲区的引用。
// 参数: source 要写出的数组
// offset 要写出区域的偏移量
// length 要写出区域的长度
Xxx getXxx()
Xxx getXxx(int index)
ByteBuffer putXxx(Xxx value)
ByteBuffer putXxx(int index, Xxx value)
// 获得或放置一个二进制数。Xxx 是Int 、Long 、Short 、Char 、Float 或Double 中的一个。
ByteBuffer order(ByteOrder order)
ByteOrder order()
// 设置或获得字节顺序, order 的值是ByteOrder 类的常量BIG_ENDIAN 或LITTLE_ENDIAN 中的一个。
static ByteBuffer allocate(int capacity)
// 构建具有给定容量的缓冲区。
static ByteBuffer wrap(byte[] values)
// 构建具有指定容量的缓冲区,该缓冲区是对给定数组的包装。
CharBuffer asCharBuffer()
// 构建字符缓冲区,它是对这个缓冲区的包装。对该字符缓冲区的变更将在这个缓冲区中反映出来,但是该字符缓冲区有自己的位置、界限和标记。

java.nio.CharBuffer 1.4

char get()
CharBuffer get(char[] destination)
CharBuffer get(char[] destination, int offset, int length)
// 从这个缓冲区的当前位置开始,获取一个char 值,或者一个范围内的所有char 值,然后将位置向前移动越过所有读入的字符。最后两个方法将返回this。
CharBuffer put(char c)
CharBuffer put(char[] source)
CharBuffer put(char[] source, int offset, int length)
CharBuffer put(String source)
CharBuffer put(CharBuffer source)
// 从这个缓冲区的当前位置开始,放置一个char 值,或者一个范围内的所有char 值,然后将位置向前移动越过所有被写出的字符。当放置的值是从CharBuffer 读入时,将读入所有剩余字符。所有方法将返回this。

2.6.2 缓冲区数据结构

java.nio.Buffer 1.4

Buffer clear()
// 通过将位置复位到0, 并将界限设置到容量,使这个缓冲区为写出做好准备。返回this 。
Buffer flip()
// 通过将界限设置到位置,并将位置复位到0, 使这个缓冲区为读入做好准备。返回this 。
Buffer rewind()
// 通过将读写位置复位到0 , 并保待界限不变,使这个缓冲区为重新读入相同的值做好准备。返回this 。
Buffer mark()
// 将这个缓冲区的标记设置到读写位置,返回this 。
Buffer reset()
// 将这个缓冲区的位置设置到标记,从而允许被标记的部分可以再次被读入或写出,返回this 。
int remaining ()
// 返回剩余可读入或可写出的值的数量,即界限与位置之间的差异。
int position()
void position(int newValue)
// 返回这个缓冲区的位置。
int capacity()
// 返回这个缓冲区的容量。

2.6.3 文件加锁机制

java.nio.channels.FileChannel 1.4

FileLock lock()
// 在整个文件上获得一个独占的锁,这个方法将阻塞直至获得锁。
FileLock tryLock()
// 在整个文件上获得一个独占的锁,或者在无法获得锁的情况下返回null 。
FileLock lock(long position, long size, boolean shared)
FileLock tryLock(long position, long size, boolean shared)
// 在文件的一个区域上获得锁。第一个方法将阻塞直至获得锁,而第二个方法将在无法获得锁时返回null 。
// 参数: position 要锁定区域的起始位置
//		 size 要锁定区域的尺寸
// 	 	 sharedtrue 为共享锁, false 为独占锁

java.nio.channels.FileLock 1.4

void close() 1. 7
// 释放这个锁。

2.7 正则表达式

java.util.regex.Pattern 1.4

static Pattern compile(String expression)
static Pattern compile(String expression, int flags)
// 把正则表达式字符串编译到一个用于快速处理匹配的模式对象中。
// 参数: expression 正则表达式
//		 flags CASE _ INSENSITIVE 、UNICODE_CASE 、MULTI LINE 、UNIX_LINES 、DOTALL 和CANON_EQ 标志中的一个
Matcher matcher(CharSequence input)
// 返回一个matcher 对象,你可以用它在输入中定位模式的匹配。
String[] split(CharSequence input)
String[] split(CharSequence input, int limit)
Stream<String> splitAsStream(CharSequence input)  8
// 将输入分割成标记,其中模式指定了分隔符的形式。返回标记数组,分隔符并非标记的一部分。
// 参数: input 要分割成标记的字符串
//		 limit 所产生的字符串的最大数量。如果已经发现了limit-1 个匹配的分隔符,那么返回的数组中的最后一项就包含所有剩余未分割的输入。如果1 i mi 区0 , 那么整个输入都被分割;如果1imit 为0 , 那么坠尾的空字符串将不会置千返回的数组中。

java.util.regex.Matcher 1.4

boolean matches()
// 如果输入匹配模式,则返回true 。
boolean lookingAt()
// 如果输入的开头匹配模式,则返回true 。
boolean find()
boolean find(int start)
// 尝试查找下一个匹配,如果找到了另一个匹配,则返回true 。
// 参数: start 开始查找的索引位置
int start()
int end()
// 返回当前匹配的开始索引和结尾之后的索引位置。
String group()
// 返回当前的匹配。
int groupCount()
// 返回输入模式中的群组数量。
int start(int groupindex)
int end(int groupindex)
// 返回当前匹配中给定群组的开始和结尾之后的位置。
// 参数: group Index 群组索引(从1 开始),或者表示整个匹配的O
String group(int groupindex)
// 返回匹配给定群组的字符串。
// 参数: group Index
String replaceAll(String rep 1 acement)
String replaceFirst(String replacement)
// 返回从匹配器输入获得的通过将所有匹配或第一个匹配用替换字符串替换之后的字符串。
// 参数: replacement 群组索引(从1 开始),或者表示整个匹配的O替换字符串,它可以包含用$n 表示的对群组的引用,这时需要用\$来表示字符串中包含一个$符号
static String quoteReplacement(String str) 5 . 0
// 引用str 中的所有\ 和$ 。
Matcher reset()
Matcher reset(CharSequence input)
// 复位匹配器的状态。第二个方法将使匹配器作用于另一个不同的输入。这两个方法都返回this 。
0
  1. 支付宝打赏

    qrcode alipay
  2. 微信打赏

    qrcode weixin

评论区