黑马程序员——Java基础:IO(二):File类、Properties类、打印流、序列流......

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

 

一、File类

File类:在IO包中,是用来将文件封装成对象,方便对文件与文件夹的属性信息进行操作,可以作为参数传递给流的构造函数,File类的实例是不可变的;也就是说,一旦创建,File对象表示的抽象路径名将永不改变。流只能操作数据,不能操作文件。

示例1:创建File对象

import java.io.*;
class FileDemo2001 
{
	public static void main(String[] args) 
	{
		//创建File对象 ,可以将已有的和未出现的文件或文件夹封装成对象
		//方式一:将a.txt封装成File对象
		File f1=new File("a.txt");
	        //方式二:
		File f2=new File("D:\\Mine_Huan","b.txt");
		//方式三:
		File d=new File("D:\\Mine_Huan");
		File f3=new File(d,"c.txt");
	}
}

注:“\\”不利于跨平台,可以将其改为File.separator。

File类的常见的方法:

     1.创建:

        boolean createNewFile():在在指定位置创建文件,如果该文件已经存在,则不创建,返回false。和输出流不一样,输出流对象一建立就创建文件。而且文件已经存在,会覆盖。

        boolean createTempFile():创建临时文件。

        boolean mkdir():创建文件夹,且只能创建一级目录。

        boolean mkdirs():创建多级文件夹。

     2.删除:

        boolean delete():如果删除失败,返回false;如果文件正在被使用,则删除不了,返回false。

        void deleteOnExit():在程序退出时删除指定文件。

     3.判断:

        boolean exists():文件是否存在。

        boolean isFile():是否是文件。

        boolean isDirectory():是否是目录。

        boolean isHidden():是否被隐藏。

        boolean isAbsolute():是否是绝对路径。

        boolean canExecute():文件是否能执行。

     4.获取信息:

        String getName():获取文件或目录的名称。

        String getPath():获取文件或目录路径。

        String getParent():获取绝对路径中的父目录,如果获取的是想对路径,则返回null,如果相对路径中有上一层目录,那么该目录就是返回结果。

        String getAbsolutePath():获取文件或目录的绝对名称。

        Long lastModified():获取文件最后一次修改的时间。

        Long length():获取文件的长度。

    5.其他方法:

        String renameTo(File file):重命名,也可以重命名到其他盘下,相当于剪切。

        String[ ] list():打印目录下所有的文件和文件夹的名称,包含隐藏文件。注:调用list方法的File对象必须封装了一个目录,而且该文件还必须存在。

        File[ ] listFiles():获取当前目录下的文件及文件夹名称。

        如:File[ ] file=File.listRoots():列出机器中有效的盘符。

示例2:获取指定目录下的java文件,即过滤文件

import java.io.*;
class MyFile implements FilenameFilter
{
	private String name;
	MyFile(String name)
	{
		this.name=name;
	}
	//测试指定文件是否应该包含在某一文件列表中。 
	//dir: 被找到的文件所在的目录
	//name:文件的名称。
	//当且仅当该名称应该包含在文件列表中时返回 true;否则返回 false。
	public boolean accept(File dir,String fileName)
	{
		return fileName.endsWith(name);
	}
}
class IOFilenameFilter 
{
	public static void main(String[] args) 
	{
		File f1=new File("D:\\Mine_Huan\\MyFile\\JavaExercise");
		String[] arr=f1.list(new MyFile(".java"));
		for(String s:arr)
		{
			System.out.println(s);
		}
	}
}

运行的部分结果为:

示例3:列出指定目录下文件或文件夹,包含子目录,即列出指定目录下所有内容。因为目录中还有目录,所以只要使用同一个列出目录功能的函数完成即可。

/*需求:列出指定目录下文件或文件夹,包含子目录,即列出指定目录下所有内容*/
import java.io.*;
class  FileDemo
{
	public static void main(String[] args) 
	{
		File f1=new File("D:\\Mine_Huan\\MyFile\\JavaExercise");
		showDir(f1,0);
		System.out.println("Hello World!");
	}
	public static void showDir(File dir,int level)
	{
		//System.out.println(dir);
		System.out.println(getLevel(level)+dir.getName());
		level++;
		File[] dirs=dir.listFiles();
		for(int i=0;i<dirs.length;i++)
		{
			if(dirs[i].isDirectory())
				showDir(dirs[i],level);
			System.out.println(getLevel(level)+dirs[i]);
		}
	}
	public static String getLevel(int level)
	{
		StringBuilder sb=new StringBuilder();
		sb.append("|--");
		for(int i=0;i<level;i++)
		{
			sb.insert(0,"   ");
		}
		return sb.toString();
	}
}

运行的部分结果为:

注:像这种在列出过程中出现的还是目录的话,还可以再调用本功能,也就是函数自身调用自身的方式,这就是递归原理。

递归要注意:1.限定条件。2.递归的次数,尽量避免内存溢出。

示例4:删除一个带内容的目录。

删除原理:在window中,删除目录从里面往外面删除的,且删除的文件不走回收站。既然是从里往外删除。就需要用到递归

/*需求:删除一个带内容的目录*/
import java.io.*;
class FileDelDemo 
{
	public static void main(String[] args) 
	{
		File f1=new File("D:\\del");
		delDir(f1);
	}
	public static void delDir(File dir)
	{
		File[] files=dir.listFiles();
		for(int i=0;i<files.length;i++)
		{
			if(files[i].isDirectory())
				delDir(files[i]);
			System.out.println(files[i]+"文件被删除"+files[i].delete());
		}
		System.out.println(dir+"目录被删除"+dir.delete());
	}
}

运行结果为:

练习:将一个指定目录下的java文件的绝对路径,存储到一个文本文件中

/*需求:将一个指定目录下的java文件的绝对路径,存储到一个文本文件中
思路:1.对指定的目录进行递归
     2.获取递归过过程中所有的java文件的路径
     3.将这些路径存储到集合中
     4.将集合中的数据写入到一个文件中*/
import java.io.*;
import java.util.*;
class File2010 
{
	public static void main(String[] args)
	{
		File dir=new File("D:\\Mine_Huan\\MyFile");
		List<File> list=new ArrayList<File>();
		fileToList(dir,list);
		writeToFile(list,"D:\\Mine_Huan\\MyFile\\1.txt");
		//.out.println(list.size());
	}
	public static void fileToList(File dir,List<File> list)
	{
		File[] files=dir.listFiles();
		for(File file:files)
		{
			if(file.isDirectory())
				fileToList(file,list);
			else 
			{
				if(file.getName().endsWith(".java"))//
					list.add(file);
					//System.out.println(file);				
			}
		}
	}
	public static void writeToFile(List<File> list,String fileName)
	{
		//System.out.println(list);				
		FileWriter fw=null;
		BufferedWriter bufw=null;
		try
		{
			fw=new FileWriter(fileName);//传文件(File)也可以
			bufw=new BufferedWriter(fw);
			/*
			Iterator<File> it=list.iterator();
			while(it.hasNext())
			{
				File f=it.next();
				bufw.write(f.getAbsolutePath());//
				bufw.newLine();
				bufw.flush();
			}*/
			for(File f:list)
			{
				String path=f.getAbsolutePath();
				bufw.write(path);//
				bufw.newLine();
				bufw.flush();
			}

		}
		catch (IOException e)
		{
			System.out.println(e.toString());
		}
		finally
		{
			try
			{
				if(bufw!=null)
					bufw.close();
			}
			catch (IOException e)
			{
				System.out.println(e.toString());
			}
		}		
	}
}
<span style="font-size:14px;">
</span>

二、Properties容器

Properties是Hashtable的子类,在util包中。

也就是说它具备map集合的特点,而且它里面存储的键值对都是字符串,不需要定义泛型。

Properties是map集合和IO技术相结合的集合容器。

特点:可以用键值对的形式来配置文件,加载数据时,通常使用的数据格式为:键=值

示例:设置和获取元素

