Java文件IO操作

目录

一、了解什么是文件

   狭义的文件:

  广义的文件:

二、文件的路径

①文件的绝对路径

②文件的相对路径

三、Java对于文件的操作

    File类的构造方法    

     File类的普通方法

 四、对于文件的内容操作

①FileInputStream(文件输入流)

      常用构造方法:传递一个字符串

       read方法

   (1)无参数的方式:read()

    (2)有参数的版本,参数为一个byte数组

 ②FileOutputStream(文件输出流)

 五、close()方法      

     需要及时关闭"流"的原因:

 六、更加优雅的close()方式

          采用Scanner来读取磁盘的内容:

  七、字符流      


一、了解什么是文件

   文件,File这个概念,在计算机当中,也是非常常见的一个词语。

   狭义的文件:

  指的的是计算机硬盘上面的文件和目录(文件夹)。

   如图:

   


  广义的文件:

 泛指计算机当中的很多的软硬件资源。

  操作系统把很多的硬件设备软件资源抽象成了文件,例如网卡等待...

下面将要重点讨论的,就是狭义的文件


二、文件的路径

       文件的路径,主要是有以下两种表示方式:

①文件的绝对路径

       绝对路径,也比较好理解,可以视为文件相对于某个盘,所存储的位置,一般情况下面,以D:/***/***这样的形式表示。

    例如在上图当中,jdk1.88这个文件夹,它的绝对路径就是:D:\jdk1.88

    

       需要注意的是,虽然在windows当中,现在表示一个文件的目录都是采用“\"作为目录的分隔符的。

       但是,实际上在代码的书写当中不可以采取反斜杠作为路径的分隔符,一般都是采取正斜杠的方式作为分隔符。 因为,反斜杠通常搭配一个普通的字符,都会把"\+某个字符"转义为其他的字符。

       这样,容易引发歧义。因此在代码的书写当中,还是要采取正斜杠"/"的方式。


②文件的相对路径

        相对路径,通常以当前所在目录作为基准,以"."或者".."开头,找到指定的路径。

        举个例子:

        当点击进入了:D:\jdk1.88\lib这个文件夹之后,当前的目录就是:D:\jdk1.88\lib       


       如果想在当前的D:\jdk1.88\lib 目录下面定位到第一个文件夹,也就是missioncontrol。就可以表示为:./missioncontrol

       其中: “./” 就表示当前的目录。


      同理可得:如果当前目录是D:\jdk1.88

      那么,想定位到lib目录,就是:./lib


三、Java对于文件的操作

    分为以下两类:

    1、针对文件的系统操作(创建、删除、重命名等等);

    2、针对文件内容的操作(针对某个文件的读写)。


    下面,首先介绍对于文件的系统操作,需要使用的类为File这个类。

    File类的构造方法    

构造方法说明
File(String pathname)

根据文件的路径创建一个File实例,实例可以是绝对路径,

也可以是相对路径

File(File parent,String child)根据父目录+孩子文件路径,创建一个新的File实例
File(String parent,String child)

根据父目录+孩子文件路径,创建一个新的File实例,与第2个不同的是,

父目录用路径表示


 其中,第一行的构造方法是最常见的

代码实现:

  File file=new File("D:/bookBook.txt");

 这样,就成功创建出来一个file的实例了。

 需要注意的事项有下面两点:


       ①file对象代表的文件,此时不一定在对应的位置当中存在。如果不存在,可以在后续调用file.mkdir()方法创建。

       如果已经存在了再次调用mkdir()方法创建,那么不会重新创建一个文件,也不会覆盖掉原来已经存在的文件的内容。

       ②如果是单个斜杠,代表创建一个文件,例如(...txt/...doc)

          如果两个及以上的斜杠,代表创建一个文件夹


     File类的普通方法

修饰符及返回
值类型
方法签名 方法签名 
StringgetParent()返回 File 对象的父目录文件路径
StringgetName()返回 FIle 对象的纯文件名称
StringgetPath()返回 File 对象的文件路径
String

 
getAbsolutePath()返回 File 对象的绝对路径
StringgetCanonicalPath()返回 File 对象的修饰过的绝对路径
booleanexists()判断 File 对象描述的文件是否真实存在
booleanisDirectory()判断 File 对象代表的文件是否是一个目录
booleanisFile()判断 File 对象代表的文件是否是一个普通文件
boolean
 
createNewFile()根据 File 对象,自动创建一个空文件。成功创建后返
回 true
booleandelete()根据 File 对象,删除该文件。成功删除后返回 true
voiddeleteOnExit()根据 File 对象,标注文件将被删除,删除动作会到JVM 运行结束时才会进行
String[]list()返回 File 对象代表的目录下的所有文件名
File[]listFiles()返回 File 对象代表的目录下的所有文件,以 File 对象表示
booleanmkdir()创建 File 对象代表的目录
booleanmkdirs()
创建 File 对象代表的目录,如果必要,会创建中间目
booleanrenameTo(File
dest)
进行文件改名,也可以视为我们平时的剪切、粘贴操作
booleancanRead()判断用户是否对文件有可读权限
booleancanWrite()判断用户是否对文件有可写权限

 下面,将演示几个常见的普通方法代码实现:

 ①file.getName(String name)

 假设此时文件"bookBook1.txt"已经存在了

 如下面的代码:

 //不一定在D盘当中一定存在这一个路径
        File file=new File("D:/bookBook.txt");
        System.out.println(file.getName());

  就会输出bookBook.txt,为对应文件的名称。


如果执行下面的代码:


        File file=new File("./bookBook.txt");
        System.out.println(file.getName());

  也会输出bookBook.txt。 


 ②file.getParent()


        File file=new File("D:/bookBook/hello.txt");
        System.out.println(file.getParent());

  此时,将输出hello.txt的最近一级的目录:即:D:\bookBook 


