JAVASE_19_#java高级IO_BufferedReader和BufferedWriter

1,字符流的缓冲区
BufferedWriter:字符写入流缓冲区
BufferedReader:字符读取流缓冲区
缓冲区的出现是为了提高流的操作效率而出现的,
所以在创建缓冲区之前,必须要现有流对象
1.1,BufferedWriter:字符写入流缓冲区
该缓冲区中提供了一个跨平台的 换行符
newLine();
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
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.close();
     }
}
1.2,BufferedReader:字符读取流缓冲区
该缓冲区提供了一个一次读一行的方法 readLine,方便于对文本数据的获取。
当返回null时,表示读到文件末尾。
readLine方法返回的时候只返回回车符之前的数据内容。并不返回回车符
该方法必须读到回车符,如果没有就堵塞!
1
2
3
4
5
6
7
8
9
10
11
12
13
14
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;//2个流没有直接关系,这个就是中转站
         while ((line=bufr.readLine())!= null ){
             System.out.print(line);
         }
         bufr.close();
     }
}
2,通过缓冲区复制文本文件
1
2
3
4
5
6
7
8
9
10
11
BufferedReader bfr =  null ;
BufferedWriter bfw =  null ;
try  {
bfr =  new  BufferedReader( new  FileReader( "E:\\demo.txt" ));
bfw =  new  BufferedWriter( new  FileWriter( "F:\\123.txt" ));
String line =  null ;
while ((line=bfr.readLine()) !=  null ){
     bfw.write(line);
     bfw.newLine(); //一行一行的读取的,写入时要插入换行符
     bfw.flush(); //刷新
}
3,readLine原理
----------------------------------------------------------------->>>>>>>>>>>>>

代码实例:
明白了BufferedReader类中特有方法readLine的原理后,
可以自定义一个类中包含一个功能和readLine一致的方法。
来模拟一下BufferedReader
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
import  java.io.*;
class  MyBufferedReader{   
     private  FileReader r;
     MyBufferedReader(FileReader 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 ;      
     }
     public  void  myClose() throws  IOException
     {
         r.close();
     }
}
--------------------------------------------------------------------------------------------->>>>>>>>>>>>>>>>


4,装饰设计模式
当想要对已有的对象进行功能增强时,
可以定义类,将已有对象传入,基于已有的功能,并提供加强功能。
那么自定义的该类称为装饰类。
装饰类通常会通过构造方法接收被装饰的对象。
并基于被装饰的对象的功能,提供更强的功能。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
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 =  new  SuperPerson(p);
         sp.superChifan();
     }
}
5,装饰和继承的区别
MyReader//专门用于读取数据的类。
      |----MyTextReader
                     |----MyBufferTextReader
      |----MyMediaReader
                     |----MyBufferMediaReader
      |----MyDataReader
                     |----MyBufferDataReader
以上是通过继承将每一个子类都具备缓冲功能。那么继承体系会复杂臃肿,并不利于扩展。
现在优化思想。单独描述一下缓冲内容。
将需要被缓冲的对象。传递进来。也就是,谁需要被缓冲,谁就作为参数传递给缓冲区。
这样继承体系就变得很简单。优化了体系结构。 
1
2
3
4
5
6
7
8
class  MyBufferReader{
     MyBufferReader(MyTextReader text){
             //
     }
     MyBufferReader(MyMediaReader media){
                   
     }
}
上面这个类扩展性很差。 
找到其参数的共同类型。通过多态的形式。可以提高扩展性.
1
2
3
4
5
6
7
class MyBufferReader extends MyReader{
    private MyReader r;
    MyBufferReader(MyReader r)
    {
        //
    }
}
装饰模式比继承要灵活。避免了继承体系臃肿。 
而且降低了类于类之间的关系。
装饰类因为增强已有对象,具备的功能和已有的是相同的,只不过提供了更强功能。
所以装饰类和被装饰类通常是都属于一个体系中的.
上面继承体系变成如下:
MyReader//专门用于读取数据的类。
 |----MyTextReader 
 |----MyMediaReader 
 |----MyDataReader 
 |----MyBufferReader
6,自定义装饰类
readLine原理代码是自定义装饰类,为了能装饰一组对象,需要将FileReader改成期抽象父类Reader,与此同时也要覆盖父类的抽象方法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
class  MyBufferedReader  extends  Reader{  
     private  Reader r;
     MyBufferedReader(Reader r){
         this .r = r;
     }
     public  String myReadLine() throws  IOException{
 
         //功能实现代码
     }
 
