Java 中通过 java.io.File 类来对一个文件(包括目录)进行抽象的描述。注意,有 File 对象,并不
代表真实存在该文件
我们先来看看 File 类中的常见属性、构造方法和方法
属性
构造方法

方法
| 修饰符及返回 值类型 | 方法签名 | 说明 |
| String | getParent() | 返回 File 对象的父目录文件路径 |
| String | getName() | 返回 FIle 对象的纯文件名称 |
| String | getPath() | 返回 File 对象的文件路径 |
| String | getAbsolutePath() | 返回 File 对象的绝对路径 |
| String | getCanonicalPath() | 返回 File 对象的修饰过的绝对路径 |
| boolean | exists() | 判断 File 对象描述的文件是否真实存在 |
| boolean | isDirectory() | 判断 File 对象代表的文件是否是一个目录 |
| boolean | isFile() | 判断 File 对象代表的文件是否是一个普通文件 |
| boolean | createNewFile() | 根据 File 对象,自动创建一个空文件。成功创建后返 回 true |
| boolean | delete() | 根据 File 对象,删除该文件。成功删除后返回 true |
| void | deleteOnExit() | 根据 File 对象,标注文件将被删除,删除动作会到 JVM 运行结束时才会进行 |
| String[] | list() | 返回 File 对象代表的目录下的所有文件名 |
| File[] | listFiles() | 返回 File 对象代表的目录下的所有文件,以 File 对象 表示 |
| boolean | mkdir() | 创建 File 对象代表的目录 |
| boolean | mkdirs() | 创建 File 对象代表的目录,如果必要,会创建中间目 录 |
| boolean | renameTo(File dest) | 进行文件改名,也可以视为我们平时的剪切、粘贴操 作 |
| boolean | canRead() | 判断用户是否对文件有可读权限 |
| boolean | canWrite() | 判断用户是否对文件有可写权限 |
在标准库中,提供的读写文件的流对象,归结到两个大类中
每次读写的最小单位是"字节"

返回值看起来是int,实际上也是byte.0-255之间的数据,使用-1表示读到文件的末尾
- public class Demo10 {
- public static void main(String[] args) {
- try(InputStream inputStream = new FileInputStream ( "D:/test.txt" )) {
- byte[] buffer = new byte[1024];
- int n = inputStream.read(buffer);
- System.out.println ("n="+n);
- for (int i = 0; i < n; i++) {
- System.out.printf ("%x\n",buffer[i]);
- }
- } catch (IOException e) {
- e.printStackTrace ();
- }
- }
- }

- public class Demo11 {
- public static void main(String[] args) {
- try(OutputStream outputStream = new FileOutputStream ( "d:/test.txt",true )){//和Writer类似,OutputStream打开一个文件,就会清空文件原有的内容,如果不想清空,就写入追加写方式(在构造方法中,第二个参数传入true)
- String str = "你好世界";
- outputStream.write(str.getBytes( StandardCharsets.UTF_8 ));
- } catch (IOException e) {
- e.printStackTrace ();
- }
- }
- }
每次读写的最小单位是"字符"
本质上是针对字节流进行有一层封装
字符流,就能自动把文件中几个相邻的字节,转换成一个字符
GBK,一个中文字符 两个字节
UTF8,一个中文字符 三个字节

无参read:一次读一个字符
一个参数read:一次读取若干个字符,会把参数指定的cbuf数组给填充满
三个参数read:一次读取若干个字符,会把参数指定的cbuf数组,中的off这个位置开始,到len这么长的范围填满
cbuf是"输出型参数":read里转入的是一个空的字符数组,然后read方法内部,对这个数组进行填充
在java标准库内部,对于字符编码进行了很多的处理工作
如果只使用char,此时使用的是字符集,固定是unicode
如果使用String,此时就会自动的把每个字符的unicode转为utf8
- public class Demo7 {
- public static void main(String[] args) throws IOException {
- Reader reader = new FileReader ( "D:/test.txt" );
-
- //1.一次read读一个字符
- while (true) {
- int c = reader.read ();
- if (c == -1) {
- break;
- }
- char ch = (char) c;
- System.out.println ( "c=" + c );
- System.out.println ( ch );
- }
-
- //2.一次read读多个字符
- try {
- while (true) {
- char[] cbuf = new char[1];
- int n = reader.read ( cbuf );
- if (n == -1) {
- break;
- }
- System.out.println ( "n=" + n );
- for (int i = 0; i < n; i++) {
- System.out.println ( cbuf[i] );
- }
- }
- } finally {
- reader.close();
- }
-
- //3.一个文件使用完后,记得要close
- //reader.close();//一旦上面的逻辑抛出异常,就会导致close执行不到
-
- //使用close方法,最主要的目的是为了释放文件描述符
-
- //PCB包含很多属性
- //1.pid
- //2.内存指针
- //3.文件描述符表 顺序表(数组) 一个进程每次打开一个文件,就需要在这个表里分配一个元素,这个数组的长度是存在上限的
- //这就是文件资源泄露问提
- //java有GC,不需要手动释放
- //但是文件还是要手动释放
-
- }
- }
- public class Demo8 {
- public static void main(String[] args) throws IOException {
- try(Reader reader = new FileReader ( "D:/test.txt" )) {//try with recourses 这个语法目的就是()定义的变量会在try代码块结束时(正常结束,还是抛出异常)自动调用其中的close方法
- //要求写到()里的对象必须要实现Closeable接口
- while (true) {
- char[] cbuf = new char[1];
- int n = reader.read ( cbuf );
- if (n == -1) {
- break;
- }
- System.out.println ( "n=" + n );
- for (int i = 0; i < n; i++) {
- System.out.println ( cbuf[i] );
- }
- }
- }
- }
- }

一次写一个字符
一次写一个字符串
一次写多个字符(字符数组)
- public class Demo9 {
- public static void main(String[] args) throws IOException {
- try(Writer writer = new FileWriter ( "d:/test.txt",true )){
- writer.write("hello world");//Writer写入文件,默认会把原有的文件内容清空掉,如果不想清空就要在构造方法中多加个参数
- }
-
- }
- }
java标准库中支持的流流对象,种类很多,功能也非常丰富,上述介绍的,只是基础的四个流对象的使用
| 构造方法 | 说明 |
| Scanner(InputStream is, String charset) | 使用 charset 字符集进行 is 的扫描读取 |
- public class Demo12 {
- public static void main(String[] args) {
- try(InputStream inputStream =new FileInputStream ( "D:/test.txt" )) {
- //此时scanner就是从文件读取了
- Scanner scanner = new Scanner ( inputStream );
- String s = scanner.next();
- System.out.println (s);
- } catch (IOException e) {
- e.printStackTrace ();
- }
- }
- }
上述,我们其实已经完成输出工作,但总是有所不方便,我们接来下将 OutputStream 处理下,使用PrintWriter 类来完成输出,因为PrintWriter 类中提供了我们熟悉的 print/println/printf 方法
- public class Demo13 {
- public static void main(String[] args) {
- try(OutputStream outputStream =new FileOutputStream ( "d:/test.txt" )) {
- //这相当于把字节流转换为字符流了
- PrintWriter writer = new PrintWriter ( outputStream );
- writer.println ("你好");
-
- } catch (IOException e) {
- e.printStackTrace ();
- }
-
- }
- }

