javase加强(七、 不可变集合、Stream、异常)

不可变集合

  • 不可变集合:集合的数据在创建时提供,生命周期内不可改变,否则报错

  • 为什么创建不可变集合:

    • 不想被别人修改
    • 集合对象被不可信的库调用时,保证安全(感觉可以类似于论文查重时,把文件先生成PDF再传到论文查重网站上去)
  • List、Set、Map创建不可变集合

  • 方法名称说明
    static List of(E…elements)创建一个具有指定元素的List集合对象
    static Set of(E…elements)创建一个具有指定元素的Set集合对象
    static <K , V> Map<K,V> of(E…elements)创建一个具有指定元素的Map集合对象
List<Double> lists = List.of(569.5, 700.5, 523.0,  570.5);

Set<String> names = Set.of("迪丽热巴", "迪丽热九", "马尔扎哈", "卡尔眨巴" );

Map<String, Integer> maps = Map.of("huawei",2, "Java开发", 1 , "手表", 1);

Stream流

  • Stream流作用:简化集合、数组操作的API

  • 它支持链式编程

  • Stream流案例:

    • 创建一个集合,存储多个字符串元素
    List<String> list = new ArrayList<>();
    list.add("张无忌");
    list.add("周芷若");
    list.add("赵敏");
    list.add("张强");
    list.add("张三丰");
    
    • 把集合中所有以"张"开头的元素存储到一个新的集合
    • 把"张"开头的集合中的长度为3的元素存储到一个新的集合
    • 遍历上一步得到的集合中的元素输出。
public class StreamTest {
    public static void main(String[] args) {
        List<String> names = new ArrayList<>();
        Collections.addAll(names, "张三丰","张无忌","周芷若","赵敏","张强");
        System.out.println(names);
//
//        // 1、从集合中找出姓张的放到新集合
//        List<String> zhangList = new ArrayList<>();
//        for (String name : names) {
//            if(name.startsWith("张")){
//                zhangList.add(name);
//            }
//        }
//        System.out.println(zhangList);
//
//        // 2、找名称长度是3的姓名
//        List<String> zhangThreeList = new ArrayList<>();
//        for (String name : zhangList) {
//            if(name.length() == 3){
//                zhangThreeList.add(name);
//            }
//        }
//        System.out.println(zhangThreeList);

        // 3、使用Stream实现的
        names.stream().filter(s -> s.startsWith("张")).filter(s -> s.length() == 3).forEach(s -> System.out.println(s));
    }
}
  • 集合获取Stream流的方式
  • 可以使用Collection接口中的默认方法stream()生成流
名称说明
default Stream stream()获取当前集合对象的Stream流
  • 数组获取Stream流的方式
名称说明
public static Stream stream(T[] array)获取当前数组的Stream流
public static Stream of(T… values)获取当前数组/可变数据的Stream流
public class StreamDemo02 {
    public static void main(String[] args) {
        /** --------------------Collection集合获取流-------------------------------   */
        Collection<String> list = new ArrayList<>();
        Stream<String> s =  list.stream();

        /** --------------------Map集合获取流-------------------------------   */
        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();

        /** ---------------------数组获取流------------------------------   */
        String[] names = {"赵敏","小昭","灭绝","周芷若"};
        Stream<String> nameStream = Arrays.stream(names);
        Stream<String> nameStream2 = Stream.of(names);
    }
}

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

  • 中间方法也称为非终结方法,调用完成后返回新的Stream流可以继续使用,支持链式编程。
  • 在Stream流中无法直接修改集合、数组中的数据。
名称说明
Stream filter(Predicate<? super T> predicate)用于对流中的数据进行过滤。
Stream limit(long maxSize)获取前几个元素
Stream skip(long n)跳过前几个元素
Stream distinct()去除流中重复的元素。依赖(hashCode和equals方法)
static Stream concat(Stream a, Stream b)合并a和b两个流为一个流
  • Stream流的终结操作
