您的位置:首页 > 职场人生

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

2014-11-19 10:04 543 查看
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.curr
4000
entTimeMillis();
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();
}

}
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: