一、概述:
该流提供了打印方法,可以将各种数据类型的数据都原样打印
原理将97先变成字符保持原样将数据打印到目的地
1、字节打印流:PrintStream 构造函数可以接收的参数类型: 1)file对象。File 2)字符串路径。String 3)字节输出流。OutputStream
2、字符打印流:PrintWriter 构造函数可以接收的参数类型: 1)file对象。File 2)字符串路径。String 3)字节输出流。OutputStream 4)字符输出流,Writer。
PrintStream out = new PrintStream("print.txt");
// int by = read();
// write(by);
// out.write(610);//只写最低8位,
// out.print(97);//将97先变成字符保持原样将数据打印到目的地。
out.close();
从控制台写到控制台
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(); } }
获取系统信息,并保存:
步骤:
1)获取系统信息: 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(); //内部类访问局部变量用final Enumeration<FileInputStream> en = new Enumeration<FileInputStream>(){ //new了个 Enumeration,匿名内部类 //枚举接口的实现中利用迭代器,这样写比较麻烦,不懂,参见io流--51 @Override public boolean hasMoreElements() { return it.hasNext(); } @Override public 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>() { @Override public FileInputStream nextElement() { return it.next(); } @Override public 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(); } }
练习1:文件切割(按大小切)
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(); }}
练习2:合并
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是可以操作对象的流。它的写方法是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; } }
一、概述:
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位,用writeint raf.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; } @Override public 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; } @Override public 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是可以用于操作基本数据类型的数据的流对象: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(); } }
一、概述:
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解码了,也就不会出现乱码了。
练习:
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// } }
在java中,字符串“abcd”与字符串“ab你好”的长度是一样,都是四个字符。
但对应的字节数不同,一个汉字占两个字节。
定义一个方法,按照最大的字节数来取子串。
如:对于“ab你好”,如果取三个字节,那么子串就是ab与“你”字的半个,
那么半个就要舍弃。如果去四个字节就是“ab你”,取五个字节还是“ab你”.
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++; else break; } 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"); else return 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++; else break; } if(count%2==0) return new String(buf,0,len,"gbk"); else return 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; } @Override public 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,小于负数 } @Override public int hashCode() {//重写hashcode方法是因为数据有可能存到hashtable中 return name.hashCode() + sum * 23;//尽可能保证hashcode值不相同,减少比较次数 } @Override public 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;//姓名和总分相同视为同一人 } @Override public 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() + "\t"); bfw.write(si.getSum() + ""); bfw.newLine(); bfw.flush(); } bfw.close(); } }