java_IO

1 ,IO流概述

2,输出流,输入流方法的应用

3,缓冲区的应用

4,转换流的应用

5,流操作规律

java把所有传统的流类型都放在java.io包中,用以实现输入/输出功能。
1.1,输出流和输入
->输入流:只能从中读取数据,而不能向其写入数据
->输出流:只能向其写入数据,而不能从中读取数据
java的输入流主要由InputStream和Reader作为基类,而输出流则主要由OutputStream和Writer作为基类,他们都是抽象基类,无法直接创建对象。
1.2,字节流和字符流
字节流和字符流的用法几乎一样,区别在于字节流和字符流所操作的数据单元不同。
字节流主要由InputStream和OutputStream作为基类,而字符流则主要由Reader和Writer作为基类。
字符流两个基类:融合了编码表,可以指定编码表。

2,输出流,输入流方法的应用
2.1在Reader里主要有读的3个方法。
public int read() 读取单个字符 
返回:如果已到达流的末尾,则返回 -1
public int read(char[] cbuf)  将字符读入数组
返回:读取的字符数,如果已到达流的末尾,则返回 -1
public abstract int read(char[] cbuf,int off,int len) 将字符读入数组的某一部分
返回:读取的字符数,如果已到达流的末尾,则返回 -1 

InputStream类里还有一个 available()方法,具体用法如下

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);
		System.out.println(new String(buf));

		fis.close();
	}



下面代码是对上述方法的简单使用:

class FileReadDemo 
{
	public static void main(String[] args) 
	{

	}
		
	public static void method_1()
	{
		//创建一个文件读取流对象,和指定名称的文件相关联。
		//要保证该文件是已经存在的,如果不存在,会发生异常,FileNotFoundException
		FileReader fr = new FileReader("demo.txt");
		int ch = 0;
		while((ch=fr.read())!=-1)//已到末尾返回-1
		{
			System.out.println("ch="+(char)ch);
		}
		fr.close();
	}
	
	public static void method_2()
	{
		FileReader fr = new FileReader("demo.txt");
		char[] buf = new char[1024];//定义一个字符数组,用于存储读到的字符
		int num = 0;
		while((num=fr.read(buf))!=-1)
		{											//无论buf数组里多少字符,长度都是1024
			System.out.print(new String(buf,0,num));//new String(buf,0,num) String类的构造器,可将数组封装成字符串
		}
		fr.close();
	}
}

注意:程序里打开的文件IO资源不属于内存里的资源,垃圾回收机制无法回收该资源,所以应该显式关闭文件IO资源。


2.2OutputStream和Writer也非常相似,两个流都提供了如下3个方法
->void write(int c) 写入单个字符
->public void write(byte[]/char[] buf) 将字节数组 /字符数组中的数据输出到指定流中
->public void write(byte[]/char[] buf,int off,int len)  将字节数组/字符数组中从off位置开始,长度为len的字节/字符输出到输出流中。
因为字符流直接以字符作为操作单位,所以Writer可以用字符串来代替字符数组,即以String对象作为参数。Writer里还包含如下两个方法。
void write(String str);将str字符串里包含的字符输出到指定输出流中。
void write(String str,int off,int len): 将str字符串里从off位置开始,长度为len的字符输出到指定输出流中。


下面用代码来简单说明输出流的用法:

class FileWriterDemo
{
	public static void main(String[] args) 
	{
		
		//创建一个FileWriter对象,该对象一被初始化,就必须要明确被操作的文件
		//而且该文件会被创建到指定的目录下。如果该目录下已有同名文件,将被覆盖。
		//其实该步就是在明确数据要存放的目的地
		FileWriter fw = new FileWriter("demo.txt");
		
		//传递一个true参数。代表不覆盖已有的文件,并在已有文件的末尾处进行数据续写。
		//FileWriter fw = new FileWriter("demo.txt",true);
		
		//调用writer方法,将字符串写入到流中。
		fw.write("asdasd");
		
		//刷新流对象中的缓冲中的数据。
		//将数据刷到目的地中、
		//fw.flush();
		
		
		//关闭流资源,但是关闭之前会刷新一次内部的缓冲中的数据。
		//将数据刷到目的地中
		//和flush区别;flush刷新后,流可以继续使用,close刷新后,会将流关闭。
		fw.close();
	}
}
class FileWriterDemo2
{
	public static void main(String[] args) 
	{
		FileWriter fw = null;//在代码块外创建引用才能在所有代码块中使用引用。
		try
		{
			fw = new FileWriter("demo.txt");
		
			fw.write("ddsffdhgjf");
		}
		catch (IOException e)
		{	
			System.out.println("catch:"+e.toString());
		}
		finally
		{	
			try
			{
				if(fw!=null)//如果没有判断,可能产生空指针异常
				fw.close();//也会抛出异常
			}
			catch (IOException e)
			{
				System.out.println(e.toString());
			}
		}
	}
}

输出流和输入流结合的简单应用:

将一个文本文件里的内容复制到另一个文件里。

class CopyTest
{
	public static void main(String[] args) throws IOException
	{
		
	}
	//从才盘读一个字符就往d盘写一个字符
	public static void copy_1()throws IOException
	{
		//创建目的地
		FileWriter fw = new FileWriter("RuntimeDemo_copy.txt");

		//与已有文件关联
		FileReader fr = new FileReader("RuntimeDemo.java");

		int ch = 0;

		while((ch=fr.read())!=-1)
		{
			fw.write(ch);
		}
		fw.close();
		fr.close();		
	}
	
	public static void copy_2()
	{
		FileWriter fw = null;
		FileReader fr = null;
		try
		{
			fw = new FileWriter("SystemDemo_copy.txt");
			fr = new FileReader("SystemDemo.java");

			char[] buf = new char[1024];

			int len = 0;

			while((len=fr.read(buf))!=-1)
			{
				fw.write(buf,0,len);
			}
		}
		catch (IOException e)
		{
			throw new RuntimeException("读写失败");
		}
		finally
		{
			if(fr!=null)
				try
				{
					fr.close();
				}
				catch (IOException e)
				{
				}
			if(fw!=null)
				try
				{
					fw.close();
				}
				catch (IOException e)
				{
		
				}
		}
	}
}
</pre><p><span style="font-size:18px">3,缓冲区的应用</span></p><p><span style="color:#993300">缓冲区的出现是为了提高流的操作效率而出现的。</span></p><span style="color:#993300">所以在创建缓冲区之前,必须要先有流对象。</span>BufferedWriter该缓冲区中提供了跨平台的换行符。<span style="color:#ff0000">newLine();</span><p>BufferedReader该缓冲区提供了更为高效率的读方法</p><p><span style="color:rgb(255,0,0)">readLine()</span></p><p><span style="color:#ff0000">注意:</span>当返回null时。表示读到文件末尾。readLine方法返回的时候只返回回车符之前的数据内容。并不返回回车符。</p><p></p><p><span style="font-size:14px; color:#ff0000">readLine的原理:</span></p><p><span style="white-space:pre"></span>无论是读一行,读取多个字符,其实最终都是在硬盘上一个一个读取。所以最终使用的还是read方法一次读一个的方法。</p><p><span style="white-space:pre"></span>在readLine方法里封装了一个存放字符的容器,当读到一行的时候,返回这个容器的字符。</p><p>代码实现如下:</p><p></p><p></p><p><span style="white-space:pre"></span></p><pre name="code" class="java">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;
	}

通过缓冲区来进行文本文件的复制,可以提高效率。

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_Copy.txt"));

			String line = null;

			while((line=bufr.readLine())!=null)
			{
				bufw.write(line);
				bufw.newLine();
				bufw.flush();
			}
		}
		catch (IOException e)
		{
			throw new 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("写入关闭失败");
			}
			
		}
	}
}


BufferedReader有一个子类为LineNumeberReader  表示跟踪行号的缓冲字符输入流,

里面定义了setLineNumber(int)和getLineNumber(),他们分别可用于设置和获取当前行号。


通过字节流缓冲区复制一段MP3

<pre name="code" class="java">class CopyMp3
{
	public static void main(String[] args) throws IOException
	{
		long start = System.currentTimeMillis();
		copy_2();
		long end = System.currentTimeMillis();
		System.out.println(end-start+"毫秒");
	}

	//通过字节流的缓冲区完成复制。
	public static void copy_1()throws IOException
	{
		BufferedInputStream bufis = new BufferedInputStream(new FileInputStream("c:\\0.mp3"));
		BufferedOutputStream bufos = new BufferedOutputStream(new FileOutputStream("c:\\1.mp3"));

		int by = 0;
		while((by=bufis.read())!=-1)//一个一个往外取
		{
			bufos.write(by);
		}

		bufos.close();
		bufis.close();
	}
	public static void copy_2()throws IOException
	{
		MyBufferedInputStream bufis = new MyBufferedInputStream(new FileInputStream("c:\\0.mp3"));
		BufferedOutputStream bufos = new BufferedOutputStream(new FileOutputStream("c:\\2.mp3"));

		int by = 0;
		while((by=bufis.myRead())!=-1)//一个一个往外取 
		{
			bufos.write(by);//<span style="font-family: Arial, Helvetica, sans-serif;">InputStream的write是将指定字节写到输出流从缓冲区里面读,</span>只写最低8位,不然最后复制的文件大于原文件4倍
		}

		bufos.close();
		bufis.myClose();
	}
}
//自定义字节缓冲区
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//为什么返回的不是byte,被提升。由4个字节存放
			//在读到8个1时为了避免和判断结束标记-1相同,可以在保留8个1的情况下,在前面补0,那就不是-1是255.在判断的时候就不会跳出while循环
	{
		//通过in对象读取硬盘上的数据,并存储 buf中。
		if(count==0)
		{
			count = in.read(buf);//count为0的时候才读取数据到缓冲区
			if(count<0)
				return -1;
			pos = 0;
			byte b = buf[pos];
	
			count--;
			pos++;
			return b&255;//在前面补0,保留最低8位
		}
		else if(count>0)//count大于0的时候,说明缓冲区还有数据,不需要读,只需要取
		{
			byte b = buf[pos];
	
			count--;
			pos++;
			return b&255;
		}
		return -1;
	}
	public void myClose()throws IOException
	{
		in.close();
	}
}



 为什么是0kb?因为读的第一个字节是-1, MP3是2进制数据,读1个字节,读了8个2进制位,连续读了8个1,是-1. 1111-1111 -->提升为一个int类型,为4个字节。那还不是-1吗?是-1的原因是因为在8个1前面补的是1导致的 那么我只要在前面补0,既可以保留原字节数据不变,又可以避免-1的出现 怎么补0呢。  1111-1111&  00000000 00000000 00000000 1111-1111  --------------------------------------read方法在做提升,write在做强转

