一万五千字究极详解Java开发File、IO流、IO框架全流程

0-引言

首先我们要思考为什么要学File和IO流。

  • 那么我们看一看以前存储数据的方案。
  • 变量:double money=99.5;
  • 数组:int[] age=new int[100];
  • 对象:Student s=new Student();
  • 集合:List<Student> students=new ArrayList<>();

可以看到,我们以前这些数据都是放在内存中,这些数据容器都在内存中,一旦程序结束、断电,数据就没有。

可我们有很多数据需要持久化的保存,于是我们引进了File和IO流的概念。

File(文件)

  • 文件可以长久保存数据
  • 文件在电脑磁盘中保存,即便断电,或者程序终止,文件中的数据也不会丢失。
  • File是java.io包下的类,File类的对象,用于代表当前操作系统的文件(可以是文件、或文件夹)。
  • 那既然是类的对象,就一定会提供我们很多方法,比如获取文件信息(大小、文件名、修改时间)、判断文件的类型、创建文件/文件夹、删除文件/文件夹...
  • 注意:File只能对文件本身进行操作,不能读写文件里面存储的数据。

IO流

  • 用于读写数据的(可以读写文件,或网络中的数据)

1-File

File类的对象可以代表文件/文件夹,并可以调用其提供的方法对象文件进行操作。

1.1-File提供的常用方法

我就不把方法列表格展示了,咱们各种语法格式直接代码里见。

public static void main(String[] args) throws Exception {
        //目标:创建File创建对象代表文件(文件/目录),搞清楚其提供的对文件进行操作的方法。
        //1.创建File对象,去获取某个文件的信息
        File f1=new File("F:/img/12.jpg");
        System.out.println(f1.length());//获取文件大小
        System.out.println(f1.getName());//获取文件名
        System.out.println(f1.isFile());//判断是否是文件
        System.out.println(f1.isDirectory());//判断是否是目录 false


        //2.使用相对路径定位文件,相对路径是相对当前java工程
        //只要带盘符的路径,都是绝对路径F:/img/12.jpg
        //相对路径:不带盘符,默认是到你的idea工程下直接寻找文件的,一般用来找到资源文件。
        File f2=new File("day03-file-io/src/xunshan01.txt");
        System.out.println(f2.length());


        //3.创建对象代表不存在的文件路径。
        File f3=new File("F:/img/createdemo.txt");
        System.out.println(f3.exists());//判断文件是否存在
        System.out.println(f3.createNewFile());//创建出这个文件


        //4.创建对象代表不存在的文件夹路径
        File f4=new File("F:/img/createdir");
        System.out.println(f4.exists());//判断文件夹是否存在
        System.out.println(f4.mkdir());//创建文件夹 mkdir只能创建一级文件夹


        File f5 = new File("F:/img/createdir/createdir1/createdir2");
        System.out.println(f5.mkdirs());//创建多级文件夹 mkdirs可以创建多级文件夹


        //5.创建File对象代表存在的文件,然后删除它
        File f6=new File("F:/img/createdemo.txt");
        System.out.println(f6.delete());//删除文件,返回是否删除成功
        
        //6.创建File对象代表存在的文件夹,然后删除它
        File f7=new File("F:/img/createdir");
        System.out.println(f7.delete());//删除文件夹,返回是否删除成功 默认只能删除文件或空文件夹,不能删除有内容的文件夹
        
        //7.获取某个目录下的全部一级文件名
        File f8=new File("F:/img");
        String[] list = f8.list();
        for (String s : list) {
            System.out.println(s);
        }
        
        //8.获取某个目录下的全部一级文件对象
        File[] files = f8.listFiles();
        for (File file : files) {
            System.out.println(file.getAbsolutePath());//获取绝对路径
        }   
    }

1.2-File提供的遍历文件夹的方法

使用listFiles方法时的注意事项:

  • 当主调是文件,或者路径不存在时,返回null
  • 当主调是空文件夹时,返回一个长度为0的数组
  • 当主调是一个有内容的文件夹时,将里面所有一级文件和文件夹的路径放在File数组中返回
  • 当主调是一个文件夹,里面有隐藏文件时,将里面所有文件和文件夹的路径放在File数组中返回,包含隐藏文件
  • 当主调是一个文件夹,但是没有权限访问该文件夹时,返回null
