【Java学习总结】chapter07:IO流

第7章:IO流


  1. 使用字节流和字符流读写文件操作
  2. 使用File类访问文件系统
  3. NIO和NIO.2的基本概念和用法

在这里插入图片描述


7.1 IO流概述
  • IO流的概念:IO(Input&Output)流即输入输出流,是Java中实现输入输出的基础,可方便的实现数据的IN&OUT

  • IO流的分类:

分类方式分类结果
根据流操作的数据单位字节流、字符流
根据流传输的方向输入流、输出流
根据流的功能节点流、处理流
  • IO流顶级类的分类:

在这里插入图片描述

注意:4个顶级类都是抽象类,并且是所有流类型的父类


7.2 字节流
(1)字节流概述:
  • 字节流概念:在计算机中文本、图片、音频包括视频都是以二进制(字节)形式存在,IO流中针对字节的IN&OUT提供了一系列的流,统称为字节流
  • 字节输入流InputStream常用方法★:
方法声明功能描述
int read()读入一个8位的字节,转化为0-255间的整数并返回。当没有可用字节时返回 -1
int read(byte[] b)读入若干字节,保存到参数b指定的字节数组中,返回读取字节的数目
int read(byte[] b, int off, int len)int read(byte[] b)基础上,off指定字节数组起始下标,len为读取字节数目
void close()关闭输入流,并释放系统资源
  • 字节输出流OutputStream常用方法★:
方法声明功能描述
void write(int b)向输出流写入一个字节
void write(byte[] b)把参数b指定的字节数组的所有字节写到输入流
void write(byte[] b, int off, int len)将指定的byte数组中从偏移量off开始的len个字节写入输入流
void flush()刷新此输出流,并强制写出所有缓冲的输出字节
void close()关闭此输出流,并释放系统资源
  • InputStream和OutputStream的子类

由于InputStreamOutputStream类是抽象类不能被实例化,针对不同的功能IS和OS提供了不同的子类

在这里插入图片描述

在这里插入图片描述

(2)字节流读写文件:

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

  • 使用FileInputStream类操作文件字节输入流,读取文件数据
package Example01;

import java.io.*;

public class Example01{
	public static void main(String[] args) throws Exception{
		//创建一个文件字节输入流来读取文件
		FileInputStream in = new FileInputStream("D:\\programming\\Java_workingspace\\test01.txt");  
		//定义一个int类型变量b
		int b = 0;
		//通过循环来读取文件,当返回值为-1时结束循环
		while((b = in.read()) != -1) {
			System.out.println(b);
		}
		//关闭流
		in.close();
	}
}

在这里插入图片描述

  • 使用FileOutputStream类操作文件字符输出流,将数据写入文件
package Example02;

import java.io.*;

public class Example02 {
	public static void main(String[] args) throws Exception{
		//创建文件输出流对象,并指定文件输出名
		FileOutputStream out = new FileOutputStream("7-2out.txt");
		//定义一个字符串
		String str = "hello";
		//将字符串转换为字节数组进行写入操作
		out.write(str.getBytes());
		//关闭流
		out.close();
	}
}

在这里插入图片描述

通过FileOutputStream向一个已经存在的文件写入,该文件数据首先将被删除,再重新写入新的数据

  • 使用FileOutputStream类的构造函数FileOutputStream(String fileName, boolean append)追加数据
package Example03;

import java.io.*;

public class Example03 {
	public static void main(String[] args) throws Exception {
		//创建文件输出流对象,并指定输出文件名
		FileOutputStream out = new FileOutputStream("7-2out.txt", true);
		//定义一个字符串
		String str = " world";
		//将字符串转化为字符数组进行写入操作
		out.write(str.getBytes());
		out.close();
	}
}

在这里插入图片描述

(3)文件的拷贝:
  • 使用输入输出流进行文件的拷贝
package Example04;

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;

public class Example04 {
    public static void main(String[] args) throws IOException {
        //创建文件输入流对象读取指定目录下的文件
        FileInputStream in = new FileInputStream("source/src.jpg");
        //创建文件输出流对象将读取到的文件内容写入到指定目录文件中
        FileOutputStream out = new FileOutputStream("target/dest.jpg");
        //定义一个int类型的变量len
        int len = 0;
        
        //获取拷贝文件前的系统时间
        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();
    }
}

在这里插入图片描述

在这里插入图片描述

(4)字节流的缓冲区:

虽然Example04实现了文件的拷贝但需要频繁的操作文件,效率很低,可以定义一个字节数组作为缓冲区

  • 使用字节数组作为缓冲区拷贝文件
package Example05;

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;

