IO流、字节流和字符流


一、IO流

1.1 概念

把这种数据的传输,可以看做是一种数据的流动,按照流动的方向,以内存为基准,分为输入input输出output ,即流向内存是输入流,流出内存的输出流。

Java中I/O操作主要是指使用java.io包下的内容,进行输入、输出操作。输入也叫做读取数据,输出也叫做作写出数据。

1.2 分类

根据数据的流向分为:输入流输出流

  • 输入流 :把数据从其他设备上读取到内存中的流。
  • 输出流 :把数据从内存 中写出到其他设备上的流。

格局数据的类型分为:字节流字符流

  • 字节流 :以字节为单位,读写数据的流。
  • 字符流 :以字符为单位,读写数据的流。

1.3 IO流的父类

名称输入流输出流
字节流字节输入流InputStream字节输出流OutputStream
字符流字符输入流Reader字符输出流Writer

这些类都是抽象类,在实际的开发过程中使用的是相应的子类。

二、字节流

2.1 概念

一切文件数据(文本、图片、视频等)在存储时,都是以二进制数字的形式保存,都一个一个的字节,那么传输时一样如此。所以,字节流可以传输任意文件数据。在操作流的时候,我们要时刻明确,无论使用什么样的流对象,底层传输的始终为二进制数据。

2.2 OutputStream

java.io.OutputStream 抽象类是表示字节输出流的所有类的超类,将指定的字节信息写出到目的地。它定义了字节输出流的基本共性功能方法。

2.2.1 常用方法

方法名说明
public abstract void write(int b)将指定的字节输出流。
public void write(byte[] b)将 b.length字节从指定的字节数组写入此输出流。
public void write(byte[] b, int off, int len)从指定的字节数组写入 len字节,从偏移量 off开始输
public void close()关闭此输出流并释放与此流相关联的任何系统资源。
public void flush()刷新此输出流并强制任何缓冲的输出字节被写出。
  • 注意事项

close方法,当完成流的操作时,必须调用此方法,释放系统资源。

2.2.2 FileOutputStream类

java.io.FileOutputStream 类是文件输出流,用于将数据写出到文件。

2.2.2.1 构造方法
方法名说明
public FileOutputStream(File file)创建文件输出流以写入由指定的 File对象表示的文件。
public FileOutputStream(String name)创建文件输出流以指定的名称写入文件。
  • 代码演示
		File f=new File("E://test/a.txt");
		//创建流文件的文件所在文件夹必须存在否则会报FileNotFoundException
		//创建流文件的文件如果存在则会清空 不存在则会创建
		FileOutputStream fos1=new FileOutputStream("E://test/b.txt");
		FileOutputStream fos2=new FileOutputStream(f);
		//fos1.write(int);每次可以写出一个字节数据
		fos1.write(97);fos1.write(98);fos1.write(99);//abc
		//fos1.write(byte[]);每次可以写出数组中的数据
		byte []b={65,66,67,68,69,70,71,72,73,74};
		fos1.write(b);//ABCDEFGHIJ
		//fos1.write(byte[], startIndex, length);每次写出从startIndex索引开始,length个字节
		fos1.write(b, 4, 4);//EFGH
		fos1.flush();//刷新缓存
		fos1.close();//关闭资源
  • 注意事项

当你创建一个流对象时,必须传入一个文件路径。该路径下,如果没有这个文件,会创建该文件。如果有这个文件,会清空这个文件的数据。

2.2.2.2 输出字符串到文件
		//将字符串输出至文件
		//创建指定文件的输出流
		FileOutputStream fos1=new FileOutputStream("E://test/b.txt");
		//创建要输出的字符串
		String str="这是一句话";
		//调用字符串方法将字符串转换为字节数组
		byte []b=str.getBytes();
		//调用输出流对象写出字节数组的方法
		//将字节数组内容输出至指定文件
		fos1.write(b);
		//刷新缓存
		fos1.flush();
		//关闭资源
		fos1.close();
