java IO流注意项总结

IO流有很多种,按照操作数据的不同,可以分为字节流和字符流

按照数据传输方向的不同又可分为输入流和输出流,程序从输入流中读取数据,向输出流中写入数据

在IO包中,字节流的输入输出流分别用java.io.InputStream和java.io.OutputStream表示,字符流的输入输出流分别用java.io.Reader和java.io.Writer表示。
在这里插入图片描述
在计算机中,无论是文本、图片、音频还是视频,所有文件都是以二进制(字节)形式存在的

IO流中针对字节的输入输出提供了一系列的流,统称为字节流。字节流是程序中最常用的流,根据数据的传输方向可将其分为字节输入流和字节输出流。在JDK中,提供了两个抽象类InputStream和OutputStream

InputStream和OutputStream是字节流的顶级父类,所有的字节输入流都继承自InputStream,所有的字节输出流都继承自OutputStream。

可以把InputStream和OutputStream比作两根“水管”,如图所示。

在这里插入图片描述
在JDK中,InputStream和 OutputStream提供了一系列与读写数据相关的方法,如下表所示。
在这里插入图片描述

前三个read()方法都是用来读数据的,其中,第一个read()方法是从输入流中逐个读入字节
第二个和第三个read()方法则将若干字节以字节数组的形式一次性读入,从而提高读数据的效率。
在进行IO流操作时,当前IO流会占用一定的内存,由于系统资源宝贵,因此,在IO操作结束后,应该调用close()方法关闭流,从而释放当前IO流所占的系统资源。

与InputStream对应的是OutputStream。OutputStream是用于写数据的,如下表所示。
在这里插入图片描述
表中OutputStream类的五个常用方法。前三个是重载的write()方法,都是用于向输出流写入字节
其中,第一个方法逐个写入字节,后两个方法是将若干个字节以字节数组的形式一次性写入,从而提高写数据的效率。
flush()方法用来将当前输出流缓冲区(通常是字节数组)中的数据强制写入目标设备,此过程称为刷新。close()方法是用来关闭流并释放与当前IO流相关的系统资源。

InputStream和OutputStream这两个类是抽象类,不能被实例化,因此,针对不同的功能,InputStream和OutputStream提供了不同的子类,这些子类形成了一个体系结构。
在这里插入图片描述
在这里插入图片描述

计算机中的数据基本都保存在硬盘的文件中,因此操作文件中的数据是一种很常见的操作。

在操作文件时,最常见的就是从文件中读取数据并将数据写入文件,即文件的读写。

针对文件的读写,JDK专门提供了两个类,分别是FileInputStream和FileOutputStream。

FileInputStream是InputStream的子类,它是操作文件的字节输入流,专门用于读取文件中的数据。由于从文件读取数据是重复的操作,因此需要通过循环语句来实现数据的持续读取。

    public static void test1() throws Exception{
//        System.out.println(System.getProperty("user.dir"));
        FileInputStream in=new FileInputStream("src/src.txt");
        int b=0;
        while (true){
            b=in.read();
            if(b==-1){
                break;
            }
            System.out.println(b);
        }
        in.close();
    }
   public static void test2() throws Exception{
        FileInputStream in=new FileInputStream("src/src.txt");
        int len;
        byte[] bytes=new byte[1024];
        while (true){
            len=in.read(bytes);
            System.out.println(len);
            if(len==-1){
                break;
            }
            for(int i=0;i<len;i++){
                System.out.println(bytes[i]);
            }
        }
        in.close();
    }

FileOutputStream是OutputStream的子类,它是操作文件的字节输出流,专门用于把数据写入文件。

public class test1 {
    public static void main(String[] args) throws Exception{
//        test1();
        test2();
    }

    public static void test1() throws Exception{
        FileOutputStream out=new FileOutputStream("src/example.txt");
        String str="java is good";
        byte[] bytes=str.getBytes();
        for(int i=0;i< bytes.length;i++){
            out.write(bytes[i]);
        }
        out.close();
    }

    public static void test2() throws Exception{
        FileOutputStream out=new FileOutputStream("src/example.txt");
        String str="java is very good";
        byte[] bytes=str.getBytes();
        out.write(bytes);
        out.close();
    }
}
public class test3 {
    public static void main(String[] args) throws Exception {
        OutputStream out=new FileOutputStream("src/example.txt",true);
        byte[] bytes="欢迎您".getBytes();
        for(int i=0;i< bytes.length;i++){
            out.write(bytes[i]);
        }
        out.close();
    }
}

由于IO流在进行数据读写操作时会出现异常,为了代码的简洁,在上面的程序中使用了throws关键字将异常抛出。
然而一旦遇到IO异常,IO流的close()方法将无法得到执行,流对象所占用的系统资源将得不到释放
因此,为了保证IO流的close()方法必须执行,通常将关闭流的操作写在finally代码块中,具体代码如
下所示:

public class test {
    public static void main(String[] args) throws Exception {
        OutputStream out=null;
        try {
            out=new FileOutputStream("example.txt",true);
            byte[] bytes="欢迎您".getBytes();
            for(int i=0;i< bytes.length;i++){
                out.write(bytes[i]);
            }
        }finally {
            if (out !=null){
                out.close();
            }
        }
    }
}

