1、 IO概述
(1)概述
IO流:即InputOutput的缩写。是用来处理设备间(内存、硬盘)的数据传输。
(2)IO流的常用基类:
a.字节流的抽象基流:InputStream和OutputStream
b.字符流的抽象基流:Reader和Writer
注:此四个类派生出来的子类名称都是以父类名作为子类名的后缀,以前缀为其功能;如InputStream子类FileInputStream;Reader子类FileReader
(2)特点:
1)IO流用来处理设备间的数据传输。
2)Java对数据的操作是通过流的方式。
3)Java用于操作流的对象都在IO包中。
4)流按操作数据分为两种:字节流和字符流。
5)流按流向分为:输入流和输出流。
注意:流只能操作数据,而不能操作文件。
2、字符流 FileWriter和FileReader
字符流中的对象融合了编码表。使用的是默认的编码,即当前系统的编码。字符流只用于处理文字数据,而字节流可以处理媒体数据。既然IO流是用于操作数据的,那么数据的最常见体现形式是文件。
2.1 输入流
查看API,找到一个专门用于操作文件的Writer子类对象:FileWriter。 后缀是父类名。前缀名是流对象的功能。该流对象一被初始化,就必须有被操作的文件存在。
(1) IO输入流的基本用法和IO的异常处理
import java.util.*;
import java.io.*;
class FileWriterDemo1
{
public static void main(String[] args)
{
FileWriter fw=null;//在外部建立引用,在内部建立对象。如果都在内部建立在finally中就找不到fw
try
{
fw=new FileWriter("K://demo.txt");
fw.write("安徽");
}
catch (IOException e)//如果创建的为“K://demo.txt”则系统找不到指定的路径就会报告异常NUllPointEXception
{
System.out.println(e.toString());
}
finally
{
try
{
if(fw!=null)//如果fw为空的情况下,不应该执行关闭的操作,不应调用close()。如果有5个流对象,得分别关闭5次。
fw.close();
}
catch (IOException e)
{
System.out.println(e.toString());
}
}
}
}
- (2) 文件的续写
//需求:文件的续写,假如文件中已经有内容“原来文件内容”;添加新的信息文件续写
import java.util.*;
import java.io.*;
class FileWriterDemo2
{
public static void main(String[] args)
{
FileWriter fw=null;//在外部建立引用,在内部建立对象。如果都在内部建立在finally中就找不到fw
try
{
fw=new FileWriter("demo.txt",true);
fw.write("续写\r\n信息");
}
catch (IOException e)//如果创建的为“K://demo.txt”则系统找不到指定的路径就会报告异常NUllPointEXception
{
System.out.println(e.toString());
}
finally
{
try
{
if(fw!=null)//如果fw为空的情况下,不应该执行关闭的操作,不应调用close()。如果有5个流对象,得分别关闭5次。
fw.close();
}
catch (IOException e)
{
System.out.println(e.toString());
}
}
}
}
- 2.2 输出流
查看API,找到一个专门用于操作文件的Writer子类对象:FileWriter。 后缀是父类名。前缀名是流对象的功能。该流对象一被初始化,就必须有被操作的文件存在。
举例说明:
//需求:读取文件的内容,并打印到控制台上
import java.util.*;
import java.io.*;
class FileReaderDemo3
{
public static void main(String[] args)
{
FileReader fr=null;
try
{
fr=new FileReader("demo.txt");
/************************第一种***************************/
while (true)
{
int ch=fr.read();
if (ch==-1)
break;
System.out.print((char)ch);
}
/************************第二种***************************/
while (true)
{
int ch=0;
while ((ch=fr.read())!=-1)
{
System.out.print((char)ch);
}
break;
}
/************************第三种***************************/
char[] buf=new char[3];
int num=0;
while ((num=fr.read(buf))!=-1)
{
System.out.print(new String(buf,0,num));
}
}
/**********************************************************/
catch (IOException e)
{
System.out.println(e.toString());
}
finally
{
try
{
fr.close();
}
catch (IOException e)
{
System.out.println(e.toString());
}
}
}
}
- 2.3 拷贝文件
//需求:demo.txt中的文本信息拷贝到copydemo.txt中去
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
class CopyDemo4
{
public static void main(String[] args)
{
private static final int BUFFER_SIZE=1024;
FileWriter fw=null;
FileReader fr=null;
try
{
fw=new FileWriter("Copydemo.txt");
fr=new FileReader("demo.txt");
char[] buf=new char[BUFFER_SIZE];
int num=0;
while ((num=fr.read(buf))!=-1)
{
fw.write(buf,0,num);
}
}
catch (IOException e)
{
System.out.println(e.toString());
}
finally
{
try
{
if (fr!=null)
fr.close();
}
catch (IOException e)
{
System.out.println(e.toString());
}
try
{
if (fw!=null)
fw.close();
}
catch (IOException e)
{
System.out.println(e.toString());
}
}
}
}
3、字符流缓冲区
- 3.1 BufferedReader和BufferedWriter简介
注:
(1)假如缓冲区是为了提高效率,但是真正的操作还是FileWriter(),关闭缓冲区就是关闭缓冲区的流对象。
(2)newLine()写入一个行分隔符,具有跨平台性。
public String readLine() throws IOException
读取一个文本行。通过下列字符之一即可认为某行已终止:换行 (‘\n’)、回车 (‘\r’) 或回车后直接跟着换行。
返回:包含该行内容的字符串,不包含任何行终止符,如果已到达流末尾,则返回 null
- 3.2 缓冲区文件复制
import java.io.*;
class BufCopyDemo1
{
public static void main(String[] args)
{
BufferedWriter bufw=null;
BufferedReader bufr=null;
try
{
bufw=new BufferedWriter(new FileWriter("copydemo.txt"));
bufr=new BufferedReader(new FileReader("demo.txt"));
String line=null;
while ((line=bufr.readLine())!=null)
{
bufw.write(line);
bufw.newLine();
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("读写关闭失败");
}
}
}
}
- 3.3 自定义的缓冲区文件内容的复制
/*
需求:既然明白了readLine()的原理,可以自定义一个类中包含一个功能和readLine()一致的方法来模拟BufferedReader()
*/
import java.io.*;
class BufCopDemo2
{
public static void main(String[] args)throws IOException
{
FileReader fr=new FileReader("demo.txt");//
MyBufferedReader mybuf=new MyBufferedReader(fr);
String line=null;
while ((line=mybuf.myReadLine())!=null)
{
System.out.println(line);
}
mybuf.myClose();
}
}
class MyBufferedReader
{
private FileReader r;//为了流对象作用于整个类,需要定义成员变量
MyBufferedReader(FileReader r)
{
this.r=r;
}
/*定义一个临时容器,原BufferedReader中定义的是字符数组,但是需要使用的指针概念.可以使用StringBuilder容器来存储,因为最终还是将数据变成字符串*/
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();
else
sb.append((char)ch);
}
if (sb.length()!=0)
return sb.toString();
return null;
}
public void myClose() throws IOException
{
r.close();
}
}
- 3.4 LineNumberReader
import java.io.*;
class LineNumberReaderDemo
{
public static void main(String[] args) throws IOException
{
FileReader fr=new FileReader("demo.txt");
LineNumberReader lnr=new LineNumberReader(fr);
String line=null;
lnr.setLineNumber(100);
while ((line=lnr.readLine())!=null)
{
System.out.println(lnr.getLineNumber()+">>"+line);
}
lnr.close();
}
}
4、IO流的装饰设计模式
- 4.1 基本概述
装饰设计模式:当想要对已有对象进行功能增强时,可以定义类将已有对象传入,基于已有对象的功能并提高加强功能,那么自定义的类称为装饰类。
装饰类通常会通过构造方法接受被装饰的对象,并基于被装饰的对象提供更强的功能。
例如:
一个人具有基本的功能吃饭
class Person
{
public static void eat()
{
System.out.println("吃饭");
}
}
进行装饰,功能增强后:
class SuperPerson
{
private Person p;
SuperPerson(Person p)
{
this.p=p;
}
public static void supereat()
{
System.out.println(“开胃酒”);
p.chifan();
System.out.println("甜点");
}
}
class Demo
{
public static void main(String[] args)
{
Person p=new Person();
SuperPerson sp=new SuperPerson(p);
sp.superchifan();
}
}
- 4.2 继承和装饰的区别
首先有一个继承体系:Writer
|--TextWriter:用于操作文本
|--BufferTextWriter:加入了缓冲技术的操作文本的对象,对操作的动作进行效率的提高。
|--MediaWriter:用于操作媒体
|--BufferMediaWriter:加入了缓冲技术的操作媒体的对象,对操作的动作进行效率的提高。
以上方式并不理想,如果这个体系需要再进行功能扩展,又多了更多流对象。这样就会发现只为提高功能,导致继承体系越来越臃肿,不够灵活。
解决问题:
既然加入的都是同一种技术–缓冲。
将缓冲进行单独的封装,哪个对象需要缓冲就将哪个对象和缓冲关联。
class BufferedWriter extends Writer
{
BufferedWriter(Writer w){}
}
装饰比继承更加的灵活。避免了继承体系臃肿,而且降低了类与类之间的关系。但是装饰类和被装饰类必须所属同一个接口和父类。装饰类因为增强已有对象。具备的功能和已有也是相同的,只不过提供了更强的功能。
举例说明:
/****************************************************************************
自定义一个读取缓冲区类,模拟一BufferedReader
分析:缓冲区无非就是封装了一个数组,并对外提供了更多的方法对数组进行访问,
其实这些方法最终操作都是数组的角标。
缓冲的原理:其实就是从源中获取一批数据到缓冲区中,再从缓冲区不断地取出一个
一个数据。在此次取完后,再从源中继续取一批数据进缓冲区,当源中的数据取完时,
用-1作为结束标记.
****************************************************************************/
import java.io.*;
class MyBufferedReader
{
private Reader r;
private char[] buf = new char[1024]; //定义一个数组作为缓冲区
private int pos = 0; //定义一个指针用于操作这个数组中的元素,当操作到最后一个元素后,指针应该归零
private int count = 0; //定义一个计数器用于记录缓冲区中的数据个数,当该数据减到0,就从源中继续获取数据到缓冲区中
MyBufferedReader(Reader r)
{
this .r = r;
}
public int myRead() throws IOException//该方法从缓冲区中一次取一个字符
{
if (count == 0)//从源中获取一批数据到缓冲区中,需要先做判断,只有计数器为0时,才需要从源中获取数据
{
count = r.read(buf);
pos = 0;//每次获取数据到缓冲区后,角标归零
}
if (count < 0)
return -1;
char ch = buf[pos];
pos++;
count--;
return ch;
}
public String myReadLine() throws IOException
{
StringBuilder sb = new StringBuilder();
int ch = 0;
while ((ch = myRead()) != -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 myClose() throws IOException
{
r.close();
}
}
class MyBufferedReaderDemo
{
public static void main(String[] args) throws IOException
{
FileReader fr = new FileReader("buf.txt" );
MyBufferedReader bufr = new MyBufferedReader(fr);
String line = null ;
while ((line = bufr.myReadLine()) != null)
{
System.out.println(line);
}
bufr.myClose();
}
}