黑马程序员——Java IO流(一)之IO流概述、字符流、字节流等

2024-03-16 09:08

本文主要是介绍黑马程序员——Java IO流(一)之IO流概述、字符流、字节流等,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

-----------android培训java培训、java学习型技术博客、期待与您交流!------------

IO流

一、概述

 1.IO流是用来处理设备之间的数据传输。

 2.Java对数据的操作时通过流的方式。

 3.Java用于操作流的对象都在IO包中。

 4.流按操作数据分为两种:字节流和字符流。流按流向分为:输入流和输出流。

 5.流常用的基类:

  1)字节流的抽象基类:ImputStream和OutputStream。

  2)字符流的抽象基类:Reader和Writer。

 6.体系架构:

  字符流体系架构:

   Reader:用于读取字符流的抽象类。

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

      |--LineNumberReader:跟踪行号的缓冲字符输入流。

    |--InputStreamReader:转换流,是字节流通向字符流的桥梁。

      |--FileReader:用来读取字符文件的便捷类。

   Writer:写入字符流的抽象类。

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

    |--OutputStreamWriter:转换流,是字符流通向字节流的桥梁。

      |--FileWriter:用来写入字符文件的便捷类。

  字节流体系架构:

   ImputStream:此抽象类是表示字节输入流的所有类的超类。

    |--FileInputStream:从文件系统中的某个文件中获得输入字节, 用于读取诸如图像数据之类的原始字节流。

    |--FilterInputStream:

      |--BufferedInputStream:字节输入流缓冲区。

   OutputStream:此抽象类是表示输出字节流的所有类的超类,输出流接受输出字节并将这些字节发送到某个接收器。

    |--FileOutputStream:文件输出流是用于将数据写入 File 或 FileDescriptor 的输出流,用于写入诸如图像数据之类的原始字节的流。

    |--FilterOutputStream:此类是过滤输出流的所有类的超类。

      |--BufferedOutputStream:该类实现缓冲的输出流。通过设置这种输出流,应用程序就可以将各个字节写入底层输出流中,而不必针对每次字节写入调用底层系统。

      |--PrintStream:为其他输出流添加了功能,使它们能够方便地打印各种数据值表示形式。打印的所有字符都使用平台的默认字符编码转换为字节。PrintStream 永远不会抛出 IOException,而是,异常情况仅设置可通过 checkError 方法测试的内部标志。在需要写入字符而不是写入字节的情况下,应该使用 PrintWriter 类。

 问题思考:字节流和字符流有什么不同?

  答:字节流可以用于进行任何数据类型的操作,而字符流带编码表,只能用于进行纯文本数据类型的操作。


二、格式示例

 IO异常格式的处理:

import java.io.*;
class  FileWriterDemo2
{public static void main(String[] args) {//需要将fw引用定义在外部,因为在finally中需要调用close方法FileWriter fw=null;try{//创建对象的时候,需要定义在try里面,因为抛出了IO异常。fw=new FileWriter("E:\\heima\\deme2.txt");//write方法抛出了IO异常。fw.write("hello world");}catch (IOException e){System.out.println("cacht="+e.toString());}finally {//关闭流资源一定要执行,需定义在finally内部,且close方法抛出了//IO异常,需要进行try处理。try{//需要对fw是否为null进行判断,因为如果fw为null,则再调用close方法//发发生NullPoterException异常。如果有多个流资源需要关闭,应进行多次//判断并关闭,不要写在一个判断里。if (fw!=null){fw.close();}}catch (IOException ex){System.out.println(ex.toString());}}}
}


三、字符流

 字符流常用对字符文件的操作。对于字符流的操作,应熟练掌握以下几个内容:

 1.在指定目录下创建一个纯文本文件,并在这个文件中写入指定内容:

  示例1:"E:\\heima"的目录下创建纯文本文件demo.txt,并向该文件demo.txt写入内容。

import java.io.*;
class FileWriterDemo 
{public static void main(String[] args) throws IOException{FileWriter fw=null;try{//fileWriter对象一被初始化就必须明确被操作的文件的对象。//而且该文件会被创建到指定目录下,如果该目录下已有同名文件存在,//则将被覆盖。由于创建的目录可能不存在,因此抛出了IOException。fw=new FileWriter("E:\\heima\\demo.txt");//调用write方法,将字符串写入到流中。fw.write("nihao");//刷新流对象中的缓冲区的数据。将数据刷到目的地中。fw.flush();//可重复进行写入数据到文件中。fw.write("hello");fw.flush();}catch (IOException e){System.out.println("cacht="+e.toString());}finally {try{if (fw!=null){//关闭流对象,在关闭前会刷新流一次。关闭后不能再向流中写入数据,//否则将发生IO异常。该动作一定做,因为流在调用window资源,需要进行关闭。fw.close();}}catch (IOException ex){System.out.println(ex.toString());}}}
}

  程序运行后在"E:\\heima"的目录下的创建了一个Demo.txt文件,文件的内容如下图:



 2.对指定目录下的一个纯文本文件进行内容续写:

  示例2:对示例1中创建的文件demo.txt进行内容续写。

import java.io.*;
class  FileWriterDemo3
{public static void main(String[] args) {FileWriter fw=null;try{//传递一个true穿参数,代表不覆盖已有文件,并在已有文件末尾处添加。//如果没有已有文件,则会新创建一个文件。fw=new FileWriter("E:\\heima\\demo2.txt",true);//添加内容fw.write("zhangsan");}catch (IOException e){System.out.println(e.toString());}finally{	try{if (fw!=null){fw.close();}}catch (IOException ex){System.out.println(ex.toString());}}}
}

  程序运行后,在示例1中的demo.txt文件内容中续写了一些指定内容,截图如下:



 3.写入内容的方法总结:

  1)写入单个字符。

   void write(int c):将int类型的字符c写入到指定目的对象中。

  2)写入字符数组或其中的某一部分。

   void write(char[] cbuf):将字符数组cbuf中的全部字符写入到指定对象中。

   void write(char[] cbuf,int off,int len):将字符数组cbuf中从索引off开始写入到指定对象,写入的字符总数为len。

  3)写入字符串或其中的某一部分。

   void write(String str):将整个字符串内容写入到指定对象中。

   void write(String str,int off,int len):将字符串str中从索引off开始写入到指定对象,写入的字符总数为len。

  注:上述所有写入的方法都会抛出IOException异常。 


 4.对指定目录下的一个纯文本文件进行内容读取:

  示例3:对示例2续写的文件demo.txt进行内容读取并打印到控制台。

import java.io.*;
class FileReaderDemo1 
{public static void main(String[] args) {FileReader fr=null;try{//创建一个文件读取流对象,和指定的文件demo.txt相关联。//要保证该文件是存在的,如果不存在则会发生FileNotFoundException异常。fr=new FileReader("E:\\heima\\demo.txt");int ch=0;//read()方法读取文件的内容,一次读一个字符,而且会自动往下读。//循环读取文件内容,如果读到文件末尾则会返回-1,可根据此条件//来判断是否继续读取。while ((ch=fr.read())!=-1){System.out.print((char)ch); //将读取到int类型数据转换成字符类型输出。}}catch (IOException e){System.out.println(e.toString());}finally{	try{if (fr!=null){fr.close();}}catch (IOException ex){System.out.println(ex.toString());}}}}
 

  程序运行后的结果如下图:

 5.读取内容的方式总结:

  1)读取单个字符。

   int read():返回值为读取的int类型字符。如果读取的字符已到达流的末尾,则返回 -1。

  2)将字符读入数组或数组中的某一部分。

   int read(char[] cbuf):将字符读入数组。在某个输入可用、发生 I/O 错误或者已到达流的末尾前,此方法一直阻塞。

   int read(char[] cbuf,int offset,int length):将字符读入字符数组cbuf中,从索引offset开始存储,存入的字符个数限制为length。

   注:返回值为实际存储到字符数组的个数。如果读取的字符数已到达流的末尾,则返回 -1。

  3)将字符读入指定的字符缓冲区。了解charBuffer更多用法,参加AIP文档。

   int read(CharBuffer target):返回值为添加到缓冲区的字符数量,如果此字符源位于缓冲区末端,则返回 -1。

 6.字符流缓冲区:

  1)BufferedWriter:字符流写入缓冲区。

  写入数据的方法有:

   a)写入单个字符:

    void write(int c):该方法抛出了IO异常。

   b)写入字符数组:

    void write(char[] c):该方法抛出了IO异常。

   c)写入字符数组的某一部分:

    void write(char[] cbuf, int off, int len) :该方法抛出了IO异常。

   d)写入字符串:

    void write(String str):该方法抛出了IO异常。

   e)写入字符串的某一部分:

    write(String s, int off, int len):如果 len 参数的值为负数,则不写入任何字符。这与超类中此方法的规范正好相反,它要求抛出 IndexOutOfBoundsException。该方法抛出了IO异常。

   示例4:使用缓冲区技术,在"E:\\heima"的目录下创建纯文本文件test.txt,并向该文件test.txt写入内容。

<pre name="code" class="java">import java.io.*;
class BufferedWriterDemo
{public static void main(String[] args) {//创建字符流读取缓冲区引用BufferedWriter bufw=null;try{//创建字符流读取缓冲区对象,并传入文件读取流对象bufw=new BufferedWriter(new FileWriter("E:\\heima\\test.txt"));//将字符串内容写入到字符流读取缓冲区中bufw.write("nihao");//刷新缓冲区中的数据,在写入内容不多的情况下,可以不用flush方法,但一定要close方法关闭//资源,即将写入的内容最后关闭的时候一次性刷入文件中。如果写入内容很多,应加flush,防止//在还没有刷新的情况下,出现停电,导致数据丢失。当加了flush后,可以即时将数据刷入文件中。bufw.flush();//换行bufw.newLine();bufw.write("nihao");}catch (IOException e){throw new RuntimeException("文件写入失败");}finally{	if (bufw!=null){try{bufw.close();}catch (IOException ex1){throw new RuntimeException("写入关闭失败");}}}}}

   程序运行后在"E:\\heima"的目录下生成了test.txt文件,文件内容如下截图所示: 
 

 2)BufferedReader:字符流读取缓冲区。

  读取数据的方法有:

   a)读取单个字符:

    int read():返回字符的int类型值。如果已到达流末尾,则返回 -1。

   b)将字符读入数组:

    int read(char[] cbuf):返回读入的字符数。如果已到达流的末尾,则返回 -1。在某个输入可用、发生 I/O 错误或者已到达流的末尾前,此方法一直阻塞。

   c)将字符读入数组的某一部分:

    int read(char[] c,int off,int len):返回读取的字符数。如果已到达流末尾,则返回 -1。

   d)读取一个文本行:

    int readLine():返回包含该行内容的字符串,不包含任何行终止符。如果已到达流末尾,则返回 null。

   e)试图将字符读入指定的字符缓冲区:

    int read(charBuffer target):返回添加到缓冲区的字符数量,如果此字符源位于缓冲区末端,则返回 -1。

   注:上述方法都抛出了IO异常。

  示例5:使用缓冲区技术,对示例4创建的test.txt文件进行内容读取并打印输出到控制台。

import java.io.*;
class BufferedReaderDemo
{public static void main(String[] args) {//创建字符流读取缓冲区引用BufferedReader bufr=null;try{//创建字符流读取缓冲区对象,并传入文件读取流对象bufr=new BufferedReader(new FileReader("E:\\heima\\test.txt"));String line=null;//调用字符流读取缓冲区对象的readLine方法,如果判断不为空,则继续读下一行while ((line=bufr.readLine())!=null){System.out.println(line);}}catch (IOException e){throw new RuntimeException("文件读取失败");}finally{	if (bufr!=null){try{bufr.close();}catch (IOException ex1){throw new RuntimeException("读取关闭失败");}}}}}
  程序运行后的结果如下截图所示:



 7.装饰设计模式:

  1)概述

   当想要对已有的对象进行功能增强时,可以定义类,将已有的功能,并提供加强功能。那么自定义的类就称为装饰类。

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

   示例6:对已存在的Person类进行功能增强,并用SuperPerson类描述。

<pre name="code" class="java">class PersonDemo 
{public static void main(String[] args) {SuperPerson sp=new SuperPerson(new Person());sp.superEat();}
}
class Person
{public void eat(){System.out.println("吃饭");}
}//装饰类
class SuperPerson
{private Person p;SuperPerson(Person p){this.p=p;}//对eat功能进行增强public void superEat(){System.out.println("开胃酒");p.eat();System.out.println("甜点");}
}

