IO流
用于处理设备间的数据传输
按操作数据分:字节流和字符流
按流向分:输入流和输出流
IO流常用基类:
字节流的抽象基类:
InputStream OutputStream
字节流的抽象基类:
Reader Writer
注意:这4个类派生出的子类名称均以其父类名作为子类名的后缀。
数据的最常见体现形式是:文件
下面代码演示常用的写入,读取操作。
import java.io.*; class FWDemo{ //输出流演示 public static void main(String[] args)throws IOException{ //只使用try时,可以不抛出异常 fw1show(); //演示文件创建及写入数据 fw2show(); fw3show(); } //文件的创建及操作 public static void fw1show() throws IOException{ //创建一个FileWriter对象,该对象一被初始化就必须要明确被操作的文件。 //而且该文件会被创建到指定目录下。如果该目录下已经有同名文件,原文件将会被覆盖。 FileWriter fw1 = new FileWriter ("d:\demo1.txt"); fw1.write("abcde");//将字符串写入内存中的流对象中。 fw1.flush(); //刷新该流的缓存,将数据写入文件,后面可以继续操作流。 fw1.close(); //关闭流资源,但是关闭之前会刷新一次内部缓冲的数据。 //close()不能再调用 fw.write 。 } //IO异常的处理方式 public static void fw2show(){ //注意变量在代码块中的作用域,所以在外部定义变量,在代码块中初始化 FileWriter fw2 = null; try{ fw2 = new FileWriter ("d:\demo2.txt"); fw2.write("aaaaa"); } catch (IOException e){ System.out.println(e.toString()); //打印异常信息 } finally { //放入必须执行的数据 try{ if(fw2!=null) fw2.close(); } catch (IOException e){ System.out.println(e.toString()); } } } //续写已有文件 public static void fw3show(){ FileWriter fw3 = null; try{ //传递一个true参数,代表不覆盖已有文件,在文件末尾处添加数据。 fw3 = new FileWriter ("d:\demo1.txt",true); fw3.write("+aa aaa"); //在demo1.txt 文件末尾添加字符串。 } //注意,在windows中换行符是两个组成的是: catch (IOException e){ System.out.println(e.toString()); } finally{ //放入必须执行的数据 try{ if(fw3!=null) fw3.close(); } catch (IOException e){ System.out.println(e.toString()); } } } } class FRDemo{ //输入流演示 public static void main(String[] args){ fr1show(); //读取文件并打印 } public static void fr1show(){ //创建一个文件读取流对象,和指定名称的文件相关联 //必须确保读取的文件存在,否则会发生异常 FileReader fr1 = null ; FileReader fr2 = null ; try{ fr1 = new FileReader("d:\demo1.txt"); fr2 = new FileReader("d:\demo1.txt"); while (true){ //read():一次读一个字符,而且会自动往下读取。 //read()读取的是字符的字码表数值。 int ch = fr1.read(); if(ch==-1) //判断是否已经读取到文件末尾 break; System.out.println((char)ch); } System.out.println("------------"); //定义一个字符数组 //read(char[]) 将字符串存入字符数组返回的是读取到的字符个数 char [] buf = new char [3]; //一般char[]数组长度取1024,一个字节。 int num = 0; while ((num = fr2.read(buf))!= -1){ System.out.println("ch="+(new String(buf,0,num))); } } catch (IOException e){ System.out.println("fr1:"+e); } finally{ try{ if(fr1!=null) fr1.close(); if(fr2!=null) fr2.close(); } catch (IOException e){ System.out.println(e.toString()); } } } }
输入流与输出流的应用演示
需求:将d盘的一个文件复制到d盘并重命名
步骤:
1.在d盘创建一个文件,用于存储d盘源文件的数据。
2.定义读取流和d盘源文件关联。
3.通过不断的读写完成数据存储。
4.关闭资源。
class CopyDemo{ public static void main(String[] args){ copy(); //调用复制函数 } public static void copy(){ FileWriter fw = null; //定义输出流 FileReader fr = null; //定义输入流 try{ fr = new FileReader("d:\demo1.txt");//将输入流与需创建文件相关联 fw = new FileWriter("d:\demo3.txt");//将输出流与需复制文件相关联 char[] buf = new char [1024]; //定义一个中转数组用于存储输入流读取的数据 int len = 0; //定义实数用于记录读取的字符串长度 while ((len = fr.read(buf))!=-1){ //将输入流读取的数据传给中转数组,并将字符长度传给len fw.write(new String(buf,0,len)); //将数据写入输出流 } } catch (IOException e){ throw new RuntimeException("读写失败");//定义IO异常处理办法 } finally{ try{ if (fw!=null) //关闭资源 fw.close(); if (fr!=null) fr.close(); } catch (IOException e){ throw new RuntimeException("读写失败");//定义IO异常处理办法 } } } }
装饰设计模式:
当想要对已有的对象进行功能增强时,
可以定义类,将已有对象传入,给予已有的功能,并提供加强功能
那么自定义的该类称为装饰类
装饰模式比继承要灵活,避免流体系的臃肿
而且降低流类与类之间的关系。
装饰类因为增强流已有对象,具备的功能和已有的是相同的,只不过提供了更强功能。
所以装饰类和被装饰类通常都是属于一个体系中。
字符流的缓冲区
缓冲区就是装饰设计模式的一种应用。
缓冲区提高了对数据的读写效率.
所以在创建缓冲区之前,必须现有流对象。
对应类
BufferedWriter BufferedReader
import java.io.*; class BufferedDemo{ public static void main(String[] args) throws IOException{ bwshow(); //演示输出缓冲区 brshow(); //演示输入缓冲区 } public static void bwshow() throws IOException{ //创建一个流对象,并将目标文件与其关联 FileWriter fw =new FileWriter("d:\buffered.txt"); //为提高写入效率,加入缓冲技术 //将需要提高效率的流对象作为参数传递给缓冲区 BufferedWriter bw = new BufferedWriter (fw); bw.write("abcde"); bw.newLine(); //缓冲区的换行方法 bw.write("aa"); bw.flush(); //缓冲区的关闭,其实是关闭的是其缓冲的流对象,所以不需要单独在关闭流对象 bw.close(); } public static void brshow() throws IOException{ FileReader fr1 =new FileReader("d:\buffered.txt"); FileReader fr2 =new FileReader("d:\buffered.txt"); //为提高写入效率,加入缓冲技术 //将需要提高效率的流对象作为参数传递给缓冲区 BufferedReader br = new BufferedReader (fr1); while (true){ String s1 = br.readLine(); //按照行读取,但是不包含行终止符。 if (s1 == null) break; System.out.println(s1); } System.out.println("-------------------"); //缓冲区的关闭,其实是关闭的是其缓冲的流对象,所以不需要单独在关闭流对象 //传入自定义的buff对象 MyBuffReader mybuff = new MyBuffReader (fr2); //定义自定义的缓冲区,并将去与fr2输入流关联 while (true) { String s2 = mybuff.myreadline(); //按照行读取,但是不包含行终止符。 if (s2 == null) break; System.out.println(s2); } br.close(); mybuff.myclose(); } } //自定义缓存区,实现readLine()方法代码 class MyBuffReader{ private FileReader r; //定义输入流 MyBuffReader(FileReader r){ //将输入流传入 this.r = r; } public String myreadline() throws IOException{ //定义自定义的readLine()函数 //定义一个临时容器,原bufferReader封装的是字符数组 StringBuilder sb = new StringBuilder (); int ch = 0; while ((ch=r.read())!=-1){ //判断输入流是否读取到文件末尾 //判断字符串运行到回车符,就返回字符串,否则就继续向sb对象中存储字符。 if(ch==' ') //判断是否是回车符的第一个字符,是就结束本次循环 continue; if(ch==' ') //判断是否是回车符的第二个字符,是就返回现在StringBuilder中的数据 return sb.toString(); else //如果读取的字符不属于回车符,就将字符存入StringBuilder sb.append((char)ch); } //当源字符串最后没有回车符时,就不能在循环中返回字符串, //此时字符串已经存储在sb对象中,需要添加判断返回字符串。 if(sb.length()!=0) return sb.toString(); return null; } public void myclose() throws IOException{ r.close(); } }
字节流
InputStream OutputStream
字节流可以操作字符和字节数据,但是在操作字符数据时没有字符流方便。
import java.io.*; class FODemo{ public static void main(String[] args) throws IOException{ long start = System.currentTimeMillis(); //fw1show(); //fr1show(); copy(); long end = System.currentTimeMillis(); System.out.println((end-start)+"ms"); } public static void fw1show() throws IOException{ FileOutputStream fo = new FileOutputStream("d:\demo1.txt");//将需写入文件与输出流相关联 fo.write("aabcde".getBytes()); //写入数据,注意,当前为字节流,所以写入的应为字符的字节数据。 fo.close(); } public static void fr1show() throws IOException{ //演示字节输入流的3种读取方法 FileInputStream fi1 = new FileInputStream ("d:\demo1.txt"); FileInputStream fi2 = new FileInputStream ("d:\demo1.txt"); FileInputStream fi3 = new FileInputStream ("d:\demo1.txt"); int ch = 0; //每次读取一个字符 while ((ch=fi1.read())!=-1){ System.out.println((char)ch); } fi1.close(); //每次读取一个字符串 byte [] cha1 = new byte [1024]; //因为是字节流,所以定义一个字节数组 int len = 0; while((len=fi2.read(cha1))!=-1){ //讲数据写入字节数组并判断是否已读取到文件末尾 System.out.println(new String(cha1,0,len)); //将读取的数据转换成字符串并打印 } fi2.close(); //available方法返回的是可最大连续读取的字节数。 //注意所使用的内存是否够用 int num = fi3.available(); byte [] cha2 = new byte [num]; int len2 = fi3.read(cha2); System.out.println(new String (cha2,0,len2)); } public static void copy(){ //字节流可对非文本文件进行复制 FileOutputStream fo = null; //定义输出流 FileInputStream fi = null; //定义输入流 byte [] cha1 = new byte [1024]; int len = 0; try{ fo = new FileOutputStream("d:\demo4.jpg"); //将需写入文件与输出流关联 fi = new FileInputStream ("d:\demo1.jpg"); //将需读取文件与输入流关联 while((len=fi.read(cha1))!=-1){ //将数据写入字节数组并判断是否已在文件末尾 fo.write(cha1); //将字节数组写入输出流 } } catch (IOException e){ //定义IO异常处理办法 System.out.println(e); } finally{ try{ fi.close(); //关闭资源 fo.close(); } catch (IOException e){ System.out.println(e); } } } }
键盘录入数据
System.out:标准输出设备 控制台
System.in:标准输入设备 键盘
流操作的基本规律:
1.明确源和目的
源:输入流
目的:输出流
2.操作的数据是否是纯文本
是:字符流
否:字节流
3.明确使用那些具体对象
源设备:内存,硬盘,键盘
目的设备:内存,硬盘,控制台
需求:使用键盘输入数据,并用控制台打出,然后保存为一个文本文件
扩展:想要把录入的数据按照指定编码表(utf-8)存入文件。
class TransInDemo{ public static void main(String[] args) throws IOException{ //回去键盘录入对象 InputStream in = System.in; //将字节流转换成字符流 InputStreamReader isr = new InputStreamReader (in); //为了提高效率 将字符串进行缓冲区技术 BufferedReader buf = new BufferedReader (isr); //同理设置使用了缓冲技术的字节流转字符流输出 BufferedWriter bufw = //将输出流与控制台关联 new BufferedWriter (new OutputStreamWriter(System.out)); BufferedWriter bufw1= //将输出流与写入文件相关联,并定义其编码为utf-8 new BufferedWriter (new OutputStreamWriter(new FileOutputStream("d:\demo05.txt",true),"utf-8")); String line = null; //定义空字符串line while ((line=buf.readLine())!=null){ //将输入流种数据按行读取写入字符串line,并判断是否读取至数据末尾 if ("over".equals(line)) //定义关闭命令 break; bufw.write(line); //向控制台写入line中的数据 bufw1.write(line); //向关联的文件中写入line的数据 bufw.newLine(); //换行 bufw1.newLine(); bufw.flush(); //刷新内存,将输出缓冲区中的数据写入目的地 bufw1.flush(); } buf.close(); //关闭缓冲区 bufw1.close(); bufw.close(); } }