黑马程序员-javaIO总结

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


本文是在整理、综合以下几篇文章后得到的,

1、J​A​V​A​ ​I​O​详​解​,​包​括​讲​解​I​O​中​各​种​流​及​其​用​法

2、java中的io系统详解



一、IO流概述:

javaIO流用于处理设备之间的数据传输;

java对数据的操作是通过流的方式;

java用于操作流的对象都在IO包中;


二、IO流分类:

1、根据处理的数据类型不同:字节流和字符流

2、根据流向不同:输入流和输出流

字符流的由来:

因为文件编码的不同,而有了对字符进行高效操作的字符流对象。

原理:其实就是基于字节流读取字节时,去查了指定的码表。

字节流和字符流的区别:

1)字节流读取的时候,读到一个字节就返回一个字节;字符流使用了字节流读到一个或多个字节(中文对应的字节数是两个,在UTF-8码表中是3个字节)时,先去查指定的编码表,将查到的字符返回。

2)字节流可以处理所有类型数据,如图片、MP3、avi;而字符流只能处理字符数据。

结论:只要是处理纯文本数据,就要优先考虑使用字符流,除此之外都用字节流。

IO流对象继承关系:


其他常用的与流有关的对象:

名称对应的对象
文件类File
属性类Properties
打印流PrintStream、PrintWriter
管道流PipedInputStream、PipedOutputStream
序列流SequenceInputStream
对象序列化流ObjectInputStream、ObjectOutputStream
随机存取文件类RandomAccessFile
基本数据流DataInputStream、DataOutputStream
字节数组流ByteArrayInputStream、ByteArrayOutputStream
字符数组流CharArrayReader、CharArrayWriter
字符串流StringReader、StringWriter


三、字符流                                                                       

字符流继承体系:

|--Reader
    |--BufferedReader

    |--InputStreamReader

        |--FileReader

|--Writer

    |--BufferedWriter

    |--OutputStreamWriter

        |--FileWriter

字符流常见方法:

Reader中常见的方法:

1) int read():读取一个字符。返回的是读到的那个字符。如果读到流的末尾,返回-1。

2))int read(char[] cbuf):将读到的字符存入指定的数组中,返回的是读到的字符个数,也就是往数组里装的元素的个数。如果读到流的末尾,返回-1。

3) void close():读取字符其实用的是window系统的功能,就希望使用完毕后,进行资源的释放。

Writer中常见的方法:

1) void write(int ch):将一个字符写入到流中。

2) void write(char[] cbuf):将一个字符数组写入到流中。

3) void write(String str):将一个字符串写入到流中。

4) void flush():刷新流,将流中的数据刷新到目的地中,流还存在。

5) void close():关闭资源,在关闭前会先调用flush(),将流中的数据刷新到目的地,然后关闭流。

FileWriter/FileReader:

Reader与Writer都是抽象类,不能建立对象。接下来,我们将介绍它们的子类。既然IO流是用于操作数据的,而数据最常见的体现形式是:文件。因此,我们先以操作文件的流来演示。

FileWriter:

该类没有特别的方法,只有自己的构造函数。

特点:

1)用于处理文本文件。2)该类中有默认的编码表。3)该类中有临时缓冲。

构造函数:在写入流对象初始化时,必须要有一个存储数据的目的地。

FileWriter(String fileName):

该构造函数做了什么事情:

1)调用系统资源;2)在指定位置创建了一个文件(若文件已经存在,将会被覆盖)。

FileWriter(String fileName,boolean append):

当传入的boolean类型值为true时,会在指定文件末尾处进行数据的续写

FileReader:

特点:

1)用于读取文本文件的流对象;2)用于关联文本文件。

构造函数:在读取流对象初始化的时候,必须要指定一个被读取的文件。如果该文件不存在,会发生FileNotFoundException

FileReader(String fileName);


例1:在硬盘上,创建一个文件并写入一些文字数据

/*
*例1:在硬盘上,创建一个文件并写入一些文字数据
*/
import java.io.*;
class Test
{
	public static void main(String[] args) 
	{
		//创建一个FileWriter对象。该对象初始化时要求必须明确被操作的文件。
		//该文件会被创建在指定目录下。如果该目录下已有同名文件,将被覆盖。
		FileWriter fw=new FileWriter("demo.txt");
		
		//调用wirte方法,将字符串写入流中。
		fw.write("abcd");
		
		//刷新流对象的缓冲中的数据到目的地中。
		fw.flush();
		
		//关闭流资源。但在关闭前,会刷新一次内部的缓冲中的数据到目的地中。
		//与flush()的区别:flush()刷新后,流可以继续使用;close()刷新后,会将流关闭。
		fw.close();
	}
}