名称说明
void forEach(Consumer action)对此流的每个元素执行遍历操作
long count()返回此流中的元素数
package com.itheima.d2_stream;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.stream.Stream;

/**
     目标:Stream流的常用API
         forEach : 逐一处理(遍历)
         count:统计个数
            -- long count();
         filter : 过滤元素
            -- Stream<T> filter(Predicate<? super T> predicate)
         limit : 取前几个元素
         skip : 跳过前几个
         map : 加工方法
         concat : 合并流。
 */
public class StreamDemo03 {
    public static void main(String[] args) {
        List<String> list = new ArrayList<>();
        list.add("张无忌");
        list.add("周芷若");
        list.add("赵敏");
        list.add("张强");
        list.add("张三丰");
        list.add("张三丰");

        // Stream<T> filter(Predicate<? super T> predicate)
        list.stream().filter(s -> s.startsWith("张")).forEach(s -> System.out.println(s));

        long size = list.stream().filter(s -> s.length() == 3).count();
        System.out.println(size);

       // list.stream().filter(s -> s.startsWith("张")).limit(2).forEach(s -> System.out.println(s));
        list.stream().filter(s -> s.startsWith("张")).limit(2).forEach(System.out::println);

        list.stream().filter(s -> s.startsWith("张")).skip(2).forEach(System.out::println);

        // map加工方法: 第一个参数原材料  -> 第二个参数是加工后的结果。
        // 给集合元素的前面都加上一个:黑马的:
        list.stream().map(s -> "黑马的:" + s).forEach(a -> System.out.println(a));

        // 需求:把所有的名称 都加工成一个学生对象。
         list.stream().map(s -> new Student(s)).forEach(s -> System.out.println(s));
//        list.stream().map(Student::new).forEach(System.out::println); // 构造器引用  方法引用

        // 合并流。
        Stream<String> s1 = list.stream().filter(s -> s.startsWith("张"));
        Stream<String> s2 = Stream.of("java1", "java2");
        // public static <T> Stream<T> concat(Stream<? extends T> a, Stream<? extends T> b)
        Stream<String> s3 = Stream.concat(s1 , s2);
        s3.distinct().forEach(s -> System.out.println(s));
    }
}

Stream流案例:

  • 需求:某个公司的开发部门,分为开发一部和二部,现在需要进行年中数据结算。

    分析:

    ①:员工信息至少包含了(名称、性别、工资、奖金、处罚记录)

    ②:开发一部有4个员工、开发二部有5名员工

    ③:分别筛选出2个部门的最高工资的员工信息,封装成优秀员工对象Topperformer

    ④:分别统计出2个部门的平均月收入,要求去掉最高和最低工资。

    ⑤:统计2个开发部门整体的平均工资,去掉最低和最高工资的平均值。

package com.itheima.d2_stream;

public class Employee {
    private String name;
    private char sex;
    private double salary;
    private double bonus;
    private String punish; // 处罚信息

    public Employee(){
    }

