Thinking in java Chapter18 IO

1 File类

File这个名字并非指代文件,它既能代表一个特定的文件,又能代表一个目录下的文件集合。如果指代的是文件集合的话,可以调用list()方法得到一个包含该目录下文件集合的数组。

1.1 目录列表器

查看一个目录列表,有两种方法:
1.调用不带参数的list()获取全部文件的集合
2.使用“目录过滤器”获得符合条件的文件集合

 package io;

import java.io.File;
import java.io.FilenameFilter;
import java.util.Arrays;
import java.util.regex.Pattern;

// args: .*\.java

public class DirList {
    public static void main(String[] args) {
//        File path = new File(".");
        File path = new File("/Users/erin/JavaProject/thinking_in_java_example/src/main/java/io");
        String[] list;
        if (args.length== 0)
            list = path.list();//指定目录的全部列表
        else
            list = path.list(new DirFilter(args[0]));//指定目录的受限列表,使用FilenameFilter 筛选器
        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);
    }

    @Override
    public boolean accept(File dir, String name) {
        return pattern.matcher(name).matches();
    }
}
list方法 会将该目录下的每个文件调用accept,来判断该文件是否包含在内。
    public String[] list(FilenameFilter filter) {
        String names[] = list();
        if ((names == null) || (filter == null)) {
            return names;
        }
        List<String> v = new ArrayList<>();
        for (int i = 0 ; i < names.length ; i++) {
            if (filter.accept(this, names[i])) {
                v.add(names[i]);
            }
        }
        return v.toArray(new String[v.size()]);
    }

匿名内部类改写

package io;

import java.io.File;
import java.io.FilenameFilter;
import java.util.Arrays;
import java.util.regex.Pattern;

// args: .*\.java

public class DirList2 {
    public static FilenameFilter filter(String regex){ //编译器默认会给匿名内部类的外部变量
        return new FilenameFilter() {
            private Pattern pattern = Pattern.compile(regex);
            @Override
            public boolean accept(File dir, String name) {
                return pattern.matcher(name).matches();
            }
        };

    }
    public static void main(String[] args) {
        File path = new File("/Users/erin/JavaProject/thinking_in_java_example/src/main/java/io");
        String[] list;
        if (args.length== 0)
            list = path.list();//指定目录的全部列表
        else
            list = path.list(filter(args[0]));//指定目录的受限列表,使用FilenameFilter 筛选器
        Arrays.sort(list,String.CASE_INSENSITIVE_ORDER);
        for (String dirItem: list)
            System.out.println(dirItem);
    }
}
package io;

import java.io.File;
import java.io.FilenameFilter;
import java.util.Arrays;
import java.util.regex.Pattern;


// args: .*\.java


public class DirList3 {
    public static void main(String[] args) {
        File path = new File("/Users/erin/JavaProject/thinking_in_java_example/src/main/java/io");
        String[] list;
        if (args.length== 0)
            list = path.list();//指定目录的全部列表
        else
            list = path.list(new FilenameFilter() {
                private Pattern pattern = Pattern.compile(args[0]);
                
                @Override
                public boolean accept(File dir, String name) {
                    return pattern.matcher(name).matches();
                }
            });//指定目录的受限列表,使用FilenameFilter 筛选器
        Arrays.sort(list,String.CASE_INSENSITIVE_ORDER);
        for (String dirItem: list)
            System.out.println(dirItem);
    }

}

优点:代码隔离,聚拢
缺点:不易阅读,谨慎使用
作业1
package io.e1;

import net.mindview.util.TextFile;

import java.io.File;
import java.io.FilenameFilter;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.regex.Pattern;


// args: .*\.java


public class DirList3 {
    public static void main(String[] args) {
//        File path = new File(".");
        File path = new File("/Users/erin/JavaProject/thinking_in_java_example/src/main/java/io");
        String[] list;
        if (args.length == 0)
            list = path.list();//指定目录的全部列表
        else
            list = path.list(new FilenameFilter() {
                // 只分析指定后缀的文件,否则对于目录,需要额外处理
                private String ext = args[0].toLowerCase();
                @Override
                public boolean accept(File dir, String name) {
                    if (name.toLowerCase().endsWith(ext)){
                        //只过滤指定文件类型
                        if (args.length == 1)
                            return true;
                        Set<String> words = new HashSet<>(new TextFile(new File(dir, name).getAbsolutePath(), "\\W+"));
                        for (int i = 1;i < args.length;i++)// 除第一个参数为后缀,尾随参数,是需要遍历检查,是否在文件中
                            if (words.contains(args[i]))
                                return true;
                    }
                    return false;
                }
            });//指定目录的受限列表,使用FilenameFilter 筛选器
        Arrays.sort(list, String.CASE_INSENSITIVE_ORDER);
        for (String dirItem : list)
            System.out.println(dirItem);
    }

}
package io.e2;

import java.io.File;
import java.io.FilenameFilter;
import java.util.Arrays;
import java.util.regex.Pattern;

public class SortedDirList {
    private File path;

    public SortedDirList() {
        path = new File(".");
    }

    public SortedDirList(File path) {
        this.path = path;
    }

    String[] list() {
        String[] list = path.list();
        Arrays.sort(list, String.CASE_INSENSITIVE_ORDER);
        return list;
    }

    String[] list(String regex) {
        String[] list = path.list(new FilenameFilter() {
            private Pattern pattern = Pattern.compile(regex);

            @Override
            public boolean accept(File dir, String name) {
                return pattern.matcher(name).matches();
            }
        });
        Arrays.sort(list, String.CASE_INSENSITIVE_ORDER);
        return list;
    }

    public static void main(String[] args) {
        SortedDirList dir = new SortedDirList();
        System.out.println((Arrays.asList(dir.list(".*\\.java"))));
    }
}
package io.e3;

import java.io.File;
import java.io.FilenameFilter;
import java.util.Arrays;
import java.util.regex.Pattern;

// args: .*\.java

public class DirList3 {
    public static void main(String[] args) {
        File path = new File("/Users/erin/JavaProject/thinking_in_java_example/src/main/java/io");
        String[] list;
        if (args.length == 0)
            list = path.list();//指定目录的全部列表
        else
            list = path.list(new FilenameFilter() {
                private Pattern pattern = Pattern.compile(args[0]);

                @Override
                public boolean accept(File dir, String name) {
                    return pattern.matcher(name).matches();
                }
            });//指定目录的受限列表,使用FilenameFilter 筛选器
        long total = 0;
        long fs;
        for (String dirItem : list) {
            fs = new File(path, dirItem).length();
            System.out.println(dirItem + ", " + fs + " bytes");
            total += fs;
        }
        System.out.println("=========");
        System.out.println(list.length + "files(s)," + total + " bytes");
    }
}

1.2 目录实用工具

package io;

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 {

    public static File[]
    local(File dir,final String regex){
        return dir.listFiles(new FilenameFilter() {
            private Pattern pattern = Pattern.compile(regex);
            @Override
            public boolean accept(File dir, String name) {
                return pattern.matcher(new File(name).getName()).matches();
            }
        });
    }

    // Overloaded
    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<>();

        @Override
        public Iterator<File> iterator() {
            return files.iterator(); //默认迭代是file
        }

        void addAll(TreeInfo other) {
            files.addAll(other.files);
            files.addAll(other.dirs);
        }

        public String toString() {
            return "dirs: " + PPrint.pformat(dirs) +
                    "\n\nfiles:: " + PPrint.pformat(files);
        }
    }

    public static TreeInfo walk(String start,String regex){
        return reCurseDirs(new File(start),regex);
    }

    public static TreeInfo walk(File start){
        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 // 文件
                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 io;

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 strategy, String ext) {
        this.strategy = strategy;
        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 {// 文件
                    // Allow user to leave off extension:
                    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 Strategy() {
            @Override
            public void process(File file) {
                System.out.println(file);
            }
        }, "java").start(args);
    }
}
作业4
package io.e4;

import io.Directory;
import java.io.File;

// args: .*\.java
public class E4 {
    public static void main(String[] args) {
        Directory.TreeInfo ti;
        String start = new File(".").getAbsolutePath()+"/src/main/java/io";

        if (args.length == 0)
            ti = Directory.walk(start);
        else
            ti = Directory.walk(start,args[0]);
        long total = 0;
        for (File file: ti)
            total += file.length();
        System.out.println( ti.files.size() + " file(s), " + total + "bytes");
    }
}

1.3目录的检查及创建

package io;

import java.io.File;

// {Args: MakeDirectoriesTest}
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() + //路径名是MakeDirectoriesTest,从这个路径字符串解析不出来上级路经 所以是null
                        "\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")) { // rename
            if (args.length != 3) usage(); // 需要三个参数,若参数数量不正确,告知usage,退出
            File
                    old = new File(args[1]),// 源文件
                    rname = new File(args[2]);// 重命名文件
            old.renameTo(rname);//重命名
            fileData(old);//被删除的文件信息
            fileData(rname);//新文件信息
            return;//Exit main
        }
        int count = 0;
        boolean del = false;
        if (args[0].equals("-d")) {//删除标记
            count++;
            del = true;
        }
        count--; //将删除 和 创建dir 统一处理
        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 {// 不存在
                if (!del) {//不是删除,也就是 mkdir
                    f.mkdirs();
                    System.out.println("created " + f);
                }
            }
            fileData(f);
        }
    }
}


/*

args: -d /Users/erin/JavaProject/thinking_in_java_example/src/main/java/io/test1 /Users/erin/JavaProject/thinking_in_java_example/src/main/java/io/test2

/Users/erin/JavaProject/thinking_in_java_example/src/main/java/io/test1 exists
deleting.../Users/erin/JavaProject/thinking_in_java_example/src/main/java/io/test1
Absolute path: /Users/erin/JavaProject/thinking_in_java_example/src/main/java/io/test1
 Can read: false
 Can write: false
 getName: test1
 getParent: /Users/erin/JavaProject/thinking_in_java_example/src/main/java/io
 getPath: /Users/erin/JavaProject/thinking_in_java_example/src/main/java/io/test1
 length: 0
 lastModified: 0
/Users/erin/JavaProject/thinking_in_java_example/src/main/java/io/test2 exists
deleting.../Users/erin/JavaProject/thinking_in_java_example/src/main/java/io/test2
Absolute path: /Users/erin/JavaProject/thinking_in_java_example/src/main/java/io/test2
 Can read: false
 Can write: false
 getName: test2
 getParent: /Users/erin/JavaProject/thinking_in_java_example/src/main/java/io
 getPath: /Users/erin/JavaProject/thinking_in_java_example/src/main/java/io/test2
 length: 0
 lastModified: 0


args: -r /Users/erin/JavaProject/thinking_in_java_example/src/main/java/io/test /Users/erin/JavaProject/thinking_in_java_example/src/main/java/io/test1

Absolute path: /Users/erin/JavaProject/thinking_in_java_example/src/main/java/io/test
 Can read: false
 Can write: false
 getName: test
 getParent: /Users/erin/JavaProject/thinking_in_java_example/src/main/java/io
 getPath: /Users/erin/JavaProject/thinking_in_java_example/src/main/java/io/test
 length: 0
 lastModified: 0
Absolute path: /Users/erin/JavaProject/thinking_in_java_example/src/main/java/io/test1
 Can read: true
 Can write: true
 getName: test1
 getParent: /Users/erin/JavaProject/thinking_in_java_example/src/main/java/io
 getPath: /Users/erin/JavaProject/thinking_in_java_example/src/main/java/io/test1
 length: 26
 lastModified: 1576557859000
It's a file


args:   -d MakeDirectoriesTest

MakeDirectoriesTest exists
deleting...MakeDirectoriesTest
Absolute path: /Users/erin/JavaProject/thinking_in_java_example/MakeDirectoriesTest
 Can read: false
 Can write: false
 getName: MakeDirectoriesTest
 getParent: null
 getPath: MakeDirectoriesTest
 length: 0
 lastModified: 0
Disconnected from the target VM, address: '127.0.0.1:60489', transport: 'socket'


args:   MakeDirectoriesTest

Connected to the target VM, address: '127.0.0.1:60500', transport: 'socket'
created MakeDirectoriesTest
Absolute path: /Users/erin/JavaProject/thinking_in_java_example/MakeDirectoriesTest
 Can read: true
 Can write: true
 getName: MakeDirectoriesTest
 getParent: null
 getPath: MakeDirectoriesTest
 length: 64
 lastModified: 1576562665000
It's a directory.

再次运行

 */

2 输入和输出

Java中“流”类库让人迷惑的主要原因就在于:创建单一的结果流,却需要创建多个对象。

2.1 InputStream类型

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

6.1 缓冲输入文件

package io;

import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;

public class BufferedInputFile {
    public static String read(String filename) throws IOException {
        // 逐行读入
//        public class FileReader extends InputStreamReader
//        public class InputStreamReader extends Reader
        BufferedReader in =new BufferedReader(new FileReader(filename));//提高速度,对文件进行缓冲
        String s;
        StringBuilder sb = new StringBuilder();
        while ((s = in.readLine())!=null)
            sb.append(s + "\n");// readline 已经将\n删掉,故需要 添加换行符
        in.close(); // 显式调用close
        return sb.toString();
    }

    public static void main(String[] args) throws IOException {
        System.out.println(read("/Users/erin/JavaProject/thinking_in_java_example/src/main/java/io/BufferedInputFile.java"));
    }
}

6.2从内存输入

从内存读入字符串

package io;

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

// public class StringReader extends Reader
// public StringReader(String s)
public class MemoryInput {
    public static void main(String[] args) throws IOException {
        StringReader in = new StringReader(BufferedInputFile.read("/Users/erin/JavaProject/thinking_in_java_example/src/main/java/io/MemoryInput.java"));
        int c;
        while ((c = in.read()) != -1)
            System.out.print((char)c);
    }
}

6.3 格式化的内存输入

package io;

import java.io.ByteArrayInputStream;
import java.io.DataInputStream;
import java.io.IOException;

public class FormattedMemoryInput {
    public static void main(String[] args) {
        try {
            DataInputStream in = new DataInputStream(
                    new ByteArrayInputStream(
                            BufferedInputFile.read(
                            "/Users/erin/JavaProject/thinking_in_java_example/src/main/java/io/FormattedMemoryInput.java").getBytes()));
            while (true)
                System.out.print((char)in.readByte());
        } catch (IOException e){
            System.err.println("End of stream");
        }
    }
}
package io;

import java.io.*;

public class TestEOF {
    public static void main(String[] args) throws IOException {
        DataInputStream in = new DataInputStream(
                new BufferedInputStream(
                        new FileInputStream("/Users/erin/JavaProject/thinking_in_java_example/src/main/java/io/TestEOF.java")));
        while (in.available() != 0)
            System.out.print((char)in.readByte());
    }
}

6.4 基本的文件输出

PrintWriter

package io;

import java.io.*;

public class BasicFileOutput {
    static String fileJava = "/Users/erin/JavaProject/thinking_in_java_example/src/main/java/io/BasicFileOutput.java";//读入文件
    static String fileOutput = "/Users/erin/JavaProject/thinking_in_java_example/src/main/java/io/BasicFileOutput.out";//输出文件

    public static void main(String[] args) throws IOException {
        BufferedReader in = new BufferedReader(
                new StringReader(
                        BufferedInputFile.read(fileJava)
                )
        );

        PrintWriter out = new PrintWriter(
                new BufferedWriter(new FileWriter(fileOutput))
        );
        int lineCount = 1;
        String s;
        while ((s = in.readLine())!= null)
            out.println(lineCount++ + ": " + s);
        out.close();//显式调用close,若不close,缓冲区内容不会刷新清空。它们就不完整。
        in.close(); // 关闭 源文件
        System.out.println(BufferedInputFile.read(fileOutput));
    }
}
/*
LineNumberReader 是个 silly class 没有用。
记录行号很容易。
 */

文本文件输出的快捷方式

同上,但其他常见的写入任务 没有快捷方式

package io;

import chapter6access.Print;

import java.io.*;

public class FileOutputShortcut {
    static String base = "/Users/erin/JavaProject/thinking_in_java_example/src/main/java/io/";

    static String file = base + "FileOutputShortcut.out";
    static String fileJava = base + "FileOutputShortcut.java";

    public static void main(String[] args) throws IOException {
        BufferedReader in = new BufferedReader(
                new StringReader(
                        BufferedInputFile.read(fileJava)));
        // shortcut
        PrintWriter out = new PrintWriter(file);
        /*
            public PrintWriter(String fileName) throws FileNotFoundException {
        this(new BufferedWriter(new OutputStreamWriter(new FileOutputStream(fileName))),
             false);
    }
         */
        int lineCount = 1;
        String s;
        while ((s = in.readLine()) != null)
            out.println(lineCount++ + ": " + s);
        out.close();
        System.out.println(BufferedInputFile.read(file));
        in.close();
    }

}

写入文件,有无IO的比较

package io.e14;

import io.e7.E7;

import java.io.BufferedWriter;
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.List;

public class E14_BufferPerformance {
    static String base = "/Users/erin/JavaProject/thinking_in_java_example/src/main/java/io/e14/";

    static String fileOutput = base + "E14_BufferPerformance.out";
    static String fileJava = base + "E14_BufferPerformance.java";

    public static void main(String[] args)
            throws IOException {
        List<String> list = E7.read(fileJava);
        PrintWriter out = new PrintWriter(
                new BufferedWriter(new FileWriter(fileOutput)));
        int lineCount = 1;
        long t1 = System.currentTimeMillis();
        for(String s : list) {
            for(int i = 0; i < 10000; i++)
                out.println(lineCount + ": " + s);
            lineCount++;
        }
        long t2 = System.currentTimeMillis();
        System.out.println("buffered: " + (t2 - t1));
        out.close();

        out = new PrintWriter(new FileWriter(fileOutput));
        lineCount = 1;
        t1 = System.currentTimeMillis();
        for(String s : list) {
            for(int i = 0; i < 10000; i++)
                out.println(lineCount + ": " + s);
            lineCount++;
        }
        t2 = System.currentTimeMillis();
        System.out.println("unbuffered: " + (t2 - t1));
        out.close();
    }
}
/*
buffered: 104
unbuffered: 227
 */

6.5 存储和恢复数据

package io;

import java.io.*;

public class StoringAndRecoveringData {
    static String base = "/Users/erin/JavaProject/thinking_in_java_example/src/main/java/io/";

    static String file = base + "test0";


    public static void main(String[] args) throws IOException {
        DataOutputStream out = new DataOutputStream(
                new BufferedOutputStream(
                        new FileOutputStream(file))); //创建文件
        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(file)));
        System.out.println(in.readDouble());//按存的顺序读取
        System.out.println(in.readUTF());
        System.out.println(in.readDouble());
        System.out.println(in.readUTF());
    }
}
/*
DataInputStream DataOutputStream 面向字节流,使用OutputStream InputStream
这种方式不常用。

3.14159
That was pi
1.41413
Square root of 2
 */

http://www.ruanyifeng.com/blog/2007/10/ascii_unicode_and_utf-8.html
UTF-8 的编码规则很简单,只有二条:

1)对于单字节的符号,字节的第一位设为0,后面7位为这个符号的 Unicode 码。因此对于英语字母,UTF-8 编码和 ASCII 码是相同的。

2)对于n字节的符号(n > 1),第一个字节的前n位都设为1,第n + 1位设为0,后面字节的前两位一律设为10。剩下的没有提及的二进制位,全部为这个符号的 Unicode 码。


UTF-8 的编码
 
Unicode符号范围     |        UTF-8编码方式
(十六进制)          |              (二进制)
----------------------+---------------------------------------------
0000 0000-0000 007F | 0xxxxxxx
0000 0080-0000 07FF | 110xxxxx 10xxxxxx
0000 0800-0000 FFFF | 1110xxxx 10xxxxxx 10xxxxxx
0001 0000-0010 FFFF | 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx

   static int writeUTF(String str, DataOutput out) throws IOException {
        int strlen = str.length();
        int utflen = 0;
        int c, count = 0;

        /* use charAt instead of copying String to char array */
        for (int i = 0; i < strlen; i++) {
            c = str.charAt(i);
            if ((c >= 0x0001) && (c <= 0x007F)) {
                utflen++;
            } else if (c > 0x07FF) {
                utflen += 3;
            } else {
                utflen += 2;
            }
        }

        if (utflen > 65535)
            throw new UTFDataFormatException(
                "encoded string too long: " + utflen + " bytes");

        byte[] bytearr = null;
        if (out instanceof DataOutputStream) {
            DataOutputStream dos = (DataOutputStream)out;
            if(dos.bytearr == null || (dos.bytearr.length < (utflen+2)))
                dos.bytearr = new byte[(utflen*2) + 2];
            bytearr = dos.bytearr;
        } else {
            bytearr = new byte[utflen+2];
        }
        
        
        // 字节数组 的前两个字节,保存UTF-8的长度
        bytearr[count++] = (byte) ((utflen >>> 8) & 0xFF);
        bytearr[count++] = (byte) ((utflen >>> 0) & 0xFF);

        // 单字节预处理
        int i=0;
        for (i=0; i<strlen; i++) {
           c = str.charAt(i);
           if (!((c >= 0x0001) && (c <= 0x007F))) break;
           bytearr[count++] = (byte) c;
        }

        // 预处理后,数据处理
        for (;i < strlen; i++){
            c = str.charAt(i);
            if ((c >= 0x0001) && (c <= 0x007F)) {
                bytearr[count++] = (byte) c;

            } else if (c > 0x07FF) {
                bytearr[count++] = (byte) (0xE0 | ((c >> 12) & 0x0F));
                bytearr[count++] = (byte) (0x80 | ((c >>  6) & 0x3F));
                bytearr[count++] = (byte) (0x80 | ((c >>  0) & 0x3F));
            } else {
                bytearr[count++] = (byte) (0xC0 | ((c >>  6) & 0x1F));
                bytearr[count++] = (byte) (0x80 | ((c >>  0) & 0x3F));
            }
        }
        out.write(bytearr, 0, utflen+2);
        return utflen + 2;
    }

6.6 读写随机访问文件

6.7 管道流

21章 多线程

7 文件读写的使用工具

8 标准I/O

8.1 从标准输入中读取

System.in public final static InputStream in = null;

System.out public final static PrintStream out = null;

System.err public final static PrintStream err = null;

package io;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;

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

8.2 将System.out 转换成 PrinterWriter

package io;

import java.io.PrintWriter;

public class ChangeSystemOut {
    public static void main(String[] args) {
        PrintWriter out = new PrintWriter(System.out,true);//打印流是输出流的子类,true 自动清空功能
        out.println("hello,world");
    }
}

8.标准I/O重定向

import java.io.*;

public class Redirecting {
    static String filePath = "/Users/erin/JavaProject/thinking_in_java_example/src/main/java/io/Redirecting.java";
    public static void main(String[] args) throws IOException { // io 操纵字节流
        PrintStream console = System.out;
        BufferedInputStream in = new BufferedInputStream(new FileInputStream(filePath));
        PrintStream out = new PrintStream(new BufferedOutputStream(new FileOutputStream("/Users/erin/JavaProject/thinking_in_java_example/src/main/java/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();// 输出流关闭
        System.setOut(console);// 改回来
    }
}

9 进程控制

package io;

public  class OSExcuteException extends RuntimeException{ // 继承运行时异常
    public OSExcuteException(String why){
        super(why);
    }
}
package io;

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 OSExcuteException("Errors executing " + command); // 抛出自定义异常
    }
}
package io;

public class OsExecuteDemo {
    public static void main(String[] args) {
        OsExecute.command("ls");
        //        OsExecute.command("javap /Users/erin/JavaProject/thinking_in_java_example/src/main/java/jvm/OsExecuteDemo");
    }
}

10 新 I/O

NIO主要有三大核心部分:Channel(通道),Buffer(缓冲区), Selector。

传统IO 基于字节流和字符流进行操作,

NIO 基于Channel和Buffer(缓冲区)进行操作,数据总是从通道读取到缓冲区中,或者从缓冲区写入到通道中。

演示三种类型的流,用以产生可写,可读可写,可读的通道

package io;

import java.io.*;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;

public class GetChannel {
    private static final int BSIZE = 1024; // 1K
    static String filePath = "/Users/erin/JavaProject/thinking_in_java_example/src/main/java/io/data.txt";

