File类
绝对路径和相对路径
- 绝对路径
- 绝对路径名是完整的路径名,根据绝对路径可以唯一确认文件和目录
- 例如:E:\demo\first\a.txt
- 相对路径
- 相对路径是不完整的路径名,只依赖相对路径不能唯一确认文件和目录
- 相对路径名必须使用其他路径名的信息进行解释,也就是常说地相对于“某个路径”
绝对路径可以唯一确认一个文件,相对路径却不可以
那么我们在IDEA中写代码,如果我们使用一个相对路径来表示文件
那这个相对路径又是相对于谁呢?
-
默认情况下,java.io包中的类总是根据当前用户目录来解析相对路径名
-
此目录由系统属性user.dir指定,通常是 Java虚拟机的调用目录
-
可以使用以下代码获取
-
System.getProperty("user.dir")
-
-
这个属性默认是project的根目录
-
可以在run configuration中修改
-
一般情况下默认就好了
-
普遍来说,在Java程序中应该优先使用绝对路径,因为相对路径会随着环境的改变而指向不同的文件
不同操作系统下路径名表示的符号其实是有区别的
Microsoft Windows平台
-
Windows操作系统下,包含盘符的路径名前缀由驱动器号和一个 “:” 组成
-
后面不同层级目录用“\”或者“\\”表示
-
例如
-
绝对路径: e:\demo\a.txt 相对路径: (相对于e:\)demo\a.txt
-
类Unix平台
包括Unix系统,Linux系统,macOS系统
-
这些系统是没有盘符标识的,而是用一个“/”表示根目录
-
绝对路径就是从根目录开始的,一个完整的目录,后面的每个层级都用“/”分隔
-
相对路径则不从根目录开始
-
例如
-
绝对路径:/home/demo/a.txt 相对路径:(相对于/home/demo)a.txt 根目录:/
-
转义字符 ‘\xxx’
-
'\t'表示制表符
-
'\r'表示回车
-
'\n'表示换行
-
'\\'表示字符串"\"
根据以上种种特征,那么我们怎么在Java程序中表示一个文件或者目录呢?
难道我们需要在用Windows写代码测试的时候用“\\”,而在代码上线后用"/"吗?
- 当然不需要,Java早已是一门成熟的语言,跨平台性上,已经对路径符号作了优化
- 你可以*选择以下一种方式书写路径名,都是可以的
- 全部用“//”
- 全部用“\\”(推荐使用)
- 全部用“/”
- 不要使用“\”,单独使用“\”,这是一个转义字符
File类的使用
首先,在使用File之前,再明确一下File类的定义
File是文件和目录(文件夹)路径名的抽象表达形式F
-
File类是对文件、目录的抽象表示,并不代表这个文件和目录就一定存在
-
创建File类对象的时候,编译器也不会去检查这个File对应的文件和目录是否存在,只是将我们给的地址当成一段字符
-
用一个file对象调用以下方法,可判断该目录文件是否存在
public boolean exists()
File类的构造方法
//创建一个File对象,该方法一般使用绝对路径来创建对象,也可以使用相对路径
File (String pathname)
//和第一种方式类似,只不过把一个路径劈成了两半
//普遍来说,parent路径表示一个绝对路径。child路径跟一个相对路径
File (String parent, Sting child)
//和第二种方式一样,只不过,子路径用一个File对象表示
File (File parent, String child)
File API
下面来学习File API的使用
几个属性
创建功能
//只负责创建文件,目录路径如果不存在,会报错而不是帮你创建
public boolean createNewFile()
//只负责创建目录,但只能创建单层目录,如果有多级目录不存在的话,创建失败
public boolean mkdir()
//只负责创建目录,但可以创建多级目录,如果多级目录不存在,则帮你全部创建
public boolean mkdirs()
- createNewFile()只能创建文件,不能创建目录,会报错
- mkdir()和mkdirs()的区别就在于能否创建多级目录
- 需要注意的是,它两个都不能创建文件
- 如果File对象路径中包括文件名,它会把文件名当成目录名处理
删除功能
public boolean delete()
- 删除此抽象路径名表示的文件或目录。如果此路径名表示一个目录,则该目录必须为空才能删除
移动且重命名文件功能
public boolean renameTo(File dest)
- 当源文件和修改之后的目标文件,在同一目录的时候,效果只是重命名
- 当源文件和修改之后的目标文件,不在同一目录的时候,效果是移动且重命名
- 当源文件和修改之后的目标文件,同目录同名时,方法返回true,实际没有效果
- 真正操作文件,应该使用(IO流操作)
判断功能
//判断File对象是否表示的是一个文件
public boolean isFile()
//判断File对象是否表示的是一个目录
public boolean isDirectory()
//判断File对象表示的文件或目录,是否真实存在
public boolean exists()
//判断File对象表示的文件,是否可读
public boolean canRead()
//判断File对象表示的文件,是否可写
public boolean canWrite()
//判断File对象表示的文件是否是隐藏文件
public boolean isHidden()
获取功能
//获取File对象表示的抽象文件的绝对路径
public File getAbsolutePath()
//获取File对象表示的抽象路径名的字符串,简单来说,创建的时候给的是什么就输出什么
public String getPath()
//获取File对象表示的文件或者目录的文件名
public String getName()
//返回由此抽象路径名表示的文件的所占硬盘空间大小,以字节为单位
//但是需要注意的是,这个方法只能获取文件的大小,不能获取目录大小
public long length()
//返回此File对象表示的文件的最后一次修改的时间
public long lastModified()
高级获取功能
//返回一个字符串数组,这些字符串包括,此抽象的路径名表示的目录中的所有文件和文件夹的名字
//如果File对象表示的是一个文件,则返回null
//只能获取当前目录的下一层,并不是获取所有层级
//如果是一个空目录,返回一个长度为0的数组,而不是null
public String[] list()
//返回指定File目录下的文件和文件夹的绝对路径形式的File对象数组
//如果File对象表示的是一个文件,则返回null
//只能获取当前目录的下一层,并不是获取所有层级
//如果是一个空目录,返回一个长度为0的数组,而不是null
public File[] listFiles()
自定义获取功能
//获取这个文件夹下,满足filter过滤器的条件的文件
File[] listFiles(FileFilter filter)
-
自定义获取功能是在高级获取功能的基础上,加了一个过滤器,所以高级功能的特点它都有
-
FileFilter是一个接口,它只有下面一个方法
-
//测试指定抽象路径名是否应该包含在某个路径名列表中 boolean accept(File pathname)
-
这个方法相当于把高级功能中listFiles()获取的File数组中File对象遍历一遍,然后逐个判断
-
符合条件的留下,不符合条件的干掉(丢弃)-
-
-
常用匿名内部类来做实现
//留下所有txt文件
public class FileTest2 {
public static void main(String[] args) {
File file = new File("E:\\temp");
//匿名内部类创建一个过滤器
FileFilter fileFilter = new FileFilter() {
@Override
public boolean accept(File dir) {
//条件是 dir对象是一个文件并且它的名字以txt结尾
return dir.isFile() && dir.getName().endsWith("txt");
}
};
//在有过滤器的情况下创建一个File[]数组,并且遍历
File[] files = file.listFiles(fileFilter);
for(File f : files){
System.out.println(f);
}
}
- 补充Arrays.sort(files, new Comparator())方法
- 带比较器的File数组排序方法
递归删除目录的思路
- 获取目录的下的所有File对象(包括文件和文件夹)
- 判断,如果是一个空目录或者file对象不是一个目录而是文件
- 直接删除
- 程序执行到这里,那么一定是一个目录,且不是空目录
- 遍历获取的file数组
- 如果这个file对象仍然是一个目录,递归删除该目录
- 如果这个file对象是文件,直接删除
- 最后不要忘记删除已经是空目录的当前目录
作业
File练习题
递归删除目录(删除方法有风险,一定要慎重考虑使用)
目录结构为如下(尽量不要在c盘中测试)
firstLevel目录中,包含一个secondLevel目录和a1.txt和b1.java文件
secondLevel目录中包含dir1和dir2两个目录,和a2.txt和b2.java文件
dir1目录中包含a3.txt和b3.java文件
dir2目录是一个空目录
要求删除firstLevel目录
public class Demo03 {
public static void main(String[] args) {
File file = new File("/Users/chelsea-he/Documents/JAVA33th/firstLevel/secondLevel/b2.java");
//File[] files = file.listFiles();
// System.out.println(files); // 当文件是空目录时,产生当文件数组不是null,只是长度为0
// 当文件名代表的文件,不是一个目录的时候,files为null
FileUtils.deleteFiles(file);
}
}
class FileUtils{
public static void deleteFiles(File file){
File[] files = file.listFiles();
if(files == null || files.length == 0){
file.delete();
return;
}
for (int i = 0; i < files.length; i++) {
deleteFiles(files[i]);
if(files[i].isDirectory()){
files[i].delete();
}
}
file.delete();
}
}
io概述(掌握)
什么是IO
- i:input 输入
- o:output 输出
为什么有io
- 把数据保存到磁盘Output
- 把文件数据读取到内存Input
- 内存有限,就存在io交互
java中如何实现io功能
- 通过流的方式(Stream)
io分类
按照流向分(以内存为参照)
- 输入流 input 外设→内存
- 输出流 output 内存→外设
按照数据类型分
- 字节流 按照一个字节一个字节进行传输 1B=8bit 0000 0000
- 字符流 一连串的字符序列 (一种文化符号 宝, ABC , の)
4个基类
- 字节输出流 OutputStream
- 字节输入流 InputStream
- 字符输出流 Writer
- 字符输入流 Reader
由这4个基类派生的子类,都是以其父类名作为后缀的
FileOutputStream
FileInputStream
什么时候该用什么流
文本数据,字符流去处理
非文本数据,字节流去处理 不知道什么类型就用字节流(字节流是万能的)
字节流(重点)
字节输出流
基类
OutputStream
此抽象类是表示输出字节流的所有类的超类
继承关系
成员方法
void | close() 关闭此输出流并释放与此流有关的所有系统资源。 |
---|---|
void | flush() 刷新此输出流并强制写出所有缓冲的输出字节。 |
void | write(byte[] b) 将 b.length 个字节从指定的 byte 数组写入此输出流。 |
void | write(byte[] b, int off, int len) 将指定 byte 数组中从偏移量 off 开始的 len 个字节写入此输出流。 |
abstract void | write(int b) 将指定的字节写入此输出流。 |
具体子类
FileOutputStream
继承关系
构造方法
FileOutputStream(File file) 创建一个向指定 File 对象表示的文件中写入数据的文件输出流。 |
---|
FileOutputStream(File file, boolean append) 创建一个向指定 File 对象表示的文件中写入数据的文件输出流。 |
FileOutputStream(String name) 创建一个向具有指定名称的文件中写入数据的输出文件流。 |
FileOutputStream(String name, boolean append) 创建一个向具有指定 name 的文件中写入数据的输出文件流。 |
demo
package com.cskaoyan.bytestream.out;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
/**
* @description:
* @author: songtao@cskaoyan.onaliyun.com
**/
public class Demo1 {
public static void main(String[] args) throws IOException {
//| FileOutputStream(File file)
// 创建一个向指定 File 对象表示的文件中写入数据的文件输出流。
File file = new File("a.txt");
FileOutputStream outputStream = new FileOutputStream(file);
// 传String 文件名
FileOutputStream outputStream1 = new FileOutputStream("b.txt");
}
}
成员方法:
void | write(byte[] b) 将 b.length 个字节从指定 byte 数组写入此文件输出流中。 |
---|---|
void | write(byte[] b, int off, int len) 将指定 byte 数组中从偏移量 off 开始的 len 个字节写入此文件输出流。 |
void | write(int b) 将指定字节写入此文件输出流。 |
注意:write 的常规协定是:向输出流写入一个字节。要写入的字节是参数 b 的八个低位。b 的 24 个高位将被忽略。或者你直接理解为write只接收byte,你传入了int会自动剪切
怎么写数据
write Demo
package com.cskaoyan.bytestream.out;
import java.io.FileOutputStream;
import java.io.IOException;
/**
* @description: write方法
* @author: songtao@cskaoyan.onaliyun.com
**/
public class Demo2 {
public static void main(String[] args) throws IOException {
// 创建字节输出流对象
FileOutputStream out = new FileOutputStream("a.txt");
// 写数据 write方法
// write(int b)
//out.write(97);
// write(byte[] b)
String s= "hello world";
byte[] bytes = s.getBytes();
//out.write(bytes);
// write(byte[] b,int off,int len)
out.write(bytes, 0, bytes.length);
// 关闭资源close
out.close();
}
}
注意事项(重要)
-
创建字节输出流对象发生了什么?
- 创建字节输出流之前,jvm会到操作系统中找文件是否存在
- 如果不存在,帮我们创建
- 如果存在,会覆盖掉
-
怎么去实现换行功能?
-
package com.cskaoyan.bytestream.out; import java.io.FileOutputStream; import java.io.IOException; /** * @description: * @author: songtao@cskaoyan.onaliyun.com **/ public class Demo3 { public static void main(String[] args) throws IOException { // 实现换行功能 // 第一种 win 的换行符 \r\n // 第二种 系统默认换行符 // 创建输出流对象 FileOutputStream out = new FileOutputStream("a.txt"); // 写点数据 out.write(97); // 换行 //out.write("\r\n".getBytes()); out.write(System.lineSeparator().getBytes()); // 写点数据 out.write(98); // close out.close(); // a // b // ab } }
-
-
如何实现文件追加?(借助构造方法)
-
append - 如果为 true,则将字节写入文件末尾处,而不是写入文件开始处
-
public static void main(String[] args) throws IOException { // 创建可以追加的输出流对象 FileOutputStream out = new FileOutputStream("a.txt", true); // write out.write(99); // close out.close(); }
-
-
为什么要close?
- io资源是操作系统资源,我们的jvm不能回收,所以只能通过close方法显式的去释放资源
- 不属于jvm的资源 都要close
-
怎么异常处理
-
第一种 传统的try catch
-
package com.cskaoyan.bytestream.out; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; /** * @description: 异常处理 * @author: songtao@cskaoyan.onaliyun.com **/ // try-with-resources public class Demo5 { public static void main(String[] args) { // 创建输出流对象 FileOutputStream out = null; try { out = new FileOutputStream("a.txt"); // 写数据 out.write("hello".getBytes()); } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); }finally { //close try {// 判断是否为null if (out != null) { out.close(); } } catch (IOException e) { e.printStackTrace(); } } } }
-
第二种
-
字节输入流
基类
具体子类
字符流(重点)
其他流(了解)
总结
作业
1.分别键盘输入文件名 和 文件内容,并按照文件名保存相应的内容
public class Demo01 {
public static void main(String[] args) throws FileNotFoundException {
Scanner sc = new Scanner(System.in);
System.out.print("请输入要创建的文件名:");
File file = new File(sc.nextLine());
FileOutputStream outputStream = new FileOutputStream(file);
System.out.print("请输入文件内容:");
String str = sc.nextLine();
byte[] bytes = str.getBytes();
try {
outputStream.write(bytes);
} catch (IOException e) {
e.printStackTrace();
}finally {
if(outputStream != null){
try {
outputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
2.键盘输入文件名,文件内容,按照输入的文件名,文件内容保存。要求输入内容的时候可以多次追加写入,以一个约定字符串表示结束输出内容,比如当输入end时表示终止内容输入。
package homework;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.Scanner;
public class Demo02 {
public static void main(String[] args) {
System.out.println("请输入文件地址及文件名:");
Scanner sc = new Scanner(System.in);
File file = new File(sc.nextLine());
FileOutputStream output = null;
try {
output = new FileOutputStream(file,true);
} catch (FileNotFoundException e) {
e.printStackTrace();
}
String str = null;
System.out.println("请输入文件内容:");
str = sc.nextLine();
while(!"end".equals(str)){
byte[] bytes = str.getBytes();
try {
if (output != null) {
output.write(bytes);
output.write("\n".getBytes());
}
} catch (IOException e) {
e.printStackTrace();
}
str = sc.nextLine();
}
try {
output.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
2.定义一个这样的一个words数组,数组中每个字符串的格式为“词性:单词”
String[] words = {“verb:eat”,“verb:drink”,“verb:sleep”,“verb:play”,“noun:rice”,“noun:meat”,“noun:hand”,“noun:hair”};
根据单词性质动词verb全部存入verb.txt文件中
根据单词性质名词noun全部存入noun.txt文件中
public class Demo03 {
public static void main(String[] args) throws IOException {
File fileVerb = new File("/Users/chelsea-he/Documents/JAVA33th/testFile/verb.txt");
File fileNoun = new File("/Users/chelsea-he/Documents/JAVA33th/testFile/noun.txt");
FileOutputStream outputStream1 = new FileOutputStream(fileVerb,true);
FileOutputStream outputStream2 = new FileOutputStream(fileNoun,true);
String[] words = {"verb:eat","verb:drink","verb:sleep","verb:play","noun:rice","noun:meat","noun:hand","noun:hair"};
for (int i = 0; i < words.length; i++) {
if(words[i].startsWith("verb")){
byte[] bytes = words[i].substring(5).getBytes();
outputStream1.write(bytes);
outputStream1.write("\n".getBytes());
}else if(words[i].startsWith("noun")){
byte[] bytes = words[i].substring(5).getBytes();
outputStream2.write(bytes);
outputStream2.write("\n".getBytes());
}
}
outputStream1.close();
outputStream2.close();
}
}