黑马程序员——IO流(一)

来源:互联网 发布:网络维护培训学校 编辑:程序博客网 时间:2024/05/22 10:25

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

 

一.基本概念


处理数据的流的方式就是IO技术

流可以理解为数据流动

 

Java对数据的操作是通过流的方式

Java用于操作流的对象都在IO包中

流按操作数据分为两种:字节流与字符流

流按流向分为:输入流,输出流

输入流和输出流相对于内存设备而言.

 

将外设中的数据读取到内存中:输入

将内存的数写入到外设中:输出。

 

二.字符流


字符流的由来:

其实就是:字节流读取文字字节数据后,不直接操作而是先查指定的编码表。获取对应的文字。

在对这个文字进行操作。简单说:字节流+编码表

 

字节流的两个顶层父类:

1,InputStream  2,OutputStream.// 读和写

 

字符流的两个顶层父类:

1,Reader 2,Writer

 

这些体系的子类都以父类名作为后缀。

而且子类名的前缀就是该对象的功能。

 

 

字符流两个基类:

Reader Writer

 

既然IO流是用于操作数据的

那么数据的最常见体现形式是:文件

 

例子

//需求:在硬盘上,创建一个文件并写入一些文字数据//找到一个专门用于操作文件的Writer子类对象 FileWriter  后缀名是父类名,前缀名是该流对象的功能import java.io.*;class FileWriteDemo{    publicstatic void main(String[] args) throws IOException//因为创建文件时可能会创建到不存在的盘符下,所有要抛出或trycatch    {        //创建一个FileWriter对象。该对象一被初始化就必须要明确被操作的文件        //而且该文件会被创建到指定目录下。如果该目录下已有同名文件,会覆盖。        //其实该步就是在明确数据要存放的目的地        FileWriterfw = new FileWriter("demo.txt");               //调用write方法,将字符串写入到流中。此时txt中无数据,在流的缓冲中        fw.writr("abcd");         //flush()刷新流对象的缓冲的数据,将数据刷到目的地中。此时txt有数据        fw.flush();         //close()关闭流资源,但是关闭之前会刷新一次内部的缓冲中的数据        //将数据刷到目的地中。        //和flush区别,flush刷新后,流可以继续使用,close刷新后,会将流关闭        fw.close();    } } 


对已有文件的数据续写

 

//传递一个true参数,代表不覆盖已有的文件,并在已有文件的末尾处进行数据续写。FileWriter fw =newFileWriter("demo.txt",true);


 

IO流异常

 

例子

