黑马程序员—java基础学习--IO ( Input Output )流(一)

IO流是Javase中比较重要的一部分,也是我们所编写的代码和文件相关联的一种体现,能够熟练的理解和运用IO流技术,可以让我们的编程能力有质一般的飞跃。今天,一起走进IO流的世界,体验一下IO的神奇色彩。

*IO流

Java对数据的操作是通过流的方式,而IO流是用来处理设备之间的数据传输,java用于操作流对象都在IO包中,流对象操作数据分为两种:字节流和字符流,而流按照流向分为输入流和输出流。

一、IO流常用的基类:

字节流的抽象基类:InputStream,OutputStream

字符流的抽象基类:Reader,Writer

注:由这四个类派生出来的子类名称都是以其父类名作为子类名的后缀。如InputStream的子类FileInputStream,Reader的子类FileReader。

二、字符流:

既然IO流是用于操作数据的,那么数据的最常见的体现形式是文件。那么先一文件演示为主。

字符流的几个常用类:

FileReader   FileWriter 

BufferedReader   BufferedWriter

需求:在硬盘上,创建一个文件并写入一些文字数据。

思路:找到一个专门用于操作文件的Writer子类对象,FileWriter,后缀名是父类名,前缀名是该流对象的功能。

示例代码如下:

import java.io.*;

class  FileWriterDemo
{
	public static void main(String[] args) throws IOException
	{
		//创建一个FileWriter对象,该对象一被初始化就必须要有被操作的文件。
		//而且该文件会被创建下指定的目录下。如果该目录下已有同名文件,将覆盖原文件。
		//其实该布就是在明确数据要存放的目的地。
		FileWriter fw = new FileWriter("d:\\1.txt");
		
		//调用write方法将字符串写入到流中。
		fw.write("abcde");

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

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

	//	fw.write("fghijk");	//流关闭以后将不再能够写入,但是刷新之后还可以写入。
	}
}

三、IO异常的处理:

IO异常的处理并非是我们日常中的throws抛出方法,因为IO有专业的try catch处理方式,其方式如下:

import java.io.*;

class  FileWriterDemo
{
	public static void main(String[] args) throws IOException
	{
		FileWriter fw = null;	//此处创建引用为null是为让finally代码块中使用到fw对象
		try
		{
			fw = new FileWriter("k:\\demo.txt");	//将fw进行文件的关联
			fw.write("abcdefg");			//写入相应的数据
		}
		catch (IOException e)
		{
			System.out.println(e.toString());
		}
		finally			//关流是必要的操作	所以定义在finally代码块中
		{
			try
			{
				if(fw!=null)		//判断是否为null是为了增强程序的健壮性。
					fw.close();		//关流动作是会发生异常的,所以需要处理
			}
			catch (IOException e)
			{
				System.out.println(e.toString());
			}	
		}

	}
}

四、FileReader的两种读取方式及原理:

import java.io.*;

class  FileReaderDemo
{
	public static void main(String[] args) throws IOException
	{
		//创建一个文件读取流对象,和指定名称的文件相关联。
		//要保证该文件是已经存在的,如果不存在,会发生异常FileNotFoundException
		FileReader fr = new FileReader("d:\\a.txt");
		
		/*
		第一种方式,通过字符读取
		//调用读取流对象的read方法,read方法一次读一个字符,并且自动往下读
		int i = 0;
		while((i = fr.read())!=-1){
			System.out.println((char)i);
		}
		*/
		
		//第二种方式:通过字符数组进行读取
		//该read ( char [ ] )返回的是读到字符的个数
		char[] buf = new char[1024];
		int num = 0;
		while((num = fr.read(buf))!=-1){
			System.out.println(buf,0,num);
		}
		//关闭流
		fr.close();
	}
}

五、字符流的缓冲区

缓冲区的出现是为了提高流的操作效率而出现的,所以在创建缓冲区之前,必须要现有流对象。

