跳到主要内容

Java基础系列 Io流

·8351 字·17 分钟

Java基础系列–IO流

一、IO流原理及流的分类 #

1.Java IO原理 #

  • I/O是Input/Output的缩写,I/O技术用于处理设备之间的数据传输。如读/写文件,网络通讯等。
  • Java程序中,对于数据的输入/输出操作以“流(stream)”的方式进行。
  • java.io包下提供了各种“流”类和接口,用以获取不同类别的数据,并通过标准的方法输入或输出数据。
  • 输入input:读取外部数据(磁盘、光盘等存储设备的数据)到程序(内存)中。
  • 输出output:将程序(内存)数据输出到磁盘、光盘等存储设备中。

2.流的分类 #

Java的IO流共涉及40多个类,实际上非常规则,都是从InputStream、OutputStream、Reader、Writer这四个抽象基类派生的。

图解IO分类:

IO流体系:

分类字节输入流字节输出流字符输入流字符输出流
抽象基类InputStreamOutputStreamReaderWriter
访问文件FileInputStreamFileOutputStreamFileReaderFileWriter
访问数组ByteArrayInputStreamByteArrayOutputStreamCharArrayReaderCharArrayWriter
访问管道PipedInputStreamPipedOutputStreamPipedReaderPipedWriter
访问字符串StringReaderStringWriter
缓冲流BufferedInputStreamBufferedOutputStreamBufferedReaderBufferedWriter
转换流InputStreamReaderOutputStreamWriter
对象流ObjectInputStreamObjectOutputStream
FilterInputStreamFilterOutputStreamFilterReaderFilterWriter
分类字节输入流字节输出流字符输入流字符输出流
打印流PrintStreamPrintWriter
推回输入流PushbackInputStreamPushbackReader
特殊流DataInputStreamDataOutputStream

3.节点流和处理流 #

  • 节点流:直接从数据源或目的地读写数据。

  • 处理流:不直接连接到数据源或目的地,而是“连接”在已存在的流(节点流或处理流)之上,通过对数据的处理为程序提供更为强大的读写功能。

InputStream和Reader

  • InputStream和Reader是所有输入流的基类。
  • InputStream(典型实现:FileInputStream)
    • int read():从输入流中读取数据的下一个字节。返回0到255范围内的int字节值。如果因为已经到达流末尾而没有可用的字节,则返回值-1。
    • int read(byte() b):从此输入流中将最多b.length个字节的数据读入一个byte数组中。如果因为已经到达流末尾而没有可用的字节,则返回值-1。否则已整数形式返回实际读取的字节数。
    • int read(byte[] b,int off,int len):将输入流中最多len个数据字节读入byte数组。尝试读取len个字节,但读取的字节也可能小于该值。以整数形式返回实际读取的字节数。如果因为流位于文件末尾而没有可用的字节,则返回值-1。
    • public void close() throws IOException:关闭此输入流并释放与该流关联的所有系统资源。
  • Reader(典型实现:FileReader)
    • int read():读取单个字符。作为整数读取的字符,范围在0到65535之间(0x00-0xffff)(2个字节的Unicode码),如果已到达流的末尾,则返回-1。
    • int read(char[] cbuf):将字符读入数组。如果已到达流的末尾,则返回-1。否则返回本次读取的字符数。
    • int read(char[] cbuf,int off,int len):将字符读入数组的某一部分。存到数组cbuf中,从off处开始存储,最多读len个字符。如果已到达流的末尾,则返回-1。否则返回本次读取的字符数。
    • public void close() throws IOException:关闭此输入流并释放与该流关联的所有系统资源。
  • 程序中打开的文件IO资源不属于内存里的资源,垃圾回收机制无法回收该资源,所以应该显示关闭IO资源。
  • FileInputStream从文件系统中的某个文件中获得输入字节。FileInputStream用于读取非文本数据之类的原始字节流。要读取字符流,需要使用FileReader。

OutputStream和Writer

  • OutputStream(典型实现:FileOutputStream)
    • void write(int b):将指定的字节写入此输入流。write的常规协定是:向输出流写入一个字节。要写入的字节是参数b的八个低位。b的24个高位将被忽略。即写入0~255范围的。
    • void write(byte[] b):将b.length个字节从指定的byte数组写入此输出流。write(b)的常规协定是:应该与调用write(b,0,b.length)的效果完全相同。
    • void write(byte[] b,int off,int len):将指定byte数组中从偏移量off开始的len个字节写入此输出流。
    • public void flush() throws IOException:刷新此输出流并强制写出所有缓存的输出字节,调用此方法指示应将这些字节立即写入它们预期的目标。
    • public void close() throws IOException:关闭此输出流并释放与该流关联的所有系统资源。
  • Writer(典型实现:FileWriter)
    • void write(int c):写入单个字符。要写入的字符包含在给定整数值的16个低位中,16高位被忽略。即写入0到65535之间的Unicode码。
    • void write(char[] cbuf):写入字符数组。
    • void write(char[] cbuf,int off,int len):写入字符数组的某一部分。从off开始,写入len个字符。
    • void write(String str):写入字符串。
    • void write(String str,int off,int len):写入字符串的某一部分。
    • void flush():刷新该流的缓冲,则立即将他们写入预期目标。
    • public void close() throws IOException:关闭此输出流并释放与该流关联的所有系统资源。
  • 因为字符流直接已字符作为操作单位,所以Writer可以用字符串来替换字符数组,即以String对象作为参数。
    • void write(String str)
    • void write(String str,int off,int len)
  • FileOutputStream从文件系统中的某个文件中获得输出字节。FileOutputStream用于写出非文本数据之类的原始字节流。要写出字符流,需要使用FileWriter。

二、节点流(或文件流)(FileReader/FileWriter,FileInputStream/FileOutputStream) #

FileReader/FileWriter

package com.anarkh.java;

import org.junit.Test;

import java.io.*;

/**
 *
 * 一、流的分类:
 * 1.操作数据单位:字节流、字符流
 * 2.数据的流向:输入流、输出流
 * 3.流的角色:节点流、处理流
 *
 * 二、流的体系结构
 * 抽象基类         节点流(或文件流)                               缓冲流(处理流的一种)
 * 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()
 *
 * @author anarkh
 * @create 2020
 */
public class FileReaderWriterTest {

    public static void main(String[] args) {
        File file = new File("hello.txt");//相较于当前工程
        System.out.println(file.getAbsolutePath();

        File file1 = new File("test\\hello.txt");
        System.out.println(file1.getAbsolutePath();
    }

    /*
    将test下的hello.txt文件内容读入程序中,并输出到控制台

    说明点:
    1. read()的理解:返回读入的一个字符。如果达到文件末尾,返回-1
    2. 异常的处理:为了保证流资源一定可以执行关闭操作。需要使用try-catch-finally处理
    3. 读入的文件一定要存在,否则就会报FileNotFoundException。

     */
    @Test
    public void testFileReader(){
        FileReader fr = null;
        try {
            //1.实例化File类的对象,指明要操作的文件
            //使用@Test相较于当前Module,而使用main方法的话需要传入"test\\hello.txt"
            File file = new File("hello.txt");//相较于当前Module
            //2.提供具体的流
            fr = new FileReader(file);

            //3.数据的读入
            //read():返回读入的一个字符。如果达到文件末尾,返回-1
            //方式一:
//        int data = fr.read();
//        while(data != -1){
//            System.out.print((char)data);
//            data = fr.read();
//        }

            //方式二:语法上针对于方式一的修改
            int data;
            while((data = fr.read() != -1){
                System.out.print((char)data);
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            //4.流的关闭操作
//            try {
//                if(fr != null)
//                    fr.close();
//            } catch (IOException e) {
//                e.printStackTrace();
//            }
            //或
            if(fr != null){
                try {
                    fr.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }

    }

    //对read()操作升级:使用read的重载方法
    @Test
    public void testFileReader1()  {
        FileReader fr = null;
        try {
            //1.File类的实例化
            File file = new File("hello.txt");

            //2.FileReader流的实例化
            fr = new FileReader(file);

            //3.读入的操作
            //read(char[] cbuf):返回每次读入cbuf数组中的字符的个数。如果达到文件末尾,返回-1
            char[] cbuf = new char[5];
            int len;
            while((len = fr.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);
//                System.out.print(str);
                //正确的写法
                String str = new String(cbuf,0,len);
                System.out.print(str);
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if(fr != null){
                //4.资源的关闭
                try {
                    fr.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }

            }
        }

    }
    /*
    从内存中写出数据到硬盘的文件里。

    说明:
    1. 输出操作,对应的File可以不存在的。并不会报异常
    2.
         File对应的硬盘中的文件如果不存在,在输出的过程中,会自动创建此文件。
         File对应的硬盘中的文件如果存在:
                如果流使用的构造器是:FileWriter(file,false) / FileWriter(file):对原有文件的覆盖
                如果流使用的构造器是:FileWriter(file,true):不会对原有文件覆盖,而是在原有文件基础上追加内容

     */
    @Test
    public void testFileWriter() {
        FileWriter fw = null;
        try {
            //1.提供File类的对象,指明写出到的文件
            File file = new File("hello1.txt");

            //2.提供FileWriter的对象,用于数据的写出
            fw = new FileWriter(file,false);

            //3.写出的操作
            fw.write("I have a dream!\n");
            fw.write("you need to have a dream!");
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            //4.流资源的关闭
            if(fw != null){

                try {
                    fw.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }


    }

    @Test
    public void testFileReaderFileWriter() {
        FileReader fr = null;
        FileWriter fw = null;
        try {
            //1.创建File类的对象,指明读入和写出的文件
            File srcFile = new File("hello.txt");
            File destFile = new File("hello2.txt");

            //不能使用字符流来处理图片等字节数据
//            File srcFile = new File("anarkh.jpg");
//            File destFile = new File("anarkh1.jpg");


            //2.创建输入流和输出流的对象
            fr = new FileReader(srcFile);
            fw = new FileWriter(destFile);


            //3.数据的读入和写出操作
            char[] cbuf = new char[5];
            int len;//记录每次读入到cbuf数组中的字符的个数
            while((len = fr.read(cbuf) != -1){
                //每次写出len个字符
                fw.write(cbuf,0,len);

            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            //4.关闭流资源
            //方式一:
//            try {
//                if(fw != null)
//                    fw.close();
//            } catch (IOException e) {
//                e.printStackTrace();
//            }finally{
//                try {
//                    if(fr != null)
//                        fr.close();
//                } catch (IOException e) {
//                    e.printStackTrace();
//                }
//            }
            //方式二:
            try {
                if(fw != null)
                    fw.close();
            } catch (IOException e) {
                e.printStackTrace();
            }

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

        }

    }

}

FileInputStream/FileOutputStream

package com.anarkh.java;

import org.junit.Test;

import java.io.*;

/**
 * 测试FileInputStream和FileOutputStream的使用
 *
 * 结论:
 * 1. 对于文本文件(.txt,.java,.c,.cpp),使用字符流处理
 * 2. 对于非文本文件(.jpg,.mp3,.mp4,.avi,.doc,.ppt,...),使用字节流处理
 *
 *一个小功能:可以将图片作异或处理加密,每个字节都^5加密,然后再^5就可以解密。(异或多少随意)
 *
 * @author anarkh
 * @create 2020
 */
public class FileInputOutputStreamTest {

    //使用字节流FileInputStream处理文本文件,可能出现乱码。
    @Test
    public void testFileInputStream() {
        FileInputStream fis = null;
        try {
            //1. 造文件
            File file = new File("hello.txt");

            //2.造流
            fis = new FileInputStream(file);

            //3.读数据
            byte[] buffer = new byte[5];
            int len;//记录每次读取的字节的个数
            while((len = fis.read(buffer) != -1){

                String str = new String(buffer,0,len);
                System.out.print(str);

            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if(fis != null){
                //4.关闭资源
                try {
                    fis.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }

            }
        }

    }

    /*
    实现对图片的复制操作
     */
    @Test
    public void testFileInputOutputStream()  {
        FileInputStream fis = null;
        FileOutputStream fos = null;
        try {
            //
            File srcFile = new File("anarkh.jpg");
            File destFile = new File("anarkh2.jpg");

            //
            fis = new FileInputStream(srcFile);
            fos = new FileOutputStream(destFile);

            //复制的过程
            byte[] buffer = new byte[5];
            int len;
            while((len = fis.read(buffer) != -1){
                fos.write(buffer,0,len);
            }

        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if(fos != null){
                //
                try {
                    fos.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if(fis != null){
                try {
                    fis.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }

            }
        }

    }

    //指定路径下文件的复制
    public void copyFile(String srcPath,String destPath){
        FileInputStream fis = null;
        FileOutputStream fos = null;
        try {
            //
            File srcFile = new File(srcPath);
            File destFile = new File(destPath);

            //
            fis = new FileInputStream(srcFile);
            fos = new FileOutputStream(destFile);

            //复制的过程
            byte[] buffer = new byte[1024];
            int len;
            while((len = fis.read(buffer) != -1){
                fos.write(buffer,0,len);
            }

        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if(fos != null){
                //
                try {
                    fos.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if(fis != null){
                try {
                    fis.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }

            }
        }


    }

    @Test
    public void testCopyFile(){

        long start = System.currentTimeMillis();

        String srcPath = "C:\\Users\\Administrator\\Desktop\\01-视频.avi";
        String destPath = "C:\\Users\\Administrator\\Desktop\\02-视频.avi";


//        String srcPath = "hello.txt";
//        String destPath = "hello3.txt";

        copyFile(srcPath,destPath);


        long end = System.currentTimeMillis();

        System.out.println("复制操作花费的时间为:" + (end - start);//618

    }

}

使用节点流注意

  • 定义文件路径时,注意:可以用“/”或者“\\”。
  • 在写入一个文件时,如果使用构造器FileOutputStream(file),则目录下有同名文件将被覆盖。
  • 如果使用构造器FileOutputStream(file,true),则目录下的同名文件不会被覆盖,在文件内容末尾追加内容。
  • 在读取文件时,必须保证该文件已存在,否则报异常。
  • 字节流操作字节,比如:.mp3,.avi,.rmvb,.mp4,.jpg,.doc,.ppt
  • 字符流操作字符,只能操作普通文本文件。最常见的文本文件:.txt,.java,.c,.cpp等语言的源代码。尤其注意.doc,.excel,.ppt这些不是文本文件。

三、缓冲流 #

  • 缓冲流是处理流之一。

  • 为了提高数据读写的速度,Java API提供了带缓冲功能的流类,在使用这些流类时,会创建一个内部缓冲区数组,缺省使用8192个字节(8k)的缓冲区。

  • 缓冲流要“套接”在相应的节点流之上,根据数据操作单位可以把缓冲流分为:

    • BufferedInputStream和BufferedOutputStream
    • BufferedReader和BufferedWriter
  • 当读取数据时,数据按块读入缓冲区,其后的读操作则直接访问缓冲区。

  • 当使用BufferedInputStream读取字节文件时,BufferedInputStream会一次性从文件中读取8192个(8k),存在缓冲区中,直到缓冲区装满了,才重新从文件中读取下一个8192个字节数组。

  • 向流中写入字节时,不会直接写到文件,先写到缓冲区中知道缓冲区写满,BufferedOutputStream才会把缓冲区中的数据一次性写到文件里。使用方法flush()可以强制将缓冲区的内容全部写入输出流。

  • 关闭流的顺序和打开流的顺序相反。只要关闭最外层流即可,关闭最外层流也会相应关闭内存节点流。

  • flush()方法的使用:手动将buffer中内容写入文件。

  • 如果是带缓冲区的流对象的close()方法,不但会关闭流,还会在关闭流之前刷新缓冲区,关闭后不能再写出。

package com.anarkh.java;

import org.junit.Test;

import java.io.*;

/**
 * 处理流之一:缓冲流的使用
 *
 * 1.缓冲流:
 * BufferedInputStream
 * BufferedOutputStream
 * BufferedReader
 * BufferedWriter
 *
 * 2.作用:提供流的读取、写入的速度
 *   提高读写速度的原因:内部提供了一个缓冲区
 *
 * 3. 处理流,就是“套接”在已有的流的基础上。
 *
 * @author anarkh
 * @create 2020
 */
public class BufferedTest {

    /*
    实现非文本文件的复制
     */
    @Test
    public void BufferedStreamTest() throws FileNotFoundException {
        BufferedInputStream bis = null;
        BufferedOutputStream bos = null;

        try {
            //1.造文件
            File srcFile = new File("anarkh.jpg");
            File destFile = new File("anarkh3.jpg");
            //2.造流
            //2.1 造节点流
            FileInputStream fis = new FileInputStream((srcFile);
            FileOutputStream fos = new FileOutputStream(destFile);
            //2.2 造缓冲流
            bis = new BufferedInputStream(fis);
            bos = new BufferedOutputStream(fos);

            //3.复制的细节:读取、写入
            byte[] buffer = new byte[10];
            int len;
            while((len = bis.read(buffer) != -1){
                bos.write(buffer,0,len);

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

            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            //4.资源关闭
            //要求:先关闭外层的流,再关闭内层的流
            if(bos != null){
                try {
                    bos.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }

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

            }
            //说明:关闭外层流的同时,内层流也会自动的进行关闭。关于内层流的关闭,我们可以省略.
//        fos.close();
//        fis.close();
        }



    }

    //实现文件复制的方法
    public void copyFileWithBuffered(String srcPath,String destPath){
        BufferedInputStream bis = null;
        BufferedOutputStream bos = null;

        try {
            //1.造文件
            File srcFile = new File(srcPath);
            File destFile = new File(destPath);
            //2.造流
            //2.1 造节点流
            FileInputStream fis = new FileInputStream((srcFile);
            FileOutputStream fos = new FileOutputStream(destFile);
            //2.2 造缓冲流
            bis = new BufferedInputStream(fis);
            bos = new BufferedOutputStream(fos);

            //3.复制的细节:读取、写入
            byte[] buffer = new byte[1024];
            int len;
            while((len = bis.read(buffer) != -1){
                bos.write(buffer,0,len);
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            //4.资源关闭
            //要求:先关闭外层的流,再关闭内层的流
            if(bos != null){
                try {
                    bos.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }

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

            }
            //说明:关闭外层流的同时,内层流也会自动的进行关闭。关于内层流的关闭,我们可以省略.
//        fos.close();
//        fis.close();
        }
    }

    @Test
    public void testCopyFileWithBuffered(){
        long start = System.currentTimeMillis();

        String srcPath = "C:\\Users\\Administrator\\Desktop\\01-视频.avi";
        String destPath = "C:\\Users\\Administrator\\Desktop\\03-视频.avi";


        copyFileWithBuffered(srcPath,destPath);


        long end = System.currentTimeMillis();

        System.out.println("复制操作花费的时间为:" + (end - start);//618 - 176
    }


    /*
    使用BufferedReader和BufferedWriter实现文本文件的复制

     */
    @Test
    public void testBufferedReaderBufferedWriter(){
        BufferedReader br = null;
        BufferedWriter bw = null;
        try {
            //创建文件和相应的流
            br = new BufferedReader(new FileReader(new File("dbcp.txt"));
            bw = new BufferedWriter(new FileWriter(new File("dbcp1.txt"));

            //读写操作
            //方式一:使用char[]数组
//            char[] cbuf = new char[1024];
//            int len;
//            while((len = br.read(cbuf) != -1){
//                bw.write(cbuf,0,len);
//    //            bw.flush();
//            }

            //方式二:使用String
            String data;
            while((data = br.readLine() != null){
                //方法一:
//                bw.write(data + "\n");//data中不包含换行符
                //方法二:
                bw.write(data);//data中不包含换行符
                bw.newLine();//提供换行的操作

            }


        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            //关闭资源
            if(bw != null){

                try {
                    bw.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if(br != null){
                try {
                    br.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }

            }
        }

    }

}

四、转换流 #

package com.anarkh.java;

import org.junit.Test;

import java.io.*;

/**
 * 处理流之二:转换流的使用
 * 1.转换流:属于字符流
 *   InputStreamReader:将一个字节的输入流转换为字符的输入流
 *   OutputStreamWriter:将一个字符的输出流转换为字节的输出流
 *
 * 2.作用:提供字节流与字符流之间的转换
 *         很多时候使用转换流来处理文件乱码问题。实现编码和解码的功能。
 *
 * 3. 解码:字节、字节数组  --->字符数组、字符串
 *    编码:字符数组、字符串 ---> 字节、字节数组
 *
 *
 * 4.字符集
 *ASCII:美国标准信息交换码。
    用一个字节的7位可以表示。
 ISO8859-1:拉丁码表。欧洲码表
    用一个字节的8位表示。
 GB2312:中国的中文编码表。最多两个字节编码所有字符
 GBK:中国的中文编码表升级,融合了更多的中文文字符号。最多两个字节编码
 Unicode:国际标准码,融合了目前人类使用的所有字符。为每个字符分配唯一的字符码。所有的文字都用两个字节来表示。
 UTF-8:变长的编码方式,可用1-4个字节来表示一个字符。

 *
 *
 * @author anarkh
 * @create 2020
 */
public class InputStreamReaderTest {

    /*
    此时处理异常的话,仍然应该使用try-catch-finally
    InputStreamReader的使用,实现字节的输入流到字符的输入流的转换
     */
    @Test
    public void test1() throws IOException {

        FileInputStream fis = new FileInputStream("dbcp.txt");
//        InputStreamReader isr = new InputStreamReader(fis);//使用系统默认的字符集
        //参数2指明了字符集,具体使用哪个字符集,取决于文件dbcp.txt保存时使用的字符集
        InputStreamReader isr = new InputStreamReader(fis,"UTF-8");//使用系统默认的字符集

        char[] cbuf = new char[20];
        int len;
        while((len = isr.read(cbuf) != -1){
            String str = new String(cbuf,0,len);
            System.out.print(str);
        }

        isr.close();

    }

    /*
    此时处理异常的话,仍然应该使用try-catch-finally

    综合使用InputStreamReader和OutputStreamWriter
     */
    @Test
    public void test2() throws Exception {
        //1.造文件、造流
        File file1 = new File("dbcp.txt");
        File file2 = new File("dbcp_gbk.txt");

        FileInputStream fis = new FileInputStream(file1);
        FileOutputStream fos = new FileOutputStream(file2);

        InputStreamReader isr = new InputStreamReader(fis,"utf-8");
        OutputStreamWriter osw = new OutputStreamWriter(fos,"gbk");

        //2.读写过程
        char[] cbuf = new char[20];
        int len;
        while((len = isr.read(cbuf) != -1){
            osw.write(cbuf,0,len);
        }

        //3.关闭资源
        isr.close();
        osw.close();
    }

}

五、其他流 #

package com.anarkh.java;

import org.junit.Test;

import java.io.*;

/**
 * 其他流的使用
 * 1.标准的输入、输出流
 * 2.打印流
 * 3.数据流
 *
 * @author anarkh
 * @create 2020
 */
public class OtherStreamTest {

    /*
    1.标准的输入、输出流
    1.1
    System.in:标准的输入流,默认从键盘输入
    System.out:标准的输出流,默认从控制台输出
    1.2
    System类的setIn(InputStream is) / setOut(PrintStream ps)方式重新指定输入和输出的流。

    1.3练习:
    从键盘输入字符串,要求将读取到的整行字符串转成大写输出。然后继续进行输入操作,
    直至当输入“e”或者“exit”时,退出程序。

    方法一:使用Scanner实现,调用next()返回一个字符串
    方法二:使用System.in实现。System.in  --->  转换流 ---> BufferedReader的readLine()

     */
    public static void main(String[] args) {
        BufferedReader br = null;
        try {
            InputStreamReader isr = new InputStreamReader(System.in);
            br = new BufferedReader(isr);

            while (true) {
                System.out.println("请输入字符串:");
                String data = br.readLine();
                if ("e".equalsIgnoreCase(data) || "exit".equalsIgnoreCase(data) {
                    System.out.println("程序结束");
                    break;
                }

                String upperCase = data.toUpperCase();
                System.out.println(upperCase);

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

            }
        }
    }

    /*
    2. 打印流:PrintStream 和PrintWriter

    2.1 提供了一系列重载的print() 和 println()
    2.2 练习:



     */

    @Test
    public void test2() {
        PrintStream ps = null;
        try {
            FileOutputStream fos = new FileOutputStream(new File("D:\\IO\\text.txt");
            // 创建打印输出流,设置为自动刷新模式(写入换行符或字节 '\n' 时都会刷新输出缓冲区)
            ps = new PrintStream(fos, true);
            if (ps != null) {// 把标准输出流(控制台输出)改成文件
                System.setOut(ps);
            }


            for (int i = 0; i <= 255; i++) { // 输出ASCII字符
                System.out.print((char) i);
                if (i % 50 == 0) { // 每50个数据一行
                    System.out.println(); // 换行
                }
            }


        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } finally {
            if (ps != null) {
                ps.close();
            }
        }

    }

    /*
    3. 数据流
    3.1 DataInputStream 和 DataOutputStream
    3.2 作用:用于读取或写出基本数据类型的变量或字符串

    练习:将内存中的字符串、基本数据类型的变量写出到文件中。

    注意:处理异常的话,仍然应该使用try-catch-finally.
     */
    @Test
    public void test3() throws IOException {
        //1.
        DataOutputStream dos = new DataOutputStream(new FileOutputStream("data.txt");
        //2.
        dos.writeUTF("汉字");
        dos.flush();//刷新操作,将内存中的数据写入文件
        dos.writeInt(23);
        dos.flush();
        dos.writeBoolean(true);
        dos.flush();
        //3.
        dos.close();


    }
    /*
    将文件中存储的基本数据类型变量和字符串读取到内存中,保存在变量中。

    注意点:读取不同类型的数据的顺序要与当初写入文件时,保存的数据的顺序一致!

     */
    @Test
    public void test4() throws IOException {
        //1.
        DataInputStream dis = new DataInputStream(new FileInputStream("data.txt");
        //2.
        String name = dis.readUTF();
        int age = dis.readInt();
        boolean isMale = dis.readBoolean();

        System.out.println("name = " + name);
        System.out.println("age = " + age);
        System.out.println("isMale = " + isMale);

        //3.
        dis.close();

    }

}

六、一种可以省略关流(自动关流)的写法 #

try括号内的资源会在try语句结束后自动释放,前提是这些可关闭的资源必须实现java.lang.AutoCloseable接口。InputStream和OutputStream父类中实现了AutoCloseable接口。

try (
        InputStream fis = new FileInputStream(source);
        OutputStream fos = new FileOutputStream(target)
     ){
        byte[] buf = new byte[8192];
  
        int i;
        while ((i = fis.read(buf) != -1) {
            fos.write(buf, 0, i);
        }
    }
    catch (Exception e) {
        e.printStackTrace();
    }
@Override
    public void downNote(String fileType, HttpServletResponse response) {
        Resource resource = null;
        if("rccbjdkhpgb".equals(fileType){
            resource = SpringContextUtils.getApplicationContext()
                    .getResource("classpath:/excel/rccbjdkhpgb.doc");
        }else if("rccbjdspb".equals(fileType){
            resource = SpringContextUtils.getApplicationContext()
                    .getResource("classpath:/excel/rccbjdspb.xls");
        }

        try (InputStream is = resource.getInputStream();
             OutputStream ouputStream = response.getOutputStream();) {
            //写入前台
            response.setContentType("application/x-download");
            response.setHeader("Content-disposition", "attachment;");
            ouputStream.write(IOUtils.toByteArray(is);
            ouputStream.flush();

        } catch (IOException e) {
            e.printStackTrace();
        }
    }