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

黑马程序员_Java基础——IO框架(上)(第5篇)

2014-08-08 00:51 519 查看
----------------------
ASP.Net+Unity开发、.Net培训、期待与您交流! ----------------------

一、概述

    在我看来,IO框架相比于集合框架要复杂得多,许多人肯定不这样认为,但是本人在学习的过程中确实被整得晕头转向,也许到现在还没转过弯儿来呢!那么什么是IO流呢?简单来说就是用于处理设备之间数据传输的东西。在Java中操作数据就是通过流的方式。在硬盘上,文件是数据最常见的表现形式,因而对文件的操作也是IO流最常见的用途。按照流向来分的话,流可分为输出流和输入流。按照操作的数据来分的话又可以分为字符流和字节流。

    IO流有4个基本的抽象类,分别是字符流抽象基类Reader、Writer,字节流抽象基类InputStream、OutputStream。这4个基类下又分别拥有自己的装饰类,即使增强类,BufferedReader、BufferedWriter、BufferedInputStream、BufferedOutputStream,都是以各自基类的名称为结尾,一一对应的。对于字节流和字符流的转换,Java中也专门提供了解决方案,就是Reader和Writer下的InputStreamReader、OutputStreamWriter两个转换流。其他的一些流也就不再赘述,下面图例一一明了。



二、Writer、Reader
1.FileWriter


    Java文档中对FileWriter的描述为用来写入文件字符的便捷类。确实对一开始接触IO流的初学者来说,这个类无疑是具有指导意义的。初始化一个FileWriter对象就会在硬盘上创建一个文件,会根据参数的设定确定是否覆盖原文件。关于这个类更多的也没什么可以多说的,举个例子就一目了然了。

//在硬盘上创建一个文本文件并向里面写入一些东西。

import java.io.*;
class FileWriterDemo
{
public static void main(String[] args)
{
FileWriter fw = null;
try
{
//fw = new FileWriter("D:\\demo.txt");//这种方式创建的文件会覆盖已有的文件
fw = new FileWriter("D:\\demo.txt", true);//这种初始化方式会判断,如果已有文件,则续写,没有,则创建,不会覆盖原有文件
fw.write("asdhashdasdasjkdasdaskkjhsd");
fw.flush();
}
catch (IOException e)
{
System.out.println(e.toString());
}
finally
{
try
{
if(fw != null)
fw.close();
}
catch (IOException e)
{
System.out.println(e.toString());
}
}
}
}
这段代码看上去挺复杂,其实大部分都是在对异常的处理,核心代码就是try中的三句,相当的简单。

2.FileReader

    FileReader用于对文件字符读取的便捷类。这个类可以很放方便的对文件的字符进行读取。初始化一个FileReader对象,其参数中的表示要读取文件,必须存在,否则会抛出文件找不到异常。还是举个例子来讲,更清晰些,也更能便于理解。

//读取一个文本文件E:\\Code\\TreeSetTest.java

import java.io.*;
class FileReaderDemo
{
public static void main(String[] args)throws IOException
{
FileReader fr = new FileReader("E:\\Code\\TreeSetTest.java");//一定得保证文件存在,否则会报文件找不到异常
/*//第一种方式,一个一个的读,读一个打一个
int ch;
while((ch = fr.read()) != -1)
{
System.out.print((char)ch);
}
*/
//第二种读取方式,读一组存入数组中,然后打印出来
char[] buf = new char[1024];
int len;
while((len = fr.read(buf)) != -1)
{
System.out.print(new String(buf, 0, len));
}

fr.close();
}
}
为了更清晰直观的理解流的学习,我把异常处理的部分直接抛出去了。其中注释中是一个字符一个字符的读取,读一次打印一次,这是最原始的方式。

从这里我们可以知道,流其实很简单,无非就是读和写,读和写一起组合使用便能够实现简单的文本文件的拷贝。具体示例如下。

//拷贝文本文件