在应用程序中,IO流通常都是成对出现的,即输入流和输出流一起使用。

一个字节一个字节的读写,需要频繁的操作文件,效率非常低。
这就好比从北京运送烤鸭到上海,如果有一万只烤鸭,每次运送一只,就必须运输一万次,这样的效率显然非常低。
为了减少运输次数,可以先把一批烤鸭装在车厢中,这样就可以成批的运送烤鸭,这时的车厢就相当于一个临时缓冲区。

当通过流的方式拷贝文件时,为了提高效率也可以定义一个字节数组作为缓冲区。在拷贝文件时,可以一次性读取多个字节的数据,并保存在字节数组中,然后将字节数组中的数据一次性写入文件。

在IO包中提供两个带缓冲的字节流,分别是BufferedInputStream和BufferedOutputStream,它们的构造方法中分别接收InputStream和OutputStream类型的参数作为对象,在读写数据时提供缓冲功能。应用程序、缓冲流和底层字节流之间的关系如图所示。
在这里插入图片描述

import java.io.*;

public class test {
    public static void main(String[] args) throws Exception {
//        test1();
//        test2();
        test3();

    }

    //原始
    public static void test1() throws Exception{
        InputStream in=new FileInputStream("src/example.txt");
        OutputStream out=new FileOutputStream("src/out.txt");
        int content;
        long startTime=System.currentTimeMillis();
        while ((content=in.read())!=-1){
            out.write(content);
        }
        long endTime=System.currentTimeMillis();
        System.out.println("拷贝文件消耗的时间是:"+(endTime-startTime));
        in.close();
        out.close();
    }

    //数组缓存
    public static void test2() throws Exception{
        InputStream in=new FileInputStream("src/example.txt");
        OutputStream out=new FileOutputStream("src/out.txt");
        byte[]  buff=new byte[1024];
        int len;
        long startTime=System.currentTimeMillis();
        while ((len=in.read(buff))!=-1){
            out.write(buff,0,len);
        }
        long endTime=System.currentTimeMillis();
        System.out.println("拷贝文件消耗的时间是:"+(endTime-startTime));
        in.close();
        out.close();
    }

    //JAVA缓存类
    public static void test3() throws Exception{
        BufferedInputStream bis=new BufferedInputStream(new FileInputStream("src/example.txt"));
        BufferedOutputStream bos=new BufferedOutputStream(new FileOutputStream("src/out.txt"));
        int content;
        long startTime=System.currentTimeMillis();
        while ((content=bis.read())!=-1){
            bos.write(content);
        }
        long endTime=System.currentTimeMillis();
        System.out.println("拷贝文件消耗的时间是:"+(endTime-startTime));
        bis.close();
        bos.close();

    }
}

在这里插入代码片

字符流

InputStream类和OutputStream类在读写文件时操作的都是字节,如果希望在程序中操作字符,使用这两个类就不太方便
为此JDK提供了字符流。同字节流一样,字符流也有两个抽象的顶级父类,分别是Reader和Writer。
其中Reader是字符输入流,用于从某个源设备读取字符
Writer是字符输出流,用于向某个目标设备写入字符。

Reader和Writer作为字符流的顶级父类,也有许多子类,接下来通过继承关系图来列出Reader和Writer的一些常用子类,如图所示。
在这里插入图片描述
在程序开发中,经常需要对文本文件的内容进行读取和写入
如果想从文件中直接读取字符便可以使用字符输入流FileReader
如果想将字符写入文件中便可以使用字符输出流FileWriter
FileReader和FileWriter都有相应的缓存类

    public static void main(String[] args) throws Exception {
        FileReader reader=new FileReader("src/src.txt");
        int ch;
        while ((ch=reader.read())!=-1){
            System.out.println((char) ch);
        }
        reader.close();
    }
    public static void main(String[] args) throws Exception{
        FileWriter writer=new FileWriter("src/writer.txt");
        String str="大家好 各位同学";
        writer.write(str);
        writer.write("\r\n");//转义字符
        writer.close();
    }
    public static void main(String[] args) throws Exception {
        System.out.println(System.getProperty("user.dir"));
        FileReader reader=new FileReader("src/src.txt");
        BufferedReader br=new BufferedReader(reader);

        FileWriter writer=new FileWriter("src/des.txt");
        BufferedWriter bw=new BufferedWriter(writer);

        String str=null;
        while ((str=br.readLine())!=null){
            bw.write(str);
            bw.newLine();//额外的换行操作
        }
        br.close();
        bw.close();
    }

IO流可分为字节流和字符流,有时字节流和字符流之间也需要进行转换。
在JDK中提供了两个类可以将字节流转换为字符流,它们分别是InputStreamReader和OutputStreamWriter。
OutputStreamWriter是Writer的子类,它可以将一个字节输出流转换成字符输出流,方便直接写入字符,InputStreamReader是Reader的子类,它可以将一个字节输入流转换成字符输入流,方便直接读取字符。通过转换流进行数据读写的过程如图所示。
在这里插入图片描述

