小黑子—Java从入门到入土过程:第九章-IO流

Java零基础入门9.0

Java系列第九章- IO流

1. 初识IO流

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

比如:

在玩游戏的时候,游戏进度的数据是保存在内存当中的
内存中的数据特点就是不能永久化的存储程序,程序停止,数据丢失
这时就需要添加一个存档功能,在存储的过程当中就是把数据保存到文件当中

想实现以上:

  1. 要知道文件在哪里
  2. 要知道如何进行数据传输,包括如何保存数据、如何读取数据
  3. IO流就是为此出现的,其与File文件息息相关

在这里插入图片描述

File类只能对文件本身进行操作,不能读写文件里面储存的数据

IO流做的事情:
IO流:用于读写文件中的数据(可以读写文件,或网络中的数据…)

  1. output:写出数据,把程序中的数据保存到本地文件当中
  2. input:读取数据,把本地数据加载到程序当中

IO流中,谁在读,谁在写?以谁为参照物看读写的方向呢?
以程序(内存)为参照物进行读写

IO流的分类:
在这里插入图片描述
纯文本文件:Windows自带的记事本打开能读懂的文件

小结:
在这里插入图片描述

2. IO流的体系

在这里插入图片描述

2.1 字节流

字节流子类:
在这里插入图片描述
字节流读取中文的时候会出现乱码,文件中不要有中文

2.1.1 FileOutputStream 字符串输出流

FileOutputStream

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

在这里插入图片描述

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

public class Main {
    public static void main(String[] args) throws IOException {
        //1.创建对象
        //写出 输出流 OutputStream
        //本地文件 File
        //异常抛出处理,检查一下当前文件下是否有a.txt
        FileOutputStream fos = new FileOutputStream("javaprogram1\\a.txt");
        //写出数据
        fos.write(97);
        //3.释放资源
        fos.close();
    }

}

在这里插入图片描述
图解:
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

FileOutputStream的原理

就是创建了一个程序与文件之间的通道
write就是将书写的内容通过通道传输到文件之中
close就是再将这个连接的通道“敲碎”

2.1.1 - I 字符串输出流的细节

1、创建字节输出流对象

  • 细节1:参数是字符串表示的路径或者是File对象都是可以的
  • 细节2:如果文件不存在会创建一个新的文件,但是要保证父级路径是存在的
  • 细节3:如果文件已经存在,则会清空文件

2、写数据

  • 细节:write方法的参数是整数,但是实际上写道本地文件中的是整数在ASCII上对应的字符
    比如:97 - a100 - d
    如果想要写入97,就57-"9",55 - "7"写两次
    在这里插入图片描述

3、释放资源

  • 每次使用完流之后都要释放资源,就是将文件从内存运行中停止,这样后续就能够继续操作文件了,不然是操作不了的
2.1.1 - II FileOutputStream写数据的3种方式

在这里插入图片描述
1.一次写一个字节数据
在这里插入图片描述
在这里插入图片描述

2.一次写一个字节数组的数据
在这里插入图片描述
原本a.txt文件存在,那么清空时就把文件内容删除掉,然后重新写入想要的内容,所以不会是覆盖效果
在这里插入图片描述

3.一次写一个数组的部分数据
在这里插入图片描述

关于wirte的第三种使用:

  • 参数一:数组
    参数二:起始索引
    参数三:个数
2.1.1 -III FileOutputStream写数据的两个小问题

写数据的两个小问题:想要换行写以及再次写不会清空而是继续写的续写

在这里插入图片描述
换行写:
再次写出一个换行符就可以了

  1. windows:\r\n, \r表示回车,\n表示换行
  2. Linux:\n
  3. Mac:\r

细节:

  • 在Windows操作系统当中,java对回车换行进行了优化,
    虽然完整的是\r\n,但是我们写其中一个\r或者\n就可以
    java也可以实现换行,因为java在底层会补全

建议:

不要省略,还是写全了

续写:
如果想要续写,打开续写开关即可
开关位置:创建对象的第二个参数
默认false:表示关闭续写,此时创建对象会清空文件
手动传递true:表示打开续写,此时创建对象不会清空文件

public class Main {
    public static void main(String[] args) throws IOException {
        //1.创建对象
        FileOutputStream fos = new FileOutputStream("javaprogram1\\a.txt",true);
        //写出数据
        String str = "fklwesnmfgklsdrniogjdlsdkg";
        byte[] bytes1 = str.getBytes();
        fos.write(bytes1);

        String wrap = "\r\n";//换行符
        byte[] bytes2 = wrap.getBytes();
        fos.write(bytes2);

        String str2 = "6666666666";
        byte[] bytes3 = str2.getBytes();
        fos.write(bytes3);
        //3.释放资源
        fos.close();

    }

}

在这里插入图片描述
小结:
在这里插入图片描述

2.1.2 FileInputStream 字符串输入流

在这里插入图片描述

public class Main {
    public static void main(String[] args) throws IOException {
        FileInputStream fis = new FileInputStream("javaprogram1\\a.txt");
        int b1 = fis.read();//只能读取一个字符,读不到了就返回-1
        System.out.println(b1);
        fis.close();

    }

}

在这里插入图片描述
如果还想读后面的数据,笨方法是一个一个写
在这里插入图片描述

2.1.2 - I FileInputStream 书写细节

1、创建字节输入流对象

细节:如果文件不存在,就直接报错

java为什么会这么设计呢?

  • 输出流:不存在,创建——把数据写到文件当中
  • 输入流:不存在,为什么不直接创建而是报错? 因为创建出来的文件是没有数据的,没有任何意义。

所以java就没有设计这种无意义的逻辑,文件不存在直接报错

程序中最重要的是:数据

2、读取数据

  • 细节1:一次读一个字节,读出来的是数据在ASCII上对应的数字
  • 细节2:读到文件末尾了,read方法返回-1
  • 细节3:如果后面是空格,空格对应的ASCII码是32
  • 细节4:如果后面是-1的,那么读取时先读-,再读1,不会把其作为一个整体
    在这里插入图片描述

3、释放资源

  • 细节:每一次使用完流必须要释放资源
2.1.2 - II FileInputStream 循环读取

在这里插入图片描述

    public static void main(String[] args) throws IOException {
        FileInputStream fis = new FileInputStream("javaprogram1\\a.txt");
        //循环读取
        int b;
        while((b=fis.read())!=-1){
            System.out.print((char)b);
        }
        
        fis.close();

    }

a.txt:
fsdejfkijsadrhgeasdklhgukjsadrhg

在这里插入图片描述
为什么要定义b,我把读取到的数据直接进行判断然后打印不行吗?

比如:

    public static void main(String[] args) throws IOException {
        FileInputStream fis = new FileInputStream("javaprogram1\\a.txt");
        //循环读取
        while ((fis.read()) != -1) {
            System.out.println(fis.read());
        }

        fis.close();

    }

a.txt:
abcde

在这里插入图片描述
read:表示读取数据,而且每读取一个数据就移动一次指针

  1. 当写了两个read的时候,在开始的时候读取到a进入到循环体当中,又调用了一次fis.read,所以第二次读的时候读到b,也就是98
  2. 再次进行循环,循环中又有一个read,第三次读取读到c,然后到循环体中第四次读取读到d,所以就是100
  3. 再循环,第五次读到e,到循环体中第六次读取,往后指针指向为空,所以返回-1

2.2 文件拷贝

在这里插入图片描述

    public static void main(String[] args) throws IOException {
        //读取文件
        FileInputStream fis = new FileInputStream("javaprogram1\\a.txt");
        //要拷贝到的文件
        FileOutputStream fos = new FileOutputStream("javaprogram1\\b.txt");

        //循环读取
        int b;
        long beginTime = System.currentTimeMillis();//开始时间
        while ((b = fis.read()) != -1) {
            fos.write(b);
        }
        //一个释放资源原则:先开的最后关闭
        fos.close();
        fis.close();
        //计算拷贝时间
        long endTime = System.currentTimeMillis();//结束时间
        System.out.println("拷贝文件花费了:" + (endTime - beginTime) + "毫秒");

    }

在这里插入图片描述

2.2.1 文件拷贝的弊端

在这里插入图片描述
为什么会慢呢?
在这里插入图片描述

2.2.2 FileInputStream 一次读取多个字节

在这里插入图片描述

   public static void main(String[] args) throws IOException {
        //读取文件
        FileInputStream fis = new FileInputStream("javaprogram1\\a.txt");
        //设计读取的数组
        byte[] bytes = new byte[2];
        //一次读取多个字节数据,具体读多少,跟数组长度有关
        //返回值:本次读取到了多少给字节数据
        int len1 = fis.read(bytes);
        System.out.println(len1);
        String str1 = new String(bytes);
        System.out.println(str1);

        int len2 = fis.read(bytes);
        System.out.println(len2);
        String str2 = new String(bytes);
        System.out.println(str2);

        int len3 = fis.read(bytes);
        System.out.println(len3);
        String str3 = new String(bytes);
        System.out.println(str3);//为什么是ed?

        int len4 = fis.read(bytes);
        System.out.println(len4);//-1
        String str4 = new String(bytes);
        System.out.println(str4);//为什么最后还是ed

        fis.close();
    }

在这里插入图片描述
图解:
第一次读取:

在这里插入图片描述
第二次读取:

  • 在第二次读取到cd的时候,会把原来ab的位置覆盖
    然后把数组里面的内容拿出来变成字符串,所以打印cd
    在这里插入图片描述

第三次读取:

  • 第三次读取的时候,数据只剩下e没有两个,所以只能读到一个e把c覆盖了,而d没有被覆盖
    读了一个数据,所以这次len为1
    因此,在读取的时候打印的就是e和残留的d
    在这里插入图片描述
    如果还要读取第四次,那么不管还是原来空参的read方法,还是现在带有数组的read方法,只要读不到数据方法就返回-1,还要打印的话里面数组数据没有被任何数据覆盖,所以打印的就是ed

解决方案:
字符串中的方法可以把字节数组的一部分变成字符串

    public static void main(String[] args) throws IOException {
        //读取文件
        FileInputStream fis = new FileInputStream("javaprogram1\\a.txt");
        //设计读取的数组
        byte[] bytes = new byte[2];
        //一次读取多个字节数据,具体读多少,跟数组长度有关
        //返回值:本次读取到了多少给字节数据
        int len1 = fis.read(bytes);
        System.out.println(len1);
        String str1 = new String(bytes,0,len1);//表示每次获取数组中从0索引开始,一共要把len1个元素变成字符串
        System.out.println(str1);

        int len2 = fis.read(bytes);
        System.out.println(len2);
        String str2 = new String(bytes,0,len2);
        System.out.println(str2);

        int len3 = fis.read(bytes);
        System.out.println(len3);
        String str3 = new String(bytes,0,len3);
        System.out.println(str3);

        fis.close();
    }

在这里插入图片描述

2.2.3 文件拷贝改写
    public static void main(String[] args) throws IOException {
        //读取文件
        FileInputStream fis = new FileInputStream("javaprogram1\\a.txt");
        FileOutputStream fos = new FileOutputStream("javaprogram1\\b.txt");
        //拷贝
        int len;
        long start = System.currentTimeMillis();
        byte[] bytes = new byte[1024];
        while((len=fis.read(bytes))!=-1){
            fos.write(bytes,0,len);
            System.out.println(len);
        }

        //释放资源
        fos.close();
        fis.close();
        long end = System.currentTimeMillis();
        System.out.println((end - start)+"毫秒");
    }

在这里插入图片描述

2.3 字符流

在这里插入图片描述

2.3.1 FileReader
2.3.1 - I 空参read

1、创建字符输入流对象
在这里插入图片描述
2、读取数据
在这里插入图片描述

3、释放资源
在这里插入图片描述
read() 细节:

  1. read():默认也是一个字节一个字节的读取的,如果遇到中文就会一次读取多个
  2. 在读取之后,方法的底层还会进行解码并转成十进制
    最终把这个十进制作为返回值
    这个十进制的数据也表示在字符集上的数字

英文:
文件里面的二进制数据比如 0110 0001
read方法进行读取,解码并转成十进制比如27721

想要看到中文汉字,就是把这些十进制数据,再进行强转就可以了

    public static void main(String[] args) throws IOException {
        //1.创建对象并关联本地文件
        FileReader fr = new FileReader("javaprogram1\\a.txt");
        //2.读取数据
        //字符流的底层也是字节流,默认也是一个字节,UTF-8一次读三给字节
        //如果遇到中文就会一次读取多个,GBK一次读两个字节,UTF-8一次读三个字节


        int ch;
        while((ch= fr.read())!=-1){
            System.out.print((char) ch);
        }
        //3.释放资源
        fr.close();
    }

2.3.1 - II 有参read
    public static void main(String[] args) throws IOException {
        //1.创建对象并关联本地文件
        FileReader fr = new FileReader("javaprogram1\\a.txt");
        char[] chars = new char[2];
        int len;
        //read(chars):读取数据,解码,强转三步合并了,把强转之后的字符放到数组当中
        //相当于空参的read+强转类型转换
        while ((len= fr.read(chars))!=-1){
            //把数组中的数据变成字符串再进行打印
            System.out.print(new String(chars,0,len));
        }
        fr.close();
    }

在这里插入图片描述

2.3.1- III 字符输入流原理解析

创建输入流对象,底层会创建长度为:8192的字节数组——缓冲区
在这里插入图片描述

在这里插入图片描述

1、在内存中定义未赋值的变量ch

2、第一次读取,判断数据是否可以被读取;如果没有,从文件中读取数据,尽可能装满缓冲区,每次都从缓冲区中读取数据提高效率

3、第一次从缓冲区中读,读的是第一个字节,按照UTF-8的形式进行解码并且转成十进制,再赋值给变量ch,所以其记录的就是97,然后强制打印a

4、第二次读取中,发现剩下的3个字节是中文的,所以一次性会读取3个字节按照UTF-8的形式进行解码,转成十进制25105,再赋值给ch,然后强转成字符再进行打印

5、第三次读取中,发现在内存当中已经没有要读的东西了,然后从文件中读取。但是,文件中也没有剩余的数据了,那么就返回-1,最后把-1赋值给ch

在这里插入图片描述

    public static void main(String[] args) throws IOException {
        FileReader fr = new FileReader("javaprogram1\\a.txt");
        fr.read();//把文件中的数据放到缓冲区当中

        //然后被FileWriter清空了文件
        FileWriter fw = new FileWriter("javaprogram1\\b.txt");
        //如果再次使用fr进行读取,会读取到数据吗?
        //会把缓冲区中的数据全部读取完毕,但是只能读取缓冲区中的数据,文件中剩余数据无法再次读取
        //因为已经被清空掉了
        int ch;
        while((ch=fr.read())!=-1){
            System.out.print((char)ch);
        }
        fw.close();
        fr.close();
    }

在这里插入图片描述

2.3.2 FileWriter

FileWriter构造方法:
在这里插入图片描述
FileWriter成员方法:
在这里插入图片描述
FileWriter书写细节:

在这里插入图片描述

    public static void main(String[] args) throws IOException {
        FileWriter fw = new FileWriter("javaprogram1\\a.txt");
        //写出一个字符
        fw.write(25105);//我

        //写出一个字符串
        fw.write("我是煞笔");//我是煞笔

        //写出一个字符串数组
        char[] chars = {'a','b','c','我'};
        fw.write(chars);//abc我

        //写出字符数组的一部分
        fw.write(chars,0,2);//ab
        fw.close();

    }
2.3.2 - I 字符输入流原理解析

在这里插入图片描述
在写出的时候,它会把所有的数据按照UTF-8进行编码,一个中文变成3个字节,一个英文变成1个字节

跟字节流是不一样的:
字节流是没有缓冲区,是直接写到文件当中的目的地
字符流是有缓冲区的

  • 那么数据是什么时候才能真正地保存到目的地呢?
    在这里插入图片描述

flush和close方法
在这里插入图片描述
区别:

  1. 如果用flush,那么下面还可以继续写数据
  2. 如果用close,那么就直接关流,下面写数据就报错

在这里插入图片描述
当数据超过缓冲区的存储时,比如8193,缓冲区放不下,那么此时缓冲区装不下,其数据就会自动地保存到文件当中

在这里插入图片描述
flush方法相当于把已经放到缓冲区的数据,刷新到本地文件,所以当程序运行完毕之后,上面的数据在文件当中就已经有了,而下面的数据还在缓冲区当中
在这里插入图片描述
如果下面进行关流,那么它在断开链接之前,首先会检查一下缓冲区里面有没有数据。如果有,就会把剩余的所有数据都刷新到本地当中

2.4 字节流和字符流综合练习

在这里插入图片描述

2.4.1 练习一:拷贝文件夹

在这里插入图片描述

public class Main {
    public static void main(String[] args) throws IOException {
        //1.创建数据源
        File src = new File("F:\\aaa\\ccc");
        //2.创建对象表示目的地
        File dest =new File("F:\\aaa\\fff");
        //3.调用方法开始拷贝
        copydir(src,dest);
    }

    public static void copydir(File src,File dest) throws IOException {
        dest.mkdirs();//如果dest文件夹不存在那么自动创建
        //1.进入数据源
        File[] files = src.listFiles();
        //2.遍历数组
        if(files!=null){
            for (File file : files) {
                if(file.isFile()){
                    //3.判断为文件,就用字节流拷贝
                    //拷贝的时候一定是从文件开始,从文件结束
                    FileInputStream fis = new FileInputStream(file);//假设a.txt
                    //所以拷贝的时候要拷贝到dest文件夹里面
                    FileOutputStream fos = new FileOutputStream(new File(dest,file.getName()));//将其拷贝到dest当作,这个文件也叫了a.txt
                    //利用字节数组进行拷贝,速度快点
                    byte[] bytes = new byte[1024];
                    int len;
                    while((len=fis.read())!=-1){
                        fos.write(bytes,0,len);
                    }
                    fos.close();
                    fis.close();
                    System.out.println("拷贝成功");
                }else {
                    //4.为文件夹,就递归
                    //第二个参数表示要拷贝文件夹的里面
                    copydir(file,new File(dest,file.getName()));
                }

            }
        }

    }
}

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

2.4.2 练习二:加密和解密文件夹

在这里插入图片描述
了解^:异或,在二进制下两给相同就false,两个不同就true
在这里插入图片描述
加密:

   public static void main(String[] args) throws IOException {
        //1.创建对象关联原始文件
        FileInputStream fis = new FileInputStream("javaprogram1\\a.txt");
        //2.创建对象关联加密文件
        FileOutputStream fos = new FileOutputStream("javaprogram1\\b.txt");
        //3.加密处理
        int b;
        while((b=fis.read())!=-1){
            fos.write(b^2);//数字任意定
        }
        //4.释放资源
        fos.close();
        fis.close();
    }

在这里插入图片描述

2.4.3 练习三:修改文件夹中的数据

在这里插入图片描述
1.

public class Main {
    public static void main(String[] args) throws IOException {
        //1.读取数据
        //在读取的时候我需要的是那个整体,而不是直接索引打印,这样的话只能看而不能用
        //所以要把其拼接到StringBuilder当中
        FileReader fr = new FileReader("javaprogram1\\a.txt");
        StringBuilder sb = new StringBuilder();
        int b;
        while ((b = fr.read()) != -1) {
            sb.append((char) b);
        }
        fr.close();
        System.out.println(sb);

        //2.排序
        //那就先把StringBuilder里面的数据先变成字符串之后才能调用split方法按照 - 进行切割
        String sbStr = sb.toString();
        String[] arrStr = sbStr.split("-");
        //然后想要把字符串变成int类型的,就循环一遍用Integer.parseInt,然后把转换的数存到数组当中
        ArrayList<Integer> list = new ArrayList<>();
        for (String s : arrStr) {
            int i = Integer.parseInt(s);
            list.add(i);
        }
        Collections.sort(list);
        System.out.println(list);

        //3.写出
        FileWriter fw = new FileWriter("javaprogram1\\b.txt");
        for (int i = 0; i < list.size(); i++) {
            if (i == list.size() - 1) {
                fw.write(list.get(i) + "");
            } else {
                fw.write(list.get(i) + "-");
            }

        }
        fw.close();
    }

}

在这里插入图片描述
2.简化版

public class Main {
    public static void main(String[] args) throws IOException {
        //1.读取数据
        //在读取的时候我需要的是那个整体,而不是直接索引打印,这样的话只能看而不能用
        //所以要把其拼接到StringBuilder当中
        FileReader fr = new FileReader("javaprogram1\\a.txt");
        StringBuilder sb = new StringBuilder();
        int b;
        while ((b = fr.read()) != -1) {
            sb.append((char) b);
        }
        fr.close();
        System.out.println(sb);

        //2.排序
        Integer[] arr = Arrays.stream(sb.toString()
                .split("-"))
                .map(Integer::parseInt)//方法引用,把字符串转换为整数
                .sorted()//默认升序排序
                .toArray(Integer[]::new);//排完后收集起来,收集到int类型的数组当中
        System.out.println(Arrays.toString(arr));

        //3.写出
        FileWriter fw =new FileWriter("javaprogram1\\b.txt");
        //那么用上面去把数组里面的 ,变成 - 呢?
        //用repalce方法
        String s = Arrays.toString(arr).replace(",", "-");
        //根据数组逗号位置,从索引1开始截取,到数组最后一个位置停止
        String result = s.substring(1, s.length() - 1);
        System.out.println(result);

        fw.write(result);
        fw.close();
    }

}

3. IO流中不同JDK版本捕获异常的方式

在这里插入图片描述

老版本try…catch下面还有个finally

特点:finally里面的代码一定被执行,除非虚拟机停止(JVM退出),什么意思?

如果在try当中写了一个system.exit(0)或者因为其他原因导致虚拟机都停止了,那么finally里面的代码是执行不到的
在这里插入图片描述
所以就非常适合将释放资源等扫尾代码放到finally之中

麻烦代码:

public class Main {
    public static void main(String[] args) {
        //ctrl + alt + t 快捷迅速环绕
        FileInputStream fis = null;
        FileOutputStream fos = null;
        try {
            //读取文件
             fis = new FileInputStream("javaprogram1\\a.txt");
             fos = new FileOutputStream("javaprogram1\\b.txt");
            //拷贝
            int len;
            byte[] bytes = new byte[1024];
            while ((len = fis.read(bytes)) != -1) {
                fos.write(bytes, 0, len);
                System.out.println(len);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            //释放资源
            
            //fos fis只能在try里面的局部变量中使用
            //一般来说可以把其放到大括号外面,但这次不能把整个创建对象都放到外面去
            //因为这个创建对象的代码它是有编译时异常的,但如果放到外面没有初始化那也会报错
            //所以放到外面只定义空值,而不会去创建其对象

            //之后报错的就是close方法,还要对其进行异常处理
            //再嵌套try……catch方法即可

            //可如果读入文件的时候没有当前路径,fis记录的值就还是null
            //在finall用null去调用方法,就肯定会报空指针异常
            //因此还要写一个非空判断
            if(fos != null){
                try {
                    fos.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }

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

简化代码的接口:
在JDK7的时候java推出了一个简单的接口AutoCloseable
在这里插入图片描述

不同JDK下的书写方式:
JDK7的方法了解就行,JDK7注意:

不能把所有创建对象的代码都写在小括号当中,只有实现了AutoCloseable的类才能在小括号当中创建对象

在这里插入图片描述

JDK7写法:

    public static void main(String[] args) {
        try (
                FileInputStream fis = new FileInputStream("javaprogram1\\a.txt");
                FileOutputStream fos = new FileOutputStream("javaprogram1\\b.txt")
        )
        {
            //拷贝
            int len;
            byte[] bytes = new byte[1024];
            while((len=fis.read(bytes))!=-1){
                fos.write(bytes,0,len);
                System.out.println(len);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        //释放资源的代码直接不要
    }

JDK9写法:

public static void main(String[] args) throws FileNotFoundException {
        //在外面进行抛出处理
        FileInputStream fis = new FileInputStream("javaprogram1\\a.txt");
        FileOutputStream fos = new FileOutputStream("javaprogram1\\b.txt");
        try (fis ; fos)
        {
            //拷贝
            int len;
            byte[] bytes = new byte[1024];
            while((len=fis.read(bytes))!=-1){
                fos.write(bytes,0,len);
                System.out.println(len);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        //释放资源的代码直接不要
    }

4.字符集

4.1 计算机储存规则

在这里插入图片描述
在这里插入图片描述

4.2 ASCII 字符集

首先是熟悉的ASCII表,就是一个字符集,也叫做编码表
128个数据(对于西方的sucker来说足够使用了)
在这里插入图片描述
所以计算机在存储英文的时候一个字节就足以

ASCII编码规则:前面补0,补齐8位
解码直接转,前面补不补零无所谓
在这里插入图片描述
如果是汉字该怎么办?
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

4.3 GBK 字符集

英文用一个字节存储,完全兼容ASCII
GBK英文编码规则:不足8位,前面补0
在这里插入图片描述
汉字:两个字节存储
前面的字节叫做高位字节
后面的字节叫做低位字节
高位字节二进制一定以1开头,转成十进制之后是一个负数
汉字编码规则:不需要变动
在这里插入图片描述
在这里插入图片描述

英文与中文的比较:
在这里插入图片描述
在这里插入图片描述
小结:
在这里插入图片描述

4.4 字符集详解

在这里插入图片描述
在这里插入图片描述
本来可以用1个字节表示的,却硬要用2个字节,导致空间过于浪费

所以后面又出了UTF-8编码规则:

在这里插入图片描述
比如英文UTF-8编码时:
在这里插入图片描述
中文UTF-8编码时:
在这里插入图片描述
练习:

UTF-8是一个字符集吗?
不是,UTF-8只是字符集中的一个编码方式

在这里插入图片描述
小结:
在这里插入图片描述

4.5 乱码

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

在这里插入图片描述
2.
在这里插入图片描述
如何不产生乱码?
在这里插入图片描述
在这里插入图片描述

在拷贝的时候,数据没有丢失,在用记事本打开的时候用的字符集和编码表,同数据源是一样的,那是不会出现乱码的

4.6 编码和解码的方法

在这里插入图片描述

    public static void main(String[] args) throws FileNotFoundException, UnsupportedEncodingException {
        //1.编码
        String str = "ai你哟";
        byte[] bytes1 = str.getBytes();//-28-67-96表示你  -27-109-97表示哟
        System.out.println(Arrays.toString(bytes1));

        //这个GBK方式有编译时异常,抛出即可
        byte[] bytes2 = str.getBytes("GBK");
        System.out.println(Arrays.toString(bytes2));//-60 -29组成 你  -45 -76组成哟

        //2.解码
        String s1 = new String(bytes1);
        System.out.println(s1);

        String s2 = new String(bytes1, "GBK");
        System.out.println(s2);//当原本用UTF-8的编码却用GBK解码,结果就会出现乱码
    }

在这里插入图片描述

5.IO流体系高级流

在这里插入图片描述
在这里插入图片描述

5.1 缓冲流

缓冲流一共有四种
在这里插入图片描述

5.1.1 字节缓存流

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
第一个构造是关于字节输入流,在它里面会默认有一个8192长度的缓冲区
第二个是除了传递一个字节输入流以为,还能手动设定缓冲区的大小

小练习:
在这里插入图片描述
1.一次操作一个字节

    public static void main(String[] args) throws IOException {
        BufferedInputStream bis = new BufferedInputStream(new FileInputStream("javaprogram\\a.txt"));
        BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("javaprogram\\b.txt"));

        int b;
        while((b=bis.read())!=-1){
            bos.write(b);
        }
        //释放资源
        //为什么只需要关闭缓冲流,而基本流不用关闭了?
        //源码在关闭缓冲流的时候会自动关闭基本流
        bos.close();
        bis.close();
    }

2.一次读写多个字节

    public static void main(String[] args) throws IOException {
        BufferedInputStream bis = new BufferedInputStream(new FileInputStream("javaprogram\\a.txt"));
        BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("javaprogram\\b.txt"));

        byte[] arr= new byte[3];
        int len;
        while((len=bis.read(arr))!=-1){
            bos.write(arr,0,len);
        }
        //释放资源
        //为什么只需要关闭缓冲流,而基本流不用关闭了?
        //源码在关闭缓冲流的时候会自动关闭基本流
        bos.close();
        bis.close();
    }
5.1.1 - I 字节缓存流提高效率原理

在这里插入图片描述
真正把数据写到文件的还是基本流,它会把缓冲区中的数据写道本地文件当中

细节:缓存输入流的缓冲区与缓冲输出流的不是一同个东西

在这里插入图片描述
中间的变量b就是充当一个倒手,在左右这两个缓冲区之间进行来回的倒腾数据,这一段都是在内存当中进行的,运行速度很快
所以倒手的时间可以忽略不记,其真正节约的是读和写时,跟硬盘打交道的时间

在这里插入图片描述
数组的倒手,运行更快

5.1.2 字符缓存流

在这里插入图片描述
字符缓冲流的构造方法:
在这里插入图片描述
字符缓冲流的特有方法:
在这里插入图片描述
方法会自动判断你是上面操作系统

1.单行输入

    public static void main(String[] args) throws IOException {
        //1.创建字符缓冲流的对象
        BufferedReader br = new BufferedReader(new FileReader("javaprogram1\\a.txt"));
        //2.读取数据
        //细节
        //readLine方法在读取的时候,一次读一整行,遇到回车换行结束
        //         但是不会把回车换行读到内存当中
        String lien1 = br.readLine();
        System.out.println(lien1);

        String line2 = br.readLine();
        System.out.println(line2);
        br.close();
        
    }

在这里插入图片描述
2.全部输入

    public static void main(String[] args) throws IOException {
        //1.创建字符缓冲流的对象
        BufferedReader br = new BufferedReader(new FileReader("javaprogram1\\a.txt"));

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

    }

在这里插入图片描述
3.字符缓冲输出

    public static void main(String[] args) throws IOException {
        //1.创建字符缓冲流的对象
        BufferedWriter bw = new BufferedWriter(new FileWriter("javaprogram1\\b.txt"));
        //2.写出
        bw.write("阿斯蒂芬和");
        //换行 br.wirte("\r\n")这个方法不够跨平台
        bw.newLine();//这个方法是BufferedWriter这个类独有的换行
        bw.close();
        
    }

在这里插入图片描述
小结:
在这里插入图片描述

5.1.3 综合练习
5.1.3 - I 练习一:拷贝文件

在这里插入图片描述
方法2和4都是比较快的
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

5.1.3 - II 练习二:拷贝文件

在这里插入图片描述
1.ArrayList排序

  public static void main(String[] args) throws IOException {
        BufferedReader br = new BufferedReader(new FileReader("javaprogram1\\a.txt"));
        String line;
        ArrayList<String> list = new ArrayList<>();
        while((line=br.readLine())!=null){
            list.add(line);
        }
        br.close();

        //2.排序
        //排序规则:按照每一行前面的序号进行排序
        Collections.sort(list, new Comparator<String>() {
            @Override
            public int compare(String o1, String o2) {
                //获取o1 o2
                int i1 = Integer.parseInt(o1.split("\\.")[0]);
                int i2 = Integer.parseInt(o2.split("\\.")[0]);
                return i1-i2;//1 2 3 4 5 6..
            }
        });

        //写出
        BufferedWriter bw = new BufferedWriter(new FileWriter("javaprogram1\\b.txt"));
        for (String s : list) {
            bw.write(s);
            bw.newLine();
        }
        bw.close();
    }

在这里插入图片描述
2.TreeMap排序

    public static void main(String[] args) throws IOException {
        BufferedReader br = new BufferedReader(new FileReader("javaprogram1\\a.txt"));
        String line;
        //2.排序
        TreeMap<Integer,String> tm = new TreeMap<>();
        while((line=br.readLine())!=null){
            String[] arr = line.split("\\.");
            //0索引时 表序号  1索引时  表内容
            tm.put(Integer.parseInt(arr[0]),arr[1]);//要是想看到序号arr[1]变成line就行
        }
        System.out.println(tm);
        br.close();
        //写出
        BufferedWriter bw = new BufferedWriter(new FileWriter("javaprogram1\\b.txt"));
        //然后获取treemap的每个键值对对象entrySet,然后遍历
        Set<Map.Entry<Integer, String>> entries = tm.entrySet();
        for (Map.Entry<Integer, String> entry : entries) {
            //entry表示每个键值对,获取其值
            String value = entry.getValue();
            bw.write(value);
            bw.newLine();
        }
        bw.close();
    }

在这里插入图片描述

5.1.3 - III 练习三:软件运行次数

IO流原则:

什么时候用就什么时候创建
什么时候不用再什么时候关闭

在这里插入图片描述

    public static void main(String[] args) throws IOException {
        BufferedReader br = new BufferedReader(new FileReader("javaprogram1\\a.txt"));
        String line = br.readLine();//0 字符串
        int count = Integer.parseInt(line);
        //表示当前软件又运行了一次
        count++;//1
        //2.判断
        if(count<=3){
            System.out.println("欢迎使用本软件,第"+count+"次免费使用");
        }else{
            System.out.println("本软件只能免费使用3次");
        }
        //3.把当前自增之后的count写出到文件当中,这样就可以保证每次重新调用的时候count不被刷新
        BufferedWriter bw = new BufferedWriter(new FileWriter("javaprogram1\\a.txt"));
        bw.write(count+"");//后面加""的目的是让其变为字符串
        bw.close();
    }

在这里插入图片描述

5.2 转换流

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

在这里插入图片描述
当创建了转换流对象的时候,需要包装一个字节输入流,再包装之后这个字节流它就变成了字符流,就拥有了字符流的特性:

  1. 读取数据不会乱码
  2. 可以根据字符集一次读取多个字节

所以转换流的输入流也叫InputStreamReader
前面的InputStream表示可以把字节流转换成字符流,后面的Reader表示转化流本身是字符流的一员,爹是Reader

同理,转化流的输出流叫OutputStreamReader
在这里插入图片描述
idea默认的字符编码是UTF-8
文件另存的文件编码ANSI就是GBK
在这里插入图片描述

  1. 第一个参数的是需要关联一个字节输入流,使用平台默认的字符编码
  2. 第二个参数除了关联字节输入流以外,还需要指定char set name字符编码,可以小写也可以大写(专业点)
  3. 下面两个同理一样的,一般都是用上面两个
5.2.1 基本练习
5.2.1 - I 练习1:手动创建GBK文件把中文读取到内存当中

在这里插入图片描述

    public static void main(String[] args) throws IOException {
        InputStreamReader isr = new InputStreamReader(new FileInputStream("javaprogram1\\gbkfile.txt"),"GBK");
        int ch;
        while((ch=isr.read())!=-1){
            System.out.print((char)ch);
        }
        isr.close();
        System.out.println("------------------------------");

        FileReader fr = new FileReader("javaprogram1\\gbkfile.txt", Charset.forName("GBK"));
        int b;
        while((b=fr.read())!=-1){
            System.out.print((char)b);
        }
        fr.close();
    }

在这里插入图片描述

基本流里指定用来表示字符编码的,使用Charset.forName
在这里插入图片描述

5.2.2 - II 把一段中文按照GBK的形式写到本地文件
  • 需求2
    public static void main(String[] args) throws IOException {
        OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("javaprogram1\\gbkfile.txt"),"GBK");
        osw.write("李在干神魔");
        osw.close();
        //上面这个方法了解一下就行了

        FileWriter fw = new FileWriter("javaprogram1\\gbkfile.txt");
        fw.write("李在干神魔");
        fw.close();
    }

在这里插入图片描述

5.2.3 - III 将本地文件的GBK转换为UTF-8
  • 需求3
    JDK11以前的方案
    public static void main(String[] args) throws IOException {
        InputStreamReader isr = new InputStreamReader(new FileInputStream("javaprogram1\\gbkfile.txt"),"GBK");
        OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("javaprogram1\\a.txt"),"UTF-8");

        int b;
        while((b= isr.read())!=-1){
            osw.write(b);
        }
        osw.close();
        isr.close();
    }

替代方案:
在这里插入图片描述

5.2.4 - IIIV

在这里插入图片描述

    public static void main(String[] args) throws IOException {
        FileInputStream fis = new FileInputStream("javaprogram1\\a.txt");
        //用fis读取肯定会有乱码,所以要把字节流变成字符流
        InputStreamReader isr = new InputStreamReader(fis);
        //要读一整行InputStreamReader搞定不了,缓冲流才行
        BufferedReader br = new BufferedReader(isr);
        String line;

        while ((line=br.readLine())!=null){
            System.out.println(line);
        }
        br.close();
    }

在这里插入图片描述
总结:
在这里插入图片描述

5.3 序列流(对象操作输出流)

5.3.1 序列化流(对象操作输出流)

在这里插入图片描述
区别于直接写对象进文件当中,序列化流写进的数据我们看不懂,要通过反序列化流把数据在这里插入图片描述
在这里插入图片描述

Serializable接口里面是没有抽象方法,这被称为标记型接口。可以理解成:一个物品的合格证

一旦实现了这个接口,那么就表示当前的类就可以被序列化

    public static void main(String[] args) throws IOException {
        Student stu = new Student("magua",24);

        //创建序列化流的对象/对象操作输出流
        ObjectOutputStream oos =new ObjectOutputStream(new FileOutputStream("javaprogram1\\a.txt"));

        //写出数据
        oos.writeObject(stu);
        oos.close();
    }

在这里插入图片描述

5.3.2 反序列化流(对象操作输出流)

在这里插入图片描述

    public static void main(String[] args) throws IOException, ClassNotFoundException {
        ObjectInputStream ois = new ObjectInputStream(new FileInputStream("javaprogram1\\a.txt"));

        Object o = ois.readObject();

        System.out.println(o);

        ois.close();
    }

在这里插入图片描述

如果说一个类实现了一个接口,表明这个类就是可序列化的,那么Java底层会根据其成员变量、静态变量、构造方法、成员方法……计算出里面的序列号,也就是版本号
在这里插入图片描述
在这里插入图片描述
但是此时如果修改了里面javabean里面的代码,就会重新计算其版本号。当用反序列化流读取版本号1到内存时,两个版本号不一样就直接报错

在这里插入图片描述
处理方案:

固定版本号就行了
在这里插入图片描述
static:表示这类的所有对象都共享同一个版本号
final:最终表示版本号不会发生变化
long:版本号的数字比较长,所以不要用int来说
serialLVersionUID:统一的变量名ID

idea系统设置快速设置serialLVersionUID
在这里插入图片描述
设置完成之后,alt+回车 自动生成
在这里插入图片描述

5.3.3 序列化流和反序列化流的细节

对上面反序化流
1.

Student类:
public class Student implements Serializable {


    private static final long serialVersionUID = 650157805827481085L;
    private String name;
    private int age;
    private String address;

    public Student() {
    }

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

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

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

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

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

//toString重写
    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", address='" + address + '\'' +
                '}';
    }
}

在这里插入图片描述
2.如果不想某个进行序列化流本地文件,比如address对其保密,那么重信生成JavaBean即可,然后把自动生成的serialVersionUID删掉,在不想序列化流的地方加transient

  • transient:瞬态关键字
    作用:不会把当前属性序列化到本地文件当中
Student类:
public class Student implements Serializable {

 @Serial
    private static final long serialVersionUID = 650157805827481085L;
    private String name;
    private int age;
    private transient String address;


    public Student() {
    }

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

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

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

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

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

    /**
     * 获取
     * @return address
     */
    public String getAddress() {
        return address;
    }

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

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

序列化测试:
    public static void main(String[] args) throws IOException {
        Student stu = new Student("magua",25,"guangdoor");
        ObjectOutputStream oos =new ObjectOutputStream(new FileOutputStream("javaprogram1\\a.txt"));
        oos.writeObject(stu);
        oos.close();
    }

反序列化测试:
    public static void main(String[] args) throws IOException, ClassNotFoundException {
        ObjectInputStream ois = new ObjectInputStream(new FileInputStream("javaprogram1\\a.txt"));

        Student o = (Student) ois.readObject();
        System.out.println(o);
        ois.close();
    }

在这里插入图片描述
可见地址无法被序列查询

小结:

在这里插入图片描述

5.3.4 序列化流和反序列化流的综合练习:用对象流读写多个对象

在这里插入图片描述

Stuend类同上,不用transient
序列化测试类:
    public static void main(String[] args) throws IOException {
        Student stu1 = new Student("magua",25,"guangdoor");
        Student stu2 = new Student("zhangsan",26,"xinrimuli");
        Student stu3 = new Student("lisi",27,"wuhu");
        ObjectOutputStream oos =new ObjectOutputStream(new FileOutputStream("javaprogram1\\a.txt"));
        oos.writeObject(stu1);
        oos.writeObject(stu2);
        oos.writeObject(stu3);
        oos.close();
    }

反序列化测试类:
    public static void main(String[] args) throws IOException, ClassNotFoundException {
        ObjectInputStream ois = new ObjectInputStream(new FileInputStream("javaprogram1\\a.txt"));

        Student o1 = (Student) ois.readObject();
        Student o2 = (Student) ois.readObject();
        Student o3 = (Student) ois.readObject();
        System.out.println(o1);
        System.out.println(o2);
        System.out.println(o3);
        ois.close();
    }

在这里插入图片描述
但是这样的代码真的好吗?
假设序列化对象是别人创建的,序列化了多少个忘记了,就只能到序列化的文件看,但是这是看不懂的。当进行反序列化的时候,就不知道序列化多少个了,总不能一直读读到出异常
在这里插入图片描述
解决方案:
如果要把多个对象序列化到本地文件当中,就一般都会把这些所有对象放到集合里面,再序列化集合就行了

序列化测试:
    public static void main(String[] args) throws IOException {
        Student stu1 = new Student("magua",25,"guangdoor");
        Student stu2 = new Student("zhangsan",26,"xinrimuli");
        Student stu3 = new Student("lisi",27,"wuhu");

        ArrayList<Student> list =new ArrayList<>();
        list.add(stu1);
        list.add(stu2);
        list.add(stu3);
        ObjectOutputStream oos =new ObjectOutputStream(new FileOutputStream("javaprogram1\\a.txt"));

        oos.writeObject(list);

        oos.close();
    }

反序列化测试:
    public static void main(String[] args) throws IOException, ClassNotFoundException {
        ObjectInputStream ois = new ObjectInputStream(new FileInputStream("javaprogram1\\a.txt"));

        ArrayList<Student> list = (ArrayList<Student>)ois.readObject();//强转ois读取的对象为ArrayList<Student>
        for (Student student : list) {
            System.out.println(student);
        }
        ois.close();
    }

在这里插入图片描述

5.4 打印流

在这里插入图片描述
在这里插入图片描述

5.4.1 字节打印流

构成方法:
在这里插入图片描述
成员方法:
在这里插入图片描述
细说参数:
在这里插入图片描述

  1. 第一个构造我们可以传递字节输出流
  2. 第二个构造除了可传递字节输出流以外,还有boolean类型的变量autoFlush,自动刷新
  3. 第三个构造,有3个参数,第1参数字节输出流的基本流,第2参数自动刷新,第3参数字符串形式的encoding(字符编码)
  4. 第四个构造,与第三个构造类似,不过后面的字符编码是Charset类型的,不能写成字符串
  5. 第五个构造,直接关联文件的路径(字符串类型的)
  6. 下方构造是关联文件路径的,同理一、二、三、四,只不过有个字符编码类型是csn
    public static void main(String[] args) throws IOException, ClassNotFoundException {
        PrintStream ps = new PrintStream(new FileOutputStream("javaprogram1\\a.txt"),true,"UTF-8");
        ps.println(97);//写出+自动刷新+自动换行
        ps.print(true);
        ps.printf("%s李在赣神魔%s","akm","阿克曼");
        ps.close();
    }

在这里插入图片描述

还有,System.out.println就用到了打印流

  1. System是javabean里面已经定义好的一个类
    在这里插入图片描述
  2. out是在System里面的一个静态变量,System.out相当于获取的就是一个打印流的对象,不需要自己来创建,虚拟机会创建。其默认指向控制台
    在这里插入图片描述
    public static void main(String[] args) throws IOException {
        //获取打印流的对象,此打印流在虚拟机启动的时候,有虚拟机创建,默认指向控制台
        //特殊的打印流,系统中的标准输出流,不能关闭,在系统中是唯一的
        PrintStream ps = System.out;

        //调用打印流中的方法println
        //写出数据,自动换行,自动刷新
        ps.println("123");
        ps.close();
        //当流关闭了,再也无法打印
        ps.println("aslkifdhas");
        System.out.println("12314");
    }

在这里插入图片描述

5.4.2 字符打印流

在这里插入图片描述

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

构造方法:与字节打印流差不多
在这里插入图片描述
成员方法:基本上与字节打印流一模一样
在这里插入图片描述
代码展示:

    public static void main(String[] args) throws IOException, ClassNotFoundException {
        PrintWriter pw = new PrintWriter(new FileWriter("javaprogram1\\a.txt"),true);
        pw.println("阿克曼,李在赣神魔");
        pw.print("曼?不想look可以blue");
        pw.close();
    }

在这里插入图片描述
小结:
在这里插入图片描述

5.5 解压缩流

在这里插入图片描述
要想解压,电脑当中首先要有一个压缩包,那么这个压缩包要是zip作为后缀的,不能是其他

在这里插入图片描述
在这里插入图片描述
1.

public class Main {
    public static void main(String[] args) throws IOException {
        //1.创建一个File表示要解压的压缩包
        File src = new File("F:aaa.zip");
        //2.创建一个File表示解压的目的地
        File dest = new File("F:\\");
        //调用方法
        unzip(src,dest);
    }
    public static void unzip(File src , File dest) throws IOException {
        //解压的本质:把压缩包里面的每一个文件或者文件夹读取出来,按照层级拷贝到目的地当中
        //创建一个解压缩流用来读取压缩包中的数据
        ZipInputStream zip = new ZipInputStream(new FileInputStream(src));
        //要先获取到压缩包里面的每一个zipentry对象
        ZipEntry entry = zip.getNextEntry();
        System.out.println(entry);
    }

}

在这里插入图片描述
2.

public class Main {
    public static void main(String[] args) throws IOException {
        //1.创建一个File表示要解压的压缩包
        File src = new File("F:aaa.zip");
        //2.创建一个File表示解压的目的地
        File dest = new File("F:\\");
        //调用方法
        unzip(src,dest);
    }
    public static void unzip(File src , File dest) throws IOException {
        //解压的本质:把压缩包里面的每一个文件或者文件夹读取出来,按照层级拷贝到目的地当中
        //创建一个解压缩流用来读取压缩包中的数据
        ZipInputStream zip = new ZipInputStream(new FileInputStream(src));
        //要先获取到压缩包里面的每一个zipentry对象
        ZipEntry entry;
        //保证zip包里面对各个对象文件不为空才循环
        while((entry=zip.getNextEntry())!=null){
            System.out.println(entry);
            //isDirectory()用于判断是不是文件夹
            if(entry.isDirectory()){
                //是文件夹,则需要在目的地 dest处创建一个同样的文件夹
                File file = new File(dest,entry.toString());//但是第二个参数不能写zip entry,没有这个类型,就要把其变成字符串才行
                file.mkdirs();//如果创建目录,则该函数返回true
            }else {
                //为文件:则需要读取到压缩包中的文件,并把他存放到目的地dest文件夹中(按照层级目录进行存放)
                FileOutputStream fos = new FileOutputStream(new File(dest,entry.toString()));
                int b;
                while((b=zip.read())!=-1){
                    //写到目的地
                    fos.write(b);
                }
                fos.close();

                //表示在压缩包中的一个文件处理完了
                zip.closeEntry();;
            }
        }
        zip.close();

    }

}

在这里插入图片描述

5.6 压缩流

在这里插入图片描述

5.6.1 压缩单个文件

在这里插入图片描述

ublic class Main {
    public static void main(String[] args) throws IOException {
        //1.创建表示要压缩的文件
        File src = new File("F:\\w.txt");
        //2.创建表示压缩包的位置
        File dest = new File("F:\\");
        toZip(src,dest);
    }

    public static void toZip(File src, File dest) throws IOException {
        //创建压缩流关联压缩包
        //aaa.zip是在F盘的根目录下面的,压缩的时候把w.txt这文件写到压缩包当中,所以就不能只写一个dest(这个只是表示F盘的目录)
        //要添加子级路径才可以
        ZipOutputStream zos =new ZipOutputStream(new FileOutputStream(new File(dest,"aaa.zip")));

        //创建zipentry对象,表示压缩包里面的每一个文件和文件夹
        ZipEntry entry = new ZipEntry("w.txt");

        //把ZipEntry放到压缩包当中 putNextEntry
        zos.putNextEntry(entry);
        //把src文件中的数据写到压缩包当中
        FileInputStream fis = new FileInputStream(src);
        int b;
        while((b=fis.read())!=-1){
            zos.write(b);
        }
        fis.close();

        zos.closeEntry();
        zos.close();
    }

}

5.6.2 压缩多个文件

ZipEntry里面的参数:表示压缩包里面的路径
所以就可以在压缩包里面,创建不同层级的子文件夹

ZipEntry entry = new ZipEntry("bbb\\w.txt");

在这里插入图片描述
代码案例:

public class Main {
    public static void main(String[] args) throws IOException {
        //1.创建File对象表示要压缩的文件夹
        File src = new File("F:\\aaa");
        //2.创建表示压缩包放在哪里,也就是父级路径
        File destParent = src.getParentFile();//根目录F:
        //3.创建表示压缩包的路径
        File dest = new File(destParent, src.getName() + ".zip");
        //4.创建压缩流关联压缩包
        ZipOutputStream zos = new ZipOutputStream(new FileOutputStream(dest));
        //5.获取src里面的每一个文件,变成ZipEntry对象,放到压缩包当中
        toZip(src,zos,src.getName());
        //释放
        zos.close();

    }

    //要获取src里面的每一个文件,变成ZipEntry对象,放入导压缩包当中
    //参数一:数据流
    //参数二:压缩流
    //参数三:压缩包内部的路径
    public static void toZip(File src,ZipOutputStream zos, String name) throws IOException {
        //1.进入src文件夹
        File[] files = src.listFiles();
        //遍历src文件夹数组
        for (File file : files) {
            if(file.isFile()){
                //如果为文件,则变成ZipEntry对象,放入到压缩包当中
                //但是如果ZipEntry里面放入file.toString的话,里面会有创建根目录也就是D盘在压缩包里面
                //当不想要根目录,想要数据源本身的文件夹路径开始时,传递src.getName(相当于传递了aaa)+\\+file.getName(文件名)
                ZipEntry entry = new ZipEntry(src.getName() + "\\" + file.getName());
                zos.putNextEntry(entry);
                //读取文件中的数据写到压缩包
                FileInputStream fis = new FileInputStream(file);

                int b;
                while((b=fis.read())!=-1){
                    zos.write(b);
                }
                fis.close();
                zos.closeEntry();
            }else {
                //如果为文件夹-则递归
                toZip(file,zos,name+"\\"+file.getName());
            }
        }
    }
}

在这里插入图片描述

5.7 常用工具包 Commons-io

在这里插入图片描述
在这里插入图片描述
Commons-io使用步骤:
在这里插入图片描述
Commons-io 常见方法:
1.FileUtils类
在这里插入图片描述
2.IOUtils类
在这里插入图片描述

导入Commons-io到idea的lib中在这里插入图片描述
当包可以展开时就说明导入成功

在这里插入图片描述
代码案例:

public class Main {
    public static void main(String[] args) throws IOException {
        File src1 = new File("javaprogram1\\a.txt");
        File dest1 = new File("javaprogram1\\b.txt");
        FileUtils.copyFile(src1,dest1); //复制文件src的内容到dest文件里面

        File src2 = new File("F:\\aaa");
        File dest2 = new File("F:\\bbb");
        FileUtils.copyDirectory(src2,dest2);//复制aaa文件夹里面的内容到bbb文件夹当中

        FileUtils.copyDirectoryToDirectory(src2,dest2);//复制aaa整个文件夹到bbb文件夹当中

        FileUtils.deleteDirectory(src2);//删除文件夹aaa

        FileUtils.cleanDirectory(dest2);//清空文件夹bbb
    }
}

5.8 Hutool工具包

在这里插入图片描述
在这里插入图片描述
Hutool官网
Hutool中文使用文档
API帮助文档

代码案例:

public class Main {
    public static void main(String[] args) throws IOException {
        //FileUtil类的file:强大之处:可以便捷拼接路径
        File file = FileUtil.file("F:\\", "aaa", "bbb");
        System.out.println(file);

        //FileUtil类的touch:当父级路径不存在时不会报错,会帮助把父级路径一起创建出来
        File touch = FileUtil.touch(file);
        System.out.println(touch);

        //FileUtil类的writeLines:把集合种的数据写到文件中,覆盖
        ArrayList<String> list1 = new ArrayList<>();
        list1.add("abc");
        list1.add("abc");
        list1.add("abc");
        File file2 = FileUtil.writeLines(list1, "F:\\a.txt", "UTF-8");
        System.out.println(file2);

        //FileUtil类的appendLines:把集合种的数据添加文件中,不覆盖
        ArrayList<String> list2 = new ArrayList<>();
        list2.add("was");
        list2.add("was");
        list2.add("was");
        File file3 = FileUtil.writeLines(list2, "F:\\a.txt", "UTF-8");
        System.out.println(file3);

        //FileUtil类的readLines:指定字符编码,把文件中的数据读到集合中
        List<String> list = FileUtil.readLines("F:\\a.txt", "UTF-8");
        System.out.println(list);

    }
}

在这里插入图片描述

6. IO流的综合练习

6.1 网络爬虫

6.1.1 爬取姓式

在这里插入图片描述

public class Test1 {
    public static void main(String[] args) throws IOException {
        //1.定义变量记录网址
        String boyNameNet = "http://www.haoming8.cn/baobao/10881.html";
        String girlNameNet = "http://www.haoming8.cn/baobao/7641.html";
        String familyNameNet = "https://hanyu.baidu.com/shici/detail?pid=0b2f26d4c0ddb3ee693fdb1137ee1b0d";

        //2.爬取数据,把王总上所有的数据拼接成一个字符串
        String boyNameStr = webCrawler(boyNameNet);
        String girlNameStr = webCrawler(girlNameNet);
        String familyNameStr = webCrawler(familyNameNet);
        //3.这些获取了网站的前端代码字符串,还需要通过正则表达式将想要的内容获取出来
        ArrayList<String> familyNameTempList = getDate(familyNameStr,"(.{4})(,|。)",1);
        System.out.println(familyNameTempList);
    }

    /*
    作用:根据正则表达式获取字符串中的数据
    参数一:代表完整的字符串
    参数二:正则表达式
    参数三:代表正则表达式的第几组,赵钱孙李。显然要获取在标点符号前面
    返回值:真正想要的值
     */
    private static ArrayList<String> getDate(String str, String regex,int index) {
        //1.创建集合存放数据
        ArrayList<String> list = new ArrayList<>();
        //2.按照正则表达式的规则获取数据 Pattern.compile
        Pattern pattern = Pattern.compile(regex);
        //按照pattern的规则,将网址的前端代码字符串转换为姓氏
        Matcher matcher = pattern.matcher(str);//文本匹配器

        while(matcher.find()){//用find查找想要的文本元素是否被找到了
            list.add(matcher.group(index));//将文本匹配器获取正则第index括号里面的东西
        }
        return list;
    }

    /*
    作用:从网络中爬取数据,把数据拼接成字符串返回
    形参:网址
    返回值:爬取到所有的数据
     */
    public static String webCrawler(String net) throws IOException {
        //1.定义StringBuilder拼接爬取到的数据
        StringBuilder sb = new StringBuilder();
        //2.创建URL对象,这个就表示网址
        //统一资源标识符(Uniform Resource Identifier ,URL)是采用一种特定语法标识一个资源的字符串。
        //所标识的资源可能是服务器上的一个文件。Java的URL网络类可以让你通过URL去练级网络服务器并获取资源
        URL url = new URL(net);

        //3.链接上这个网址
        //细节:必须要保证网络畅通,而且这个网址可以链接上
        URLConnection conn = url.openConnection();
        //4.读取数据,怎么读取?
        //一般通过IO流读取,获取输入流读到,但是InputStream是字节流,网站有中文该怎么办?
        //所以要转换为字符流InputStreamReader
        InputStreamReader isr = new InputStreamReader(conn.getInputStream());
        int b;
        while((b=isr.read())!=-1){
            sb.append((char) b);//一定要强转,不然获取到的是数字
        }
        isr.close();
        //5.直接返回StringBuilder的字符串形式
        return sb.toString();

    }
}

在这里插入图片描述
接下来的任务就是把集合里面的姓氏分开

6.1.2 爬取名字

AnyRule插件:在字符编码中获取范围,比如说中文的范围
在这里插入图片描述
在这里插入图片描述
女孩的网站名字需要重新找新的规则,不能以空格和回车为标准进行分割
所以要将一整行作为一个元素来进行分割处理
在这里插入图片描述

        ArrayList<String> boyNameTempList = getDate(boyNameStr,"([\\u4E00-\\u9FA5]{2})(、|。)",1);//如果是(..)(、|。),这样获取的名字里面就有数字标点等组合,而要求的是只要有汉字
        System.out.println(boyNameTempList);

        ArrayList<String> girlNameTempList = getDate(girlNameStr,"((.. ){4}(..))",0);
        System.out.println(girlNameTempList);

在这里插入图片描述

6.1.3 数据处理
      //4.处理数据
        //先处理familyNameTempList姓氏
        //方案:把每一个姓氏拆开并添加到一个新的集合当中
        ArrayList<String> familyNameList = new ArrayList<>();
        for (String str : familyNameTempList) {
            //此时str为 赵钱孙李 周吴郑王
            //任务就是将每个姓氏都拆开
            for (int i = 0; i < str.length(); i++) {
                char c = str.charAt(i);
                familyNameList.add(c+"");//以字符串的形式放进集合
            }
        }
        System.out.println(familyNameList);

        //男生名字
        //处理方案:去除其中的重复元素
        ArrayList<String> boyNameList = new ArrayList<>();
        for (String str : boyNameTempList) {
            if(!boyNameList.contains(str)){//如果男生名字集合里面没有相同的内容才指向添加
                boyNameList.add(str);
            }
        }
        System.out.println(boyNameList);

        //女生的名字
        //处理方案:把里面读到每一个元素用空格进行切割,就可以得到
        ArrayList<String> girlNameList = new ArrayList<>();
        for (String str : girlNameTempList) {
            String[] arr = str.split(" ");
            //获取到分割后的女孩名字字符串数组后,再将器每个元素循环出来放到集合当中
            for (int i = 0; i < arr.length; i++) {
                girlNameList.add(arr[i]);
            }
        }
        System.out.println(girlNameList);

在这里插入图片描述

生成数据

public class Test1 {
    public static void main(String[] args) throws IOException {
        //1.定义变量记录网址
        String boyNameNet = "http://www.haoming8.cn/baobao/10881.html";
        String girlNameNet = "http://www.haoming8.cn/baobao/7641.html";
        String familyNameNet = "https://hanyu.baidu.com/shici/detail?pid=0b2f26d4c0ddb3ee693fdb1137ee1b0d";

        //2.爬取数据,把王总上所有的数据拼接成一个字符串
        String boyNameStr = webCrawler(boyNameNet);
        String girlNameStr = webCrawler(girlNameNet);
        String familyNameStr = webCrawler(familyNameNet);
        //3.这些获取了网站的前端代码字符串,还需要通过正则表达式将想要的内容获取出来
        ArrayList<String> familyNameTempList = getDate(familyNameStr,"(.{4})(,|。)",1);
        ArrayList<String> boyNameTempList = getDate(boyNameStr,"([\\u4E00-\\u9FA5]{2})(、|。)",1);//如果是(..)(、|。),这样获取的名字里面就有数字标点等组合,而要求的是只要有汉字
        ArrayList<String> girlNameTempList = getDate(girlNameStr,"((.. ){4}(..))",0);


        //4.处理数据
        //先处理familyNameTempList姓氏
        //方案:把每一个姓氏拆开并添加到一个新的集合当中
        ArrayList<String> familyNameList = new ArrayList<>();
        for (String str : familyNameTempList) {
            //此时str为 赵钱孙李 周吴郑王
            //任务就是将每个姓氏都拆开
            for (int i = 0; i < str.length(); i++) {
                char c = str.charAt(i);
                familyNameList.add(c+"");//以字符串的形式放进集合
            }
        }
        System.out.println(familyNameList);

        //男生名字
        //处理方案:去除其中的重复元素
        ArrayList<String> boyNameList = new ArrayList<>();
        for (String str : boyNameTempList) {
            if(!boyNameList.contains(str)){//如果男生名字集合里面没有相同的内容才指向添加
                boyNameList.add(str);
            }
        }
        System.out.println(boyNameList);

        //女生的名字
        //处理方案:把里面读到每一个元素用空格进行切割,就可以得到
        ArrayList<String> girlNameList = new ArrayList<>();
        for (String str : girlNameTempList) {
            String[] arr = str.split(" ");
            //获取到分割后的女孩名字字符串数组后,再将器每个元素循环出来放到集合当中
            for (int i = 0; i < arr.length; i++) {
                girlNameList.add(arr[i]);
            }
        }
        System.out.println(girlNameList);

        //5.生成数据
        //姓名(唯一)-性别-年龄
        ArrayList<String> list = getInfos(familyNameList, boyNameList, girlNameList, 10, 10);
        Collections.shuffle(list);
        System.out.println(list);

        //6.写出数据,用流
        BufferedWriter bw = new BufferedWriter(new FileWriter("javaprogram1\\a.txt"));
        for (String str : list) {
            bw.write(str);
            bw.newLine();
        }
        bw.close();
    }

    /*
    作用:获取男生和女生的信息:比如张三-男-24
    参数一:装着姓氏的集合
    参数二:装着男生名字的集合
    参数三:装着女生名字的集合
    参数四:表示男生的个数
    参数五:表示女生的个数
     */
    public static ArrayList<String> getInfos(ArrayList<String> familyNameList,ArrayList<String> boyNameList,ArrayList<String> girlNameList,int boyCount,int girlCount){
        //1.生成男生不重复的姓名
        HashSet<String> boyhs = new HashSet<>();
        while(true){
            if(boyhs.size()==boyCount){
                //男生名字足够了
                break;
            }
            //随机通过Collections.shuffle打乱集合的方式进行
            Collections.shuffle(familyNameList);
            Collections.shuffle(boyNameList);
            //拼接
            boyhs.add(familyNameList.get(0)+boyNameList.get(0));
        }
        System.out.println(boyhs);

        //2.生成女生不重复的姓名
        HashSet<String> girlhs = new HashSet<>();
        while(true){
            if(girlhs.size()==girlCount){
                //男生名字足够了
                break;
            }
            //随机通过Collections.shuffle打乱集合的方式进行
            Collections.shuffle(familyNameList);
            Collections.shuffle(girlNameList);
            //拼接
            girlhs.add(familyNameList.get(0)+girlNameList.get(0));
        }
        System.out.println(girlhs);

        //3.调试成名字-男-年龄 添加到集合当中
        ArrayList<String> list = new ArrayList<>();
        Random r = new Random();
        //想要[18~27] - 18 = 0~9
        //例如:尾部+1 => 9+1=10
        for (String boyName : boyhs) {
            int age = r.nextInt(10) + 18;//加上18后就到了18~27的范围选取
            list.add(boyName+"-男"+"-"+age);
        }

        //4.调试成名字-女-年龄 添加到集合当中
        for (String girlName : boyhs) {
            int age = r.nextInt(8) + 18;//加上18后就到了16~25的范围选取
            list.add(girlName+"-女"+"-"+age);
        }
        return list;
    }

    /*
    作用:根据正则表达式获取字符串中的数据
    参数一:代表完整的字符串
    参数二:正则表达式
    参数三:代表正则表达式的第几组,赵钱孙李。显然要获取在标点符号前面
    返回值:真正想要的值
     */
    private static ArrayList<String> getDate(String str, String regex,int index) {
        //1.创建集合存放数据
        ArrayList<String> list = new ArrayList<>();
        //2.按照正则表达式的规则获取数据 Pattern.compile
        Pattern pattern = Pattern.compile(regex);
        //按照pattern的规则,将网址的前端代码字符串转换为姓氏
        Matcher matcher = pattern.matcher(str);//文本匹配器

        while(matcher.find()){//用find查找想要的文本元素是否被找到了
            list.add(matcher.group(index));//将文本匹配器获取正则第index括号里面的东西
        }
        return list;
    }

    /*
    作用:从网络中爬取数据,把数据拼接成字符串返回
    形参:网址
    返回值:爬取到所有的数据
     */
    public static String webCrawler(String net) throws IOException {
        //1.定义StringBuilder拼接爬取到的数据
        StringBuilder sb = new StringBuilder();
        //2.创建URL对象,这个就表示网址
        //统一资源标识符(Uniform Resource Identifier ,URL)是采用一种特定语法标识一个资源的字符串。
        //所标识的资源可能是服务器上的一个文件。Java的URL网络类可以让你通过URL去练级网络服务器并获取资源
        URL url = new URL(net);

        //3.链接上这个网址
        //细节:必须要保证网络畅通,而且这个网址可以链接上
        URLConnection conn = url.openConnection();
        //4.读取数据,怎么读取?
        //一般通过IO流读取,获取输入流读到,但是InputStream是字节流,网站有中文该怎么办?
        //所以要转换为字符流InputStreamReader
        InputStreamReader isr = new InputStreamReader(conn.getInputStream());
        int b;
        while((b=isr.read())!=-1){
            sb.append((char) b);//一定要强转,不然获取到的是数字
        }
        isr.close();
        //5.直接返回StringBuilder的字符串形式
        return sb.toString();

    }
}

在这里插入图片描述

在这里插入图片描述

6.2 利用糊涂包生成假数据

Hutool包爬取
在这里插入图片描述

public class Test1 {
    public static void main(String[] args) throws IOException {
        //1.定义变量记录网址
        String boyNameNet = "http://www.haoming8.cn/baobao/10881.html";
        String girlNameNet = "http://www.haoming8.cn/baobao/7641.html";
        String familyNameNet = "https://hanyu.baidu.com/shici/detail?pid=0b2f26d4c0ddb3ee693fdb1137ee1b0d";

        //爬取利用糊涂包直接获取网址的前端代码
        String familyNameStr = HttpUtil.get(familyNameNet);
        String boyNameStr = HttpUtil.get(boyNameNet);
        String girlNameStr = HttpUtil.get(girlNameNet);

        //3.利用正则表达式获取数据,把其中符合要求的数据获取出来
        // ArrayList<String> familyNameTempList = getDate(familyNameStr,"(.{4})(,|。)",1);
        // ArrayList<String> boyNameTempList = getDate(boyNameStr,"([\\u4E00-\\u9FA5]{2})(、|。)",1);//如果是(..)(、|。),这样获取的名字里面就有数字标点等组合,而要求的是只要有汉字
        // ArrayList<String> girlNameTempList = getDate(girlNameStr,"((.. ){4}(..))",0);
        //对照着写,可以写ArrayList集合接收,只不过该方法ReUtil.findAll返回的是List集合,要进行强转成ArrayList,要么就直接用List接收
        List<String> familyNameTempList = ReUtil.findAll("(.{4})(,|。)", familyNameStr, 1);
        List<String> boyNameTempList = ReUtil.findAll("([\\u4E00-\\u9FA5]{2})(、|。)", boyNameStr, 1);
        List<String> girlNameTempList = ReUtil.findAll("((.. ){4}(..))", girlNameStr, 0);
        System.out.println(familyNameTempList);
        System.out.println(boyNameTempList);
        System.out.println(girlNameTempList);

        //4.处理数据和生成数据还是要自己写的
        ArrayList<String> familyNameList = new ArrayList<>();
        for (String str : familyNameTempList) {
            //此时str为 赵钱孙李 周吴郑王
            //任务就是将每个姓氏都拆开
            for (int i = 0; i < str.length(); i++) {
                char c = str.charAt(i);
                familyNameList.add(c+"");//以字符串的形式放进集合
            }
        }
        System.out.println(familyNameList);

        //男生名字
        //处理方案:去除其中的重复元素
        ArrayList<String> boyNameList = new ArrayList<>();
        for (String str : boyNameTempList) {
            if(!boyNameList.contains(str)){//如果男生名字集合里面没有相同的内容才指向添加
                boyNameList.add(str);
            }
        }
        System.out.println(boyNameList);

        //女生的名字
        //处理方案:把里面读到每一个元素用空格进行切割,就可以得到
        ArrayList<String> girlNameList = new ArrayList<>();
        for (String str : girlNameTempList) {
            String[] arr = str.split(" ");
            //获取到分割后的女孩名字字符串数组后,再将器每个元素循环出来放到集合当中
            for (int i = 0; i < arr.length; i++) {
                girlNameList.add(arr[i]);
            }
        }
        System.out.println(girlNameList);

        //5.生成数据
        //姓名(唯一)-性别-年龄
        ArrayList<String> list = getInfos(familyNameList, boyNameList, girlNameList, 10, 10);
        Collections.shuffle(list);
        System.out.println(list);

        //6.写出数据采用hutool包
        //细节:
        //糊涂包的相对路径,不是相当于当前项目而言的,而是相对class文件而言的
        FileUtil.writeLines(list,"names.txt","UTF-8");
    }

    /*
      作用:获取男生和女生的信息:比如张三-男-24
      参数一:装着姓氏的集合
      参数二:装着男生名字的集合
      参数三:装着女生名字的集合
      参数四:表示男生的个数
      参数五:表示女生的个数
       */
    public static ArrayList<String> getInfos(ArrayList<String> familyNameList,ArrayList<String> boyNameList,ArrayList<String> girlNameList,int boyCount,int girlCount){
        //1.生成男生不重复的姓名
        HashSet<String> boyhs = new HashSet<>();
        while(true){
            if(boyhs.size()==boyCount){
                //男生名字足够了
                break;
            }
            //随机通过Collections.shuffle打乱集合的方式进行
            Collections.shuffle(familyNameList);
            Collections.shuffle(boyNameList);
            //拼接
            boyhs.add(familyNameList.get(0)+boyNameList.get(0));
        }
        System.out.println(boyhs);

        //2.生成女生不重复的姓名
        HashSet<String> girlhs = new HashSet<>();
        while(true){
            if(girlhs.size()==girlCount){
                //男生名字足够了
                break;
            }
            //随机通过Collections.shuffle打乱集合的方式进行
            Collections.shuffle(familyNameList);
            Collections.shuffle(girlNameList);
            //拼接
            girlhs.add(familyNameList.get(0)+girlNameList.get(0));
        }
        System.out.println(girlhs);

        //3.调试成名字-男-年龄 添加到集合当中
        ArrayList<String> list = new ArrayList<>();
        Random r = new Random();
        //想要[18~27] - 18 = 0~9
        //例如:尾部+1 => 9+1=10
        for (String boyName : boyhs) {
            int age = r.nextInt(10) + 18;//加上18后就到了18~27的范围选取
            list.add(boyName+"-男"+age);
        }

        //4.调试成名字-女-年龄 添加到集合当中
        for (String girlName : boyhs) {
            int age = r.nextInt(8) + 18;//加上18后就到了16~25的范围选取
            list.add(girlName+"-女"+age);
        }
        return list;
    }

}

在这里插入图片描述

在这里插入图片描述

6.3 带权重的随机数

在这里插入图片描述
了解一下微服务:
在这里插入图片描述
用户在上网的时候不知道该访问哪台服务器,所以在中间就会有一个服务网关,它会根据算法来计算哪个服务器人多了哪个人少了,然后在调整服务器的权重,让用户到人少的地方

梳理过程:
在这里插入图片描述
那么这10%的概率怎么计算呢?
之前学的是往集合里面添加七个1、三个0,再根据1和0的占比情况来决定概率

在这里插入图片描述
但是这样做只适合数据比较少的情况(男生和女生这两个种类),一旦数据比较多,种类一旦多起来了,就不能再这样子表示了

new方案:
去求出每个数据中的权重占比
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

Student类:
public class Student {
    private String name;
    private String gender;
    private int age;
    private double weight;


    public Student() {
    }

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

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

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

    /**
     * 获取
     * @return gender
     */
    public String getGender() {
        return gender;
    }

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

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

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

    /**
     * 获取
     * @return weight
     */
    public double getWeight() {
        return weight;
    }

    /**
     * 设置
     * @param weight
     */
    public void setWeight(double weight) {
        this.weight = weight;
    }

    public String toString() {
        return name+"-"+gender+"-"+age+"-"+weight;
    }
}

测试类:
public class Test1 {
    public static void main(String[] args) throws IOException {
        //1.把文件中所有的学生信息读取到内存当中,并封装一个Student对象再放到集合里,才方便统一进行管理
        ArrayList<Student> StudnetList = new ArrayList<>();
        BufferedReader br = new BufferedReader(new FileReader("javaprogram1\\names.txt"));
        String line;
        while((line=br.readLine())!=null){
            String[] arr = line.split("-");
            Student stu = new Student(arr[0], arr[1], Integer.parseInt(arr[2]), Double.parseDouble(arr[3]));
            StudnetList.add(stu);
        }
        br.close();
        System.out.println(StudnetList);

        //2.计算总权重
        double weight = 0;
        for (Student stu : StudnetList) {
            weight = weight + stu.getWeight();
        }
        System.out.println(weight);
        
        //3.计算每个人的权重占比
        double[] arr = new double[StudnetList.size()];
        int index = 0;
        for (Student stu : StudnetList) {
            arr[index]=stu.getWeight()/weight;
            index++;
        }
        System.out.println(Arrays.toString(arr));
        //蓝色权重占比部分完成

        //4.计算权重占比范围
        for (int i = 1; i < arr.length; i++) {
            arr[i] = arr[i]+arr[i-1];
        }
        System.out.println(Arrays.toString(arr));

        //5.随机抽取
        //获取一个0 ~ 1.0之间的随机数
        double Rnumber = Math.random();//小数参与的计算是不精确的,当时不影响范围的选取
        System.out.println(Rnumber);

        //接下来判断number在arr中的位置,就不要一个一个遍历了,不够便捷
        //二分查找法
        //Arrays.binarySearch方法返回:-插入点-1 ,这个插入点表示如果在数组当中就应该是什么位置
        //怎么获取number这个数据在数组当中的插入点位置?
        //获取插入点 = -方法返回值-1
        int result = -Arrays.binarySearch(arr,Rnumber)-1;
        Student stu = StudnetList.get(result);
        System.out.println(stu);

        //6.被点到了,概率降低,修改当前学生的权重
        double w = stu.getWeight() / 2;
        stu.setWeight(w);

        //7.把集合中的数据再次写到文件中
        BufferedWriter bw = new BufferedWriter(new FileWriter("javaprogram1\\names.txt"));
        for (Student student : StudnetList) {
            bw.write(student.toString());
            bw.newLine();
        }
        bw.close();
    }
}

在这里插入图片描述
在这里插入图片描述

6.4 登录注册

在这里插入图片描述
在这里插入图片描述

    public static void main(String[] args) throws IOException {
        //1.读取正确的用户名和密码
        BufferedReader br = new BufferedReader(new FileReader("javaprogram1\\a.txt"));
        String line = br.readLine();//一行信息
        br.close();

        String[] userInfo = line.split("&");
        System.out.println(Arrays.toString(userInfo));
        String[] userList = userInfo[0].split("=");
        String[] passwordList = userInfo[1].split("=");
        System.out.println(Arrays.toString(userList));
        System.out.println(Arrays.toString(passwordList));

        String rightUsername = userList[1];
        String rightPassword = passwordList[1];

        //2.键盘录入输入
        Scanner sc = new Scanner(System.in);
        System.out.println("请输入用户名:");
        String username = sc.nextLine();
        System.out.println("请输入密码:");
        String password = sc.nextLine();

        //3.判断
        if(rightUsername.equals(username)&&rightPassword.equals(password)){//不能用username == rightUsername && password==rightPassword,因为原本不是统一在栈里面的真实数据
            System.out.println("登录成功");
        }else {
            System.out.println("登录失败");
        }
    }

在这里插入图片描述
2.
在这里插入图片描述

在这里插入图片描述
修改以下代码:

public class Main {
    public static void main(String[] args) throws IOException {
        //1.读取正确的用户名和密码
        BufferedReader br = new BufferedReader(new FileReader("javaprogram1\\a.txt"));
        String line = br.readLine();//一行信息
        br.close();

        String[] userInfo = line.split("&");
        System.out.println(Arrays.toString(userInfo));
        String[] userList = userInfo[0].split("=");
        String[] passwordList = userInfo[1].split("=");
        String[] countList = userInfo[2].split("=");
        System.out.println(Arrays.toString(userList));
        System.out.println(Arrays.toString(passwordList));
        System.out.println(Arrays.toString(countList));

        String rightUsername = userList[1];
        String rightPassword = passwordList[1];
        int count = Integer.parseInt(countList[1]);

        //2.键盘录入输入
        Scanner sc = new Scanner(System.in);
        System.out.println("请输入用户名:");
        String username = sc.nextLine();
        System.out.println("请输入密码:");
        String password = sc.nextLine();

        //3.判断
        if(rightUsername.equals(username)&&rightPassword.equals(password)&&count<3){//不能用username == rightUsername && password==rightPassword,因为原本不是统一在栈里面的真实数据
            System.out.println("登录成功");
            writeInfo("username"+rightUsername+"&password="+rightPassword+"&count=0");

        }else {
            count++;
            if(count<3){
                System.out.println("登录失败,还剩下"+(3-count)+"次机会");
            }else {
                System.out.println("登录失败,用户账号被锁定");
            }
            writeInfo("username"+rightUsername+"&password="+rightPassword+"&count="+count);
        }

    }

    public static void writeInfo(String content) throws IOException {
        BufferedWriter bw = new BufferedWriter(new FileWriter("javaprogram1\\a.txt"));
        bw.write(content);
        bw.close();
    }
}

6.5 配置文件

在这里插入图片描述
在这里插入图片描述

6.5.1 properties配置文件

在这里插入图片描述
properties配置文件都是按照键值对的形式存储的

properties不是一个泛型类,那么在添加数据的时候就可以添加任意的数据类型,但是一般只会添加字符串类型的数据
在这里插入图片描述

properties作为map集合的基本用法:

public class Main {
    public static void main(String[] args) {
        //1.创建
        Properties prop = new Properties();
        //2.添加数据
        prop.put("aaa","111");
        prop.put("bbb","222");
        prop.put("ccc","333");
        prop.put("ddd","444");
        //3.遍历集合
        Set<Object> keys = prop.keySet();
        for (Object key : keys) {
            Object value = prop.get(key);
            System.out.println(key+"="+value);
        }
        System.out.println("==================");

        Set<Map.Entry<Object, Object>> entries = prop.entrySet();
        for (Map.Entry<Object, Object> entry : entries) {
            Object key = entry.getKey();
            Object value = entry.getValue();
            System.out.println(key+"="+value);
        }

    }
}

在这里插入图片描述

properties与IO流结合的操作:

    public static void main(String[] args) throws IOException {
        //1.创建
        Properties prop = new Properties();
        //2.添加数据
        prop.put("aaa","111");
        prop.put("bbb","222");
        prop.put("ccc","333");
        prop.put("ddd","444");

        //麻烦的代码
        /*BufferedWriter bw = new BufferedWriter(new FileWriter("javaprogram1\\a.txt"));
        Set<Map.Entry<Object, Object>> entries = prop.entrySet();
        for (Map.Entry<Object, Object> entry : entries) {
            Object key = entry.getKey();
            Object value = entry.getValue();
            bw.write(key+"="+value);
            bw.newLine();
        }
        bw.close();
         */

        //使用properties的特有方法
        FileOutputStream fos = new FileOutputStream("javaprogram1\\a.txt");
        prop.store(fos,"test");
        fos.close();

        //读取本地properties文件里面的数据
        FileInputStream fis = new FileInputStream("javaprogram1\\a.txt");
        prop.load(fis);
        fis.close();
        System.out.println(prop);
    }

在这里插入图片描述

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值