IO流学习笔记

IO流

  • 1. IO流的分类
  • 2.字节流
    • 2.1 字节输出流的顶层类OutputStream
    • 2.2 文件的字节输出流FileOutputStream
      • 2.2.1 FileOutputStream的构造方法
      • 2.2.2 文件的字节输出流的使用
        • 2.2.2.1 一次输出一个字节到文件
        • 2.2.2.2 一次输出多个字节到文件
        • 2.2.2.3 追加写
        • 2.2.2.4 写换行
    • 2.3 字节输入流的顶层类InputStream
    • 2.4 文件的字节输入流FileInputStream
      • 2.4.1 FileInputStream的构造方法
      • 2.4.2 FileInputStream的使用
        • 2.4.2.1 一次读一个字节
        • 2.4.2.2 一次读取多个字节
    • 2.5 例子:文件的复制
    • 2.6 使用字节流的问题
  • 3. 字符流
    • 3.1 字符输入流的顶层类Reader
    • 3.2 文件的字符输入流FileReader类
      • 3.2.1 构造方法
      • 3.2.2 FileReader的使用
        • 3.2.2.1 一次读取一个字符
        • 3.2.2.2 一次读取多个字符
    • 3.3 字符输出流的顶层类Writer
    • 3.4 文件的字符输出流FileWriter
      • 3.4.1 构造方法
      • 3.4.2 FileWriter的使用
        • 3.4.2.1 一次输出单个字符
        • 3.4.2.2 flush方法与close方法的区别
        • 3.4.2.3 一次输出多个字符/字符串
        • 3.4.2.4 追加写
        • 3.4.2.5 换行写
  • 4. 流的异常
    • 4.1 jdk1.7之前处理流异常的方式
    • 4.2 jdk1.7之后处理流异常的方式
  • 5. 缓冲流
    • 5.1 缓冲的字节输出流BufferedOutputStream类
      • 5.1.1 构造方法
      • 5.1.2 BufferedOutputStream的使用
        • 5.1.2.1 一次输出多个字节
    • 5.2 缓冲的字节输入流BufferedInputStream
      • 5.2.1 构造方法
      • 5.2.2 BufferedInputStream的使用
        • 5.2.2.1 一次读取多个字节

1. IO流的分类

按照输入/输出分类
把硬盘中的数据读入内存,叫输入流
把内存中的数据写入硬盘,叫输出流
按照字节/字符分类
IO流还可以分为字节流和字符流

所以两种分类结合,就可以分为这个四类,这几个类都是IO流的顶层父类
IO流学习笔记_第1张图片

2.字节流

一切皆为字节,计算机上的文件、图片、视频,都是以字节的形式存储的。传输时,字节流可以传输任意类型的文件。
IO流学习笔记_第2张图片

2.1 字节输出流的顶层类OutputStream

java.io.OutputStream是一个抽象类,是所有字节输出流的超类。
这个类定义了子类共性的成员方法。

成员方法
方法1.public void close()
关闭字节输出流,并释放与此流相关的任何系统资源。

方法2.public void flush()
刷新字节输出流,并强制缓冲中的字节被写出。

方法3.public void write(byte[] b)
将字节数组b,写入此输出流
byte[]是字节数组,byte是字节
1个字节=8个比特位(例如10100111)

方法4.public void write(byte[] b, int off, int len)
将字节数组b,从off开始的,len长度的字节,写入此输出流。

方法5.public abstract void write(int b)
将指定的字节写入此输出流

2.2 文件的字节输出流FileOutputStream

1.继承关系: java.io.FileOutputStream extends OutputStream
2.功能: FileOutputStream把内存中的数据,写入到硬盘的文件中。
3.把数据写入文件的原理:
java程序 —> JVM —> OS操作系统 —> OS调用写数据的方法 —> 把数据写入文件中

2.2.1 FileOutputStream的构造方法

构造方法1.FileOutputStream(String name)
String name是写入数据的文件路径
构造方法2.FileOutputStream(File file)
File file是写入数据的文件

以上两个构造方法的作用:
1.创建一个FIleOutputStream对象;
2.根据构造方法中的文件/文件路径,创造一个空的文件;
3.把FileOutputStream对象指向文件

