黑马程序员——Java IO流 4——功能流

------- android培训java培训、期待与您交流! ----------

功能流是指提供特殊功能的流对象。

合并流

合并流主要是利用SequenceInputStream类的方法,API如下

SequenceInputStream
SequenceInputStream(Enumeration<InputStream> e)

构造方法,创建SequeneceInputStream对象

e:输入流的一个枚举

SequenceInputStream(InputStream s1,InputStream s2)

构造方法,创建SequeneceInputStream对象

s1:要读取的第一个输入流

s2:要读取的第二个输入流

int read(byte[] b)

从流中读取b.length个字节

b:存储读取数据的缓冲区

返回:读入缓冲区的字节总数,如果到达末尾则返回-1

此类用法的例子如下:

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.SequenceInputStream;
import java.util.Enumeration;
import java.util.Vector;

public class SequenceDemo {
	public static void main(String[] args) {
		//定义变量
		byte[] buf=new byte[1024];
		int len=0;
		FileOutputStream fos=null;
		SequenceInputStream sis=null;
		Vector<FileInputStream> v=new Vector<FileInputStream>();
		try {
			//创建3个FileInputStream对象放到Vector集合中
			v.add(new FileInputStream("1.txt"));
			v.add(new FileInputStream("2.txt"));
			v.add(new FileInputStream("3.txt"));
			
			//创建合并输入流对象和输出流对象
			Enumeration<FileInputStream> en=v.elements();				
			sis=new SequenceInputStream(en);
			fos=new FileOutputStream("4.txt");
			
			//将输入流的内容写入到输出流
			while((len=sis.read(buf))!=-1){
				fos.write(buf,0,len);
			}
		} catch (IOException e) {
			throw new RuntimeException(e);
		} finally {
			try {
				if (sis != null)
					sis.close();
				if (fos!=null) {
					fos.close();
				}
			} catch (IOException e) {
				throw new RuntimeException(e);
			}
		}
	}
}

切割文件

将一个图片文件切割成三份,然后再合并回来。
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.SequenceInputStream;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.Iterator;

public class SplitDemo {
	public static void main(String[] args) {
		splitFile();
		mergeFile();
	}
	//把一个MP3文件切割成三份。
	public static void splitFile(){
		int len=0;
		int number=0;
		byte[] buf=new byte[1024*1024];
		FileInputStream fis=null;
		FileOutputStream fos=null;
		try {
			fis=new FileInputStream("test.mp3");
			while((len=fis.read(buf))!=-1){
				fos=new FileOutputStream((++number)+".part");
				fos.write(buf,0,len);
				fos.close();
			}
		} catch (IOException e) {
			throw new RuntimeException(e);
		} finally {
			try {
				if (fis != null)
					fis.close();
				if (fos != null)
					fos.close();
			} catch (IOException e) {
				throw new RuntimeException(e);
			}
		}
		
	}
	//把切割后的碎片文件合并为一个文件
	public static void mergeFile(){
		int len=0;
		byte[] buf=new byte[1024];
		ArrayList<FileInputStream> ins=new ArrayList<FileInputStream>();
		FileOutputStream fos=null;
		SequenceInputStream sis=null;
		try {
			for (int i = 1; i <= 4; i++) {
				ins.add(new FileInputStream(i+".part"));
				
			}
			final Iterator<FileInputStream> it=ins.iterator();
			Enumeration<FileInputStream> en=new Enumeration<FileInputStream>() {

				@Override
				public boolean hasMoreElements() {
					return it.hasNext();
				}

				@Override
				public FileInputStream nextElement() {
					return it.next();
				}
			};
			sis=new SequenceInputStream(en);
			fos=new FileOutputStream("test1.mp3");
			while((len=sis.read(buf))!=-1){
				fos.write(buf,0,len);
			}

		} catch (IOException e) {
			throw new RuntimeException(e);
		} finally {
			try {
				if (sis != null)
					sis.close();
				if (fos!=null) {
					fos.close();
				}
			} catch (IOException e) {
				throw new RuntimeException(e);
			}
		}
	}
}

对象的序列化

对象的序列化主要用了下面的几个类、接口

class ObjectOutputStream
ObjectOutputStream(OutputStream out)

构造函数,创建一个ObjectOutputStream对象。

out:要写入数据的输出流

IOException:发生IO错误时触发

NotSerializableException:要序列化的对象没实现Serializable接口,不是必须要处理的异常

writeObject(Object obj)

将指定的对象写入ObjectOutputStream

obj:要写入的对象

IOException:由底层OutputStream抛出的任何异常

class ObjectInputStream
ObjectInputStream(InputStream in)

构造函数,创建一个ObjectInputStream对象。

in:要从中读取数据的输入流

IOException:发生IO错误时触发

Object readObject()

从ObjectInputStream读取对象。

IOException:发生IO错误时触发

ClassNotFoundException:找不到序列化对象的类

interface Serializable
static final long serialVersionUID 当一个类实现了Serializable接口时,在编译时会自动给该类添加一个serialVersionUID字段,并根据类里边的函数和字段的签名在运行时生成一个特定值。可以在实现类中覆盖此字段并将serialVersionUID写死。
用法如下,下面的代码,将Person类的对象保存到一个文件person.object中,然后再读出来:

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;

public class ObjectStreamDemo {
	public static void main(String[] args) {
		writeObject();
		readObject();
	}
	public static void readObject(){
		ObjectInputStream ois=null;
		try {
			ois=new ObjectInputStream(new FileInputStream("person.object"));
			Person p1=(Person)ois.readObject();
			System.out.println(p1);
			Person p2=(Person)ois.readObject();
			System.out.println(p2);
		} catch (IOException e) {
			throw new RuntimeException(e);
		} catch (ClassNotFoundException e) {
			throw new RuntimeException(e);
		} finally {
			try {
				if (ois!= null)
					ois.close();
			} catch (IOException e) {
				throw new RuntimeException(e);
			}
		}
	}
	public static void writeObject() {
		ObjectOutputStream oos = null;
		try {
			oos = new ObjectOutputStream(new FileOutputStream("person.object"));
			oos.writeObject(new Person("张三", 12));
			oos.writeObject(new Person("李四", 11));
		} catch (IOException e) {
			throw new RuntimeException(e);
		} finally {
			try {
				if (oos != null)
					oos.close();
			} catch (IOException e) {
				throw new RuntimeException(e);
			}
		}
	}
}
注意,传进writeObject方法的对象必须实现Serializable接口,否则会抛异常。可以同时保存和读取多个对象,readObject()方法执行时,读取对象时一个接一个,既第一次执行读取第一个,第二次执行读取第二个。还要注意readObject()方法会抛出ClassNotFoundException异常,这个异常必须处理,否则无法编译。

管道流

管道流主要利用了PipedInputStream和PipedOutputStream两个类,这两个类一般用在多线程中,例子如下:

import java.io.IOException;
import java.io.PipedInputStream;
import java.io.PipedOutputStream;

class Read implements Runnable
{
	PipedInputStream in=null;
	Read(PipedInputStream in){
		this.in=in;
	}
	@Override
	public void run() {
		byte[] buf=new byte[1024];
		int len;			
		try {
			System.out.println("开始读入");
			Thread.sleep(2000);
			len = in.read(buf);
			System.out.println("读入完毕");
		} catch (Exception e) {
			throw new RuntimeException(e);
		} finally {
			try {
				if (in != null)
					in.close();
			} catch (IOException e) {
				throw new RuntimeException(e);
			}
		}
		String s=new String(buf,0,len);
		System.out.println(s);
	}
}
class Write implements Runnable{
	PipedOutputStream out=null;
	Write(PipedOutputStream out){
		this.out=out;
	}
	public void run(){
		try {
			System.out.println("写出线程开始执行");
			Thread.sleep(2000);
			out.write("success!".getBytes());
			System.out.println("写出完毕");
		} catch (Exception e) {
			throw new RuntimeException(e);
		} finally {
			try {
				if (out != null)
					out.close();
			} catch (IOException e) {
				throw new RuntimeException(e);
			}
		}
	}
}
public class PipedStreamDemo {
	@SuppressWarnings("resource")
	public static void main(String[] args) {
		PipedInputStream in=new PipedInputStream();
		PipedOutputStream out=new PipedOutputStream();
		try {
			in.connect(out);
			Read r=new Read(in);
			Write w=new Write(out);
			new Thread(r).start();
			new Thread(w).start();
			
		} catch (IOException e) {
			throw new RuntimeException(e);
		}
	}	
}
运行结果如下:

开始读入
写出线程开始执行
写出完毕
读入完毕
success!

写出线程开始执行
开始读入
写出完毕
读入完毕
success!

RandomAccessFile

RandomAccessFile类是用于随机访问文件的类,意思是不是按顺序读写文件数据,而是可以从任意为值读写数据,因此用它就可以多线程读写。该类不算是IO体系中的子类,而是继承Object。但它属于java.io包中的成员,所以它的作用还是和IO有关。它内部封装了一个数组,通过指针对数组元素进行操作,从而读写数据。可以通过getFilePointer()方法获取指针位置,通过seek()方法改变指针位置。它内部封装了字节输入流和字节输出流。

RandomAccessFile
RandomAccessFile(String name,String mode) 构造函数,name为文件路径,mode为模式,主要有"r"读,"rw"读写。如果文件不存在,当为“r”模式时,会出现异常,如果此时为“rw”模式,则会自动创建文件。
RandomAccessFile(File file,String mode) 构造函数,file为文件对象,mode为模式,主要有"r"读,"rw"读写。如果文件不存在,当为“r”模式时,会出现异常,如果此时为“rw”模式,则会自动创建文件。
write(byte[] b) 写入b.length个字节数据
writeInt(int n) 按4个字节将n写入
read(byte[] b) 读取b.length个字节数据
readInt() 读取4个字节并解码为32位整型
seek(int n) 从n指定字节位置处开始读写,由此可实现多线程读写。当写数据时,如果n指定位置已有数据,则会覆盖原来的数据。
skipBytes(int n) 尝试跳过n个字节
该类用法例子如下:

import java.io.IOException;
import java.io.RandomAccessFile;

public class RandomAccessFileDemo {
	public static void main(String[] args) {
		writeFile();
		readFile();
	}
	public static void readFile(){
		RandomAccessFile raf=null;
		try {
			raf=new RandomAccessFile("randomData.txt", "rw");
			byte[] buf=new byte[4];
			raf.read(buf);
			int age=raf.readInt();
			System.out.println("name="+new String(buf)+" age="+age);
			raf.seek(8);
			byte[] buf1=new byte[4];
			raf.read(buf1);
			int age1=raf.readInt();
			System.out.println("name="+new String(buf1)+" age="+age1);
		} catch (IOException e) {
			throw new RuntimeException(e);
		} finally {
			try {
				if (raf != null)
					raf.close();
			} catch (IOException e) {
				throw new RuntimeException(e);
			}
		}
	}
	public static void writeFile(){
		RandomAccessFile raf=null;
		try {
			raf=new RandomAccessFile("randomData.txt", "rw");
			raf.write("张三".getBytes());
			raf.writeInt(97);
			raf.seek(8*2);
			raf.write("李四".getBytes());
			raf.writeInt(99);
			raf.seek(8*0);
			raf.write("张一".getBytes());
			raf.writeInt(96);
		} catch (IOException e) {
			throw new RuntimeException(e);
		} finally {
			try {
				if (raf != null)
					raf.close();
			} catch (IOException e) {
				throw new RuntimeException(e);
			}
		}
	}
}

程序输出结果:

name=张一 age=96
name=李四 age=99

之所以第一行name=张一而不是name=张三,就是因为seek()方法把指针重新设置会了起始位置,导致覆盖了原有数据。

DataStream

DataStream包括DataInputStream和DataOutputStream,这些流对象可以操作基本数据类型,以及以UTF-8修改版,读写字符串。示例代码如下:
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;

public class DataStreamDemo {
	public static void main(String[] args) {
		writeBasicData();
		readBasicData();
	}
	public static void readBasicData(){
		DataInputStream dis=null;
		try {
			dis=new DataInputStream(new FileInputStream("DataStreamDemo.txt"));
			Boolean a= dis.readBoolean();
			int b=dis.readInt();
			String c=dis.readUTF();
			System.out.println("a="+a);
			System.out.println("b="+b);
			System.out.println("c="+c);
		} catch (IOException e) {
			throw new RuntimeException(e);
		} finally {
			try {
				if (dis != null)
					dis.close();
			} catch (IOException e) {
				throw new RuntimeException(e);
			}
		}
		
	}
	public static void writeBasicData(){
		DataOutputStream dos=null;
		try {
			dos=new DataOutputStream(new FileOutputStream("DataStreamDemo.txt"));
			dos.writeBoolean(true);
			dos.writeInt(4);
			dos.writeUTF("你好");
		} catch (IOException e) {
			throw new RuntimeException(e);
		} finally {
			try {
				if (dos != null)
					dos.close();
			} catch (IOException e) {
				throw new RuntimeException(e);
			}
		}		
	}
}
注意,如果用writeUTF()方法向文本文件中写入字符,用记事本打开时,前几个字节会乱码。

