---------------------- <a href="http://edu.csdn.net"target="blank">ASP.Net+Android+IO开发S</a>、<a href="http://edu.csdn.net"target="blank">.Net培训</a>、期待与您交流! ----------------------
12、IO流
12.1、IO(Input Output)流
·IO流用来处理设备之间的数据传输
·java对数据的操作是通过流的方式
·java用于操作流的对象都是在IO包中
·流按操作数据分为两种:字节流和字符流
·流按流向分为:输入流和输出流
12.2、IO流常用基类
字节流的抽象基类:
InputStream,OutputStream
字符流的抽象基类:
Reader,Writer
注意:由这四个类派生出来的子类名称都是以其父类名作为子类名的后缀。
如:InputStream的子类FileInputStream
如:Reader的子类FileReader
12.3、IO程序的书写
12.3.1基本步骤
导入IO包中的类
进行IO异常处理
在finally中对流进行关闭
思考:
有了垃圾回收机制为什么还要调用close方法进行关闭
为什么IO异常一定要处理
12.3.2IO异常的标准处理方式代码示例
只要与系统相关的设备有关的操作都会抛出IO异常
import java.io.*;
class FileWriterDemo2
{
public static void main(String[] args)
{
//在外边建立引用,在try内进行初始化,作用域为整个函数(关闭流也会使用)
FileWriter fw = null;
try
{
//try内部初始化,且创建和写入都会发生异常,且他们有关联,所以一起处理
fw = new FileWriter("demo.txt");
fw.write("abcdefg");
}
catch (IOException e)
{
System.out.println("catch:"+e.toString());
}
finally//在此代码块中,必须进行关闭流的操作
{
try//关闭流时会发生IO异常,所以必须try处理
{
//当有多个流时,应当分别关闭和进行非空判断
//此句话可以写在try外面,当创建文件发生异常时,会发生空指针异常
if(fw!=null)
fw.close();
}
catch (IOException e)
{
System.out.println(e.toString());
}
}
}
}
12.4、字符流——创建文件
12.4.1字符流创建文件的步骤
·创建流对象,建立数据存放文件
FileWriter fw = new FileWriter(“Test.txt”);
·调用流对象的写入方法,将数据写入流
fw.write(“test”);
·关闭流资源,并将流中的数据清空到文件中
fw.close();
12.4.2FileWriter子类的代码演示
找到一个专门用于操作文件的Writer子类对象。FileWriter。
后缀名是父类名。 前缀名是该流对象的功能。
import java.io.*;
class FileWriterDemo
{
publicstatic void main(String[] args) throws IOException
{
//1、创建一个FileWriter对象。该对象一被初始化就必须要明确被操作的文件。而且该文件会被创建到指定目录下。如果该目录下已有同名文件,将被覆盖。其实该步就是在明确数据要存放的目的地。
FileWriterfw = new FileWriter("demo.txt");
//2、调用write方法,将字符串写入到流中。
fw.write("abcde");
//3、刷新流对象中的缓冲中的数据。将数据刷到目的地中。
//fw.flush();//可以多次使用,但是必须最后关闭
//4、关闭流资源,但是关闭之前会刷新一次内部的缓冲中的数据。将数据刷到目的地中。和flush区别:flush刷新后,流可以继续使用,close刷新后,会将流关闭。
fw.close();
}
}
12.4.3对已有文件的数据续写
import java.io.*;
class FileWriterDemo3
{
public static void main(String[] args) throws IOException
{
//传递一个true参数,若没有文件会创建文件,若有文件则不覆盖已有的文件。并在已有文件的末尾处进行数据续写。
FileWriter fw = new FileWriter("demo.txt",true);
//在window(包括XP和win7)系统中的记事本程序中,换行是识别符是“\r\n”。
fw.write("nihao\r\nxiexie");
fw.close();
}
}
12.5、字符流——读取文件
12.5.1字符流读取文件的步骤
·建立一个流对象,将已存在的一个文件加载进流
FileReader fr =new FileReader(“Test.txt”);
·创建一个临时存放数据的数组
char[] ch = new char[1024];
·调用流对象的读取方法将流中的数据读入到数组中
fr.read(ch);
思考:
在加载文件时候是否将文件全部加载进流
为什么定义数组,要定义多大呢?
12.5.2注意
定义文件路径时,可以用“/”或者“\\”
在创建一个文件时,如果目录下有同名文件将被覆盖
在读取文件时,必须保证该文件已存在,否则出异常
会有一个默认的编码方式就是系统的默认编码
12.5.3文本文件读取方式一:一次读一个字符
import java.io.*;
class FileReaderDemo
{
public static void main(String[] args) throws IOException
{
//1、创建一个文件读取流对象和指定名称的文件相关联。要保证该文件是已经存在的,如果不存在,会发生异常FileNotFoundException
FileReader fr = new FileReader("demo.txt");
//2、调用读取流对象的read方法。
//read():一次读一个字符。而且会自动往下读。独到末尾处返回-1
//while循环读取文件内容,书写比较简便
int ch = 0;
while((ch=fr.read())!=-1)
{
System.out.println(
}
/*
while(true)
{
int ch = fr.read();
if(ch==-1)
break;
System.out.println("ch="+(char)ch);
}
*/
fr.close();//关闭该流并释放与之关联的所有资源,不刷新
}
}图示
12.5.4文本文件读取方式二:通过字符数组进行读取
第二种方式:通过字符数组进行读取。
import java.io.*;
class FileReaderDemo2
{
public static void main(String[] args) throws IOException
{
FileReader fr = new FileReader("demo.txt");
//定义一个字符数组。用于存储读到字符。
//该read(char[])返回的是读到的字符个数。
//字符是2个字节,定义1024的整数倍,这里是2K。
char[] buf = new char[1024];
//int num=fr.read(buf);//把流关联的数据读取num个存入数组中。
int num = 0;
while((num=fr.read(buf))!=-1)
{
System.out.println(new String(buf,0,num));
}
fr.close();
}
}图示
12.5.5练习:读取一个.java文件,并打印在控制台上
文本文件使用字符流
最好打印语句不加换行,因为当打印的数据长度大于1024时,会出现一次换行。
import java.io.*;
class FileReaderTest
{
public static void main(String[] args) throws IOException
{
FileReader fr = new FileReader("DateDemo.java");
char[] buf = new char[1024];
int num = 0;
while((num=fr.read(buf))!=-1)
{
System.out.print(new String(buf,0,num));
}
fr.close();
}
}
12.5.6练习:文本文件的复制
将C盘一个文本文件复制到D盘。
复制的原理:其实就是将C盘下的文件数据存储到D盘的一个文件中。
步骤:
1,在D盘创建一个文件。用于存储C盘文件中的数据。
2,定义读取流和C盘文件关联。
3,通过不断的读写完成数据存储。
4,关闭资源。
import java.io.*;
class CopyText
{
public static void main(String[] args) throws IOException
{
copy_2();
}
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)
{
}
}
}
//第一种方式:从C盘读一个字符,就往D盘写一个字符。
public static void copy_1()throws IOException
{
//1、创建目的地。
FileWriter fw = new FileWriter("RuntimeDemo_copy.txt");
//2、与已有文件关联。
FileReader fr = new FileReader("RuntimeDemo.java");
//3、读取一个字符,存一个字符
int ch = 0;
while((ch=fr.read())!=-1)
{
fw.write(ch);
}
fw.close();
fr.close();
}
}复制图例
12.6、字符流的缓冲区
12.6.1缓冲区的作用及注意事项
缓冲区的出现是为了提高流的操作效率而出现的。所以在创建缓冲区之前,必须要先有流对象。
缓冲区的出现提高了对数据的读写效率
对应类
BufferedWriter
BufferedReader
缓冲区要结合流才可以使用
在流的基础上对流的功能进行增强
12.6.2字符写入缓冲区代码示例
该缓冲区中提供了一个跨平台的换行符。
newLine();写入一个行分隔符(在window和linux系统中都是换行,只有缓冲区对象具有此方法)
\r\n:window系统中是换行,\n:在linux系统中是换行
import java.io.*;
class BufferedWriterDemo
{
public static void main(String[] args) throws IOException
{
//1、创建一个字符写入流对象。
FileWriter fw = new FileWriter("buf.txt");
//2、为了提高字符写入流效率。加入了缓冲技术。只要将需要被提高效率的流对象作为参数传递给缓冲区的构造函数即可。
BufferedWriter bufw = new BufferedWriter(fw);
for(int x=1; x<5; x++)
{
bufw.write("abcd"+x);
bufw.newLine();//跨平台的通用换行方法
bufw.flush();//刷新缓冲区
}
//记住,只要用到缓冲区,就要记得刷新。
//bufw.flush();
//其实关闭缓冲区,就是在关闭缓冲区中的流对象。
bufw.close();
}
}
12.6.3字符读取缓冲区代码示例
字符读取流缓冲区:字符、数组、行
该缓冲区提供了一个一次读一行的方法 readLine,方便于对文本数据的获取。当返回null时,表示读到文件末尾。
readLine方法返回的时候只返回回车符之前的数据内容。并不返回回车符。
import java.io.*;
class BufferedReaderDemo
{
public static void main(String[] args) throws IOException
{
//1、创建一个读取流对象和文件相关联。
FileReader fr = new FileReader("buf.txt");
//2、为了提高效率。加入缓冲技术。将字符读取流对象作为参数传递给缓冲对象的构造函数。
BufferedReader bufr = new BufferedReader(fr);
String line = null;
while((line=bufr.readLine())!=null)
{
System.out.println(line);
}
bufr.close();
}
}
12.6.4通过缓冲区复制一个.java文件
import java.io.*;
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();//补充readLine方法的缺陷
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("写入关闭失败");
}
}
}
}
readLine方法图例
12.6.5模拟BufferedReader,自定义readLine方法
明白了BufferedReader类中特有方法readLine的原理后,
可以自定义一个类中包含一个功能和readLine一致的方法。
来模拟一下BufferedReader
此类是一个使得读取增强的装饰类
import java.io.*;
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;
}
/*
覆盖Reader类中的抽象方法。
*/
public int read(char[] cbuf, int off, int len) throws IOException
{
return r.read(cbuf,off,len) ;
}
public void close()throws IOException
{
r.close();
}
public void myClose()throws IOException
{
r.close();
}
}
class MyBufferedReaderDemo
{
public static void main(String[] args) throws IOException
{
FileReader fr = new FileReader("buf.txt");
MyBufferedReader myBuf = new MyBufferedReader(fr);
String line = null;
while((line=myBuf.myReadLine())!=null)
{
System.out.println(line);
}
myBuf.myClose();
}
}
12.7、装饰设计模式
对原有类进行了功能的改变,增强
装饰设计模式的基本格式
它与继承有什么不同?
了解BufferedReader的原理
12.7.1装饰类
当想要对已有的对象进行功能增强时,可以定义类,将已有对象传入,基于已有的功能,并提供加强功能。那么自定义的该类称为装饰类。
装饰类通常会通过构造方法接收被装饰的对象。并基于被装饰的对象的功能,提供更强的功能。
class Person
{
public void chifan()
{
System.out.println("吃饭");
}
}
class SuperPerson //与Person同属于一个接口或者一个类
{
private Person p ;
SuperPerson(Person p)//传递被增强的对象
{
this.p = p;
}
public void superChifan()
{
System.out.println("开胃酒");
p.chifan();
System.out.println("甜点");
System.out.println("来一根");
}
}
class PersonDemo
{
public static void main(String[] args)
{
Person p = new Person();
//p.chifan();
SuperPerson sp = new SuperPerson(p);
sp.superChifan();
}
}
12.7.2装饰和继承的区别
例子:MyReader//专门用于读取数据的类。
|--MyTextReader
|--MyBufferTextReader
|--MyMediaReader
|--MyBufferMediaReader
|--MyDataReader
|--MyBufferDataReader
class MyBufferReader
{
MyBufferReader(MyTextReader text)
{}
MyBufferReader(MyMediaReader media)
{}
}
上面这个类扩展性很差。找到其参数的共同类型。通过多态的形式。可以提高扩展性。
//组合结构
class MyBufferReader extends MyReader
{
private MyReader r;
MyBufferReader(MyReader r)
{}
}
MyReader//专门用于读取数据的类。,优化后的体系,扩展性更强
|--MyTextReader
|--MyMediaReader
|--MyDataReader
|--MyBufferReader//基于前类增加的类
以前是通过继承将每一个子类都具备缓冲功能。
那么继承体系会复杂,并不利于扩展。
现在优化思想。单独描述一下缓冲内容。将需要被缓冲的对象。传递进来。也就是,谁需要被缓冲,谁就作为参数传递给缓冲区。这样继承体系就变得很简单。优化了体系结构。