构造方法3.FileOutputStream(String name, boolean append)
String name是写入数据的文件路径
构造方法4.FileOutputStream(File file, boolean append)
File file是写入数据的文件

以上两个构造方法的boolean append表示追加写
1.true创建FileOutputStream对象时,不会覆盖原文件,继续在原文件末尾追加写;
2.false创建FileOutputStream对象时,会创建一个新文件,覆盖原文件,不会在原文件追加写;

2.2.2 文件的字节输出流的使用

FileOutputStream的使用步骤
1.创建一个FileOutputStream对象,构造方法的参数是写入数据的目的地;
2.调用FileOutputStream对象的write方法,把数据写入到文件中;
3.释放系统资源(流使用会占用内存,使用完毕要清内存)。

2.2.2.1 一次输出一个字节到文件

举例1
使用FileOutputStream的public abstract void write(int b)方法

public static void mian(String[] args) throw IOException {
// step1.创建一个FileOutputStream对象,构造方法的参数是写入数据的目的地
FileOutputStream fos = new FileOutputStream("09_IOAndProperties\\a.txt");
// step2.调用FileOutputStream对象的write方法,把数据写入到文件中;
// 这里用public abstract void write(int b),将指定的字节写入此输出流
fos.write(97);
// step3.释放系统资源
fos.close();
}

注意:

  1. 任何文本编辑器(txt、notepad++、word)在打开文件的时候,都会查询编码表,把字节按照编码表转成字符表示,方便我们阅读。
  2. 规则:
    0-127,就查询ASCII表。所以这里写入的97,a.txt打开显示的是a
    其他值,查询系统默认编码表

2.2.2.2 一次输出多个字节到文件

使用FileOutputStream的public void write(byte[] b)方法或者public void write(byte[] b, int off, int len)方法,可以一次输出多个字节到文件

举例1
使用public void write(byte[] b)方法

public static void mian(String[] args) throw IOException {
FileOutputStream fos = new FileOutputStream(new File("09_IOAndProperties\\b.txt"));
// 这里用public void write(byte[] b)
byte[] bytes = new byte[]{65, 66, 67, 68, 69};
fos.write(bytes);
fos.close();
}

此时,b.txt打开显示的是ABCDE,因为bytes里的值在0-127之间会查询ASCII表。

举例2
public void write(byte[] b)方法
改变bytes的值,里面有不在0-127范围的值

public static void mian(String[] args) throw IOException {
FileOutputStream fos = new FileOutputStream(new File("09_IOAndProperties\\b.txt"));
// 这里用public void write(byte[] b)
byte[] bytes = new byte[]{-65, 66, -67, 68, 69};
fos.write(bytes);
fos.close();
}

有不在0-127范围内的,此时会查询系统的默认编码表,按照编码表的规则将字节转换为字符。

举例3
使用public void write(byte[] b, int off, int len)方法

public static void mian(String[] args) throw IOException {
FileOutputStream fos = new FileOutputStream(new File("09_IOAndProperties\\b.txt"));
// 这里用public void write(byte[] b, int off, int len)
byte[] bytes = new byte[]{65, 66, 67, 68, 69};
fos.write(bytes,1,2);
fos.close();
}

此时打开b.txt,只有BC

举例4
使用String的getBytes()方法获取字节数组

public static void mian(String[] args) throw IOException {
  FileOutputStream fos = new FileOutputStream(new    File("09_IOAndProperties\\b.txt"));
  byte[] bytes = "您好".getBytes();
  System.out.println(bytes); // [-28, -67, -96, -27, -91, -67] 此时两个字符打印出来有6个字节,是因为IDEA里的编码是UTF-8,一个中文字符对应3个字节,而windows里的默认编码是GBK,一个中文字符对应2个字节。
  fos.write(bytes);
  fos.close();
}

此时b.txt可以看到"您好"

2.2.2.3 追加写

举例
在FileOutputStream的构造方法里,把boolean append设置为true