public class Example05 {
    public static void main(String[] args) throws IOException {
        //创建文件输入流对象取指定目录下的文件
        FileInputStream in = new FileInputStream("source/src.jpg");
        //创建文件输出流对象将读取到的文件内容写入到指定目录中
        FileOutputStream out = new FileOutputStream("target/dest2.jpg");
        //定义一个int类型的变量
        int len = 0;
        //定义一个长度为1024的字节数组作为缓冲区
        
        byte[] buff = new byte[1024];
        //获取拷贝文件前的系统时间
        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();
    }
}

在这里插入图片描述

在这里插入图片描述

程序中的缓冲区就是一块内存,用于暂时存放输入输出的数据减少对文件的操作次数,提高读写数据效率

(5)字节缓冲流:

在IO包中提供了两个带缓冲的字节流BufferedInputStreamBufferedOutputStream,构造方法中分别接收InputStreamOutputStream类型的参数作为对象,在读写数据时提供缓冲功能

  • 使用字节缓冲流拷贝文件
//利用字节缓冲流拷贝文件
package Example06;

import java.io.*;

public class Example06 {
    public static void main(String[] args) throws IOException {
        //创建用于输入和输出的字节缓冲流对象
        BufferedInputStream bis = new BufferedInputStream(new FileInputStream("source/src.jpg"));
        BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("target/dest3.jpg"));
        //定义一个int类型的变量
        int len = 0;
        //获取拷贝文件前的系统时间
        long beginTime = System.currentTimeMillis();
        while ((bis.read()) != -1){
            bos.write(len);
        }
        //获取拷贝文件后的系统时间
        long endTime = System.currentTimeMillis();
        //输出拷贝花费的时间
        System.out.println("花费时间为:" + (endTime - beginTime) + "毫秒");
        //关闭流
        bis.close();
        bos.close();
    }
}

在这里插入图片描述

在这里插入图片描述

BufferedInputStreamBufferedOutputStream中都定义了一个大小为8192的字节数组,调用read和write方读写数据时,先将数据存入字节数组再一次性读写到文件中


7.3 字符流
(1)字符流概述:

由于Reader和Writer类是抽象类不能被实例化,针对不同的功能字符输入/输出流提供了一些常用的子类

  • Reader和Writer常用的子类:

在这里插入图片描述

在这里插入图片描述

(2)字符流操作文件:

在程序开发中,经常需要对文本文件的内容进行读取,可使用FileReader从文件中读取一个或一组字符

  • 使用FileReader文件字符输入流读取文件中的字符
package Example07;

import java.io.FileReader;

public class Example07 {
	public static void main(String[] args)throws Exception {
		//创建FileReader对象,并指定需要读取的文件
		FileReader fileReader = new FileReader("7-7reader.txt");
		//定义一个int类型变量len,其初始化值为0
		int len = 0;
		//通过循环来判断是否读到了文件末尾
		while((len = fileReader.read()) != -1) {
			//输出读取到的字符
			System.out.print((char)len);
		}
		//关闭流
		fileReader.close();
	}
    
}

在这里插入图片描述

通过while循环每次从文件中读取一个字符并打印,实现了文件字符的读取,由于字符输入流reader的read方法返回的是int类型,获得字符需要进行强制转换

  • 使用FileWriter文件字符输出流将字符写入文件
package Example08;

import java.io.FileWriter;
import java.io.IOException;

public class Example08 {
    public static void main(String[] args) throws IOException {
        //创建字符输出流对象,并指定输出文件
        FileWriter fileWriter = new FileWriter("7-8writer.txt");
        //将定义的字符写入文件
        fileWriter.write("轻轻的我走了,\r\n");
        fileWriter.write("正如我轻轻的来;\r\n");
        fileWriter.write("我轻轻的招手,\r\n");
        fileWriter.write("作别西天的云彩。\r\n");
        //关闭流
        fileWriter.close();
    }
}

在这里插入图片描述

FileWriterFileOutputStream相似,如果指定文件不存在,就会先创建文件再写入数据;如果文件存在,就会先清空文件内容再进行写入;文件数据的追加也相似

  • 使用字符流的缓冲区实现文件的拷贝

通过字符流进行文件的读写操作,也是逐个字符进行读写效率很低,可以利用字符流缓冲区提高效率

package Example09;

import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;

public class Example09 {
    public static void main(String[] args) throws IOException {
        //创建FileReader对象,并指定需要读取的文件
        FileReader filereader = new FileReader("7-9reader.txt");
        //创建FileWriter对象,并指定需要写入数据的目标文件
        FileWriter fileWriter = new FileWriter("7-9writer.txt");
        //定义一个int类型变量len,并初始化值为0
        int len = 0;
        //定义一个长度为1024的字符数组
        char[] buff = new char[1024];
        //通过循环来判断是否读取到了文件末尾
        while ((len = filereader.read(buff)) != -1){
            //输出读取到的字符
            fileWriter.write(buff, 0, len);
        }
        //关闭流
        filereader.close();
        fileWriter.close();
    }
}

在这里插入图片描述

  • 使用BufferedReader和BufferedWriter缓冲流实现文件的拷贝
package Example10;

import java.io.*;

public class Example10 {
    public static void main(String[] args) throws IOException {
        //创建一个字符输入缓冲流对象
        BufferedReader br = new BufferedReader(new FileReader("7-10reader.txt"));
        //创建一个字符输出缓冲流对象
        BufferedWriter bw = new BufferedWriter(new FileWriter("7-10writer.txt"));
        //声明一个字符串变量str
        String str = null;
        while ((str = br.readLine()) != null){
            //通过缓冲流对象写入文件
            bw.write(str);
            //写入一个换行符,该方法会根据操作系统不同生成相应的换行符
            bw.newLine();
        }
        br.close();
        bw.close();
    }
}

在这里插入图片描述

值得注意

  1. 每次循环使用readline()方法读取一行,然后通过wirter()方法写入文件;循环判断条件为(str != null)
  2. 程序中使用newLine()方法进行换行,否则所有内容都会追加写入到一行中
  3. 当调用writer()方法写入字符时字符会先被写入缓冲区,当缓冲区写满或调用close()方法时,缓冲区字符才会被写入目标文件
(3)转换流:

有时字符流与字节流之间需要进行转换,再JDK中提供了InputStreamReaderOutputStreamWriter两个类用于转换

  • 使用InputStreamReader和OutputStreamWriter实现字节流与字符流间的转换
package Example11;

import java.io.*;

public class Example11 {
    public static void main(String[] args) throws IOException {
        //1.创建字节输入流对象,获取源文件
        FileInputStream in = new FileInputStream("7-11reader.txt");
        //将字节输入流对象转换成字符输入流对象
        InputStreamReader isr = new InputStreamReader(in);
        //创建字符输入缓冲流对象
        BufferedReader br = new BufferedReader(isr);
        //2.创建字节输出流对象,指定目标文件
        FileOutputStream out = new FileOutputStream("7-11writer.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();
    }
}

在这里插入图片描述

需要注意在进行转化时只针对操作文本文件的字节流转换,如果操作的是字节码内容的文件(图片、视频)将造成数据丢失


7.4 File类

通过IO流可对文件内容进行读写操作,但对文件本身的常规操作(创建、删除、重命名、判断存在等)无法实现,需要File类实现

(1)File类常用方法:
  • File类概念:用于封装一个路径(绝对路径或相对路径),封装的路径可以指向一个文件也可以指向一个目录
  • File类常用的构造方法:
方法声明功能描述
File(String pathname)通过指定一个String类型的文件路径来创建一个新的File对象
File(String pathname, String child)根据一个String类型的父路径和一个String类型的子路径创建一个File对象
File(File parent, String child)根据一个File类型的父路径和一个String类型的子路径创建一个File对象
  • File类中常用的方法:

  • 使用File类常用方法查看文件信息

package Example12;

import java.io.File;

public class Example12 {
	public static void main(String[] args) {
		//创建File类文件对象
		File file = new File("7-12example.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.isAbsolute() ? "是一个绝对路径" : "不是一个绝对路径");
		System.out.println("最后修改时间为:" + file.lastModified());
		System.out.println("文件大小为:" + file.length() + "bytes");
		System.out.println("是否成功删除文件" + file.delete());
	}
}

在这里插入图片描述

(2)遍历目录下的文件:
  • 使用File类中的list()方法遍历指定目录下的所有文件的名称
package Example13;

import java.io.File;
import java.util.Arrays;

public class Example13 {
	public static void main(String[] args) {
		//创建File对象,并指定文件路径
		File file = new File("C:");
		//判断是否是目录
		if(file.isDirectory()) {
			//获取目录中的所有文件的名称
			String[] fileNames = file.list();
			//对指定路径下的文件或目录进行遍历
			Arrays.stream(fileNames).forEach(f->System.out.println(f));
		}
	}
}

在这里插入图片描述

  • 使用File中的list(FilenameFilter filter)方法筛选遍历指定目录下指定类型的文件
package Example14;

import java.io.File;
import java.util.Arrays;

public class Example14 {
	public static void main(String[] args) {
		//创建File对象,并指定文件路径
		File file = new File("D:\\programming\\Java_workingspace\\Example");
		//判断是否是目录
		if(file.isDirectory()) {
			//使用Lambda表达式过滤目录中所有以.txt结尾的文件的名称
			String[] fileNames = file.list((dir,name)->name.endsWith(".txt"));
			//对指定路径下的文件或目录进行遍历
			Arrays.stream(fileNames).forEach(f->System.out.println(f));
		}
	}
}

在这里插入图片描述

  • 使用File中的listFiles()方法递归筛选深度遍历指定目录下指定类型的文件
package Example15;

import java.io.*;

public class Example15 {
	public static void main(String[] args) {
		//创建File对象,并指定文件路径
		File file = new File("D:\\programming");
		//调用fileDir()方法,遍历目录
		fileDir(file);
	}
	//遍历目录及其子目录
	private static void fileDir(File file) {
		//获得目录下所有文件,并赋值给数组
		File[] listFiles = file.listFiles();
		//循环遍历数组
		for(File files : listFiles){
			//如果遍历的是数组,则调用递归fileDir()方法
			if(files.isDirectory()){
				fileDir(files);
			}
			//输出文件路径
			System.out.println(files);
		}
	}

}

在这里插入图片描述

(3)删除文件及目录:

在使用File类中的delete()删除文件时,需要判断目录下是否存在文件,需要先删除内部文件再删除空文件

  • 使用File类中的delete()删除指定目录下的文件和文件夹
package Example16;

import java.io.File;

public class Example16 {
    public static void main(String[] args) {
        //创建File类,并指定文件路径
        File files = new File("D:\\programming\\Java_workingspace\\Example\\target\\delete.txt");
        //调用删除方法
        deleteDir(files);
    }

    private static void deleteDir(File files) {
        //获取file对象中所有的文件,并将其放在数组中
        File[] listFiles = files.listFiles();
        //循环遍历数组
        for(File file : listFiles){
            //如果是目录文件,则递归调用删除方法
            if(file.isDirectory()){
                deleteDir(file);
            }
            //如果是文件则删除
            file.delete();
        }
        //删除文件夹内所有文件后,再删除文件夹
        files.delete();
    }
}

在这里插入图片描述


7.5 RandomAccessFile随机读写文件

如果希望从文件的任意位置开始执行读写操作,字节流和字符流都无法实现,使用RandomAccessFile可以实现,它不是流类但具有读写文件数据的功能

  • RandomAccessFile的构造方法:
方法声明功能描述
RandomAccessFile(File file, String mode)使用参数file指定访问的文件,并使用mode来指定访问模式
RandomAccessFile(String name, String mode)使用参数name指定访问文件路径,并使用mode来指定访问模式
  • 参数mode
参数mode含义
r以只读的方式打开文件
rw以读写的方式打开文件,如果文件不存在将自动创建文件
rws以读写的方式打开文件,要求文件内容或元数据的每个更新,都同步写入底层底层存储设备
rwd以读写的方式打开文件,要求文件内容的每个更新,都同步写入底层底层存储设备
  • RandomAccessFile的常用方法:
方法声明功能描述
long getFilePointer()返回当前读写指针所处位置
void seek(long pos)设定读写指针的位置,与开头文件相隔pos个字节数
int skipBytes(int n)使读写指针从当前位置开始,跳过n个字节
void write(byte[] b)将指定的字节数组写入到文件,并从当前文件指针开始
void setLength(long newLength)设置此文件的长度
final String readLine()从指定文件当前指针读取下一行内容
  • 使用RandomAccessFile类来模拟实现记录软件使用次数的过程:
package Example17;

import java.io.RandomAccessFile;

public class Example17 {
	public static void main(String[] args) throws Exception {
		//创建RandomAccessFile对象,并以读写方式打开time.txt文件
		RandomAccessFile raf = new RandomAccessFile("7-17time.txt","rw");
		//读取软件剩余使用次数
		int times = Integer.parseInt(raf.readLine())-1;
		//判断剩余次数
		if(times > 0) {
			//每执行一次代表使用一次,次数就减少一次
			System.out.println("您还可以试用"+times+"次");
			//将记录指针重新指向文件开头
			raf.seek(0);
			//将剩余次数再次写入文件
			raf.write((times+"").getBytes());
		}else {
			System.out.println("使用次数已经用完!");
		}
		//关闭这个随机存取文件流并释放任何系统
		raf.close();
	}
}

在这里插入图片描述


7.6 对象序列化

数据在Java中都是保存在对象中的,如果需要将数据保存到磁盘上,需要使用对象序列化

  • 对象序列化概念:将一个Java对象转换成一个IO流中字节序列的过程,目的是将对象保存在磁盘中或允许在网络中直接传输对象

可序列化的必须实现SerializableExteralizable两个接口之一

  • 实现Serializable或Exteralizable接口对比:
实现Serializable接口实现Exteralizable接口
系统自动存储必要信息程序员决定存储信息
Java内部类支持,易于实现,不需要其他代码支持接口中提供了两个空方法,必须为两个空方法提供实现
性能较差性能较好
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值