17 IO 流
IO流用来处理设备之间的数据传输Java对数据的操作是通过流的方式
Java用于操作流的对象都在IO包中
流按操作数据分为两种:字节流 input,output 与字符流 reader,writer 。
流按流向分为:输入流 input,reader,输出流 output, writer。
17.1 IO常用基类
字节流的抽象基类:InputStream ,OutputStream。
字符流的抽象基类:
Reader , Writer。
注:由这四个类派生出来的子类名称都是以其父类名作为子类名的后缀。
如:InputStream的子类FileInputStream。
如:Reader的子类FileReader。
io的书写规范
导入IO包中的类
进行IO异常处理
在finally中对流进行关闭
思考:
有了垃圾回收机制为什么还要调用close方法进行关闭。
io流调用的windos的流处理线程 因此回收机制不会对此处理 需要单独 关闭
为什么IO异常一定要处理。
17.2 字符流 Reader Writer
文件字符流 FileReader FileWriter创建流对象,建立数据存放文件
FileWriter fw = new FileWriter(“Test.txt”);
调用流对象的写入方法,将数据写入流
fw.write(“text”);
关闭流资源,并将流中的数据清空到文件中。
fw.close();
不写close方法会有什么结果呢?
java调用的是windos的系统资源 如果流不关闭着不会关闭该系统资源
如果想在原有文件上继续加入新的数据呢?
1.FileWriter(File file, boolean append)
根据给定的 File 对象构造一个 FileWriter 对象。 appede 确定是否添加在文件末尾
2.FileWriter(String fileName, boolean append)
根据给定的文件名以及指示是否附加写入数据的 boolean 值来构造 FileWriter 对象。
IO异常的专业处理方式
IO 异常一般要往上层抛 要判断 fw是否等于null;
建立一个流对象,将已存在的一个文件加载进流。
FileReader fr = new FileReader(“Test.txt”);
创建一个临时存放数据的数组。
char[] ch = new char[1024];
调用流对象的读取方法将流中的数据读入到数组中。
fr.read(ch);
思考:
在加载文件时候是否是将文件全部加载进流
不会,一般会有缓冲机制
为什么定义数组,要定义多大呢?
定义数组就相当于水龙头的开关 决定每次重缓冲池读取多少量的数据
注意事项:
定义文件路径时,可以用“/”或者“\\”。
在创建一个文件时,如果目录下有同名文件将被覆盖。
在读取文件时,必须保证该文件已存在,否则出异常。
文件Copy综合示例 使用缓冲流copy 复制
package cn;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
/**
* 使用带缓冲功能的字节流复制文件。
* @author 李昂志
*/
public class Test5 {
public static void copy(String fileName,String toWhere) throws IOException{
System.out.println("开始copy文件 文件路径:"+fileName +",复制路径:"+toWhere);
BufferedReader br = null;
BufferedWriter out = null;
try {
//BufferedReader 缓冲读取流
br = new BufferedReader(new FileReader(fileName));
//BufferedWriter 缓冲写入流
out = new BufferedWriter(new FileWriter(new File(toWhere)));
String s = null;
while((s=br.readLine())!=null){
System.out.println(s);
out.append(s+"\n"); //写入的时候要记得换行
out.flush(); //记得流要刷新
}
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} finally{
//关闭流
if(br!=null){
br.close();
}
if(out!=null){
out.close();
}
}
System.out.println("copy完毕");
}
public static void main(String[] args) {
try {
Test5.copy("abc.txt", "CopyFile/abc.txt");
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
上述示例使用了 Buffered 缓冲流
17.3 字符流的缓冲区
作用:缓冲区的出现提高了对数据的读写效率。对应类
BufferedWriter
BufferedReader
缓冲区要结合流才可以使用。
在流的基础上对流的功能进行了增强。
自定义缓冲流
package test2;
import java.io.FileReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.Reader;
/**
* 自定义缓冲字符流
* 难点: 如果要实现一个类似缓冲流的缓冲区 这个问题还没想透彻
* @author 李昂志
*/
public class MyBufferedReader extends Reader{
private static int bufferdSize = 9999; //字符缓冲区
private Reader in = null;
private char[] cs; //用来存储字符缓冲的数据
public MyBufferedReader(Reader in , int size){
super(in);
if(size<=0){
throw new IllegalArgumentException("Buffer size <= 0");
}
MyBufferedReader.bufferdSize = size;
this.in = in;
}
public MyBufferedReader(Reader in){
this(in,bufferdSize);
}
@Override
public int read(char[] cbuf, int off, int len) throws IOException {
return in.read(cbuf, off, len);
}
/**
* 读取一行文本 ,读到换行处则 退出
* @return 读取一行的文本 字符串
* @throws IOException
*/
public String readline() throws IOException{
StringBuilder sb = null;
char c ='A';
cs = new char[bufferdSize];
int i,j =0;
while((i = in.read()) != -1){
if(sb == null){
sb = new StringBuilder();
}
c = (char) i;
if ( c == '\r') continue;
if ( c == '\n') break;
cs[j++] = c;
}
if(sb != null ) sb.append(cs ,0 ,j);
else return null;
return sb.toString();
}
/**
* 获取缓冲池大小
* @return
*/
public int getBufferdSize() {
return bufferdSize;
}
@Override
public void close() throws IOException {
// TODO Auto-generated method stub
if(in != null){
in.close();
}
}
public static void main(String[] args) throws IOException {
System.out.println("----");
MyBufferedReader in = new MyBufferedReader(new FileReader("abc.txt"));
String s = null;
while((s = in.readline())!= null){
System.out.println(s);
}
in.close();
}
}
17.4 装饰设计模式
1. 概述
Decorator装饰模式是一种结构型模式,它主要是解决:“过度地使用了继承来扩展对象的功能”,由于继承为类型引入的静态特质,使得这种扩展方式缺乏灵活性;并且随着子类的增多(扩展功能的
增多),各种子类的组合(扩展功能的组合)会导致更多子类的膨胀(多继承)。继承为类型引入的
静态特质的意思是说以继承的方式使某一类型要获得功能是在编译时。所谓静态,是指在编译时;动态,是指在运行时。
GoF《设计模式》中说道:动态的给一个对象添加一些额外的职责。就增加功能而言,Decorator模式
比生成子类更为灵活。
缓冲流使用了装饰设计模式
BufferedWriter --- 装饰 Writer
BufferedReader --- 装饰 Reader
这两个类均是使用的装饰设计模式
在装饰类中 一般会给构造函数传入一个需要装饰的类
如在: BufferedReader的构造方法中都需要 传入一个Reader的子类
下面我们来看看BufferedReader的几个构造方法
BufferedReader(Reader in)
创建一个使用默认大小输入缓冲区的缓冲字符输入流。
BufferedReader(Reader in, int sz)
创建一个使用指定大小输入缓冲区的缓冲字符输入流。
2. 特点
(1) 装饰对象和真实对象有相同的接口。这样客户端对象就可以和真实对象相同的方式和装饰对象交互。(2) 装饰对象包含一个真实对象的引用(reference)
(3) 装饰对象接受所有来自客户端的请求。它把这些请求转发给真实的对象。
(4) 装饰对象可以在转发这些请求以前或以后增加一些附加功能。这样就确保了在运行时,不用修改
给定对象的结构就可以在外部增加附加的功能。在面向对象的设计中,通常是通过继承来实现对给定
类的功能扩展。
3.适用性
以下情况使用Decorator模式(1) 需要扩展一个类的功能,或给一个类添加附加职责。
(2) 需要动态的给一个对象添加功能,这些功能可以再动态的撤销。
(3) 需要增加由一些基本功能的排列组合而产生的非常大量的功能,从而使继承关系变的不现实。
(4) 当不能采用生成子类的方法进行扩充时。一种情况是,可能有大量独立的扩展,为支持每一种
组合将产生大量的子类,使得子类数目呈爆炸性增长。另一种情况可能是因为类定义被隐藏,或
类定义不能用于生成子类。
4.优点
(1) Decorator模式与继承关系的目的都是要扩展对象的功能,但是Decorator可以提供比继承更多的灵活性。(2) 通过使用不同的具体装饰类以及这些装饰类的排列组合,设计师可以创造出很多不同行为的组合。
5.缺点
(1) 这种比继承更加灵活机动的特性,也同时意味着更加多的复杂性。(2) 装饰模式会导致设计中出现许多小类,如果过度使用,会使程序变得很复杂。
(3) 装饰模式是针对抽象组件(Component)类型编程。但是,如果你要针对具体组件编程时,
就应该重新思考你的应用架构,以及装饰者是否合适。当然也可以改变Component接口,增加新的公
开的行为,实现“半透明”的装饰者模式。在实际项目中要做出最佳选择。
6.代码示例
在装饰模式中的各个角色有:(1)抽象构件(Component)角色:给出一个抽象接口,以规范准备接收附加责任的对象。
(2)具体构件(Concrete Component)角色:定义一个将要接收附加责任的类。
(3)装饰(Decorator)角色:持有一个构件(Component)对象的实例,并实现一个与抽象构件接口一致的接口。
(4)具体装饰(Concrete Decorator)角色:负责给构件对象添加上附加的责任。
下面我们来实现一个装饰模式 基类为GpsPhone
package test2;
/**
* 定义一个手机的接口 gps 定位
* @author 李昂志
*
*/
public abstract class GpsPhone {
/**
* 拨号吗 成功返回true
* @return
*/
public abstract String gps();
}
/**
* 三星手机属于 手机的一个子类
* @author 李昂志
*/
class SanXinPhone extends GpsPhone{
@Override
public String gps() {
// TODO Auto-generated method stub
return "SanXinPhone 发送 Gps 定位";
}
}
/**
* 装饰类 --- 加强手机的功能
* 这个类继承了一个GpsPhone 类 又 自定义了一个 GpsPhone 对象
* 这样做的意义是:Decorator类又使用了另外一个Component类。
* 我们可以使用一个或多个Decorator对象来“装饰”一个Component对象,且装饰后的对象仍然是一个Component对象。
* @author 李昂志
*/
class Decorator extends GpsPhone {
private GpsPhone ph;
public Decorator(GpsPhone ph){
this.ph = ph;
}
/**
* 提供一个记忆查找功能
*/
public void RemberPhone(){
System.out.println("查找");
}
@Override
public String gps() {
System.out.println("Decorator : 进行了装饰优化" );
return ph.gps();
}
}