> 文档中心 > IO流基础详解,内附源码,看了包会

IO流基础详解,内附源码,看了包会

目录

1.使用File类操作文件

1.1创建File对象

1.2File类的常用方法

1.3临时文件操作

1.4遍历目录下文件

1.5删除文件及目录

2.使用字节流读写文件

2.1什么是字节流

2.2InputStream读文件

2.3OutputStream写文件

2.4文件复制

2.5字节缓冲流

3.使用字符流读写文件

3.1字符流定义

3.2字符流读文件

3.3字符流写文件

3.4转换流


1.使用File类操作文件

2.使用字节流读写文件

3.使用字符流读写文件

1.使用File类操作文件

1.1创建File对象

创建File对象的构造方法

方法声明 功能描述
File(String pathname) 通过指定的一个字符串类型的文件路径来创建一个新的File对象
File(String parent,
String child)
根据指定的一个字符串类型的父路径和一个字符串类型的子路径(包括文件名称)创建一个File对象
File(File parent, String
child)
根据指定的File类的父路径和字符串类型的子路径(包括文件名称)创建一个File对象

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

public class test01 {    public static void main(String[] args) { File f1 = new File("D:\\file\\test01.txt"); // 使用绝对路径构造File对象 File f2 = new File("D:\\file", "test\\test02.txt"); File fileParent = new File("D:\\file"); File f3 = new File(fileParent, "test\\test03.txt"); File f4 = new File("src\\com\\cy\\HelloWorld.java"); // 使用相对路径构造File对象 System.out.println(f1); System.out.println(f2); System.out.println(f3); System.out.println(f4);    }}

 注意:使用File类的构造方法在创建File对象时,并不会创建对应文件路径的目录或文件,只是指向此文件路径对应的目录或文件。

1.2File类的常用方法

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

public static void main(String[] args) throws IOException { // 创建文件时需要保证文件的父目录存在,否则运行时会抛IOException异常 File file1 = new File("E:\\test01\\demo.txt"); if(file1.exists()){     //判断文件是否存在     file1.delete();     //存在则删除 }else {   //不存在则创建文件     System.out.println(file1.createNewFile()); }    }

其他方法与上述案例相似,创建File对象,使用对象名调用方法即可

1.3临时文件操作

在一些特定情况下,程序需要读写一些临时文件,File对象提供了createTempFile()来创建一个临时文件,以及deleteOnExit()在JVM退出时自动删除该文件。

public static void main(String[] args) throws IOException { // 提供临时文件的前缀和后缀 File f = File.createTempFile("temp", ".txt"); f.deleteOnExit(); // JVM退出时自动删除 System.out.println(f.isFile()); System.out.println(f.getPath());    }

1.4遍历目录下文件

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

以下案例演示遍历E盘下java文件夹下的文件

public class test04 {    public static void main(String[] args) { File file = new File("E:\\java"); if (file.isDirectory()) { // 判断File对象对应的目录是否存在     String[] names = file.list(); // 获得目录下的所有文件的文件名     for (String name : names) {  System.out.println(name); // 输出文件名     } }    }}

      有时候在一个目录下,除了文件,还有子目录,如果想得到所有子目录下的File类型对象,list()方法显然不能满足要求,这时需要使用File类提供的另一个方法listFiles()。listFiles()方法返回一个File对象数组,当对数组中的元素进行遍历时,如果元素中还有子目录需要遍历,则需要使用递归。

public class test05 {    public static void main(String[] args) { File file = new File("E:\\java"); fileDir(file);    }    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.5删除文件及目录

首先在电脑的E盘中创建一个名称为test的文件夹,然后在文件夹中创建一个txt文本文件,接下来创建一个类,在类中使用delete()方法删除文件。

public class test06 {    public static void main(String[] args) { File file = new File("E:\\test.txt"); if(file.exists()){     System.out.println(file.delete()); }    }}

运行结果为true

File类的delete()方法只能删除一个指定的文件或者空文件夹,假如File对象代表目录,并且目录下包含子目录或文件,则File类的delete()方法不允许对这个目录直接删除。在这种情况下,需要通过递归的方式将整个目录以及其中的文件全部删除。删除目录是从虚拟机直接删除而不放入回收站的,文件一旦删除就无法恢复。

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

2.使用字节流读写文件

2.1什么是字节流

        在程序的开发中,我们经常会需要处理设备之间的数据传输,而计算机中,无论是文本、图片、音频还是视频,所有文件都是以二进制( 字节)形式存在的。而对于字节的输入输出IO流提供了一系列的流,统称为字节流,字节流是程序中最常用的流,根据数据的传输方向可将其分为字节输入流和字节输出流。

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

2.2InputStream读文件

InputStream的常用方法

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

InputStream是一个抽象类,如果使用此类,则首先必须通过子类实例化对象。FileInputStream是InputStream的子类,它是操作文件的字节输入流,专⻔用于读取文件数据。

public class test08 {    public static void main(String[] args) throws IOException { // 创建一个文件字节输入流读取E盘下的test.txt文件(123456789) FileInputStream in = null; try {     in = new FileInputStream("E:\\test.txt");     int b = 0;// 定义一个int类型的变量b,记住每次读取的一个字节     while (true){  // read(): 从输入流读取一个8位的字节,把它转换为0~255之间的整数,并返回这一整数  b = in.read();  if(b==-1){// 如果读取的字节为-1,跳出while循环      break;  }  System.out.println((char) b);     } } catch (IOException e) {     e.printStackTrace(); }finally {     try {  if(in != null){      in.close();  }     } catch (IOException e) {  e.printStackTrace();     } }    }}

文件不存在时控制台的报错信息对于上述异常错误,会有一个潜在的问题,如果读取过程中发生了IO异常,InputStream流就无法正常关闭,资源也无法及时释放。对于这种问题我们可以使用try...finally来保证InputStream在无论是否发生IO错误的时候都能够正确关闭。

2.3OutputStream写文件

OutputStream的常用方法

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

OutputStream是一个抽象类,如果使用此类,则首先必须通过子类实例化对象。FileOutputStream是OutputStream的子类,它是操作文件的字节输出流,专⻔用于把数据写入文件。

public class test09 {    public static void main(String[] args) throws IOException { OutputStream out = new FileOutputStream("E:\\test.txt"); String str = "hello"; byte[] b = str.getBytes(); for (int i = 0; i < b.length; i++) {     out.write(b[i]); } out.flush(); out.close();    }}

 

 程序运行之后会在E盘下创建一个test.txt文件,然后写入数据。如果通过FileOutputStream向一个已经存在的文件写入数据,那么该文件的数据会先被清空然后写入写的数据。

若是希望再已经存在的文件内容之后添加新的数据,则使用FileOutputStream的构造函FileOutputStream(String fileName, boolean append)来创建文件输出流对象,并把append参数的值设置为true。

public class test10 {    public static void main(String[] args) throws IOException { OutputStream out = new FileOutputStream("E:\\test.txt",true); String str = "world"; byte[] b = str.getBytes(StandardCharsets.UTF_8); for (int i = 0; i < b.length; i++) {     out.write(b[i]); } out.flush(); out.close();    }}

 

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

2.4文件复制

InputStream和OutputStream组合使用实现文件的复制.
public class test11 {    public static void main(String[] args) throws IOException { InputStream in = new FileInputStream("E:\\test.txt"); OutputStream out = new FileOutputStream("E:\\test02.txt"); 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();    }}

 上述实现的文件拷⻉是一个字节一个字节的读写,需要频繁的操作文件,效率非常低。

可以通过一次性读取多个字节的数据,并保存在字节数组中,然后将字节数组中的数据一次性写入文件以提高读写效率

public class test12 {    public static void main(String[] args) throws IOException { InputStream in = new FileInputStream("E:\\test.txt"); OutputStream out = new FileOutputStream("E:\\test02.txt"); byte[] buff = new byte[1024];//定义一个字节数组,作为缓冲区 int len;//记录读取读取如缓冲区的字节数 while((len = in.read(buff)) != -1){     out.write(buff,0,len);// 从第一个字节开始,向文件写入len个字节 } in.close(); out.close();    }}

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

2.5字节缓冲流

public class test13 {    public static void main(String[] args) throws IOException { // 创建一个带缓冲区的输入流 BufferedInputStream bis = new BufferedInputStream(  new FileInputStream("E:\\test.txt")); // 创建一个带缓冲区的输出流 BufferedOutputStream bos = new BufferedOutputStream(  new FileOutputStream("E:\\test02.txt")); int len; while((len = bis.read()) !=-1){     bos.write(len); } bis.close(); bos.close();    }}

         创建了BufferedInputStream和BufferedOutputStream两个缓冲流对象,这两个流内部都定义
了一个大小为8192的字节数组,然后调用read()或者write()方法读写数据时,首先将读写的数据存入定义好的字节数组,然后将字节数组的数据一次性读写到文件中。这种方式对数据进行了缓冲,从而有效地提高了数据的读写效率。

3.使用字符流读写文件

3.1字符流定义

        如果希望在程序中操作字符,那么就要使用字符流,同字节流一样,字符流也有两个抽象的顶级父类,分别是Reader和Writer。其中Reader是字符输入流,用于从某个源设备读取字符。Writer是字符输出流,用于向某个目标设备写入字符。Reader和Writer作为字符流的顶级父类,也有许多子类。

        字符流的继承关系与字节流的继承关系有些类似,很多子类都是成对(输入流和输出流)出现的,其中FileReader和FileWriter用于读写文件,BufferedReader和BufferedWriter是具有缓冲功能的流,使用它们可以提高读写效率。

3.2字符流读文件

使用FileReader读取文件

public class test14 {    public static void main(String[] args) throws IOException { FileReader reader = new FileReader("E:\\test.txt"); int ch; while ((ch = reader.read())!=-1){     System.out.println((char)ch); } reader.close();    }}

 字符输入流的read()方法返回的是int类型的值,如果想获得字符就需要进行强制类型转换,如程序
中就将变量ch转为char类型再打印。

3.3字符流写文件

使用FileWriter写文件

public class test15 {    public static void main(String[] args) throws IOException { FileWriter writer = new FileWriter("E:\\test03.txt"); String str = "点点赞,谢谢"; writer.write(str); writer.close();    }}

FileWriter同FileOutputStream一样,如果指定的文件不存在,就会先创建文件,再写入数据,如果文件存在,则会首先清空文件中的内容,再进行写入。如果想在文件末尾追加数据,同样需要调用重载的构造方法,现将程序中的代码进行如下修改:

FileWriter writer = new FileWriter("E:\\test03.txt",true);

 使用字符流复制文件与字节流基本一致,这里不做过多赘述

3.4转换流

前面提到IO流可分为字节流和字符流,有时字节流和字符流之间也需要进行转换。在JDK中提供了两个类可以将字节流转换为字符流,它们分别是InputStreamReader和OutputStreamWriter。
         InputStreamReader是Reader的子类,它可以将一个字节输入流转换成字符输入流,方便直接读取字符。
         OutputStreamWriter是Writer的子类,它可以将一个字节输出流转换成字符输出流,方便直接写入字符。

public class test16 {    public static void main(String[] args) throws IOException { FileInputStream in = new FileInputStream("test.txt"); // 将字节流输入转换成字符输入流 InputStreamReader inr = new InputStreamReader(in); // 赋予字符流对象缓冲区 BufferedReader br = new BufferedReader(inr); FileOutputStream out = new FileOutputStream("E:\\test04.txt"); // 将字节输出流转换成字符输出流 OutputStreamWriter outw = new OutputStreamWriter(out); // 赋予字符输出流对象缓冲区 BufferedWriter bw = new BufferedWriter(outw); String line; while ((line = br.readLine()) != null){// 判断是否读到文件末尾     bw.write(line); } br.close(); bw.close();    }}

 上述代码实现了字节流和字符流之间的转换,将字节流转换为字符流,从而实现直接对字符的读写。需要注意的是,在使用转换流时,只能针对操作文本文件的字节流进行转换,如果字节流操作的是一张图片,此时转换为字符流就会造成数据丢失。

如果觉得有用,请点点赞谢谢。

海量搞笑GIF动态图片