字符流

为什么会出现字符流
由于字节流操作中文不是特别的方便,所以Java就提供字符流

  • 字符流=字节流+编码表

用字符流复制文本文件时,文本文件也会有中文,但是没有问题,原因是最终底层操作会自动进行字节拼接成中文,如何识别是中文呢?

  • 汉字在存储的时候,无论选择哪种编码存储,第一个字节都是负数

例如:

java 字符流 读取一行_java 字符流 读取一行

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

/*
需求:字节流读文本文件数据

	一个汉字的存储:
		如果是GBK编码,占用2个字节
		如果是UTF-8编码,占用3个字节

*/

public class FileInputStreamDemo3 {
	public static void main(String[] args) throws IOException{
//		FileInputStream fis=new FileInputStream("..\\hello java\\a.txt");
//		
//		int by;
//		while((by=fis.read())!=-1) {
//			System.out.print((char)by);
//		}
//		
//		fis.close();
		
//		String s="abc";//[97, 98, 99]
		
		String s="中国";//[-42, -48, -71, -6] 默认GBK编码
//		byte[] bys = s.getBytes();
		
//		byte[] bys = s.getBytes("UTF-8");//[-28, -72, -83, -27, -101, -67]
		byte[] bys = s.getBytes("GBK");//[-42, -48, -71, -6]
		System.out.println(Arrays.toString(bys));
		
		
	}
}

编码表

基础知识:

  • 计算机中存储的信息都是用二进制数表示的;我们在屏幕上看到的英文、汉字等字符是二进制数转换之后的结果
  • 按照某种规则,将字符存储到计算机中,称为编码。反之,将存储在计算机中的二进制数按照某种规则解析显示出来,称为解码。这里强调一下:按照A编码存储,必须按照A编码解析,这样才能显示正确的文本符号。否则就会导致乱码现象。
    字符编码:就是一套自然语言的字符与二进制数之间的对应规则(A,65)

字符集:

  • 是一个系统支持的所有字符的集合,包括各国家文字、标点符号、图形符号、数字等
  • 计算机要准确的存储和识别个只能怪字符集符号,就需要进行字符编码,一套字符集必然至少有一套字符编码。
    常见字符集有ASCII字符集、GBXXX字符集、Unicode字符集等

ASCII字符集:

  • ASCII(American Standard Code for Information Interchange,美国信息交换标准代码):是基于拉丁字母的一套电脑编码系统,用于显示现代英文,主要包括控制字符(回车键、退格、换行键等)和可显示字符(英文大小写字符、阿拉伯数字和西文符号)
  • 基本的ASCII字符集,使用7为表示一个字符,共128字符。ASCII的扩展字符集使用8位表示一个字符,共256字符,方便支持欧洲常用字符。是一个系统支持的所有字符的集合,包括各国家文字、标点符号、图形符号、数字等

GBXXX字符集:

  • GB2312:简体中文码表。一个小于127的字符的意义与原来相同,但两个大于127的字符连在一起时,就表示一个汉字,这样大约可以组合了包含7000多个简体汉字,此外数学符号、罗马希腊的字母、日文的假名等都编进去了,连在ASCII里本来就有的数字、标点、字母都统统重写编了两个字节长的编码,这就是常说的“全角”字符,而原来在127号以下的那些就饿叫“半角”字符了
  • GBK: 最常用的中文码表。是在GB2312标准基础上扩展规范,使用了双字节编码方案,共收录了21003个汉字,完全兼容GB2312标准,同时支持繁体汉字以及日韩汉字等
  • GB18030:最新的中文码表。共收录汉字70244个,采用多字节编码,每个字可以有1个、2个或4个字节组成。支持中国国内少数民族的文字,同时支持繁体汉字以及日韩汉字等

Unicode字符集:

  • 为表达任意语言的任意字符而设计,是业界的一种标准,也称为统一码、标准万国码。它最多使用4个字节的数字来表达每个字母、符号、或者文字。有三种编码方案,UTF-8、UTF-16和UTF32。最为常用的UTF-8编码
  • UTF-8编码:可以用来表示Unicode标准中任意字符,它是电子邮件、网页及其他存储或传送文字的应用中,优先采用的编码。互联网工程工作小组(IETF)要求所有互联网协议都必须支持UTF-8编码。它使用一至4个字节为每个字符编码
    编码规则:
    128个US-ASCII字符,只需一个字节编码
    拉丁文等文字,需要二个字节编码
    大部分常用字(含中文),使用三个字节编码
    其他极少使用的Unicode辅助字符,使用四字节编码

小结:采用何种规则编码,就要采用对应规则解码,否则就会出现乱码

字符串中的编码解码问题

编码:

  • byte[] getBytes():使用平台的默认字符集将该String编码为以一系列字节,将结果存储到新的字节数组中
  • byte[] getBytes(String charsetName):使用指定的字符集将该String编码为一系列字节,将结果存储到新的字节数组中

解码:

  • String(byte[] bytes):通过使用平台的默认字符集解码指定的字节数组来构造新的String
  • String(byte[] bytes,String charsetName):通过指定的字符集解码指定的字节数组来构造新的String
import java.io.UnsupportedEncodingException;
import java.util.Arrays;

/*
编码:

- byte[] getBytes():使用平台的默认字符集将该String编码为以一系列字节,将结果存储到新的字节数组中
- byte[] getBytes(String charsetName):使用指定的字符集将该String编码为一系列字节,将结果存储到新的字节数组中

解码:

- String(byte[] bytes):通过使用平台的默认字符集解码指定的字节数组来构造新的String
- String(byte[] bytes,String charsetName):通过指定的字符集解码指定的字节数组来构造新的String

*/
public class StringDemo {
	public static void main(String[] args) throws UnsupportedEncodingException {
		//定义一个字符串
		String s="中国";
		//编码:
		//byte[] getBytes():使用平台的默认字符集将该String编码为以一系列字节,将结果存储到新的字节数组中
		byte[] bys = s.getBytes();//[-42, -48, -71, -6]
		
		//byte[] getBytes(String charsetName):使用指定的字符集将该String编码为一系列字节,将结果存储到新的字节数组中
//		byte[] bys = s.getBytes("UTF-8");//[-28, -72, -83, -27, -101, -67]
//		byte[] bys = s.getBytes("GBK");//[-42, -48, -71, -6]
		System.out.println(Arrays.toString(bys));
		
		//解码:
		//String(byte[] bytes):通过使用平台的默认字符集解码指定的字节数组来构造新的String
//		String ss=new String(bys);//中国
		//String(byte[] bytes,String charsetName):通过指定的字符集解码指定的字节数组来构造新的String
//		String ss=new String(bys,"UTF-8");//?й?
		String ss=new String(bys,"GBK");//中国
		System.out.println(ss);
	}
}

