Java9

(一)、stream流

1.先得到一条stream流,并把数据放上去
2.利用stream流中的Api进行各种操作(中间方法,终结方法)

获取stream流
在这里插入图片描述
使用of时,如果将基本类型的数组放进去,会将整个数组当做一个元素,放进stream中
传递数组必须传引用数据类型

1.1 Stream流的中间方法和终结方法

中间方法:
在这里插入图片描述
注:修改stream流中的数据,不会影响原数组或集合中的数据
中间方法会返回新的stream流,原来的stream流只会用一次,建议链式编程
合并时,如果两个流的类型不一样,会提升为两个类型的父类(类型的提升)

终结方法:
在这里插入图片描述
toArray的返回值是装着流中所有数据的数组,这个方法中的形参是创建一个指定类型数组
底层:依次得到每一个数据,放进数组当中

collect方法注意点:
收集到map集合中,键不能重复,否则会报错

练习1:
在这里插入图片描述

代码:

package day3next;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;

public class stream1 {
    public static void main(String[] args) {
        ArrayList<Integer> a1=new ArrayList<>();
        /*a1.add(1);
        a1.add(2);
        a1.add(3);
        a1.add(4);
        a1.add(5);
        a1.add(6);
        a1.add(7);
        a1.add(8);
        a1.add(9);
        a1.add(10);
        add方法一个一个添加数据
        */
        Collections.addAll(a1,1,2,3,4,5,6,7,8,9,10);
        //批量添加数据
        //System.out.println(a1);

        //a1.stream().filter(n->n%2==0).forEach(n-> System.out.println(n));
        //先把数组放到stream流中,再用filter过滤,留下偶数,最后用foreach打印
        List<Integer> list1 = a1.stream()
                .filter(n -> n % 2 == 0)
                .collect(Collectors.toList());
        //最后用collect收集数据,收集到list数组中
        System.out.println(list1);
    }
}


练2:
在这里插入图片描述

package day3next;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Map;
import java.util.function.Function;
import java.util.stream.Collectors;

public class stream2 {
    public static void main(String[] args) {
        ArrayList<String> list1=new ArrayList<>();
        Collections.addAll(list1,"zhangsan,23","lisi,24","wangwu,25");
        //System.out.println(list1);

//        list1.stream().filter(s->Integer.parseInt(s.split(",")[1])>=24)
//                .collect(Collectors.toMap(new Function<String, String>() {
//                    @Override
//                    public String apply(String s) {
//                        return s.split(",")[0];
//                    }
//                }, new Function<String, Integer>() {
//                    @Override
//                    public Integer apply(String s) {
//                        return Integer.parseInt(s.split(",")[1]);
//                    }
//                })); 匿名内部类的具体写法,一般使用lambda表达式。
//                toMap中两个参数,第一个表示的是键的规则,第二个表示的是值的规则

        Map<String, Integer> maplist1 = list1.stream().filter(s -> Integer.parseInt(s.split(",")[1]) >= 24)
                .collect(Collectors.toMap(s -> s.split(",")[0], s -> Integer.parseInt(s.split(",")[1])));

        System.out.println(maplist1);
    }
}

练3:
在这里插入图片描述

package day3next;

import javafx.print.Collation;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;

public class stream3 {
    public static void main(String[] args) {
        ArrayList<String> list1=new ArrayList<>();
        ArrayList<String> list2=new ArrayList<>();
        Collections.addAll(list1,"蔡坤坤,23","刘不甜,24","王三五,25","杨七六,26","陈七,27","沙八,28");
        Collections.addAll(list2,"张一,20","陈二,21","王三,22","杨四,22","杨五,23","赵七,24");

        Stream<String> man = list1.stream().filter(s -> s.split(",")[0].length() == 3)
                .limit(2);

        Stream<String> woman = list2.stream().filter(s -> s.split(",")[0].startsWith("杨"))
                .skip(1);
        //stratWith筛选开头为杨的人

        List<Actor> listall = Stream.concat(man, woman)
                .map(s -> new Actor(s.split("',")[0], Integer.parseInt(s.split(",")[1])))
                .collect(Collectors.toList());
        //使用concat合并两个流,然后再使用map将数据变成actor对象
        System.out.println(listall);
    }
}

(二)、方法引用

把已有方法拿过来使用,当做函数式接口中抽象方法的方法体

要求:
1.引用处必须是函数式接口
2.被引用方法必须已经存在
3.被引用方法的形参和返回值需要跟抽象方法保持一致
4.被引用的方法必须满足当前需求

调用格式:类名::方法名
::是方法引用符

2.1 方法引用的分类

1.引用静态方法
格式:类名::静态方法

package yingyong;

import java.util.ArrayList;
import java.util.Collections;

public class yy1 {
    public static void main(String[] args) {
        ArrayList<String> list=new ArrayList<>();
        Collections.addAll(list,"1","2","3","6");

        list.stream()
                .map(Integer::parseInt)
                .forEach(s-> System.out.println(s));
        //直接引用Integer中的parseInt方法,将list中的字符串转为整数
    }
}

2.引用成员方法
格式:对象::成员方法
在这里插入图片描述

静态方法是没有this的,如果要调用本类静态方法,则直接创建一个本类的对象进行调用

3.引用构造方法

格式:类名::new

4.类名引用成员方法
格式:类名::成员方法

特殊规则:被引用方法的形参,需要跟抽象方法的第二个形参到最后一个形参保持一致,返回值需要保持一致
其他规则一样
在这里插入图片描述
不能引用所有类中的成员方法

5.引用数组的构造方法
格式:数据类型[]::new
数组的类型,需要跟流中的数据类型保持一致

数组底层存在构造方法

(三)、异常

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

学会出了异常如何处理

Error:代表系统级别的错误
是sun公司自己使用的,开发人员不用管

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

RuntimeException:运行时出现的异常,编译阶段不会出现异常提醒

编译时异常:编译阶段就出现异常提醒
在这里插入图片描述

3.1 编译时异常和运行时异常

编译时异常:在编译阶段,必须手动处理,不然报错。主要是提醒程序员检查本地信息。

运行时异常:编译阶段不用处理,代码运行时才会出现

3.2 异常的作用

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

3.3 异常的处理方式

  1. JVM默认的处理方式:
    把异常的名称,异常原因以及异常出现的位置等信息输出在控制台
    程序停止运行,下面的代码不执行

  2. 自己处理(捕获异常)

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

目的:当代码出现异常,程序可以继续往下执行

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

问2:如果try中遇到多个问题,怎么执行?
会写多个 catch与之对应
若这些异常存在父子关系,则父类一定要写在下面
jdk7后,可以在catch中同时捕获多个异常,中间用 | 隔开

问3:如果try中遇到的问题没有被捕获,怎么执行?
相当于try catch白写,最终交给虚拟机处理

问4:如果try中遇到了问题,那么try下面的代码还会执行吗
下面的代码不会执行,会直接跳转到对应的catch当中,执行catch里的语句体
若没有与之对应的catch语句体,那么还是交给虚拟机处理

  1. 抛出处理
    throws :写在方法定义处,表示声明一个异常
    告诉调用者,本方法可能有的异常
    编译时异常必须要写,运行时异常可以不写

    格式:
    在这里插入图片描述

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

    对调用者的异常需要进行处理,不然最终会交给虚拟机处理

    在这里插入图片描述

3.4 异常中的常见方法

在这里插入图片描述
Throwable类中的方法

printStackTrace只是打印错误信息,不会停止程序运行。以红色字体将错误输出在控制台

练习:
在这里插入图片描述

测试代码:

package yingyong;

import java.util.Scanner;

public class g1 {
    public static void main(String[] args) {
        Scanner sc=new Scanner(System.in);
        GirlFriend gf =new GirlFriend();

        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("年龄格式有误,请输入数字");
            }catch (RuntimeException e){
                System.out.println("姓名的长度或年龄范围错误");
            }
        }
        System.out.println(gf);
    }
}

GrilFriend类:

package yingyong;

public class GirlFriend {
    private String name;
    private  int age;

    public GirlFriend() {
    }

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

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

    /**
     * 设置
     * @param name
     */
    public void setName(String name) {
        int len=name.length();
        if (len<3 || len>10){
            throw new  RuntimeException();
        }

        this.name = name;
    }

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

    /**
     * 设置
     * @param age
     */
    public void setAge(int age) {
        if (age<18 ||age>40){
            throw new RuntimeException();
        }
        this.age = age;
    }

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

3.5 自定义异常

1.定义异常类
2.写继承关系
3.空参构造
4.带参构造

运行时异常继承:RuntimeException
编译时异常继承:Exception

(四)、File

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

构造方法 :
在这里插入图片描述
后两个方法都是路径的拼接

绝对路径:带盘符的路径
相对路径:不带盘符,默认在当前项目下找

4.1 成员方法

判断和获取:
在这里插入图片描述
length方法 只能返回文件的大小 ,单位是字节
getName 如果是文件,会返回文件名字和后缀名、拓展名
如果是文件夹,只会返回名字

创建和删除:
在这里插入图片描述
delete方法默认只能删文件和空文件夹,直接删除不走回收站
不能删除有内容的文件夹

createNewFile 如果当前路径表示的文件不存在,则创建成功,方法返回true。反之,不成功
如果父级路径不存在,会有异常IOExpection
此方法创建的一定是文件,如果路径中不包含后缀名,则创建的是一个没有后缀的文件

mkdir:在windows中,路径是唯一的。只能创建单级文件夹

获取并遍历:
在这里插入图片描述
在这里插入图片描述

注:
在这里插入图片描述

练1:删除一个多级文件夹:使用递归思想

package filedemo;

import java.io.File;

public class file2 {
    public static void main(String[] args) {
    File file =new File("D:\\aaa\\src");
    delete(file);
    }

    public static void delete(File src){
    //1.先进入文件夹删除里面的所有内容
        //进入src
        File[] files =src.listFiles();
        //遍历里面的内容
        for (File file : files) {
            if (file.isFile()){
                file.delete();//判断是文件还是文件夹,文件就删除
            }else {
            delete(file);
                //是文件夹就递归
            }
        }
        //2.再删除自己
        src.delete();
    }
}

练2:统计文件总大小

package filedemo;

import java.io.File;

public class file3 {

    public static void main(String[] args) {
        File f1=new File("D:\\aaa");
        long len1 = getLen(f1);
        System.out.println(len1);
    }

    public static long getLen(File src){
        //1.定义变量len用来记录文件的大小
        long len=0;
        //2.进入文件夹aaa
        File[] files=src.listFiles();
        //3.遍历
        for (File file : files) {
            if(file.isFile()){
                len+=file.length();
            }else {
                len+=getLen(file);
                //递归传递的参数是子类文件夹
                //递归后len是一个新的变量,所以返回值是返回给调用者也就是getLen(file)
            }
        }
        return  len;
    }
}

练3:
在这里插入图片描述

package filedemo;

import java.io.File;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;

public class file4 {
    public static void main(String[] args) {
        File file=new File("D:\\aaa");

        HashMap<String, Integer> result = getCount(file);
        System.out.println(result);

    }

    //统计一个文件夹中各种文件的个数
    //返回值:用来统计的map集合
    //键:后缀名,值:次数
    public static  HashMap<String,Integer>  getCount(File src){
        HashMap<String,Integer> hm=new HashMap<>();

        File[] files = src.listFiles();

        for (File file : files) {
            if (file.isFile()){
                String name=file.getName();//获取文件名和后缀名
                String[] arr=name.split("\\.");//将得到的字符串切割,获得后缀名
                if (arr.length>=2){
                    String endname=arr[arr.length-1];//endname为后缀名
                    if(hm.containsKey(endname)){
                        //键已经存在的情况
                        int count=hm.get(endname);//键已经存在,则让值+1
                        count++;
                        hm.put(endname,count);
                    }else {
                        //键不存在的情况
                        hm.put(endname,1);//添加新的键,并且值设为1
                    }
                }
            }else {
                //子文件夹中的统计情况
                HashMap<String, Integer> sonMap = getCount(file);
                Set<Map.Entry<String, Integer>> entries = sonMap.entrySet();//表示每一个键值对
                for (Map.Entry<String, Integer> entry : entries) {
                    String key = entry.getKey();//sonMap中每个键值对中的键
                    int value = entry.getValue();//sonMap中每个键值对中的值
                if (hm.containsKey(key)){
                    //如果hm中存在和sonmap中的键,则直接将值累加进hm中
                    Integer count = hm.get(key);
                    count+=value;
                    hm.put(key,count);
                }else {
                    //如果不存在,直接将键和值存进去
                    hm.put(key,value);
                }

                }
            }

        }

        return hm;
    }
}

(五)、IO流

存储和读取数据的解决方案
所有的输入和输出都是相对于程序来说

5.1 分类

流的方向:
在这里插入图片描述
输出流:程序-> 文件
输入流:文件-> 程序

操作文件类型:
在这里插入图片描述
字节流可以操作所有类型文件
字符流只能操作纯文本文件(Windows自带的记事本能打开并且读懂的,如md,txt文件)

5.2 字节流的基本使用

两类都是抽象类,不能直接用
InputStream:字节输入流
OutputStream:字节输出流

5.2.1FileOutputStream

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

步骤:
1.创建字节输出流对象(创建时要指定文件的路径)
注意:
1.1:参数是字符串表示的路径或file对象都可以
1.2:如果文件不存在会创建一个新的文件,但是要保证父级路径存在
1.3:如果文件存在,则会清空文件

2.写数据
注:
2.1write方法中的参数是整数,但是实际上写到本地文件中是ASCII表上对应的字符

3.释放资源
注:3.1每次使用完后都要释放资源
如果不释放,文件会一直被java占用

简单使用:

package IOstudy;

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

public class i1 {
    public static void main(String[] args) throws IOException {
        FileOutputStream fos=new FileOutputStream("day4\\a.txt");//相当于创建了一个传输通道
        fos.write(97);//传入数据
        fos.close();//关闭传输通道	
    }
}

写数据的多种方法:
在这里插入图片描述

package IOstudy;

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

public class i1 {
    public static void main(String[] args) throws IOException {
        FileOutputStream fos=new FileOutputStream("day4\\a.txt");

//        第二种方法
//        fos.write(97);
//        fos.close();

        /*第三种方法
        * void write(byte[],int off, int len)
        *   参数一:数组
        *   参数二:起始索引
        *   参数三:个数 (写入的个数)
        * 
        * fos.write(bytes,1,2);
        * fos.close();
        * */
    }
}

两个问题:
换行:写出一个换行符
Windows:\r\n
Linux:\n
Mac: \r

Windows系统中,Java对换行进行了优化,写\r或\n都可以,底层会补全

package IOstudy;

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

public class i3 {
    public static void main(String[] args) throws IOException {
        FileOutputStream fos=new FileOutputStream("day4\\a.txt");
        String str="asdwdsqweqeadad";
        byte[] bytes = str.getBytes();
        fos.write(bytes);


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

        String str3="as213131eqeadad";
        byte[] bytes3 = str3.getBytes();
        fos.write(bytes3);


        fos.close();
    }
}

续写:创建FileOutputStream对象时,第二个参数是控制续写开关,默认是false
打开续写后,再次创建就不会清空文件里的数据

5.2.2 FileInputStream

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

步骤:

  1. 创建字节输入流的对象
    注:如果文件不存在,则直接报错

  2. 读数据
    注:使用read()读取,有int返回值。一个一个读取,读取的结果是在ASCII上对应的数字,如果读取不到则返回-1

循环读取:

package IOstudy;

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;

public class i4 {
    public static void main(String[] args) throws IOException {
        FileInputStream fis=new FileInputStream("day4\\a.txt");

        int b;//用来接受读取到的数据,返回-1时,循环结束
        while ((b=fis.read()) != -1){
            System.out.println((char)b);
        }

        fis.close();
    }
}

  1. 释放资源

练习:文件拷贝

package IOstudy;

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;

public class i5 {
    public static void main(String[] args) throws IOException {
        FileInputStream fis=new FileInputStream("D:\\新建文件夹 (6)\\t1.mp4");//把数据写进程序
        FileOutputStream fos=new FileOutputStream("day4\\copy.mp4");//把数据写到本地

        int b;//读取t1.mp4中的数据
        while ((b=fis.read()) !=-1){
            fos.write(b);//将读取到的数据写进copy中
        }

        //先开的流后关
        fos.close();
        fis.close();
    }
}

弊端:速度慢,一次只读一个字节

读取多个数据:每次读取会尽量填满数组
会返回一次读取了几个数据,并且会将读取到的数据填进数组中

每次读取都是在覆盖数组里的数据,所以有时候会造成数据的残留
解决方法:
在这里插入图片描述

在这里插入图片描述
一般数组的大小为1024的整数倍

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

try catch finally
finally中的代码一定被执行,除非虚拟机停止

AutoCloseable 自动释放接口

5.4 字符集详解

ASCII编码规则:前面补0,补齐8位
解码规则:直接转成10进制

GBK:英文也是一个字节存储,兼容ASCII。不足8位,补0
汉字规则:使用2个字节存储,编码不用变动。高位字节二进制一定以1开头,转成10进制后是一个负数
简中Windows默认使用GBK
在这里插入图片描述

Unicode:万国码
UTF-8 :1-4个字节保存
中文用三个字节,英文一个字节
在这里插入图片描述
UTF-8是一种编码方式

为什么会有乱码?
1.读取数据时未读完整个汉字
字节流一次读取一个字节
2.编码和解码方式不统一

解决方法:
在这里插入图片描述

5.5 Java中的编码和解码

编码方法:
在这里插入图片描述
解码方法:
在这里插入图片描述

5.6 字符流的基本使用

Reader:字符输入流
Writer:字符输出流

字符输入流的底层:创建对象时,会创建一个缓冲区(长度为8192的字节数组)
读取数据时,判断缓冲区有没有数据可以读取
缓冲区没有数据,就从文件中获取数据,装到缓冲区中,每次尽可能装满缓冲区
缓冲区有数据:直接从缓冲区读取

字符输出流的底层:写进去的数据会先存进缓冲区,当缓冲区满了或flush(手动刷新)或释放资源,才会将数据存入目的地。

flush:刷新之后,还能继续往文件中写数据
在这里插入图片描述

5.6.1 字符输入流

FileReader:操作本地文件的字符输入流

1.创建对象
文件不存在则直接报错

构造方法:
在这里插入图片描述
2.读取数据
在这里插入图片描述
空参read,读取数据后,在底层会进行解码并转成10进制,然后返回,这些10进制数字也表示在字符集上的数字
有参read,把强转后的字符放到数组当中
3.释放资源
close()

5.6.2 字符输出流

FileWriter:操作本地文件的字符输出流
构造方法:
在这里插入图片描述

成员方法:
在这里插入图片描述
步骤和注意事项:
在这里插入图片描述

练1:
拷贝文件夹

package IOstudy;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.lang.annotation.ElementType;

public class i6 {
    public static void main(String[] args) {
        //数据源
        File src=new File("D:\\aaa");
        //目的地
        File dest=new File("D:\\bbb");

    }

    private static  void copydir(File src,File dest) throws IOException{
        dest.mkdirs();//如果文件夹不存在就直接创建,存在就直接创建失败

        File[] files=src.listFiles();//获取数据

        //遍历数组
        for (File file : files) {
            if (file.isFile()){
                FileInputStream fis=new FileInputStream(file);//当前遍历到的数据作为数据源

                //将父级路径和子级路径拼接,如果目的地没有相应的文件,则直接创建出来
                //file.getName()获取当前数据源的名字和后缀
                FileOutputStream fos=new FileOutputStream(new File(dest,file.getName()));

                byte[] bytes=new byte[1024];
                int len;
                while ((len=fis.read(bytes)) !=-1){
                    fos.write(bytes,0,len);//写入数据
                }
                fos.close();
                fis.close();
            }else {
                //递归的目的地应该是子类路径,如果不存在就直接创建出来
                copydir(file,new File(dest,file.getName()));
            }
        }

    }
}

练2:文件加密
在这里插入图片描述
使用异或:^
两次^得到原始数据

package IOstudy;

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

public class i7 {
    public static void main(String[] args) throws IOException {
        //原始文件
        FileInputStream fis=new FileInputStream("day4\\331.jpg");
        //加密文件
        FileOutputStream fos=new FileOutputStream("day4\\ency.jpg");

        //拷贝时进行加密处理
        int b;
        while ((b= fis.read()) !=-1){
            fos.write(b^2);
        }
        fos.close();
        fis.close();
    }
}

解密时:将加密文件变为数据源,解密后的文件为目的文件,其他不变

练3:
在这里插入图片描述
代码:

package IOstudy;

import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

public class i8 {
    public static void main(String[] args) throws IOException {
        //1.读取数据
        FileReader fr=new FileReader("day4\\a.txt");
        StringBuilder sb=new StringBuilder();//储存读取到的数据

        int ch;
        while ((ch=fr.read())!=1){
            sb.append((char)ch);
        }
        fr.close();

        //2.排序
        String str=sb.toString();
        String[] arrstr = str.split("-");

        ArrayList<Integer> al=new ArrayList();//用集合存储切割后的数据
        for (String s : arrstr) {
            int i=Integer.parseInt(s);
            al.add(i);
        }
        Collections.sort(al);

        //3.写出数据
        FileWriter fw=new FileWriter("day4\\a.txt");
        for (int i = 0; i < al.size(); i++) {
            if(i== al.size()-1){
                fw.write(al.get(i)+"");
            }else {
                fw.write(al.get(i)+"-");
            }
        }
        fw.close();
    }
}

5.7 缓冲流

在这里插入图片描述

5.7.1 字节缓冲流

底层自带了长度为8192的缓冲区提高性能
在这里插入图片描述

package IOstudy;

import java.io.*;

public class i9 {
    public static void main(String[] args) throws IOException {

        //创建缓冲流的对象
        BufferedInputStream bis=new BufferedInputStream(new FileInputStream("day4\\a.txt"));
        BufferedOutputStream bos=new BufferedOutputStream(new FileOutputStream("day4\\copy.txt"));
    
        //循环拷贝数据
        int b;
        while ((b=bis.read()) !=-1){
            bos.write(b);
        }
        bos.close();
        bis.close();
    }
}

原理:
在这里插入图片描述

变量b相当于两个缓冲区之间的运输工具,左手倒右手。使用数组可以一次运输的数据更多,速度更快

5.7.2 字符缓冲流

底层自带了长度为8192的缓冲区提高性能
在这里插入图片描述
输入流特有方法:
在这里插入图片描述
一次读取一整行,遇到回车换行结束,但是不会将回车换行读到内存当中

输出流特有方法:
在这里插入图片描述

练:
在这里插入图片描述

package IOstudy;

import java.io.*;

public class i10 {
    public static void main(String[] args) throws IOException {
        //不能直接在内存中创建计数器,每次运行会被重置。所以在文件中创建,使用时读取,自增后更改
        BufferedReader br=new BufferedReader(new FileReader("day4\\a.txt"));


        String line=br.readLine();
        int count=Integer.parseInt(line);
        count++;

        if (count<=3){
            System.out.println("这是第"+count+"次使用,免费");
        }else {
            System.out.println("本软件免费使用3次,继续使用请注册会员");
        }
        br.close();
        //把更改后的count值存进本地文件
        BufferedWriter bw=new BufferedWriter(new FileWriter("day4\\a.txt"));
        bw.write(count+"");//要把数字转为字符串,不然传进去的数据会被ASCII码表转换

        bw.close();
    }
}

5.8 转换流

属于字符流的高级流
字符流和字节流之间的桥梁
在这里插入图片描述
作用:字节流想要使用字符流中的方法

练1:
需求:利用转换流按照指定的字符编码方式读取数据

jdk11的写法:新增了一种构造方式,第一个参数写地址,第二个写编码类型

package IOstudy;

import java.io.FileReader;
import java.io.IOException;
import java.nio.charset.Charset;

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

练2:
需求:利用转换流按照指定的字符编码方式写出
jdk11版本写法:

package IOstudy;


import java.io.FileWriter;
import java.io.IOException;
import java.nio.charset.Charset;

public class i11 {
    public static void main(String[] args) throws IOException {

        FileWriter fw=new FileWriter("day4\\a.txt",Charset.forName("GBK"));
        fw.write("你好");
        fw.close();
    }
}

练3:
在这里插入图片描述

package IOstudy;


import java.io.*;


public class i11 {
    public static void main(String[] args) throws IOException {
        //将字节流转换为转换流再转为缓冲流
        BufferedReader br=new BufferedReader(new InputStreamReader(new FileInputStream("day4\\a.txt")));
        String line;

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

5.9 序列化流和反序列化流

属于字节流的高级流,是输出流。可以把Java中的对象写到本地文件中
反序列化流是输入流
在这里插入图片描述

序列化流的构造方法和成员方法:

注:
在这里插入图片描述
这个接口里面没有抽象方法,这种接口叫做标记型接口
一旦实现这个接口,就表示这个类可以被序列化

package IOstudy;

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

public class i12 {
    public static void main(String[] args) throws IOException {
        //1.创建对象
        Student stu=new Student("zhangsan",23);
        //2.创建序列化流的对象
        ObjectOutputStream oos=new ObjectOutputStream(new FileOutputStream("day4\\a.txt"));

        //3.写出数据
        oos.writeObject(stu);
        //4.释放数据
        oos.close();
    }
}

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

在这里插入图片描述

package IOstudy;

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

public class i13 {
    public static void main(String[] args) throws IOException, ClassNotFoundException {
        //1.创建反序列化流的的对象
        ObjectInputStream ois=new ObjectInputStream(new FileInputStream("day4\\a.txt"));
        //2.读取数据
        Object o=ois.readObject();
        //3.打印对象
        System.out.println(o);
        //4.释放资源
        ois.close();
    }
}

两种流的使用细节:
创建完javabean时并实现接口,会有一个版本号,在创建对象时也会有此版本号
当修改javabean后,版本号就改变了
当文件版本号,和javabean版本号不同时,就会报错
解决方案:把版本号固定在javabean中
在这里插入图片描述

   // private static final long serialVersionUID=1L; 固定版本号的方法1

方法2:在idea中直接设置版本号,勾选其中两个。然后alt+ctrl 让 idea自动生成
在这里插入图片描述

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

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

练习:
在这里插入图片描述
序列化多个对象

package IOstudy;

import IOstudy.Student;

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

public class i15 {
    public static void main(String[] args) throws IOException {
        Student s1=new Student("zhangsasn ",23,"南京");
        Student s2=new Student("lisi ",24,"重庆");
        Student s3=new Student("wangwu ",25,"北京");

        ArrayList<Student> list=new ArrayList<>();//直接将多个对象放进数组当中
        list.add(s1);
        list.add(s2);
        list.add(s3);
            
        ObjectOutputStream oos=new ObjectOutputStream(new FileOutputStream("day4\\a.txt"));
        oos.writeObject(list);//添加时,只需添加数组
      
        oos.close();
    }
}


读取代码:

package IOstudy;

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

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

        //只用读取一次
        ArrayList<Student> list=(ArrayList<Student>) ois.readObject();

        for (Student student : list) {
            System.out.println(student);
        }
        
        ois.close();
    }
}

5.10 打印流

字节打印流:PrintStream
字符打印流:PrintWriter

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

5.10.1 字节打印流

构造方法:
在这里插入图片描述

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

成员方法:
在这里插入图片描述

package IOstudy;

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

public class i16 {
    public static void main(String[] args) throws FileNotFoundException, UnsupportedEncodingException {
        PrintStream ps=new PrintStream(new FileOutputStream("day4\\a.txt"),true, "UTF-8");

        ps.println(999);
        ps.print(true);
        ps.println();
        ps.printf("%s爱上了%s","阿真","阿强");
    }
}

5.10.2 字符打印流

字符流底层有缓冲区,需要手动开自动刷新
在这里插入图片描述
成员方法:
在这里插入图片描述

sout是特殊的打印流,叫做系统中的标准输出流,不能关闭,在系统中是唯一的。此打印流由虚拟机自动创建,默认指向控制台

5.11 压缩流和解压缩流

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

package IOstudy;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;

public class i17 {
    public static void main(String[] args) throws IOException {
        //1.创建一个File对象表示要解压的压缩包
        File src=new File("day4\\aaaa.zip");
        //2.创建一个File对象表示要解压的目的地
        File dest=new File("D:\\");
    unzip(src,dest);
    }

    private static void unzip(File src, File dest) throws IOException {
        //创建一个解压缩流读取压缩包里的数据
        ZipInputStream zip=new ZipInputStream(new FileInputStream(src));
        //先获取到压缩包里的每一个zipentry对象

        //表示当前在压缩包中获取到的文件或文件夹
        ZipEntry entry;

        while ((entry=zip.getNextEntry()) !=null){
            //判断是不是文件夹
            if (entry.isDirectory()) {
                //是文件夹,需要在目的地创建一个相同的文件夹
            File file=new File(dest,entry.toString());//第一个参数是父级路径也就是目的地,第二个是当前遍历到的数据
            file.mkdir();
            }else {
                //是文件,需要读取压缩包中的文件,并存放到目的地的文件夹中(按照层级存放)
                FileOutputStream fos=new FileOutputStream(new File(dest,entry.toString()));
                int b;
                while ((b=zip.read()) !=-1){
                    fos.write(b);
                }
                zip.closeEntry();
            }
        }
        zip.close();

    }

}

压缩流:ZipOutputStream
需求:将单个文件压缩

package IOstudy;

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

public class i18 {
    public static void main(String[] args) throws IOException {

        //创建File对象表示要压缩的文件
        File src=new File("day4\\a.txt");
        //创建File对象表示压缩包的位置
        File dest=new File("D:\\");

        tozip(src,dest);
    }

    private static void tozip(File src, File dest) throws IOException {
        //1.创建压缩流关联压缩包,将数据写入压缩包中
        ZipOutputStream zos=new ZipOutputStream(new FileOutputStream(new File(dest,"a.zip")));
        //2.创建ZipEntry对象,表示压缩包里面的每一个文件和文件夹
        //这里相当于创建了a.txt文件
        ZipEntry entry=new ZipEntry("a.txt");
        //3.把ZipEntry对象放进压缩包当中
        zos.putNextEntry(entry);
        //4.把src文件中的数据写到压缩包文件当中
        FileInputStream fis=new FileInputStream(src);//用来读取数据
        int b;
        while ((b=fis.read()) !=-1){
            zos.write(b);
        }
        zos.closeEntry();
        zos.close();
    }
}

需求:压缩文件夹

package IOstudy;

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

public class i19 {
    public static void main(String[] args) throws IOException {
        //1.创建File对象表示要压缩的文件夹
        File src=new File("D:\\aaaa");
        //2.压缩包的父级路径
        File destParent=src.getParentFile();
        //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();
    }

    /*
     * 参数一:数据源
     * 参数二:压缩流
     * 参数三:压缩包内部的路径
     */

    public static void toZip(File src,ZipOutputStream zos,String name) throws IOException {

        //1.进入src文件夹
        File[] files=src.listFiles();
        //2.遍历数组
        for (File file : files) {
            if (file.isFile()){
                //3.判断是文件还是文件夹,变成ZipEntry对象,放入压缩包当中
                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());
            }
        }
    }
}

5.12 工具包

5.12.1 Commons-is

由apache提供的关于IO操作的开源工具包
使用步骤:
在这里插入图片描述
常见的方法:
在这里插入图片描述
在这里插入图片描述
copyDirectory是拷贝文件夹里面的内容
copyDirectoryToDirectory 是拷贝包括当前文件夹,整个拷贝到目的地中
清空文件夹是清空文件夹里面的内容,本身文件夹还在

package IOstudy;

import org.apache.commons.io.FileUtils;

import java.io.File;
import java.io.IOException;

public class i20 {
    public static void main(String[] args) throws IOException {
        File src=new File("day4\\a.txt");
        File dest=new File("day4\\copy.txt");

        FileUtils.copyFile(src,dest);
    }
}

5.12.2 Hutool

在这里插入图片描述

常见方法
在这里插入图片描述

package IOstudy.filedemo;

import cn.hutool.core.io.FileUtil;

import java.io.File;

public class i21 {
    public static void main(String[] args) {
        File file = FileUtil.file("D:\\", "aaa", "bbb", "ccc");
        System.out.println(file);
    }
}

5.13 IO流综合练习

1.网络爬虫(生成假数据)
package IOzhlx;

import java.io.BufferedWriter;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLConnection;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.Random;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class lx1 {
    public static void main(String[] args) throws IOException {
        //1.定义变量记录网址
        //姓氏
        String familyNameNet="https://hanyu.baidu.com/s?wd=%E7%99%BE%E5%AE%B6%E5%A7%93&device=pc&from=home";
        //男生名字
        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<String> 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 : boyNameTempList) {
            if (!boyNameList.contains(str)){
                boyNameList.add(str);
            }
        }
        //女生名字,每一个元素用空格切割
        ArrayList<String> girlNameList =new ArrayList<>();
        for (String str : girlNameTempList) {
            String[] girlname = str.split(" ");
            for (int i = 0; i < girlname.length; i++) {
                girlNameList.add(girlname[i]);
            }
        }
        //5.生成数据
        //姓名(唯一)-性别-年龄
        ArrayList<String> lastlist = getInfos(familyNameList, boyNameList, girlNameList, 30, 20);
        System.out.println(lastlist);

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

    /*
    * 参数一:装着姓氏的集合
    * 参数二:装着男生名字集合
    * 参数三:装着女生名字集合
    * 参数四:男生个数
    * 参数五:女生个数
    *
    * */
    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);
            girlhs.add(familyNameList.get(0)+girlNameList.get(0));
        }

        //3.最终数据
        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;

    }


    /*
    * 参数一:完整字符串
    * 参数二:正则表达式
    * 参数三:把正则表达式分为两组,表示要获取第几组数据,0代表所有数据
    * 返回值:需要的数据
    *
    * */
    private static ArrayList<String> getData(String str,String regex,int index){
    //1.创建集合存 数据
        ArrayList<String> list=new ArrayList<>();
    //2.按正则表达式获取数据
        Pattern pattern=Pattern.compile(regex);
        Matcher matcher=pattern.matcher(str);
        while (matcher.find()){
            String group = matcher.group(index);//获取满足要求的数据
            list.add(group);
        }
        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);
        }
        isr.close();
        return sb.toString();
    }
}

2.利用糊涂包生成假数据
package IOzhlx;

import cn.hutool.core.io.FileUtil;
import cn.hutool.core.util.ReUtil;
import cn.hutool.http.HttpUtil;

import java.util.List;

public class lx2 {
    public static void main(String[] args) {
        //姓氏
        String familyNameNet="https://hanyu.baidu.com/s?wd=%E7%99%BE%E5%AE%B6%E5%A7%93&device=pc&from=home";
        //男生名字
        String boyNameNet="http://www.haoming8.cn/baobao/10881.html";
        //女生名字
        String girlNameNet="http://www.haoming8.cn/baobao/7641.html";

        //爬取数据
        String familtNameStr= HttpUtil.get(familyNameNet);
        String boyNameStr= HttpUtil.get(boyNameNet);
        String girlNameStr= HttpUtil.get(girlNameNet);

        //正则表达式
        List<String> familyNameTempList = ReUtil.findAll("(.{4})(,|。)", familtNameStr, 1);
        List<String> boyNameTempList = ReUtil.findAll("([\\u4e00-\\u9fa5·]{2})(、|。)",boyNameStr,1);
        List<String> girlNameTempList =  ReUtil.findAll("(.. ){4}..",girlNameStr,0);

        //糊涂包的相对路径,不是相对于当前项目而言,而是相对于class文件而言
        FileUtil.writeLines(list,"a.txt","UTF-8");
        
        //其他步骤代码相同

    }
}

3.随机点名器

带权重的随机算法
在这里插入图片描述

package IOzhlx;

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

public class lx3 {
    public static void main(String[] args) throws IOException {

        //把文件中的信息读取到内存当中
        ArrayList<Student> list=new ArrayList<>();
        BufferedReader br=new BufferedReader(new FileReader("IOlx\\a.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]));
                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;
            index++;
        }

        //4.计算每一个人权重占比范围
        //每一个人的范围=等于前一个人的权重+自己的,0.1,0.2,0.1
        //0.2+0.1=0.3 前一个人的加自己的=0.3
        //计算完结果为 0.1,0.2,0.3

        for (int i = 1; i < arr.length; i++) {
            arr[i]=arr[i]+arr[i-1];
        }
        //5.随机抽取
        double number=Math.random();//获取一个0.0——1.0的随机数
        //判断number在arr中的位置
        //二分查找法
        //此方法返回 -插入点-1
        //插入点= -结果-1
        int index2 = -Arrays.binarySearch(arr, number) -1;
        Student stu=list.get(index2);
        System.out.println(stu);
        //6.修改当前学生权重
       stu.setWeight(stu.getWeight()/2) ;
        //7.将数据写入文件
        BufferedWriter bw=new BufferedWriter(new FileWriter("IOlx\\a.txt"));
        for (Student s : list) {
            bw.write(s.toString());//改写下tostring,更方便存储
            bw.newLine();//换行
        }
        bw.close();
    }
}

Student类:

package IOzhlx;

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;
    }
}

4.登录注册

在这里插入图片描述

package IOzhlx;

import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
import java.util.Scanner;

public class lx4 {
    public static void main(String[] args) throws IOException {
        BufferedReader br=new BufferedReader(new FileReader("IOlx\\userinfo.txt"));
        String line=br.readLine();
        br.close();
        String[] userInfo = line.split("&");//分割文本,账号和密码
        String[] arr1 = userInfo[0].split("=");//账号
        String[] arr2 = userInfo[1].split("=");//密码

        String rightUsername=arr1[1];
        String rightPassword=arr2[1];

        Scanner sc=new Scanner(System.in);
        System.out.println("请输入账号");
        String zh = sc.next();
        System.out.println("请输入密码");
        String mm = sc.next();
        if (zh.equals(rightUsername)  && mm.equals(rightPassword) ){
            System.out.println("登录成功");
        }else {
            System.out.println("登录失败");
        }
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值