对于读取或者写入流对象的构造函数,以及读写方法,还有刷新关闭功能都会抛出IOException或其子类,所以都要进行处理,或者throws抛出,或者try...catch处理。

例2:完整的IO异常处理方式。

/*
*例2:完整的异常处理方式。
*/
import java.io.*;
class Test
{
	public static void main(String[] args) 
	{
		
		FileWriter fw=null;
		try
		{
			fw=new FileWriter("demo.txt");
			fw.write("abcd");
			fw.flush();
		}
		catch (IOException e)
		{
			System.out.println(e.toString());
		}
		finally
		{
			if(fw!=null)
				try
				{
					fw.close();
				}
				catch (IOException e)
				{
					System.out.println("close:"+e.toString());
				}
		}
	}
}

另一个小细节:

当指定绝对路径时,定义目录分隔符有三种方式:

1)两个斜杠\\。new FileWriter("c:\\demo.txt"); 2)一个反斜杠/。new FileWriter("c:/demo.txt");3)File.separator。new FileWriter("c:"+File.separator+"demo.txt");


例3:读取一个已有的文本文件,并将其打印至控制台

方法一:一次读取一个字符就打印出来

/*
*例3:读取一个已有的文本文件,并将其打印至控制台
*方法一:一次读一个字符就打印出来,效率低。
*/
import java.io.*;
class Test
{
	public static void main(String[] args) 
	{
		FileReader fr=null;
		try
		{
			//创建一个文件读取流对象,和指定名称的文件相关联。要保证该文件是
			//已经存在的,否则会发生FileNotFoundException。
			fr=new FileReader("demo.txt");
			int ch=0;
			while ((ch=fr.read())!=-1)   //read()方法一次读一个字符,而且会自动往下读。
			{
				System.out.println((char)ch);
			}
		}
		catch (IOException e)
		{
			System.out.println(e.toString());
		}
		finally
		{
			if(fr!=null)
				try
				{
					fr.close();	
				}
				catch (IOException e)
				{
					System.out.println("close:"+e.toString());
				}
		}
	}
}

方法二:读一个字符就存入字符数组里,待读完1Kb后再打印。

/*
*例3:读取一个已有的文本文件,并将其打印至控制台
*方法二:读一个字符就存入字符数组里,待读完1Kb后再打印。。
*/
import java.io.*;
class Test
{
	public static void main(String[] args) 
	{
		FileReader fr=null;
		try
		{
			fr=new FileReader("demo.txt");
			char[] cbuf=new char[1024];
			int num=0;
			while ((num=fr.read(cbuf))!=-1)   //read(cbuf)方法读一个字符就存入字符数组里,待读完1Kb后再打印。
			{
				System.out.println(new String(cbuf,0,num));
			}
		}
		catch (IOException e)
		{
			System.out.println(e.toString());
		}
		finally
		{
			if(fr!=null)
				try
				{
					fr.close();	
				}
				catch (IOException e)
				{
					System.out.println("close:"+e.toString());
				}
		}
	}
}


例4:将C盘的一个文本文件复制到D盘

/*
*例4:将C盘的一个文本文件复制到D盘
*/
import java.io.*;
class Test
{
	public static void main(String[] args) 
	{
		FileReader fr=null;
		FileWriter fw=null;
		try
		{
			fr=new FileReader("c:\\demo.txt");
			fw=new FileWriter("d:\\demo_copy.txt");
			char[] cbuf=new char[1024];
			int num=0;
			while ((num=fr.read(cbuf))!=-1)   
			{
				fw.write(cbuf,0,num);
			}
		}
		catch (IOException e)
		{
			System.out.println(e.toString());
		}
		finally
		{
			if(fr!=null)
				try
				{
					fr.close();	
				}
				catch (IOException e)
				{
					System.out.println("close:"+e.toString());
				}
			if(fw!=null)
				try
				{
					fw.close();	
				}
				catch (IOException e)
				{
					System.out.println("close:"+e.toString());
				}
		}
	}
}