ByteArrayStream

ByteArrayStream包括ByteArrayInputStream和ByteArrayOutputStream两个流对象,这两个流对象的特点是,不需要close()方法关闭,不抛出IOException异常。这两个流对象内部都封装了一个可变长度的数组,ByteArrayInputStream对象的操作实际上相当于对一个字节数组的读取访问,ByteArrayOutputStream对象的操作实际上相当于对一个字节数组进行赋值。相对于字节数组的不一样的地方是,ByteArrayStream中的字节数组可变长度,根据输入数据的多少自动改变字节数组长度。封装的read方法和遍历角标的方法不一样。

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;

public class MemoryDemo {
	public static void main(String[] args) {
		ByteArrayInputStream bais=new ByteArrayInputStream("agsrdadf".getBytes());
		ByteArrayOutputStream bois=new ByteArrayOutputStream();
		int temp=0;
		while((temp=bais.read())!=-1){
			bois.write(temp);
		}
		System.out.println(bois.size());
		System.out.println(bois.toString());
	}
}
size()方法用于获取ByteArrayOutputStream中封装的字节数组的长度,toString()方法用于将字节数组转化为字符串。

还有一些流对象如CharArrayReader和CharArrayWriter、StringReader和StringWriter都是对内存中的数据进行读写,用法类似,不再举例。

字符编码

字符编码(encoding),也可以叫做字符集(charset)。可以通过两个转换流对象来进行编码转换,即InputStreamReader和OutputStreamWriter。编码表即字符集与二进制数据的的对应关系。常见的编码表有如下几个:

名称 文字 描述
ASCII 英文 1967年发布,用1个字节的7位表示1个字符。编码表见这里
ISO8859-1 拉丁文 1985年发布,用1个字节表示1个字符,主要用于西欧语言。兼容ASCII。
GB2312 简体中文 1980年发布,用2个字节表示1个字符。6763个汉字。
GBK 简体中文

1995年发布,用2个字节表示1个字符

,兼容GB2312。21003个汉字。

GB18030 简体中文 2000年发布,用1、2、4个字节表示1个字符,兼容ASCII、GBK、Unicode。70244个汉字。
Unicode 世界主要文字 1991年发布,用2个字节表示1个字符。
UTF-8 世界主要文字 1996年发布,用1、2、3个字节表示1个字符。兼容ASCII。
英文版Windows操作系统默认字符编码是ISO8859-1,简体中文版Windows操作系统的默认字符编码是GBK,由于JDK的默认字符编码引用的是系统的默认字符编码,所以此时JDK的默认字符编码也是GBK。记事本的ANSI编码指的是引用Windows操作系统默认字符编码,所以简体中文Windows操作系统下,记事本的ANSI编码就是GBK。

下面的程序,用UTF-8编码把“你好”两个字写入到一个文本文件中,再用UTF-8编码把“你好”两个字读取到程序的字符串中,并输出。

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

public class CharsetDemo {
	public static void main(String[] args) {
		writeData();
		readData();
	}
	public static void readData(){
		InputStreamReader isr=null;
		try {
			isr=new InputStreamReader(new FileInputStream("utf8.txt"),"UTF-8");
			char[] buf=new char[10];
			int len=isr.read(buf);
			String str=new String(buf,0,len);
			System.out.println(str);
		} catch (IOException e) {
			throw new RuntimeException(e);
		} finally {
			try {
				if (isr != null)
					isr.close();
			} catch (IOException e) {
				throw new RuntimeException(e);
			}
		}
	}
	public static void writeData(){
		OutputStreamWriter osw=null;
		try {
			osw=new OutputStreamWriter(new FileOutputStream("utf8.txt"),"UTF-8");
			osw.write("你好");
		} catch (IOException e) {
			throw new RuntimeException(e);
		} finally {
			try {
				if (osw != null)
					osw.close();
			} catch (IOException e) {
				throw new RuntimeException(e);
			}
		}		
	}
}
上述程序中,如果读取的时候,用GBK解码,则会出现乱码,既不能识别含义的字符串。原因是UTF-8编码时,会把“你好”两个字编码为六个字节,存到文本文件中,如果用GBK进行解码,则会把六个字节解码为3个字符,造成乱码。
上述程序原理图如下:

黑马程序员——Java IO流 4——功能流_第1张图片


