java基础学习-7

文章目录

  • 异常
    • 小总结
    • 编译时异常和运行时异常
      • 小总结
    • 异常在代码中的两个作用
    • 异常的方式
      • JVM虚拟机默认处理异常的方式
      • 自己处理(捕获异常)
      • 灵魂四问
      • 抛出异常
      • 异常中的常见方法
      • 小总结
      • 小练习
    • 自定义异常
  • File----路径
    • File的概述和构造方法
      • 小总结
    • File的成员方法
      • 判断、获取
      • 创建、删除
      • 获取并遍历----1
      • 获取并遍历----2
      • 小练习
        • 练习-1
        • 练习-2
        • 练习-3
        • 练习-4
        • 练习-5
        • 练习-6
  • IO流
    • IO流的概述
    • IO流的分类
      • 小总结
    • IO流的体系
    • 字节输出流
      • 字节输出流基本用法
      • 字节输出流的细节
      • FileOutputStream写数据的3种方式
      • FileOutputStream写数据的两个小问题
      • 小总结
    • 字节输入流
      • 字节输入流的基本用法
      • FileInputStream书写细节
      • FileInputStream循环读取
      • FileInputStream读取的问题
    • 文件拷贝
      • 文件拷贝——改
    • IO流中不同JDK版本捕获异常的方式
    • 字符集
      • ASCII
      • GBK
        • 小总结
      • Unicode
        • 小总结
      • 乱码出现的原因
      • 如何不产生乱码
    • 编码和解码
    • 字符流
      • FileReader
      • 字符流输出流写出数据
    • 缓冲流
      • 字节缓冲流
      • 字符缓冲流
      • 小总结
    • 转换流
      • 小练习
        • 1
        • 2
        • 3
    • 序列化流(对象操作输出流)
    • 反序列化流
      • 序列化流和反序列化流中的细节
        • 细节汇总
    • 字节打印流
    • 字符打印流
      • 小总结
    • 解压缩流
    • 压缩流
      • 压缩一个文件
      • 压缩文件夹
    • 常用工具包
      • Commons-io
        • 使用步骤
        • FileUtils类(文件相关)
        • IOUtils类(流相关)
        • 下载
        • 使用
      • Hutool
        • 使用
  • 项目练习
    • 制造假数据
    • 随机点名器

52~134

异常

异常就是代表程序出现的问题

误区:不是让我们以后不出异常,而是程序出现异常之后,该如何处理

打人家玻璃是不对的
现在学习的不是让你以后不打别人家的玻璃
而是打碎别人家的玻璃之后该怎么处理

Erroe:代表的系统级别错误(属于严重问题)
系统一旦出现问题,sun公司会把这些错误封装成Error对象
Erroe是给sun公司自己用的,不是给我们程序员用的,
因此我们开发人员不管他

Exception:叫做异常,代表程序可能出现的问题
我们通常会用Exception以及他的子类来封装程序出现的问题

运行时异常:RuntimeException及其子类,编译阶段不会出现异常提醒
运行时出现的异常(如:数组索引越界异常)

编译时异常:编译阶段就会出现异常提醒的。(如:日期解析异常)
java基础学习-7_第1张图片

小总结

1、异常是什么?
程序中可能出现的问题
2、一场体系的最上层父类是谁?异常分几类?
父类:Exception
异常分为两类:编译时异常、运行时异常
3、编译时异常和运行时异常的区别?
编译时异常:没有继承RuntimeException的异常,直接继承于Exception。编译阶段就会错误提示
运行时异常:RuntimeException本身和子类
编译阶段没有错误提示,运行时出现的

编译时异常和运行时异常

String name = "2030年1月1日";
SimpleDateFormat sdf = new SimpleDateFormat("yyyy年MM月dd日");
Date date = sdf.parse(time);
System.out.println(date);

parse正常情况下会报错,需要在main函数的后面加上throws ParseException才能将报错取消
这就是编译时异常,在编译阶段,必须要手动处理,否则代码报错

int[] arr = {1,2,3,4,5};
System.out.println(arr[10]);

数组越界异常,标准的运行时异常,在编译阶段是不需要处理的,是代码运行时出现的异常

java基础学习-7_第2张图片
为什么分两种异常?
在编译阶段,java不会运行diamagnetic,只会检查语法是否错误,或者做一些性能的优化,更多的是提醒程序员检查本地信息

运行时异常就是代码出错而导致程序出现问题

小总结

运行时异常和编译时异常的区别?
编译时异常:除了RuntimeException和他的子类,其他都是编译时异常,编译阶段需要进行处理,作用在于提醒程序员
运行时异常:RuntimeException本身和所有子类,都是运行时异常,编译阶段不报错,是程序运行时出现的,一般是由参数传递错误带来的问题

异常在代码中的两个作用

作用一:异常是用来查询bug的关键参考信息
作用二:异常可以作为方法内部的一种特殊返回值,以便通知调用者底层的执行情况

public void setAge(int age){
	if(age < 18 || age > 40){
		throw new RuntimeException();
	}else {
		this.age=age;
	}
}

学生类中的赋值年龄成员函数,如果是年龄小于18或者大于40的打印输出语句提示错误,只是在控制台打印出来,并不能直接告诉调用处(代码编写处)
这样就可以有两种解决方法,第一种自己处理掉问题,或者打印在控制台上

异常的方式

1、JVM虚拟机默认处理异常的方式
2、自己处理
3、抛出异常(交给调用者)

JVM虚拟机默认处理异常的方式

把异常的名称,异常原因及异常出现的位置等信息输出在了控制台上
程序停止运行,下面的代码不会再执行了

System.out.println("狂踹瘸子那条好腿");
System.out.println(2/0);
System.out.println("是秃子终会发光");
System.out.println("火鸡味锅巴");

请添加图片描述

自己处理(捕获异常)

格式:

下面展示一些 内联代码片

try{
    可能出现异常的代码;
} catch(异常类名 变量名){
    异常的处理代码;
}

自己处理捕获异常的目的就是当代码出现问题的时候,可以让程序继续往下执行

int[] arr = {1,2,3,4,5,6};
try {
    System.out.println(arr[10]);
}catch (ArrayIndexOutOfBoundsException e){
    System.out.println("索引越界了");
}
System.out.println("看看我执行了吗");

灵魂四问

灵魂一问:如果try中没有遇到问题,怎么执行?
会把try里面的代码全部执行完毕,不会执行catch里面的代码
注意:只有当出现了异常才会执行catch里面的代码

灵魂二问:如果try中可能会遇到多个问题,怎么执行?
要写多个catch与之对应
细节:如果我们要捕获多个异常,这些异常中如果存在父子关系的话,那么父类一定要写在下面
了解性:在JDK7之后,我们可以在catch中同时捕获多个异常,中间用|进行隔开
表示如果出现了A异常或者B异常的话,采取同一种处理方案

灵魂三问:如果try中遇到的问题没有被捕获,怎么执行?
相当于try…catch的代码白写了,最终还是会交给虚拟机进行处理

灵魂四问:如果try中遇到了问题,那么try下面的其他代码还会执行吗?
下面的代码就不会运行了,直接跳转到对应的catch当中,执行catch里面的语句体
但是如果没有对应catch与之匹配,那么还是会交给虚拟机进行处理

抛出异常

throws:
注意:写在方法定义出,表示声明一个异常
告诉调用者,使用本方法可能会有哪些异常

public void 方法()throws 异常类名1,异常类名2...{
	...
}

编译时异常:必须要写
运行时异常:可以不写

