代码改变世界

黑马程序员--IO流

2013-07-22 17:20  java20130722  阅读(199)  评论(0编辑  收藏  举报

---------------------- <a href="http://edu.csdn.net"target="blank">ASP.Net+Android+IO开发S</a>、<a href="http://edu.csdn.net"target="blank">.Net培训</a>、期待与您交流! ----------------------


java的输出/输入是以流的方式来处理的,流是在计算机的输入、输出操作中流动的数据序列。


流的分类:

(1)按照操作数据分为字节流和字符流。

(2)按照流向分为输入和输出流。


输入输出流(IO流)常用基类:

字节流基类:InputStream和OutputStream。

字符流基类:Reader和Writer。

注:由这四个类派生出来的子类名称都是以其父类名作为其子类名的后缀。

     如,InputStream的子类FileInputStream

  Reader的子类FilReader


例子,在硬盘上创建一个文件,并写入一些文字数据,并进行异常处理。


import java.io.*;
class FileWriterDemo
{
	public static void main(String[] args)
	{
		//创建一个FileWrite对象。
		FileWriter  fw =null;
		try
		{
			fw = new FileWriter("d:\\demo.txt");
			//调用write方法,将字符串写入到流中
			fw.write("wee332ddd");
		}
		catch(IOException e)
		{
			System.out.println(e.toString());
		}
		finally
		{
			try
			{
				if(!fw=null)
					fw.close();
			}
			catch(IOException e)
			{
				System.out.println(e.toString());
			}
		}
	}
}



注:创建一个FileWriter对象,该对象一被初始化就必须要明确被操作的文件,而且该文件会被创建到指定目录下。如果该目录下已有同名文件,将被覆盖。

fw.flush();//刷新对象中的缓冲数据,将数据刷到目的地中

fw.close();//关闭流资源,但是关闭后会刷新一次内部的缓冲区中的数据,将数据刷到目的地中

flush()和close()区别:

flush刷新后,流可以继续使用,close刷新后,会将流关闭。


实现对已有文件的续写:

//传递一个true参数,代表不覆盖已有文件,并在已有文件的末尾处进行数据续写。

FileWriter fw = new FileWriter("d:\\demo.txt",true);

fw.write("ssd\r\nhello");//\r\n是换行

fw.close();


例子:从指定文件中读取数据

import java.io.*;
class FileReaderDemo
{
	public static void main(String[] args)throws IOException
	{
		//创建一个文件读取流对象,和指定名称的文件相关联
		//要保证该文件是存在的,如果不存在,就会发生异常FileNotFoundException
		FileReader fd = new FileReader("d:\\demo.txt");
		/*
		第一种方式:一次读取一个字符
		int ch = 0;
		//read()是一次读取一个字符
		while((ch=fd.read())!=-1)
		{
			System.out.println("ch="+(char)ch);
		}*/
		//第二种:通过字符数组进行读取
		char[] buf = new char[1024];
		int len = 0;
		while((len=fd.read(buf))!=-1)
		{
			System.out.println(new String(buf,0,len));
		}
		fd.close();
	}
}


例子:拷贝文件(将一个盘中的文本文件拷贝到另一个另一个盘中的文本文件中)

import java.io.*;
class CopyFileDemo
{
	public static void main(String[] args)
	{
	//创建一个FileWrite对象。
		FileWriter  fw = null;
		FileReader fd = null;
		try
		{
			fw = new FileWriter("e:\\copy_file.txt");
			fd = new FileReader("d:\\demo1.txt");
			int len = 0;
			char[] buf= new char[1024];
			while((len=fr.read(buf))!=-1)
			{
				fw.write(buf,0,len);
			} 
		}
		catch(IOException e)
		{
			throw new RuntimeException("文件读写失败!");
		}
		finally
		{
			if(!fr=null)
			try
			{
				fr.close();
			}
			catch(IOException e)
			{
				System.out.println("关闭读取流失败");
			}
			if(!fw=null)
			try
			{
				fw.close();
			}
			catch(IOException e)
			{
				System.out.println("关闭写入流失败");
			}
		}
	}
}



缓冲区:是为了提高流的操作效率而出现的,在缓冲区之前必须要先用流对象。该缓冲区提供了一个跨平台的换行符newLine(),只要用到缓冲区就要记得刷新。

字符读取流缓冲区:该缓冲区提供了一个一次读一行的方法readLine(),方便于对文本数据的读取,当返回null时,表示读到文件末尾。readLine方法返回的时候只返回回车符之前的数据内容,并不返回回车符。


装饰类:当想要对已有对象进行功能增强时,可以定义类,将已有对象传入,基于已有的功能,并提供加强功能,那么自定义的该类就称为装饰类。

装饰类通常会通过构造方法接收被装饰的对象 ,并基于被装饰的对象的功能,提供更强功能。


装饰和继承的区别,如下:

装饰模式比继承要灵活,避免了继承体态臃肿,而且降低了类与类之间的关系。装饰因为增强已有对象,具备的功能和已有的是相同的,只不过提供了跟强功能,所以装饰类和被装饰类通常都属于一个体系中。


例子:通过缓冲区复制一个java文件

import java.io.*;
class CopyDemo
{
	public static void main(String[] args)
	{
	//创建一个FileWrite对象。
		BufferedReader bufr= null;
		BufferedWriter bufw= null;
		try
		{
			bufr = new BufferedReader(new FileWriter("d:\\FileReaderDemo.java"));
			bufw =new BufferedWriter(new FileReader("copy.txt"));
			String line =null;
			char[] buf= new char[1024];
			while((len=bufr.readLine())!=null)
			{
				bufw.write(line);
				bufw.newLine();
				bufw.flush();
			} 
		}
		catch(IOException e)
		{
			throw new RuntimeException("文件读写失败!");
		}
		finally
		{
			if(bufr!=null)
			try
			{
				bufr.close();
			}
			catch(IOException e)
			{
				System.out.println("关闭读取流失败");
			}
			if(bufw!=null)
			try
			{
				bufw.close();
			}
			catch(IOException e)
			{
				System.out.println("关闭写入流失败");
			}
		}
	}
}


例子:模拟BufferedReader功能

import java.io.*;
class MyBufferedReader
{
	private FileReader r;
	MyBufferedReader(FileReader r)
	{
		this.r = r;
	}

	public String myReadLine() throws IOException
	{
		StringBuilder sb = new StringBuilder();
		int ch = 0;
		while((ch=r.read())!=-1 )
		{
			if(ch == '\r')
				continue;
			if(ch == '\n')
				return sb.toString();
			else
				sb.append((char)ch);
		}
		return null;
	}

	public void myClose() throws IOException
	{
		r.close();
	}
}


class MyBufferedReaderDemo
{
	public static void main(String[] args) throws IOException
	{
		FileReader fr = new FileReader("buf.txt");
		MyBufferedReader mbr = new MyBufferedReader(fr);
		String line = null;
		while((line = mbr.myReadLine())!=null)
		{
			System.out.println(line);
		}
		mbr.myClose();
	}
}

例子:键盘录入

import java.io.*;
class TransDemo
{
	public static void main(String[] args)throws IOException
	{
		InputStreamReader isr = new InputStreamReader(new InputStream(System.in));
		BufferedReader bufr = new BufferedReader(isr);
		BufferedWriter bufw = 
		new BufferedWriter(new OutputStreamWriter(new OutputStream(System.out)));
		String line = null;
		while((line=bufr.readLine())!=null) 
		{
			if("over".equals(line))
			break;
			bufw.write(line.toUpperCase());
			bufw.newLine();
			bufw.flush();
			//System.out.println(line.toUpperCase());
		}
		bufw.close();
		bufr.close();
	}
}

流操作的基本规律:

(1)明确源和目的

源:输入流  InputStream和Reader。

目的:输出流OutputStream和Writer。

(2)操作的是否是文本

是:选择字符流。

不是:选择字节流。

(3)当体系明确后,再明确要使用哪个具体对象。通过设备来进行区分

源设备:内存、硬盘(FileStream)、键盘(System.in)。

目的设备:内存、硬盘(FileStream)、控制台(System.out)。


例如:将一个文本文件中数据存到另一个文件中。(复制文件)

分析:

第一步:确定源。选择读取流InputStream、Reader,因为操作的是文本文件,所以最终选择Reader。

第二步:明确对象。硬盘,一个文件。因为reader体系中可以操作文件的对象是FileReader。

   是否需要提高效率?是,加入Reader体系中的缓冲区BufferedReader。

 BufferedReader  bufr = new BufferedReader(new FileReader("a.txt"));

第三步:确定目的。选择OutputStream、Writer,因为操作的是文本文件,所以最终选择Writer。

  因为Writer体系中可以操作文件的对象是FileWriter。

 是否需要高效?是,加入Writer体系中的缓冲区BufferedWriter。

BufferedWriter bufw = new BufferedWriter(new FileWriter("b.txt"));


File类:用来将文件和文件夹封装成对象。方便对文件和文件夹的属性信息进行操作。

File对象可以作为参数传递给流的构造函数。


File类常见方法:

(1)创建

boolean  createNewFile();在指定位置创建文件,如果该文件已经存在,则不创建,返回false。

boolean  mkdir();创建文件夹。

(2)删除

boolean  delete();删除失败则返回false。

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

(3)判断

boolean  canExecute();判断是否可执行。

boolean  exists();判断文件是否存在。

boolean  isDirectory();判断是否是目录。

boolean  isFile();判断是否是文件。

boolean isAbsolute();判断是否是绝对路径。

注:在判断文件或者目录是否存在时,必须要先判断该文件对象是否存在。

(4)获取

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

getAbsolutePath();获取绝对路径。

lastModified();文件最后一次修改时间。

length();返回由此抽象路径名表示的文件的长度。

String[]  list();返回一个字符串数组,这些字符串指定此抽象路径名表示的目录中的文件和目录。调用list方法的File对象必须是封装了一个目录,该目录还必须存在。


例子:列出指定目录下的文件或文件夹,包含子目录中的内容。

分析:因为目录中还有目录,只要使用同一个列出目录功能的函数即可。在列出过程中出现的还是目录的话,可以再次调用本功能,也就是函数自身调用自身。这种表现形式或者编程手法称为递归。

递归需要注意的:第一点,限定条件。第二点,要注意递归次数,尽量避免内存溢出。

import java.io.*;
class FileDemo2
{
	public static void main(String[] args)
	{
		File dir = new File("d:\\java0327");
		showDir(dir);
	}

public static void showDir(File dir)
	{
		System.out.println(dir);
		File[] files=dir.listFiles();
		for(int i =0;i<files.length;i++)
		{
		//判读如果是目录的话,就调用方法本身
			if(files[i].isDirectory())
				showDir(files[i]);
			else
				System.out.println(files[i]);
		}
	}
}

printStream和printWriter区别:

(1)printStream是字节打印流。

构造函数可以接收的参数类型:file对象,File类型;字符串路径,String类型;字节输出流,OutputStream。

(2)printWriter是字符打印流。

构造函数可以接收的参数类型:file对象,File类型;字符串路径,String类型;字节输出流,OutputStream;字符输出流,Writer。


RandomAccessFile:该类不算是IO体系中的子类,而是直接继承Object。但它是IO包成员,因为它具备读和写的功能。内部封装了一个数组,而且通过指针对数组的元素进行操作。可以通过getFilePointer获取指针位置,同时可以通过seek改变指针的位置。


通过他的构造函数RandomAccessFile(File file,String mode)可以看出,该类只能操作文件,而且操作文件还有模式:r,rw,rws,rwd,而且该对象的构造函数要操作的文件如果不存在,会自动创建,如果存在则不会覆盖。如果模式为"r",不会创建,会去读取一个已存在的文件,如果文件不存在,则会出现异常。如果模式为"rw",操作的文件不存在,会自动创建,如果存在,则不会覆盖。

例子:RandomAccessFile

import java.io.*;
class RandomAccessFileDemo 
{
	public static void main(String[] args) throws IOException
	{
		writeFile();
		readFile();

	}

	public static void readFile()throws IOException
	{
		RandomAccessFile raf = new RandomAccessFile("raf.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:"+name);
		System.out.println("age:"+age);
		raf.close();
	}

	public static void writeFile()throws IOException
	{
		RandomAccessFile raf = new RandomAccessFile("raf.txt","rw"); 

		raf.write("董董".getBytes());
		raf.writeInt(97);
		raf.write("丽丽".getBytes());
		raf.writeInt(99);
		raf.close();
	}
}
   



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


ByteArrayInputStream和ByteArrayOutputStream:用来操作字节数组的流对象。

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

ByteArrayOutputStream:在构造的时候,不用定义数据目的,因为该对象中内部已经封装了可变长度的字节数组。

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






-------------------- <a href="http://edu.csdn.net"target="blank">ASP.Net+Android+IOS开发</a>、<a href="http://edu.csdn.net"target="blank">.Net培训</a>、期待与您交流! ----------------------