黑马程序员-Java IO输入与输出-day19

---------------------- ASP.Net+Android+IOS开发、.Net培训、期待与您交流! ----------------------

1、IO流(BufferedWriter)

字符流的缓冲区

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

2.对应类:

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

BufferedReader

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

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

LineNumberReader:跟踪行号

转换流的由来就是为了方便转换编码表的转换而来

缓冲区的出现时为了提高流的操作效率而出现的,所以在创建缓冲区之前,必须要先有流对象。

 

该缓冲区中提供了一个跨平台的换行符:newLine();

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=0;x<5; x++)
              {
                     bufw.write("abcd"+x);
                     bufw.newLine();//跨平台的换行符
                     bufw.flush();//防止数据在内存中丢失,因此就需要每写入一次流数据,就要将流数据写入到目的地
              }
              //bufw.write("abcde");
 
              //记住,只要用到缓冲技术,就要记得刷新
              bufw.flush();
 
              //其实关闭缓冲区,就是在关闭缓冲区中的流对象
              bufw.close();
       }
}

小结:

newLine():跨平台换行

 

2、IO流BufferedReader

public class BufferedReader extends Reader:从字符输入流中读取文本,缓冲各个字符,从而实现字符,数组和行的高效读取。

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)//readLine()读取到文件末尾没有数据时,会返回null
              {
                     System.out.println(line);
              }
              bufr.close();
       }
}

通过缓冲区复制一个.java文件

import java.io.*;
class CopyTextByBuf
{
       public static void main(String[] args)
       {
              BufferedReader bufr = null;
              BufferedWriter bufw = null;
              try
              {
                     bufr= new BufferedReader(new FileReader("BufferedWriterDemo.java"));
                     bufw= new BufferedWriter(new FileWriter("bufWriter.txt"));
                     Stringline = null;
                     while((line=bufr.readLine())!=null)
                     {
                            bufw.write(line);//写入数据
                            bufw.newLine();//跨平台换行
                            bufw.flush();//刷新数据
                     }
              }
              catch (IOException e)
              {
                     thrownew RuntimeException("读写失败");
              }
              finally
              {
                     try
                     {
                            if(bufr!=null)
                                   bufr.close();
                     }
                     catch(IOException e)
                     {
                            throw new RuntimeException("读取关闭失败");
                     }
 
                     try
                     {
                            if(bufw!=null)
                                   bufw.close();
                     }
                     catch(IOException e)
                     {
                            throw new RuntimeException("写入关闭失败");
                     }
              }
       }
}
小结:

字符读取流缓冲区,该缓冲区提供了一个一次读一行的方法readLine,方便于对文本数据的获取。当返回为null时,表示读到文件末尾。

readLine()方法返回的时候只返回回车符之前的数据内容,并不返回回车符。

 

3、readLine原理

明白了BufferedReader类中特有方法readLine的原理后,

可以自定义一个类中包含一个功能和readLine一致的方法,

来模拟一下BufferedReader

import java.io.*;
class MyBufferedReader
{
       private FileReader r;
       MyBufferedReader(FileReader r)
       {
              this.r = r;
       }
       //可以一次读一行数据的方法
       public String myReadLine()throws IOException
       {
              //定义一个临时容器,原BufferedReader封装的是字符数组。
              //为了掩饰方便,定义一个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);//时刻记住需要将获取的元素转义成character类型
              }
              if(sb.length()!=0)
                     return sb.toString();//为了防止文件末尾没有换行符,因此在获取文件长度不为零的时候,需要将其转换
              return null;
       }
       public void myClose()throws IOException
       {
              r.close();
       }
}
class MyBufferedReaderDemo
{
       publics tatic void main(String[] args) throws IOException
       {
              FileReader fr = new FileReader("buf.txt");
              MyBufferedReader myBuf = new MyBufferedReader(fr);
 
              String line = null;
              while((line=myBuf.myReadLine())!=null)
              {
                     System.out.println(line);
              }
              myBuf.myClose();
       }
}
需要特别注意一点的是:在定义ReadLine功能的时候,最后面一定要加上sb.length()!=0,如果不为零,则将剩余的字符转换成字符串并返回!

 

4、装饰设计模式

当想要对已有的对象进行功能增强时,

可以定义类,将已有对象传入,基于已有的功能,并提供加强功能。

那么自定义的该类称为装饰类。

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

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();//这里可以直接调用Person中的chifan()功能
              System.out.println("甜点");
              System.out.println("来一根");
       }
}
 
class PersonDemo
{
       public static void main(String[] args)
       {
              Person p = new Person();
              SuperPerson sp = new SuperPerson(p);
 
              sp.superChifan();
       }
}


