Java——I/O(输入/输出)

1、File类

File类中的java.io包是唯一代表磁盘文件本身的对象,能够创建、删除或重命名文件,判断硬盘上某个文件是否存在,查询文件最后修改时间等。

File类的常用的构造方法

1.1 创建File对象

所有的构造方法都需要传入文件的路径。如上图,如果程序只处理一个目录或文件,并且知道该目录或文件的路径,使用第一个构造方法较方便。如果程序处理的是一个公共目录中的若干子目录或文件,那么使用第二个或者第三个构造方法会更方便。

【案例】,

需要注意的是在创建File文件时传入的路径使用了" \\ " ,这是因为在windows中目录符号为反斜线" \ ",但反斜线在java中是特殊字符,表示转义符,所以使用反斜线时,前面应该再添加一个反斜线,即为" \\ ",除此之外,目录符号还可以用正斜线" / "表示,如:src/Hello.java

import java.io.File;
public class test{
    public static void main (String[] args){
        File f = new File("E:\\Java base\\第七章IO\\a.txt"); //使用绝对路径构造File对象
        File f1=new File("src\\Hello.java"); //使用相对路径构造File对象
        System.out.println(f);
        System.out.println(f1);
    }
}

【运行结果】

1.2 File类的常用方法

扫描二维码关注公众号,回复: 16307289 查看本文章

File类提供了一系列方法,用于操作其内部封装的路径指向的文件或目录。例如,判断文件或目录是否存在,文件的创建于删除文件等,如下图:

 【案例】

import java.io.File;
import java.io.IOException;
public class test{
    public static void main (String[] args)throws IOException{
        File f = new File("E:\\Java base\\第七章IO\\b.txt"); //使用绝对路径构造File对象
        if(f.exists()){     //如果存在这个文件就删除,否则就创建
            f.delete();
        }else{
            System.out.println(f.createNewFile());
        }
        //在磁盘下创建一层目录,并且在目录下创建文件
        File fileDemo=new File("E:\\Java base\\第七章IO\\hello\\b.txt");
        if(!(fileDemo.getParentFile().exists())){   //判断E:\\Java base\\第七章IO\\hello\目录是否存在
            fileDemo.getParentFile().mkdir();
        }
        if(fileDemo.exists()){      //如果存在这个文件就删除,否则就创建
            fileDemo.delete();
        }else{
            System.out.println(fileDemo.createNewFile());
        }
    }
}

 【运行结果】

 1.3 遍历目录下的文件

File类的list()方法用于遍历指定目录下的所有文件。

【案例】

import java.io.File;
import java.io.IOException;
public class test{
    public static void main (String[] args)throws IOException{
        //创建File对象
        File f = new File("E:\\Java base\\第七章IO"); 
        if(f.isDirectory()){   //判断File对象对应的目录是否存在
            String[] names=f.list(); //获得目录下的所有文件的文件名
            for (String name:names){
                System.out.println(name); // 输出文件名
            }

        }
    }
}

 【运行结果】

下面通过一个案例来演示如何遍历指定目录下所有扩展名为" .java "的文件。

import java.io.File;
import java.io.FilenameFilter;
import java.io.IOException;
public class test{
    public static void main (String[] args)throws IOException{
        //创建File对象
        File f = new File("E:\\Java base\\第七章IO");
        //创建过滤器对象
        FilenameFilter filter = new FilenameFilter() {
            //实现accept()方法
            public boolean accept (File dir,String name){
                File currFile = new File(dir, name);
                // 如果文件名以.java结尾返回true,否则返回false
                if (currFile.isFile()&&name.endsWith(".java")){
                    return true;
                }else{
                    return false;
                }
            }
        };
        if (f.exists()){  // 判断File对象对应的目录是否存在
            String[] lists=f.list(filter);  //获得过滤后的所有文件名数组
            for(String name:lists){
                System.out.println(name);
            }
        }
    }
}

【运行结果】


前面两个案例演示的都是遍历目录下文件的文件名,有时候在一个目录下,除了文件,还有子目录,如果想得到所有子目录下的File类对象,list()方法显然不能满足要求,这时需要使用FIle类提供的另一种方法listFiles()。