public static void mian(String[] args) throw IOException {
  FileOutputStream fos = new FileOutputStream("09_IOAndProperties\\b.txt", true);
  byte[] bytes = "您好".getBytes();
  fos.write(bytes);
  fos.close();
}

执行第一次,b.txt里面是“您好”。执行第二次,b.txt里面是“您好您好”。

2.2.2.4 写换行

不同操作系统的换行符不同,windows中是\r\n,linux中是/n,mac中是/r

public static void mian(String[] args) throw IOException {
  FileOutputStream fos = new FileOutputStream("09_IOAndProperties\\b.txt", true);
  for(int i=0; i < 5; i++) {
	 fos.write("您好".getBytes());
	 fos.write("\r\n".getBytes());
	}
  fos.close();
}

此时,b.txt中会出现换行的“您好”

2.3 字节输入流的顶层类InputStream

InputStream是抽象类,是所有字节输入流的超类。
这个类定义了子类共性的成员方法
成员方法
方法1.public int read(),从输入流中,读取数据的下一个字节
方法2.public int read(byte[] b),从输入流中,读取一定数量的字节,并存储在缓冲区的字节数组b中
方法3.public void close(), 关闭输入流,并释放相关系统资源

2.4 文件的字节输入流FileInputStream

java.io.FileInputStream继承了InputStream类
作用:FileInputStream把硬盘文件中的数据,读取到内存中使用

2.4.1 FileInputStream的构造方法

构造方法1. FileInputStream(String name)
参数:String name读取的文件路径
构造方法2. FileInputStream(File file)
参数:File file读取的文件

构造方法的作用

  • 创建一个FileInputStream对象
  • 把FileInputStream对象指向要读取的文件

读取的原理
硬盘–>内存:
java程序 --> JVM --> OS --> 调用OS的读取方法 --> 读取文件

2.4.2 FileInputStream的使用

FIleInputStream的使用步骤:
1.创建FileInputStream对象,构造方法中传要读取的文件
2.使用FileInputStream对象的read方法,读取文件
3.释放资源

2.4.2.1 一次读一个字节

此时a.txt里的内容是abc

public static void main(String[] args) throws IOException{
	// 1.创建FileInputStream对象,构造方法中传要读取的文件
	FileInputStream fis = new FileInputStream("09_IOAndProperties\\a.txt");
	// 2.使用FileInputStream对象的read方法,读取文件
	// 这里使用int read()读取文件中的一个字节并返回,读取到文件的末尾返回-1
	int res = fis.read();
	System.out.println(res); // 打印出来是97,也就是a
	
	res = fis.read();
	System.out.println(res); // 打印出来是98,也就是b

	res = fis.read();
	System.out.println(res); // 打印出来是99,也就是c

	res = fis.read();
	System.out.println(res); // 打印出来是-1
	// 3.释放资源
	fis.close();
}

以上有重复的步骤,使用while循环读,结束条件是读取到-1

public static void main(String[] args) throws IOException{
	FileInputStream fis = new FileInputStream("09_IOAndProperties\\a.txt");
	int res = 0; // 记录读取到的字节
	while((res=fis.read()) != -1) {
		System.out.println(res);
	}
 	fis.close();
}

打印出来是97 98 99
注意

  1. (res=fis.read()) != -1的含义,为什么要用一个变量res来接收读取的数据。
    • fis.read()读取一个字节
    • res = fis.read() 把读取到的字节赋值给res
    • (res=fis.read()) != -1判断变量res不等于-1

2.写成这样就是错的,因为fis.read()每读一次,指针会往后移动

while(fis.read() != -1) {
	System.out.println(fis.read());
}

2.4.2.2 一次读取多个字节

b.txt里的是ABCDE