字符流的缓冲区:

缓冲区的出现提高了对流的操作效率。

原理:将数组进行了封装。

对应的对象:

BufferedWriter:

特有方法:newLine():跨平台的换行符。

BufferedReader:

特有方法:readLine():一次读一行,到行标记时,将行标记之前的字符数据作为字符串返回。当读到末尾时,返回null。


在使用缓冲区对象时,要明确缓冲的存在是为了增强流的功能而存在,所以在建立缓冲区对象时,要先有流对象存在。其实缓冲区内部就是在使用流对象的方法,只不过加入了数组对数据进行了临时存储,来提高操作数据的效率。

例5:通过缓冲区将C盘的一个文本文件复制到D盘

/*
*例5:通过缓冲区将C盘的一个文本文件复制到D盘
*/
import java.io.*;
class Test
{
	public static void main(String[] args) 
	{
		BufferedReader bufr=null;
		BufferedWriter bufw=null;
		try
		{
			//建立缓冲区对象必须把流对象作为参数传给缓冲区对象的构造函数
			bufr=new BufferedReader(new FileReader("c:\\demo.txt"));
			bufw=new BufferedWriter(new FileWriter("d:\\demo_copy.txt"));
			String line=null;
			while ((line=bufr.readLine())!=null)   //readLine()方法按照行的形式取出数据(不包括回车符)
			{
				bufw.write(line);
				bufw.newLine();                    //newLine()方法,换行
				bufw.flush();
			}
		}
		catch (IOException e)
		{
			System.out.println(e.toString());
		}
		finally
		{
			if(bufr!=null)
				try
				{
					bufr.close();	//关闭缓冲区,其实关闭的是被包装在内部的流对象。
				}
				catch (IOException e)
				{
					System.out.println("close:"+e.toString());
				}
			if(bufw!=null)
				try
				{
					bufw.close();	
				}
				catch (IOException e)
				{
					System.out.println("close:"+e.toString());
				}
		}
	}
}

readLine()方法原理:

其实该方法,用的还是与缓冲区相关联的流对象的read()方法。只不过,每一次读到一个字符,先不进行具体操作,而是进行临时存储。当读取到回车标记时,才将临时容器中存储的数据一次性返回。

既然明白了原理,我们也可以实现一个类似功能的方法。

例6:模拟readLine()方法

/*
*例6:模拟readLine()方法
*/
import java.io.*;
class BufferedDemo 
{
	public static void main(String[] args) 
	{
		MyBufferedReader mbufr=null;
		try
		{
			mbufr=new MyBufferedReader(new FileReader("c:\\demo.txt"));
			String line=null;
			while ((line=mbufr.myReadLine())!=null)  
			{
				System.out.println(line);
			}
		}
		catch (IOException e)
		{
			System.out.println(e.toString());
		}
		finally
		{
			if(mbufr!=null)
				try
				{
					mbufr.myClose();	
				}
				catch (IOException e)
				{
					System.out.println("close:"+e.toString());
				}
		}
	}
}
class MyBufferedReader
{
	private Reader r;
	MyBufferedReader(Reader 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);
		}
		if(sb.length()!=0)
			return sb.toString();
		return null;
	}
	public void myClose()throws IOException
	{
		r.close();
	}
}

装饰设计模式:

缓冲区基于流并增强了流的功能,这体现了一种设计模式:装饰设计模式

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

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

通常装饰类和被装饰类都属于同一个父类或者接口

//装饰设计模式的演示
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("甜点");
	}
}

装饰与继承的区别:

装饰比继承有更好的灵活性。举例来说,

Writer

    |--MediaWriter

    |--TextWriter

(注:实际JDK中无这两个类,只为了更形象的举例说明而“创建”的)

需求:想要提高对数据的操作效率,用到了缓冲技术。

方法一:通过继承

继承体系变为,

Writer

    |--MediaWriter

        |--BufferedMediaWriter

    |--TextWriter

        |--BufferedTextWriter

建立子类复写父类中的方法即可满足需求。可是,当Writer中子类对象过多,那么为了提高每一个对象的操作效率,每一个对象都有一个自己的子类Buffered。这样,虽然可以实现功能,但是继承体系变得十分臃肿。

方法二:通过装饰

每个子类都是在使用缓冲技术,则可以对缓冲技术进行描述,将需要增强的对象传给缓冲区即可。

class BufferedWriter extends Writer
{
	private Writer w;
	BufferedWriter(Writer w) //通过多态,Writer w可以引用它的子类对象
	{
		this.w=w;
	}
	...
}

继承体系变为,

Writer
    |--MediaWriter

    |--TextWriter

    |--BufferedWriter
由此看见,装饰设计模式,优化增强功能的部分,比继承要灵活许多。


LineNumberReader:

BufferedReader的子类,可以在读一行的基础上添加一个行号

例7:LineNumberReader

/*
*例7:LineNumberReader
*/
import java.io.*;
class Test
{
	public static void main(String[] args) 
	{
		LineNumberReader lnr=null;
		try
		{
			lnr=new LineNumberReader(new FileReader("c:\\demo.txt"));
			String line=null;
			lnr.setLineNumber(100);  //设定初始行号
			while ((line=lnr.readLine())!=null)  
			{
				System.out.println(lnr.getLineNumber()+":"+line);//返回行号
			}
		}
		catch (IOException e)
		{
			System.out.println(e.toString());
		}
		finally
		{
			if(lnr!=null)
				try
				{
					lnr.close();	
				}
				catch (IOException e)
				{
					System.out.println("close:"+e.toString());
				}
		}
	}
}

例8:模拟LineNumberReader

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;
	}
}

四、字节流

字节流的继承体系:

|--InputStream

    |--FileInputStream

    |--FilterInputStream

        |--BufferedInputStream

|--OutputStream

    |--FileOutputStream

    |--FilterOutputStream

        |--BufferedOutputStream


InputStream/OutputStream常见方法:

InputStream中常见的方法:
1) int read():
读取一个字节。返回的是读到的那个字节(被提升为int型)。如果读到流的末尾,返回-1。
2))int read(byte[] buf):将读到的字节存入指定的数组中,返回的是读到的字节个数,也就是往数组里装的元素的个数。如果读到流的末尾,返回-1。
3) void close():读取字符其实用的是window系统的功能,就希望使用完毕后,进行资源的释放。
OutputStream中常见的方法:
1) void write(int b):
将一个字节(被提升为int型)写入到流中。
2) void write(byte[] buf):将一个字节数组写入到流中。
3) void flush():刷新流,将流中的数据刷新到目的地中,流还存在。
4) void close():关闭资源,在关闭前会先调用flush(),将流中的数据刷新到目的地,然后关闭流。


InputStream与OutputStream都是抽象类,不能建立对象。接下来,我们将介绍它们的子类。既然IO流是用于操作数据的,而数据最常见的体现形式是:文件。因此,我们先以操作文件的流来演示。

FileOutputStream\FileInputStream:

FileOutputStream\FileInputStream与FileWriter\FileReader类似,只是后者只能操作字符数据,而前者则可以操作任何数据。字节流使用的数组是字节数组,byte[] b;字符流使用的数组是字符数组,char[] c

下面展示FileOutputStream\FileInputStream的功能,

write:

FileOutputStream fos=new FileOutputStream("fos.txt");
fos.write("abcd".getBytes());
fos.close();

Read:

FileInputStream fis=new FileInputStream("fos.txt");
int ch=0;
while ((ch=fis.read())!=-1)
{
	System.out.println((char)ch);
}
fis.close();

FileInputStream fis=new FileInputStream("fos.txt");
//int num=fis.available();	//返回读取流从文件中可读取的字节数,即文件大小
//byte[] buf=new byte[num]; //定义一个和文件一样大的缓冲区,这样不用循环。文件过大
							//文件过大时,这种方法不好,会造成内容溢出
int num=0;
byte[] buf=new byte[1024];
while ((num=fis.read(buf))!=-1)
{
	System.out.println(new String(buf));
}
fis.close();

下面演示字节流的缓冲区

例9:通过缓冲区复制mp3文件

/*
*例9:通过缓冲区复制mp3文件
*/
import java.io.*;
class  BufferedTest
{
	public static void main(String[] args) 
	{
		BufferedInputStream bufis=null;
		BufferedOutputStream bufos=null;
		try
		{
			bufis=new BufferedInputStream(new FileInputStream("c:\\123.mp3"));
			bufos=new BufferedOutputStream(new FileOutputStream("d:\\321.mp3"));
			int ch=0;
			while ((ch=bufis.read())!=-1)  //通过字节流的缓冲区完成复制
			{
				bufos.write(ch);
			}
		}
		catch (IOException e)
		{
			throw new RuntimeException();
		}
		finally
		{
			if(bufis!=null)
				try
				{
					bufis.close();
				}
				catch (IOException e)
				{
				}
			if(bufos!=null)
				try
				{
					bufos.close();
				}
				catch (IOException e)
				{
				}
		}
	}
}