import java.util.*;
import java.io.*;
class PropertiesDemo 
{
	public static void main(String[] args) 
	{
		Properties pro=new Properties();
		pro.setProperty("zhangsan","34");
		pro.setProperty("lisi","46");
		String value=pro.getProperty("lisi");//获取键的值
		System.out.println(value);

		pro.setProperty("lisi","55");//重新赋值
		Set<String> names=pro.stringPropertyNames();//返回此属性列表中的键集
		for(String name:names)
		{
				System.out.println(name+":"+pro.getProperty(name));//根据键来获取值
		}
	}
}

运行结果为:

练习:用于记录应用程序运行次数,如果使用的次数已到,那么给出注册提示

/*需求:用于记录应用程序运行次数
如果使用的次数已到,那么给出注册提示
想到的是:计数器,
可是该计数器定义在程序中,随着程序的运行而在内存中存在,并进行自增
可是随着该应用程序的退出,该计数器也会在内存中消失
下一次再启动该程序,又重新开始从0计数,这不是我们想要的
我们需要的是:程序即使结束,该计数器的值也会存在
下次程序启动时会先加载该计数器的值(+1)后再重新存储起来
所以要建立一个配置文件,用于记录该软件的使用次数
该配置文件使用键值对的形式,便于阅读,与操作
键值对数据是map集合
数据是以文件形式存储,使用io技术
那么map+io--->properties
配置文件可以实现应用程序数据的共享*/
import java.util.*;
import java.io.*;
class  Properties2014
{
	public static void main(String[] args) throws IOException
	{
		Properties pro=new Properties();//相当于map与io相结合的容器
		File f=new File("2.txt");
		if(!f.exists())
			f.createNewFile();
		BufferedReader bufr=new BufferedReader(new FileReader(f));
		pro.load(bufr);//将流中的数据加载进集合
		int count=0;
		String value=pro.getProperty("time");
		if(value!=null)
		{
			count=Integer.parseInt(value);
			if(count>=5)
			{
				System.out.println("使用的次数已到,请交钱");
				return;
			}
		}
		count++;
		pro.setProperty("time",count+"");
		FileWriter fw=new FileWriter(f);//不会发生异常
		BufferedWriter bufw=new BufferedWriter(fw);
		pro.store(bufw,"haha");//haha:注释信息
		pro.list(System.out);
		bufw.close();
		bufr.close();
	}
}

三、打印流——PrintStream,PrintWriter

打印流:该流提供了打印方法,可以将各种类型的数据原样打印。

1)字节打印流——PrintStream

      构造函数可以接收的参数类型有:

           a).File对象:File

           b).字符串路径:String

           c).字符输出流:OutputStream

2)字符串打印流——PrintWriter

      构造方法中可接受的参数类型

           a).File对象:File

           b).字符串路径:String

           c).字节输出流:OutputStream

           d).字符输出流:Writer

示例:当录入一行数据后,就将该行数据进行打印,如果输入over就结束录入

import java.io.*;
class IOKey1915 
{
	public static void main(String[] args) throws IOException
	{
		//键盘录入
		BufferedReader bufr=new BufferedReader(new InputStreamReader(System.in));
		//打印流
		PrintWriter pw=new PrintWriter(System.out);

		while(true)
		{
			//使用字符流中特有的readLine方法
			String line=bufr.readLine();
			if(line.equals("over"))
				break;
			pw.println(line.toUpperCase());
			pw.flush();
		}
		pw.close();
		bufr.close();
	}
}

注:PrintWriter(OutputStream out):不带自动刷新,所以要使用“pw.flush()”。

       PrintWriter(OutputStream out,boolean autoFlush):当autoFlush为true时,则println,printf,forma将刷新输出缓冲区

四、序列流——SequenceInputStream

序列流:又称合并流。对多个流进行合并。

示例:将1,2,3文件中的数据合并到一个流中,然后再将流中的数据传到目的。

/*将1,2,3文件中的数据合并到一个流中,然后再将流中的数据传到目的*/
import java.io.*;
import java.util.*;
class  SequenceInputStreamDemo
{
	public static void main(String[] args) throws IOException
	{
		Vector v=new Vector();
		v.add(new FileInputStream("1.txt"));
		v.add(new FileInputStream("2.txt"));
		v.add(new FileInputStream("3.txt"));
		Enumeration<FileInputStream> en=v.elements();
		SequenceInputStream si=new SequenceInputStream(en);
		//PrintStream ps=new PrintStream(System.out,true);
		FileOutputStream fos=new FileOutputStream("4.txt");

		byte[] buf=new byte[1024];
		int num=0;
		while((num=si.read(buf))!=-1)
		{
			//ps.println(new String(buf,0,num));
			fos.write(buf,0,num);
			fos.flush();
		}
		//ps.close();
		fos.close();
		si.close();
	}
}

