1.创建不可变集合

  • 概述

    • 不可变集合,就是不可被修改或删除的集合
    • 集合的数据项在创建的时候提供,并且在整个生命周期重都不可被改编。否则报错
  • 为何有不可变集合

    • 如果某个数据不能被修改,把它防御性的拷贝倒不可变集合中【高考成绩】
    • 或当集合对象被不可信的库调用时,不可变形式是安全的
  • 如何创建

    • 在List、Set、Map接口中,都存在of方法,可以创建一个不可变集合

image-20230810161209469.png

  • 这个集合不能添加,不能删除,不能修改
public class Test {
    public static void main(String[] args) {
        //1.不可变List集合
        List<Double> scores = List.of(457.0,703.5,570.0,403.5);
        //scores.add(200.5);报错
        //scores.set(1,500.0);报错
        System.out.println(scores);
        System.out.println(scores.get(1));

        //2.不可变Set集合
        Set<String> names = Set.of("马浩楠", "程明辉", "李泽", "吴赛");
        //names.add("马儿扎哈");报错
        //names.remove("李泽");报错
        System.out.println(names);

        //3.不可变Map集合
        Map<String, Integer> commodity = Map.of("舒肤佳", 2, "手表", 3, "JavaSe", 1);
        //commodity.put("HTML",1);报错
        System.out.println(commodity);
    }
}

2.Stream流

2.1、Stream流概述

  • 在Java 8 中,得益于Lambda 所带来的函数式编程,引入了一个全新的 Stream 概念
  • 目的:用于简化集合和数组操作的API

  • Stream流思想核心:

    • 先得到集合或数组的Stream流
    • 把元素放上去
    • 然后就用Stream流来简化的API来方便的操作元素

image-20230810161217594.png

  • 体验Stream流的作用
    • 创建一个集合,存储多个字符串元素
    • 把集合中所有以“张”开头的元素存储到一个新的集合
    • 遍历上一步得到的集合中的元素输出
public class Test {
    public static void main(String[] args) {
        ArrayList<String> names = new ArrayList<>();
        Collections.addAll(names,"张三丰","张无忌","赵敏","光明左使","张三");
        System.out.println(names);

        //1.从集合中找出姓张的放到新集合
        /*ArrayList<String> zhangList = new ArrayList<>();
        for (String name : names) {
            if(name.startsWith("张")){
                zhangList.add(name);
            }
        }
        System.out.println(zhangList);*/
        //2.找出名字长度为3姓张的名字放到新集合
        /*ArrayList<String> zhangThreel = new ArrayList<>();
        for (String s : zhangList) {
            if(s.length() == 3){
                zhangThreel.add(s);
            }
        }
        System.out.println(zhangThreel);*/
        //3.使用stream实现
        names.stream().filter(s -> s.startsWith("张")).filter(s -> s.length() == 3).forEach(s -> System.out.println(s));
    }
}

2.2、Stream流的获取

  • 集合获取Stream流的方式【Collection接口中默认方法stream()生成】

image-20230810161226637.png

  • 数组获取Stream流的方式

image-20230810161232368.png

public class Test {
    public static void main(String[] args) {
        //1.Collection集合获取Stream流
        List<String> list = new ArrayList<>();
        Stream<String> listStream = list.stream();
        //2.Map集合获取Stream流
        Map<String,Integer> maps = new HashMap<>();
        //键流
        Stream<String> keyStream = maps.keySet().stream();
        //值流
        Stream<Integer> valueStream = maps.values().stream();
        //键值流
        Stream<Map.Entry<String, Integer>> keyAndValueStream = maps.entrySet().stream();
        //3.数组获取Stream流
        String[] names = {"马浩楠", "程明辉", "李泽", "吴赛"};
        Stream<String> arrStream = Arrays.stream(names);
        Stream<String> arrStream1 = Stream.of(names);
    }
}

2.3、Stream流的常用方法

  • Stream流的常用API(中间操作方法)

image-20230810161239282.png

【注意】:

  • 中间方法也称为非终结方法,调用完成后返回新的Stream流可以继续使用,支持链式标称
  • 在Stream流中无法直接修改集合、数组中的数据