5、装饰和继承的区别

MyReader//专门用于读取数据的类。

       |--MyTextReader

              |--MyBufferTextReader

       |--MyMediaReader

              |--MyBufferMediaReader

       |--MyDataReader

              |--MyBufferDataReader

 

class MyBufferReader

{

       MyBufferReader(MyReaderr)

       {}

       MyBufferReader(MyMediaReadermedia)

       {}

}

上面这个类扩展性很差。

找到其参数的共同类型,通过多态的形式,可以提高扩展性。

class MyBufferReader extends MyReader

{

       privateMyReader r;

       MyBufferReader(MyReaderr)

       {}

}

MyReader//专门用于读取数据的类

       |--MyTextReader

       |--MyMediaReader

       |--MyDataReader

       |--MyBufferReader

装饰模式比继承要灵活,避免了继承体系臃肿。而且降低了类与类之间的关系。

装饰类因为增强已有对象,具备的功能和已有是相同的,只不过提供了更强功能。

所以装饰类和被装饰类通常是都属于一个体系中的。


6、IO流——LineNumberReader

LineNumberReader:跟踪行号

       |--setLineNumber();

       |--getLineNumber();

import java.io.*;
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(0);
              while((line=lnr.readLine())!=null)
              {
                     System.out.println(lnr.getLineNumber()+":"+line);
              }
              lnr.close();
       }
}


7、MyLineNumberReader原理

import java.io.*;
class MyLineNumber extends MyBufferedReader
{
       private int lineNumber;
       MyLineNumberReader(Reader r)
       {
              this.r = r;
       }
       public String myReadLine()throws IOException
       {
              lineNumber++;//这里必须让行号自增,否则行号都是一样
              return super.myReadLine();
       }
 
       public void setLineNumber(int lineNumber)
       {
              this.lineNumber = lineNumber;
       }
       public int getLineNumber()
       {
              return lineNumber;
       }
}
class MyLineNumberReaderDemo
{
       public static void main(String[] args) throws IOException
       {
              FileReader fr = new FileReader("PersonDemo.java");
              MyLineNumberReader mlnr = new MyLineNumberReader(fr);
              String line = null;
              mlnr.setLineNumber(100);
              while((line=mlnr.myReadLine())!=null)
              {
                     System.out.println(mlnr.getLineNumber()+"::"+line);
              }
              mlnr.close();
       }
}


8、字符流:

FileReader

FileWriter

BufferedReader

BufferedWriter


字节流:

InputStream:读

OutputStream:写

字符流操作的是字符数组:char[]

字节流操作的是字节数组:byte[]

需求:想要操作图片数据,这时就要用到字节流。

import java.io.*;
class FileStream
{
       public static void main(String[] args) throws IOException
       {
              readFile_3();
              //readFile_2();
              //readFile_1();
              //writeFile();
       }
       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);//available只可获取较小文件的大小,文件过大还是得用循环
              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());//由于字节流操作的是字节数组,因此这里需要将字符串转换成字节数组
       }
}
小结:

如果一个数据不是太大,则可以直接使用available的方法获取其大小,间接提高程序运行效率;而如果数据超大,则还得使用定义一个1024整数倍的容器来获取数据,否则会导致内存溢出。


9、复制一个图片

思路:

1.用字节读取流对象和图片关联;

2.用字节写入流对象创建一个图片文件,用于存储获取到的图片数据;

3.通过循环读写,完成数据的存储;

4.关闭资源

import java.io.*;
class  CopyPic
{
       public static void main(String[] args)
       {
              FileInputStream fis = null;
              FileOutputStream fos = null;
              try
              {
                     fis= new FileInputStream("C:\\1.jpg");
                     fos= new FileOutputStream("C:\\2.jpg");
                     byte[]buf = new byte[1024];
                     int len = 0;
                     while((len=fis.read(buf))!=-1)
                     {
                            fos.write(buf,0,len);
                     }
              }
              catch (IOException e)
              {
                     throw new RuntimeException("复制文件失败");
              }
              finally
              {
                     try
                     {
                            if(fis!=null)
                                   fis.close();
                     }
                     catch(IOException e)
                     {
                            throw new RuntimeException("读取关闭文件失败");
                     }
 
                     try
                     {
                            if(fos!=null)
                                   fos.close();
                     }
                     catch(IOException e)
                     {
                            throw new RuntimeException("写入关闭文件失败");
                     }
              }
       }
}


10、字节流的缓冲区

BufferedOutputStream BufferedInputStream

复制一个mp3文件

import java.io.*;
class CopyMp3
{
       public static void main(String[] args) throws IOException
       {
              long start = System.currentTimeMillis();
              copy_1();
              long end = System.currentTimeMillis();
              System.out.println((end-start)+"毫秒");
       }
       public static void copy_1()throws IOException
       {
              MyBufferedInputStream bufis = new MyBufferedInputStream(new FileInputStream("C:\\1.mp3"));
              BufferedOutputStream bufos = new BufferedOutputStream(new FileOutputStream("C:\\2.mp3"));
              int by = 0;
	      //System.out.println(“第一个字节:”+bufis.myRead());
              while((by=bufis.mRead())!=-1)
              {
                     bufos.write(by);
              }
              bufis.myClose();
              bufos.close();
       }
}

//自定义字节流的缓冲区:read和write的特点
import java.io.*;
class MyBufferedInputStream
{
       private InputStream in;
       private byte[] buf= new byte[1024];
       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;
                     byteb = buf[pos];
 
                     count--;
                     pos++;
                     returnb&255;
              }
              else if(count>0)
              {
                     byteb = buf[pos];
 
                     count--;
                     pos++;
                     returnb&0xff;
              }
              return -1;
       }
       public void myClose()throws IOException
       {
              in.close();
       }
}

/*

byte: -1 -->int:-1

11111111         -->11111111 11111111 11111111 11111111

而通过与上255,即可去掉前面三个八位的前提下,不影响结果:

 11111111 11111111 11111111 11111111

&00000000 00000000 00000000 11111111

------------------------------------------------------

 00000000 00000000 00000000 11111111

*/

小结:

1.自定义MyBufferedInputStream中的read方法的返回值为什么是int而不是byte,这里是由于读取到的数据可能读取到首个八位转换成byte的时候变成-1继而导致文件复制失败,因此这里需要将其强制转换成int类型后,可以通过不改变值的前提下,同255或者0xff相与,去除首八位为-1的情况且不影响结果;

2.这就是为什么在该MyBufferedInputStream的read方法的返回值中必须同255相与的结果。

3.返回值是int类型而不是byte类型,按理说类型提升,所占空间应该是原来四倍,可所得到的结果却不是,这是为什么?通过查阅API的write底层可得知,write只将int类型中的最低八位写出去了,这样就获取到了最终正确结果,结论就是read在做提升,write在做强转动作,因此就能保证原数据没有变化

 

11、IO流—读取键盘录入

System.out:对应的是标准输出设备,控制台。

System.in:对应的是标准输入设备:键盘。

import java.io.*;
class ReadIn
{
       public static void main(String[] args) throws IOException
       {
              InputStream in = System.in;
              StringBuilder sb = new StringBuilder();
              while(true)
              {
                     intch = 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());//必须要将临时存储的数据清空,否则输入的字符会连在一起
                     }
                     else
                            sb.append((char)ch);
              }
       }
}
小结:in.read()是一个阻塞式方法,没有读到数据,它就会在那一直等。

 

12、读取转换流

public class InputStreamReader extendsReader :InputStreamReader是字节流通向字符流的桥梁。

import java.io.*;
class TransStreamDemo
{
       public static void main(String[] args) throws IOException
       {
             
//获取键盘录入对象
              //InputStream in = System.in;
 
              //将字节流对象转换成字符流对象,使用转换流InputStreamReader
              //InputStreamReader isr = new InputStreamReader(in);
 
              //为了提高效率,将字符串进行缓冲区技术高效操作,使用BufferedReader
              //BufferedReader bufr = newBufferedReader(isr);
	      //键盘录入的最常见写法
              BufferedReader bufr =
                     new BufferedReader(new InputStreamReader(System.in));
 
//            OutputStream out = System.out;
//            OutputStreamWriter osw = new OutputStreamWriter(out);
//            BufferedWriter bufw = new BufferedWriter(osw);
 
              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();
 
              }     }
}


13、IO流—流操作规律

通过三个明确来完成

1.明确源和目的。

       源:输入流:InputStream Reader

       目的:输出流:OutputStream Writer

2.操作的数据是否是纯文本。

       是:字符流。

       不是:字节流。

3.当体系明确后,在明确要使用哪个具体的对象。

       通过设备来进行区分:

       源设备:内存、硬盘、键盘。

       目的设备:内存、硬盘、控制台。

------------------------------------------------