public static void main(String[] args) throws IOException{
	// 1.创建FileInputStream对象,构造方法中传要读取的文件
	FileInputStream fis = new FileInputStream("09_IOAndProperties\\b.txt");
	// 2.使用FileInputStream对象的read方法,读取文件
	// 这里使用public int read(byte[] b),从输入流中,读取一定数量的字节,并存储在缓冲区的字节数组b中。
	byte[] bytes = new byte[2];
	int len = fis.read(bytes);
	System.out.println(len); // 打印的是2
	System.out.println(Arrays.toString(bytes)); // 打印的是[65,66]
	System.out.println(new String(bytes)); // 打印的是AB

	len = fis.read(bytes);
	System.out.println(len); // 打印的是2
	System.out.println(new String(bytes)); // 打印的是CD

	len = fis.read(bytes);
	System.out.println(len); // 打印的是1
	System.out.println(new String(bytes)); // 打印的是ED

	len = fis.read(bytes);
	System.out.println(len); // 打印的是-1
	System.out.println(new String(bytes)); // 打印的是ED
	// 3.释放资源
	fis.close();
}

注意:
1.String类的构造方法里有:

  • String(byte[] bytes)把字节数组转为字符串
  • String(byte[] bytes, int offset, int length)把字节数组的一部分转为字符串,offset是转换开始的索引,length是转换的字节个数

2.要注意public int read(byte[] b)方法里的int是啥?byte[]是啥?

  • len是读取到的有效字节个数
  • byte[] 起到缓冲的作用,存读取到的多个字节。bytes的长度通常定义为1024或者1024的整数倍

3.读取的原理

  • b.txt里内容是ABCDE,其实在后面还会有一个操作系统的结束标记
  • byte[] bytes = new byte[2]每次可以读两个字节
  • 第一次读取,bytes里的是AB,len = fis.read(bytes)是2
  • 第二次读取,bytes里的是CD, len = fis.read(bytes)是2
  • 第三次读取,只有效读取到E,那么bytes里的是C会被覆盖掉,D没有变, 所以打印出来是ED,len = fis.read(bytes)是1
  • 第四次读取,读到的是结束标记,len此时是-1

以上代码可以使用while来优化,结束条件读取到-1
此时打印出来是ABCDE

public static void main(String[] args) throws IOException{
	// 1.创建FileInputStream对象,构造方法中传要读取的文件
	FileInputStream fis = new FileInputStream("09_IOAndProperties\\b.txt");
	// 2.使用FileInputStream对象的read方法,读取文件
	// 这里使用public int read(byte[] b),从输入流中,读取一定数量的字节,并存储在缓冲区的字节数组b中。
	byte[] bytes = new byte[2];
	int len = 0; // 记录每次读取的有效字节个数
	while((len = fis.reas(bytes)) != -1) {
		System.out.println(new String(bytes, 0, len));
	}
	// 3.释放资源
	fis.close();
}

2.5 例子:文件的复制

将C盘中的1.jpg图片,复制到D盘。
步骤:

  • 创建一个字节输入流对象,构造方法中绑定要读取的数据源
  • 创建一个字节输出流对象,构造方法中绑定要写入的目的地
  • 使用字节输入流对象的read方法读取文件
  • 使用字节输出流的write方法,把读取到的字节写入到目的文件中
  • 释放资源, 先关闭写的流对象,再关闭读的流对象。因为如果写完了,肯定已经读完了。
public static void main(String[] args) throws IOException{
	// 1.创建一个字节输入流对象,构造方法中绑定要读取的数据源
	FileInputStream fis = new FileInputStream("c:\\1.jpg");
	// 2.创建一个字节输出流对象,构造方法中绑定要写入的目的地
	FileOutputStream fos = new FileOutputStream("d:\\1.jpg");
	// 3.使用字节输入流对象的read方法读取文件
	byte[] bytes = new bytes[1024];
	int len = 0;
	while((len = fis.read(bytes)) != -1) {
		// 4.使用字节输出流的write方法,把读取到的字节写入到目的文件中
		fos.write(bytes, 0, len);
	}
	// 5. 释放资源, 先关闭写的流对象,再关闭读的流对象。因为如果写完了,肯定已经读完了。
	fos.close();
	fis.close();
}

2.6 使用字节流的问题

使用字节流读取中文时会遇到问题,因为GBK中,1个中文占用2个字节,UTF-8中,一个中午占用3个字节。
举例
c.txt的内容是"你好",编码格式是UTF-8,也就是"你"占用3个字节,"好"占用3个字节。此时打印出来的是6个数字,因为每次就读取1/3个中文,如果用char强转,打印出来是乱码。但是用字节流读取英文字符是没有问题的。
为了解决这个问题,Java提供了字符流,字符流一次读写一个字符,不管字符是中文、英文还是数字。

