Java中 IO流 详情解析总结(不是一般的详细)

一、io流结构分析

要学习io流,我们先来认识几个io流操作有关的类或接口。
io流操作有关的类或接口
以及io流的结构图:
io流的结构图

1. 流的概念和作用

流是一组有顺序的,有起点和终点的字节集合,是对数据传输的总称或抽象。即数据在两设备间的传输称为流,流的本质是数据传输,根据数据传输特性将流抽象为各种类,方便更直观的进行数据操作。

2. 分类:

按照流的单位分的话,可以分为字节流字符流
按照流的方向分的话,可以分为输入流输出流

  • 字节流:多用于读取或书写二进制数据,这些类的基类为InputStream或OutputStream。可以处理所有以bit为单位储存的文件,也就是说可以处理所有的文件,但是在处理字符上的速度不如字符流。

  • 字符流:操作的是为了支持Unicode编码,用于字符国际化,一个字符占用两个字节,这些类的基类为Reader或Writer。该流只能处理字符,但处理字符速度很快

流向:都是相对内存来说,内存输入为读,输出为写,I读O写。即从文件中读取数据到内存(程序)里为流入(输入流),从内存(程序)中将数据写入文件中为流出(输出流),

3. 另外:

整个io流所用类就在java的io包之中,整个io包大量应用了 装饰模式(在这不再介绍)。另外读取数据io流分为 节点流处理流

  • 节点流:文件(File),管道(Piped)和数组(Array)(他们每个类都分别包括输入输出和字节字符四种流);
  • 处理流:其余的都是处理类,他们都是属于节点流的装饰类,下面我整理了一个关于处理流的表格。
    在这里插入图片描述

二、io流分项解析

1、File类
我们先来学习一下File这个类,File是文件和目录路径名称的抽象表示形式,File只关注文件本身的信息,例如:文件名、路径、可读、可写等,不会关注文件的内容,要关注文件的内容的话,那是IO流的技术。

File中的方法,几个常用的是:mkdir(),创建单个目录;mkdirs(),创建多个目录;getPath(),获取文件的路径;length(),获取文件的长度;getName()获取文件名字;getParentFile(),获取文件的父路径名字,即获取这个文件的上一层目录;exists(),判断文件是否存在;createNewFile(),创建文件,是创建文件,不是目录;list(),返回指定的目录里面包含的文件和目录,返回一个字符串数组;listFiles(),返回一个抽象路径名数组,也就是包含文件目录和文件目录的抽象路径,通过getName()和getAbsolutePath()来获取名字和路径;delete(),删除此文件或目录。【可查看API】

例题:输出指定目录下的所有文件信息(这里输出的只是在当前目录,不会输出子目录下的),要求值包含后缀名为.txt的文件,这个有多个方法,先说一个,使用String类里面的endsWith()方法

    public void t(){
    		File file = new File("H:\\javaio");
    		String[] list = file.list();
    		for (String s : list) {
    			if(s.endsWith(".txt")){
    				System.out.println(s);
    			}
    		}
    	}

二(1)、字节流

在这里插入图片描述
2、FileInputStream和FileOutputStream
FileInputStream继承于InputStream,FileOutputStream继承于OutputStream,是用来对二进制文件进行操作的。
值得注意的地方是,使用完了流,记得要关闭,在文件末尾追加内容,要从基础流来考虑,当输出流文件不存在的时候,会自动创建文件,当输入流文件不存在时,会报错。所以,使用输入流时,要确定文件是否存在。

这两个常用的方法是:
read(),从此输入流中读取一个数据字节;
read(byte[] b),从此输入流中将最多b.length个字节的数据读入一个byte数中;
read(byte[] b,int off,int len),从此输入流中将最多len个字节的数据读入一个byte数组中;
write(byte[] b),将b.length个字节从指定byte数组写入此文件输出流中;
write (byte [] b,int off,int len),将指定byte数组中从偏移量off开始的len个字节写入此文件输出流;
write(int b),将指定字节写入此文件输出流;
close(),关闭流。

关于其的一些构造方法,看看API就懂了
案例:
拷贝文件,一个一个字节的拷贝

    public void t1() throws Exception{
    	FileInputStream fis = new FileInputStream("H:\\javaio\\copy01.txt");
    	FileOutputStream fos = new FileOutputStream("H:\\javaio\\copy02.txt");
    	int n;
        //这里面是n等于读取到的字节,当读取到末尾时,返回的是-1,所以这里用!=-1来表示没有读到文件末尾
    	while((n = fis.read()) != -1){
    		fos.write(n);
    	}
    	fos.close();
    	fis.close();
    }

3、BuffereInputStream和BuffereOutputStream 缓冲流
BuffereInputStream(带有缓冲区的字节输入流)继承于FilterInputStream,而FilterInputStream继承于InputStream,BuffereOutputStream(带有缓冲区的字节输出流)继承于FilterOutputStream,而FilterOutputStream继承于OutputStream,关于FilterInputStream和FilterOutputStream,其实它们只是个“装饰器模式”的封装,也就是说它并没有给出具体的功能实现,它具体的功能实现都是通过它的子类来实现的。

可以将数据流从数据源中处理完毕都存入内存缓冲区,然后统一一次性与底层IO进行操作,可以有效降低程序直接操作IO的频率,提高io执行速度。

这两个其实主要就是一个缓冲的作用,我们知道,如果直接让文件或程序跟内存进行交互,效率是十分低下的,而通过缓冲流进行交互,能够大大提高效率,缓冲流的主要作用就是提高了效率。

对于文件的存在与否的反应,和其它一样,不存在时,输入流会报错,输出流会自动创建。

常用的方法是:read(),read(byte [] b,int off,int len),write(int b),write(byte[] b,int off,int len),close();这些具体参照InputStream和OutputStream中这些方法的使用。

案例:
利用缓冲流进行拷贝,多个字节多个字节拷贝

    public void t1() throws Exception{
    		BufferedInputStream bis = new BufferedInputStream(new FileInputStream("H:\\javaio\\测试.avi"));
    		BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("H:javaio\\测试copy1.avi"));
    		byte[] b = new byte[2*1024];
    		int len;
    		while((len = bis.read(b)) != -1){
    			bos.write(b, 0, len);
    		}
    		bos.close();
    		bis.close();
    	}

二(2)、字符流

在这里插入图片描述4、InputStreamReader和OutputStreamWriter(转换流)
InputStreamReader(字符输入转换流)继承于Reader,OutputStreamWriter(字符输出转换流)继承于Writer。它们是字节流和字符流之间的“桥梁”。我们只需要记住,当要操作字节和字符串时,用着两个当纽带来操作。从字节流到字符流的桥,读取字节流转为字符流。可设定编码格式!

InputStreamReader和OutputStreamWriter作用于字节,而FileReader和FileWriter作用于字符,显然两者直接作用有差别的。
案例:
进行文件的拷贝

    public void t1() throws Exception{
    		InputStreamReader isr = new InputStreamReader(new FileInputStream("H:\\javaio\\copy01.txt"));
    		OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("H:\\javaio\\copy02.txt"));
    		char[] cbuf = new char[1024];
    		int len;
    		while((len = isr.read(cbuf)) != -1){
    			osw.write(cbuf, 0, len);
    		}
    		osw.close();
    		isr.close();
    	}

5、FileReader和FileWriter(字符流)
FileReader继承于InputStreamReader,FileWriter继承于OutputStreamWriter。用于对字符流进行操作。直接对文件进行字符操作。
案例:
进行文件的拷贝

    public void t1() throws Exception{
    		FileReader fr = new FileReader("H:\\javaio\\copy01.txt");
    		FileWriter fw = new FileWriter("H:\\javaio\\copy02.txt");
    		char[] cbuf = new char[1024];
    		int len;
    		while((len = fr.read(cbuf)) != -1){
    			fw.write(cbuf, 0, len);
    		}
    		fw.close();
    		fr.close();
    	}

②将内容追加到文件

public void t2() throws Exception{
    	FileWriter fw = new FileWriter("H:\\javaio\\test.txt",true);
    	fw.write("我是测试用例!");
    	fw.close();
    }

6、BufferedReader和BufferedWriter(字符缓冲流)
BuffereReader继承于Reader,BufferedWriter继承于Writer,是字符缓冲流。

这两者常用的方法是:read(),读取单个字符;read(char[] cbuf,int off,int
len),将字符读入数组的某一部分;readLine(),读取一个文本行;write(char[] cbuf,int off,int
len),写入字符数组的某一部分;write(int c)写入单个字符;write(String s,int off,int
len),写入字符串的某一部分;close(),关闭该流;

an案例:
进行文件的拷贝

public void t1() throws Exception{
    		BufferedReader br = new BufferedReader(new FileReader("H:\\javaio\\copy01.txt"));
    		BufferedWriter bw = new BufferedWriter(new FileWriter("H:\\javaio\\copy02.txt"));
    //		char[] cbuf = new char[1024];
    //		int len;
    //		while((len=br.read(cbuf)) != -1){
    //			bw.write(cbuf, 0, len);
    //		}
    		//BufferedReader提供了readLine方法,可以不再使用字节读取方式
    		String readline;
    		while((readline = br.readLine()) != null){
    			bw.write(readline);
    			bw.newLine();
    		}
    		bw.close();
    		br.close();
    	}

二(3)、其他流

7、ObjectInputStream和ObjectOutputStream(对象流
ObjectInputStream(对象输入流)继承于InputStream,ObjectOutputStream(对象输出流)继承于OutputStream。对象流是将对象的基本数据和图形实现持久存储。ObjectOutputStream实际是在对流进行序列化操作,ObjectInputStream实际是在对流进行反序列化操作,要实现序列化,必须实现Serializable接口,否则是无法进行序列化和反序列化的,如果对象中的属性加了transient和static关键字的话,则该属性不会被序列化。

补充:序列化与反序列化
数据传输过程中,都会默认采用二进制文件的方式,因为计算机的底层识别方式就是二进制,不依赖任何运行环境或是程序设计语言,所以这是实现数据传输跨平台跨网络的基础。*序列化可以直接将java对象转化为一个字节序列,并能够在以后将这个字节序列完全恢复为原来的对象(反序列化),*这一过程甚至可以通过网络进行,这意味着序列化机制能自动弥补不同操作系统之间的差异。
注:实现序列化的对象必须实现Serializable接口

实例:
先创建一个对象

 import java.io.Serializable;
     
    /*
     * 为测试对象流创建一个对象
     */
    public class Student implements Serializable{
     
    	/**
    	 * 
    	 */
    	private static final long serialVersionUID = 6271405124073931152L;
    	private String name;
    	private int age;
    	private transient String info1;
    	private static String info2;
    	
    	public Student() {
    	}
     
    	public Student(String name, int age, String info1, String info2) {
    		super();
    		this.name = name;
    		this.age = age;
    		this.info1 = info1;
    		this.info2 = info2;
    	}
     
    	public String getName() {
    		return name;
    	}
     
    	public void setName(String name) {
    		this.name = name;
    	}
     
    	public int getAge() {
    		return age;
    	}
     
    	public void setAge(int age) {
    		this.age = age;
    	}
     
    	public String getInfo1() {
    		return info1;
    	}
     
    	public void setInfo1(String info1) {
    		this.info1 = info1;
    	}
     
    	public static String getInfo2() {
    		return info2;
    	}
     
    	public static void setInfo2(String info2) {
    		Student.info2 = info2;
    	}
     
    	@Override
    	public String toString() {
    		return "Student [name=" + name + ", age=" + age + ", info1=" + info1 + "]" + ", info2=" + info2 + "]";
    	}

    }

写入一个Student对象

    public void t3() throws Exception{
    		ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("H:\\javaio\\objecttest.txt"));
    		oos.writeObject(new Student("测试",18,"信息1","信息2"));
    		oos.close();
    	}

读取对象

   public void t4() throws Exception{
    		ObjectInputStream ois = new ObjectInputStream(new FileInputStream("H:\\javaio\\objecttest.txt"));
    		Student stu = (Student) ois.readObject();
    		System.out.println(stu);
    		ois.close();
    	}

8、PrintStream和PrintWriter(打印流)
PrintStream(可以将字节流封装成打印流)继承于FilterOutputStream,FilterOutputStream是继承于OutputStream的;PrintWriter(可以将字节流、字符流封装成打印流)继承于Writer的。

首先请问java的标准输入流是什么?是InputStream,正确。那么java的标准输出流是什么?是OutputSteam?No!而是PrintStream。

因为标准输入输出流是System类的定义,System中有三个字段,in是InputStream类型,对应的是标准输入流,err和out都是PrintStream对象,out对应的是标准输出流。我们常用的System.out.println(data)方法的返回值就是PrintStream对象,此流默认输出在控制台,也可以重定向输出位置;

其中可以使用PrintStream进行重定向的操作:

系统标准输入流的方向:控制台 -> 程序,重新定义系统标准输入流使用的方向System.setIn(),重新定义后的方向为:文件->程序

   System.setIn(new FileInputStream("H:\\javaio\\testofprint.txt"));
    		//现在获取的next不是控制台输入的 而是文件中的
    		Scanner input = new Scanner(System.in);
    		String next = input.next();
    		System.out.println(next);
    		input.close();

系统的标准输出流的方向:程序->控制台,重定向系统标准输出流使用的方法System.setOut(),重新定义后的方向为:程序->文件

    System.setOut(new PrintStream(new FileOutputStream(file)));
    System.out.println("这些内容只能在file对象的文件中才能看到哦!");//并非在控制台打印而是打印到文件中!

PrintWriter就是PrintStream的字符操作的版本。PrintStream都是针对字节流进行操作的,如果要操作字符流,可以使用PrintWriter。用法相似!
①使用PrintStream进行打印到文件的操作

    public void t1() throws Exception{
    		PrintStream ps = new PrintStream(new FileOutputStream("H:\\javaio\\testofprint.txt"));
    		ps.print("我是打印流测试(PrintStream)");
    		ps.close();
    	}

②使用PrintWriter进行打印到文件的操作

    public void t2() throws Exception{
    		PrintWriter pw = new PrintWriter(new FileWriter("H:\\javaio\\testofprint.txt"));
    		pw.write("我是打印流测试(PrintWriter)");
    		pw.close();
    	}

9、RandomAccessFile(随机访问文件)
RandomAccessFile 是任意位置进入文件的意思,适用于由大小已知的记录组成的文件,它有一个seek方法定义了文件的位置,所以要注意在对文件进行RandomAccessFile操作时,要记住文件的内容的位置和大小,否则会发生内容复写更改的后果。

RandomAccessFile对文件进行读和写操作,具体是读还是写,要根据设置的模式(构造时的参数 “r” “rw”)来决定。底层实际可以理解为一个byte数组,是带有指针的,通过指针的指向对文件进行读操作和写操作。

它的常用方法是:getFilePointer(),返回此文件中的当前偏移量,即返回指针位置;
length(),返回此文件的长度;
read(),read方法包含一系列参数和以read开头的方法,具体请看API,无非就是某些数据类型;
readLine(),从此文件读取文本的下一行;
seek(long pos),设置到此文件开头测量到的文件指针偏移量,在该位置发生下一个读取或写入操作,即设置指针位置,下次读写从该位置开始;
skipBytes(int n),跳过输入的n个字节,丢弃跳过的字节;
write(),write包含一系列参数和以write开头的方法,具体看API,无非就是某些数据类型。

案例:
直接从某一位置开始读取数据,即跳过某一位置之前,使用seek

    public void t3() throws Exception{
    		RandomAccessFile raf = new RandomAccessFile("H:\\javaio\\randomtest.txt", "r");
    		//使用seek设置指针位置为3
    		raf.seek(3);
    		byte[] b = new byte[1024];
    		int len;
    		while((len = raf.read(b)) != -1){
    			System.out.println(new String(b, 0, len));
    		}
    		raf.close();
    	}

10、ByteArrayInoutStream和ByteArrayOutputStream(内存流)
ByteArrayInputStream(内存输入流)继承于InputStream,ByteArrayOutputStream(内存输出流)继承于OutputStream。内存流是关不掉的,一般用来存放一些临时性的数据,理论值是内存大小。
字节数组处理,把字节数组当作输入输出流

①从内存流读出信息,创建内存流时,就把数据存入到内存中

    public void t1() throws Exception{
    		ByteArrayInputStream bais = new ByteArrayInputStream("我是内存流测试".getBytes());//参数为字符数组
    		byte[] b = new byte[1024];
    		int len;
    		while((len = bais.read(b)) != -1){
    			System.out.println(new String(b, 0, len));
    		}
    	}

11、DataOutputStream 和 DataInputStream

这一对类可以直接写入java基本类型数据(没有String),但写入以后是一个二进制文件的形式,不可以直接查看。

DataOutputStream / DataInputStream是常用的过滤流类,如果对象的序列化是整个对象转换为一个字节序列的话,DataOutputStream / DataInputStream就是将字段序列化,转为二进制数据。

三、NIO

io流大体分为BIO,AIO,NIO,常用的基本就是BIO,也就是上文所介绍的那些,

传统io是靠字节或字符来传输,nio是靠块传输,也就是一个一个的buffer速度相对于较快。传统io是阻塞型io,nio是非阻塞型io。多适用于进行流畅的网络读写操作。
具体用法不再介绍

猜你喜欢

转载自blog.csdn.net/weixin_43896747/article/details/86171681
今日推荐