import java.io.*;class FileDemo{    publicstatic void main(String[] args)    {        FileWriterfw = null;   //在外面建立引用,因为finally中的fw.close()无法                    //读取到try中建立的fw,所以要先建立        try        {            fw= new FileWriter("demo.txt");            fw.write("abccccc");        }        catch(IOExceptione)        {            System.out.println(e.toString());        }        finally        {            try            {                  if(fw!=null)//创建失败时,若fw为空,则无法调用close()                fw.close();//一定要执行,避免出错后浪费资源                    //三句话都可能发生异常,这句话单独try            }            catch(IOExceptione)            {                System.out.println(e.toString());            }        }    }}



读取数据

例子

import java.io.*;class FileReaderDemo{    publicstatic void main(String[] args) throws IOException    {    //创建一个文件读取对象,和指定名称的文件相关联    //要保证该文件是已经存在的,如果不存在,会发生异常FileNotFoundException        FileReaderfr = new FileReader("demo.txt");/*    //"demo.txt"内写ab    //调用读取流对象的read方法    //read():一次读一个字符,而且会自动往下读    intch1 = fr.read();    System.out.println("ch1="+(char)ch1);  //强转成char型,不写char会转成int类型,结果a       intch2 = fr.read();    System.out.println("ch2="+(char)ch2);  //结果b    fr.close();*/    //写成循环     intch = 0;    while((ch=fr.read())!=-1){     //返回-1就是读到结尾了        System.out.println((char)ch);    }      


//读取一个字符数组//read(char[])返回的是读到字符个数char[] buf = new char[3];//缓冲区通常定义成1024的整数倍 如char[] buf = new char[1024]int num = 0;while((num=fr.read(buf))!=-1) {    System.out.println(newString(buf,0,num));}fr.close();


 

拷贝文本文件

复制的原理:

其实就是将C盘下的文件数据存储到D盘的一个文件中

 

步骤:

1、在D盘创建一个文件,用于存储C盘文件中的数据

2、定义读取流和C盘文件关联

3、通过不断的读写完成数据存储

4、关闭资源

例子

public static void main(String[] args)throws IOException{    //1,读取一个已有的文本文件,使用字符读取流和文件相关联。    FileReaderfr = new FileReader("IO流_2.txt");    //2,创建一个目的,用于存储读到数据。    FileWriterfw = new FileWriter("copytext_1.txt");    //3,频繁的读写操作。    intch = 0;    while((ch=fr.read())!=-1)    {        fw.write(ch);    }    //4,关闭流资源。    fw.close();    fr.close();}  


 

字符流缓冲区:

BufferedWriter

    :newLine();    //换行

BufferedReader

    :readLine();   //读一行文本

BufferedWriter: 使用缓冲区

例子

FileWriter fw = newFileWriter("buf.txt"); //创建一个文件       //为了提高写入的效率。使用了字符流的缓冲区。//创建了一个字符写入流的缓冲区对象,并和指定要被缓冲的流对象相关联BufferedWriter bufw = newBufferedWriter(fw);       //使用缓冲区的写入方法将数据先写入到缓冲区中。//bufw.write("abcdefq"+LINE_SEPARATOR+"hahahha");//bufw.write("xixiixii");//bufw.newLine();//bufw.write("heheheheh");       for(int x=1; x<=4; x++){    bufw.write("abcdef"+x);    bufw.newLine();    bufw.flush();}              //使用缓冲区的刷新方法将数据刷目的地中。//bufw.flush();       //关闭缓冲区。其实关闭的就是被缓冲的流对象(fw)。bufw.close();


缓冲区的出现是为了提高流的操作效率而出现的

所有在创建缓冲区之前,必须要先有流对象

该缓冲区提供了一个跨平台的换行符

newLine();

 

 

readLine()方法的原理

无论是读一行,还是读取多个字符。其实最终都是在硬盘上一个一个读取。

所以最终使用的还是read方法一次读一个的方法。

使用了读取缓冲区的read方法,将读取到的字符进行缓冲并判断换行标记。将标记前的缓冲数据变成字符串返回。

 


装饰设计模式

当想要对已有的对象进行功能增强时,

可以定义类,将已有对象传入,基于已有的功能,并提供加强功能。

那么自定义的该类称为装饰类

 

装饰类通常会通过构造方法接收被装饰的对象

并基于被装饰的对象的功能,提供更强的功能

 

 

装饰和继承的区别

 

继承:

首先有一个继承体系。

Writer

    |--TextWriter:用于操作文本

    |--MediaWriter:用于操作媒体。

 

装饰: 

想要对操作的动作进行效率的提高。

按照面向对象,可以通过继承对具体的进行功能的扩展。

效率提高需要加入缓冲技术。

   

Writer

    |--TextWriter:用于操作文本

        |--BufferTextWriter:加入了缓冲技术的操作文本的对象。

    |--MediaWriter:用于操作媒体。

        |--BufferMediaWriter:

 

装饰模式比继承灵活,避免了继承体系臃肿

而且降低了类与类之间的关系

 

例子:

 

class PersonDemo {     publicstatic void main(String[] args){        Personx = new Person();  //写成X方便理解               NewPersonp1 = new NewPerson(x);     //装饰类构造方法,传入被装饰类对象。此时this.p = x,把x所指对象的地址赋给了NewPerson中的p,所以NewPerson中的p.chifan();调用的是x所指对象的方法        p1.chifan();    }} class Person{    voidchifan(){        System.out.println("吃饭");    }} //这个类的出现时为了增强Person而出现的class NewPerson{    privatePerson p;    NewPerson(Personp){        this.p= p;    }    publicvoid chifan(){        System.out.println("开胃酒");        p.chifan();            //假如这里写成chifan(); 结果就会变成不断循环开胃酒        System.out.println("甜点");    }}// 结果//  开胃酒//  吃饭//  甜点  


特点:装饰类和被装饰类都必须所属同一个接口或者父类。

 

装饰类因为增强已有对象,具备的功能和已有的是相同的,只不过提供了更强功能

所以装饰类和被装饰类通常都是属于一个体系中的

 

LineNumberReader

带行号的装饰类,基于readLine,但是带行号

 

 

 

三.字节流

 

基本操作与字符流相同

但它不仅可以操作字符,还可以操作其他媒体文件

 

字节流写数据直接写入目的地,没有必要调flush();

 

available():字节长度,如果abcds,就是5个字节,多大就是多少

FileInputStream fis = newFileInputStream("fos.txt");int num = fis.available();byte[] buf = newbyte[fis.available()];//定义一个刚刚好的缓冲区,不用再循环了//但若数据过大,则不要用此方法,如一个视频几G,没必要定义这么大的缓冲区//此时还是定义成1024的整数倍比较好,如byte[] buf = new byte[1024];fis.read(buf);System.out.println(new String(buf));fis.close();


 

//复制一张图片//思路:/1、用字节读取流对象和图片关联//2、用字节写入流对象创建一个图片文件,用于存储获取到的图片数据//3、通过循环读写,完成数据的存储//4、关闭资源//例子import java.io.*;class CopyPic{    publicstatic void main(String[] args)    {        FileOutputStreamfos = null;        FileOutputStreamfis = null;        try        {            fos= new FileOutputStream("c:\\2.bmp");            fis= new FileInputStream("c:\\1.bmp");// 这个是源                       //建议使用这种读取数据的方法            byte[]buf = new byte[1024]; //定义一个缓冲区             intlen = 0;            while((len=fis.read(buf))!=1)            {                for.write(buf,0,len);            }        }        catch(IOExceptione)        {            thrownew RuntimeException("复制文件失败");        }        finally        {            try            {                if(fis!=null)                fis.close();            }            catch(IOExceptione)            {                thrownew RuntimeException("读取关闭失败");            }            try            {                if(fos!=null)//需要分别关闭(出现异常的情况下)                fos.close();            }            catch(IOExceptione)            {                thrownew RuntimeException("写入关闭失败");            }        }    }}


 

 

读取键盘录入

System.out:对应的是标准输出设备,控制台

System.in:对应的标准输入设备,键盘

 

//需求://通过键盘录入数据//当录入一行数据后,就将该行数据进行打印//如果录入的数据时over,那么停止录入      publicstatic void readKey2() throws IOException    {               /*         * 获取用户键盘录入的数据,         * 并将数据变成大写显示在控制台上,         * 如果用户输入的是over,结束键盘录入。         *         * 思路:         * 1,因为键盘录入只读取一个字节,要判断是否是over,需要将读取到的字节拼成字符串。         * 2,那就需要一个容器。StringBuilder.         * 3,在用户回车之前将录入的数据变成字符串判断即可。         *         */               //1,创建容器。        StringBuildersb = new StringBuilder();               //2,获取键盘读取流。              InputStreamin = System.in;               //3,定义变量记录读取到的字节,并循环获取。                intch = 0;               while((ch=in.read())!=-1)        {                       //在存储之前需要判断是否是换行标记 ,因为换行标记不存储。            if(ch=='\r')                continue;            if(ch=='\n'){                Stringtemp = sb.toString();                if("over".equals(temp))                break;                System.out.println(temp.toUpperCase());                sb.delete(0,sb.length());            }            else            //将读取到的字节存储到StringBuilder中。            sb.append((char)ch);                       System.out.println(ch);        } 


 

 

转换流

1 .字节流
InputStream                  ->FileInputStream
OutputStream                 ->FileOutputStream


2 . 字符流
Reader -> BufferedReader     -> LineNumberReader 
       -> InputStreamReader  -> FileReader

Writer -> BufferedWriter
       -> OutputStreamWriter -> FileWriter
InputStreamReader   OutputStreamWriter  为转换流,FileReader FileWriter为其子类


如何选用哪种流读取文件?
IO流分为字符流 与 字节流,根据读写文件确定使用哪一种流,比如
读取文件是否为文本:
     是:用字符流
    不是:用字节流

何时使用转换流?
1.
如果使用非默认编码保存文件或者读取文件时,需要用到转换流,因为字节流的重载构造方法中有指定编码格式的参数,而FielReader与 FileWriter 是默认编码的文本文件
比如:

当我们使用默认GBK编码保存文本时,下面2句代码其实是一样的效果,

new OutputStreamWriter(new FileOutputStream("out.txt"))

new FileWriter("out.txt")

当要求保存为其他编码比如UTF-8时,就要这样写



流的操作规律:

之所以要弄清楚这个规律,是因为流对象太多,开发时不知道用哪个对象合适。

想要知道开发时用到哪些对象。只要通过四个明确即可。

 

1,明确源和目的(汇)

    源:输入流。InputStream  Reader

    目的:输出流。OutputStream  Writer

 

2,明确数据是否是纯文本数据。

    源:是纯文本:Reader

        否:InputStream

    目的:是纯文本 Writer

        否:OutputStream

   

    到这里,就可以明确需求中具体要使用哪个体系。

   

3,明确具体的设备。

    源设备:

        硬盘:File

        键盘:System.in

        内存:数组

        网络:Socket流

       

    目的设备:

        硬盘:File

        控制台:System.out

        内存:数组

        网络:Socket流

 

4,是否需要其他额外功能。

    1,是否需要高效(缓冲区);

        是,就加上buffer.

    2,转换。

 

 

例子

需求1:复制一个文本文件。1,明确源和目的。源:InputStream Reader目的:OutputStream  Writer 2,是否是纯文本?是!源:Reader目的:Writer 3,明确具体设备。源:硬盘:File目的:硬盘:FileFileReader fr = newFileReader("a.txt");FileWriter fw = newFileWriter("b.txt");       4,需要额外功能吗?需要,需要高效。BufferedReader bufr = newBufferedReader(new FileReader("a.txt"));BufferedWriter bufw = newBufferedWriter(new FileWriter("b.txt"));

 

需求2:读取键盘录入信息,并写入到一个文件中。1,明确源和目的。源:InputStream Reader目的:OutputStream  Writer 2,是否是纯文本呢?是源:Reader目的:Writer 3,明确设备源:键盘。System.in目的:硬盘。FileFileWriter fw = newFileWriter("b.txt");这样做可以完成,但是麻烦。将读取的字节数据转成字符串。再由字符流操作。 4,需要额外功能吗?需要。转换。   将字节流转成字符流。因为名确的源是Reader,这样操作文本数据做便捷。所以要将已有的字节流转成字符流。使用字节-->字符。InputStreamReaderInputStreamReader isr = newInputStreamReader(System.in);FileWriter fw = newFileWriter("b.txt");       还需要功能吗?需要:想高效。BufferedReader bufr = newBufferedReader(new InputStreamReader(System.in));BufferedWriter bufw = newBufferedWriter(new FileWriter("b.txt"));


需求3:将一个文本文件数据显示在控制台上。1,明确源和目的。源:InputStream Reader目的:OutputStream  Writer 2,是否是纯文本呢?是源:Reader目的:Writer 3,明确具体设备源:硬盘:File目的:控制台:System.outFileReader fr = new FileReader("a.txt");OutputStream out =System.out;//PrintStream 4,需要额外功能吗?需要,转换。FileReader fr= newFileReader("a.txt");OutputStreamWriter osw = newOutputStreamWriter(System.out);需要,高效。BufferedReader bufr = newBufferedReader(new FileReader("a.txt"));BufferedWriter bufw = newBufferedWriter(new OutputStreamWriter(System.out)); 


 

需求4:读取键盘录入数据,显示在控制台上。1,明确源和目的。源:InputStream Reader目的:OutputStream  Writer 2,是否是纯文本呢?是源:Reader目的:Writer 3,明确设备。源:键盘:System.in目的:控制台:System.outInputStream in = System.in;OutputStream out = System.out;       4,明确额外功能?需要转换,因为都是字节流,但是操作的却是文本数据。所以使用字符流操作起来更为便捷。InputStreamReader isr = newInputStreamReader(System.in);OutputStreamWriter osw = newOutputStreamWriter(System.out);为了将其高效。BufferedReader bufr = newBufferedReader(new InputStreamReader(System.in));BufferedWriter bufw = newBufferedWriter(new OutputStreamWriter(System.out));       


       

需求5,将一个中文字符串数据按照指定的编码表写入到一个文本文件中.1,目的。OutputStream,Writer2,是纯文本,Writer。3,设备:硬盘FileFileWriter fw = newFileWriter("a.txt");fw.write("你好");   注意:既然需求中已经明确了指定编码表的动作。那就不可以使用FileWriter,因为FileWriter内部是使用默认的本地码表。只能使用其父类。OutputStreamWriter.OutputStreamWriter接收一个字节输出流对象,既然是操作文件,那么该对象应该是FileOutputStreamOutputStreamWriter osw = newOutputStreamWriter(new FileOutputStream("a.txt"),charsetName);   //如 (newFileOutputStream("a.txt"),"GBK")   需要高效吗?BufferedWriter bufw = newBufferedWriter(new OutputStreamWriter(newFileOutputStream("a.txt"),charsetName)); 


 

什么时候使用转换流呢?

1,源或者目的对应的设备是字节流,但是操作的却是文本数据,可以使用转换作为桥梁。

提高对文本操作的便捷。

2,一旦操作文本涉及到具体的指定编码表时,必须使用转换流 。

 


目前为止,10个流对象重点掌握。

字符流:

FileReader

FileWriter

 

BufferedReader

BufferedWriter

 

InputStreamReader

OutputStreamWrier

 

字节流:

 

FileInputStream

FileOutputStream

 

BufferedInputStream

BufferedOutputStream


0 0
原创粉丝点击