黑马程序员--IO流概述、字符流、字符流缓冲区

本文详细介绍了Java IO流的概念,包括字节流与字符流的分类,以及如何使用FileReader和FileWriter进行文件读写操作。同时,文章探讨了字符流缓冲区的作用与使用方法,通过实例展示了如何利用缓冲区技术提高数据读写效率。

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

第一节--IO流概述

设备(如硬盘、内存)之间需要进行数据传输。按java面向对象的思想,这需要对象去操作数据,即IO流对象,这些对象都存在于IO包中。

 

按操作的数据可以分为字符流和字节流:

数据都是以字节码形式存储,所以字节流可以操作所有类型的数据。字符流融合了字符编码表,所以可以操作文本数据。

 

按数据的流向可以分为输入流和输出流:

IO流对数据的操作只有两种:读取和写入。读取是指将指定数据读取到流对象中,写入是指将数据写入到指定的位置。

 

字节流的两个基类:InputStreamOutputStream

这两个基类描述了对字节码数据进行读写的共性方法。

字符流的两个基类:ReaderWriter

这两个基类描述了对文本数据进行读写的共性方法。

这四个基类的子类特点:类名基本上都是以父类名作为后缀,前缀体现子类的特有功能。

第二节--字符流

数据存在的最常见形式:文件。以对文件的读写操作为例,学习IO流的基本操作。

对文件进行操作的字符流的具体子类是:FileReaderFileWriter

FileWriter的使用:对字符文件进行写入操作

public class FileWriterDemo {
public static void main(String[] args) throws IOException {
		//1.创建一个FileWriter对象,一初始化就要明确写入到哪个文件
		//在指定目录下创建了一个demo.txt文件,如果该路径下有同名
		//文件,同名文件会被覆盖
		FileWriter fw = null;
		try{
			fw = new FileWriter("e:\\demo.txt");
			//2.调用write方法,将数据写入流中
			fw.write(" hello");
			//3.调用flush方法,将流中数据刷进目的地
			fw.flush();
//flush后流还存在,可以继续接受数据
			fw.write(" world");
			fw.flush();
		}
		catch(IOException e){
			System.out.println("文件写入失败");
		}
		//java本身不能写入数据,而是调用系统资源进行写入操作,
		//使用完毕,要调用close方法,关闭资源关闭前会先刷新
		finally{
			try{
				if(fw!=null)
				fw.close();
			}
			catch(IOException e){
				System.out.print("关闭失败");
			}
		}
}
}

注意:演示中的数据写入是覆盖性的,即每次写入都会将文件中的数据覆盖,如果想在原来数据后续写,需要在初始化流对象时使用此构造方法FileWriter(File file, boolean append),传递参数true。创建流对象时指定的目的地可能不存在或创建失败,所以需要进行异常处理,流对象创建失败,关流时也会失败,所以关闭资源也需要进行异常处理。

FileReader的使用:对字符文件进行读取操作

读取的第一种方式:使用read()方法,一次读取一个字符,返回int类型的字符,并自动往下读取,读到末尾返回-1.

ublic class FileReaderDemo {

	public static void main(String[] args) {
FileReader fr = null;
		try{
			//创建字符读取流对象,关联要读取的文件
			fr = new FileReader("e:\\demo.txt");
			//read方法返回的是读取的一个字符,int类型,定义一个变量存储
			//当数据被读取完会返回-1
			int ch = 0;
			while((ch=fr.read())!=-1){
				System.out.println("ch="+(char)ch);
			}
		}catch(IOException e){
			
		}
		finally{
			try {
				if(fr!=null)
				fr.close();
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
}
}

读取的第二种方式:使用read(char [] cbuff)方法,将字符读入字符数组,返回的是读取的字符个数,并自动往下读,读到末尾返回-1.

public class FileReaderDemo {

	public static void main(String[] args) {
		FileReader fr = null;
		int len = 0;
		char[]buf = new char[2];
		try {
			fr = new FileReader("e:\\demo.txt");
			//fr.read()返回的是读取的字符个数
			while((len=fr.read(buf))!=-1)
				System.out.print(new String(buf,0,len));
		} 
		catch (Exception e) {
			// TODO: handle exception
		}
		finally{
			try {
				if(fr!=null)
				fr.close();
			} 
			catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
	}
}

注意:将字符读入数组,数组相当于一个缓冲区,每一次读取都会将数组中的数据。如果最后一次读取字符的个数小于数组长度,那么数组中的数据只有前半部分被覆盖。所以使用数组中的数据时,注意读取到的字符个数。尤其写入数组时,要使用写入数组一部分的方法write(char[] cbuf, int off, int len),off=0,len=读取的字符个数。

练习:使用字符流复制文件

/*
 * 需求:将D盘下的demo.txt文件复制到D盘下的demo_copy.txt中
 * 分析:复制,其实就是在D盘下创建文件demo_copy.txt,读取demo.txt文件,写入到demo_copy.txt中
 * 		 读取方式有两种:一次读取一个字符和将字符读取到字符数组中
 * 		 写入方式有两种:一次写入一个字符和一次写入一个数组
 */
public class FileCopy {

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		copy_2();
	}
	//方式一:一次读取一个字符
	//将复制文件的操作封装成方法
	public static void copy_1(){
		//创建操作数据的字符流对象的引用
		FileReader fr = null;
		FileWriter fw = null;
		int ch = 0;
		try {
			fr =new FileReader("e:\\demo.txt");
			fw = new FileWriter("e:\\demo_copy.txt");
			while((ch = fr.read())!=-1)
				fw.write(ch);
		} catch (Exception e) {
			// TODO: handle exception
		}
		finally{
			try {
				if(fr!=null)
				fr.close();
			} 
			catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			try {
				if(fw!=null)
				fw.close();
			} 
			catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
	}
	//读取到数组中,然后一次写入一个数组
	public static void copy_2(){
		//创建操作数据的字符流对象的引用
		FileReader fr = null;
		FileWriter fw = null;
		char[]buf = new char[1024];
		int len = 0;
		try {
			fr =new FileReader("e:\\demo.txt");
			fw = new FileWriter("e:\\demo_copy.txt",true);
			while((len = fr.read(buf))!=-1)
				fw.write(buf,0,len);
		} catch (Exception e) {
			// TODO: handle exception
		}
		finally{
			try {
				if(fr!=null)
				fr.close();
			} 
			catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			try {
				if(fw!=null)
				fw.close();
			} 
			catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
	}
}

文件复制过程:读取流对象和写入流对象之间不能直接进行数据传输,需要中间变量。第一种方式是一次读取一个字符,使用int型变量存储,然后写入字符。第二种方式是将数据读取到字符数组中,使用数组存储,然后写入数组。在这里,数组其实实现了缓冲区的功能。 

第三节--字符流缓冲区

为了提高字符数据读写效率,java提供了字符流缓冲区

BufferedReaderBufferedWriter

字符写入流缓冲区BufferedWriter的使用:

练习:利用缓冲区技术向文件:e:\\demo.txt写入文本数据

public class BufferedWriteDemo {

	public static void main(String[] args)throws IOException {
		//创建字符写入流对象
		FileWriter fw = new FileWriter("e:\\demo.txt");
		//创建字符写入流缓冲区,将流对象作为实际参数传递给字符写入流构造函数,即被缓冲对象
		BufferedWriter bw = new BufferedWriter(fw);
		//调用缓冲区写入方法,将数据写进缓冲区
		for(int x = 0;x < 10;x++){
			bw.write("avdjfaksljf");
			//缓冲区特有的换行方法
			bw.newLine();
		//将数据从缓冲区flush进目的地
			bw.flush();
		}
		bw.close();
	}
}

注意:字符写入流缓冲区的缓冲原理是底层封装了数组。特有的换行方法newLine(),底层封装了换行符,跨平台。

字符读取流缓冲区BufferedReader的使用:

//练习字符读取流缓冲区,使用其一次读取一行的方法(未做异常处理)
public class BufferReaderDemo {

	public static void main(String[] args)throws IOException {
		//创建字符读取流对象
		FileReader fr = new FileReader("e:\\demo.txt");
		//创建字符读取流对象,指定被缓冲的流对象
		BufferedReader br = new BufferedReader(fr);
		//使用一次读取一行的方式读取数据,返回字符串,读到文件末尾时,返null
		String line;
		while((line=br.readLine())!=null){
			System.out.println(line);
		}
	}
}

注意:字符读取流缓冲区BufferedReader提供了一次读取一个文本行的readLine()方法,返回该文本行字符,但不包含任何行终止符。这个方法其实是基于read()方法,通过下列字符之一即可认为某行已终止:换行 ('\n')、回车 ('\r') 或回车后直接跟着换行。读到数据末尾返回null

练习:使用字符流缓冲区复制文件

将E盘下demo.txt文件复制到E盘下demo_copy.txt中
public class BufferedCopy {

	public static void main(String[] args) {
		//创建文本读写流缓冲区对象的引用
		BufferedReader bufr = null;
		BufferedWriter bufw = null;
		try {
			bufr = new BufferedReader(new FileReader("e:\\demo.txt"));
			bufw = new BufferedWriter(new FileWriter("e:\\demo_copy.txt"));
			String line = null;
			while((line=bufr.readLine())!=null){
				bufw.write(line);
				bufw.newLine();
				bufw.flush();
			}
		} 
		catch (IOException e) {
			System.out.print("读写失败");
		}
		finally{
			try {
				if(bufr!=null)
				bufr.close();
			} 
			catch (Exception e2) {
				System.out.print("数据读取失败");
			}
			try {
				if(bufw!=null)
					bufw.close();
			}
			catch (Exception e2) {
				System.out.print("数据写入失败");
			}
		}
	}
}

自定义字符读取流缓冲区,定义一次读取一个文本行的方法

public class MyBufferedReader {
	private FileReader r;
	MyBufferedReader(FileReader r) {
		this.r = r;
	}
	public static void main(String[] args) {
		// TODO Auto-generated method stub

	}
	public String myReadLine() throws IOException{
		//定义字符串缓冲区,存储读取到的字符
		StringBuilder sb = new StringBuilder();
		int ch;
		while((ch=r.read())!=-1){
			//读到'\r'时跳过
			if(ch=='\r')
				continue;
			//读到'\n'说明到了一行末尾,结束函数,返回此行字符
			if(ch=='\n')
				return sb.toString();
			else
				//否则就将读取的字符添加到字符串缓冲区
				sb.append((char)ch);
		}
		/*
		 *一行结束的标志是读取到了行终止符,但最后一行一般没有行终止符,
		 *也就是说,最后一行读取结束并添加到字符串缓冲区后,也必须返回
		 */
		if(sb.length()!=0)
			return sb.toString();
		return null;
	}
}

字符流缓冲区其实就是对字符流的功能进行了增强,提高了读写效率,运用了装饰设计思想。


装饰设计模式:
为了使已有对象的功能更加强大,可以定义类传入该对象,基于已有功能,提供增强功能,定义的类就是装饰类。
装饰类的构造函数一般接收被增强的对象,增强其功能。
装饰和继承的区别:
对某一个类的对象增加功能时,可以自定义类继承这个类,添加特有的功能。对另一个类增加功能时,也需要自定义另一个类继承另一个类并添加特有功能,如此一来继承体系会越来越臃肿。
装饰类可以对一组类进行功能增强,比如,设计一个装饰类,对一个类基于已有功能进行增强,需要装饰类继承该类,将被增强类的对象传给装饰类的构造函数,根据多态思想,也可以传递被增强类的子类对象。也就是说,装饰类可以增强一个类及其所有子类的对象,只要把对象传递给装饰类。一般的,装饰类和被增强的类属于同一个类的体系,降低了类与类之间的关系。所以装饰设计模式比继承更灵活。
注意:装饰类需要复写父类所有抽象方法,比如自定义字符读取流缓冲区

class MyBufferedReader extends Reader
{
	
	private Reader r;
	MyBufferedReader(Reader r)
	{
		this.r = r;
	}

	//可以一次读一行数据的方法。
	public String myReadLine()throws IOException
	{
		//定义一个临时容器。原BufferReader封装的是字符数组。
		//为了演示方便。定义一个StringBuilder容器。因为最终还是要将数据变成字符串。
		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);
		}

		if(sb.length()!=0)
			return sb.toString();
		return null;		
	}

	/*
	覆盖Reader类中的抽象方法。

	*/
	public int read(char[] cbuf, int off, int len) throws IOException
	{
		return r.read(cbuf,off,len) ;
	}

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


class  MyBufferedReaderDemo
{
	public static void main(String[] args) throws IOException
	{
		FileReader fr = new FileReader("buf.txt");

		MyBufferedReader myBuf = new MyBufferedReader(fr);

		String line = null;

		while((line=myBuf.myReadLine())!=null)
		{
			System.out.println(line);
		}

		myBuf.myClose();
	}
}

BufferedReader的子类:LineNumberReader

LineNumberReader多了个行号属性及访问该属性的方法,并复写了父类的readLine方法,方法中添加了行号计数器。





评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值