Java 文件基本技术

介绍一下 Java IO 中二进制文件(字节流)、文本文件(字符流),以及文件&目录的操作

一、字节流

1.1 InputStream, OutputStream

抽象类

InputStream


int read() throws IOException

// 读一个字节,返回(int模式),实际上只有一个字节(8位)的内容



int read(byte b[]) throws IOException

// 读入的字节放入数组 b 中,返回读入字节个数,如果开始读取时已到流末尾,返回 -1



int read(byte b[], int off, int len) throws IOException

// 读入b[off], len 长度的数据



// 最后应该调用 close

finally{

input.close();

}



// 都有 throws IOException, 省略

long skip(long n) // 跳过输入流 n 个字节,返回实际略过的字节数

int avalable() // 返回下一次不需阻塞就可以读到的大概字节数,默认实现是0,常用于网络读取数据

// 下面支持流重复读取

synchronized void mark(int readlimit) // 标记(能够往后读的最多字节数,超过了标记会无效)

boolean markSupported() // 不是所有流都支持标记, 要用此方法判断

synchronized void reset() // 重新从标记位置读

OutputStream


// 都有 throws IOException,省略

void write(int b)

// 向流中写入一个字节(int 模式),实际也只有8位



void write(byte b[])



void write(byte b[], int off, int len)



void flush() // 将缓冲而未写入的数据进行写入,比如 BufferedOutputStream 中, 没缓冲功能的为方法体为空



void close()

1.2 FileInputStream, FileOutputStream

FileOutputStream


// 构造方法, 必须是文件而不是目录

public FileOutputStream(File file, boolean append) throws FileNotFoundException

// append true 追加, false 覆盖



public FileOutputStream(String name) throws FileNotFoundException // 文件名路径也可以,可以是相对路径或绝对路径



// new 一个 FileOutputStream 对象会实际打开文件,分配相关资源

// 如果没有权限,会抛出 SecurityException(未检查异常 RuntimeException)

// 如果指定的是已存在的目录,或由于其他原因不能打开文件,会抛出 FileNotFoundException(IOException子类)



OutputStream output = new FileOutputStream("hello.txt");

try{

String data = DateFormat.getInstance().format(Calendar.getInstance().getTime());

byte[] bytes = data.getBytes("UTF-8"); // Strring 转换为字节数组

output.write(bytes);

} catch (UnsupportedEncodingException e) {

e.printStackTrace();

} catch (IOException e) {

e.printStackTrace();

} finally{

output.close(); // 也会抛出 IOException

}





// 额外的方法

FileChannel getChanel()

// FileChannel 定义在 java.nio 中,表示文件通道



final FileDescriptor getFD()

// 文件描述符,与操作系统的一些文件内存结构相连, FileDescriptor 有一个 sync 方法(本地方法),会将系统缓冲的数据写到硬盘,和flush有区别(是从缓冲区写到操作系统,不一定写入硬盘)

FileInputStream


// 构造方法

public FileOutputStream(File file) throws FileNotFoundException



public FileOutputStream(String name) throws FileNotFoundException // 文件名路径也可以,可以是相对路径或绝对路径



// 都有 throws IOException, 省略

int read()

// 读一个字节,返回int模式



int read(byte buf[])

// 读数据到buf中,最大长度是 buf length, 返回读取字节数



int read(byte buf[], int off, int len)

// off: 偏移, len 长度





// 1. int read() 逐个字节读

int b = -1;

int bytesRead = 0;

while(b=input.read()!=-1){

buf[bytesRead++] = (byte)b;

}



// 2. int read(byte buf[]) 已经知道文件大小(小于 buf 长度)

InputStream input = new FileInputStream("hello.txt");

try{

byte[] buf = new byte[1024];

int bytesRead = input.read(buf);

String data = new String(buf, 0, bytesRead, "UTF-8");

System.out.println(data);



} catch (UnsupportedEncodingException e) {

e.printStackTrace();

} catch (IOException e) {

e.printStackTrace();

} finally{

input.close();

}



// 3. int read(byte buf[], int off, int len) 已经知道文件大小(小于 buf 长度)

byte[] buf = new byte[1024];

int off = 0;

int bytesRead = 0;

while((bytesRead = input.read(buf, off, 1024-off))!=-1){

off += bytesRead;

}

String data = new String(buf, 0, off, "UTF-8");

不知道文件大小,不想分配太大的 bytes 数组,怎么办?应该使用ByteArrayOutputStream

1.3 ByteArrayOutputStream, ByteArrayInputStream

ByteArrayOutputStream

这个类实现了“数据写入字节数组”的 output stream, 此字节数组(缓冲区)会动态增长,使用toByteArray()能获得其数组形态 byte [],或者toString()可以获得字符串形式

Closing a ByteArrayOutputStream has no effect. The methods in this class can be called after the stream has been closed without generating an IOException.

除非其向 OutputStream 写入(writeTo),向其写入(write())不会抛出 IOException

可以是一个普通的 OutputStream, 也可以是一个中转站写入其他 OutputStream


// 构造方法

public ByteArrayOutputStream()



public ByteArrayOutputStream(int size) // 指定初始数组大小, 默认是 32, 指数扩展,至少1倍



public synchronized byte[] toByteArray()

public synchronized byte[] toString() // 默认使用系统默认编码(通常是UTF-8)

public synchronized byte[] toString(String charsetName)





// 写入到另一个 OutputStream (out.write(byte buf[]))

public synchronized void writeTo(OutputStream out) throws IOException



public synchronized int size() // 返回当且写入buf的字节数

public synchronized void reset() // 重用已分配数组(清空)



InputStream input = new FileInputStream("hello.txt");

try{

ByteArrayOutputStream output = new ByteArrayOutputStream(); // 输出流

byte[] buf = new byte[1024];

int bytesRead = 0;

while((bytesRead=input.read(buf))!=-1){

output.write(buf, 0, bytesRead); // 将从input读到的buf写入ByteArrayOutputStream

}

String data = output.toString("UTF-8");

System.out.println(data);

}

finally{

input.close();

}

ByteArrayInputStream

将 byte[] 数组包装为输入流,是一种适配器模式(将byte数组转换为InputStream,方便参与输入/输出体系)

所有数据都在内存,支持 mark/reset 重复读取


public ByteArrayInputStream(byte buf[])



public ByteArrayInputStream(byte buf[], int offset, int length)

1.4 DataInputStream, DataOutputStream

除了用字节为单位读写,还可读写为基本类型

都是装饰类(原基础上添加额外的功能)

DataOutputStream

DataOutputStream 是装饰类 FilterOutputStream 的子类(也是 OutputStream 子类)

接受一个OutputStream,所有操作都代理给了它,只不过实现了DataOutput接口,可以以各种基本类型和字符串写入数据(转换为对应的二进制字节,注意不是文本,是最原始的二进制,当然也可以用原始类型转换为二进制再写入,相当于是对这部分做了包装)


// 构造方法

public DataOutputStream(OutputStream out)



// 都有 IOException

void writeBoolean(boolean v) // 1 个字节,true 是 1, 否则 0

void writeInt(int v) // 4 个字节,高位先写入,低位后写入

void writeUTF(String s)





class Student{

String name;

int age;

double score;

}



List<Student> students = Arrays.asList(

new Student("张三", 18, 80.9d),

new Student ("李四",19,99.0d)

);



DataOutputStream output = new DataOutputStream(

new FileOutputStream("student.dat")

);

try {

output.writeInt(students.size());

for(Student s:students){

output.writeUTF(s.getName());

output.writeInt(s.getAge());

output.writeDouble(s.getScore());

}

} catch (IOException e) {

e.printStackTrace();

}

finally {

output.close();

}

DataInputStream

同理,不再赘述


// 构造方法

public DataInputStream(InputStream input)



void readBoolean(boolean v)

void readInt(int v)

void readUTF(String s)



public static List<Student> testIn() throws IOException {

DataInputStream input = new DataInputStream(

new FileInputStream("student.dat")

);

try{

int size = input.readInt();

List<Student> students = new ArrayList<Student>(size);

for(int i = 0; i < size; i++){

Student s = new Student();

s.setName(input.readUTF());

s.setAge(input.readInt());

s.setScore(input.readDouble());

students.add(s);

}

return students;

}

finally {

input.close();

}



}

}