编码和解码

编码:字符串变成字节数组、

String→byte[]:  str.getBytes(charsetName);

解码:字节数组变成字符串

byte[]→String:new String(byte[],charsetName);

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

public class EncodeAndDecodeDemo {
	public static void main(String[] args) {
		test5();
	}
	public static void test1(){
		String str="你好";
		byte[] code=null;
		
		//默认字符编码是GBK
		code=encode(str, "");
		System.out.println("\""+str+"\""+"经默认字符编码编码后为:"+Arrays.toString(code));
		str=decode(code, "");
		System.out.println(Arrays.toString(code)+"经默认字符编码解码后为:\""+str+"\"");
	}
	public static void test2(){
		String str="你好";
		byte[] code=null;
		
		//GBK编解码
		code=encode(str,"GBK");
		System.out.println("\""+str+"\""+"经GBK编码后为:"+Arrays.toString(code));
		str=decode(code, "GBK");
		System.out.println(Arrays.toString(code)+"经GBK解码后为:\""+str+"\"");
	}
	public static void test3(){
		String str="你好";
		byte[] code=null;
		
		//UTF-8编解码
		code=encode(str,"UTF-8");
		System.out.println("\""+str+"\""+"经GBK编码后为:"+Arrays.toString(code));
		str=decode(code, "UTF-8");
		System.out.println(Arrays.toString(code)+"经GBK解码后为:\""+str+"\"");
	}
	public static void test4(){
		String str="你好";
		byte[] code=null;		
		
		//解错码时如何处理
		code=encode(str,"GBK");
		System.out.println("\""+str+"\""+"经GBK编码后为:"+Arrays.toString(code));
		str=decode(code, "ISO8859-1");
		System.out.println(Arrays.toString(code)+"经ISO8859-1解码后为:\""+str+"\"");
		code=encode(str,"ISO8859-1");
		System.out.println("\""+str+"\""+"经ISO8859-1编码后为:"+Arrays.toString(code));
		str=decode(code, "GBK");
		System.out.println(Arrays.toString(code)+"经GBK解码后为:\""+str+"\"");
	}
	public static void test5(){
		String str="你好";
		byte[] code=null;		
		
		//解错码时如何处理,此时行不通
		code=encode(str,"GBK");
		System.out.println("\""+str+"\""+"经GBK编码后为:"+Arrays.toString(code));
		str=decode(code, "UTF-8");
		System.out.println(Arrays.toString(code)+"经UTF-8解码后为:\""+str+"\"");
		code=encode(str,"UTF-8");
		System.out.println("\""+str+"\""+"经UTF-8编码后为:"+Arrays.toString(code));
		str=decode(code, "GBK");
		System.out.println(Arrays.toString(code)+"经GBK解码后为:\""+str+"\"");
	}
	/**
	 * 给字符串编码
	 * @param str 要编码的字符串
	 * @param charset 字符编码
	 * @return 编码后的字节数组的十进制表示形式
	 */
	public static byte[] encode(String str,String charset){
		byte[] code=null;
		try {
			if (charset.equals("")) {
			    code=str.getBytes();
			}else {
			    code=str.getBytes(charset);
			}

		} catch (UnsupportedEncodingException e) {
			System.err.println("本程序不支持"+charset+"编码");
		}
		return code;	
	}
	/**
	 * 给字符串解码
	 * @param code 要解码的字节数组
	 * @param charset 字符编码
	 * @return 解码后的字符串
	 */
	public static String decode(byte[] code,String charset){
		String str="";
		try {
			if (charset.equals("")) {
				str=new String(code);
			}else{
				str=new String(code,charset);
			}
			
		} catch (UnsupportedEncodingException e) {
			System.err.println("本程序不支持"+charset+"编码");
		}
		return str;
	}
}

Windows记事本的编码

问题:首先新建一个txt文件,在里边输入“联通”两个字,然后保存,关闭记事本,然后再用记事本打开此文件,会发现txt文件中的文字变为乱码。

