java I/O系统

 

18.1 File类

18.2输入和输出

18.3 添加属性和有用的接口

18.4 Reader和Writer

18.5 自我独立的类: RandomAccessFile

18.6 I/O流的典型使用方式

文件读写的工具

18.8 标准I/O

18.9 进程的控制

18.10 新I/O

18.11 压缩

18.12 对象的序列化

18.13 XML

18.14 Preferences

 File类既可以代表一个特定文件的名称,又能代表一个目录下的一组文件的名称
File file = new File(dir)//读取目录或文件
File file = new File(Path,dir)//读取Path路径下的dir文件或或dir目录下的文件或目录
public boolean endsWith(String str);//检查文件名末尾是否是str
public boolean matches(String regex) //检查文件名是否匹配正则表达式
public File getCanonicalFile() throws IOException //返回目录的规范形式,相当于new File(dir)
"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());//得到最后修改日期 
old.renameTo(rname);//将old重命名重命名为另一个文件或目录rname

		
  • 18.1.1 目录列表器

    • 用不带参数的list()方法可以获得File对象包含的全部列表
      如果我们想获得一个受限列表,例如.java的文件,那么我们就要用到"目录过滤器"
      public interface FilenameFilter {
      	boolean accept(File dir,String name); //
      }
      					
      //: io/DirList.java
      // Display a directory listing using regular expressions.
      // {Args: "D.*\.java"}
      import java.util.regex.*;
      import java.io.*;
      import java.util.*;
      
      public class DirList {
        public static void main(String[] args) {
          File path = new File(".");//"."表示项目目录 可以用其它路径比如 E:\\
          String[] list;
         
            list = path.list(new DirFilter(args[0]));//这里需要一个正则表达式,比如 .java
          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();//检查是否又匹配正则表达式args[0]的文件
        }
      } /* Output:
      DirectoryDemo.java
      DirList.java
      DirList2.java
      DirList3.java
      *///:~
      
      	
    • 匿名内部类
      • 上述例子很适合用匿名内部类进行改写
        //: io/DirList2.java
        // Uses anonymous inner classes.
        // {Args: "D.*\.java"}
        package io;
        import java.util.regex.*;
        import java.io.*;
        import java.util.*;
        
        public class DirList2 {
          public static FilenameFilter filter(final String regex) {
            // Creation of anonymous inner class:
            return new FilenameFilter() {
              private Pattern pattern = Pattern.compile(regex);
              public boolean accept(File dir, String name) {
                return pattern.matcher(name).matches();
              }
            }; // End of anonymous inner class
          }
          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]));// 注意传向filter的参数必须时final的这在匿名类中时必须的
            Arrays.sort(list, String.CASE_INSENSITIVE_ORDER);
            for(String dirItem : list)
              System.out.println(dirItem);
          }
        } /* Output:
        DirectoryDemo.java
        DirList.java
        DirList2.java
        DirList3.java
        *///:~
        						
      • 改进版的匿名内部类
        //: io/DirList3.java
        // Building the anonymous inner class "in-place."
        // {Args: "D.*\.java"}
        import java.util.regex.*;
        import java.io.*;
        import java.util.*;
        
        public class DirList3 {
          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]);
                public boolean accept(File dir, String name) {
                  return pattern.matcher(name).matches();
                }
              });
            Arrays.sort(list, String.CASE_INSENSITIVE_ORDER);
            for(String dirItem : list)
              System.out.println(dirItem);
          }
        } /* Output:
        DirectoryDemo.java
        DirList.java
        DirList2.java
        DirList3.java
        *///:~						
        						
  • 18.1.2 目录实用工具

    • net.mindview.util.Diectory.java类可以产生文件集
      //: net/mindview/util/Directory.java
      package ch04;
      import java.util.regex.*;
      import java.io.*;
      import java.util.*;
      import net.mindview.util.*;
      
      public final class Directory {
        public static File[]
        local(File dir, final String regex) { //local方法需要文件路径和正则表达式,返回文件和目录
          return dir.listFiles(new FilenameFilter() {
            private Pattern pattern = Pattern.compile(regex);
            public boolean accept(File dir, String name) {
              return pattern.matcher(
                new File(name).getName()).matches();
            }
          });
        }
        public static File[]
        local(String path, final String regex) { // Overloaded
          return local(new File(path), regex);
        }
        // A two-tuple for returning a pair of objects:
        public static class TreeInfo implements Iterable {
          public List files = new ArrayList();
          public List dirs = new ArrayList();
          // The default iterable element is the file list:
          public Iterator iterator() {
            return files.iterator();
          }
          void addAll(TreeInfo other) {
            files.addAll(other.files);
            dirs.addAll(other.dirs);
          }
          public String toString() {
            return "dirs: " + PPrint.pformat(dirs) +
              "\n\nfiles: " + PPrint.pformat(files);
          }
        }
        public static TreeInfo
        walk(String start, String regex) { // Begin recursion
          return recurseDirs(new File(start), regex);
        }
        public static TreeInfo
        walk(File start, String regex) { // Overloaded
          return recurseDirs(start, regex);
        }
        public static TreeInfo walk(File start) { // Everything //此walk方法只需要文件路径,返回文件和目录
          return recurseDirs(start, ".*");
        }
        public static TreeInfo walk(String start) {
          return recurseDirs(new File(start), ".*");
        }
        static TreeInfo recurseDirs(File startDir, String regex){
          TreeInfo result = new TreeInfo();
          for(File item : startDir.listFiles()) {
            if(item.isDirectory()) {
              result.dirs.add(item);
              result.addAll(recurseDirs(item, regex));
            } else // Regular file
              if(item.getName().matches(regex))
                result.files.add(item);
          }
          return result;
        }
        // Simple validation test:
        public static void main(String[] args) {
          if(args.length == 0)
            System.out.println(walk("."));
          else
            for(String arg : args)
             System.out.println(walk(arg));
        }
      } ///:~
      					
        • net.mindview.util.Directory工具的使用示例
      package io;
      //: io/DirectoryDemo.java
      // Sample use of Directory utilities.
      import java.io.*;
      import net.mindview.util.*;
      import static net.mindview.util.Print.*;
      
      public class DirectoryDemo {
        public static void main(String[] args) {
          // All directories:
          PPrint.pprint(Directory.walk(".").dirs);
          // All files beginning with 'T'
          for(File file : Directory.local(".", "T.*"))
            print(file);
          print("----------------------");
          // All Java files beginning with 'T':
          for(File file : Directory.walk(".", "T.*\\.java"))
            print(file);
          print("======================");
          // Class files containing "Z" or "z":
          for(File file : Directory.walk(".",".*[Zz].*\\.class"))
            print(file);
        }
      } /* Output: (Sample)
      [.\xfiles]
      .\TestEOF.class
      .\TestEOF.java
      .\TransferTo.class
      .\TransferTo.java
      ----------------------
      .\TestEOF.java
      .\TransferTo.java
      .\xfiles\ThawAlien.java
      ======================
      .\FreezeAlien.class
      .\GZIPcompress.class
      .\ZipCompress.class
      *///:~
      						
  • 18.1.3 目录的检查及创建
    • //: io/MakeDirectories.java
      // Demonstrates the use of the File class to
      // create directories and manipulate files.
      // {Args: MakeDirectoriesTest}
      package io;
      import java.io.*;
      
      public class MakeDirectories {
        private static void usage() {
          System.err.println(
            "Usage:MakeDirectories path1 ...\n" +
            "Creates each path\n" +
            "Usage:MakeDirectories -d path1 ...\n" +
            "Deletes each path\n" +
            "Usage:MakeDirectories -r path1 path2\n" +
            "Renames from path1 to path2");
          System.exit(1);
        }
        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();
          if(args[0].equals("-r")) {
            if(args.length != 3) usage();
            File
              old = new File(args[1]),
              rname = new File(args[2]);
            old.renameTo(rname);//将old重命名重命名为另一个文件或目录rname
            fileData(old);
            fileData(rname);
            return; // Exit main
          }
          int count = 0;
          boolean del = false;
          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 { // Doesn't exist
              if(!del) {
                f.mkdirs();
                System.out.println("created " + f);
              }
            }
            fileData(f);
          }
        }
      } /* Output: (80% match)
      created MakeDirectoriesTest
      Absolute path: d:\aaa-TIJ4\code\io\MakeDirectoriesTest
       Can read: true
       Can write: true
       getName: MakeDirectoriesTest
       getParent: null
       getPath: MakeDirectoriesTest
       length: 0
       lastModified: 1101690308831
      It's a directory
      *///:~
                

18.2输入和输出

 任何自Inputstream和Reader派生的类都有read()的基本方法,用于读取单个字节或字节数组

  • 18.2.1 InputStream类型

  • InputStream的作用是用来表示那些从不同数据产生输入的类,这些数据包括
    1. 字节数组
    2. String对象
    3. 文件
    4. 管道,工作方式与实际管道一样,即,从一端输入,从另一端输出
    5. 一个由其他种类的流组成的序列,以便我们可以将它们收集合并到一个流内
    6. 其他数据源,如Internet连接等
    InputStream类型
    功 能构造器参数
    如何使用
    ByteArrayInputStream 允许将内存的缓冲区当作InputStream使用 缓冲区,字节将从中取出
    作为一种数据源:将其与FilterInputStream对象相连以提供接口
    StringBufferInputStream 将String转换成InputStream 字符串,底层实际使用的是StringBuffer
    作为一种数据源:将其与FilterInputStream对象想连以提供有用接口
    FileInputStream 用于从文件中读取信息 字符串,表示文件名,文件,或FileDescriptor对象
    作为一种数据源:将其与FilterInputStream对象相连以提供有用的接口
    PipeInputStream 产生用于写入相关PipedOutput Stream的数据.实现"管道化的概念" PipedOutputStream
    作为多线程中数据源:将其与FilterInpuptStream对象相连以提供有用接口
    SequenceInputStream 将两个或多个InputStram对象转换成单一InputStream 两个InputStream对象或一个容纳InputStream对象的容器Enumeration
    作为一种数据源:将其与FilterInputStream对象相连以提供有用接口
    FilterInputStream 抽象类,作为"装饰器"的接口.其中,"装饰器"为其他的InputStream提供有用功能  
  • 18.2.2 OutputStream类型

    • 该类别的类决定了输出所要去往的目标:字节数组(但不是String,不过你当然可以用字节数组字节创建),文件过管道
    • FilteroutputStream为"装饰器"类提供了一个基类,"装饰器"类把属性或者有用的接口与输出流连接了起来
    OutputStream类型
     构造器参数
    如何使用
    ByteArrayOutputStream 在内存中创建缓冲区,所有发往"流"的数据都要放置在此缓冲区 缓冲区初始尺寸(可选的)
    用于指定数据的目的地,将其与FilterOutputStram对象相连以提供有用接口
    FileOutputStream 用于将信息写至文件 字符串,表示文件名,文件或FileDescriptor对象
    指定数据的目的地,将其与FilteroutputStream对象相连以提供有用接口
    PipedOutputStream 任何写入其中的信息都会自动作为相关PipedInputStream的输出,实现管道化的概念 PipedInputStream
    指定用于多线程的数据的目的地,将其与FilterOutputStream对象相连以提供有用接口
    FilterOutputStream 抽象类,作为"装饰器"的接口.其中,"装饰器"为其他的OutputStream提供有用功能  