1.将一个文本文件中的数据存储到另一个文件中,复制文件

       源:因为是源,所以使用读取流:InputStream       Reader

       是不是操作纯文本?是!

       这时就可以选择字符流:Reader,这样体系就明确了。

 

       接下来明确要使用该体系中的哪个对象

       明确设备:硬盘上的一个文件。

       Reader体系中可以操作文件的对象是FileReader

       是否需要提高效率:是!加入Reader体系中的缓冲区BufferedReader。

       FileReaderfr = new FileReader();

       BufferedReaderbufr = new BufferedReader(fr);


       目的;OutputStream Writer

       是否是纯文本?是,则选择Writer

       设备:硬盘上的一个文件

       Writer体系中可以操作文件的对象是FileWriter

 

       是否需要提高效率:是!加入Writer体系中的缓冲区BufferedWriter。

       FileWriterfw = new FileWriter();

       BufferedWriterbufw = new BufferedWriter(fw);

 

--------------------------------------------------------------------------------------------

2.需求:将键盘录入的数据保存到一个文件中。

       这个需求中有源和目的都存在。那么分别分析,

       源:InputStream Reader

       是不是纯文本?是!Reader

 

       设备:键盘。对应的对象是System.in.

       不是选择Reader吗?System.in对应的不是字节流吗?

       为了操作键盘的文本数据方便,转成字符流按照字符串操作是最方便的。

       所以既然明确了Reader,那么就将System.in转换成Reader。

       用了Reader体系中的转换流:InputStreamReader

       InputStreamReaderisr = new InputStreamReader(System.in);

       需要提高效率吗?需要!BufferedReader

       BufferedReaderbufr = new BufferedReader(isr);

 

       目的:OutputStream Writer

       是否是纯文本?是!Writer

       设备:硬盘,一个文件,使用FileWriter

       FileWriterfw = new FileWriter("c.txt");

       需要提高效率吗?需要!

       BufferedWriterbufw = new BufferedWriter(fw);

 

--------------------------------------------------------------------------------

       扩展一下,想要把录入的数据按照指定的编码表(UTF-8),将数据存到文件中

       目的:OutputStream Writer

       是否是纯文本?是!Writer

       设备:硬盘,一个文件,使用FileWriter

       但是FileWriter使用的是默认编码表GBK。

       但是存储时,需要加入指定编码表UTF-8,而指定的编码表只有转换流可以指定。

       所以要使用的对象是OutputStreamWriter。

       而该转换流对象要接收一个字节输出流,而且还可以操作文件的字节输出流:FileOutputStream

       OutputStreamWriterosw = new OutputStreamWriter(new FileOutputStream("d.txt"));

       需要高效吗?需要

       BufferedWriterbufw = new BufferedWriter(osw);

       什么时候使用转换流呢?

       字符和字节之间的桥梁,通常,涉及到字符编码转换时,就需要用到转换流。

import java.io.*;
class TransStreamDemo2
{
       public static void main(String[] args) throws IOException
       {
              //设置输入文件
              //System.setIn(new FileInputStream("TransStreamTest00.java"));
              System.setOut(new PrintStream("z.txt"));
              //录入键盘常用写法
              BufferedReader bufr =
                     new BufferedReader(new InputStreamReader(System.in));
              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();
       }
}
小结:

由于考虑到readLine方法是字符流BufferedReader类中的方法,而键盘录入的read方法是字节流InputStreamReader类中的方法,于是这里就用到了转换流


14、改变标准输入输出设备

//复制文件
import java.io.*;
class TransStreamDemo2
{
       public static void main(String[] args) throws IOException
       {
              System.setIn(new FileInputStream("PersonDemo.java"));//设置输入源
 
              System.setOut(new PrintStream("zz.txt"));//设置输出源
              BufferedReader bufr =
                     new BufferedReader(new InputStreamReader(System.in));
 
              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();
       }
}


15、异常的日志信息

void printStackTrace(PrintStream s) 将此throwable及其追踪输出到指定的输出流

import java.io.*;
import java.util.*;
import java.text.*;
class ExceptionInfo
{
       public static void main(String[] args)
       {
              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("Exception.log");
                            ps.println(s);
                            System.setOut(ps);
                           
                     }
                     catch(Exception ex)
                     {
                            throw new RuntimeException("日志文件创建失败");
                     }
                     e.printStackTrace(System.out);
              }
       }
}


16、获取系统信息

import java.io.*;
import java.util.*;
class SystemInfo
{
       public static void main(String[] args) throws IOException
       {
              Properties prop = System.getProperties();
              //System.out.println(prop);
              //prop.list(System.out);
              prop.list(newPrintStream("sysinfo.txt"));
       }
}




---------------------- ASP.Net+Android+IOS开发、 .Net培训、期待与您交流! ----------------------
详细请查看:http://edu.csdn.net

你可能感兴趣的:(JAVA)