     /*
     覆盖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();
     }
}
7,LineNumberReader
            --------------------跟踪行号的缓冲字符输入流
            --------------------默认行标号从0开始
            java.lang.Object
                    |----java.io.Reader
                                 |----java.io.BufferedReader 
                                                  |----java.io.LineNumberReader
1
2
3
4
5
6
7
8
9
10
11
12
class  LineNumberReaderDemo{
     public  static  void  main(String[] args) throws  IOException {
         FileReader fr =  new  FileReader( "PersonDemo.java" );
         LineNumberReader lnr =  new  LineNumberReader(fr);
         String line =  null ;
         lnr.setLineNumber( 100 ); //设置起始行号
         while ((line=lnr.readLine())!= null ){
             System.out.println(lnr.getLineNumber()+ ":" +line);
         }
         lnr.close();
     }
}
//练习:模拟一个带行号的缓冲区对象。
 
 
8,MyLineNumberReader
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class  MyLineNumberReader  extends  MyBufferedReader{
     private  int  lineNumber;
     MyLineNumberReader(Reader r){
         super (r);
     }
 
     public  String myReadLine() throws  IOException{
 
         lineNumber++;
         return  super .myReadLine();
     }
     public  void  setLineNumber( int  lineNumber){
         this .lineNumber = lineNumber;
     }
     public  int  getLineNumber(){
         return  lineNumber;
     }
}
9,字节流File读写操作
字符流:------char
FileReader
FileWriter。
BufferedReader
BufferedWriter
字节流: -------byte
InputStream  
OutputStream
需求,想要操作图片数据。这时就要用到字节流。
比如:复制一个图片.----代码演示在10
虚拟机启动的时候分配了64M的空间
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
class   FileStream{
     public  static  void  main(String[] args)  throws  IOException{
         readFile_3();
     }
     //方法一:此方法第一了一个刚刚好的缓冲区避免了循环,
     //但是JVM启动默认只分配了64M的空间,比如操作流媒体文件,文件过大,会发生内存溢出,
     //此方法慎用
     public  static  void  readFile_3() throws  IOException{
         FileInputStream fis =  new  FileInputStream( "fos.txt" );      
//      int num = fis.available();//有可能超出虚拟机的大小
         byte [] buf =  new  byte [fis.available()]; //定义一个刚刚好的缓冲区。不用在循环了。
         fis.read(buf);
         System.out.println( new  String(buf));
         fis.close();
     }
     //方法二
     public  static  void  readFile_2() throws  IOException{
         FileInputStream fis =  new  FileInputStream( "fos.txt" );
         byte [] buf =  new  byte [ 1024 ];
         int  len =  0 ;
         while ((len=fis.read(buf))!=- 1 ){
             System.out.println( new  String(buf, 0 ,len));
         }
         fis.close();       
     }
     //方法一
     public  static  void  readFile_1() throws  IOException{
         FileInputStream fis =  new  FileInputStream( "fos.txt" );
         int  ch =  0 ;
         while ((ch=fis.read())!=- 1 ){
             System.out.println(( char )ch);
         }
         fis.close();
     }
 
     public  static  void  writeFile() throws  IOException{
         FileOutputStream fos =  new  FileOutputStream( "fos.txt" );
         fos.write( "abcde" .getBytes());
         fos.close();       
     }
}
10,IO流--拷贝图片
思路:
1,用字节读取流对象和图片关联。
2,用字节写入流对象创建一个图片文件。用于存储获取到的图片数据。
3,通过循环读写,完成数据的存储。
4,关闭资源。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
public  class  CopyPic {
     public  static  void  main(String[] args) {
         FileInputStream fis =  null ;
         FileOutputStream fos =  null ;
         try {
             fis =  new  FileInputStream( "E:\\123.jpg" );
             fos =  new  FileOutputStream( "F:\\my.jpg" );
             byte [] buf =  new  byte [ 1024 ];
             int  len =  0 ;
             while ((len=fis.read(buf)) != - 1 ){
                 fos.write(buf, 0 ,len- 2 );
             }
         } catch (IOException e){
             throw  new  RuntimeException( "读写失败" );
         } finally {
             if (fis !=  null  ){
                 try  {
                     fis.close();
                 catch  (IOException e) {}
             }
             if (fos !=  null  ){
                 try  {
                     fos.close();
                 catch  (IOException e) {}
             }
         }
         
     }
}
11,自定义字节流的缓冲区--read和write的特点
int read();// 返回值为何是int不是byte?自定义时如何解决?
1,因为一个字节是8位二进制数,
     可能读到二进制数“1111-1111”8个1的情况 ,此值为-1,造成无数读取数据
2,让read(byte[] b)返回的数 num&255 就可以
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
class  MyBufferedInputStream{
     private  InputStream in;
     private  byte [] buf =  new  byte [ 1024 * 4 ];    
     private  int  pos =  0 ,count =  0 ;   
     MyBufferedInputStream(InputStream in){
         this .in = in;
     }
     //一次读一个字节,从缓冲区(字节数组)获取。
     public  int  myRead() throws  IOException{
         //通过in对象读取硬盘上数据,并存储buf中。
         if (count== 0 ){
             count = in.read(buf);
             if (count< 0 )
                 return  - 1 ;
             pos =  0 ;
             byte  b = buf[pos];
 
             count--;
             pos++;
             return  b& 255 ;
         }
         else  if (count> 0 ){
             byte  b = buf[pos];
             count--;
             pos++;
             return  b& 0xff ;
         }
         return  - 1 ;
     }
     public  void  myClose() throws  IOException{
         in.close();
     }
byte在read()中被提升:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
11111111 - 111111110000000000101001001010100101010010101001010
 
byte : - 1   --->   int  : - 1 ;
00000000  00000000  00000000  11111111   255
11111111  11111111  11111111  11111111
 
11111111   -->提升了一个 int 类型 那不还是- 1 吗?是- 1 的原因是因为在 8 1 前面补的是 1 导致的。
那么我只要在前面补 0 ,即可以保留原字节数据不变,又可以避免- 1 的出现。
怎么补 0 呢?
 
  11111111  11111111  11111111  11111111                        
& 00000000  00000000  00000000  11111111 
------------------------------------
  00000000  00000000  00000000  11111111 
 
0000 - 0001
1111 - 1110
000000001
1111 - 1111   - 1
结论:
字节流的读一个字节的read方法为什么返回值类型不是byte,而是int。
因为有可能会读到连续8个二进制1的情况,8个二进制1对应的十进制是-1.
那么就会数据还没有读完,就结束的情况。因为我们判断读取结束是通过结尾标记-1来确定的。
所以,为了避免这种情况将读到的字节进行int类型的提升。
并在保留原字节数据的情况前面了补了24个0,变成了int类型的数值。
另外在writer(buf);其实是吧此值得前32位砍掉了,只保留了有效8位二进制数



12,读取键盘录入
读取键盘录入。
System.out:对应的是标准输出设备,控制台。
System.in:对应的标准输入设备:键盘。
需求:
通过键盘录入数据。
当录入一行数据后,就将该行数据进行打印。
如果录入的数据是over,那么停止录入。   代码演示: ----原理其实就是readLine方法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
class   ReadIn{
     public  static  void  main(String[] args)  throws  IOException{
         InputStream in = System.in;
         StringBuilder sb =  new  StringBuilder();
         while ( true ){
             int  ch = in.read();
             if (ch== '\r' )
                 continue ;
             if (ch== '\n' ){
                 String s = sb.toString();
                 if ( "over" .equals(s))
                     break ;
                 System.out.println(s.toUpperCase());
                 sb.delete( 0 ,sb.length()); //清空缓冲区StringBuilder
             }
             else
                 sb.append(( char )ch);
         }
     }
}
13,转换流----------------字节流<----->字符流
InputStreamReader
OutputStreamReader-----两者构造时可以加入字符集编码表
能不能直接使用readLine方法来完成键盘录入的一行数据的读取呢?
readLine方法是字符流BufferedReader类中的方法。
而键盘录入的read方法是字节流InputStream的方法。
那么能不能将字节流转成字符流在使用字符流缓冲去的readLine方法呢?
        1.读取转换流:InputStreamReader
                                    --------将字流转换成字
                代码演示:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class   TransStreamDemo{
     public  static  void  main(String[] args)  throws  IOException{
         //获取键盘录入对象。
         InputStream in = System.in;
         //将字节流对象转成字符流对象,使用转换流----InputStreamReader
         InputStreamReader isr =  new  InputStreamReader(in);
         //为了提高效率,将字符串进行缓冲区技术高效操作。使用BufferedReader
         BufferedReader bufr =  new  BufferedReader(isr);
         String line =  null ;
         while ((line=bufr.readLine())!= null )
         {
             if ( "over" .equals(line))
                 break ;
             bufw.write(line.toUpperCase());
         }
         bufr.close();
     }
}
        2.读取转换流:OutputStreamWriter
--------将字流转换成字
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
class   TransStreamDemo{
     public  static  void  main(String[] args)  throws  IOException{
         //获取键盘录入对象。
         //InputStream in = System.in;
         //将字节流对象转成字符流对象,使用转换流。InputStreamReader
         //InputStreamReader isr = new InputStreamReader(in);
         //为了提高效率,将字符串进行缓冲区技术高效操作。使用BufferedReader
         //BufferedReader bufr = new BufferedReader(isr);
         //键盘的最常见写法。------**熟记背过**
        BufferedReader bufr = 
                new BufferedReader(new InputStreamReader(System.in));//简写
         
//      OutputStream out = System.out;
//      OutputStreamWriter osw = new OutputStreamWriter(out);
//      BufferedWriter bufw = new BufferedWriter(osw);//写入的时候有缓冲区存在,因此一定要flush
         //上面3句话的简写
         BufferedWriter bufw =  new  BufferedWriter( new  OutputStreamWriter(System.out));
         String line =  null ;
         while ((line=bufr.readLine())!= null ){
             if ( "over" .equals(line))
                 break ;
             bufw.write(line.toUpperCase());
             bufw.newLine();
             bufw.flush();
         }
         bufr.close();
     }
}



14,流操作的规律
流操作的基本规律:
最痛苦的就是流对象有很多,不知道该用哪一个。
(一)通过三个明确来完成。
1,明确源和目的。
 源:输入流。InputStream Reader
 目的:输出流。OutputStream Writer。
2,操作的数据是否是纯文本。
 是:字符流。
 不是:字节流。
3,当体系明确后,在明确要使用哪个具体的对象。
 通过设备来进行区分:
 源设备:内存,硬盘。键盘
 目的设备:内存,硬盘,控制台。
最后问自己:是否需要提高效率:是!。加入Reader/Writer体系中缓冲区 BufferedReader/BufferedWriter.

扩展一下,想要把录入的数据按照指定的编码表(utf-8),将数据存到文件中。
目的:OutputStream Writer
是否是存文本?是!Writer。
设备:硬盘。一个文件。使用 FileWriter。
是FileWriter是使用的默认编码表。GBK.
但是存储时,需要加入指定编码表utf-8。而指定的编码表只有转换流可以指定。
所以要使用的对象是OutputStreamWriter。
而该转换流对象要接收一个字节输出流。而且还可以操作的文件的字节输出流。FileOutputStream
1
OutputStreamWriter osw =  new  OutputStreamWriter( new  FileOutputStream( "d.txt" ), "UTF-8" );
需要高效吗?需要。
BufferedWriter bufw = new BufferedWriter(osw);
所以,记住。转换流什么使用。字符和字节之间的桥梁,通常,涉及到字符编码转换时,
需要用到转换流。

16,改变标准输入输出设备
1
2
3
System.setIn( new  FileInputStream( "PersonDemo.java" ));
 
System.setOut( new  PrintStream( "zzz.txt" ));
当输入设备是一个文件,输出设备是一个文件时,就可以实现copy了

17,异常的日志信息
                      ----------异常发生的时间+异常信息
                      ----------网上日志工具包log4j
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
try {
     int [] arr =  new  int [ 2 ];
     System.out.println(arr[ 3 ]);
}
catch  (Exception e){
     try {
         Date d =  new  Date();
         SimpleDateFormat sdf =  new  SimpleDateFormat( "yyyy-MM-dd HH:mm:ss" );
         String s = sdf.format(d);
         PrintStream ps =  new  PrintStream( "exeception.log" );
         ps.println(s);
         System.setOut(ps);             
     }
     catch  (IOException ex){
         throw  new  RuntimeException( "日志文件创建失败" );
     }
     e.printStackTrace(System.out);
}

18系统信息
1
2
3
4
5
6
7
8
9
10
import  java.util.*;
import  java.io.*;
class   SystemInfo{
     public  static  void  main(String[] args)  throws  IOException{
         Properties prop = System.getProperties();
         //System.out.println(prop);
         //prop.list(System.out);
         prop.list( new  PrintStream( "sysinfo.txt" ));
     }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值