2.2.2.3 追加输入字符串
方法名说明
public FileOutputStream(File file,boolean append)创建文件输出流以写入由指定的 File对象表示的文件。
public FileOutputStream(String name, boolean append)创建文件输出流以指定的名称写入文件。

这两个构造方法,参数中都需要传入一个boolean类型的值,true 表示追加数据,false 表示清空原有数据。这样创建的输出流对象,就可以指定是否追加续写了

		//创建流文件的文件所在文件夹必须存在否则会报FileNotFoundException
		//创建流文件的文件如果 不存在则会创建
		//boolean代表是否追加输入
		//如果为false会清空文件内容
		FileOutputStream fos1=new FileOutputStream("E://test/b.txt",true);
		
		fos1.write("这又是一句话".getBytes());
		fos1.flush();
		fos1.close();
2.2.2.4 写出换行

如果写的数据比较多,那么就会在文件中在一行展示。会造成阅读不方便。我们现在的需求是,每写一次数据,都要从下行开始,这个时候就要涉及到换行的问题。

  • 回车符与换行符
  • 回车符\r和换行符\n
    • 回车符:回到一行的开头(return)。
    • 换行符:下一行(newline)。
  • 系统中的换行:
    • Windows系统里,每行结尾是 回车+换行 ,即\r\n
    • Unix系统里,每行结尾只有 换行 ,即\n
    • Mac系统里,每行结尾是 回车 ,即\r。从 Mac OS X开始与Linux统一。
  • 代码演示
public class FOSWrite {
    public static void main(String[] args) throws IOException {
        // 使用文件名称创建流对象
        FileOutputStream fos = new FileOutputStream("fos.txt");  
      	// 定义字节数组
      	byte[] words = {97,98,99,100,101};
      	// 遍历数组
        for (int i = 0; i < words.length; i++) {
          	// 写出一个字节
            fos.write(words[i]);
          	// 写出一个换行, 换行符号转成数组写出
            fos.write("\r\n".getBytes());
        }
      	// 关闭资源
        fos.close();
    }
}

2.3 InputStream

java.io.InputStream 抽象类是表示字节输入流的所有类的超类,可以读取字节信息到内存中。它定义了字节输入流的基本共性功能方法。

2.3.1 常用方法

方法名说明
public void close()关闭此输入流并释放与此流相关联的任何系统资源。
public abstract int read()从输入流读取数据的下一个字节。
public int read(byte[] b)从输入流中读取一些字节数,并将它们存储到字节数组 b中 。

close方法,当完成流的操作时,必须调用此方法,释放系统资源。

2.3.2 FileInputStrean

2.3.2.1 构造方法
方法名说明
FileInputStream(File file)通过打开与实际文件的连接来创建一个 FileInputStream ,该文件由文件系统中的 File对象 file命名。
FileInputStream(String name)通过打开与实际文件的连接来创建一个 FileInputStream ,该文件由文件系统中的路径名 name命名。

当你创建一个流对象时,必须传入一个文件路径。该路径下,如果没有该文件,会抛出FileNotFoundException

  • 代码演示
		//使用指定的文件对象或代表文件的字符串路径创建文件输入流
		FileInputStream fis1 = new FileInputStream("E://test/a.txt");
		FileInputStream fis2 = new FileInputStream(new File("E://test/b.txt"));
		
		//fis1.read();
		//读取一个字节并使用int返回
		int read1 = fis1.read();
		int read2 = fis1.read();
		int read3 = fis1.read();
		System.out.println((char)read1);
		System.out.println((char)read2);
		System.out.println((char)read3);
		//fis1.read(byte[])
		//使用指定数组获取输入内容
		byte[] b=new byte[5];
		int len = fis1.read(b);
		System.out.println(len);
		System.out.println(new String(b,0,len));
		 
		fis1.close();//关闭输入流