原因:当用记事本默认的保存功能保存文字时(既用ANSI编码保存文本时),txt文本文件本身不会保存任何的字符编码信息,只会保存字符编码后的二进制数据,这样就会造成,记事本打开一个文本文件时不知道用什么字符集,所以就运行一个算法来猜字符集,这个算法首先考察前几个字节的数据是否符合UTF-8的特点,如果符合,就把整个文件以UTF-8读取进记事本显示出来,如果不符合就以ANSI编码读进记事本。而“联通”两个字的4字节二进制数据恰巧符合UTF-8的特点,所以此时记事本以UTF-8解码GBK编码的字符,所以出现乱码。解决此问题的方法是,用记事本保存文本时,选择另存为UTF-8,这样记事本就会在txt文件头部加一个标识,从而下一次用记事本打开此文件时,可以识别此文本文件的字符编码。EditPlus也存在此问题。写字板和记事本不存在此问题。

小知识,记事本中的ANSI编码指的是随操作系统语言变化的本地字符编码,比如如果当前操作系统的语言是简体中文,则ANSI编码就是GBK,如果当前操作系统的语言是英文,则ANSI编码就是ISO8859-1,如果当前操作系统的语言是繁体中文,则ANSI编码就是BIG5。

综合练习

题目:有5个学生,每个学生有3门课的成绩,从键盘输入以上数据,输入格式:如:zhangsan,30,40,50,计算出总成绩,并把学生信息和计算出的总分数按从高到低的书序放在student.txt文件中。

面向对象分析:

1,需要一个类描述学生对象

2,需要一个工具类保存操作学生对象的方法

面向对象设计:

1,通过获取键盘录入一行数据,并将该行中的信息取出封装成学生对象

2,因为学生有很多,所以需要使用集合,因为要排序,所以用TreeSet集合

3,将集合信息写入一个文件

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.Collections;
import java.util.Comparator;
import java.util.Set;
import java.util.TreeSet;


class Student implements Comparable<Student>{
	String name;
	int math,chinese,english;
	int sum;
	public Student(String name,int math,int chinese,int english) {
		this.name=name;
		this.math=math;
		this.chinese=chinese;
		this.english=english;
		this.sum=math+chinese+english;
	}
	public String getName(){
		return this.name;
	}
	public int getSum(){
		return sum;
	}
	@Override
	public int compareTo(Student s) {
		int num=new Integer(this.sum).compareTo(new Integer(s.sum));
		if (num==0) {
			return this.name.compareTo(s.name);
		}
		return num;
	}
	public int hashCode(){
		return name.hashCode()+sum*78;
	}
	public boolean equals(Object obj){
		if (!(obj instanceof Student)) {
			throw new ClassCastException("类型不匹配");
		}
		Student s=(Student)obj;
		return this.name.equals(s.name)&&this.sum==s.sum;
	}
	public String toString(){
		return "student["+this.name+", "+this.math+", "+this.chinese+", "+this.english+"]";
	}
}
class StudentInfoTool
{
	public static Set<Student> getStudents(){
		return getStudents(null);
	}
	
	public static Set<Student> getStudents(Comparator<Student> cmp){
		BufferedReader br=null;
		Set<Student> stus=null;
		if (cmp==null) {
			stus=new TreeSet<Student>(); 
		}else {
			stus=new TreeSet<Student>(cmp);
		}
		try {
			br=new BufferedReader(new InputStreamReader(System.in));
			String str=null;
			while((str=br.readLine())!=null){
				if ("over".equals(str)) {
					break;
				}
				String[] info=str.split(",");
				Student stu=new Student(info[0], 
						Integer.parseInt(info[1]), 
						Integer.parseInt(info[2]), 
						Integer.parseInt(info[3]));
				stus.add(stu);
			}
		} catch (IOException e) {
			throw new RuntimeException(e);
		} finally {
			try {
				if (br != null)
					br.close();
			} catch (IOException e) {
				throw new RuntimeException(e);
			}
		}
		return stus;
	}
	
	public static void write2File(Set<Student> stus){
		BufferedWriter bw=null;
		try {
			bw=new BufferedWriter(new FileWriter("student.txt"));
			for (Student student : stus) {
				bw.write(student.toString()+"\t");
				bw.write(student.getSum()+"\n");
			}
		} catch (IOException e) {
			throw new RuntimeException(e);
		} finally {
			try {
				if (bw != null)
					bw.close();
			} catch (IOException e) {
				throw new RuntimeException(e);
			}
		}
	}
}
public class ComprehensiveExercise {
	public static void main(String[] args) {
		Set<Student> stus=new TreeSet<Student>();
		Comparator<Student> cmp=Collections.reverseOrder();
		stus=StudentInfoTool.getStudents(cmp);
		StudentInfoTool.write2File(stus);
	}
}


你可能感兴趣的:(黑马程序员——Java IO流 4——功能流)