BufferedReader该缓冲区中提供了一个跨平台的换行符,就是newLine( ) 方法

BufferedWriter 该缓冲区提供了一次读取一行的方法,方便于对文本数据的获取。 readLine ( ) 方法,当返回nul时,表示读到文件末尾。需要注意的是,readLine方法返回的是回车符之前的数据内容,并不返回回车符,需要自行加入回车符。

使用字符流缓冲区进行字符文件的复制代码:

import java.io.*;

class CopyTextByBuf 
{
	public static void main(String[] args) 
	{
		BufferedReader bufr = null;
		BufferedWriter bufw = null;

		try
		{
			//将字符读取流对象作为参数传递给缓冲对象的构造函数。
			bufr = new BufferedReader(new FileReader("a.txt"));
			bufw = new BufferedWriter(new FileWriter("b.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 e)
				{
					throw new RuntimeException("关流失败");
				}

			if(bufw!=null)
				try
				{
					bufw.close();
				}
				catch (IOException e)
				{
					throw new RuntimeException("关流失败");
				}
		}
	}
}

六、装饰设计模式

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

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

装饰设计模式的通俗理解代码如下:

class Person
{
	private void eat()
	{
		System.out.println("something eat");
	}
}

//没有使得Person和SuperPerson产生相应的关系,降低耦合性
class SuperPerson
{
	private Person p;
	SuperPerson(Person p){
		this.p = p;
	}
	public void superEat()
	{
		System.out.println("something drink");
		p.eat();
		System.out.println("something somke");
	}
}
class  PersonDemo
{
	public static void main(String[] args) 
	{
		Person p = new Person();
		//p.eat();
		
		SuperPerson sp = new SuperPerson(p);
		sp.supereat();
	}
}

装饰模式的特点:

装饰模式比继承更为灵活,避免了体系的臃肿。而且降低了类与类之间的关系。

装饰类因为增强已有对象,具备的功能和已有的相同,只不过提供了增强功能,所以装饰类和被装饰类通常是都属于一个体系中的。

七:字节流

基类: InputStream   OutputStream

缓冲区: BufferedInputStream   BufferedOutputStream

使用字节流对图片的复制代码如下:

import java.io.*;

class  CopyPicTest
{
	public static void main(String[] args) 
	{
		FileInputStream fis = null;
		FileOutputStream fos = null;
		
		try
		{
			//用字节读取流对象和图片文件关联
			fis = new FileInputStream("c:\\a.jpg");
			//用字节读取流对象创建一个图片文件,用于存储获取到的图片数据
			fos = new FileOutputStream("c:\\b.jpg");

			//通过循环读写,完成数据的存储
			byte[] buf = new byte[1024];
			int len = 0;
			while((len = fis.read(buf))!=-1){
				fos.write(buf);
			}						
		}
		catch (IOException e)
		{
			throw new RuntimeException("复制文件失败");
		}
		finally
		{
			try
			{
				//关闭资源
				if(fis != null)	
					fis.close();					
			}
			catch (IOException e)
			{
				throw new RuntimeException("资源关闭失败");
			}

			try
			{
				//关闭资源
				if(fos != null)	
					fos.close();					
			}
			catch (IOException e)
			{
				throw new RuntimeException("资源关闭失败");
			}
		}
	}
}
字节流缓冲区:

和字符流一样,字节流也有相应的缓冲区,使用缓冲区使得效率更为高效,下为使用字节流缓冲区复制mp3,并计算出复制mp3所使用的时间的代码:

import java.io.*;

class  CopyMP3Test
{
	public static void main(String[] args) 
	{
		long start = System.currentTimeMillis();
		copy_1();
		long end = System.currentTimeMillis();
		System.out.println((end-start)+"毫秒");
	}
	public static void copy_1(){
		BufferedInputStream bis = null;
		BufferedOutputStream bos = null;

		try
		{
			//建立缓冲区对象并关联相关的mp3文件
			bis = new BufferedInputStream(new FileInputStream("c:\\a.mp3"));
			bos = new BufferedOutputStream(new FileOutputStream("c:\\b.mp3"));
			
			//进行高效的读写动作
			int by = 0;

			while((by = bis.read())!=-1){
				bos.write(by);
			}

		}
		catch (IOException e)
		{
			throw new RuntimeException("复制失败");
		}
		finally
		{
			//关闭流的动作
			if(bis!=null)
				try
				{
					bis.close();
				}
				catch (IOException e)
				{
					throw new RuntimeException("关闭失败");
				}

			if(bos!=null)
				try
				{
					bos.close();
				}
				catch (IOException e)
				{
					throw new RuntimeException("关闭失败");
				}

		}	
	}
}

八、转换流:

转换流:
InputStreamReader:字节到字符的桥梁,解码
OutputStreamWriter:字符到字节的桥梁,编码

例题:通过转换流将键盘的录入数据进行在控制台上的打印动作,代码如下:

import java.io.*;

class  TransStreamDemo
{
	public static void main(String[] args) throws IOException
	{
		//获取键盘录入
		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();
	}	
}
上述例子中,只要控制好源和目的,就能将数据进行相应的操作,如果控制目的为文件,即为将键盘录入写入到文件中去。

九、流的操作规律:

开发中最痛苦的事情莫过于流对象很多,但是不知道用哪一个。

通过两个明确来完成

1,明确源和目的

源:输入流。 InputStream  Reader

目的:输出流。OutputStream Writer

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

是:字符流。

不是:字节流。

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

通过设备来进行区分:

源设备:

硬盘:File

键盘:System.in

内存:数组

网络:socket流

目的设备:

硬盘:File

控制台:System.out

内存:数组

网络:Socket流

4,是否需要其他额外功能。

1,需要高效:

是,添加缓冲区Buffered

2,需要转换

是,使用转换流


实际需求:

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体系中的转换流。InputStreamReader

InputStreamReader isr = new InputStream(System.in);

需要提高效率么?需要,使用BufferedReader

BufferedReader bufr = new BufferedReader(isr);

目的:OutputStream Writer

是否为纯文本? 是,使用Writer

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

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

FileWriter使用的是默认的编码表,GBK,不满足下述要求

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

存储时,需要加入指定的编码表,而指定的编码表只有转换流可以指定,所以需要使用OutputStreamWriter

而该转换流需要接受一个字节输出流,而且还可以操作文件的字节输出流。 FileOutputStream,所以代码应如下:

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

需要提高效率么?需要

BufferedWriter bufw = new BufferedWriter(osw);

所以,需要注意的是:转换流什么时候使用,字符和字节之间的桥梁,通常涉及到字符编码转换时,需要用到转换流。


十、异常信息及日志文件的建立

如何将异常信息写入到相应的日志文件中呢?要注意的细节很多,同时也运用到了IO流技术。那么异常信息的书写方式如下:

import java.io.*;
import java.util.*;
import java.text.*;

class ExceptionInfo 
{
	public static void main(String[] args) 
	{
		try
		{
			int[] arr = new int[2];
			System.out.println(arr[3]);
		}
		catch (Exception e)
		{
			try
			{	
				//为日志文件信息创建添加时间
				Date d = new Date();
				SimpleDateFormat sdf = new SimpleDateFormat("YYYY-MM-dd HH:mm:ss");
				String s = sdf.format(d);
				
				PrintStream ps = new PrintStream("exception.log");
				//将时间信息添加到日志文件中
				ps.println(s);
				//将目的设置为日志文件中
				System.setOut(ps);
			}
			catch (Exception ee)
			{	
				//此处抛出日志创建未成功异常
				throw new RuntimeException("日志文件创建失败");
			}
			finally
			{
				//关流动作
				ps.close();
			}
			//将异常信息写入到日志文件中
			e.printStackTrace();
		}

	}
}



你可能感兴趣的:(黑马程序员—java基础学习--IO ( Input Output )流(一))