自定义字节流缓冲区:

class MyBufferedInputStream
{
	private InputStream in;
	private byte[] buf=new byte[1024];
	private int pos=0,count=0;
	MyBufferedInputSteam(InputStream in)
	{
		this.in=in;
	}
	public int myRead()
	{
		if(count==0)
		{
			count=in.read(buf);
			if(count<0)
				return -1;
			pos=0;
			byte b=buf[pos];
			count--;
			pos++;
			return b&255;
		}
		if(count>0)
		{
			byte b=buf[pos];
			count--;
			pos++;
			return b&255;
		}
		return -1;		
	}
}
为什么字节流的read()方法及自定义字节流缓冲区的myRead()的返回值类型是Int,而不是byte:

因为read()方法读到末尾时返回的是-1。而在所操作的数据中,很容易出现连续多个的1的情况,而连续读到8个1就代表-1,会导致读取提前停止。所以将读到的一个字节给提升为一个int类型的数值,但是保留原字节并在剩余二进制位补0。具体操作:byte&255 or byte&0xff

对于write方法,可以一次写入一个字节,但接收的是一个int类型数值。不过只写入该int类型数值的最低一个字节(8位)。

总结:read方法对读到数据进行提升,write方法对操作的数据进行转换。


五、转换流与字符编码

1、转化流:

特点:

1)字节流和字符流之间的桥梁;2)该流对象中可以对读取到字节数据进行指定编码表的编码转换

什么时候使用转换流:

1)当字节和字符之间有转换动作时;2)当流操作的数据需要进行编码表的指定时。

具体的对象体现:

1)InputStreamReader:字节到字符的桥梁;2)OutputStreamWriter:字符到字节的桥梁。

Reader

    |--InputStreamReader

        |--FileReader

Writer

    |--OutputStreamWriter

        |--FileWriter

这两个流对象是字符流体系中的成员。它们有转换作用,本身又是字符流,所以在构造的时候,需要传入字节流对象进来。

构造函数:

InputStreamReader(InputStream is):通过该构造函数初始化,使用的是本系统默认的编码表GBK。

InputStreamReader(InputStream is,String charSet):通过该构造函数初始化,可以指定编码表。

OutputStreamReader(OutputStream os):通过该构造函数初始化,使用的是本系统默认的编码表GBK。
OutputStreamReader(OutputStream os,String charSet):通过该构造函数初始化,可以指定编码表。


转换流中的read方法,已经融入了编码表。在底层调用字节流的read方法时,将获取的一个或多个字节数据进行临时存储,并去查指定的编码表。如果编码表没有指定,则查的是默认码表。这样转换流的read方法就可以返回一个字符,比如中文。

转换流已经完成了编码转换的动作,对于直接操作文本文件的FileReader,就不用再重新定义,只要继承该转换流,获取其方法,就可以直接操作文本文件中的字符数据了。(注意,FileReader操作文本数据时,该对象使用的是默认编码表,若要使用指定编码表,必须使用转换流

例10:读取键盘录入

/*
*例9:通过缓冲区复制mp3文件
*当录入一行数据后,就将改行数据打印。
*如果录入的数据是“over”,就停止录入。
*/
import java.io.*;
class  BufferedTest
{
	public static void main(String[] args) 
	{
		//System.out、System.in都是字节流。但键盘录入的都是字符数据,
		//且录入一行就打印的行为与readLine()功能相似。然而BufferedReader是字符流
		//中的方法。为此,需要用转换流。
		BufferedReader bufr=
			new BufferedReader(new InputStreamReader(System.in));//System.in:标准输入设备,键盘
		BufferedWriter bufw=
			new BufferedWriter(new OutputStreamWriter(System.out));//System.out:标准输出设备,控制台
		String line=null;
		while ((line=bufr.readLine())!=null)
		{
			if("over".equals(line))
				break;
			bufw.write(line);
			bufw.newLine();
			bufw.flush();
		}
		bufr.close();
		bufw.close();
	}
}

2、字符编码:

编码表的由来:

计算机只能识别二进制数据,早期由来是电信号。为了方便应用计算机,让它可以识别各个国家的文字,就将各个国家的文字用数字来表示,并一一对应,形成一张表,这就是编码表。

常见的编码表:

1)ASCII:美国标准信息交换码。用一个字节的7位表示。