18.3 添加属性和有用的接口

  • Java I/O类库需要多种不同功能的组合,这正是使用装饰器模式的原理所在
  • 抽象类Filter是所有装饰器类的基类,装饰器必须具有和它所装饰的对象相同的接口,但它也可以扩展接口
  • FilterInputStream和FilteroutputStream是用来提供装饰器类接口以控制特定输入(InputStream)和输出(OutputStream)的两个类
  • 18.3.1 通过FileterInputStream从InputStream读取数据

    • FilterInputStream类能够做两件完全不同的事情,DataInputStream允许我们从不同的进本数据以及String对象(所有方法以"read"开头,例如:reaByte(),readFloat()等等,搭配相应的DataOutStream,我们就可以通过"流"将基本数据类型从一个地方迁移到另一个地方
    • 其他FilterInputStream类则在内部修改InputStream的行为方式:是否缓冲,是否保留它所读过的行(允许我们查询行数和设置行数),以及是否把单一字符退回输入流等等
    FilterInputStream
    功能构造器参数
    如何使用
    DataInputStream 与DataOutputStream搭配使用,因此我么按照可移植方式从流读取基本数据类型(int,char,long等) InputStream
    包含用于读取基本数据类型的全部接口
    BuffereInputStream 使用它可以防止每次读取时都得进行实际写操作,代表缓冲区 InputStream,可以指定缓冲区大小(可选的)
    本质上不提供接口,只不过是向进程中添加缓冲区所必需,与接口对象搭配使用
    LineNumberInputStream 跟踪输入流中的行号,可调用getLineNumber()和setLineNumber(int) InputStream
    仅增加了行号,因此可能要与接口对象搭配使用
    PushbackInputStream 具有"能弹出一个字节的缓冲区".因此可以将读到的最后一个字节回退 InputStream
    通常作为编译器的扫描器,之所以包含在内是因为Java编译器的需要,我们可能永远不会用到
  • 18.3.2 通过FilterOutPutStream向OutputStream写入

    • DataInputStream和DataOutputStream相对应,它可以将各种基本数据类型以及String对象格式化输出到"流"中
    • PrintStream最初的目的是可视化格式打印所有的基本数据类型以及String对象,有两个重要方法print()和println()
    • PrintStream捕捉所有IOExceptions,因此必须使用checkError()自行测试错误状态,如果出现问题它返回true,另外PrintStream未国际化
    • BufferedOutputStream是一个修改过的OutputStream,它对数据提供缓冲技术
    FilterOutputStream
    功能构造器参数
    如何使用
    DataOutputStream 与DataInputStream搭配使用,因此可以按照可移植方式向流中写入基本数据类型(int,char,long等) OutputStream
    包含用于写入基本数据类型的全部接口
    PrintStream 用于产生格式化输出.其中DataOutputStream处理数据的存储,PrintStream处理显示 OutputStream,可以用boolean值指示是否在每次换行时清空缓冲区(可选的)应该是对OutputStream对象的"final"封装.可能会经常使用它们
    BufferedOutputStream 使用它可以避免每次发送数据时都要进行实际的写操作,代表"使用缓冲区",可以调用flush()清空缓冲区 OutputStream,可以指定缓冲区的大小(可选)
    本质上并不提供接口,只不过向进程中添加缓冲区所必需的,与接口对象搭配

18.4 Reader和Writer

Reader和Writer提供兼容Unicode与面向字符的I/o功能

InputStreamReader可以把InputStream转换为Reader,而OutputWriter可以把OutputStream转化为Writer

18.4.1 数据的来源和去处

      • 尽量使用Reader和Writer,一旦无法编译就用面向字节的类库
数据的来源和去处
来源和去处: Java1.0类相应的Java.1.1类
InputStream Reader
适配器: InputStreamWriter
OutputStream Writer
适配器: OutputStreamWriter
FileInputStream FileReader
FileOutputStream FileWriter
StringBufferInputStream(已弃用) StringReader
(无相应的类) StringWriter
ByteArrayInputStream CharArrayReader
ByteArrayOutputStream CharArrayWriter
PipedInputStream PipedReader
PipedOutputStream PipedWriter

18.4.2 更改流的行为

      • InputStream和OutputStream,使用FilterInputStream和FilterOutputStream的装饰子类修改流的行为以满足需要
      • Rreader和Writer的类继承层次结构继续沿用相同的思想--但是并不完全相同
过滤器:java1.0 类相应的java1.1类
FilterInputStream FilterReader
FilterOutputStream FilterWriter
BufferedInputStream BufferedReader(也有readline())
BufferedOutputStream BufferedWriter
DadaInputStream 使用DataInputStream(除了当需要使用readline()时以外,这时应该使用BufferedReader)
PrintStream PrintWriter
LineNumberInputStream(已弃用) LineNumberReader
StreamTokenizer StreamTokenizer(使用接受Reader的构造器)
PushbackInputStream PushbackReader
    • 无任何事使用readLine(),都不应该使用DataInputStream,应该用BufferedReader

18.5 自我独立的类: RandomAccessFile

    • RandomAccessFile适用于由大小已知的记录组成的文件
    • RandomAccessFile不是InputStream和OutputStream的继承层次的一部分
    seek() //将记录从一处转移到另一处,然后读取或者修改记录
    length()
    构造器还有第二个参数,用来指示我们只是"随机读"(r) 还是"即写又读"(rw)
    
  • 在jdk1.4中,RandomAccessFile的大多数功能(但不是全部)有nio存储映射文件所取代

18.6 I/O流的典型应用方式

18.6.1 缓冲读入文件

本节列出了一些I/O读写的示例

      • 如果想要打开一个文件用于读取字符输入,可以使用String和File对象作为文件名的FileInputReader,为了读取速度,我们将产生的引用传递给了BufferedReader构造器
        //: io/BufferedInputFile.java
package io;
import java.io.*;

public class BufferedInputFile {
  // Throw exceptions to console:
  public static String
  read(String filename) throws IOException {
    // Reading input by lines:
    BufferedReader in = new BufferedReader(
      new FileReader(filename));
    String s;
    StringBuilder sb = new StringBuilder();
    while((s = in.readLine())!= null)
      sb.append(s + "\n");
    in.close();
    return sb.toString();
  }
  public static void main(String[] args)
  throws IOException {
    System.out.print(read("src/io/BufferedInputFile.java"));
  }
} /* (Execute to see output) *///:~

        

18.6.2 从内存输入

      • 在下面的示例中,从BufferedInputFile.read()读入的String结果被用来创建一个StringReader,然后调用read()每次读取一个字符
        //: io/MemoryInput.java
package io;
import java.io.*;

public class MemoryInput {
  public static void main(String[] args)
  throws IOException {
    StringReader in = new StringReader(
      BufferedInputFile.read("src/io/MemoryInput.java"));
    int c;
    while((c = in.read()) != -1)
      System.out.print((char)c);
  }
} /* (Execute to see output) *///:~
        

18.6.3 格式化的内存输入

      • DataInputStream可以读取格式化数据,他是一个面向字节的I/O类(不是面向字符的),因此我们必须使用InputStream类而不是Reader
      //: io/TestEOF.java
// Testing for end of file while reading a byte at a time.
package io;
import java.io.*;

public class TestEOF {
  public static void main(String[] args)
  throws IOException {
    DataInputStream in = new DataInputStream(
      new BufferedInputStream(
        new FileInputStream("../test/src/io/TestEOF.java")));
    while(in.available() != 0)
      System.out.print((char)in.readByte());
  }
} /* (Execute to see output) *///:~
      

18.6.4 基本文件输出

      • FileWriter对象可以向文件写入数据
      • 首先创建一个与指定文件连接的FileWriter,再用BufferedWriter包装起来,如果要格式化输出,可以装饰成PrintWriter
        //: io/BasicFileOutput.java
import java.io.*;

public class BasicFileOutput {
  static String file = "BasicFileOutput.out";
  public static void main(String[] args)
  throws IOException {
    BufferedReader in = new BufferedReader(
      new StringReader(
        BufferedInputFile.read("BasicFileOutput.java")));
    PrintWriter out = new PrintWriter(
      new BufferedWriter(new FileWriter(file)));
    int lineCount = 1;
    String s;
    while((s = in.readLine()) != null )
      out.println(lineCount++ + ": " + s);
    out.close();
    // Show the stored file:
    System.out.println(BufferedInputFile.read(file));
  }
} /* (Execute to see output) *///:~
        
      • 文本文件输出的快捷方式
        • PrintWriter有一个辅助构造器,使你不必在每次希望创建文本并写入时,都去执行所有的构造工作
      //: io/FileOutputShortcut.java
import java.io.*;

public class FileOutputShortcut {
  static String file = "FileOutputShortcut.out";
  public static void main(String[] args)
  throws IOException {
    BufferedReader in = new BufferedReader(
      new StringReader(
       BufferedInputFile.read("FileOutputShortcut.java")));
    // Here's the shortcut:
    PrintWriter out = new PrintWriter(file);
    int lineCount = 1;
    String s;
    while((s = in.readLine()) != null )
      out.println(lineCount++ + ": " + s);
    out.close();
    // Show the stored file:
    System.out.println(BufferedInputFile.read(file));
  }
} /* (Execute to see output) *///:~
      

18.6.5 存储和恢复数据

      • 如果用DataOutputStream写入数据,Java保证我们可以使用DataInputStream准确的读取数据
        //: io/StoringAndRecoveringData.java
package io;
import java.io.*;

public class StoringAndRecoveringData {
  public static void main(String[] args)
  throws IOException {
    DataOutputStream out = new DataOutputStream(
      new BufferedOutputStream(
        new FileOutputStream("Data.txt")));
    out.writeDouble(3.14159);
    out.writeUTF("That was pi");
    out.writeDouble(1.41413);
    out.writeUTF("Square root of 2"); //使用utf-8输出字符串
    out.close();
    DataInputStream in = new DataInputStream(
      new BufferedInputStream(
        new FileInputStream("Data.txt")));
    System.out.println(in.readDouble());
    // Only readUTF() will recover the
    // Java-UTF String properly:
    System.out.println(in.readUTF());
    System.out.println(in.readDouble());
    System.out.println(in.readUTF());
  }
} /* Output:
3.14159
That was pi
1.41413
Square root of 2
*///:~
        
  • 18.6. 随机读取文件
    • RandomAccessFile相当于DataInputStream和DataOutputStream的组合
    • 使用RandomAccessFile时,必须知道文件排版,这样才能正确操作它,RandomAccessFile拥有读取基本数据类型和UTF-8字符串的各种方法
    • 可以自行选择第二个构造器参数: "r"或"rw"
        //: io/UsingRandomAccessFile.java
    package io;
    import java.io.*;
    
    public class UsingRandomAccessFile {
      static String file = "rtest.dat";
      static void display() throws IOException {
        RandomAccessFile rf = new RandomAccessFile(file, "r");
        for(int i = 0; i < 7; i++)
          System.out.println(
            "Value " + i + ": " + rf.readDouble());
        System.out.println(rf.readUTF());
        rf.close();
      }
      public static void main(String[] args)
      throws IOException {
        RandomAccessFile rf = new RandomAccessFile(file, "rw");
        for(int i = 0; i < 7; i++)
          rf.writeDouble(i*1.414);
        rf.writeUTF("The end of the file");
        rf.close();
        display();
        rf = new RandomAccessFile(file, "rw");
        rf.seek(5*8);
        rf.writeDouble(47.0001);
        rf.close();
        display();
      }
    } /* Output:
    Value 0: 0.0
    Value 1: 1.414
    Value 2: 2.828
    Value 3: 4.242
    Value 4: 5.656
    Value 5: 7.069999999999999
    Value 6: 8.484
    The end of the file
    Value 0: 0.0
    Value 1: 1.414
    Value 2: 2.828
    Value 3: 4.242
    Value 4: 5.656
    Value 5: 47.0001
    Value 6: 8.484
    The end of the file
    *///:~
        

    18.6.7 管道流

  • 管道流有:PipedInputStream, PipedOutputStream, PipedReader, PipedWriter
  • 文件读写的工具

  • TextFile类是作者编写的文件读写帮助类
  •     //: net/mindview/util/TextFile.java
    // Static functions for reading and writing text files as
    // a single string, and treating a file as an ArrayList.
    package net.mindview.util;
    import java.io.*;
    import java.util.*;
    
    public class TextFile extends ArrayList {
      // Read a file as a single string:
      public static String read(String fileName) {
        StringBuilder sb = new StringBuilder();
        try {
          BufferedReader in= new BufferedReader(new FileReader(
            new File(fileName).getAbsoluteFile()));
          try {
            String s;
            while((s = in.readLine()) != null) {
              sb.append(s);
              sb.append("\n");
            }
          } finally {
            in.close();
          }
        } catch(IOException e) {
          throw new RuntimeException(e);
        }
        return sb.toString();
      }
      // Write a single file in one method call:
      public static void write(String fileName, String text) {
        try {
          PrintWriter out = new PrintWriter(
            new File(fileName).getAbsoluteFile());
          try {
            out.print(text);
          } finally {
            out.close();
          }
        } catch(IOException e) {
          throw new RuntimeException(e);
        }
      }
      // Read a file, split by any regular expression:
      public TextFile(String fileName, String splitter) {
        super(Arrays.asList(read(fileName).split(splitter)));
        // Regular expression split() often leaves an empty
        // String at the first position:
        if(get(0).equals("")) remove(0);
      }
      // Normally read by lines:
      public TextFile(String fileName) {
        this(fileName, "\n");
      }
      public void write(String fileName) {
        try {
          PrintWriter out = new PrintWriter(
            new File(fileName).getAbsoluteFile());
          try {
            for(String item : this)
              out.println(item);
          } finally {
            out.close();
          }
        } catch(IOException e) {
          throw new RuntimeException(e);
        }
      }
      // Simple test:
      public static void main(String[] args) {
        String file = read("TextFile.java");
        write("test.txt", file);
        TextFile text = new TextFile("test.txt");
        text.write("test2.txt");
        // Break into unique sorted list of words:
        TreeSet words = new TreeSet(
          new TextFile("TextFile.java", "\\W+"));
        // Display the capitalized words:
        System.out.println(words.headSet("a"));
      }
    } /* Output:
    [0, ArrayList, Arrays, Break, BufferedReader, BufferedWriter, Clean, Display, File, FileReader, FileWriter, IOException, Normally, Output, PrintWriter, Read, Regular, RuntimeException, Simple, Static, String, StringBuilder, System, TextFile, Tools, TreeSet, W, Write]
    *///:~
        

    18.7.1 读取二进制文件

          //: net/mindview/util/BinaryFile.java
    // Utility for reading files in binary form.
    package net.mindview.util;
    import java.io.*;
    
    public class BinaryFile {
      public static byte[] read(File bFile) throws IOException{
        BufferedInputStream bf = new BufferedInputStream(
          new FileInputStream(bFile));
        try {
          byte[] data = new byte[bf.available()];
          bf.read(data);
          return data;
        } finally {
          bf.close();
        }
      }
      public static byte[]
      read(String bFile) throws IOException {
        return read(new File(bFile).getAbsoluteFile());
      }
    } ///:~
          

    18.8 标准I/O

    • 标准I/O的意义在于,我们可以很容易把程序串联起来,一个程序的标准输出可以成为另一个程序的标准输入

    18.8.1 从标准输入中读取

    • 从标准输入中读取
          //: io/Echo.java
    // How to read from standard input.
    // {RunByHand}
    package io;
    import java.io.*;
    
    public class Echo {
      public static void main(String[] args)
      throws IOException {
        BufferedReader stdin = new BufferedReader(
          new InputStreamReader(System.in));
        String s;
        while((s = stdin.readLine()) != null && s.length()!= 0)
          System.out.println(s);
        // An empty line or Ctrl-Z terminates the program
      }
    } ///:~
          

    18.8.2 将System.out转换成PrintWriter

    //: io/ChangeSystemOut.java
    // Turn System.out into a PrintWriter.
    package io;
    import java.io.*;
    
    public class ChangeSystemOut {
      public static void main(String[] args) {
        PrintWriter out = new PrintWriter(System.out, true);
        out.println("Hello, world");
      }
    } /* Output:
    Hello, world
    *///:~
          

    18.8.3 标准I/O重定向

  • java的System类提供了一些简单的静态方法调用,以允许我们对标准输入,输出和错误I/O进行重定向
  • setIn(Inputstream)
    setOut(PrintStream)
    setErr(PrintStream)
          
    //: io/Redirecting.java
    // Demonstrates standard I/O redirection.
    package io;
    import java.io.*;
    
    public class Redirecting {
      public static void main(String[] args)
      throws IOException {
        PrintStream console = System.out;
        BufferedInputStream in = new BufferedInputStream(
          new FileInputStream("src/io/Redirecting.java"));
        PrintStream out = new PrintStream(
          new BufferedOutputStream(
            new FileOutputStream("src/io/test.out")));
        System.setIn(in);
        System.setOut(out);
        System.setErr(out);
        BufferedReader br = new BufferedReader(
          new InputStreamReader(System.in));
        String s;
        while((s = br.readLine()) != null)
          System.out.println(s);
        out.close(); // Remember this!
        System.setOut(console);
      }
    } ///:~
          

    18.9 进程的控制

    • java 提供了执行其他操作系统程序输入输出的类
    //: net/mindview/util/OSExecuteException.java
    package net.mindview.util;
    
    public class OSExecuteException extends RuntimeException {
      public OSExecuteException(String why) { super(why); }
    } ///:~
    //: net/mindview/util/OSExecute.java
    // Run an operating system command
    // and send the output to the console.
    package net.mindview.util;
    import java.io.*;
    
    public class OSExecute {
      public static void command(String command) {
        boolean err = false;
        try {
          Process process =
            new ProcessBuilder(command.split(" ")).start();
          BufferedReader results = new BufferedReader(
            new InputStreamReader(process.getInputStream()));
          String s;
          while((s = results.readLine())!= null)
            System.out.println(s);
          BufferedReader errors = new BufferedReader(
            new InputStreamReader(process.getErrorStream()));
          // Report errors and return nonzero value
          // to calling process if there are problems:
          while((s = errors.readLine())!= null) {
            System.err.println(s);
            err = true;
          }
        } catch(Exception e) {
          // Compensate for Windows 2000, which throws an
          // exception for the default command line:
          if(!command.startsWith("CMD /C"))
            command("CMD /C " + command);
          else
            throw new RuntimeException(e);
        }
        if(err)
          throw new OSExecuteException("Errors executing " +
            command);
      }
    } ///:~
    
    //: io/OSExecuteDemo.java
    package io;
    import net.mindview.util.*;
    
    public class OSExecuteDemo {// Demonstrates standard I/O redirection.
    
      public static void main(String[] args) {
        OSExecute.command("javap bin/io/OSExecuteDemo");//这里的命令可以换成别的.例如:ping 192.168.1.104
      }
    } /* Output:
    Compiled from "OSExecuteDemo.java"
    public class OSExecuteDemo extends java.lang.Object{
        public OSExecuteDemo();
        public static void main(java.lang.String[]);
    }
    *///:~
          

    18.10 新I/O

  • JDK1.4的java.nio.*包中新加入了javaI/O类库,其目的在于提高速度,实际上就得I/O已经使用nio实现过,因此即使不显示的使用nio编写代码,也能从中受益
  • package io;
    
    import java.nio.*;
    import java.nio.channels.*;
    import java.io.*;
    
    public class GetChannel {
      private static final int BSIZE = 1024;
    
      public static void main(String[] args) throws Exception {
        // Write a file:
        @SuppressWarnings("resource")
        FileChannel fc = new FileOutputStream("data.txt").getChannel();
        fc.write(ByteBuffer.wrap("Some text ".getBytes()));
        fc.close();
        // Add to the end of the file:
        fc = new RandomAccessFile("data.txt", "rw").getChannel();
        fc.position(fc.size()); // Move to the end
        fc.write(ByteBuffer.wrap("Some more".getBytes()));//wrap方法将已经存在的数组包装到ByteBuffer
        fc.close();
        // Read the file:
        fc = new FileInputStream("data.txt").getChannel(); // 获得输入通道
        ByteBuffer buff = ByteBuffer.allocate(BSIZE); // 分配缓冲器大小 //还有一种更快的方式 ByteBuffer.allocateDirect()
        fc.read(buff); // 读取字节
        buff.flip();// 告诉缓冲器做好让别人读取字节的准备
        while (buff.hasRemaining())
          System.out.print((char) buff.get());
      }
    } /*
       * Output: Some text Some more
       */// :~
           
  • ByteBuffer是唯一直接与通道交互的缓冲器
  • 如果打算使用缓冲器执行进一步的read()操作,我们必须使用claer()为read()做好准备
  •        //: io/ChannelCopy.java
    // Copying a file using channels and buffers
    // {Args: ChannelCopy.java test.txt}
    import java.nio.*;
    import java.nio.channels.*;
    import java.io.*;
    
    public class ChannelCopy {
      private static final int BSIZE = 1024;
      public static void main(String[] args) throws Exception {
        if(args.length != 2) {
          System.out.println("arguments: sourcefile destfile");
          System.exit(1);
        }
        FileChannel
          in = new FileInputStream(args[0]).getChannel(),
          out = new FileOutputStream(args[1]).getChannel();
        ByteBuffer buffer = ByteBuffer.allocate(BSIZE);
        while(in.read(buffer) != -1) {
          buffer.flip(); // Prepare for writing
          out.write(buffer);
          buffer.clear();  // Prepare for reading
        }
      }
    } ///:~
    
           
  • transferTo()和transferFrom()允许我们将一个通道和另一个通道直接相连
  • //: io/TransferTo.java
    // Using transferTo() between channels
    // {Args: TransferTo.java TransferTo.txt}
    package io;
    import java.nio.channels.*;
    import java.io.*;
    
    public class TransferTo {
      public static void main(String[] args) throws Exception {
        if(args.length != 2) {
          System.out.println("arguments: sourcefile destfile");
          System.exit(1);
        }
        FileChannel
          in = new FileInputStream(args[0]).getChannel(),
          out = new FileOutputStream(args[1]).getChannel();
        in.transferTo(0, in.size(), out);
        // Or:
        // out.transferFrom(in, 0, in.size());
      }
    } ///:~
           

    18.10 新I/O

    18.10.1 转换数据

      • 数据要么在输入时进行编码,要么输出时进行解码
            //: io/BufferToText.java
    // Converting text to and from ByteBuffers
    package io;
    import java.nio.*;
    import java.nio.channels.*;
    import java.nio.charset.*;
    import java.io.*;
    
    public class BufferToText {
      private static final int BSIZE = 1024;
      public static void main(String[] args) throws Exception {
        FileChannel fc =
          new FileOutputStream("data2.txt").getChannel();
        fc.write(ByteBuffer.wrap("Some text".getBytes()));
        fc.close();
        fc = new FileInputStream("data2.txt").getChannel();
        ByteBuffer buff = ByteBuffer.allocate(BSIZE);
        fc.read(buff);
        buff.flip();
        // Doesn't work:
        System.out.println(buff.asCharBuffer());
        // Decode using this system's default Charset:
        buff.rewind();//返回数据开始部分
        String encoding = System.getProperty("file.encoding");//getProperty 获得系统字符编码
        System.out.println("Decoded using " + encoding + ": "
          + Charset.forName(encoding).decode(buff));//decode() //Charset.forName(encoding).decode(buff)) 对数据解码
        // Or, we could encode with something that will print:
        fc = new FileOutputStream("data2.txt").getChannel();
        fc.write(ByteBuffer.wrap(
          "Some text".getBytes("UTF-16BE")));//写入时指定编码 输出时转换成charBuffer 即buff.asCharBuffer()
        fc.close();
        // Now try reading again:
        fc = new FileInputStream("data2.txt").getChannel();
        buff.clear();
        fc.read(buff);
        buff.flip(); //让buff准备读取数据,这步很重要,不做可能会乱码
        System.out.println(buff.asCharBuffer());
        // Use a CharBuffer to write through:
        fc = new FileOutputStream("data2.txt").getChannel();
        buff = ByteBuffer.allocate(24); // More than needed
        buff.asCharBuffer().put("Some text");
        fc.write(buff);
        fc.close();
        // Read and display:
        fc = new FileInputStream("data2.txt").getChannel();
        buff.clear();
        fc.read(buff);
        buff.flip();
        System.out.println(buff.asCharBuffer());
      }
    } /* Output:
    ????
    Decoded using Cp1252: Some text
    Some text
    Some text
    *///:~
    
            
      • java.io.charset.Charset类提供了把数据编码成多种不同类型的字符集的工具
    //: io/AvailableCharSets.java
    // Displays Charsets and aliases
    package io;
    import java.nio.charset.*;
    import java.util.*;
    import static net.mindview.util.Print.*;
    
    public class AvailableCharSets {
      public static void main(String[] args) {
        SortedMap<String,Charset> charSets =
          Charset.availableCharsets();
        Iterator it = charSets.keySet().iterator();
        while(it.hasNext()) {
          String csName = it.next();
          printnb(csName);
          Iterator aliases =
            charSets.get(csName).aliases().iterator();
          if(aliases.hasNext())
            printnb(": ");
          while(aliases.hasNext()) {
            printnb(aliases.next());
            if(aliases.hasNext())
              printnb(", ");
          }
          print();
        }
      }
    } /* Output:
    Big5: csBig5
    Big5-HKSCS: big5-hkscs, big5hk, big5-hkscs:unicode3.0, big5hkscs, Big5_HKSCS
    EUC-JP: eucjis, x-eucjp, csEUCPkdFmtjapanese, eucjp, Extended_UNIX_Code_Packed_Format_for_Japanese, x-euc-jp, euc_jp
    EUC-KR: ksc5601, 5601, ksc5601_1987, ksc_5601, ksc5601-1987, euc_kr, ks_c_5601-1987, euckr, csEUCKR
    GB18030: gb18030-2000
    GB2312: gb2312-1980, gb2312, EUC_CN, gb2312-80, euc-cn, euccn, x-EUC-CN
    GBK: windows-936, CP936
    ...
    *///:~
    
            

    18.10.2 获取基本类型

  • 尽管ByteBuffer只能保持字节类型的数据,但是它可以从其所容纳的字节中产出各种数据
  • 利用asCharBuffer, asInt ,as Short等方法获得缓冲器上的视图,然后用视图的put方法可以插入基本数据类型
  • //: io/GetData.java
    // Getting different representations from a ByteBuffer
    package io;
    import java.nio.*;
    import static net.mindview.util.Print.*;
    
    public class GetData {
      private static final int BSIZE = 1024;
      public static void main(String[] args) {
        ByteBuffer bb = ByteBuffer.allocate(BSIZE);
        // Allocation automatically zeroes the ByteBuffer:
        int i = 0;
        while(i++ < bb.limit())
          if(bb.get() != 0)
            print("nonzero");
        print("i = " + i);
        bb.rewind();
        // Store and read a char array:
        bb.asCharBuffer().put("Howdy!");
        char c;
        while((c = bb.getChar()) != 0)
          printnb(c + " ");
        print();
        bb.rewind();
        // Store and read a short:
        bb.asShortBuffer().put((short)471142);
        print(bb.getShort());
        bb.rewind();
        // Store and read an int:
        bb.asIntBuffer().put(99471142);
        print(bb.getInt());
        bb.rewind();
        // Store and read a long:
        bb.asLongBuffer().put(99471142);
        print(bb.getLong());
        bb.rewind();
        // Store and read a float:
        bb.asFloatBuffer().put(99471142);
        print(bb.getFloat());
        bb.rewind();
        // Store and read a double:
        bb.asDoubleBuffer().put(99471142);
        print(bb.getDouble());
        bb.rewind();
      }
    } /* Output:
    i = 1025
    H o w d y !
    12390
    99471142
    99471142
    9.9471144E7
    9.9471142E7
    *///:~
    
          

    18.10.3 视图缓冲器

  • 视图缓冲器(view buffer)可以让我们通过某个特定的基本数据类型的视窗查看其底层的ByteBuffer,对视图的任何修改都会映射成为对ByteBuffer中的数据的修改
  •  //: io/IntBufferDemo.java
    // Manipulating ints in a ByteBuffer with an IntBuffer
    package io;
    import java.nio.*;
    
    public class IntBufferDemo {
      private static final int BSIZE = 1024;
      public static void main(String[] args) {
        ByteBuffer bb = ByteBuffer.allocate(BSIZE);
        IntBuffer ib = bb.asIntBuffer();
        // Store an array of int:
        ib.put(new int[]{ 11, 42, 47, 99, 143, 811, 1016 });
        // Absolute location read and write:
        System.out.println(ib.get(3));
        ib.put(3, 1811);
        // Setting a new limit before rewinding the buffer.
        ib.flip();
        while(ib.hasRemaining()) {
          int i = ib.get();
          System.out.println(i);
        }
      }
    } /* Output:
    99
    11
    42
    47
    1811
    143
    811
    1016
    *///:~
          
  • 一些底层的ByteBuffer通过视图缓冲器填满了整数或其它基本类型时,就可以被直接写到通道中了,注意:用视图修改了后,写入通道还是要用ByteBuffer
  • ByteBuffer通过一个被"包装"过的8字节数组产生,然后通过不同的视图缓冲器显示了出来
  • //: io/ViewBuffers.java
    package io;
    import java.nio.*;
    import static net.mindview.util.Print.*;
    
    public class ViewBuffers {
      public static void main(String[] args) {
        ByteBuffer bb = ByteBuffer.wrap(
          new byte[]{ 0, 0, 0, 0, 0, 0, 0, 'a' });
        bb.rewind();
        printnb("Byte Buffer ");
        while(bb.hasRemaining())
          printnb(bb.position()+ " -> " + bb.get() + ", ");
        print();
        CharBuffer cb =
          ((ByteBuffer)bb.rewind()).asCharBuffer();
        printnb("Char Buffer ");
        while(cb.hasRemaining())
          printnb(cb.position() + " -> " + cb.get() + ", ");
        print();
        FloatBuffer fb =
          ((ByteBuffer)bb.rewind()).asFloatBuffer();
        printnb("Float Buffer ");
        while(fb.hasRemaining())
          printnb(fb.position()+ " -> " + fb.get() + ", ");
        print();
        IntBuffer ib =
          ((ByteBuffer)bb.rewind()).asIntBuffer();
        printnb("Int Buffer ");
        while(ib.hasRemaining())
          printnb(ib.position()+ " -> " + ib.get() + ", ");
        print();
        LongBuffer lb =
          ((ByteBuffer)bb.rewind()).asLongBuffer();
        printnb("Long Buffer ");
        while(lb.hasRemaining())
          printnb(lb.position()+ " -> " + lb.get() + ", ");
        print();
        ShortBuffer sb =
          ((ByteBuffer)bb.rewind()).asShortBuffer();
        printnb("Short Buffer ");
        while(sb.hasRemaining())
          printnb(sb.position()+ " -> " + sb.get() + ", ");
        print();
        DoubleBuffer db =
          ((ByteBuffer)bb.rewind()).asDoubleBuffer();
        printnb("Double Buffer ");
        while(db.hasRemaining())
          printnb(db.position()+ " -> " + db.get() + ", ");
      }
    } /* Output:
    Byte Buffer 0 -> 0, 1 -> 0, 2 -> 0, 3 -> 0, 4 -> 0, 5 -> 0, 6 -> 0, 7 -> 97,
    Char Buffer 0 ->  , 1 ->  , 2 ->  , 3 -> a,
    Float Buffer 0 -> 0.0, 1 -> 1.36E-43,
    Int Buffer 0 -> 0, 1 -> 97,
    Long Buffer 0 -> 97,
    Short Buffer 0 -> 0, 1 -> 0, 2 -> 0, 3 -> 97,
    Double Buffer 0 -> 4.8E-322,
    *///:~
    
          
  • 字节存放顺序
      • ByteBuffer是以高位优先的顺序存储数据的,并且网上也常常用高位优先的形式
      • 我么可以用带有参数的Byte.Order.BIG_ENDIAN(高位优先)或ByteOrder.LITTLE_ENDIAN(地位优先)的order()方法改变ByteBuffer的存储顺序
    //: io/Endians.java
    // Endian differences and data storage.
    package io;
    import java.nio.*;
    import java.util.*;
    import static net.mindview.util.Print.*;
    
    public class Endians {
      public static void main(String[] args) {
        ByteBuffer bb = ByteBuffer.wrap(new byte[12]);
        bb.asCharBuffer().put("abcdef");
        print(Arrays.toString(bb.array()));
        bb.rewind();
        bb.order(ByteOrder.BIG_ENDIAN);
        bb.asCharBuffer().put("abcdef");
        print(Arrays.toString(bb.array()));
        bb.rewind();
        bb.order(ByteOrder.LITTLE_ENDIAN);
        bb.asCharBuffer().put("abcdef");
        print(Arrays.toString(bb.array()));
      }
    } /* Output:
    [0, 97, 0, 98, 0, 99, 0, 100, 0, 101, 0, 102]
    [0, 97, 0, 98, 0, 99, 0, 100, 0, 101, 0, 102]
    [97, 0, 98, 0, 99, 0, 100, 0, 101, 0, 102, 0]
    *///:~
            

    18.10.4 用缓冲器操纵数据

  • Buffer有数据和可以高效地访问及操纵这些数据的四个索引组成,这四个索引是: mark(标记),limit(界限),position(位置),capacity(容量)

  • cpacity() 返回缓冲器容量
    clear() 清空缓冲器,将postion设置为0
    flip() 将limit设置为position,position设置为0,此方法用于准备从缓冲区读取已经写入的数据
    limit(int lim) 设置limi值
    mark() 将mark设置为position
    position() 返回position的值
    position(int pos) 设置position的值
    remaining() 返回(limit-position)
    hasRemaining() 若有介于position和limit之间的元素,则返回true
       
  • 下面的示例用了一个很简单的算法,对Charbuffer中的字节进行编码和解码

  • //: io/UsingBuffers.java
    package io;
    import java.nio.*;
    import static net.mindview.util.Print.*;
    
    public class UsingBuffers {
      private static void symmetricScramble(CharBuffer buffer){
        while(buffer.hasRemaining()) {
          buffer.mark();
          char c1 = buffer.get();
          char c2 = buffer.get();
          buffer.reset();
          buffer.put(c2).put(c1);
        }
      }
      public static void main(String[] args) {
        char[] data = "UsingBuffers".toCharArray();
        ByteBuffer bb = ByteBuffer.allocate(data.length * 2);
        CharBuffer cb = bb.asCharBuffer();
        cb.put(data);
        print(cb.rewind());
        symmetricScramble(cb);
        print(cb.rewind());
        symmetricScramble(cb);
        print(cb.rewind());
      }
    } /* Output:
    UsingBuffers
    sUniBgfuefsr
    UsingBuffers
    *///:~
    
          

    18.10.5 内存映射文件

  • 内存映射文件允许我们创建和修改那些因为太大而不能放入内存的文件,
  • MappedByteBuffer是一种特殊类型的直接缓冲器
  • MappedByteBuffer继承自ByteBuffer因此具有ByteBuffer的所有方法
  • //: io/LargeMappedFiles.java
    // Creating a very large file using mapping.
    // {RunByHand}
    package io;
    import java.nio.*;
    import java.nio.channels.*;
    import java.io.*;
    import static net.mindview.util.Print.*;
    
    public class LargeMappedFiles {
      static int length = Integer.MAX_VALUE; // 128 MB
      public static void main(String[] args) throws Exception {
        MappedByteBuffer out =
          new RandomAccessFile("test.dat", "rw").getChannel()//为了即能读又能写我们使用了RandomAccessFile,获得了该文件上的通道
          .map(FileChannel.MapMode.READ_WRITE, 0, length);//然后调用map产生MappedByteBuffer
        for(int i = 0; i < length; i++)
          out.put((byte)'x');
        print("Finished writing");
        for(int i = length/2; i < length/2 + 6; i++)
          printnb((char)out.get(i));
      }
    } ///:~
          

    性能

  • 尽管旧的I/O流在用nio实现后性能有所提高,但是"映射文件访问"往往可以更加显著加快速度
  • //: io/MappedIO.java
    package io;
    import java.nio.*;
    import java.nio.channels.*;
    import java.io.*;
    
    public class MappedIO {
      private static int numOfInts = 4000000;
      private static int numOfUbuffInts = 200000;
      private abstract static class Tester {
        private String name;
        public Tester(String name) { this.name = name; }
        public void runTest() {
          System.out.print(name + ": ");
          try {
            long start = System.nanoTime(); //返回 系统计时器的当前值,以毫微秒为单位。
            test();
            double duration = System.nanoTime() - start;
            System.out.format("%.2f\n", duration/1.0e9);
          } catch(IOException e) {
            throw new RuntimeException(e);
          }
        }
        public abstract void test() throws IOException;
      }
      private static Tester[] tests = {
        new Tester("Stream Write") {
          public void test() throws IOException {
            DataOutputStream dos = new DataOutputStream(
              new BufferedOutputStream(
                new FileOutputStream(new File("temp.tmp"))));
            for(int i = 0; i < numOfInts; i++)
              dos.writeInt(i);
            dos.close();
          }
        },
        new Tester("Mapped Write") {
          public void test() throws IOException {
            FileChannel fc =
              new RandomAccessFile("temp.tmp", "rw")
              .getChannel();
            IntBuffer ib = fc.map(
              FileChannel.MapMode.READ_WRITE, 0, fc.size())
              .asIntBuffer();
            for(int i = 0; i < numOfInts; i++)
              ib.put(i);
            fc.close();
          }
        },
        new Tester("Stream Read") {
          public void test() throws IOException {
            DataInputStream dis = new DataInputStream(
              new BufferedInputStream(
                new FileInputStream("temp.tmp")));
            for(int i = 0; i < numOfInts; i++)
              dis.readInt();
            dis.close();
          }
        },
        new Tester("Mapped Read") {
          public void test() throws IOException {
            FileChannel fc = new FileInputStream(
              new File("temp.tmp")).getChannel();
            IntBuffer ib = fc.map(
              FileChannel.MapMode.READ_ONLY, 0, fc.size())
              .asIntBuffer();
            while(ib.hasRemaining())
              ib.get();
            fc.close();
          }
        },
        new Tester("Stream Read/Write") {
          public void test() throws IOException {
            RandomAccessFile raf = new RandomAccessFile(
              new File("temp.tmp"), "rw");
            raf.writeInt(1);
            for(int i = 0; i < numOfUbuffInts; i++) {
              raf.seek(raf.length() - 4);
              raf.writeInt(raf.readInt());
            }
            raf.close();
          }
        },
        new Tester("Mapped Read/Write") {
          public void test() throws IOException {
            FileChannel fc = new RandomAccessFile(
              new File("temp.tmp"), "rw").getChannel();
            IntBuffer ib = fc.map(
              FileChannel.MapMode.READ_WRITE, 0, fc.size())
              .asIntBuffer();
            ib.put(0);
            for(int i = 1; i < numOfUbuffInts; i++)
              ib.put(ib.get(i - 1));
            fc.close();
          }
        }
      };
      public static void main(String[] args) {
        for(Tester test : tests)
          test.runTest();
      }
    } /* Output: (90% match)
    Stream Write: 0.56
    Mapped Write: 0.12
    Stream Read: 0.80
    Mapped Read: 0.07
    Stream Read/Write: 5.32
    Mapped Read/Write: 0.02
    *///:~
          

    18.10.6 文件加锁

    https://qianshangding.iteye.com/blog/2259419

    1. JDk1.4 引入了文件加锁机制,它允许我们同步访问某个作为共享资源的文件
    2. FileLock是独占锁,控制不同程序(JVM)对同一文件的并发访问。
    3. 可以对写文件(w)加锁,而且必须是可写文件,不然回报:java.nio.channels.NonWritableChannelException异常,这样可以保证只有同一个进程才能拿到锁对文件访问。其他进程无法访问改文件,或者删除该文件的目录。
    4. 由于是独占锁,所以可以保证进程间顺序访问该文件,避免数据错误。
    5. FileLock的生命周期,在调用FileLock.release(),或者Channel.close(),或者JVM关闭,则生命周期结束。
FileLock的lock和tryLock只能调用一次,释放后才能继续获得锁。
  • java.io.File.deleteOnExit()在FileLock生命周期结束是,文件被删除,一般会用于临时文件的删除。强制关闭虚拟机,是不会删除文件的
   FileLock tryLock(long position,long size,boolean shared) //非阻塞式
  position:锁定文件中的开始位置
  size: 锁定文件中的内容长度
  shared: 是否使用共享锁。true为共享锁定;false为独占锁定。   

  public FileLock lock(long position, long size, boolean shared)//阻塞式
     
  • 对于一个只读文件通过任意方式加锁时会报NonWritableChannelException异常 无参lock()
  • 默认为独占锁,不会报NonReadableChannelException异常,因为独占就是为了写
  • 有参lock()为共享锁,所谓的共享也只能读共享,写是独占的,共享锁控制的代码只能是读操作,当有写冲突时会报NonWritableChannelException异常
//: io/FileLocking.java
package io;
import java.nio.channels.*;
import java.util.concurrent.*;
import java.io.*;

public class FileLocking {
  public static void main(String[] args) throws Exception {
    FileOutputStream fos= new FileOutputStream("src/ch26/test.txt");
    FileLock fl = fos.getChannel().tryLock();//尝试加锁
    if(fl != null) {
      System.out.println("Locked File");
      TimeUnit.MILLISECONDS.sleep(100);
      fl.release();//关闭锁
      System.out.println("Released Lock");
    }
    fos.close();
  }
} /* Output:
Locked File
Released Lock
*///:~
     

对映射的文件加锁

  • 文件映射通常应用于极大的文件,我么可以部分加锁,就像数据库一样
 
p //: io/LockingMappedFiles.java
// Locking portions of a mapped file.
// {RunByHand}
package io;
import java.nio.*;
import java.nio.channels.*;
import java.io.*;

public class LockingMappedFiles {
  static final int LENGTH = 0x8FFFFFF; // 128 MB
  static FileChannel fc;
  public static void main(String[] args) throws Exception {
    fc =
      new RandomAccessFile("test.txt", "rw").getChannel();
    MappedByteBuffer out =
      fc.map(FileChannel.MapMode.READ_WRITE, 0, LENGTH);
    for(int i = 0; i < LENGTH; i++)
      out.put((byte)'x');
    new LockAndModify(out, 0, 0 + LENGTH/3);
    new LockAndModify(out, LENGTH/2, LENGTH/2 + LENGTH/4);
  }
  private static class LockAndModify extends Thread {
    private ByteBuffer buff;
    private int start, end;
    LockAndModify(ByteBuffer mbb, int start, int end) {
      this.start = start;
      this.end = end;
      mbb.limit(end);
      mbb.position(start);
      buff = mbb.slice();//获得用于修改的slice()
      start();
    }
    public void run() {
      try {
        // Exclusive lock with no overlap:
        FileLock fl = fc.lock(start, end, false);
        System.out.println("Locked: "+ start +" to "+ end);
        // Perform modification:
        while(buff.position() < buff.limit() - 1)
          buff.put((byte)(buff.get() + 1));
        fl.release();
        System.out.println("Released: "+start+" to "+ end);
      } catch(IOException e) {
        throw new RuntimeException(e);
      }
    }
  }
} ///:~
     

18.11 压缩

  • java的压缩类属于InputStream和OutputStream继承层次结构的一部分,而不是从Reader和Writer继承而来
  • 可以用InputstreamReader和OutputStreamWriter在两种类之间进行转换
压缩类功能
CheckedInputStream GetCheckSum() 为任何InputStream产生效验和(不仅是解压缩)
CheckedOutputStream GetCheckSum() 为任何InputStream产生效验和(不仅是压缩)
DeflaterOutputStream 压缩类的基类
ZipOutputStream 一个DeflaterOutputStream, 用于将压缩数据压缩成Zip文件格式
GZIPOutputStream 一个DeflaterOutputStream, 用于将压缩数据压缩成GZIP文件格式
InflaterOutputStream 解压缩类的基类
ZipInputStream 一个InflaterOutputStream, 用于解压缩Zip文件格式的数据
GZIPInputStream 一个InflaterOutputStream, 用于解压缩GZIP文件格式的数据

18.11.1 用GZIP进行简单的压缩

  • GZIP接口适合对单个数据流(而不是一序列互异数据)进行压缩,那么它可能是比较合适的选择
  • 严肃累的使用非常直观--直接将输出流封装成GZIPOutputStream和ZipOutStream,并将输入流封装成GZIPInputstream和ZipInputStream即可
//: io/GZIPcompress.java
// {Args: GZIPcompress.java}
package io;
import java.util.zip.*;
import java.io.*;

public class GZIPcompress {
  public static void main(String[] args)
  throws IOException {
    if(args.length == 0) {
      System.out.println(
        "Usage: \nGZIPcompress file\n" +
        "\tUses GZIP compression to compress " +
        "the file to test.gz");
      System.exit(1);
    }
    BufferedReader in = new BufferedReader(
      new FileReader(args[0]));
    BufferedOutputStream out = new BufferedOutputStream(
      new GZIPOutputStream(
        new FileOutputStream("test.gz")));
    System.out.println("Writing file");
    int c;
    while((c = in.read()) != -1)
      out.write(c);
    in.close();
    out.close();
    System.out.println("Reading file");
    BufferedReader in2 = new BufferedReader(
      new InputStreamReader(new GZIPInputStream(
        new FileInputStream("test.gz"))));
    String s;
    while((s = in2.readLine()) != null)
      System.out.println(s);
  }
} /* (Execute to see output) *///:~    
      

18.11.3 用Zip进行多文件的保存

  • 一共有两种Checksum类型:Adler32(快一些)和CRC32(慢一些,但更准确)
package io;
//: io/ZipCompress.java
// Uses Zip compression to compress any
// number of files given on the command line.
// {Args: ZipCompress.java}
import java.util.zip.*;
import java.io.*;
import java.util.*;
import static net.mindview.util.Print.*;

public class ZipCompress {
  public static void main(String[] args)
  throws IOException {
    FileOutputStream f = new FileOutputStream("test.zip");
    CheckedOutputStream csum =
      new CheckedOutputStream(f, new Adler32());//产生效验和
     ZipOutputStream zos = new ZipOutputStream(csum);//封装成压缩类
     BufferedOutputStream out =
      new BufferedOutputStream(zos);//将压缩流封装成缓冲流
    zos.setComment("A test of Java Zipping");
    // No corresponding getComment(), though.
    for(String arg : args) {
      print("Writing file " + arg);
      BufferedReader in =
        new BufferedReader(new FileReader(arg));
      zos.putNextEntry(new ZipEntry(arg)); //放入一个压缩文件
      int c;
      while((c = in.read()) != -1)
        out.write(c);
      in.close();
      out.flush();
    }
    out.close();
    // Checksum valid only after the file has been closed!
    print("Checksum: " + csum.getChecksum().getValue());
    // Now extract the files:
    print("Reading file");
    FileInputStream fi = new FileInputStream("test.zip");
    CheckedInputStream csumi =
      new CheckedInputStream(fi, new Adler32()); //产生效验和
    ZipInputStream in2 = new ZipInputStream(csumi); //封装成解压缩流
    BufferedInputStream bis = new BufferedInputStream(in2);//封装成缓冲流
    ZipEntry ze;   //定义一个压缩文件
    while((ze = in2.getNextEntry()) != null) {//得到一个压缩文件
      print("Reading file " + ze);
      int x;
      while((x = bis.read()) != -1)//解压缩
        System.out.write(x);
    }
    if(args.length == 1)
    print("Checksum: " + csumi.getChecksum().getValue());//得到 效验值
    bis.close();
    // Alternative way to open and read Zip files:
    ZipFile zf = new ZipFile("test.zip");
    Enumeration e = zf.entries(); //解压缩文件的简单方法 entries()方法产生Enumeration()
    while(e.hasMoreElements()) {
      ZipEntry ze2 = (ZipEntry)e.nextElement(); //
      print("File: " + ze2);
      // ... and extract the data as before
    }
    /* if(args.length == 1) */
  }
} /* (Execute to see output) *///:~
      

18.11.3 java档案文件

  • jar程序可以用命令行的形式使用,压缩文件的格式: jar [options] destination [manifest] inputfile(s)
  • options 只是一个字母的集合,以下这些选项字符在Unix和Linux系统中的tar文件具有相同的意义
c 创建一个新的空的压缩文档
t 列出目录表
x 解压所有文件
x file 解压该文件
f 意指:"我打算指定一个文件名." 如果没有这个选项, jar假设所有的输入都来自于标准输入, 或者在创建一个文件时,输出对象也假设为标注你输出
m 表示第一个参数将是用户自建的清单文件的名字
v 产生详细输出,描述jar所做的工作
0(零) 只存储文件,不压缩文件(用来创建一个可放在类路径中的JAR文件
M 不自动创建清单文件

下面是一些调用jar的典型用法

jar cf myJarFile.jar *.class 该命令创建了一个名为myJarFile.jar的文件,该文件包含了当前目录的所有.class文件,以及自动产生的清单文件
jar cmf myJarFile.jar myManifestFile.mf *.class 该命令与前例类似,但添加了一个名为myManifestFile.mf的用户自建清单文件,注意要先mkdir 一个myManifestFile.mf文件才能运行该命令
jar tf myJarFile.jar 该命令产生myJarFile.jar内所有文件的一个目录表
jar tvf myJarFile.jar 该命令添加了"v"详情标志,可以提供有关myJarFile.jar中的文件的更详细的信息
jar cvf myapp.jar audio classes image 将目录audio classes image 以及其中的文件全部打包到 myapp.jar, 可以用"."将该路径下的所有文件打包
假如用0选项创建这个文件 那么该文件就可以加入类路径变量.
(CLASSPATH)中: CLASSPATH="lib1.jar;lib2.jar;"

18.12 对象的序列化

  • 对象序列化能将对象转换成一个字节序列,并能够在以后将这个对象完全恢复为原来对象,这一过程甚至可以通过网络进行
  • 只要对象实现了Serializable接口(改接口仅是一个标记,不包括任何方法)和(Externalizable接口(改接口可以对序列化过程进行控制),就可以实现该对象的序列化
  • 所有基本类型的封装器,所有容器以及许多其他的东西,甚至Class对象也可以被序列化
  • 序列化一个对象的步骤:创建一个OutputStream对象->将其封装在一个ObjectOutputStream对象内->调用writeObject()实现对象序列化->将序列化对象发送给OutputStream
  • 反向序列化需要将一个InputStram封装在ObjectInputStream内,然后调用readObject(),最后获得一个引用,它指向一个向上转型的Object,所以必须向下转型才能直接设置它们
  • 下面这个例子通过对链接的对象生产一个worm(蠕虫)对序列化机制进行了测试
//: io/Worm.java
package io;
// Demonstrates object serialization.
import java.io.*;
import java.util.*;
import static net.mindview.util.Print.*;

class Data implements Serializable {
  private int n;
  public Data(int n) { this.n = n; }
  public String toString() { return Integer.toString(n); }
}

public class Worm implements Serializable {
  private static Random rand = new Random(47);
  private Data[] d = {
    new Data(rand.nextInt(10)),
    new Data(rand.nextInt(10)),
    new Data(rand.nextInt(10))
  };
  private Worm next;
  private char c;
  // Value of i == number of segments
  public Worm(int i, char x) {
    print("Worm constructor: " + i);
    c = x;
    if(--i > 0)
      next = new Worm(i, (char)(x + 1));
  }
  public Worm() {
    print("Default constructor");
  }
  public String toString() {
    StringBuilder result = new StringBuilder(":");
    result.append(c);
    result.append("(");
    for(Data dat : d)
      result.append(dat);
    result.append(")");
    if(next != null)
      result.append(next);
    return result.toString();
  }
  public static void main(String[] args)
  throws ClassNotFoundException, IOException {
    Worm w = new Worm(6, 'a');
    print("w = " + w);
    ObjectOutputStream out = new ObjectOutputStream(
      new FileOutputStream("worm.out"));
    out.writeObject("Worm storage\n");
    out.writeObject(w);
    out.close(); // Also flushes output
    ObjectInputStream in = new ObjectInputStream(
      new FileInputStream("worm.out"));
    String s = (String)in.readObject();
    Worm w2 = (Worm)in.readObject();
    print(s + "w2 = " + w2);
    ByteArrayOutputStream bout =
      new ByteArrayOutputStream();
    ObjectOutputStream out2 = new ObjectOutputStream(bout);//字节数组
    out2.writeObject("Worm storage\n");
    out2.writeObject(w);
    out2.flush();
    ObjectInputStream in2 = new ObjectInputStream(
      new ByteArrayInputStream(bout.toByteArray()));
    s = (String)in2.readObject();
    Worm w3 = (Worm)in2.readObject();
    print(s + "w3 = " + w3);
  }
} /* Output:
Worm constructor: 6
Worm constructor: 5
Worm constructor: 4
Worm constructor: 3
Worm constructor: 2
Worm constructor: 1
w = :a(853):b(119):c(802):d(788):e(199):f(881)
Worm storage
w2 = :a(853):b(119):c(802):d(788):e(199):f(881)
Worm storage
w3 = :a(853):b(119):c(802):d(788):e(199):f(881)
*///:~
      

18.12.1 寻找类

  • 反序列化对象时,对象的 .class文件必须在ClassPath路径内

18.12.2 序列化的控制

  • Externalizable接口继承了Serializable接口,同时添加了两个方法,writeExternal()和readExternal(),这两个方法会在序列化和反序列化还原的过程中被自动调用,以便执行特殊操作
  • Externalizable对象序列化和反序列化时,首先所有默认构造器都会被调用(包括在字段定义时的初始化),然后调用writeExternal或readExternal()方法.如果默认构造器为private,将不能反序列化
//: io/Blips.java
package io;
// Simple use of Externalizable & a pitfall.
import java.io.*;
import static net.mindview.util.Print.*;

class Blip1 implements Externalizable {
  public Blip1() {
    print("Blip1 Constructor");  //首先调用默认构造器
  }
  public void writeExternal(ObjectOutput out) //然后调用writeExternal或readExternal()方法
      throws IOException {
    print("Blip1.writeExternal");
  }
  public void readExternal(ObjectInput in)
     throws IOException, ClassNotFoundException {
    print("Blip1.readExternal");
  }
}

class Blip2 implements Externalizable {
  Blip2() {
    print("Blip2 Constructor");
  }
  public void writeExternal(ObjectOutput out)
      throws IOException {
    print("Blip2.writeExternal");
  }
  public void readExternal(ObjectInput in)
     throws IOException, ClassNotFoundException {
    print("Blip2.readExternal");
  }
}

public class Blips {
  public static void main(String[] args)
  throws IOException, ClassNotFoundException {
    print("Constructing objects:");
    Blip1 b1 = new Blip1();
    Blip2 b2 = new Blip2();
    ObjectOutputStream o = new ObjectOutputStream(
      new FileOutputStream("Blips.out"));
    print("Saving objects:");
    o.writeObject(b1);
    o.writeObject(b2);
    o.close();
    // Now get them back:
    ObjectInputStream in = new ObjectInputStream(
      new FileInputStream("Blips.out"));
    print("Recovering b1:");
    b1 = (Blip1)in.readObject();
    // OOPS! Throws an exception:
//! print("Recovering b2:");
//! b2 = (Blip2)in.readObject();
  }
} /* Output:
Constructing objects:
Blip1 Constructor
Blip2 Constructor
Saving objects:
Blip1.writeExternal
Blip2.writeExternal
Recovering b1:
Blip1 Constructor
Blip1.readExternal
*///:~
      
  • Externalizable对象的序列化,必须调用writeExternal()方法将重要信息写入,并在恢复时嗲用readExternal()方法读出数据
//: io/Blip3.java
package io;
// Reconstructing an externalizable object.
import java.io.*;
import static net.mindview.util.Print.*;

public class Blip3 implements Externalizable {
  private int i;
  private String s; // No initialization
  public Blip3() {
    print("Blip3 Constructor");
    // s, i not initialized
  }
  public Blip3(String x, int a) {
    print("Blip3(String x, int a)");
    s = x;
    i = a;
    // s & i initialized only in non-default constructor.
  }
  public String toString() { return s + i; }
  public void writeExternal(ObjectOutput out)
  throws IOException {
    print("Blip3.writeExternal");
    // You must do this:
    out.writeObject(s); //注意如果没有这两句, s和i将不会被恢复
    out.writeInt(i);
  }
  public void readExternal(ObjectInput in)
  throws IOException, ClassNotFoundException {
    print("Blip3.readExternal");
    // You must do this:
    s = (String)in.readObject(); //writeExternal 存储的内容,必须在这里恢复
    i = in.readInt();
  }
  public static void main(String[] args)
  throws IOException, ClassNotFoundException {
    print("Constructing objects:");
    Blip3 b3 = new Blip3("A String ", 47);
    print(b3);
    ObjectOutputStream o = new ObjectOutputStream(
      new FileOutputStream("Blip3.out"));
    print("Saving object:");
    o.writeObject(b3);
    o.close();
    // Now get it back:
    ObjectInputStream in = new ObjectInputStream(
      new FileInputStream("Blip3.out"));
    print("Recovering b3:");
    b3 = (Blip3)in.readObject();
    print(b3);
  }
} /* Output:
Constructing objects:
Blip3(String x, int a)
A String 47
Saving object:
Blip3.writeExternal
Recovering b3:
Blip3 Constructor
Blip3.readExternal
A String 47
*///:~
      
  • transient(瞬时)关键字
  • 对Serializalbe对象序列化时,如果有些特殊对象不想序列化,可以用transient关键字关闭特殊序列化,
//: io/Logon.java
package io;
// Demonstrates the "transient" keyword.
import java.util.concurrent.*;
import java.io.*;
import java.util.*;
import static net.mindview.util.Print.*;

public class Logon implements Serializable {
  private Date date = new Date();
  private String username;
  private transient String password; //该对象被transient 标记后不能被序列化
  public Logon(String name, String pwd) {
    username = name;
    password = pwd;
  }
  public String toString() {
    return "logon info: \n   username: " + username +
      "\n   date: " + date + "\n   password: " + password;
  }
  public static void main(String[] args) throws Exception {
    Logon a = new Logon("Hulk", "myLittlePony");
    print("logon a = " + a);
    ObjectOutputStream o = new ObjectOutputStream(
      new FileOutputStream("Logon.out"));
    o.writeObject(a);
    o.close();
    TimeUnit.SECONDS.sleep(1); // Delay
    // Now get them back:
    ObjectInputStream in = new ObjectInputStream(
      new FileInputStream("Logon.out"));
    print("Recovering object at " + new Date());
    a = (Logon)in.readObject();
    print("logon a = " + a);
  }
} /* Output: (Sample)
logon a = logon info:
   username: Hulk
   date: Sat Nov 19 15:03:26 MST 2005
   password: myLittlePony
Recovering object at Sat Nov 19 15:03:28 MST 2005
logon a = logon info:
   username: Hulk
   date: Sat Nov 19 15:03:26 MST 2005
   password: null
*///:~      
      
  • Externalizable的替代方法
  • 通过实现Serializable接口,并添加writeObject()和readObject()方法,可以实现Externalizable和Serializable两个接口的功能
//: io/SerialCtl.java
package io;
// Controlling serialization by adding your own
// writeObject() and readObject() methods.
import java.io.*;

public class SerialCtl implements Serializable {
  private String a;
  private transient String b;//这里标记为不默认序列化
  public SerialCtl(String aa, String bb) {
    a = "Not Transient: " + aa;
    b = "Transient: " + bb;
  }
  public String toString() { return a + "\n" + b; }
  private void writeObject(ObjectOutputStream stream)
  throws IOException {
    stream.defaultWriteObject();
    stream.writeObject(b);//这里序列化了b
  }
  private void readObject(ObjectInputStream stream)
  throws IOException, ClassNotFoundException {
    stream.defaultReadObject();
    b = (String)stream.readObject();
  }
  public static void main(String[] args)
  throws IOException, ClassNotFoundException {
    SerialCtl sc = new SerialCtl("Test1", "Test2");
    System.out.println("Before:\n" + sc);
    ByteArrayOutputStream buf= new ByteArrayOutputStream();
    ObjectOutputStream o = new ObjectOutputStream(buf);
    o.writeObject(sc);
    // Now get it back:
    ObjectInputStream in = new ObjectInputStream(
      new ByteArrayInputStream(buf.toByteArray()));
    SerialCtl sc2 = (SerialCtl)in.readObject();
    System.out.println("After:\n" + sc2);
  }
} /* Output:
Before:
Not Transient: Test1
Transient: Test2
After:
Not Transient: Test1
Transient: Test2
*///:~      
      
  • 版本控制

18.12.3 使用"持久性"

    • 如果两个序列化对象指向向第三个引用,或两个序列化对象保存为不同的文件,在恢复时会产生不同的行为
//: io/MyWorld.java
package io;
import java.io.*;
import java.util.*;
import static net.mindview.util.Print.*;

class House implements Serializable {}

class Animal implements Serializable {
  private String name;
  private House preferredHouse;
  Animal(String nm, House h) {
    name = nm;
    preferredHouse = h;
  }
  public String toString() {
    return name + "[" + super.toString() +
      "], " + preferredHouse + "\n";
  }
}

public class MyWorld {
  public static void main(String[] args)
  throws IOException, ClassNotFoundException {
    House house = new House();
    List animals = new ArrayList();
    animals.add(new Animal("Bosco the dog", house));
    animals.add(new Animal("Ralph the hamster", house));
    animals.add(new Animal("Molly the cat", house));
    print("animals: " + animals);
    ByteArrayOutputStream buf1 =
      new ByteArrayOutputStream();
    ObjectOutputStream o1 = new ObjectOutputStream(buf1);
    o1.writeObject(animals);
    o1.writeObject(animals); // Write a 2nd set
    // Write to a different stream:
    ByteArrayOutputStream buf2 =
      new ByteArrayOutputStream();
    ObjectOutputStream o2 = new ObjectOutputStream(buf2);
    o2.writeObject(animals);
    // Now get them back:
    ObjectInputStream in1 = new ObjectInputStream(
      new ByteArrayInputStream(buf1.toByteArray()));
    ObjectInputStream in2 = new ObjectInputStream(
      new ByteArrayInputStream(buf2.toByteArray()));
    List
      animals1 = (List)in1.readObject(),
      animals2 = (List)in1.readObject(),
      animals3 = (List)in2.readObject();
    print("animals1: " + animals1);
    print("animals2: " + animals2);
    print("animals3: " + animals3); //生成不同的对象
  }
} /* Output: (Sample)
animals: [Bosco the dog[Animal@addbf1], House@42e816
, Ralph the hamster[Animal@9304b1], House@42e816
, Molly the cat[Animal@190d11], House@42e816
]
animals1: [Bosco the dog[Animal@de6f34], House@156ee8e
, Ralph the hamster[Animal@47b480], House@156ee8e
, Molly the cat[Animal@19b49e6], House@156ee8e
]
animals2: [Bosco the dog[Animal@de6f34], House@156ee8e
, Ralph the hamster[Animal@47b480], House@156ee8e
, Molly the cat[Animal@19b49e6], House@156ee8e
]
animals3: [Bosco the dog[Animal@10d448], House@e0e1c6
, Ralph the hamster[Animal@6ca1c], House@e0e1c6
, Molly the cat[Animal@1bf216a], House@e0e1c6
]
*///:~        
        
  • 只要将任何对象序列化到单一流中,就可以恢复出与我们写出时一样的对象网
  • 将构成系统状态的所有对象都置入单一容器内,并在一个操作中将该容器直接写出,然后只需一次方法调用,即可将其写出
  • static数据不会被自动初始化,必须手动去实现
  • 所有Classs类都是Serializable的,可以直接序列化
//: io/StoreCADState.java
package ch30;
import java.io.*;
import java.util.*;

abstract class Shape implements Serializable {
  public static final int RED = 1, BLUE = 2, GREEN = 3;
  private int xPos, yPos, dimension;
  private static Random rand = new Random(47);
  private static int counter = 0;
  public abstract void setColor(int newColor);
  public abstract int getColor();
  public Shape(int xVal, int yVal, int dim) {
    xPos = xVal;
    yPos = yVal;
    dimension = dim;
  }
  public String toString() {
    return getClass() +
      "color[" + getColor() + "] xPos[" + xPos +
      "] yPos[" + yPos + "] dim[" + dimension + "]\n";
  }
  public static Shape randomFactory() {
      int xVal = rand.nextInt(100);
      int yVal = rand.nextInt(100);
      int dim = rand.nextInt(100);
      switch(counter++ % 3) {
        default:
        case 0: return new Circle(xVal, yVal, dim);
        case 1: return new Square(xVal, yVal, dim);
        case 2: return new Line(xVal, yVal, dim);
      }
    }
  }

class Circle extends Shape {
  private static int color = RED;
  public Circle(int xVal, int yVal, int dim) {
    super(xVal, yVal, dim);
  }
  public void setColor(int newColor) { color = newColor; }
  public int getColor() { return color; }
  public static void serializeStaticState(ObjectOutputStream os)throws IOException{
    os.writeInt(color);
  }
  public static void serializeStaticState(ObjectInputStream os)throws IOException{
    color = os.readInt();
  }
}

class Square extends Shape {
  private static int color;
  public Square(int xVal, int yVal, int dim) {
    super(xVal, yVal, dim);
    color = RED;
  }
  public void setColor(int newColor) { color = newColor; }
  public int getColor() { return color; }
  public static void serializeStaticState(ObjectOutputStream os)throws IOException{
    os.writeInt(color);
  }
  public static void serializeStaticState(ObjectInputStream os)throws IOException{
    color = os.readInt();
  }
}

class Line extends Shape {
  private static int color = RED;
  public static void
  serializeStaticState(ObjectOutputStream os)
  throws IOException { os.writeInt(color); }
  public static void
  deserializeStaticState(ObjectInputStream os)
  throws IOException { color = os.readInt(); }
  public Line(int xVal, int yVal, int dim) {
    super(xVal, yVal, dim);
  }
  public void setColor(int newColor) { color = newColor; }
  public int getColor() { return color; }
}

public class StoreCADState {
  public static void main(String[] args) throws Exception {
    /*List<Class<? extends Shape>> shapeTypes =
      new ArrayList<Class<? extends Shape>>();
    // Add references to the class objects:
    shapeTypes.add(Circle.class);
    shapeTypes.add(Square.class);
    shapeTypes.add(Line.class); */
    List shapes = new ArrayList();
    // Make some shapes:
    for(int i = 0; i < 10; i++)
      shapes.add(Shape.randomFactory());
    // Set all the static colors to GREEN:
    for(int i = 0; i < 10; i++)
      ((Shape)shapes.get(i)).setColor(Shape.GREEN);
    // Save the state vector:
    ObjectOutputStream out = new ObjectOutputStream(
      new FileOutputStream("CADState.out"));
    //out.writeObject(shapeTypes);
    Circle.serializeStaticState(out);//必须为每个类的static数据单独序列化
    Square.serializeStaticState(out);
    Line.serializeStaticState(out);
    out.writeObject(shapes);//序列化普通数据
    // Display the shapes:
    System.out.println(shapes);
    
    
    ObjectInputStream in = new ObjectInputStream(
            new FileInputStream("CADState.out"));
          // Read in the same order they were written:
          //List<Class<? extends Shape>> shapeTypes =
           // (List<Class<? extends Shape>>)in.readObject();
          Circle.serializeStaticState(in);
          Square.serializeStaticState(in);
          Line.deserializeStaticState(in);
          shapes = (List)in.readObject();
          System.out.println(shapes);
  }
} /* Output:
[class io.Circlecolor[3] xPos[58] yPos[55] dim[93]
, class io.Squarecolor[3] xPos[61] yPos[61] dim[29]
, class io.Linecolor[3] xPos[68] yPos[0] dim[22]
, class io.Circlecolor[3] xPos[7] yPos[88] dim[28]
, class io.Squarecolor[3] xPos[51] yPos[89] dim[9]
, class io.Linecolor[3] xPos[78] yPos[98] dim[61]
, class io.Circlecolor[3] xPos[20] yPos[58] dim[16]
, class io.Squarecolor[3] xPos[40] yPos[11] dim[22]
, class io.Linecolor[3] xPos[4] yPos[83] dim[6]
, class io.Circlecolor[3] xPos[75] yPos[10] dim[42]
]
[class io.Circlecolor[3] xPos[58] yPos[55] dim[93]
, class io.Squarecolor[3] xPos[61] yPos[61] dim[29]
, class io.Linecolor[3] xPos[68] yPos[0] dim[22]
, class io.Circlecolor[3] xPos[7] yPos[88] dim[28]
, class io.Squarecolor[3] xPos[51] yPos[89] dim[9]
, class io.Linecolor[3] xPos[78] yPos[98] dim[61]
, class io.Circlecolor[3] xPos[20] yPos[58] dim[16]
, class io.Squarecolor[3] xPos[40] yPos[11] dim[22]
, class io.Linecolor[3] xPos[4] yPos[83] dim[6]
, class io.Circlecolor[3] xPos[75] yPos[10] dim[42]
]

*///:~
      

18.13 XML

  • Elliotte Rusty Harold 的开源XOM类库网址:www.xom.nu
//序列化xml
//: xml/Person.java
// Use the XOM library to write and read XML
// {Requires: nu.xom.Node; You must install
// the XOM library from http://www.xom.nu }
package xml;
import nu.xom.*;
import java.io.*;
import java.util.*;

public class Person {
  private String first, last;
  public Person(String first, String last) {
    this.first = first;
    this.last = last;
  }
  // Produce an XML Element from this Person object:
  public Element getXML() {
    Element person = new Element("person");
    Element firstName = new Element("first");
    firstName.appendChild(first);
    Element lastName = new Element("last");
    lastName.appendChild(last);
    person.appendChild(firstName);
    person.appendChild(lastName);
    return person;
  }
  // Constructor to restore a Person from an XML Element:
  public Person(Element person) {
    first= person.getFirstChildElement("first").getValue();
    last = person.getFirstChildElement("last").getValue();
  }
  public String toString() { return first + " " + last; }
  // Make it human-readable:
  public static void
  format(OutputStream os, Document doc) throws Exception {
    Serializer serializer= new Serializer(os,"ISO-8859-1");//第二个属性为字符编码
    serializer.setIndent(4); //左边缩进
    serializer.setMaxLength(60); //换行的设置
    serializer.write(doc);
    serializer.flush();
  }
  public static void main(String[] args) throws Exception {
    List people = Arrays.asList(
      new Person("Dr. Bunsen", "Honeydew"),
      new Person("Gonzo", "The Great"),
      new Person("Phillip J.", "Fry"));
    System.out.println(people);
    Element root = new Element("people");
    for(Person p : people)
      root.appendChild(p.getXML());
    Document doc = new Document(root);
    format(System.out, doc);
    format(new BufferedOutputStream(new FileOutputStream(
      "People.xml")), doc);
  }
} /* Output:
[Dr. Bunsen Honeydew, Gonzo The Great, Phillip J. Fry]


    
        Dr. Bunsen
        Honeydew
    
    
        Gonzo
        The Great
    
    
        Phillip J.
        Fry
    

*///:~
     
  • 反序列化xml
//: xml/People.java
// {Requires: nu.xom.Node; You must install
// the XOM library from http://www.xom.nu }
// {RunFirst: Person}
package xml;
import nu.xom.*;

import java.io.File;
import java.io.FileReader;
import java.nio.file.Path;
import java.util.*;

public class People extends ArrayList {
  public People(String fileName) throws Exception  {
    Document doc = new Builder().build(fileName);//打开一个xml文档
    Elements elements =
      doc.getRootElement().getChildElements();//得到元素列表
    for(int i = 0; i < elements.size(); i++)
      add(new Person(elements.get(i)));  //反序列化
  }
  public static void main(String[] args) throws Exception {
  Path path = new File("people.xml").toPath();
  String s = path.toAbsolutePath().toString();
  System.out.println(s);
    People p = new People("file:\\" + s); //注意这里的路径好复杂
    System.out.println(p);
  }
} /* Output:
[Dr. Bunsen Honeydew, Gonzo The Great, Phillip J. Fry]
*///:~
     

18.14 Preferences

  • Preferences是一个键-值集合(类似映射),存储在一个节点层次结构中.
  • Preferences通常用于存储和读取用户的偏好(preferences)以及程序配置项的设置:用户偏好用userNodeForPackage(),系统配置用systemNodeForPackage()
  • Java-Preferences用法-入门 https://www.cnblogs.com/pzy4447/p/4710539.html
Properties提供的应用程序解决方案主要存在两个问题:

(1)配置文件不能放在主目录中,因为某些OS(如Win9X)没有主目录的概念;

(2)没有标准的文件命名规则,存在文件名冲突的可能性。

Java中的Preferences类可以解决这些问题。Preferences提供一个存储配置信息的中心知识库,与平台无关。在Windows系统中,它存储在注册表中,在Linux中存储在本地文件系统中。它的实现是透明的,程序员无需深究它的底层是如何实现的。

Preferences的中心知识库是树状结构,因此可以避免文件名冲突。每个用户都有一棵树,存放与本用户有关的配置;还有一个系统树,存放全体用户的公共信息。内部的配置信息仍然以key-value的结构进行存储。

Preferences的使用步骤如下:

(1)获得根节点

Preferences root = Preferences.userRoot();

Preferences root = Preferences.systemRoot();

如果配置信息位于用户树,则获取用户树的根节点,否则获取系统树根节点;

(2)获取配置节点

preferences = root.node("path");

path是配置节点相对于根节点的路径;

如果节点的路径名与类的包名相同,则可通过类的对象直接获得配置节点:

Preferences node = Preferences.userNodeForPackage(this.getClass());
Preferences node = Preferences.systemNodeForPackage(this.getClass());

(3)读取配置项

String title =  preferences.get("title", "default title");

Preferences要求读取配置项时必须指定默认值。因为在实际环境中总会有各种不如意,比如系统中还没有中心知识库,或者网络暂时不可用等等。

(4)设置配置项

preferences.put(key, value);

(5)同步配置项

preferences.flush();

flush()方法用于立即将配置项写入到文件中
//: io/PreferencesDemo.java
package io;
import java.util.prefs.*;
import static net.mindview.util.Print.*;

public class PreferencesDemo {
  public static void main(String[] args) throws Exception {
    Preferences prefs = Preferences
      .userNodeForPackage(PreferencesDemo.class);
    prefs.put("Location", "Oz"); //
    prefs.put("Footwear", "Ruby Slippers");
    prefs.putInt("Companions", 4);
    prefs.putBoolean("Are there witches?", true);
    int usageCount = prefs.getInt("UsageCount", 0);//得到PreferncesDemo.class对象被使用的次数
    usageCount++;
    prefs.putInt("UsageCount", usageCount);
    for(String key : prefs.keys())//
      print(key + ": "+ prefs.get(key, null));//如果没有key则输出null
    // You must always provide a default value:
    print("How many companions does Dorothy have? " +
      prefs.getInt("Companions", 0));
    prefs.flush();
  }
} /* Output: (Sample)
Location: Oz
Footwear: Ruby Slippers
Companions: 4
Are there witches?: true
UsageCount: 53
How many companions does Dorothy have? 4
*///:~      
      
posted @ 2019-03-05 09:27  江期玉  阅读(269)  评论(0编辑  收藏  举报