public static void main(String[] args) throws IOException{
	FileInputStream fis = new FileInputStream("c.txt");
	int len = 0;
	while((len = fis.read()) != -1) {
		System.out.println(len); // 打印出来的是数字
		System.out.println((char)len); // 打印出来的是乱码
	}
	fis.close();
}

3. 字符流

3.1 字符输入流的顶层类Reader

  • java.io.Reader类是一个抽象类,是字符输入流的顶层父类
  • Reader类里定义了共用的成员方法
    • int read() 一次读取一个字符
    • int read(char[] cbuf) 一次读取多个字符,并将字符放入数组
    • void close() 释放资源

3.2 文件的字符输入流FileReader类

  • 把硬盘文件中的数据以字符的形式读取到内存中
  • FileReader类的顶层父类就是Reader类
  • java.io.FileReader extends InputStreamReader extends Reader
  • InputStreamReader类是转换流

3.2.1 构造方法

构造方法1.FileReader(String fileName)
参数String fileName是要读取的文件路径名
构造方法2.FileReader(File file)
参数File file是要读取的文件

  • 构造方法的作用
    • 创建一个FileReader对象
    • 把FileReader对象指向要读取的文件

3.2.2 FileReader的使用

FileReader的使用步骤

  • 创建FileReader对象,构造方法中传要读取的数据源
  • 使用FileReader对象的read()方法读取文件
  • 释放资源

3.2.2.1 一次读取一个字符

举例
c.txt的内容是"您好abc123"

public static void main(String[] args) throws IOException{
	// 1.创建FileReader对象,构造方法中传要读取的数据源
	FileReader fr = new FileReader("src\\c.txt");
	// 2.使用FileReader对象的read()方法读取文件
	// 使用int read()读取单个字符并返回
	int len = 0;
	while((len = fr.read()) != -1) {
		System.out.println(len); // 此时打印的是数字,因为len就是int型,要转回字符
		System.out.print(len); // 此时打印的是: 您好123abc###
 	}
	fis.close();
}

3.2.2.2 一次读取多个字符

c.txt的内容是"您好abc123"

public static void main(String[] args) throws IOException{
	// 1.创建FileReader对象,构造方法中传要读取的数据源
	FileReader fr = new FileReader("src\\c.txt");
	// 2.使用FileReader对象的read()方法读取文件
	// 使用int read(char[] cbuf)一次读取多个字符,并将字符存在数组char[] cbuf中,返回有效读取的字符个数
	int len = 0; // 每次读取到的有效字符个数
	char[] cs = new char[1024];
	while((len = fr.read(cs)) != -1) {
	  System.out.printlnl(new String(cs, offset, count)); //打印出来是:您好abc123###
 	}
	fis.close();
}
  • String类的构造方法
    • String(char[] value) 把字符数组转成字符串
    • String(char[] value, int offset, int count) 把字符数组从offset开始,count个字符转成字符串

3.3 字符输出流的顶层类Writer

  • java.io.Writer是所有字符输出流的顶层父类,是抽象类
  • 定义了字符输出流中共性的成员方法
    • 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.4 文件的字符输出流FileWriter

  • java.io.FileWriter extends OutputStreamWriter extends Writer
  • OutputStreamWriter是转换流
  • FileWriter类作用:把内存中的字符数据写入文件

3.4.1 构造方法

构造方法1 FileWriter(String fileName)
参数String fileName是要写入数据的文件路径名
构造方法2 FileWriter(File file)
参数File file是要写入数据的文件

  • 构造方法的作用
    • 创建一个FileWriter对象
    • 根据构造方法中的文件/文件路径,创建要写入的文件
    • 把FileWriter对象指向创建好的文件

构造方法3 FileWriter(String fileName, boolean apend)
参数String fileName是要写入数据的文件路径名
构造方法4 FileWriter(File file, boolean append)
参数File file是要写入数据的文件

以上两个构造函数中的boolean append:

  • true:不会创建新的文件去覆盖原来的文件,可以追加写
  • false:创建新的文件覆盖原来的文件,不可以追加写

