字节流
在计算机中,所有文件都能以二进制的形式存在,这个二进制形式就是字节数据
java 的io中针对字节传输操作提供了一系列的流,为字节流。
两个抽象基类
InputStream、OutpuStream。
分别处理字节流的输入和输出
所有的字节都继承自这俩。
void close() 关闭此流并释放相关联所有系统资源
//操作IO流的时候回占用宝贵的系统资源,当操作完成后就应该讲IO所占用的系统资源释放
输入、输出,相对于谁?
当然,输出和输出这个概念出现了,就必须要有一个参照物,这里的参照物是——程序。
InputStream类的方法
boolean markSupported() 此输入流是否支持mark和reset
void mark(int readlimit) 在此输入流中标记当前位置
void reset() 将此流重新定位到上次调用mark时的位置
int available 返回此输入流下一个方法调用,可以不受阻赛地从此输入流读取(或跳过)的估计字节数
long skip(long n) 跳过和丢弃此输入流中数据的n个字节
最常用就是三个重载的read()和close()。
int read() 从输入流中逐个读取数据每个字节
int read(byte[] b) 从输入流中读取一定数量的字节,存储在缓冲区数组b中,并返回读取的字节数
int read(byte[] b,int off,int len)将输入流中最多len个数据字节读入b数组
//后面这两个带数组的都是,一次性全部读入,提高读取效率。
OutputStream类的方法
void wirte(byte[] b) 将b数组中的字节全部写入此输出流
void wirte(int b) 将指定的字节写入此输出流
void wirte(byte[] b) 将指定的b数组中的从偏移量off开始的len个字节写入此输出流
void close() 关闭此输出流,并释放相关所有系统资源
void flush() 刷新此输出流,强制写出所有缓冲区的输出字节
字节流读取文件
FileInputStream
FileOutputStream
如何从文件中读取数据?
首先新建一个文本文件read.txt 内容如下
Look at me now!
package test1;
import java.io.*;
public class Class1{
public static void main(String[] args){
//定义一个文件输出流
FileInputStream fis = null;
try{
//创建文件输入流对象
fis = new FileInputStream("F:/eclipse/test1/src/test1/read.txt");
//设定读取的字节数,设定缓冲数组buffer
int n = 512;
byte buffer[] = new byte[n];
//读取输入流,一直读到 n = -1,就读完了 一定要有while才有效
while((fis.read(buffer,0,n) != -1)&&(n > 0)){
System.out.print(new String(buffer));
}
}catch(Exception e){
System.out.println(e);
}finally{
try{
fis.close();
}catch(IOException e){
e.printStackTrace();
}
}
}}
I'm so handsome.
若中途出现错误,程序会直接中断,所以一定将close()方法写到finally中。
因为finally中不能直接访问try中的内容,所以要将FileInputStream定义在try的外面。
遇到问题:找不到文件
java.io.FileNotFoundException: read.txt (系统找不到指定的文件。)
原因是:没有加上路径,即使和当前类创建在同一个目录下,eclipse也不会去寻找
在要打开的文件前加上路径即可。
以下两种皆可:
1、Unix中,/ 表示目录; \ 表示转义字符。
"F:/eclipse/test1/src/read.txt"
2、Windows中,正斜杠 / 表示除法;反斜杠 \ 用来表示目录。
fis = new FileInputStream("F:\\\\eclipse\\\\test1\\\\src\\\\read.txt");
字节流写入文件
package test1;
import java.io.*;
public class Class1{
public static void main(String[] args) throws IOException{
System.out.print("输入要保存的内容:");
int count,n = 512;
byte buffer[] = new byte[n];
//读取标准输入流
count = System.in.read(buffer);
//创建文件输出流对象
FileOutputStream fos = new FileOutputStream("F:/eclipse/test1/src/test1/read.txt");
//write() 写入字节流
fos.write(buffer,0,count);
System.out.println("已成功保存到read.txt。");
fos.close();//释放资源
}}
结果
输入要保存的内容:I'm so handsome.
已成功保存到read.txt。
弊端
虽然当文件不存在的时候,就会自己创建文件,再输出内容到文件中。
这里,read.txt已经存在,从结果看来,程序是将之前的全部覆盖掉了。
遇到报错
Unhandled exception type IOException
原因:未抛出异常。
解决:处理 main()函数,抛出异常即可
public static void main(String[] args) throws IOException{
添加文件内容
如果想要不清除文件内容,就需要
FileOutputStream(String FileName,boolean append)来创关键文件输出流的对象。
并且把指定的参数appedn设定为true。
package test1;
import java.io.*;
public class Class1{
public static void main(String[] args) throws IOException{
System.out.print("输入要添加的内容:");
int count,n = 512;
byte buffer[] = new byte[n];
//读取标准输入流
count = System.in.read(buffer);
//创建文件输出流对象,在构造时比覆写的多一个参数true即可
FileOutputStream fos = new FileOutputStream("F:/eclipse/test1/src/test1/read.txt",true);
//写入
fos.write(buffer,0,count);
System.out.println("已成功保存到read.txt。");
fos.close();//释放资源
}}
I'm so handsome.
I'm so handsome,too.
文件的复制
先创建一个文件copy.txt
package test1;
import java.io.*;
public class Class1{
public static void main(String[] args) throws IOException{
//创建输入流数据
FileInputStream fis = new FileInputStream("F:/eclipse/test1/src/test1/copy.txt");
//创建输出流数据
FileOutputStream fos = new FileOutputStream("F:/eclipse/test1/copy.txt");
int len; //定义len,记录每次读取的字节数
//记录复制文件前的系统时间
long begin = System.currentTimeMillis();
//读取文件并判断是否到达文件末尾
while((len = fis.read()) != -1){
fos.write(len);//将读到的字节写入文件
}
//复制文件后的系统时间
long end = System.currentTimeMillis();
System.out.println("复制文件耗时:" + (end - begin) + "毫秒");
fos.close();
fis.close();
}}
复制文件耗时:2毫秒
字节流的缓冲区
上节虽然是复制文件,但是复制方式是一个个字节地复制,效率很低。
这里使用缓冲区,就是说,把一堆字节全部堆到一起,要用的时候,一次性全部带走。
一次性直接输出到文件,大大提高效率。
package test1;
import java.io.*;
public class Class1{
public static void main(String[] args) throws IOException{
//创建输入流数据
FileInputStream fis = new FileInputStream("F:/eclipse/test1/src/test1/copy.txt");
//创建输出流数据
FileOutputStream fos = new FileOutputStream("F:/eclipse/test1/copy.txt");
//定义缓冲区数组b的大小。 定义i,记录每次读取的字节数
byte[]b = new byte[512];
int i;
//记录复制文件前的系统时间
long begin = System.currentTimeMillis();
//读取文件并判断是否到达文件末尾
while((i = fis.read()) != -1){
fos.write(len);//将读到的字节写入文件
}
//复制文件后的系统时间
long end = System.currentTimeMillis();
System.out.println("复制文件耗时:" + (end - begin) + "毫秒");
fos.close();
fis.close();
}}
复制文件耗时:1毫秒
应用缓冲区后,操作文件的次数减少了,从而提高了读写效率。
装饰设计模式
就是在不改变原来的类和使用的继承的情况下,动态扩展一个对象的功能。
创建一个包装对象,来装饰包裹真实的对象。
就像买了一套房,再安装一台空调,这样夏天才不会太热,过几年空调太老化了,想换就换掉。
要求
首先,装饰对象和被装饰的都要实现同一个接口。装饰对象要拥有被装饰对象的实例。
如图,Scource\Decorator都实现了Sourceable接口。
package test1;
import java.io.*;
public class Class1{
public static void main(String[] args) throws IOException{
//实例化source
Sourceable source = new Source();
System.out.println("----------装饰前----------");
source.method();
System.out.println("----------装饰后----------");
//创建装饰类对象,并且将被装饰类当成参数传入
Sourceable obj =new Decorator(source);
obj.method();
}
}
//定义公共接口
interface Sourceable{
public void method();
}
//定义被装饰类,只给一个method,房子
class Source implements Sourceable{
public void method(){
System.out.println("只是一栋房子");
}
}
//定义装饰类,
class Decorator implements Sourceable{
//定义一个
private Sourceable source;
//构造方法
public Decorator(Sourceable source){
super();
this.source = source;
}
public void method(){
source.method();
System.out.println("有空调啦");
System.out.println("有电视!");
}}
----------装饰前----------
只是一栋房子
----------装饰后----------
只是一栋房子
有空调啦
有电视!
这样呢,还能动态撤销。
而继承的话,就不可以,是静态的,不能动态地增删。
字节缓冲流
IO中的一些流也用到了装饰设计模式
分别是BufferedInputStream、BufferedOutputStream
在其构造方法中,分别接收InputStream,OutputStream类型的参数作为被装饰对象,在执行的时候提供缓冲。
如图,程序和文件这两个节点互传的数据是节点流,并且是由缓冲流包裹的。
缓冲流就是对已经存在的节点流的连接和封装。
package test1;
import java.io.*;
public class Class1{
public static void main(String[] args) throws IOException{
//创建文件输入流对象
FileInputStream fis = new FileInputStream("F:/eclipse/test1/src/test1/copy.txt");
//创建文件输出流对象
FileOutputStream fos = new FileOutputStream("F:/eclipse/test1/copy.txt");
//将创建的节点流的对象,作为形参传递给缓冲流的构造方法
BufferedInputStream bis = new BufferedInputStream(fis);
BufferedOutputStream bos = new BufferedOutputStream(fos);
int len; //定义len,记录每次读取的字节数
//记录复制文件前的系统时间
long begin = System.currentTimeMillis();
//读取文件并判断是否到达文件末尾
while((len = fis.read()) != -1){
fos.write(len);//将读到的字节写入文件
}
//复制文件后的系统时间
long end = System.currentTimeMillis();
System.out.println("复制文件耗时:" + (end - begin) + "毫秒");
fos.close();
fis.close();
}}
复制文件耗时:1毫秒
和之前的字节流缓冲区类似,都是对数据进行缓冲,减少操作次数。