1.5 BufferedInputStream, BufferedOutputStream

FileInputStream / FileOutputStream 没有缓冲, 按单字节读写性能较低

为提高性能,可以将文件流包装到缓冲流中,即也是装饰类

读取时先从缓冲区读取,读完了再调用包装的流读


// 构造方法

public BufferedInputStream(InputStream in)



public BufferedOutputStream(InputStream in, int size) // size 表示缓冲区大小,默认是 8192





// BufferedInputStream 为提高性能,支持重复读取(mark/reset)



// BufferedOutputStream 构造方法类似, 有 flush 方法,会将缓冲区的内容写到 包装的流中



InputStream input = new BufferedInputStream(new FileInputStream("hello.txt"))



OutputStream output = new BufferedOutputStream(new FileOutputStream("hello.txt"))





// 两层包装

DataOutputStream out = new DataOutputStream(

new BufferedOutputStream(

new FileOutputStream("students.dat")

)

)



DataInputStream new DataInputStream(

new BufferedInputStream(

new FileInputStream("students.dat")

)

)

1.6 实用方法,更为简单的接口

复制输入流到输出流, 通常是输入流读到 byte 数组中,再从 byte 数组输出到输出流


public static void copy(InputStream input, OutputStream output) throws IOException{

byte[] buf = new byte[4096];

int bytesRead;

while((bytesRead = input.read(buf))!=-1){

output.write(buf, 0, bytesRead);

}

}



// Java9 中,InputStream增加了一个transferTo()方法,实现类似的方法

public long transferTo(OutputStream out) throws IOException{}



// 文件读入字节数组

public static byte[] readFileToByteArray(String fileName) throws IOException{

InputStream input = new FileInputStream(fileName);

ByteArrayOutputStream output = new ByteArrayOutputStream();

try{

copy(input, output);

return output.toByteArray();

}

finally {

input.close();

}

}

Apache 有一个 Commons IO 库,提供了很多易用的方法

二、文本文件和字符流

字节流没有编码的概念,不能按行处理,使用不太方便,更适合的是使用字符流.

  1. Reader / Writer 字符流的抽象基类

  2. InputStreamReader / OutputStreamWriter 适配器类, 将字节流转换为字符流

  3. FileReader / FileWriter 输入源和输出目标是文件字符流

  4. CharArrayReader / CharArrayWriter 输入源和输目标是 char 数组字符流

  5. StringReader / StringWriter

  6. BufferedReader / BufferedWriter 装饰类,提供缓冲、按行读写功能

  7. PrintWriter 装饰类,可将基本类型和对象转换为其字符串形式输出的类

  8. Scanner 类似一个 Reader,可以读取基本类型的字符串形式,类似于 PrintWriter 逆操作

2.1 基本概念

在文本文件中,同一个字符,不同编码对应的二进制形式可能不一样,比如中文。

字节流是按字节读取的,而字符流是按 char 读取的,而一个 char 在文件中是几个字节,和编码有关, 字符流封装了字节的细节,之和 char 有关

注意,一个 char 不等同于一个字符(大部分情况是等同的),对于增补字符集,需要用 2 个 char 表示,但 java 的字符流还是按 char (1个)而不是完整的字符(2个char)读取的。

2.2 Reader, Writer

Reader

Reader 与字节流的 InputStream 类似,也是抽象类


public int read() throws IOException

// read 读取一个char,范围 0-65535



public int read(char cbuf[]) throws IOException

abstract public void close() throws IOException

public long skip(long n) throws IOException

public boolean ready() throws IOException

// Reader 没有 available 方法,对应的是 ready()

Writer


public void write(int c)

public void write(char cbuf[])

public void write(String str) throws IOException // 还接受 String 类型, 会调用 String 的 getChar 获取 char 数组

abstract public void close() throws IOException

abstract public void flush() throws IOException

2.3 InputStreamReader, OutputStreamWriter

它们两个都是适配器类,可以把字节流 InputStream, OutputStream 转换为 Reader 和 Writer

OutputStreamWriter


public OutputStreamWriter(OutputStream out, String charsetName) // charsetName 编码类型, 默认是系统编码



Writer writer = new OutputStreamWriter(new FileOutputStream("hello.txt"), "GB2312");