    public static void main(String[] args) throws IOException {
        // 写文件
        FileChannel fc = new FileOutputStream(filePath).getChannel();
        fc.write(ByteBuffer.wrap("Some text ".getBytes()));
        fc.close();

        // 追加文件
        fc = new RandomAccessFile(filePath,"rw").getChannel();
        fc.position(fc.size());//移动FileChannel
        fc.write(ByteBuffer.wrap("Some more".getBytes()));
        fc.close();

        // 只 读文件
        fc = new FileInputStream(filePath).getChannel();
        ByteBuffer buff = ByteBuffer.allocate(BSIZE);
        fc.read(buff);
        buff.flip();
        while (buff.hasRemaining())
            System.out.print((char) buff.get());
    }
}

简单的文件复制

package io;

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

// {Args:/Users/erin/JavaProject/thinking_in_java_example/src/main/java/io/ChannelCopy.java /Users/erin/JavaProject/thinking_in_java_example/src/main/java/io/test.txt }
public class ChannelCopy {
    private static final int BSIZE = 1024;

    public static void main(String[] args) throws IOException {
        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(); // 准备读
            out.write(buffer);
            buffer.clear();//准备写
        }
    }
}

上述不是处理此类操作的理想方式。transferTo transferFrom将一个通道和另一个通道相连

package io;

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

public class TransferTo {
    static String srcPath = "/Users/erin/JavaProject/thinking_in_java_example/src/main/java/io/TransferTo.java";
    static String desPath = "/Users/erin/JavaProject/thinking_in_java_example/src/main/java/io/TransferTo.txt";

    public static void main(String[] args) throws IOException {
        FileChannel
                in = new FileInputStream(srcPath).getChannel(),
                out = new FileOutputStream(desPath).getChannel();
        in.transferTo(0,in.size(),out);
//        out.transferFrom(in,0,in.size());
    }
}

10.1 转换数据

GetChannel 是字节缓冲器

10.2 获取基本类型

10.3 视图缓冲器

10.4 用缓冲器操纵数据

10.5 缓冲器细节

10.6 内存映射文件

10.7 文件加锁

11 压缩

属于InputStream OutputStream继承层次结构,压缩类库,按字节方式,而不是字符方式处理。

ZIP 和GZIP最常用

11.1 用GZIP进行简单压缩

单数据流进行压缩

package io;

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

//{Args:/Users/erin/JavaProject/thinking_in_java_example/src/main/java/io/GZIPcompress.java}
public class GZIPcompress {
    static String gzFile = "/Users/erin/JavaProject/thinking_in_java_example/src/main/java/io/test.gz";
    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);
        }


        // 1. 被压缩的文件
        BufferedReader in = new BufferedReader(
                new FileReader(args[0]));

        // 2. 生成压缩文件 将输出流 封装成 GZIPOutputStream
        BufferedOutputStream out = new BufferedOutputStream(
                new GZIPOutputStream(
                        new FileOutputStream(gzFile)));

        // 写入压缩文件
        System.out.println("Writing file");
        int c;
        while ((c = in.read())!= -1)
            out.write(c);
        in.close();
        out.close();

        // 读取压缩文件 将输入流 封装成 GZIPOutputStream
        System.out.println("Reading file");
        BufferedReader in2 = new BufferedReader(
                new InputStreamReader(new GZIPInputStream(
                        new FileInputStream(gzFile))));
        String s;
        while ((s = in2.readLine())!= null)
            System.out.println(s);
    }
}

11.2 用Zip进行多文件保存

Checksum 类 用来计算和校验文件的校验和的方法。有两种Checksum类型:Adler32(更快些)和 CRC32(慢一些,但更准确)

package io;

import java.io.*;
import java.util.Enumeration;
import java.util.zip.*;

public class ZipCompress {
    static String zipFile= "/Users/erin/JavaProject/thinking_in_java_example/src/main/java/io/test.zip";
    public static void main(String[] args) throws IOException {
        FileOutputStream f = new FileOutputStream(zipFile);
        // public class CheckedOutputStream extends FilterOutputStream 校验和 用于验证输出数据的完整性
        CheckedOutputStream csum = new CheckedOutputStream(f,new Adler32());
        ZipOutputStream zos = new ZipOutputStream(csum);
        BufferedOutputStream out = new BufferedOutputStream(zos);

        zos.setComment("A test of Java Zipping"); // 设置ZIP 文件注释

        for (String arg: args){
            System.out.println("Writing file "  + arg);
            BufferedReader in = new BufferedReader(new FileReader(arg));
            //开始写入新的 ZIP 文件条目并将流定位到条目数据的开始处
            //ZipEntry是Java中进行压缩与解压缩的单位,它用来标记ZIP压缩文件中每个原始文件的入口

            zos.putNextEntry(new ZipEntry(arg));
            int c;
            while ((c = in.read()) != -1)
                out.write(c);
            in.close();
            out.flush();
        }
        out.close();

        System.out.println("Checksum: " + csum.getChecksum().getValue());

        // 解压缩
        System.out.println("Reading file");
        FileInputStream fi = new FileInputStream(zipFile);
        CheckedInputStream csumi = new CheckedInputStream(fi,new Adler32());
        ZipInputStream in2 = new ZipInputStream(csumi);
        BufferedInputStream bis = new BufferedInputStream(in2);
        ZipEntry ze;
        while ((ze = in2.getNextEntry()) != null){
            System.out.println("Reading file " + ze);
            int x;
            while ((x = bis.read()) != -1)
                System.out.write(x); //输出字符流
        }

        if (args.length == 1)
            System.out.println("Checksum: " + csumi.getChecksum().getValue());
        bis.close();

        // 另种方式 打开和读取zip文件 查看压缩文件的目录
        ZipFile zf = new ZipFile(zipFile);
        Enumeration e = zf.entries();
        while (e.hasMoreElements()){
            ZipEntry ze2 = (ZipEntry)e.nextElement();
            System.out.println("File: " + ze2);
            //...and extract the data as before
        }
        // if args.length == 1
      }
}

11.3 Java档案文件

Zip格式应用于JAR(Java ARchive)文件格式中,JAR跨平台
JAR文件 一组压缩文件构成,且有一张“文件清单”(自行创建或jar程序自动生成)

Sun JDK自带jar程序 jar [options] destination [manifest] inputfile(s)
jar cf myJarFile.jar .class
jar cmf myJarFile.jar myManifestFile.mf
.class 自建清单文件
jar tf myJarFile.jar 产生所有文件的一个目录表
jar tvf myJarFile.jar 详尽
jar cvf myApp.jar audio classes image 将子目录合并到文件myAppljar中
0选项创建JAR文件,文件可放入类路径变量(CLASSPATH)中

12 对象序列化

package io;

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

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){
        System.out.println("Worm constructor: " + i);
        c = x;
        if (--i > 0)
            next = new Worm(i,(char)(x + 1));
    }
    public Worm(){
        System.out.println("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();
    }

    // 可序列化到文件或数据库
    static String wormOut = "/Users/erin/JavaProject/thinking_in_java_example/src/main/java/io/Worm.out";
    public static void main(String[] args) throws IOException, ClassNotFoundException {

        //
        Worm w = new Worm(6,'a');
        System.out.println("w = " + w);

        //读写文件
        ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream(wormOut));
        out.writeObject("Worm storage\n");
        out.writeObject(w);
        out.close();

        ObjectInputStream in = new ObjectInputStream(new FileInputStream(wormOut));
        String s = (String)in.readObject();
        Worm w2 = (Worm)in.readObject();
        System.out.println(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();
        System.out.println(s + "w3 = " + w3);
    }
}

12.1 寻找类

package io;

import java.io.*;

public class Alien implements Serializable {
}
package io;

import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;

public class FreezeAlien {
    static String outFile = "/Users/erin/JavaProject/thinking_in_java_example/src/main/java/io/X.file";
    public static void main(String[] args) throws IOException {
        ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream(outFile));
        Alien auellek = new Alien();
        out.writeObject(auellek);
    }
}

保证java虚拟机能找到.class文件

package io.Xfiles;

import java.io.FileInputStream;
import java.io.IOException;
import java.io.ObjectInputStream;

public class ThawAlien {//解冻外星人
    static String outFile = "/Users/erin/JavaProject/thinking_in_java_example/src/main/java/io/X.file";

    public static void main(String[] args) throws IOException, ClassNotFoundException {
        ObjectInputStream in = new ObjectInputStream(new FileInputStream(outFile));
        Object mystery = in.readObject();
        System.out.println(mystery.getClass()); //删掉 class后,ClassNotFoundException
    }
}
/*
class io.Alien
 */

12.2 序列化的控制

Externalizable 对序列化过程进行控制
自动调用
public void writeExternal(ObjectOutput out) throws IOException { //序列化
public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {//反序列化

Serializable 以存储的二进制为基础来构造,不需要调用构造器

Externalizable 普通的默认构造器都会被调用

package io;

import java.io.*;

class Blips1 implements Externalizable {
    public Blips1(){
        System.out.println("Blips1 Constructor");
    }
    @Override
    public void writeExternal(ObjectOutput out) throws IOException {
        System.out.println("Blips1.writeExternal");

    }

    @Override
    public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
        System.out.println("Blips1.readExternal");
    }
}


class Blips2 implements Externalizable {
    Blips2(){
        System.out.println("Blips2 Constructor");
    }
    @Override
    public void writeExternal(ObjectOutput out) throws IOException {
        System.out.println("Blips2.writeExternal");

    }

    @Override
    public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
        System.out.println("Blips2.readExternal");
    }
}


public class Blips{
    static String outFile = "/Users/erin/JavaProject/thinking_in_java_example/src/main/java/io/Blips.out";
    public static void main(String[] args) throws IOException, ClassNotFoundException {
        System.out.println("Constructing objects:");
        Blips1 b1 = new Blips1();
        Blips2 b2 = new Blips2();

        ObjectOutputStream o = new ObjectOutputStream(new FileOutputStream(outFile));
        System.out.println("Saving objects:");
        o.writeObject(b1);
        o.writeObject(b2);
        o.close();

        ObjectInputStream in = new ObjectInputStream(new FileInputStream(outFile));
        System.out.println("Recovering b1:");
        b1 = (Blips1) in.readObject();
        System.out.println("Recovering b2:");
        b2 = (Blips2) in.readObject(); //调用默认构造器,但不是public  InvalidClassException
    }
}
/*
Constructing objects:
Blips1 Constructor
Blips2 Constructor
Saving objects:
Blips1.writeExternal
Blips2.writeExternal
Recovering b1:
Blips1 Constructor
Blips1.readExternal
Recovering b2:
 */

对象的某一部分被序列化 Externalizable writeExternal/readExternal

package io;

import java.io.*;

public class Blip3 implements Externalizable {
    // 未初始化
    private int i;
    private String s;

    public Blip3(){//默认构造器
        System.out.println("Blip3 Constructor");
    }

    public Blip3(String x, int a){//非默认构造器
        System.out.println("Blip3(String s, int i)");
        s = x;
        i = a;
    }

    public String toString(){return s + i; }

    @Override
    public void writeExternal(ObjectOutput out) throws IOException { // 把希望序列化的部分存起来
        System.out.println("Blip3.writeExternal");

        // you must do this
        out.writeObject(s);
        out.writeInt(i);

    }

    @Override
    public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
        System.out.println("Blip3.readExternal");

        // you must do this
        s =(String)in.readObject();
        i = in.readInt();
    }
    static String outFile= "/Users/erin/JavaProject/thinking_in_java_example/src/main/java/io/Blip3.out";

    public static void main(String[] args) throws IOException, ClassNotFoundException {

        System.out.println("Construction objects:");
        Blip3 b3 = new Blip3("A String ", 47);
        System.out.println(b3);

        ObjectOutputStream o = new ObjectOutputStream(new FileOutputStream(outFile));
        System.out.println("Saving object:");
        o.writeObject(b3);
        o.close();

        ObjectInputStream in = new ObjectInputStream(new FileInputStream(outFile));
        System.out.println("Recoving b3: ");
        b3 = (Blip3)in.readObject();
        System.out.println(b3);

    }
}

transient(瞬时)关键字

Externalizable 没有任何东西可以自动序列化,可以在writeExternal内部对所需部分进行显示的序列化

Serializable 序列化自动进行。可以用transient 逐个字段关闭序列化

package io;

import java.io.*;
import java.util.Date;
import java.util.concurrent.TimeUnit;

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

    static String outFile = "/Users/erin/JavaProject/thinking_in_java_example/src/main/java/io/Logon.out";

    public static void main(String[] args) throws IOException, InterruptedException, ClassNotFoundException {
        Logon a = new Logon("Hulk", "myLittlePony");
        System.out.println("Logon a = " + a);
        ObjectOutputStream o = new ObjectOutputStream(new FileOutputStream(outFile));
        o.writeObject(a);
        o.close();

        TimeUnit.SECONDS.sleep(1);

        ObjectInputStream in = new ObjectInputStream(new FileInputStream(outFile));
        System.out.println("Recoving object at " + new Date());
        a = (Logon) in.readObject();
        System.out.println("logon a = " + a);

    }
}
/*
Logon a = Logon info: 
 username: Hulk
 date: Fri Dec 27 14:40:14 CST 2019
 password: myLittlePony
Recoving object at Fri Dec 27 14:40:15 CST 2019
logon a = Logon info: 
 username: Hulk
 date: Fri Dec 27 14:40:14 CST 2019 
 password: null

 */

12.3 使用“持久性”

两个对象都有指向第三个对象的引用,进行序列化

package io;

import java.io.*;
import java.util.ArrayList;
import java.util.List;

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

        System.out.println("animals: " + animals);
        ByteArrayOutputStream buf1 = new ByteArrayOutputStream(); // 字节数组, 对Serializable对象对“deep copy”
        ObjectOutputStream o1 = new ObjectOutputStream(buf1);
        o1.writeObject(animals);
        o1.writeObject(animals);

        ByteArrayOutputStream buf2 = new ByteArrayOutputStream();
        ObjectOutputStream o2 = new ObjectOutputStream(buf2);
        o2.writeObject(animals);


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

        System.out.println("animals1: " + animals1);
        System.out.println("animals2: " + animals2);
        System.out.println("animals3: " + animals3);
    }
}
/*
animals: [Bosco the dog[io.Animal@5e481248],io.House@66d3c617
, Ralph the hamster[io.Animal@63947c6b],io.House@66d3c617
, Molly the cat[io.Animal@2b193f2d],io.House@66d3c617
]
animals1: [Bosco the dog[io.Animal@1b2c6ec2],io.House@4edde6e5
, Ralph the hamster[io.Animal@70177ecd],io.House@4edde6e5
, Molly the cat[io.Animal@1e80bfe8],io.House@4edde6e5
]
animals2: [Bosco the dog[io.Animal@1b2c6ec2],io.House@4edde6e5
, Ralph the hamster[io.Animal@70177ecd],io.House@4edde6e5
, Molly the cat[io.Animal@1e80bfe8],io.House@4edde6e5
]
animals3: [Bosco the dog[io.Animal@66a29884],io.House@4769b07b //系统无法知道流内的对象是第一个流的对象的别名,因此会产生除完全不同的对象网
, Ralph the hamster[io.Animal@cc34f4d],io.House@4769b07b
, Molly the cat[io.Animal@17a7cec2],io.House@4769b07b
]
 */

保存系统状态,最安全对做法是 将其作为“原子”操作进行序列化

package io;

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

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

    @Override
    public void setColor(int newColor) { color = newColor;}

    @Override
    public int getColor() { return color;}
}

class Square extends Shape{
    private static int color;// 静态成员变量无初值


    public Square(int xVal, int yVal, int dim) {
        super(xVal, yVal, dim);
        color = RED; // 构造函数内 设置静态成员变量值
    }

    @Override
    public void setColor(int newColor) { color = newColor;}

    @Override
    public int getColor() { return color;}
}

class Line extends Shape{
    private static int color = RED; // 静态成员变量有初值

    public static void serializeStaticState(ObjectOutputStream os) throws IOException {
        os.writeInt(color);
    }
    public static void derializeStaticState(ObjectInputStream is) throws IOException {
        color = is.readInt();
    }

    public Line(int xVal, int yVal, int dim) {
        super(xVal, yVal, dim);
    }

    @Override
    public void setColor(int newColor) { color = newColor;}

    @Override
    public int getColor() { return color;}
}

public class StoreCADState {
    static String outFile = "/Users/erin/JavaProject/thinking_in_java_example/src/main/java/io/StoreCADState.out";
    public static void main(String[] args) throws IOException {
        List<Class<? extends Shape>> shapeTypes = new ArrayList<>();
        shapeTypes.add(Circle.class);
        shapeTypes.add(Square.class);
        shapeTypes.add(Line.class);
        List<Shape> shapes = new ArrayList<>();
        for (int i = 0; i < 10; i++)
            shapes.add(Shape.randomFactory());

        // 设置全部 颜色 为绿色
        for (int i = 0; i < 10; i++)
            shapes.get(i).setColor(Shape.GREEN);

        ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream(outFile));
        out.writeObject(shapeTypes); // 保存class对象
        Line.serializeStaticState(out);
        out.writeObject(shapes);// 保存几何形状

