黑马程序员-java基础(七)-IO流

来源:互联网 发布:印度 巴基斯坦 知乎 编辑:程序博客网 时间:2024/05/20 14:18

------- android培训、java培训、期待与您交流! ----------

IO流

1  IO流

IO流(Input Output)用来处理设备之间的数据传输。Java对数据的操作是通过流的方式。
按操作数据分:字节流、字符流;字节流抽象基类:InputStream,OutStream;字符流抽象基类:Reader,Writer
按流向分:输入流,输出流
输入输出都是相对于内存而言:
输入:将外设中的数据读取到内存中
输出:将内存的数写入到外设中
p.s.
字符流其实其实就是:字节流读取文字字节数据后,不直接操作而是先查指定的编码表,获取对应的文字。再对这个文字进行操作。简单说:字节流+编码表。

2  字符流

1、创建文件写入字符
FileWriter(String fileName)
          根据给定的文件名构造一个 FileWriter 对象。
import java.io.*;class  Test{public static void main(String[] args) throws IOException{FileWriter fw=new FileWriter("Demo.txt"); //创建一个FileWriter对象,该对象一初始化被徐有明确被操作文件,有同名文件会直接覆盖fw.write("ok,success");//调用write方法,将字符串写进流,待刷新后写入目的地fw.close();//刷新流对象的缓存中的数据到目的地中,也可以用flush刷新,但是close刷新后会关闭流}}

2、文件续写

/*已有文件续写*/import java.io.*;class  Test{public static void main(String[] args) throws IOException{FileWriter fw=new FileWriter("Demo.txt",true); fw.write("\r\nWrite again");fw.close();}}



3.IO异常处理:
/*IO异常处理方式*/import java.io.*;class  FileWriterDemo2{public static void main(String[] args) throws IOException{FileWriter fw=null ; try{fw=new FileWriter("FileWriter2.txt");fw.write("ok,success");}catch (IOException e){System.out.println("catch:"+e.toString());}finally{try{if(fw!=null)fw.close();}catch (IOException e){System.out.println(e.toString());}}}}

4、字符流文件读取
第一种方式:读单个字符reader()
import java.io.*;class  Test{public static void main(String[] args) throws IOException{FileReader fr=new FileReader("Demo.txt");//创建一个文件读取流对象,且保证文件存在while (true){int ch=fr.read();                    //一次读一个字符,且会自动往下读,读到末尾则返回-1if (ch==-1)break;System.out.println("ch="+(char)ch);//强转为char型}fr.close();}}
运行结果:

第二种方式:通过字符数组进行reader(char[] cbuf)
import java.io.*;class Test{    public static void main(String[] args) throws IOException    {        FileReader fr=new FileReader("Demo.txt");        char[] buf=new char[1024];        int len=0;        while((num=fr.read(buf))!=-1)//read([])返回字符个数        {        System.out.println(new String (buf,0,len));        }        fr.close();    }}
5、字符流缓冲区(BufferReader、BufferWriter)
在流的基础上对流的功能进行增强,提高对数据的读写效率
P.S.
缓冲区要结合字符流才可以使用
/*BufferedReader、BufferedWriter*/import java.io.*;class  Test{public static void main(String[] args) throws IOException{FileWriter fw=new FileWriter("Demo.txt"); //建立字符写入流对象BufferedWriter bw=new BufferedWriter(fw);//建立字符写入流流对应的缓冲区对象for(int i=0;i<4;i++){bw.write("This is BufferedWriter"+i);//用字符缓冲区一次写入一个字符串bw.newLine();//写入一个行分割符}bw.close();FileReader fr=new FileReader("Demo.txt"); //建立字符读取流对象BufferedReader br=new BufferedReader(fr);//建立字符读取流对应的缓冲区对象String line;while((line=br.readLine())!=null){//一次读取一行System.out.println(line);}br.close();}}

P.S.
readLine()方法原理:


【自定义读一行】
/*自定义读一行 MyReaderDemo*/import java.io.*;class MyReader {private FileReader f;MyReader(FileReader f)//输入流对象{this.f=f;}        public String MyReaderLine() throws IOException//读一行方法{StringBuilder sb=new StringBuilder();//定义一个临时容器StringBuilderint ch=0;while((ch=f.read())!=-1){  if(ch=='\r')continue;if(ch=='\n')//判断是否到行结尾return sb.toString();//将字符串输出 sb.append((char)ch);//读一个字符存入容器中}           if(sb.length()!=0)//防止最后一行没有换行符,造成数据丢失                  return sb.toString();         return null;}public void MyClose() throws IOException{f.close();}}class MyReaderDemo {public static void main(String[] args) throws IOException{FileReader fr=new FileReader("demo.txt");        MyReader mr=new MyReader(fr);String line=null;while((line=mr.MyReaderLine())!=null){System.out.println(line);//打印一行}mr.MyClose();}}
6、装饰设计模式
在原有类进行功能改变,增强已有对象的功能(如上:自定义的可以读一行类)
/*装饰类和继承的区别*/class Person{    void chifan(){         System.out.println( "吃饭");   }}//采用装饰的方式增强Person类//这个类的出现是为了增强Person而出现的class NewPerson{    private Person p;    NewPerson(Person p){          this.p = p;   }   public void chifan(){   p.chifan();   System.out.println( "开胃酒");   System.out.println( "甜点");   }}//采用继承的方式增强Person类class NewPerson2 extends Person{    public void chifan(){    super.chifan();     System.out.println( "开胃酒");    System.out.println( "甜点");   }}public class Test{    public static void main(String[] args){         Person p = new Person();         NewPerson np1 = new NewPerson(p);         np1.chifan();         System.out.println( "---------------");         NewPerson2 np2 = new NewPerson2();         np2.chifan();   }}
运行结果:

P.S.

装饰体系较继承体系灵活,避免了装饰体系的臃肿,降低了类与类之间的关系


3  字节流

1、基本操作与字符流类相同。但它不仅可以操作字符,还可以操作其他媒体文件。
/*字节流输入、输出基本操作*/public class Test{    public static void main(String[] args)throws IOException    {    writeStream();    readStream1();    System.out.println();    System.out.println("--------");    readStream2();    System.out.println();    System.out.println("--------");    readStream3();    }    public static void writeStream()throws IOException    {    FileOutputStream fos=new FileOutputStream("Demo.txt");    //getBytes()使用平台的默认字符集将此 String 编码为 byte 序列    fos.write("hallo java".getBytes());//字节为最小操作单位不用flush刷新,但要关流    fos.close();    }    //读取方式一:一次读取一个字节    //比较慢    public static void readStream1()throws IOException    {    FileInputStream fis=new FileInputStream("Demo.txt");    int ch;    while((ch=fis.read())!=-1){//从输入流中读取一个字节    System.out.print((char)ch);    }    }    //读取方式二:定义一个1024的字节,一次读1024个字节    //较合理,建议使用    public static void readStream2()throws IOException    {    FileInputStream fis=new FileInputStream("Demo.txt");    byte[] buf=new byte[1024];    int len=0;    while((len=fis.read(buf))!=-1){//从输入流中读取一定数量的字节,并将其存储在缓冲区数组 b 中    System.out.print(new String(buf,0,len));    }    }    //通过available(),获取字节长度,定义一个刚好的字节数组存放    //如果字节长度过长,容易造成溢出    public static void readStream3()throws IOException    {    FileInputStream fis=new FileInputStream("Demo.txt");    byte[] buf=new byte[fis.available()];    fis.read(buf);    System.out.print(new String(buf,0,buf.length));    }}
运行结果:
/*复制一个图片*/import java.io.*;public class Test{    public static void main(String[] args)throws IOException    {    FileInputStream fis=new FileInputStream("123.jpg");//创建输入字节流,关联文件    FileOutputStream fos=new FileOutputStream("123_copy.jpg");//创建输出字节流,关联文件    byte[] buf=new byte[1024];    int len=0;    while((len=fis.read(buf))!=-1){    fos.write(buf);//每次存储2kb数据    }}}

2、字节流缓冲区:
BufferedOutputStream、BufferedInputStream(和字符流缓冲区使用方法一致)
/* * 需求:拷贝一个mp3文件,并比较几种方式的效率 * */import java.io.BufferedInputStream;import java.io.BufferedOutputStream;import java.io.FileInputStream;import java.io.FileOutputStream;import java.io.IOException;public class Test1{       public static void main(String[] args) throws IOException {           long s1=System.currentTimeMillis();        copy_1();//一次读取1kb到缓冲数组中,然后输出       long s2=System.currentTimeMillis();             copy_2();//一次读取一个字节,加入缓冲计数BufferInputStream            long s3=System.currentTimeMillis();             copy_3();//一次读取一个节,不使用缓冲区            long s4=System.currentTimeMillis();            System.out.println("1:"+(s2-s1)+"ms...2:"+(s3-s2)+"ms...2:"+(s4-s3)+"ms");      }       public static void copy_1() throws IOException {            FileInputStream fis = new FileInputStream("1.mp3" );            FileOutputStream fos = new FileOutputStream("1_copy.mp3" );                   byte[] buf = new byte[1024];             int len = 0;             while((len = fis.read(buf)) != -1){                  fos.write(buf,0,len);            }            fis.close();            fos.close();      }       public static void copy_2() throws IOException {            FileInputStream fis = new FileInputStream("2.mp3" );            BufferedInputStream bufis = new BufferedInputStream(fis);            FileOutputStream fos = new FileOutputStream("2_copy.mp3" );            BufferedOutputStream bufos = new BufferedOutputStream(fos);             int ch = 0;             //一次读取一个             while((ch = bufis.read()) != -1){                  bufos.write(ch);            }            bufis.close();            bufos.close();      }       public static void copy_3() throws IOException {       FileInputStream fis = new FileInputStream("2.mp3" );           FileOutputStream fos = new FileOutputStream("3_copy.mp3" );           int ch = 0;            //一次读取一个            while((ch = fis.read()) != -1){                 fos.write(ch);           }           fis.close();           fos.close();       }}
运行结果


从以上结果,可以很明显看出拷贝效率:加入缓冲的字节数组>使用字节流缓冲区>字节流一次读取一个字节

3、读取键盘输入
System.in:标准输入设备:键盘
System.out:标准输出设备:控制台
/*获取用户键盘录入的数据并将数据变成大写显示在控制台上,如果用户输入的是over,结束键盘录入。1、键盘录入只读取一个字节,要判断是否为over,需要将读取的字节拼成字符串2、定义StringBuilder来存储3.在用户回车前,转换成字符串并判断*/import java.io.*;public class Test{    public static void main(String[] args)throws IOException    {    InputStream is=System.in;//System.in:返回标准输入流    StringBuilder sb=new StringBuilder();    int ch=0;    while(true){    ch=is.read();    if(ch=='\r')    continue;    if(ch=='\n'){    String s=sb.toString();    System.out.println(s.toUpperCase());//转换为大写输出        if("over".equals(s))//判断是否输入的是over    break;    sb.delete(0, sb.length());//存入一行后要初始化一下    }    else    sb.append((char)(ch));    }}}



4、转换流
字符流和字节流之间的桥梁,方便字节流和字符流之间的转换
转换流:
    InputStreamReader:字节到字符的桥梁,解码。
    OutputStreamWriter:字符到字节的桥梁,编码。

转换流的应用:
    字节流中的数据都是字符时,转成字符流操作更高效。
/*获取用户键盘录入的数据并将数据变成大写显示在控制台上,如果用户输入的是over,结束键盘录入。1、键盘录入是字节流,将字节流转换成字符流2、定义字符流的缓冲区,用readLine方法高效读取3、判断是否为over*/import java.io.*;public class Test{    public static void main(String[] args)throws IOException    {    InputStream is=System.in;//System.in:返回标准输入流    InputStreamReader isr=new InputStreamReader(is);//将字节输入流转换成字符输入流    BufferedReader br=new BufferedReader(isr);//建立字符输入流的缓冲区    while(true){    String line=br.readLine();    if("over".equals(line))    break;    System.out.println(line.toUpperCase());    }}}
P.S.
    使用字节流读取一个中文字符需要读取两次,因为一个中文字符由两个字节组成,而使用字符流只需读取一次。
import java.io.*;public class Test{    public static void main(String[] args)throws IOException    {    //将键盘输入字节流转换成字符流,并创建字符读取缓冲区    BufferedReader br=new BufferedReader(new InputStreamReader(System.in));    BufferedWriter bw=new BufferedWriter(new OutputStreamWriter(System.out));    String line;    while((line=br.readLine())!=null){    if("over".equals(line))    break;    bw.write(line.toUpperCase());    bw.newLine();    bw.flush();    }}}

5、改变标准输入输出设备
System类
static voidsetIn(InputStream in)
          重新分配“标准”输入流。static voidsetOut(PrintStream out)
          重新分配“标准”输出流。
/*改变标准输入、输出设备*/import java.io.*;public class Test{    public static void main(String[] args)throws IOException    {    System.setIn(new FileInputStream("Demo.txt"));//改变标准的输入设备    System.setOut(new PrintStream("Demo_copy.txt"));//改变标准输出设备    BufferedReader br=new BufferedReader(new InputStreamReader(System.in));    BufferedWriter bw=new BufferedWriter(new OutputStreamWriter(System.out));    String line;    while((line=br.readLine())!=null){    if("over".equals(line))    break;    bw.write(line.toUpperCase());    bw.newLine();    bw.flush();    }}}



P.S.
    字符流继承体系简图


    字节流继承体系简图


  4  File类 

File类用来将文件或者文件夹封装成对象,方便对文件与文件夹的属性信息进行操作。
File对象可以作为参数传递给流的构造函数。
import java.io.*;import java.io.File;public class Test{       public static void main(String[] args){            constructorDemo();      }       public static void constructorDemo(){             //可以将一个已存在的,或者不存在的文件或者目录封装成file对象             //方式一            File f1 = new File("d:\\demo\\a.txt" );             //方式二            File f2 = new File("d:\\demo" ,"a.txt" );                   //方式三            File f = new File("d:\\demo" );            File f3 = new File(f,"a.txt" );             //考虑到Windows和Linux系统通用            File f4 = new File("d:" + File.separator + "demo" + File.separator + "a.txt" );      }}

P.S
流只能操作数据不能操作文件
File.separator是与系统有关的默认名称分隔符。在 UNIX 系统上,此字段的值为 '/';在 Microsoft Windows 系统上,它为 '\\'。
【例】过滤指定目录中的文件
/* 需求:获取指定目录下后缀名为java的文件。思路:用list(FilenameFilter filter)对文件进行过滤*/import java.io.*;public class Test{       public static void main(String[] args){          File dir=new File("f:\\WJava\\Day18");//指定目录           String[] list=dir.list(new FilenameFilter(){//定义内部类获取FilenameFilter对象          public boolean accept(File dir, String name){//dir:目录 name:文件名称,返回真则取出        return name.endsWith(".java");//过滤.java文件          }          });          for(String name:list){          System.out.println(name);}       }}

递归:
函数自身直接或者间接的调用到了自身。
    一个功能在被重复使用,并每次使用时,参与运算的结果和上一次调用有关。这时可以用递归来解决问题。

 P.S.
1、递归一定明确条件,否则容易栈溢出。
2、注意一下递归的次数。
/* 列出目录下所有内容*/import java.io.*;public class Test{       public static void main(String[] args)       {       File dir=new File("f:\\WJava\\exam");        showFile(dir);       }       public static void showFile(File f)//定义一个打印目录下所文件的方法       {              File[] files=f.listFiles();       for(int i=0;i<files.length;i++){       System.out.println(files[i]);       if(files[i].isDirectory())       showFile(files[i]);//如果是一个目录,递归调用该方法              }              }}
【例】
/*  需求:利用递归求1到10的和。*/import java.io.*;public class Test{       public static void main(String[] args)       {              System.out.println(getSum(10));              }       public static int getSum(int num)       {       int sum=0;       if(num>=1)       sum=num+getSum(num-1);     return sum;       }}
运行结果:

【例】 需求:对指定目录进行删除
/*  需求:删除一个带内容的目录   思考:  1、目录里面有内容是无法删除,原理上必须从内往外删  2、定义一个删除指定目录中文件的方法(当存在目录时递归此方法)*/import java.io.*;public class Test{       public static void main(String[] args)       {              File dir=new File("f:\\exam");       removeDir(dir);              }       public static void removeDir(File f)       {       File[] files=f.listFiles();      for(int i=0;i<files.length;i++){      if(files[i].isDirectory())      removeDir(files[i]);      else      System.out.println(files[i].toString()+"::"+files[i].delete());//将文件删除      }      System.out.println(f.toString()+"::"+f.delete());//将文件夹删除       }}
运行结果:


5 Properties

一、概述

Properties是Hashtable的子类,它具备Map集合的特点。而且它里面还有存储的键值对,都是字符串,无泛型定义。是集合中和IO技术想结合的集合容器。
特点:

1)可用于键值对形式的配置文件

2)在加载时,需要数据有固定的格式,常用的是:键=值

二、特有方法

1、设置

Object setProperty(String key,String value);//设置键和值,调用Hashtable的方法put

2、获取

String getProperty(String key);//指定key搜索value

Set<String> stringPropertyName();//返回属性列表的键集,存入Set集合

3、加载流和存入流

void load(InputStream ism);//从输入字节流中读取属性列表(键和元素对)。又称将流中的数据加载进集合。

void load(Readerreader);//从输入字符流中读取属性列表(键和元素对)。又称将流中的数据加载进集合。

voidlist(PrintStream out);//将属性列表输出到指定的输出流

void store(OutputStreamout,String comments);//对应load(InputStream )将属性列表(键值对)写入输出流。comments属性列表的描述。

void store(Writerwriter, String comments);//对应load(Reader)将属性列表(键值对)写入输出流。comments属性列表的描述。

package cn.swu1;/*练习:用于记录应用程序运行次数。如果使用次数已到,那么给出注册提示。分析:很容易想到的是:计数器。可是该计数器定义在程序中,随着该应用程序的退出,该计数器也在内存中消失了。所以要建立一个配置文件,用于记录该软件的使用次数。该配置文件使用键值对的形式。键值对数据是map集合。数据是以文件形式存储。使用io技术。那么map+io——>Properties。思路:1、用读取流关联文本信息文件。如果存在则读取,如果不存在,则创建  2、每次运行,将文件数据存入集合中,读取值,判断次数,如果小于等于5次,则次数增加1次,如果大于则输出提示信息。  3、将值小于等于5次的信息数据存入文件中*/import java.util.*;import java.io.*;class  APPNum{public static void main(String[] args)throws IOException {int count=runCount();if(count>5)//如果程序被使用了超过5次,则终止使用,并提示{System.out.println("次数到了,交钱!!!!!");return ;}elseSystem.out.println("程序第"+count+"次Run!");}//获取程序运行的次数public static int runCount()throws IOException{Properties ps=new Properties();//创建集合对象File file=new File("info.ini");//将文件进行封装if(!file.exists())//判断是否存在file.createNewFile();FileReader fr=new FileReader(file);//将文件于读取流进行关联ps.load(fr);//加载流中的文件数据到集合中int count=0;//定义计数器String value=ps.getProperty("time");//获取次数值if(value!=null)//如过值不等于null,则将其赋值给count{count=Integer.parseInt(value);}count++;//每启动一次自增ps.setProperty("time",count+"");//将次数记录住集合FileWriter fw=new FileWriter(file);ps.store(fw,"");//将集合中的数据存入硬盘文件中fr.close();//关流fw.close();return count;//返回程序启动的次数}}

运行结果:

6  打印流

概述
1、打印流包括:PrintStream和PrintWriter
2、该流提供了打印方法,可将各种类型的数据都原样打印。
字节打印流:PrintStream

构造方法中可接收的参数类型:

1、File对象。File

2、字符串路径:String

3、字符输出流:OutputStream

字符串打印流:PrintWriter

构造方法中可接受的参数类型

1、File对象:File

2、字符串路径:String

3、字节输出流:OutputStream

4、字符输出流:Writer

import java.io.*;class  PrintStreamDemo{public static void main(String[] args) throws IOException{//键盘录入BufferedReader bufr = new BufferedReader(new InputStreamReader(System.in));//打印流关联文件,自动刷新PrintWriter out = new PrintWriter(new FileWriter("a.txt"),true);String line = null;while((line=bufr.readLine())!=null){if("over".equals(line))//结束字符break;out.println(line.toUpperCase());//out.flush();}//关流out.close();bufr.close();}}

总结



1、流的对象很多,开发是具体选择那类流:
a、明确源和目的
         源:InputStream Reader
         目的:OutputStream Writer
b、明确数据是否是纯文本数据
         源:是纯文本:Reader
                否:InputStream
         目的:是纯文本:Writer
                否:OutputStream
         到这里,就可以明确需求中具体要使用哪个体系。
c、明确具体的设备(通过setIn和setOut可以改变默认输入输出设备)
         源设备:硬盘:File        键盘:System.in        内存:数组        网络:Socket流
         目的设备:硬盘:File     控制台:System.out   内存:数组        网络:Socket流
d、是否需要其他额外功能
         是否需要高效(缓冲区):     是,就加上buffer









0 0