   程序运行后的结果如下图所示: 

  

  2)装饰与继承的区别:

   继承结构:    

    MyReader     

     |--MyTextReader      

       |--MyBuffferedTextReader:继承增强类。     

     |--MyMediaReader      

       |--MyBufferedMediaReader:继承增强类。     

     |--MyDataReader      

       |--MyBufferedDataReader:继承增强类。

   装饰结构:    

    MyReader     

     |--MyTextReader     

     |--MyMediaReader     

     |--MyDataReader     

     |--MyBufferedReader:装饰类,用于给MyReader子类的功能进行增强。

   区别:装饰模式比继承要灵活,避免了继承体系的臃肿,而且降低了类与类之间的关系。装饰类因为增强已有对象,具备的功能和已有功能是相同的,只不过提供了更强的功能。所以装饰类和被装饰类通常属于一个体系中的。但装饰类只能在被装饰类的父类的基础上进行增强,而继承可以在被继承类的基础上直接增强。

  3)自定义装饰类:

   示例7:模拟BufferedReader类。

//自定义装饰类
class MyBufferedReader extends Reader
{private FileReader r;MyBufferedReader(FileReader r){this.r=r;}//定义读取行功能public String myReadLine() 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;}//直接用传进来的子类进行覆盖,因为该子类一定实现了父类的方法public int read(char buf,int off,int len){r.read(buf,off,len);}//覆盖父类的close抽象方法public void close(){r.close();}}


 8.LinenumberReader类:

  1)概述

   LineNumberReader是BufferedReader的子类,用于跟踪行号的缓冲字符输入流。该类的setLineNumber和getLineNumber方法可以用于设置和获取行号。

   示例8:对示例4创建的test.txt文件进行内容读取并打印输出到控制台,且要求打印的内容带有行号,并要求从100行开始输出。

import java.io.*;
class LineNumberReaderDemo
{public static void main(String[] args) {LineNumberReader lnr=null;try{lnr=new LineNumberReader(new FileReader("E:\\heima\\test.txt"));String line=null;//设置行号为100lnr.setLineNumber(100);while ((line=lnr.readLine())!=null){//连同行号一起打印System.out.println(lnr.getLineNumber()+"::"+line);}}catch (IOException e){throw new RuntimeException("文件读取失败");}finally{	if (lnr!=null){try{lnr.close();}catch (IOException ex1){throw new RuntimeException("读取关闭失败");}}}}}
  程序运行后的结果如下图:


  2)自定义LineNumberReader类:

/*
自定义LineNumberReader类:思路:1.定义MyLineNumberReader类,并继承BufferedReader。2.复写readLine方法,使其没读取一次该方法,行号计数加1。3.对外提供设置行号和获取的方法。
*/import java.io.*;//继承BufferedReader类,可以使类更加简单,只需定义特有方法即可。
class MyLineNumberReader extends BufferedReader
{//定义行号属性private int lineNumber;MyLineNumberReader(Reader r){super(r);}public String readLine() throws IOException{//每调用一次行号自增1lineNumber++;return super.readLine();}//设置行号public void setLineNumber(int lineNumber){this.lineNumber=lineNumber;}//获取行号public int getLineNumber(){return lineNumber;}}

 9.代码练习:

  练习1:完成对示例4创建的test.txt文件的复制。

/*需求:完成<span style="font-size:14px;">对示例4创建的test.txt文件</span>的复制。思路:1.在指定目录下创建一个文件,用来存储复制文本内容。2.创建一个文件读取流对象,与要复制文本文件相关联。3.将要复制的文本文件的内容读取到数组中。4.将数组中的内容写入到新创建的文件中。
*/
import java.io.*;
class FileCopyDemo1
{public static void main(String[] args) {FileWriter fw=null;FileReader fr=null;try{fw=new FileWriter("E:\\heima\\testCopy.txt");fr=new FileReader("E:\\heima\\test.txt");//定义字符数组用来存储char[] buf=new char[1024];int num=0;while ((num=fr.read(buf))!=-1){//将数组中的内容写入到新创建的文本文件中,不需要刷新。fw.write(buf,0,num);}}catch (IOException e){throw new RuntimeException("文件读取失败");}finally{	if (fr!=null){try{fr.close();}catch (IOException ex1){throw new RuntimeException("读取关闭失败");}}if (fw!=null){try{fw.close();}catch (IOException ex2){throw new RuntimeException("写入关闭失败");}}}}}
  程序运行后在"E:\\heima"的目录下创建了一个testCopy.txt文件,该文件的内容的截图如下:



  练习2:使用字符流缓冲区技术完成练习1。

/*
思路:1.在指定目录下创建一个文件,用来存储复制文本内容。定义一个字符写入流缓冲区,并与新创建的文本文件相关联。2.创建一个文件读取流对象,与要复制文本文件相关联,定义一个字符读取流缓冲区,并与文件读取流对象相关联。3.用readLine方法循环取出字符读取缓冲区的内容。4.将readLine取出的内容写入到字符写入流缓冲区中。5.用newLine方法换行,并刷新到新创建的文件中。
*/
import java.io.*;
class FileCopyDemo2
{public static void main(String[] args) {BufferedWriter bufw=null;BufferedReader bufr=null;try{bufw=new BufferedWriter(new FileWriter("E:\\heima\\test_Copy.txt"));bufr=new BufferedReader(new FileReader("E:\\heima\\test.txt"));String line=null;while ((line=bufr.readLine())!=null){bufw.write(line);bufw.newLine();bufw.flush();}}catch (IOException e){throw new RuntimeException("文件读取失败");}finally{	if (bufr!=null){try{bufr.close();}catch (IOException ex1){throw new RuntimeException("读取关闭失败");}}if (bufw!=null){try{bufw.close();}catch (IOException ex2){throw new RuntimeException("写入关闭失败");}}}}}
  程序运行后在"E:\\heima"的目录下创建了test_Copy.txt文件,该文件的内容截图如下:



四、字节流

 字节流常用于对非字符文件的操作,如图象、视频等,但不表示字节流不能操作字符文件,而是用字符流来操作字符文件更为便捷。字节流应掌握的内容基本同字符流一致。

 1.写入内容的方法总结:

  1)将指定字节写入此文件输出流。

   void write(int b):该方法抛出了IO异常。

  2)将 b.length 个字节从指定 byte 数组写入此文件输出流中。

   void write(byte[] b):该方法抛出了IO异常。

  3)将指定 byte 数组中从偏移量 off 开始的 len 个字节写入此文件输出流。

   void write(byte[] b,int off,int len):该方法抛出了IO异常。

  注:上述所有方法都是FileOutputStream类特有方法,且写入文件时,不需要进行刷新操作。


 2.读取内容的方法总结:

  1)从此输入流中读取一个数据字节。

   int read():返回下一个数据字节。如果已到达文件末尾,则返回 -1。

  2)从此输入流中将最多 b.length 个字节的数据读入一个 byte 数组中。

   int read(byte[] b):返回读入缓冲区的字节总数。如果因为已经到达文件末尾而没有更多的数据,则返回 -1。在某些输入可用之前,此方法将阻塞。

  3)从此输入流中将最多 len 个字节的数据读入一个 byte 数组中。

   int read(byte[] b,int off,int len):读入缓冲区的字节总数,如果因为已经到达文件末尾而没有更多的数据,则返回 -1。如果 len 不为 0,则在输入可用之前,该方法将阻塞;否则,不读取任何字节并返回 0。


 3.复制图片(复制视频等原理类似):

  示例9:复制指定目录的文件。

/*思路:1.用字节读取流对象与图片关联。2.用字节写入流对象创建一个新的图片文件,用来进行存储获取到的文件数据。3.通过循环读写完成数据的存储。4.关闭资源。注:字节流写入数据时不需要刷新,但仍需关闭资源。
*/
import java.io.*;
class  CopyPicDemo
{public static void main(String[] args) {FileInputStream fis=null;FileOutputStream fos=null;try{fos=new FileOutputStream("E:\\heima\\2.png"); //创建字节写入流对象,生成复制图像文件。fis=new FileInputStream("E:\\heima\\1.png"); //创建字节读取流对象,并与要被复制的图像文件关联。byte[] by=new byte[1024];int len=0;while ((len=fis.read(by))!=-1){fos.write(by,0,len);}}catch (IOException e){throw new RuntimeException("文件复制失败");}finally{	if (fis!=null){try{fis.close();}catch (IOException ex1){throw new RuntimeException("读取关闭失败");}}if (fos!=null){try{fos.close();}catch (IOException ex1){throw new RuntimeException("写入关闭失败");}}}}
}
  程序运行后,复制图像文件与被复制图形文件对比图如下:



 4.字节流缓冲区

  字节流缓冲区有两个:BufferedInputStream(字节流读取缓冲区)和BufferedOutputStream(字节流写入缓冲区)。

  从输入流读取数据的方法总结:

   1)从输入流中读取数据的下一个字节: 

    int read():返回下一个数据字节。如果到达流末尾,则返回 -1。

   2)从此输入流中将 byte.length 个字节的数据读入一个 byte 数组中:

    int read(byte[] b):返回读入缓冲区的字节总数,如果因为已经到达流末尾而没有更多的数据,则返回 -1。在某些输入可用之前,此方法将阻塞。

   3)从此字节输入流中给定偏移量处开始将各字节读取到指定的 byte 数组中:

    int read(byte[] b, int off, int len):返回读取的字节数。如果已到达流末尾,则返回 -1。

   注:上述方法都抛出了IO异常。


  将数据写入缓冲的输出流的方法:

   1)将指定的字节写入此缓冲的输出流:

    void write(int b):该方法抛出了IO异常。

   2)将 b.length 个字节写入此输出流:

    void write(byte[] b):该方法抛出了IO异常。

   3)将指定 byte 数组中从偏移量 off 开始的 len 个字节写入此缓冲的输出流:

    void write(byte[] b,int off,int len):该方法抛出了IO异常。

  示例10:使用缓冲区技术完成对示例9的复制文件操作。

