File
File类,文件路径,既代表了一个特定文件的名称,有能代表一个目录下的一组文件的名称(list()方法,返回文件名的字符串数组)。
- 目录过滤器 java.io.FilenameFilter
过滤器包含有accept()方法,返回一个布尔值,表示是否接受该文件。file.list(filenameFilter)获取符合过滤器的文件名。 - 目录实用工具
public final class Directory{
//过滤目录下的文件
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(new File(name).getName()).matches();
}
});
}
//重载
public static File[] local(String path, final String regex){
return local(new File(path), regex);
}
}
//
public static class TreeInfo implements Iterable{
public List files = new ArrayList();
public List dirs = new ArrayList();
//默认迭代器是files的迭代器
public Iterator iterator(){
return files.iterator();
}
void addAll(TreeInfo other){
files.addAll(other.files);
dirs.addAll(other.dirs);
}
//walk():递归的查找某个目录下的所有文件
public static TreeInfo walk(File start, String regex){
return recurseDirs(start, regex);
}
public static TreeInfo walk(File start){
return recurseDirs(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;
}
}
File对象有很多作用,还可以创建新的目录和删除文件等等,可以查看api全面了解一下。
输入和输出
输入:自Inputstream或Reader派生而来的类都含有名为read()的基本方法,用于读取单个字节或者字节数组。
输出:自OutputStream或Writer派生而来的类都含有名为write()的基本方法,用于写单个字节或字节数组。
1.InputStream类型
数据源:字节数组、String对象、文件、pipe、由其他种类的流组成的序列,其他数据源,比如Internet连接。不同数据源有相应的子类。
类 | 功能 | 使用 |
---|---|---|
ByteArrayInputStream | 允许将内存的缓冲区当做InnputStream使用 | 缓冲区,字节从中取出。一种数据源,将其与FilterInputStream对象相连以提供有用接口 |
StringBufferInputStream | 将String换成InputStream | 字符串,底层实现实际使用StringBuffer。一种数据源,将其与FilterInputStream对象相连以提供有用接口 |
FileInputStream | 用于从文件中读取信息 | 字符串表示文件名、文件或FileDescriptor对象。一种数据源,将其与FilterInputStream对象相连以提供有用接口 |
PipedInputStream | 产生用于写入相关PipedOutputStream的数据,实现管道化 | PipedOutputStream作为多线程中数据源;将其与FilterInputStream对象相连以提供有用接口 |
SequenceInputStream | 将两个或多个InputStream对象转换成单一InputStream | 多个Input对象的容器Enumertation;一种数据源,将其与FilterInputStream对象相连以提供有用接口 |
FilterInputStream | 抽象类,作为“装饰器”的接口,装饰器为其他InputStream提供有用功能 | --见下表 |
- OutputStream类型
类 | 功能 | 使用 |
---|---|---|
ByteArrayOutputStream | 在内存中创建缓冲区。所有送往“流”的数据都要放置在此缓冲器 | 缓冲区初始化尺寸,用于指定数据的目的地。将其与FilterOutputStream对象相连以提供有用接口 |
FileOutputStream | 用于将信息写入文件 | 字符串表示文件名、文件或FileDescriptor对象。将其与FilterOutputStream对象相连以提供有用接口 |
PipedOutputStream | 任何写入其中的信息都会自动作为PipedOutputStream的输出,实现管道化 | PipedInputStream指定用于多线程数据的目的地;将其与FilterOutputStream对象相连以提供有用接口 |
FilterOutputStream | 抽象类,作为“装饰器”的接口,装饰器为其他OutputStream提供有用功能 | --见下表 |
添加属性和有用的接口
因为Java I/O类库需要多种不同功能的组合,所以需要使用装饰器模式。装饰器必须具有和它所装饰对象相同的接口。FilterXXXX两个输入输出流就是装饰器的必要条件。
- FIlterInputStream类型(FilterReader):
类 | 功能 | 使用 |
---|---|---|
DataInputStream | 读取不同的基本类型数据以及String对象 | 所有方法都以“read”开头,如readByte()。。。 |
BufferedInputStream | 代表使用缓冲区,防止每次读取时都得进行实际写操作 | 可以指定缓冲区大小,本质上不提供接口,只不过是向进程中添加缓冲区所必须的,与接口对象搭配 |
LineNumberInputStream(已弃用)(LineNumberReader) | 跟踪输入流的行号 | 调用getLineNumber()和setLineNumber() |
PushbackInputStream | 具有“能弹出一个字节的缓冲区”,因此可以将督导的最后一个字符回退 | 通常作为编译器的扫描器,java编译器需要 |
- FilterOutPutStream(FilterWriter抽象类,没有子类)向OutputStream写入
类 | 功能 | 使用 |
---|---|---|
DataOutputStream | 与DataInputStream搭配使用,写入基本类型数据,处理数据的存储 | writeByte()、writeFloat() |
PrintStream | 用于产生格式化输出。处理显示 | print()和println()。捕捉所有IOExceptions,所以必须使用checkErrors()->true有错,自行测试错误状态。可以使用布尔值指示是否每次换行时清空缓冲区 |
BufferedOutputStream | 避免每次发送数据都要进行写操作,可以使用flush()清空缓冲区 | 可指定缓冲区大小。本质上并不提供接口,只不过是向进程中添加缓冲区所必需的,与接口对象搭配 |
Reader和Writer
Stream面向字节,Reader和Writer面向兼容Unicode的字符I/O,国际化。有时,使用适配器类:InputStreamReader将InputStream转换为Reader,OutputStreamWriter将OutoutStream转换为Writer。一般先用Reader和Writer,不行的话再用stream。
来源与去处:java 1.0类 | 相应的Java1.1类 |
---|---|
FileInputStream | FileReader |
FileOutputStream | FileWriter |
StrngBufferInputStream(已弃用) | StringReader |
ByteArrayInputStream | CharArrayReader |
ByteArrayOutputStream | CharArrayWriter |
PipedInputStream | PipedReader |
PipedOutputStream | PipedWriter |
更改流的行为:
过滤器:Java 1.0类 | 相应的Java 1.1类 |
---|---|
FilterInputStream | FilterReader |
FilterOutputStream | FilterWriter(抽象类,没有子类) |
BufferedInputStream | BufferedReader(也有readLine()) |
BufferedOutputStream | BufferedWriter |
DataInputStream | 使用DataInputStream,除了当使用readLine()时之外,这时应该使用BufferedReader |
PrintStream | PrintWriter,接口一样,接受OutputStream和Writer的构造器 |
LineNumberInputStream(已弃用) | LineNumberReader |
StreamTokenizer | StreamTokenizer(使用接受Reader的构造器) |
PushbackInputStream | PushbackReader |
自我独立的类:RandomAccessFile
RandomAccessFile继承了DataInput、DataOutput这两个接口,提供的对文件内容的访问,它既可以读文件,也可以写文件,并且RandomAccessFile支持随机访问文件,可以指定位置进行访问。
与File的区别最根本的是在Java中Class File代表的是“文件/目录”本身,可以想象成是一个文件句柄、标识,这个类本身只提供对文件的打开,关闭,删除,属性访问等等;而RandomAccessFile类则是文件访问的类,从类名可以看出它是一种文件访问方法:随机访问文件。这样就很好理解,比如在vi或者notepad中你的光标随意游走,改变一些内容,然后保存,关闭等等,这些是它的功能。
作用:
1.断点续传;2.RandomAccessFile可以提高读取的速度。根据文件的hashcode生成一个位置存入文件,取得时候再反过来根据这个固定的位置直接就能找到文件。
jdk1.4中大多数功能被nio存储映射取代。
I/O流的使用方式
- 缓冲输入文件
public class BufferedInputFile{
public static String read(String filename) throws IOException{
//reading input by lines
BufferedReader in = new BufferedReader(new FileReader(filename));
String s;
StringBuffer sb = new StringBuffer();
while((s = in.readLine() ) != null){
sb.append(s + "\n");
}
in.close();
return sb.toString();
}
}
- 从内存输入
用上面的BufferedInputFile.read()读入的String结果被用来创建一个StringReader。然后调用read()每次读入一个字符,发送至控制台
public static String read(String filename) throws IOException{
StringReader in=new StringReader(BufferedInputFile(filename));
int c;
while((c=in.read())!= -1){
System.put.print((char) c);
}
}
- 格式化内存输入
要读取格式化数据,可以使用DataInputStream。
public static void main throws IOException{
try{
DataInputStream in = new DataInputStream(
new ByteArrayInputStream( //必须为ByteArrayInputStream提供字节数组
new BufferedInputFile("xxxx.java").getBytes()));
while(true)
print((char)in.readByte());
} catch(EOFException e){//一个IO异常的子类,名字也是END OF FILE的缩写,表示流的末尾
print("End of Stream");
}
}
使用DataInputStream用readByte()读取字节,任何字节都是合法结果,返回值不能用来检测输入是否结束。可以使用available()查看还有多少可供存取的字符。注意,available()会随着所读取的媒介类型不同而不同,字面意思是没有阻塞的情况下所能读取的字节数,对于文件,可能意味着整个文件,对其他可能不同。
public static void main throws IOException{
DataInputStream in = new DataInputStream(
new BufferedInputStream(
new FileInputStream("xxx.java")));
while(in.available() != 0)
print((char)in.readByte());
}
- 基本文件输出
FileWriter可以向文件写入数据,为了提供格式化机制,被装饰城PrintWriter。这种方式创建的文件可作为普通文本文件读取。
public static void main throws IOException{
BufferReader in = new BufferReader(
new StringReader(
BufferedInputFile.read("xxx.java")));
PrintWriter out = new PrintWriter(
new BufferedWriter( new FileWriter("xxxx.out")));
//PrintWriter添加了辅助构造器,可以直接创建文本,并缓冲写入
// PrintWriter out = new PrinterWriter("xxxx.out");
int lineCount = 1;
String s;
while((s = in.readLine() )!= null)
out.println(lineCount++ + ": " + s);
out.close();
//显示存入的文件
print(new BufferedInputFile("xxxx.out"));
}
- 存储和恢复数据
当使用DataOutputStream写入数据,java保证可以使用DataInputStream准确读取数据。做法就是使用UTF-8编码,writeUTF()和readUTF()。java使用的是UTF-8辩题,非java程序读取需要编写一些特殊代码才能读取字符串。但如果用writeDouble()将double类型写入流,需要用readDouble()恢复。但可能被作为其他类型读入。因此,要么为文件中的数据采用固定的格式,要么将额外信息保存如文件中,以便能够解析以确定数据的存放文职。因此,对象序列化和XML可能是更容易的存储和读取复杂数据结构的方式。
public static void main throws IOException{
DataOutputStream out = new DataOutputStream(
new BufferedOutputStream(
new FIleOutputStream("Data.txt")));
out.writeDouble(3.14159);
out.writeUTF("that wa pi");
out.writeDouble(1.41413);
out.writeUTF("Square root of 2");
out.close();
DataInputStream in = new DataInputStream(
new BufferedInputStream(
new FileInputStream("Data.txt"))):
print(in.readDouble());
//注意读入方式
print(in.readUTF());
print(in.readDouble());
print(in.readUTF());
}
- 读写随机访问文件
RandomAccessFile组合了DataInput和DataOutput接口,并可以用seek()在文件中到处移动,并修改文件中的值。不支持装饰。
static String file = "rtest.dat";
static void display() throws IOException{
RandomAccessFile rf = new RandomAccessFile(file, "r");
for(int i=0; i<7; i++)
println("Value " + i + ": " + rf.readDouble());
println(rf.readUTF());
rf.close();
}
public stativ 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();
dispaly();
rf = new RandomAccessFile(file, "rw");
rf.seek(5*8);//double8字节长
rf.writeDouble(47.001);
rf.close();
dispaly();
}
}
- 管道流
PipedInputSream等管道流用于任务之间的通信,在多线程用处很大。
标准I/O
标准I/O参照Unix中“程序所使用的单一信息流”这一概念,程序的所有输入都可以来自标准输入,所有输出也可以发送到标准输出,所有错位信息都可以发送到标准错误。意义在于,方便将程序串联起来,像管道一样。
java提供System.in, System.out, System.err。
- 从标准输入中读取
为了使用readLine(),BufferedReader要求使用InputStreamReader把System.in转换成Reader。
public stativ void main(String[] args) throws IOException{
BufferedReader stdin = new BufferedReader(
new InputSreamReader(System.in));
String s;
while( (s = stdin.readLine()) != null && s.length!=0)
System.out.println(s);
//空行或者Ctrl-z终止程序
}
- 将System.out转换成PrintWriter
System.out是个PrintStream,PrintStream是一个OutputStream。PrintWriter有一个接受OutputSream作为参数的构造器。
PrintWriter out = new PrintWriter(System.out, true);
- 标准I/O重定向
System类提供静态方法,以将标准输入输出重定向:
· setIn(InputStream)
· setOut(PrintStream)
· setErr(PrintStream)
进程控制
若要在java内部执行其他操作系统的程序,并且要控制这些程序的输入和输出,则需要进程控制。
比如运行程序,将输出发送到控制台。可以使用java.lang.ProcessBuilder。在使用时,可能会产生两种类型的错误,普通的导致异常的错误——抛出运行时异常,还有从进程自身的执行过程中产生的错误,可以用单独的异常报告来记录。
public static void command(String command){ //命令字符串
try{
boolean err = false;
Process process = new ProcessBuilder(command.split(" ")).start();
BufferReader results = new BufferReader(
new InputStreamReader(process.getInputStream()));
String s;
while((s = results.readLine()) != null)
println(s);
BufferedReader errors = new BufferedReader(
new InputStreamReader(process.getErrorStream()));
while((s = errors.readLine()) != null){
println(s);
err = true;
}
} catch(Exception e){
throw new RuntimeException(e);
}
NIO
实际上,java旧的I/O包已经使用nio重新实现过,以提高速度。速度的提高来自于所使用的结构更接近与操作系统执行I/O的方式:通道和缓冲器。直接与缓冲器交互,将缓冲器派送到通道。
唯一直接与通道交互的缓冲器是ByteBuffer——存储未加工字节(8字节)。将字节存放于ByteBuffer的方法:一是“put”直接填充一个或多个字节或基本数据类型的值。二是warp()将已存在的字节数组包装进ByteBuffer中。
FileInputStream、FileOutputStream、RandomAccessFile可以使用getChannel()产生FileChannel( 一个读,写,映射,操作文件的通道)。
public class GetChannal {
private static final int BSIZE = 1024;
public stativ void main(String[] args) throws IOException{
//写文件
FileChannel fc =new FileOutputStream("data.txt").getChannel();
fc.write(ByteBuffer.wrap("some text".getBytes()));
fc.close();
//将内容添加到文件尾部
fc = new RandomAccessFile("data.txt").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);//allocateDirect()更快,资源消耗更多
fc.read(buff);// FileChannel 向ByteBuffer存储字节
buff.flip();//must 准备
while(buff.hasRemaining())
print((char)bugg.get());
}
}
读写例子:
.....
FileChannel in = new FileInputStream("in.txt").getChannel(),
out = new FileInputStream("out.txt").getChannel();
ByteBuffer buffer = ByteBuffer.allocate(BSIZE);
while(in.read(buffer) != -1){
buffer.flip(); //准备写
out.write(buffer); //操作之后,信息仍在缓冲器
buffer.clear(); //清理准备
}
....
使用transferTo()和transferFrom()能将两个通道相连。
...
in.transferTo(0, in.size(), out);
//或
out.transferFrom(in, 0, in.size());
转换数据:缓冲器放的是字节,要转换成字符,一是可以在输入的时候对其进行编码,二是从缓冲器输出是对其进行解码。可以使用java.nio.charset.Charset实现。
//第一种方法
String encoding = System.getProperty("file.encoding");
System.out.println("Decoded using " + encoding + ": "
+ Charset.forName(encoding).decode(buff));
//第二种
fc = new FileOutputStream("data2.txt").getChannel();
fc.write(ByteBuffer.wrap(
"Some text".getBytes("UTF-16BE")));
fc.close();
// Now try reading again:
fc = new FileInputStream("data2.txt").getChannel();
buff.clear();
fc.read(buff);
buff.flip();
System.out.println(buff.asCharBuffer());
// 使用CharBuffer 写
fc = new FileOutputStream("data2.txt").getChannel();
buff = ByteBuffer.allocate(24); // More than needed
buff.asCharBuffer().put("Some text");
fc.write(buff);
fc.close();
// 再来读
fc = new FileInputStream("data2.txt").getChannel();
buff.clear();
fc.read(buff);
buff.flip();
System.out.println(buff.asCharBuffer());
获取基本类型:
ByteBuffer bb = ByteBuffer.allocate(BSIZE);
// Allocation automatically zeroes the ByteBuffer:
int i = 0;
while(i++ < bb.limit())
if(bb.get() != 0)
print("nonzero");
print("i = " + i);
bb.rewind();
// Store and read a char array:
bb.asCharBuffer().put("Howdy!");
char c;
while((c = bb.getChar()) != 0)
printnb(c + " ");
print();
bb.rewind();
// Store and read a short:
bb.asShortBuffer().put((short)471142);//需要类型转换
print(bb.getShort());
bb.rewind();
// Store and read an int:
bb.asIntBuffer().put(99471142);
//bb.asIntBuffer().put(new int[]{1,2,4,5,});
//bb.asIntBuffer().put(1,3,5);
print(bb.getInt());
bb.rewind();
// Store and read a long:
bb.asLongBuffer().put(99471142);
print(bb.getLong());
bb.rewind();
// Store and read a float:
bb.asFloatBuffer().put(99471142);
print(bb.getFloat());
bb.rewind();
// Store and read a double:
bb.asDoubleBuffer().put(99471142);
print(bb.getDouble());
bb.rewind();
除了short都不用类型转换。asXXBuffer()获取的是视图缓冲器XXBuffer。等底层的ByteBuffer通过视图缓冲器填满了基本类型是,就 直接写到通道中了。
缓冲器有四个索引操作:mark、positon、limit和capacity。
方法 | 功能 |
---|---|
capacity() | 返回缓冲区容量 |
clear() | 清空缓冲区,position=0,limit=capacity,可以覆写缓冲区 |
flip() | limi=positon, position=0,准备从缓冲器读取写入数据 |
limit() | 返回limit值 |
limit(int lim) | 设置limit值 |
mark() | mark=position |
position() | 返回position值 |
position(int pos) | 设置position值 |
remaining() | 返回limit-position值 |
hasRemaining() | 若有介于position和limit之间的元素,则返回true |
reset() | position=mark值 |
rewind() | position=0,mark值不明确,在写完读之前用 |
内存映射文件:允许创建和修改因太大而不能放入内存的文件,假定整个文件都在内存中,当做非常大的数组来访问。BufferXXX对大文件力不从心,而MapedByteBuffer就很适合。
static int length = 0x8FFFFFF; // 128 MB
public static void main(String[] args) throws Exception {
MappedByteBuffer out = //ByteBuffer的子类
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));
}
文件加锁:
使用FileChannel调用tryLock()(非阻塞,若不能获取,则返回方法调用)或lock()(阻塞直至锁获得)就能获取整个文件的FileLock。如果返回值不为空,则可以进行操作。
压缩
压缩使用直接将输出流封装成GZIPOutputStream或ZipOutputStream,输入流封装成GZIPInputStream。。即可。批量的话有ZipEntry对每个entry操作。
对象序列化
java的对象序列化将实现了Serializable接口的对象转换成字节序列,并能通过字节序列恢复对象,可以利用这个实现轻量级持久性。这主要为了支持java的RMI和java Beans。
要序列化一个对象,首先OutputStream对象,然后将其封装再ObjectOutputStream对象内,调用writeObject()对象序列化,并将其发送到OutputStream。对象序列化是基于字节的。
若有特殊需要,可以实现Externalizable接口(继承了Serializable接口,添加了两个方法,writeExternal()和readExternal())。Externalizable对象默认不保存任何字段。
一般序列化默认无参构造器。若有参数,则需要实现Externalizable接口
public class Blip3 implements Externalizable {
private int i;
private String s; // No initialization
public Blip3() {
print("Blip3 Constructor");
// s, i not initialized
}
public Blip3(String x, int a) {
print("Blip3(String x, int a)");
s = x;
i = a;
// s & i initialized only in non-default constructor.
}
public String toString() { return s + i; }
public void writeExternal(ObjectOutput out)
throws IOException {
print("Blip3.writeExternal");
// You must do this:
out.writeObject(s);
out.writeInt(i);
}
public void readExternal(ObjectInput in)
throws IOException, ClassNotFoundException {
print("Blip3.readExternal");
// You must do this:
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();
// Now get it back:
ObjectInputStream in = new ObjectInputStream(
new FileInputStream("Blip3.out"));
print("Recovering b3:");
b3 = (Blip3)in.readObject();
print(b3);
}
}
transient关键字:若不希望对某个子对象序列化。
Externalizable替代方法
可以实现Serializable接口,并添加名为writeObject()和readObject()的私有方法。
private void writeObject(ObjectOutputStream stream) throws IOException;
private void readObject(ObjectOutputStream stream) throws IOException, ClassNotFoundException;
在方法内部,可以调用defaultWriteObjec()或者defaultReadObject()。非transient字段一定要在之前调用defaultXXX(), transient字段一定要明确保存和恢复。
public class SerialCtl implements Serializable {
private String a;
private transient String b;
public SerialCtl(String aa, String bb) {
a = "Not Transient: " + aa;
b = "Transient: " + bb;
}
public String toString() { return a + "\n" + b; }
private void writeObject(ObjectOutputStream stream)
throws IOException {
stream.defaultWriteObject();
stream.writeObject(b);
}
private void readObject(ObjectInputStream stream)
throws IOException, ClassNotFoundException {
stream.defaultReadObject();
b = (String)stream.readObject();
}
public static void main(String[] args)
throws IOException, ClassNotFoundException {
SerialCtl sc = new SerialCtl("Test1", "Test2");
System.out.println("Before:\n" + sc);
ByteArrayOutputStream buf= new ByteArrayOutputStream();
ObjectOutputStream o = new ObjectOutputStream(buf);
o.writeObject(sc);
// Now get it back:
ObjectInputStream in = new ObjectInputStream(
new ByteArrayInputStream(buf.toByteArray()));
SerialCtl sc2 = (SerialCtl)in.readObject();
System.out.println("After:\n" + sc2);
}
} /* Output:
Before:
Not Transient: Test1
Transient: Test2
After:
Not Transient: Test1
Transient: Test2
*///:~
XML
XOM类库可以实现对象xml化。还有个Serializer类将XML转换为可读性的格式。
Preferences
Preference API用于存储和读取用户的偏好以及程序配置项的设置,是一个键-值集合,简单好上手。
补充资料