try {

String str = "hello, 123, 你好";

writer.write(str);

}finally {

writer.close();

}

InputStreamReader


Reader reader = new InputStreamReader(new FileInputStream("hello.txt"),"GB2312");

try{

char[] cbuf = new char[1024];

int charsRead = reader.read(cbuf); // 注意读取的是 char 数组

System.out.println(new String(cbuf, 0, charsRead)); // char 数组构建 String 字符串,也可以是字节数组哦

}

finally {

reader.close();

}

2.4 FileReader, FileWriter

是上面 InputStreamReader 和 OutputStreamWriter 的子类

FileReader


public FileReader(File file) throws FileNotFoundException



public FileReader(String fileName) throws FileNotFoundException

FileWriter


public FileWriter(File file) throws IOException



public FileWriter(String fileName, boolean append) throws IOException // 一般都是 String 才有 append 的

比起 InputStreamReader / OutputStreamWriter 方便是方便了, 只不过不能制定 charset,是使用系统默认编码的

2.5 CharArrayReader, CharArrayWriter

CharArrayWriter 和 ByteArrayStream 类似,输出目标是 char 数组,数组长度可以自动扩展


public char[] toCharArray() // 转换为 char 数组



public String toString()





// 从 reader 中读数据到 cbuf,再写到 CharArrayWriter

Reader reader = new InputStreamReader( new FileInputStream("hello.txt"), "GB2312") );

try{

CharArrayWriter writer = new CharArrayWriter(); // 也不是包装类哦

char[] cbuf = new char[1024];

int charsRead = 0;

while((charsRead=reader.read(cbuf))!=-1){

writer.write(cbuf, 0, charsRead);

}

System.out.prinln(writer.toString());

}finally{

reader.close();

}



// charArrayReader

// 将 char 数组包装为一个 reader ,适配器模式,和 ByteArrayReader 类似

public CharArrayReader(char buf[])

public CharArrayReader(char buf[], int offset, int length)

2.6 StringReader, StringWriter

与 CharArrayReader, CharArrayWriter 类似,输出源是 String, 输出目标是 StringBuffer

String / StringBuffer 内部是由 char 数组组成的,本质是一样的

之所以要将 char 数组和 String 与 Reader / Writer 进行转换,是为了能方便地参与 Reader / Writer 构成的协作体系

2.7 BufferedReader, BufferedWriter

BufferedWriter


public BufferedWriter(Writer out)



public BufferedWriter(Writer out, int sz) // sz 默认缓冲大小 8192



public void newLine() throws IOException // 特定平台的换行符

BufferedReader


public BufferedReader(Reader in)

public BufferedReader(Reader in, int sz)



public String readLine() throws IOException

// '\r', '\n', '\r\n' 被视为换行符, 返回一行内容(不包含换行符),读到流结尾返回 null

FileReader / FileWriter 是没有缓冲区的,也不能按行读写,一般应该在它们的外面包上对应的缓冲类


// 注意 BufferedReader 由于是装饰类,里面包装 Reader 可能出问题,因此要判断 != null 才能 close()

public static void writeStudents(List<Student> students) throws IOException{

BufferedWriter writer = null;

try{

writer = new BufferedWriter(new FileWriter("students.txt")); // FileWriter 是比较简单的 Writer, 只要一个文件参数

for(Student s : students){

writer.write(s.getName() + "," + s.getAge() + "," + s.getScore());

writer.newLine(); // 写入换行符(这样就是一行)

}

}finally{

if(writer!=null) writer.close();

}

}





public static List<Student> readStudents() throws IOException{

BufferedReader reader = null;

try{

reader = new BufferedReader(new FileReader("students.txt"));

List<Student> students = new ArrayList<>();

String line = reader.readLine();

while(line!=null){

String[] fields = line.split(",");

Student s = new Student();

s.setName(fields[0]);

s.setAge(Integer.parseInt(fields[1]));

s.setScore(Double.parseDouble(fields[2]));

students.add(s);

line = reader.readLine();

}

return students;

}

finally {

if(reader!=null) reader.close();

}

}

2.8 PrintWriter

PrintWriter 有很多重载的 print 方法


// 调用 String.valueOf() 将参数转换为其字符串形式, 再调用 write

// 注意基本类型是没有 toString 方法的

public void print(int i)



public void print(Object obj)





public void println(Object obj) // 输出一行

public PrintWriter format(String format, Object ... args)





PrintWriter writer = ...

writer.format("%.2f", 123.456f);



// PrintWriter 的方便之处在于有很多构造方法

public PrintWriter(File file) throws FileNotFoundException

public PrintWriter(String fileName, String csn) // csn 是编码类型

public PrintWriter(OutputStream out, boolean autoFlush)

public PrintWriter(Writer out)



// 对文件对象和文件名为参数的构造方法,会在内部构建一个 BufferedWriter

public PrintWriter(String fileName) throws FileNotFoundException {

this(new BufferedWriter(new OutputStreamWriter(new FileOutputStream(fileName))), false);

}

// 对于OutputStream也是一样的

public PrintWriter(OutputStream out, boolean autoFlush) throws FileNotFoundException {

this(new BufferedWriter(new OutputStreamWriter(out)), autoFlush);

// autoFlush 表示同步缓冲区的时机

// 如果是 true, 则在调用 println, printf, format 等方法时同步缓冲区,没有则要根据情况调用flush方法





}



public static void writeStudents(List<Student> students) throws IOException{

PrintWriter writer = new PrintWriter("students.txt");

try{

for(Student s : students){

writer.println(s.getName()+","+s.getAge()+","+s.getScore());

}finally{

writer.close();

}

}

}

PrintWriter 有一个非常相似的类:PrintStream,除了不能接受Writer作为参数外,其他构造方法和PrintWriter一样

PrintStream 同步缓冲区的时机不太一样,只要碰到一个换行符’\n’就会自动同步缓冲区

还有一个区别是public void write(int b)的含义不一样,PrintStream 是只使用最低的8位,输出一个字节,而 PrintWriter 是使用最低的两位,输出一个 char

2.9 Scanner

Scanner 是单独的类,简单的文本扫描其,能够分析基本类型和字符串,需要一个分隔符来将不同的数据去分开,默认是空白符,可以用useDelimiter()方法指定, 有很多next()方法,读取下一个基本类型


public float nextFloat()

public int nextInt()

public nextLine()





// Scanner 也有很多构造方法,可以接受 File 对象,InputStream, Reader 作为参数,也可以将字符串作为参数(会创建一个 StringReader)

Scanner scanner = new Scanner(line).useDelimiter(","); // 创建字符串的 Scanner 指定分割符



public static List<Student> readStudents() throws IOException{

BufferedReader reader = new BufferedReader(

new FileReader("students.txt")

);

try{

List<Student> students = new ArrayList<>();

String line = reader.readLine();

while(line!=null){

Student s = new Student();

Scanner scanner = new Scanner(line).useDelimiter(","); // 创建字符串的 Scanner 指定分割符

s.setName(scanner.next()); // 字符串是 next

s.setAge(scanner.nextInt());

s.setScore(scanner.nextDouble());

students.add(s);

line = reader.readLine();

}

return students;

}

finally {

if(reader!=null) reader.close();

}



}

2.10 标准流

System.out 是一个 PrintStream 对象,输出的目标是标准输出,经常是屏幕

除此之外还有 System.inSystem.err

System.in 表示标准输入,是一个InputStream对象,输入源经常是键盘


Scanner in = new Scanner(System.in)

int num = in.nextInt();

System.out.println(num);

System.err 表示标准错误流,也是一个PrintStream对象,输出的目标一般也是屏幕

标准流可以重定向,使用System.setIn(), System.setOut(), System.setErr()


System.setIn(new ByteArrayInputStream("hello".getBytes("UTF-8")));

SYstem.setOut(new PrintStream("out.txt"));

System.setErr(new PrintStream("err.txt"));



try{

Scanner in = new Scanner(System.in);

System.out.println(in.nextLine());

System.out.println(in.nextLine());

}catch(Exception e){

System.err.println(e.getMessage());

}

实际开发中,常需要重定向标准流,比如在一些自动化程序中,经常需要重定向标准输入流,以从文件中接受参数,自动执行,避免人工输入。