throw:
注意:写在方法内,结束方法
手动抛出异常对象,交给调用者
方法中下面的代码不再执行了

public void 方法(){
	throw new NullPointerException();
}
public static void main(String[] args) {
    int[] arr = {};
    try{
		int max = getMax(arr);
	}catch (NullPointerException e){
		System.out.println("空指针异常");
    }catch (ArrayIndexOutBoundsException e){
    	System.out.println("索引越界异常");
    }
    System.out.println(max);
}
public static int getMax(int[] arr){
    if(arr == null){
        //手动创建一个异常,并把这个异常交给方法的调用者处理
        //此时方法就会结束,下面的代码不会再执行了
        throw new NullPointerException();
    }
    if(arr.length == 0){
    	throw new ArratIndexOutOfBoundsException();
    }
    int max = arr[0];
    for (int i = 1; i < arr.length; i++) {
        if(arr[i]>max){
            max = arr[i];
        }
    }
    return max;
}

异常中的常见方法

Throeable的成员方法

public String getMessage()//返回此Throwable的详细信息字符串
public String toString()//返回此可抛出的简短描述
public void printStackTrace()//把异常的错误信息输出在控制台,仅仅是打印红字信息,不会停止程序运行

小总结

1、虚拟机默认处理异常的方式
把异常信息以红色字体打印在控制台,并结束程序

2、捕获:try…catch
一般用在调用出,能让代码继续往下运行

3、抛出:throw throws
在方法中,出现异常了
方法就没有继续运行下去的意义了,采取抛出异常
让该方法结束运行并告诉调用者出现了问题

小练习

需求:
键盘录入自己心仪的女朋友姓名和年龄
姓名的长度在3~10之间
年龄的范围为18~40岁
超出这个范围是异常数据不能赋值,需要重新录入,一直录到正确为止
提示:
需要考虑用户在键盘录入时的所有情况
比如:录入年龄时超出范围,录入年龄时录入了abc等情况

Scanner sc = new Scanner(System.in);
GrilFriend gf = new GrilFriend();
while (true) {
    try {
        System.out.println("请输入你心仪的女朋友的名字");
        String name = sc.nextLine();
        gf.setName(name);
        System.out.println("请输入你心仪的女朋友的年龄");
        String ageStr = sc.nextLine();
        int age = Integer.parseInt(ageStr);
        gf.setAge(age);
        //如果所有的数据都是正确的,那么跳出循环
        break;
    } catch (NumberFormatException e) {
        System.out.println("年龄的格式有误,请输入数字");
        continue;
    } catch (RuntimeException e){
        System.out.println("姓名的长度 或者 年龄的范围有误");
        continue;
    }
}
System.out.println(gf);
public class GrilFriend {
    private int age;
    private String name;

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

    public GrilFriend() {
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        if(age<18||age>40){
            throw new RuntimeException();
        }
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        int len = name.length();
        if (len<3 || len>10){
            throw new RuntimeException();
        }
        this.name = name;
    }
}

自定义异常

创建自定义异常
1、定义异常类
2、写继承关系
3、空参构造
4、带参构造

自定义异常的意义就是为了让控制台的报错信息更加的见名知意

public class NameFormatException extends RuntimeException{
    //技巧:
    //NameFormat:当前异常的名字,表示姓名格式化问题
    //Exception:表示当前类是一个异常类

    //继承问题
    //运行时异常:RuntimeException
    //编译时异常:Exception 核心 提醒程序员检查本地信息
	public NameFormatException() {
    }

    public NameFormatException(String message) {
        super(message);
    }
}
public class AgeOutOfBoundsException extends RuntimeException{
    public AgeOutOfBoundsException() {
    }

    public AgeOutOfBoundsException(String message) {
        super(message);
    }
}

File----路径

File的概述和构造方法

File对象就表示一个路径,可以是文件的路径、也可以是文件夹的路径
这个路径可以是存在的,也允许是不存在的

public File(String pathname)//根据文件路径创建文件对象
public File(String parent, String child)//根据父路径名字符串和子路径名字符串创建文件对象
public File(File parent, String child)//根据父路径对应文件对象和子路径名字符串创建文件对象

以“C:\Users\xiaoyou\Desktop\a.txt”为例
父级路径就是“C:\Users\xiaoyou\Desktop”
子级路径就是“a.txt”

小总结

1、File表示什么?
File对象表示路径,可以是文件、也可以是文件夹
这个路径可以是存在的,也可以是不存在的
2、绝对路径和相对路径是什么意思?
绝对路径是带盘符的
相对路径是不带盘符的,默认到当前项目下去找
3、File三种构造方法的作用

public File(String pathname)//根据文件路径创建文件对象
public File(String parent, String child)//根据父路径名字符串和子路径名字符串创建文件对象
public File(File parent, String child)//根据父路径对应文件对象和子路径名字符串创建文件对象

File的成员方法

判断、获取

public boolean isDirectory()//判断路径名表示的File是否为文件夹
public boolean isFile()//判断此路径名表示的File是否为文件
public boolean exists()//判断此路径名表示的File是否存在
public long length()//返回文件的大小(字节数量)
public String getAbsolutePath()//返回文件的绝对路径
public String getPath()//返回定义文件时使用的路径
public String getName()//返回文件的名称,带后缀
public long lastModeidied()//返回文件的最后修改时间(时间毫秒值)

length细节:
细节1、
这个方法只能获取文件的大小,单位是字节
如果单位我们要是M,G可以不断地除以1024

细节2、
这个方法无法获取文件夹的大小,(0/4096)
如果我们要获取一个文件夹的大小,需要把这个文件夹里面所有的文件大小都累加在一起

getName细节:
细节1、
如果调用者是个文件,会返回文件名和后缀名
细节2、
如果调用者是文件夹,返回的就是文件夹的名字

String str = "C:\\Users\\xiaoyou\\Desktop\\信息.txt";
File f1 = new File(str);
Date date = new Date(f1.lastModified());
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss E");
System.out.println(sdf.format(date));

创建、删除

public boolean creatNewFile()//创建一个新的空的文件
public boolean mkdir()//创建单级文件夹
public boolean mkdirs()//创建多级文件夹
public boolean delete()//删除文件夹、空文件夹

delete方法默认只能删除文件和空文件夹,delete方法直接删除不走回收站

createNewFile细节:
细节1、
如果当前路径表示的文件是不存在的,则创建成功,方法返回true
如果当前路径表示的文件是存在的,则创建失败,方法返回false

细节2、
如果父级路径是不存在的,那么方法会有异常IOException

细节3、
createNewFile方法创建的一定是文件,如果路径中不包含后缀名,则创建一个没有后缀名的文件

mkdir细节:
细节1、
Windows当中路径一定是唯一的,如果当前路径已经存在,则创建失败,返回false

细节2、
mkdir方法只能创建单级文件夹,无法创建多级文件夹

mkdirs细节:
即可以创建单级的,又可以创建多级的文件夹

delete细节
细节1、
如果删除的是文件,则直接删除,不走回收站
如果删除的是空文件夹,则直接删除,不走回收站
如果删除的是有内容的文件夹,则删除失败

获取并遍历----1

public File[] listFiles()//获取当前该路径下所有内容
//1、创建File对象
File f = new File("D:\\aaa");
//2、listFiles方法
//作用:获取aaa文件夹里面的所有内容,把所有的内容放到数组中返回
File[] files = f.listFiles();
for (File file : files){
	//file依次表示aaa文件夹里面的每一个文件或者文件夹
	System.out.println(file);
}

