在计算机中,所有文件都能以二进制的形式存在,这个二进制形式就是字节数据
java 的io中针对字节传输操作提供了一系列的流,为字节流。
InputStream、OutpuStream。
分别处理字节流的输入和输出
所有的字节都继承自这俩。
void close() 关闭此流并释放相关联所有系统资源 //操作IO流的时候回占用宝贵的系统资源,当操作完成后就应该讲IO所占用的系统资源释放
当然,输出和输出这个概念出现了,就必须要有一个参照物,这里的参照物是——程序。
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数组 //后面这两个带数组的都是,一次性全部读入,提高读取效率。
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毫秒
和之前的字节流缓冲区类似,都是对数据进行缓冲,减少操作次数。