    public Employee(String name, char sex, double salary, double bonus, String punish) {
        this.name = name;
        this.sex = sex;
        this.salary = salary;
        this.bonus = bonus;
        this.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 getBonus() {
        return bonus;
    }

    public void setBonus(double bonus) {
        this.bonus = bonus;
    }

    public String getPunish() {
        return punish;
    }

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

    public double getTotalSalay(){
        return salary * 12 + bonus;
    }

    @Override
    public String toString() {
        return "Employee{" +
                "name='" + name + '\'' +
                ", sex=" + sex +
                ", salary=" + salary +
                ", bonus=" + bonus +
                ", punish='" + punish + '\'' +
                '}'+"\n";
    }
}
package com.itheima.d2_stream;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Stream;
public class StreamDemo04 {
    public static double allMoney ;
    public static double allMoney2 ; // 2个部门去掉最高工资,最低工资的总和
    public static void main(String[] args) {
        List<Employee> one = new ArrayList<>();
        one.add(new Employee("猪八戒",'男',30000 , 25000, null));
        one.add(new Employee("孙悟空",'男',25000 , 1000, "顶撞上司"));
        one.add(new Employee("沙僧",'男',20000 , 20000, null));
        one.add(new Employee("小白龙",'男',20000 , 25000, null));

        List<Employee> two = new ArrayList<>();
        two.add(new Employee("武松",'男',15000 , 9000, null));
        two.add(new Employee("李逵",'男',20000 , 10000, null));
        two.add(new Employee("西门庆",'男',50000 , 100000, "被打"));
        two.add(new Employee("潘金莲",'女',3500 , 1000, "被打"));
        two.add(new Employee("武大郎",'女',20000 , 0, "下毒"));

        // 1、开发一部的最高工资的员工。(API)
        // 指定大小规则了
//        Employee e = one.stream().max((e1, e2) -> Double.compare(e1.getSalary() + e1.getBonus(),  e2.getSalary() + e2.getBonus()))
//                .get();
//       System.out.println(e);
        Topperformer t = one.stream().max((e1, e2) -> Double.compare(e1.getSalary() + e1.getBonus(),  e2.getSalary() + e2.getBonus()))
                .map(e -> new Topperformer(e.getName(),  e.getSalary() + e.getBonus())).get();
        System.out.println(t);

        // 2、统计平均工资,去掉最高工资和最低工资
        one.stream().sorted((e1, e2) -> Double.compare(e1.getSalary() + e1.getBonus(),  e2.getSalary() + e2.getBonus()))
                .skip(1).limit(one.size() - 2).forEach(e -> {
                    // 求出总和:剩余员工的工资总和
            allMoney += (e.getSalary() + e.getBonus());
        });
        System.out.println("开发一部的平均工资是:" + allMoney / (one.size() - 2));

        // 3、合并2个集合流,再统计
        Stream<Employee> s1 = one.stream();
        Stream<Employee> s2 = two.stream();
        Stream<Employee> s3 = Stream.concat(s1 , s2);
        s3.sorted((e1, e2) -> Double.compare(e1.getSalary() + e1.getBonus(),  e2.getSalary() + e2.getBonus()))
                .skip(1).limit(one.size() + two.size() - 2).forEach(e -> {
            // 求出总和:剩余员工的工资总和
            allMoney2 += (e.getSalary() + e.getBonus());
        });

        // BigDecimal
        BigDecimal a = BigDecimal.valueOf(allMoney2);
        BigDecimal b = BigDecimal.valueOf(one.size()  + two.size() - 2);
        System.out.println("开发部的平均工资是:" + a.divide(b,2, RoundingMode.HALF_UP));
    }
}

收集Stream流:

  • 把Stream流操作后的数据重新转化成数组或者集合
  • 收集方法:
名称说明
R collect(Collector collector)开始收集Stream流,指定收集器
名称说明
public static Collector toList()把元素收集到List集合中
public static Collector toSet()把元素收集到Set集合中
public static Collector toMap(Function keyMapper , Function valueMapper)把元素收集到Map集合中
  • 流转成数组或者集合只能操作一次
package com.itheima.d2_stream;

import java.util.*;
import java.util.function.IntFunction;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
     目标:收集Stream流的数据到 集合或者数组中去。
 */
public class StreamDemo05 {
    public static void main(String[] args) {
        List<String> list = new ArrayList<>();
        list.add("张无忌");
        list.add("周芷若");
        list.add("赵敏");
        list.add("张强");
        list.add("张三丰");
        list.add("张三丰");

        Stream<String> s1 = list.stream().filter(s -> s.startsWith("张"));
        List<String> zhangList = s1.collect(Collectors.toList()); // 可变集合
        zhangList.add("java1");
        System.out.println(zhangList);

//       List<String> list1 = s1.toList(); // 得到不可变集合
//       list1.add("java");
//       System.out.println(list1);

        // 注意注意注意:“流只能使用一次”
        Stream<String> s2 = list.stream().filter(s -> s.startsWith("张"));
        Set<String> zhangSet = s2.collect(Collectors.toSet());
        System.out.println(zhangSet);

        Stream<String> s3 = list.stream().filter(s -> s.startsWith("张"));
//         Object[] arrs = s3.toArray();
        String[] arrs = s3.toArray(String[]::new); // 可以不管,拓展一下思维!!
        System.out.println("Arrays数组内容:" + Arrays.toString(arrs));

    }
}

异常

