黑马程序员_IO流

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

IO流。

Io流用来处理设备之间的数据传输,分为字节流和字符流。有四个顶层父类字节流(inputStream  outputStream)字符流(Reader  Writer)

输入(读):将硬盘数据读入内存中

输出(写):将内存中数据写入硬盘中。

字符流的由来:字节流读取文字字节数据后,不直接操作而是先查指定的编码表,获取对应的文字,简单说就是字节码+编码表。如果操作文字优先考虑字符流。

需求:将一些文字写到硬盘中

public static voidmain(String[] args) throws IOException {

              // TODOAuto-generated method stub

              FileWriter fw =new FileWriter("E://damo.txt",true);//true表示是否续写数据,而不是覆盖数据。

         fw.write("abcde");

         fw.flush();//刷新缓冲区

       }

Privatestatic finalString=LINE_SEPARATOR=System.getProperty("line.separator");//定义换行常量

FileReader用来读取字符文件的便捷类

public int read() throws IOException

            读取单个字符。在字符可用、发生 I/O 错误或者已到达流的末尾前,此方法一直阻塞。

            用于支持高效的单字符输入的子类应重写此方法。

            返回:

            作为整数读取的字符,范围在 0 到 65535 之间 (0x00-0xffff),如果已到达流的末尾,则返    

            回 -1

需求;读取文本文件:

public static voidmain(String[] args) throws IOException  {

 FileReader fr=newFileReader("E://damo.txt");

          int ch=0;

       while ( (ch=fr.read()) !=-1) {

              System.out.print( (char)ch);

       }

fr.close();

public int read(char[]cbuf) throws IOException

                       将字符读入数组。在某个输入可用、发生 I/O 错误或者已到达流的末尾前,此方法一直阻塞。

 参数:cbuf- 目标缓冲区

 返回:读取的字符数,如果已到达流的末尾,则返回 -1

字符流的缓冲区:缓冲区的出现提高了对数据的读写效率。

对应类BufferedWriter:将文本写入字符输出流,缓冲各个字符,从而提供单个字符、数组和字符串的高  

                      效写入。可以指定缓冲区的大小,或者接受默认的大小。在大多数情况下,默认  

                      值就足够大了。

BufferedReader从字符输入流中读取文本,缓冲各个字符,从而实现字符、数组和行的高效读取。

           可以指定缓冲区的大小,或者可使用默认的大小。大多数情况下,默认值就足够大了。

 

 

 

装饰模式与继承:

共同点:装饰模式和继承都能实现对对象功能的拓展。

不同点:装饰比继承灵活

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

键盘录入:system. Int

public static voidmain(String[] args) throws IOException {

              // TODOAuto-generated method stub

              InputStream input= System.in;

              int ch =0;

              while ((ch=input.read())!=-1) {

                     System.out.print ((char)ch);

              }}

字节流转化字符流:InputStreamReader:InputStreamReader 是字节流通向字符流的桥梁:

public static voidmain(String[] args) throws IOException {

 

              InputStream in =System.in;

              InputStreamReaderisr = new InputStreamReader(in);

        BufferedReader buf=new BufferedReader(isr);

        String str=null;

        while ((str=buf.readLine())!=null) {

             if("over".equals(str))

                    break;

             else{

      System.out.println(str.toUpperCase());

             }}

字符流通向字节流OutputStreamWriter

流的操作规律:

            明确源和目的(源inputStream   Reader)(目的OutputStream   Writer)

            明确数据是否是纯文本数据(源:是纯文本用Reader 目的:Writer)(否:源

inputStream目的OutputStream)

             明确源设备:硬盘:File       键 盘System.in     内存:数组        网络:Socket流

             明确目的设备 硬盘:File   内存:数组        网络:Socket流

            是否需要缓冲区是就加上Buffer

需求1:复制一个文件

源:InputStream Reader      目:OutputStream Writer     是否是纯文本    是:源:Reader     目:Writer     明确具体设备   源:硬盘File       目:硬盘File

File

递归:函数自身直接或间接的调用自己。一个功能被重复使用,并每次使用时参与运行的结果和上一次有关。

注意:递归一定要明确条件,否则容易造成栈溢出。

      递归的次数。

Properties集合:

     特点:该集合的键和值都是字符串类型,集合中的数据可以保存到流中,或从流中获取。

常用方法:

         public String getProperty(String key)

                                    用指定的键在此属性列表中搜索属性。如果在此属性列表中未找

                                     到该键,则接着递归检查默认属性列表及其默认值。如果未找到 

                                     属性,则此方法返回 null。

                                      参数:key- 属性键。

                                       返回:属性列表中具有指定键值的值

         publicvoid list(PrintStream out)

                                   将属性列表输出到指定的输出流。此方法对调试很有用。

                                   参数:out- 输出流。

                                   抛出:ClassCastException- 如果此属性列表中的任何键不是字符

                                     串。

。     public void load(InputStreaminStream)throws IOException

                                   从输入流中读取属性列表(键和元素对)。输入流按 load(Reader) 

                                   中所指定的、简单的面向行的格式,并假定使用 ISO 8859-1 字符

                                  编码;即每个字节都是 Latin1 字符。对于非 Latin1 的字符和某

                                    些特殊字符,可以使用 Unicode 转义以键和元素的形式来表示它

                                    们。

                                    此方法返回后,指定的流仍保持打开状态。

                                  参数inStream- 输入流。

                                   抛出:IOException- 如果读取输入流时发生错误。

                                    IllegalArgumentException- 如果输入流包含错误的 Unicode 转义

                                     序列。

      public Set<String> stringPropertyNames()

                                  返回此属性列表中的键集,其中该键及其对应值是字符串,如果在

                                  主属性列表中未找到同名的键,则还包括默认属性列表中不同的

                                  键。其键或值不是 String 类型的属性被忽略。

                                  返回的 set 不受 Properties 对象支持。对此 Properties 的改变不能  

                                  在该 set 中反映出来,反之亦然。

                                  返回:此属性列表中的键集,其中该键及其对应值是字符串,包括

                                  默认属性列表中的键。

     public Object setProperty(String key , String value)

                                  调用 Hashtable 的方法 put。使用 getProperty 方法提供并行性。强

                                  制要求为属性的键和值使用字符串。返回值是 Hashtable 调用 put  

                                  的结果。

                                  参数:key- 要置于属性列表中的键。value- 对应于 key 的值。

                                  返回:属性列表中指定键的旧值,如果没有值,则为 null。

PrintStream 为其他输出流添加了功能,提供了打印方法可以对多种数据类型值进行打印使它们能够方便地打印各种数据值表示形式不会抛出 IOException

构造函数,接收三种类型的值:

                1,字符串路径。

                2,File对象。

                3,字节输出流。

PrintWriter:字符打印流。

                构造函数参数:

                1,字符串路径。

                2,File对象。

                3,字节输出流。

                4,字符输出流。

SequenceInputStreamSequenceInputStream 表示其他输入流的逻辑串联。它从输入流的有序集合开始,并从第一个输入流开始读取,直到到达文件末尾,接着从第二个输入流读取,依次类推,直到到达包含的最后一个输入流的文件末尾为止。

ObjectOutputStream将 Java 对象的基本数据类型和图形写入 OutputStream。可以使用 ObjectInputStream   

                读取(重构)对象。通过在流中使用文件可以实现对象的持久存储。

RandomAccessFile:

              特点:既可以读也可以写。

                该对象内部维护着一个byte数组,并通过指针操作数组中的元素

                 可以通过getFilePointer方法获取指针的位置,和通过seek方法设置指

                  针该对象的源或者目的只能是文件

代码练习

packagecn.itcast.io;

 

importjava.io.FileInputStream;

importjava.io.FileNotFoundException;

importjava.io.FileOutputStream;

importjava.io.IOException;

 

public classFileOut {

 

       public static void main(String[] args)throws IOException {

              FileInputStreamfr = new FileInputStream("E:\\");

              FileOutputStreamfo = new FileOutputStream("E:\\oo");

              System.out.println(fr.available());

               byte [] b=new byte[10240];

               int ch=0;

               while ((ch=fr.read(b))!=-1 ) {

                      fo.write(b,0,ch);

                      System.out.println("wancheng");

              }

              System.out.println("asd");

fr.close();

fo.close();

       }

}

packagecn.itcast.io;

 

importjava.io.BufferedReader;

importjava.io.BufferedWriter;

importjava.io.FileNotFoundException;

importjava.io.FileReader;

importjava.io.FileWriter;

importjava.io.IOException;

 

public classFileWriterDamo {

 

       /**

        *@param args

        *@throws IOException

        */

       private static final String LINE_SEPARATOR=System.getProperty("line.separator");

       public static void main(String[] args)throws IOException  {

              //TODO Auto-generated method stub

     FileWriter fw=newFileWriter("E:\\dame2.txt");

     BufferedWriter bufw=newBufferedWriter(fw);

        FileReader fr=newFileReader("E:\\damo.txt");

        BufferedReader bufr=newBufferedReader(fr);

        String ch=null;

        while ((ch=bufr.readLine())!=null) {

               bufw.write(ch);

               bufw.newLine();

               bufw.flush();

              

                    

              }

       fr.close();

       fw.close();}

}

递归:就是函数自身调用自身。

什么时候用递归呢?

当一个功能被重复使用,而每一次使用该功能时的参数不确定,都由上次的功能元素结果来确定。

简单说:功能内部又用到该功能,但是传递的参数值不确定。(每次功能参与运算的未知内容不确定)。

 

递归的注意事项:

1:一定要定义递归的条件。

2:递归的次数不要过多。容易出现 StackOverflowError 栈内存溢出错误

其实递归就是在栈内存中不断的加载同一个函数。

------------------------------------------------------------------------------------------------

Java.util.Properties一个可以将键值进行持久化存储的对象。Map--Hashtable的子类。

Map

       |--Hashtable

              |--Properties用于属性配置文件,键和值都是字符串类型。

特点:1:可以持久化存储数据。2:键值都是字符串。3:一般用于配置文件。

 

|-- load()将流中的数据加载进集合。

原理:其实就是将读取流和指定文件相关联,并读取一行数据,因为数据是规则的key=value,所以获取一行后,通过 = 对该行数据进行切割,左边就是键,右边就是值,将键、值存储到properties集合中。

|-- store():写入各个项后,刷新输出流。

|-- list():将集合的键值数据列出到指定的目的地。

-------------------------------------------------------------------------------------------------

以下介绍IO包中扩展功能的流对象:基本都是装饰设计模式。

Java.io.outputstream.PrintStream打印流

1:提供了更多的功能,比如打印方法。可以直接打印任意类型的数据。

2:它有一个自动刷新机制,创建该对象,指定参数,对于指定方法可以自动刷新。

3:它使用的本机默认的字符编码.

4:该流的print方法不抛出IOException。

 

该对象的构造函数。

PrintStream(File file)  :创建具有指定文件且不带自动行刷新的新打印流。

PrintStream(File file, String csn) :创建具有指定文件名称和字符集且不带自动行刷新的新打印流。

PrintStream(OutputStream out) :创建新的打印流。

PrintStream(OutputStream out, boolean autoFlush) :创建新的打印流。

PrintStream(OutputStream out, boolean autoFlush, Stringencoding) :创建新的打印流。

PrintStream(String fileName) :创建具有指定文件名称且不带自动行刷新的新打印流。

PrintStream(String fileName, String csn)

 

PrintStream可以操作目的:1:File对象。2:字符串路径。3:字节输出流。

前两个都JDK1.5版本才出现。而且在操作文本文件时,可指定字符编码了。

 

当目的是一个字节输出流时,如果使用的println方法,可以在printStream对象上加入一个true参数。这样对于println方法可以进行自动的刷新,而不是等待缓冲区满了再刷新。最终print方法都将具体的数据转成字符串,而且都对IO异常进行了内部处理。

 

既然操作的数据都转成了字符串,那么使用PrintWriter更好一些。因为PrintWrite是字符流的子类,可以直接操作字符数据,同时也可以指定具体的编码。

--------------------------------------------------------

PrintWriter具备了PrintStream的特点同时,还有自身特点:

该对象的目的地有四个:1:File对象。2:字符串路径。3:字节输出流。4:字符输出流。

 

开发时尽量使用PrintWriter

 

方法中直接操作文件的第二参数是编码表。

直接操作输出流的,第二参数是自动刷新。

 

//读取键盘录入将数据转成大写显示在控制台.

BufferedReader bufr = new BufferedReader(newInputStreamReader(System.in));//源:键盘输入

//目的:把数据写到文件中,还想自动刷新。

PrintWriter out = new PrintWriter(new FileWriter("out.txt"),true);//设置true后自动刷新

String line = null;

while((line=bufr.readLine())!=null){

    if("over".equals(line))

        break;

    out.println(line.toUpperCase());//转大写输出

}

    //注意:System.in,System.out这两个标准的输入输出流,在jvm启动时已经存在了。随时可以使用。当jvm结束了,这两个流就结束了。但是,当使用了显示的close方法关闭时,这两个流在提前结束了。

out.close();

bufr.close();

------------------------------------------------------------------------------------------------

SequenceInputStream:序列流,作用就是将多个读取流合并成一个读取流。实现数据合并。

表示其他输入流的逻辑串联。它从输入流的有序集合开始,并从第一个输入流开始读取,直到到达文件末尾,接着从第二个输入流读取,依次类推,直到到达包含的最后一个输入流的文件末尾为止。

这样做,可以更方便的操作多个读取流,其实这个序列流内部会有一个有序的集合容器,用于存储多个读取流对象。

该对象的构造函数参数是枚举,想要获取枚举,需要有Vector集合,但不高效。需用ArrayList,但ArrayList中没有枚举,只有自己去创建枚举对象。

但是方法怎么实现呢?因为枚举操作的是具体集合中的元素,所以无法具体实现,但是枚举和迭代器是功能一样的,所以,可以用迭代替代枚举。

 

合并原理:多个读取流对应一个输出流。

切割原理:一个读取流对应多个输出流。

 

import java.io.*;

import java.util.*;

class  SplitFileDemo{

    private static final String CFG =".properties";

    private static final String SP =".part";

    public static void main(String[] args)throws IOException{

        Filefile = new File("c:\\0.bmp");

        Filedir = new File("c:\\partfiles");

        meger(dir);

    }

    //数据的合并。

    public static void meger(File dir)throwsIOException{

        if(!(dir.exists()&& dir.isDirectory()))

            throw new RuntimeException("指定的目录不存在,或者不是正确的目录");

        File[]files = dir.listFiles(new SuffixFilter(CFG));

        if(files.length==0)

            throw new RuntimeException("扩展名.proerpties的文件不存在");

        //获取到配置文件

        Fileconfig = files[0];

        //获取配置文件的信息。

        Propertiesprop = new Properties();

        FileInputStreamfis = new FileInputStream(config);

        prop.load(fis);

        StringfileName = prop.getProperty("filename");

        intpartcount = Integer.parseInt(prop.getProperty("partcount"));

        //--------------------------

        File[]partFiles = dir.listFiles(new SuffixFilter(SP));

        if(partFiles.length!=partcount)

            throw new RuntimeException("缺少碎片文件");

        //---------------------

        ArrayList<FileInputStream>al = new ArrayList<FileInputStream>();

        for(intx=0; x<partcount; x++){

            al.add(new FileInputStream(newFile(dir,x+SP)));

        }

        Enumeration<FileInputStream>en = Collections.enumeration(al);

        SequenceInputStreamsis = new SequenceInputStream(en);

        Filefile = new File(dir,fileName);

        FileOutputStreamfos = new FileOutputStream(file);

        byte[]buf = new byte[1024];

        intlen = 0;

        while((len=sis.read(buf))!=-1){

            fos.write(buf,0,len);

        }

        fos.close();

        sis.close();

    }

    //带有配置信息的数据切割。

    public static void splitFile(Filefile)throws IOException{

        //用一个读取流和文件关联。

        FileInputStreamfis = new FileInputStream(file);

        //创建目的地。因为有多个。所以先创建引用。

        FileOutputStreamfos = null;

        //指定碎片的位置。

        Filedir = new File("c:\\partfiles");

        if(!dir.exists())

            dir.mkdir();

        //碎片文件大小引用。

        Filef = null;

        byte[]buf = new byte[1024*1024];

        //因为切割完的文件通常都有规律的。为了简单标记规律使用计数器。

        intcount = 0;

        intlen = 0;

        while((len=fis.read(buf))!=-1){

            f = newFile(dir,(count++)+".part");

            fos = new FileOutputStream(f);

            fos.write(buf,0,len);

            fos.close();

        }

        //碎片文件生成后,还需要定义配置文件记录生成的碎片文件个数。以及被切割文件的名称。

        //定义简单的键值信息,可是用Properties。

        Stringfilename = file.getName();

        Propertiesprop = new Properties();

        prop.setProperty("filename",filename);

        prop.setProperty("partcount",count+"");

        Fileconfig = new File(dir,count+".properties");

        fos= new FileOutputStream(config);

        prop.store(fos,"");

        fos.close();

        fis.close();

    }

}

class SuffixFilterimplements FileFilter{

    private String suffix;

    SuffixFilter(String suffix){

        this.suffix  = suffix;

    }

    public boolean accept(File file){

        return  file.getName().endsWith(suffix);

    }

}

-----------------------------------------------------------------------------------------------

RandomAccessFile:

特点:

1:该对象即可读取,又可写入。

2:该对象中的定义了一个大型的byte数组,通过定义指针来操作这个数组。

3:可以通过该对象的getFilePointer()获取指针的位置,通过seek()方法设置指针的位置。

4:该对象操作的源和目的必须是文件。

5:其实该对象内部封装了字节读取流和字节写入流。

注意:实现随机访问,最好是数据有规律。

 

class RandomAccessFileDemo{

    public static voidmain(String[] args) throws IOException{

        write();

        read();

        randomWrite();

    }

    //随机写入数据,可以实现已有数据的修改。

    public static voidrandomWrite()throws IOException{

        RandomAccessFile raf = newRandomAccessFile("random.txt","rw");

        raf.seek(8*4);

        System.out.println("pos:"+raf.getFilePointer());

        raf.write("王武".getBytes());

        raf.writeInt(102);

        raf.close();

    }

    public static voidread()throws IOException{

        RandomAccessFile raf = newRandomAccessFile("random.txt","r");//只读模式。

        //指定指针的位置。

        raf.seek(8*1);//实现随机读取文件中的数据。注意:数据最好有规律。

        System.out.println("pos1 :"+raf.getFilePointer());

        byte[] buf = new byte[4];

        raf.read(buf);

        String name = new String(buf);

        int age = raf.readInt();

        System.out.println(name+"::"+age);

        System.out.println("pos2:"+raf.getFilePointer());

        raf.close();

    }

    public static voidwrite()throws IOException{

        //rw:当这个文件不存在,会创建该文件。当文件已存在,不会创建。所以不会像输出流一样覆盖。

        RandomAccessFile raf = newRandomAccessFile("random.txt","rw");//rw读写模式

        //往文件中写入人的基本信息,姓名,年龄。

        raf.write("张三".getBytes());

        raf.writeInt(97);

        raf.close();

    }

}

------------------------------------------------------------------------------------------------

管道流:管道读取流和管道写入流可以像管道一样对接上,管道读取流就可以读取管道写入流写入的数据。

注意:需要加入多线程技术,因为单线程,先执行read,会发生死锁,因为read方法是阻塞式的,没有数据的read方法会让线程等待。

public static void main(String[] args) throws IOException{

    PipedInputStream pipin= new PipedInputStream();

    PipedOutputStreampipout = new PipedOutputStream();

    pipin.connect(pipout);

    new Thread(newInput(pipin)).start();

    new Thread(newOutput(pipout)).start();

}

------------------------------------------------------------------------------------------------

对象的序列化:目的:将一个具体的对象进行持久化,写入到硬盘上。

注意:静态数据不能被序列化,因为静态数据不在堆内存中,是存储在静态方法区中。

 

如何将非静态的数据不进行序列化?transient 关键字修饰此变量即可。

 

Serializable用于启动对象的序列化功能,可以强制让指定类具备序列化功能,该接口中没有成员,这是一个标记接口。这个标记接口用于给序列化类提供UID。这个uid是依据类中的成员的数字签名进行运行获取的。如果不需要自动获取一个uid,可以在类中,手动指定一个名称为serialVersionUID id号。依据编译器的不同,或者对信息的高度敏感性。最好每一个序列化的类都进行手动显示的UID的指定。

 

import java.io.*;

class ObjectStreamDemo {

    public static voidmain(String[] args) throws Exception{

        writeObj();

        readObj();

    }

    public static voidreadObj()throws Exception{

        ObjectInputStream ois = newObjectInputStream(new FileInputStream("obj.txt"));

        Object obj = ois.readObject();//读取一个对象。

        System.out.println(obj.toString());

    }

    public static voidwriteObj()throws IOException{

        ObjectOutputStream oos = newObjectOutputStream(new FileOutputStream("obj.txt"));

        oos.writeObject(newPerson("lisi",25)); //写入一个对象。

        oos.close();

    }

}

class Person implements Serializable{

    private static finallong serialVersionUID = 42L;

    private transient Stringname;//用transient修饰后name将不会进行序列化

    public int age;

    Person(String name,intage){

        this.name = name;

        this.age = age;

    }

    public StringtoString(){

        return name+"::"+age;

    }

}

-----------------------------------------------------------------------------------------------

DataOutputStream、DataInputStream专门用于操作基本数据类型数据的对象。

DataOutputStream dos =  new DataOutputStream(newFileOutputStream("data.txt"));

    dos.writeInt(256);

    dos.close();

 

    DataInputStream dis =new DataInputStream(new FileInputStream("data.txt"));

    int num =dis.readInt();

    System.out.println(num);

    dis.close();

-----------------------------------------------------------------------------------------------

ByteArrayInputStream:源:内存

ByteArrayOutputStream:目的:内存。

这两个流对象不涉及底层资源调用,操作的都是内存中数组,所以不需要关闭。

直接操作字节数组就可以了,为什么还要把数组封装到流对象中呢?因为数组本身没有方法,只有一个length属性。为了便于数组的操作,将数组进行封装,对外提供方法操作数组中的元素。

 

对于数组元素操作无非两种操作:设置(写)和获取(读),而这两操作正好对应流的读写操作。这两个对象就是使用了流的读写思想来操作数组。

//创建源:

    ByteArrayInputStreambis = new ByteArrayInputStream("abcdef".getBytes());

    //创建目的:

    ByteArrayOutputStreambos = new ByteArrayOutputStream();

    int ch = 0;

    while((ch=bis.read())!=-1){

        bos.write(ch);

    }

    System.out.println(bos.toString());

 

 

 

 

每日心得与总结:我是想知道,很多不同功能流我都不懂得用什么规律来记住,还有各个知识点的运用,头脑条理不清晰,然后我多看了几遍视频在网上百度知道里面也提问了,我觉得这个答案还挺好就是:有字符流,字节流。字符流一般是需要处理字符串的时候用的,比如按行读,对读出来的字符串进行替换,比较等。字节流一般就是简单的读或者写文件。具体到每个细的,就需要经验积累了,常用的就那几个。字符流就是BufferedWriter,BufferedReader.字节流就是FileInputStream,FileOutputStream,DataInputStream,DataOutputStream。

而我认为IO流从数据流动方向上来说分为两大方面:可以分为输入流和输出流。输入流相当于数据流进cpu中进行处理,输出流就是从cpu里读出来。根据流里面的数据类型不同,又可以分为字符流与字节流两大块。所有的流都是基于这两点展开的。




 





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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值