黑马程序员—JAVA基础—io流

------Java培训、Android培训、iOS培训、.Net培训、期待与您交流! -------
一、概念:IO(Input Output):用来处理设备之间的数据传输。
二、分类:按流向分:输入流和输出流。输入流是读,输出流是写。(相对io流对象分析)。
                  按数据类型分类:字节流和字符流,字节流操作文本以及其他数据(比如图片、音频);字符流专门用于操作文本数据。
三、在io流体系中:
                           字节流基类:InputStream 和OutputStream
                           字符流基类:Reader和Writer
       这四个类派生出来的子类都是以父类名作为后缀。
四、编写方式:
        1.导入io包
        2.对编写的代码进行异常处理
        3.关闭流方法要放在finally中
        问题1:java中有垃圾回收机制为什么还要调用close()方法进行关闭流
                   io流涉及到操作系统的资源,不但在内存中分配空间,同时在操作系统中占用资源,
                   java回收机制只能回收内存中资源,所以一定要调用close方法进行关闭。
        问题2:为什么一定要处理io异常
五、java中一些常用对象:System   Runtime   Data   Calendar   Math   Random
       System类中的方法和属性都是静态的
           out:标准的输入,默认是控制台
           in:标准的输出,默认是键盘
           类中一些常用方法
           exit(0)结束程序
           Properties getProperties():获取当前的系统属性。 
           String getProperty(String key):根据key来获取属性值
           setIn(InputStream in):改变输入流的源。 
           setOut(PrintStream out):改变输出流的目的。  
           setProperty(String key, String value):设置指定的key和value进去当前系统属性
       Runtime类中没有构造方法,不能通过new来建立对象,通过getRuntime获取实例(单例设计模式)
           exec方法,在单独的进程中运行一个程序
       Data类通常用于获取当前时间
class DateDemo 
{
	public static void main(String[] args) 
	{
		Date d = new Date();
		System.out.println(d);//Fri Jul 31 07:58:13 CST 2015

		//SimpleDateFormat用于格式化时间,指定格式将Date获取的时间转换
		SimpleDateFormat sdf = new SimpleDateFormat("yyyy年MM月dd日E hh:mm:ss");
		String time = sdf.format(d);
		System.out.println(time);//2015年07月31日星期五 07:58:13
	}
}
        Calendar类可以按照特定的格式获取日历的各个字段
代码示例:
class CalendarDemo 
{
	public static void main(String[] args) 
	{
		Calendar c = Calendar.getInstance();
		c.add(Calendar.DAY_OF_MONTH,10);//在当前月的基础上,向后推10天
		printCalendar(c);
	}
	public static void printCalendar(Calendar c)
	{
		String[] month = {"一月","二月","三月","四月"
					,"五月","六月","七月","八月"
					,"九月","十月","十一月","十二月"};
		String[] week = {"","星期日","星期一","星期二","星期三","星期四","星期五","星期六"};
		sop(c.get(Calendar.YEAR)+"年");//获取当前年
		sop(month[c.get(Calendar.MONTH)]);//获取当前月,通过查表法显示
		sop(c.get(Calendar.DAY_OF_MONTH)+"日");//获取当前日
		sop(week[c.get(Calendar.DAY_OF_WEEK)]);//获取当前周,通过查表法显示
	}
	public static void sop(Object obj)
	{
		System.out.println(obj);
	}
}
      Math类包含用于执行基本数学运算的方法
      Random类用于生成伪随机数
代码示例:
class MathDemo 
{
	public static void main(String[] args) 
	{
		double d = Math.ceil(12.33);//返回大于指定数据的最小整数
		System.out.println(d);
		double d2 = Math.floor(12.33);//返回小于指定数据的最大整数
		System.out.println(d2);
		long l = Math.round(12.33);//四舍五入
		System.out.println(l);
		double d3 = Math.pow(2,3);//2的3次幂
		System.out.println(d3);
		System.out.println("---------------");
		//从1~10获取是个随机数,用Math.random方法
		for (int x=0; x<10; x++)
		{
			int d4 = (int)(Math.random()*10+1);
			System.out.println(d4);
		}
		System.out.println("---------------");
		//从1~10获取是个随机数,直接实例化Random对象
		Random r = new Random();
		for (int x=0; x<10; x++)
		{
			int d5 = r.nextInt(10)+1;
			System.out.println(d5);
		}
	}
}

六、字符流:
Reader
  |--BufferedReader:字符流缓冲区,用于提高效率
         |--LineNumberReader:带有行号的缓冲输入流
  |--CharArrayReader:字符数组流,用于操作char类型数组
  |--FilterReader:过滤流(抽象类)
  |--InputStreamReader:转换流,将字节流转换为字符流,可自定义编码
        |--FileReader:读取文本文件,常用
  |--PipedReader:管道流
  |--StringReader:字符串流,便于操作字符串
Writer
  |--BufferedWriter:字符流缓冲区,用于提高效率
  |--CharArrayWriter:字符数组流,用于操作char类型数组
  |--FilterWriter:过滤流(抽象类)
  |--OutputStreamWriter:转换流,将字节流转换为字符流,可自定义编码
       |--FileWriter:写入文本文件,常用
  |--PipedWriter:管道流
  |--PrintWriter:打印流,只能操作文本文件
  |--StringWriter:字符串流,便于操作字符串
代码示例:复制文本文件:用字符输入流读取文件,再用字符输出流将读取到的内容写到新文件中
<span style="white-space:pre">	</span>public static void copy_1()
	{
		FileReader fr = null;
		FileWriter fw = null;
		try
		{
			fr = new FileReader("CopyTextFile.java");//创建读取字符流对象,并指定名称
			fw = new FileWriter("d:\\CopyTextFile.java");//创建写入字符流对象,并指定名称
			char[] buf = new char[1024];//创建字符数组作为缓冲区
			int len = 0;//定义标记,用于记录读取返回值
			while ((len=fr.read(buf))!=-1)//当返回值不为-1,则表示有内容,继续读取,并存储在字符数组中
			{
				fw.write(buf,0,len);//将字符数组的数据从0到读取到的长度写入文件
			}
		}
		catch (IOException e)
		{
			throw new RuntimeException("读写失败");
		}
		finally
		{
			if (fr!=null)//在关闭之前进行判断,是否为空,不判断容易出现空指针异常
			{
				try
				{
					fr.close();//关闭读取流
				}
				catch (IOException e)
				{
				}
			}
			if (fw!=null)
			{
				try
				{
					fw.close();//关闭写入流
				}
				catch (IOException e)
				{
				}
			}
		}
	}
代码示例2:对于复制文件,需要用到字符缓冲流。在上面的程序中的try部分代码可以修改为如下
/*
如果使用字符缓冲流,在读取数据时,不用定义数组,直接使用readLine方法
*/
BufferedReader bfr = null;
BufferedWriter bfw = null;
try
{
	bfr = new BufferedReader(new FileReader("CopyTextFile.java"));
	bfw = new BufferedWriter(new FileWriter("d:\\CopyTextFile.java"));
	String line = null;
	while ((line=bfr.readLine())!=null)//readLine方法,返回字符串
	{
		bfw.write(line);
		bfw.newLine();
		bfw.flush();
		//close方法执行时会进行刷新数据,但是在这里定义flush方法
		//是因为在写入过程中,如果发生异常,也可以将一部分数据写入文件
	}
}
对于BufferedReader类,其实是内部将缓冲数组封装起来,提供readLine方法,这时我们可以自定义字符缓冲输入流
代码示例:
class MyBufferedReader extends Reader
{
	private Reader r;
	MyBufferedReader(FileReader r)//在初始化字符输入缓冲流时,需要有字符输入流对象
	{
		this.r = r;
	}
	//自定义readLine方法,抛出异常,因为这是功能,要让调用者进行处理
	public String myReadLine() throws IOException
	{
		//定义字符缓冲区存储读取的字符,其实在BufferedReader内部是字符数组
		StringBuilder sb = new StringBuilder();
		int len = 0;
		while ((len=r.read())!=-1)//调用Reader中的read方法
		{
			if (len=='\r')//因为windows操作系统中换行标记是\r\n,所以读到\r要继续读
				continue;
			if (len=='\n')
				return sb.toString();//读到\n就将一行数据返回
			else
				sb.append((char)len);
		}
		//如果文件中最后一行没有换行,就读不到\n,也就是最后一行数据
		//没有返回,所以要多一次判断字符缓冲区的长度
		if ((sb.length())>0)
				return sb.toString();
		return null;
	}
	//自定义关闭方法,调用Reader中的close方法
	public void close() throws IOException
	{
		r.close();
	}
	//覆盖Reader中的两个抽象方法,不是必要的
	public int read(char[] cbuf, int off, int len) throws IOException
	{
		return r.read(cbuf,off,len);
	}
	public void myClose() throws IOException
	{
		r.close();
	}
}
LineNumberReader是BufferedReader的子类,除了有父类的方法外,特有setLineNumber和getLineNumber方法,
分别设置行号和获取行号
定义一个LineNumberReader类:
class MyLineNumberReader extends MyBufferedReader
{
	/*
	在自定义LineNumberReader类时,可以直接继承自定义BufferedReader类中加入计数器,并
	且提供设置行号和获取行号方法。
	*/
	private int lineNumber;//定义计数器,也就是行号
	MyLineNumberReader(FileReader r)
	{
		super(r);
	}
	public String myReadLine() throws IOException
	{
		lineNumber ++;//当读取一行时,行号进行加1操作
		return super.myReadLine();//父类中已经定义过读取一行的方法,可以直接使用
	}
	//设置行号,让行号从用户设置的数值开始计数
	public void setLineNumber(int lineNumber)
	{
		this.lineNumber = lineNumber;
	}
	//获取行号
	public int getLineNumber()
	{
		return lineNumber;
	}
}

七、字节流
InputStream
       |--ByteArrayInputStream:字节数组输入流
       |--FileInputStream:用于读取文件,常用
       |--FilterInputStream:过滤输入流的所有类的超类
              |--BufferedInputStream:字节缓冲输入流
              |--DataInputStream:基本数据类型输入流
              |--LineNumberInputStream:带有行号的字节输入流
       |--ObjectInputStream:可操作自定义的
列化的 对象和基本数据类型
       |--PipedInputStream:管道字节输入流
       |--SequenceInputStream:合并流
OutputStream
       |--ByteArrayOutputStream:字节数组输出流
       |--FileOutputStream:文件输出流,常用
       |--FilterOutputStream:过滤输出流的所有类的超类
               |--BufferedOutputStream:字节缓冲的输出流
               |--DataOutputStream:基本数据类型输出流
               |--PrintStream:打印流,如果是文本文件,要使用PrintWriter
       |--ObjectOutputStream:可操作自定义的列化的对象和基本数据类型
       |--PipedOutputStream:管道字节输出流

代码示例:复制一张图片,实际就用字节输入流读取图片,再用字节输出流写入一张新图片
<span style="font-size:14px;">class CopyPic 
{
	public static void main(String[] args) 
	{
		FileInputStream fis = null;
		FileOutputStream fos = null;
		try
		{
			fis = new FileInputStream("1.jpg");//字节输入流对象,用于读取图片
			fos = new FileOutputStream("2.jpg");//字节输出流对象,用于写入图片
			byte[] buf = new byte[fis.available()];//自定义字节数组,用于缓冲读取到的数据
			int len = 0;
			while ((len=fis.read(buf))!=-1)//read当返回-1时,表示文件读取完毕
			{
				fos.write(buf,0,len);//写入数据
			}
		}
		catch (IOException e)
		{
			throw new RuntimeException("读取失败");
		}
		finally
		{
			if (fis!=null)
			{
				try
				{
					fis.close();
				}
				catch (IOException e)
				{
					throw new RuntimeException("关闭读取流失败");
				}
			}
			if (fos!=null)
			{
				try
				{
					fos.close();
				}
				catch (IOException e)
				{
					throw new RuntimeException("关闭写入流失败");
				}
			}
		}
	}
}</span>
代码示例2:复制音频文件,并加入缓冲流技术
<span style="font-size:14px;">public static void copy_1()
{
	BufferedInputStream bfis = null;
	BufferedOutputStream bfos = null;
	try
	{
		bfis = new BufferedInputStream(new FileInputStream("1.mp3"));//字节缓冲输入流
		bfos = new BufferedOutputStream(new FileOutputStream("2.mp3"));//字节缓冲输出流
		int by = 0;
		while ((by=bfis.read())!=-1)
		{
			bfos.write(by);
			bfos.flush();//写入数据后进行刷新
		}
	}
	catch (IOException e)
	{
		throw new RuntimeException("读取失败"+e.toString()); 
	}
	finally
	{
		if (bfis!=null)
		{
			try
			{
				bfis.close();//关闭输入流
			}
			catch (IOException e)
			{
				throw new RuntimeException("读取流关闭失败");
			}
		}
		if (bfos!=null)
		{
			try
			{
				bfos.close();//关闭输出流
			}
			catch (IOException e)
			{
				throw new RuntimeException("写入流关闭失败");
			}
		}
	}
}</span>
代码示例3:自定义字节缓冲流
class MyBufferedInputStream 
{
	private InputStream in;
	private byte[] buf = new byte[1024*4];//定义字节数组,用于存储数据
	private int pos = 0;//定义指针,用于标记数组脚标
	private int count = 0;//定义计数器,用于记录数组的长度
	MyBufferedInputStream(InputStream in)//初始化字节流输入缓冲区,就要有字节输入流对象
	{
		this.in = in;
	}
	public int myRead() throws IOException
	{
		if (count==0)//判断如果count为0,代表数组中内容为空
		{
			count = in.read(buf);//就进行读操作
			if (count<0)//读完数据后,如果文件已经读完,count就是-1
				return -1;//在自定义的读取方法中就返回-1,标志数据已经读完
			pos = 0;//每次读到一个新的数组就将指针归零
			byte b =  buf[pos];//定义byte变量记录数组中的数据
			pos ++;//读到数组中一个byte,指针就进行加1操作,继续往下读
			count --;//count就减1操作,因为已经读了一个字节,数组的长度就减少1
			return b&255;
			//&255的原因是有可能读到连续8个1的情况,8个1就是-1,就会造成数据已经读取完毕的标志
			//read方法将读到的byte提升为int并返回,在提升为int类型后还是-1
			//但是&225后,可以保留最低8位,也就是数据不改变,并且提升后不再是-1,是255
		}
		else if (count>0)//当count大于0,继续读取
		{
			byte b = buf[pos];
			pos ++;
			count --;
			return b&255;
		}
		return -1;
	}
	public void myClose() throws IOException
	{
		in.close();
	}
}
八、装饰类设计模式:
当一个类中的功能需要进行增强时,可以定义一个新的类继承本类,再复写要增强的功能,但是这样会造成继承体系臃肿,
这时可以定义一个类,将本类对象作为成员定义在增强类中,再进行功能增强,这个类称为装饰类,一般是与被装饰类是
统一体系中。装饰类比继承灵活,并且降低类与类之间的关系。
例如:字符缓冲流和字节缓冲流就是装饰类,对读取操作进行功能增加,提高效率。
九、转化流:控制台输出和键盘输入都是属于字节流,当要将键盘录入的信息存储到文本文件中,字节流可以操作,但是
                      转换为字符流再操作,更加方便,并且可以用字符缓冲流,提高效率,并且转换流可以指定编码。而要将一
                      个文本文件输出到控制台中也是同样道理
