File 类的用法和 InputStream, OutputStream 的用法

前言        (红字部分要重点理解)   

    在上一篇文章中,  我们已经介绍过File类的一些用法了,这篇具体总结下。

 

 Java代码对文件内容操作:标准库中提供了很多类,是一组类,而学习一个类,先从构造方法开始,如下图:

       也就是说在构造File实例时,可以传的参数就是上图中的参数,可以是相对路径,也可以是绝对路径,如下代码:

package IO;

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

public class IODemo1 {
    public static void main(String[] args) throws IOException {
        //File file = new File("./美食.jpg"); //相对路径
        File file = new File("d:/我的相册/美食.jpg");//绝对路径
        System.out.println(file.getParent());
        System.out.println(file.getName());
        System.out.println(file.getPath());
        System.out.println(file.getCanonicalPath());
    }
}

运行结果:    

    Java代码在系统中创建一个文件: 如下代码,mkdir是创建目录(但是只能是创建一级目录),mkdirs是创建多级目录。

package IO;

import java.io.File;

public class IODemo3 {
    public static void main(String[] args) {
        File file = new File("text-dir/aaa/bbb");
        file.mkdir();
        //file.mkdir只能创建一级目录
        //创建多级目录
        file.mkdirs();
    }
}

     运行结果:

 在创建好文件之后,还可以用list方法看一下当前目录中都有哪些文件,有两种显示方式,显示的效果是一样的,只是返回的参数不同,如下代码:

public static void main1(String[] args) {
        //还可以显示文件里的内容  有两种显示方式
        File file = new File("text-dir");
        //list是以字符串数组的方式来显示
        String[] results = file.list();
        System.out.println(Arrays.toString(results));
        //listFiles是以文件数组的方式显示的
        File[] results2 = file.listFiles();
        System.out.println(Arrays.toString(results2));
    }

   运行结果:

   接下来就是Java操作文件内容了,标准库种提供了两种类,在介绍这两组类之前,要分清啥是输入和输出,认清楚方向:   下图解释了输入输出

 

   Java标准库中对操作文件内容提供了两组类:(上文已经说过普通文件的两种类型)

   1.针对文本文件,提供了一组类,统称为“字符流”  (典型代表:Reader,Writer)。

     说到流(stream)这个字可能很抽象,我们可以想想一下水流,水流的特点就是源源不断,一直流完为止,可以类比到从水龙头中放水,接100ml水可以有很多种接法,可以是一次性接100ml,也可以一次接50ml,分两次来接,也可以一次接1ml,分100次来接,但是总是要有一个单位的,比如说水龙头一次最少流一滴水,接水时再也不能少过这个基本单位了。

    字符流,基本单位就是每次最少读一个字符(这里的字符不仅仅是char类型,也可以是其他字符集的字符),也可以读多个字符(根据方法中的参数来决定),如下图,不带参数的方法默认就是读一个字符,

   注:创建File实例的时候,分割每个目录最好用 “ / ” 来表示,如果是反斜杠表示,就需要写成“\\”,因为需要转义。

package IO;

import java.io.*;

