Java基础系列35-IO流

x33g5p2x  于2021-12-18 转载在 其他  
字(31.7k)|赞(0)|评价(0)|浏览(438)

一.IO流概述

什么是IO流,其作用为?

  1. I ------ Input --> 输入 --> 读取
  2. O — Output --> 输出 --> 写出

常见应用:

  1. 文件复制
  2. 文件上传
  3. 文件下载

IO流的分类汇总:

  1. 字节流 (支持图片等格式的文件,直接用记事本打开文件是看不懂的乱码
  2. 字节缓冲区流 (字符缓冲输入流 b、c方式 可以读取中文,不会出现乱码)
  3. 转换流 (字符流) = 字节流 + 编码表
  4. 字符流 (字符流数据通过Windows自带的记事本软件打开是可以读懂里面内容的)
  5. 字符缓冲区流
  • 字节流 (支持图片等格式的文件,直接用记事本打开文件是看不懂的乱码)
    a1 InputStream 字节流输入超类
      a1.1 FileInputStream 不建议读取中文,中文一般2-4字节,此方式会从中间截断,导致乱码
      
//指定输入流
//FileInputStream fis = new FileInputStream(new File("fis.txt"));//效果同下行代码
FileInputStream fis = new FileInputStream("fis.txt");

//a.一次读取一个字符
  int by;
//如果fis.read不等于-1说明还有数据,则继续读文件
  while ((by = fis.read()) != -1) {
  	System.out.print((char) by);//打印结果
  }

  //b.一次读取一个字符数组
  byte[] bys = new byte[1024];//每次读取最大bys长度个字节存入数组,长度建议1024或1024的整数倍
  int len;//代表有效个数
  //将数据读取到数组中, 并用len记录读取到的有效字节个数
  //fis.read(bys)=>读取一次bys长度内容写入bys(每次覆盖之前内容),如果剩余文件内容不足以写满bys,(长度1024,剩余1000字节)则bys中最后字节(24字节)保持上一次写入的内容 => 所以每次要截取new String(bys,0,len),避免读到上一次剩余的内容
  while ((len = fis.read(bys)) != -1) {
  	System.out.print(new String(bys, 0, len));//打印结果
  }

  //释放资源
  fis.close();

b1 字节缓冲区流  BufferedInputStream 字符缓冲输入流 b、c方式 可以读取中文,不会出现乱码

//字节流一次读写一个数组的速度比一次读写一个字节的速度快很多,这是加入了数组这样的缓冲区效果,java本身在设计的时候,也考虑到了这样的设计思想,所以提供了字节缓冲区流
  
  //Q:为什么字节缓冲流的构造方法需要传入一个In/OutputStream
  //A:字节缓冲区流仅仅提供缓冲区,而真正的底层的读写数据还得需要基本的流对象进行操作。
      
  //指定输入流
  BufferedInputStream bis = new BufferedInputStream(new FileInputStream("name.txt"));
  
  //a.一次读取一个字符(不能读取中文,中文一般2-4字节,一个一个字节读会乱码)
  int by;
  while ((by = bis.read()) != -1) {
      System.out.print((char) by);
  }
  
  //b.一次读取一个字符数组
  byte[] bys = new byte[1024];
  int len;
  while ((len = bis.read(bys)) != -1) {
      System.out.print(new String(bys, 0, len));
  }
  
  //释放资源
  bis.close();

a2 OutPutStream 字节流输出超类
  a2.1 FileOutputStream 不建议读取中文,中文一般2-4字节,此方式会从中间截断,导致乱码

//指定输出流
//FileOutputStream fos = new FileOutputStream(new File("fos.txt"));//效果同下行代码
//FileOutputStream fos = new FileOutputStream("fos.txt" , false);//false表示不追加文件->每次写入覆盖之前的内容
FileOutputStream fos = new FileOutputStream("fos.txt");

FileInputStream fis = new FileInputStream("fis.txt");

//a.一次写入一个字符(不能读取中文,中文一般2-4字节,一个一个字节读会乱码)
int by;
while ((by = fis.read()) != -1) {
	System.out.print((char) by);//打印结果
}

//b.一次写入一个字符数组
byte[] bytes = new byte[1024];
int len;
while ((len = fis.read(bytes)) != -1) {
	fos.write(bytes, 0, len);
}

//释放资源
fos.close();
fis.close();

b2 字节缓冲区流  BufferedOutputStream 字节缓冲输出流 可以读取中文,不会出现乱码

//字节流一次读写一个数组的速度比一次读写一个字节的速度快很多,这是加入了数组这样的缓冲区效果,java本身在设计的时候,也考虑到了这样的设计思想,所以提供了字节缓冲区流

//Q:为什么字节缓冲流的构造方法需要传入一个In/OutputStream
//A:字节缓冲区流仅仅提供缓冲区,而真正的底层的读写数据还得需要基本的流对象进行操作。

//指定输入流
BufferedInputStream bis = new BufferedInputStream(new FileInputStream("a.txt"));
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("bos.txt"));

//a.一次读取一个字符
int by;
while ((by = bis.read()) != -1) {
    bos.write(by);//写入字符串
    bos.flush();//刷新该流的缓冲,写入文件
}

//b.一次读取一个字符数组
byte[] bytes = new byte[1024];
int len;
while ((len = bis.read(bytes)) != -1) {
    System.out.println(new String(bytes,0,len));
    bos.write(bytes,0,len);//写入字符串
    bos.flush();//刷新该流的缓冲,写入文件
}

//释放资源
bis.close();
bos.close();
  • c 转换流 (字符流) = 字节流 + 编码表
//指定编码为UTF-8的字符输出流
OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("source.txt"),"UTF-8");

//指定编码为UTF-8的字符输入流
InputStreamReader isr = new InputStreamReader(new FileInputStream("target.txt"),"UTF-8");

/* 
常见的编码表:
		ASCII : 美国标准信息交换码, 用一个字节的7位表示数据
		ISO-8859-1 : 欧洲码表, 用一个字节的8位表示数据, 兼容ASCII
		GB2312 : 中文码表的升级版, 融合了更多的中文文字符号, 兼容ASCII
		UTF-8 : 是一种可变长度的字符编码, 用1-3个字节表示数据, 又称为万国码, 兼容ASCII用在网页上可以
				统一页面中的中文简体繁体和其他语言的显示.
*/
  • d字符流 (字符流数据通过Windows自带的记事本软件打开是可以读懂里面内容的)
    d1 Reader 字符流输入超类
      d1.1 InputStreamReader 用 默认/指定 编码读数据
//指定输入流 目标文件和字符集 ,如果不指定字符集则按照默认编码表 (java7=GBK,java8=UTF-8)
InputStreamReader isr = new InputStreamReader(new FileInputStream(""),"GBK");

d1.1.1 FileReader ↓作为BufferedReader构造器的参数使用↓

//转换流的名字比较长,而我们常见的操作都是按照本地默认编码实现的,所以,为了简化我们的书写,转换流InputStreamReader提供了对应的子类InputStreamReader
/*
  (子类)FileWriter:用来写入字符文件的便捷类
  (父类)OutputStreamWriter: FileWriter + 默认编码表(java7=GBK,java8=UTF-8)
 
  (子类)FileReader:用来读取字符文件的便捷类
  (父类)InputStreamReader: FileReader + 默认编码表(java7=GBK,java8=UTF-8)
*/

//封装数据源
FileReader fr = new FileReader("source.txt");
//封装目的地
FileWriter fw = new FileWriter("target.txt");

e1 字符缓冲区流  BufferedReader 最常用

//指定输入流
BufferedReader br = new BufferedReader(new FileReader("source.txt"));
//指定输出流
BufferedWriter bw = new BufferedWriter(new FileWriter("target.txt"));

//a.一次读取一个字符
int ch;
while ((ch = br.read()) != -1) {
    //对应Unicode编码10进制的值 查询工具 => 
    //http://www.mytju.com/classcode/tools/encode_gb2312.asp
	System.out.println(ch);
}

//b.一次读取多个字符【注意 换行符也算字符】
char[] bytes = new char[1024];//一次取出1024个字符
int len;//代表有效个数
while((len = br.read(bytes)) != -1)
{
    System.out.print(new String(chars, 0, len));//注意是print不是println
}

//c.读取一整行字符串【注意 不包括换行符,需要手动newLine()换行】
//String line = br.readLine();
String line;//代表一整行字符串
//按行读取整篇文章的内容
while((line = br.readLine()) != null)
{
    System.out.println(line);
}

//释放资源
br.close();
bw.close();

d2 Writer 字符流输出超类
   d2.1 PrintWriter
   d2.2 OutputStreamWriter 用 默认/指定 编码写数据

//指定输出流 目标文件和字符集,如果不指定字符集则按照默认编码表 (java7=GBK,java8=UTF-8)
OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("target.txt"),"GBK");

d2.2.1 FileWriter ↓作为BufferedWriter构造器的参数使用↓

//转换流的名字比较长,而我们常见的操作都是按照本地默认编码实现的,所以,为了简化我们的书写,转换流OutputStreamWriter提供了对应的子类FileWriter
/*
  (子类)FileWriter:用来写入字符文件的便捷类
  (父类)OutputStreamWriter: FileWriter + 默认编码表(java7=GBK,java8=UTF-8)
 
  (子类)FileReader:用来读取字符文件的便捷类
  (父类)InputStreamReader: FileReader + 默认编码表(java7=GBK,java8=UTF-8)
*/

//封装数据源
FileReader fr = new FileReader("source.txt");
//封装目的地
FileWriter fw = new FileWriter("target.txt");

e2 字符缓冲区流 BufferedWriter 最常用

//指定输入流
BufferedReader br = new BufferedReader(new FileReader("br.txt"));
//指定输出流
BufferedWriter bw = new BufferedWriter(new FileWriter("bw.txt"));

//a.一次读取一个字符
int ch;
while ((ch = br.read()) != -1) {
	bw.write(ch);//写入字符串
	bw.flush();//刷新该流的缓冲,写入文件
}

//b.一次读取多个字符【注意 换行符也算字符】
char[] bytes = new char[1024];//一次取出1024个字符
int len;//代表有效个数
while((len = br.read(bytes)) != -1)
{
    bw.write(new String(bytes, 0 , len));//写入字符串
    bw.newLine();
    bw.flush();//刷新该流的缓冲,写入文件
}

//c.读取一整行字符串【注意 不包括换行符,需要手动newLine()换行】
//String line = br.readLine();
String line;//代表一整行字符串
//按行读取整篇文章的内容
while((line = br.readLine()) != null)
{
    bw.write(line);//写入字符串
    bw.newLine();
    bw.flush();//刷新该流的缓冲,写入文件
}

//释放资源
br.close();
bw.close();

二. 字节流

a.字节流 (支持图片等格式的文件,直接用记事本打开文件是看不懂的乱码)
 InputStream 字节流输入超类
 FileInputStream 不建议读取中文,中文一般2-4字节,此方式会从中间截断,导致乱码
  b. 字节缓冲区流  BufferedInputStream 字符缓冲输入流 b、c方式 可以读取中文,不会出现乱码
 OutPutStream 字节流输出超类
  FileOutputStream 不建议读取中文,中文一般2-4字节,此方式会从中间截断,导致乱码
   b 字节缓冲区流  BufferedOutputStream 字节缓冲输出流 可以读取中文,不会出现乱码

2.1 FOS与FIS

简写说明:

  • FIS = FileInPutStream
  • FOS = FileOutPutStream

字节流写数据:

  • OutputStream:此抽象类是表示输出字节流的所有类的超类
  • FileOutputStream:文件输出流是用于将数据写入 File

字符流读数据:

  • OutputStream:此抽象类是表示输出字节流的所有类的超类
  • FileOutputStream:文件输出流是用于将数据写入 File

构造方法:

//创建一个向具有指定名称的文件中写入数据的输出文件流。
FileOutputStream(String name)

字节流写数据的步骤:

  1. 创建字节输出流对象
  2. 调用写数据的方法 write(int);
  3. 释放资源 close()

代码:

package Java_study;

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

/**
 * 
 * @author  只是甲
 * @date    2021-07-19
 * @remark  字节流FileOutputStream写出数据
 *
 */

public class io1 {
	public static void main(String[] args) throws IOException {
		//创建直接输出流对象  throws FileNotFoundException
		FileOutputStream fos = new FileOutputStream("a.txt");
		
		//throws IOException (包含FileNotFoundException)
		System.out.println("fos.write");
		
		fos.write(65);//A
		fos.write(66);//B
		System.out.println("写入结束");
		//关闭IO流,回收系统资源
		fos.close();
	}

}

2.2 FOS写数据的三种方式

方法摘要:

//a.写出数据的三个方法
  //一次写一个字节
  public void write(int b)
  
  //一次写一个字节数组
  public void write(byte[] b)
  
  //一次写一个字节数组的一部分
  public void write(byte[] b,int off,int len)
  
  //b.String类中的方法
  //将字符串转换为字节数组
  byte[] getBytes()

代码:

package Java_study;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;

/**
 * 
 * @author 只是甲
 * @date   2021-07-19
 * @remark 字节流FileOutputStream写数据的三种方式
 * A.构造方法的三种方式:
 *      1. FileOutputStream(String name)
 *      2. FileOutPutStream(File file)
 *      3. FileOutPutStream(new File(xxx))
 *      4. FileOutputStream(xxx,true) //true=追加数据,默认为false,清空后重新写入数据
 *
 * B.写数据的三种方式: throws IOException
 *      1. FileOutputStream.write(int b):一次写一个字节
 *      2. FileOutputStream.write(byte[] b):一次写一个字节数组
 *      3. FileOutputStream.write(byte[] b,inf off,int len):一次写一个字节数组的一部分
 *
 * 字节流写数据的步骤:
 *      a: 创建字节输出流对象 new FileOutPutStream(String name)
 *      b: 调用写数据的方法
 *      c: 释放资源 fos.close()
 *
 */

public class io2 {
	public static void main(String[] args) throws IOException{
		//A1. FileOutputStream构造方式1 FileOutputStream(String name)
		FileOutputStream fos1 = new FileOutputStream("b.txt");
		
		//A2. FileOutputStream构造方式2 FileOutPutStream(File file)
		File f2 = new File("c.txt");
		FileOutputStream fos2 = new FileOutputStream(f2);
		
		//A3. FileOutPutStream构造方式3 FileOutPutStream(new File(xxx))
		FileOutputStream fos3 = new FileOutputStream(new File("d.txt"));
		
		//B1. 写入数据方式1 FileOutputStream.write(int b):一次写一个字节
        //throws IOException
		fos1.write(65);
		
		//B2. 写入数据方式2 FileOutputStream.write(byte[] b):一次写一个字节数组
		byte[] bys = {65, 66, 67, 68, 69};
		fos2.write(bys);
		
		//B3. 写入数据方式3 FileOutputStream.write(byte[] b,inf off,int len):一次写一个字节数组的一部分
		fos3.write("ABCDE".getBytes(), 0, 3);
		
		File path = new File("");
		System.out.println("文件路径:" + path.getAbsolutePath());
		
		//释放资源
		fos1.close();
		fos2.close();
		fos3.close();
	}

}

2.3 FOS如何实现换行和追加写数据

FIS = FileInPutStream

不同的操作系统,针对换行的符号识别是不一样的:

  • windows:\r\n
  • linux:\n
  • mac:\r

如何实现数据的追加写入?

  • 用构造方法带第二个参数是true的情况即可

代码:

package Java_study;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;

/**
 * 
 * @author 只是甲
 * @date   2021-07-19
 * @remark 字节流FileOutputStream写数据换行、追加
 * 
 */

public class io3 {
	public static void main(String[] args) throws IOException{
		//先清空文件
		FileOutputStream clear = new FileOutputStream("b.txt");
		clear.write("".getBytes());
		
		//如果第二个参数为 true,则将字节写入文件末尾处,而不是写入文件开始处(默认为false)
		FileOutputStream fos = new FileOutputStream("b.txt", true);
		
		//玄幻写入三次hello和换行
		for (int i = 0; i < 3; i++) {
			fos.write("hello".getBytes());
			fos.write("\r\n".getBytes());
		}
		
		System.out.println("文件路径为:" + new File("").getAbsolutePath());
		
		//释放资源
		fos.close();
	}

}

2.4 FOS写数据加入异常处理

FIS = FileInPutStream

try…catch.finally

//格式
  try{
  	可能发生问题的代码
  }catch(){
  	处理异常代码
  }finally{
  	一定会被执行的代码.   // 通常用于释放资源, 做善后的动作
  }

代码:

package Java_study;

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

/**
 * 
 * @author 只是甲
 * @date   2021-07-19
 * @remark 字节流FileOutputStream写数据加入异常处理
 * 
 */

public class io4 {
	public static void main(String[] args) {
		//fos放在外面是为了在finally中可以关闭
		FileOutputStream fos = null;
		
		try {
			fos = new FileOutputStream("d.txt");
			fos.write("hello".getBytes());
		} catch (IOException e) {
			e.printStackTrace();
		} finally {
			if (fos != null) {
				//释放资源
				try {
					fos.close();
				} catch (IOException e) {
					System.out.println("fos关闭失败");
				}
			}
		}
	}

}

2.5 FIS读数据方式1一次读取一个字节

FIS = FileInPutStream

字节流读数据的步骤:

  • A : 创建字节输入流对象
  • B : 调用读数据的方法
  • C : 释放资源

代码:

package Java_study;

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

/**
 * 
 * @author 只是甲
 * @remark 字节流FileInputStream读数据方式1
 * 
 */

public class io5 {
	public static void main(String[] args) throws IOException {
		System.out.println("读取数据方式1: 一次读取一个字节");
		//创建字节输入流对象
		FileInputStream fis1 = new FileInputStream("b.txt");//刚才在b.txt写入了3行hello
		
		for (int i = 0; i < 20; i++) {
			System.out.println(fis1.read());
		}
		
		System.out.println("改进为循环方式打印");
		FileInputStream fis2 = new FileInputStream("b.txt");//刚才在b.txt写入了3行helloWorld
		int by;
		//如果fis.read不等于-1说明还有数据,则继续读文件
		while ((by = fis2.read()) != -1) {
			System.out.println((char) by);
		}
	}

}

测试记录:

读取数据方式1: 一次读取一个字节
104
101
108
108
111
13
10
104
101
108
108
111
13
10
104
101
108
108
111
13
改进为循环方式打印
h
e
l
l
o



h
e
l
l
o



h
e
l
l
o

2.6 FIS读数据方式2一次读取一个字节数组

方法摘要:

/*从此输入流中将最多 b.length 个字节的数据读入一个 byte 数组中
  返回值是读入缓冲区的字节总数,也就是实际的读取个数
  如果因为已经到达文件末尾而没有更多的数据,则返回 -1。*/
  public int read(byte[] b):

代码:

package Java_study;

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

/**
 * 
 * @author 只是甲
 * @remark FileInputStream读数据方式2
 * 
 */