listFiles()方法返回一个File对象数组,当对数组中的元素进行遍历时,如果元素中还有子目录需要遍历,则需要使用递归。

【案例】

调用listFiles()方法把该目录下所有的子目录和文件存到一个File类型的数组fires中,在通过for循环遍历数组files,并对当前遍历的File对象进行判断,如果是目录就重新调用fileDir()方法进行递归,如果是文件就直接打印输出文件的路径,这样就成功遍历了该目录下所有的文件。

import java.io.File;
import java.io.IOException;
public class test{
    public static void main (String[] args)throws IOException{
        File file= 
            new File("E:\\Java base\\第七章IO");
            fileDir(file); //调用FileDir方法
    }
    public static void fileDir(File dir){
        File[] files = dir.listFiles(); //获得表示目录下所有文件的数组
        for (File file :files){     //遍历所有的子目录和文件
            if(file.isDirectory()){ 
                fileDir(file); //如果是目录,递归调用fileDir()
            }
            System.out.println(file.getAbsolutePath()); //输出文件的绝对路径
        }
    }
}

【运行结果】

这样就遍历到了所有的文件

1.4 删除文件及目录

【案例】

import java.io.File;
import java.io.IOException;
public class test{
    public static void main (String[] args)throws IOException{
        File file= new File("E:\\Java base\\第七章IO\\hello");
        if(file.exists()){
            System.out.println(file.delete());
        }
    }
}

【运行结果】

运行结果中输出了false,这说明删除文件失败了,原因在于File类的delete()方法只能删除一个指定的文件,如果File对象代表目录,并且目录下包含子目录或者文件,则File类的detele()方法不允许直接删数这个目录。

这时候我们需要使用递归的方式来进行删除了

import java.io.File;
import java.io.IOException;
public class test{
    public static void main (String[] args)throws IOException{
        File file= new File("E:\\Java base\\第七章IO\\hello");
        deleteDir(file); // 调用deleteDir删除方法
    }
    public static void deleteDir(File dir){
        if (dir.exists()){ //判断传入的File对象是否存在
            File[] files=dir.listFiles();   //得到FIle数组
            for(File file:files){  //遍历所有的子目录和文件
                if(file.isDirectory()){
                deleteDir(file);    //如果是目录,则递归调用deleDir()
                }else{
                //如果是文件,则直接删除
                file.delete();
            }
        }
        // 删除完一个目录里的所有文件后,就删除这个目录
        dir.delete();
    }
}
}

【运行结果】

可以发现,该目录已经被删除了。 

2 字节流

2.1 字节流的概念

在程序开发过程中,经常会需要处理设备之间的数据传输,而所有的文件都是以二进制(字节)的形式存在的。为字节的输入/输出(I/O)l流提供了一系列的流。被统称为字节流。根据数据的传输方向可将其分为字节输入流和字节输出流。

在JDK中,提供了两个抽象类InputStream和OutputStream,他们是字节流的顶级父类,所有的字节输入流都继承自InputStream,所有的字节输出流都继承自OutputStream。 

在JDK中,InputStream和OutputStream提供了一系列与读写数据相关的方法,如下两图。

前三个read()方法都是用来读数据的,其中第一个read()方法是从输入流逐个读入字节,而第二个和第三个read()方法则将若干字节以字节数组的形式一次性读入,从而提高读数据的效率。

在进行I/O流操作时,当前I/O流会占用一定的内存,因此在操作结束之后,应该调用close()方法关闭流,从而释放当前I/O流所占的系统资源。 

 前三个是重载的write()方法,都用于向输出流写入字节。其中第一个方法逐个写入字节,第二个和第三个方法是将若干个字节以字节数组的形式一次性写入,从而提高写数据的效率。

flush()方法是用来将当前输出缓存区(通常是字节数组)中的数据强制写入目标设备,这个过程称为刷新。close()方法用来关闭流并释放与当前I/O流相关的系统资源。

InputStream和OutputStream这两个类虽然提供了一系列与读写数据相关的方法,但是这两个类都是抽象类,不能被实例化,因此针对不同的的功能,InputStream和OutputStream提供了不同的子类,这些子类形成了一个体系结构,如下图

2.2 InputStream读文件