程序已经写入字符串,但文件还是空着的,为什么呢?
这涉及到"缓冲区"
PrintWriter这样的类,在进行写入的时候,不一定是直接写入硬盘,而是先把数据写入到一个内存构成的"缓冲区"(buffer)中
引入缓冲区是为了提高效率
相当于垃圾堆成一堆再一起去楼下扔掉
当我们把数据写入缓冲区后,如果还没来得及把缓冲区的数据写入硬盘,进程就结束了,此时数据就丢失了.
为了能够确保数据确实被写入硬盘
就应该在合适的时机使用flush方法,手动刷新缓冲区
- public class Demo13 {
- public static void main(String[] args) {
- try(OutputStream outputStream =new FileOutputStream ( "d:/test.txt" )) {
- //这相当于把字节流转换为字符流了
- PrintWriter writer = new PrintWriter ( outputStream );
- writer.println ("你好");
- writer.flush ();
- } catch (IOException e) {
- e.printStackTrace ();
- }
-
- }
- }
需要手动使用flush方法确保数据确实被写入硬盘--"刷新缓冲区"
扫描指定目录,并找到名称中包含指定字符的所有普通文件(不包含目录),并且后续询问用户是否要删除该文件
- public class Demo14 {
- public static void main(String[] args) {
- Scanner scanner = new Scanner ( System.in );
- //1.先让用户输入一个要扫描的目录
- System.out.println ("请输入要扫描的根目录:");
- String path = scanner.next ();
- File rootPath = new File ( path );
- if (!rootPath.isDirectory ()){
- System.out.println ("输入的文件路径有误");
- return;
- }
-
- //2.再让用户输入一个要查询的关键词
- System.out.println ("请输入要找的文件名中的字符:");
- String word = scanner.next ();
-
-
-
- //3.可以进行递归扫描
- //通过这个方法进行递归
- scanDir(rootPath,word);
-
-
-
- }
-
- private static void scanDir(File rootPath , String word) {
-
- //1.先列出rootPath中所有的文件和目录
- File[] files = rootPath.listFiles ();
- if(files == null){
- //当前目录为null,就可以直接返回了
- return;
- }else{
- for(File file:files){
- System.out.println (file.getAbsolutePath ());
- if(file.isDirectory ()){
- scanDir ( file,word );
- }else{
- checkDelete(file,word);
- }
- }
- }
-
- }
-
- private static void checkDelete(File file , String word) {
- if(file.getName ().contains ( word )){
- System.out.println ("当前文件为"+file.getAbsolutePath ());
- System.out.println ("请确认是否要删除(y/n):");
- Scanner scanner = new Scanner ( System.in );
- String choice = scanner.next ();
- if (choice.equals ( "Y" )||choice.equals ( "y" )){
- file.delete ();
- System.out.println ("删除完毕");
- }else{
- System.out.println ("取消删除");
- }
-
-
- }else{
- return;
- }
- }
- }
进行普通文件的复制
- public class Demo15 {
- public static void main(String[] args) throws IOException {
- Scanner scanner = new Scanner ( System.in );
-
- System.out.println ("请输入要复制的文件路径:");
- String srcPath = scanner.next ();
- File srcFile = new File ( srcPath );
-
- if(!srcFile.exists ()){
- System.out.println ("文件不存在,请确认路径是否正确");
- return;
- }
-
- if(!srcFile.isFile ()){
- System.out.println ("文件不是普通文件,请确认路径是否正确");
- return;
- }
-
- System.out.println ("请输入要复制到的目标路径:");
- String destPath = scanner.next ();
- File destFile = new File ( destPath );
- if(destFile.exists ()){
- if(destFile.isDirectory ()){
- System.out.println ("目标路径已经存在,并且是一个目录,请确认路径是否正确");
- return;
- }
-
- if(destFile.isFile ()){
- System.out.println ("目标路径已经存在,是否进行覆盖?y/n");
- String choice = scanner.next ();
- if(!choice.toLowerCase ().equals ( "y" )){
- System.out.println ("停止复制");
- return;
- }
- }
- }
-
- try(InputStream inputStream = new FileInputStream ( srcFile )){
- try(OutputStream outputStream = new FileOutputStream ( destFile )){
- byte[] buf = new byte[1024];
- int len;
- while(true){
- len = inputStream.read (buf);
- if (len==-1){
- break;
- }
-
- outputStream.write ( buf ,0,len);
- }
- outputStream.flush ();
- }
- }
- System.out.println ("复制完成");
- }
- }
扫描指定目录,并找到名称或者内容中包含指定字符的所有普通文件(不包含目录)
- public class Demo17 {
- public static void main(String[] args) throws IOException {
- Scanner scanner = new Scanner ( System.in );
- System.out.print ( "请输入要扫描的根目录(绝对路径 OR 相对路径): " );
- String rootDirPath = scanner.next ();
- File rootDir = new File ( rootDirPath );
- if (!rootDir.isDirectory ()) {
- System.out.println ( "您输入的根目录不存在或者不是目录,退出" );
- return;
- }
- System.out.print ( "请输入要找出的文件名中的字符: " );
- String token = scanner.next ();
- List
result = new ArrayList<> (); - // 因为文件系统是树形结构,所以我们使用深度优先遍历(递归)完成遍历
- scanDir ( rootDir , token , result );
- System.out.println ( "共找到了符合条件的文件 " + result.size () + " 个,它们分别 是" );
- for (File file : result) {
- System.out.println ( file.getCanonicalPath () );
-
- }
- }
-
- private static void scanDir(File rootDir , String token , List
result) { - File[] files = rootDir.listFiles ();
- if (files == null || files.length == 0) {
- return;
- }
- for (File file : files) {
- if (file.isDirectory ()) {
- scanDir ( file , token , result );
- } else {
- if (file.getName ().contains ( token )) {
- result.add ( file.getAbsoluteFile () );
- }
- }
- }
- }
- }