——-android培训、java培训、期待与您交流! ———-
一、IO(Input,Output)流
特点:
1.IO流用来处理设备间的数据传输。
2.Java对数据的操作是通过流的方式。
3.Java用于操作流的对象都在IO包中。
4.输入流和输出流相对于内存设备而言
5. 流按流向分为:输入流(将外设中的数据读取到内存中)和输出流(将内存的数写入到外设中)。
6. 流按操作数据分为:字节流和字符流(由来:是基于字节流而来的。字节流读取文字字节数据后,不直接操作而是先查指定的编码表,获取对应的文字。再对这个文字进行操作。简单说:字节流+编码表)。
7. 流按功能分为:节点流(直接操作目标设备)和处理流(通过操作节点流从而完成输入/输出功能的流,是建立在存在的输入/出流之上)。
IO流的常用基类:
1)字节流的抽象基流:InputStream和OutputStream
2)字符流的抽象基流:Reader和Writer
注:此四个类派生出来的子类名称都是以父类名作为子类名的后缀,以前缀为其功能。
如:InputStream的子类FileInputStream;Reader的子类FileReader。
IO流是用于操作数据的。数据的最常见的体现形式是:文件。以操作文件为例:
示例:在硬盘上,创建一个文件并写入一些文字数据
import java.io.*; class IODemo1 { public static void main(String[] args) { write(); } //在硬盘上,创建一个文件并写入一些文字数据 public static void write() { FileWriter fw=null; try { //创建一个FileWriter对象,该对象一初始化就必须明确被操作的文件 //而且该文件会被创建在指定的目录下,如果该目录下已经有同名文件,将被覆盖 //如果不存在,就会自动创建,其实这步就是在明确数据要存放的目的地 fw=new FileWriter("demo.txt"); //调用write方法,将数据写入到流中 fw.write("addsa"); //刷新流对象中的缓冲区数据,将数据刷新到目的地 fw.flush(); } catch (IOException ex) { throw new RuntimeException("写入流失败"); } finally { try { if(fw!=null) { //关闭流资源,但是在关闭前会先调用flush刷新缓冲中的数据到目的地 //和flush的区别:flush刷新后流可以继续用,close刷新后流将会关闭 fw.close(); } } catch (IOException ex) { throw new RuntimeException("关闭流失败"); } } } }
运行结果为:
如果想对已有文件的数据进行续写,可以对FileWriter对象进行传参数true。
如:FileWriter fw=new FileWriter("demo.txt",true); //传递一个true参数,代表不覆盖原有数据,并可以在原数据末尾出进行数据续写。
文本文件的读取
方式一:一个字符一个字符的读
import java.io.*; class IODemo1 { public static void main(String[] args) { //write(); reader_1(); } public static void reader_1() { FileReader fr=null; try { //创建一个文件读取对象,和指定名称的文件相关联 //要保证文件存在,如果不存在,会放生异常 fr=new FileReader("demo.txt"); int ch=0; while((ch=fr.read())!=-1)//read():返回值是整数,当到达末尾时为-1 {//read():一次读一个字符,而且会自动往下读 System.out.print((char)ch); } } catch (IOException ex) { throw new RuntimeException("读取流失败"); } finally { try { if(fr!=null) { fr.close(); } } catch (IOException ex) { throw new RuntimeException("关闭流失败"); } } } }
运行结果为:
方式二:通过字符数组进行读取
import java.io.*; class IODemo1 { public static void main(String[] args) { //write(); reader_2(); } public static void reader_2() { FileReader fr=null; try { fr=new FileReader("demo.txt"); //定义一个字符数组,用于存储读到的数据 //read(char[]):返回的是读到的字符个数 char[] buf=new char[1024]; int num=0; while((num=fr.read(buf))!=-1) { System.out.print(new String(buf,0,num)); } } catch (IOException ex) { throw new RuntimeException("读取流失败"); } finally { try { if(fr!=null) { fr.close(); } } catch (IOException ex) { throw new RuntimeException("关闭流失败"); } } } }
运行结果如上。
二、字符流的缓冲区——BufferedReader和BufferedWriter
缓冲区的出现是为了提高流的操作效率。所以在创建缓冲区之前必须要先有流对象。而且该缓冲区提供了一个跨平台的换行符:newLine()。
示例:通过示例演示缓冲区的使用
/*需求:通过缓冲区来复制一个java文件*/ import java.io.*; class IO1903 { public static void main(String[] args) { copy(); } public static void copy() { FileReader fr=null; FileWriter fw=null; BufferedReader br=null; BufferedWriter bw=null; try { //创建一个字符读取流对象,和文件相关联 fr=new FileReader("Date1804.java"); //创建一个字符写入流对象 fw=new FileWriter("1.txt"); //为了提高读写流的效率,加入了缓冲区 //只要将需要被提高效率的流对象作为参数传递给缓冲区的构造函数即可 br=new BufferedReader(fr); bw=new BufferedWriter(fw); String line=null; while((line=br.readLine())!=null)//读取一行数据,读到末尾为null { bw.write(line); bw.newLine(); } } catch (IOException e) { System.out.println(e.toString()); } finally { try { if(bw!=null) bw.close();//其实关闭缓冲区就是在关闭缓冲区中的流对象 } catch (IOException e) { System.out.println(e.toString()); } try { if(br!=null) br.close(); } catch (IOException e) { System.out.println(e.toString()); } } } }
运行结果为:
注:1.readLine方法返回的时候只返回回车符之前的数据内容,并不返回回车符。
2.readLine方法的原理:无论是读一行获取读取多个字符,其实最终都是在硬盘上一个一个的读,所以最终使用的还是read()一次读一个的方法。
示例:模拟readLine()的功能
/*模拟readLine()的功能*/ import java.io.*; class MyBufferedReader { private FileReader fr; MyBufferedReader(FileReader fr) { this.fr=fr; } public String myReadLine()throws IOException { //定义一个容器存在字符 StringBuilder sb=new StringBuilder(); int ch=0; while((ch=fr.read())!=-1) { if(ch=='\r') continue; if(ch=='\n') return sb.toString(); else sb.append((char)ch); } if(sb.length()!=0) return sb.toString(); return null; } public void close()throws IOException { fr.close(); } } class IO1904 { public static void main(String[] args) { MyBufferedReader bufr=null; BufferedWriter bufw=null; try { bufr=new MyBufferedReader(new FileReader("demo.txt")); bufw=new BufferedWriter(new FileWriter("1.txt")); String line=null; while((line=bufr.myReadLine())!=null) { bufw.write(line); bufw.newLine(); bufw.flush(); } } catch (IOException ex) { throw new RuntimeException("读写流失败"); } finally { try { if(bufw!=null) { bufw.close(); } } catch (IOException ex) { throw new RuntimeException("关闭写入流失败"); } try { if(bufr!=null) { bufr.close(); } } catch (IOException ex) { throw new RuntimeException("关闭读取流失败"); } } } }
三、装饰设计模式
当想要对已有的对象进行功能增强时,可以定义类,将已有对象传入,基于已有的功能,并提供增强功能,那么自定义的该类称为装饰类。如BufferedReader,上述的MyBufferedReader
装饰类通常会通过构造函数方法接收装饰类的对象,并基于被装饰的对象功能提供更强的功能。
装饰类和继承的区别:
1.装饰模式比继承要灵活。避免了继承体系的臃肿,且降低了类与类之间的关系。
2.装饰类因为增强已有对象,具备的功能和已有的是相同的,只不过提供了更强的功能,所以装饰类和被装饰的类通常都是属于一个体系。
3.从继承结构转为组合结构。
注:在定义类的时候,不要以继承为主;可通过装饰设计模式进行增强类功能。灵活性较强,当装饰类中的功能不适合,可再使用被装饰类的功能。
例如:带行号的装饰类
FileReader fr=new FileReader();
LineNumberReader lnr=new LineNumberReader(fr);
String line=null;
lnr.setLineNumber(100);
while((line=lnr.readLine)!=null)
{
System.out.println(lnr.getLineNumber()+":"+line);
}
lnr.close();练习:模拟带行号的装饰类
/*模拟LineNumberReader装饰类,带行号的装饰类*/ import java.io.*; import java.util.*; class MyLineNumberReader { private FileReader fr; public int LineNumber; public MyLineNumberReader(FileReader fr) { this.fr=fr; } public String MyLine()throws IOException { int ch=0; LineNumber++;//读一行加一个 StringBuilder sb=new StringBuilder(); while((ch=fr.read())!=-1) { if(ch==13)//13为:'\r' continue; if(ch==10)//10为:'\n' return sb.toString(); else sb.append((char)ch); } if(sb.length()!=0) return sb.toString(); return null; } public void setLineNumber(int num) { LineNumber=num; } public int getLineNumber() { return LineNumber; } public void close()throws IOException { fr.close(); } } class LineNumberReader1910 { public static void main(String[] args) { FileReader fr=null; MyLineNumberReader mylnr=null; try { fr=new FileReader("Date1804.java"); //fr=new FileReader("1.txt"); mylnr=new MyLineNumberReader(fr); String line=null; mylnr.setLineNumber(3); while((line=mylnr.MyLine())!=null) { System.out.println(mylnr.getLineNumber()+"::"+line); } } catch (IOException e) { System.out.println(e.toString()); } finally { try { if(mylnr!=null) mylnr.close(); } catch (IOException e) { System.out.println(e.toString()); } } } }
运行的部分结果为:
四、字节流
字节流的基本操作与字符流类相同。但它不仅可以操作字符,还可以操作其他媒体文件。
字节流可以不用进行刷流动作:因为字节流操作的是字节,即数据的最小单位,不需要像字符流一样要进行转换为字节。所以可直接将字节数据写入到指定文件中。
示例:复制一个mp3
/*需求:复制一个图片,mp3*/ import java.io.*; class IO1912 { public static void main(String[] args) { copyPic(); } public static void copyPic() { FileInputStream fin=null; FileOutputStream fout=null; try { fin=new FileInputStream("D:\\Mine_Huan\\2.mp3"); fout=new FileOutputStream("D:\\Mine_Huan\\12.mp3"); //读取方式: //方式一: byte[] buf=new byte[1024]; int len=0; while((len=fin.read(buf))!=-1) { fout.write(buf,0,len); } /*//方式二: int ch=0; while((ch=fin.read())!=-1) { fout.write((char)ch); }*/ /*//方式三: if(fin.available()>1024*1024) throw new IOException("文件过大"); byte[] buf=new byte[fin.available()]; //定义刚刚好大小的缓冲区,不用再循环了。但小心系统崩溃 System.out.println(fin.available()); fin.read(buf); fout.write(buf);*/ } catch (IOException e) { System.out.println(e.toString()); } finally { try { if(fout!=null) fout.close(); } catch (IOException e) { System.out.println(e.toString()); } try { if(fin!=null) fin.close(); } catch (IOException e) { System.out.println(e.toString()); } } } }
字节流的缓冲区:BufferedInputStream和BufferedOutputStream
同样是为了提高了字节流的读写效率。
读写特点:
read():返回值是int类型,会将字节byte型值提升为int型值
write():会将int型强转为byte型,即保留二进制数的最后八位。
五、键盘录入
键盘本身就是一个标准的输入设备。对于java而言,对于这种输入设备都有对应的对象:System.in
示例:当录入一行数据后,就将该行数据进行打印,如果输入over就结束录入
import java.io.*; class IOKey1915 { public static void main(String[] args) throws IOException { InputStream in=System.in; StringBuilder sb=new StringBuilder();//定义一个容器,用于存储读到的字符 while(true) { int ch=in.read(); if(ch=='\r') continue; if(ch=='\n') { String s=sb.toString(); if(s.equals("over"))//判断是否录入over break; System.out.println(s.toUpperCase()); sb.delete(0,s.length());//清空 } else sb.append((char)ch); } } }
运行结果为:
通过键盘录入一行数据并打印其大写,发现其实就是读一行数据的原理:readLine方法。可是readLine方法是字符流BufferedReader类中方法。而键盘录入的read方法是字节流InputStream的方法。所以想要使用readLine方法,要将字节流转换成字符流,要用到转换流:InputStreamReader,OutputStreamWriter。
示例:将上例使用转换流实现
import java.io.*; class IOKey1915 { public static void main(String[] args) throws IOException { //使用读取转换流,将字节流转换成字符流 InputStream in=System.in; InputStreamReader isr=new InputStreamReader(in); BufferedReader bufr=new BufferedReader(isr); //使用写入转换流,将字节流转换成字符流 OutputStream out=System.out; OutputStreamWriter iso=new OutputStreamWriter(out); BufferedWriter bufw=new BufferedWriter(iso); while(true) { //使用字符流中特有的readLine方法 String line=bufr.readLine(); if(line.equals("over")) break; //使用字符流中特有的newLine方法 bufw.write(line.toUpperCase()); bufw.newLine(); bufw.flush(); } } }
键盘录入的常见写法:
BufferedReader bufr=new BufferedReader(new InputStreamReader(System.in));也可写成:Scanner scan=new Scanner(System.in);
流操作的基本规律:通过三个明确来确定采用哪个流对象。
1.明确源和目的
源:输入流——InputStream,Reader
目的:输出流——OutputStream,Writer
2.明确操作的数据是否是纯文本
是:字符流
不是:字节流
3.当明确体系后,再明确使用哪一个具体的对象,通过设备来进行区分:
源设备有:内存(如:ArrayStream)、硬盘(如:FileStream)、键盘(如:System.in)、 网络:Socket流
目的设备:内存(如:ArrayStream)、硬盘(如:FileStream)、控制台(如:System.out)、 网络:Socket流
例如:
1.需求:将键盘录入的数据存储到一个文件中
分析:源:键盘;是否是纯文本:不是;使用设备:键盘
目的:文件;是否是纯文本:是;存储到一个文件中:使用设备硬盘
源:BufferedReader bufr=newBufferedReader (new InputStreamReader(System.in));
目的:BufferedWriter bufw=newBufferedWriter (new OutputStreamWriter(new FileOutputStream("xxx.txt")));
2.需求:将一个文件的数据打印到控制台上
分析:源:文件;目的:控制台;
源:BufferedReader bufr=new BufferedReader (new InputStreamReader(new FileInputStream("xxx.txt")));
目的:BufferedWriter bufw=new BufferedWriter (new OutputStreamWriter(System.out));
3.需求:将文本文件中的数据存储到另一个文件中,即复制文件
分析:源:文件,所以使用字符流,Reader,使用设备:硬盘
目的:文件;所以使用字符流,Writer,使用设备:硬盘
源:BufferedReader bufr=new BufferedReader (new FileReader("xxx.txt"));
目的:BufferedWriter bufw=new BufferedWriter (new FileWriter("xxx.txt"));
4.需求:将键盘录入的数据保存到一个文件中
分析:源:InputStream,Reader;是否是纯文本:是,Reader;设备:键盘,对应的对象是System.in
目的:OutputStream,Writer;是否是纯文本:是,Writer;设备:硬盘,一个文件,使用FileWriter
源:BufferedReader bufr=new BufferedReader (new InputStreamReader(System.in));
目的:BufferedWriter bufw=new BufferedWriter (new FileWriter("xxx.txt"));
扩展:
5.需求:将键盘录入的数据按指定的编码表utf-8,将数据存储到文件中
分析:源:InputStream,Reader;是否是纯文本:是,Reader;设备:硬盘,对应的对象是FileReader
目的:OutputStream,Writer;是否是纯文本:不是,OutputStream;设备:控制台,使用FileOutputStream
源:BufferedReader bufr=new BufferedReader (new FileReader("xxx.txt"));
目的:BufferedWriter bufw=new BufferedWriter (new OutputStreamWriter(newFileOutputStream("xxx.txt"),"utf-8"));
注:转换流是字节流和字符流之间的桥梁,通常涉及到字符编码转换时要用到转换流。