黑马程序员——Java语言基础——07.IO流(1)字符流和字节流

------- android培训java培训、期待与您交流! ----------

本节考点:
一、IO流概述,常用IO流有哪些
二、复制java文件,并改后缀为.txt
三、读取键盘录入

1-1 IO流概述

IO流:用于处理设备上数据。

(1)用来处理设备(硬盘,控制台,内存)间的数据。
(2)java中对数据的操作都是通过流的方式。
(3)java用于操作流的类都在io包中。
(4)按照流操作的数据的类型不同:分为字节流和字符流。字符流是为了方便中文的操作而来的。
(5)按照流的流向不同分为:输入流,输出流

流也进行分类:

1:输入流(读)和输出流(写)

2:因为处理的数据不同,分为字节流和字符流。 

 字节流:处理字节数据的流对象。设备上的数据无论是图片或者dvd,文字,它们都以二进制存储的。二进制的最终都是以一个8位为数据单元进行体现,所以计算机中的最小数据单元就是字节。意味着,字节流可以处理设备上的所有数据,所以字节流一样可以处理字符数据。

 那么为什么要有字符流呢?因为字符每个国家都不一样,所以涉及到了字符编码问题,那么GBK编码的中文用unicode编码解析是有问题的,所以需要获取中文字节数据的同时+ 指定的编码表才可以解析正确数据。为了方便于文字的解析,所以将字节流和编码表封装成对象,这个对象就是字符流。只要操作字符数据,优先考虑使用字符流体系。

注意:流的操作只有两种:读和写。

 IO流基类

输出字节流:OutputStream
输入字节流:InputStream

输出字符流:Writer

输入字符流:Reader

在这四个系统中,它们的子类,都有一个共性特点:子类名后缀都是父类名,前缀名都是这个子类的功能名称

1-2 字符流

输出字符流:Writer:字符写入流的抽象
|--->FileWriter:字符写入流
|--->BufferedWriter:字符写入流缓冲区
|--->OutputStreamWriter:字符通向字节的转换流(涉及键盘录入时用)
|--->OutputStreamWriter:打印流,可处理各种类型的数据
输入字符流:Reader: 字符读取流的抽象类
|--->FileReader:字符读取流
|--->LineNumberReader:跟踪行号的缓冲字符读取流
|--->BufferedReader:字符读取流缓冲区
|--->InputStreamReader:字节通向字符的转换流(涉及键盘录入时用)

1-2-1 FileWriter

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

<span style="font-family:Microsoft YaHei;">class  FileWriterDemo
{
	public static void main(String[] args) throws IOException
	{
		//创建一个FileWriter对象。该对象一被初始化就必须要明确被操作的文件。
		//而且该文件会被创建到指定目录下。如果该目录下已有同名文件,将被覆盖。
		//其实该步就是在明确数据要存放的目的地。
		FileWriter fw = new FileWriter("demo.txt");

		//调用write方法,将字符串写入到流中。
		fw.write("abcde");

		//刷新流对象中的缓冲中的数据。
		//将数据刷到目的地中。
		//fw.flush();


		//关闭流资源,但是关闭之前会刷新一次内部的缓冲中的数据。
		//将数据刷到目的地中。
		//和flush区别:flush刷新后,流可以继续使用,close刷新后,会将流关闭。
		fw.close();
	}
}</span>

close()和flush()的区别:

flush():将缓冲区的数据刷到目的地中后,流可以使用。

close():将缓冲区的数据刷到目的地中后,流就关闭了,该方法主要用于结束调用的底层资源。这个动作一定做。

FileWriter写入数据的细节:

1:window中的换行符:\r\n两个符号组成。 linux:\n。

2:续写数据,只要在构造函数中传入新的参数true。

3:目录分割符:window \\

文件续写:

<span style="font-family:Microsoft YaHei;">/*
演示对已有文件的数据续写。
*/
import java.io.*;
class  FileWriterDemo3
{
	public static void main(String[] args) throws IOException
	{

		//传递一个true参数,代表不覆盖已有的文件。并在已有文件的末尾处进行数据续写。
		FileWriter fw = new FileWriter("demo.txt",true);

		fw.write("nihao\r\nxiexie");

		fw.close();
	}
}</span>

