zoukankan      html  css  js  c++  java
  • 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
  • 相关阅读:
    [Java解惑]数值表达式
    Java使用LdAP获取AD域用户
    LDAP Error Codes
    Excel向上取整
    java中的三种取整函数
    Dwz手册的补充说明和常见问题
    【转】BSON数据格式
    go语言合并两个数组
    vscode远程修改文件('file': A system error occured )
    [转]Linux 桌面玩家指南:20. 把 Linux 系统装入 U 盘打包带走
  • 原文地址:https://www.cnblogs.com/yiqiu2324/p/3085750.html
Copyright © 2011-2022 走看看