Java的io通过java.io
包下的类和接口支持。主要有输入、输出流,又分为字节流和字符流。Java的io流使用了一种装饰器设计模式,他将io流分为底层节点流和上层处理流,节点流用于和底层的物理存储结点直接关联。
Java7在java.nio
及其子包下提供了一系列全新的api,是对原有新io的升级。
一、File类
File类是java.io
包下代表与平台无关的文件和目录。File能新建、删除、重命名文件和目录,不能访问文件本身内容。
访问文件和目录
访问文件名的方法
- String getName()
- String getPath()
- File getAbsoluteFile()
- String getAbsolutePath()
- String getParent()
- boolean renameTo(FIle newName)
文件检测相关的方法
- boolean exists()
- boolean canWrite()
- boolean canRead()
- boolean isFile()
- boolean isDirectory()
- boolean isAbsolute()
获取常规文件信息
- long lastModified()
- long length()
文件操作相关的方法
- boolean createNewFile()
- boolean delete()
- static File createTempFile(String prefix, String suffix)
- static File CreateTempFIle(String prefix, String suffix, File directory)
- void deleteOnExit()
目录操作相关的方法
- boolean mkdir()
- String[] list()
- File[] listFiles()
- static File[] listRoots()
文件过滤器
File类的list()
方法可以接收一个FilenameFilter
参数,通过该参数可以只列出符合条件的文件。
FilenameFilter
接口和javax.swing.filechooser
包下的FileFilter
抽象类的功能非常相似。
二、Java的IO流
流的分类
输入流和输出流
抽象基类:
InputStream和Reader
OutputStream和Writer
字节流和字符流
字节流:8位,InputStream和OutputStream
字符流:16位,Reader和Writer
节点流和处理流
向一个特定的io设备读写数据的流为节点流,Low Level Stream
处理流对于已有的节点流进行连接和封装,通过封装后的流实现数据读/写功能。High Level Stream
处理流是一种典型的装饰器设计模式,也称为包装流
流的概念模型
io流涉及40多个类。
三、字节流和字符流
InputStream和Reader
InputStream接口的方法:
- int read()
- int read(byte[] b)
- int read(byte[] b, int off, int len)
Reader接口的方法:
- int read()
- int read(char[] cbuf)
- int read(char[] cbuf, int off, int len)
读取文件:FileInputStream,FileReader
还有一些移动指针的方法:
- void mark(int readAheadLimit)
- boolean markSupported()
- void reset()
- long skip(long n)
OutputStream和Writer
提供的方法
- void write(int c)
- void write(byte[]/char[]/String buf)
- void write(byte[]/char[]/String buf, int off, int len)
Writer可以使用String
代替char[]
四、输入/输出流体系
处理流的用法
使用处理流包装节点流,程序通过处理流执行输入/输出功能,让节点流与底层的IO设备、文件交互。
所有节点流都是以物理io节点作为构造器参数的。其他的流为处理流。
处理流的优点
- 操作简单
- 执行效率更高
PrintStream
的输出功能很强大,System.out
的类型就是PrintStream
在使用处理流包装了底层节点流之后,关闭输入/输出流资源时,只要关闭最上层的处理流即可。
输入/输出流体系
规则:
输入输出是文本,则考虑使用字符流;是二进制,则使用字节流
转换流
InputStreamReader,OutputStreamWriter
将字节流转换为字符流。使处理更方便。
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
public class Test{
public static void main(String[] args) {
try(
InputStreamReader reader = new InputStreamReader(System.in);
BufferedReader br = new BufferedReader(reader);
){
String line = null;
while((line = br.readLine() != null){
if (line.equals("exit")){
System.exit(1);
}
System.out.println(line);
}
}
catch(IOException e){
e.printStackTrace();
}
}
}
BufferedReader有一个readLine()方法,很方便,所以经常把读取文本内容的流包装成BufferedReader
推回输入流
PushbackInputStream和PushbackReader。
提供的方法:
- void unread(byte[]/char[] buf)
- void unread(byte[]/char[] b, int off, int len)
- void unread(int b)
五、重定向标准输入/输出
Java的标准输入/输出分别通过Systemin
和System.out
代表。在默认情况下代表键盘和显示器。
在System
类里包含三个重定向标准输入/输出的方法:
- static void setErr(PrintStream err)
- static void setIn(InputStream in)
- static void setOut(OutputStream out)
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.PrintStream;
public class Test{
public static void main(String[] args) {
try(
PrintStream ps = new PrintStream(new FileOutputStream("out.txt"));
){
System.setOut(ps);
System.out.println();
}
catch(IOException e){
e.printStackTrace();
}
}
}
import java.io.FileInputStream;
import java.io.IOException;
import java.util.Scanner;
public class Test{
public static void main(String[] args) {
try(
FileInputStream fis = new FileInputStream("test.txt");
){
System.setIn(fis);
Scanner s = new Scanner(System.in);
s.useDelimiter('\n');
while(s.hasNext()){
System.out.println(s.next());
}
}
catch(IOException e){
e.printStackTrace();
}
}
}
六、RandomAccessFile
可以读写文件。支持"随机访问"。
只访问文件的部分内容,可以选择使用该流。
追加内容也应该选择。
只能读写文件。
- long getFilePointer()
返回文件记录指针的当前位置
- void seek(long pos)
将文件记录指针定位到pos位置
包含了InputStream的read方法和OutputStream的write方法。
创建RandomAccessFile除了需要一个String
或File
还需要一个mode
参数。
- "r":只读。
- "rw":读写
- "rws":读写,每个更新同步写入到底层存储设备
- "rwd":读写,同步写入到底层设备。
import java.io.IOException;
import java.io.RandomAccessFile;
public class Test{
public static void main(String[] args) {
try(
RandomAccessFile ra = new RandomAccessFile("Test.java", "r");
){
ra.seek(300);
byte[] buf = new byte[1024];
int hasRead = 0;
while((hasRead = ra.read(buf))>0){
System.out.println(new String(buf, 0, hasRead));
}
}
catch(IOException e){
e.printStackTrace();
}
}
}
追加内容:
import java.io.IOException;
import java.io.RandomAccessFile;
public class Test{
public static void main(String[] args) {
try(
RandomAccessFile ra = new RandomAccessFile("Test.java", "r");
){
ra.seek(ra.length());
ra.write("\r\nHello world".getBytes());
}
catch(IOException e){
e.printStackTrace();
}
}
}
向指定文件、指定位置插入内容:
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.RandomAccessFile;
public class Test{
public static void insert(String filename, long pos, String content) throws IOException{
File temp = File.createTempFile("temp", null);
temp.deleteOnExit();
try(
RandomAccessFile ra = new RandomAccessFile(filename, "rw");
FileOutputStream fos = new FileOutputStream(temp);
FileInputStream fis = new FileInputStream(temp);
){
raf.seek(pos);
byte[] buf = new byte[64];
int hasRead = 0;
while((hasRead=ra.read(buf))>0){
fos.write(buf, 0, hasRead);
}
raf.seek(pos);
raf.write(content.getBytes());
while((hasRead=fis.read(buf))>0){
raf.write(buf, 0, hasRead);
}
}
}
public static void main(String[] args) {
try{
Test.insert("test.java", 45, "Hello World\r\n");
}
catch(IOException e){
e.printStackTrace();
}
}
}
七、NIO
Java7对原来的io进行了重大改进:
- 提供了全面的文件IO和文件系统访问支持
- 基于异步Channel的IO
第一个体现为java.nio.file
包及其子包
第二个体现为java.nio.channels
包下面增加了多个以Asynchronous
开头的Channel
接口和类。
Path、Paths和Files核心api
早期只有File访问文件系统,但File类的功能比较有限,不能利用特定文件系统的特性;FIle提供的方法的性能不高,其方法在出错时返回失败,但不提供异常信息。
NIO为了弥补这种不足,引入了一个Path
接口,代表一个平台无关的平台路径。Files
和Paths
两个工具类。
Files包含大量静态工具方法来操作文件,Paths包含两个返回Path的静态工厂方法。
import java.nio.file.Path;
import java.nio.file.Paths;
public class Test{
public static void main(String[] args) {
Path path = Paths.get(".");
System.out.println(path.getNameCount());
Path abPath = path.toAbsolutePath();
System.out.println(abPath.getRoot());
//c:/test
Path path2 = Paths.get("c", "test");
}
}
import java.io.FileOutputStream;
import java.nio.charset.Charset;
import java.nio.file.FileStore;
import java.nio.file.Files;
import java.nio.file.Paths;
public class Test{
public static void main(String[] args) {
Files.copy(Paths.get("Test.java"), new FileOutputStream("a.txt"));
System.out.println(Files.isHidden(Pahts.get("Test.java")));
List<String> lines = Files.readAllLines(Paths.get("Test.java"), Charset.forName("gbk"));
System.out.println(Files.size(Paths.get("test.java")));
Files.write(Paths.get("test.txt"), lines, Charset.forName("gbk"));
Files.list(Paths.get("."));
Files.lines(Paths.get("Test.java"), Charset.forName("gbk"));
FileStore cStore = Files.getFileStore(Paths.get("c:"));
cStore.getTotalSpace();
cStore.getUsableSpace();
}
}
使用FileVisitor遍历文件和目录
- walkFileTree(Path start, FileVisitor<? super Path> visitor)
- walkFileTree(Path start, Set<FileVisitOpeion> options, int maxDepth, FileVisitor<? super Path> visitor)
FileVisitor定义了如下4个方法:
- FileVisitResult postVisitDirectory(T dir, IOException exc)
- FileVisitResult preVisitDirectory(T dir, BasicFileAttributes attrs)
- FileVisitResult visitFIle(T file, BasicFileAttributes attrs)
- FileVisitResult visitFileFailed(T file, BasicFileAttributes attrs)
FileVisitResult
是一个枚举类,代表了访问之后的后续行为。
- CONTINUE
- SKIP_SIBLINGS
- SKIP_SUBTREE
- TERMINATE
实际编程可以通过继承SimpleFileVisitor
(FileVisitor
的实现类)实现自己的文件访问器可以根据需要选择性重写指定方法。
import java.io.IOException;
import java.nio.file.FileVisitResult;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.nio.file.SimpleFileVisitor;
import java.nio.file.attribute.BasicFileAttributes;
public class Test{
public static void main(String[] args) throws IOException{
//c:/test
Files.walkFileTree(Paths.get("c:", "test"), new SimpleFileVisitor<Path>(){
//访问文件时触发的方法
@Override
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException{
return FileVisitResult.CONTINUE;
}
//开始访问目录时触发该方法
@Override
public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException{
return FileVisitResult.CONTINUE;
}
});
}
}
使用WatchService
监控文件变化
早期需要后台启用一个线程每隔一段时间去遍历一次指定目录的文件。繁琐,性能不好
Path类提供了如下一个方法监控文件系统的变化:
- register(WatchService watcher, WatchEvent.Kind<?>... events)
WatchService
代表一个文件系统监听服务,它负责监听path
代表目录下的文件变化。一旦使用register
方法完成注册,接下来可以调用WatchService的如下三个方法获取监听目录的文件变化:
- WatchKey poll()
获取一个WatchKey,如果没有WatchKey发生,则返回null
- WatchKey poll(long timeout, TimeUnit unit)
尝试等待timeout时间去获取下一个WatchKey
- WatchKey take()
获取下一个WatchKey,如果没有则一直等待
如果程序需要一直监控,应该选用take
方法,否则可以选用poll
方法
import java.nio.file.FileSystems;
import java.nio.file.WatchService;
import java.nio.file.Paths;
import java.nio.file.StandardWatchEventKinds;
import java.nio.file.WatchEvent;
import java.nio.file.WatchKey;
public class Test{
public static void main(String[] args) {
WatchService watchService = FileSystems.getDefault().newWatchService();
Paths.get("c:/").register(watcherService,
StandardWatchEventKinds.ENTRY_CREATE,
StandardWatchEventKinds.ENTRY_DELETE,
StandardWatchEventKinds.ENTRY_MODIFY);
while(true){
WatchKey key = watchService.take();
for(WatchEvent<?> event:key.pollEvents()){
System.out.println(event.context()+" 文件发生了 "+event.kind()+" 事件!");
}
//重设watchkey
boolean valid = key.reset();
if(!valid){
break;
}
}
}
}
访问文件属性
早期的File
类可以访问一些简单的文件属性。获取或者修改更多会很难。
Java7下面的java.nio.file.attribute
包下面提供了大量的工具类,通过这些工具类,可以简单方便的读取、修改文件属性。这些工具类主要分为一下两类:
- XxxAttributeView
代表某种文件属性的"视图"
- XxxAttributes
代表某种文件属性的"集合",程序一般通过XxxAttributeView获取XxxAttributes
FileAttributeView
是其他view的父接口。
AclFileAttributeView
可以为特定文件设置ACL(access control list)及文件所有属性。getAcl(),setAcl(List)
BasicFileAttributeView
获取和修改文件的基本属性,包括文件的最后修改时间、最后访问时间、创建时间、大小、是否为目录、是否为符号链接等。readAttributes()放回BasicFileAttributes对象
DosFileAttributeView
主要用于获取或修改文件dos相关属性。是否只读、是否隐藏、是否为系统文件、是否是存档文件等。
FileOwnerAttributeView
获取或修改文件的所有者
PosixFileAttributeView
主要用于修改POSIX(portable operating system interface of unix)属性。
UserDefinedFileAttributeView
让开发者为文件设置一些自定义属性