注:由于Vector容器效率低,可使用ArrayList集合。将上例改用ArrayList集合如下:

import java.io.*;
import java.util.*;
class  SequenceInputStreamDemo
{
	public static void main(String[] args) throws IOException
	{
		ArrayList al=new ArrayList();
		al.add(new FileInputStream("1.txt"));
		al.add(new FileInputStream("2.txt"));
		al.add(new FileInputStream("3.txt"));
		Iterator<FileInputStream> it=al.iterator();
		Enumeration<FileInputStream> en=new Enumeration<FileInputStream>()
		{
			public boolean hasMoreElements()//测试此枚举是否包含更多的元素
			{
				return it.hasNext();
			}
			public FileInputStream nextElement()//如果此枚举对象至少还有一个可提供的元素,则返回此枚举的下一个元素。
			{
				return it.next();
			}
		};

		SequenceInputStream si=new SequenceInputStream(en);
		//PrintStream ps=new PrintStream(System.out,true);
		FileOutputStream fos=new FileOutputStream("4.txt");

		byte[] buf=new byte[1024];
		int num=0;
		while((num=si.read(buf))!=-1)
		{
			//ps.println(new String(buf,0,num));
			fos.write(buf,0,num);
			fos.flush();
		}
		//ps.close();
		fos.close();
		si.close();
	}
}

五、切割文件

切割文件:将一个文件分成多个文件,原理是:一个源对应多个目的。

示例:

import java.io.*;
class SplitFile2017 
{
	public static void main(String[] args) throws IOException
	{
		FileInputStream fis=new FileInputStream("D:\\Mine_Huan\\MyFile\\JavaExercise\\4.txt");
		FileOutputStream fos=null;
		int i=1;
		byte[] buf=new byte[10];
		int num=0;
		while((num=fis.read(buf))!=-1)
		{
		    fos=new FileOutputStream("D:\\Mine_Huan\\MyFile\\JavaExercise\\"+(i++)+".part");
			fos.write(buf,0,num);
			fos.flush();
			fos.close();
		}
		fos.close();
		fis.close();
	}
}

六、直接操作对象的流——ObjectInputStream,ObjectOutputStream

对象的序列化:将堆内存中的对象存储到硬盘中,让对象永久化存储。

示例:将Person对象的数据永久化存储

import java.io.*;
class Person implements Serializable//标记接口,该接口中没有方法
{
	static final long serialVersionUID = 42L;//固定标识

	private String name;
	transient int age;//不让age序列化,age还在堆内存中
	static String country="CN";//静态不能被序列化
	Person(String name,int age,String country)
	{
		this.name =name;
		this.age =age;
		this.country=country;
	}
	public String toString()
	{
		return "name="+name+"   age="+age+"   country="+country;
	}
}
class ObjectStreamDemo 
{
	public static void main(String[] args) throws Exception
	{
		writeObject();
		readObject();
	}
	//读取
	public static void readObject()throws Exception
	{
		ObjectInputStream ois=new ObjectInputStream(new FileInputStream("Person.object"));
		Person p=(Person)ois.readObject();
		System.out.println(p.toString());
		ois.close();
	}
	//写入
	public static void writeObject() throws IOException
	{
		ObjectOutputStream oos=new ObjectOutputStream(new FileOutputStream("Person.object"));
		oos.writeObject(new Person("zhangsan",12,"kkkk"));
		oos.close();
	}
}

运行结果为:

注:ObjectInputStream和ObjectOutputStream两个对象要成对使用。

七、管道流——PipedInputStream和PipedOutputStream

PipedInputStream和PipedOutputStream:输入,输出流直接进行连接,通过结合线程使用。

示例:

import java.io.*;
class WritePiped implements Runnable
{
	PipedOutputStream pos;
	WritePiped(PipedOutputStream pos)
	{
		this.pos=pos;
	}
	public void run()
	{
		try
		{
			pos.write("Piped".getBytes());
			pos.close();
		}
		catch (Exception ex)
		{
			throw new RuntimeException("管道输出流失败");
		}
	}
}
class ReadPiped implements Runnable 
{
	PipedInputStream pis;
	ReadPiped(PipedInputStream pis)
	{
		this.pis=pis;
	}
	public void run()
	{
		try
		{
			byte[] buf=new byte[1024];
			int num=0;
			num=pis.read(buf);
			System.out.println(new String(buf,0,num));
			pis.close();
		}
		catch (Exception ex)
		{
			throw new RuntimeException("管道读取流失败");
		}
	}
}
class PipedStreamDemo 
{
	public static void main(String[] args) throws IOException
	{
		PipedInputStream in=new PipedInputStream();
		PipedOutputStream out=new PipedOutputStream();
		in.connect(out);//将读取流和输出流相连接
		WritePiped wp=new WritePiped(out);
		ReadPiped rp=new ReadPiped(in);
		Thread t1=new Thread(wp);
		Thread t2=new Thread(rp);
		t1.start();
		t2.start();
	}
}

运行结果为:

八、随机访问文件——RandomAccessFile

随机访问文件的特征:

1.自身具备读写的方法,通过skipBytes(int x),seek(int x)来达到随机访问。

2.该类不算是IO体系中的子类,而是直接继承自Object;但是它是IO包中的成员,因为它具备读和写的功能。

3.内部封装了一个数组,而且通过指针对数组的元素进行操作。

4.可以通过getFilePointer获取指针位置,通过seek改变指针位置。

注:其实随机访问文件完成读写的原理就是内部封装了字节输入流和输出流。

通过构造函数可以看出:该类只能操作文件,而且操作文件有模式(r,rw,rws,rwd);而且该对象的构造函数要操作的文件不存在,不自动创建;如果文件存在则不会覆盖。

如果模式为只读r,不会创建文件,会去取一个已经存在的文件,如果文件不存在,会发生异常。

如果模式为读写rw,操作的文件不存在,会自动创建,如果存在则不会覆盖。

示例:

import java.io.*;
class RandomAccessFileDemo 
{
	public static void main(String[] args) throws Exception
	{
		//writeFile();
		//writeFile_2();
		readFile();
	}
	public static void readFile() throws IOException
	{
		RandomAccessFile raf=new RandomAccessFile("5.txt","r");
		raf.seek(8*1);//调整对象中指针,前后都可指。指到第8位
		raf.skipBytes(8);//跳过指定的字节数,只可往下跳,不可往回跳。跳8个字节
		//读取文件中内容
		byte[] buf=new byte[4];
		raf.read(buf);
		String name=new String(buf);
		int age=raf.readInt();
		System.out.println("name="+name+"   age="+age);
		raf.close();

	}
	public static void writeFile() throws IOException
	{
		//如果文件不存在,则创建,如果文件存在,不创建
		RandomAccessFile raf=new RandomAccessFile("5.txt","rw");
		//在指定文件中写入内容
		raf.write("张三".getBytes());
		raf.writeInt(97);//使用writeInt方法写入四个字节(int类型)
		raf.write("李四".getBytes());
		raf.writeInt(99);

		raf.write("赵六".getBytes());
		raf.writeInt(97);//使用writeInt方法写入四个字节(int类型)
		raf.write("王五".getBytes());
		raf.writeInt(99);
		raf.close();
	}
	public static void writeFile_2() throws IOException
	{
		RandomAccessFile raf=new RandomAccessFile("5.txt","rw");
		raf.seek(8*3);//空着两位,没输入数据
		raf.write("周琦".getBytes());
		raf.writeInt(103);
		raf.close();
	}
}

调用writeFile方法的运行结果为:

调用readFile方法的运行结果为:

九、操作基本数据类型的流对象——DataInputStream和DataOutputStream

DataInputStream和DataOutputStream:可用于操作基本数据类型的数据的流对象。

示例:

import java.io.*;
class DataStreamDemo 
{
	public static void main(String[] args)  throws IOException
	{
		writeData();
		readData();
	}	
	public static void writeData() throws IOException
	{
		DataOutputStream dos=new DataOutputStream(new FileOutputStream("6.txt"));
		dos.writeInt(234);
		dos.writeBoolean(true);
		dos.writeDouble(3.14);
		dos.writeUTF("您好");
		dos.close();

	}
	public static void readData() throws IOException
	{
		DataInputStream dis=new DataInputStream(new FileInputStream("6.txt"));
		int i=dis.readInt();
		boolean b=dis.readBoolean();
		double d=dis.readDouble();
		String s=dis.readUTF();
		System.out.println(i+"\t"+b+"\t"+d+"\t"+s);
		dis.close();
	}
}

运行结果为:

注:读顺序应和写顺序相对应。

十、操作字节数组的流对象——ByteArrayInputStream和ByteArrayOutputStream

ByteArrayInputStream:在构造的时候,需要接收数据源,而且数据源是一个字节数组。

ByteArrayOutputStream:在构造的时候,不用定义数据的目的,因为该对象已经在内部封装了可变长度的字节数组(缓冲区大小会随着数据的增长而增长),这就是数据的目的地。

因为这两个流对象都是操作的数组,并没有使用系统资源,所以不用进行close关闭流资源,不会发生异常。

示例:用流的思想来操作数组。

import java.io.*;
class  ByteArrayStreamDemo
{
	public static void main(String[] args)  throws IOException
	{
		ByteArrayInputStream bais=new ByteArrayInputStream("ABCDEF".getBytes());
		ByteArrayOutputStream baos=new ByteArrayOutputStream();//以内存为设备
		int num=0;
		byte[] buf=new byte[1024];
		while((num=bais.read(buf))!=-1)
		{
			baos.write(buf,0,num);
		}
		System.out.println(baos.size());
		System.out.println(baos.toString());
	}
}

运行结果为:

另外还有:操作字符数组的流——CharArrayReader和CharArrayWriter

                  操作字符串的流对象——StringReader和StringWriter

十一、字符编码

字符流的出现是为了方便操作字符,是字节流中加入了编码表。

通过子类转换流来完成:InputStreamReader和OutputStreamWriter;在这两个对象进行构造的时候可以加入字符集(编码表)。

编码表的由来:计算机只能识别二进制数据,早期由来是电信号。为了方便应用计算机,让它可以识别各个国家的文字。就将各个国家的文字用数字来表示,并一一对应,形成一张表,这就是编码表。

常见的编码表:

    ASCII:美国标准信息交换码,用一个字节的7位可以表示。
    ISO8859-1:拉丁码表。欧洲码表,用一个字节的8位表示。
    GB2312:中国的中文编码表。
    GBK:中国的中文编码表升级,融合了更多的中文文字符号。
    Unicode:国际标准码,融合了多种文字。所有文字都用两个字节来表示,Java语言使用的就是unicode。
    UTF-8:最多用三个字节来表示一个字符。

示例:

import java.io.*;
class BianMaDemo2108 
{
	public static void main(String[] args)  throws IOException
	{
		writeText();
		readText();
	}
	public static void readText() throws IOException//读取
	{
		InputStreamReader isr=new InputStreamReader(new FileInputStream("7.txt"),"UTF-8");
		int num=0;
		char[] buf=new char[1024];
		while((num=isr.read(buf))!=-1)
		{
			System.out.println(new String(buf,0,num));
		}
		isr.close();
	}
	public static void writeText() throws IOException//写入
	{
		OutputStreamWriter osr=new OutputStreamWriter(new FileOutputStream("7.txt"),"UTF-8");
		osr.write("您好");
		osr.close();
	}
}

运行结果为:

注:编码:字符串变成字节数组,String——>byte ;str.getBytes()

       解码:字节数组变成字符串,byte——>String;new String[byte[ ]]

示例:

import java.io.*;
import java.util.*;
class BianMaDemo2108 
{
	public static void main(String[] args)  throws Exception
	{
		//编码
		String str="您好";
		byte[] b1=str.getBytes("GBK");
		System.out.println("b1: "+Arrays.toString(b1));

		//解码
		String s1=new String(b1,"GBK");
		System.out.println("s1: "+s1);
	}
}

