黑马程序员——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;

			//设置行号为100
			lnr.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
	{
		//每调用一次行号自增1
		lineNumber++;
		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();
	}
}
  程序运行后的截图如下:




  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值