1-2-2 异常处理

只要涉及到IO就会有异常,io异常的处理方式:io一定要写finally,因为IO调用了系统底层资源,必须在finally中关闭资源

IO异常处理示例:

<span style="font-family:Microsoft YaHei;">/*
IO异常的处理方式。
*/
import java.io.*;

class  FileWriterDemo2
{
	public static void main(String[] args) 
	{
		FileWriter fw = null;//在成员位置初始化,否则后面无法调用该变量
		try
		{
			fw = new FileWriter("demo.txt");
			fw.write("abcdefg");

		}
		catch (IOException e)
		{
			System.out.println("catch:"+e.toString());
		}
		finally
		{
			try
			{
				if(fw!=null)
					fw.close();				
			}
			catch (IOException e)
			{
				System.out.println(e.toString());
			}
			
		}		
	}
}</span>

1-2-3 FileReader

第一种读取方式:一次读一个字符,返回-1表示已到末尾。

<span style="font-family:Microsoft YaHei;">class  FileReaderDemo
{
	public static void main(String[] args) throws IOException
	{
		//创建一个文件读取流对象,和指定名称的文件相关联。
		//要保证该文件是已经存在的,如果不存在,会发生异常FileNotFoundException
		FileReader fr = new FileReader("demo.txt");

		//调用读取流对象的read方法。
		//read():一次读一个字符。而且会自动往下读。
		
		int ch = 0;

		while((ch=fr.read())!=-1)
		{
			System.out.println(ch);
		}

		fr.close();


	}
}</span>

第二种方式:将数据读入数组,再一次写出,比较高效

<span style="font-family:Microsoft YaHei;">/*
第二种方式:通过字符数组进行读取。
*/

import java.io.*;

class FileReaderDemo2 
{
	public static void main(String[] args) throws IOException
	{
		FileReader fr = new FileReader("demo.txt");
		
		//定义一个字符数组。用于存储读到字符。
		//该read(char[])返回的是读到字符个数。
		char[] buf = new char[1024];

		int num = 0;
		while((num=fr.read(buf))!=-1)
		{
			System.out.println(new String(buf,0,num));
		}
		fr.close();
	}
}</span>
练习:将C盘一个文本文件复制到D盘。
复制的原理:
其实就是将C盘下的文件数据存储到D盘的一个文件中。
步骤:
1,在D盘创建一个文件。用于存储C盘文件中的数据。
2,定义读取流和C盘文件关联。
3,通过不断的读写完成数据存储。
4,关闭资源

<span style="font-family:Microsoft YaHei;">class CopyText 
{
	public static void main(String[] args) 
	{
		FileWriter fw = null;
		FileReader fr = null;

		try
		{
			fw = new FileWriter("D:\\FileWriterCopy.java");//关联读取和写入的路径
			fr = new FileReader("C:\\FileWriterDemo.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
		{
			try
			{
				if (fr != null)
					fr.close();//关闭两个流要分别关
				
			}
			catch (IOException e)
			{
				throw new RuntimeException("关闭读取流失败");
			}

			try
			{
				if(fw != null)
					fw.close();
			}
			catch (IOException e)
			{
				throw new RuntimeException("关闭写入流失败");
			}
		}
	}
}</span>

1-2-4 BufferedReader和BufferedWriter

<span style="font-family:Microsoft YaHei;">class BufferedReaderDemo 
{
	public static void main(String[] args) throws IOException
	{
		FileReader fr = new FileReader("Demo.txt");
		BufferedReader bufr = new BufferedReader(fr);

		//BufferedReader继承了Reader类的方法,所以可以使用reader类中的读取方式
//		char[] ch = new char[1024];
//		int len = 0;
//
//		while ((len = bufr.read(ch))!= -1)
//		{
//			System.out.println(new String(ch,0,len));
//		}

		//但是一般使用读取整行的方式readLine()
		String line = null;

		while ((line = bufr.readLine()) != null)//返回的line是String类型,就是一整行的数据
		{
			System.out.print(line);
		}
		bufr.close();
	}
}</span>
<span style="font-family:Microsoft YaHei;">/*
缓冲区的出现是为了提高流的操作效率而出现的。

所以在创建缓冲区之前,必须要先有流对象。

该缓冲区中提供了一个跨平台的换行符。
newLine();

*/
import java.io.*;

class BufferedWriterDemo
{
	public static void main(String[] args)throws IOException
	{
		FileWriter fw = new FileWriter("Demo.txt");
		BufferedWriter bufw = new BufferedWriter(fw);

		for(int i=0; i<5; i++)
		{
			bufw.write("shabi"+i);
			bufw.newLine();
			bufw.flush();
		}

		bufw.close();

	}
}</span>
练习:使用BufferedReader和BufferedWriter复制文件

<span style="font-family:Microsoft YaHei;">class CopyJavaDemo 
{
	public static void main(String[] args) 
	{
		//建立四个空引用
		FileWriter fw = null;
		FileReader fr = null;
		BufferedWriter bufw = null;
		BufferedReader bufr = null;

		try
		{
			fw = new FileWriter("CopyDemo.txt");
			fr = new FileReader("BufferedReaderDemo.java");
			bufw = new BufferedWriter(fw);
			bufr = new BufferedReader(fr);

			String line = null;

			while ((line = bufr.readLine()) != null)
			{
				bufw.write(line);
				bufw.newLine();
				bufw.flush();
			}

		}
		catch (IOException e)
		{
			throw new RuntimeException("读写失败");
		}

		finally 
		{
			try
			{
				if(bufw != null)
					bufw.close();
			}
			catch (IOException e)
			{
				throw new RuntimeException("写入流关闭失败");
			}
			try
			{
				if(bufr != null)
					bufr.close();
			}
			catch (IOException e)
			{
				throw new RuntimeException("读取流关闭失败");
			}
		}
	}
}</span>

1-3 字节流

基本操作与字符流类相同,但它不仅可以操作字符,还可以操作其他媒体文件

InputStream是表示字节输入流的所有类的超类。

     |--- FileInputStream:从文件系统中的某个文件中获得输入字节。哪些文件可用取决于主机环境。FileInputStream 用于读取诸如图像数据之类的原始字节流。要读取字符流,请考虑使用 FileReader。

     |--- FilterInputStream:包含其他一些输入流,它将这些流用作其基本数据源,它可以直接传输数据或提供一些额外的功能。

     |--- BufferedInputStream:该类实现缓冲的输入流。

     |--- ObjectInputStream:

     |--- PipedInputStream:

OutputStream此抽象类是表示输出字节流的所有类的超类。

     |--- FileOutputStream:文件输出流是用于将数据写入 File 或 FileDescriptor 的输出流。

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

     |--- BufferedOutputStream:该类实现缓冲的输出流。

     |--- PrintStream

     |--- DataOutputStream

     |--- ObjectOutputStream

     |--- PipedOutputStream

练习一:复制jpg文件
复制一个图片
思路:
1,用字节读取流对象和图片关联。
2,用字节写入流对象创建一个图片文件。用于存储获取到的图片数据。
3,通过循环读写,完成数据的存储。
4,关闭资源。

<span style="font-family:Microsoft YaHei;">class CopyImage 
{
	public static void main(String[] args) 
	{
		FileOutputStream fos = null;
		FileInputStream fis = null;

		try
		{
			fos = new FileOutputStream("E:\\2.jpg");
			fis = new FileInputStream("D:\\1.jpg");

			byte[] buf = new byte[1024];
			int len = 0;

			while ((len = fis.read(buf)) != -1)
			{
				fos.write(buf,0,len);
			}
		}
		catch (IOException e)
		{
			throw new RuntimeException("复制失败");
		}

		finally
		{
			try
			{
				if(fos != null)
					fos.close();
			}
			catch (IOException e)
			{
				throw new RuntimeException("关闭写入字节流失败");
			}
			try
			{
				if(fis != null)
					fis.close();
			}
			catch (IOException e)
			{
				throw new RuntimeException("关闭读取字节流失败");
			}
		}
		
	}
}
</span>
练习二:复制Mp3文件

<span style="font-family:Microsoft YaHei;">class CopyMp3 
{
	public static void main(String[] args) throws IOException
	{
		long start = System.currentTimeMillis();
		copy3();
		long end = System.currentTimeMillis();

		System.out.println(end-start+"ms");
	}
	
	//最快,仅用时99毫秒
	public static void copy()throws IOException
	{
		FileOutputStream fos = new FileOutputStream("D:\\Rin_copy.mp3");
		FileInputStream fis = new FileInputStream("D:\\Rin.mp3");

		BufferedOutputStream bufo = new BufferedOutputStream(fos);
		BufferedInputStream bufi = new BufferedInputStream(fis);

		byte[] buf = new byte[1024];
		int len = 0;

		while ((len = bufi.read(buf)) != -1)
		{
			bufo.write(buf,0,len);
		}

		bufo.close();
		bufi.close();
	}

	//此种也比较慢
	public static void copy3()throws IOException
	{
		FileOutputStream fos = new FileOutputStream("D:\\Rin_copy.mp3");
		FileInputStream fis = new FileInputStream("D:\\Rin.mp3");

		BufferedOutputStream bufo = new BufferedOutputStream(fos);
		MyBufferedInputStream bufi = new MyBufferedInputStream(fis);

		
		int len = 0;

		while ((len = bufi.read()) != -1)
		{
			bufo.write(len);
		}

		bufo.close();
		bufi.close();
	}
	
	//此方法不用缓冲区,但比上一种快三四倍
	public static void copy1()throws IOException
	{
		FileOutputStream fos = new FileOutputStream("D:\\Rin_copy.mp3");
		FileInputStream fis = new FileInputStream("D:\\Rin.mp3");

//		BufferedOutputStream bufo = new BufferedOutputStream(fos);
//		BufferedInputStream bufi = new BufferedInputStream(fis);

		byte[] buf = new byte[1024];
		int len = 0;

		while ((len = fis.read(buf)) != -1)
		{
			fos.write(buf,0,len);
		}

		fos.close();
		fis.close();
	}


	//该方法慢到发指
	public static void copy2()throws IOException
	{
		FileOutputStream fos = new FileOutputStream("D:\\Rin_copy.mp3");
		FileInputStream fis = new FileInputStream("D:\\Rin.mp3");

//		BufferedOutputStream bufo = new BufferedOutputStream(fos);
//		BufferedInputStream bufi = new BufferedInputStream(fis);

//		byte[] buf = new byte[1024];
		int len = 0;

		while ((len = fis.read()) != -1)
		{
			fos.write(len);
		}

		fos.close();
		fis.close();
	}
}</span>

1-4 转换流

读取键盘录入,很重要
BufferedReader bufr = new BufferedReader(new InputStreamReader(System.in));

打印到控制台:

BufferedWriter bufw = new BufferedWriter(new OutputStreamWriter(System.out));

转换流特有功能:转换流可以将字节转成字符,原因在于,将获取到的字节通过查编码表获取到指定对应字符。

转换流的最强功能就是基于 字节流 + 编码表 。没有转换,没有字符流。

发现转换流有一个子类就是操作文件的字符流对象:

InputStreamReader

|--FileReader

OutputStreamWriter

|--FileWrier

想要操作文本文件,必须要进行编码转换,而编码转换动作转换流都完成了。所以操作文件的流对象只要继承自转换流就可以读取一个字符了。

但是子类有一个局限性,就是子类中使用的编码是固定的,是本机默认的编码表,对于简体中文版的系统默认码表是GBK。

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

InputStreamReader isr = new InputStreamReader(new FileInputStream("a.txt"),"gbk");

以上两句代码功能一致,

如果仅仅使用平台默认码表,就使用FileReader fr = new FileReader("a.txt"); //因为简化。

如果需要制定码表,必须用转换流。

转换流 = 字节流+编码表。

转换流的子类File = 字节流 + 默认编码表。

凡是操作设备上的文本数据,涉及编码转换,必须使用转换流。

1-5 装饰设计模式

IO中的使用到了一个设计模式:装饰设计模式。

装饰设计模式解决:对一组类进行功能的增强。

包装:写一个类(包装类)对被包装对象进行包装;

 * 1、包装类和被包装对象要实现同样的接口;

 * 2、包装类要持有一个被包装对象;

 * 3、包装类在实现接口时,大部分方法是靠调用被包装对象来实现的,对于需要修改的方法我们自己实现;

示例:自己编写BufferedReader

<span style="font-family:Microsoft YaHei;">class MyBufferedReaderDemo 
{
	public static void main(String[] args) throws IOException
	{
		FileReader fr = new FileReader("CopyDemo.txt");
		MyBufferedReader my = new MyBufferedReader(fr);
		
		String line = null;
		while ((line = my.myReadLine())!=null)
		{
			System.out.println(line);
		}
	}
}

class MyBufferedReader extends Reader
{
	private Reader r;
	MyBufferedReader(Reader 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;
			if(ch == '\n')
				return sb.toString();
			sb.append((char)ch);
		}

		if (sb.length() != 0)
		{
			return sb.toString();
		}
		return null;
	}

	public void close()throws IOException
	{
		r.close();
	}

	public int read(char[] cbuf, int off, int len) throws IOException
	{
		return r.read(cbuf,off,len);
	}
}</span>

1-6 流的操作规律:

流对象:其实很简单,就是读取和写入。但是因为功能的不同,流的体系中提供N多的对象。

那么开始时,到底该用哪个对象更为合适呢?这就需要明确流的操作规律

1,源:键盘录入。
目的:控制台。
BufferedReader bufr = new BufferedReader(new InputStreamReader(System.in));
BufferedWriter bufw = new BufferedWriter(new OutputStreamWriter(System.out));
2,需求:想把键盘录入的数据存储到一个文件中。
源:键盘。
目的:文件。
BufferedReader bufr = new BufferedReader(new InputStreamReader(System.in));
BufferedWriter bufw = new BufferedWriter(new OutputStreamWriter(new FileOutputStream("Demo.txt")));
3,需求:想要将一个文件的数据打印在控制台上。
源:文件。
目的:控制台。
BufferedReader bufr = new BufferedReader(new InputStreamReader(new FileInputStream("Demo.txt")));
BufferedWriter bufw = new BufferedWriter(new OutputStreamWriter(System.out));
流操作的基本规律:
最痛苦的就是流对象有很多,不知道该用哪一个。
通过三个明确来完成。
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);
练习:将一个图片文件中数据存储到另一个文件中。复制文件。要按照以上格式自己完成三个明确。
1.读取流 InputStream Reader
2.不是文本文档
InputStream
同时是从硬盘中,所以用FileInputStream
3.是否需要提高效率
是,用BufferedInputStream
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);
所以,记住。转换流什么使用。字符和字节之间的桥梁,通常,涉及到字符编码转换时,
需要用到转换流。

1-7 小知识点

<span style="font-family:Microsoft YaHei;">class SetOutDemo 
{
	public static void main(String[] args) throws IOException
	{
		//通过setIn和setOut可以改变标准输入输出的途径
		System.setOut(new PrintStream("Demo.txt"));
		//System.setIn(new FileInputStream("Demo.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();
		bufw.close();
	}
}</span>
编写异常日志:

<span style="font-family:Microsoft YaHei;">class ExceptionLog 
{
	public static void main(String[] args)throws IOException 
	{
		try
		{
			int[] arr = new int[2];
			System.out.println(arr[3]);
		}
		catch (Exception e)
		{
			
			try
			{
				Date d = new Date();
				SimpleDateFormat date = new SimpleDateFormat("yyyy年MM月dd日 HH:mm");
				String s = date.format(d);

				PrintStream ps = new PrintStream("Exception.log");
				ps.println(s);
				System.setOut(ps);

				
			}
			catch (IOException ex)
			{
				throw new RuntimeException("日志文件创建失败");
			}
			e.printStackTrace(System.out);
		}
	}
}</span>

------- android培训java培训、期待与您交流! ----------


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值