listFiles细节
1、当调用者File表示的路径不存在时,返回null
2、当调用者File表示的路径是文件时,返回null
3、当调用者File表示的路径是一个空文件夹时,返回一个长度为0的数组
4、当调用者File表示的路径是一个有内容的文件夹时,将里面所有的文件和文件夹的路径放在File数组中返回
5、当调用者File表示的路径是一个有隐藏文件的文件夹时,将里面所有文件和文件夹的路劲放在File数组中返回,包含隐藏文件
6、当调用者File表示的路径是需要权限才能访问的文件夹时,返回null

获取并遍历----2

public static File[] listRoots()//列出可用的文件系统根
public String list()//获取当前该路径下所有内容
public String list(FilenameFilter filter)//利用文件名过滤器获取当前该路径下所有内容
public File[] listFiles()//获取当前该路径下所有内容
public File[] list(FileFilter filter)//利用文件名过滤器获取当前该路径下所有内容
public File[] listFiles(FilenameFilter filter)//利用文件名过滤器获取当前该路径下所有内容

list(FilenameFilter filter)
accept方法的形参,依次表示aaa文件夹李米娜每一个文件或者文件夹路径
参数一:父级路径
参数二:子级路径
返回值:如果返回值为true,就表示当前路径保留;如果返回值为false,就表示当前路径舍弃不要

小练习

练习-1

练习-2

练习-3

练习-4

练习-5

练习-6

IO流

IO流的概述

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

File:表示系统中文件或者文件夹的路径
获取文件信息(大小,文件名,修改时间)、判断文件类型、创建文件/文件夹、删除文件/文件夹…
File类只能对文本本身进行操作,不能读写文件里面存储的数据

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

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

IO流的分类

按流的方向可以分为输入流和输出流
输入流:读取
输出流:写去

按操作文件类型可以分为字节流和字符流
字节流可以操作所有类型的文件
字符流只能操作纯文本文件

纯文本文件:Windows自带的记事本打开能读懂

小总结

1、什么是IO流?
存取和读取数据的解决方案
I:input
O:output
流:像水流一样传输数据

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

3、IO流按照流向可以分类哪两种流?
输出流:程序---->文件
输入流:文件---->程序

4、IO流按照操作文件的类型可以分类哪两种流?
字节流:可以操作所有类型的文件
字符流:只能操作纯文本文件

5、什么是纯文本文件?
用Windows自带的记事本打开能读懂
txt文件,md文件,xml文件,lrc文件等

IO流的体系

java基础学习-7_第3张图片

java基础学习-7_第4张图片

字节输出流

字节输出流基本用法

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

书写步骤
1、创建字节输出流对象
2、写数据
3、释放资源

FileOutStream fos = new FileOutputStream("myio//a.txt");
fos.write(97);
fos.close();

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

字节输出流的细节

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

2、写数据
细节:write方法的参数是整数,但是实际上写道本地文件中的是整数在ASCII上对应的字符

3、释放资源
每次使用完流之后都要释放资源,就是将文件从内存运行中停止,这样后续就能够继续操作文件了,不然是操作不了的

FileOutputStream写数据的3种方式

void write(int b)//一次写一个字节数据
void write(byte[] b)//一次写一个字节数组数据
void write(byte[] b, int off, int len)//一次写一个字节数组的部分数据

关于wirte的第三种使用
参数一:数组
参数二:起始索引
参数三:个数

FileOutputStream写数据的两个小问题

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

换行写:
再次写出一个换行符就可以了
windows:\r\n
Linux:\n
Mac:\r
细节:
在Windows操作系统当中,java对回车换行进行了优化,
虽然完整的是\r\n,但是我们写其中一个\r或者\n就可以
java也可以实现换行,因为java在底层会补全
建议:不要省略,还是写全了

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

FileOutputStream fos = new FileOutputStream("myrio\\a.txt");
Stirng str = "laozizhengshuai";
byte[] bytes = str.getBytes();
fos.write(bytes);

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

String str2 = "666";
byte[] byte3 = str2.getBytes();
fos.write(bytes3);

fos.close();

小总结

1、FileOutputStream的作用
可以把程序中的数据写到本地文件上,是字节流的基本流

2、书写步骤
创建对象,写出数据,释放资源

3、三步操作的细节
创建对象:文件存在、文件不存在、追加写入
写出数据:写出整数、写出字节数组、换行写
释放资源:关闭通道

字节输入流

字节输入流的基本用法

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

书写步骤:
1、创建字节输入流对象
2、读数据
3、释放资源

FileInputStream fis = new FileInputStream("巴拉巴拉路径");
int b1 = fis.read();//只能读取一个字符,读不到了就返回-1
System.out.println(b1);
fis.close();

FileInputStream书写细节

1、创建字节输入流对象
细节:如果文件不存在,就直接报错
java为什么会这么设计呢?
输出流:不存在,创建——把数据写到文件当中
输入流:不存在,而是报错?
因为创建出来的文件是没有数据的,没有任何意义。
所以java就没有设计这种无意义的逻辑,文件不存在直接报错

程序中最重要的是:数据。

2、读取数据
细节1:一次读一个字节,读出来的是数据在ASCII上对应的数字
细节2:读到文件末尾了,read方法返回-1。

3、释放资源
细节:每一次使用完流必须要释放资源

FileInputStream循环读取

FileInputStream fis = new FileInputStream("巴拉巴拉路径");
int b;
while((b == fis.read()) != -1){
	System.out.println((char)b);
}
fis.close();

FileInputStream读取的问题

IO流:如果拷贝的文件过大,那么速度会不会有影响?
当然,会很慢

FileInputStream一次读写一个字节
弊端:一次读写一个字节,太慢了

File InputStream一次读取多个字节:

public int read()//一次读一个字节数据
public int read(byte[] buffer)//一次读一个字节数组数据

注意:一次读一个字节数组的数据,每次读取会尽可能把数组装满
数组的长度一般都是1024的整数倍

//1、创建对象
FileInputStreamfis = new FileInputStream("巴拉巴拉路径");
//2、读取数据
byte[] bytes = new byte[2];
//一次读取多个字节数据,具体读多少,跟数组的长度有关
//返回值:本次读取到了多少个字节数据
int len = fis.read(bytes);
System.out.println(len);//2

String n str = new String(bytes);
System.out.println(str);

//3、释放资源
fis.close();

文件拷贝

注意:先选择一个比较小的文件,不要太大

//1、创建对象
FileInputStream fis = new FileInputStream("巴拉巴拉路径");
FileOutputStream fos = new FileOutputStream("巴啦啦路径");
//2、拷贝
//核心思想:边读边写
int b;
while((b = fis.read()) != -1){
	fos.write(b);
}
//3、释放资源
//规则:先开的最后关闭
fos.close();
fis.close()

文件拷贝——改

//1、创建对象
FileInputStream fis = new FileInputStream("巴拉巴拉路径");
FileOutputStream fos = new FileOutputStream("巴拉巴拉路径");
//2、拷贝
int len;
byte[] bytes = new byte[1024*1024*5];
while((len = fis.read(bytes))  != -1){
	fos.wirte(bytes,0,len);
}
//3、释放资源
fos.close();
fis.close();
//实践代码
FileInputStream fis = new FileInputStream("C:\\Users\\xiaoyou\\Desktop\\宝的照片\\306751AF0EAB631BBBFAC17471282421.png");
FileOutputStream fos = new FileOutputStream("C:\\Users\\xiaoyou\\Desktop\\copy.png");
int len;
byte[] bytes=new byte[1024*1024*5];
while((len = fis.read(bytes))  != -1){
    fos.write(bytes,0,len);
}
fos.close();
fis.close();

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

老版本try…catch下面还有个finally
特点:finally里面的代码一定被执行,除非虚拟机停止,除非JVM推出
将扫尾代码放到finally之中

FileInputStream fis=null;
FileOutputStream fos=null;
try {
    fis = new FileInputStream("C:\\Users\\xiaoyou\\Desktop\\宝的照片\\306751AF0EAB631BBBFAC17471282421.png");
    fos = new FileOutputStream("C:\\Users\\xiaoyou\\Desktop\\copy.png");
    int len;
    byte[] bytes=new byte[1024*1024*5];
    while((len = fis.read(bytes))  != -1){
        fos.write(bytes,0,len);
    }
}catch (IOException e){
    e.printStackTrace();
}finally {
    if (fos != null){
        try {
            fos.close();
        }catch (IOException e){
            e.printStackTrace();
        }
    }
    if (fis != null){
        try {
            fis.close();
        }catch (IOException e){
            e.printStackTrace();
        }
    }
}

java基础学习-7_第5张图片

//jdk7的写法
//try后面的小括号中写创建对象的代码
//注意:只有实现了AutoCloseable接口的类,才能在小括号中创建对象
try(FileInputStream fis = new FileInputStream("C:\\Users\\xiaoyou\\Desktop\\宝的照片\\306751AF0EAB631BBBFAC17471282421.png");
	FileOutputStream fos = new FileOutputStream("C:\\Users\\xiaoyou\\Desktop\\copy.png");){
	int len;
	byte[] bytes=new byte[1024*1024*5];
	while((len = fis.read(bytes))  != -1){
   		 fos.write(bytes,0,len);
	}
}catch (IOException e){
    e.printStackTrace();
}

//JDK9:IO流中捕获异常的写法
FileInputStream fis = new FileInputStream("C:\\Users\\xiaoyou\\Desktop\\宝的照片\\306751AF0EAB631BBBFAC17471282421.png");
FileOutputStream fos = new FileOutputStream("C:\\Users\\xiaoyou\\Desktop\\copy.png");
try(fis,fos){
	int len;
	byte[] bytes=new byte[1024*1024*5];
	while((len = fis.read(bytes))  != -1){
   		 fos.write(bytes,0,len);
	}
}catch (IOException e){
    e.printStackTrace();
}

字符集

计算机存储规则:在计算机中,任意数据都是以二进制的形式来存储的
GB2312,1980年发布,1981年5月1日实施的简体中文汉字编码国家标准
收录了7445个图形字符,其中包括6763个简体汉字
有趣的冷知识
BIG5字符集:台湾地区繁体中文标准字符集,共收录13053个中文字,1984年实施。
GBK字符集
2000年3月17日发布,共收录21003个汉字,包含国家标准GB13000-1中的全部中日韩汉字,和BIG5编码中的所有汉字
windows系统默认使用的就是GBK(系统显示:ANSI)

Unicode字符集:国际标准字符集,它将世界各种语言的每一个字符定义一个唯一的编码,以满足跨语言、跨平台的文本信息转换

ASCII

128个数据(对于西方的sucker来说足够使用了)
存储英文的时候一个字节就足以
ASCII编码规则:前面补0,补齐8位
解码直接转,前面补不补零无所谓

GBK

英文用一个字节存储,完全兼容ASCII
GBK英文编码规则:不足8位,前面补0

汉字:两个字节存储
前面的字节叫做高位字节
后面的字节叫做低位字节
高位字节二进制一定以1开头,转成十进制之后是一个负数
汉字编码规则:不需要变动

小总结

1、在计算机中,热议数据都是以二进制的形式来存储的
2、计算机中最小的存储单元是一个字节
3、ASCII字符集中,一个英文占一个字节
4、简体中文版Windows,默认使用GBK字符集
5、GBK字符集完全兼容ASCII字符集
一个英文占一个字节,二进制第一位是0
一个中文占两个字节,二进制高位字节的第一位是1

Unicode

万国码
研发方:统一码联盟(也叫Unicode组织)
总部位置:美国加州
研发时间:1990年
发布时间:1994年发布1.0版本,期间不断添加新的蚊子
最新的版本是2022年9月13日发布的15.0版本
联盟组成:世界各地主要的电脑制造商、软件开发商、数据库开发商、政府部门、研究机构、国际机构及个人组织
UTF-8编码规则:用1~4个字节保存
ASCII一个字节
叙利亚文两个字节
简体中文三个字节
其他语言四个字节
java基础学习-7_第6张图片

小总结

1、Unicode字符集的UTF-8编码格式
一个英文占一个字节,二进制第一位是0,转成十进制是正数
一个中文占三个字节,二进制第一位是1,第一个字节转成十进制是负数

乱码出现的原因

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

如何不产生乱码

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

编码和解码

编码的方法

public byte[] getBytes()//使用默认方式进行编码
public byte[] getBytes(String charsetName)//使用指定方式进行编码

解码的方法

String(byte[] bytes)//使用默认方式进行解码
String(byte[] bytes, String charsetName)//使用指定方式进行解码
String str = "ai你哟";
byte[] bytes = str.getBytes(StandardCharsets.UTF_8);
System.out.println(Arrays.toString(bytes));//[97, 105, -28, -67, -96, -27, -109, -97]
String str2 = new String(bytes);
System.out.println(str2);

字符流

字符流的底层其实就是字节流
字符流=字节流+字符集

特点:
输入流:一次读一个字截,遇到中文时,一次读多个字节
输出流:底层会把数据按照指定的编码方式进行编码,变成字节再写到文件中

使用场景
对于纯文本进行读写操作

FileReader

1、创建字符输入流对象

public FileReader(File file)//创建字符输入流关联本地文件
public FileReader(String pathname)//创建字符输入流关联本地文件

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

2、读取数据

public int read()//读取数据,读到末尾返回-1
public int read(char[] buffer)//读取多个数据,读到末尾返回-1

按字节进行读取,遇到中文,一次读多个字节,读取后解码,返回一个整数
读到文件末尾了,read方法返回-1

3、释放资源

public int close()//释放资源/关流
FileReader fr = new FileReader("巴拉巴拉路径");
int ch;
while((ch = fr.read()) != -1){
	System.out.print(ch);
}
fr.close();

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

想看见中文汉字,还得把这些十进制数字进行强转

//空参
FileReader fr = new FileReader("");
int ch;
while((ch = fr.read()) != -1){
	System.out.print((char)ch);
}
fr.close();

reade(chars):读取数据,解码,强转三步合并了,把强转之后的字符放到数组当中

//有参
FileReader fr = new FileReader("巴拉巴拉路径");
char[] chars = new char[2];
int len;
while((len = fr.read(chars)) != -1){
	System.out.print(new String(chars,0,len));
}
fr.close();

字符流输出流写出数据

FileWriter构造方法

public FileWriter(File file)//创建字符输出流关联本地文件
public FileWriter(String pathname)//创建字符输出流关联文件
public FileWriter(File file,boolean append)//创建字符输出流关联本地文件,续写
public FileWriter(String, boolean append)//创建字符输出流关联本地文件,续写

FileWriter成员方法

void write(int c)//写出一个字符
void write(String str)//写出一个字符串
void write(string str, int off, int len)//写出一个字符串的一部分
void write(char[] cbuf)//写出一个字符数组
void write(char[] cbuf, int off, int len)//写出字符数组的一部分

FileWriter书写细节
1、创建字符输出流对象
细节1:参数是字符串表示的路径或者File对象都是可以的
细节2:如果文件不存在会创建一个新的文件,但是要保证父级路径是存在的
细节3:如果文件已经存在,则会清空文件,但是不想清空可以打开续写开关
2、写数据
细节:如果write方法的参数是整数,但是实际上写到本地文件中的是整数在字符集上对应的字符
3、释放资源
细节:每次使用完流之后都要释放资源

FileWriter fw = new FileWriter("C:\\Users\\xiaoyou\\Desktop\\demo.txt");
fw.write("阿巴阿巴");
fw.close();
        FileWriter fw = new FileWriter("C:\\Users\\xiaoyou\\Desktop\\demo.txt");
        char[] chars ={'a','c','e','q'};
        fw.write(chars);
        fw.close();

缓冲流

字节缓冲流

底层自带了长度为8192的缓冲区提高性能

public BufferedInputStream(InuputStream is)//把基本流包装成高级流,提高读取数据的性能
public BufferedOutputStream(OutputStream os)//把基本流包装成高级流,提高写出数据的性能
BufferedInputStream bis = new BufferedInputStream(new FileInputStream(""));
BufferedOutputStream bos = new BufferedOutputStream(FileOutputStream(""));
int b;
while((b = bis.read()) != -1){
    bos.write(b);
}
bos.close();
bis.close();
BufferedInputStream bis = new BufferedInputStream(new FileInputStream(""));
BufferedOutputStream bos = new BufferedOutputStream(FileOutputStream(""));
byte[] bytes = new byte[1024];
int b;
while((b = bis.read(bytes)) != -1){
    bos.write(bytes,0,b;
}
bos.close();
bis.close();

字符缓冲流

底层自带了长度为8192的缓冲区提高性能

字符缓冲流的构造方法

public BufferedReader(Reader r)//把基本流变成高级流
public BufferedWrite(Writer r)//把基本流变成高级流
public String readLine()//读取一行数据,如果没有数据可读了,会返回null
public void newLine()//跨平台的换行(不同的操作系统换行的代码不一样)
BufferedReader br = new BufferedReader(new FileReader(""));
String line = br.readLine();
System.out.println(line);
br.close();

细节:readline方法在读取的时候,一次读一整行,遇到回车换行结束,但是他不会把回车换行读到内存当中

BufferedReader br = new BufferedReader(new FileReader(""));
String line;
while(((line = br.readLine()) != null){
	System.out.println(line);
}
br.close();
BufferedWriter bw = new BufferedWriter(new FileWriter(""));
bw.write("你嘴角上扬的样子,百度搜索不到");
bw.newLine();
bw.write("以后如果我结婚了,你一定要来哦,没有新娘我会很尴尬");
bw.newLine();
bw.close();

小总结

1、缓冲流有几种?
字节缓冲输入流:BufferedInputStream
字节缓冲输出流:BufferedOutputStream
字符缓冲输入流:BufferedReader
字符缓冲输出流:BufferedWrite

2、缓冲流为什么能提高性能
缓冲流自带8KB缓冲区
可以显著提高字节流的读写性能
对于字符流提升不明显,对于字符缓冲流而言关键点是两个特有的方法

3、字符缓冲流两个特有的方法是什么?
字符缓冲输入流BufferedReader:readLine()
字符缓冲输出流BufferedWriter:newLine()

转换流

是字符流和字节流之间的桥梁java基础学习-7_第7张图片

作用1:指定字符集读写(已淘汰
作用二:字节流想要使用字符流中的方法

小练习

1

需求:利用转换流按照指定字符编码读取

InputStreamReader isr = new InputStreamReader(new FileInputStream(""),"GBK");
int ch;
while((ch = isr.read()) != -1){
	System.out.print((char)ch);
}
isr.close();
FileReader fr = new FileReader("",Charset.forName("GBK"));
int ch;
while((ch = fr.read()) != -1){
	System.out.print((char)ch);
}
fr.close();

2

需求:利用转换流按照指定字符编码写出

OutputSreamWriter osw = new OutputStreamWriter(new FileOutputStream(""),"GBK");
osw.write("你好你好");
osw.close();
FileWriter fw = new FileWriter("",Charset.forName("GBK"));
fw.write("你好你好");
fw.close();

3

需求:将本地文件中的GBK文件,转成UTF-8

InputStreamReader isr = new InputStreamReader(new FileInputStream(""),"GBK");
OutputStreamReader osr = new OutputStreamReader(new FileOutputStream(""),"UTF-8");
int b
while((b = isr.read()) != -1){
	osw.write(b);
}
osw.close();
isr.close();
FileReader fe = new FileReader("",Charset.forName("GBK"));
FileWriter fw = new FIleWriter("",Charset.forName("UTF-8"));
int b;
while((b = fe.read()) != -1){
	fw.write(b);
}
fw.close();
fr.close();

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

可以把java中的对象写到本地文件中

public ObjectOutputStream(OutputStream out)//把基本流包装成高级流
public final void writeObject(Object obj)//把对象序列化(写出)到文件中去
Student stu = new Student("zhangsan",23);
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(""));
oos.writeObject(stu);
oos.close();

序列化流的小细节
使用对象输出流将对象保存到文件时会出现NotSerializableException异常
解决方案:需要让Javabean类实现Serializable接口

关于Serializable接口
它里面是没有抽象方法的,标记型接口
一旦实现了这个接口,那么就表示当前的Student类可以被序列化
(一个物品的合格证)

反序列化流

可以把序列化到本地文件中的对象,读取到程序中来

public ObjectInputStream(InputStream out)//把基本流变成高级流
public Object readObject()//把序列化到本地文件中的对象,读取到程序中来
ObjectInputStream ois = new ObjectInputStream(new FileInputStream(""));
Object o = ois.readObject();
System.out.println(o);
ois.close();

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

当使用序列化流存储信息时会附加上一个计算出来的版本号,如是对序列化信息进行修改,版本号会进行改变,而存储的版本号不变,两者不相同,代码报错
原因:文件中的版本号,跟Javabean的版本号不匹配
解决方案:手动固定版本号

private static final long serialVersionUID = 1L;

java基础学习-7_第8张图片
第二种方法,在这两个打勾的地方加上勾,系统会自动为其加上固定的版本号

第三种方法,抄别人的(ctrl+n,搜索已经定义好的类,抄类的版本号,不过那个是定义好的类的,需要改一改)

transient:瞬态关键字
作用:不会把当前属性序列化到本地文件中

细节汇总

1、使用序列化流将对象写到文件时,需要让Javabean类实现Serializable接口
否则,会出现NotSerializableException异常

2、序列化流写到文件中的数据是不能修改的,一旦修改就无法再次读回来了

3、序列化对象后,修改了Javabean类,再次反序列化,会不会有问题?
会出现问题,会抛出InvalidClassException异常
解决方案:给Javabean类添加serialVersionUID(序列号版本号)

4、如果一个对象中的某个成员变量的值不想被序列化,又该如何实现呢?
解决方案:给该成员变量加transient关键字修饰,该关键字标记的成员变量不参与序列化过程

字节打印流

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

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

//构造方法
public PeintStream(OutputStream/File/Stirng)//关联字节输出流/文件/文件路径
public PrintStream(String fileName, Charset charset)//指定字符编码
public PrintStream(OutputStream out, boolean autoFlush)//自动刷新
public PrintStream(OutputStream out, boolean autoFlush, Stirng encoding)//指定字符编码切自动刷新

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

//成员方法
public void write(int b)//常规方法:规则跟之前一样,将指定的字节写出
public void println(Xxx xx)//特有方法:打印任何数据,自动刷新,自动换行
public void print(Xxx xx)//特有方法:打印任意数据,不换行
public void printf(Stirng format, Object... args)//特有方法:带有占位符的打印语句,不换行
PrintStream ps = new PrintStream(new FileOutputStream(""),true,Charset.forName("UTF-8"));
ps.println(97);//写出+自动刷新+自动换行
ps.print(true);
ps.printf("%s 爱上了%s","阿珍","阿强");
ps.close();

字符打印流

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

//成员方法
public PrintWriter(Write/File/String)//关联字节输出流/文件/文件路劲
public PrintWriter(String fileName, Charset charset)//指定字符编码
public PrintWriter(Write w, boolean autoFlush)//自动刷新
public PrintWriter(OutStream out, boolean autoFlush, Charset charset)//指定字符编码且自动刷新
//构造方法
public void write(...)//常规方法:规则跟之前
public void println(Xxx xx)//特有方法:打印任意类型的数据并且换行
public void print(Xxx xx)//特有方法:打印任意类型的数据,不换行
public void printf(String format, Object... args)//特有方法:带有占位符的打印语句
PrintWriter pw = new PrintWriter(new FileWriter("",true));
pw.println("今天你终于叫我名字了,虽然叫错了,但是没关系,我马上改");
pw.print("Hello");
pw.printf("%s 爱上了%s\",\"阿珍\",\"阿强");
pw.close();
//获取打印流的对象,此打印流在虚拟机启动的时候,由虚拟机创建,默认指向控制台
//特殊的打印流(标准输出流),系统中的标准输出流,是不能关闭的,在系统中是唯一的
PrintStream ps = System.out;

//调用打印流中的方法println
//写出苏剧,自动换行,自动刷新
ps.println("123")

小总结

1、打印流有几种?各自有什么特点?
有字节打印流和字符打印流两种
打印流不操作数据源,只能操作目的地
字节打印流:默认自动刷新,特有的println自动换行
字符打印流:自动率先你需要开,特有的println自动换行

解压缩流

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

public static void main(Stirng[] args){
	//解压的本质:把压缩包里面的每一个文件或者文件夹读取出来,按照层级拷贝到目的地当中
	//创建一个解压缩流用来读取压缩包中的数据
	File src = new File("***.zip");
	//要先获取到压缩包里面的每一个zipentry对象
	//表示当前在压缩包中获取到的文件或者文件夹
	File dest = new File("***");
}
public static void unzip(File src,File dest) throws IOException{
	ZipInputStream zip = new ZipInputStream(new FileInputStream(src));
	ZipEntry entry;
	while((entry = zip.getNetxtEntry()) != null){
		System.out.println(entry);
		if(entry.isDirectory()){
			//文件夹:需要在目的地dest处创建一个同样的文件夹
			Fi le file = new File(dest, entry.toStirng());
			file.mkdirs();
		}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();
		}
	}
}
	

压缩流

压缩里面的每一个文件或文件夹都是一个ZipEntry对象

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

压缩一个文件

把一个txt文件打包成一个压缩包

public static void main(String[] args){
	//1、创建FIle对象表示要压缩的文件
	File src = new File("**");
	//2、创建File对象表示压缩包的文件
	File dest = new File("**");
	//3、调用方法用来压缩
	toZip(src,dest);
}

//作用:压缩
///参数一:表示要压缩的文件
//参数二:表示压缩包的位置
Public static void toZip(File src, File dest){
	//1、创建压缩流关联压缩包
	ZipOutputStream zos = new ZipOutputStream(new FileOutputStream(new File(dest,"a.zip")));
	//2、创建ZipEntry对象,表示压缩包里面的每一个文件和文件夹
	//参数:压缩包里面的路径
	ZipEntry entry = new ZipEntry("a.txt");
	//3、把ZipEntry对象放到压缩包当中
	zos.putNextEntry(entry);
	FileInputStream fis = new FileInputStream(src);
	int b;
	while((b = fis.read()) != -1){
		zos.write(b);
	}
	zos.closeEntry();
	zos.close();
}

压缩文件夹

public static void main(String[] args){
	//1、创建File对象表示要压缩的文件
	File src = new File("***");
	//2、创建File对象表示压缩包放在哪里(压缩包的父级路径)
	File destParent = src.getParentFile();
	//3、创建File对象表示压缩包的路径
	File dest = new File(destParent,src.getName()+".zip");
	//4、创建压缩流关联压缩包
	ZipOutputStream zos = new ZipOutputStream(new FileOutputStream(dest));
	//5、获取src里面的每一个文件,变成ZipEntry对象,放入到压缩包当中
	toZip(src,zos,src.getName);
	//6、释放资源
	zos.close();
}
//作用:获取src里面的每一个文件,变成ZipEntry对象,放入到压缩包当中
//参数一:数据源
//参数二:压缩流
//参数三:压缩包内部的路径
public static void toZip(File src, ZipOutputStream zos, String name){
	//
	for(File file : files){
		if(file.isFile()){
			ZipEntry entry - new ZipEntry(name+"\\"+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());
		}
}

常用工具包

Commons-io

Commons-io是apache开源基金组织提供的一组有关IO操作的开源工具包
作用:提高IO流的开发效率

使用步骤

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

FileUtils类(文件相关)

static void copyFile(File srcfile, File destFile)//复制文件
static void copyDirectory(File srcDir, File destDir)//复制文件夹
static void copyDirectoryToDirectory(File srcDir, File destDir)//复制文件夹
static void deleteDirectory(File directory)//删除文件夹
static void cleanDirectory(File directory)//清空文件夹
static String readFileToString(File file, Charset encoding)//读取文件中的数据变成字符串
static void write(File file, CharSequence data, String encoding)//写出数据

copyDirectoryToDirectory是将目标文件拷贝到指定文件里面

IOUtils类(流相关)

public static int copy(InputStream input, OutputSream output)//复制文件
public static int copyLarge(Reader input, Writer output)//复制大文件
public static String readLines(Reader input)//读取数据
public static void write(String data, OutputStream output)//写出数据

下载

官网:commons-io下载官网
下载完成以后需要放进代码块中并进行导入
选中commons-io包,右键点击“添加为库”,无脑确定确定确定确定,还有这个。java,准备起飞
导入成功以后,jar包前会有这个标志请添加图片描述

使用

File src = new File("***");
File dest = new File("***");
FileUtils.copyFile(src,dest);

Hutool

GitHub国际性的代码托管平台,最大的同性交友网站(正经的)

hutool是俺们天朝大国滴,也称为糊涂包,男的糊涂

相关类:
IoUtil————————流操作工具类
FileUtil———————文件读写和操作的工具类
FileTypeUtil—————文件雷子那个判断工具类
WatchMonitor————目录、文件监听
ClassPathResource—针对ClassPath中资源的访问封装
FileReader—————封装文件读取
FileWriter——————封装文件写入

Hutool官网:Hutool官网————没什么大用
API文档:API帮助文档
中文使用文档:中文使用文档

使用

file:根据参数创建一个file对象
touch:根据参数创建文件

writeLines:把集合中的数据写出到文件中,覆盖模式
appendLines:把集合中的数据写出到文件中,续写模式
readLines:指定字符编码,把文件中的数据,读到集合中
readUtf8Lines:按照UTF-8的形式,把文件中的和数据,读到集合中去

copy:拷贝文件或者文件夹

File file = FileUtil.file("D:\\", "aaa", "bbb", "a.txt");
System.out.println(file);//D:\aaa\bbb\a.txt

File touch = FileUtil.touch(file);
System.out.println(touch);

ArrayList<String> list = new ArrayList<>();
list.add("aaa");
list.add("aaa");
list.add("aaa");

File file2 = FileUtil.writeLines(list, "***", "UTF-8");
System.out.println(file2);

项目练习

制造假数据

需求:制造假数据也是开发中的一个能力,在各个网上爬取数据,是其中的一个方法

获取姓氏:
https://hanyu.baidu.com/shici/detail?pid=0b2f26d4c0ddb3ee693fdb1137ee1b0d&from=kg0

男生名字
http://www.haoming8.cn/baobao/10881.html

女生名字
http://www.haoming8.cn/baobao/7641/html

第一部:

import cn.hutool.core.io.FileUtil;
import org.apache.commons.io.FileUtils;

import java.io.*;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLConnection;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.regex.Matcher;
import java.util.regex.Pattern;



public class Main {
    public static void main(String[] args) throws IOException {
        //1、记录变量记录网址
        String familyNameNet = "https://hanyu.baidu.com/shici/detail?pid=0b2f26d4c0ddb3ee693fdb1137ee1b0d&from=kg0";
        String boyNameNet = "http://www.haoming8.cn/baobao/10881.html";
        //String girlNameNet = "http://www.haoming8.cn/baobao/7641/html";
        //2、爬取数据,把网址上所有的数据拼接成一个字符串
        String familyNameStr = webCrawler(familyNameNet);
        String boyNameStr = webCrawler(boyNameNet);
        //String girlNameStr = webCrawler(girlNameNet);

        //3、通过正则表达式,把其中符合要求的数据获取出来
        ArrayList<String> familyNameTempList = getData(familyNameStr,"(.{4})(,|。)",1);

        System.out.println(familyNameTempList);
    }
    //作用:根据正则表达式获取字符串中的数据
    //参数一:完整的字符串
    //参数二:正则表达式
    //参数三:获取的部分

    private static ArrayList<String> getData(String str, String regex,int index) {
        //1、创建集合存放数据
        ArrayList<String> list = new ArrayList<>();
        //2、按照正则表达式的规则,去获取数据
        Pattern pattern = Pattern.compile(regex);
        //按照pattern的规则,到str当中获取数据
        Matcher matcher = pattern.matcher(str);
        while (matcher.find()){
            list.add(matcher.group(index));
        }
        return list;
    }

    //作用:从网络上爬取数据,把数据拼接成
    //形参:网址
    //返回值:爬取到的所有数据
    public static String webCrawler(String net) throws IOException {
        //1、定义StringBuilder拼接爬取到的数据
        StringBuilder sb = new StringBuilder();
        //2、创建一个URL对象
        URL url = new URL(net);
        //3、链接上这个网址
        //细节:保证网络是通畅的,而且这个网址是可以链接上的
        URLConnection conn = url.openConnection();
        //4、读取数据
        InputStreamReader isr = new InputStreamReader(conn.getInputStream());
        int ch;
        while((ch = isr.read()) != -1){
            sb.append((char)ch);
        }
        //5、释放资源
        isr.close();
        //6、读取到的数据返回
        return sb.toString();
    }
}

第二版

import cn.hutool.core.io.FileUtil;
import org.apache.commons.io.FileUtils;

import java.io.*;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLConnection;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.regex.Matcher;
import java.util.regex.Pattern;



public class Main {
    public static void main(String[] args) throws IOException {
        //1、记录变量记录网址
        String familyNameNet = "https://hanyu.baidu.com/shici/detail?pid=0b2f26d4c0ddb3ee693fdb1137ee1b0d&from=kg0";
        String boyNameNet = "http://www.haoming8.cn/baobao/10881.html";
        //String girlNameNet = "http://www.haoming8.cn/baobao/7641/html";
        //2、爬取数据,把网址上所有的数据拼接成一个字符串
        String familyNameStr = webCrawler(familyNameNet);
        String boyNameStr = webCrawler(boyNameNet);
        //String girlNameStr = webCrawler(girlNameNet);

        //3、通过正则表达式,把其中符合要求的数据获取出来
        ArrayList<String> familyNameTempList = getData(familyNameStr,"(.{4})(,|。)",1);
        ArrayList<String> boyNameTempList = getData(boyNameStr,"([\\u4E00-\\u9FA5]{2})(、|。)",1);
        //ArrayList girlNameTempList = getData(girlNameStr,"(..){4}..",0)

        //4、处理数据
        ArrayList<String> familyNameList = new ArrayList<>();
        for (String str : familyNameTempList){
            for (int i = 0; i < str.length() ; i++) {
                char c = str.charAt(i);
                familyNameList.add(c+"");
            }
        }

        //男名去重
        ArrayList<String> boyNameList = new ArrayList<>();
        for (String str : boyNameList){
            if(boyNameList.contains(str)){
                boyNameList.add(str);
            }
        }
        //女名将每一个元素用空格进行切割,得到每一个女生的名字
        ArrayList<String> girlNameList = new ArrayList<>();

        for (String str : girlNameList) {
            String[] arr = str.split(" ");
            for (int i = 0; i < arr.length; i++) {
                girlNameList.add(arr[i]);
            }
        }
    }
    //作用:根据正则表达式获取字符串中的数据
    //参数一:完整的字符串
    //参数二:正则表达式
    //参数三:获取的部分

    private static ArrayList<String> getData(String str, String regex,int index) {
        //1、创建集合存放数据
        ArrayList<String> list = new ArrayList<>();
        //2、按照正则表达式的规则,去获取数据
        Pattern pattern = Pattern.compile(regex);
        //按照pattern的规则,到str当中获取数据
        Matcher matcher = pattern.matcher(str);
        while (matcher.find()){
            list.add(matcher.group(index));
        }
        return list;
    }

    //作用:从网络上爬取数据,把数据拼接成
    //形参:网址
    //返回值:爬取到的所有数据
    public static String webCrawler(String net) throws IOException {
        //1、定义StringBuilder拼接爬取到的数据
        StringBuilder sb = new StringBuilder();
        //2、创建一个URL对象
        URL url = new URL(net);
        //3、链接上这个网址
        //细节:保证网络是通畅的,而且这个网址是可以链接上的
        URLConnection conn = url.openConnection();
        //4、读取数据
        InputStreamReader isr = new InputStreamReader(conn.getInputStream());
        int ch;
        while((ch = isr.read()) != -1){
            sb.append((char)ch);
        }
        //5、释放资源
        isr.close();
        //6、读取到的数据返回
        return sb.toString();
    }
}

最终版

import cn.hutool.core.io.FileUtil;
import org.apache.commons.io.FileUtils;

import java.io.*;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLConnection;
import java.nio.charset.StandardCharsets;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;



public class Main {
    public static void main(String[] args) throws IOException {
        //1、记录变量记录网址
        String familyNameNet = "https://hanyu.baidu.com/shici/detail?pid=0b2f26d4c0ddb3ee693fdb1137ee1b0d&from=kg0";
        String boyNameNet = "http://www.haoming8.cn/baobao/10881.html";
        //String girlNameNet = "http://www.haoming8.cn/baobao/7641/html";
        //2、爬取数据,把网址上所有的数据拼接成一个字符串
        String familyNameStr = webCrawler(familyNameNet);
        String boyNameStr = webCrawler(boyNameNet);
        //String girlNameStr = webCrawler(girlNameNet);

        //3、通过正则表达式,把其中符合要求的数据获取出来
        ArrayList<String> familyNameTempList = getData(familyNameStr,"(.{4})(,|。)",1);
        ArrayList<String> boyNameTempList = getData(boyNameStr,"([\\u4E00-\\u9FA5]{2})(、|。)",1);
        //ArrayList girlNameTempList = getData(girlNameStr,"(..){4}..",0)

        //4、处理数据
        ArrayList<String> familyNameList = new ArrayList<>();
        for (String str : familyNameTempList){
            for (int i = 0; i < str.length() ; i++) {
                char c = str.charAt(i);
                familyNameList.add(c+"");
            }
        }

        //男名去重
        ArrayList<String> boyNameList = new ArrayList<>();
        for (String str : boyNameList){
            if(boyNameList.contains(str)){
                boyNameList.add(str);
            }
        }
        //女名将每一个元素用空格进行切割,得到每一个女生的名字
        ArrayList<String> girlNameList = new ArrayList<>();

        for (String str : girlNameList) {
            String[] arr = str.split(" ");
            for (int i = 0; i < arr.length; i++) {
                girlNameList.add(arr[i]);
            }
        }
        //5、生成数据
        //姓名(唯一)-性别-年龄
        ArrayList<String> list = getInfos(familyNameList, boyNameList, girlNameList, 70, 50);
        System.out.println(list);
    }
    //作用:获取男生和女生的信息:张三-男-23
    //形参:
    //参数一:装着形式
    //参数二:装着男生名字的集合
    //参数三:装着女生名字的集合
    //参数四:男生的个数
    //参数五:女生的个数
    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(familyNameList);
            Collections.shuffle(boyNameList);
            boyhs.add(familyNameList.get(0)+boyNameList.get(0));
        }
        //2、生成女生不重复的名字
        HashSet<String> girlhs = new HashSet<>();
        while(true){
            if(girlhs.size() == girlCount){
                break;
            }
            Collections.shuffle(familyNameList);
            Collections.shuffle(girlNameList);
            boyhs.add(familyNameList.get(0)+girlNameList.get(0));
        }
        ArrayList<String> list = new ArrayList<>();
        Random r = new Random();
        for (String boyName : boyhs) {
            int age = r.nextInt(10) + 18;
            list.add(boyName+"-男-"+age);
        }
        for (String girlName : girlhs) {
            int age = r.nextInt(8) + 18;
            list.add(girlName+"-女-"+age);
        }
        return list;
    }


    //作用:根据正则表达式获取字符串中的数据
    //参数一:完整的字符串
    //参数二:正则表达式
    //参数三:获取的部分

    private static ArrayList<String> getData(String str, String regex,int index) {
        //1、创建集合存放数据
        ArrayList<String> list = new ArrayList<>();
        //2、按照正则表达式的规则,去获取数据
        Pattern pattern = Pattern.compile(regex);
        //按照pattern的规则,到str当中获取数据
        Matcher matcher = pattern.matcher(str);
        while (matcher.find()){
            list.add(matcher.group(index));
        }
        return list;
    }

    //作用:从网络上爬取数据,把数据拼接成
    //形参:网址
    //返回值:爬取到的所有数据
    public static String webCrawler(String net) throws IOException {
        //1、定义StringBuilder拼接爬取到的数据
        StringBuilder sb = new StringBuilder();
        //2、创建一个URL对象
        URL url = new URL(net);
        //3、链接上这个网址
        //细节:保证网络是通畅的,而且这个网址是可以链接上的
        URLConnection conn = url.openConnection();
        //4、读取数据
        InputStreamReader isr = new InputStreamReader(conn.getInputStream());
        int ch;
        while((ch = isr.read()) != -1){
            sb.append((char)ch);
        }
        //5、释放资源
        isr.close();
        //6、读取到的数据返回
        return sb.toString();
    }
}

糊涂包爬取数据

//1、定义网址
String familyNameNet = "https://hanyu.baidu.com/shici/detail?pid=0b2f26d4c0ddb3ee693fdb1137ee1b0d&from=kg0";
String boyNameNet = "http://www.haoming8.cn/baobao/10881.html";
String girlNameNet = "http://www.haoming8.cn/baobao/7641/html";
//2、爬取数据
String familyNameStr = HttpUtil.get(familyNameNet);
String boyNameStr = HttpUtil.get(boyNameNet);
String girlNameStr = HttpUtil.get(girlNameNet);
//3、正则表达式
List<String> familyNameTempList = ReUtil.findAll("(.{4})(,|。)", familyNameNet, 1);
List<String> boyNameTempList = ReUtil.findAll("[\\\\u4E00-\\\\u9FA5]{2})(、|。)", boyNameStr, 1);
List<String> girlNameTempList = ReUtil.findAll("(..){4}..", girlNameStr, 1);

随机点名器

需求:
txt文件中实现准备好一些学生信息,每个学生的信息独占一行
要求1:每次被点到的学生,再次被点到的概率在原先的基础上降低一半
举例:80个学生,点名5次,每次都点到小A,概率变化情况如下
第一次每人概率:1.25%
第二次小A概率:0.625%————其他学生概率:1.2579%
第三次小A概率:0.3125%———其他学生概率:1.261867%
第四次小A概率:0.15625%———其他学生概率:1.2638449%

package com.example.demo;

import java.io.*;
import java.util.ArrayList;
import java.util.Arrays;

public class Text {
    public static void main(String[] args) throws IOException {
        //1、把文件中所有的学生信息读取到内存中
        ArrayList<Student> list = new ArrayList<>();
        BufferedReader br = new BufferedReader(new FileReader("####"));
        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]));
            list.add(stu);
        }
        br.close();
        //2、计算权重的总和
        double weight = 0;
        for (Student stu : list) {
            weight += stu.getWeight();
        }
        //3、计算每一个人的实际占比
        double[] arr = new double[list.size()];
        int index = 0;
        for (Student stu : list) {
            arr[index++] = stu.getWeight() / weight;
        }

        //4、计算每一个人的权重占比范围
        for (int i = 1; i < arr.length; i++) {
            arr[i] = arr[i] + arr[i-1];
        }
        //5、随机抽取
        //获取一个0.0~1.0之间的随机数
        double number = Math.random();
        //判断number在arr中的位置
        //二分查找
        //方法间返回:    - 插入点 - 1

        //获取number这个数据在数组当中的插入点位置
        int result = Arrays.binarySearch(arr, number) -1 ;
        Student stu = list.get(result);

        //6、修改当前学生的权重
        double w = stu.getWeight()/2;
        stu.setWeight(w);

        //7、把集合中的数据再次写到文件中
        BufferedWriter bw = new BufferedWriter(new FileWriter("###"));
        for (Student s : list) {
            bw.write(s.toString());
            bw.newLine();
        }
        bw.close();
    }
}

package com.example.demo;

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

    public Student() {
    }

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

    public int getAge() {
        return age;
    }

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

    public String getName() {
        return name;
    }

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

    public String getGender() {
        return gender;
    }

    public void setGender(String gender) {
        this.gender = gender;
    }

    public double getWeight() {
        return weight;
    }

    public void setWeight(double weight) {
        this.weight = weight;
    }
    public String toString(){
        return name+"-"+gender+"-"+age+"-"+weight;
    }
}

你可能感兴趣的:(黑马java,java,学习)