public class io6 {
	public static void main(String[] args) throws IOException{
		System.out.println("----读取数据方式2: 一次读取一个直接数组-----");
		FileInputStream fis1 = new FileInputStream("b.txt");//刚才在b.txt写入了3行hello
		//标准代码字符缓冲区bys1长度应为1024或1024的整数倍,这里先用8演示读取的原理
		byte[] bys1 = new byte[8];
		//第一次读取
		int len1 = fis1.read(bys1);
		System.out.println(len1);//8
		
		System.out.println("第一次读取: " + new String(bys1));
		//第二次读取
		len1 = fis1.read(bys1);
		System.out.println(len1);//8
		/*
        第二次读取: ello
        he
        */
		System.out.println("第二次读取: " + new String(bys1));
		// 第三次读取
		len1 = fis1.read(bys1);
		System.out.println(len1);//5 => 只读取了5个字符,就返回了5
		
		/*
        第三次读取: llo

        he
        */
        /* 解析:
          文件内容为
          hello\r\n
          hello\r\n
          hello\r\n

          第一次读取8个字节为 [h] [e] [l] [l] [o] [\r][\n] [h]
          第二次读取8个字节为 [e] [l] [l] [o] [\r][\n] [h] [e]
          第三次读取8个字节为 [l] [l] [o] [\r][\n]
          但数组写入方式是每次覆盖前一次的,所以第二次写入的还保留着
          bys[5] = \r
          bys[6] = h
          bys[7] = e
          所以第三次数组内容为  [l] [l] [o] [\r][\n][\n] [h] [e]
          所以才会打印出以上内容
         */
		System.out.println("第三次读取: " + new String(bys1));
		//第四次读取
		len1 = fis1.read(bys1);
		System.out.println(len1);//-1 => 表示没有数据了
		
		
		
		System.out.println("-----改进为循环读取文件-----");
		//创建字节流输入对象
		FileInputStream fis2 = new FileInputStream("b.txt");
		byte[] bys2 = new byte[1024];//1024或者1024的整数倍
		
		int len2;
		//将数据读取到数组中, 并用len记录读取到的有效字节个数
		while ((len2 = fis2.read(bys2)) != -1 ) {
			//只读取到len的长度,否则会出现
			System.out.print(new String(bys2, 0, len2));
		}
		System.out.println("\r\n读取结束");
		fis2.close();

	}

}

测试记录:

----读取数据方式2: 一次读取一个直接数组-----
8
第一次读取: hello
h
8
第二次读取: ello
he
5
第三次读取: llo

he
-1
-----改进为循环读取文件-----
hello
hello
hello

读取结束

2.7 字节流练习之复制文本文件

需求:

  • 拷贝文本文件

分析:

  • 第一步: 创建输入输出流对象关联数据源和数据目的
  • 第二步: 定义字节数组,为了提高效率
  • 第三步: 将数据通过while循环不断读取到字节数组中
  • 第四步: 将数据从字节数组中取出并写出
  • 第五步: 释放资源

待拷贝的文本文件(d:\窗里窗外.txt):

《窗里窗外》是林青霞近5年所写的46篇散文的结集,虽是旧作,但该书记录她19岁以《窗外》成名后多方面的人生经历,字字真实深刻。《窗里窗外》共分为六个章节:“戏”里说的是她的出道故事、拍戏的甘苦、对于作品的内心话;“亲”谈她的家人亲情;“友”则书写她与挚友的交往,细谈她与三毛、黄霑、张国荣、龙应台、琼瑶、徐克等人的往来互动;“趣”是她的生活记趣,有旅行见闻,也有她与影迷的邂逅;“缘”则书写她一生难忘的相遇,像是和记者的友谊,和季羡林的会面之缘;“悟”里记录了她对人生的体悟和感动,以及她向圣严法师求道的故事。此外,书中还完整收录了林青霞一些未公开的照片。
第1节:人生小语(1)…

代码:

package Java_study;

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

/**
 * 
 * @author 只是甲
 * @remark 直接流复习之复制文本文件
 * 
 */

public class io7 {
	public static void main(String[] args) throws IOException{
		//封装数据源
		FileInputStream fis = new FileInputStream("d:" + File.separator + "窗里窗外.txt");
		//封装目的地(目标文件,追加true清空重写false)
		FileOutputStream fos = new FileOutputStream("d:" + File.separator + "杜兰特.txt", false);
		
		//a.一次写入一个字符
		int by;
		while ((by = fis.read()) != -1 ) {
			fos.write(by);
		}
		
		//b.一次写入一个字符数组
		byte[] bys = new byte[1024];
		int len;
		while ((len = fis.read(bys)) != -1 ) {
			fos.write(bys, 0, len);
		}
		
		//释放资源
		fos.close();
		fis.close();
	}

}

2.8 字节流练习之复制图片

思路:

  • 同理2.7案例

代码:

package Java_study;

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

/**
 * 
 * @author  只是甲
 * @date    2021-07-20
 * @remark  直接流复习之复制文本文件
 * 
 */

public class io8 {
	public static void main(String[] args) throws IOException{
		//封装数据源
		FileInputStream fis1 = new FileInputStream("d:" + File.separator + "字节流.bmp");
		FileInputStream fis2 = new FileInputStream("d:" + File.separator + "字节流.bmp");
	    //封装目的地
		FileOutputStream fos1 = new FileOutputStream("d:" + File.separator + "copy1.bmp");
		FileOutputStream fos2 = new FileOutputStream("d:" + File.separator + "copy2.bmp");
		
		//读取数据
		int by;
		//方式1: 一次读取一个直接
		//如果fis.read不等于-1说明还有数据,则继续读文件
		while ((by = fis1.read()) != -1 ) {
			fos1.write(by);
		}
		
		//方式2: 一次读取一个字节数组
		byte[] bys = new byte[1024];
		int len;
		while ((len = fis2.read(bys)) != -1) {
			fos2.write(bys, 0, len);
		}
		
		//释放资源
		fis1.close();
		fis2.close();
		fos1.close();
		fos2.close();
	}

}

三. 字节缓冲区流

a.字节流 (支持图片等格式的文件,直接用记事本打开文件是看不懂的乱码)

  • InputStream 字节流输入超类
  • FileInputStream 不建议读取中文,中文一般2-4字节,此方式会从中间截断,导致乱码
      b. 字节缓冲区流  BufferedInputStream 字符缓冲输入流 b、c方式 可以读取中文,不会出现乱码
  • OutPutStream 字节流输出超类
      FileOutputStream 不建议读取中文,中文一般2-4字节,此方式会从中间截断,导致乱码
        b 字节缓冲区流  BufferedOutputStream 字节缓冲输出流 可以读取中文,不会出现乱码

3.1 概述

作用:

  • 字节流一次读写一个数组的速度比一次读写一个字节的速度快很多,这是加入了数组这样的缓冲区效果
  • java本身在设计的时候,也考虑到了这样的设计思想,所以提供了字节缓冲区流

字节缓冲流:

  • BufferedOutputStream : 字节缓冲输出流
  • BufferedInputStream : 字节缓冲输入流

为什么字节缓冲流的构造方法需要传入一个OutputStream:

  • 字节缓冲区流仅仅提供缓冲区,而真正的底层的读写数据还得需要基本的流对象进行操作

代码:

package Java_study;

import java.io.*;

/**
 * 
 * @author  只是甲
 * @date    2021-07-20
 * @remark  字节缓冲流
 * 
 */

public class io9 {
	public static void main(String[] args) throws IOException{
		//指定字符缓冲输出流

        // FileOutputStream fos = new FileOutputStream("a.txt");
        // BufferedOutputStream bos = new BufferedOutputStream(fos);
        // 上面的两句等价于下面的这一句
		BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("a.txt"));
		
		byte[] bytes = "hello".getBytes();
		bos.write(bytes);
		bos.close();
		
		//指定字符缓冲输入流
		BufferedInputStream bis = new BufferedInputStream(new FileInputStream("a.txt"));
		//方式1:  一次读取一个字节
		int by;
		while ((by = bis.read()) != -1) {
			System.out.println((char) (by));
		}
		
		//方式2: 一次读取一个直接数组
		byte[] bys = new byte[1024];
		int len;
		while ((len = bis.read(bys)) != -1) {
			System.out.println(new String(bys, 0, len));
		}
		
		//释放资源
		bos.close();
		bis.close();
	}
}

3.2 四种方式复制图片效率测试

方法摘要:

//返回以毫秒为单位的当前时间。
public static long currentTimeMillis()

代码:

package Java_study;

import java.io.*;

/**
 * @author GroupiesM
 * @date 2021/04/27
 * @introduction 字节流&字节缓冲区流四种方式复制AVI并测试效率 
 *
 * 源文件: d:\\复制视频.avi
 * 大小: 34904KB
 *
 * method1: 基本字节流,一次读取一个字节    => method1.avi => 198001毫秒
 * method2: 基本字节流,一次读写一个字节数组 => method2.avi => 278毫秒
 * method3: 缓冲字节流,一次读取一个字节    => method3.avi => 384毫秒
 * method4: 缓冲字节流,一次读写一个字节数组 => method4.avi => 190毫秒
 *
 * 结论: method4 > method2 > method3 > method1
 */
public class io10 {
    public static void main(String[] args) throws IOException {
        //记录method1时间
        long start1 = System.currentTimeMillis();
        method1();//method1共耗时198001毫秒
        long end1 = System.currentTimeMillis();
        System.out.println("method1共耗时" + (end1 - start1) + "毫秒");

        //记录method2时间
        long start2 = System.currentTimeMillis();
        method2();//method2共耗时278毫秒
        long end2 = System.currentTimeMillis();
        System.out.println("method2共耗时" + (end2 - start2) + "毫秒");

        //记录method3时间
        long start3 = System.currentTimeMillis();
        method3();//method3共耗时384毫秒
        long end3 = System.currentTimeMillis();
        System.out.println("method3共耗时" + (end3 - start3) + "毫秒");

        //记录method4时间
        long start4 = System.currentTimeMillis();
        method4();//method4共耗时190毫秒
        long end4 = System.currentTimeMillis();
        System.out.println("method4共耗时" + (end4 - start4) + "毫秒");
    }

    //method1: 基本字节流,一次读取一个字节    => method1.avi
    private static void method1() throws IOException {
        //封装数据源
        FileInputStream fis = new FileInputStream("d:\\复制视频.avi");
        //封装目的地
        FileOutputStream fos = new FileOutputStream("d:\\method1.avi");

        int by;
        while ((by = fis.read()) != -1) {
            fos.write(by);
        }
        //释放资源
        fis.close();
        fos.close();
    }

    //method2: 基本字节流,一次读写一个字节数组 => method2.avi
    private static void method2() throws IOException {
        //封装数据源
        FileInputStream fis = new FileInputStream("d:\\复制视频.avi");
        //封装目的地
        FileOutputStream fos = new FileOutputStream("d:\\method2.avi");

        byte[] bys = new byte[1024];
        int len;
        while ((len = fis.read(bys)) != -1) {
            fos.write(bys, 0, len);
        }
        //释放资源
        fis.close();
        fos.close();
    }

    //method3: 缓冲字节流,一次读取一个字节    => method3.avi
    private static void method3() throws IOException {
        //封装数据源
        BufferedInputStream bis = new BufferedInputStream(new FileInputStream("d:\\复制视频.avi"));
        //封装目的地
        BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("d:\\method3.avi"));

        int by;
        while ((by = bis.read()) != -1) {
            bos.write(by);
        }
        //释放资源
        bis.close();
        bos.close();
    }

    //method4: 缓冲字节流,一次读写一个字节数组 => method4.avi
    private static void method4() throws IOException {
        //封装数据源
        BufferedInputStream bis = new BufferedInputStream(new FileInputStream("d:\\复制视频.avi"));
        //封装目的地
        BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("d:\\method4.avi"));

        byte[] bys = new byte[1024];
        int len;
        while ((len = bis.read(bys)) != -1) {
            bos.write(bys, 0, len);
        }
        //释放资源
        bis.close();
        bos.close();
    }
}

四. 转换流

c. 转换流 (字符流) = 字节流 + 编码表

转换流出现的原因:

  • 由于字节流操作中文不是特别方便,所以,java就提供了转换流
  • 转换流 = 字节流 + 编码表

字节流读数据可能出现问题:

  • 字节流一次读取一个字节的方式读取带有汉字的文件是有问题的,因为你读取到一个字节后就转为字符在控制台输出了,而汉字是由2个字节组成的,所以这里会出问题。
  • 文件复制的时候,字节流读取一个字节,写入一个字节,这个没有出现问题,是因为最终底层会根据字节做拼接,得到汉字。
  • 汉字存储的规则
      左边的字节数据肯定是负数,右边的字节数据可能是负数,也可能是正数,大部分情况下是负数。

代码:

package Java_study;

import java.io.FileInputStream;
import java.io.IOException;
import java.util.Arrays;

/**
 * @author 只是甲
 * @date   2021-07-20
 * @remark 转换流出现的原因
 *
 */

public class io11 {
	public static void main(String[] args) throws IOException{
		//基本字节流一次读取一个字节
		FileInputStream fis = new FileInputStream("a.txt");
		
		int by;
		while ((by = fis.read()) != -1 ) {
			System.out.print((char) by);
		}
		//释放资源,刷新缓冲区
		fis.close();
		
		System.out.println("");
		String s = "你";
		byte[] bys = s.getBytes("GBK");
		/*常见的编码表测试 中文"你"转换为字节数组
        UTF-8          => [-28, -67, -96]  => JDK8中不指定编码则默认为 UTF-8
        GBK/GB2312     => [-60, -29]       => JDK7中不指定编码则默认为 GBK/GB2312
        ISO-8859-1     => [63]
        ASCII          => [63]
        UNICODE/UTF-16 => [-2, -1, 79, 96] => UNICODE 默认使用的是UTF-16的实现方式
     */
		
		System.out.println(Arrays.toString(bys));
	}

}

测试记录:

hi
????
[-60, -29]

4.1 什么是编码表

编码表

  • 由字符及其对应的数据组成的一张表
ASCII
	‘a’	97
	‘A’	65
	‘0’	48

常见的编码表:

  • ASCII : 美国标准信息交换码, 用一个字节的7位表示数据;(ASCII 0-126)
  • ISO-8859-1 : 欧洲码表, 用一个字节的8位表示数据, 兼容ASCII;
  • GB2312/GBK : 中文码表的升级版, 融合了更多的中文文字符号, 兼容ASCII;
  • UTF-8 : 使用一到四个字节来编码一个码点。, 兼容ASCII;用在网页上可以统一页面中的中文简体繁体和其他语言的显示;
  • UNICODE : 为世界上所有字符都分配了一个唯一的数字编号,又称万国码;有多种实现方案(UTF-32 、UTF-16 、UTF-8)。

乱码问题:

  • 针对同一个数据, 采用的编码和解码不一致导致

代码:

package Java_study;

/**
 * 
 * @author  只是甲
 * @date    2021-07-21
 * @remark  遍历编码表_int转char
 *
 */

public class io12 {
	public static void main(String[] args) {
		int count = 0;
		for (int i = 8; i < 127; i++) {
			//每打印5个就换行一次
			if (count == 5) {
				System.out.println("");
				count = 0;
			}
			//13换行一次,否则CR归位键会回到行最左侧位置,导致控制台打印的10-12被吞掉
			if (i == 13) {
				System.out.println("");
			}
			char ch = (char) i;
			System.out.print("[" + ch + " = " + i + "]\t");
			count++;
		}
	}

}

测试记录:

[ = 8]	[	 = 9]	[
 = 10]	[ = 11]	[ = 12]	

[
 = 13]	[ = 14]	[ = 15]	[ = 16]	[ = 17]	