public class IODemo5 {
    public static void main(String[] args) {
        try(OutputStream outputStream = new FileOutputStream("d:/text.txt")) {
            outputStream.write(97);
            outputStream.write(98);
            outputStream.write(99);
            //此时也是一次写一个byte   read和write也可以一次读写多个字节
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    public static void main1(String[] args) throws IOException {
        //InputStream是一个抽象类,所以不能直接new对象,相当于IO操作不只是可以读写硬盘,也可以读写网卡
        //所以FileInputStream继承于InputStream,就说明此时是读系统文件
        /*InputStream inputStream = new FileInputStream("d:/text.txt");
        inputStream.close();//最后操作完文件内容一定要关闭资源,一定要记得!!很重要*/

        //此处就不用手动关闭资源了,InputStream实现了closed接口,这种写法会自动的调用关闭资源的方法
        try(InputStream inputStream =new FileInputStream("d:/text.txt")) {
            //读文件
            //read 一次返回的是一个字节
            //但是不是用byte来接收,看源码有解释,用一个-1来表示已经读完文件了
            //此处的返回值类型是int
            while (true) {
                int b = inputStream.read();
                if (b == -1) {
                    break;
                }
                System.out.println(b);
                //因为此时操作是字节流,所以输出的是对应的ASCII码值
            }

        }
    }
}

  看完上述代码,需要注意的点: (3)文件资源泄露是重点一定要重视!!

    (1)OutputStream outputStream = new FileOutputStream("d:/text.txt")  这一行代码可以看到,在new一个对象时,并没有直接new OutputStream,原因就是它是一个抽象类(可以看下源码),所以需要new 继承OutputStream类的子类。为啥它是一个抽象类呢?OutputStream就是IO,所以不只是可以读写硬盘上的文件,还可以读写网卡等等,其中FileOutputStream(文件中读写)这个子类就明确的说明了IO是要从文件中进行读写。

    (2)new一个对象在一个内存中,那咋样才能去读写硬盘中的文件呢,InputStream inputStream =new FileInputStream("d:/text.txt")这一行代码的意思就是打开文件,打开文件的意思就是让inputStream这个对象和硬盘中的("d:/text.txt")这个文件关联起来(就相当于有了一个遥控器(上一篇文章已经介绍过可以看下)),操作这个内存中的对象就相当于间接的操作硬盘中的文件了。

     (3)有打开文件就得有关闭文件,这个还真需要咱们Java程序员注意!(当然我还不是程序猿),Java不像C++一样需要手动释放资源(像内存啥的都是手动释放的),一般GC会帮我们释放资源(垃圾回收器)但是在文件操作这里,是需要我们手动进行释放的,释放的这个资源就是文件描述符,文件描述符这个概念是在进程,进程是使用PCB这样的结构来进行描述的,这里面就包含各种信息:1.pid(进程id)2.内存指针(表示当前这个进程是在、哪一部分内存中) 3.文件描述符表     (这个文件描述符表中就是一个个的文件描述符,文件描述符就是标识了当前进程都打开了哪些文件,一个文件描述符就相当于这个表中的位置,位置记载了这个文件的信息,当打开一个文件时,就会在这里边申请一个位置,文件描述符表可以当成一个数组,数组下标就是一个文件描述符,下标对应的元素就是文件在内核中的结构体的表示。)   可是这个数组的大小终究是有限的,不能无限的放这样的一个一个的文件描述符,所以就不能无限制的打开一个个文件但是又不释放资源(就是数组的那个下标),一旦数组存的元素满了,再继续打开文件,就会打开失败 -->  这个操作就交文件资源泄露。一旦文件资源泄露之后,会有很严重的后果(甚至比C++的内存泄露还要严重!!)

      但是又不用写很挫的代码,就是手动关闭文件,可以看上边代码:try(InputStream inputStream =new FileInputStream("d:/text.txt")) 用了这样的 写法,此时就不用手动关闭资源了,这个写法是try with resources(带资源的try操作) 它会在try中的代码执行完毕后自动帮助我们去关闭文件,为啥呢,我们可以看下图中的源码:OutputStream(InputStream也一样)实现了特定的Closeable接口,这个接口就是帮我们关闭文件的,也就相当于当try代码块结束后,就会自动帮助我们调用这个接口,进行文件的关闭,如果我们自己的代码实现了这个Closeable接口,也是可以用try with resources这样的语法来完成。

 

 2.针对二进制文件,提供了一组类,统称为“字节流” (典型代表:InputStream,OutputStream)

     而回到字节流中,就是从文件中读100个字节的数据,可以一次性读100个字节,也可以一次读50个字节,分两次来读,也可以一次读一个字节,分100次来读取。而字节流这个类中最小单位就是一个字节(就像是上文中的一滴水一样)。

package IO;

import java.io.*;

public class IODemo5 {
    public static void main(String[] args) {
        try(OutputStream outputStream = new FileOutputStream("d:/text.txt")) {
            outputStream.write(97);
            outputStream.write(98);
            outputStream.write(99);
            //此时也是一次写一个byte   read和write也可以一次读写多个字节
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    public static void main1(String[] args) throws IOException {
        //InputStream是一个抽象类,所以不能直接new对象,相当于IO操作不只是可以读写硬盘,也可以读写网卡
        //所以FileInputStream继承于InputStream,就说明此时是读系统文件
        /*InputStream inputStream = new FileInputStream("d:/text.txt");
        inputStream.close();//最后操作完文件内容一定要关闭资源,一定要记得!!很重要*/

        //此处就不用手动关闭资源了,InputStream实现了closed接口,这种写法会自动的调用关闭资源的方法
        try(InputStream inputStream =new FileInputStream("d:/text.txt")) {
            //读文件
            //read 一次返回的是一个字节
            //但是不是用byte来接收,看源码有解释,用一个-1来表示已经读完文件了
            //此处的返回值类型是int
            while (true) {
                int b = inputStream.read();
                if (b == -1) {
                    break;
                }
                System.out.println(b);
                //因为此时操作是字节流,所以输出的是对应的ASCII码值
            }

        }
    }
}

   此处需要注意的点:

       可能看完上述代码会有疑问:int b = inputStream.read()   无参数的read()不是一次读一个字节吗,为啥用int类型的变量来接收呢,我们可以看下源码:

 这段英文就给了我们答案:

     (1)一个字节(byte)可以表示整数的范围就是 -128 ~ +127,如果是无符号就是0 ~ 255,此时表示的范围是 0 ~ 255 ,但是后边又说了,如果读到这个文件的最后一个字节(就是读完文件时),要用 -1 来返回,也就是说要用一个byte之外的数字来返回,代表读完了这个文件,所以此时byte就超范围了,所以此时用int来接收这个read的字节。

     (2)此时我们来看运行结果:  如下图,我这read的是一个abc,为啥运行read的是二进制了呢,别忘了,我们现在介绍的是字节流,不是字符流,read的是一个个的字节,所以运行出来的这串数字就是对应文件里一个个字符的ASCII码值。

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

良月初十♧

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

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

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

打赏作者

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

抵扣说明:

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

余额充值