public static void main(String[] args) {
        //目标:掌握File遍历一级文件对象的操作
        //当主调是文件,或者路径不存在时,返回null
        File dir = new File("E:/img/abc.txt");
        File[] files = dir.listFiles();
        System.out.println(files);
        //当主调是空文件夹时,返回一个长度为0的数组
        File dir2= new File("F:/img/createdir");
        File[] files1 = dir2.listFiles();
        System.out.println(Arrays.toString(files1));
        //当主调是一个有内容的文件夹时,将里面所有一级文件和文件夹的路径放在File数组中返回
        File dir3= new File("F:/img");
        File[] files2 = dir3.listFiles();
        System.out.println(Arrays.toString(files2));
    }


2-方法递归

2.1-认识递归

什么是递归?

  • 递归是一种算法,在程序设计语言中广泛应用。
  • 从形式上来说:方法调用自身的形式称为方法递归(recursion)。

递归的形式

  • 直接递归:方法自己调用自己
  • 间接递归:方法调用其他方法,其他方法又回调方法自己。

使用递归需要注意的问题

  • 递归如果没有控制好终止,会出现死循环,导致内存栈溢出错误。

2.2-递归算法和其执行流程

因为递归的思想非常抽象,我们从数学上着手讲递归的算法,做一个小案例。

2.2.1-案例·计算n的阶乘

需求:

计算n的阶乘,5的阶乘=1*2*3*4*5,6的阶乘=1*2*3*4*5*6

分析:

  • 假如我们认为存在一个公式f(n)=1*2*3*4*5*...*(n-1)*n;
  • 那么公式等价形式就是:f(n)=f(n-1)*n
  • 如果求1~5的阶乘的结果,我们手工应该如何应用上述公式计算?

→f(5)=f(4)*5

→f(3)=f(2)*3

→f(4)=f(3)*4

→f(2)=f(1)*2

→f(1)=1

public static void main(String[] args) {
    //目标:计算n的阶乘
    System.out.println(f(5));//5的阶乘:120
}
public static int f(int n){
    if(n==1){
        return 1;
    }else{
        return n*f(n-1);
    }
}

递归算法的三要素

  • 递归的公式
  • 递归的终结点
  • 递归的方向必须走向终结点

2.2.2-案例·递归求1~n的和

需求

  • 计算1~n的和的结果,使用递归思想解决。

分析

  • 假如我们认为存在一个公式f(n)=1+2+3+4+5+...+(n-1)+n
  • 那么等价形式公式就是:f(n)=f(n-1)+n
  • 递归的终结点:f(1)=1
 public static int f2(int n2){
        if(n2==1){
            return 1;
        }else {
            return n2+f2(n2-1);
        }
    }

2.2.3-经典面试题:猴子吃桃问题

需求

  • 猴子第一天摘下若干桃子,当即吃了一半,觉得不过瘾,于是又多吃了一个。第二天又吃了前一天剩余桃子的一半,觉得不过瘾,于是多吃了一个,以后每天都吃前一天剩余桃子数量的一半,觉得不过瘾,多吃了一个,等到第十天发现桃子只剩一个了。
  • 现在问:猴子第一天摘了多少个桃子?

分析

  • 公式:f(n+1)=f(n)-f(n)/2-1

变形

→2f(n+1)=f(n)-2

→f(n)=2f(n+1)+2

终结点

  • f(10)=1
    public static void main(String[] args) {
        System.out.println(f(1));
        System.out.println(f(2));
        System.out.println(f(3));
    }
    public static int f(int day){
        if(day==10){
            return 1;
        }else {
            return 2*f(day+1)+2;
        }
    }

2.3-小案例:文件搜索

需求

从D:盘中找到“QQ.exe”这个文件,找到后直接输出其位置

分析

先找出D盘下的所有一级文件对象

判断全部一级文件对象,判断是否是文件

如果是文件,判断是否是自己想要的文件

如果是文件夹,需要继续进入到该文件夹,重复上述步骤。

代码

package com.xunshan.demo2recursion;


import java.io.File;


public class FileSearchDemo4 {
    public static void main(String[] args) {
        //目标:完成文件搜索,找出D盘下的QQ.exe位置
        File dir=new File("D:/");
        searchFile(dir,"QQ.exe");
    }


    public static void searchFile(File dir,String fileName) {
        //1.判断极端情况
        if (dir==null||!dir.exists()||dir.isFile()) {
            return;//不搜索
        }


        //2.获取当前目录下的所有一级文件或文件夹对象。
        File[] files = dir.listFiles();


        //3.判断当前目录下是否存在一级文件对象,存在才可以遍历
        if (files!=null&&files.length>0) {
            //4.遍历一级文件对象
            for (File file : files){
                //5.判断当前文件对象是否是目标文件
                if (file.isFile()) {
                    //6.是文件,判断文件名是否是目标文件名
                    if (file.getName().equals(fileName)) {
                        System.out.println(file.getAbsolutePath());
                        return;//找到目标文件,直接返回
                    }
                }else {
                    //7.当前文件对象是文件夹,递归调用
                    searchFile(file,fileName);
                }
            }
        }


    }
}

3-字符集

3.1-常见字符集介绍

标准ASCII编码→码点→二进制(使用一个字节存储)

标准ADCII字符集

  • ASCII:美国信息交换标准代码,包含了英文、符号等
  • 标准ASCII使用1个字节存储一个字符,首位是0,因此,总共可以表示128个字符,对美国佬来说完全够用。

GBK(汉字内码扩展规范,国标)

  • 汉字编码字符集,包含了两万多个汉字等字符,GBK中一个中文字符编码成两个字节的形式存储
  • GBK兼容了ASCII字符集

那这个时候就出现了一个问题,如果有人问“我a你”,这个怎么存储呢?

因为汉字是两个字节,a是一个字节,所以GBK字符集做出了规定,汉字的第一个字节的第一位必须是1。

Unicode字符集(统一码,也叫万国码)

  • Unicode是国际组织制定的,可以容纳世界上所有文字,符号的字符集。
  • UTF-32 每四个字节表示一个字符 42个亿个字符 有容乃大 但是占存储空间大,通信效率变低。

UTF-8这个时候就站出来了,我们所使用的就是UTF-8

UTF-8字符集

  • 是Unicode字符集的一种编码方案,采取可变长编码方案,共分四个长度区:1个字节,2个字节,3个字节,4个字节。
  • 英文字符、数字等只占1个字节(兼容标准ASCII编码),汉字字符占3个字节。

那么编码方案是这样的

一字节区:0开头 二字节区:110、10开头 三字节区:1110、10、10 四字节区:11110、10、10、10

这个就叫前缀码

举个例子:a我m

a

97

01100001

25105

110 001000 010001

m

109

01101101

注意

  • 字符编码时使用的字符集和解码时使用的字符集必须一致,否则会出现乱码。
  • 英文、数字一般不会乱码,因为很多字符集都兼容了ASCII编码。

3.2-字符集的编码、解码操作

对字符的编码(平台默认UTF-8编码)

对字符的解码(指定GBK编码,GBK解码)


4-IO流

4.1-认识IO流

  • I指的是Input,称为输入流:负责把数据读到内存中去
  • O指的是Output,称为输出流:负责写数据出去

4.1.1-IO流的常见应用场景

  • 比如在记事本记录文件
  • 玩游戏(游戏的最高分)
  • 文件的复制拷贝
  • 网络上聊天信息的发送接收都是IO流的读写...

4.1.2-IO流的分类

按照流方向:IO流分为

  • 输入流
  • 输出流

按照流的内容:IO流分为

  • 字节流:适合操作所有类型的文件

比如音频、视频、图片、文本文件的复制,转移等

  • 字符流:只适合操作纯文本文件

比如读写txt、java文件等

IO流的体系

抽象类→实现类→对象

  • 字节输入流InputStream→FileInputStream
  • 字节输出流OutputStream→FileOutputStream
  • 字符输入流Reader→FileReader
  • 字符输出流Writer→FileWrite

4.2-字节流

4.2.1-FileInputStream(文件字节输入流)

  • 作用:以内存为基准,可以把磁盘文件中的数据以字节的形式读入到内存中去。
    public static void main(String[] args) throws Exception {
        //目标:掌握文件字节输入流读取文件中的字节数组到内存中来。
        //1.创建文件字节输入流管道与源文件接通。
//        InputStream is=new FileInputStream(new File("day03-file-io\\src\\xunshan02.txt"));
        InputStream is=new FileInputStream("day03-file-io\\src\\xunshan02.txt");//简化写法
    }

我们接下来看一下怎么读,我们先写了一个愚蠢的代码作为引子。

然后我们看一下正常的我们通过字节数组读是怎么写的。

    public static void main(String[] args) throws Exception {
        //目标:掌握文件字节输入流读取文件中的字节数组到内存中来。
        //1.创建文件字节输入流管道与源文件接通。
//        InputStream is=new FileInputStream(new File("day03-file-io\\src\\xunshan02.txt"));
        InputStream is=new FileInputStream("day03-file-io\\src\\xunshan03.txt");//简化写法


        //2.开始读取文件中的字节并输出:每次读多个字节
        //定义一个字节数组用于每次读取字节数组
        byte[] buffer=new byte[3];
        //定义一个变量,统计每次读取了多少个字节
        int count;
        while((count=is.read(buffer))!=-1){
            //3.把读取的字节数组转换成字符串进行输出。
            String str=new String(buffer,0,count);
            System.out.println(str);
        }
    }

拓展

  • 每次读取多个字节,性能得到提升,因为每次读取多个字节,可以减少硬盘和内存的交互次数,从而提升效率,但是依然无法避免读取汉字出现输出乱码的问题,存在截断汉字字节的可能性。

注意事项

  • 使用FileInputStream每次读取一个字节,读取性能较差,并且读取汉字输出会乱码。
  • 使用FileInputStream每次读取多个字节,读取性能得到了提升,但读取汉字输出还是会乱码。

问题:使用字节流读取中文,如何保证输出不乱码?如何解决?

  • 定义一个与文件一样大的字节数组,一次性读取完文件的全部字节,java也提供了readAll解决方案。(如果文件过大,创建的字节数组也会过大,可能引起内存溢出)
  • 读取文本适合用字符流;字节流适合做数据的转移,比如文件复制。

4.2.2-FileOutputStream(文件字节输出流)

public static void main(String[] args) throws Exception {
    //目标:学会使用文件字节输出流。
    //1.创建文件字节输出流管道与目标文件接通
    OutputStream outputStream = new FileOutputStream("day03-file-io/src/xunshan05.txt", true);//这个true是追加数据,而不是完全覆盖重写。


    //2.写入数据到文件中
    outputStream.write(97);
    outputStream.write('雷'); //会乱码
    outputStream.write('b');
    outputStream.write("\r\n".getBytes());


    //3.写一个字节数组出去
    byte[] buffer = "我爱你中国".getBytes();
    outputStream.write(buffer);
    outputStream.write("\r\n".getBytes());


    //4.写一个字节数组的一部分出去
    outputStream.write(buffer,0,3);
    outputStream.write("\r\n".getBytes());
    
    outputStream.close();//关闭管道 释放资源
}

4.2.3-文件复制

  • 任何文件的底层都是字节,字节流做复制,是一字不漏的转移完全部字节,只要复制后的文件格式一致就没问题。

public static void main(String[] args) {
    //目标:使用字节流完成文件的复制操作。
    //源文件:F:\img\7.jpg
    //目标文件:E:\copydemo\img7.jpg(复制过去必须带文件名,无法自动生成文件名)
    try {
        copyFile("F:\\img\\7.jpg","E:\\copydemo\\img7.jpg");
    } catch (Exception e) {
        e.printStackTrace();
    }


}
//复制文件
public static void copyFile(String srcPath,String destPath) throws Exception {
    //1.创建一个字节输入流管道与源文件接通
    FileInputStream fis =new FileInputStream(srcPath);
    FileOutputStream fos = new FileOutputStream(destPath);
    //2.读取一个字节数组,写入一个字节数组 1024+1024+3
    byte[] buffer=new byte[1024];
    int count;
    while((count=fis.read(buffer))!=-1){
        fos.write(buffer,0,count);//读取多少字节就写入多少字节
    }
    System.out.println("复制完毕");
}

4.2.4-关闭资源的方式

try-catch-finally

  • finally代码区的特点:无论try中的程序是正常执行了,还是出现了异常,最后都一定会执行finally区,除非JVM终止。
  • 作用:一般用于在程序执行完成后进行资源的释放操作(专业级做法)
public static void copyFile(String srcPath,String destPath)  {
    FileOutputStream fos=null;
    FileInputStream fis=null;
    try {
        //1.创建一个字节输入流管道与源文件接通
        fis =new FileInputStream(srcPath);
        fos = new FileOutputStream(destPath);
        //2.读取一个字节数组,写入一个字节数组 1024+1024+3
        byte[] buffer=new byte[1024];
        int count;
        while((count=fis.read(buffer))!=-1){
            fos.write(buffer,0,count);//读取多少字节就写入多少字节
        }
        System.out.println("复制完毕");
    } catch (IOException e) {
        e.printStackTrace();
    }finally {
        //最后一定会执行一次,即便程序出现异常!
        try {
            if(fos != null)fos.close();
            if (fos != null)fis.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

上面这个代码的执行方案虽然很完美,但是两次try-catch非常冗余。

所以JDK7开始提供了更简单的资源释放方案try-with-resource。

try-with-resource

public static void copyFile(String srcPath,String destPath)  {

    try (
            //1.创建一个字节输入流管道与源文件接通
            FileInputStream fis =new FileInputStream(srcPath);
            FileOutputStream fos =  new FileOutputStream(destPath);
            ){
        //2.读取一个字节数组,写入一个字节数组 1024+1024+3
        byte[] buffer=new byte[1024];
        int count;
        while((count=fis.read(buffer))!=-1){
            fos.write(buffer,0,count);//读取多少字节就写入多少字节
        }
        System.out.println("复制完毕");
    } catch (IOException e) {
        e.printStackTrace();
    }
} 

4.3-字符流

4.3.1FileReader文件字符输入流

  • 作用:以内存为基准,可以把文件中的数据以字符的形式读入到内存中去。
public static void main(String[] args) {
    //目标:掌握文件字符输入流读取字符内容到内存中来。
    try (
            //1.创建文件字符输入流管道与源文件接通。
            Reader fr = new FileReader("day03-file-io\\src\\xunshan06.txt")
    ) {
        //2.定义一个字符数组,每次读取多个字符。
        char[] buffer=new char[3];
        int count;//统计每次读取的字符个数。
        while((count=fr.read(buffer))!=-1){
            //3.把读取的字符数组转换成字符串进行输出。
            String str=new String(buffer,0,count);
            System.out.println(str);
        }
        //拓展:文件字符输入流一次读取多个字符,性能较好,而且读取中文
        //是按照字符读取,不会出现乱码,这是一种读取中文很好的方案。
    }catch (Exception e){
        e.printStackTrace();
    }
}

4.3.2FileWriter文件字符输出流

  • 作用:以内存为基准,把内存中的数据以字符的形式写出到文件中去。
  • 和输入流一样,FileWriter作为Java语言也提供了构造器和其内部的方法。
//目标:搞清楚文件字符输出流的使用,写字符出去的流。
//1.创建一个文件字符输出流管道与源文件接通。
try (
        //1.创建文件字符输出流管道与源文件接通。
        FileWriter fw = new java.io.FileWriter("day03-file-io\\src\\xunshan07.txt")
) {
    //2.写出数据
    fw.write('a');
    fw.write("\r\n");
    fw.write("hello");//3.写多个字符出去。
    fw.write("\r\n");
    fw.write("java",1,2);//3.写字符串的一部分出去。
    fw.write("\r\n");
    char[] buffer=new char[]{'a','b','c','d','e'};//4.写一个字符数组出去。
    fw.write(buffer);
    fw.write(buffer,1,2);//5.写一个字符数组的一部分出去。
}catch (Exception e){
    e.printStackTrace();
}

以上方法是覆盖,我们也可以追加数据而不是把原文件数据覆盖。

字符输出流的注意实现

  • 字符流输出流写出数据后,必须刷新流,或者关闭流,写出去的数据才能生效。
fw.flush()
//刷新后,流可以继续使用
fw.close()
//关闭包含了刷新,但是关闭后流不能继续使用了

4.4-缓冲流

4.4.1-缓冲字节流

BuffedInputStream缓冲字节输入流

BuffedOutputStream缓冲字节输出流

  • 作用:可以提高字节输入流数据的性能
  • 原理:缓冲字节输入流自带了8KB缓冲池;缓冲字节输出流也自带了8KB缓冲池。

这是BufferdInputStream的部分源码,可以看到自带了8KB的缓冲池,输出流也是同理。



public static void main(String[] args) {
    //目标:使用缓冲字节流完成文件的复制操作。
    //源文件:F:\img\7.jpg
    //目标文件:E:\copydemo\img7.jpg(复制过去必须带文件名,无法自动生成文件名)
    try {
        copyFile("F:\\img\\7.jpg","E:\\copydemo\\img7.jpg");
    } catch (Exception e) {
        e.printStackTrace();
    }
}
//复制文件
public static void copyFile(String srcPath,String destPath)  {




    try (
            //1.创建一个字节输入流管道与源文件接通
            FileInputStream fis =new FileInputStream(srcPath);
            //把低级的字节输入流包装成缓冲字节输入流,提高读的效率,默认缓冲区是8kb。
            BufferedInputStream bis = new BufferedInputStream(fis);
            FileOutputStream fos =  new FileOutputStream(destPath);
            //3.把低级的字节输出流包装成缓冲字节输出流,提高写的效率,默认缓冲区是8kb。
            BufferedOutputStream bos = new BufferedOutputStream(fos);
    ){
        //2.读取一个字节数组,写入一个字节数组 1024+1024+3
        byte[] buffer=new byte[1024];
        int count;
        while((count=bis.read(buffer))!=-1){
            bos.write(buffer,0,count);//读取多少字节就写入多少字节
        }
        System.out.println("复制完毕");
    } catch (IOException e) {
        e.printStackTrace();
    }
}
我们的使用,只需要用Bufferd包一下就可以了。

4.4.2-缓冲字符流

BuffedReader缓冲字符输入流

  • 作用:可以提高字符输入流数据的性能
  • 原理:缓冲字符输入流自带了8KB缓冲池;缓冲字符输出流也自带了8KB缓冲池。

字符缓冲输入流新增的功能:按照行读取字符

public static void main(String[] args) {
    //目标:搞清楚缓冲字符输入流读取字符内容:性能提升了,多了按行读取文本的内容。
    try (
            //1.创建文件字符输入流管道与源文件接通。
            Reader fr = new FileReader("day03-file-io\\src\\xunshan06.txt");
            //把低级的流包装成缓冲字符输入流管道
            BufferedReader br = new BufferedReader(fr)
    ) {
        //2.定义一个字符串变量存储读取的一行内容。
        String line;
        while ((line = br.readLine()) != null) {
            System.out.println(line);
        }
    } catch (Exception e) {
        e.printStackTrace();
    }
}

  • 目前读取文本最优雅的方案;性能好,不乱码,可以按行读取。

BuffedWriter缓冲字符输出流

  • 作用:可以提高字符输出流数据的性能
  • 原理:缓冲字符输入流自带了8KB缓冲池;缓冲字符输出流也自带了8KB缓冲池。

字符缓冲输出流新增的功能:换行

public static void main(String[] args) {
    // 目标:搞清楚缓冲字符输出流的使用,提高了字符输出流写字符的效率,多了换行的功能。
    // 1. 创建一个文件字符输出流管道与源文件接通。
    try (
            // 1. 创建文件字符输出流管道与源文件接通。
            Writer fw = new FileWriter("day03-file-io\\src\\xunshan07.txt", true);
            // 2. 创建一个缓冲字符输出流管道与源文件接通。
            BufferedWriter bw = new BufferedWriter(fw);
    ) {
        // 2. 写出数据
        bw.write('a');
        bw.newLine(); // 使用 newLine() 方法换行
        // 3. 写多个字符出去。
        bw.write("hello");
        bw.newLine();
        // 4. 写字符串的一部分出去。
        bw.write("java", 1, 2);
        bw.newLine();
        // 5. 写一个字符数组出去。
        char[] buffer = new char[]{'a', 'b', 'c', 'd', 'e'};
        bw.write(buffer);
        bw.newLine();
        // 6. 写一个字符数组的一部分出去。
        bw.write(buffer, 1, 2);
    } catch (IOException e) {
        e.printStackTrace();
    }
}

4.5-其他流

4.5.1-InputStreamReader(字符输入转换流)

  • 解决不同编码时,字符流读取文本内容乱码的问题。
  • 解决思路:先获取文件的原始字节流,再将其按真实的字符集编码转成字符输入流,这样字符输入流中的字符也就不乱码了。

  • 那么我们现在用字符输入转换流解决这个问题。
//目标:使用字符输入转换流解决问题:不同编码读取乱码
//代码:UTF-8 文件UTF-8 读取不乱码
//代码:UTF-8 文件GBK 读取乱码
try (
        //先提取文件的原始字节流
        InputStream is=new FileInputStream("day03-file-io\\src\\xunshan09.txt");
        //指定编码把原始字节流转换成字符输入流
        InputStreamReader isr=new InputStreamReader(is,"GBK");


        //把低级的流包装成缓冲字符输入流管道
        BufferedReader br = new BufferedReader(isr)
) {
    //2.定义一个字符串变量存储读取的一行内容。
    String line;
    while ((line = br.readLine()) != null) {
        System.out.println(line);
    }
} catch (Exception e) {
    e.printStackTrace();
}

4.5.2-PrintStream/PrintWriter打印流

  • 作用:打印流可以实现更方便、更高效的打印数据出去,能实现打印啥出去就是啥出去。(我们可能打印97出去变成了‘a’,但是使用打印流97就是97)
    public static void main(String[] args) {
        //目标:认识PrintStream打印流的常用方法。
        try (PrintStream ps = new PrintStream("day03-file-io/src/ps.txt")) {


            ps.print("hello");
            ps.println("hello");
            ps.println(true);
            ps.println(100);
            ps.println(100.5);
            ps.println('a');
            ps.println(new String[]{"a","b","c"});
            ps.println(new int[]{1,2,3,4,5});
            ps.println(new Object());
        }catch (Exception e){
            e.printStackTrace();
        }


    }

PrintStream/PrintWriter的使用方法完全一样,如果想要追加可以写一个低级流,然后加上true之后用打印流包装一下。

4.5.3-DataInputStream/DataOutputStream(特殊数据输出/输入流)

  • 允许把数据和其类型一并写出去。

因为输入输出两个代码的区别只有Input和Output的差别,所以我们只罗列一个。

    public static void main(String[] args) {
        //目标:认识特殊数据流的使用。
        try (DataInputStream dis = new DataInputStream(new FileInputStream("day03-file-io/src/data.txt"))) {


            System.out.println(dis.readInt());
            System.out.println(dis.readUTF());
            System.out.println(dis.readBoolean());
            System.out.println(dis.readDouble());
            System.out.println(dis.readChar());
        }catch (Exception e){
            e.printStackTrace();
        }
    }

4.6-IO框架

我们用IO框架,对于前面各种流动辄好几行的代码,大部分都能直接简化成一行,但是我们学前面的内容也是有意义的,它可以打好我们的基本功,让我们无论是调用IO框架还是解读别人的源码,都知道是怎么来的。

什么是框架?

  • 框架(Framework)是一个预先写好的代码库或一组工具,旨在简化和加速开发过程。
  • 框架的形式:一般是把类、接口等编译成class形式,再压缩成一个.jar结尾的文件发行出去。

什么是IO框架

  • 封装了java提供的对文件、数据进行操作的代码,对外提供了更简单的方式来对文件进行操作,对数据进行读写等。

导入commons-ios-2.11.0.jar

  • 在项目中创建一个文件夹:lib
  • 将commons-ios.jar文件复制到lib文件夹
  • 在jar文件上右键,选择Add as Library→点击OK
  • 在类中导包使用

Commons-io框架

Commons-io是apache开源基金组织提供的一组有关IO操作的小框架,目的是提高IO流的开发效率

public static void main(String[] args) throws IOException {
    //目标:用框架封装的IO流,完成文件的复制。
    //1、把文件复制到指定的目录下。
    FileUtils.copyFile(new File("F:\\img\\7.jpg"),new File("E:\\copydemo\\img7.jpg"));
    //2.删除文件
    FileUtils.forceDelete(new File("E:\\copydemo\\img7.jpg"));


}

因为诸多功能的用法有了前文的基础,都是一看就懂的,所以我们在这里就只演示一个复制和一个删除操作。

能看到这里真的很不容易,这应该也是博主目前写过最长的一个博客,感谢大家的观看。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值