  • 运行时异常:直接继承自RuntimeException或者其子类,编译阶段不会报错,运行时可能出现的错误。
public class ExceptionDemo {
    public static void main(String[] args) {
        System.out.println("程序开始。。。。。。");
        /** 1.数组索引越界异常: ArrayIndexOutOfBoundsException。*/
        int[] arr = {1, 2, 3};
        System.out.println(arr[2]);
        // System.out.println(arr[3]); // 运行出错,程序终止

        /** 2.空指针异常 : NullPointerException。直接输出没有问题。但是调用空指针的变量的功能就会报错!! */
        String name = null;
        System.out.println(name); // null
        // System.out.println(name.length()); // 运行出错,程序终止

        /** 3.类型转换异常:ClassCastException。 */
        Object o = 23;
        // String s = (String) o;  // 运行出错,程序终止

        /** 5.数学操作异常:ArithmeticException。 */
        //int c = 10 / 0;

        /** 6.数字转换异常: NumberFormatException。 */
        //String number = "23";
        String number = "23aabbc";
        Integer it = Integer.valueOf(number); // 运行出错,程序终止
        System.out.println(it + 1);

        System.out.println("程序结束。。。。。");
    }
}
  • 编译时异常:不是RuntimeException或者其子类的异常,编译阶就报错,必须处理,否则代码不通过。
  • 继承自Exception的异常或者其子类
public class ExceptionDemo {
    public static void main(String[] args) throws ParseException {
        String date = "2015-01-12 10:23:21";
        // 创建一个简单日期格式化类:
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy/MM-dd HH:mm:ss");
        // 解析字符串时间成为日期对象
        Date d = sdf.parse(date);    //日期解析异常
        //
        System.out.println(d);
    }
}
  • 异常的默认处理机制:
    • 默认会在出现异常的代码那里自动的创建一个异常对象:ArithmeticException。
    • 异常会从方法中出现的点这里抛出给调用者,调用者最终抛出给JVM虚拟机。
    • 虚拟机接收到异常对象后,先在控制台直接输出异常栈信息数据。
    • 直接从当前执行的异常点干掉当前程序。
    • 后续代码没有机会执行了,因为程序已经死亡。

编译时异常的处理形式

  • 有三种:

  • 出现异常直接抛出去给调用者,调用者也继续抛出去。

  • 出现异常自己捕获处理,不麻烦别人。

  • 前两者结合,出现异常直接抛出去给调用者,调用者捕获处理。

  • 第一种:throws

  • 用在方法上,可以将方法内部出现的异常抛出去给本方法的调用者处理。

  • 这种方式并不好,发生异常的方法自己不处理异常,如果异常最终抛出去给虚拟机将引起程序死亡。

    • 抛出异常格式:
      • 方法 throws 异常1 ,异常2 ,异常3 …{
        }
    • 规范做法:
      • 方法 throws Exception{
        }
public class ExceptionDemo01 {

//    public static void main(String[] args) throws ParseException, FileNotFoundException {
//        System.out.println("程序开始。。。。。");
//        parseTime("2011-11-11 11:11:11");
//        System.out.println("程序结束。。。。。");
//    }
//
//    public static void parseTime(String date) throws ParseException, FileNotFoundException {
//        SimpleDateFormat sdf = new SimpleDateFormat("yyyy/MM-dd HH:mm:ss");
//        Date d = sdf.parse(date);
//        System.out.println(d);
//
//        InputStream is = new FileInputStream("E:/meinv.jpg");
//    }

