Java基础21--IO流--装饰设计模式--缓冲流

21-1,IO练习-将c盘的一个文本文件复制到d盘。

将c盘的文件读取,再将读取到的文件写到d盘。

思路:

(1)需要读取源。

(2)将读到的源数据写入到目的地。

(3)既然是操作文本数据,就使用字符流。

public class CopyTextTest { //使用第一种方式,一个一个的读取
	public static void main(String[] args) throws IOException {
		//1,读取一个已有的文本文件,使用字符读取流和文件相关联。
		FileReader fr = new FilrReader("IO流.txt");
		//2,创建一个目的地,用于存储读到的数据
		FileWriter fw = new FileWriter("copytest.txt");
		//3,频繁的读写操作
		int ch = 0;
		while((ch = fr.read()) != -1) {
			fw.write(ch);
		}
		//4,关闭流
		fw.close();
		fr.close();
	}
}

 

21-2,复制练习

需求与上例一样,这里使用了缓冲区,并解决了异常。

public class CopyTextTest {
	private static final int BUFFER_SIZE = 1024;
	public static void main(String[] args) {
		FileReader fr = null;
		FileWriter fw = null;
		try {
			fr = new FileReader("IO流.txt");
			fw = new FileWriter("copytest.txt");
			//创建一个临时容器,用于缓存读取到的字符
			char[] buf = new buf[BUFFER_SIZE];//这是缓冲区
			//定义一个变量记录读取到的字符数,其实就是往数组里装的字符的个数
			int len = 0;
			while((len = fr.read(buf)) != -1) {
				fw.write(buf,0,len);
			}
		} catch(Exception e) {
			System.out.println("读写失败");
			throw new RuntimeException("读写失败");
		} finally {
			if(fw != null) {
				try {
					fw.close();
				} catch(IOException e) {
					e.printStackTrace();
				}
			}
			if(fr != null) {
				try {
					fr.close();
				} catch(IOException e) {
					e.printStackTrace();
				}
			}
		}
	}
}

 

21-3,复制文本文件图解

Java基础21--IO流--装饰设计模式--缓冲流

21-4,字符流缓冲区解释

1,缓冲区的出现提高了对数据的读写效率。

对应类:BufferedWriter

       BufferedReader

缓冲区要结合流才可以使用。

在流的基础上对流的功能进行了增强。

2,为什么提高了效率呢?以硬盘读写为例。

若要在硬盘上读取一段数据并存储到另一片硬盘空间去。

如果没有缓冲区,则会一个字符一个字符的读取,再一个一个的写过去,硬盘的磁头频繁的做着切换,效率低。

如果有缓冲区,则读一个字符先存在缓冲中,再读一个再存,这时磁头只在一个区域做读的操作。等缓冲区满时,再将缓冲区中的所有数据写到指定区域,这时磁头只在这个区域做写的操作,减少了工作量,提高了效率。

 

21-5,缓冲区-BufferedWriter

1,public class BufferedWriter

将文本写入字符输出流,缓冲各种字符,从而提供单个字符、数组、字符串的高效写入。

可以指定大小,也可以用默认大小,一般默认大小就足够了。

该类提供了newLine()方法,它使用平台自己的行分隔符概念,此概念由系统属性line.separator定义。

2,构造器

BufferedWriter(Writer out)指定被缓存对象

BufferedWriter(Writer out,int sz)指定大小

3,示例

public class Demo {
	private static final String LINE_SEPARATOR = System.getProperty("line.separator");
	public static void main(String[] args) {
		FileWriter fw = new FileWriter("buf.txt");
		//为了提高写入的效率,使用了字符流的缓冲区
		//创建了一个字符写入流的缓冲区对象,并和指定要被缓冲的流对象相关联。
		BufferedWriter bufw = new BufferedWriter(fw);
		//使用缓冲区写入的方法,将数据先写入到缓冲区中
		/*
		bufw.write("abcdef" + LINE_SEPARATOR + "hahaha");
		bufw.write("xxxxx");
		bufw.newLine();//相当于LINE_SEPARATOR
		bufw.write("hehehe");
		*/
		for(int x=1;x<=4;x++) {
			bufw.write("abcde" + x);
			bufw.newLine();//换行
			bufw.flush();
		}
		//关闭缓冲区,其实关闭的就是被缓冲的流对象。
		bufw.close();//把fw也关闭了。
		//fw.write("hhe");//错误,流已经关闭
	}
}

 

21-6,缓冲区-BufferedReader

1,public class BufferedReader

从字符输入流中读取文本,缓冲各个字符,从而实现字符、数组和行的高效读取。

2,构造器

BufferedReader(Reader in)

BufferedReader(Reader in,int sz)

3,String readLine();读取一个文本行

    longskip(long n);跳过n个字符

4,示例

public class Demo {
	public static void main(String[] args) {
		FileReader fr = new FileReader("buf.txt");
		BufferedReader bufr = new BufferedReader(fr);
		String line = null;
		//若超出了文本的最大行,则返回null
		while((line = bufr.readLine()) != null) {
			System.out.println(line);
		}
		bufr.close();
	}
}

 

21-7,BufferedReader-readLine()方法原理

Java基础21--IO流--装饰设计模式--缓冲流


21-8,缓冲区-赋值文本文件

用缓冲区复制文本文件。

public class Demo {
	public static void main(String[] args) {
		FileReader fr = new FileReader("buf.txt");
		BufferedReader bufr = new BufferedReader(fr);
		FileWriter fw = new FileWriter("buf_copy.txt");
		BufferedWriter bufw = new BufferedWriter(fw);
		String line = null;
		while((line = bufr.readLine()) != null) {
			bufw.write(line);
			bufw.newLine();
			bufw.flush();
		}
		bufw.close();
		bufr.close();
	}
}

21-9,自定义MyBufferedReader-readLine()方法

自己实现readLine()方法。

模拟一个BufferedReader。

分析:

缓冲区中无非就是封装了一个数组,并对外提供了更多的方法对数组进行访问。

其实这些方法最终操作都是数组的角标。

缓冲区的原理:

其实就是从源中获取一批数据进缓冲区。再从缓冲区中不断取出一个个的数据。

在此次取完后,再从源中继续取一批数据进缓冲区,当源中数据取光时,用-1标记结束。

public class MyBufferedReader {
	private Reader r;
	//定义一个数组作为缓冲区
	private char[] buf = new char[1024];
	//定义一个指针用于操作这个数组中的元素,当操作到最后一个元素后,指针应该归零。
	private int pos = 0;
	//定义一个计数器用于记录缓冲区中的数据个数,当该数据减到0,
	//就从源中继续获取数据到缓冲区中。
	private int count = 0;
	MyBufferedReader(Reader r) {
		this.r = r;
	}
	//该方法从缓冲区中一次获取一个字符
	public int myRead() throws IOException {
		if(count == 0) {
			count = r.read(buf);
			pos = 0;
		}
		if(count < 0) {
			return -1;
		}
		char ch = buf[pos++];
		
	}
	//读取一行数据的方法
	public String myReadLine() throws IOException {
		StringBuilder sb = new StringBuilder();
		int ch = 0;
		while((ch = myRead()) != -1) {
			if(ch == ‘\r‘) {
				continue;
			}
			if(ch == ‘\n‘) {
				return sb.toString();
			}
			//将从缓冲区读到的字符存储到存储行数据的缓冲区中
			sb.append((char)ch);
		}
		if(sb.length() != 0) {
			return sb.toString();
		}
		return null;
	}
	//关闭缓冲区的方法
	public void myClose() throws IOException {
		r.close();
	}
}

 

21-10,缓冲区-装饰设计模式

1,装饰设计模式:

当对一组对象的功能进行增强时,就可以使用该模式解决问题。

public class Demo {
	public static void main(String[] args) {
		Person p = new Person();
		p.chifan();
		NewPerson p1 = new NewPerson();
		p1.chifan();
		NewPerson2 p2 = new NewPerson2();
		p2.chifan();
	}
}
class Person {
	void chifan() {
		System.out.println("吃饭");
	}
}
//这个类的出现是为了增强Person的功能而出现的,是装饰设计模式
class NewPerson {
	private Person p;
	NewPerson(Person p) {
		this.p = p;
	}
	public void chifan() {
		System.out.println("开胃酒");
		p.chifan();
		System.out.println("甜点");
	}
}
//用extends实现对Person的扩展
class NewPerson2 extends Person {
	public void chifan() {
		System.out.println("开胃酒");
		super.chifan();
		System.out.println("甜点");
	}
}

21-11,装饰设计模式和继承的区别

装饰设计模式和继承都能实现同样的功能:进行功能的扩展增强。那么他们有什么区别呢?

继承:首先有一个继承体系。

Writer

 |--TextWriter:用于操作文本。

 |--MediaWriter:用于操作媒体。

想要对操作的动作进行效率的提高,按照面向对象,可以通过继承对具体进行功能的扩展。效率提高需要加入缓冲技术。

Writer

 |--TextWriter:用于操作文本

      |--BufferTextWriter:加入了缓冲技术的操作文本的对象。

 |--MediaWriter

      |--BufferMediaWriter:加入了缓冲技术的操作媒体的对象。

但是用继承做好像并不理想。如果这个体系进行功能扩展,又多了流对象,那么这个流要提高效率,是不是也要产生子类呢?答案是是的,这时就会发现,只为提高功能而进行的继承,会导致继承体系越来越臃肿,不够灵活。

就要重新思考这个问题,既然加入的都是同一种技术——缓冲。前一种是让缓冲与具体的对象相结合,可不可以将缓冲进行单独的封装,哪个对象需要缓冲就将哪个对象与缓冲相关联。

装饰比继承更灵活。

特点:装饰类和被装饰的类都必须所属同一个接口或者父类。

 

21-12,LineNumberReader

LineNumberReader是一个装饰流对象的类,所以必须要有流的源。

功能是读取行号和设置初始行号。

public class Demo {
	public static void main(String[] args) {
		FileReader fr = new FileReader("IO流.txt");
		LineNumberReader lnr = new LineNumberReader(fr);
		String line = null;
		//lnr.setLineNumber(100);设置初始行号为100
		while((line = lnr.readLine()) != null) {
			System.out.println(lnr.getLineNumber() + ":" + line);
		}
		lnr.close();
	}
}

 

21-13,字节流-操作文件基本演示

1,字节流的两个基类:

    InputStream

    OutputStream

基本方法基本与字符流一致。

顾名思义,字节流是对字节的IO。它操作的基本单元都是字节。

2,FileOutputStream的基本演示

public class Demo {
	public static void main(String[] args) {
		demo_write();
	}
	public static void demo_write() throws IOException {
		//创建字节输出流对象,用于操作文件
		FileOutputStream fos = new FileOutputStream("dytedemo.txt");
		//写数据,直接写入到目的地中
		fos.write("abcdefg".getBytes());
		//fos.flush();//字节流不做缓冲处理,直接写入到目的地中,不用刷新
		fos.close();//关闭资源动作必须完成
	}
}

3,FileInputStream演示

public class Demo {
	public static void main(String[] args) {
		demo_read();
	}
	public static void demo_read() throws IOException {
		//1,创建一个读取流对象,和指定文件关联
		FileInputStream fis = new FileInputStream("bytedemo.txt");
		//一次读取一个字节
		/*
		int ch = fis.read();
		System.out.println(ch);
		*/
		//2,字节数组读取方式,建议用这种方式
		byte[] buf = new byte[1024];
		int len = 0;
		while((len = fis.read(buf)) != -1) {
			System.out.println(new String(buf,0,len));
		}
		//3,创建长度正好的数组,用available()方法获取整个文件的字节数
		/*
		这种方法不建议使用,可以用于小文件的读取,
		读取大文件会导致内存溢出,但可用于先获取大文件字节,
		根据字节数分批读取。
		System.out.println(fis.available());
		byte[] buf = new byte[fis.available()];
		fis.read(buf);
		System.out.println(new String(buf));
		*/
		fis.close();
	}
}

 

21-14,练习-复制mp3

这里提供4种方式。

(1)这种方式不要用,没有任何效率可言。

FileInputStream fis = new FileInputStream("c:\\0.mp3");
FileOutputStream fos = new FileOutputStream("c:\\1.mp3");
int ch = 0;
while((ch = fis.read()) != -1) {
	fos.write(ch);
}
fos.close();
fis.close();

 

(2)依然不建议使用这个方法

FileInputStream fis = new FileInputStream("c:\\0.mp3");
FileOutputStream fos = new FileOutputStream("c:\\2.mp3");
byte[] buf = new byte[fis.available()];
fis.read(buf);
fos.write(buf);
fos.close();
fis.close();


(3)下面两种方式可以使用

FileInputStream fis = new FileInputStream("c:\\0.mp3");
BufferedInputStream bufis = new BufferedInputStream(fis);
FileOutputStream fos = new FileOutputStream("c:\\2.mp3");
BufferedOutputStream bufos = new BufferedOutputStream(fos);
int ch = 0;
while((ch = bufis.read()) != -1) {
	bufos.write(ch);
}
bufos.close();
bufis.close();

 

(4)

FileInputStream fis = new FileInputStream("c:\\0.mp3");
FileOutputStream fos = new FileOutputStream("c:\\2.mp3");
byte[] buf = new byte[1024];
int len = 0;
while((len = fis.read(buf)) != -1) {
	fos.write(buf,0,len);
}
fos.close();
fis.close();

Java基础21--IO流--装饰设计模式--缓冲流

上一篇:treap树模版


下一篇:POJ1797--Heavy Transportation