【Stream流常见终结操作方法】

image-20230810161248611.png

终结操作方法,调用完成后流就无法继续使用了,原因是不会返回Stream了

public class Test {
    public static void main(String[] args) {
        //1.forEach:遍历
        ArrayList<String> list = new ArrayList<>();
        Collections.addAll(list,"张三丰","张无忌","赵敏","光明左使","张三","张三丰");
        list.stream().forEach(s -> System.out.println(s));
        //2.统计个数
        System.out.println("元素个数:"+list.stream().count());
        //3.过滤元素(取姓张的元素)
        list.stream().filter(s -> s.startsWith("张")).forEach(s -> System.out.println(s));
        //4.取前几个元素
        System.out.println("-------------取前几个---------------");
        list.stream().limit(2).forEach(s -> System.out.println(s));
        //5.跳过前几个元素
        System.out.println("-------------跳过前几个--------------");
        list.stream().skip(3).forEach(s -> System.out.println(s));
        //6.加工
        System.out.println("加工后:");
        list.stream().map(s -> "洛阳理工的"+s).forEach(s -> System.out.println(s));
        //加工成对象
        list.stream().map(s -> new Student(s)).forEach(s -> System.out.println(s));
        //7.合并流
        Stream<String> s1 = list.stream().filter(s -> s.startsWith("张"));
        Stream<String> s2 = Stream.of("Java", "HTML");
        Stream.concat(s1,s2).forEach(s -> System.out.println(s));
    }
}

2.4、Stream流的综合应用

【案例】:某公司的开发部门,分为一部开发和二部开发,现在需要进行年中数据结算

  1. 员工信息(名称、性别、工资、奖金、处罚记录)
  2. 开发一部有4名员工,二部有5名员工
  3. 分别筛选出两个部门的最高工资员工信息,并封装成优秀员工对象Topperformer
  4. 分别统计出两个部门的平均月收入,去掉最高和最低工资
  5. 统计两个开发部门整体的平均工资,去掉最高和最低工资

  6. Employee

public class Employee {
    private String name;
    private char sex;
    private double salary;
    private double prize;
    private String punish;

    @Override
    public String toString() {
        return "Employee{" +
                "name='" + name + '\'' +
                ", sex=" + sex +
                ", salary=" + salary +
                ", prize=" + prize +
                ", punish='" + punish + '\'' +
                '}';
    }

    public String getName() {
        return name;
    }

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

    public char getSex() {
        return sex;
    }

    public void setSex(char sex) {
        this.sex = sex;
    }

    public double getSalary() {
        return salary;
    }

    public void setSalary(double salary) {
        this.salary = salary;
    }

    public double getPrize() {
        return prize;
    }

    public void setPrize(double prize) {
        this.prize = prize;
    }

    public String getPunish() {
        return punish;
    }

    public void setPunish(String punish) {
        this.punish = punish;
    }

    public Employee(String name, char sex, double salary, double prize, String punish) {
        this.name = name;
        this.sex = sex;
        this.salary = salary;
        this.prize = prize;
        this.punish = punish;
    }

    public Employee() {
    }
}
  • Topperformer
public class Topperformer {
    private String name;
    private double salary;
    private double prize;

    @Override
    public String toString() {
        return "Topperformer{" +
                "name='" + name + '\'' +
                ", salary=" + salary +
                ", prize=" + prize +
                '}';
    }

    public String getName() {
        return name;
    }

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

    public double getSalary() {
        return salary;
    }

    public void setSalary(double salary) {
        this.salary = salary;
    }

    public double getPrize() {
        return prize;
    }

    public void setPrize(double prize) {
        this.prize = prize;
    }

    public Topperformer(String name, double salary, double prize) {
        this.name = name;
        this.salary = salary;
        this.prize = prize;
    }

    public Topperformer() {
    }
}
  • Test
public class Test {
    //部门1 去掉最高和最低工资
    public static double oneAllMoney;
    //部门2 去掉最高和最低工资
    public static double twoAllMoney;
    //两个部门平均月收入
    public static double allMoney;
    public static void main(String[] args) {
        List<Employee> one = new ArrayList<>();
        one.add(new Employee("猪八戒",'男',30000.0,25000.0,null));
        one.add(new Employee("孙悟空",'男',25000.0,1000.0,"顶撞上司"));
        one.add(new Employee("沙僧",'男',20000.0,20000.0,null));
        one.add(new Employee("小白龙",'男',20000.0,25000.0,null));

        List<Employee> two = new ArrayList<>();
        two.add(new Employee("武松",'男',15000,9000.0,null));
        two.add(new Employee("李逵",'男',20000,10000.0,null));
        two.add(new Employee("西门庆",'男',50000.0,100000.0,"被打"));
        two.add(new Employee("潘金莲",'女',3500.0,1000.0,"被打"));
        two.add(new Employee("武大郎",'男',20000.0,0.0,"下毒"));

        //分别筛选出2个部门最高工资的员工信息,封装成优秀员工对象Topperformer
        Topperformer t = perfectEmployee(one);
        System.out.println("部门一优秀员工:"+t);
        Topperformer t2 = perfectEmployee(two);
        System.out.println("部门二优秀员工:"+t2);

        //分别统计出2个部门的平均月收入,去掉最高和最低工资
        System.out.println("部门一平均收入(去掉最高和最低):"+averageIncome(one));
        System.out.println("部门二平均收入(去掉最高和最低):"+averageIncome(two));

        //统计2个开发部门整体的平均工资,去掉最低和最高工资的平均值
        Stream<Employee> s1 = one.stream();
        Stream<Employee> s2 = two.stream();
        Stream.concat(s1,s2).sorted((o1,o2) -> Double.compare(o1.getSalary()+o1.getPrize(),o2.getSalary()+o2.getPrize()))
                .skip(1).limit(one.size()+two.size() - 2).forEach(s ->{
                    allMoney += s.getSalary()+s.getPrize();
        });
        BigDecimal big1 = BigDecimal.valueOf(allMoney);
        BigDecimal big2 = BigDecimal.valueOf(one.size() + two.size() - 2);
        //精度失真问题,使用BigDecimal,(RoundingMode.HALF_UP:四舍五入)
        System.out.println("两个部门一起的平均收入为:"+big1.divide(big2,2, RoundingMode.HALF_UP));
    }
    //分别筛选出2个部门最高工资的员工信息,封装成优秀员工对象Topperformer
    public static Topperformer perfectEmployee(List<Employee> list){
        return list.stream().max((o1, o2) -> Double.compare(o1.getSalary() + o1.getPrize(), o2.getSalary() + o2.getPrize()))
                .map(o -> new Topperformer(o.getName(), o.getSalary(), o.getPrize())).get();
    }
    //分别统计出2个部门的平均月收入,去掉最高和最低工资
    public static double averageIncome(List<Employee> list){
        list.stream().sorted(((o1, o2) -> Double.compare((o1.getSalary()+o1.getPrize()),(o2.getSalary()+o2.getPrize()))))
                .skip(1).limit(list.size() - 2).forEach(s -> {
                    oneAllMoney += (s.getPrize()+s.getSalary());
        });
        return oneAllMoney / (list.size() -2);
    }
}

2.5、收集Stream流

  • 收集Stream流的含义:就是把Stream流操作后的结果数据转回到集合或者数组中去
  • Stream流:方便操作集合/数组的手段
  • 集合/数组:才是开发中的目的

image-20230810161309305.png

public class Test {
    public static void main(String[] args) {
        ArrayList<String> names = new ArrayList<>();
        Collections.addAll(names,"张三丰","张无忌","赵敏","光明左使","张三");

        //1.流转为List集合
        Stream<String> s1 = names.stream().filter(s -> s.startsWith("张"));
        //List<String> list = s1.collect(Collectors.toList());
        //当前版本存在了新的API
        List<String> list = s1.toList();//但这个获取的List是不可变集合
        System.out.println(list);

        //2.转为Set集合  【注意:流只能使用一次,所以需要再次拿到Stream)
        Stream<String> s2 = names.stream().filter(s -> s.startsWith("张"));
        Set<String> set = s2.collect(Collectors.toSet());
        System.out.println(set);

        //3.转为数组
        Stream<String> s3 = names.stream().filter(s -> s.startsWith("张"));
        Object[] arr = s3.toArray();
        System.out.println(Arrays.toString(arr));
    }
}

3.异常处理

3.1、异常概述、体系

  • 概述
    • 异常是指在“编译”或者“执行”的过程中可能出现的问题【注意:语法错误不算在异常体系当中
    • 例如:数组索引越界、空指针、日期格式化异常...
  • 学习原有
    • 异常一旦出现,如果没有提前处理,程序就会退出JVM虚拟机而终止
    • 研究异常并且避免异常,提前处理异常,体现的是程序的安全,健壮性
  • Error
    • 系统级别问题、JVM退出等,代码无法控制
  • Excpetion:java.lang包下,称为异常类,表示程序本身可以处理的问题
    • RuntimeException及其子类:运行时异常,编译阶段不会报错(空指针,数组索引越界)
    • 除RantimeException之外所有的异常:编译时异常,编译时必须处理,否则程序不能通过编译(日期格式化异常)

3.2、常见运行时异常

  • 运行时异常
    • 直接继承自RuntimeExcpetion或其子类,编译阶段不会报错,运行时可能回出现错误
  • 运行时异常示例
    • 数组索引越界异常:ArrayIndexOutOfBoundsException
    • 空指针异常:NullPointerException,直接输出没问题,但调用空指针的变量功能就会报错
    • 数字操作异常:ArithmeticException
    • 类型转换异常:ClassCastException
    • 数字转换异常:NumberFormatException
public class Test {
    public static void main(String[] args) {
        //1.数组索引越界异常:ArrayIndexOutOfBoundsException
        int[] arr = {26,33,99};
        System.out.println(arr[1]);
        //System.out.println(arr[3]);运行报错,程序终止

        //2.空指针异常:NullPointerExcpetion
        String name = null;
        System.out.println(name);
        //System.out.println(name.length());运行报错,程序终止

        //3.类型转换异常:ClassCastExcpetion
        Object s = 23;
        //String str = (String) s;运行报错,程序终止

        //4.数学操作异常:ArithmeticException
        //int a = 3/0;

        //5.数字转换异常:NumberFormatExcpetion
        String number = "50aaa";
        //Integer num = Integer.valueOf(number);运行报错,程序终止
    }
}

3.3、常见编译时异常

  • 编译时异常
    • 不是RuntimeException或者其子类的异常,编译阶段就报错,必须处理,否则代码无法编译
  • 异常示例
String date = "2001-03-02 10:23:21";
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
Date d = sdf.parse(date);//出现编译时异常
System.out.println(d);
  • 编译时异常的作用:
    • 在于提醒不要出错

3.4、异常的默认处理流程

  1. 默认会在出现异常的代码那里自动的创建一个异常对象
  2. 异常会从方法中出现的点抛给调用者,调用者最终抛出给JVM虚拟机
  3. 虚拟机接收到异常对象后,先在控制台直接输出异常栈信息数据
  4. 直接从当前执行的异常点干掉当前程序
  5. 后续代码不会再继续执行,程序停止。

3.5、编译时异常的处理机制

【编译时异常是编译阶段就出现错误的,必须处理,否则代码无法编译运行】

  • 编译时异常的处理形式:
    • 出现异常直接跑出去给调用者,调用者也继续跑出去
    • 出现异常自己捕获,不麻烦别人
    • 前两者结合,出现异常直接跑出去给调用者,调用者捕获处理
  • 异常处理方式1——throws
    • throws:用在方法上,可以将方法内部出现的异常抛出去给本方法的调用者处理
    • 这种方法并不好,发生异常的方法自己不处理异常,如果异常最终跑出去给虚拟机将引起程序死亡
public class Test {
    public static void main(String[] args) throws Exception{
        String birthDay = "2001-03-02 11:12:13";
        Date date = formatDate(birthDay);
        System.out.println(date);
    }
    public static Date formatDate(String str) throws Exception{
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        Date date = sdf.parse(str);
        return date;
    }
}
  • 异常处理方式2——try catch
    • 监视捕获异常,用在方法内部,可以将方法内部出现的异常直接捕获处理
    • 这种方式还可以,发生异常的方式自己独立完成异常的处理,程序可以继续往下执行