十、改变标准输入输出设备:System.setIn:改变标准输入设备,System.setOut改变标准输出设备。
十一、读取键盘录入:
代码示例:
<span style="font-size:14px;">public static void method_1() throws IOException
{
	InputStream in = System.in;//System.in返回的是字节流对象
	StringBuilder sb = new StringBuilder();//新建字符缓冲区,用于记录一行数据
	while (true)//循环录入
	{
		int by = in.read();//读取一个字节
		if(by=='\r')//如果读到\r就继续读
			continue;
		if(by=='\n')//如果读到\n就代表一行数据读取完毕
		{
			String s = sb.toString();//将字符缓冲区变成字符串
			if ("over".equals(s))//如果是over,就停止程序
				break;
			else
				System.out.println(s.toUpperCase());//返回大写形式
			sb.delete(0,sb.length());//没返回一次就清空字符缓冲区
		}
		else
			sb.append((char)by);//没有读到\r和\n 就将读到的字节强转成字符,并存储进字符缓冲区
	}
}</span>
对于上面的程序,读取一行后再返回大写形式,跟字符流缓冲区的readLine方法相识,这时就涉及到转换流,将字节输入流转换成字符输入流
上面的程序就可以改为如下形式:这样可以简化书写和提高效率
<span style="font-size:14px;">public static void method_2() throws IOException
{
	//读取键盘的标准书写形式
	BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
	//控制台输出的便准书写形式
	BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(System.out));
	String line = null;
	while ((line=br.readLine())!=null)//一次读取一行
	{
		if("over".equals(line))
			break;
		bw.write(line.toUpperCase());//将数据输出到控制台
		bw.newLine();//进行换行一次,不然数据会一直连续输出
		bw.flush();
	}
	br.close();
	bw.close();
}</span>
十二、操作规律:在使用io流时,需要
          明确源和目的
                 源:输入流 InputStream Reader
                 目的:输出流 OutputStream Writer
         明确是否操作文本数据
                 是:字符流 常用:FileReader FileWriter
                 否:字节流 常用:FileInputStram FileOutputStream
         明确具体对象
                  源:内存 硬盘 键盘
                  目的:内存 硬盘 控制台