③file.getPath(),getAbsolutePath()

       此时,将分情况讨论:

       如果file的构造方法当中指定的是一个绝对路径,也就是类似于D:/***/***这样的路径,那么二者输出的时候没有任何差别。

        File file=new File("D:/bookBook/hello.txt");
        System.out.println(file.getAbsolutePath());
        System.out.println(file.getPath());

        File file=new File("./hello.txt");
        System.out.println(file.getAbsolutePath());
        System.out.println(file.getPath());

        如果file的构造方法当中指定的是一个相对路径,也就是类似于./***这样的路径,那么二者的差别在于:

        file.getAbsolutePatrh()返回的是”绝对路径“,也就是从D盘开始的路径。

        由于此时hello.txt位于idea当中,因此,返回的路径就是当前idea项目的路径+hello.txt

        file.getPath()返回的是这一个文件的”工作路径"。

        运行结果截图:

       


④file.isDirectory(String name)

   输出是否为一个目录。

  如果传递的参数为一个目录,也就是一个文件夹,那么返回true。

  如果传递的参数为一个文件的地址,那么返回false。

        //demo26为一个具体的项目文件夹
        File file=new File("D:/demo26");

        //test为一个txt文本文档
        File file1=new File("./test.txt");

        //输出true
        System.out.println(file.isDirectory());
        //输出false
        System.out.println(file1.isDirectory());

⑤file.createNewFile:在指定的目录创建一个文件,如果创建成功返回true,否则false

        //test为一个txt文本文档
        File file1=new File("./test1.txt");

        //创建成功返回true,创建失败返回false
        System.out.println(file1.createNewFile());

 同理,file.delete()也是删除一个指定目录的文件。


 四、对于文件的内容操作

       在三当中,提到了对文件内容进行读取操作,那么,下面将介绍对于文件内容的读取操作的一些介绍。

       我们常说的IO流,就是内存和硬盘资的数据交互的一种工具。

       图解:输入流、输出流:

       

 从磁盘读取数据到内存的流,就是输入流------->FileInputStream。

 从内存读取数据到磁盘的流,就是输出流------->FileOutputStream。

 因此,所谓的输入输出流,都是相对于应用程序来讲的。


①FileInputStream(文件输入流)

      常用构造方法:传递一个字符串

       此处的字符串,可以是相对目录,也可以是绝对目录。下面代码采用的是绝对目录的方式

            //如果文件不存在,就会抛出异常
            FileInputStream inputStream=new FileInputStream("./test.txt");

   需要注意的是,读取的字符串一定要是一个确确实实存在的文件,否则会抛出                  FileNotFoundException


       read方法

        此方法在FileInputStream内部是存在多态的:       

       下面,将简单介绍一下read方法的三个多态形式:


        (1)无参数的方式:read()

           FileInputStream是按照一个一个字节的顺序读取的

           相当于一次只读取一个字节的数据。

           read()方法的返回值却是int类型的。

                 那么它的含义就是,每次只读取一个字节的数据,读取的是它的ASCII码值。

          读取完一个数据之后,再次调用read()方法,会接着读取接下来的一个数据。

          当把整个文件都读取完毕之后,如果继续调用read()方法,那么会直接返回-1.

          代码实现

             

public static void main(String[] args) {
        try {
            FileInputStream inputStream= new FileInputStream("./test.txt");
            while (true){
                int b=inputStream.read();
                //当读取到的值为-1的时候,意味着已经读取完毕了,直接跳出循环
                if(b==-1){
                    break;
                }
                //此处需要强制类型转换,才可以读取到对应的真实的字符
                System.out.print(""+(char)b);
            }
            //使用完毕之后,需要手动关闭这个流
            inputStream.close();
        } catch (IOException e) {
            e.printStackTrace();
        }

     运行结果截图:

         


    (2)有参数的版本,参数为一个byte数组

      byte数组为read读取到的值所传递的数组。

      当第一次调用inputStream.read(byteArray)的时候,会一次性把目标文件当中的所有内容一次保存到byteArray当中,并且返回实际读取的文件的字节数量。

      读取完毕之后,第二次再次读取,会返回-1.


      代码实现:

public static void main(String[] args) {
        try {
            //如果文件不存在,就会抛出异常
            FileInputStream inputStream=new FileInputStream("./test.txt");
            //read无参数版本:一次读取一个字节
            byte[] buffer=new byte[1024];
            while (true){
                //读取到某一个值,让字符数组对这一个buffer进行读取
                //尽可能让
                int len=inputStream.read(buffer);
                if(len==-1){
                    break;
                }
                //遍历buffer数组
                for(int i=0;i<len;i++){
                    System.out.println(buffer[i]);
                }
            }
            //使用完毕之后,需要手动关闭这个流
            inputStream.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

     

        此时,运行结果为:

        从上往下依次对应hello的ASCII值 


  灵魂拷问:为什么第(2)种方式的效率更高?

       首先需要了解一个前提条件,就是,使用FileInputStream进行IO操作的时候,每IO一次的时间是相同的。

       而第一种方式当中,每循一次,read一次,相当于只IO一次,读取了一次文件,就把所有的内容读取到内存当中了。

       因此,第二种方式减少了IO次数,提高了执行的效率。


 ②FileOutputStream(文件输出流)

       文件专门是把内存当中的数据读取到文件(磁盘)当中。

       也是在构造方法当中指定了需要传输的对应的文件。

OutputStream outputStream=new FileOutputStream("./hello.txt");

       需要注意的是,构造方法当中指定的字符串一定要合法,合法的体现性为字符串一定要以文件目录的形式开头,例如./某某文件这样的格式。或者D:/***/***这样的格式,需要指定文件的目录。


       如果对应的目录下面不存在这个文件,那么会首先创建一个这样的文件出来;


        write()方法

        跟FileInputStream一样,也是存在多态的

       这个write() 方法的操作和read()类似,只是一个输入,一个输出。

       需要注意的是,如果程序启动之前,原来的文件当中仍然有内容,那么,第一次write()的时候,会把原来文件当中的内容全部清空。


 五、close()方法      

       这个方法的含义就是关闭当前的"流”。

       当进行完对应的读写操作之后,应当及时把这个"流"给关闭掉。

       就是调用inputStream.close()或者outputStream.close()。


     需要及时关闭"流"的原因:

        根据前面的知识,我们了解到,进程当中有一个重要的属性,就是文件描述符表,这样的一个"表"。其实相当于一个数组,记录了进程打开了哪些文件。

        一个进程当中的所有线程,共用一个文件描述符表

        每次打开文件的操作,就会在文件描述表当中,申请一个位置,把对应文件的信息存放进去。  每一个被打开了的文件,在内核当中,都是一个file_struct对象。 

        每关闭一个文件,都会把对应的表项释放掉。也就是销毁对应的file_struct对象

        而调用fileInputStream.close()或者fileOutputStream.close(),就相当于上面的"释放"操作。

        如果没有这个操作,那么会造成什么后果呢?

       也就是会让这个file_struct对象一直保留在文件描述符表当中,无法被销毁。这样,会造成极大的内存空间浪费。

       并且,当打开的文件数量越来越多的时候,有可能很快就会让这个"数组"被填满了。也就是当需要再次打开文件的时候,已经无法操作了。

       虽然Java当中有gc垃圾回收机制,会在inputStream或者outputStream被回收的时候释放资源。但是,如果打开的文件太多,有可能gc也来不及释放了。


     


 六、更加优雅的close()方式

       在了解了close()方法的含义之后,可以得出一个结论,就是close()操作一定是在打开文件之后必定进行的一个操作,也就是close()操作是必不可少的。因此,最好把close()操作放在finally代码块当中执行:如下代码:

 FileOutputStream outputStream=null;
 try {
         outputStream=new FileOutputStream("./hello.txt");
         outputStream.write(99);
     }finally {
          outputStream.close();
     }

      这样,就可以让close()操作一定被执行到了。 

      但是,Java当中提供了下面的更加"优雅"的关闭方式:

try (FileOutputStream outputStream = new FileOutputStream("./hello.txt")) {
            outputStream.write(99);
            outputStream.write(100);
            outputStream.write(101);
            outputStream.write(103);
        }

     这样,会在write()完所有的内容之后,自动关闭流,释放资源。

     但是,需要注意的是,不是任何一个对象都可以放到try()的括号当中的。

      一定要实现closeable接口:


          采用Scanner来读取磁盘的内容:

           我们通常想从控制台获取输入的时候,会创建Scanner对象:

Scanner scanner=new Scanner(System.in)

            其中,System.in就是一个输入流对象: 

             如果想让Scanner从磁盘当中读取内容,可以考虑这样操作:

public static void main(String[] args) {
        try (InputStream inputStream=new FileInputStream("./hello.txt")){
            
            Scanner scanner=new Scanner(inputStream);

            //此时读取的内容,就是从hello.txt当中读取的
            //这里将一次读取全部的内容
            System.out.println(scanner.next());

        } catch (IOException e) {
            e.printStackTrace();
        }
    }

           那么这样,读取到的内容就是对应hello.txt文件的全部内容了。

           同时,inputStream对象也已经被关闭了。,也就意味着scanner也被关闭了。 


  七、字符流      

         前面介绍的FileInputStream和FileOutputStream都是字节流,以字节为单位进行读写的。

         下面,将介绍字符流:FileReader以及字节流:FileWriter,以字符为单位进行读写的。

public static void main(String[] args) {
        try (Writer writer=new FileWriter("./hello.txt")){
            //写入的时候,如果是数字,那么会转化为对应的ASCII码的字母
            //如果是’单引号的字符,那么会直接写入单引号的字符
            writer.write('?');
            writer.flush();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

         当传入的为一个数字的时候,会把它转换为一个字符来进行读写。

   当传入的为一个字符的时候,直接写入一个对应的字符。


         flush方法

        当直接调用writer.write()方法的时候,其实是写入一个缓冲区当中。

        写操作执行完毕之后,内容可能还残留在缓冲区当中,还没有真正进入磁盘当中。

         因此,调用flush()方法,可以确保缓冲区当中的内容被同步到磁盘。

         


 

   

        

     

          

        

        

  • 4
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 4
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值