JAVA IO流(十七)

IO流(十七)

IO流:存储和读取数据的解决方案。

IO流的作用:用于读写数据(本地文件,网络)

按流向分类

  1. 输出流:写出,从程序到文件
  2. 输入流:读取,从文件到程序

按操作文件类型分类

  1. 字节流:可操作所有类型的文件
  2. 字符流:只可操作纯文本文件(用Windows系统自带的记事本打开且能读懂的文件,如:txt,md,xml,lrc)

体系

IO流体系

  • 字节流:可以拷贝任意类型的文件
    • InputStream:字节输入流,抽象类
      • 基本流FileInputStream:操作本地文件的字节输入流
      • 缓冲流BufferedInputStream:字节缓冲输入流
      • 反序列化流ObjectInputStream
      • 解压缩流ZipInputStream
    • OutputStream:字节输出流,抽象类
      • 基本流FileOutputStream:操作本地文件的字节输出流
      • 缓冲流BufferedOutputStream:字节缓冲输出流
      • 序列化流ObjectOutputStream
      • 字节打印流PrintStream
      • 压缩流ZipOutputStream
  • 字符流:读取纯文本文件中的数据,向纯文本文件写出数据
    • Reader:字符输入流,抽象类
      • 基本流FileReader:操作本地文件的字符输入流
      • 缓冲流BufferedReader:字符缓冲输入流
      • 转换流InputStreamReader:转换输入流
    • Writer:字符输出流,抽象类
      • 基本流FileWriter:操作本地文件的字符输出流
      • 缓冲流BufferedWriter:字符缓冲输出流
      • 转换流OutputStreamWriter:转换输出流
      • 字符打印流PrintWriter

字节流

字节输出流OutputStream

FileOutputStream

操作本地文件的字节输出流,可把程序中的数据写到本地文件。

步骤

  1. 创建字节输出流对象,就像在两个地点之间修一条公路
    1. 参数可以是字符串表示的路径或File对象
    2. 若文件不存在,则会创建新文件,但要保证父级路径存在
    3. 若文件存在,则会清空文件
  2. 写数据,就像在公路上传输
    1. write方法的参数是整数,则会写入整数对应的ASCII字符
  3. 释放资源,就像把公路拆掉
    1. 每次用完流后,均要释放资源

范例

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

public class Test{
    public static void main(String[] args) throws IOException {
        FileOutputStream fos=new FileOutputStream("b.txt");
        fos.write(97);
        fos.close();
    }
}
写数据的三种方式

方式一

void write(int b)

说明:一次写一个字节数据

范例

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

public class Test{
    public static void main(String[] args) throws IOException {
        FileOutputStream fos=new FileOutputStream("a.txt");
        fos.write(97);
        fos.close();
    }
}

/*a.txt的内容
a
*/

方式二

void write(byte[] b)

说明:一次写一个字节数组数据

范例:

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

public class Test{
    public static void main(String[] args) throws IOException {
        FileOutputStream fos=new FileOutputStream("a.txt");
        byte[] arr={97,98,99,100};
        fos.write(arr);
        fos.close();
    }
}

/*a.txt的内容
abcd
*/

方式三

void write(byte[] b,int off,int len)

说明:一次写一个字节数组的部分数据,off表示起始索引,len表示长度

范例

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

public class Test{
    public static void main(String[] args) throws IOException {
        FileOutputStream fos=new FileOutputStream("a.txt");
        byte[] arr={97,98,99,100};
        fos.write(arr,1,2);
        fos.close();
    }
}
/*a.txt的内容
bc
*/
换行和续写

换行:再次写出换行符即可。

不同系统下的换行符不同:window是\r\n,linux是\n,Mac是\r。

在Windows操作系统中,java对回车换行进行优化,虽然完整的是\r\n,但写其中一个\n或\r,java在底层便会自动补全,也可实现换行,不过还是建议不要省略,直接写全。

续写

若不想清空文件,想续写,打开续写开关即可,开关位置在创建对象的第二个参数,默认为false,手动传递true便会打开续写。

范例

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

public class Test{
    public static void main(String[] args) throws IOException {
        //换行
        FileOutputStream fos1=new FileOutputStream("a.txt");
        String string="ljsblog";
        byte[] bytes=string.getBytes();
        fos1.write(bytes);
        String strLine="\r\n";
        byte[] line=strLine.getBytes();
        fos1.write(line);
        fos1.write(bytes);
        fos1.close();

        //续写
        FileOutputStream fos2=new FileOutputStream("a.txt",true);
        String str="666";
        byte[] bytes1=str.getBytes();
        fos2.write(bytes1);
        fos2.close();
    }
}
/*a.txt内容
ljsblog
ljsblog666
*/

字节输入流InputStream

FileInputStream

操作本地文件的字节输入流,可把本地文件中的数据读取到程序中来。

步骤

  1. 创建对象
    1. 若文件不存在,则直接报错
  2. 读取数据
    1. 一次读一个字节,读出来的是数据在ASCII上对应的数字
    2. 若读到问价末尾,read方法返回-1
  3. 释放资源
    1. 每次使用完流必须要释放资源
/*a.txt的内容
ab
*/
import java.io.FileInputStream;
import java.io.IOException;

public class Test{
    public static void main(String[] args) throws IOException {
        FileInputStream fis=new FileInputStream("a.txt");
        int c1=fis.read();
        int c2=fis.read();
        int c3=fis.read();
        System.out.println(c1+" "+c2+" "+c3);
        System.out.println(""+(char)c1+(char)c2+(char)c3);
        fis.close();

    }
}
/*
97 98 -1
ab
*/
循环读取
/*a.txt内容
absafsaf
*/
import java.io.FileInputStream;
import java.io.IOException;