运行结果:

java 字符流 读取一行_java_02


字符流中的编码解码问题

字符流抽象基类:

  • Reader:字符输入流的抽象类
  • Writer:字符输出流的抽象类

字符流中和编码解码问题相关的两个类:

  • InputStreamReader:是从字节流到字符流的桥梁
    它读取字节,并使用指定的编码将其解码为字符
    它使用的字符集可以由名称指定,也可以被明确指定,或者可以接收平台的默认字符集
  • OutputStreamWriter:是从字符流到字节流的桥梁
    是从字符流到字节流的桥梁,使用指定的编码将写入的字符编码为字节
    它使用的字符集可以由名称指定,也可以被明确指定,或者可以接收平台的默认字符集
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;

/*
- InputStreamReader:是从字节流到字符流的桥梁
	它读取字节,并使用指定的编码将其解码为字符
	它使用的字符集可以由名称指定,也可以被明确指定,或者可以接收平台的默认字符集

- OutputStreamWriter:是从字符流到字节流的桥梁
	是从字符流到字节流的桥梁,使用指定的编码将写入的字符编码为字节
	它使用的字符集可以由名称指定,也可以被明确指定,或者可以接收平台的默认字符集
*/
public class ConversionStreamDemo {
	public static void main(String[] args) throws IOException{
		//OutputStreamWriter(OutputStream out)创建一个使用默认字符编码的OutputStreamWriter
		//OutputStreamWriter(OutputStream out,String charsetName)创建一个使用命名字符集的OutputStreamWriter
		
//		FileOutputStream fos=new FileOutputStream("..\\hello java\\osw.txt");
//		OutputStreamWriter osw=new OutputStreamWriter(fos);
		//两步合并
//		OutputStreamWriter osw=new OutputStreamWriter(new FileOutputStream("..\\hello java\\osw.txt"));
//		OutputStreamWriter osw=new OutputStreamWriter(new FileOutputStream("..\\hello java\\osw.txt"),"GBK");//中国
		OutputStreamWriter osw=new OutputStreamWriter(new FileOutputStream("..\\hello java\\osw.txt"),"UTF-8");//涓浗
		osw.write("中国");
		osw.close();
		
		//InputStreamReader(InputStream in)创建一个使用默认字符集的InputStreamReader
		//InputStreamReader(InputStream in,String charsetName)创建一个使用命名字符集的InputStreamReader
		
//		InputStreamReader isr=new InputStreamReader(new FileInputStream("..\\hello java\\osw.txt"));//涓浗
//		InputStreamReader isr=new InputStreamReader(new FileInputStream("..\\hello java\\osw.txt"),"GBK");//涓浗
		InputStreamReader isr=new InputStreamReader(new FileInputStream("..\\hello java\\osw.txt"),"UTF-8");//中国
		//一次读取一个字符数据
		int ch;
		while((ch=isr.read())!=-1) {
			System.out.print((char)ch);
		}
		
		isr.close();
		
	}
}

运行结果:

java 字符流 读取一行_java 字符流 读取一行_03


java 字符流 读取一行_java 字符流 读取一行_04


字符流写数据的5种方式

方法名

说明

void write(int c)

写一个字符

void write(char[] cbuf)

写入一个字符数组

void write(char[] cbuf,int off,int len)

写入字符数组的一部分

void write(String str)

写一个字符串

void write(String str,int off,int len)

写一个字符串的一部分

flush()

刷新流,还可以继续写数据

close()

关闭流,释放资源,但是在关闭之前会先刷新流。一旦关闭,就不能再写数据

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

/*
构造方法:
	OutputStreamWriter(OutputStream out)创建一个使用默认字符编码的OutputStreamWriter
	
	字符流写数据的5种方式**

	void write(int c)   写一个字符
	void write(char[] cbuf)写入一个字符数组
	void write(char[] cbuf,int off,int len)写入字符数组的一部分
	void write(String str)写一个字符串
	void write(String str,int off,int len)写一个字符串的一部分
*/

public class OutputStreamWriterDemo {
	public static void main(String[] args) throws IOException {
		//		OutputStreamWriter(OutputStream out)创建一个使用默认字符编码的OutputStreamWriter
		OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("..\\hello java\\osw.txt"));
		/*
		//void write(int c)   写一个字符
		osw.write(97);//不刷新数据进不来,因为真正底层写数据是字节流,而字符流相对于字节流是有缓冲的
		//void flush()刷新流
		osw.flush();
		osw.write(98);
		osw.flush();
		osw.write(99);*/

		//void write(char[] cbuf)写入一个字符数组
		char[] chs = { 'a', 'b', 'c', 'd', 'e' };
		//osw.write(chs);//abcde

		//void write(char[] cbuf,int off,int len)写入字符数组的一部分
		//osw.write(chs,0,chs.length);//abcde
		//osw.write(chs,1,3);//bcd

		//void write(String str)写一个字符串
		//osw.write("abcde");//abcde

		//void write(String str,int off,int len)写一个字符串的一部分
		//osw.write("abcde",0,"abcde".length());//abcde
		osw.write("abcde", 1, 3);//bcd

		osw.close();//关闭流,先刷新
		//osw.write(100);//java.io.IOException: Stream closed

	}
}

字符流读数据的2种方式

方法名

说明

int read()

一次读一个字符数据

int read(char[] cbuf)

一次读一个字符数组数据

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

/*
构造方法:
	InputStreamReader(InputStream in)创建一个使用默认字符集的InputStreamReader
	
字符流读数据的2种方式**
	int read()  一次读一个字符数据
	int read(char[] cbuf)一次读一个字符数组数据

*/
public class InputStreamReaderDemo {
	public static void main(String[] args) throws IOException{
		//InputStreamReader(InputStream in)创建一个使用默认字符集的InputStreamReader
//		InputStreamReader isr=new InputStreamReader(new FileInputStream("..\\hello java\\osw.txt"));
		InputStreamReader isr=new InputStreamReader(new FileInputStream("..\\hello java\\ConversionStreamDemo.java"));
		
		
		//int read()  一次读一个字符数据
//		int ch;
//		while((ch=isr.read())!=-1) {
//			System.out.print((char)ch);
//		}
		/*运行结果:
		hello
		world
		java*/
		
		//int read(char[] cbuf)一次读一个字符数组数据
		char[] chs=new char[1024];
		int len;
		while((len=isr.read(chs))!=-1) {
			System.out.print(new String(chs,0,len));
		}
		/*运行结果:
		hello
		world
		java*/
		
		//释放资源
		isr.close();
		
	}
}

运行结果:

java 字符流 读取一行_java 字符流 读取一行_05


java 字符流 读取一行_java_06

案例:复制Java文件

需求:把模块目录下的“ConversionSteamDemo.java”复制到模块目录下的“Copy.java”

思路:

  1. 根据数据源创建字符输入流对象
  2. 根据目的地创建字符输出流对象
  3. 读写数据,复制文件
  4. 释放资源
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;

/*
案例:复制Java文件
需求:把模块目录下的“ConversionSteamDemo.java”复制到模块目录下的“Copy.java”

思路:

 1. 根据数据源创建字输入流对象
 2. 根据目的地创建字符输出流对象
 3. 读写数据,复制文件
 4. 释放资源
*/

public class CopyJavaDemo {
	public static void main(String[] args) throws IOException {
		//1. 根据数据源创建字输入流对象
		InputStreamReader isr = new InputStreamReader(new FileInputStream("..\\hello java\\ConversionStreamDemo.java"));
		//2. 根据目的地创建字符输出流对象
		OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("..\\hello java\\Copy.java"));
		//3. 读写数据,复制文件
		//一次读写一个字符数据
		//int ch;
		//while((ch=isr.read())!=-1) {
		//	osw.write(ch);
		//}

		//一次读写一个字符数组数据
		char[] chs = new char[1024];
		int len;
		while ((len = isr.read(chs)) != -1) {
			osw.write(chs, 0, len);
		}

		//释放资源
		isr.close();
		osw.close();

	}
}

运行结果:

java 字符流 读取一行_java_07


java 字符流 读取一行_字节数组_08

案例:复制Java文件(改进版)

需求:把模块目录下的“ConversionSteamDemo.java”复制到模块目录下的“Copy.java”

分析:

  1. 转换流的名字比较长,而我们常见的操作都是按照本地默认编码实现的,所以,为了简化书写,转换流提供了对应的子类
  2. FileReader:用于读取字符文件的便捷类
    FileReader(String fileName)
  3. FileWriter:用于写入字符文件的便捷类
    FileWriter(String fileName)
  4. 数据源和目的地的分析
    数据源:…\hello java\ConversionStreamDemo.java—读数据—Reader—InputStreamReader—FileReader
    目的地:…\hello java\Copy.java–写数据–Writer–OutputStreamWriter—FileWriter
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;

/*
案例:复制Java文件(改进版)

需求:把模块目录下的“ConversionSteamDemo.java”复制到模块目录下的“Copy.java”

分析:

 1. 转换流的名字比较长,而我们常见的操作都是按照本地默认编码实现的,所以,为了简化书写,转换流提供了对应的子类
 2. FileReader:用于读取字符文件的便捷类
	FileReader(String fileName)
 3. FileWriter:用于写入字符文件的便捷类
	FileWriter(String fileName)
 4. 数据源和目的地的分析
	数据源:---读数据---Reader---InputStreamReader---FileReader
	目的地:..\\hello java\\Copy.java--写数据--Writer--OutputStreamWriter---FileWriter

思路:

1. 根据数据源创建字输入流对象
2. 根据目的地创建字符输出流对象
3. 读写数据,复制文件
4. 释放资源

*/
public class CopyJavaDemo2 {
	public static void main(String[] args) throws IOException {
		//1. 根据数据源创建字输入流对象
		FileReader fr = new FileReader("..\\hello java\\ConversionStreamDemo.java");

		//2. 根据目的地创建字符输出流对象
		FileWriter fw = new FileWriter("..\\hello java\\Copy.java");

		//3. 读写数据,复制文件
		//一次读写一个字符数据
		//int ch;
		//while((ch=fr.read())!=-1) {
		//	fw.write(ch);
		//}

		//一次读写一个字符数组数据
		char[] chs = new char[1024];
		int len;
		while ((len = fr.read(chs)) != -1) {
			fw.write(chs, 0, len);
		}

		//4. 释放资源
		fr.close();
		fw.close();

	}
}

运行结果:

java 字符流 读取一行_java_07


java 字符流 读取一行_字节数组_08


字符缓冲流

字符缓冲流:

  • BufferedWriter:将文本写入字符输出流,缓冲字符,以提供单个字符,数组和字符串的高效写入,可以指定缓冲区大小,或者可以接收默认大小。默认值足够大,可用于大多数用途
  • BufferedReader:从字符输入流读取文本,缓冲字符,以提供字符,数组和行的高效读取,可以指定缓冲区大小,或者可以使用默认大小。默认值足够大,可用于大多数用途

构造方法:

  • BufferedWriter(Writer out)
  • BufferedReader(Reader in)
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;

/*
字符缓冲流:

- BufferedWriter:将文本写入字符输出流,缓冲字符,以提供单个字符,数组和字符串的高效写入,可以指定缓冲区大小,或者可以接收默认大小。默认值足够大,可用于大多数用途
- BufferedReader:从字符输入流读取文本,缓冲字符,以提供字符,数组和行的高效读取,可以指定缓冲区大小,或者可以使用默认大小。默认值足够大,可用于大多数用途

构造方法:

- BufferedWriter(Writer out)
- BufferedReader(Reader in)
*/

