day19(中)_IO流3(模拟缓冲区,装饰设计模式)


1.MyBufferedReader和MyBufferedInputStream

1.模拟字符读取流的缓冲区:

/*
 根据readLine原理:
   自定义一个类包含一个功能和readLine一致的方法
 来模拟以下BufferedReader方法
*/
package myreadline;
import java.io.FileReader;
import java.io.IOException;
 class MyBufferedReader{
  
  private FileReader r;//需要用到FileReader的read方法,因此初始化时传入FileReader对象
  private char[] chArr=new char[1024];
  private int pointer=0;
  MyBufferedReader(FileReader r){
   this.r=r;
  }

  //自定义readLine方法
  public String readLine()throws IOException{//自定义功能不在内部处理,而是抛出去,让调用者处理

       StringBuilder sb=new StringBuilder();//定义一个临时容器模拟缓冲区
       int ch=0;
    
       while((ch=r.read())!=-1){
           if(ch=='\r') 
             continue;//读下一个进行判断
           else
            if(ch=='\n')
              return sb.toString();
            else
             sb.append((char)ch);
       }   
       /*
    ①以上代码也不可写成
      if(ch==’\r’||ch==’\n’)
       returnsb.toString()
      因为当读到'\r’ 立即返回改行数据,
      但是下次会读'\n’ 会再次返回,此时StringBuilder无数据

    ②如果当前平台的换行为'\r’以上方法并不适用
      以上方法仅适用与\r\n或\n换行
      此时直接
      if(ch==’\r’)
         return sb.toString();
      else
        return sb.append((char)ch);
对于以上代码可能发生一种情况:
       当读到文件最后一行:
         例如:abcd后面没有回车,此时执行不到sb.toString(),
         也就是说:虽然存入容器但是这一行不能返回.
     那么加上一个判断(如下): 
*/
       if(sb.length()!=0)
         return sb.toString();
return null;//此时读到文件末尾

}
 //模拟自定义缓冲区,把关闭单独封装在一个方法中
 public void myClose()throws IOException{
     r.close();
 }



public static void main(String[] args){
   MyBufferedReader mbr=null;
   try{
     mbr=new MyBufferedReader(new FileReader("4_MyBufferedReader.java"));
     String line=null;
     while((line=mbr.readLine_2())!=null)
       System.out.println(line);
   }
   catch(IOException e){
    throw new RuntimeException("读异常");
   }
   finally{
     try{
     if(mbr!=null)
       mbr.myClose();
     }
     catch(IOException e){
      throw new RuntimeException("关闭异常");
     }
   }
 }
}
/*
如果不利用StringBuilder而利用
字符数组
*/
public  String readLine_2()throws IOException{
  pointer=0;
  int ch=0;
  while((ch=r.read())!=-1){
    if(ch=='\r')
     continue;
    else
      if(ch=='\n')
        return new String(chArr,0,pointer);
      else
        chArr[pointer++]=(char)ch; 
   }
   if(pointer!=0)
    return new String(chArr,0,pointer);
 return null;
}

模拟readLine方法

2.模拟字节读取流缓冲区:

/*
自定义缓冲区:
    算法思想:
      参照示意图
*/  

package mybuffered;
import java.io.InputStream;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io. BufferedOutputStream;
class MyBufferedInputStream{
  private byte[] buff=new byte[1024];
  private InputStream is=null;
  private int pointer=0;//指针用于取出缓冲区(数组)中的元素
  private int count=0;//计数器用于记录每次从文件中取出的字节数
  MyBufferedInputStream(InputStream is){
   this.is=is;
  }
  public int myBufferedRead()throws IOException{
    
    if(count==0){//只有当缓冲区中的数据被读完时,再从文件中
                 //取一部分数据
        count=is.read(buff);
        pointer=0;//当count==0,把指针重新移到0角标
     }
     if(count!=-1){//是否到达文件尾
     --count;//读取一个字节,count-1
     return (int)buff[pointer++] & 255;//0x00ff
     }
     else
        return -1;
        
  }
  public void close()throws IOException{
   is.close();
  }
}


class TestBuffered{
 public static void main(String[] args)throws IOException{
  
  //用自定义缓冲区拷贝MP3
 
 MyBufferedInputStream mbis=new MyBufferedInputStream(new FileInputStream("e:\\song\\Amy Diamond - Heartbeats.mp3"));
 BufferedOutputStream bos=new BufferedOutputStream(new FileOutputStream("c:\\Hertbeats.mp3"));
 long start=System.currentTimeMillis();
 int aByte=0;
 System.out.println(aByte=(mbis.myBufferedRead()));
while((aByte=(mbis.myBufferedRead()))!=-1){//从自定义缓冲区中读一个字节,写入到系统的缓冲区
   bos.write(aByte);
   bos.flush();
  }
 long end=System.currentTimeMillis();
 System.out.println(end-start+"ms");//时间的长短和cpu性能以及其它因素有关
  bos.close();
  mbis.close();

 } 
}
模拟字节流缓冲区

2.装饰设计模式:

①概述:

/*
装饰设计模式:(目前学的第三种设计模式)
    当想要对已有的对象进行功能增强时
    可以定义类,将已有对象(new FileReader)传入,基于已有的功能(read),并提供加强功能(myReadLine).
    那么自定义的该类成为装饰类.

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

*/
//例如:增强人的吃饭功能
class Person{
    public void eat(){
     System.out.println("吃饭");
    }
} 
class SuperPerson{
  private Person p;
  SuperPerson(Person p){
   this.p=p;
  }
  public void superEat(){
   System.out.println("喝小酒");
   p.eat();
   System.out.println("甜点");
   System.out.println("整一根");
  }
}
class PersonDemo{
    public static void main(String[] args){
      Person p=new Person();    
      p.eat();
      new SuperPerson(p).superEat();
    }
}
/*
 到目前为止已经学到3中设计模式:
 1.单例设计模式
 2.模板方法设计模式
 3.装饰设计模式
*/

②※装饰相对继承优点:

/*
 在人吃饭例子中,可以不用装饰类,我直接
 让SuperPerson extends Person,然后复写eat方法
 不也OK?
 
 在举例说明为什么要有装饰设计模式?
      //MyTextReader类用来操作文件读取,为了提高读操作效率,使用缓冲区技术
      //又定义一个类MyBufferedTextReader继承它,提供更高效的readLine方法
    MyReader  
           <--MyTextReader<--MyBufferedTextReader
           <--MyMediaReader<--MyBufferedMediaReader//读取多媒体文件,然后为了提高读效率,使用缓冲区
            <--MyDataReader<--MyBufferedDataReader//同理
           ......
     那么,我后期在来个类用于操作读取其它文件,为了高效还得定义缓冲区子类,
     产生一些缺点:
      1.使整个体系变得异常冗杂
      2.后期的扩展性极差
     那么干脆:
       找到其参数的共同类型.通过多态形式.可以提高扩展性
     MyBufferedReader extends MyReader{
       private MyReader mr;
       pubic MyBufferedReader(MyReader mr){//MyReader的子类对象均能传入
        this.mr=mr;                           //谁需要使用缓冲技术,把它传进来
      }
     
     }
    简化了原有体系结构:
      MyReader
               <--MyTextReader
               <--MyMediaReader
               <--MyDataReader
               <--MyBufferedReader

*/


/*
装饰设计模式对比继承:
    1.装饰设计模式比继承更加灵活
      提高扩展性,避免了继承的冗杂
      而且降低了类与类之间关系
    2.装饰类因为增强已有对象,具备的功能和已有的功能是相同的
      只不过提供了更强的功能(这个功能是原有的增强,而不是从本质上改变原有功能:在Reader体系中,
      例如:装饰类BufferedReader,其中readLine就是增强了FileReader中read功能)
      那么装饰类和被装饰类通常是同属于一个体系(具有共同的父类/父接口)  
*/

鉴于以上我们可以把自定义缓冲区的代码优化下: (改进自定义字符读取流缓冲区)

//把MyBufferedReader修改为装饰类:
package myreadline;
import java.io.FileReader;
import java.io.IOException;
import java.io.Reader;
public class MyBufferedReader2 extends Reader{//继承Reader,那么必须复写Reader中抽象方法,不然无法创建对象
 private Reader r;
public MyBufferedReader2(Reader r){//多态提高扩展性
    this.r=r;  
  }
  public String readLine()throws IOException{
       StringBuilder sb=new StringBuilder();
       int ch=0;
    
       while((ch=r.read())!=-1){
           if(ch=='\r') 
             continue;//读下一个进行判断
           else
            if(ch=='\n')
              return sb.toString();
            else
             sb.append((char)ch);
       }   
       if(sb.length()!=0)
         return sb.toString();
return null;
}

 
 //复写close方法
 public void close()throws IOException{
     r.close();
 }
 //复写abstract int read(char[] cbuf, int off, int len)
  public int read(char[] cbuf, int off, int len)throws IOException{
    return 
r
.read(cbuf,off,len);//利用了 传入对象已复写该方法
  }

 
 
 
 public static void main(String[] args){
   MyBufferedReader mbr=null;
   try{
     mbr=new MyBufferedReader(new FileReader("4_MyBufferedReader.java"));
     String line=null;
     while((line=mbr.readLine())!=null)
       System.out.println(line);
   }
   catch(IOException e){
    throw new RuntimeException("读异常");
   }
   finally{
     try{
     if(mbr!=null)
       mbr.close();
     }
     catch(IOException e){
      throw new RuntimeException("关闭异常");
     }
   }
 }
}

3.BufferedReader子类LineNumberReader(相对于BufferedReader提供更强功能)

①.LineNumberReader示例:

/*
在BufferedReader下有一个子类:LineNumberReader
    跟踪行号的缓冲字符输入流,进一步加强功能.
    打印每行的同时可以带上行号
*/
package bufferedreader;
import java.io.LineNumberReader;
import java.io.FileReader;
import java.io.IOException;
class LineNumberReaderDemo{
        public static void main(String[] args){
         LineNumberReader lnr=null;
         try{
          lnr=new LineNumberReader(new FileReader("7_LineNumberReader.java"));
          String line=null;
          lnr.setLineNumber(10);//那么行号将从11开始
          while((line=lnr.readLine())!=null)
           System.out.println(lnr.getLineNumber()+":"+line);
         }
         catch(IOException e){
          e.printStackTrace();
         }
        finally{
          try{
           if(lnr!=null)
             lnr.close();
          }
          catch(IOException e){
           e.printStackTrace();
          }
        }
    }
}

LineNumberReader

②自定义MyLineNumberReader方法:


package mylinenumber;

import java.io.Reader;
import java.io.FileReader;
import java.io.IOException;
import myreadline.MyBufferedReader2;

class MyLineNumberReader extends MyBufferedReader2{//在MyBufferedReader2已有方法,不再重复定义
    private int lineNumber=0;//行号
    public MyLineNumberReader(Reader r){
        super(r);
    }
    
     //设置行号
    public void mySetLineNumber(int lineNumber){
    
      this.lineNumber=lineNumber;
    }
    
    //获取行号
    public int myGetLineNumber(){
      return lineNumber; 
    }
    
    public String myReadLine()throws IOException{
      ++lineNumber;//读一行自增一次
     return super.readLine();//提高代码重用性
    }
 
}


class MyLineNumberDemo{
    public static void main(String[] args){
     MyLineNumberReader mlnr=null;
     try{
      mlnr=new MyLineNumberReader(new FileReader("8_MyLineNumberReader.java"));
      String line=null;
      mlnr.mySetLineNumber(100);
      
      while((line=mlnr.myReadLine())!=null)
        System.out.println(mlnr.myGetLineNumber()+":"+line);
     }
     catch(IOException e){
       e.printStackTrace();
     }
     finally{
       try{
       if(mlnr!=null)
         mlnr.close();
       }
       catch(IOException e){
        e.printStackTrace();
       }
     }
    }
}
MyLineNumberReader
原文地址:https://www.cnblogs.com/yiqiu2324/p/3085750.html