[ = 18]	[ = 19]	[ = 20]	[ = 21]	[ = 22]	
[ = 23]	[ = 24]	[ = 25]	[ = 26]	[ = 27]	
[ = 28]	[ = 29]	[ = 30]	[ = 31]	[  = 32]	
[! = 33]	[" = 34]	[# = 35]	[$ = 36]	[% = 37]	
[& = 38]	[' = 39]	[( = 40]	[) = 41]	[* = 42]	
[+ = 43]	[, = 44]	[- = 45]	[. = 46]	[/ = 47]	
[0 = 48]	[1 = 49]	[2 = 50]	[3 = 51]	[4 = 52]	
[5 = 53]	[6 = 54]	[7 = 55]	[8 = 56]	[9 = 57]	
[: = 58]	[; = 59]	[< = 60]	[= = 61]	[> = 62]	
[? = 63]	[@ = 64]	[A = 65]	[B = 66]	[C = 67]	
[D = 68]	[E = 69]	[F = 70]	[G = 71]	[H = 72]	
[I = 73]	[J = 74]	[K = 75]	[L = 76]	[M = 77]	
[N = 78]	[O = 79]	[P = 80]	[Q = 81]	[R = 82]	
[S = 83]	[T = 84]	[U = 85]	[V = 86]	[W = 87]	
[X = 88]	[Y = 89]	[Z = 90]	[[ = 91]	[\ = 92]	
[] = 93]	[^ = 94]	[_ = 95]	[` = 96]	[a = 97]	
[b = 98]	[c = 99]	[d = 100]	[e = 101]	[f = 102]	
[g = 103]	[h = 104]	[i = 105]	[j = 106]	[k = 107]	
[l = 108]	[m = 109]	[n = 110]	[o = 111]	[p = 112]	
[q = 113]	[r = 114]	[s = 115]	[t = 116]	[u = 117]	
[v = 118]	[w = 119]	[x = 120]	[y = 121]	[z = 122]	
[{ = 123]	[| = 124]	[} = 125]	[~ = 126]

4.2 String类的编解码

方法摘要:
编码 : 把看得懂的变成看不懂的

//使用指定的字符集将此 String 编码为 byte 序列,并将结果存储到一个新的 byte 数组中。 
public byte[] getBytes(String charsetName) throws UnsupportedEncodingException

解码 : 把看不懂的变成看得懂的

//通过使用指定的 charset解码指定的 byte 数组,构造一个新的 String。
public String(byte[] bytes, String charsetName)

重点强调: 编码和解码的方式需要一致

常见的编码表测试 中文"你"转换为字节数组
UTF-8 => [-28, -67, -96] => JDK8中不指定编码则默认为 UTF-8
GBK/GB2312 => [-60, -29] => JDK7中不指定编码则默认为 GBK/GB2312
ISO-8859-1 => [63]
ASCII => [63]
UNICODE/UTF-16 => [-2, -1, 79, 96] => UNICODE 默认使用的是UTF-16的实现方式

代码:

package Java_study;

import java.io.UnsupportedEncodingException;
import java.util.Arrays;

/**
 * 
 * @author  只是甲
 * @date    2021-07-21
 * @remark  String 类的编解码
 *
 */

public class io13 {
	public static void main(String[] args) throws UnsupportedEncodingException {
		//定义一个字符串
		String s = "你好";
		
		//a.编码 getBytes => throws UnsupportedEncodingException 不支持的编码异常
		byte[] encodeGBK = s.getBytes();
		System.out.println(Arrays.toString(encodeGBK));//[-60, -29, -70, -61]
		
		byte[] encodeUTF = s.getBytes("UTF-8");
		System.out.println(Arrays.toString(encodeUTF));//[-28, -67, -96, -27, -91, -67]
		
		//b.解码
		byte[] bytesGBK = {-60, -29, -70, -61};
		String decodeGBK = new String(bytesGBK);
		//Java8默认编解码字符集为UTF8 用UTF8解码 GBK编码的字节码文件就会出现乱码
		System.out.println(decodeGBK);
		String decodeGBKAgain = new String(bytesGBK, "GBK");
		//指定用GBK解码
		System.out.println(decodeGBKAgain);//你好
		
		byte[] bytesUTF = {-28, -67, -96, -27, -91, -67};
		//不指定解码字符集,Java8环境默认使用UTF8字符集进行编解码,刚好和字节数组匹配
		String decodeUTF = new String(bytesUTF);
		System.out.println(decodeUTF);//你好
	}

}

测试记录:

[-60, -29, -70, -61]
[-28, -67, -96, -27, -91, -67]
你好
你好
浣犲ソ

4.3 字符流Stream的编解码(写入文件)

OutputStreamWriter 字符输出流

//根据【默认编码】把字节流的数据转换为字符流
 public OutputStreamWriter(OutputStream out)

 //根据【指定编码】把字节流数据转换为字符流
 public OutputStreamWriter(OutputStream out,String charsetName)

InputStreamReader 字符输入流

//用【默认编码】读数据 
 public InputStreamReader(InputStream in)

 //用【指定编码】读数据
 public InputStreamReader(InputStream in,String charsetName)

IDEA设置文件编码

代码:

package Java_study;

import java.io.*;

/**
 * 
 * @author  只是甲
 * @date    2021-07-21
 * @remark  String 类的编解码
 *
 */

public class io14 {
	public static void main(String[] args) throws IOException{
		//指定输出流 目标文件和字符集
		OutputStreamWriter oswGBK = new OutputStreamWriter(new FileOutputStream("osw.txt"), "GBK");
		//指定输入流 目标文件和字符集
		InputStreamReader isrGBK = new InputStreamReader(new FileInputStream("osw.txt"), "GBK");
		InputStreamReader isrUTF = new InputStreamReader(new FileInputStream("osw.txt"), "utf-8");
		
		//调用写数据的方法
		oswGBK.write("你好");
		oswGBK.flush();//刷新缓冲流(执行一次写入)
		
		//用GBK解码GBK文本
		int byGBK;
		while ((byGBK = isrGBK.read()) != -1) {
			System.out.print((char) byGBK);//你好
		}
		
		System.out.println("");
		
		//用UTF-解码GBK文本
		int byUTF;
		while ((byUTF = isrUTF.read()) != -1) {
			System.out.print((char) byUTF);//乱码
		}
		
		//释放资源
		oswGBK.close();
		isrGBK.close();
		isrUTF.close();
	}

}

测试记录:

你好
???

五. 字符流

5.1 复制Java文件

需求:
把项目目录下的io1.java内容复制到项目目录下的Copy.java中

三种方式:

  • InputStreamReader & OutputStreamWriter (字符流方式 见5.1) 379毫秒
  • FileReader & FileWriter (字符流方式 见5.1) 297毫秒
  • BufferedReader & BufferedWriter (字符缓冲区流方式 见6.2) 533毫秒

代码:

package Java_study;

import java.io.*;

/**
 * 
 * @author  只是甲
 * @date    2021-07-21
 * @remark  字符流 复制Java文件
 *
 */

public class io15 {
	public static void main(String[] args) throws IOException {
		long start1 = System.currentTimeMillis();
		for (int i = 0; i <  1000; i++) {
			method1("io1.java", "Copy.java");//method1共耗时2153毫秒
		}
		long end1 = System.currentTimeMillis();
		System.out.println("method1共耗时"+ (end1 - start1) + "毫秒");
		
		long start2 = System.currentTimeMillis();
		for (int i = 0; i < 1000; i++) {
			method1("io1.java", "Copy.java");
		}
		long end2 = System.currentTimeMillis();
		System.out.println("method1共耗时"+ (end2 - start2) + "毫秒");
	}
	
	public static void method1(String source, String target) throws IOException {
		//封装数据源
		InputStreamReader isr = new InputStreamReader(new FileInputStream(source));
		//封装目的地
		OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream(target));
		
		char[] chArr = new char[1024];
		int len;
		while ((len = isr.read(chArr)) != -1) {
			osw.write(chArr, 0, len);
		}
		
		//释放资源
		osw.close();
		isr.close();
	}
	
	public static void method2(String source, String target) throws IOException{
		//封装数据源
		FileReader fr = new FileReader(source);
		//封装目的地
		FileWriter fw = new FileWriter(target);
		
		char[] chArr = new char[1024];
		int len;
		while ((len = fr.read(chArr)) != -1) {
			fw.write(chArr,0,len);
		}
		
		//释放资源
		fw.close();
		fr.close();
	}

}

测试记录:

method1共耗时379毫秒
method1共耗时297毫秒

5.2 OutputStreamWriter写数据的6种方式

方法摘要:

/* @introduction 写一个字符
 * @param c 要写入的字符
 */
public void write(char c)
    
/* @introduction 写一个字符
 * @param c 要写入的字符对应码表中int值
 */
public void write(int c)

/* @introduction 写一个字符数组
 * @param cbuf 要写入的字符数组
 */
public void write(char[] cbuf)

/* @introduction 写一个字符数组的一部分
 * @param cbuf 要写入的字符数组
 * @param off 起始索引
 * @param len 从起始索引起,写入字符数组长度
 */    
public void write(char[] cbuf,int off,int len)
    
/* @introduction 写一个字符串
 * @param str 要写入的字符串
 */    
public void write(String str)
    
/* @introduction 写一个字符串的一部分
 * @param str 要写入的字符串
 * @param off 起始索引
 * @param len 从起始索引起,写入字符串长度
 */    
public void write(String str,int off,int len)

代码:

package Java_study;

import java.io.*;

/**
 * 
 * @author  只是甲
 * @date    2021-07-21
 * @remark  OutputStreamWriter写数据的6种方式
 *
 */

public class io16 {
	public static void main(String[] args) throws IOException{
		//创建字符输出流对象 (同时创建文件)
		OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("target.txt"));
		
		//a. public void write(char c); 写一个字符
		methodA(osw);//写入一个字符a
		osw.write('\r');//换行
		
		//b. public void write(int c); 写一个字符
        methodB(osw);//写入一个字符b
        osw.write('\r');//换行

        //c. public void write(char[] cbuf); 写一个字符数组
        methodC(osw);//写入abcde
        osw.write('\r');//换行

        //d. public void write(char[] cbuf,int off,int len); 写一个字符数组的一部分
        methodD(osw);//写入c
        osw.write('\r');//换行

        //e. public void write(String str); 写一个字符串
        methodE(osw);//写入hello
        osw.write('\r');//换行

        //f. public void write(String str,int off,int len); 写一个字符串的一部分
        methodF(osw);//写入lo
        osw.write('\r');//换行

        //释放资源
        osw.close();//关闭此流,关闭前将缓冲区(内存)字符刷新(flush=>写入硬盘)
        
        
	}
	
	public static void methodA(OutputStreamWriter osw) throws IOException {
		osw.write('a');
	}
	
	public static void methodB(OutputStreamWriter osw) throws IOException {
		osw.write(98);
	}
	
	public static void methodC(OutputStreamWriter osw) throws IOException {
		char[] chs = {'a', 'b', 'c', 'd', 'e'};
		osw.write(chs);//写入 abcde
	}
	
	
	public static void methodD(OutputStreamWriter osw) throws IOException {
		char[] chs = {'a', 'b', 'c', 'd', 'e' };
		 osw.write(chs, 2, 1);//写入bcd
	}
	
	public static void methodE(OutputStreamWriter osw) throws IOException {
        String s = "hello";
        //e. public void write(String str); 写一个字符串
        osw.write(s);//写入hello
        //void flush():刷新该流的缓冲
        //osw.flush();
    }
	
	public static void methodF(OutputStreamWriter osw) throws IOException {
        String s = "hello";
        //f. public void write(String str,int off,int len); 写一个字符串的一部分
        osw.write(s, 3, 2);
        //void flush():刷新该流的缓冲
        //osw.flush();
    }
	

}

5.3 InputStreamReader读数据的2种方式

方法摘要

//一次读取一个字符    
public int read()

//一次读取一个字符数组
public int read(char[] cbuf)

代码:

package Java_study;

import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStreamReader;

/**
 * 
 * @author  只是甲
 * @date    2021-07-21
 * @remark  InputStreamReader读数据的2种方式
 *
 */

public class io17 {
	public static void main(String[] args) throws IOException{
		//创建字符流输入对象
		InputStreamReader isrA = new InputStreamReader(new FileInputStream("a.txt"));
		InputStreamReader isrB = new InputStreamReader(new FileInputStream("a.txt"));
		//a. public int read(); 一次读取一个字符
		methodA(isrA);
		System.out.println("\r\n==========================");
		//b. public int read(char[] cbuf); 一次读取一个字符数组
		methodB(isrB);
		
		//释放资源
		isrA.close();
		isrB.close();
	}
	
	
	public static void methodA(InputStreamReader isr) throws IOException{
		int ch;
		while ((ch = isr.read()) != -1) {
			System.out.print((char) ch);
		}
	}
	
	public static void methodB(InputStreamReader isr) throws IOException{
		char[] chs = new char[1024];
		int len;
		while ((len = isr.read(chs)) != -1) {
			System.out.println(new String(chs, 0, len));
		}
	}

}

六. 字符缓冲区流

6.1 概述

BufferedWriter

  • 将文本写入字符输出流,缓冲各个字符,从而提供单个字符、数组和字符串的高效写入。
  • 可以指定缓冲区的大小,或者接受默认的大小。在大多数情况下,默认值就足够大了。
  • 构造方法
BufferedWriter(Writer out)

BufferedReader

  • 从字符输入流中读取文本,缓冲各个字符,从而实现字符、数组和行的高效读取。
  • 可以指定缓冲区的大小,或者可使用默认的大小。大多数情况下,默认值就足够大了。
  • 构造方法
BufferedReader(Reader in)

代码:

package Java_study;

import java.io.*;

/**
 * 
 * @author  只是甲
 * @date    2021-07-21
 * @remark  字符缓冲区流概述
 *
 */

public class io18 {
	public static void main(String[] args) throws IOException{
		//创建字符缓冲输出流对象
		BufferedWriter bw = new BufferedWriter(new FileWriter("a.txt"));
		//调用写数据的方法
		bw.write("hello");
		//释放资源
		bw.close();
		
		//创建字符缓冲输入流对象
		BufferedReader br1 = new BufferedReader(new FileReader("io1.java"));
		BufferedReader br2 = new BufferedReader(new FileReader("io1.java"));
		
		//方式1:一次读取一个字符
		method1(br1);
		//方式2:一次读取一个字符数组
		method2(br2);
		
		//释放资源
		br1.close();
		br2.close();
   }
	
	public static void method1(BufferedReader br) throws IOException{
		int ch;
		while((ch=br.read()) != -1) {
			System.out.print((char)ch);
		}
	}
	
	public static void method2(BufferedReader br) throws IOException{
		char[] chs = new char[1024];
		int len;
		while ((len = br.read(chs)) != -1) {
			System.out.print(new String(chs, 0, len));
		}
	}

}

6.2 复制文本文件

需求:

  • 把项目目录下的a.txt内容复制到项目目录下的b.txt中
  • 数据源
     a.txt—读数据—字符流—InputStreamReader—FileReader—BufferedReader
  • 目的地
     b.txt—写数据—字符流—OutputStreamWriter—FileWriter—BufferedWriter

代码:

package Java_study;

import java.io.*;

/**
 * 
 * @author  只是甲
 * @date    2021-07-21
 * @remark  字符缓冲区流 复制文本文件
 *
 */

public class Io19 {
	public static void main(String[] args) throws IOException{
		//封装数据源
		BufferedReader br = new BufferedReader(new FileReader("a.txt"));
		//封装目的地
		BufferedWriter bw = new BufferedWriter(new FileWriter("b.txt"));
		
		//读写数据
		char[] chs = new char[1024];
		int len;
		while ((len = br.read(chs)) != -1 ) bw.write(chs, 0, len);
		
		//释放资源
		bw.close();
		br.close();
	}

}

6.3 复制Java文件

需求:
把项目目录下的io1.java内容复制到项目目录下的Copy.java中

三种方式:

  • InputStreamReader & OutputStreamWriter (字符流方式 见5.1) 379毫秒
  • FileReader & FileWriter (字符流方式 见5.1) 297毫秒
  • BufferedReader & BufferedWriter (字符缓冲区流方式 见6.2) 533毫秒

代码:

package Java_study;

import java.io.*;

/**
 * 
 * @author  只是甲
 * @date    2021-07-21
 * @remark  字符缓冲区流 字符缓冲区流复制Java文件
 *
 */

public class Io20 {
	public static void main(String[] args) throws IOException {
		long start3 = System.currentTimeMillis();
		for (int i = 0; i < 1000; i++) {
			method3("io1.java", "Copy.java");
		}
		long end3 = System.currentTimeMillis();
		System.out.println("method3共耗时"+ (end3 - start3) + "毫秒");
	}
	
	public static void method3(String source,String target) throws IOException {
		//封装数据源
		BufferedReader br = new BufferedReader(new FileReader(source));
		//封装目的地
		BufferedWriter bw = new BufferedWriter(new FileWriter(target));
		//读写数据
		String line;
		while ((line = br.readLine()) != null) {
			bw.write(line);
			bw.newLine();
			bw.flush();
		}
		
		//释放资源
		bw.close();
	}

}

6.4 字符缓冲区流的特殊功能

BufferedWriter

//写入一个行分隔符,这个行分隔符是由系统决定的
void newLine()

BufferedReader

//包含该行内容的字符串,不包含任何行终止符,如果已到达流末尾,则返回 null
String readLine()

代码:

package Java_study;

import java.io.*;

/**
 * 
 * @author  只是甲
 * @date    2021-07-21
 * @remark  字符缓冲区流 字符缓冲区流复制Java文件
 *
 */

public class Io21 {
	public static void main(String[] args) throws IOException{
		//创建字符缓冲输出流对象
		BufferedWriter bw = new BufferedWriter(new FileWriter("bw.txt"));
		//写数据
		for (int x = 0; x < 3; x++) {
			bw.write("hello");
			bw.newLine();
			bw.flush();
		}
		
		//释放资源
		bw.close();
		
		//创建字符缓冲输入流对象
		BufferedReader br = new BufferedReader(new FileReader("bw.txt"));
		
		//创建line对象接收每一行数据
		String line;
		//b. String readLine():包含该行内容的字符串,不包含任何行终止符,如果已到达流末尾,则返回 null
		while ((line = br.readLine()) != null) {
			System.out.println(line);
		}
		
		//释放资源
		br.close();
	}

}

参考:

  1. https://blog.csdn.net/qq_43529621/article/details/116603510
上一篇:file类别

相关文章