4,转换流的应用

转换流什么时候使用,字符和字节之间的

4.1读取键盘录入System.out:对应的是标准的输出设备,控制台。System.in:对应的是标准的输入设备,键盘。

class ReadIn
 {
	public static void main(String[] args) throws IOException
	{
		InputStream in = System.in;
		int ch = 0;
		StringBuilder sb = new StringBuilder();//可变长度
		while(true)
		{		
			int ch = 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.close();
	}
}

4.2转换流

以上键盘录入一行数据并打印其大写,发现其实就是一行数据的原理。也就是readLine方法

能不能直接使用readLine方法来完成键盘录入的一行数据的读取呢?

readLine方法是字符流BufferedReader类中方法。
而键盘录入的read方法是字节流InputStream的方法。
那么能不能将字节流转成字符流在使用字符流缓冲区的readLine方法呢?通过转换流可以实现

具体用法见以下代码

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 =  new BufferedReader(isr);

		//BufferedReader bufr = new BufferedReader(new InputStreamReader(System.in));
		//键盘录入就输以上语句,
		
		//字符流通向字节流的桥梁
		OutputStream out = System.out;
		OutputStreamWriter osw = new OutputStreamWriter(out);
		BufferedWriter bufw = new BufferedWriter(osw);

		String line = null;

		while((line=bufr.readLine())!=null)
		{
			if("over".equals(line))
				break;	
			//System.out.println(line.toUpperCase());
			bufw.write(line.toUpperCase()+);//要刷新,数据才能出来。
			bufw.newLine();//换行。

			bufw.flush();
		}
		bufr.close();
	}
}

5,流操作规律

5.1通过两个明确来完成。

1,明确源和目的。
源:输入流。
目的:输出流。
2,明确操作的数据是否是纯文本。
是:用字符流。
不是:字节流
3,当体系明确后,在明确要使用哪个具体的对象。
通过设备进行区分:
源设备:内存,硬盘,键盘。
目的设备:内存,硬盘,控制台。

例子:

1,将一个文本文件中数据存储到另一个文件中,复制文件。
源:因为是源所以选择读取流,InputStream Reader
操作文本文件,所以选择Reader
设备是硬盘上的文件,所以选择FileReader

BufferedReader bufr = new BufferedReader(new FileReader("a.txt"));
目的:OutputStream Writer
  操作文本文件,所以选择Writer
  设备是硬盘上的文件,所以选择FileWriter

BufferedWriter bufw = new BufferedWriter(new FileWriter("b.txt"));
2,将键盘录入的数据保存到一个文件中。
源:InputStream Reader
操作纯文本 Reader
设备:键盘,对应对象时System.in
System.in对应的是字节流,为了操作键盘的文本数据方便,
转成字符流按照字符串操作是最方便的。
所以既然明确了Reader。那么将System.in转换成Reader。
用了Reader体系中转换流,InputStreamReader


InputStreamReader isr = new InputStreamReader(System.in);
加入缓冲区提高效率。
BufferedReader bufr = new BufferedReader(isr);


目的:OutputSteam Writer
操作存文本 Writer
设备:硬盘(一个文件)FileWriter


FileWriter fw = new FileWriter("w.txt");
加入缓冲区提高效率
BufferedWriter bufw = new BufferedWriter(fw);

扩展,想要把录入的数据按照指定的编码表(utf-8),将数据存到文件中
目的:OutputSteam Writer
操作存文本 Writer
设备:硬盘(一个文件)FileWriter

但是存储时,需要加入指定编码表,而指定的编码表只有转换流可以指定。
所以要使用的对象时OutputStreamWriter.
而该转换流对象要接收一个字节输出流,而且还可以操作的文件的字节输出流,FileOutputStream
OutputStreamWriter osw = new OutputStream(new FileOutputStream("d.txt"),"utf-8");

5.2改变标准输入输出设备

System.in默认的输入设备是键盘

System .out默认的输出设备是控制台

通过System类的setIn(InputStream)setOut(OutputStream)和改变标准输入输出设备,

比如:

System.setIn(new FileInputStream("PersonDemo.java")); 

BufferedReader bufr = new BufferedReader(new InputStreamReader(System.in));

这样标准输入设备就是一个文件。

posted on 2014-11-02 20:20  grkbeyonf  阅读(111)  评论(0编辑  收藏  举报

导航