2.3.2.2 读取文件所有内容并输出至控制台
		//读取文件所有内容并输出至控制台
		//1获取文件字节后使用读取单个字节方法读取
		//2使用字节数组一次读取多个字节
		//创建读取指定文件的输入流对象
		FileInputStream fis2 = new FileInputStream(new File("E://test/b.txt"));
		
		//创建读取使用的字节数组
		byte[] b=new byte[1024];
		
		
		//创建变量保存每次读取的长度
		int len=0;
		
		//如果调用方法返回读取长度不为-1
		while(len!=-1){
			//将本次读取的内容存储至字节数组中
			len=fis2.read(b);
			//使用字节数组创建字符串对象
			if(len!=-1){
				String string = new String(b,0,len);
				System.out.println(string);
			}
		}
//		while((len=fis2.read(b))!=-1){//将本次读取的内容存储至字节数组中
//			//使用字节数组创建字符串对象
//			if(len!=-1){
//				String string = new String(b,0,len);
//				System.out.println(string);
//			}
//		}
		
		//关闭输入流对象
		fis2.close();
2.3.2.3 文件复制

所有文件本质都是字节文件(使用字节流可以复制所有类型的文件)

		//文件复制
		File f1=new File("E://test/e.zip");//源文件
		File f2=new File("E://test/a/b/c/d/e.zip");//复制位置
		//1、创建复制位置的文件夹
		//获取复制文件的父文件夹对象
		File parentFile = f2.getParentFile();
		//调用创建方法创建多级目录
		parentFile.mkdirs();
		
		//2、创建源文件的文件字节输入流与赋值文件的文件字节输出流
		FileInputStream fis=new FileInputStream(f1);
		FileOutputStream fos=new FileOutputStream(f2);
		 
		//3、读取源文件向复制文件输出
		//创建读取字节数组
		byte []b=new byte[102400];
		//创建读取长度
		int len=0;
		//读取并输出
		while((len=fis.read(b))!=-1){
			fos.write(b, 0, len);
		}
		
		//关闭流释放资源
		fis.close();
		fos.flush();
		fos.close();

三、字符流

3.1 概念

字符流可以看做是特殊的字节流,在使用字节流读取文件夹时,如果读取的是文档文件,有时可能会因为字符编码导致读取出现问题

在这里插入图片描述

即使文件编码统一,在进行读取时,读取字节数与对应字符编码代表字节数读取不配,那么也会造成乱码问题.这个时候就需要是同字符流进行读取

3.2 Writer

java.io.Writer 抽象类是表示用于写出字符流的所有类的超类,将指定的字符信息写出到目的地。它定义了字符输出流的基本共性功能方法。

3.2.1常用方法

方法名说明
void write(int c)写入单个字符
void write(char[] cbuf)写入字符数组。
abstract void write(char[] cbuf, int off, int len)写入字符数组的某一部分,off数组的开始索引,len写的字符个数
void write(String str)写入字符串
void write(String str, int off, int len)写入字符串的某一部分,off字符串的开始索引,len写的字符个数。
void flush()刷新该流的缓冲。
void close()关闭此流,但要先刷新它。

3.2.2 FileWriter

java.io.FileWriter 类是写出字符到文件的便利类。构造时使用系统默认的字符编码和默认字节缓冲区。

3.2.2.1 构造方法
方法名说明
FileWriter(File file)创建一个新的 FileWriter,给定要读取的File对象。
FileWriter(String fileName)创建一个新的 FileWriter,给定要读取的文件的名称。
FileWriter(File file)创建一个新的 FileWriter,给定要读取的File对象。
FileWriter(String fileName)创建一个新的 FileWriter,给定要读取的文件的名称。

当你创建一个流对象时,必须传入一个文件路径,类似于FileOutputStream。

  • 代码演示
		File file=new File("E://test/a.txt");
		//创建以指定文档作为输出位置的写入流,如果不存在则创建
		//如果存在则清空
		//FileWriter fw1=new FileWriter(file);
		//FileWriter fw2=new FileWriter("E://test/a.txt");
		//true代表继续写入
		FileWriter fw3=new FileWriter(file,true);
		FileWriter fw4=new FileWriter("E://test/a.txt",true);
		
	
		//将字符串指定索引起始长度写入文本文件
		//fw3.write(String,off,len);
		fw3.write("abcdefghijk",3,5);
		
		//将字节数组指定索引起始长度写入文本文件
		//fw3.write(char[], off, len);
		char [] cbuf={'你','是','大','帅','哥','F'};
		fw3.write(cbuf, 0, 5);
		
		//刷新关闭释放资源
		fw3.flush();
		fw3.close();
