一、缓冲区
1、缓冲区的作用:缓冲区的出现提高了对数据的读写效率。
注:缓冲区提高效率的原理:就是缓冲区对象里面封装了数组。先将数据存储到数组中,一次性的写入,再一次性的读取。
2、缓冲区的特点:要结合流才可以使用。并在流的基础上对流的功能进行了增强。
3、字符流缓冲区对应的类:
BufferedWriter
BufferedReader
4、缓冲区的使用:只要将需要被提高效率的流对象做为参数传递给缓冲区的构造函数即可。
二、字符流缓冲区
1、字符写入流缓冲区:BufferedWriter
①、构造函数:
BufferedWriter(Writer out):使用默认大小输出缓冲区的缓冲字符输出流
BufferedWriter(Writer out, int sz):使用指定大小输出缓冲区的缓冲字符输出流。
注:缓冲区都有默认的大小长度,也可以手动指定缓冲区长度。一般情况下,使用默认大小的就可以了。
②、BufferedWriter缓冲区提供的特有方法:newLine()换行。该方法可以跨平台。
其实newLine()方法底层原理就是:在Windows系统中封装的是\r\n,而在Linux系统中则封装的是\n。
代码示例:
import java.io.*;
class BufferedWriterDemo
{
public static void main(String[] args) throws IOException
{
//创建一个字符写入流对象。
FileWriter fw = new FileWriter("buf.txt");
//为了提高字符写入流的效率,加入缓冲技术。
BufferedWriter bufw = new BufferedWriter(fw);
for(int x=1;x<5; x++)
{
bufw.write("abcd"+x);
bufw.newLine();
bufw.flush();
}
//记住:只要用到缓冲区,就要记得刷新。
//bufw.flush();
//其实关闭缓冲区,就是在关闭缓冲区中的流对象。
bufw.close();
}
}
2、字符读取流缓冲区:BufferedReader
①、构造函数:
BufferedWriter(Writer out):使用默认大小输出缓冲区的缓冲字符输出流
BufferedWriter(Writer out, int sz):使用指定大小输出缓冲区的缓冲字符输出流。
②、BufferedWriter缓冲区提供的特有方法:readLine() 一次读一行文本数据,当返回null的时候表示读到了文件末尾。
注意:readLine方法返回的时候只返回回车符之前的数据内容。并不返回回车符。
代码示例:
import java.io.*;
class BufferedReaderDemo
{
public static void main(String[] args) throws IOException
{
//创建一个读取流对象和文件相关联。
FileReader fr = new FileReader("buf.txt");
//为了提高效率。加入缓冲技术,将字符读取流对象做为参数传递给缓冲对象的构造函数。
BufferedReader bufr = new BufferedReader(fr);
String line = null;
while((line = bufr.readLine()) != null)
{
System.out.println(line);
}
bufr.close();
}
}
③、readLine方法的原理:
readLine()方法表示一次读取一行内容的字符串,但是不包含任何终止符号,即不包含末尾的\r\n (Windows系统中)。
实际上,readLine方法底层调用的还是read方法(一次读取一个字符),只是读取一行数据之后临时存储在缓冲区当中,当一行数据读取完了之后再一次性的返回来。
readLine方法内存原理图:
3、自定义字符读取流缓冲区:
明白了BufferedReader类中特有方法readLine的原理后,可以自定义一个类中包含一个功能和readLine一致的方法来模拟BufferedReader缓冲区。
示例代码:
import java.io.*;
class MyBufferedReader extends Reader
{
private Reader r;
MyBufferedReader(Readerr)
{
this.r = r;
}
//可以一次读一行数据的方法。
public String myReaderLine() throws IOException //自定义readLine方法。
{
//定义一个临时容器。源BufferedReader中封装的是字符数组。
//为了演示方便。定义一个StringBuilder容器,因为最终还是要将数据变成字符串。
StringBuilder sb = new StringBuilder();
int ch = 0;
while((ch = r.read()) != -1)
{
if(ch == '\r') //当判断到一行的末尾\r的时候就继续判断下一个字符。
continue;
if(ch == '\n') //当判断到\n的时候,表示改行已经读取完毕,直接返回改行字符串即可。
return sb.toString();
else
sb.append((char)ch);
}
if(sb.length() != 0) //当容器中还有内容读取到了但是没有返回来的时候,就判断一下进行返回。
return sb.toString();
return null;
}
public int read(char[] cbuf,int off,int len) throws IOException //覆写read方法。
{
return r.read(cbuf,off,len);
}
public void close() throws IOException //覆写Reader类中的close方法。
{
r.close();
}
public void myClose() throws IOException //自定义close方法。
{
r.close(); //关闭流实际上是关闭FileReader的流。直接调用FileReader类中的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.myReaderLine()) != null)
{
System.out.println(line);
}
myBuf.myClose();
}
}
运行结果:
abcd1
abcd2
abcd3
abcd4
三、装饰设计模式
1、什么叫装饰设计模式?
当想要对已有的对象进行功能增强时,可以通过定义类,将已有的对象作为参数传入,基于已有的对象的功能,并提供加强功能。
2、装饰类:增强已有对象功能的类就称为装饰类。
装饰类通常会通过构造方法接收被装饰的对象。并基于被装饰的对象的功能,提供更强的功能。
3、装饰设计模式在现实生活中的体现举例:
例如:装修房子:给房子装上电梯,使得上下楼更加方便。
企业面试:求职者穿上正装,显得更加正式专业,精神面貌增强,求职成功率变高。
Java中的代码实现:
class Person //被装饰类。普通人
{
public void chifan() //普通人的吃饭方法。
{
System.out.println("吃饭");
}
}
class SuperPerson //装饰类。
{
private Person p;
SuperPerson(Person p) //通过构造方法传入已有的普通人对象,用于装饰。
{
this.p = p;
}
public void superChifan() //功能增强的吃饭方法。
{
System.out.println("开胃酒");
p.chifan();
System.out.println("甜点");
System.out.println("来一根");
}
}
class PersonDemo
{
public static void main(String[] args)
{
Person p = new Person();
//p.chifan();
SuperPerson sp = newSuperPerson(p);
sp.superChifan();
}
}
4、装饰设计模式和继承的区别:
①、装饰设计模式是一种动态行为,对已经存在的类进行随意组合。而类的继承是一种静态的行为,一个类定义成什么样,该类的对象便具有什么样的功能,无法动态的改变。
②、装饰设计模式扩展的是对象的功能,不需要增加类的数量。而继承扩展的是类的功能。在继承的关系中,如果想增加一个对象的功能,则只能通过继承关系,在子类中增加功能增强的方法。
总结:
①、装饰模式比继承更加灵活,避免了继承体系的臃肿,并且也降低了类与类之间的关系。
②、装饰设计模式可以在不创建更多子类的前提下,将对象功能进行扩展。
5、LineNumberReader类
该类是一个装饰类。可以用于设置和获取行号,默认是从0开始,通过该类提供的特有方法:setLineNumber(int)和getLineNumber()。并且该类也覆写类BufferedReader类中的readLine()方法。
示例代码:
import java.io.*;
class LineNumberReaderDemo
{
public static void main(String[] args) throws IOException
{
FileReader fr = new FileReader("HelloWrold.java");
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();
}
}
运行输出结果:
6、自定义LineNumberReader类
class MyLineNumberReader
{
private Reader r;
private int lineNumber;
MyLineNumberReader(Readerr)
{
this.r = r;
}
public String myReadLine() throws IOException
{
lineNumber++;
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;
}
public void setLineNumber(int lineNumber)
{
this.lineNumber = lineNumber;
}
public int getLineNumber()
{
return lineNumber;
}
public void myClose() throws IOException
{
r.close();
}
}
class MyLineNumberReaderDemo
{
public static void main(String[] args) throws IOException
{
FileReader fr = new FileReader("CopyTextBuf.java");
MyLineNumberReader mylnr = new MyLineNumberReader(fr);
String line = null;
mylnr.setLineNumber(100);
while((line = mylnr.myReadLine()) != null)
{
System.out.println(mylnr.getLineNumber()+"::"+line);
}
mylnr.myClose();
}
}