Thinking in Java 18 JavaIO

18.1 File类(better be FilePath )

用作是系统输入资源(读取文件内容)和输出的目的地(写入文件)。

18.1.1 目录列表器

package Ch18;
import java.io.*;
import java.util.*;
import java.util.regex.*;

public class DirList {
    public static void main(String[] args) {
        Integer[] tasks =new Integer[]{1,2,3};
        Arrays.sort(tasks);
        File path = new File(".");
        String[] list;
        if(args.length==0)
            list = path.list();
        else
          //目录过滤
          list = path.list(new DirFilter(args[0]));
        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();
    }
}
/*
public interface FilenameFilter {
    boolean accept(File dir, String name) ;
}
*/
// 匿名内部类形式
import java.io.*;
import java.util.*;
import java.util.regex.*;

public class DirList {
  // args 为final
    public static void main(final String[] args) {

        File path = new File("../7-zip");
        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);
    }
}

18.1.2 目录实用工具

package Ch18;

import net.mindview.util.PPrint;
import java.io.File;
import java.io.FilenameFilter;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.regex.Pattern;

public final class Directory {
  //local不迭代,该目录下第一级
    public static File[]
    local(File dir, final String regex){
        return dir.listFiles(new FilenameFilter(){
            private Pattern pattern = Pattern.compile(regex);
            public boolean accept(File dir, String name){
                return pattern.matcher(name).matches();
            }
        });
 }

  public static File[]
    local(String path, final String regex){
    return local(new File(path), regex);
  }
// 用于保存文件的元组(包括 目录(也是文件)和非目录文件)
  public static class TreeInfo implements Iterable<File>{
    public List<File> files = new ArrayList<>();
    public List<File> dirs = new ArrayList<>();
    public Iterator<File> iterator(){
      return files.iterator();
    }
    void addAll(TreeInfo other){
      files.addAll(other.files);
      dirs.addAll(other.dirs);
    }
    public String toString(){
    // PPrint打印方式
      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(String start) {//Everything
    return recurseDirs(new File(start),".+");
  }
    //实际遍历工具
  static TreeInfo recurseDirs(File startDir, String regex){
    TreeInfo result = new TreeInfo();
    for(File item : startDir.listFiles()) {
      // 目录 1,添加目录 2.执行迭代
      if(item.isDirectory()) {
        result.dirs.add(item);
        result.addAll(recurseDirs(item, regex));
      }// 文件就只加入文件
      else if(item.getName().matches(regex)){
        result.files.add(item);
      }
    }
    return result;
  }

  public static void main(String[] args) {
    if(args.length==0)
      System.out.println(walk("."));
    else
      for (String arg : args)
        System.out.println(walk(arg));
  }
}

//打印工具类
package net.mindview.util;
import java.util.*;

public class PPrint {
  public static String pformat(Collection<?> c) {
    if(c.size() == 0) return "[]";
    StringBuilder result = new StringBuilder("[");
    for(Object elem : c) {
      if(c.size() != 1)
        result.append("\n  ");
      result.append(elem);
    }
    if(c.size() != 1)
      result.append("\n");
    result.append("]");
    return result.toString();
  }
  public static void pprint(Collection<?> c) {
    System.out.println(pformat(c));
  }
  public static void pprint(Object[] c) {
    System.out.println(pformat(Arrays.asList(c)));
  }
}
package Ch18;

import java.io.File;
import java.io.IOException;

public class ProcessFiles {
    public interface Strategy{
        void process(File file);
    }
  // 策略模式
    private Strategy strategy;
    private String ext;
    public ProcessFiles(Strategy stratege, String ext){
        this.strategy = stratege;
        this.ext =ext;
    }

    public void start(String[] args){
        try{
            if(args.length == 0)
                processDirectoryTree(new File("."));
            else
                for(String arg : args) {
                    File fileArg = new File(arg);
                    if(fileArg.isDirectory())
                        processDirectoryTree(fileArg);
                    else{
                        if(!arg.endsWith("."+ext))
                            arg+="."+ext;
                        strategy.process(
                            new File(arg).getCanonicalFile());
                        }
                    }
                }
        catch(IOException e){
            throw new RuntimeException(e);
        }
    }
    public void
    processDirectoryTree(File root) throws IOException{
        for(File file : Directory.walk(root.getAbsolutePath(),".*\\."+ext))
            strategy.process(file.getCanonicalFile());
    }