在后台运行的程序中,一般都需要重定向标准输出和错误到日志文件,以记录和分析运行的状态和问题。

2.11 实用方法

字符流包含了很多类,虽然灵活,但对于简单的开发需要写很多代码。

实际开发中,经常需要对常用功能进行封装,提供简单的接口


// 复制 Reader 到 Writer

public static void copy(final Reader input, final Writer output) throws IOException {

char[] buf = new char[4096];

int charsRead = 0;

while ((charsRead = input.read(buf)) != -1) {

output.write(buf, 0, charsRead);

}

}



// 将文件全部内容读到一个字符串,参数为文件名和编码类型

public static String readFileToString(final String fileName, final String encoding) throws IOException {

BufferedReader reader = null;

try {

// 因为要指定编码,不能用 FileReader,只好用 InputStreamReader

reader = new BufferedReader(new InputStreamReader(new FileInputStream(fileName), encoding));

StringWriter writer = new StringWriter();

copy(reader, writer); // 复制 Reader 到 Writer

return writer.toString();

} finally {

if (reader != null) {

reader.close();

}

}

}





// 将字符串写到文件

public static void writeStringToFile(final String fileName, final String data, final String encoding) throws IOException {

Writer writer = null;

try {

// 又要指定编码, 不能用 FileWriter, 只好用 OutputStreamWriter

// 这里也不用输出缓冲了,直接写

writer = new OutputStreamWriter(new FileOutputStream(fileName), encoding);

writer.write(data);

} finally {

if (writer != null) {

writer.close();

}

}

}



// 按行将多行数据写到文件

public static void writeLines(final String fileName, final String encoding, final Collection<?> lines) throws IOException {

PrintWriter writer = null;

try {

writer = new PrintWriter(fileName, encoding);

for (Object line : lines) {

writer.println(line);

}

} finally {

if (writer != null) {

writer.close();

}

}

}





// 按行将文件内容读取到一个列表中

public static List<String> readLines(final String fileName, final String encoding) throws IOException {

BufferedReader reader = null;

try {

reader = new BufferedReader(new InputStreamReader(new FileInputStream(fileName), encoding));

List<String> list = new ArrayList<>();

String line = reader.readLine();

while (line != null) {

list.add(line);

line = reader.readLine();

}

return list;

} finally {

if (reader != null) {

reader.close();

}

}

}

三、文件,目录操作

对于文件和目录的操作,不同系统的实现是不一样的,而java.io.File类提供了统一的接口

3.1 File

File 对象可以是文件或目录


public File(String pathname)



public File(String parent, String child)



public File(File parent, String child)

3.2 文件元数据


public String getName() // 返回文件或路径名,不含路径名



public boolean isAbsolute() // File 中的路径是否是绝对路径



public String getPath() // 返回构造时的完整路径名



public String getAbsolutePath() // 返回绝对路径名



public String getCanonicalPath() throws IOException // (去除'.','..',跟踪软链接)



public String getParent() // 返回父目录路径



public File getParentFile() // 返回父目录 File 对象



public File getAbsoluteFile() // 返回新对象,使用 getAbsolutePath() 返回值作为参数构造

public File getCanonicalPath() throws IOException // 返回新对象,使用 getCanonicalPath() 返回值作为参数构造

File 有 4 个静态变量,表示路径分割符:


public static final String separator // '\','/'

public static final char separatorChar

public static final String pathSeparator // class path 中 ‘;’, ':'

public static final char pathSeparatorChar

文件、目录信息:


public boolean exists()

public boolean isDirecotry()

public boolean isFile()

public long length() // 文件长度,字节数,对目录无意义

public long lastModified() // 最后i修改时间,纪元开始毫秒数

public boolean setLastModified(long time) // 设置最后修改时间,返回是否修改成功

需要说明的是,File对象没有返回创建时间的方法,因为创建时间不是一个公共概念,Linux/Unix就没有创建时间的概念。

File类中与安全和权限相关的主要方法有:


public boolean isHidden() //是否为隐藏文件

public boolean canExecute() //是否可执行

public boolean canRead() //是否可读

public boolean canWrite() //是否可写

public boolean setReadOnly() //设置文件为只读文件







public boolean setReadable(boolean readable, boolean ownerOnly)