public class BufferedStreamDemo2 {
	public static void main(String[] args) throws IOException {
		//BufferedWriter(Writer out)
		//		FileWriter fw=new FileWriter("..\\hello java\\bw.txt");
		//		BufferedWriter bw=new BufferedWriter(fw);
		/*
		//可合并为一句
		BufferedWriter bw=new BufferedWriter(new FileWriter("..\\hello java\\bw.txt"));
		
		bw.write("hello\r\n");
		bw.write("world\r\n");
		
		bw.close();*/

		//BufferedReader(Reader in)
		BufferedReader br = new BufferedReader(new FileReader("..\\hello java\\bw.txt"));
		/*
		//一次读一个字符数据
		int ch;
		while((ch=br.read())!=-1) {
			System.out.print((char)ch);
		}*/

		//一次读一个字符数组数据
		char[] chs = new char[1024];
		int len;
		while ((len = br.read(chs)) != -1) {
			System.out.print(new String(chs, 0, len));
		}

		br.close();

	}
}

运行结果:

java 字符流 读取一行_字符流_11

案例:复制Java文件(字符缓冲流改进版)

需求:把模块目录下的“ConversionSteamDemo.java”复制到模块目录下的“Copy.java”

思路:

  1. 根据数据源创建字符缓冲输入流对象
  2. 根据目的地创建字符缓冲输出流对象
  3. 读写数据,复制文件
  4. 释放资源
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;

/*
案例:复制Java文件(字符缓冲流改进版)

需求:把模块目录下的“ConversionStreamDemo.java”复制到模块目录下的“Copy.java”

思路:
	1. 根据数据源创建字符缓冲输入流对象
	2. 根据目的地创建字符缓冲输出流对象
	3. 读写数据,复制文件
	4. 释放资源
*/

public class CopyJavaDemo3 {
	public static void main(String[] args) throws IOException {
		//1. 根据数据源创建字符缓冲输入流对象
		BufferedReader br = new BufferedReader(new FileReader("..\\hello java\\ConversionStreamDemo.java"));

		//2. 根据目的地创建字符缓冲输出流对象
		BufferedWriter bw = new BufferedWriter(new FileWriter("..\\hello java\\Copy.java"));

		//3. 读写数据,复制文件
		/*
		//一次读写一个字符数据
		int ch;
		while((ch=br.read())!=-1) {
			bw.write(ch);
		}*/

		//一次读写一个字符数组数据
		char[] chs = new char[1024];
		int len;
		while ((len = br.read(chs)) != -1) {
			bw.write(chs, 0, len);
		}

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

	}
}

运行结果:

java 字符流 读取一行_java_12


java 字符流 读取一行_java 字符流 读取一行_13


字符缓冲流的特有功能

BufferedWrite:

  • void newLine():写一行行分隔符,行分隔符字符串有系统属性定义

BufferedReader:

  • public String readLine():读一行文字。结果包含行的内容的字符串,不包括任何行终止字符,如果流的结尾已经到大,则为null

java 字符流 读取一行_java_14

/*
字符缓冲流的特有功能

BufferedWrite:
	void newLine():写一行行分隔符,行分隔符字符串有系统属性定义

BufferedReader:
	public String readLine():读一行文字。结果包含行的内容的字符串,不包括任何行终止字符,如果流的结尾已经到大,则为null

*/

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

public class BufferedStreamDemo3 {
	public static void main(String[] args) throws IOException {
		/*//创建字符缓冲输出流
		BufferedWriter bw=new BufferedWriter(new FileWriter("..\\hello java\\bw.txt"));
		
		//写数据
		for(int i=0;i<10;i++) {
			bw.write("hello"+i);
		//			bw.write("\r\n");//只适用于Windows
			bw.newLine();
			bw.flush();
		} 
			hello0
			hello1
			hello2
			hello3
			hello4
			hello5
			hello6
			hello7
			hello8
			hello9
		
		//释放资源
		bw.close();
		*/

		//创建字符缓冲输入流
		BufferedReader br = new BufferedReader(new FileReader("..\\hello java\\bw.txt"));

		//public String readLine():读一行文字。结果包含行的内容的字符串,不包括任何行终止字符,如果流的结尾已经到大,则为null
		/*//第一次读取数据
		String line = br.readLine();
		System.out.println(line);//hello0
		//第二次读取数据
		line = br.readLine();
		System.out.println(line);//hello1
		//再多读两次
		line = br.readLine();
		System.out.println(line);//null
		line = br.readLine();
		System.out.println(line);//null
		*/

		String line;
		while ((line = br.readLine()) != null) {
			System.out.println(line);//readline只读内容,不包含换行符,要加ln
		}

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

	}
}

运行结果:

java 字符流 读取一行_字符流_15

案例:复制Java文件(字符缓冲流特有功能改进版)

需求:把模块目录下的“ConversionSteamDemo.java”复制到模块目录下的“Copy.java”

思路:

  1. 根据数据源创建字符缓冲输入流对象
  2. 根据目的地创建字符缓冲输出流对象
  3. 读写数据,复制文件
    使用字符缓冲流特有功能实现
  4. 释放资源
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;

/*
案例:复制Java文件(字符缓冲流特有功能改进版)

需求:把模块目录下的“ConversionSteamDemo.java”复制到模块目录下的“Copy.java”

思路:

 1. 根据数据源创建字符缓冲输入流对象
 2. 根据目的地创建字符缓冲输出流对象
 3. 读写数据,复制文件
 		使用字符缓冲流特有功能实现
 4. 释放资源
*/
public class CopyJavaDemo4 {
	public static void main(String[] args) throws IOException {
		//1. 根据数据源创建字符缓冲输入流对象
		BufferedReader br = new BufferedReader(new FileReader("..\\hello java\\ConversionStreamDemo.java"));

		//2. 根据目的地创建字符缓冲输出流对象
		BufferedWriter bw = new BufferedWriter(new FileWriter("..\\hello java\\Copy.java"));

		//3. 读写数据,复制文件(使用字符缓冲流特有功能实现)
		String line;
		while ((line = br.readLine()) != null) {
			bw.write(line);
			bw.newLine();//要加换行
			bw.flush();
		}

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

	}
}

运行结果:

java 字符流 读取一行_java_12


java 字符流 读取一行_java 字符流 读取一行_13

IO流小结

java 字符流 读取一行_java_18

小结:字节流可以复制任意文件数据,有4种方式一般采用字节缓冲流一次读写一个字节数组的方式

java 字符流 读取一行_字节数组_19

小结:字符流只能复制文本数据,有5种方式,一般采用字符缓冲流的特有功能

案例:集合到文件

需求:把ArrayList集合中的字符串数据写入到文本文件。要求:每一个字符串元素作为文件中的一行数据

思路:

  1. 创建ArrayList集合
  2. 往集合中存储字符串元素
  3. 创建字符缓冲输出流对象
  4. 遍历集合,得到每一个字符串数据
  5. 调用字符缓冲输出流对象的方法写数据
  6. 释放资源
import java.io.BufferedWriter;
import java.io.FileWriter;
import java.io.IOException;
import java.util.ArrayList;

/*
案例:集合到文件

需求:把ArrayList集合中的字符串数据写入到文本文件。

要求:每一个字符串元素作为文件中的一行数据

思路:

 1. 创建ArrayList集合
 2. 往集合中存储字符串元素
 3. 创建字符缓冲输出流对象
 4. 遍历集合,得到每一个字符串数据
 5. 调用字符缓冲输出流对象的方法写数据
 6. 释放资源
*/

public class ArrayListToTxtDemo {
	public static void main(String[] args) throws IOException {
		//1. 创建ArrayList集合
		ArrayList<String> array = new ArrayList<String>();

		//2. 往集合中存储字符串元素
		array.add("hello");
		array.add("world");
		array.add("java");

		//3. 创建字符缓冲输出流对象
		BufferedWriter bw = new BufferedWriter(new FileWriter("..\\hello java\\array.txt"));

		//4. 遍历集合,得到每一个字符串数据
		for (String s : array) {
			//5. 调用字符缓冲输出流对象的方法写数据
			bw.write(s);
			bw.newLine();
			bw.flush();

		}

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

	}
}

运行结果:

java 字符流 读取一行_java 字符流 读取一行_20

案例:文件到集合

需求:把文本文件中的数据读取到集合中,并遍历集合。要求:文件中每一行数据是一个集合元素

思路:

  1. 创建字符缓冲输入流对象
  2. 创建ArrayList集合
  3. 调用字符缓冲输入流对象的方法读数据
  4. 把读取到的字符串数据存储到集合中
  5. 释放资源
  6. 遍历集合
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
import java.util.ArrayList;

/*
案例:文件到集合

需求:把文本文件中的数据读取到集合中,并遍历集合。要求:文件中每一行数据是一个集合元素

思路:

 1. 创建字符缓冲输入流对象
 2. 创建ArrayList集合
 3. 调用字符缓冲输入流对象的方法读数据
 4. 把读取到的字符串数据存储到集合中
 5. 释放资源
 6. 遍历集合 

*/
public class TxtToArrayListDemo {
	public static void main(String[] args) throws IOException {
		//1. 创建字符缓冲输入流对象
		BufferedReader br = new BufferedReader(new FileReader("..\\hello java\\array.txt"));

		//2. 创建ArrayList集合
		ArrayList<String> array = new ArrayList<String>();

		//3. 调用字符缓冲输入流对象的方法读数据
		String line;
		while ((line = br.readLine()) != null) {
			//4. 把读取到的字符串数据存储到集合中
			array.add(line);

		}

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

		//6. 遍历集合 
		for (String s : array) {
			System.out.println(s);
		}

	}
}

运行结果:

java 字符流 读取一行_字符流_21

案例:点名器

需求:有一个文件里面存储了班级同学的姓名,每一个姓名占一行,要求通过程序实现随机点名器

思路:

  1. 创建字符缓冲输入流对象
  2. 创建ArrayList集合对象
  3. 调用字符缓冲输入流对象的方法读数据
  4. 把读取到的字符串数据存储到集合中
  5. 释放资源
  6. 使用Random产生一个随机数,随机数的范围在:[0,集合的长度)
  7. 把第6步产生的随机数最为索引到ArrayList集合中获取值
  8. 把第7步得到的数据输出在控制台

java 字符流 读取一行_字节数组_22

import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Random;

/*
案例:点名器

需求:有一个文件里面存储了班级同学的姓名,每一个姓名占一行,要求通过程序实现随机点名器

思路:

 1. 创建字符缓冲输入流对象
 2. 创建ArrayList集合对象
 3. 调用字符缓冲输入流对象的方法读数据
 4. 把读取到的字符串数据存储到集合中
 5. 释放资源
 6. 使用Random产生一个随机数,随机数的范围在:[0,集合的长度)
 7. 把第6步产生的随机数最为索引到ArrayList集合中获取值
 8. 把第7步得到的数据输出在控制台 

*/
public class CallNameDemo {
	public static void main(String[] args) throws IOException {
		//1. 创建字符缓冲输入流对象
		BufferedReader br = new BufferedReader(new FileReader("..\\hello java\\array.txt"));

		//2. 创建ArrayList集合对象
		ArrayList<String> array = new ArrayList<String>();

		//3. 调用字符缓冲输入流对象的方法读数据
		String line;
		while ((line = br.readLine()) != null) {
			//4. 把读取到的字符串数据存储到集合中
			array.add(line);
		}

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

		//6. 使用Random产生一个随机数,随机数的范围在:[0,集合的长度)
		Random r = new Random();
		int index = r.nextInt(array.size());

		//7. 把第6步产生的随机数最为索引到ArrayList集合中获取值
		String name = array.get(index);

		//8. 把第7步得到的数据输出在控制台 

		System.out.println("幸运者是:" + name);

	}
}

运行结果:

java 字符流 读取一行_java 字符流 读取一行_23

案例:集合到文件(改进版)

需求:把ArrayList集合中的学生数据写入到文本文件。要求:每一个学生对象的数据作为文件中的一行数据

格式:学号,姓名,年龄,居住地
举例:001,小白,12,北京

思路:

  1. 定义学生类
  2. 创建ArrayList集合
  3. 创建学生对象
  4. 把学生对象添加到集合
  5. 创建字符缓冲输出流对象
  6. 遍历集合,得到每一个学生对象
  7. 把学生对象的数据拼接成指定格式的字符串
  8. 调用字符缓冲输出流对象的方法写数据
  9. 释放资源