练习:
<span style="font-size:14px;">class TransStreamTest 
{
	public static void main(String[] args) 
	{
		//copyPic();
		showText();
	}
	/*
	将一个图片文件中数据存储到另一个文件中
	1,要读取一个图片文件并写入到另一个文件,所以同时要用到输入流和输出流
	2,图片是以2进制存储,要用到字节流。
	3,源和目的都是硬盘:明确用FileInputStream和FileOutputStream
	  是否要提高效率?是,所以要用到BufferedInputStream和BufferedOutputStream
	*/
	public static void copyPic()
	{
		BufferedInputStream bfis = null;
		BufferedOutputStream bfos = null;
		try
		{
			bfis = new BufferedInputStream(new FileInputStream("1.jpg"));
			bfos = new BufferedOutputStream(new FileOutputStream("3.jpg"));
			int by = 0;
			while ((by=bfis.read())!=-1)
			{
				bfos.write(by);
			}
		}
		catch (IOException e)
		{
			throw new RuntimeException("复制失败");
		}
		finally
		{
			try
			{
				if(bfis!=null)
					bfis.close();
			}
			catch (IOException e)
			{
				throw new RuntimeException("关闭读取流失败");
			}
			try
			{
				if(bfos!=null)
					bfos.close();
			}
			catch (IOException e)
			{
				throw new RuntimeException("关闭写入流失败");
			}
		}
	}
	/*
	将一个文本数据打印在控制台上
	1,源为文本,目的为控制台
	2,文本为字符数据,用到字符流,Reader
	  目的为控制台对应的对象是System.out,源是字符数据,目的是字节数据,所以要用到转换流
	  要用到OutputStreamWriter
	3,源为硬盘,明确用FileReader,要提高效率,用到BufferReader
	*/
	public static void showText()
	{
		BufferedReader bfr = null;
		BufferedWriter bfw= null;
		try
		{
			bfr = new BufferedReader(new FileReader("trans.txt"));
			bfw = new BufferedWriter(new OutputStreamWriter(System.out));
			String line = null;
			while ((line=bfr.readLine())!=null)
			{
				bfw.write(line);
				bfw.newLine();
			}
		}
		catch (IOException e)
		{
			throw new RuntimeException("读取失败");
		}
		finally
		{
			try
			{
				if(bfr!=null)
					bfr.close();
			}
			catch (IOException e)
			{
				throw new RuntimeException("读取流关闭失败");
			}
			try
			{
				if(bfw!=null)
					bfw.close();
			}
			catch (IOException e)
			{
				throw new RuntimeException("写入流关闭失败");
			}
		}
	}
}</span>
练习:保存异常信息
<span style="font-size:14px;">class ExceptionInfoDemo 
{
	//保存异常信息
	public static void main(String[] args) 
	{
		try
		{
			int[] arr = new int[1];
			System.out.println(arr[2]);//定义一个异常
		}
		catch (ArrayIndexOutOfBoundsException e)
		{
			//保存异常信息时,记录时间
			Date d = new Date();
			SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
			String s = sdf.format(d);
			try
			{
				//定义一个字节打印流
				PrintStream ps = new PrintStream("Exception.log");
				System.setOut(ps);//从控制台中写入异常信息
				ps.println(s);
			}
			catch (IOException io)
			{
				throw new RuntimeException("保存异常信息失败");
			}
		}
	}
}</span>
File类:文件和目录路径名的抽象表示形式
一、常用方法
创建:
boolean createNewFile() :创建文件,如果文件存在,则不创建,返回false
boolean mkdir() :创建文件夹
boolean mkdirs() :创建多级文件夹
删除:
boolean delete():删除指定文件
void deleteOnExit():在程序退出时删除指定文件
判断:
boolean exists():判断是否存在
boolean isAbsolute():是否为绝对路径名。 
boolean isDirectory():是否是一个目录。 
boolean isFile():是否是一个标准文件。 
boolean isHidden():是否是一个隐藏文件。
获取:
String getName():获取文件或者文件夹名称 
String getParent():获取父目录,如果没有则返回null  
String getPath():获取路径
String getAbsolutePath():获取绝对路径
long lastModified():获取文件最后修改的时间
long length():获取文件长度
        String[] list():返回指定目录下所有文件和文件夹的名称
        String[] list(FilenameFilter filter):返回指定目录下满足指定过滤器的文件和文件夹的名称
        File[] listFiles():返回指定目录下所有文件
        File[] listFiles(FileFilter filter):返回指定目录下符合指定过滤器的文件 
        File[] listFiles(FilenameFilter filter):返回指定目录下符合指定过滤器的文件 
代码示例:
public static void method_1()throws IOException
{
	File f = new File("file.txt");
	System.out.println("创建文件"+f.createNewFile());
	File dir = new File("files");
	System.out.println("创建文件夹"+dir.mkdir());
	File dirs = new File("files\\aa\\bb\\cc\\dd\\kk");
	System.out.println("创建多级文件夹"+dirs.mkdirs());

	System.out.println("文件是否存在"+f.exists());
	System.out.println("是否为文件"+f.isFile());
	System.out.println("是否为文件夹"+dir.isDirectory());

	System.out.println("父目录名称为"+f.getParent());
	System.out.println("path:"+f.getPath());
	System.out.println("AbsolutePath:"+f.getAbsolutePath());
	System.out.println("toString:"+f.toString());
}
代码示例:
//获取指定文件夹下的所有文件,listFiles方法返回的是文件
public static void showListFiles()throws IOException
{
	File dir = new File("d:\\java\\day18");
	File[] files = dir.listFiles();
	for(File f:files)
	{
		System.out.println(f.getName()+"----"+f.length());
	}
}
//自定义过滤器,列出指定文件
public static void showListFiles_1()throws IOException
{
	File dir = new File("d:\\java\\day18");
	File[] files = dir.listFiles(new FilenameFilter()//匿名内部类,实现FilenameFilter接口
	{
		public boolean accept(File dir,String name)//覆盖accept方法,自定义过滤方法
		{
			if (name.endsWith(".mp3"))
				return true;
			return false;
		}
	});
	for(File f:files)
	{
		System.out.println(f.getName()+"----"+f.length());
	}
}
//列出指定目录下的所有文件的文件名称,list方法返回的是文件的名称
public static void showListFiles_2()throws IOException
{
	File dir = new File("d:\\java\\day18");
	String[] name = dir.list();
	for(String n:name)
	{
		System.out.println(n);
	}
}
练习1:
/*
列出目录下的所有文件,如果有子文件夹,也要列出子文件夹的文件
*/
class FileDemo3 
{
	public static void main(String[] args) 
	{
		File dir = new File("d:\\java");//指定文件夹
		showDir(dir);
	}
	public static void showDir(File dir)
	{
		System.out.println(dir);
		File[] files = dir.listFiles();//调用listFiles方法返回文件夹下的文件和文件夹
		for(File f:files)
		{
			if(f.isDirectory())//进行判断,如果是文件夹,再次调用该方法,递归
				showDir(f);
			else
			System.out.println(f);
		}
	}
}
练习2:
/*
删除指定文件夹下的所有文件以及文件夹,
删除文件是从里到外的,这里也用到递归的思想
*/
class RemoveDir 
{
	public static void main(String[] args) 
	{
		File dir = new File("e:\\java_1");//指定要删除的文件夹
		removeDir(dir);
	}
	public static void removeDir(File dir)
	{
		File[] files = dir.listFiles();//调用listFiles方法,列出文件夹下的所有文件以及文件夹
		for(File f:files)
		{
			if(f.isDirectory())//如果是文件夹,再次调用该方法
				removeDir(f);
			else
				System.out.println(f.getName()+"-----"+f.delete());//否则就调用delete方法将文件删除
		}
		System.out.println(dir.getName()+"----"+dir.delete());
	}
}
练习3:
/*
需求:将一个文件夹下的所有java文件的绝对路径写到一个文本文件中
*/
import java.io.*;
import java.util.*;
class ListJavaFiles 
{
	public static void main(String[] args) 
	{
		File dir = new File("d:\\java");
		ArrayList<File> list = new ArrayList<File>();
		filesToList(dir,list);
		File f = new File("javalist.txt");
		writeToFile(list,f.toString());
	}
	//获取文件列表的方法,并存储进一个File类型的集合
	public static void filesToList(File dir,List<File> list)
	{
		File[] files = dir.listFiles();//获取指定目录所有文件
		for(File f:files)
		{
			if(f.isDirectory())//判断如果是文件夹,再次调用该方法
				filesToList(f,list);
			else
				//在添加文件进集合的时候对文件名称进行判断,如果是以.java结尾就添加进集合
				if((f.getName()).endsWith(".java"))
					list.add(f);
		}
	}
	//将集合中的数据写入文本文件
	public static void writeToFile(List<File> list,String fileName)
	{
		BufferedWriter bw = null;//字符缓冲流
		try
		{
			bw = new BufferedWriter(new FileWriter(fileName));
			for(File f:list)//增加for循环
			{
				bw.write(f.getAbsolutePath());//获取文件的绝对路径
				bw.newLine();
				bw.flush();
			}
		}
		catch (IOException e)
		{
			throw new RuntimeException("保存文件列表失败");
		}
		finally
		{
			try
			{
				if(bw!=null)
					bw.close();
			}
			catch (IOException e)
			{
				throw new RuntimeException("关闭流失败");
			}
		}
	}
}
二、properties:属性文件,是Hashtable的子类,具有map集合的特点,里面的键值对都是String类型
练习1:
/*
需求:模拟使用软件,如果软件使用次数已经达到5次,就不能使用软件
要用定义计数器,但是程序每运行一次,计数器就在内存中就会消失,
所有要将计数器的值写在properties文件中
*/
import java.io.*;
import java.util.*;
class Count 
{
	public static void main(String[] args) throws IOException
	{
		Properties prpo = new Properties();//新建Properties对象
		File file = new File("count.ini");//定义一个文件记录次数
		if(!(file.exists()))//判断文件是否存在,不存在就新建文件
			file.createNewFile();

		FileInputStream fos = new FileInputStream(file);//定义字节输入流
		prpo.load(fos);//从输入流中读取属性列表(键和元素对)

		int count = 0;//定义计数器
		String value = prpo.getProperty("time");//获取文件中key文time的值
		if (value!=null)
		{
			count = Integer.parseInt(value);//将值转成Integer类型,再赋值给计数器
			if (count>=5)//判断次数是否超过5次
			{
				System.out.println("使用次数已到,继续使用请购买!");
				return ;
			}
		}
		count ++;//计数器加1
		prpo.setProperty("time",count+"");//将加1后的值写入文件中

		FileOutputStream fis = new FileOutputStream(file);//定义字节输出流
		prpo.store(fis,"");//将字节输出流对象传给store方法,将流中的数据写入文件

		fos.close();
		fis.close();
	}
}
练习2:
/*
将一个文本文件中的信息存到properties中
文本中的信息是key=value的形式
读取文本文件,以“=”作为分隔符,用String类中的split方法取出key和value
*/
class PropertiesDemo 
{
	public static void main(String[] args) throws IOException
	{
		method();
	}
	public static void method()throws IOException
	{
		//字符缓冲输入流
		BufferedReader bfr = new BufferedReader(new FileReader("prop.txt"));
		//properties对象
		Properties p = new Properties();
		String line = null;
		while ((line=bfr.readLine())!=null)
		{
			String[] arr = line.split("=");//读取一行后以“=”切割字符串
			p.setProperty(arr[0],arr[1]);//setPropertry方法设置key和value
		}
		bfr.close();//关闭流
		System.out.println(p);
	}
}
三、序列流和切割文件
(序列流)SequenceInputStream 表示其他输入流的逻辑串联。它从输入流的有序集合开始,并从第一个输入流开始读取,直到到达文件末尾,接着从第二个输入流读取,依次类推,直到到达包含的最后一个输入流的文件末尾为止 
代码示例:
import java.io.*;
import java.util.*;
class SplitFile 
{
	public static void main(String[] args) throws IOException
	{
		//splitFile();
		mergeFile();
	}
	//合并文件
	public static void mergeFile()throws IOException
	{
		//定义vactor集合,用于存储字节输入流对象
		Vector<FileInputStream> v = new Vector<FileInputStream>();
		v.add(new FileInputStream("files\\1.part"));
		v.add(new FileInputStream("files\\2.part"));
		v.add(new FileInputStream("files\\3.part"));
		//获取枚举
		Enumeration<FileInputStream> en = v.elements();
		//将枚举作为参数传递给合并流对象
		SequenceInputStream sis = new SequenceInputStream(en);
		//定义字节输出流,将多个文件写入到一个文件
		FileOutputStream fos = new FileOutputStream("files\\4.mp3");
		byte[] buf = new byte[1024*1024*8];
		int len = 0;
		while ((len=sis.read(buf))!=-1)
		{
			fos.write(buf);
			fos.flush();
		}
		sis.close();
		fos.close();
	}
	//切割文件
	public static void splitFile()
	{
		FileInputStream fis = null;
		FileOutputStream fos = null;
		try
		{
			//定义缓冲区,文件以800kb大小分割
			byte[] buf = new byte[1024*800];
			int len = 0;
			int count =1;//定义计数器,用于给文件命名
			fis = new FileInputStream("1.mp3");
			while ((len=fis.read(buf))!=-1)
			{
				//没填满缓冲区一次,就新建一个字节输出流,写入一个文件
				fos = new FileOutputStream("files\\"+(count++)+".part");
				fos.write(buf,0,len);
			}
		}
		catch (IOException e)
		{
			throw new RuntimeException("切割文件失败");
		}
		finally
		{
			try
			{
				if(fis!=null)
					fis.close();
			}
			catch (IOException e)
			{
				throw new RuntimeException("关闭读取流失败");
			}
			try
			{
				if(fos!=null)
					fos.close();
			}
			catch (IOException e)
			{
				throw new RuntimeException("关闭写入流失败");
			}
		}
	}
}
四、对象的序列化:ObjectInputStream和ObjectOutputStream,能够读取和写入自定义对象。
       自定义的对象要实现Serializable接口,该接口的作用就是给对象序列化,给对象进行唯一标识。
代码示例:将自定义对象写入到文本文件中并进行读取
自定义Proson对象
import java.io.*;
class Person implements Serializable
{
	//该句代码的作用是给对象进行固定的标识,修改成员权限后,依然可以读取文件
	public static final long serialVersionUID = 42L;
	private String name;
	private int age;
	Person(String name,int age)
	{
		this.name = name;
		this.age = age;
	}
	public String getName()
	{
		return name;
	}
	public int getAge()
	{
		return age;
	}
}
写入和读取的类
import java.io.*;
class ObjectStreamDemo 
{
	public static void main(String[] args) throws Exception
	{
		//write();
		read();
	}
	public static void write()throws IOException
	{
		ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("obj.txt"));
		oos.writeObject(new Person("zhangsan",21));
		oos.close();
	}
	public static void read()throws Exception
	{
		ObjectInputStream ois = new ObjectInputStream(new FileInputStream("obj.txt"));
		Person p = (Person)ois.readObject();
		System.out.println(p.getName()+"-----"+p.getAge());
	}
}
五、管道流: PipedInputStream和PipedOutputStream:输入流和输出流进行连接,通常结合多线程进行使用
        在读取过程中,如果没有数据写入,则会等待。这种现象称为阻塞

六、RandomAccessFile:可以随机对文件进行读写操作,类中封装了输入流和输出流,并有一个文件指针,实现随机写入和读取
       通过getFilePointer获取指针位置,通过seek方法设置指针位置。
七、io一些其他类
       DataInputStream和DataOutputStream:用于操作基本数据类型
       ByteArrayInputStream和ByteArrayOutputStream:用于操作字节数组
       CharArrayReader和CharArrayWriter:用于操作字符数组
       StringReader 和StringWriter:用于操作字符串