    public static void main(String[] args) {
        new ProcessFiles(
          new ProcessFiles.Strategy(){
            public void process(File file){
                System.out.println(file);
            }},
          "java").
          start(args);
    }
}

18.1.3 目录的检查和创建

18.2 输入和输出

继承Inputstream和Reader的类都有read()方法,读取单个字节或者字节数组。

继承Outputstream和Writer的类都有read()方法,写单个字节或者字节数组。

流模型: 将对资源的写入和读取的路径,抽象为流这种接口,提供给我们使用。

输入、输出的角度不是资源,是使用资源的对象。

输入,read,将资源读取出来。

输出,write,将修改写入资源。

InputStream类型

功能
ByteArrayInputStream允许将内存的缓冲当作InputStream使用
StringBufferInputStream(弃用)将String转换为InputStream
FileInputStream从文件中读取信息
PipedInputStream
SequenceInputStream将多个InputStream转换为一个InputStream
FilterInputStream抽象类,作为装饰器模式,为其他InputStream提供有用功能

Output类型

功能
ByteArrayOutputStream在内存中创建缓冲区,所有送往流的数据在此缓冲
FileOutputStream将信息写至OutputStream
PipedOutputStream
FilterOutputStream

18.3 添加属性和有用的接口

FilterInputStream 和 FileterOutputStream类型

功能
DataInputStream与DataOutputStream搭配使用
BufferedInputStream代表使用缓冲区。
功能
DataOutputStream
PrintStream格式化输出,DataOutputStream处理数据的存储,PrintStreamc处理显示
BufferedOutputStream使用缓冲,flush清空缓冲区

18.4 Reader 和 writer

Reader和Writer并不是来代替InputStream和OutputStream(再面向字节形式的I/O中仍可以提供极有价值的功能)。Reader and Writer则提供兼容Unicode(用于字符国际化)和面向字符的I/O功能。

Java 1.0Java 1.1
InputStreamReader
OutputStreamWriter
FileInputStreamFileReader
FileOutputStreamFileWriter
StringBufferInputStream(已经弃用)StringReader
没有对应类StringWriter
ByteArrayInputStreamCharArrayReader
ByteArrayOutputStreamCharArrayWriter
PipedInputStreamPipedReader
PipedOutputStreamPepedWriter

装饰器改变

FilterInputStreamFilterReader
FilterOuputStreamFilterWriter
BufferedInputStreamBufferedReader
BufferedOutputStreamBufferedWriter
DataInputStream??使用readLine()时,不能使用这个类,使用BufferedWriter
PrintStreamPrintWriter
LineNumberInputStreamLineNumberReader
StreamTokenizerStreamTokenizer
PushbackInputStreamPushbackReader

未发生变化

DataOutputStream
File
ReandomAccessFile
SequenceInputStream

RandomAccessFile:自我独立类

I/O流的典型使用方法

缓冲输入文件

package Ch18;

import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
public class BufferedInputFile {
    public static String read(String filename) throws IOException{
      // FileReader读取文件。BufferedReader提供缓冲。
        BufferedReader in = new BufferedReader(new FileReader(filename));
        String s;
        StringBuilder sb = new StringBuilder();
      //BufferedReader提供readLine方法
        while((s=in.readLine())!=null)
            sb.append(s+"\n");
        in.close();
        return sb.toString();
    }

    public static void main(String[] args) throws IOException{
 //使用绝对地址,变成两斜杠,表达转义?  
      //当前工作空间
     System.out.println( System.getProperty("user.dir")); System.out.println(read("src\\Ch18\\BufferedI nputFile.java"));
    }
}

从内存输入

package Ch18;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.StringReader;

public class MemoryInput {
    public static void main(String[] args) throws IOException{
        StringReader in =new StringReader(BufferedInputFile.read("src\\Ch18\\" +
                "MemoryInput.java"));
        int c;
        while((c = in.read())!=-1){
            System.out.print((char)c);
        }
    }
}

格式化的内存输入

读取格式化的数据,可以使用DataInputStream

package Ch18;
import java.io.*;

public class FormattedMemoryInput {
    public static void main(String[] args) throws IOException{
        try{
            DataInputStream in = new DataInputStream(new ByteArrayInputStream(BufferedInputFile.read("src\\Ch18\\FormattedMemoryInput.java").getBytes()));
            while (true)
                System.out.print((char)in.readByte());
        //使用异常进行流控制,被认为是对异常特性的错误使用。
        }catch (EOFException e){
            System.out.println("End of stream");
        }
    }

}

package Ch18;
import java.io.*;

public class TestEOF {
    public static void main(String[] args) throws IOException {
        DataInputStream in = new DataInputStream(new BufferedInputStream(new FileInputStream("src\\Ch18\\TestEOF.java")));
      //readByte读取的任何字节都是合法结果,不能用作判断流结束。
      //使用avaliable。avaliable的工作方式会随着所读取的媒介类型不同而不同。
        while(in.available()!=0)
            System.out.print((char)in.readByte());
    }

}

18.6.4 基本的文件输出

//完成读取一个文件,添加行号后,输出生成一个新文件。
package Ch18;

import java.io.*;

public class BasicFileOutput {
  // 用于生成输出文件地址。只有文件名,则存在默认工作空间。
    static String file = "src\\Ch18\\BasicFileOutput.out";

    public static void main(String[] args) throws IOException{
      //包装为缓冲流可以提高效率。
        BufferedReader in = new BufferedReader(new StringReader(BufferedInputFile.read("src\\Ch18\\BasicFileOutput.java")));
      //先创建与指定文件连接的FileWriter,然后包装为缓冲流,最后提供格式化机制,装饰为PrintWriter.可以当作普通文件读取。
        PrintWriter out = new PrintWriter(new BufferedWriter(new FileWriter(file)));
      //记录行号
        int lineCount =1;
        String s;
        while ((s = in.readLine()) != null)
          //平常使用的System.out是,System类的静态成员变量out,输出到控制台。这里out底层是指定的文件,println就输出到文件中。不是打印到控制台。
            out.println(lineCount++ +":"+s);
      //关闭流
        out.close();
        System.out.println(BufferedInputFile.read(file));
    }
}
文本文件输出的快捷方式
//PrintWriter中有一个辅助构造器,使得创建文本文件并向其中写入时,默认执行所有装饰工作。
package Ch18;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.StringReader;

public class FileOutputShortcut {
    static String file = "src\\Ch18\\BasicFileOutput.out";

    public static void main(String[] args) throws IOException{
        BufferedReader in = new BufferedReader(new StringReader(BufferedInputFile.read("src\\Ch18\\FileOutputShortcut.java")));
        PrintWriter out = new PrintWriter(file);
        int lineCount = 1;
        String s;
        while((s=in.readLine())!=null)
            out.println(lineCount++ +": "+s);
        out.close();
        System.out.println(BufferedInputFile.read(file));
    }
}

18.6.5 存储和恢复数据

PrintWriter可以对数据进行格式化,方便人们阅读。

但是为了输出一个可供另外一个流恢复的数据,要使用DataOutputStream写入数据,Java保证可以使用DataInputStream恢复数据,无论读写数据的平台多么不同。

package Ch18;

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");
        out.close();
        DataInputStream in = new DataInputStream(new BufferedInputStream(new FileInputStream("Data.txt")));
        System.out.println(in.readDouble());
        System.out.println(in.readUTF());
        System.out.println(in.readDouble());
        System.out.println(in.readUTF());

    }
}
/*3.14159
That was pi
1.41413
Square root of 2*/

18.6.6 随机访问文件

RandomAccessFile实现了DataInput和DataOutput接口。有效地与I/O继承层次的其他部分实现了分离。不支持装饰,所以不能将其与InputStream以及OutputStream子类任何部分组合使用。必须假设已经正确缓冲。

package Ch18;

import java.io.IOException;
import java.io.RandomAccessFile;

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");
      //double是8字节长,所以5*8找到第五个数据。
        rf.seek(5*8);
      //重写,覆盖这个数据
        rf.writeDouble(47.001);
        rf.close();
        display();
    }
}
/*
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.001
Value 6: 8.484
The end of the file*/

18.6.7 管道流

18.7 文件读写的实用工具

package Ch18;

import java.io.*;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.TreeSet;

public class TextFile extends ArrayList<String> {
  // read读取文件,返回文件内容构成的字符。
  // 读取文件找不到时,会出异常。而写文件时,找不到会新建目标文件。
    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();
    }
//静态方法,将text内容写到fileName文件中中
    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);
        }
    }
//新建TextFile文件,按照指定正则表达式对fileName文件内容修改。
    public TextFile(String fileName, String splitter) {
        super(Arrays.asList(read(fileName).split(splitter)));
        if(get(0).equals("")) remove(0);
    }
新建TextFile文件,按照默认正则表达式对fileName文件内容修改。
    public TextFile(String fileName){
        this(fileName, "\n");
    }
//非静态方法,将对象的内容写入指定文件。
    public void write(String fileName) {
        try{
          //write找不到会新建文件
            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);
        }
    }
    //简单测试
    public static void main(String[] args) {
        String file = read("src\\Ch18\\TextFile.java");
        write("test.txt", file);
        TextFile text = new TextFile("test.txt");
        text.write("test2.txt");
        TreeSet<String> words = new TreeSet<>(new TextFile("src\\Ch18\\TextFile.java", "\\W+"));
        System.out.println(words.headSet("a"));

    }

}
/*[0, ArrayList, Arrays, BufferedReader, Ch18, File, FileReader, IOException, PrintWriter, RuntimeException, String, StringBuilder, System, TextFile, TreeSet, W]*/

18.7.1 读取二进制文件

package Ch18;

import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;

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

程序所使用的单一信息流。标准输入,标准输出,标准错误。

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

18.1.1 从标准输入中读取

System.in,System.out,System.err

out and err 被包装成了PrintStream,in却是一个没有包装的InputStream,读取System.in之前必须对其进行包装。

package Ch18;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
//回显你输入的每一行
public class Echo {
    public static void main(String[] args) throws IOException{
      // InputStreamReader System.in转化为Reader。
      //通常是readLine一行一行读取。进一步包装为BufferedReader
        BufferedReader stdin = new BufferedReader(new InputStreamReader(System.in));
        String s;
        while((s = stdin.readLine())!=null && s.length()!=0){
            System.out.println(s);
        }

    }
}

18.2.2 将转化为PrintWrtier

package Ch18;

import java.io.PrintWriter;

public class ChangeSystemOut {
    public static void main(String[] args) {
      //PrintWriter(par1,par2),par1接受OutoutStream作为参数,par2设置为true,开启自动清空功能。
        PrintWriter out = new PrintWriter(System.out, true);
        out.println("Hello World" );
    }

18.8.3 标准I/O重定向

package Ch18;

import java.io.*;

public class Redirecting {
    public static void main(String[] args) throws IOException{
      //存储标准输出,方便后面重定向回来。
        PrintStream console = System.out;
      //读文件,先使用FileInputStream,再装饰为BufferedInputStream,提高效率。
        BufferedInputStream in = new BufferedInputStream(new FileInputStream("src\\Ch18\\Redirecting.java"));
        PrintStream out = new PrintStream(new BufferedOutputStream(new FileOutputStream("test.out")));
      //所谓重定向就是将标准输入输出的对象更改。
      //I/O重定向操作的是字节流,而不是字符流,在装饰为InputStream之前,重定向。
        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后,重定向回原来out,仍可继续输出。
      //close后,out将无法再输出,后面的输出语句没有用。
        out.close();
      //没有重定向回标准输出,输出将继续再当前文件。
        System.setOut(console);

        BufferedReader red = new BufferedReader(new InputStreamReader(new FileInputStream("src\\\\Ch18\\\\Redirecting.java")));
    //没有重定向回标准输出,将在当前out中继续输出,使得文件有两份相同代码。
      //重定向回控制台后,将在控制台打印代码。
        while((s=red.readLine())!=null)
            System.out.println(s);
      //没有重定向回控制台,不close,当前输出生成的文件里,没有内容。
        out.close();
/*
package Ch18;
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\\Ch18\\Redirecting.java"));
        PrintStream out = new PrintStream(new BufferedOutputStream(new FileOutputStream("test.out")));

    }
}*/
    }
}

18.9 进程控制

package Ch18;

public class OSExecuteException extends RuntimeException{
    public OSExecuteException(String why){ super(why);}
}

package Ch18;

import java.io.BufferedReader;
import java.io.InputStreamReader;
//实用工具类
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()));
            while( (s = errors.readLine())!=null){
                System.err.println(s);
                err = true;
            }
        }catch (Exception e){
            if(!command.startsWith("CMD /C"))
                command("CMD /C"+command);
            else
                throw new RuntimeException(e);
        }
        if(err)
            throw new OSExecuteException("Errors executing "+command);

    }
}