//1. 定义学生类
public class Student {
	private String number;//学号
	private String name;//姓名
	private int age;//年龄
	private String address;//居住地

	public String getNumber() {
		return number;
	}

	public void setNumber(String number) {
		this.number = number;
	}

	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 getAddress() {
		return address;
	}

	public void setAddress(String address) {
		this.address = address;
	}

	public Student(String number, String name, int age, String address) {
		super();
		this.number = number;
		this.name = name;
		this.age = age;
		this.address = address;
	}

	public Student() {
		super();
	}

}
import java.io.BufferedWriter;
import java.io.FileWriter;
import java.io.IOException;
import java.util.ArrayList;

/*
案例:集合到文件(改进版)

需求:把ArrayList集合中的学生数据写入到文本文件。要求:每一个学生对象的数据作为文件中的一行数据

格式:学号,姓名,年龄,居住地
举例:001,小白,12,北京

思路:

 1. 定义学生类
 2. 创建ArrayList集合
 3. 创建学生对象
 4. 把学生对象添加到集合
 5. 创建字符缓冲输出流对象
 6. 遍历集合,得到每一个学生对象
 7. 把学生对象的数据拼接成指定格式的字符串
 8. 调用字符缓冲输出流对象的方法写数据
 9. 释放资源

 */

public class ArrayListToTxtDemo2 {
	public static void main(String[] args) throws IOException {
		//2. 创建ArrayList集合
		ArrayList<Student> array = new ArrayList<Student>();

		//3. 创建学生对象
		Student s1 = new Student("001", "小白", 12, "北京");
		Student s2 = new Student("002", "小黑", 11, "上海");
		Student s3 = new Student("003", "小红", 15, "深圳");

		//4. 把学生对象添加到集合
		array.add(s1);
		array.add(s2);
		array.add(s3);

		//5. 创建字符缓冲输出流对象
		BufferedWriter bw = new BufferedWriter(new FileWriter("..\\hello java\\array.txt"));

		//6. 遍历集合,得到每一个学生对象
		for (Student stu : array) {
			//7. 把学生对象的数据拼接成指定格式的字符串
			StringBuilder sb = new StringBuilder();
			sb.append(stu.getNumber()).append(",").append(stu.getName()).append(",").append(stu.getAge()).append(",")
					.append(stu.getAddress());

			//8. 调用字符缓冲输出流对象的方法写数据
			bw.write(sb.toString());
			bw.newLine();
			bw.flush();

		}

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

	}
}

运行结果:

java 字符流 读取一行_java_24

案例:文件到集合(改进版)

需求:把文本文件中的数据读取到集合中,并遍历集合。要求:文件中每一行数据是一个学生对象的成员变量值

举例:001,小白,12,北京

思路:

  1. 定义学生类
  2. 创建字符缓冲输入流对象
  3. 创建ArrayList集合
  4. 调用字符缓冲输入流对象的方法读数据
  5. 把读取到的字符串数据用split()进行分割,得到一个字符串数组
  6. 创建学生对象
  7. 把字符串数组中的每一个元素取出来对应的复制给学生对象的成员变量值
  8. 把学生对象添加到集合
  9. 释放资源
    10.遍历集合

java 字符流 读取一行_字符流_25

import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
import java.util.ArrayList;

/*
案例:文件到集合(改进版)
需求:把文本文件中的数据读取到集合中,并遍历集合。

要求:文件中每一行数据是一个学生对象的成员变量值

举例:001,小白,12,北京

思路:

1. 定义学生类
2. 创建字符缓冲输入流对象
3. 创建ArrayList集合
4. 调用字符缓冲输入流对象的方法读数据
5. 把读取到的字符串数据用split()进行分割,得到一个字符串数组
6. 创建学生对象
7. 把字符串数组中的每一个元素取出来对应的复制给学生对象的成员变量值
8. 把学生对象添加到集合 
9. 释放资源
10.遍历集合

```java
//1. 定义学生类
public class Student {
	private String number;//学号
	private String name;//姓名
	private int age;//年龄
	private String address;//居住地

	public String getNumber() {
		return number;
	}

	public void setNumber(String number) {
		this.number = number;
	}

	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 getAddress() {
		return address;
	}

	public void setAddress(String address) {
		this.address = address;
	}

	public Student(String number, String name, int age, String address) {
		super();
		this.number = number;
		this.name = name;
		this.age = age;
		this.address = address;
	}

	public Student() {
		super();
	}

}

*/
public class FileToArrayListDemo {
public static void main(String[] args) throws IOException {
//2. 创建字符缓冲输入流对象
BufferedReader br = new BufferedReader(new FileReader("…\hello java\array.txt"));

//3. 创建ArrayList集合
	ArrayList<Student> array = new ArrayList<Student>();

	//4. 调用字符缓冲输入流对象的方法读数据
	String line;
	while (null != (line = br.readLine())) {
		//5. 把读取到的字符串数据用split()进行分割,得到一个字符串数组
		String[] strArray = line.split(",");
		//6. 创建学生对象
		Student s = new Student();

		//7. 把字符串数组中的每一个元素取出来对应的复制给学生对象的成员变量值
		s.setNumber(strArray[0]);
		s.setName(strArray[1]);
		s.setAge(Integer.parseInt(strArray[2]));//把字符串转成Integer类型的数据
		s.setAddress(strArray[3]);

		//8. 把学生对象添加到集合 
		array.add(s);
	}

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

	//10.遍历集合
	for (Student s : array) {
		System.out.println(s.getNumber() + "," + s.getName() + "," + s.getAge() + "," + s.getAddress());
	}

}

}

运行结果:
 ![在这里插入图片描述]()

## 案例:集合到文件(数据排序改进版)
需求:键盘录入5个学生信息(姓名,语文成绩,数学成绩,英语成绩)。要求按照成绩总分从高到低写入文本文件

格式:姓名,语文成绩,数学成绩,英语成绩 

举例:小白,89,99,78