运行结果为:

注:编码再解码会产生乱码的情况。遇到乱码要编一次解一次。

但是要注意编码用GDK,解码用UTF-8产生的乱码,如果还是用编一次解一次的方法是得不到原数据的,还是乱码。因为GDK和UTF-8都是支持中文照成的。

练习:有五个学生,每个学生有三门课的成绩,从键盘输入以上数据(包括姓名,三门课成绩)输入的格式:如:张三,30,40,60计算出总成绩,并把学生的信息和计算出的总分数按高低顺序存放到磁盘文件"stud.txt"中

/*需求:有五个学生,每个学生有三门课的成绩,
从键盘输入以上数据(包括姓名,三门课成绩)
输入的格式:如:张三,30,40,60计算出总成绩
并把学生的信息和计算出的总分数按高低顺序存放到磁盘文件"stud.txt"中
思路:1.描述学生对象
      2.定义一个可操作学生对象的工具类
步骤:1.通过获取键盘录入一行数据,并将该行中的信息取出封装成学生对象
      2.因为学生有很多,那么就需要存储,使用到集合,以为要对学生的总分排序
        所以可用TreeSet,没有映射关系
      3.将集合中的信息写入到文件中*/
import java.io.*;
import java.util.*;
class Student implements Comparable<Student>
{
	private String name;
	private int cn,en,ma,sum;
	Student(String name,int cn,int en,int ma)
	{
		this.name=name;
		this.cn=cn;
		this.en=en;
		this.ma=ma;
		sum=cn+en+ma;
	}
	public String getName()
	{
		return name;
	}
	public int getSum()
	{
		return sum;
	}
	public int hashCode()
	{
		return name.hashCode()+sum*21;
	}
	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 int compareTo(Student stu)
	{
		
		int num=new Integer(this.sum).compareTo(new Integer(stu.sum));
		if(num==0)
			return this.name.compareTo(stu.name);
		return num;
	}
	public String toString()
	{
		return "Student["+name+", "+cn+","+en+","+ma+"]";
	}	
}
class StudentInfoTool
{
	public static TreeSet<Student> getInfo()throws IOException
	{
		return getInfo(null);
	}
	public static TreeSet<Student> getInfo(Comparator<Student> cmp)throws IOException//加一个比较器
	{
		BufferedReader bufr=new BufferedReader(new InputStreamReader(System.in));//键盘录入
		TreeSet<Student> ts=null;//TreeSet容器
		if(cmp==null)
			ts=new TreeSet<Student>();
		else
			ts=new TreeSet<Student>(cmp);
		String line=null;
		while((line=bufr.readLine())!=null)
		{
			if("over".equals(line))//over时结束
				break;
			String[] arr=line.split(",");
			ts.add(new Student(arr[0],Integer.parseInt(arr[1])
				       ,Integer.parseInt(arr[2]),Integer.parseInt(arr[3])));			
		}
		bufr.close();
		return ts;
	}
	public static void writeInfo(TreeSet<Student> ts,String fileName)throws IOException
	{
		File file=new File(fileName);
		if(!file.exists())
			file.createNewFile();
		BufferedWriter bufw=new BufferedWriter(new FileWriter(file));
		for(Student st:ts)
		{
			bufw.write(st.toString()+"\t");
			bufw.write(st.getSum()+"");//st.getSum()返回的是一个整数
			bufw.newLine();
			bufw.flush();
		}
		bufw.close();
	}
}
class  IOTest2109
{
	public static void main(String[] args) throws IOException
	{
		Comparator<Student> cmp=Collections.reverseOrder();//定义比较器
		TreeSet<Student> ts=StudentInfoTool.getInfo(cmp);
		StudentInfoTool.writeInfo(ts,"stud.txt");
	}
}

运行结果为:

注:以上示例的异常处理,建议使用try,不要抛,我这里使用抛是为了演示方便简洁。还有导入包时最好导入具体的类,不要像以上示例所演示的直接使用*。

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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值