import java.io.*;
class CopyWenBenWenJian
{
public static void main(String[] args)throws IOException
{
FileWriter fw = new FileWriter("D:\\Copy_TreeSetTest.java");
FileReader fr = new FileReader("E:\\Code\\TreeSetTest.java");
char[] buf = new char[1024];
int len;
while((len = fr.read(buf)) != -1)//读进数组中
{
fw.write(buf, 0, len);//从数组写入文件中
fw.flush();
}
fr.close();
fw.close();
}
}
三、装饰设计模式

    何为装饰设计模式呢?简单来说,当想要对已有的对象进行功能增强时,可以定义类,将已有的对象传入,基于已有的功能,并提供增强功能,那么自定义的该类就被称为装饰类。装饰类通常会通过构造方法接受被装饰的类对象,并基于被装饰的类的功能,提供更强的增强功能。

    那么有同学可能会对装饰类和继承会有疑问。为什么要使用装饰类?继承也是可以实现的啊?简单来说,装饰模式比继承更加的灵活,如果一个体系中的所有的类都要增强,那么你会发现,继承的体系将会相当的臃肿,而且代码的重复率相当的高。那么在这个体系中定义装饰类,便会降低类与类之间的关系,避免了大量的继承而使得整个体系的臃肿不堪。

//装饰类的基本示例

class Person
{
public void chiFan()
{
System.out.println("吃饭");
}
}
class SuperPerson
{
private Person p;
SuperPerson(Person p)
{
this.p = p;
}
public void superChiFan()
{
System.out.println("开胃酒");
p.chiFan();
System.out.println("甜点");
System.out.println("来一根");
}
}
class ZhuangShiDemo
{
public static void main(String[] args)
{
Person p = new Person();
p.chiFan();
System.out.println();
SuperPerson sp = new SuperPerson(p);
sp.superChiFan();
}
}
3.BufferedReader

    BufferedReader为Reader的装饰类,是一个增强类。其具体用法与Reader中的其他流大同小异,只是其提供了更强的、效率更高的功能。比如其重写了int read();和int read(char[], int, int);方法,并增强提供了读取行的方法String readLine();方法。

//自己实现一个Reader的增强类MyBufferedReader

import java.io.*;
class MyBufferedReader extends Reader
{
private Reader fr = null;
MyBufferedReader(Reader fr)
{
this.fr = fr;
}
public String MyReadLine()throws IOException
{
StringBuilder sb = new StringBuilder();
int ch;
while((ch = fr.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
{
fr.close();//使用子类自己的close()方法
}
p
4000
ublic int read(char[] cbuf, int off, int length)throws IOException
{
return fr.read(cbuf, off, length);
}
}
4.InputStream、OutputStream

    字节流的操作和字符流大同小异,只不过字符流操作的文本,字节流可以操作图片、mp3等媒体文件,其使用的范围更加的广泛。下面通过一个拷贝mp3的例子来说明下。

//拷贝mp3

import java.io.*;
class Copy_TuPian
{
public static void main(String[] args)throws IOException
{
BufferedInputStream fis =
new BufferedInputStream(new FileInputStream("E:\\Code\\如果没有你.mp3"));
BufferedOutputStream fos =
new BufferedOutputStream(new FileOutputStream("E:\\Code\\Copy_如果没有你.mp3"));
byte[] buf = new byte[1024];
int len;
while((len = fis.read(buf)) != -1)
{
fos.write(buf, 0, len);
}
fis.close();
fos.close();
}
}
5.InputStreamReader、OutputstreamWriter

转换流可以将字节转换成字符,原因在于,其将获得到的字节通过查编码表获取到指定对应字符,其就是基于 字节流 + 编码表,没有转换就没有字符流,所以凡是操作设备上的文本数据,涉及编码转换都必须使用转换流。

    转换流的一个最特殊的运用就是标准输入和标准输出。

    标准输入(键盘录入):BufferedReader bufr = new BufferedReader(new InputStreamReader(System.in));

    标准输出(屏幕):BufferedWrite bufw = new BufferedWriter(new OutputStreamWriter(System.out));

四、总结-流操作规律


    通过以上的学习,我们不难发现,流就是读和写的操作,明确源和目的之后,就很好操作了。

--明确源和目的地

数据源:就是从哪里读取数据,可以使用的两个体系:InputStream、Reader

目的地:就是要往哪里写入数据,可以使用的两个体系为:OutputStream、Writer

--操作的数据是否纯文本

如果是:读-Reader,写-Writer

若不是:读-InputStream,写-OutputStream

--虽然明确了体系,但是体系中有太多的对象,到底用哪个?

数据源对应的设备:硬盘(File)、内存(数组)、键盘(System.in)

目的地对应的设备:硬盘(File)、内存(数组)、控制台(System.out)

--需要在基本操作上附加其他功能吗?比如缓冲

如果需要就进行装饰

----------------------
ASP.Net+Unity开发、.Net培训、期待与您交流! ----------------------
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: