Fork me on CSDN

Java-IO流

IO流

File类概述

  • File类在包java.io.File下、代表操作系统的文件对象(文件、文件夹)。
  • File类提供了诸如:定位文件,获取文件本身的信息、删除文件、创建文件(文件夹)等功能。

File类创建对象:

在这里插入图片描述

  • File对象可以定位文件和文件夹
  • File封装的对象仅仅是一个路径名,这个路径可以是存在的,也可以是不存在的。
//1、创建File对象
//路径写法:G:\\JavaSE\\img\\1.jpg
//        G:/JavaSE/img/1.jpg
//        File.separator
//File f = new File("G:\\JavaSE\\img\\1.jpg");
//File f = new File("G:/JavaSE/img/1.jpg");
File f = new File("G:"+File.separator+"JavaSE"+File.separator+"img"+File.separator+"1.jpg");
long size = f.length();//是文件的字节大小
System.out.println(size);
//2、File创建对象,支持绝对路径支持相对路径(重点)
File f1 = new File("G:\\JavaSE\\img\\1.jpg");//绝对路径
System.out.println(f1.length());

//相对路径:一般定位模块中的文件的。相对到工程下!!
File f2 = new File("src/data.txt");
System.out.println(f2.length());
//3、File创建对象,可以是文件也可以是文件夹
File f3 = new File("G:\\JavaSE");
System.out.println(f3.exists());//判断这个路径是否存在,这个文件夹存在否

绝对路径和相对路径:

  • 绝对路径:从盘符开始
File f = new File("G:\\JavaSE\\img\\1.jpg");
  • 相对路径:不带盘符,默认直接到当前工程下的目录寻找文件。
File f2 = new File("模块名\\1.txt");

File类的常用API

判断文件类型、获取文件信息

在这里插入图片描述

//1.绝对路径创建一个文件对象
File f1 = new File("G:\\JavaSE\\img\\1.jpg");
//a.获取它的绝对路径。
System.out.println(f1.getAbsolutePath());//G:\JavaSE\img\1.jpg
//b.获取文件定义的时候使用的路径。
System.out.println(f1.getPath());//G:\JavaSE\img\1.jpg
//c.获取文件的名称:带后缀。
System.out.println(f1.getName());//1.jpg
//d.获取文件的大小:字节个数。
System.out.println(f1.length());//字节大小 317777
//e.获取文件的最后修改时间
long time = f1.lastModified();
System.out.println("最后修改时间:"+new SimpleDateFormat("yyyy:MM:dd HH:mm:ss").format(time));//最后修改时间:2022:03:03 10:16:19
//f.判断文件是文件还是文件夹
System.out.println(f1.isFile());//true  文件
System.out.println(f1.isDirectory());//false  文件夹

System.out.println("========================================");
//1.绝对路径创建一个文件对象
File f2 = new File("src\\data.txt");
//a.获取它的绝对路径。
System.out.println(f2.getAbsolutePath());//G:\JavaSE\SE\code\IO\src\data.txt
//b.获取文件定义的时候使用的路径。
System.out.println(f2.getPath());//src\data.txt
//c.获取文件的名称:带后缀。
System.out.println(f2.getName());//data.txt
//d.获取文件的大小:字节个数。
System.out.println(f2.length());//字节大小  6
//e.获取文件的最后修改时间
long time2 = f2.lastModified();
System.out.println("最后修改时间:"+new SimpleDateFormat("yyyy:MM:dd HH:mm:ss").format(time2));//最后修改时间:2022:04:16 17:25:23
//f.判断文件是文件还是文件夹
System.out.println(f2.isFile());//true  文件
System.out.println(f2.isDirectory());//false  文件夹

创建文件、删除文件功能

File类创建文件的功能:

在这里插入图片描述

File类删除文件的功能:

在这里插入图片描述

  • delete方法直接删除不走回收站;如果删除的是一个文件,且文件没有被占用则直接删除。
  • delete方法默认只能删除空文件夹。
File f = new File("src\\data.txt");
//a.创建新文件,创建成功返回true ,反之,不需要这个,以后文件写出去的时候都会自动创建
System.out.println(f.createNewFile());//(几乎不用的,因为以后文件都是自动创建的! )

File f1 = new File("src\\data2.txt");
System.out.println(f1.createNewFile());
//b.mkdir创建一级目录
File f2 = new File("G:\\JavaSE\\img\\a");
System.out.println(f2.mkdir());//true 不支持多级
//c.mkdirs创建多级目录(重点)
File f3 = new File("G:\\JavaSE\\img\\a\\c\\b");
System.out.println(f3.mkdirs());//true
//d.删除文件或者空文件夹
System.out.println(f1.delete());//true

File f4 = new File("G:\\JavaSE\\img\\2.jpg");
System.out.println(f4.delete());//true 占用一样可以删除
//只能删除空文件夹,不能刷除非空文件夹.
File f5 = new File("G:\\JavaSE\\img\\a\\c\\b");
System.out.println(f5.delete());//true

遍历文件夹

在这里插入图片描述

listFiles方法注意事项:

  • 当调用者不存在时,返回null
  • 当调用者是一个文件时,返回null
  • 当调用者是一个空文件夹时,返回一个长度为0的数组
  • 当调用者是一个有内容的文件夹时,将里面所有文件和文件夹的路径放在File数组中返回
  • 当调用者是一个有隐藏文件的文件夹时,将里面所有文件和文件夹的路径放在File数组中返回,包含隐藏内容
  • 当调用者是一个需要权限才能进入的文件夹时,返回null
//1、定位一个目录
File f = new File("G:\\JavaSE\\img");
String[] names = f.list();
for (String name : names) {
    System.out.println(name);
}
//2.一级文件对象
//获取当前目录下所有的"一级文件对象"到一个文件对象数组中去返回 (重点)
File[] files = f.listFiles();
for (File file : files) {
    System.out.println(file.getAbsolutePath());
}
//注意事项
File dir = new File("G:/aaaaaa");//没有这个文件夹
File[] files1 = dir.listFiles();
System.out.println(files1);//null

方法递归

递归的形式和特点

递归的形式:

  • 直接递归:方法自己调用自己。
  • 间接递归:方法调用其他方法,其他方法又回调方法自己。
public static void main(String[] args) {
    test();
    test2();
}

private static void test() {
    System.out.println("===========test被执行=================");
    test();//方法递归  直接递归形式
}

private static void test2() {
    System.out.println("===========test2被执行=================");
    test3();//方法递归  间接递归形式
}

private static void test3() {
    System.out.println("===========test3被执行=================");
    test2();//方法递归  间接递归形式
}

递归的算法流程、核心要素

递归算法三要素大体可以总结为:

  • 递归的公式:f(n) = f(n-1)*n;
  • 递归的终结点: f(1)
  • 递归的方向必须走向终结点

案例:计算1-n的阶乘

需求:

  • 计算1-n的阶乘的结果,使用递归思想解决,我们先从数学思维上理解递归的流程和核心点。

分析:

  • 假如我们认为存在一个公式是f(n)= 1*2*3*4*5*6*7*...(n-1)*n;
  • 那么公式等价形式就是: f(n)= f(n-1) *n
  • 如果求的是1-5的阶乘的结果,我们手工应该应该如何应用上述公式计算。
public static void main(String[] args) {
    System.out.println(f(5));
}
public static int f(int n){
    if (n==1){
        return 1;
    }else {
        return f(n-1)*n;
    }
}

递归常见案例

案例:计算1-n的和

需求:

  • 计算1-n的和的结果,使用递归思想解决,我们先从数学思维上理解递归的流程和核心点。

分析:

  • 假如我们认为存在一个公式是f(n)= 1+2+3+4+5+6+7+ ...(n-1)+ n;
  • 那么公式等价形式就是:f(n) = f(n-1) +n
  • 递归的终结点: f(1)= 1
  • 如果求的是1-5的和的结果,应该如何计算。
public static void main(String[] args) {
    System.out.println(f(5));
}
public static int f(int n){
    if (n==1){
        return 1;
    }else {
        return f(n-1)+n;
    }
}

递归的经典问题

猴子吃桃问题

猴子第一天摘下若干桃子,当即吃了一半,觉得好不过瘾,于是又多吃了一个第二天又吃了前天剩余桃子数量的一半,觉得好不过瘾,于是又多吃了一个以后每天都是吃前天剩余桃子数量的一半,觉得好不过瘾,又多吃了一个等到第10天的时候发现桃子只有1个了。

需求:请问猴子第一天摘了多少个桃子?

/**
 * 猴子吃桃
 * 公式(合理的):f(x) -f(x)/2 - 1 = f(x+1)
 *              2f(x) - f(x) - 2 = 2f(x+1)
 *              f(x) = 2f(x+1) + 2
 *  求f(1) = ?
 *  终结点:f (10)= 1
 *  递归的方向:合理的
 *
 */
public class Recursion {
    public static void main(String[] args) {
        System.out.println(f(1));
    }
    public static int f(int n){
        if (n==10){
            return 1;
        }else {
            return 2*f(n+1) + 2;
        }
    }

}

非规律化递归案例-文件搜索

案例1:文件搜索

需求:

  • 文件搜索、从G:盘中,搜索出某个文件名称并输出绝对路径。

分析:

  • 先定位出的应该是一级文件对象
  • 遍历全部一级文件对象,判断是否是文件
  • 如果是文件,判断是否是自己想要的
  • 如果是文件夹,需要继续递归进去重复上述过程
public static void main(String[] args) {
    searchFile(new File("G:/"),"Typora.exe");
}

/**
 * 1、搜索某个目录下的全部文件,找到我们想要的文件。
 * @param dir 被搜索的源目录
 * @param fileName 被搜索的文件名称
 */
public static void searchFile(File dir,String fileName){
    //3、判断dir是否是目录
    if (dir!=null && dir.isDirectory()){
        //可以找了
        //4、提取当前目录下的一级文件对象
        File[] files = dir.listFiles();//null  []
        //5、判断是否存在一级文件对象,存在才可以遍历
        if (files!=null && files.length>0){
            for (File file : files) {
                //6、判断当前遍历的一级文件对象是文件 还是 目录
                if (file.isFile()){
                    //7、是不是咱们要找的,是把其路径输出即可
                    if (file.getName().contains(fileName)){
                        System.out.println("找到了:"+file.getAbsolutePath());
                        //启动它
                        try {
                            Runtime r = Runtime.getRuntime();
                            r.exec(file.getAbsolutePath());
                        } catch (IOException e) {
                            e.printStackTrace();
                        }
                    }
                }else {
                    //8、是文件夹,需要继续递归寻找
                    searchFile(file,fileName);
                }
            }
        }

    }else {
        System.out.println("对不起,当前搜索的位置不是文件夹!");
    }
}

案例2:啤酒问题

需求:

  • 啤酒2元1瓶,4个盖子可以换一瓶,2个空瓶可以换一瓶,请问10元钱可以喝多少瓶酒,剩余多少空瓶和盖子。
  • 答案:15瓶3盖子1瓶子
//定义一个静态的成员变量用于存储可以买的酒数量
public static int totalNumber;//总数量
public static int lastBottleNumber;//记录每次剩余的瓶子个数
public static int lastCoverNumber;//记录每次剩余的盖子个数
public static void main(String[] args) {
    //1、拿钱买酒
    buy(10);
    System.out.println("总数量:"+totalNumber);
    System.out.println("剩余盖子个数:"+lastCoverNumber);
    System.out.println("剩余瓶子个数:"+lastBottleNumber);
}

private static void buy(int money) {
    //2、看可以立马买多少瓶
    int buyNumber=money/2;
    totalNumber+=buyNumber;
    //3、把盖子和瓶子换算成钱
    //统计本轮总的盖子数  和  瓶子数
    int coverNumber=lastCoverNumber+buyNumber;
    int bottleNumber=lastBottleNumber+buyNumber;
    //统计可以换算的钱
    //盖子的钱
    int allMoney=0;
    if (coverNumber>=4){
        allMoney+=(coverNumber/4)*2;
    }
    lastCoverNumber=coverNumber%4;
    //瓶子的钱
    if (bottleNumber>=2){
        allMoney+=(bottleNumber/2)*2;
    }
    lastBottleNumber=bottleNumber%2;

    if (allMoney>=2){
        buy(allMoney);
    }
}

字符集

常见字符集介绍

字符集基础知识:

  • 计算机底层不可以直接存储字符的。计算机中底层只能存储二进制(0、1)
  • 二进制是可以转换成十进制的
  • 结论:计算机底层可以表示十进制编号。计算机可以给人类字符进行编号存储,这套编号规则就是字符集。

ASCII字符集:

  • ASCII(American Standard Code for Information Interchange,美国信息交换标准代码):包括了数字、英文、符号。
  • ASCII使用1个字节存储一个字符,一个字节是8位,总共可以表示128个字符信息,对于英文,数字来说是够用的。

GBK:

  • window系统默认的码表。兼容ASCII码表,也包含了几万个汉字,并支持繁体汉字以及部分日韩文字。
  • 注意:GBK是中国的码表,一个中文以两个字节的形式存储。但不包含世界上所有国家的文字

Unicode码表:

  • unicode (又称统一码、万国码、单一码)是计算机科学领域里的一项业界字符编码标准。
  • 容纳世界上大多数国家的所有常见文字和符号。
  • 由于Unicode会先通过UTF-8,UTF-16,以及UTF-32的编码成二进制后再存储到计算机,其中最为常见的就是UTF-8。

注意:

  • Unicode是万国码,以UTF-8编码后一个中文一般以三个字节的形式存储
  • UTF-8也要兼容ASCIl编码表。
  • 技术人员都应该使用UTF-8的字符集编码。
  • 编码前和编码后的字符集需要一致,否则会出现中文乱码。

在这里插入图片描述

总结:

1.字符串常见的字符底层组成是什么样的?

  • 英文和数字等在任何国家的字符集中都占1个字节
  • GBK字符中一个中文字符占2个字节
  • UTF-8编码中一个中文1般占3个字节

2.编码前的字符集和编码好的字符集有什么要求?

  • 必须一致,否则会出现中文字符乱码
  • 英文和数字在任何国家的编码中都不会乱码

字符集的编码、解码操作

String编码:

在这里插入图片描述

String解码:

在这里插入图片描述

//1、编码:把文字转换成字节(使用指定的编码)
String name="我爱你中国abc";
//byte[] bytes = name.getBytes();//以当前代码默认字符集进行编码(UTF-8)
byte[] bytes = name.getBytes("GBK");//指定编码
System.out.println(bytes.length);
System.out.println(Arrays.toString(bytes));

//2、解码:把字节转换成对应的中文形式(编码前 和 编码后的字符集必须一致,否则乱码)
String rs=new String(bytes);//默认的UTF-8 乱码
String rs1=new String(bytes,"GBK");//指定GBK解码
System.out.println(rs);
System.out.println(rs1);

IO流概述

IO流也称为输入、输出流,就是用来读写数据的

  • l表示intput,是数据从硬盘文件读入到内存的过程,称之输入,负责读。
  • O表示output,是内存程序的数据从内存到写出到硬盘文件的过程,称之输出,负责写。

在这里插入图片描述

总结流的四大类:

  • 字节输入流:以内存为基准,来自磁盘文件/网络中的数据以字节的形式读入到内存中去的流称为字节输入流。
  • 字节输出流:以内存为基准,把内存中的数据以字节写出到磁盘文件或者网络中去的流称为字节输出流。
  • 字符输入流:以内存为基准,来自磁盘文件/网络中的数据以字符的形式读入到内存中去的流称为字符输入流。
  • 字符输出流:以内存为基准,把内存中的数据以字符写出到磁盘文件或者网络介质中去的流称为字符输出流。

在这里插入图片描述

字节流的使用

在这里插入图片描述

文件字节输入流:每次读取一个字节

文件字节输入流:FileInputStream

  • 作用:以内存为基准,把磁盘文件中的数据以字节的形式读取到内存中去。

在这里插入图片描述

在这里插入图片描述

//1、创建一个文件字节输入流管道与源文件接通。
InputStream is1 = new FileInputStream(new File("src\\data.txt"));
//简化写法
InputStream is = new FileInputStream("src\\data.txt");
//2、读取一个字节返回  (每次读取一滴水)
//int b1 = is.read();
//System.out.println((char) b1);
//int b2 = is.read();
//System.out.println((char) b2);
//int b3 = is.read();
//System.out.println((char) b3);
//int b4 = is.read();
//System.out.println(b4);//读取完毕返回-1

//3、使用循环改进
//定义一个变量记录每次读取的字节
int b;
while ((b= is.read())!=-1){
    System.out.print((char) b);
}

文件字节输入流:每次读取一个字节数组

文件字节输入流:FilelnputStream

  • 作用:以内存为基准,把磁盘文件中的数据以字节的形式读取到内存中去。

在这里插入图片描述

public static void main(String[] args) throws Exception {
        //1、创建一个文件字节输入流管道与源文件接通。
        InputStream is = new FileInputStream("src\\data1.txt");//ab3abccd
        //2、定义一个字节数组,用于读取字节数组
//        byte[] buffer = new byte[3];//3B
//        int len = is.read(buffer);
//        System.out.println("读取了"+len+"个字节");//读取了3个字节
//        //转换UTF-8
//        String rs = new String(buffer);
//        System.out.println(rs);//ab3
//
//        int len1 = is.read(buffer);
//        System.out.println("读取了"+len1+"个字节");//读取了3个字节
//        String rs1 = new String(buffer);
//        System.out.println(rs1);//abc
//        //buffer = [a b c]
//
//        //buffer = [a b c]==> [c d c]
//        int len2 = is.read(buffer);
//        System.out.println("读取了"+len2+"个字节");//读取了2个字节
//        //读取多少倒出多少
//        String rs2 = new String(buffer,0,len2);//读取0到len2个字节
//        System.out.println(rs2);//cd
//
//        int len3 = is.read(buffer);
//        System.out.println(len3);//读取完毕返回-1

        //3、改进使用循环,每次读取一个字节数组
        byte[] buffer = new byte[3];//3B
        int len;
        while ((len= is.read(buffer))!=-1){
            System.out.print(new String(buffer,0,len));//ab3abccd
        }
    }

文件字节输入流:一次读完全部字节

方式一

  • 自己定义一个字节数组与文件的大小一样大,然后使用读取字节数组的方法,一次性读取完成。

在这里插入图片描述

方式二----JDK9开始才有的api

  • 官方为字节输入流InputStream提供了如下API可以直接把文件的全部数据读取到一个字节数组中

在这里插入图片描述

//1、创建一个文件字节输入流管道与源文件接通。
File f = new File("src\\data1.txt");
InputStream is = new FileInputStream(f);
//2、定义一个字节数组与文件的大小刚刚一样大。
byte[] buffer = new byte[(int) f.length()];
int len = is.read(buffer);
System.out.println("读取了"+len+"个字节");
System.out.println("文件大小:"+f.length());
System.out.println(new String(buffer));

//读取全部字节数组--JDK9开始才有的api
//byte[] buffer=is.readAllBytes();
//System.out.println(new String(buffer));

文件字节输出流:写字节数据到文件

文件字节输出流:FileOutputStream

  • 作用:以内存为基准,把内存中的数据以字节的形式写出到磁盘文件中去的流。

在这里插入图片描述

文件字节输出流(FileoutputStream)写数据出去的API

在这里插入图片描述

流的关闭与刷新

在这里插入图片描述

//1、创建一个文件字节输出流管道与目标文件接通
//OutputStream os = new FileOutputStream("src/out1.txt");//先清空之前的数据,写新数据进入
OutputStream os = new FileOutputStream("src/out1.txt",true);//追加数据管道
//2、写数据出去
//a. public void write(int a)∶写一个字节出去。
os.write('a');
os.write(98);
os.write("\r\n".getBytes());//换行
//b. public void write (byte[] buffer):写一个字节数组出去。
byte[] buffer={'a',97,98,99};
os.write(buffer);

byte[] buffer2="我是中国人".getBytes();
//byte[] buffer2="我是中国人".getBytes("GBK");
os.write(buffer2);
os.write("\r\n".getBytes());//换行
//c. public void write(byte[] buffer,int pos,int len):写一个字节数组的一部分出去。
byte[] buffer3={'a',97,98,99};
os.write(buffer3,0,3);//aab

os.flush();//写数据必须,刷新数据可以继续使用流
os.close();//释放资源,包含了刷新的!关闭后流不可以使用了

文件拷贝

案例---文件拷贝

需求:

  • 把某个图片复制到其他目录下的"1.jpg"

思路:

  • 根据数据源创建字节输入流对象
  • 根据目的地创建字节输出流对象
  • 读写数据,复制视频
  • 释放资源
try {
    //1、创建一个字节输入流管道与原图片接通
    InputStream is = new FileInputStream("G:\\JavaSE\\img\\1.jpg");
    //2、创建一个字节输出流管道与目标文件接通
    OutputStream os = new FileOutputStream("G:\\JavaSE\\img\\2.jpg");
    //3、定义一个字节数组转移数据
    byte[] buffer = new byte[1024];
    int len;
    while ((len= is.read(buffer))!=-1){
        os.write(buffer,0,len);
    }
    System.out.println("复制完成!");
    //4、关闭流
    os.close();
    is.close();
} catch (Exception e) {
    e.printStackTrace();
}

资源释放的方式

try-catch-finally

  • finally:在异常处理时提供finally块来执行所有清除操作,比如说IO流中的释放资源
  • 特点:被finally控制的语句最终一定会执行,除非JVM退出
  • 异常处理标准格式: try....catch...finally
public static void main(String[] args) {
    InputStream is=null;
    OutputStream os=null;
    try {
        //1、创建一个字节输入流管道与原图片接通
        is = new FileInputStream("G:\\JavaSE\\img\\1.jpg");
        //2、创建一个字节输出流管道与目标文件接通
        os = new FileOutputStream("G:\\JavaSE\\img\\2.jpg");
        //3、定义一个字节数组转移数据
        byte[] buffer = new byte[1024];
        int len;
        while ((len= is.read(buffer))!=-1){
            os.write(buffer,0,len);
        }
        System.out.println("复制完成!");
        //4、关闭流
        os.close();
        is.close();
    } catch (Exception e) {
        e.printStackTrace();
    }finally {
        //无论代码是正常结束,还是出现异常都要最后执行这里
        System.out.println("===========finally================");
        try {
            if (os!=null)os.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
        try {
            if (is!=null)is.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    //System.out.println(test(10,0));
}
public static int test(int a,int b){
    try {
        int c=a/b;
        return c;
    } catch (Exception e) {
        e.printStackTrace();
        return -111111111;//计算出现bug
    } finally {
        //哪怕上面有return语句执行,也必须先执行完这里才可以!
        //开发中不建议在这里加return ,如果加了,返回的永远是这里的数据了,这样会出问题!
        return 100;
    }
}

try-with-resource

在这里插入图片描述

在这里插入图片描述

一般不用JDK9方案

在这里插入图片描述

注意:

  • JDK7以及JDK 9的()中只能放置资源对象,否则报错
  • 资源都是实现了Closeable/AutoCloseable接口的类对象
try (
        //这里面只能放置资源对象,用完会自动关闭:自动调用资源对象的close方法关闭资源〈即使出现异常也会做关闭操作)
        //1、创建一个字节输入流管道与原图片接通
        InputStream is = new FileInputStream("G:\\JavaSE\\img\\1.jpg");
        //2、创建一个字节输出流管道与目标文件接通
        OutputStream os = new FileOutputStream("G:\\JavaSE\\img\\2.jpg");

        //int age=23;//这里只能放资源
        ){

    //3、定义一个字节数组转移数据
    byte[] buffer = new byte[1024];
    int len;
    while ((len= is.read(buffer))!=-1){
        os.write(buffer,0,len);
    }
    System.out.println("复制完成!");
} catch (Exception e) {
    e.printStackTrace();
}

字符流的使用

在这里插入图片描述

文件字符输入流-一次读取一个字符

文件字符输入流:Reader

  • 作用:以内存为基准,把磁盘文件中的数据以字符的形式读取到内存中去。

在这里插入图片描述

//1、创建一个字符输入流
Reader fr = new FileReader("src/data2.txt");
//2、读取一个字符返回,没有可读的字符了返回-1
int code1=fr.read();
System.out.print((char) code1);
//3、使用循环读取字符
int code;
while ((code=fr.read())!=-1){
    System.out.print((char) code);
}

文件字符输入流-一次读取一个字符数组

在这里插入图片描述

//1、创建一个字符输入流
Reader fr = new FileReader("src/data2.txt");
//2、用循环,每次读取一个字符数组的数据。
char[] buffer = new char[1024];
int len;
while ((len=fr.read(buffer))!=-1){
    String rs = new String(buffer, 0, len);
    System.out.println(rs);
}

文件字符输出流

文件字符输出流:FileWriter

  • 作用:以内存为基准,把内存中的数据以字符的形式写出到磁盘文件中去的流。

在这里插入图片描述

文件字符输出流(FileWriter)写数据出去的API

在这里插入图片描述

//1、创建一个字符输出流管道与目标文件接通
//Writer fw = new FileWriter("src/out2.txt");//覆盖管道,每次启动都会清空文件之前的数据
Writer fw = new FileWriter("src/out2.txt",true);//追加数据管道
//a.public void write(int c):写一个字符出去
fw.write(98);
fw.write('a');
fw.write('中');//不会出问题
fw.write("\r\n");//换行
//b.public void write(String c)写一个字符串出去
fw.write("abc我是中国人");
fw.write("\r\n");//换行
//c.public void write(char[ ] buffer):写一个字符数组出去
char[] chars="abc我是中国人".toCharArray();
fw.write(chars);
fw.write("\r\n");//换行
//d.public void write(String c ,int pos ,int len):写字符串的一部分出去
fw.write("abc我是中国人",0,5);//abc我是
fw.write("\r\n");//换行
//e.public void write(char[] buffer ,int pos ,int len):写字符数组的一部分出去
fw.write(chars,3,5);//我是中国人
fw.write("\r\n");//换行

fw.flush();//刷新后流可以继续使用
fw.close();//关闭包含刷线,关闭后流不能使用

总结:字节流、字符流如何选择使用?

  • 字节流适合做一切文件数据的拷贝(音视频,文本)
  • 字节流不适合读取中文内容输出
  • 字符流适合做文本文件的操作(读,写)

缓冲流

缓冲流概述

  • 缓冲流也称为高效流、或者高级流。之前学习的字节流可以称为原始流
  • 作用:缓冲流自带缓冲区、可以提高原始字节流、字符流读写数据的性能

在这里插入图片描述

字节缓冲流

字节缓冲流性能优化原理:

  • 字节缓冲输入流自带了8KB缓冲池,以后我们直接从缓冲池读取数据,所以性能较好。
  • 字节缓冲输出流自带了8KB缓冲池,数据就直接写入到缓冲池中去,写数据性能极高了。
try (
        //这里面只能放置资源对象,用完会自动关闭:自动调用资源对象的close方法关闭资源〈即使出现异常也会做关闭操作)
        //1、创建一个字节输入流管道与原图片接通
        InputStream is = new FileInputStream("G:\\JavaSE\\img\\1.jpg");
        //a.把原始的字节输入流包装成高级的缓冲字节输入流
        InputStream bis=new BufferedInputStream(is);
        //2、创建一个字节输出流管道与目标文件接通
        OutputStream os = new FileOutputStream("G:\\JavaSE\\img\\3.jpg");
        //b .把字节输出流管道包装成高级的缓冲字节输出流管道
        OutputStream bos = new BufferedOutputStream(os);

        //int age=23;//这里只能放资源
){

    //3、定义一个字节数组转移数据
    byte[] buffer = new byte[1024];
    int len;
    while ((len= bis.read(buffer))!=-1){
        bos.write(buffer,0,len);
    }
    System.out.println("复制完成!");
} catch (Exception e) {
    e.printStackTrace();
}

字节缓冲流的性能分析

分别使用不同的方式复制大视频观察性能情况

需求:

  • 分别使用低级字节流和高级字节缓冲流拷贝大视频,记录耗时。

分析:

  • 使用低级的字节流按照一个一个字节的形式复制文件。
  • 使用低级的字节流按照一个一个字节数组的形式复制文件。
  • 使用高级的缓冲字节流按照一个一个字节的形式复制文件。
  • 使用高级的缓冲字节流按照一个一个字节数组的形式复制文件。
private static final String SRC_FILE="G:\\JavaSE\\img\\视频\\typecho建站.mp4";
private static final String DEST_FILE="G:\\JavaSE\\img\\";
public static void main(String[] args) {
    copy01();//使用低级的字节流按照一个一个字节的形式复制文件:慢的让人简直无法忍受。直接被淘汰。
    copy02();//使用低级的字节流按照一个一个字节数组的形式复制文件:比较慢,但是还是可以忍受的!
    copy03();//缓冲流一个一个字节复制:很慢,不建议使用。
    copy04();//缓冲流一个一个字节数组复制:飞快,简直太完美了(推荐使用)
}

private static void copy04() {
    long startTime=System.currentTimeMillis();
    try (
            //这里面只能放置资源对象,用完会自动关闭:自动调用资源对象的close方法关闭资源〈即使出现异常也会做关闭操作)
            //1、创建一个字节输入流管道与原图片接通
            InputStream is = new FileInputStream(SRC_FILE);
            //a.把原始的字节输入流包装成高级的缓冲字节输入流
            InputStream bis=new BufferedInputStream(is);
            //2、创建一个字节输出流管道与目标文件接通
            OutputStream os = new FileOutputStream(DEST_FILE+"typecho4.mp4");
            //b .把字节输出流管道包装成高级的缓冲字节输出流管道
            OutputStream bos = new BufferedOutputStream(os);

            //int age=23;//这里只能放资源
    ){

        //3、定义一个字节数组转移数据
        byte[] buffer = new byte[1024];
        int len;
        while ((len= bis.read(buffer))!=-1){
            bos.write(buffer,0,len);
        }
        System.out.println("复制完成!");
    } catch (Exception e) {
        e.printStackTrace();
    }
    long endTime=System.currentTimeMillis();
    System.out.println("使用缓冲的字节流按照一个一个字节数组的形式复制文件耗时:"+(endTime-startTime)/1000.0+"s");
}

/**
 * 缓冲流一个一个字节复制
 */
private static void copy03() {
    long startTime=System.currentTimeMillis();
    try (
            //这里面只能放置资源对象,用完会自动关闭:自动调用资源对象的close方法关闭资源〈即使出现异常也会做关闭操作)
            //1、创建一个字节输入流管道与原图片接通
            InputStream is = new FileInputStream(SRC_FILE);
            //a.把原始的字节输入流包装成高级的缓冲字节输入流
            InputStream bis=new BufferedInputStream(is);
            //2、创建一个字节输出流管道与目标文件接通
            OutputStream os = new FileOutputStream(DEST_FILE+"typecho3.mp4");
            //b .把字节输出流管道包装成高级的缓冲字节输出流管道
            OutputStream bos = new BufferedOutputStream(os);

            //int age=23;//这里只能放资源
    ){

        //3、定义一个字节数组转移数据
        int b;
        while ((b= bis.read())!=-1){
            bos.write(b);
        }
        System.out.println("复制完成!");
    } catch (Exception e) {
        e.printStackTrace();
    }
    long endTime=System.currentTimeMillis();
    System.out.println("使用缓冲的字节流按照一个一个字节的形式复制文件耗时:"+(endTime-startTime)/1000.0+"s");
}

/**
 * 使用低级的字节流按照一个一个字节数组的形式复制文件
 */
private static void copy02() {
    long startTime=System.currentTimeMillis();
    try (
            //这里面只能放置资源对象,用完会自动关闭:自动调用资源对象的close方法关闭资源〈即使出现异常也会做关闭操作)
            //1、创建一个字节输入流管道与原图片接通
            InputStream is = new FileInputStream(SRC_FILE);
            //2、创建一个字节输出流管道与目标文件接通
            OutputStream os = new FileOutputStream(DEST_FILE+"typecho2.mp4");
    ){

        //3、定义一个字节数组转移数据
        byte[] buffer = new byte[1024];
        int len;
        while ((len= is.read(buffer))!=-1){
            os.write(buffer,0,len);
        }
        System.out.println("复制完成!");
    } catch (Exception e) {
        e.printStackTrace();
    }
    long endTime=System.currentTimeMillis();
    System.out.println("使用低级的字节流按照一个一个字节数组的形式复制文件耗时:"+(endTime-startTime)/1000.0+"s");
}

/**
 * 使用低级的字节流按照一个一个字节的形式复制文件
 */
private static void copy01() {
    long startTime=System.currentTimeMillis();
    try (
            //1、创建低级的字节输入流与源文件接通
            InputStream is = new FileInputStream(SRC_FILE);
            //2、创建低级的字节输出流与目标文件接通
            OutputStream os = new FileOutputStream(DEST_FILE+"typecho1.mp4");

            ){
        //3、定义一个变量记录每次读取的字节(一个一个字节的复制)
        int b;
        while ((b= is.read())!=-1){
            os.write(b);
        }
    } catch (Exception e) {
        e.printStackTrace();
    }
    long endTime=System.currentTimeMillis();
    System.out.println("使用低级的字节流按照一个一个字节的形式复制文件耗时:"+(endTime-startTime)/1000.0+"s");
}

字符缓冲流

1.字符缓冲输入流:

  • 字符缓冲输入流:BufferedReader。
  • 作用:提高字符输入流读取数据的性能,除此之外多了按照行读取数据的功能。

在这里插入图片描述

字符缓冲输入流新增功能:

在这里插入图片描述

try (
        //1、创建一个字符输入流
        Reader fr = new FileReader("src/data2.txt");
        //a、把低级的字符输入流包装成高级的缓冲字符输入流
        BufferedReader br = new BufferedReader(fr);
        ){

    //2、用循环,每次读取一个字符数组的数据。
    //char[] buffer = new char[1024];
    //int len;
    //while ((len=br.read(buffer))!=-1){
        //String rs = new String(buffer, 0, len);
        //System.out.println(rs);
    //}
    
    //一行一行读,空格也要读(经典代码)
    String line;
    while ((line= br.readLine())!=null){
        System.out.println(line);
    }
} catch (IOException e) {
    e.printStackTrace();
}

2.字符缓冲输出流:

  • 字符缓冲输出流:Bufferedwriter。
  • 作用:提高字符输出流写取数据的性能,除此之外多了换行功能

在这里插入图片描述

在这里插入图片描述

//1、创建一个字符输出流管道与目标文件接通
//Writer fw = new FileWriter("src/out2.txt");//覆盖管道,每次启动都会清空文件之前的数据
Writer fw = new FileWriter("src/out2.txt",true);//追加数据管道

BufferedWriter bw = new BufferedWriter(fw);
//a.public void write(int c):写一个字符出去
bw.write(98);
bw.write('a');
bw.write('中');//不会出问题
bw.newLine();// bw.write("\r\n"); 换行
//b.public void write(String c)写一个字符串出去
bw.write("abc我是中国人");
bw.write("\r\n");//换行
//c.public void write(char[ ] buffer):写一个字符数组出去
char[] chars="abc我是中国人".toCharArray();
bw.write(chars);
bw.newLine();//换行
//d.public void write(String c ,int pos ,int len):写字符串的一部分出去
bw.write("abc我是中国人",0,5);//abc我是
bw.newLine();//换行
//e.public void write(char[] buffer ,int pos ,int len):写字符数组的一部分出去
bw.write(chars,3,5);//我是中国人
bw.newLine();//换行

bw.flush();//刷新后流可以继续使用
bw.close();//关闭包含刷线,关闭后流不能使用

案例--出师表

案例--拷贝出师表到另一个文件,恢复顺序

需求:

  • 把《出师表》的文章顺序进行恢复到一个新文件中。

分析:

  • 定义一个缓存字符输入流管道与源文件接通。
  • 定义一个List集合存储读取的每行数据。
  • 定义一个循环按照行读取数据,存入到List集合中去。
  • 对List集合中的每行数据按照首字符编号升序排序。
  • 定义一个缓存字符输出管道与目标文件接通。
  • 遍历List集合中的每个元素,用缓冲输出管道写出并换行。
三 3.侍中、侍郎郭攸之、费祎、董允等,此皆良实,志虑忠纯,是以先帝简拔以遗陛下。愚以为宫中之事,事无大小,悉以咨之,然后施行,必能裨补阙漏,有所广益。
八 8.愿陛下托臣以讨贼兴复之效,不效,则治臣之罪,以告先帝之灵。若无兴德之言,则责攸之、祎、允等之慢,以彰其咎;陛下亦宜自谋,以咨诹善道,察纳雅言,深追先帝遗诏,臣不胜受恩感激。
四 4.将军向宠,性行淑均,晓畅军事,试用于昔日,先帝称之曰能,是以众议举宠为督。愚以为营中之事,悉以咨之,必能使行阵和睦,优劣得所。
二 2.宫中府中,俱为一体,陟罚臧否,不宜异同。若有作奸犯科及为忠善者,宜付有司论其刑赏,以昭陛下平明之理,不宜偏私,使内外异法也。
一 1.先帝创业未半而中道崩殂,今天下三分,益州疲弊,此诚危急存亡之秋也。然侍卫之臣不懈于内,忠志之士忘身于外者,盖追先帝之殊遇,欲报之于陛下也。诚宜开张圣听,以光先帝遗德,恢弘志士之气,不宜妄自菲薄,引喻失义,以塞忠谏之路也。
九 9.今当远离,临表涕零,不知所言。
六 6.臣本布衣,躬耕于南阳,苟全性命于乱世,不求闻达于诸侯。先帝不以臣卑鄙,猥自枉屈,三顾臣于草庐之中,咨臣以当世之事,由是感激,遂许先帝以驱驰。后值倾覆,受任于败军之际,奉命于危难之间,尔来二十有一年矣。
七 7.先帝知臣谨慎,故临崩寄臣以大事也。受命以来,夙夜忧叹,恐托付不效,以伤先帝之明,故五月渡泸,深入不毛。今南方已定,兵甲已足,当奖率三军,北定中原,庶竭驽钝,攘除奸凶,兴复汉室,还于旧都。此臣所以报先帝而忠陛下之职分也。至于斟酌损益,进尽忠言,则攸之、祎、允之任也。
五 5.亲贤臣,远小人,此先汉所以兴隆也;亲小人,远贤臣,此后汉所以倾颓也。先帝在时,每与臣论此事,未尝不叹息痛恨于桓、灵也。侍中、尚书、长史、参军,此悉贞良死节之臣,愿陛下亲之信之,则汉室之隆,可计日而待也。
try (
        //1、创建缓冲字符输入流管道与源文件接通
        BufferedReader br = new BufferedReader(new FileReader("src/出师表.txt"));
        //5、定义缓冲字符输出管道与目标文件接通
        BufferedWriter bw = new BufferedWriter(new FileWriter("src/新_出师表.txt"));
        ){

    //2、定义一个List集合存储每行内容
    List<String> data = new ArrayList<>();
    //3、定义循环,按照行读取文章
    String line;
    while ((line= br.readLine())!=null){
        data.add(line);
    }
    System.out.println(data);
    //4、排序
    //自定义排序规则
    List<String> sizes = new ArrayList<>();
    Collections.addAll(sizes,"一","二","三","四","五","六","七","八","九");
    Collections.sort(data, new Comparator<String>() {
        @Override
        public int compare(String o1, String o2) {
            //o1   八 8.
            //o2   七 7.
            return sizes.indexOf(o1.substring(0,o1.indexOf(" ")))-sizes.indexOf(o2.substring(0,o2.indexOf(" ")));
        }
    });
    System.out.println(data);

    //6、遍历集合中的每行文章写出去,且要换行
    for (String datum : data) {
        bw.write(datum);
        bw.newLine();//换行
    }
} catch (Exception e) {
    e.printStackTrace();
}

转换流

使用相同编码读取不同编码的文件内容

需求:

  • 分别使用如下两种方式读取文件内容

分析:

  • 代码编码是UTF-8,文件编码也是UTF-8,使用字符流读取观察输出的中文字符结果。
  • 代码编码是UTF-8,文件编码使用GBK,使用字符流读取观察输出的中文字符结果

字符输入转换流

在这里插入图片描述

  • 字符输入转换流: InputStreamReader,可以把原始的字节流按照指定编码转换成字符输入流。

在这里插入图片描述

//代码UTF-8  文件 GBK  "G:\\JavaSE\\img\\data.txt"
//1、提取GBK文件的原始字节流。
InputStream is = new FileInputStream("G:\\JavaSE\\img\\data.txt");
//2、把原始字节流转换成宁符输入流
//Reader isr = new InputStreamReader(is);//默认以UTF-8的方式转换成字符流。还是会乱码的﹑跟直接使用FileReader是一样的
Reader isr = new InputStreamReader(is,"GBK");//以指定的GBK编码转换成字符输入流  完美的解决了乱码问题

BufferedReader br = new BufferedReader(isr);
String line;
while ((line= br.readLine())!=null){
    System.out.println(line);
}

字符输出转换流

  • 字符输入转换流:OutputStreamWriter,可以把字节输出流按照指定编码转换成字符输出流。

在这里插入图片描述

//1、定义一个字节输出流
OutputStream os = new FileOutputStream("src/out3.txt");
//2、把原始的字节输出流转换成字符输出流
//Writer osw = new OutputStreamWriter(os);//以默认的UTF-8写字符出去跟直接写FileWriter一样
Writer osw = new OutputStreamWriter(os,"GBK");//指定GBK的方式写字符出去

//3、把低级的字符输出流包装成高级的缓冲字符输出流。
BufferedWriter bw = new BufferedWriter(osw);
bw.write("我爱中国1");
bw.write("我爱中国2");
bw.write("我爱中国3");

bw.close();

序列化对象

对象序列化

在这里插入图片描述

  • 作用:以内存为基准,把内存中的对象存储到磁盘文件中去,称为对象序列化
  • 使用到的流是对象字节输出流: ObjectOutputStream

在这里插入图片描述

/**
 * 对象如果要序列化,必须实现Serializable序列化接口
 */

//申明序列化的版本号码
//序列化的版本号与反序列化的版本号必须一致才不会出错!
 private static final long serialVersionUID=1;

private String name;
private String loginName;
//transient修饰的成员变量不参与序列化了
private transient String passWord;
private int age;

public Student() {
}

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

public String getName() {
    return name;
}

public void setName(String name) {
    this.name = name;
}

public String getLoginName() {
    return loginName;
}

public void setLoginName(String loginName) {
    this.loginName = loginName;
}

public String getPassWord() {
    return passWord;
}

public void setPassWord(String passWord) {
    this.passWord = passWord;
}

public int getAge() {
    return age;
}

public void setAge(int age) {
    this.age = age;
}

@Override
public String toString() {
    return "Student{" +
            "name='" + name + '\'' +
            ", loginName='" + loginName + '\'' +
            ", passWord='" + passWord + '\'' +
            ", age=" + age +
            '}';
}
//1、创建学生对象
Student s = new Student("潆勖","yingxu","123",20);
//2、对象序列化:使用对象字节输出流包装字节输出流管道
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("src/obj.txt"));
//3、直接调用序列化方法
oos.writeObject(s);
//4、释放资源
oos.close();
System.out.println("序列化完成");

对象反序列化

  • 使用到的流是对象字节输入流:ObjectlnputStream
  • 作用:以内存为基准,把存储到磁盘文件中去的对象数据恢复成内存中的对象,称为对象反序列化。

在这里插入图片描述

ObjectlnputStream序列化方法:

在这里插入图片描述

transient修饰的成员变量不参与序列化了

//1、创建对象字节输入流管道包装低级的字节输入流管道
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("src/obj.txt"));
//2、调用对象字节输入流的反序列化方法
Student s = (Student) ois.readObject();

System.out.println(s);

打印流

在这里插入图片描述

  • 作用:打印流可以实现方便、高效的打印数据到文件中去。打印流一般是指:PrintStream,PrintWriter两个类。
  • 可以实现打印什么数据就是什么数据,例如打印整数97写出去就是97,打印boolean的true,写出去就是true。

PrintStream、PrintWriter

PrintStream:

在这里插入图片描述

在这里插入图片描述

//1、创建一个打印流对象
PrintStream ps = new PrintStream(new FileOutputStream("src/ps.txt",true));//追加数据,在低级管道后面加True

ps.println(97);
ps.println('a');
ps.println(23.3);
ps.println("我是打印流输出");

ps.write('b');//只能输出字节数据

ps.close();

PrintWriter:

在这里插入图片描述

在这里插入图片描述

//1、创建一个打印流对象
PrintWriter pw= new PrintWriter(new FileWriter("src/pw.txt",true));//追加数据,在低级管道后面加True

pw.println(97);
pw.println('a');
pw.println(23.3);
pw.println("我是打印流输出");

pw.write("我是打印流输出,我可以输出字符数据");//可以输出字符串数据

pw.close();

PrintStream和PrintWriter的区别:

  • 打印数据功能上是一模一样的,都是使用方便,性能高效(核心优势)
  • PrintStream继承自字节输出流OutputStream,支持写字节数据的方法。
  • PrintWriter继承自字符输出流Writer,支持写字符数据出去。

输出语句的重定向

  • 属于打印流的一种应用,可以把输出语句的打印位置改到文件。
System.out.println("床前明月光");
System.out.println("疑是地上霜");

//改变输出语句的位置(重定向)
PrintStream ps = new PrintStream("src/log.txt");
System.setOut(ps);//把系统打印流改成我们自己的打印流

System.out.println("举头望明月");
System.out.println("低头思故乡");

补充知识:Properties

在这里插入图片描述

Properties属性集对象

  • 其实就是一个Map集合,但是我们一般不会当集合使用,因为HashMap更好用。

Properties核心作用:

  • Properties代表的是一个属性文件,可以把自己对象中的键值对信息存入到一个属性文件中去。
  • 属性文件:后缀是.properties结尾的文件,里面的内容都是key=value,后续做系统配置信息的

Properties的API:

  • Properties和IO流结合的方法:

在这里插入图片描述

//需求;使用Properties把键值对信息存入到属性文件中去。
Properties properties = new Properties();
properties.setProperty("admin","123");
properties.setProperty("yingxu","123");
properties.setProperty("xiangming","123");
properties.setProperty("xianghong","123");

/**
 *参数一:保存管道字符输出流管道
 * 参数二:保存心得,备注(只能用英文写)
 */
properties.store(new FileWriter("src/users.properties"),"remark!!");
//需求:Properties读取属性文件中的键值对信息。(读取)
Properties properties = new Properties();
System.out.println(properties);//{}

//加载属性文件中的键值对数据到属性对象properties中去
properties.load(new FileReader("src/users.properties"));

System.out.println(properties);//{admin=123, xianghong=123, yingxu=123, xiangming=123}
String rs = properties.getProperty("yingxu");
System.out.println(rs);//123

补充知识:IO框架

commons-io概述:

  • commons-io是apache开源基金组织提供的一组有关IO操作的类库,可以提高IO功能开发的效率。
  • commons-io工具包提供了很多有关io操作的类。有两个主要的类FileUtils, IOUtils
  • commons-io官网

导入commons-io-2.6.jar步骤:

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

FileUtils主要有如下方法:

在这里插入图片描述

//1.完成文件复制!
IOUtils.copy(new FileInputStream("G:\\JavaSE\\img\\1.jpg"),new FileOutputStream("G:\\JavaSE\\img\\2.jpg"));
//2.完成文件复制到某个文件夹下!
FileUtils.copyFileToDirectory(new File("G:\\JavaSE\\img\\1.jpg"),new File("G:\\JavaSE\\img\\tp"));
//3.完成文件夹复制到某个文件夹下!
FileUtils.copyDirectoryToDirectory(new File("G:\\JavaSE\\img"),new File("G:\\JavaSE\\img\\tp"));
//删除
FileUtils.deleteDirectory(new File("G:\\JavaSE\\img\\tp"));
//JDK1.7自己也做了一些一行代码完成复制的操作:New IO的技术
//Files.copy(Path.of("G:\\JavaSE\\img\\1.jpg"),Path.of("G:\\JavaSE\\img\\tp\\3.jpg"));
//删除(只能删除空文件夹)
//Files.deleteIfExists(Path.of("G:\\JavaSE\\img\\tp"));
//Files.delete(Path.of("G:\\JavaSE\\img\\tp"));
posted @   潆勖  阅读(33)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 25岁的心里话
· 闲置电脑爆改个人服务器(超详细) #公网映射 #Vmware虚拟网络编辑器
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· 零经验选手,Compose 一天开发一款小游戏!
· 一起来玩mcp_server_sqlite,让AI帮你做增删改查!!
点击右上角即可分享
微信分享提示