3.4.2 FileWriter的使用

  • 使用步骤

    • 创建FileWriter对象,构造方法中绑定要写入数据的目的地
    • 使用FileWriter对象的write方法,把数据写入内存缓冲区中(这里有个字符转换成字节的过程)
    • 使用FileWriter对象的flush方法,把内存缓冲区中的数据,刷新到文件中
    • 释放资源(会先把内存缓冲区中的数据刷新到文件中)
  • 字符输出流与字节输出流的最大区别是,字符输出流不是直接把数据写入到文件中,而是写入到内存缓冲区中。字节输出流是直接把数据写入到硬盘的文件中。

3.4.2.1 一次输出单个字符

此时,c.txt里显示a

public static void main(String[] args) throws IOException{
	// 1.创建FileWriter对象,构造方法中绑定要写入数据的目的地
	FileWriter fw = new FileWriter("src\\c.txt");
	// 2.使用FileWriter对象的write方法,把数据写入内存缓冲区中(这里有个字符转换成字节的过程)
	// 使用void write(int c) 写入单个字符
	fw.write(97); // 如果没有后续的flush或者close操作,数据不会被写入到c.txt中。此时数据还在内存中,停止程序,数据会消失。
	// 3.使用FileWriter对象的flush方法,把内存缓冲区中的数据,刷新到文件中
	fw.flush();
	// 4.释放资源(会先把内存缓冲区中的数据刷新到文件中)
	fw.close();
}

3.4.2.2 flush方法与close方法的区别

+ flush:刷新缓冲区,流对象可以继续使用
+ close:先刷新缓冲区,然后通知系统释放资源,流对象不可以继续使用 
public static void main(String[] args) throws IOException{
	FileWriter fw = new FileWriter("src\\d.txt");
	fw.write(97);
	fw.flush();
	// 刷新后,流对象可以继续使用
	fw.write(98);
	fw.flush();
	fw.close();
	// close方法后,流对象已经关闭,从内存中消失了,不能再使用
	fw.write(99); // 这里会报IOException,告诉Stream Closed
}

d.txt里只有ab,没有c

3.4.2.3 一次输出多个字符/字符串

public static void main(String[] args) throws IOException{
	FileWriter fw = new FileWriter("src\\e.txt");
	char[] charArr = {'a', 'b', 'c', 'd', 'e'};
	// 1.void write(char[] cbuf)写入字符数组
	fw.write(charArr); // abcde

	// 2.abstract void write(char[] cbuf, int off, int len)写入字符数组的一部分,从off开始,写len个字符
	fw.write(charArr, 1, 3); // bcd

	// 3.void write(String str) 写入字符串
	fw.write("我是程序员"); // 我是程序员

	// 4.void write(String str, int off, int len) 写入字符串的一部分,从off开始,写入len个字符
	fw.write("我是程序员", 2, 3); //程序员

	fw.close();
}

3.4.2.4 追加写

public static void main(String[] args) throws IOException{
	// 构造方法里的boolean append设置为true,追加写
	FileWriter fw = new FileWriter("src\\f.txt", true); 
	for(int i=0; i < 10; i++) {
		fw.write("helloworld");
	}
	fw.close();
}

运行一次,f.txt文件内容是10次不带换行的helloworld
运行2次,f.txt文件内容是20次不带换行的helloworld

3.4.2.5 换行写

换行符号

  • windows换行是\r\n
  • linux换行是/n
  • mac换行是/r
public static void main(String[] args) throws IOException{
	// 构造方法里的boolean append设置为true,追加写
	FileWriter fw = new FileWriter("src\\f.txt", true); 
	for(int i=0; i < 10; i++) {
		fw.write("helloworld" + "\r\n");
	}
	fw.close();
}

运行一次,f.txt文件里的是10个带换行的helloworld
运行2次,f.txt文件里的是20个带换行的helloworld,说明是追加写的

4. 流的异常