public class Test{
    public static void main(String[] args) throws IOException {
        FileInputStream fis=new FileInputStream("a.txt");
        int c;
        while((c=fis.read())!=-1){
            System.out.print((char)c);
        }
        fis.close();

    }
}
/*
absafsaf
*/
一次读多个字节

格式

public int read()

说明:一次读一个字节数据。

格式

public int read(byte[] buffer)

说明:一次读一个字节数组数据,每次读取的数量和字节数组长度有关,读取的内容会存入字节数组,返回值是本次读出数据的字节长度,字节数组长度尽量是1024的倍数。

范例

/*a.txt内容
abcde
*/
import java.io.FileInputStream;
import java.io.IOException;

public class Test{
    public static void main(String[] args) throws IOException {
        FileInputStream fis=new FileInputStream("a.txt");
        byte[] bytes=new byte[2];
        int len1=fis.read(bytes);
        System.out.println(len1);
        System.out.println(new String(bytes,0,len1));
        int len2=fis.read(bytes);
        System.out.println(len2);
        System.out.println(new String(bytes,0,len2));
        int len3=fis.read(bytes);
        System.out.println(len3);
        System.out.println(new String(bytes,0,len3));
        fis.close();
    }
}
/*
2
ab
2
cd
1
e
*/
拷贝文件
//将source.mp4拷贝,拷贝文件名copy.mp4
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;

public class Test{
    public static void main(String[] args) throws IOException {
        FileInputStream fis=new FileInputStream("source.mp4");
        FileOutputStream fos=new FileOutputStream("copy.mp4");
        byte[] bytes=new byte[1024*1024*10];//10M
        int len=0;
        while((len= fis.read(bytes))!=-1){
            fos.write(bytes,0,len);
        }
        fos.close();
        fis.close();
    }
}
异常处理
AutoCloseable

AutoCloseable是接口。

特点:特定情况下,可自动释放资源

基本做法
//手动释放资源
try{
	可能出现问题的代码
}catch(异常类名 变量名){
	异常的处理代码
}finally{
	执行所有资源释放操作
}
JDK7方案
//资源用完最终自动释放
try(创建流对象1;创建流对象2){//只有实现AutoCloseable接口的类才能在在此创建对象
	可能出现问题的代码
}catch(异常类名 变量名){
	异常的处理代码
}
JDK9方案
//资源用完最终自动释放
创建流对象1;
创建流对象2;
try(1;2){//只有实现AutoCloseable接口的类才能在在此创建对象
	可能出现问题的代码
}catch(异常类名 变量名){
	异常的处理代码
}

字符集

ASCII

一个英文占一个字符。

ASCII编码规则:前面补0,补全8位。

ASCII解码规则:直接转换成十进制。

GBK

简体中文版Windows,默认使用GBK字符集。

GBK字符集完全兼容ASCII字符集

一个英文占一个字节,二进制第一位是0。

一个中文占两个字节,二进制高位字节的第一位是1。

Unicode

Unicode万国码。

Unicode字符集的UTF-8编码格式:

  • 一个英文占一个字节,二进制第一位是0,转成十进制是正数
  • 一个中文占三个字节,二进制第一位是1,装成十进制数是负数

乱码

产生乱码的原因

  1. 读取数据时未读完整个汉字
  2. 编码和解码时的方式不统一

如何不产生乱码

  1. 不要用字节流读取文本文件
  2. 编码解码时,使用同一个码表,同一个编码方式

编码和解码方法

编码

格式1

public byte[] getBytes()

说明:使用默认方式编码,IDEA为UTF-8。

格式2

public byte[] getBytes(String charsetName)

说明:使用指定方式编码。

解码

格式1

String(byte[] bytes)

说明:使用默认方式解码。

格式2

String(byte[] bytes,String charsetName)

说明:使用指定方式解码。

范例
import java.io.UnsupportedEncodingException;
import java.util.Arrays;

public class Test{
    public static void main(String[] args) throws UnsupportedEncodingException {
        String str1="ni是憨批";
        //默认utf-8
        byte[] bytes1=str1.getBytes();
        System.out.println(Arrays.toString(bytes1));
        //指定GBK
        byte[] bytes2=str1.getBytes("GBK");
        System.out.println(Arrays.toString(bytes2));

        //bytes1编码方式为utf-8
        //以utf-8方式解码bytes1
        String strUtf1=new String(bytes1);
        System.out.println(strUtf1);
        //以gbk方式解码bytes1,结果为乱码
        String strGbk1=new String(bytes1,"GBK");
        System.out.println(strGbk1);
        //bytes2编码方式为GBK
        //以utf-8方式解码bytes2,结果为乱码
        String strUtf2=new String(bytes2);
        System.out.println(strUtf2);
        //以gbk方式解码bytes2
        String strGbk2=new String(bytes2,"GBK");
        System.out.println(strGbk2);
    }
}
/*
[110, 105, -26, -104, -81, -26, -122, -88, -26, -119, -71]
[110, 105, -54, -57, -70, -87, -59, -6]
ni是憨批
ni鏄啫鎵�
ni�Ǻ���
ni是憨批
*/

字符流

字符流底层就是字节流,适用于对纯文本文件进行操作。

字符流=字节流+字符集。

输入流:一次读一个字节,遇到中文,一次读取多个字节。

输出流:底层会将数据按照指定的编码方式进行编码,变成字节再写到文件中。

字符输入流Reader

FileReader

步骤:

  1. 创建字符输入流对象,若文件不存在会报错
    1. 构造方法:FileReader(File file)
      • 创建字符输入流并关联本地文件
    2. 构造方法:FileReader(String pathname)
      • 创建字符输入流关联本地文件
  2. 读取数据:按字节进行读取,遇到中文,一次读取多个字节,读取后编码,返回一个整数
    1. 方法:public int read()
      • 读取数据,读到末尾返回-1
      • 默认是一个字节一个字节读取,若遇到中文,会一次读取多个
      • 读取之后,方法的底层还会进行解码,并转换为十进制,并将该十进制作为返回值
    2. 方法:public int read(char[] buffer)
      • 读取多个数据,读到末尾返回-1
      • 返回数组长度
      • 空参的read+强制类型转换,无需再手动转换
  3. 释放资源
    • 成员方法:public int close()
    • 说明:释放资源/关闭

范例

//read()
import java.io.FileReader;
import java.io.IOException;

public class Test{
    public static void main(String[] args) throws IOException {
        FileReader fr=new FileReader("a.txt");
        int c;
        while((c=fr.read())!=-1){
            System.out.print((char)c);
        }
        fr.close();
    }
}
//read(char[] buffer)
import java.io.FileReader;
import java.io.IOException;

public class Test{
    public static void main(String[] args) throws IOException {
        FileReader fr=new FileReader("a.txt");
        char[] chars=new char[5];
        int len;
        while((len=fr.read(chars))!=-1){
            System.out.print(new String(chars,0,len));
        }
        fr.close();
    }
}
FileWriter

步骤

  1. 创建字符输出流对象
    1. 参数可以是字符串表示的路径或File对象
    2. 若文件不存在则会创建新文件,但要保证父级路径存在
    3. 若文件已经存在则会清空文件,不想清空可以打开续写开关,即第二个参数写true
    4. 构造方法
      • public FileWriter(File file) 创建字符输出流并关联本地文件
      • public FileWriter(String pathname) 创建字符输出流并关联本地文件
      • public FileWriter(File file,boolean append) 创建字符输出流并关联本地文件,续写
      • public FileWriter(String pathname,boolean append) 创建字符输出流并关联本地文件,续写
  2. 写数据:若write方法是整数,写到本地文件的是整数在字符集上对应的字符
    • 成员方法
      • void write(int c) 写出一个字符
      • void write(String str) 写出一个字符串
      • void write(String str,int off,int len) 写出一个字符串的一部分
      • void write(char[] cbuf) 写出一个字符数组
      • void write(char[] cbuf,int off,int len) 写出字符数组的一部分
  3. 释放资源:每次用完流后都要释放资源

范例

import java.io.FileWriter;
import java.io.IOException;

public class Test{
    public static void main(String[] args) throws IOException {
        FileWriter fw1=new FileWriter("a.txt");
        fw1.write(25105);
        FileWriter fw2=new FileWriter("a.txt",true);
        fw1.write("是博主");
        fw1.write("abcde",0,2);
        char[] chars={'你','是','观','众'};
        fw1.write(chars);
        fw1.write(chars,0,2);
        fw1.close();

    }
}
/*a.txt
我是博主ab你是观众你是
*/
字符输入流底层原理
  1. 创建字符输入流对象,底层关联文件,并创建缓冲区(长度为8192的字节数组)
  2. 读取数据,
    1. 判断缓冲区中是否有数据可以读取
    2. 缓冲区没有数据:从文件获取数据,装到缓冲区,每次尽可能填满缓冲区,若文件也没有数据,返回-1
    3. 缓冲区有数据:从缓冲区读取数据
      • 空参的read方法,一次读取一个字节,遇到中文一次读多个字节,把字节解码并转成十进制返回
      • 有参的read方法,把读取字节,解码,强转三步合并,强转之后的字符放到数组中。
字符输出流底层原理
  1. 创建字符输出流对象,底层关联文件,并创建缓冲区(长度为8192的字节数组)
  2. 当数据缓冲区,手动刷新(flush)或关流(close)时,会将缓冲区中的数据存储到文件中。
    • flush刷新:刷新之后,依旧可向文件写出数据
    • close关流:断开通道,不可忘文件写出数据。

范例

例1

//需求:拷贝一个文件夹,考虑子文件夹
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;

public class Test{
    public static void main(String[] args) throws IOException {
        File src=new File("d:\\javafiletest\\a");
        File det=new File("d:\\javafiletest\\copy");
        copyFile(src,det);
    }
    public static void copyFile(File src,File det) throws IOException {
        det.mkdirs();
        File[] files=src.listFiles();
        for(File file:files) {
            if (file.isFile()) {
                FileInputStream fis = new FileInputStream(file);
                FileOutputStream fos = new FileOutputStream(new File(det, file.getName()));
                byte[] bytes = new byte[1024 * 5];
                int len;
                while ((len = fis.read(bytes)) != -1) {
                    fos.write(bytes, 0, len);
                }
                fis.close();
                fos.close();
            } else {
                copyFile(file,new File(det,file.getName()) );
            }
        }
    }
}

例2

/*
为保证文件的安全性,需对原始文件进行加密存储,在使用时进行解密
加密原理:
	对原始文件的每个字节进行更改,将更改的数据存到新文件中
解密原理:
	读取加密后的文件,按加密规则反向操作,变成原始文件
*/
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;

public class Test{
    public static void main(String[] args) throws IOException {
        FileInputStream fis1=new FileInputStream("src.PNG");
        FileOutputStream fos1=new FileOutputStream("jiami.PNG");

        int c;
        while ((c=fis1.read())!=-1){
            fos1.write(c^100);
        }
        fos1.close();
        fis1.close();
        //一个数字异或另一个数字两次,得到的结果还是数字本身
        FileInputStream fis2=new FileInputStream("jiami.PNG");
        FileOutputStream fos2=new FileOutputStream("jiemi.PNG");
        while((c=fis2.read())!=-1){
            fos2.write(c^100);
        }
        fos2.close();
        fis2.close();
    }
}

例3

/*
文本文件中有以下的数据:
6-8-4-2-1
将文件中的数据进行排序,变成以下数据
1-2-4-6-8
*/
//方式1
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.Arrays;
import java.util.StringJoiner;

public class Test{
    public static void main(String[] args) throws IOException {
        FileInputStream fis=new FileInputStream("src.txt");
        StringBuilder sb=new StringBuilder();
        int c;
        while((c=fis.read())!=-1){
            sb.append((char)c);
        }
        String[] s=sb.toString().split("-");
        int[] arr=new int[s.length];
        for (int i=0;i<arr.length;i++){
            arr[i]=Integer.parseInt(s[i]);
        }
        Arrays.sort(arr);
        System.out.println(Arrays.toString(arr));
        StringJoiner sj=new StringJoiner("-");
        for(int a:arr){
            sj.add(""+a);
        }
        FileOutputStream fos=new FileOutputStream("src.txt");
        byte[] bytes=sj.toString().getBytes();
        fos.write(bytes);
        fos.close();
        fis.close();
    }
}

//方式2
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.util.Arrays;
public class Test{
    public static void main(String[] args) throws IOException {
        FileReader fis=new FileReader("src.txt");
        StringBuilder sb=new StringBuilder();
        int c;
        while((c=fis.read())!=-1){
            sb.append((char)c);
        }
        String[] sarr=sb.toString().split("-");
        Integer[] iarr=Arrays.stream(sarr).
                map(Integer::parseInt).
                sorted().
                toArray(Integer[]::new);
        System.out.println(Arrays.toString(iarr));
        String s=Arrays.toString(iarr).
                replace(", ","-");
        String str=s.substring(1,s.length()-1);
        FileWriter fos=new FileWriter("src.txt");
        fos.write(str);
        fos.close();
        fis.close();
    }
}

缓冲流

原理:底层自带长度为8192的缓冲区提高性能,字节缓冲流8192个byte,字符缓冲流8192个char。

  • 字节缓冲流,字节流本身并无缓冲区,但可加入缓冲流加快效率,即字节缓冲流
    • BufferedInputStream:字节缓冲输入流
    • BufferedOutputStream:字节缓冲输出流
  • 字符缓冲流,字符缓冲流对效率提升不大,但字符缓冲流中特有方法比较重要
    • BufferedReader:字符缓冲输入流
    • BufferedWriter:字符缓冲输出流

字节缓冲流

构造方法
BufferedInputStream

格式

public BufferedInputStream(InputStream is)

说明:把基本流包装成高级流,提高读取数据的性能

BufferedOutputSream

格式

public BufferedOutputStream(OutputStream os)

说明:把基本流包装成高级流,提高写出数据的性能

范例
/*
需求:
利用字节缓冲流拷贝文件
*/
import java.io.*;

public class Test{
    public static void main(String[] args) throws IOException {
        BufferedInputStream bis=new BufferedInputStream(new FileInputStream("src.txt"));
        BufferedOutputStream bos=new BufferedOutputStream(new FileOutputStream("copy.txt"));
        byte[] bytes=new byte[1024];
        int len;
        while((len=bis.read(bytes))!=-1){
            bos.write(bytes,0,len);
        }
        bos.close();
        bis.close();
    }
}

字符缓冲流

构造方法
BufferedReader

格式

public BufferedReader(Reader r)

说明:把基本流变成高级流

BufferedWriter

格式

public BufferedWriter(Writer r)

说明:把基本流变成高级流

特有方法
readLine

格式

public String readLine()

说明:读取一行数据,遇到回车换行结束,读取的数据不包括回车换行,若没有数据可读,返回null

newline

格式

public void newLine()

说明:跨平台的换行

范例
/*src.txt内容
君不见黄河之水天上来,奔流到海不复回。
君不见高堂明镜悲白发,朝如青丝暮成雪。
人生得意须尽欢,莫使金樽空对月。
天生我材必有用,千金散尽还复来。
烹羊宰牛且为乐,会须一饮三百杯。
岑夫子,丹丘生,将进酒,杯莫停。
与君歌一曲,请君为我倾耳听。
钟鼓馔玉不足贵,但愿长醉不复醒。
古来圣贤皆寂寞,惟有饮者留其名。
陈王昔时宴平乐,斗酒十千恣欢谑。
*/
import java.io.*;

public class Test{
    public static void main(String[] args) throws IOException {
        BufferedReader br=new BufferedReader(new FileReader("src.txt"));
        BufferedWriter bw=new BufferedWriter(new FileWriter("src.txt",true));
        String s;
        while((s=br.readLine())!=null){
            System.out.println(s);
        }
        bw.newLine();
        bw.write("主人何为言少钱,径须沽取对君酌。");
        bw.newLine();
        bw.write("五花马、千金裘,呼儿将出换美酒,与尔同销万古愁。");
        bw.close();
        br.close();
    }
}
/*打印
君不见黄河之水天上来,奔流到海不复回。
君不见高堂明镜悲白发,朝如青丝暮成雪。
人生得意须尽欢,莫使金樽空对月。
天生我材必有用,千金散尽还复来。
烹羊宰牛且为乐,会须一饮三百杯。
岑夫子,丹丘生,将进酒,杯莫停。
与君歌一曲,请君为我倾耳听。
钟鼓馔玉不足贵,但愿长醉不复醒。
古来圣贤皆寂寞,惟有饮者留其名。
陈王昔时宴平乐,斗酒十千恣欢谑。
*/
/*src.txt内容
君不见黄河之水天上来,奔流到海不复回。
君不见高堂明镜悲白发,朝如青丝暮成雪。
人生得意须尽欢,莫使金樽空对月。
天生我材必有用,千金散尽还复来。
烹羊宰牛且为乐,会须一饮三百杯。
岑夫子,丹丘生,将进酒,杯莫停。
与君歌一曲,请君为我倾耳听。
钟鼓馔玉不足贵,但愿长醉不复醒。
古来圣贤皆寂寞,惟有饮者留其名。
陈王昔时宴平乐,斗酒十千恣欢谑。
主人何为言少钱,径须沽取对君酌。
五花马、千金裘,呼儿将出换美酒,与尔同销万古愁。
*/

范例

例1
//四种方式拷贝文件,并统计各自用时
import java.io.*;
public class Test{
    public static void main(String[] args) throws IOException {
        method1();
        method2();
        method3();
        method4();
    }
    public static void method1() throws IOException {
        System.out.println("方式1:字节流的基本流:一次读写一个字节");
        FileInputStream fis1=new FileInputStream("a.mp4");
        FileOutputStream fos1=new FileOutputStream("copy1.mp4");
        int c;
        long start=System.currentTimeMillis();
        System.out.println("start:"+start);
        while((c=fis1.read())!=-1){
            fos1.write(c);
        }
        long end=System.currentTimeMillis();
        System.out.println("end:"+end);
        fos1.close();
        fis1.close();
        System.out.println((end-start)/1000.0+"秒");
    }
    public static void method2() throws IOException {
        System.out.println("方式2:字节流的基本流,一次读写一个字节数组");
        FileInputStream fis2=new FileInputStream("a.mp4");
        FileOutputStream fos2=new FileOutputStream("copy2.mp4");
        byte[] bytes2=new byte[1024*1024*10];
        long start=System.currentTimeMillis();
        System.out.println("start:"+start);
        int len;
        while((len=fis2.read(bytes2))!=-1){
            fos2.write(bytes2,0,len);
        }
        long end=System.currentTimeMillis();
        System.out.println("end:"+end);
        fos2.close();
        fis2.close();
        System.out.println((end-start)/1000.0+"秒");
    }
    public static void method3() throws IOException {
        System.out.println("方式3:字节缓冲流:一次读写一个字节");
        BufferedInputStream bis1=new BufferedInputStream(new FileInputStream("a.mp4"));
        BufferedOutputStream bos1=new BufferedOutputStream(new FileOutputStream("copy3.mp4"));
        int bc;
        long start=System.currentTimeMillis();
        System.out.println("start:"+start);
        while((bc=bis1.read())!=-1){
            bos1.write(bc);
        }
        long end=System.currentTimeMillis();
        System.out.println("end:"+end);
        bos1.close();
        bis1.close();
        System.out.println((end-start)/1000.0+"秒");
    }
    public static void method4() throws IOException {
        System.out.println("方式4:字节缓冲流,一次读写一个字节数组");
        BufferedInputStream bis2=new BufferedInputStream(new FileInputStream("a.mp4"));
        BufferedOutputStream bos2=new BufferedOutputStream(new FileOutputStream("copy4.mp4"));
        int blen;
        byte[] bbytes=new byte[1024*1024*10];
        long start=System.currentTimeMillis();
        System.out.println("start:"+start);
        while((blen=bis2.read(bbytes))!=-1){
            bos2.write(bbytes,0,blen);
        }
        long end=System.currentTimeMillis();
        System.out.println("end:"+end);
        bos2.close();
        bis2.close();
        System.out.println((end-start)/1000.0+"秒");
    }

}
/*
方式1:字节流的基本流:一次读写一个字节
start:1680080313285
end:1680081118655
805.37秒
方式2:字节流的基本流,一次读写一个字节数组
start:1680081118661
end:1680081118804
0.143秒
方式3:字节缓冲流:一次读写一个字节
start:1680081118806
end:1680081119696
0.89秒
方式4:字节缓冲流,一次读写一个字节数组
start:1680081119701
end:1680081119846
0.145秒

*/
例2
//给《将进酒》的文章顺序进行恢复到一个新文件夹中
/*src.txt
2.君不见高堂明镜悲白发,朝如青丝暮成雪。
3.人生得意须尽欢,莫使金樽空对月。
1.君不见黄河之水天上来,奔流到海不复回。
12.五花马、千金裘,呼儿将出换美酒,与尔同销万古愁。
4.天生我材必有用,千金散尽还复来。
5.烹羊宰牛且为乐,会须一饮三百杯。
7.与君歌一曲,请君为我倾耳听。
10.陈王昔时宴平乐,斗酒十千恣欢谑。
11.主人何为言少钱,径须沽取对君酌。
9.古来圣贤皆寂寞,惟有饮者留其名。
6.岑夫子,丹丘生,将进酒,杯莫停。
8.钟鼓馔玉不足贵,但愿长醉不复醒。
*/
import java.io.*;
import java.util.TreeSet;

public class Test{
    public static void main(String[] args) throws IOException {
        BufferedReader bis=new BufferedReader(new FileReader("src.txt"));
        BufferedWriter bos=new BufferedWriter(new FileWriter("det.txt"));
        TreeSet<String> ts=new TreeSet<>((o1,o2) ->Integer.parseInt(o1.split("\\.")[0])-Integer.parseInt(o2.split("\\.")[0]));
        String s;
        while((s=bis.readLine())!=null){
            ts.add(s);
        }
        for(String str:ts){
            bos.write(str);
            bos.newLine();
        }
        bos.close();
        bis.close();
    }
}
/*det文件
1.君不见黄河之水天上来,奔流到海不复回。
2.君不见高堂明镜悲白发,朝如青丝暮成雪。
3.人生得意须尽欢,莫使金樽空对月。
4.天生我材必有用,千金散尽还复来。
5.烹羊宰牛且为乐,会须一饮三百杯。
6.岑夫子,丹丘生,将进酒,杯莫停。
7.与君歌一曲,请君为我倾耳听。
8.钟鼓馔玉不足贵,但愿长醉不复醒。
9.古来圣贤皆寂寞,惟有饮者留其名。
10.陈王昔时宴平乐,斗酒十千恣欢谑。
11.主人何为言少钱,径须沽取对君酌。
12.五花马、千金裘,呼儿将出换美酒,与尔同销万古愁。

*/
例3
/*实现一个验证程序运行次数的小程序,要求如下:
1.当程序运行次数超过3次,给出提示:运行次数超过3次,请充值会员后使用
2.程序运行演示如下:
第一次:第1次运行此文件
第二次:第2次运行此文件
第三次:第3次运行此文件
第四次及以后:运行次数超过3次,请充值会员后使用
*/
import java.io.*;

public class Test{
    public static void main(String[] args) throws IOException {
        BufferedReader br=new BufferedReader(new FileReader("src.txt"));
        int c=Integer.parseInt(br.readLine());
        c++;
        if(c<=3){
            System.out.println("第"+c+"次运行此文件");
        }else{
            System.out.println("运行次数超过3次,请充值会员后使用");
        }
        BufferedWriter bw=new BufferedWriter(new FileWriter("src.txt"));
        bw.write(c+"");
        bw.close();
        br.close();
    }
}

转换流

转换流是字符流和字节流之间的桥梁。

作用:

  1. 指定字符集读写(在jdk11被淘汰)
  2. 字节流想要使用字符流中的方法

InputStreamReader

字节流转换成字符流,可以根据字符集一次读取多个字节,读取数据不会乱码

OutputStreamWriter

字符流转换成字节流

范例

例1
//利用转换流按照指定字符编码读取
//jdk8
import java.io.*;
public class Test{
    public static void main(String[] args) throws IOException {
        InputStreamReader isr=new InputStreamReader(new FileInputStream("src.txt"),"GBK");
        int ch;
        while((ch=isr.read())!=-1){
            System.out.print((char)ch);
        }
        isr.close();
    }
}
//jdk11
import java.io.*;
import java.nio.charset.Charset;

public class Test{
    public static void main(String[] args) throws IOException {
        FileReader fr=new FileReader("src.txt",Charset.forName("GBK"));
        int ch;
        while((ch=fr.read())!=-1){
            System.out.print((char)ch);
        }
        fr.close();

    }
}
例2
//利用转换流按照指定字符编码写出
//jdk8
import java.io.*;
public class Test{
    public static void main(String[] args) throws IOException {
        OutputStreamWriter osr=new OutputStreamWriter(new FileOutputStream("src.txt"),"GBK");
        osr.write("你好!");
        osr.close();
    }
}
//jdk11
import java.io.FileWriter;
import java.io.IOException;
import java.nio.charset.Charset;

public class Test{
    public static void main(String[] args) throws IOException {
        FileWriter fw=new FileWriter("src.txt", Charset.forName("GBK"));
        fw.write("你好啊");
        fw.close();

    }
}
例3
//将本地文件的GBK文件,转成UTF-8文件
//jdk8
import java.io.*;

public class Test{
    public static void main(String[] args) throws IOException {
        InputStreamReader isr=new InputStreamReader(new FileInputStream("src.txt"),"GBK");
        OutputStreamWriter osw=new OutputStreamWriter(new FileOutputStream("det.txt"),"UTF-8");
        int ch;
        while((ch=isr.read())!=-1){
            osw.write(ch);
        }
        osw.close();
        isr.close();
    }
}
//jdk11
import java.io.*;
import java.nio.charset.Charset;

public class Test{
    public static void main(String[] args) throws IOException {
        FileReader fr=new FileReader("src.txt", Charset.forName("GBK"));
        FileWriter fw=new FileWriter("det.txt",Charset.forName("UTF-8"));
        int ch;
        while((ch=fr.read())!=-1){
            fw.write(ch);
        }
        fw.close();
        fr.close();
    }
}
例4
//利用字节流读取文件中的数据,每次读一整行,而且不能出现乱码
import java.io.*;
public class Test{
    public static void main(String[] args) throws IOException {
        InputStreamReader isr=new InputStreamReader(new FileInputStream("src.txt"),"GBK");
        BufferedReader br=new BufferedReader(isr);
        String s;
        while((s=br.readLine())!=null){
            System.out.println(s);
        }
        isr.close();
    }
}

序列化流和反序列化流

细节

  1. 使用序列化流将对象写到文件时,需使javabean类实现Serializable接口,否则就会出现NotSerializableException异常
  2. 序列化流写到文件中的数据不可修改,一旦修改就无法再读回来
  3. 序列化对象后,修改Javabean类,再次序列化,便会抛出InvalidClassException异常
    • 解决方案:可以通过给javabean类添加serialVersionUID(版本号,序列号)
  4. 若对象中的某成员变量不想被序列化,则给该变量添加transient关键字修饰,该关键字表示的成员变量不参与序列化过程。

序列化流

又称对象操作输出流,可以把java中的对象写到本地文件中。

对象需要实现Serializable接口,写到文件中的内容是看不懂的。

Serializable接口里面没有抽象方法,标记性接口。

实现了该接口,就表示当前的类可以被序列化。

构造方法

public ObjectOutputStream(OutputStream out)

说明:把基本流包装成高级流

成员方法

public final void writeObject(Object obj)

说明:把对象序列化(写出)到文件夹中去

范例
//Student.java
import java.io.Serializable;

public class Student implements Serializable {
    private int age;
    private String name;

    public Student() {
    }

    public Student(int age, String name) {
        this.age = age;
        this.name = name;
    }

    /**
     * 获取
     * @return age
     */
    public int getAge() {
        return age;
    }

    /**
     * 设置
     * @param age
     */
    public void setAge(int age) {
        this.age = age;
    }

    /**
     * 获取
     * @return name
     */
    public String getName() {
        return name;
    }

    /**
     * 设置
     * @param name
     */
    public void setName(String name) {
        this.name = name;
    }

    public String toString() {
        return "Student{age = " + age + ", name = " + name + "}";
    }
}

//Test.java
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;

public class Test{
    public static void main(String[] args) throws IOException {
        Student student=new Student(19,"张三");
        ObjectOutputStream oos=new ObjectOutputStream(new FileOutputStream("src.txt"));
        oos.writeObject(student);
        oos.close();
    }
}

反序列流

又称对象操作输入流,可以把序列化到本地文件中的对象,读取到程序中来。

构造方法

public ObjectInputStream(OutputStream out)

说明:把基本流包装成高级流

成员方法

public final Object readObject()

说明:把序列化到本地文件中的对象,读取到程序中来。

范例
//Student.java
import java.io.Serializable;

public class Student implements Serializable {
    private int age;
    private String name;

    public Student() {
    }

    public Student(int age, String name) {
        this.age = age;
        this.name = name;
    }

    /**
     * 获取
     * @return age
     */
    public int getAge() {
        return age;
    }

    /**
     * 设置
     * @param age
     */
    public void setAge(int age) {
        this.age = age;
    }

    /**
     * 获取
     * @return name
     */
    public String getName() {
        return name;
    }

    /**
     * 设置
     * @param name
     */
    public void setName(String name) {
        this.name = name;
    }

    public String toString() {
        return "Student{age = " + age + ", name = " + name + "}";
    }
}

//Test.java
import java.io.FileInputStream;
import java.io.IOException;
import java.io.ObjectInputStream;

public class Test{
    public static void main(String[] args) throws IOException, ClassNotFoundException {
        ObjectInputStream oim=new ObjectInputStream(new FileInputStream("src.txt"));
        Student student=(Student)oim.readObject();
        System.out.println(student);
    }
}

范例

//将多个自定义对象序列化到文件中,由于对象个数不确定,反序列化该如何读取?
//Student.java
import java.io.Serializable;

public class Student implements Serializable{
    private static final long serialVersionUID = -4683704568207367494L;
    private int age;
    private String name;

    public Student() {
    }

    public Student(int age, String name) {
        this.age = age;
        this.name = name;
    }

    /**
     * 获取
     * @return age
     */
    public int getAge() {
        return age;
    }

    /**
     * 设置
     * @param age
     */
    public void setAge(int age) {
        this.age = age;
    }

    /**
     * 获取
     * @return name
     */
    public String getName() {
        return name;
    }

    /**
     * 设置
     * @param name
     */
    public void setName(String name) {
        this.name = name;
    }

    public String toString() {
        return "Student{age = " + age + ", name = " + name + "}";
    }
}

//Test.java
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;
import java.util.ArrayList;

public class Test{
    public static void main(String[] args) throws IOException, ClassNotFoundException {
        ArrayList<Student> list=new ArrayList<>();
        Student s1=new Student(19,"张三");
        Student s2=new Student(18,"李四");
        Student s3=new Student(20,"王五");
        list.add(s1);
        list.add(s2);
        list.add(s3);
        ObjectOutputStream oos=new ObjectOutputStream(new FileOutputStream("src.txt"));
        oos.writeObject(list);
        oos.close();
    }
}

//Test1.java
import java.io.FileInputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.util.ArrayList;

public class Test1 {
    public static void main(String[] args) throws IOException, ClassNotFoundException {
        ObjectInputStream ois=new ObjectInputStream(new FileInputStream("src.txt"));
        ArrayList<Student> list=(ArrayList<Student>) ois.readObject();
        for (Student student:list){
            System.out.println(student);
        }
        ois.close();
    }
}
/*
Student{age = 19, name = 张三}
Student{age = 18, name = 李四}
Student{age = 20, name = 王五}
*/

打印流

打印流一般是指:PrintStream,PrintWriter两个类。

特点

  1. 打印流只操作文件目的地,不操作数据源
  2. 特有的写出方法可以实现,数据原样打出
  3. 特有的写出方法。可以实现自动刷新。自动换行,打印一次数据=写出+换行+刷新

:字节流底层没有缓冲区,开不开自动刷新都一样

字节打印流

构造方法

格式1

public PrintStream(OutputStream/File/String)

说明:关联字节输出流/文件/文件路径

格式2

public PrintStream(String fileName,Charset charset)

说明:指定字节编码

格式3

public PrintStream(OutputStream out,boolean autoFlush)

说明:自动刷新

格式4

public PrintStream(OutputStream out,boolean autoFlush,String encoding)

说明:指定字符编码且自动刷新

成员方法
write

格式

public void write(int b)

说明:常规方法,规则跟之前一样,将指定的字节写出。

println

格式

public void println(Xxx xx)

说明:特有方法,打印任意数据,自动刷新,自动换行,数据原样写出

print

格式

public void print(Xxx xx)

说明:特有方法,打印任意数据,不换行,数据原样写出

printf

格式

public void printf(String format,Object…args)

说明:特有方法,带有占位符的打印语句,不换行,数据原样写出

范例
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.PrintStream;
import java.io.UnsupportedEncodingException;

public class Test{
    public static void main(String[] args) throws FileNotFoundException, UnsupportedEncodingException {
        PrintStream ps=new PrintStream(new FileOutputStream("src.txt"),true,"GBK");
        ps.println(97);
        ps.print(true);
        ps.println();
        ps.printf("学生姓名:%s,年龄%d","张三",18);
        ps.close();
    }
}
/*src.txt内容
97
true
学生姓名:张三,年龄18
*/

字符打印流

字符流底层有缓冲区,要自动刷新需要开启。

构造方法

格式1

public PrintWriter(Write/File/String)

说明:关联字符输出流/文件/文件路径

格式2

public PrintWriter(String fileName,Charset charset)

说明:指定字符编码

格式3

public PrintWriter(Write out,boolean autoFlush)

说明:自动刷新

格式4

public PrintWriter(OutputStream,boolean autoFlush,String encoding)

说明:指定字符编码且自动刷新

成员方法
write

格式

public void write(int b)

说明:常规方法,规则跟之前一样,写出字节或字符串。

println

格式

public void println(Xxx xx)

说明:特有方法,打印任意数据且换行,数据原样写出

print

格式

public void print(Xxx xx)

说明:特有方法,打印任意数据,不换行,数据原样写出

printf

格式

public void printf(String format,Object…args)

说明:特有方法,带有占位符的打印语句,不换行,数据原样写出

范例
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;

public class Test{
    public static void main(String[] args) throws IOException {
        PrintWriter pw=new PrintWriter(new FileWriter("src.txt"),true);
        pw.println(97);
        pw.print(true);
        pw.println();
        pw.printf("学生姓名:%s,年龄%d","张三",18);
        pw.close();
    }
}

压缩和解压缩

压缩包里的每一个文件。在Java中都是一个ZipEntry对象。

在java中只能识别zip格式的压缩包

解压缩

解压本质:把每一个ZipEntry按照层级拷贝到本地另一个文件夹中。

import java.io.*;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;

public class Test{
    public static void main(String[] args) throws IOException {
        //创建一个File表示要解压的文件
        File src=new File("d:\\javafiletest.zip");
        //创建一个File表示解压的目的地
        File det=new File("d:\\");
        unzip(src,det);
    }
    public static void unzip(File src,File det) throws IOException {
        //创建一个解压缩流用来读取压缩包中的数据
        ZipInputStream zip=new ZipInputStream(new FileInputStream(src));
        //要先获取到压缩包里的每个zipEntry对象
        //表示当前在压缩包中获取到的文件或文件夹
        ZipEntry entry;
        while((entry=zip.getNextEntry())!=null){
            System.out.println(entry);
            if(entry.isDirectory()){
                //若entry是文件夹,则创建对应的文件夹
                File file=new File(det,entry.toString());
                file.mkdirs();
            }else {
                //entry是文件,读取该文件,并将其放到指定位置
                FileOutputStream fos=new FileOutputStream(new File(det,entry.toString()));
                int ch;
                while ((ch=zip.read())!=-1){
                    //写到目的地
                    fos.write((char)ch);
                }
                fos.close();
                //表示在压缩包中一个文件处理完毕
                zip.closeEntry();

            }
        }
        zip.close();

    }
}

压缩

压缩本质:把每一个(文件/文件夹)看成ZipEntry对象放到压缩包中。

//压缩单个文件
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;

public class Test{
    public static void main(String[] args) throws IOException {
        //创建一个File表示要压缩的文件
        File src=new File("d:\\javafiletest\\b.txt");
        //创建一个File表示压缩包的位置
        File det=new File("d:\\javafiletest\\b.zip");
        zip(src,det);
    }
    public static void zip(File src,File det) throws IOException {
        //创建关联压缩包
        ZipOutputStream zos=new ZipOutputStream(new FileOutputStream(det));
        //创建ZipEntry对象,表示压缩包里面的每个文件和文件夹
        ZipEntry zipEntry=new ZipEntry("b.txt");
        //把ZipEntry对象放到压缩包中
        zos.putNextEntry(zipEntry);
        //把src文件的数据写到压缩包中
        FileInputStream fis=new FileInputStream(src);
        int c;
        while((c=fis.read())!=-1){
            zos.write(c);
        }
        fis.close();
        zos.closeEntry();
        zos.close();

    }
}
//压缩文件夹
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;

public class Test{
    public static void main(String[] args) throws IOException {
        File src=new File("d:\\javafiletest");
        File destParent=src.getParentFile();
        File det=new File(destParent,src.getName()+".zip");
        ZipOutputStream zos=new ZipOutputStream(new FileOutputStream(det));
        directoryZip(src,zos,src.getName());
        zos.close();

    }
    public static void directoryZip(File src,ZipOutputStream zos,String name) throws IOException {
        File[] files=src.listFiles();
        for(File file:files){
            if(file.isFile()){
                ZipEntry zipEntry=new ZipEntry(name+"\\"+file.getName());
                zos.putNextEntry(zipEntry);
                FileInputStream fis=new FileInputStream(file);
                int c;
                while ((c=fis.read())!=-1){
                    zos.write(c);
                }
                fis.close();
                zos.closeEntry();
            }else {
                directoryZip(file,zos,name+"\\"+file.getName());
            }
        }
    }
}

Common-io

Common-io是apache开源基金组织提供的一组有关IO操作的开源工具包。

作用:提高IO流的开发效率

使用步骤

IDEA下的使用步骤:

  1. 在项目中创建文件lib
  2. 将jar包复制粘贴到lib文件夹
  3. 右键点击jar包,选择Add as Library->点击OK
  4. 在类中导包使用

常见方法

FileUtils类

FileUtils类,文件/文件夹相关。

copyFile

static void copyFile(File srcFile,File destFile)

说明:复制文件

copyDirectory

static void copyDirectory(File srcDir,File destDir)

说明:复制文件夹

copDirectoryToDirectory

static void copDirectoryToDirectory(File srcDir,File destDir)

说明:复制文件夹,包括最顶层的文件夹

deleteDirectory

static void deleteDirectory(File Directory)

说明:删除文件夹

clearDirectory

static void clearDirectory()

说明:清空文件夹

IOUtils类

IOUtils类(流相关相关)

copy

public static int copy(InputStream input,OutStream output)

说明:复制文件

copyLarge

public static int copyLarge(Reader input,Writer output)

说明:复制大文件

readlines

public static String readlines(Reader input)

说明:读取数据

write

public static void write(String data,OutputStream output)

说明:写出数据

hutool

常见方法

FileUtil类
file

说明:根据参数创建一个File对象,参数可以是多个字符串

touch

说明:根据参数创建对象,若父级路径不存在,则会自动创建父级路径

writeLines

说明:把集合中的数据写出到文件中,覆盖模式

appendLines

说明:把集合中的数据写出到文件中,续写模式

readLines

说明:指定字符编码,把文件中的数据,读到集合中

readUtf8Lines

说明:按照Utf-8的形式,把文件中的数据,读到集合中

copy

说明:拷贝文件或文件夹

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值