字节流:InputStream, OutputStream
字符流:Reader, Writer
File
表示文件和目录路径,以及创建、删除文件和目录。
3种路径形式
File f = new File("../zc/src/Main.java");
f.getPath();//返回构造方法传入的路径 ..\zc\src\Main.java
f.getAbsolutePath();//返回绝对路径 D:\code\java\zc\..\zc\src\Main.java
f.getCanonicalPath();//和绝对路径类似,但是返回的是规范路径 D:\code\java\zc\src\Main.java
文件和目录属性
boolean isDirectory();
boolean canRead();
boolean canWrite();
boolean canExecute();//对目录而言,是否可执行表示能否列出它包含的文件和子目录
long length();//文件字节大小
创建和删除文件
//新建文件
//返回true(文件不存在,且创建成功)
//返回false(文件已存在)
//throws IOException(创建文件错误)
boolean createNewFile() throws IOException;
//删除文件或目录(目录需要为空)
//返回true(删除成功)
//返回false(删除失败)
boolean delete();
创建临时文件:
/* 如果前缀小于3个字符,则异常 */
// 提供临时文件的前缀和后缀,使用系统的临时文件目录
File f = File.createTempFile("tmp-", ".txt");
// 提供临时文件的前缀和后缀,指定临时文件存放目录(目录必须存在)
File f2 = File.createTempFile("tmp-", ".txt", new File("./tmp"));
// JVM退出时自动删除
f.deleteOnExit();
System.out.println(f.getAbsolutePath());//C:\Users\zc\AppData\Local\Temp\tmp-4816163966082070967.txt
创建和删除目录
boolean mkdir(); //创建当前File对象表示的目录
boolean mkdirs();//创建当前File对象表示的目录,并在必要时将不存在的父目录也创建出来
boolean delete();//删除当前File对象表示的目录,当前目录必须为空才能删除成功
遍历文件和目录
String[] list();//返回当前目录下的所有文件和目录,名字数组
String[] list(FilenameFilter filter);//指定过滤规则
File[] listFiles();//返回当前目录下的所有文件和目录,File数组
File[] listFiles(FilenameFilter filter);//指定过滤规则
File[] listFiles(FileFilter filter);//指定过滤规则
过滤规则:
public interface FilenameFilter {
boolean accept(File dir, String name);
}
public interface FileFilter {
boolean accept(File pathname);
}
Path
Java标准库还提供了一个Path
对象,它位于java.nio.file
包。Path
对象和File
对象类似,但操作更加简单:
Path只表示文件和目录的路径,没有创建和删除等能力。
Path p1 = Paths.get(".", "project", "study"); // 构造一个Path对象
System.out.println(p1); // .\project\study
Path p2 = p1.toAbsolutePath(); // 转换为绝对路径
System.out.println(p2); // D:\code\java\zc\.\project\study
Path p3 = p2.normalize(); // 转换为规范路径
System.out.println(p3); // D:\code\java\zc\project\study
File f = p3.toFile(); // 转换为File对象
System.out.println(f); // D:\code\java\zc\project\study
/* 遍历Path中每个子路径
* >code
* >java
* >zc
* >project
* >study
*/
for (Path p : p3) {
System.out.println(">" + p);
}
常用的流分类
分类 | 字节输入流 | 字节输出流 | 字符输入流 | 字符输出流 |
---|---|---|---|---|
抽象基类 | InputStream | OutputStream | Reader | Writer |
访问文件 | FileInputStream | FileOutputStream | FileReader | FileWriter |
访问数组 | ByteArrayInputStream | ByteArrayOutputStream | CharArrayReader | CharArrayWriter |
访问管道 | PipedInputStream | PipedOutputStream | PipedReader | PipedWriter |
访问字符串 | StringReader | StringWriter | ||
缓冲流 | BufferedInputStream | BufferedOutputStream | BufferedReader | BufferedWriter |
转换流 | InputStreamReader | OutputStreamWriter | ||
对象流 | ObjectInputStream | ObjectOutputStream | ||
过滤类(作基类用) | FilterInputStream | FilterOutputStream | FilterReader | FilterWriter |
打印流 | PrintStream | PrintWriter | ||
推回输入流 | PushbackInputStream | PushbackReader | ||
特殊流 | DataInputStream | DataOutputStream |
(1)流的分类:
- 输入流 - 输出流
- 字节流 - 字符流
- 节点流 - 处理流
节点流:提供数据源的流。
处理流:用于对一个已存在的流进行连接或封装,通过封装后的流来实现数据读/写功能。
(2)PrintStream和PrintWriter区别:
2个类的功能基本相同,PrintStream能做的PrintWriter也都能实现,并且PrintWriter的功能更为强大。但是由于PrintWriter出现的比较晚,较早的System.out使用的是PrintStream来实现的,所以为了兼容就没有废弃PrintStream。
PrintStream在输出字符,将字符转换为字节时采用的是系统默认的编码格式。而PrintWriter可以在传入Writer时OutputStreamWriter(OutputStream out, Charset cs)
可由程序员指定字符转换为字节时的编码格式。
try(resource)语法
Java 7引入的新的try(resource)
的语法,只需要编写try
语句,让编译器自动为我们关闭资源。
实际上,编译器并不会特别地为InputStream
加上自动关闭。编译器只看try(resource = ...)
中的对象是否实现了java.lang.AutoCloseable
接口,如果实现了,就自动加上finally
语句并调用close()
方法。InputStream
和OutputStream
都实现了这个接口,因此,都可以用在try(resource)
中。
关于flush()
OutputStream什么时候自动调用flush():缓冲区满、调用close()时。
读写文件
手动逐行读
static void f1() {
try (
InputStream is = new FileInputStream("./src/Main.java");
InputStreamReader isr = new InputStreamReader(is, StandardCharsets.UTF_8);
BufferedReader br = new BufferedReader(isr)
) {
String line;
while ((line = br.readLine()) != null) {
System.out.println(line);
}
} catch (IOException e) {
System.out.println("发生错误");
}
}
手动逐行写
static void f2() {
try (
OutputStream os = new FileOutputStream("./src/z.txt");
OutputStreamWriter osw = new OutputStreamWriter(os, StandardCharsets.UTF_8);
BufferedWriter bw = new BufferedWriter(osw)
) {
bw.write("one");
bw.newLine();
bw.write("two");
} catch (IOException e) {
System.out.println("发生错误");
}
}
一次性读
一次性读文件:自己创建好数组
//一次性读文件:自己创建好数组
static String readToString(String file) throws IOException {
//获取文件大小
long fileLength = new File(file).length();
//创建数组
byte[] fileContent = new byte[(int) fileLength];
//文件 -> byte[]
try (FileInputStream fis = new FileInputStream(file)) {
fis.read(fileContent); //读文件
}
//byte[] -> String
return new String(fileContent, StandardCharsets.UTF_8);
}
一次性读文件:让库返回数组(更好方案)
//一次性读文件:让库返回数组(更好方案)
static String readToString2(String file) throws IOException {
//文件 -> byte[]
byte[] result = Files.readAllBytes(Paths.get(file));
//byte[] -> String
return new String(result, StandardCharsets.UTF_8);
}
一次性写
static void writeFromString2(String file, String content) throws IOException {
//String -> byte[]
byte[] data = content.getBytes(StandardCharsets.UTF_8);
//byte[] -> 文件
Files.write(Paths.get(file), data);
}
按行读写
//按行读
List<String> lines = Files.readAllLines(Paths.get("in.txt"), StandardCharsets.UTF_8);
//按行写
Files.write(Paths.get("out.txt"), lines, StandardCharsets.UTF_8);
自定义封装InputStream
下面的例子演示了如何编写一个CountInputStream
,它的作用是对输入的字节进行计数:
public class Main {
public static void main(String[] args) throws IOException {
byte[] data = "hello, world!".getBytes("UTF-8");
try (CountInputStream input = new CountInputStream(new ByteArrayInputStream(data))) {
int n;
while ((n = input.read()) != -1) {
System.out.println((char)n);
}
System.out.println("Total read " + input.getBytesRead() + " bytes");
}
}
}
// 为什么是继承 FilterInputStream,因为它已经帮我们实现了基本方法,并包含了内部 InputStream
class CountInputStream extends FilterInputStream {
private int count = 0;
CountInputStream(InputStream in) {
super(in);
}
public int getBytesRead() {
return this.count;
}
public int read() throws IOException {
int n = in.read();
if (n != -1) {
this.count ++;
}
return n;
}
public int read(byte[] b, int off, int len) throws IOException {
int n = in.read(b, off, len);
if (n != -1) {
this.count += n;
}
return n;
}
}
读取classpath资源
Java存放.class
的目录或jar包也可以包含任意其他类型的文件,从classpath读取文件就可以避免不同环境下文件路径不一致的问题:如果我们把default.properties
文件放到classpath中,就不用关心它的实际存放路径。
在classpath中的资源文件,路径总是以/
开头,调用Class.getResourceAsStream()
就可以直接从classpath读取任意的资源文件,需要特别注意的一点是,如果资源文件不存在,它将返回null
。因此,我们需要检查返回的InputStream
是否为null
,如果为null
,表示资源文件在classpath中没有找到:
try (InputStream input = getClass().getResourceAsStream("/com/zc/demo/default.properties")) {
if (input != null) {
// TODO:
}
}