    public static void main(String[] args) throws Exception {
        System.out.println("程序开始。。。。。");
        parseTime("2011-11-11 11:11:11");
        System.out.println("程序结束。。。。。");
    }

    public static void parseTime(String date) throws Exception {
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        Date d = sdf.parse(date);
        System.out.println(d);

        InputStream is = new FileInputStream("E:/meinv.jpg");
    }

}
  • 第二种:try…catch…

  • 监视捕获异常,用在方法内部,可以将方法内部出现的异常直接捕获处理。

  • 这种方式还可以,发生异常的方法自己独立完成异常的处理,程序可以继续往下执行。

  • 将一大段代码try…catch…:把一段代码选中,ctrl + alt + T

    • 格式:

      try{
      	// 监视可能出现异常的代码!
      }catch(异常类型1 变量){ 
      	// 处理异常
      }catch(异常类型2 变量){
      	// 处理异常 
      }...
      
    • 建议格式:

    try{
    	// 可能出现异常的代码!
    }catch (Exception e){
    	e.printStackTrace(); // 直接打印异常栈信息
    }
    
public class ExceptionDemo02 {
    public static void main(String[] args) {
        System.out.println("程序开始。。。。");
        parseTime("2011-11-11 11:11:11");
        System.out.println("程序结束。。。。");
    }

    public static void parseTime(String date) {
        try {
            SimpleDateFormat sdf = new SimpleDateFormat("yyyy/MM-dd HH:mm:ss");
            Date d = sdf.parse(date);
            System.out.println(d);

            InputStream is = new FileInputStream("E:/meinv.jpg");
        } catch (Exception e) {
            e.printStackTrace(); // 打印异常栈信息
        }
    }


//    public static void parseTime(String date) {
//        try {
//            SimpleDateFormat sdf = new SimpleDateFormat("yyyy/MM-dd HH:mm:ss");
//            Date d = sdf.parse(date);
//            System.out.println(d);
//
//            InputStream is = new FileInputStream("E:/meinv.jpg");
//        } catch (FileNotFoundException|ParseException e) {
//            e.printStackTrace(); // 打印异常栈信息
//        }
//    }

//    public static void parseTime(String date) {
//        try {
//            SimpleDateFormat sdf = new SimpleDateFormat("yyyy/MM-dd HH:mm:ss");
//            Date d = sdf.parse(date);
//            System.out.println(d);
//
//            InputStream is = new FileInputStream("E:/meinv.jpg");
//        } catch (FileNotFoundException e) {
//           e.printStackTrace(); // 打印异常栈信息
//        } catch (ParseException e) {
//           e.printStackTrace();
//        }
//    }

//    public static void parseTime(String date) {
//        try {
//            SimpleDateFormat sdf = new SimpleDateFormat("yyyy/MM-dd HH:mm:ss");
//            Date d = sdf.parse(date);
//            System.out.println(d);
//        } catch (ParseException e) {
//            // 解析出现问题
//            System.out.println("出现了解析时间异常哦,走点心!!");
//        }
//
//        try {
//            InputStream is = new FileInputStream("E:/meinv.jpg");
//        } catch (FileNotFoundException e) {
//            System.out.println("您的文件根本就没有啊,不要骗我哦!!");
//        }
//    }
}
  • 第三种:前两种结合(理论上第三种方式是最好的:底层的异常抛出去给最外层,最外层集中捕获处理。)
  • 方法直接将异通过throws抛出去给调用者
  • 调用者收到异常后直接捕获处理。
public class ExceptionDemo03 {
    public static void main(String[] args) {
        System.out.println("程序开始。。。。");
        try {
            parseTime("2011-11-11 11:11:11");
            System.out.println("功能操作成功~~~");
        } catch (Exception e) {
            e.printStackTrace();
            System.out.println("功能操作失败~~~");
        }
        System.out.println("程序结束。。。。");
    }