18.10 新I/O

​ java.nio.*包引入了新的JavaI/O类库。速度的提高在于使用的结构更接近于操作系统执行I/O的方式:通道和缓冲器。

​ 我们<—->缓冲器<—->通道(包含数据)

​ 唯一直接与通道交互的的缓冲器是ByteBuffer,存储字节。

​ FileInputStream, FileOutputStream,RandomAccessFile,被修改提供方法getChannel()获得通道。字节操纵流。

package Ch18;

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.RandomAccessFile;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;

public class GetChannel {
    private static int BSIZE = 1024;

    public static void main(String[] args) throws Exception{
        FileChannel fc = new FileOutputStream("data.txt").getChannel();
      //wrap将已存在的字节数组包装到ByteBuffer中。
        fc.write(ByteBuffer.wrap("Some text ".getBytes()));
      //关闭该流,才能打开其他流
        fc.close();
    //RandomAccessFile再次打开 ,在首
        fc = new RandomAccessFile("data.txt", "rw").getChannel();
      //移动到末尾
        fc.position(fc.size());
        fc.write(ByteBuffer.wrap("Some more".getBytes()));
        fc.close();
    // 
        fc = new FileInputStream("data.txt").getChannel();
      //分配大小
        ByteBuffer buff = ByteBuffer.allocate(BSIZE);
      //read告知fc(fileChannel)向ByteBuffer存储字节。
        fc.read(buff);
      //因为前面一旦写入,读写都就在末尾,必须调用flip做好读取字节的准备。flip将读写头重置到开头。
        buff.flip();
      //hasRemaining()方法
        while(buff.hasRemaining())
          //get读取
            System.out.print((char)buff.get());
    }
    /*Some text Some more*/

  package Ch18;

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;

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);
      //read将数据输入到缓冲器,write将数据写出到out中,再清空buffer,准备下一次输入。
        while(in.read(buffer)!=-1){
            buffer.flip();//perpare for writing
            out.write(buffer);
            buffer.clear();//perpare for reading
        }
    }

//transferTo和transferFrom,允许我们将一个通道和另一个通道相连。
package Ch18;

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.nio.channels.FileChannel;

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

18.10.1 转换数据

//缓冲区容纳的是普通字节,为了转换为字符,要么在输入时进行编码,要么在输出时进行解码。才能显示正常的字符。
package Ch18;

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.charset.Charset;

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 text1".getBytes()));
        fc.close();

        fc = new FileInputStream("data2.txt").getChannel();
        ByteBuffer buff = ByteBuffer.allocate(BSIZE);
        fc.read(buff);
        buff.flip();
        System.out.println(buff.asCharBuffer());

        buff.rewind();
      // 用主机编码方式解码
      //System.getProperty方法取得系统属性。
        String encoding = System.getProperty("file.encoding");
        System.out.println("Decoded using "+encoding +": "+ Charset.forName(encoding).decode(buff));

    //输入时,就用特定编码方式编码,就可以使用asCharBUffer()方法
        fc = new FileOutputStream("data2.txt").getChannel();
        fc.write(ByteBuffer.wrap("Some text2".getBytes("UTF-16BE")));
        fc.close();

        fc = new FileInputStream("data2.txt").getChannel();
        buff.clear();
        fc.read(buff);
        buff.flip();
        System.out.println(buff.asCharBuffer());
    //
        fc = new FileOutputStream("data2.txt").getChannel();
        buff = ByteBuffer.allocate(24);
        buff.asCharBuffer().put("Some text3");
        fc.write(buff);
        fc.close();

        fc = new FileInputStream("data2.txt").getChannel();
        buff.clear();
        fc.read(buff);
        buff.flip();
        System.out.println(buff.asCharBuffer());

    }
}

/*卯浥⁴數琱
Decoded using UTF-8: Some text1
Some text2
Some text3   
*/

18.10.2 获取基本类型

尽管ByteBuffer只能保存字节类型数据,但又办法获取基本类型。

package Ch18;


import java.nio.ByteBuffer;

import static net.mindview.util.Print.print;
import static net.mindview.util.Print.printnb;

public class GetData {
    private static final int BSIZE = 1024;

    public static void main(String[] args) {
        ByteBuffer bb =ByteBuffer.allocate(BSIZE);
        int i = 0;
        while(i++ < bb.limit())
            if(bb.get() !=0)
                print("nonzero");
            print("i= "+i);
            bb.rewind();
            bb.asCharBuffer().put("Howdy!");
            char c;
            while ((c=bb.getChar())!=0)
                printnb(c+ " ");
            print();
            bb.rewind();
            //只有short需要类型转换
            bb.asShortBuffer().put((short)471142);
            print(bb.getShort());
            bb.rewind();
            bb.asIntBuffer().put(99471142);
            print(bb.getInt());
            bb.rewind();
            bb.asLongBuffer().put(99471142);
            print(bb.getLong());
            bb.rewind();
            bb.asFloatBuffer().put(99471142);
            print(bb.getFloat());
            bb.rewind();
            bb.asDoubleBuffer().put(99471142);
            print(bb.getDouble());
            bb.rewind();
    }
}
/*i= 1025
H o w d y ! 
12390
99471142
99471142
9.9471144E7
9.9471142E7*/

18.10.3 视图缓冲器

package Ch18;

import java.nio.ByteBuffer;
import java.nio.IntBuffer;

public class IntBufferDemo {
    private static final int BSIZE = 1024;

    public static void main(String[] args) {
        ByteBuffer bb = ByteBuffer.allocate(BSIZE);
        IntBuffer ib = bb.asIntBuffer();
        ib.put(new int[]{11, 42, 47, 99, 143, 811, 1016});
      //get
        System.out.println(ib.get(3));
      //put
        ib.put(3, 1811);
        ib.flip();
        while(ib.hasRemaining()){
            int i = ib.get();
            System.out.println(i);
        }
    }
}
/*99
11
42
47
1811
143
811
1016
*/

字节存放顺序

ByteBuffer是以高位优先的形式存储数据。

package Ch18;

import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.util.Arrays;

import static net.mindview.util.Print.print;
import static net.mindview.util.Print.printnb;

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("abcedf");
        print(Arrays.toString(bb.array()));

    }
}
/*[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, 101, 0, 100, 0, 102, 0]*/

18.10.4 用缓冲器操纵数据

18.10.5 缓冲器细节

capacity()返回缓冲区容量
clear()清空缓冲区,将position设置为0,limit设置为容量。
flip()将limit设置为position,position设置为0,用于准备从缓冲区读取已经写入的数据。
limit()返回limit值
limit(int lim)设置limit值
mark()将mark设置为position
positioin()返回position值
positioni(int pos)设置position值
remaining()返回(limit-position)
hasRemaining()若有介于position和limit之间的元素,返回true
//交换相邻字符。
package Ch18;

import java.nio.ByteBuffer;
import java.nio.CharBuffer;

import static net.mindview.util.Print.print;
import static net.mindview.util.Print.printnb;

public class UsingBuffers {
    public static void symmetricsScramble(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);
      //取得视图,put和get会改变pos位置
        CharBuffer cb = bb.asCharBuffer();
        cb.put(data);

        print(cb.rewind());
        symmetricsScramble(cb);
        print(cb.rewind());
        symmetricsScramble(cb);
        print(cb.rewind());
    }
}
/*UsingBuffers
sUniBgfuefsr
UsingBuffers*/

18.10.6 内存映射文件

允许我们创建和修改那些太大而不能放入内存的文件。

package Ch18;

import java.io.RandomAccessFile;
import java.nio.MappedByteBuffer;
import java.nio.channels.FileChannel;

import static net.mindview.util.Print.print;
import static net.mindview.util.Print.printnb;

public class LargeMappedFiles {
    static int length = 0x8FFFFFF;