3.2.2.2 读取文档输出控制台并复制
	//读取文本文档并将内容输出至控制台
	public static void main(String[] args) throws  Exception {
		//创建指定文本文档的文件对象
		File file=new File("E://hlm.txt");
		//创建文件复制输出对象
		File file1=new File("E://hlm1.txt");
		
		//创建对应文件的字符读取与写入流
		FileReader fr=new FileReader(file);
		FileWriter fw=new FileWriter(file1);
		
		//创建读取字符数组
		char [] c=new char[1000];
		//创建读取长度
		int len=0;
		while((len=fr.read(c))!=-1){
			 String string = new String(c,0,len);
			 System.out.println(string);
			 fw.write(string);
		}
		
		//关闭释放资源
		fr.close();
		fw.flush();
		fw.close();
	}

3.3 Reader

java.io.Reader抽象类是表示用于读取字符流的所有类的超类,可以读取字符信息到内存中。它定义了字符输入流的基本共性功能方法。

3.3.1 常用方法

方法名说明
public void close()关闭此流并释放与此流相关联的任何系统资源。
public int read()从输入流读取一个字符。
public int read(char[] cbuf)从输入流中读取一些字符,并将它们存储到字符数组 cbuf中
  • 注意事项
  1. 字符编码: 字节与字符的对应规则。Windows系统的中文编码默认是GBK编码表。idea中UTF-8
  2. 字节缓冲区: 一个字节数组,用来临时存储字节数据。

3.3.2 FileReader

3.3.2.1 构造方法
方法名说明
FileReader(File file)创建一个新的 FileReader ,给定要读取的File对象。
FileReader(String fileName)创建一个新的 FileReader ,给定要读取的文件的名称。

当你创建一个流对象时,必须传入一个文件路径。类似于FileInputStream 。

  • 代码演示
		File file =new File("E://hlm.txt");
		//使用指定文件获取字符串创建对应的字符输入流对象
		FileReader fr1=new FileReader(file);
		FileReader fr2=new FileReader("E://hlm.txt");
		
		//读取一个字符返回对应字符的ASCII对应数值
		//fr1.read();
		System.out.print((char)fr1.read());
		System.out.print((char)fr1.read());
		System.out.print((char)fr1.read());
		
		//使用char数组一次读取多个字符
		//fr1.read(char[]);
		char [] c=new char[1000];
		int len = fr1.read(c);
		System.out.print(new String(c,0,len));
3.3.2.2 读取文本文档内容并输出至控制台
		//创建读取文本文档的文件对象
		File file =new File("E://hlm.txt");
		//使用文本文档的文件对象创建字符读取流
		FileReader fr=new FileReader(file);
		//创建每次取出数据的字符数组
		char [] c=new char[1024];	
		//创建每次取出的字符数变量
		int len=0;
		//使用循环读取数据输出至控制台
		while((len=fr.read(c))!=-1){
			System.out.print(new String(c,0,len));
		}
		//关闭文件字符读取流
		fr.close();

面试题


1、字节流与字符流的区别

  • 字符流本质使用的就是字节流,只不过是根据当前编码进行字节流缓冲读取写入
  • 字节流通常读取文件(可以对任意文件进行操作)
  • 字符只能对文本文件进行操作(我们能看懂的)
    当需要对文件内容进行操作时使用字符流,如果进行文件复制使用字节流

2、flushclose之间的区别

  • 因为内置缓冲区的原因,如果不关闭输出流,无法写出字符到文件中。但是关闭的流对象,是无法继续写出数据的。如果我们既想写出数据,又想继续使用流,就需要flush 方法了。
    • flush:刷新缓冲区,流对象可以继续使用。
    • close :先刷新缓冲区,然后通知系统释放资源。流对象不可以再被使用了。

每日一点点进步
不进则退