import java.io.*;
class  BufferedStreamDemo
{public static void main(String[] args) {BufferedInputStream bufis=null;BufferedOutputStream bufos=null;try{//创建字节流写入缓冲区对象bufos=new BufferedOutputStream(new FileOutputStream("E:\\heima\\2.png"));//创建字节流读取缓冲区对象bufis=new BufferedInputStream(new FileInputStream("E:\\heima\\1.png"));int num=0;while ((num=bufis.read())!=-1){bufos.write(num);}}catch (IOException e){System.out.println(e.toString());}finally{	if (bufis!=null){try{bufis.close();}catch (IOException ex1){throw new RuntimeException("读取关闭失败");}}if (bufos!=null){try{bufos.close();}catch (IOException ex1){throw new RuntimeException("写入关闭失败");}}}}
}
   程序运行后,复制图像文件与被复制图形文件对比图如下:



 5.读取键盘录入:

  示例11:通过键盘录入,当录入一行的数据后,就将该行数据转换成大写并进行打印。如果录入的数据是over,那么停止录入。

/*System.out:对应的标准输出设备,控制台。
System.in:对应的标准输入设备,键盘。
*/
import java.io.*;
class  ReadInDemo
{public static void main(String[] args) {InputStream in=System.in;StringBuilder sb=new StringBuilder();try{while (true){int ch=in.read();if (ch=='\r'){continue;}else if (ch=='\n'){String s=sb.toString();if (s.equals("over")){break;}System.out.println(s.toUpperCase());sb.delete(0,sb.length());}else sb.append((char)ch);}}catch (IOException ex){throw new RuntimeException("发生异常");}finally{try{in.close();}catch (IOException e){throw new RuntimeException("关闭失败");}}}
}
  程序运行后的结果如下图:


 

 6.转换流

  转换流包括两个:InputStreamReader(字节流通向字符流)和OutpuStreamWriter(字符流通向字节流)。

  示例12:通过转换流完成对示例11的操作。

import java.io.*;
class  TransStreamDemo
{public static void main(String[] args) {BufferedReader bufr=null;BufferedWriter bufw=null;try{//将字节流转换成字符流输入bufr=new BufferedReader(new InputStreamReader(System.in));//将字符流转换成字节流输出bufw=new BufferedWriter(new OutputStreamWriter(System.out));String line=null;while ((line=bufr.readLine())!=null){if (line.equals("over")){break;}bufw.write(line.toUpperCase());  //将读取内容转换成大写,并写入字符流写入缓冲区bufw.newLine();  //换行bufw.flush();  //刷新字符流写入缓冲区的数据到控制台}}catch (IOException ex){throw new RuntimeException("发生异常");}finally{if (bufr!=null){try{bufr.close();}catch (IOException e){throw new RuntimeException("读取关闭失败");}}if (bufw!=null){try{bufw.close();}catch (IOException e){throw new RuntimeException("写入关闭失败");}}}}
}
  程序运行后的结果如下图:



五、自定义缓冲区

 1.自定义字符流缓冲区:

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

  readLine方法的原理:无论是读一行,获取读取的多个字符。其实最终都是在硬盘上一个一个读取,所以最终使用的还是read方法一次读一个的方法。

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

  示例13:自定义字符流读取缓冲区。

<pre name="code" class="java">import java.io.*;
class MyBufferedReaderDemo
{public static void main(String[] args) {MyBufferedReader bufr=null;try{//创建自定义字符流读取缓冲区对象bufr=new MyBufferedReader(new FileReader("E:\\heima\\BufferDemo.txt"));String line=null;while ((line=bufr.myReadLine())!=null){System.out.println(line);}}catch (IOException e){throw new RuntimeException("文件读取失败");}finally{	if (bufr!=null){try{bufr.myClose();}catch (IOException ex1){throw new RuntimeException("读取关闭失败");}}}}}//自定义字符读取流缓冲区
class MyBufferedReader
{private FileReader r;MyBufferedReader(FileReader r){this.r=r;}//定义读取行功能public String myReadLine() 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;}//定义关闭字符读取流功能public void myClose() throws IOException{r.close();}
}

   文件BufferedDemo.txt内容截图如下: 

  

   程序运行后的结果如下图:

 2.自定义字节流缓冲区:

  示例14:自定义字节流读取缓冲区。

import java.io.*;
class  MyBufferedInputStreamDemo
{public static void main(String[] args) {MyBufferedInputStream mybufis=null;BufferedOutputStream bufos=null;try{//创建自定义字节流写入缓冲区对象bufos=new BufferedOutputStream(new FileOutputStream("E:\\heima\\2.mp3"));//创建字节流读取缓冲区对象mybufis=new MyBufferedInputStream(new FileInputStream("E:\\heima\\1.mp3"));//			byte[] by=new byte[1024];int num=0;while ((num=mybufis.myRead())!=-1){//num是int类型,那么写入文件的字节数是不是4个字节呢?不是的,因为write方法//里对int类型做了强制类型转换,只保留了int类型的最后八位。所以还是只写入了//一个字节的数据。bufos.write(num);}}catch (IOException e){throw new RuntimeException("文件复制失败");}finally{	if (mybufis!=null){try{mybufis.myClose();}catch (IOException ex1){throw new RuntimeException("读取关闭失败");}}if (bufos!=null){try{bufos.close();}catch (IOException ex1){throw new RuntimeException("写入关闭失败");}}}}
}//自定义字节流读取缓冲区
class MyBufferedInputStream 
{private InputStream is;private byte[] by=new byte[1024*4];private int pos=0,count=0;MyBufferedInputStream(InputStream is){this.is=is;}//一次读一个字节,从缓冲区(字节数组)获取public int myRead() throws IOException{if (count==0){//通过is对象获取硬盘上的数据,并存储在by数组中count=is.read(by);pos=0;}if (count>0){int b=by[pos];pos++;count--;//为什么要与上255?因为字节流的字节数据都是二进制,如果读取的一个字节数据为1111-1111,即十进制为-1,//那么提升为int类型时,还是-1,此时这个字节数据的-1就会和循环读取的判断条件-1一致,导致循环停止,复//制文件失败,所以需与上255,从而保证int类型的最后一个八位不变,前面三个八位都为0,此时就不会出现返回//-1的现象。这也是返回类型不是byte而是int的原因所在。return b&255;}return -1;}public void myClose() throws IOException{is.close();}
}
  程序运行后的截图如下:




这篇关于黑马程序员——Java IO流(一)之IO流概述、字符流、字节流等的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/814987

相关文章

JVM 的类初始化机制

前言 当你在 Java 程序中new对象时,有没有考虑过 JVM 是如何把静态的字节码(byte code)转化为运行时对象的呢,这个问题看似简单,但清楚的同学相信也不会太多,这篇文章首先介绍 JVM 类初始化的机制,然后给出几个易出错的实例来分析,帮助大家更好理解这个知识点。 JVM 将字节码转化为运行时对象分为三个阶段,分别是:loading 、Linking、initialization

Spring Security 基于表达式的权限控制

前言 spring security 3.0已经可以使用spring el表达式来控制授权,允许在表达式中使用复杂的布尔逻辑来控制访问的权限。 常见的表达式 Spring Security可用表达式对象的基类是SecurityExpressionRoot。 表达式描述hasRole([role])用户拥有制定的角色时返回true (Spring security默认会带有ROLE_前缀),去

浅析Spring Security认证过程

类图 为了方便理解Spring Security认证流程,特意画了如下的类图,包含相关的核心认证类 概述 核心验证器 AuthenticationManager 该对象提供了认证方法的入口,接收一个Authentiaton对象作为参数; public interface AuthenticationManager {Authentication authenticate(Authenti

Spring Security--Architecture Overview

1 核心组件 这一节主要介绍一些在Spring Security中常见且核心的Java类,它们之间的依赖,构建起了整个框架。想要理解整个架构,最起码得对这些类眼熟。 1.1 SecurityContextHolder SecurityContextHolder用于存储安全上下文(security context)的信息。当前操作的用户是谁,该用户是否已经被认证,他拥有哪些角色权限…这些都被保

Spring Security基于数据库验证流程详解

Spring Security 校验流程图 相关解释说明(认真看哦) AbstractAuthenticationProcessingFilter 抽象类 /*** 调用 #requiresAuthentication(HttpServletRequest, HttpServletResponse) 决定是否需要进行验证操作。* 如果需要验证,则会调用 #attemptAuthentica

Spring Security 从入门到进阶系列教程

Spring Security 入门系列 《保护 Web 应用的安全》 《Spring-Security-入门(一):登录与退出》 《Spring-Security-入门(二):基于数据库验证》 《Spring-Security-入门(三):密码加密》 《Spring-Security-入门(四):自定义-Filter》 《Spring-Security-入门(五):在 Sprin

Java架构师知识体认识

源码分析 常用设计模式 Proxy代理模式Factory工厂模式Singleton单例模式Delegate委派模式Strategy策略模式Prototype原型模式Template模板模式 Spring5 beans 接口实例化代理Bean操作 Context Ioc容器设计原理及高级特性Aop设计原理Factorybean与Beanfactory Transaction 声明式事物

水位雨量在线监测系统概述及应用介绍

在当今社会,随着科技的飞速发展,各种智能监测系统已成为保障公共安全、促进资源管理和环境保护的重要工具。其中,水位雨量在线监测系统作为自然灾害预警、水资源管理及水利工程运行的关键技术,其重要性不言而喻。 一、水位雨量在线监测系统的基本原理 水位雨量在线监测系统主要由数据采集单元、数据传输网络、数据处理中心及用户终端四大部分构成,形成了一个完整的闭环系统。 数据采集单元:这是系统的“眼睛”,

Java进阶13讲__第12讲_1/2

多线程、线程池 1.  线程概念 1.1  什么是线程 1.2  线程的好处 2.   创建线程的三种方式 注意事项 2.1  继承Thread类 2.1.1 认识  2.1.2  编码实现  package cn.hdc.oop10.Thread;import org.slf4j.Logger;import org.slf4j.LoggerFactory

字节面试 | 如何测试RocketMQ、RocketMQ?

字节面试:RocketMQ是怎么测试的呢? 答: 首先保证消息的消费正确、设计逆向用例,在验证消息内容为空等情况时的消费正确性; 推送大批量MQ,通过Admin控制台查看MQ消费的情况,是否出现消费假死、TPS是否正常等等问题。(上述都是临场发挥,但是RocketMQ真正的测试点,还真的需要探讨) 01 先了解RocketMQ 作为测试也是要简单了解RocketMQ。简单来说,就是一个分