八、字符编码:使用转换流时,可以指定字符编码
       常见字符编码表
       ASCII:美国标准信息交换码。
       用一个字节的7位可以表示。
       ISO8859-1:拉丁码表。欧洲码表
       用一个字节的8位表示。
       GB2312:中国的中文编码表。
       GBK:中国的中文编码表升级,融合了更多的中文文字符号。
       Unicode:国际标准码,融合了多种文字。
       所有文字都用两个字节来表示,Java语言使用的就是unicode
       UTF-8:最多用三个字节来表示一个字符。
       编码解码过程
       编码就是按照指定的编码表,将字符串转换成字节数组
       解码就是按照指定的编码表,将字节数组转换成字符串
练习:
/*
有五个学生,每个学生有3门课的成绩,
从键盘输入以上数据(包括姓名,三门课成绩),
输入的格式:如:zhagnsan,30,40,60计算出总成绩,
并把学生的信息和计算出的总分数高低顺序存放在磁盘文件"stud.txt"中。

分析:定义学生对象,定义学生对象工具类
从键盘录入数据,再存储到文件中,要用到转换流
要存储多个对象,要用到集合,并且要按照总分排序,明确用TreeSet
*/
import java.io.*;
import java.util.*;
//定义学生对象,并实现Comparable接口,让学生具有比较性
class Student implements Comparable<Student>
{
	private String name;
	private int cn,ma,en;
	private int sum;
	Student(String name,int cn,int ma,int en)
	{
		this.name = name;
		this.cn = cn;
		this.ma = ma;
		this.en = en;
		this.sum = cn+ma+en;
	}
	public String getName()
	{
		return name;
	}
	public int getCn()
	{
		return cn;
	}
	public int getMa()
	{
		return ma;
	}
	public int getEn()
	{
		return en;
	}
	public int getSum()
	{
		return sum;
	}
	//复写hashCode方法,返回自定义姓名加年龄的哈希值
	public int hashCode()
	{
		return this.name.hashCode()+sum*39;
	}
	//复写equals方法,姓名和年龄一致时,就视为同一个对象
	public boolean equals(Object obj)
	{
		if(!(obj instanceof Student))
			throw new ClassCastException("类型不兼容");
		Student s = (Student)obj;
		return this.name.equals(s.getName()) && this.sum==s.getSum();
	}
	//复写compareTo方法,按照总分排序
	public int compareTo(Student s)
	{
		int a = new Integer(this.sum).compareTo(new Integer(s.getSum()));
		if(a==0)
			return this.name.compareTo(s.getName());
		return a;
	}
}
//定义学生工具类
class StuTool
{
	//定义一个空比较器,从键盘获取学生信息的方法
	public static Set<Student> getStudents()throws IOException
	{
		//直接调用定义比较器的方法,然后比较器参数传入null,提高代码复用
		return getStudents(null);
	}
	//定义一个有比较器参数的方法
	public static Set<Student> getStudents(Comparator<Student> comp)throws IOException
	{
		//定义字节流缓冲区对象,从键盘录入信息
		BufferedReader bfr = new BufferedReader(new InputStreamReader(System.in));
		Set<Student> stus;
		//进行判断,如果比较器为null,就新建一个null比较器的对象
		//如果比较器不为空,就新建一个有比较器参数的对象
		if(comp==null)
			stus = new TreeSet<Student>();
		else
			stus = new TreeSet<Student>(comp);
		String line =null;
		while ((line=bfr.readLine())!=null)
		{
			if("ok".equals(line))
				break;
			//将键盘录入的内容,用“,”进行切割,并存储集合
			String[] arr = line.split(",");
			stus.add(new Student(arr[0],Integer.parseInt(arr[1]),
										Integer.parseInt(arr[2]),
										Integer.parseInt(arr[3])));
		}
		bfr.close();
		return stus;
	}
	//将学生信息写入文件的方法
	public static void writeToFile(Set<Student> stus)throws IOException
	{
		//定义字符缓冲流兑现
		BufferedWriter bfw = new BufferedWriter(new FileWriter("student.txt"));
		for(Student s:stus)//用增强for循环将集合中的学生信息取出并写入文件
		{
			bfw.write("姓名:"+s.getName());
			bfw.write("语文:"+s.getCn());
			bfw.write("数学:"+s.getMa());
			bfw.write("英语:"+s.getEn());
			bfw.write("总分:"+s.getSum());
			bfw.newLine();
		}
		bfw.close();
	}
}
class StudentInfo 
{
	public static void main(String[] args) throws IOException
	{
		Comparator<Student> comp = Collections.reverseOrder();
		Set<Student> stus = StuTool.getStudents(comp);
		StuTool.writeToFile(stus);
	}
}


     







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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值