    public static void main(String[] args) throws Exception{
      // 先由RandomAccessFile开始,获得文件上的通道,然后调用map产生
      //mappedByteBuffer,同时需要指定开始位置和结束位置。
        MappedByteBuffer out = new RandomAccessFile("test.dat", "rw").getChannel().map(FileChannel.MapMode.READ_WRITE, 0, length);
        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));
    }
}

18.10.7 文件加锁

Java的文件加锁直接映射到了本地操作系统的加锁工具。所以竞争同一文件的两个线程可能不在同一java虚拟机。或者是操作系统本地线程。

package Ch18;

import java.io.FileOutputStream;
import java.nio.channels.FileLock;
import java.util.concurrent.TimeUnit;

public class FileLocking {
    public static void main(String[] args) throws Exception{
        FileOutputStream fos = new FileOutputStream("file.txt");
      //非阻塞tryLock,Lock阻塞
        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();
    }
}
/*Locked File
Released Lock*/
对映射文件的部分加锁
package Ch18;

import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.ByteBuffer;
import java.nio.MappedByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.channels.FileLock;

public class LockingMappedFiles {
    static final int LENGTH = 0x8FFFFFF;
    static FileChannel fc;

    public static void main(String[] args) throws Exception{
        fc = new RandomAccessFile("test.dat", "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);
          //新缓冲区position=0,limit = limit-position
            buff = mbb.slice();
            start();
        }
        public void run(){
            try{
                FileLock fl =fc.lock(start, end , false);
                System.out.println("Locked: "+start+" to "+end);
                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);
            }
        }
    }
}
/*Locked: 0 to 50331647
Locked: 75497471 to 113246206
Released: 75497471 to 113246206
Released: 0 to 50331647*/

18.11 压缩

压缩类库是按字节方式而不是字符方式处理的,派生于Stream。

CheckedInputStreamGetCkeckSum()为任何InputStream产生校验和(不仅是解压缩)
CheckedOutputStreamGetCheckSum()为任何OutputStream产生校验和(不仅是压缩)
DeflaterOutputStream压缩类的基类
ZipOutputStream一个DeflaterOutputStream,用于将数据压缩成zip文件格式
GZIPOutputStream一个。。。。,GZIP格式
InflaterInputStream解压缩基类
ZipInputStreamInflaterInputStream派生类,
GZIPInputStreamInflaterInputStream派生类,
GZIP进行简单压缩

单个数据流

package Ch18;

import java.io.*;
import java.util.zip.GZIPInputStream;
import java.util.zip.GZIPOutputStream;

public class GZIPcompress {
    public static void main(String[] args) throws IOException{
        if(args.length==0){
            System.out.println("00");
            System.exit(1);
        }
        System.out.println("run");
        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);
    }
}
18.11.2 用Zip进行多文件保存
package Ch18;

import java.io.*;
import java.util.Enumeration;
import java.util.zip.*;
import static net.mindview.util.Print.print;

public class ZipCompress {
    public static void main(String[] args) throws Exception{
        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");
        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());
      // 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);
        }
    //为了读取校验和,必须拥有与之相关联的Checksum对象的访问权限。
      //所以,保留了CheckedOutputStream和CheckedInpuStream对象的引用。
        if(args.length == 1)
            print("Checksum: "+csumi.getChecksum().getValue());
        bis.close();
        //解压缩的另外的方法,利用ZipFile对象读取文件(不是内容)。
        ZipFile zf = new ZipFile("test.zip");
        Enumeration e = zf.entries();
        while(e.hasMoreElements()){
            ZipEntry ze2 = (ZipEntry)e.nextElement();
            print("File:"+ze2);
        }
    }
}
// 将args置为 src//Ch18//Echo.java
/*
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);
        }
    }
}

checksum: 1191221539
File:src/Ch18/Echo.java
*/

18.11.3 Java档案文件

JAR(Java ARchive)

18.12 对象序列化(基于字节)

​ 对象序列化将实现了Serializable接口的对象转换成一个字节序列,并能够在以后将这个字节序列完全恢复为原来的对象。(跨平台)。实现了对象的轻量(必须显示序列化和反序列化)持久性(一个对象的生存周期并不取决于程序是否正在执行)。

package Ch18;

import java.io.*;
import java.util.Random;

import static net.mindview.util.Print.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;
    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);
      //序列化 s1:创建一些OutputStream对象,封装在ObjectOutputStream内
        ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("worm.out"));
      //s2 :调用writeObject即可将对象序列化,并将其发送给该OutputStream.
        out.writeObject("Worm storage\n");
        out.writeObject(w);
        out.close();
      //反序列化 s1:将一个InputStrea封装在ObjectInputStream内
        ObjectInputStream in = new ObjectInputStream(new FileInputStream("Worm.out"));
      //s2:调用readObject,再向下转型。
        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();
      //bout是OutputStream,同时也是对象。
        ObjectInputStream in2 = new ObjectInputStream(new ByteArrayInputStream(bout.toByteArray()));
        s = (String)in2.readObject();
        Worm w3 = (Worm)in2.readObject();
        print(s + "w3 = "+w3);
    }

}
/*
* 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 寻找类

18.12.2 序列化的控制

/*Serializable对象恢复,完全按照它存储的二进制为基础来构造。而不调用构造器。对于一个Externalizable对象,恢复Externalizable对象后,所有默认的构造器都会调用,然后调用readExternal。
*/
package Ch18;

import java.io.*;

import static net.mindview.util.Print.print;

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

class Blip2 implements Externalizable {
  //不是默认构造器。方法名,参数,返回值一致。且权限只能更大(保证原来的劝权限范围),返回类型(只能是子类)保证可以也是父类。
  //这里Blip2省略了权限,默认proceted,将其重载了。不是默认构造器。
  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("Constrcting 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();

        ObjectInputStream in = new ObjectInputStream(new FileInputStream("Blips.out"));
        print("Recovering b1:");
        b1 = (Blip1)in.readObject();
        //print("Recovering b2:");
       // b2 = (Blip2)in.readObject();
      //Blip2的构造器不是public
    }
}

/************************/

package Ch18;

import java.io.*;

import static net.mindview.util.Print.print;

public class Blip3 implements Externalizable{
    private int i;
    private String s;
    public Blip3(){
        print("Blip3 Constructor");
    }

    public Blip3(String x, int a){
        print("Blip3(String x, int a)");
        s = x;
        i = a;
    }

    public String toString() {
        return s+i;
    }
    //参数是调writeObject的参数
    public void writeExternal(ObjectOutput out) throws IOException{
        print("Blip3.writeExternal");
        out.writeObject(s);
        out.writeInt(i);
    }

    public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException{
        print("Blip3.readExternal");
        s = (String) in.readObject();
        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();
        ObjectInputStream in = new ObjectInputStream(new FileInputStream("Blip3.out"));
        print("Recovering b3");
        b3 = (Blip3)in.readObject();
        print(b3);
    }

}

/*
Constructing Objects:
Blip3(String x, int a)
A String 47
Saving object
Blip3.writeExternal
Recovering b3
Blip3 Constructor
Blip3.readExternal
A String 47*/
transient(瞬时)关键字

使用Seriablizable时,通过rransient瞬时关闭关键字,控制哪些序列化。

package Ch18;

import java.io.*;
import java.util.Date;
import java.util.concurrent.TimeUnit;
import static net.mindview.util.Print.print;

public class Logon implements Serializable{
    private Date date = new Date();
    private String username;
    private transient String password;
    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);
        ObjectInputStream in = new ObjectInputStream(new FileInputStream("Logon.out"));
        print("Recovering object at "+new Date());
        a = (Logon)in.readObject();
        print("Logon a = "+a);
    }
}
/*Logon a =logon info: 
 username: Hulk
 date: Wed Nov 15 16:06:12 CST 2017
 password: myLittlePony
Recovering object at Wed Nov 15 16:06:13 CST 2017
Logon a = logon info: 
 username: Hulk
 date: Wed Nov 15 16:06:12 CST 2017
 password: null
*/
Externalizable的替代方法

实现Serializable接口,并添加(不是覆盖)writeObject和readObject方法。序列化时会自动调用这两个方法。

但必须具有准确的方法特征签名:

private void writeObject(ObjectoutputStream stream) throws IOException

private void readObject(ObjectInputStream stream) throws IOException, ClassNotFoundException

版本控制

18.12.3 使用“持久性”

18.13 XML

序列化只是Java的解决方案,XML是通用方法。

18.14 Preferences

用于存储和读取用户的偏好一级程序配置项的设置

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值