Java IO(Input Output)流总结_JAVA_编程开发_程序员俱乐部

中国优秀的程序员网站程序员频道CXYCLUB技术地图
热搜:
更多>>
 
您所在的位置: 程序员俱乐部 > 编程开发 > JAVA > Java IO(Input Output)流总结

Java IO(Input Output)流总结

 2013/6/19 11:47:36  wangjie2013  程序员俱乐部  我要评论(0)
  • 摘要:Lastmodified:2013-06-1308:01:44***********************************************IO流总结一:IO流是处理数据之间数据传输的。Java对数据的操作是通过流的方式。Java中用于操作IO流的对象都放在java.io包中。流的分类:按照操作数据分为:字符流和字节流。按照流向分为:输入流和输出流。输入流:输出流:字符流:ReaderWriter|--BufferedReader|--BufferedWriter|-
  • 标签:总结 Java

?

? ? ? ?Last modified:2013-06-13 08:01:44

? ? ? ?***********************************************

class="p0" style="text-indent: 21.0000pt; margin-bottom: 0pt; margin-top: 0pt; line-height: 15.7500pt;">IO流总结一:

IO流是处理数据之间数据传输的。

Java对数据的操作是通过流的方式。

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

流的分类:按照操作数据分为:字符流和字节流。

??????????按照流向分为:输入流和输出流。

????????????????

?????????????????输入流:????????????????输出流:

??字符流:

Reader ? ? ? ? ? ? ? ? ? ? ? ? Writer

??|--BufferedReader ? ? ? ? ? ? |--?BufferedWriter

??|--inputStreamReader ? ? ? ? ?|--OutputStreamWriter

??????|--FileReader ? ? ? ? ? ? ? ? |--FileWriter

??字节流:

InputStream ? ? ? ? ? ? ? ? ? OutputStream

|--FileInputStream ? ? ? ? ? ? ? |--FileOutputStream

?

InputStream ? ? ? ? ? ? ? ? ? OutputStream

|--FilterInputStream ? ? ? ? ? ? |--FilterOutputStream

???|--BufferedInputStream ? ? ? ? |--BufferedOutputStream

?

在计算机中存储的都是1和0的序列。也就是二进制的有序序列,不论是文本、音乐或者是视频。

那么为什么要在流中定义字节流和字符流呢?

这个与我们的字符编码方式有关。我们都知道世界上有很多种的语言,比如:ASCII、GB2312、GBK、UTF-8和Unicode等。

如果一个文本文件使用GB2312编码的方式存储的,如果我们用Unicode的编码方式来读取,那么就会出现乱码。所以字符流是一种特殊的流,在java中就定义了专门处理字符的流对象。

?

当我们拿到一个API文档我们应该如何使用呢?

1,确定要使用的功能。

2,查阅API看有没有相关的功能类。

3,如果没有,就分析需要如何自定义一个出来,并且要使用到那些相关功能的类,这些类在API中有没有定义。

4,不论有或者没有需要自定义一个,我们都要先查阅相关功能类的根类,那么查阅一个API的时候我们要注意一些什么呢?

5,找到相关功能根类,先看一下它是一个类,还是接口,还是抽象类,如果是接口或者是抽象类我们就不能直接使用这个类,而要使用这个类的子类。

6,但是,我们不用急于先看它有哪些子类,我们先看一下它到底暴露了什么字段、构造函数和方法。

7,在查看暴露的信息时,我们要注意几点:

a.?如果是static修饰的,说明是静态的,我们不用new对象也可以直接使用。

b.?确定使用的方法返回数据的类型,是void呢、String呢、int呢、还是其他的。查找的时候就重点找返回这些类型的方法。(注意:如果我们使用的类是一个使用单例设计模式设计的,那么他就没有构造函数,我们就一般可以通过静态的getIstance()方法获取相应的对象,这时我们就要找返回值是对象类型的方法。)

8,如果在根类中找到了需要的方法,但是根类又不能创建对象,那么我们就看看,继承这个根类的子类有哪些,一般子类都继承了父类的方法,所以子类可以直接调用父类的方法,并且子类又定义了一些自身特别的方法。

9,找到需要的类创建对象,或者找到相关功能的对象自定义一个需要的类。

?

下面我们按照以上的方法来阐述IO流的学习:

字节流:

字节流的根类有:读取流(Reader)、写入流(Writer)

根类都是abstract(抽象)的,我们不能直接创建对象,但是我们可以看一看父类都定义了什么方法。

Reader:

int?read()?

??????????读取单个字符。?

int?read(char[]?cbuf)?

??????????将字符读入数组。?

abstract??int?read(char[]?cbuf,?int?off,?int?len)?

??????????将字符读入数组的某一部分。?

int??read(CharBuffer?target)?

??????????试图将字符读入指定的字符缓冲区。?

abstract??void?close()?

??????????关闭该流并释放与之关联的所有资源。?

?

FileReader:Reader的子类,可以创建对象,专门用来操作字符数据流的。

?

Writer:

void?write(char[]?cbuf)?

??????????写入字符数组。?

abstract??void?write(char[]?cbuf,?int?off,?int?len)?

??????????写入字符数组的某一部分。?

void?write(int?c)?

??????????写入单个字符。?

void?write(String?str)?

??????????写入字符串。?

void?write(String?str,?int?off,?int?len)?

??????????写入字符串的某一部分。?

abstract??void?close()?

??????????关闭此流,但要先刷新它。?

abstract??void?flush()?

??????????刷新该流的缓冲。

?

写入流(Writer)在每次调用write()方法的时候都要flush()(刷新)一次,当然Writer也不能创建对象,我们可以使用他的子类FileWriter,FileWriter是专门处理字符写入流的类。

?

FileReader?和?FileWriter?为我们提供了操作字符数据流的一般方法,其中比较高效的就是自定义一个数组,用这个数组作为临时存储区,每次读取一块(装满数组)数据,然后再将这一块数据写入相应的目的地。这样就提高了效率,如果每次读取一个字符然后写入一个字符,就很低效,是不可取的。

自定一个数组为我们提高了效率,但是每次使用都要写很多代码,所以开发者就将这个数组封装为了一个特殊的对象。那就是缓冲区!BufferedReader(字符读取缓冲区)和BufferedWriter(字符写入缓冲区)。他是用的是装饰设计模式,是再不改变原来已定义类的基础上增强类的功能。

补充:BufferedReader的子类LineNumberReader,增加了int?getLineNumber()?:获得当前行号的功能,我们可以在使用缓冲区的同时读取行号。

?

?

装饰设计模式:

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

?

?

字符流学习完了,那么如果要学习字节流就会简单很多,我们通过查阅API知道:很多方法都似曾相识。所以,总结一下就是下面几点:

A.?字节流分为:InputStream(读取流)和OutputStream(写入流)

B.?字节流和字符流的功能基本相同,只不过传入的参数由字符流中的字节char,变成了字节中的byte。

C.?字节流也具有缓冲区:

BufferedInputStream(字节读取缓冲区)和BufferedOutputStream(字节写入缓冲区)。方法与字符缓冲区相似。

下面自定义一个BufferedInputStream:

?

import java.io.*;

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

?

?

结论:

字节流的读一个字节的read方法为什么返回值类型不是byte,而是int。

因为有可能会读到连续8个二进制1的情况,8个二进制1对应的十进制是-1.

那么就会数据还没有读完,就结束的情况。因为我们判断读取结束是通过结尾标记-1来确定的。

所以,为了避免这种情况将读到的字节进行int类型的提升。

并在保留原字节数据的情况前面了补了24个0,变成了int类型的数值。

而在写入数据时,只写该int类型数据的最低8位。

?

?

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

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

来模拟一下BufferedReader

?

import java.io.*;
class MyBufferedReader extends Reader
{
	
	private Reader r;
	MyBufferedReader(Reader 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;		
	}

	/*
	覆盖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();
	}
}

class  MyBufferedReaderDemo
{
	public static 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();
	}
}

import java.io.*;

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

/*
class MyLineNumberReader 
{
	private Reader r;
	private int lineNumber;
	MyLineNumberReader(Reader r)
	{
		this.r = r;
	}

	public String myReadLine()throws IOException
	{

		lineNumber++;
		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 setLineNumber(int lineNumber)
	{
		this.lineNumber = lineNumber;
	}
	public int getLineNumber()
	{
		return lineNumber;
	}

	public void myClose()throws IOException
	{
		r.close();
	}
}
*/
class  MyLineNumberReaderDemo
{
	public static void main(String[] args) throws IOException
	{
		FileReader fr = new FileReader("copyTextByBuf.java");

		MyLineNumberReader mylnr = new MyLineNumberReader(fr);

		String line = null;
		mylnr.setLineNumber(100);
		while((line=mylnr.myReadLine())!=null)
		{
			System.out.println(mylnr.getLineNumber()+"::"+line);
		}

		mylnr.myClose();
	}
}

?

?

?

io流有很多实例,那么在实际操作中如何确定使用哪个io流的实例呢?

?1,

源:键盘录入。

目的:控制台。

?

2,需求:想把键盘录入的数据存储到一个文件中。

源:键盘。

目的:文件。

?

3,需求:想要将一个文件的数据打印在控制台上。

源:文件。

目的:控制台。

?

流操作的基本规律:

最痛苦的就是流对象有很多,不知道该用哪一个。

?

通过三个明确来完成。

?

1,明确源和目的。

源:输入流。InputStream??Reader

目的:输出流。OutputStream??Writer

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

是:字符流。

不是:字节流。

?

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

通过设备来进行区分:

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

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

?

?

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

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

是不是操作文本文件。

是!这时就可以选择Reader

这样体系就明确了。

?

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

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

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

?

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

?

?

FileReader?fr?=?new?FileReader("a.txt");

BufferedReader?bufr?=?new?BufferedReader(fr);

?

?

目的:OutputStream?Writer

是否是纯文本。

是!Writer

设备:硬盘,一个文件。

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

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

?

FileWriter?fw?=?new?FileWriter("b.txt");

BufferedWriter?bufw?=?new?BufferedWriter(fw);

?

?

练习:将一个图片文件中数据存储到另一个文件中。复制文件。要按照以上格式自己完成三个明确。

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

?

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

这个需求中有源和目的都存在。

那么分别分析

源:InputStream?Reader

是不是纯文本?是!Reader

?

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

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

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

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

用了Reader体系中转换流,InputStreamReader

?

InputStreamReader?isr?=?new?InputStreamReader(System.in);

?

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

BufferedReader?bufr?=?new?BufferedReader(isr);

?

目的:OutputStream??Writer

是否是存文本?是!Writer

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

FileWriter?fw?=?new?FileWriter("c.txt");

需要提高效率吗?需要。

BufferedWriter?bufw?=?new?BufferedWriter(fw);

?

**************

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

?

目的:OutputStream??Writer

是否是存文本?是!Writer

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

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

?

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

所以要使用的对象是OutputStreamWriter

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

?

OutputStreamWriter?osw?=?

new?OutputStreamWriter(new?FileOutputStream("d.txt"),"UTF-8");

?

需要高效吗?需要。

BufferedWriter?bufw?=?new?BufferedWriter(osw);

?

所以,记住。转换流什么使用。字符和字节之间的桥梁,通常,涉及到字符编码转换时,

需要用到转换流。

?

练习:将一个文本数据打印在控制台上。要按照以上格式自己完成三个明确。

class  TransStreamDemo2
{
	public static void main(String[] args) throws IOException
	{
		System.setIn(new FileInputStream("PersonDemo.java"));
		System.setOut(new PrintStream("zzz.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();
	}
}

?

?

?

?

?

?

?

发表评论
用户名: 匿名