InputStream就是JDK提供的基本输入流·。但InputStream并不是接口,而是抽象类,他是所有输入流的父类,FileInputStream是InputStream的子类,它是操作文件的字节输入流,专门用于读取文件中的数据。由于从文件读取数据是重复操作,因此需要通过循环语句来实现数据的持续读取。

下面通过一个案例来实现字节流对文件数据的读取,首先在java项目的根目录下创建一个文本文件test.txt,在文件中输入内容"itcast"并保存;然后使用字节输入流对象来读取test.txt文本文件。

【案例】

import java.io.FileInputStream;
import java.io.IOException;
public class test{
    public static void main (String[] args)throws IOException{
       //创建一个文件字节输入流
       FileInputStream in = new FileInputStream("test.txt");
       int b = 0;   //定义一个int类型的变量b,记住每次读取的一个字节
       while(true){
           b = in.read(); //变量b记住读取的一个字节
           if(b==-1){   //如果读取的字节为-1,跳出while循环
               break;
           }
           System.out.println(b); //否则将b写出
       }
       in.close();
    }
}

 【运行结果】

计算机中的数据都是以字节的形式存在的,每个字符都占一个字节,因此最终结果显示的就是文件"test.txt"中的6个字节所对应的十进制数。

105
116
99
97
115
116

有的时候文件不存在时控制台会的报错信息会有一个潜在的问题,即如果文件不存在或别的原因导致读取过程中发生了I/O错误,InputStream就无法正常的关闭,资源也无法及时释放,针对这个问题,可以使用try...finally来保证无论是否发生I/O错误InputStream都能够正确关闭。


import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
public class test{
    public static void main (String[] args)throws IOException{
        InputStream input=null;
        try{
       //创建一个文件字节输入流
       FileInputStream in = new FileInputStream("test.txt");
       int b = 0;   //定义一个int类型的变量b,记住每次读取的一个字节
       while(true){
           b = in.read(); //变量b记住读取的一个字节
           if(b==-1){   //如果读取的字节为-1,跳出while循环
               break;
           }
           System.out.println(b); //否则将b写出
       }
    }finally{
        if(input !=null){
            input.close();
        }
    }
}
}

2.3 OutputStream

OutputStream是JDK提供的最基本的输出流,也是抽象类,是所有输出流的父类。

【案例】——如何使用FileOutputStream将数据写入文件

import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
public class Example01 {
    public static void main (String[] args)throws IOException{
        //创建一个文件字节输出流
        OutputStream out = new FileOutputStream("example.txt");
        String str="传智播客";
        byte[] b=str.getBytes();
        for(int i = 0;i<b.length;i++){
            out.write(b[i]);
        }
        out.close();
    } 
}

【运行结果】

在目录下会新生成一个文本文件“example.txt”,但是我打开以后是乱码的

 2.4 文件的复制

首先在项目里创建两个文件夹source和target,然后在source里存放一个五环之歌.doc

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
public class Example01 {
    public static void main (String[] args)throws Exception{
        //创建一个字节输入流,用于读取当前目录下source文件夹中的文件
        InputStream in = new FileInputStream("source/五环之歌.doc");
        //创建一个文件字节输出流,用于将读取的数据写入target目录下的文件中
        OutputStream out = new FileOutputStream("target/五环之歌.doc");
        int len; //定义一个int类型的变量len,记住每次读取的一个字节
        //获取复制文件前的系统时间
        long begintime = System.currentTimeMillis();
        while((len = in.read())!=-1){ //读取一个字节并判断是否读到文件末尾
            out.write(len); //将读到的字节写入文件
        }
        // 获取文件复制结束时的系统时间
        long endtime = System.currentTimeMillis();
        System.out.println("复制文件所消耗的时间是:"+(endtime-begintime)+"毫秒");
        in.close();
        out.close();
    } 

【运行结果】

在target中自动生成了一个doc文件 

并且传输文件的时间为1毫秒

 在复制文件时,受计算机性能等方面的影响,会导致复制文件所消耗的时间不确定,上述的文件复制是一个字节一个字节地读写,需要频繁地去操作文件,效率非常低,为了提高效率也可以定义一个字节数组作为缓冲区。在复制文件时,可以一次性读取多个字节的数据,并保存在字节数组中,然后将字节数组中的数据一次性写入文件。

【案例】

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
public class Example01 {
    public static void main (String[] args)throws Exception{
        //创建一个字节输入流,用于读取当前目录下source文件夹中的文件
        InputStream in = new FileInputStream("source/五环之歌.doc");
        //创建一个文件字节输出流,用于将读取的数据写入target目录下的文件中
        OutputStream out = new FileOutputStream("target/五环之歌.doc");
        //以下是用缓冲区读写文件
        byte[] buff = new byte[1024]; //定义一个字节数组,作为缓冲区
        // 定义一个int类型的变量len记住读取读入缓冲区的字节数
        int len; 
        //获取复制文件前的系统时间
        long begintime = System.currentTimeMillis();
        while((len = in.read(buff))!=-1){ //读取一个字节并判断是否读到文件末尾
            out.write(buff,0,len); //将读到的字节写入文件
        }
        // 获取文件复制结束时的系统时间
        long endtime = System.currentTimeMillis();
        System.out.println("复制文件所消耗的时间是:"+(endtime-begintime)+"毫秒");
        in.close();
        out.close();
    } 
}

【运行结果】

同样实现了文件的复制,在复制过程中,使用while循环语句逐渐实现字节文件的复制,每循环一次,从文件读取若干字节填充字节数组,并通过变量len记住读入数组的字节数,然后从数组的第一个字节开始 ,将len个字节依次写入文件,循环往复,当len值为-1的时候,说明已经读到了文化的末尾,循环结束。

2.5 字节缓冲流 

I/O提供两个带缓冲的字节流,分别是BufferedInputStream和BufferedOutputStream,他们的构造·方法中分别接收InputStream和OutputStream类型的参数作为对象,在读写数据时提供缓冲功能

 下面通过案例学习BufferedInputStream和BufferedOutputStream这两个流的用法。首先在目录下创建一个src.txt的文件,并且随意写入一些内容;然后创建一个类,在类中使用FileOutputStream创建文件des.txt,并使用字节缓冲流对象将文件src.txt中的内容复制到文件des.txt中。

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.FileInputStream;
import java.io.FileOutputStream;

public class Example01 {
    public static void main (String[] args)throws Exception{
       BufferedInputStream bis = new BufferedInputStream(new FileInputStream("example.txt"));
       BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("des.txt"));
       int len;
       while((len=bis.read())!=-1){
           bos.write(len);
       }
       bis.close();
       bos.close();
    } 
}

3. 字符流

3.1 字符流定义及基本用法

同字节流一样,字符流也有两个抽象的顶级父类,分别是Reader和Writer。其中Reader是字符输入流,用于从某个源设备读取字符;Writer是字符输出流,用于向某个目标设备写入字符。Reader和Writer作为字符流的顶级父类,也有许多子类。下面通过一张继承关系图列举Reader和Writer的一些常用子类。

 3.2 字符流操作文件

在程序开发中,经常需要对文本文件的内容进行读取,如果想从文件中直接读取字符便可以使用字符输入流FileReader,通过此流可以从关联文件中读取一个或一组字符。

下面通过一个案例来学习如何使用FileReader读取文件的字符

首先在目录下新建文本文件"test.txt"并在其中输入字符"itcast"。

import java.io.FileReader;

public class Example01 {
    public static void main (String[] args)throws Exception{
        // 创建一个FileReader对象用来读取文件中的字符
       FileReader reader = new FileReader("test.txt");
       int ch;  //定义一个变量用于用于记录读取的字符
       while ((ch=reader.read())!=-1){  //循环判断是否读取到文件的末尾
           System.out.println((char)ch); //不是字符流末尾就转为字符打印
       }
       reader.close(); //关闭文件读取流·,释放资源
    } 
}

【运行结果】

实现了读取文件字符的功能,创建了一个FileReader对象与文件关联,接着通过while循环每次从文件中读取一个字符并打印,这样便实现了FIleRead读文件字符的操作。需要注意的是,字符输入流的read()方法返回的是int类型的值

猜你喜欢

转载自blog.csdn.net/m0_48936146/article/details/124602114