4.1 jdk1.7之前处理流异常的方式

  • 格式
    try{
    可能产生异常的代码
    }catch(异常类 变量名){
    异常的处理逻辑
    }finally{
    一定要执行的代码,比如释放资源
public static void main(String[] args) {
	// 提高fw变量的作用域,让finally里可以使用fw
	FileWriter fw = null;
	try{
		// 可能会产生异常的代码
	    fw = new FileWriter("src\\c.txt", true);
		for(int i=0; i < 10; i++) {
			fw.write("helloworld" + "\r\n");
		}
		// fw.close(); 关闭资源的代码放在这里有问题,因为一旦上面的代码执行有异常,这里就不会执行
	} catch (IOException e) {
		// 异常的处理
		System.out.println(e);
	} finally {
		// 一定要执行的代码,比如释放资源
		// 如果最上面的fw创建的时候,没有创建成功,fw是null,下面的fw.close()还会抛出空指针异常,所以要先if判断下
		if(fw != null) {
			try {
				// fw.close也会抛出IOException异常,所以要在这里try catch
				fw.close()
		    } catch(IOException e) {
				Syste.out.println(e);
			}
		}
	}
}

以上的实现非常的复杂

4.2 jdk1.7之后处理流异常的方式

  • JDK7的新特性
    在try的后面可以增加一个(),这个括号中可以定义流对象,流对象的作用域是try中有效,try中代码执行完毕,会自动把流对象释放,不用写finally的部分来释放流对象了。

  • 格式
    try(定义流对象1;定义流对象2…){
    可能产生异常的代码
    }catch(异常类 变量名){
    异常的处理逻辑

  • 以复制图片为例

public static void main(String[] args) {
	// 在try的()里定义流对象
	try(
		FileInputStream fis = new FileInputStream(c:\\1.jpg);
		FileOutputStream fos = new FileOutputStream(d:\\2.jpg);
	){
		// 可能会产生异常的代码
	   int len = 0;
	   while((len = fis.read()) != -1) {
			fos.write(len);
		}
	} catch (IOException e) {
		// 异常的处理
		System.out.println(e);
	}
}

5. 缓冲流

  • 之前的字节流和字符流都是IO流的入门,还有几种强大的流

    • 能够高效读写的缓冲流
    • 能够转换编码的转换流
    • 能够持久化对象的序列化流
  • 缓冲流有4种

    • 字节缓冲流:
      • BufferedInputStream缓冲字节输入流
      • BufferedOutputStream缓冲字节输出流
    • 字符缓冲流:
      • BufferedReader缓冲字符输入流
      • BufferedWriter缓冲字符输出流
  • 缓冲流的原理:创建流对象时,会创建一个内置的缓冲区数组,通过缓冲区读写,减少系统的IO次数,从而提高读写的效率。

5.1 缓冲的字节输出流BufferedOutputStream类

  • 继承关系:java.io.BufferedOutputStream extends OutputStream
  • 继承自父类OutputStream的共性成员方法:
    • 方法1.public void close()
      关闭字节输出流,并释放与此流相关的任何系统资源。
    • 方法2.public void flush()
      刷新字节输出流,并强制缓冲中的字节被写出。
    • 方法3.public void write(byte[] b)
      将字节数组b,写入此输出流
    • 方法4.public void write(byte[] b, int off, int len)
      将字节数组b,从off开始的,len长度的字节,写入此输出流。
    • 方法5.public abstract void write(int b)
      将指定的字节写入此输出流

5.1.1 构造方法

  • 构造方法1. BufferedOutputStream(OutputStream out)

    • 功能:创建缓冲的字节输出流,以将数据写入指定的底层输出流
  • 构造方法2.BufferedOutputStream(OutputStream out, int size)

    • 创建缓冲的字节输出流,以将size大小的缓冲区数据,写入指定的底层输出流
  • 以上两个构造方法的参数:

    • OutputStream out:字节输出流。
      • 实际使用时,可以传递FileOutputStream对象,缓冲流会给FileOutputStream增加一个缓冲区,提高FileOutputStream的写入效率
    • int size:用于指定缓冲流内部的缓冲区大小,不指定的话,就是默认的大小。

5.1.2 BufferedOutputStream的使用

  • 使用步骤
    • 创建一个FileOutputStream对象,构造方法中绑定要输出的目的地
    • 创建BufferedOutputStream对象,构造方法中传递FileOutputStream对象,提高FIleOutputStream的效率
    • 使用BufferedOutputStream对象的write方法,把数据写入到缓冲区中
    • 使用BufferedOutputStream对象的flush方法,把缓冲区中的数据,刷新到文件中
    • 释放资源
      • 会先自动调用flush方法刷新数据,所以第4步可以省略
      • 关闭缓冲流就行了,不用手动关闭基本的字节流,因为关闭缓冲流的时候,自动地关闭字节流

5.1.2.1 一次输出多个字节

public static void main(String[] args) throws IOException{
	// 1.创建一个FileOutputStream对象,构造方法中绑定要输出的目的地
	FileOutputStream fos = new FileOutputStream("src\\a.txt");
	// 2.创建BufferedOutputStream对象,构造方法中传递FileOutputStream对象,提高FIleOutputStream的效率
	BufferedOutputStream bos = new BufferedOutputStream(fos);
	// 3.使用BufferedOutputStream对象的write方法,把数据写入到缓冲区中
	bos.write("写入数据到缓冲区".getBytes());
	// 4.使用BufferedOutputStream对象的flush方法,把缓冲区中的数据,刷新到文件中
	bos.flush();
	// 5.释放资源(会先自动调用flush方法刷新数据,所以第4步可以省略)
	bos.close();
}

5.2 缓冲的字节输入流BufferedInputStream

  • 继承关系:java.io.BufferedInputStream extends InputStream
  • 继承自父类的成员方法
    • 方法1.public int read(),从输入流中,读取数据的下一个字节
    • 方法2.public int read(byte[] b),从输入流中,读取一定数量的字节,并存储在缓冲区的字节数组b中
    • 方法3.public void close(), 关闭输入流,并释放相关系统资源

5.2.1 构造方法

  • 构造方法1.BufferedInputStream(InputStream in)
    • 创建一个BufferedInputStream对象,并保存其参数,即输入流in,以便后来使用
  • 构造方法2.BufferedInputStream(InputStream in, int size)
    • 创建一个BufferedInputStream对象,缓冲区的大小是size,同时有个参数是输入流in,以便后来使用

以上两个方法的参数:

  • InputStream in:字节输入流
    • 实际使用的时候,可以传一个FileInputStream对象,缓冲流会给FileInputStream对象增加一个缓冲区,提高FileInputStream对象的读效率
  • int size: 缓冲流内部的缓冲区的大小,不指定的话,就是默认大小

5.2.2 BufferedInputStream的使用

使用步骤

  • 创建FileInputStream对象,构造方法中绑定要读取的数据源
  • 创建BufferedInputStream对象,构造方法中传递FileInputStream对象,提高FileInputStream对象的读取效率
  • 使用BufferedInputStream对象的read方法,读取文件中的内容
  • 释放资源
    • 关闭缓冲流就行了,不用手动关闭基本的字节流,因为关闭缓冲流的时候,自动地关闭字节流

5.2.2.1 一次读取多个字节

此时a.txt内容是abcde,打印出来的是abced

public static void main(String[] args) throws IOException{
	// 1.创建FileInputStream对象,构造方法中绑定要读取的数据源
	FileInputStream fis = new FileInputStream("src\\a.txt");
	// 2.创建BufferedInputStream对象,构造方法中传递FileInputStream对象,提高FileInputStream对象的读取效率
	BufferedInputStream bis = new BufferedInputStream(fis);
	// 3.使用BufferedInputStream对象的read方法,读取文件中的内容
	byte[] arr = new byte[1024]; // 存储每次读取到的数据
	int len = 0; //每次读取的有效字节个数
	while((len = bis.read(arr)) != -1){
		System.out.println(new String(bytes, 0, len));
	}
	// 5. 释放资源(关闭缓冲流就行了,不用手动关闭基本的字节流,因为关闭缓冲流的时候,自动地关闭字节流)
	bis.close();
}
  • 疑问:BufferedInputStream构造方法中的size与new byte[1024]是啥关系

你可能感兴趣的:(java,java)