//修改文件读权限



public boolean setWritable(boolean writable, boolean ownerOnly)

public boolean setWritable(boolean writable)

//修改文件写权限



public boolean setExecutable(boolean executable, boolean ownerOnly)

public boolean setExecutable(boolean executable)

//修改文件可执行权限



// 如果修改成功,返回true,否则返回false

// owner-Only为true表示只针对owner,为false表示针对所有用户,没有指定ownerOnly的方法中,ownerOnly相当于是true。

3.3 文件操作

文件操作主要有创建、删除、重命名。

创建文件方法


// 新建一个File对象不会实际创建文件,但如下方法可以:

public boolean createNewFile() throws IOException // 已经存在,不会创建



// File对象还有两个静态方法,可以创建临时文件:

public static File createTempFile(String prefix, String suffix) throws IOException

public static File createTempFile(String prefix, String suffix, File directory) throws IOException



// 临时文件的完整路径名是系统指定的、唯一的,但可以通过参数指定前缀(prefix)、后缀(suffix)和目录(directory)。prefix是必需的,且至少要三个字符;suffix如果为null,则默认为.tmp;directory如果不指定或指定为null,则使用系统默认目录。

File类的删除方法为:


public boolean delete() // 删除文件或目录, 如果File是目录且不为空,则delete不会成功,返回false, 换句话说,要删除目录,先要删除目录下的所有子目录和文件。

public void deleteOnExit() // 将File对象加入到待删列表,在Java虚拟机正常退出的时候进行实际删除。

File类的重命名方法为:


public boolean renameTo(File dest) // 参数dest代表重命名后的文件,重命名能否成功与系统有关,返回值代表是否成功。

3.4 目录操作

创建目录


// 如果目录已存在,返回值是false

public boolean mkdir()

public boolean mkdirs()

// 如果某一个中间父目录不存在,则mkdir会失败,返回false

// mkdirs 则会创建必需的中间父目录(相当与是shell 命令中 mkdir -p)

访问一个目录下的子目录和文件


// list返回的是文件名数组,而listFiles返回的是File对象数组



public String[] list()

public String[] list(FilenameFilter filter)

public File[] listFiles()

public File[] listFiles(FileFilter filter)

public File[] listFiles(FilenameFilter filter)





// FilenameFilter和FileFilter都是接口,用于过滤,FileFilter的定义为:

public interface FileFilter {

boolean accept(File pathname);

}



// FilenameFilter的定义为:

public interface FilenameFilter {

boolean accept(File dir, String name); // dir表示父目录,name表示子目录或文件名

}



// 在遍历子目录和文件时,针对每个文件,会调用Filename-Filter或FileFilter的accept方法,只有accept方法返回true时,才将该子目录或文件包含到返回结果中



File f = new File(".");

File[] files = f.listFiles(new FilenameFilter(){

@Override

public boolean accept(File dir, String name) {

if(name.endsWith(".txt")){

return true;

}

return false;

}

});



// 递归目录获得文件夹大小

public static long sizeOfDirectory(final File directory) {

long size = 0;

if(directory.isFile()){

return directory.length();

} else {

for(File file : directory.listFiles()){

if(file.isFile()){

size += file.length();

}

else{ // 是目录,递归

size += sizeOfDirectory(file);

}

}

}

return size;

}



// 查找所有给定文件名的文件

public static Collection<File> findFile(final File directory, final String fileName) {

List<File> files = new ArrayList<>();

for (File f : directory.listFiles()) {

if (f.isFile() && f.getName().equals(fileName)) {

files.add(f);

} else if (f.isDirectory()) {

files.addAll(findFile(f, fileName)); // 注意 addAll 用法

}

}

return files;

}



// 删除非空目录的方法

public static void deleteRecursively(final File file) throws IOException {

if (file.isFile()) {

if (!file.delete()) {

throw new IOException("Failed to delete "

+ file.getCanonicalPath());

}

} else if (file.isDirectory()) {

// 清空目录中的文件

for (File child : file.listFiles()) {

deleteRecursively(child);

}

// 删除目录

if (!file.delete()) {

throw new IOException("Failed to delete "

+ file.getCanonicalPath());

}

}

}