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.0 | Java 1.1 |
---|---|
InputStream | Reader |
OutputStream | Writer |
FileInputStream | FileReader |
FileOutputStream | FileWriter |
StringBufferInputStream(已经弃用) | StringReader |
没有对应类 | StringWriter |
ByteArrayInputStream | CharArrayReader |
ByteArrayOutputStream | CharArrayWriter |
PipedInputStream | PipedReader |
PipedOutputStream | PepedWriter |
装饰器改变
FilterInputStream | FilterReader |
---|---|
FilterOuputStream | FilterWriter |
BufferedInputStream | BufferedReader |
BufferedOutputStream | BufferedWriter |
DataInputStream | ??使用readLine()时,不能使用这个类,使用BufferedWriter |
PrintStream | PrintWriter |
LineNumberInputStream | LineNumberReader |
StreamTokenizer | StreamTokenizer |
PushbackInputStream | PushbackReader |
未发生变化
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。
CheckedInputStream | GetCkeckSum()为任何InputStream产生校验和(不仅是解压缩) |
---|---|
CheckedOutputStream | GetCheckSum()为任何OutputStream产生校验和(不仅是压缩) |
DeflaterOutputStream | 压缩类的基类 |
ZipOutputStream | 一个DeflaterOutputStream,用于将数据压缩成zip文件格式 |
GZIPOutputStream | 一个。。。。,GZIP格式 |
InflaterInputStream | 解压缩基类 |
ZipInputStream | InflaterInputStream派生类, |
GZIPInputStream | InflaterInputStream派生类, |
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
用于存储和读取用户的偏好一级程序配置项的设置