第一 打印流
该流提供了打印方法,可以将各种数据类型的数据都原样打印
原理将97先变成字符保持原样将数据打印到目的地
1、字节打印流:PrintStream 构造函数可以接收的参数类型:
1)file对象。File
2)字符串路径。String
3)字节输出流。OutputStream
2、字符打印流:PrintWriter 构造函数可以接收的参数类型:
1)file对象。File
2)字符串路径。String
3)字节输出流。OutputStream
4)字符输出流,Writer。
private static void controlToC() throws IOException {BufferedReader bf = new BufferedReader(new InputStreamReader(System.in));PrintWriter pw = new PrintWriter(System.out);String line = null;while ((line = bf.readLine()) != null) {if ("over".equals(line)) {break;}pw.println(line.toUpperCase());pw.flush();}pw.close();bf.close();}

public static void main(String[] args) throws IOException {BufferedReader bufr = new BufferedReader(new InputStreamReader(System.in));- //特有构造器,turn就自动刷新,可是写到文件中不能自动刷新new FileWriter("out.txt"),所以写到一个输出流中,完成自动刷新
PrintWriter out = new PrintWriter(new FileWriter("out.txt"),true);String line = null;while((line=bufr.readLine())!=null){if("over".equals(line))break;out.println(line.toUpperCase());// out.flush();}out.close();bufr.close();}}
Properties getProperties()
2)将信息输出到指定输出流中
void list(PrintStream out)
3)将输出流中数据存入指定文件中
new PrintStream("systeminfo.txt")
- public class SysInfo {
public static void main(String[] args) {Properties pro = System.getProperties();try {pro.list(new PrintStream("sysinfo.txt"));// list方法将属性列表输出到指定的输出流。} catch (FileNotFoundException e) {e.printStackTrace();}}}
第二 合并流(序列流)
一、概述:
SequenceInputStream 表示其他输入流的逻辑串联。它从输入流的有序集合开始,并从第一个输入流开始读取,直到到达文件末尾,接着从第二个输入流读取,依次类推,直到到达包含的最后一个输入流的文件末尾为止。
二、如何合并多个文件:
1、创建集合,并将流对象添加进集合或者SequenceInputStream(InputStream s1,InputStream s2)加入两个输入流
2、创建Enumeration对象,将集合元素加入。
3、创建SequenceInputStream对象,合并流对象
4、创建写入流对象,FileOutputStream,将数据写入流资源
5、定义数组,将读取流数据存入数组,并将数组中元素写入文件中。
public class SequenceInputStreamDemo {/** 需求:将1.txt 2.txt 3.txt文件中的数据合并到一个文件中。*/Vector(枚举)中才有Enumeration// Vector<FileInputStream> v = new Vector<FileInputStream>();// v.add(new FileInputStream("1.txt"));// v.add(new FileInputStream("2.txt"));// v.add(new FileInputStream("3.txt"));// Enumeration<FileInputStream> en = v.elements(); //这样就获取了枚举了//枚举和迭代一样,枚举没过时,过时是编译器不认识了,不过 ArrayList有效率ArrayList<FileInputStream> al = new ArrayList<FileInputStream>();for(int x=1; x<=3; x++){al.add(new FileInputStream(x+".txt"));}Enumeration<FileInputStream> en = Collections.enumeration(al);//有个返回枚举的方法,它的原理就是下边这个/*final Iterator<FileInputStream> it = al.iterator(); //内部类访问局部变量用finalEnumeration<FileInputStream> en = new Enumeration<FileInputStream>(){//new了个 Enumeration,匿名内部类//枚举接口的实现中利用迭代器,这样写比较麻烦,不懂,参见io流--51@Overridepublic boolean hasMoreElements() {return it.hasNext();}@Overridepublic FileInputStream nextElement() {return it.next();}};*///合并SequenceInputStream sis = new SequenceInputStream(en);FileOutputStream fos = new FileOutputStream("1234.txt");byte[] buf = new byte[1024];int len = 0;while((len=sis.read(buf))!=-1){fos.write(buf,0,len);}fos.close();sis.close();}}
* 文件的切割和合并*/public class SplitFile {public static void main(String[] args) throws IOException {// splitFile();sequence();}/** 切割文件*/public static void splitFile() throws IOException{FileInputStream fis = new FileInputStream("0.jpg");FileOutputStream fos = null;byte[] byf = new byte[1024*1024];//定义需要切割大小的空间int len = 0;int count = 1;//为文件取名用的数字while((len=fis.read(byf)) != -1){fos = new FileOutputStream((count++) + ".patch");//创建指定的文件fos.write(byf,0,len);//把读取到的数据写入指定的文件中fos.close();//关闭资源}fis.close();//关闭资源}/** 合并文件*/public static void sequence() throws IOException{ArrayList<FileInputStream> list = new ArrayList<FileInputStream>();list.add(new FileInputStream("1.patch"));list.add(new FileInputStream("2.patch"));list.add(new FileInputStream("3.patch"));//通过迭代器方法获取枚举final Iterator<FileInputStream> it = list.iterator();//内部类使用的成员变量必须是final修饰的//通过内部类实现枚举Enumeration<FileInputStream> en = new Enumeration<FileInputStream>() {@Overridepublic FileInputStream nextElement() {return it.next();}@Overridepublic boolean hasMoreElements() {return it.hasNext();}};SequenceInputStream sequence = new SequenceInputStream(en);FileOutputStream fos = new FileOutputStream("10.jpg");byte[] byf = new byte[1024];int len = 0;while((len=sequence.read(byf)) != -1){fos.write(byf,0,len);}sequence.close();fos.close();}}
public class SplitFileDemo {private static final int SIZE = 1024 * 1024;public static void main(String[] args) throws Exception {File file = new File("c:\aa.mp3");splitFile_2(file);}// 修改后private static void splitFile_2(File file) throws IOException {// 用读取流关联源文件。FileInputStream fis = new FileInputStream(file);// 定义一个1M的缓冲区。byte[] buf = new byte[SIZE];// 创建目的。FileOutputStream fos = null;int len = 0;int count = 1;/** 切割文件时,必须记录住被切割文件的名称,以及切割出来碎片文件的个数。 以方便于合并。* 这个信息为了进行描述,使用键值对的方式。用到了properties对象*/Properties prop = new Properties();File dir = new File("c:\partfiles");// 将碎片放到固定文件夹中,也可以不写if (!dir.exists())dir.mkdirs();while ((len = fis.read(buf)) != -1) {fos = new FileOutputStream(new File(dir, (count++) + ".part"));// 不能写new// FileOutputStream("1.txt"),问题1:第二次new把第一次new的覆盖了,所有1不能写死,还要合并。// 问题2:扩展名不能写死,碎片文件不能阅读fos.write(buf, 0, len);fos.close();}// 将被切割文件的信息保存到prop集合中。prop.setProperty("partcount", count + "");// 碎片文件的个数prop.setProperty("filename", file.getName());// 文件的名字fos = new FileOutputStream(new File(dir, count + ".properties"));// 将prop集合中的数据存储到文件中。prop.store(fos, "save file info");//fos.close();fis.close();}// 原始:public static void splitFile(File file) throws IOException {// 用读取流关联源文件。FileInputStream fis = new FileInputStream(file);// 定义一个1M的缓冲区。byte[] buf = new byte[SIZE];// 创建目的。FileOutputStream fos = null;int len = 0;int count = 1;File dir = new File("c:\partfiles");if (!dir.exists())dir.mkdirs();while ((len = fis.read(buf)) != -1) {fos = new FileOutputStream(new File(dir, (count++) + ".part"));fos.write(buf, 0, len);}fos.close();fis.close();}}
public class MergeFile {public static void main(String[] args) throws IOException {File dir = new File("c:\partfiles");mergeFile_2(dir);}public static void mergeFile(File dir) throws IOException {ArrayList<FileInputStream> al = new ArrayList<FileInputStream>();for (int x = 1; x <= 3; x++) {al.add(new FileInputStream(new File(dir, x + ".part")));}Enumeration<FileInputStream> en = Collections.enumeration(al);SequenceInputStream sis = new SequenceInputStream(en);FileOutputStream fos = new FileOutputStream(new File(dir, "1.bmp"));// 合并到当前目录下byte[] buf = new byte[1024];int len = 0;while ((len = sis.read(buf)) != -1) {fos.write(buf, 0, len);}fos.close();sis.close();}}

public static void mergeFile_2(File dir) throws IOException {/** 获取指定目录下的配置文件对象。*/File[] files = dir.listFiles(new SuffixFilter(".properties"));//过滤器,过滤文件名if(files.length!=1)throw new RuntimeException(dir+",该目录下没有properties扩展名的文件或者不唯一");//自定义异常//记录配置文件对象。File confile = files[0];//获取该文件中的信息================================================。Properties prop = new Properties();FileInputStream fis = new FileInputStream(confile);prop.load(fis);//流中信息加载进来String filename = prop.getProperty("filename");int count = Integer.parseInt(prop.getProperty("partcount"));//获取该目录下的所有碎片文件。 ==============================================File[] partFiles = dir.listFiles(new SuffixFilter(".part"));if(partFiles.length!=(count-1)){throw new RuntimeException(" 碎片文件不符合要求,个数不对!应该"+count+"个");//如果少了一个}//将碎片文件和流对象关联 并存储到集合中。ArrayList<FileInputStream> al = new ArrayList<FileInputStream>();for(int x=0; x<partFiles.length; x++){al.add(new FileInputStream(partFiles[x]));}//将多个流合并成一个序列流。Enumeration<FileInputStream> en = Collections.enumeration(al);SequenceInputStream sis = new SequenceInputStream(en);FileOutputStream fos = new FileOutputStream(new File(dir,filename));byte[] buf = new byte[1024];int len = 0;while((len=sis.read(buf))!=-1){fos.write(buf,0,len);}fos.close();sis.close();}}
第三 Objectstream(对象流)
一、概述:
ObjectStream是可以操作对象的流。它的写方法是ObjectOutputStream,读方法是ObjectInputStream。它主要操作的是对象,而对象中也能封装数据,所以它也具备操作基本数据类型的方法。被它操作的对象必须是实现了序列化的对象也就是Serializable接口,但是输入流还多支持一种Externalizable 接口的对象。持久化

实例:
- * 对对象进行读写操作
public class ObjectstreamDemo {public static void main(String[] args) throws Exception {// writeObj();readObj();}/** 写对象*/public static void writeObj() throws Exception{ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("obj.object"));//仅仅是把对内存的对象存在硬盘上,下次拿来用,不需要用记事本解析oos.writeObject(new Person("zhangsan", 23, "ASC"));oos.close();}/** 读取对象 ,读出来不是一个组合的对象,读出是数据.注意:没class文件,取不出来*/public static void readObj() throws Exception{ObjectInputStream ois = new ObjectInputStream(new FileInputStream("obj.object"));// 他是person的属性Person p = (Person) ois.readObject();System.out.println(p);ois.close();}}
*
* Serializable:用于给被序列化的类加入ID号。
* 用于判断类和对象是否是同一个版本。
* 静态不能被序列化* transient关键字修饰的不能被序列化
public class Person implements Serializable///*标记接口*/实现这个接口,让该类被序列化{//给类指定一个序列化标识,方便序列化,不定义会自动生成一个,如果不定义序列化,改变属性就会读不出来private static final long serialVersionUID = 42L;private String name; //private transient String name;private int age;private static String country = "cn";Person(String name, int age, String country){this.name = name;this.age = age;Person.country = country;}public String toString(){return name+":"+age+":"+country;}}
第四 RandomAccessFile
一、概述:
1、RandomAccessFile称之为随机访问文件的类,自身具备读写方法。
2、该类不算是IO体系中的子类,而是直接继承Object,但是它是IO包成员,因为它具备读写功能,内部封装了一个数组,且通过指针对数组的元素进行操作,同时可通过seek改变指针的位置。
3、可以完成读写的原理:内部封装了字节输入流
4、构造函数:RandomAccessFile(File file,String mode),可已从它的构造函数中看出,该类只能操作文件(也有字符串),而且操作文件还有模式。
模式传入值:”r“:以只读方式打开;”rw“:打开以便读写
如果模式为只读,则不会创建文件,会去读一个已存在的文件,若文件不存在,则会出现异常,如果模式为rw,且该对象的构造函数要操作的文件不存在,会自动创建,如果存在,则不会覆盖,也可通过seek方法修改。
二、特有方法:
1、seek(int n):设置指针,可以将指针设置到前面或后面
2、skipBytes(int n):跳过指定字节数,不可往前跳
实例:
public class RandomAccessFileDemo {public static void main(String[] args) throws IOException {writeFile();// readFile();}public static void writeFile() throws IOException{RandomAccessFile raf = new RandomAccessFile("random.txt","rw");//如果文件不存在,则创建,如果文件存在,不创建,把下边的代码注释掉,再读这些东西还在raf.write("张三".getBytes()); //内部封装的是一个字节数组,转成字节数组raf.writeInt(97);//write方法只读取最低8位,用writeintraf.write("李四".getBytes()); //不是覆盖,是接着写raf.writeInt(98); //不是覆盖,是接着写//********往指定位置上存储数据,也可以修改数据raf.seek(8*4);raf.write("王五".getBytes());raf.writeInt(103);raf.close();}public static void readFile() throws IOException{RandomAccessFile raf = new RandomAccessFile("random.txt","r");//调整对象中的指针// raf.seek(8*1);//根据数组定,数组大小*跳几个//跳过指定的字节数,只能向前不能向后raf.skipBytes(8);byte[] buf = new byte[4];raf.read(buf);String name = new String(buf);int age = raf.readInt();System.out.println(name + " : " + age);raf.close();}}
第五 管道流
可以将管道输出流连接到管道输入流来创建通信管道。管道输出流是管道的发送端。通常,数据由某个线程写入PipedOutputStream 对象,并由其他线程从连接的PipedInputStream 读取。不建议对这两个对象尝试使用单个线程,因为这样可能会造成该线程死锁。如果某个线程正从连接的管道输入流中读取数据字节,但该线程不再处于活动状态,则该管道被视为处于毁坏状态(read是阻塞状态,他会一直等)
二、使用步骤:
1、要先创建一个读和写的两个类,实现Runnable接口,因为是两个不同的线程,覆盖run方法,注意,需要在内部处理异常,因为重写run方法
2、创建两个管道流,并用connect()方法将两个流连接
3、创建读写对象,并传入两个线程内,并start执行
- /
* 管道流* 用多线操作,单线程容易造成死锁*/public class PipedStreamDemo {public static void main(String[] args) throws IOException {PipedInputStream in = new PipedInputStream();PipedOutputStream out = new PipedOutputStream();in.connect(out); //将俩个线程连接起来,谁连谁都一样Read r = new Read(in);Write w = new Write(out);new Thread(r).start();new Thread(w).start();}}class Write implements Runnable {private PipedOutputStream out;Write(PipedOutputStream out) {this.out = out;}@Overridepublic void run() {try {Thread.sleep(4000);out.write("piped stream come".getBytes());out.close();} catch (IOException e) {e.printStackTrace();} catch (InterruptedException e) {e.printStackTrace();}}}class Read implements Runnable {private PipedInputStream in;Read(PipedInputStream in) {this.in = in;}@Overridepublic void run() {byte[] byf = new byte[1024];int len = 0;try {while ((len = in.read(byf)) != -1) {String str = new String(byf, 0, len);System.out.println(str);}} catch (IOException e) {e.printStackTrace();}}}
第六 DataStream(操作基本类型的流)
一、概述:
DataStream是可以用于操作基本数据类型的数据的流对象:DataInputStream与DataOutputStream,它主要的特点就是操作基本数据类型。
实例:
* DataInputStream与DataOutputStream** 可以用于操作基本数据类型的数据的流对象。*/public class DataStreamDemo {public static void main(String[] args) throws IOException {// write();// read();// writeUTF();readUTF();//一般加入编码的方法// OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("utf.txt"),"utf-8");// osw.write("你好");// osw.close();}public static void write() throws IOException{DataOutputStream dos = new DataOutputStream(new FileOutputStream("data.txt"));dos.writeInt(236);dos.writeBoolean(false);dos.writeDouble(2323.02154);dos.close();}public static void read() throws IOException{DataInputStream dis = new DataInputStream(new FileInputStream("data.txt"));int i = dis.readInt();boolean b = dis.readBoolean();double d = dis.readDouble();System.out.println("int: " + i);System.out.println("boolean: " + b);System.out.println("double: " + d);dis.close();}//加入了改编后的UTF-8编码,所以只有它自己能读public static void writeUTF() throws IOException{DataOutputStream dos = new DataOutputStream(new FileOutputStream("data.txt"));dos.writeUTF("你家婆");dos.close();}public static void readUTF() throws IOException{DataInputStream dis = new DataInputStream(new FileInputStream("utf.txt"));String s = dis.readUTF();System.out.println(s);dis.close();}}
第七 ByteArraytStream(操作数组的流)
一、概述:ByteArrayInputStream :在构造的时候,需要接收数据源,而且数据源是一个字节数组。
ByteArrayOutputStream: 在构造的时候,不用定义数据目的,因为该对象中已经内部封装了可变长度的字节数组,这就是数据目的地。
参数是字节数组,访问网络写的是字节,需要new string(bos.tobyteArray)
注:因为这两个流对象都操作的数组,并没有使用系统资源。所以,不用进行close关闭,而且关闭也是无效的。
源设备,
键盘 System.in,硬盘 FileStream,内存 ArrayStream
目的设备:
控制台 System.out,硬盘FileStream,内存 ArrayStream。
public class ByteArrayStreamDemo {public static void main(String[] args) {//数据源ByteArrayInputStream bis = new ByteArrayInputStream("defef".getBytes());//目的地ByteArrayOutputStream bos = new ByteArrayOutputStream();int ch = 0;while((ch=bis.read()) != -1){bos.write(ch);}System.out.println(bos.size());System.out.println(bos.toString());try {//将此 byte 数组输出流的全部内容写入到指定的输出流参数中bos.writeTo(new FileOutputStream("b.txt"));} catch (FileNotFoundException e) {e.printStackTrace();} catch (IOException e) {e.printStackTrace();}}}
第八 字符编码
一、‘常见的编码表:
1、ASCII:美国标准信息交换码表。用一个字节的7位表示
2、IOS8859-1:拉丁码表;欧洲码表。用一个字节的8位表示
3、GB2312:中国的中文编码表
4、GBK:中国的中文编码表升级,融合了更多的中文文字字符。打头的是两个高位为1的两个字节编码。为负数
5、Unicode:国际标准码,融合了多种文字
6、UTF-8:升级版国际码表,是可变长度的码表。最多用三个字节表示一个字符的编码表,包括:一位、两位、三位表示的字符
UTF-8有自己的字节码:
一个字节:0开头
两个字节:字节一 ---> 110 位数:10 ~ 6
字节二 ---> 10 位数:5 ~ 0
三个字节:字节一 ---> 1110 位数:15 ~ 12
字节二 ---> 10 位数:11 ~ 6
字节三 ---> 10 位数:5 ~ 0
二、编码和解码:
1、编码:字符串变成字节数组
解码:字节数组变成字符串
2、转换:
1)默认字符集:
String ---> byte[] :srt.getBytes()
byte[] ---> String :new String(byte[])
2)指定字符集:
String ---> byte[] :srt.getBytes(charsetName)
byte[] ---> String :new String(byte[],charsetName)
三、对于编码和解码的字符集转换
1、如果编码失败,解码就没意义了。
2、如果编码成功,解码出来的是乱码,,则需对乱码通过再次编码(用解错码的编码表),然后再通过正确的编码表解码。针对于IOS8859-1是通用的。
3、如果用的是GBK编码,UTF-8解码,那么再通过2的方式,就不能成功了,因为UTF-8也支持中文,在UTF-8解的时候,会将对应的字节数改变,所以不会成功。
特别注意:对于中文的”联通“,这两个字比较特别,它的二进制位正好是和在UTF-8中两个字节打头的相同,可以找到对应的符号,但不再是”联通“了。
但是只要他前面还有其他汉字就不会被被UTF-8解码了,也就不会出现乱码了。
* 编码:字符串变成字节数组。* 解码:字节数组变成字符串。* String-->byte[]; str.getBytes(charsetName);* byte[] -->String: new String(byte[],charsetName);*/public class EncodeDemo {public static void main(String[] args) {method();}public static void show() {String s = "你好";try {byte[] b = s.getBytes("GBK");System.out.println(Arrays.toString(b)); // printBytes(buf);String str = new String(b, "iso8859-1");// 编码正确,解码错误System.out.println(str);// 对str进行iso8859-1编码。以这样的方式解决乱码byte[] b1 = str.getBytes("iso8859-1");System.out.println(Arrays.toString(b1));String str1 = new String(b1, "GBK");System.out.println(str1);} catch (UnsupportedEncodingException e) {e.printStackTrace();}}

/** 联通*/public static void method() {String s = "联通";try {byte[] bs = s.getBytes("GBK");for (byte b : bs) {// Integer.toBinaryString将其转换为二进制,&255取出前面多余的1,保留最低8位// 联通的二进制是110...10...110...10...这个刚好符合utf-8的读取两个字节的编码形式,// 所以当度存在会被默认为utf-8编码来解读,从而造成乱码/** 'u0001' 到 'u007F' 范围内的所有字符都是用单个字节表示的:** 位值 字节一 0 位 6-0*** null 字符 'u0000' 以及从 'u0080' 到 'u07FF' 的范围内的字符用两个字节表示:** 位值 字节一 1 1 0 位 10-6** 字节二 1 0 位 5-0** 'u0800' 到 'uFFFF' 范围内的 char 值用三个字节表示: 字节一 1 1 1 0 位* 15-12** 字节二 1 0 位 11-6** 字节三 1 0 位 5-0*/System.out.println(Integer.toBinaryString(b & 255));}} catch (UnsupportedEncodingException e) {e.printStackTrace();}}}
public class Test {public static void main(String[] args) throws IOException {String str = "ab你好cd谢谢";// str = "ab琲琲cd琲琲";// int len = str.getBytes("gbk").length;// for(int x=0; x<len; x++){// System.out.println("截取"+(x+1)+"个字节结果是:"+cutStringByByte(str, x+1));// }int len = str.getBytes("utf-8").length;for(int x=0; x<len; x++){System.out.println("截取"+(x+1)+"个字节结果是:"+cutStringByU8Byte(str, x+1));}// String str = "琲";// byte[] buf = str.getBytes("gbk");// for(byte b : buf){// System.out.println(b);//-84 105// }}
public static String cutStringByU8Byte(String str, int len) throws IOException {byte[] buf = str.getBytes("utf-8");int count = 0;for(int x=len-1; x>=0; x--){if(buf[x]<0)count++;elsebreak;}if(count%3==0)return new String(buf,0,len,"utf-8");else if(count%3==1)return new String(buf,0,len-1,"utf-8");elsereturn new String(buf,0,len-2,"utf-8");}public static String cutStringByByte(String str,int len) throws IOException{byte[] buf = str.getBytes("gbk");int count = 0;for(int x=len-1; x>=0; x--){if(buf[x]<0)count++;elsebreak;}if(count%2==0)return new String(buf,0,len,"gbk");elsereturn new String(buf,0,len-1,"gbk");}}}
练习
五个学生,每个学生有3门课程的成绩,从键盘输入以上数据(姓名,三门课成绩),
输入格式:如:zahngsan,30,40,60计算出总成绩,并把学生的信息和计算出的总分数高低按顺序存放在磁盘文件stud.txt中
步骤:
1、描述学生对象
2、定义一个可操作学生对象的工具类
思路:
1、通过获取键盘录入一行的数据,并将该行数据的信息取出,封装成学生对象
2、因为学生对象很多,则需要存储,使用集合,因为要对学生总分排序
所以可以使用TreeSet
3、将集合中的信息写入到一个文件中
}
public class StudentInfoTest {public static void main(String[] args) {try {Comparator<StudentInfo> cmp = Collections.reverseOrder();// 强行反正比较顺序// Set<StudentInfo> stus = StudentInfoTool.getStudents();//默认比较Set<StudentInfo> stus = StudentInfoTool.getStudents(cmp);// 自定义比较器StudentInfoTool.write2File(stus);//调用写方法,将数据写文件中去} catch (IOException e) {e.printStackTrace();}}}class StudentInfo implements Comparable<StudentInfo> {private String name;// 姓名private int ma, cn, en;// 数学、语文、英语成绩private int sum;// 总分//初始化就需要具备姓名,数学、语文、英语成绩StudentInfo(String name, int ma, int cn, int en) {this.name = name;this.ma = ma;this.cn = cn;this.en = en;sum = ma + cn + en;//计算出总成绩}public String getName() {return name;}public void setName(String name) {this.name = name;}public int getMa() {return ma;}public void setMa(int ma) {this.ma = ma;}public int getCn() {return cn;}public void setCn(int cn) {this.cn = cn;}public int getEn() {return en;}public void setEn(int en) {this.en = en;}public int getSum() {return sum;}public void setSum(int sum) {this.sum = sum;}@Overridepublic int compareTo(StudentInfo o) {//建立自己的比较方法int num = new Integer(this.sum).compareTo(new Integer(o.sum));//比较分数if (num == 0) {return this.name.compareTo(o.name);//主条件相同判定次要条件}return num;//返回比较的值,大于正数,相同0,小于负数}@Overridepublic int hashCode() {//重写hashcode方法是因为数据有可能存到hashtable中return name.hashCode() + sum * 23;//尽可能保证hashcode值不相同,减少比较次数}@Overridepublic boolean equals(Object obj) {//建立自己独有的判断相同方式if (!(obj instanceof StudentInfo)) {throw new ClassCastException("类型不匹配");}StudentInfo si = (StudentInfo) obj;return this.name.equals(si.name) && this.sum == si.sum;//姓名和总分相同视为同一人}@Overridepublic String toString() {//建立自己的toString方法return "studentinfo[" + name + ", " + ma + ", " + cn + ", " + en + "]";}}class StudentInfoTool {//定义默认比较顺序public static Set<StudentInfo> getStudents() throws IOException {return getStudents(null);}//定义自定义的比较顺序public static Set<StudentInfo> getStudents(Comparator<StudentInfo> cmp)throws IOException {BufferedReader bfr = new BufferedReader(new InputStreamReader(System.in));Set<StudentInfo> stus = null;if (cmp == null) {stus = new TreeSet<StudentInfo>();} else {stus = new TreeSet<StudentInfo>(cmp);}String line = null;while ((line = bfr.readLine()) != null) {if ("over".equals(line)) {break;}String[] info = line.split(",");//将数据以“,”切开,形成新的数据StudentInfo si = new StudentInfo(info[0],Integer.parseInt(info[1]), Integer.parseInt(info[2]),Integer.parseInt(info[3]));stus.add(si);}bfr.close();return stus;}public static void write2File(Set<StudentInfo> stus) throws IOException {BufferedWriter bfw = new BufferedWriter(new FileWriter("stuinfo.txt"));for (StudentInfo si : stus) {bfw.write(si.toString() + " ");bfw.write(si.getSum() + "");bfw.newLine();bfw.flush();}bfw.close();}