2)ISO8859-1:拉丁码表、欧洲码表。用一个字节的8位表示。

3)GB2312:中国的中文编码表。

4)GBK:中国的中文编码表升级版,融合了更多的中文文字符号。

5)Unicode:国际标准码,融合了多种文字。所有文字都用两个字节来表示,java语言使用的就是Unicode。

6)UTF-8:一字字节表示一个英文,三个字节表示一个中文。

……

/*
*例9:编码与解码
*/
import java.io.*;
class  Test
{
	public static void main(String[] args) 
	{
		try
		{
			writeText();
			readText();
		}
		catch (IOException e)
		{
			System.out.println(e.toString());
		}
	}
	public static void writeText()throws IOException
	{
		//编码时,指定编码表为GBK
		OutputStreamWriter osw=new OutputStreamWriter(new FileOutputStream("gbk.txt"),"GBK");
		osw.write("你好");
		osw.close();
	}
	public static void readText()throws IOException
	{
		//解码时,编码表也必须相同,否则会解码错误
		InputStreamReader isr=new InputStreamReader(new FileInputStream("gbk.txt"),"GBK");
		char[] buf=new char[1024];
		int num=isr.read(buf);
		System.out.println(new String(buf,0,num));
		isr.close();
	}
}

编码原理及解码错误时的补救措施:

注意绿色箭头的走向,它体现了解码错误的结果以及相应的补救措施。


六、IO流的一般使用原则

1、按输入输出分类:

1)输入:Reader、InputStream类型的子类。

2)输出:Writer、OutputStream类型的子类。

2、按数据格式分类:

1)二进制格式(只要不能确定是纯文本的):InputStream、OutputStream类型的子类。

2)纯文本格式(含纯英文与汉字或其他编码方式):Reader、Writer类型的子类。

3、按数据来源(去向)分类

1)是文件:FileInputStream、FileOutputStream(字节流);FileReader、FileWriter(字符流)。

2)是byte[]:ByteArrayInputStream、ByteArrayOutputStream(字节流)。

3)是char[]:CharArrayReader、CharArrayWriter(字符流)。

4)是String:StringBufferInputStream、StringBufferOuputStream (字节流);StringReader、StringWriter(字符流)。

5)网络数据流:InputStream、OutputStream(字节流);Reader、Writer(字符流)。

4、按是否要缓冲分类:

要缓冲:BufferedInputStream、BufferedOutputStream(字节流);BufferedReader、BufferedWriter(字符流)。

5、按是否格式化输出分:

要格式化输出:PrintStream、PrintWriter。

6、特殊需求:

1)从字节流到字符流的转换流:InputStreamReader、OutputStreamWriter。

2)输入、输出对象:ObjectInputStream、ObjectOutputStream。

3)线程间通信:PipeInputStream、PipeOutputStream(字节流);PipeReader、PipeWriter(字符流)。

4)合并输入:SequenceInputStream。

5)更特殊的需要:PushbackInputStream、PushbackReader、LineNumberInputStream、LineNumberReader。


原则的应用:

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

那么按照原则分析:

对于输入向,

1、按照数据格式判断?

是纯文本格式,键盘录入的都是字符。选择Reader。

2、按照数据来源判断?

键盘对应的是System.in。这是字节流,与1选择的Reader冲突,因此需要转换流InputStreamReader。

3、是否要缓冲判断?

要缓冲,选择BufferedReader。

对于输出向,

1、按照数据格式判断?

是纯文本,键盘录入的都是字符。选择Writer。

2、按照数据去向判断?

硬盘里的一个文件。选择FileWriter。

3、是否要缓冲判断?

要缓冲,选择BufferedWriter。

至此,完成IO流的选择,如下,

BufferedReader bufr=
			new BufferedReader(new InputStreamReader(System.in));
BufferedWriter bufw=
			new BufferedWriter(new FileWriter("demo.txt"));



---------------------- ASP.Net+Unity开发.Net培训、期待与您交流! ----------------------详细请查看: www.itheima.com

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值