        System.out.println(shapes);
    }
}
/*
[class io.Circlecolor[3] xPos[70] yPos[7] dim[19]
, class io.Squarecolor[3] xPos[86] yPos[65] dim[56]
, class io.Linecolor[3] xPos[36] yPos[56] dim[20]
, class io.Circlecolor[3] xPos[94] yPos[35] dim[0]
, class io.Squarecolor[3] xPos[41] yPos[99] dim[88]
, class io.Linecolor[3] xPos[64] yPos[69] dim[79]
, class io.Circlecolor[3] xPos[53] yPos[21] dim[19]
, class io.Squarecolor[3] xPos[28] yPos[86] dim[75]
, class io.Linecolor[3] xPos[90] yPos[85] dim[16]
, class io.Circlecolor[3] xPos[20] yPos[65] dim[63]
]
 */


package io;

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.util.List;

public class RecoverCADState {
    static String outFile = "/Users/erin/JavaProject/thinking_in_java_example/src/main/java/io/StoreCADState.out";

    public static void main(String[] args) throws IOException, ClassNotFoundException {
        ObjectInputStream in = new ObjectInputStream(new FileInputStream(outFile));

        List<Class<? extends Shape>> shapeTypes = (List<Class<? extends Shape>>)in.readObject();
        Line.derializeStaticState(in);
        List<Shape> shapes = (List<Shape>) in.readObject();
        System.out.println(shapes);

    }
}
/*
[class io.Circlecolor[1] xPos[70] yPos[7] dim[19]
, class io.Squarecolor[0] xPos[86] yPos[65] dim[56]
, class io.Linecolor[3] xPos[36] yPos[56] dim[20]
, class io.Circlecolor[1] xPos[94] yPos[35] dim[0]
, class io.Squarecolor[0] xPos[41] yPos[99] dim[88]
, class io.Linecolor[3] xPos[64] yPos[69] dim[79]
, class io.Circlecolor[1] xPos[53] yPos[21] dim[19]
, class io.Squarecolor[0] xPos[28] yPos[86] dim[75]
, class io.Linecolor[3] xPos[90] yPos[85] dim[16]
, class io.Circlecolor[1] xPos[20] yPos[65] dim[63]
]
 */
  

13 XML

对象序列化 只是java的解决方案,只有java程序才能反序列化。

XML太流行,用XML编程的选择太多,包括JDK的javax.xml.*

我们选择开源的XOM类库,最简单,直观 产生和修改XML,并强调XML的正确性。

http://www.xom.nu/ 获得

<dependency>
  <groupId>xom</groupId>
  <artifactId>xom</artifactId>
  <version>1.3.2</version>
</dependency>
package io;
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;
    }
    // 从这个Person对象生成XML元素
    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;
    }

    // 从XML 还原 Person
    public Person(Element person){
        first = person.getFirstChildElement("first").getValue();
        last = person.getFirstChildElement("last").getValue();
    }

    public String toString(){ return first + " " + last; }

    public static void format(OutputStream os,Document doc) throws IOException {
        Serializer serializer = new Serializer(os,"ISO-8859-1");
        serializer.setIndent(4);
        serializer.setMaxLength(60);
        serializer.write(doc);
        serializer.flush();
    }

    static String outPath = "/Users/erin/JavaProject/thinking_in_java_example/src/main/java/io/Person.xml";
    public static void main(String[] args) throws IOException {
        List<Person> 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(outPath)),doc);
    }
}
/*
[Dr. Bunsen Honeydew, Gonzo The Great, Phillip J. Fry]
<?xml version="1.0" encoding="ISO-8859-1"?>
<people>
    <person>
        <first>Dr. Bunsen</first>
        <last>Honeydew</last>
    </person>
    <person>
        <first>Gonzo</first>
        <last>The Great</last>
    </person>
    <person>
        <first>Phillip J.</first>
        <last>Fry</last>
    </person>
</people>
 */

反序列化Person

package io;

        import nu.xom.*;

        import java.io.IOException;
        import java.util.ArrayList;

public class People extends ArrayList<Person> {
    public People(String fileName) throws Exception {
        Document doc = new Builder().build(fileName);
        Elements elements = doc.getRootElement().getChildElements();
        for (int i = 0; i < elements.size();i++)
            add(new Person(elements.get(i)));
    }

    static String outPath = "/Users/erin/JavaProject/thinking_in_java_example/src/main/java/io/Person.xml";

    public static void main(String[] args) throws Exception {
        People p = new People(outPath);
        System.out.println(p);
    }
}
/*
[Dr. Bunsen Honeydew, Gonzo The Great, Phillip J. Fry]
 */

14 Preferences

自动存储和读取信息,用于小的,受限的数据集合。

只能存储基本类型和字符串,且每个字符串长度不能超过8K

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

package io;

import java.util.prefs.Preferences;

public class PreferencesDemo {
    public static void main(String[] args) throws Exception {
        // systemNodeForPackage  通用的安装配置 userNodeForPackage 个别用户的偏好
        Preferences prefs = Preferences.userNodeForPackage(PreferencesDemo.class);//标识结点 非静态方法中,通常使用getClass
        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);
        usageCount++;
        prefs.putInt("UsageCount",usageCount);//每运行一次会增加,windows 存注册表 mac 存 /Users/erin/Library/Preferences/com.apple.java.util.prefs.plist
        for (String key: prefs.keys())
            System.out.println(key + ":" + prefs.get(key,null));
        System.out.println("How many companions does Dorothy hava ?" + prefs.getInt("Companions",0  ));
    }
}
/*
Footwear:Ruby Slippers
Are there witches?:true
Location:Oz
Companions:4
UsageCount:4
How many companions does Dorothy hava ?4
 */

15 总结

控制台,文件,内存块,网络

上一篇:Thinking in java Chapter5


下一篇:4C: