IO概述
概述
1、IO流:即InputOutput的缩写。
2、特点:
1)IO流用来处理设备间的数据传输。
2)Java对数据的操作是通过流的方式。
3)Java用于操作流的对象都在IO包中。
4)流按操作数据分为两种:字节流和字符流。
5)流按流向分为:输入流和输出流。
注意:流只能操作数据,而不能操作文件。
3、IO流的常用基类:
1)字节流的抽象基流:InputStream和OutputStream
2)字符流的抽象基流:Reader和Writer
注:此四个类派生出来的子类名称都是以父类名作为子类名的后缀,以前缀为其功能;
如 InputStream子类FileInputStream;
Reader子类FileReader
第二讲 字符流
一、简述
1、字符流中的对象融合了编码表。使用的是默认的编码,即当前系统的编码。
2、字符流只用于处理文字数据,而字节流可以处理媒体数据。
3、既然IO流是用于操作数据的,那么数据的最常见体现形式是文件。查看API,找到一个专门用于操作文件的Writer子类对象:FileWriter。 后缀是父类名。前缀名是流对象的功能。该流对象一被初始化,就必须有被操作的文件存在。
二、字符流的读写
1、写入字符流步骤
1 import java.io.*; 2 class FileWriterDemo 3 { 4 public static void main(String[] args )throws Exception 5 { 6 //创建一个FileWriter对象。该对象一被初始化就必须要明确被操作的文件。 7 //而且该文件会被创建到指定目录下。如果该目录下已有同名文件,将被覆盖。 8 //其实该步 就是明确 数据要存放的目的地 9 FileWriter fw=new FileWriter("demo.txt"); 10 11 //调用write方法,将字符串写入到流中 12 13 fw.write("abcde"); 14 15 //刷新流对象中的缓冲区中的数据 16 //将数据流刷到目的地中 17 //fw.flush(); 18 19 //关闭流资源,但是关闭之前会刷新一次内部的缓冲区的数据 20 //和flush区别,flush刷新后,流可以继续使用,close刷新后,会将流关闭 21 fw.close(); 22 23 } 24 }
注意:
1、其实java自身不能写入数据,而是调用系统内部方式完成数据的书写,使用系统资源后,一定要关闭资源。
即可以在finally中对流进行关闭资源。
2、文件的数据的续写是通过构造函数 FileWriter(Strings,boolean append),在创建对象时,传递一个true参数,代表不覆盖已有的文件。并在已有文件的末尾处进行数据续写。(windows系统中的文件内换行用\r\n两个转义字符表示,在linux系统中只用\n表示换行)
3、由于在创建对象时,需要指定创建文件位置,如果指定的位置不存在,就会发生IOException异常,所以在整个步骤中,需要对IO异常进行try处理。
思考:
1、有了垃圾回收机制为什么还要调用close方法进行关闭?
答:垃圾回收机制是不定时的,回收时间不是我们所能控制的,并且流不单单在内存中分配了空间,也在操作系统占有了资源,java的gc是能从内存中回收不 使用的对象,但对操作系统分配的资源是无能为力的,所以就要调用close()方法来通知OS来释放这个资源。
2、为什么IO异常一定要处理?
答:程序运行中出现异常就会终止程序, 导致后面的代码运行不到, 我们要是写的代码面向客户, 出现问题客户不一定知道为什么, 所以我们要对可能出现的
问题进行处理. 一旦发现可能出现问题的代码,而我们可以通过捕获异常,使异常后面的程序正常运行。即提高代码的健壮性。
2、读取字符流步骤
1)创建一个文件读取流对象,和指定名称的文件相关联。要保证该文件已经存在,若不存在,将会发生异常FileNotFoundException。
2)调用读取流对象的read()方法。read():一次读一个字符,且会继续往下读。
第一种方式:读取单个字符。第二种方式:通过字符数组进行读取。
3)读取后要调用close方法将流资源关闭。
示例:
1 //将C盘一个文本文件复制到D盘 2 3 /* 4 复制的原理: 5 其实就是将C盘下的文件数据存储到D盘的一个文件中。 6 7 步骤: 8 1 在D盘创建一个文件。用于存储C盘文件中的数据 9 2 定义读取流和Cpan文件关联 10 3 通过不断的读写完成数据存储 11 4 关闭资源 12 */ 13 import java.io.*; 14 class CopyTest 15 { 16 public static void main(String[] args) 17 { 18 copy_2(); 19 } 20 public static void copy_2() 21 { 22 FileWriter fw=null; 23 FileReader fr=null; 24 try 25 { 26 fw=new FileWriter("SystemDemo_copy.txt");//写入流关联文本文件 27 fr=new FileReader("SystemDemo.java");//读取流关联文本文件 28 char[] buf=new char[1024];//创造第三方 字节数组,使两个流能够联系起来 29 int len=0; 30 while((len=fr.read(buf))!=-1) //将读取流的字节存到字节数组中,只要没有读到-1(即结束标志),继续存 31 { 32 fw.write(buf,0,len);//将字节数组中的数据,通过写入流 写入 关联的文本文件 33 } 34 } 35 catch (IOException e) 36 { 37 throw new RuntimeException("读写失败"); 38 } 39 finally 40 { 41 if(fr!=null) 42 try 43 { 44 fr.close(); //将读取流的数据关闭 45 } 46 catch (IOException e) 47 { 48 } 49 if(fw!=null) 50 try 51 { 52 fw.close();//将写入流的数据刷到指定文件内,并关闭流资源 53 } 54 catch (IOException e) 55 { 56 } 57 } 58 } 59 60 61 public static void copy_1()throws IOException 62 { 63 //创建目的地 64 FileWriter fw=new FileWriter("Demo_copy1.txt"); 65 //与已有文件关联 66 FileReader fr=new FileReader("Demo.txt"); 67 int ch=0; 68 while((ch=fr.read())!=-1)//一个字节一个字节读的,而且会自动往下读 69 { 70 fw.write(ch); 71 } 72 fw.close(); 73 fr.close(); 74 } 75 76 }
注意:
1、定义文件路径时,可以用“/”或者“\\”。
2、在创建一个文件时,如果目录下有同名文件将被覆盖。
3、在读取文件时,必须保证该文件已存在,否则出异常。
三、字符流的缓冲区——BufferedReader和BufferedWriter
1、缓冲区的出现:提高了流的读写效率,所以在缓冲区创建前,要先创建流对象。即先将流对象初始化到构造函数中。
2、缓冲技术原理:此对象中封装了数组,将数据存入,再一次性取出。
3、写入流缓冲区BufferedWriter的步骤:
1 /*缓冲区的出现是为了提高流的操作效率而出现的 2 3 所以在创建缓冲区之前,必须要先有流对象 4 5 该缓冲区中提供了一个跨平台的换行符 6 newline() 7 */ 8 import java.io.*; 9 class BufferedWriterDemo 10 { 11 public static void main(String[] args)throws IOException 12 { 13 //创建一个字符流写入流对象 14 15 FileWriter fw=new FileWriter("buf.txt"); 16 //为了提高字符写入流的效率。加入了缓冲技术 17 //只要将需要被提高效率的流对象作为参数传递给缓冲区的构造函数即可 18 BufferedWriter bufw=new BufferedWriter(fw); 19 20 for(int x=1;x<5;x++) 21 { 22 bufw.write("abcd"+x); 23 bufw.newLine(); 24 bufw.flush(); 25 } 26 //记住,只要用到缓冲区,就要记得刷新 27 //bufw.flush(); 28 //其实关闭缓冲区,就是在关闭缓冲区中的流对象。 29 bufw.close(); 30 31 } 32 }
小知识:BufferedWriter缓冲区中提供了一个跨平台的换行符:newLine();可以在不同操作系统上调用,用作数据换行。
如:bufw.newLine();
4、读取流缓冲区BufferedReader
该缓冲区提供了一个一次读一行的方法readLine,方便于堆文本数据的获取,当返回null时表示读到文件末尾。readLine方法返回的时候,只返回回车符之前的数据内容。并不返回回车符。
readLine方法原理:
无论是读一行。或者读取多个字符。其实最终都是在在硬盘上一个一个读取。所以最终使用的还是read方法一次读一个的方法。
步骤:
1)创建一个读取流对象和文件相关联
如: FileReader fr=newFileReader("buf.txt");
2)为了提高效率。加入缓冲技术。将字符读取流对象作为参数传递给缓冲区对象的构造函数。
如: BufferedReader bufr=new BufferedReader(fr);
3)调用该缓冲区提供的readLine方法一行一行读取,如果到达文件末尾,则返回null
如: String s=bufr.readLine();
4)关闭流资源
如: bufr.close();、
示例:通过缓冲区复制一个文本文件。
1 /* 2 3 通过缓冲区复制一个。java文件*/ 4 import java.io.*; 5 class CopyTextBybuf 6 { 7 public static void main(String[] args) 8 { 9 BufferedReader bufr=null;//为了防止异常的产生,首先要创建缓冲区 10 BufferedWriter bufw=null; 11 try 12 { 13 bufr=new BufferedReader(new FileReader("BufferedWriterDemo.java"));//将关联文件后的读写流都放到缓冲区中 14 bufw=new BufferedWriter(new FileWriter("bufWriter_Copy.txt")); 15 16 String line=null; 17 while((line=bufr.readLine())!=null)//缓冲区提供了一次读一行的方法 18 { 19 bufw.write(line); 20 bufw.newLine(); 21 bufw.flush();//刷新缓冲区 22 } 23 } 24 catch (IOException e) 25 { 26 throw new RuntimeException("读写失败"); 27 } 28 finally 29 { 30 try 31 { 32 if(bufr!=null) 33 bufr.close(); 34 } 35 catch (IOException e) 36 { 37 throw new RuntimeException("读取关闭失败"); 38 } 39 40 try 41 { 42 if(bufw!=null) 43 bufr.close(); 44 } 45 catch (IOException e) 46 { 47 throw new RuntimeException("写入关闭失败"); 48 } 49 } 50 } 51 }
5、自定义BufferedReader:
原理:
可根据BufferedReader类中特有一行一行读取方法readLine()的原理,自定义一个类中包含相同功能的方法
步骤:
a、初始化自定义的类,加入流对象。
b、定义一个临时容器,原BufferedReader封装的是字符数组,此类中可定义一个StringBuilder的容器,最终可实现字符串的提取。
1 /* 2 明白了BufferedReader 类中特有方法 readLine() 的原理后, 3 可以自定义一个类中包含一个功能和readLine 一致的方法 4 5 用来模拟一下 BufferedReader 6 */ 7 import java.io.*; 8 class MyBufferedReader extends Reader 9 { 10 private Reader r; 11 MyBufferedReader(Reader r) 12 { 13 this.r=r; 14 15 } 16 //可以一次读一行 数据的方法 17 public String myReaderLine() throws IOException 18 { 19 //定义一个临时容器。 原BufferReader 封装的是字符数组 20 //为了演示方便。定义一个StringBuilder容器 21 //因为最终还是要将数据变成字符串 22 StringBuilder sb=new StringBuilder();//创建字符串容器,用于存储文本中的字符 23 int ch=0; 24 while((ch=r.read())!=-1)//一次读一个,但是没有遇到回车符时,会暂时先存储在StringBuilder中 25 { 26 if(ch==‘\r‘) 27 continue; 28 if(ch==‘\r‘) 29 return sb.toString(); 30 else 31 sb.append((char)ch); 32 } 33 if(sb.length()!=0) 34 return sb.toString(); 35 return null; 36 37 38 } /* 39 因为MyBufferedReader extends Reader,而Reader中存在抽象方法 40 所以我们要覆盖Reader类中的抽象方法。 41 42 */ 43 public int read(char[] cbuf, int off, int len) throws IOException 44 { 45 return r.read(cbuf,off,len) ; 46 } 47 48 public void close()throws IOException 49 { 50 r.close(); 51 } 52 public void myClose()throws IOException 53 { 54 r.close(); 55 } 56 } 57 class MyBufferedReaderDemo 58 { 59 public static void main(String[] args)throws IOException 60 { 61 FileReader fr=new FileReader("buf.txt");//字符读取流关联文本文件 62 MyBufferedReader myBuf=new MyBufferedReader(fr);//建立读取缓冲区容器,将字符读取流放入 63 String line=null; 64 while((line=myBuf.myReaderLine())!=null) //实现自定义读取方式 65 { 66 System.out.println(line);//打印出每行 读取到的内容 67 } 68 69 myBuf.myClose();//关闭 读取缓冲区 70 } 71 }
四、装饰设计模式
1 /* 2 装饰设计模式: 3 当想要对已有的对象进行功能增强时, 4 可以定义类,将已有对象传入,基于已有的功能,并提供加强功能。 5 那么自定义的该类称为装饰类 6 7 装饰类通常会通过构造函数方法接收被装饰的对象。 8 9 并基于被装饰的对象的功能,提供更强的功能 10 */ 11 class Person 12 { 13 public void chifan() 14 { 15 System.out.println("吃饭"); 16 } 17 public void wc() 18 { 19 System.out.println("go to wc"); 20 } 21 } 22 class SuperPerson 23 { 24 private Person p; 25 SuperPerson(Person p) 26 { 27 this.p=p; 28 } 29 public void superChifan() 30 { 31 32 System.out.println("开胃酒"); 33 p.chifan(); 34 p.wc(); 35 System.out.println("甜点"); 36 System.out.println("来一根"); 37 } 38 } 39 class PersonDemo 40 { 41 public static void main(String[] args) 42 { 43 Person p=new Person(); 44 //p.chifan; 45 SuperPerson sp=new SuperPerson(p); 46 sp.superChifan(); 47 } 48 }
装饰和继承的区别:
1)装饰模式比继承要灵活。避免了继承体系的臃肿,且降低了类与类之间的关系。
2)装饰类因为增强已有对象,具备的功能和已有的是相同的,只不过提供了更强的功能,所以装饰类和被装饰的类通常都是属于一个体系。
3)从继承结构转为组合结构。
注:在定义类的时候,不要以继承为主;可通过装饰设计模式进行增强类功能。灵活性较强,当装饰类中的功能不适合,可再使用被装饰类的功能。