为了提高读写效率,可以通过BufferedReader和BufferedWriter来实现转换工作

    public static void main(String[] args) throws Exception{
        FileInputStream in=new FileInputStream("src/src.txt");
        InputStreamReader isr=new InputStreamReader(in);
        BufferedReader br=new BufferedReader(isr);

        //
        OutputStream out=new FileOutputStream("src/des.txt");
        OutputStreamWriter osw=new OutputStreamWriter(out);
        BufferedWriter bw=new BufferedWriter(osw);

        //
        String line=null;
        while ((line=br.readLine())!=null){
            bw.write(line);
            bw.newLine();
        }
        br.close();
        bw.close();

    }

File类
File类用于封装一个路径,这个路径可以是从系统盘符开始的绝对路径,如:
“D:\file\a.txt”,也可以是相对于当前目录而言的相对路径,如:“src\Hello.java”
File类内部封装的路径可以指向一个文件,也可以指向一个目录,在File类中提供了针对这些文件或目录的一些常规操作。接下来首先介绍一下File类常用的构造方法,如表所示。
在这里插入图片描述
表中列出了File类的三个构造方法。通常来讲,如果程序只处理一个目录或文件,并且知道该目录或文件的路径,使用第一个构造方法较方便。如果程序处理的是一个公共目录中的若干子目录或文件,那么使用第二个或者第三个构造方法会更方便。

File类中提供了一系列方法,用于操作其内部封装的路径指向的文件或者目录,例如判断文件/目录是否存在、创建、删除文件/目录等。
在这里插入图片描述
首先在当前目录下创建一个文件“example.txt”并输入内容“test”

    public static void main(String[] args) {
        File file=new File("src/example.txt");//相对路径
        System.out.println("文件名称:"+file.getName());
        System.out.println("文件的相对路径:"+file.getPath());
        System.out.println("文件的绝对路径:"+file.getAbsolutePath());
        System.out.println("文件的父路径:"+file.getParent());
        System.out.println(file.canRead()?"文件可读":"文件不可读");
        System.out.println(file.canWrite()?"文件可写":"文件不可写");
        System.out.println(file.isFile()?"是一个文件":"不是一个文件");
        System.out.println(file.isDirectory()?"是绝对路径":"不是绝对路径");
        System.out.println("最后的修改时间:"+file.lastModified());
        System.out.println("文件大小为:"+file.length()+"bytes");
        System.out.println("是否成功删除文件:"+file.delete());
    }

File类中有一个list()方法,该方法用于遍历某个指定目录下的所有文件的名称。

    public static void main(String[] args) {
//        test1();
        test2();
    }

    //遍历目录下的文件
    public static void test1(){
        File file=new File("src/test");
        if(file.isDirectory()){
            String[] names=file.list();
            for (String name:names){
                System.out.println(name);
            }
        }
    }

    //遍历目录及其子目录下的文件
    public static void test2(){
        File file=new File("src/test");
        filterDir(file);
    }


    private static void filterDir(File dir){
        File[] files=dir.listFiles();
        for(File file:files){
            if(file.isDirectory()){//方法的递归
                filterDir(file);
            }
            System.out.println(file.getAbsolutePath());
            file.delete();
        }
    }

接下来分步骤分析list(FilenameFilter filter)方法的工作原理:
1.调用list()方法传入FilenameFilter文件过滤器对象。
2.取出当前File对象所代表目录下的所有子目录和文件。
3. 对于每一个子目录或文件,都会调用文件过滤器对象的accept(File dir,String name)方法,并把代表当前目录的File对象以及这个子目录或文件的名字作为参数dir和name传递给方法。
4. 如果accept()方法返回true,就将当前遍历的这个子目录或文件添加到数组中,如果返回false,则不添加。

  public static void main(String[] args) {
        FilenameFilter filter=new FilenameFilter() {
            @Override
            public boolean accept(File dir, String name) {
                File currFile=new File(dir,name);
                if(currFile.isFile() && name.endsWith(".txt")){
                    return true;
                }else {
                    return false;
                }
            }
        };
        File file=new File("src/test");
        if(file.exists()){
            String[] lists=file.list(filter);
            for(String name:lists){
                System.out.println(name);
            }
        }
    }

在操作文件时,经常需要删除一个目录下的某个文件或者删除整个目录,这时读者可以使用File类的delete()方法。接下来通过一个案例来演示使用delete()方法删除文件。
首先在电脑中创建一个名称为test的文件夹,然后在文件夹中创建一个文本文件。

  public static void main(String[] args) {
//        test1();
        test2();
    }

    //删除普通文件
    public static void test1(){
        File file=new File("src/test/test1.txt");
        if(file.exists()){
            System.out.println(file.delete());
        }
    }

    //删除目录及其子目录 文件
    public static void test2(){
        File file=new File("src/test");
        deleteDir(file);
    }

    private static void deleteDir(File dir){
        if(dir.exists()){
            File[] files=dir.listFiles();
            for(File file:files){
                if(file.isDirectory()){
                    deleteDir(file);
                }else {
                    file.delete();
                }
            }
            dir.delete();
        }
    }
  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

侬本多情。

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值