思路:

 1. 定义学生类
 2. 创建TreeSet集合,通过比较器排序进行排序
 3. 键盘录入学生数据
 4. 创建学生对象,把键盘录入的数据对应赋值给学生对象的成员变量
 5. 把学生对象添加到TreeSet集合
 6. 创建字符缓冲输出流对象
 7. 遍历集合,得到每一个学生对象
 8. 把学生对象的数据拼接成指定格式的字符串
 9. 调用字符缓冲输出流对象的方法写数据
 10.释放资源

 

```java
import java.io.BufferedWriter;
import java.io.FileWriter;
import java.io.IOException;
import java.util.Comparator;
import java.util.Scanner;
import java.util.TreeSet;

/*
案例:集合到文件(数据排序改进版)
需求:键盘录入5个学生信息(姓名,语文成绩,数学成绩,英语成绩)。要求按照成绩总分从高到低写入文本文件

格式:姓名,语文成绩,数学成绩,英语成绩 

举例:小白,89,99,78

思路:

 1. 定义学生类
 2. 创建TreeSet集合,通过比较器排序进行排序
 3. 键盘录入学生数据
 4. 创建学生对象,把键盘录入的数据对应赋值给学生对象的成员变量
 5. 把学生对象添加到TreeSet集合
 6. 创建字符缓冲输出流对象
 7. 遍历集合,得到每一个学生对象
 8. 把学生对象的数据拼接成指定格式的字符串
 9. 调用字符缓冲输出流对象的方法写数据
 10.释放资源
*/

public class TreeSetToFileDemo {
	public static void main(String[] args) throws IOException {
		//2. 创建TreeSet集合,通过比较器排序进行排序
		TreeSet<Student2> tree = new TreeSet<Student2>(new Comparator<Student2>() {
			@Override
			public int compare(Student2 s1, Student2 s2) {
				//按照成绩总分从高到低
				int num = s2.getSum() - s2.getSum();
				//次要条件
				int num2 = num == 0 ? s1.getName().compareTo(s2.getName()) : num;
				return num2;
			};
		});

		//3. 键盘录入学生数据
		for (int i = 0; i < 5; i++) {
			Scanner sc = new Scanner(System.in);
			System.out.println("请录入第" + (i + 1) + "个学生的信息:");
			System.out.println("姓名:");
			String n = sc.nextLine();
			System.out.println("语文成绩:");
			int c = sc.nextInt();
			System.out.println("数学成绩:");
			int m = sc.nextInt();
			System.out.println("英语成绩:");
			int e = sc.nextInt();

			//4. 创建学生对象,把键盘录入的数据对应赋值给学生对象的成员变量
			Student2 s = new Student2();
			s.setName(n);
			s.setChanese(c);
			s.setMath(m);
			s.setEnglish(e);

			//5. 把学生对象添加到TreeSet集合
			tree.add(s);
		}

		//6. 创建字符缓冲输出流对象
		BufferedWriter bw = new BufferedWriter(new FileWriter("..\\hello java\\tree.txt"));

		//7. 遍历集合,得到每一个学生对象
		for (Student2 s : tree) {
			//8. 把学生对象的数据拼接成指定格式的字符串
			StringBuilder sb = new StringBuilder();
			sb.append(s.getName()).append(",").append(s.getChinese()).append(",").append(s.getMath()).append(",")
					.append(s.getEnglish());
			//9. 调用字符缓冲输出流对象的方法写数据
			bw.write(sb.toString());
			bw.newLine();
			bw.flush();

		}

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

		System.out.println("录入成功!");

	}
}

运行结果:

java 字符流 读取一行_字节数组_26


java 字符流 读取一行_java_27

案例:复制单级文件夹

需求:把“E:\\itcast”这个文件夹复制到模块目录下

思路:

  1. 创建数据源目录File对象,路径是E:\\itcast
  2. 获取数据源目录File对象的名称(itcast)
  3. 创建目的地目录File对象,路径名是模块名+itcast组成(…\\hello Java\\itcast)
  4. 判断目的地目录对应的File是否存在,如果不存在,就创建
  5. 获取数据源目录下所有文件的File数组
  6. 遍历File数组,得到每一个File对象,该File对象,其实就是数据源文件
    数据源文件:E:\\itcast\\mn.jpg
  7. 获取数据源文件File对象的名称(mn.jpg)
  8. 创建目的地文件File对象,路径名是目的地目录+mn.jpg组成(…\\hello java\\mn.jpg)
  9. 复制文件
    由于文件不仅仅是文本文件,还有图片,视频等文件,所以采用字节路复制文件

java 字符流 读取一行_java 字符流 读取一行_28

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

/*
案例:复制单级文件夹
需求:把“E:\\\itcast”这个文件夹复制到模块目录下

思路:

 1. 创建数据源目录File对象,路径是E:\\\itcast
 2. 获取数据源目录File对象的名称(itcast)
 3. 创建目的地目录File对象,路径名是模块名+itcast组成(..\\\hello Java\\\itcast)
 4. 判断目的地目录对应的File是否存在,如果不存在,就创建
 5. 获取数据源目录下所有文件的File数组
 6. 遍历File数组,得到每一个File对象,该File对象,其实就是数据源文件
		数据源文件:E:\\\itcast\\\mn.jpg
 7. 获取数据源文件File对象的名称(mn.jpg)
 8. 创建目的地文件File对象,路径名是目的地目录+mn.jpg组成(..\\\hello java\\\mn.jpg)
 9. 复制文件
		由于文件不仅仅是文本文件,还有图片,视频等文件,所以采用字节路复制文件

 */

public class CopyFolderDemo {
	public static void main(String[] args) throws IOException {
		//1. 创建数据源目录File对象,路径是
		File srcFolder = new File("E:\\itcast");

		//2. 获取数据源目录File对象的名称(itcast)
		String srcFolderName = srcFolder.getName();

		//3. 创建目的地目录File对象,路径名是模块名+itcast组成()
		File destFolder = new File("..\\hello Java", srcFolderName);

		//4. 判断目的地目录对应的File是否存在,如果不存在,就创建
		if (!destFolder.exists()) {
			destFolder.mkdir();
		}

		//5. 获取数据源目录下所有文件的File数组
		File[] listFiles = srcFolder.listFiles();

		//6. 遍历File数组,得到每一个File对象,该File对象,其实就是数据源文件
		for (File srcFile : listFiles) {
			;
			//7. 获取数据源文件File对象的名称(mn.jpg)
			String srcFilename = srcFile.getName();
			//8. 创建目的地文件File对象,路径名是目的地目录+mn.jpg组成(..\\\hello java\\\mn.jpg)
			File destFile = new File(destFolder, srcFilename);
			//9. 复制文件
			copyFile(srcFile, destFile);

		}

	}

	private static void copyFile(File srcFile, File destFile) throws IOException {
		BufferedInputStream bis = new BufferedInputStream(new FileInputStream(srcFile));

		BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(destFile));

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

java 字符流 读取一行_java 字符流 读取一行_29

案例:复制多级文件夹

需求:把“E:\\itcast”复制到F盘目录下

思路:

  1. 创建数据源File对象,路径是E:\\itcast
  2. 创建目的地File对象,路径是F:\\
  3. 写方法实现文件夹的复制,参数为数据源File对象和目的地FIle对象
  4. 判断数据源File是否是目录
  • 是:
    1.在目的地下创建和数据源FIle名称一样的目录
    2.获取数据源File下所有的文件或者目录的File数组
    3.遍历该File数组,得到每一个File对象
    4.把该File作为数据源File对象,递归调用复制文件夹的方法
  • 不是
    说明是文件,直接复制,用字节流

java 字符流 读取一行_java 字符流 读取一行_30

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

/*
案例:复制多级文件夹
需求:把“E:\\itcast”复制到F盘目录下

思路:

 1. 创建数据源File对象,路径是E:\\itcast
 2. 创建目的地File对象,路径是F:\\
 3. 写方法实现文件夹的复制,参数为数据源File对象和目的地FIle对象
 4. 判断数据源File是否是目录
 	- 是:
		1.在目的地下创建和数据源FIle名称一样的目录
		2.获取数据源File下所有的文件或者目录的File数组
		3.遍历该File数组,得到每一个File对象
		4.把该File作为数据源File对象,递归调用复制文件夹的方法
 	- 不是
		说明是文件,直接复制,用字节流
*/

public class CopyFoldersDemo {
	public static void main(String[] args) throws IOException {
		//1. 创建数据源File对象,路径是E:\\itcast
		File srcFile = new File("E:\\itcast");
		//2. 创建目的地File对象,路径是F:\\
		File destFile = new File("F:\\");

		//3. 写方法实现文件夹的复制,参数为数据源File对象和目的地FIle对象
		copyFolder(srcFile, destFile);

	}

	//复制文件夹
	private static void copyFolder(File srcFile, File destFile) throws IOException {
		//4. 判断数据源File是否是目录
		if (srcFile.isDirectory()) {
			//1.在目的地下创建和数据源FIle名称一样的目录
			String srcFileName = srcFile.getName();
			File newFolder = new File(destFile, srcFileName);//F:\\itcast
			if (!newFolder.exists()) {
				newFolder.mkdir();
			}

			//2.获取数据源File下所有的文件或者目录的File数组
			File[] fileArray = srcFile.listFiles();

			//3.遍历该File数组,得到每一个File对象
			for (File file : fileArray) {
				//4.把该File作为数据源File对象,递归调用复制文件夹的方法
				copyFolder(file, newFolder);

			}

		} else {
			//说明是文件,直接复制,用字节流
			File newFile = new File(destFile, srcFile.getName());
			copyFile(srcFile, newFile);
		}

	}

	//字节缓冲流复制文件的方法
	private static void copyFile(File srcFile, File destFile) throws IOException {
		BufferedInputStream bis = new BufferedInputStream(new FileInputStream(srcFile));
		BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(destFile));

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

		bis.close();
		bos.close();

	}
}

运行结果:

java 字符流 读取一行_java_31


复制文件的异常处理

try…catch…finally的做法:

try{
	可能出现异常的代码;
	}catch(异常类名 变量名){
		异常的处理代码;
	}finally{
		执行所有清除操作;
	}

JDK7改进方案:

try(定义流对象){
	可能出现异常的代码;
}catch(异常类名 变量名){
	异常的处理代码;
}

自动释放资源

JDK9的改进方案:

定义输入流对象;
定义输出流对象;
try(输入流对象;输出流对象){
	可能出现异常的代码;
}catch(异常类名 变量名){
	异常的处理代码;
}

自动释放资源

演示代码:

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

/*
复制文件加入异常处理
*/
public class CopyFileDemo {
	public static void main(String[] args) {

	}

	//JDK9的改进方案
	private static void method4() throws IOException {
		FileReader fr = new FileReader("fr.txt");
		FileWriter fw = new FileWriter("fw.txt");
		try (fr; fw) {

			char[] chs = new char[1024];
			int len;
			while ((len = fr.read()) != -1) {
				fw.write(chs, 0, len);
			}
		} catch (IOException e) {
			e.printStackTrace();
		}

	}

	//JDK7的改进方案
	private static void method3() {
		try (FileReader fr = new FileReader("fr.txt"); FileWriter fw = new FileWriter("fw.txt");) {

			char[] chs = new char[1024];
			int len;
			while ((len = fr.read()) != -1) {
				fw.write(chs, 0, len);
			}
		} catch (IOException e) {
			e.printStackTrace();
		}

	}

	//try...catch...finally
	private static void method2() {
		FileReader fr = null;//放外面定义,并给初始化
		FileWriter fw = null;
		try {
			fr = new FileReader("fr.txt");
			fw = new FileWriter("fw.txt");

			char[] chs = new char[1024];
			int len;
			while ((len = fr.read()) != -1) {
				fw.write(chs, 0, len);
			}

		} catch (IOException e) {
			e.printStackTrace();
		} finally {//close出现问题,先判断不为空,再用try...catch处理
			if (fw != null) {

				try {
					fw.close();
				} catch (IOException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}
			//同理
			if (fr != null) {
				try {
					fr.close();
				} catch (IOException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}

		}
	}

	//抛出异常
	private static void method1() throws IOException {
		FileReader fr = new FileReader("fr.txt");
		FileWriter fw = new FileWriter("fw.txt");

		char[] chs = new char[1024];
		int len;
		while ((len = fr.read()) != -1) {
			fw.write(chs, 0, len);
		}

		fr.close();
		fw.close();
	}

}