    public static void parseTime(String date) throws Exception {
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy、MM-dd HH:mm:ss");
        Date d = sdf.parse(date);
        System.out.println(d);

        InputStream is = new FileInputStream("D:/meinv.jpg");
    }

}
  • 运行时异常:可以处理也可以不处理,处理的话就在最外层try…catch…

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

  • 键盘录入一个合理的价格为止(必须是数值)。

  • (如果没有设置异常处理的话,如果乱输数据如:44.4.4.4,那么程序会直接崩掉,加了异常处理之后,检测到发生异常还会重新输入数据,从而使程序稳健,秒啊~~~)

public class Test2 {
    public static void main(String[] args) {
        Scanner sc  = new Scanner(System.in);
        while (true) {
            try {
                System.out.println("请您输入合法的价格:");
                String priceStr = sc.nextLine();
                // 转换成double类型的价格
                double price = Double.valueOf(priceStr);

                // 判断价格是否大于 0
                if(price > 0) {
                    System.out.println("定价:" + price);
                    break;
                }else {
                    System.out.println("价格必须是正数~~~");
                }
            } catch (Exception e) {
                System.out.println("用户输入的数据有毛病,请您输入合法的数值,建议为正数~~");
            }
        }
    }
}
  • 自定义异常的必要性

    • Java不能为所有的问题提供异常
    • 企业通过异常的方式管理自己的业务
  • 自定义异常的分类

    • 1、自定义编译时异常
      • 定义一个异常类继承Exception.
      • 重写构造器。
      • 在出现异常的地方用throw new 自定义对象抛出,
      • 作用:编译时异常是编译阶段就报错,提醒更加强烈,一定需要处理!!
    • 2、自定义运行时异常
      • 定义一个异常类继承RuntimeException.
      • 重写构造器。
      • 在出现异常的地方用throw new 自定义对象抛出!
      • 作用:提醒不强烈,编译阶段不报错!!运行时才可能出现!!
package com.itheima.d9_exception_custom;

/**
    自定义的编译时异常
      1、继承Exception
      2、重写构造器
 */
public class ItheimaAgeIlleagalException extends Exception{
    public ItheimaAgeIlleagalException() {
    }

    public ItheimaAgeIlleagalException(String message) {
        super(message);
    }
}
package com.itheima.d9_exception_custom;

/**
    自定义的编译时异常
      1、继承RuntimeException
      2、重写构造器
 */
public class ItheimaAgeIlleagalRuntimeException extends RuntimeException{
    public ItheimaAgeIlleagalRuntimeException() {
    }

    public ItheimaAgeIlleagalRuntimeException(String message) {
        super(message);
    }
}
public class ExceptionDemo {
    public static void main(String[] args) {
//        try {
//            checkAge(-34);
//        } catch (ItheimaAgeIlleagalException e) {
//            e.printStackTrace();
//        }

        try {
            checkAge2(-23);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public static void checkAge2(int age)  {
        if(age < 0 || age > 200){
            // 抛出去一个异常对象给调用者
            // throw :在方法内部直接创建一个异常对象,并从此点抛出
            // throws : 用在方法申明上的,抛出方法内部的异常
            throw new ItheimaAgeIlleagalRuntimeException(age + " is illeagal!");
        }else {
            System.out.println("年龄合法:推荐商品给其购买~~");
        }
    }

    public static void checkAge(int age) throws ItheimaAgeIlleagalException {
        if(age < 0 || age > 200){
            // 抛出去一个异常对象给调用者
            // throw :在方法内部直接创建一个异常对象,并从此点抛出
            // throws : 用在方法申明上的,抛出方法内部的异常
            throw new ItheimaAgeIlleagalException(age + " is illeagal!");
        }else {
            System.out.println("年龄合法:推荐商品给其购买~~");
        }
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值