public class Test {
    public static void main(String[] args) throws Exception{
        String birthDay = "2001-03-02 11:12:13";
        Date date = formatDate(birthDay);
        System.out.println(date);
    }
    public static Date formatDate(String str){
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        Date date = null;
        try {
            date = sdf.parse(str);
        } catch (ParseException e) {
            e.printStackTrace();
        }
        return date;
    }
}
  • 异常处理方式3——前两者结合
    • 方法直接将异常通过throws抛出去给调用者
    • 调用者收到异常后直接捕获处理
public class Test {
    public static void main(String[] args){
        String birthDay = "2001-03-02 11:12:13";
        Date date = null;
        try {
            date = formatDate(birthDay);
        } catch (Exception e) {
            e.printStackTrace();
        }
        System.out.println(date);
    }
    public static Date formatDate(String str) throws Exception{
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        Date date = sdf.parse(str);
        return date;
    }
}

3.6、运行时异常的处理机制

  • 运行时异常的处理形式
    • 运行时异常编译阶段不会出错,是运行时才可能出错,所以编译阶段不处理也是没问题的
    • 按照规范建议还是处理:建议最外层调用处捕获处理

3.7、异常处理使代码更稳健的案例

【需求】

  • 键盘录入一个合理的价格(必须是数值且不为负数)
public class Test {
    public static void main(String[] args) {
        Scanner input = new Scanner(System.in);
        try {
            while(true){
                System.out.println("请输入定价:");
                String priceStr = input.nextLine();

                Double price = Double.valueOf(priceStr);
                if(price > 0){
                    System.out.println("定价为:"+price);
                    break;
                }
                System.out.println("定价必须为正数,请重新输入");
            }
        } catch (NumberFormatException e) {
            System.out.println("输入有误,请重新输入,建议为正数!");
        }
    }
}

3.8、自定义异常

  • 自定义异常的缘由
    • Java无法解决全部的问题来提供异常类
    • 如果企业向通过异常的方式来管理自己的某个业务问题,那就需要自己自定义
  • 好处

    • 可以使用异常的机制管理业务问题,如提醒程序员注意...
    • 同时一旦出现bug,可以用异常的形式清晰的看出错误的地方
  • 自定义编译时异常

    • 定义一个异常类继承Exception
    • 重写构造器
    • 在出现异常的地方用 throw new 自定义对象抛出
    • 作用:编译时异常是编译阶段就报错的,提醒更加强烈,需要处理的

【AgeIllegalException自定义编译时异常】

public class AgeIllegalException extends Exception{
    public AgeIllegalException() {
    }

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

【Test】

public class Test {
    public static void main(String[] args) {
        Scanner input = new Scanner(System.in);
        System.out.println("请输入年龄:");
        try {
            checkAge(Integer.valueOf(input.nextInt()));
        } catch (AgeIllegalException e) {
            e.printStackTrace();
        }
    }
    public static void checkAge(Integer age) throws AgeIllegalException {
        if(age<0 || age>200){
            throw new AgeIllegalException(age+"is illegal!");
        }else {
            System.out.println("年龄合法!");
        }
    }
}
  • 自定义运行时异常
    • 定义一个异常类基础RuntimeException
    • 重写构造方法
    • 在出现异常的地方用 throw new 自定义对象抛出
    • 作用:提醒不强烈,编译阶段不报错,运行时才可能报错

【AgeIllegalRuntimeException自定义运行异常】

public class AgeIllegalRunTimeException extends RuntimeException{
    public AgeIllegalRunTimeException() {
    }

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

【Test】

public class Test {
    public static void main(String[] args) {
        Scanner input = new Scanner(System.in);
        System.out.println("请输入年龄:");
        checkAge(Integer.valueOf(input.nextInt()));//不会编译报错,但建议外层依然进行 try catch
    }
    public static void checkAge(Integer age) {
        if(age<0 || age>200){
            throw new AgeIllegalRunTimeException(age+"is illegal!");
        }else {
            System.out.println("年龄合法!");
        }
    }
}

results matching ""

    No results matching ""