Java I/O流 02

IO流·字节流

IO流概述及其分类

* A:概念
  * IO流用来处理设备之间的数据传输
  * Java对数据的操作是通过流操作的
  * Java用于操作流的类都在IO包中
  * 流按流向分为两种输入流、输出流
  * 流按操作类型分为两种
    * 字节流:字节流可以操作任何数据,因为再计算机中任何数据都是以字节的形式存储的
    * 字符流:字符流只能操作纯字符数据,比较方便

* B:IO流常用父类
  * 字节流的抽象父类
    * InputStream
    * OutputStream
  * 字符流的抽象父类
    * Reader
    * Writer

* C:IO程序书写
  * 使用前,导入IO包中的类
  * 使用时:进行IO异常处理
  * 使用后:释放资源

FileInputStream

* read() 一次读取一个字节

package com.heima.stream;

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;

public class Demo1_FileInputStream {

    public static void main(String[] args) throws IOException {
        // demo1();
        // demo2();

    }

    public static void demo2() throws FileNotFoundException, IOException {
        FileInputStream fis = new FileInputStream("xxx.txt"); 
        int b;
        while ((b = fis.read()) != -1) { // 判断文件是否读到末尾
            System.out.print(Character.toChars(b));
        }
        fis.close(); // 关流
    }

    public static void demo1() throws FileNotFoundException, IOException {
        FileInputStream fis = new FileInputStream("xxx.txt"); // 创建流对象,传入文件名或File对象
        int x = fis.read(); // 从输入流中读取一个字节
        System.out.println(x);

        int y = fis.read(); // 每read一次,指针就会向后移动一个单位
        System.out.println(y);
        
        int z = fis.read();
        System.out.println(z);
        
        int a = fis.read();    // 文件的结束标记是-1字节,-1不是有效字节
        System.out.println(a);
        
        fis.close(); // 关闭流,释放资源
    }
}
FileInputStream

read() 方法返回值为什么是int

* read() 方法读取的是一个字节,为什么返回的是int,而不是byte?

  * 因为字节输入流可以操作任意类型的文件,比如图片音频等,这些文件的底层都是二进制形式存储的
  * 如果每次读取都返回byte,有可能再中间的时候遇到 11111111 ,而这是byte类型的-1,程序一旦遇到就不读了
  * 所以在读取的时候,用int类型接收,如果有 11111111 会在其前面补上24个0 凑足4个字节,
  * 那么byte类型的-1,变成了int类型的255,int类型的-1变成了结束标记

FileOutputStream

* write() 一次写出一个字节
* FileoutputStream的构造方法实现数据追加

package com.heima.stream;

import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;

public class Demo2_FileOutputStream {
    // FileOutputStream在创建对象的时候如果 没有这个文件会帮我们创建出来 如果有这个文件,会先将这个文件清空
    public static void main(String[] args) throws IOException {
        // demo1();
        // demo2();
    }

    public static void demo2() throws FileNotFoundException, IOException {
        FileOutputStream fos = new FileOutputStream("yyy.txt", true); // 如果想续写,就在第二个参数传入true
        fos.write(97);
        fos.write(97);
        fos.write(97);

        fos.close();
    }

    public static void demo1() throws FileNotFoundException, IOException {
        FileOutputStream fos = new FileOutputStream("yyy.txt"); // 创建字节输出流对象,如果没有该文件,就自动创建一个
        fos.write(97); // 虽然写出的是一个int数,但是到文件上的是一个字节,会自动去除前3个8位
        fos.write(98);
        fos.write(99);

        fos.close(); // 关流
    }
}
FileOutputStream

拷贝文件

* FileInputStream读取
* FileOutputStream写出

package com.heima.stream;

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;

public class Demo3_Copy {

    public static void main(String[] args) throws IOException {
        // demo1();
        // demo2();
        // demo3();
    }

    public static void demo3() throws FileNotFoundException, IOException {
        // 第二种拷贝方法,但是开发中不推荐,因为容易导致内存溢出
        FileInputStream fis = new FileInputStream("亲爱的小孩.mp3");
        FileOutputStream fos = new FileOutputStream("copy2.mp3");
        int len = fis.available();

        byte[] arr = new byte[len]; // 创建与文件一样大小的字节数组
        fis.read(arr); // 将文件上的字节读取到内存中
        fos.write(arr); // 将字节数组写入到文件上

        fis.close();
        fos.close();
    }

    public static void demo2() throws FileNotFoundException, IOException {
        // 第一种拷贝方式,不推荐,效率太低
        FileInputStream fis = new FileInputStream("亲爱的小孩.mp3"); // 创建输入流对象
        FileOutputStream fos = new FileOutputStream("copy.mp3"); // 创建输出流对象
        int b;
        while ((b = fis.read()) != -1) { // 逐个字节拷贝,效率较低
            fos.write(b);
        }
        fis.close();
        fos.close();
    }

    public static void demo1() throws FileNotFoundException, IOException {
        FileInputStream fis = new FileInputStream("19051435  赵文彬 (2).jpg"); // 创建输入流对象
        FileOutputStream fos = new FileOutputStream("copy.jpg"); // 创建输出流对象

        int b;
        int count = 0;
        while ((b = fis.read()) != -1) { // 在不断的读取每一个字节
            fos.write(b); // 将每一个字节写出
            count++; // 记录字节数
        }
        fis.close(); // 关流,释放资源
        fos.close();
        System.out.println(count / 1024); // 照片为196KB大小
    }
}
Copy

定义小数组的标准格式

* 案例演示
  * 字节流一次读写一个字节数组复制图片和视频

package com.heima.stream;

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;

public class Demo4_ArrayCopy {
    // 第三种拷贝方式: 定义小数组
    public static void main(String[] args) throws IOException {
        // demo1();
        // demo2();
        // demo3();
    }

    public static void demo3() throws FileNotFoundException, IOException {
        FileInputStream fis = new FileInputStream("亲爱的小孩.mp3");
        FileOutputStream fos = new FileOutputStream("亲爱的小孩1.mp3");

        byte[] arr = new byte[1024 * 8]; // 一般把数组的大小设置为1024的整数倍
        int len;
        while ((len = fis.read(arr)) != -1) { // 如果忘记加arr,返回的就不是读取的字节个数,而是字节的码表值
            fos.write(arr, 0, len);
        }

        fis.close();
        fos.close();
    }

    public static void demo2() throws FileNotFoundException, IOException {
        FileInputStream fis = new FileInputStream("xxx.txt"); // 创建字节输入流
        FileOutputStream fos = new FileOutputStream("yyy.txt"); // 创建字节输出流

        byte[] arr = new byte[2];
        int len; // 记录有效的字节个数
        while ((len = fis.read(arr)) != -1) {
            // fos.write(arr);
            fos.write(arr, 0, len); // 保证写入的是abc,而不是abcb
        }
        fis.close(); // 关流
        fos.close();
    }

    public static void demo1() throws FileNotFoundException, IOException {
        FileInputStream fis = new FileInputStream("xxx.txt");
        byte[] arr = new byte[2];
        int a = fis.read(arr); // 将文件上的字节读取到字节数组中

        System.out.println(a); // 读到的有效的字节个数
        for (byte b : arr) { // 第一次获取到文件上的a和b
            System.out.println(b);
        }
        System.out.println("--------------");
        int c = fis.read(arr); // 返回读取的字节数
        System.out.println(c);
        for (byte b : arr) { // 读取到了c和b
            System.out.println(b);
        }
        fis.close(); // 关流
    }
}
Copy

BufferedInputStream 和 BufferedOutputStream 拷贝

* A:缓冲思想
  * 字节流一次读写一个数组的速度明显比一次读写一个字节的速度快很多,这是加入了数组这样的缓冲区效果
  * Java本身在设计的时候,也考虑到了这样的设计思想(装饰设计模式),所以提供了字节缓冲流

* B:BufferedInputStream
  * BufferedInputStream内置了一个缓冲区(数组)
  * 从BufferedInputStream中读取一个字节时,它会一次性从文件读取8192个,存储在缓冲区中,返回给程序一个
  * 程序再次读取时,就不用再找文件了,直接从缓冲区中获取即可
  * 知道缓冲区中所有的都被使用过后,才会重新从文件中国读取8192个

* C:BufferedOutputSteam
  * BufferedOutputStream也内置了一个缓冲区(数组)
  * 程序向流中写出字节时,不会直接写到文件中,而是先写到缓冲区内
  * 当缓冲区写满后,BufferedOutputStream才会把缓冲区中的数据一次性写到文件里

* D:案例演示

package com.heima.stream;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;

public class Demo5_BufferedCopy {
    
    public static void main(String[] args) throws IOException {
        // demo1();
        // demo2();

    }

    public static void demo2() throws FileNotFoundException, IOException {
        /*flush和close的方法的区别:
         * close方法:具备刷新功能,在关闭流之前,就会刷新一次缓冲区,
         *              将缓冲区的字节全都刷新到文件上,再关闭
         * flush方法:flush刷新完后还能写,但是close刷新后就不能再写了
         */
        
        BufferedInputStream bis = new BufferedInputStream(new FileInputStream("亲爱的小孩.mp3"));
        BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("亲爱的小孩5"));

        int b;
        while ((b = bis.read()) != -1) {
            bos.write(b);
        }
        bos.flush();

        bos.close();
        bis.close();
    }

    public static void demo1() throws FileNotFoundException, IOException {
        // 美女素颜状态
        FileInputStream fis = new FileInputStream("亲爱的小孩.mp3"); // 创建输入流对象,关联文件
        FileOutputStream fos = new FileOutputStream("亲爱的小孩2.mp3"); // 创建输出流对象
        // 美女化妆状态
        BufferedInputStream bis = new BufferedInputStream(fis); // 创建缓冲区对象,对输入流进行包装
        BufferedOutputStream bos = new BufferedOutputStream(fos); // 对输出流进行包装
        
        int b;
        while ((b = bis.read()) != -1) {
            bos.write(b);
        }
        
        bis.close(); // 只需要关闭包装后的就行了
        bos.close();
    }
}
BufferedInputStream

 * E:小数组的读写和带Buffered的读取哪个更快
  * 定义小数组如果是8192个字节大小的个Buffered进行比较的话
  * 定义小数组的会略胜一筹,因为读和写操作的都是同一个数组
  * 而Buffered操作的是两个数组

* F:flush 和 close 的方法的区别
  * flush() 方法
    * 用来刷新缓冲区,刷新后可以再次写出
  * close() 方法
    * 用来关闭流释放资源的,如果是带缓冲区的流对象的 close()方法,不但会关闭流,还会刷新缓冲区

字节流读写中文

* 字节流读取中文的问题
  * 字节流在读中文的时候可能会读到半个中文,造成乱码

* 字节流写出中文的问题
  * 字节流直接操作字节,所以写出中文必须将字符串转换成字节数组
  * 写出回车换行 write(" ".getBytes())

package com.heima.stream;

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;

public class Demo6_Chinese {
    
    public static void main(String[] args) throws IOException {
        // demo1();
        // demo2();
    }

    public static void demo2() throws FileNotFoundException, IOException {
        // 字节流写入中文需要将字符串转成字节数组
        FileOutputStream fos = new FileOutputStream("zzz.txt");
        fos.write("我读书少,你别骗我".getBytes()); // 要转换成字节数组写入
        fos.write("
".getBytes()); // 换行符也需要转换成字节数组
        fos.write("我读书少,你别骗我".getBytes());
        fos.close();
    }

    public static void demo1() throws FileNotFoundException, IOException {
        // 字节流读取中文可能会导致乱码,和码表有关
        FileInputStream fis = new FileInputStream("yyy.txt");
        byte[] arr = new byte[6];
        int len;
        while ((len = fis.read(arr)) != -1) {
            System.out.println(new String(arr, 0, len)); // 调用字符串的构造方法
        }
    }
}
FileInputStream

流的标准处理异常代码

* 1.5 版本 try...finally 嵌套

* 1.7 版本 try...close 
  * 原理:在try()中创建的流对象必须实现了AutoCloseable这个接口,如果实现了,
      在try后面的{ }(读写代码)执行后就会自动调用流对象的close方法将流关掉

package com.heima.stream;

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;

public class Demo7_TryFinally {

    public static void main(String[] args) throws IOException {
        // demo1();
        // demo2();
    }

    public static void demo2() throws IOException, FileNotFoundException {
        try (FileInputStream fis = new FileInputStream("xxx.txt");
                FileOutputStream fos = new FileOutputStream("yyy.txt");
                MyClose mc = new MyClose();) // 里面只能写具有自动关闭的功能的东西
        {
            int b;
            while ((b = fis.read()) != -1) {
                fos.write(b);
            } // 大括号执行完之后,会自动关闭小括号内的内容
        }
    }

    public static void demo1() throws FileNotFoundException, IOException {
        // 随意的抛出异常是不负责任的,一旦出现异常,流很有可能不会被关闭
        FileInputStream fis = null;
        FileOutputStream fos = null;

        try {
            fis = new FileInputStream("xxx.txt");
            fos = new FileOutputStream("yyy.txt");

            int b;
            while ((b = fis.read()) != -1) {
                fos.write(b);
            }
        } finally {
            try {
                if (fis != null) {
                    fis.close();
                }
            } finally { // try - finally 的嵌套的目的是尽可能多的关闭文件句柄
                if (fos != null) {
                    fos.close();
                }
            }
        }
    }
}

class MyClose implements AutoCloseable {
    public void close() {
        System.out.println("我关了");
    }
}
try...finally

图片加密

* 给图片加密

package com.heima.test;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;

public class Test1 {
    // 将写出的字节异或上一个数,这个数就是密钥,解密的时候再次异或密钥即可
    public static void main(String[] args) throws IOException {
        // demo1(); // 加密
        // demo2(); // 解密
        
    }
    public static void demo2() throws FileNotFoundException, IOException {
        BufferedInputStream bis = new BufferedInputStream(new FileInputStream("copy.jpg")); // 创建字节输入缓冲流,关联字节输入流对象
        BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("copy1.jpg")); // 创建字节输出缓冲流,关联字节输出流对象
        
        int b;
        while ((b = bis.read())!= -1) {
            bos.write(b ^ 123); // 再次异或,打回原形
        }
        
        bis.close(); // 关流
        bos.close();
    }
    
    public static void demo1() throws FileNotFoundException, IOException {
        BufferedInputStream bis = new BufferedInputStream(new FileInputStream("19051435  赵文彬 (2).jpg")); // 创建字节输入缓冲流,关联字节输入流对象
        BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("copy.jpg")); // 创建字节输出缓冲流,关联字节输出流对象
    
        int b;
        while ((b = bis.read())!= -1) {
            bos.write(b ^ 123); // 对每个字节进行异或处理
        }
        bis.close(); // 关流
        bos.close();
    }
}
Test1

拷贝文件

* 在控制台录入文件的路径,将文件拷贝到当前项目下

package com.heima.test;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.Scanner;

public class Test2 {
    /*
     * 分析: 
     * 1、创建键盘录入对象 
     * 2、定义方法对键盘录入的路径进行一系列的判断,如果是文件,就返回 
     * 3、在主方法中接收该文件 
     * 4、进行读写操作
     * 5、关流
     */
    public static void main(String[] args) throws IOException {
        File file = getFile(); // 获取文件
        BufferedInputStream bis = new BufferedInputStream(new FileInputStream(file)); // 创建字符输入缓冲流对象,关联字符输入对象
        BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(file.getName())); // 创建字符输出缓冲流流对象,关联字符输出流对象,文件对象和读取的文件同名

        int b;
        while ((b = bis.read()) != -1) { // 读取原文件中的数据
            bos.write(b); // 写到新文件中
        }
        System.out.println("文件拷贝成功");
        
        bis.close(); // 关流
        bos.close();
    }
    /*
     * 定义一个方法,获取键盘录入的文件路径,并封装成file对象返回 
     * 1、返回值类型是file 
     * 2、无参数列表
     */
    public static File getFile() {
        Scanner sc = new Scanner(System.in); // 创建键盘录入对象
        System.out.println("请输入一个文件的路径");
        while (true) {
            String line = sc.nextLine(); // 接收键盘录入的路径
            File file = new File(line); // 封装成File对象,并对其进行判断
            if (!file.exists()) {
                System.out.println("您录入的文件的路径不存在,请重新录入");
            } else if (file.isDirectory()) {
                System.out.println("您录入的是文件夹路径,请重新录入");
            } else {
                sc.close();
                return file; // 返回文件
            }
        }

    }

}
Test2

录入数据拷贝到文件

* 将键盘录入的数据拷贝到当前项目下的text.txt 文档中,录入遇到 quit时退出

package com.heima.test;

import java.io.FileOutputStream;
import java.io.IOException;
import java.util.Scanner;

public class Test3 {
    /*
     * 分析:
     * 1、创建键盘录入对象
     * 2、创建输出流对象,关联text.txt
     * 3、定义无限循环,遇到quit就退出循环,
     * 4、如果不是quit,就将内容录入
     * 5、关闭流
     */
    public static void main(String[] args) throws IOException {
        Scanner sc = new Scanner(System.in); // 创建键盘录入对象
        FileOutputStream fos = new FileOutputStream("test.txt"); // 创建字节输入流关联text.txt文件
        
        System.out.println("请输入数据"); // 提示输入
        while (true) { // 定义无限循环
            String line = sc.next(); // 录入数据
            
            if ("quit".equals(line)) { // 判断录入的内容是否位quit
                break; // 退出循环            
            } else {
                fos.write(line.getBytes());    // 将字符串转换成字节数组并写到文件中
                fos.write("
".getBytes()); // 写入换行符
            }
        }
        fos.close(); // 关流
    }
}
Test3
原文地址:https://www.cnblogs.com/zhaochuming/p/12710741.html