Bootstrap

JAVA常用类库——文件IO

File类

File有文件的意思,是唯一与文件有关的类。File类就是文件和目录路径名的抽象表示,通俗来说一个File对象就可以表示一个文件的抽象。

此类的定义方式如下

public class File extends Object implements Serializable,Comparable<File>

FIle类中的常量

No.常量名称类型描述
1public static final String pathSeparator常量表示路径的分隔符(windows是:";"),方便起见用字符串表示
2public static final String sparator常量表示路径的分隔符(windows是:"\"),方便起见用字符串表示

File类中的构造方法

No.方法名称类型描述
1File(File parent,String child)构造从父抽象路径名和子路径名字符串创新的File实例
2Flie(String pathname)构造通过将给定的路径名字符串转换为抽象路径名来创建新的File实例
3File(String parent,String child)构造从父路径名字符串和子路径名字符串创建新的File实例
4File(URI uri)构造通过将给定的file:URL转换为抽象路径名来创建新的File实例

File类中的主要操作方法

No.方法名称类型描述
1public boolean creatNewFile()普通当且仅当具有此名称的文件不存在时,创建由此抽象路径命名的新空文件
2public boolean delete()普通删除由此抽象路径名表示的目录或文件
3public boolean is Directory()普通

判断此文件是否为目录

4public long length()普通返回文件的大小(字节表示)
5public String[] list()普通返回一个字符串数组,用于命名此抽象路径名表示的目录中的文件和目录。
6public boolean exists()普通判断文件是否存在
7public FIle[] listFiles()普通列出指定目录的全部内容
8public boolean mkdir()普通创建一个目录
9public boolean renameTo(File dest)普通重命名次抽象路径所表示的文件
10public String getAbsolutePath()普通获取该文件的绝对路径
11public String getParent()普通获取该文件的父文件夹的绝对路径

创建文件实例:

import java.io.File;
import java.io.IOException;

public class Text1 {
    public static void main(String[] args) throws IOException {
        File f = new File("D://1.txt"); //创建一个file对象,这个对象代表,D盘下的1.txt文件。
        boolean l = f.createNewFile(); //调用createNewFile()方法创建这个文件夹,并将结果返回给l
        System.out.println(l);
        boolean k = f.createNewFile(); //再创建一次
        System.out.println(k);
    }
}

输出:
true
false

输出true,表示D盘下原本没有1.txt文件,创建成功。

输出false,表示D盘下已存在1.txt这个文件,创建失败

文件创建结果:

 文件重命名实例:

public class Text1 {
    public static void main(String[] args) throws IOException {
        File f = new File("D:\\1.txt"); //创建f对象代表1.txt文件
        File newf = new File("D:\\5.txt"); //创建newf对象代表5.txt文件
        f.renameTo(newf); //调用f的renameTo方法传入newf对象,1.txt重命名为5.txt 
                          //(本质:创建5.txt,将1.txt的内容复制到5.txt,删除1.txt)
    }
}

遍历文件实例:

以“谷歌浏览器下载”为例,遍历该文件夹,输出所以pdf文件的绝对路径

 代码:

import java.io.File;
import java.io.IOException;

public class Text1 {
    public static void main(String[] args) throws IOException {
        File file = new File("D:\\谷歌浏览器下载"); //实例化一个File对象,代表D盘下的谷歌浏览器下载文件夹
        File[] files = file.listFiles();//将文件夹的子文件依次实例化并存入File对象数组
        listFiles(files); //调用文件遍历方法,传入数组
    }
    //文件遍历方法
    public static void listFiles(File[] files){
        if(files != null && files.length > 0){ //判断对象数组是否为空,数组长度是否为零
            for (File f:files) { //遍历数组
                if(f.isFile()){ //如果该对象是文件
                    if (f.getName().endsWith(".pdf")){ //如果该文件后缀是.pdf
                        System.out.println("找到了一个pdf文件:"+f.getAbsolutePath());//输出文件的绝对路径
                    }
                }else{ //如果是文件夹
                    File[] files2 = f.listFiles(); //将文件夹的子文件依次实例化并存入数组
                    listFiles(files2); //递归
                }
            }
        }

    }
}

 输出

找到了一个pdf文件:D:\谷歌浏览器下载\IDEA的下载与安装使用.pdf
找到了一个pdf文件:D:\谷歌浏览器下载\XZK-Java教学-002-测试题-OOP.pdf
找到了一个pdf文件:D:\谷歌浏览器下载\XZK-Java教学-10101001-支线任务-语法训练 - 入门与学习方式(环境搭建与笔记整理).pdf
找到了一个pdf文件:D:\谷歌浏览器下载\XZK-Java教学-10101002-支线任务-语法训练 - 小王的面试(基础语法).pdf
找到了一个pdf文件:D:\谷歌浏览器下载\XZK-Java教学-10101006-支线任务-语法训练 - 选队长的游戏(数组).pdf
找到了一个pdf文件:D:\谷歌浏览器下载\XZK-Java教学-10201-主线任务-快递e栈-面向对象.pdf
找到了一个pdf文件:D:\谷歌浏览器下载\XZK-Java教学-10201001-支线任务-面向对象 - 面向对象基础训练(面向对象基础)  (1).pdf
找到了一个pdf文件:D:\谷歌浏览器下载\XZK-Java教学-10201001-支线任务-面向对象 - 面向对象基础训练(面向对象基础) .pdf
找到了一个pdf文件:D:\谷歌浏览器下载\XZK-Java教学-10201003-支线任务-面向对象 - 面向对象进阶训练(面向对象高级) .pdf
找到了一个pdf文件:D:\谷歌浏览器下载\XZK-Java教学-10201004-支线任务-面向对象 - 猜拳游戏(面向对象) .pdf
找到了一个pdf文件:D:\谷歌浏览器下载\XZK-Java教学-10202-主线任务-快递e栈-更换IDEA (1).pdf
找到了一个pdf文件:D:\谷歌浏览器下载\XZK-Java教学-10202-主线任务-快递e栈-更换IDEA.pdf
找到了一个pdf文件:D:\谷歌浏览器下载\XZK-Java教学-10203001-支线任务-异常处理 - 快递管理(异常处理) (1).pdf
找到了一个pdf文件:D:\谷歌浏览器下载\XZK-Java教学-10203001-支线任务-异常处理 - 快递管理(异常处理).pdf
找到了一个pdf文件:D:\谷歌浏览器下载\XZK-Java教学-10301-主线任务-快递e栈-核心类库.pdf
找到了一个pdf文件:D:\谷歌浏览器下载\XZK-Java教学-10301001-支线任务-String类.pdf

 大伙可以运用这个知识点自己写一个垃圾清理软件,只需再添加一个删除语句即可。但是,如果你还不是很了解文件的话,建议做好重装系统的准备hhhhhh。

 

相对路径和绝对路径

绝对路径:从盘符开始,是一个完整的路径,例如D:\1.txt
相对路径:在JAVA代码中是相对于项目目录路径,这是一个不完整的便捷路径,在java开发中很常用。例如:a.txt

import java.io.File;
import java.io.IOException;

public class Text1 {
    public static void main(String[] args) throws IOException {
        File f1 = new File("D:\\1.txt");//绝对
        File f2 = new File("a.txt");  //相对
        System.out.println("f1的路径:"+f1.getAbsolutePath());
        System.out.println("f2的路径:"+f2.getAbsolutePath());
    }
}

输出

f1的路径D:\1.txt
f2的路径D:\IDEA projects\TEXT1\a.txt //D:\IDEA projects\TEXT1是我项目的路径,每个人都有各自的项目路径,不必追求相同

IO流

概述:可以将数据传输操作,看做一种数据的流动,按照流动的方向分为输入Input和输出Output。JAVA中的IO操作主要是java.io包下的一些常用类的使用,通过这些类对数据进行读取(输入流Input)和写出(输出流Output)

IO流分类:
按照流动方向:输入流、输出流
按照流动的数据类型:字节流、字符流

输入有N种方式,输出也有N种方式,他们是按照类的结构划分 ,以下是他们的顶级父类:

字节流

  • 输入流:InputStream
  • 输出流:OutputStream

字符流

  • 输入流:Reader
  • 输出流:Writer

字节输出流-OutputStream

一切皆字节:计算机中的任何数据(文本、图片、视频、音乐)都是以二进制存储的。在数据传输时,也都是以二进制的形式存储的。后续学习的任何流,在传输时底层都是二进制。

这是一个抽象类,此抽象类是表示输出字节流的所有类的顶级父类

该类定义如下

public abstract class OutputStream extends Object implements Closeable, Flushable

该类的所以方法

No.方法名称类型描述
1public void close()普通关闭此输出流并释放与此流关联的所以系统资源
2public void flush()普通刷新此输出流并强制写出任何缓冲的输出字节
3public static OutputSteram nullOutputStream()普通返回一个新的OutputSteram,它丢弃所以字节
4public void write(byte[] b)普通将b.length字节从指定的字节数组写入此输出流
5public void write(byte[] b,int off,int len)普通将从下标off开始的指定字节数组中的len字节写入此输出流
6public abstract void write(int b)抽象将制定的字节写出此输出流

FileOutputStream

输出实例一

import java.io.FileOutputStream;
import java.io.IOException;

public class Text1 {

    public static void main(String[] args) throws IOException {
        FileOutputStream f = new FileOutputStream("D:\\1.txt"); //创建一个输出流对象,可以理解为一个向文件输出东西的管道
        f.write(65); //输出字节65
        f.close();  //释放资源
        System.out.println("已经写出");
    }
}
输出
已经写出

此时我们的D盘下会自动生成1.txt文件:

为什么我们写出65会变成A ?因为按照ASCII码,65字节对应的就是字符A

简而言之:字符A的ASCII码是65 字节(Byte) 

输出案例二

import java.io.FileOutputStream;
import java.io.IOException;

public class Text1 {

    public static void main(String[] args) throws IOException {
        FileOutputStream f = new FileOutputStream("D:\\1.txt");
        byte[] bytes = {65,66,67}; //给一个字节数组
        f.write(bytes); //写出这个字节数组
        f.close();
        System.out.println("已经写出");
    }
}

输出案例三

import java.io.FileOutputStream;
import java.io.IOException;

public class Text1 {

    public static void main(String[] args) throws IOException {
        FileOutputStream f = new FileOutputStream("D:\\1.txt",true); //传入true ,证明不清空文件内容,继续追加
        byte[] bytes = {65,66,67};
        f.write(bytes);
        f.close();
        System.out.println("已经写出");
    }
}

 输出案例四

import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.charset.StandardCharsets;

public class Text1 {

    public static void main(String[] args) throws IOException {
        FileOutputStream f = new FileOutputStream("D:\\1.txt");
        byte[] bytes = {65,66,67,68};
        f.write(bytes,1,2); //从1下标开始往后写出,总共写2位
        f.close();
        System.out.println("已经写出");
    }
}

字节输入流-InputStream

此抽象类是表示输入字节流的所有类的顶级父类。定义如下

public abstract class InputStream extends Object implements Closeable

此类的方法如下

 

No.方法名称类型描述
1public int available()普通返回可以从此输入流中无阻塞地读取(或跳过)的字节数的估计值,可以使0,或者在检测到流结束时为0
2public void close()普通关闭此输入流并释放与改流关联的所有系统资源
3public mark(int readlimit)普通标记此输入流的当前位置
4public static InputStream boolean markSupported()普通返回一个不读取任何字节的新InputStream
5public abstract int read() 抽象从输出流中读取下一个数据字节
6public int read(byte[] b)普通从输入流中读取一些字节数并将它们存储到缓冲区数组b
7public int read(byte[] b, int off, int len)普通读入一个字节数组,从off开始len个数据
8public byte[] readAllBytes()普通从输入流中读取所以剩余字节。
9public int readNBytes(byte[] b, int off, int len)普通从输入流中读取请求的字节数到给定的字符数组中
10public byte[] readNBytes(int len)普通从输出流中读取指定的字节数
11public void reset()普通将此流重新定位到上次在此输入流上调用mark方法时的位置
12public long skip(long n)普通跳过并丢弃此输入流中的n字节数据
13public transferTo(OutputStream out)普通从该输入流中读取 所以字节,并按读取顺序将字节写入给定的输出流
14public boolean markSupported()普通测试此输入流是否指出mark和reset方法

FileInputStream

此类是InputStream的子类

写入文件实例一:

import java.io.*;

public class Text1 {

    public static void main(String[] args) throws IOException {
        FileInputStream fis = new FileInputStream("D:\\1.txt"); //实例化文件输入流对象,并指定文件1.txt
        while(true){
            byte b = (byte)fis.read(); //读入一个字节,赋值给b
            if(b == -1){
                break;  //如果读取到-1,证明文件已被读完,退出循环
            } 
            System.out.println((char)b); //输出该字节代表的字符
        }
    }
}
输出
A
B
C
D
E
F
G

写入文件实例二及其问题

import java.io.*;

public class Text1 {

    public static void main(String[] args) throws IOException {
        FileInputStream fis = new FileInputStream("D:\\1.txt");
        byte[] b = new byte[10]; //定义一个是个长度的字节数组
        fis.read(b); //读入数据直到b存满
        System.out.println(new String(b)); //输出转化为字符串的b
        fis.read(b); //重复
        System.out.println(new String(b));
        fis.read(b);
        System.out.println(new String(b));
        fis.close();
    }
}
输出
ABCDEFGHIJ
KLMNOPQRST
UVWXYZQRST

我们观察到输出结果好像与我们的预期不太符合,按道理最后依次输出到Z后面就没有了,可为什么会给我们补上了。原因是后四位是上一次存入数组的旧数据,最后一次读入只将前六位覆盖了,而最后四个没有覆盖,仍然是之前的数据。

解决办法是:获取每次读取到的字节数,只输出该字节数个字符串即可。

修改代码

import java.io.*;

public class Text1 {

    public static void main(String[] args) throws IOException {
        FileInputStream fis = new FileInputStream("D:\\1.txt");
        byte[] b = new byte[10];
        int len = fis.read(b); //获取读取的字节的数量 10
        System.out.println(new String(b,0,len)); //表示从0下标开始,读取len个字符串
        len = fis.read(b); //10
        System.out.println(new String(b,0,len));
        len = fis.read(b); //6
        System.out.println(new String(b,0,len));
        fis.close();
    }
}
输出
ABCDEFGHIJ
KLMNOPQRST
UVWXYZ

注:我们更多使用字节数组来读数据

字节流读取文字问题

import java.io.*;
public class Text1 {

    public static void main(String[] args) throws IOException {
        FileInputStream fis = new FileInputStream("D:\\1.txt");
        byte[] b = new byte[10];
        fis.read(b);
        String s = new String(b);
        System.out.println(s);
        fis.close();
    }
}
输出
锄禾日�

我们发现只读取了三个字,后面加一个乱码,这是因为“当”字还没有读取完字节数组就已经装满。导致不完整的显示,当然,我们可以通过增大字节数组容量解决该问题,那如果文字量非常巨大,那么通过增加字节数组的容量解决问题太过麻烦。所以我们读取文字的时候,一般使用字符流。

 

字符输出流-Writer

输出实例:

import java.io.*;

public class Text1 {

    public static void main(String[] args) throws IOException {
        Writer w = new FileWriter("D:\\1.txt",true);
        w.write("锄禾日当午");
        w.close();
    }
}

结果

字符输入流-Reader

输入实例一:输入单个字符

import java.io.*;
public class Text1 {

    public static void main(String[] args) throws IOException {
        Reader r = new FileReader("D:\\1.txt");
        char c =(char)r.read();
        System.out.println(c);
        r.close();
    }
}
输出
锄

输入实例二:输入一组字符及其问题

import java.io.*;
public class Text1 {

    public static void main(String[] args) throws IOException {
        Reader r = new FileReader("D:\\1.txt");
        char[] c = new char[100];
        r.read(c); 
        String s = new String(c);
        System.out.println(s);
        System.out.println(s.length());
        r.close();
    }
}
输出
锄禾日当午,汗滴禾下土                                                                                         
100

此时我们会发现,明明就只有11个字符,为什么字符串的长度是100?因为我们创建的字符数组,长度是100,并且是100个初始值,也就是空格,那么我们拼接到字符串的时候,空格也拼接过来了。

解决办法

import java.io.*;
public class Text1 {
    public static void main(String[] args) throws IOException {
        Reader r = new FileReader("D:\\1.txt");
        char[] c = new char[100];
        int len = r.read(c);
        String s = new String(c,0,len);
        System.out.println(s);
        System.out.println(s.length());
        r.close();
    }
}
输出
锄禾日当午,汗滴禾下土
11

定义一个变量len记录下存了读取了多少个字符,拼接字符串时只拼接前len个字符即可。

flush刷新管道

flush的功能是清空缓存区并将缓存区的内容全部写出到文件,而我们平时没有调用flush方法,内容也被写出了,那是因为我们的close方法,其实也是调用了flush方法的

来看一个例子

import java.io.*;
public class Text1 {

    public static void main(String[] args) throws IOException {
        Writer w = new FileWriter("D:\\1.txt");
        w.write("锄禾日当午");
    }
}

结果

内容没有被写入文件

再看

import java.io.*;
public class Text1 {

    public static void main(String[] args) throws IOException {
        Writer w = new FileWriter("D:\\1.txt");
        w.write("锄禾日当午");
        w.flush();
    }
}

结果

由此可得,当我们需要利用循环向文件写内容时,需要在循环体最后加上一句flush。这样就可以保证每次循环都向文件写入了数据。

转换流-字节流转换(装饰)为字符流

当我们要从网上获取数据时,拿到的是一个字节流,不太好获取文字,那么我们就可以进行流转换,转成字符流,就可以方便获取文字

以下拿字节输入流转字符输出流为例,输出流同理

FileInputStream fis = new FileInputStream("D:\\1.txt");//假设这是你拿到的一个字节输入流
InputStreamReader isr = new InputStreamReader(fis);//转化为字符输入流

缓存流——BufferedReader/BufferedWriter

可以一次读取或写入一行的缓存字符流

例:缓存输入流

import java.io.*;

public class Text2 {
    public static void main(String[] args) throws IOException {
       FileReader fr = new FileReader("1.txt");
       BufferedReader br = new BufferedReader(fr);
       br.readLine();    
       br.close(); 
    }

}

例:缓存输出流

import java.io.*;

public class Text2 {
    public static void main(String[] args) throws IOException {
       FileWriter fr = new FileWriter("1.txt");
       BufferedWriter bw = new BufferedWriter(fr);
       bw.write("123");
       bw.newLine();
       bw.close(); 
    }

}

 

try-with-resources

当我们文件IO时发生异常,并且try处理时,我们会发现如上图所示的问题。我们都知道流用完之后都要close关闭释放资源,那么在处理异常时,将close语句放到finally块里是最为合理的,但是我们发现finally块无法调用try块里创建的对象。此时利用try语句的新语法可以解决该问题。

我们可以在try块内创建对象,这样的话,程序会在try/catch过后,自动执行该对象的close方法。

注意:想要完成这个操作的类,必须实现Closeable或者AutoCloseable接口。Closeable接口继承了AutoCloseable接口,AutoCloseable接口内定义了一个抽象方法close()。也就是说,所有拥有close()方法的类,都实现可Closeable或AutoCloseable接口。

倘若,从外部传入了一个对象,那么此时我们无法在try的括号里new这个对象,那么怎么办,在JDK9时,进行了优化

直接将对象名放入括号内即可,如果要实现多个流对象自动关闭,那么以 ";" 隔开。

刚才我们说,只要是实现了Closeable接口,并且实现了其中的close抽象方法,那么该类就可以在try后自动调用close,那么我们自己来写一个类,实现一下Closeable接口玩玩看。

import java.io.*;

public class Text2 {
    public static void main(String[] args) throws IOException {
        Book b = new Book();
        try(b){

        }catch (Exception e){

        }
    }
    static class Book implements Closeable{

        @Override
        public void close() throws IOException {
            System.out.println("close方法被调用了");
        }
    }
}

运行结果

close方法被调用了

 

;