IO流体系中流的分类


IO流体系

分类

字节输入流

字节输出流

字符输入流

字符输出流

抽象基类

InputStream

OutputStream

Reader

Writer

访问文件

FileInputStream

FileOutputStream

FileReader

FileWriter

访问数组

ByteArrayInputStream

ByteArrayOutputStream

CharArrayReader

CharArrayWriter

访问管道

PipedInputStream

PipedOutputStream

PipedReader

PipedWriter

访问字符串

StringReader

StringWriter

缓冲流

BufferedInputStream

BufferedOuputStream

BufferedReader

BufferedWriter

转换流

InputStreamReader

OutputStreamWriter

对象流

ObjectnputStream

ObjectOutputStream

FilterInputStream

FiterOutputStream

FilterReader

FilterWriter

打印流

PrintStream

PrintWriter

推回输入流

PushbacklnputStream

PushbackReader

特殊流

DatalnputStream

DataOutputStream


常用的流

抽象基类

节点流(或文件流)

缓冲流(处理流的一种

InputStream

FileInputStream(read(byte[] buffer))

BufferedInputStream(read(byte[] buffer))

OutputStream

FileOutputStream (write(byte[] buffer,0,len))

BufferedOutputStream (write(byte[] buffer,0,len)) / flush()

Reader

FileReader (read(char[] cbuf))

BufferedReader (read(char[] cbuf)) / readLine()

Writer

FileWriter (write(char[] cbuf, 0, len))

BufferedWriter (write(char[] cbuf, 0, len)) / flush()

1,FileReader读入数据的基本操作


例:将项目下的hello.txt文件内容读入程序中,并输出到控制台

//程序中出现的异常需要使用try-catch-finally处理
@Test
public void testFileReader() throws IOException {
    //1.实例化File类的对象,指明要操作的文件
    File file = new File("hello.txt");
    //2.提供具体的流
    FileReader fileReader = new FileReader(file);
    //3.数据的读入,read():返回读入的一个字符串,如果达到文件末尾,返回-1
    int data = fileReader.read();
    while (data != -1){
        System.out.println((char)data);
        data = fileReader.read();
    }
    //4.流的关闭操作
    fileReader.close();
}

1.1,FileReader中使用read(char[] cbuf)读入数据

//对read()操作升级,使用read重装方法
    @Test
    public void testFileReader1(){
        //1,File类的实例化
        File file = new File("hello.txt");
        //2,FileReader流的实例化
        FileReader fileReader = null;
        try {
            fileReader = new FileReader(file);
            //3,读入的操作
            //read(char[] cbuf):返回每次读入cbuf数据中的字符的个数,如果达到文件末尾返回-1
            char[] cbuf = new char[5];
            int len;
            while ((len = fileReader.read(cbuf)) != -1){
                //方式一:错误的写法
//            for (int i = 0;i<cbuf.length;i++){
//                System.out.print(cbuf[i]);
//            }
                //正确的写法
                for (int i = 0;i<len;i++){
                    System.out.print(cbuf[i]);
                }

                //方式二
                String str = new String(cbuf,0,len);
                System.out.print(str);
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                //4.流的关闭操作
                fileReader.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

    }

2,FileWriter写出数据的操作


例:从内存中写出数据到硬盘的文件里

说明:
1,输出操作,对应的File可以不存在的。并不会报异常

2,File对应的硬盘中的文件如果不存在,在输出的过程中,会自动创建此文件

      File对应的硬盘中的文件如果存在:

            如果流使用的构造器是FileWriter(file,flase)/FileReader(file):对原有文件的覆盖
            如果流使用的构造器是FileWriter(file,true):不会对原有文件的覆盖,而是在原文件中添加

@Test
//程序中出现的异常需要使用try-catch-finally处理
public void testFileReader2() throws IOException {
    //1,提供File类的对象,指明写出到的文件
    File file = new File("hello.txt");
    //2,提供FileWriter的对象,用于数据的写出
    FileWriter fileWriter = new FileWriter(file,false);
    //3,写出的操作
    fileWriter.write("梦想");
    fileWriter.write("是用来实现的");
    //4,流资源的关闭
    fileWriter.close();
}

3,使用FileReader喝FileWriter实现文本文件的复制

@Test
public void testFileReader3() {
    FileReader fileReader = null;
    FileWriter fileWriter = null;
    try {
        //1,创建File类的对象,指明读写的文件
        File src = new File("hello.txt");
        File dest = new File("hello1.txt");
        //2,创建输入流喝输出流的对象
        fileReader = new FileReader(src);
        fileWriter = new FileWriter(dest);
        //3,数据的读入和写出操作
        char[] cbuf = new char[5];
        int len;//记录每次读入到cbuf数组中的字符的个数
        while ((len = fileReader.read(cbuf)) != -1){
            //每次写出的len个字符
            fileWriter.write(cbuf,0,len);
        }
    } catch (IOException e) {
        e.printStackTrace();
    } finally {
        //4,流资源的关闭
        try {
            if (fileReader != null){
                fileReader.close();
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        try {
            if (fileWriter != null){
                fileWriter.close();
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

不能使用字符流处理图片

总结

①,对于文本文件(.txt,.java, .c, .cpp),使用字符流处理(复制的时候可以使用字节流,读写不可以)

②,对于非文本文件(.jpg, .mp3, .avi, .doc, .ppt, …),使用字节流处理


4,缓冲流(字节型)实现非文本文件的复制


       作用:提高流的读取,写入的速度,原因:内部提供了一个缓冲区

@Test
    public void testFileReader3() {
        BufferedInputStream bis = null;
        BufferedOutputStream bos = null;
        try {
            //1,造文件
            File src = new File("hello.txt");
            File dest = new File("hello1.txt");
            //2,造流(造节点流)
            FileInputStream fileInputStream = new FileInputStream(src);
            FileOutputStream fileOutputStream = new FileOutputStream(dest);
            //3.造缓存流
            bis = new BufferedInputStream(fileInputStream);
            bos = new BufferedOutputStream(fileOutputStream);


            //3,复制细节,读取,写入
            byte[] buffer = new byte[1024];
            int len;//记录每次读入到buffer数组中的字符的个数
            while ((len = bis.read(buffer)) != -1){
                //每次写出的len个字符
                bos.write(buffer,0,len);
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            //4,流资源的关闭,关闭外层流的同时,内层流也会关闭
            try {
                if (bis != null){
                    bis.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
            try {
                if (bos != null){
                    bos.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

例:实现图片加密操作

int b=0;
while((b = fis.read()) != -1){
        fos .write(b^5);
}


获取文本上每个字符出现的次数


提示:遍历文本的每一个字符:字符及出现的次数保存在map中:将map中数据写入文件

@Test
public void testFileReader5() {
    FileReader fileReader = null;
    BufferedWriter bufferedWriter = null;
    try {
        //1,创建map集合
        Map<Character, Integer> map = new HashMap<>();
        //2,遍历每一个字符,每一个字符出现的次数写入map中
        fileReader = new FileReader("hello.txt");

        int c;
        while ((c = fileReader.read()) != -1){
            //int 还原char
            char ch = (char)c;
            //判断char是否在map中第一次出现
            if(map.get(ch) == null){
                map.put(ch,1);
            }else {
                map.put(ch,map.get(ch)+1);
            }
        }
        //3,把map中数据存在文件count.txt,创建writer
        bufferedWriter = new BufferedWriter(new FileWriter("count.txt"));
        //遍历map,再写入数据
        Set<Map.Entry<Character,Integer>> entrySet = map.entrySet();
        for (Map.Entry<Character,Integer> entry : entrySet) {
            switch (entry.getKey()){
                case ' ':
                    bufferedWriter.write("空格 = "+entry.getValue());
                    break;
                case '\t':
                    bufferedWriter.write("tab键 = "+entry.getValue());
                    break;
                case '\r':
                    bufferedWriter.write("回车 = "+entry.getValue());
                    break;
                case '\n':
                    bufferedWriter.write("换行 = "+entry.getValue());
                    break;
                default:
                    bufferedWriter.write(entry.getKey()+" = "+entry.getValue());
                    break;
            }
            bufferedWriter.newLine();
        }


    } catch (IOException e) {
        e.printStackTrace();
    } finally {
        try {
            if (fileReader != null){
                fileReader.close();
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        try {
            if (bufferedWriter != null){
                bufferedWriter.close();
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}


5,转换流的使用


①,转换流:属于字符流

      InputStreamReader:将一个字节的输入流转换为字符的输入流

      outputStreamWriter:将一个字符的输出流转换为字节的输出流

②,作用:提供字节流与字符流之间的转换

③,解码
      字节,字节数组 —> 字符数组,字符串
      编码:字符数组,字符串 —> 字节,字节数组

//程序中出现的异常需要使用try-catch-finally处理
@Test
public void demo01() throws IOException {
    FileInputStream fileInputStream = new FileInputStream("hello.txt");
    //参数指明的字符集,具体使用那个字符集,取决于文件hello.txt保存时使用的字符集,不填使用系统默认的字符集
    InputStreamReader inputStreamReader = new InputStreamReader(fileInputStream,"UTF-8");
    char[] cbuf = new char[20];
    int len;
    while ((len = inputStreamReader.read(cbuf)) != -1){
        String str = new String(cbuf, 0, len);
        System.out.print(str);
    }
    inputStreamReader.close();
}
  • 字符流

      ASCII:美国标准信息交换码,用一个字节的7位可以表示

      ISO8859-1:拉丁码表。欧洲码表,用一个字节的8位表示

      GB2312:中国的中文编码表。最多两个字节编码所有字符

      GBK:中国的中文编码表升级,融合了更多的中文文字符号。最多两个字节编码

      Unicode:国际标准码。融合了目前人类使用的所有字符。为每个字符分配唯一的字符码

      UTF-8:变长的编码方式,可用1-4个字节来表示一个字符

  • 字符流输入、输出的标准化过程

      输入过程

            ①,创建File类的对象,指明读取的数据的来源。(要求此文件 - -定要存在)

            ②,创建相应的输入流,将File类的对象作为参数,传入流的构造器中

            ③,具体的读入过程: 创建相应的byte[]或char[]。

            ④,关闭流资源

说明:程序中出现的异常需要使用try-catch-finally处理。

      输出过程

            ①,创建File类的对象,指明写出的数据的位置。(不要求此文件一 定要存在)

            ②,创建相应的输出流,将File类的对象作为参数,传入流的构造器中

            ③,具体的写出过程:write( char[]/byte[] buffer,0,1en)

            ④,关闭流资源

说明:程序中出现的异常需要使用try-catch-finally处理。


5,对象流:ObjectInputStream和ObjectOutputStream


      概念:

      用于存储和读取基本数据类型数据或对象的处理流。它的强大之处就是可以把Java中的对象写入到数据源中,也能把对象从数据源中还原回来

  • 序列化:用ObjectOutputStream类保存基本类型数据或对象的机制
  • 反序列化:用ObjectInputStream类读取基本类型数据或对象的机制
  • 序列化的好处在于可将任何实现了Serializable接口的对象转化为字节数据,使其在保存和传输时可被还原
  • 注意:ObjectOutputStream和ObjectInputStream不能序列化static和transient修饰的成员变量
@Test
public void demo02() throws IOException {
    ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("object.dat"));
    oos.writeObject(new Person("阿里", 11));
    oos.flush();
    ObjectInputStream ois = new ObjectInputStream(new FileInputStream("object.dat"));
    Person p = (Person)ois.readObject();
    System.out.println(p);
    ois.close();
}

随机存取文件流

  • RandomAccessFile的使用

       ①,RandomAccessFile直接继承于java.lang.Object类,实现了DataInput和DataOutput接口

       ②,RandomAccessFile即可是输入流,也可是输出流

       ③,如果RandomAccessFile作为输出流时:

                     写出到文件如果不存在,则在执行过程中自动创建

                     写出的文件存在,则会对原有文件内容进行覆盖,(默认情况下,从头覆盖)raf.seek(3);//将指针调到角标为3的位置

       ④,RandomAccessFile可以实现一个多线程断点下载的功能,下载前会建立两个临时文件,一个是与被下载文件大小相同的空文件,另一个是记录文件指针的位置文件,每次暂停的时候会保存上一次的指针,然后断点下载的时候,会继续从上一次的地方下载

构造器

public RandomAccessFile(File file, String mode)
  • mode参数------->
           r:以只读方式打开
           rw:打开以便读取和写入
           rwd:打开以便读取和写入,同步文件内容的更新
           rws:打开以便读取和写入,同步文件内容和元数据的更新
@Test
public void demo03() throws IOException {
    RandomAccessFile raf1= new RandomAccessFile(new File("1.jpg"),"r");
    RandomAccessFile raf1= new RandomAccessFile(new File("2.jpg"),"rw");
    byte[] buffer = new byte[1024]'
    int len;
    while((len = raf1.read(buffer)) != -1){
            raf2.write(buffer, 0, len);
    }
    raf1.close();
    raf2.close();
}

例:在文件中指定的位置插入内容

@Test
public void demo04() throws IOException {
    RandomAccessFile raf1= new RandomAccessFile(new File("hello.txt"),"rw");
    raf1.seek(3);//将指针调到角标为3的位置(0开始)
    //保存指针3后面的所有数据到StringBuilder中
    StringBuilder builder = new StringBuilder((int)new File("hello.txt").length());
    byte[] buffer = new byte[20];
    int len;
    while ((len = raf1.read(buffer)) != -1){
        builder.append(new String(buffer, 0, len));
    }
    //调回指针,写入zxc
    raf1.seek(3);
    raf1.write("123".getBytes());
    
    //将StringBuilder中的数据写入到文件中
    raf1.write(builder.toString().getBytes());
    raf1.close();
}

下一章,(30)Java中的网络编程