Java基础 Stream流方法引用异常

news2024/9/21 15:22:02

Stream流

引例
需求:按照下面要求完成集合的创建和遍历
创建一个集合,存储多个字符串元素
1. 把所有以“曹”开头的元素存储到新集合中
2. 把曹开头,长度为3的元素存储到新集合中

 List<String> list = List.of("曹操", "曹孟德", "曹子恒", "曹子建", "司马懿", "司马师", "司马昭", "曹丕");

        // 1. 把所有以“曹”开头的元素存储到新集合中
        List<String> list1 = new ArrayList<>();
        for (String s : list) {
            if(s.startsWith("曹")){
                list1.add(s);
            }
        }
        System.out.println(list1);

        // 2. 把曹开头,长度为3的元素存储到新集合中
        List<String> list2 = new ArrayList<>();
        for (String s : list1) {
            if(s.length() == 3){
                list2.add(s);
            }
        }
        System.out.println(list2);

输出结果:

[曹操, 曹孟德, 曹子恒, 曹子建, 曹丕]
[曹孟德, 曹子恒, 曹子建]

上面太麻烦了
用Stream流只需要一行代码:

list.stream().filter(name -> name.startsWith("曹")).filter(name -> name.length() == 3).forEach(name -> System.out.println(name));
  • stream流的作用:结合Lamada表达式,简化集合、数组的操作
  • 使用步骤:
    • 先得到一条stream流,并把数据放上去

      获取方式方法名说明
      单列集合default Stream<E> stream()Collection中的默认方法
      双列集合无法直接使用Stream流,需要通过keySet()或entrySet()转换成单列集合
      数组public static<T> Stream stream(T[] array)Arrays工具类中的静态方法
      一堆零散数据public static<T> Stream of(T…values)Stream接口中的静态方法
    • 利用stream流中的API进行各种操作:(过滤,转换,统计,打印等等)

      • 中间方法:过滤、转换。方法调用完毕之后还可以调用其他方法
      • 终结方法:统计、打印。最后一步,调用完毕之后不能调用其他方法。
    • 使用中间方法对流水线上的数据进行操作

    • 使用终结方法对流水线上的数据进行操作

		//单列集合Stream流
        List<String> list = List.of("aa", "bb", "cc", "dd");
        list.stream().forEach(s -> System.out.println(s));

        //双列集合Stream流
        Map<String, String> map = Map.of("aa", "11", "bb", "22", "cc", "33");
        map.entrySet().stream().forEach(m -> System.out.println(m));

        //数组Stream流
        int[] arr = {1,2,3,4,5};
        Arrays.stream(arr).forEach(a -> System.out.println(a));

        //零散数据Stream流
        Stream.of(11,12,13,14,15).forEach(s -> System.out.println(s));

注意:Stream接口中静态方法of的细节:方法的形参是一个可变参数,可以传递一堆零散的数据,也可以传递数组,但数组必须是引用数据类型的,如果传递基本数据类型,会把整个数组当做一个元素传到Steam流中

		int[] arr1 = {1,2,3,4,5};
        String[] arr2 = {"a", "b", "c", "d", "e"};
        Stream.of(arr1).forEach(s -> System.out.println(s)); //输出[I@7699a589
        Stream.of(arr2).forEach(s -> System.out.println(s)); //输出a b c d e
  • Stream流的终结方法
    名称说明
    void forEach(Consumer action)遍历
    long count()统计
    toArray()收集流中的数据,放到数组中
    collect(Collector collrctor)收集流中的数据,放到集合中

1. forEach

返回值是void,因此是终结方法,不能再在后面调用函数

  • Consumer的泛型:表示流中数据的类型

    • accept方法得形态integer:依次表示流里面的每一个数据
    • 方法体:对每一个数据要做的操作(打印)
    		List<Integer> list = List.of(1,2,3,4,5);
            list.stream().forEach(new Consumer<Integer>() {
                @Override
                public void accept(Integer integer) {
                    System.out.println(integer);
                }
            });
    

2. long

返回值是long类型的整数,因此是终结方法

long c = list.stream().count();
System.out.println(c);

3. toArray()

  • 空参:返回值是object类型
			System.out.println("---------------------");
	        Object[] arr1 = list.stream().toArray();        System.out.println(Arrays.toString(arr1));
	        //
	        //IntFunction的泛型:具体类型的数组
	        //apply的形态:流中数据的个数,要和数组长度保持一致
	        //apply函数返回值:具体类型的数组
	
	        //toArray方法的参数的作用:负责创建一个指定类型的数组
	        //方法底层会依次得到流里面每一个数据,并把数据放到数组中
	        //方法返回值:装着流里面所有数组的数组
	        String[] arr = list.stream().toArray(new IntFunction<String[]>() {
	            @Override
	            public String[] apply(int value) {
	                return new String[0];
	            }
	        });
	        //简化成Lamada表达式形式:
	        list.stream().toArray(value -> new String[value]);
  • 有参:返回值是任意类型
    toArray方法的参数的作用:负责创建一个指定类型的数组
    方法底层会依次得到流里面每一个数据,并把数据放到数组中
    方法返回值:装着流里面所有数组的数组

    		//IntFunction的泛型:具体类型的数组
            //apply的形态:流中数据的个数,要和数组长度保持一致
            //apply函数返回值:具体类型的数组
    
            //toArray方法的参数的作用:负责创建一个指定类型的数组
            //方法底层会依次得到流里面每一个数据,并把数据放到数组中
            //方法返回值:装着流里面所有数组的数组
            String[] arr = list.stream().toArray(new IntFunction<String[]>() {
                @Override
                public String[] apply(int value) {
                    return new String[0];
                }
            });
            //简化成Lamada表达式形式:
            list.stream().toArray(value -> new String[value]);
    

4. collect

 /*
           collect(Collector collector)            收集流中的数据,放到集合中 (List Set Map)

           注意点:
               如果我们要收集到Map集合当中,键不能重复,否则会报错
      */

       ArrayList<String> list = new ArrayList<>();
       Collections.addAll(list, "张无忌-男-15", "周芷若-女-14", "赵敏-女-13", "张强-男-20",
               "张三丰-男-100", "张翠山-男-40", "张良-男-35", "王二麻子-男-37", "谢广坤-男-41");


       //收集List集合当中
       //需求:
       //我要把所有的男性收集起来
       List<String> newList1 = list.stream()
               .filter(s -> "男".equals(s.split("-")[1])) //把"男"放前面防止list为null时调用equals报错
               .collect(Collectors.toList());
       //System.out.println(newList1);


       //收集Set集合当中
       //需求:
       //我要把所有的男性收集起来
       Set<String> newList2 = list.stream().filter(s -> "男".equals(s.split("-")[1]))
               .collect(Collectors.toSet());
       //System.out.println(newList2);


       //收集Map集合当中
       //谁作为键,谁作为值.
       //我要把所有的男性收集起来
       //键:姓名。 值:年龄
       Map<String, Integer> map = list.stream()
               .filter(s -> "男".equals(s.split("-")[1]))
               /*
                *   toMap : 参数一表示键的生成规则
                *           参数二表示值的生成规则
                *
                * 参数一:
                *       Function泛型一:表示流中每一个数据的类型
                *               泛型二:表示Map集合中键的数据类型
                *
                *        方法apply形参:依次表示流里面的每一个数据
                *               方法体:生成键的代码
                *               返回值:已经生成的键
                *
                *
                * 参数二:
                *        Function泛型一:表示流中每一个数据的类型
                *                泛型二:表示Map集合中值的数据类型
                *
                *       方法apply形参:依次表示流里面的每一个数据
                *               方法体:生成值的代码
                *               返回值:已经生成的值
                *
                * */
               .collect(Collectors.toMap(new Function<String, String>() {
                                             @Override
                                             public String apply(String s) {
                                                 //张无忌-男-15
                                                 return s.split("-")[0];
                                             }
                                         },
                       new Function<String, Integer>() {
                           @Override
                           public Integer apply(String s) {
                               return Integer.parseInt(s.split("-")[2]);
                           }
                       }));


       Map<String, Integer> map2 = list.stream()
               .filter(s -> "男".equals(s.split("-")[1]))
               .collect(Collectors.toMap(
                       s -> s.split("-")[0],
                       s -> Integer.parseInt(s.split("-")[2])));

       System.out.println(map2);

toMap的一些底层代码在这里插入图片描述

在这里插入图片描述

总结:
  1. Stream流的作用

    结合了Lambda表达式,简化集合、数组的操作

  2. Stream的使用步骤
    获取Stream流对象
    使用中间方法处理数据
    使用终结方法处理数据

  3. 如何获取Stream流对象
    单列集合:Collection中的默认方法stream
    双列集合:不能直接获取
    数组:Arrays工具类型中的静态方法stream
    一堆零散的数据:Stream接口中的静态方法of

  4. 常见方法
    中间方法:filter,limit,skip,distinct,concat,

    中间方法用法作用
    limitlimit(n)保留流中的前n个人
    skipskip(n)跳过流中的前n个人
    concatStream.concat(stream1, stream2)将两个流拼接在一起
    map比较复杂,看练习3转换流的类型

    终结方法:forEach,count,coIlect

练习

  1. 定义一个集合,并添加一些整数 1,2,3,4,5,6,7,8,9,10
    过滤奇数,只留下偶数。
    并将结果保存起来

     //1. 定义一个集合
            ArrayList<Integer> list = new ArrayList<>();
            //2.添加一些整数
            Collections.addAll(list, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
            //3.过滤奇数,只留下偶数
            //进行判断,如果是偶数,返回true 保留
            List<Integer> newList = list.stream()
                    .filter(n -> n % 2 == 0)
                    .collect(Collectors.toList());
            //4.打印集合
    
  2. 创建一个ArrayList集合,并添加以下字符串,字符串中前面是姓名,后面是年龄
    “zhangsan,23”
    “lisi,24”
    “wangwu,25”
    保留年龄大于等于24岁的人,并将结果收集到Map集合中,姓名为键,年龄为值

    //1.创建一个ArrayList集合
        ArrayList<String> list = new ArrayList<>();
        //2.添加以下字符串
        list.add("zhangsan,23");
        list.add("lisi,24");
        list.add("wangwu,25");
        //3.保留年龄大于等于24岁的人
      /*  list.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]);
                    }
                }));*/
    
        Map<String, Integer> map = list.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(map);
    
  3. 现在有两个ArrayList集合,分别存储6名男演员的名字和年龄以及6名女演员的名字和年龄。
    姓名和年龄中间用逗号隔开。
    比如:张三,23
    要求完成如下的操作:
    1,男演员只要名字为3个字的前两人
    2,女演员只要姓杨的,并且不要第一个
    3,把过滤后的男演员姓名和女演员姓名合并到一起
    4,将上一步的演员信息封装成Actor对象。
    5,将所有的演员对象都保存到List集合中。
    备注:演员类Actor,属性有:name,age

     男演员:  "蔡坤坤,24" , "叶齁咸,23", "刘不甜,22", "吴签,24", "谷嘉,30", "肖梁梁,27"
     女演员:  "赵小颖,35" , "杨颖,36", "高元元,43", "张天天,31", "刘诗,35", "杨小幂,33"
    
//1.创建两个ArrayList集合
        ArrayList<String> manList = new ArrayList<>();
        ArrayList<String> womenList = new ArrayList<>();
        //2.添加数据
        Collections.addAll(manList, "蔡坤坤,24", "叶齁咸,23", "刘不甜,22", "吴签,24", "谷嘉,30", "肖梁梁,27");
        Collections.addAll(womenList, "赵小颖,35", "杨颖,36", "高元元,43", "张天天,31", "刘诗,35", "杨小幂,33");
        //3.男演员只要名字为3个字的前两人
        Stream<String> stream1 = manList.stream()
                .filter(s -> s.split(",")[0].length() == 3)
                .limit(2);
        //4.女演员只要姓杨的,并且不要第一个
        Stream<String> stream2 = womenList.stream()
                .filter(s -> s.split(",")[0].startsWith("杨"))
                .skip(1);
        //5.把过滤后的男演员姓名和女演员姓名合并到一起
        //演员信息封装成Actor对象。

        //String -> Actor对象 (类型转换)
       /* Stream.concat(stream1,stream2).map(new Function<String, Actor>() {
            @Override
            public Actor apply(String s) {
                //"赵小颖,35"
                String name = s.split(",")[0];
                int age = Integer.parseInt(s.split(",")[1]);
                return new Actor(name,age);
            }
        }).forEach(s-> System.out.println(s));*/

        List<Actor> list = Stream.concat(stream1, stream2)
                .map(s -> new Actor(s.split(",")[0], Integer.parseInt(s.split(",")[1])))
                .collect(Collectors.toList());
        System.out.println(list);
public class Actor {
    private String name;
    private int age;

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

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

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

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

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

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

方法引用

含义:把已经有的方法拿过来用,当做函数式接口中抽象方法的方法体。
在这里插入图片描述

=>
在这里插入图片描述
方法引用格式:类名::函数名 例如Arrays.sort(arr, FunctionDemo1::subtraction);(见如下代码)
::是方法引用符

public class FunctionDemo1 {
    public static void main(String[] args) {
        //需求:创建一个数组,进行倒序排列
        Integer[] arr = {3, 5, 4, 1, 6, 2};
        //匿名内部类

        Arrays.sort(arr, new Comparator<Integer>() {
            @Override
            public int compare(Integer o1, Integer o2) {
                return o2 - o1;
            }
        });

        //lambda表达式
        //因为第二个参数的类型Comparator是一个函数式接口
        Arrays.sort(arr, (Integer o1, Integer o2)->{
            return o2 - o1;
        });

        //lambda表达式简化格式
        Arrays.sort(arr, (o1, o2)->o2 - o1 );


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

        //表示引用FunctionDemo1类里面的subtraction方法
        //把这个方法当做抽象方法的方法体
        Arrays.sort(arr, FunctionDemo1::subtraction);

        System.out.println(Arrays.toString(arr));

    }

    //可以是Java已经写好的,也可以是一些第三方的工具类
    public static int subtraction(int num1, int num2) {
        return num2 - num1;
    }
}
  • 方法引用的分类

    1. 引用静态方法

    2. 引用成员方法

    • 引用其他类的成员方法

    • 引厍本类的成员方法

    • 引用父类的成员方法

    1. 引用构造方法

    2. 其他调用方式

    • 使用类名引用成员方法

    • 引用数组的构造方法

引用静态方法

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

/*
        方法引用(引用静态方法)
        格式
              类::方法名

        需求:
            集合中有以下数字,要求把他们都变成int类型
            "1","2","3","4","5"
       */


        //1.创建集合并添加元素
        ArrayList<String> list = new ArrayList<>();
        Collections.addAll(list,"1","2","3","4","5");

        //2.把他们都变成int类型
       /* list.stream().map(new Function<String, Integer>() {
            @Override
            public Integer apply(String s) {
                int i = Integer.parseInt(s);
                return i;
            }
        }).forEach(s -> System.out.println(s));*/



        //1.方法需要已经存在
        //2.方法的形参和返回值需要跟抽象方法的形参和返回值保持一致
        //3.方法的功能需要把形参的字符串转换成整数

        list.stream()
                .map(Integer::parseInt)
                .forEach(s-> System.out.println(s));

注:只有函数式接口才能使用引用方法

引用成员方法

格式:对象::成员方法

  • 其他类:其他类对象::方法名
  • 本类:this::方法名 (引用处不能是静态方法)
  • 父类:super::方法名(引用处不能是静态方法)
/*
        方法引用(引用成员方法)
        格式
                其他类:其他类对象::方法名
                本类:this::方法名(引用处不能是静态方法)
                父类:super::方法名(引用处不能是静态方法)
        需求:
            集合中有一些名字,按照要求过滤数据
            数据:"张无忌","周芷若","赵敏","张强","张三丰"
            要求:只要以张开头,而且名字是3个字的

       */
		//1.创建集合
        ArrayList<String> list = new ArrayList<>();
        //2.添加数据
        Collections.addAll(list,"张无忌","周芷若","赵敏","张强","张三丰");
        //3.过滤数据(只要以张开头,而且名字是3个字的)
        //以前的方法
        list.stream().filter(s->s.startsWith("张")).filter(s->s.length() == 3).forEach(s-> System.out.println(s)); 
        
		//引用方法
		//filter的Predicate是函数式接口,可以使用引用方法
		//可以先写如下代码,以便观察如何写引用方法
		list.stream().filter(new Predicate<String>() {
            @Override
            public boolean test(String s) {
                return s.startsWith("张") && s.length() == 3;
            }
        }).forEach(s-> System.out.println(s));
    

其他类

//另外创建一个类
package com.itheima.a01myfunction;

public class StringOperation {
    public boolean stringJudge(String s){
        return s.startsWith("张") && s.length() == 3;
    }
}
//修改为引用成员方法形式
		StringOperation so = new StringOperation();
        list.stream().filter(so::stringJudge)
                .forEach(s-> System.out.println(s));

本类

public class FunctionDemo3  {
    public static void main(String[] args) {
        /*
        方法引用(引用成员方法)
        格式
                其他类:其他类对象::方法名
                本类:this::方法名(引用处不能是静态方法)
                父类:super::方法名(引用处不能是静态方法)
        需求:
            集合中有一些名字,按照要求过滤数据
            数据:"张无忌","周芷若","赵敏","张强","张三丰"
            要求:只要以张开头,而且名字是3个字的

       */

        //1.创建集合
        ArrayList<String> list = new ArrayList<>();
        //2.添加数据
        Collections.addAll(list,"张无忌","周芷若","赵敏","张强","张三丰");
        //3.过滤数据(只要以张开头,而且名字是3个字的)
        //list.stream().filter(s->s.startsWith("张")).filter(s->s.length() == 3).forEach(s-> System.out.println(s));


        list.stream().filter(new Predicate<String>() {
            @Override
            public boolean test(String s) {
                return s.startsWith("张") && s.length() == 3;
            }
        }).forEach(s-> System.out.println(s));

        

        //**静态方法中是没有this的,所以this::stringJudge会报错,需改为new FunctionDemo3()::stringJudge
        list.stream().filter(new FunctionDemo3()::stringJudge)
                .forEach(s-> System.out.println(s));

    }
    public boolean stringJudge(String s){
        return s.startsWith("张") && s.length() == 3;
    }
}

引用构造方法

目的:创建对象
格式:类名::new (例如:Student::new)

public class FunctionDemo4 {
    public static void main(String[] args) {
        /*
        方法引用(引用构造方法)
        格式
                类名::new

        目的:
                创建这个类的对象

        需求:
             集合里面存储姓名和年龄,要求封装成Student对象并收集到List集合中

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

        //1.创建集合对象
        ArrayList<String> list = new ArrayList<>();
        //2.添加数据
        Collections.addAll(list, "张无忌,15", "周芷若,14", "赵敏,13", "张强,20", "张三丰,100", "张翠山,40", "张良,35", "王二麻子,37", "谢广坤,41");
        //3.封装成Student对象并收集到List集合中
        //String --> Student
      /*  List<Student> newList = list.stream().map(new Function<String, Student>() {
            @Override
            public Student apply(String s) {
                String[] arr = s.split(",");
                String name = arr[0];
                int age = Integer.parseInt(arr[1]);
                return new Student(name, age);
            }
        }).collect(Collectors.toList());
        System.out.println(newList);*/


        List<Student> newList2 = list.stream().map(Student::new).collect(Collectors.toList());
        System.out.println(newList2);

    }
}
public class Student {
    private String name;
    private int age;


    public Student() {
    }


    public Student(String str) {
        String[] arr = str.split(",");
        this.name = arr[0];
        this.age = Integer.parseInt(arr[1]);
    }






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

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

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

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

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

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

类名引用成员方法

格式:类名::成员方法
范例:String::subString

  • 方法引用的规则:
    1.需要有函数式接口
    2.被引用的方法必须已经存在

    3.被引用方法的形参,需要跟抽象方法的第二个形参到最后一个形参保持一致,返回值需要保持一致。
    (所以下图中方法参数不一样也没关系,因为类名引用成员方法的参数规则比较特殊)在这里插入图片描述
    4.被引用方法的功能需要满足当前的需求

  • 抽象方法形参的详解:

    • 第一个参数:表示被引用方法的调用者,决定了可以引用哪些类中的方法
      在Stream流当中,第一个参数一般都表示流里面的每一个数据。
      假设流里面的数据是字符串,那么使用这种方式进行方法引用,只能引用String这个类中的方法

    • 第二个参数到最后一个参数:跟被引用方法的形参保持一致,如果没有第二个参数,说明被引用的方法需要是无参的成员方法

  • 局限性:
    不能引用所有类中的成员方法。
    是跟抽象方法的第一个参数有关,这个参数是什么类型的,那么就只能引用这个类中的方法。
    (比如代码中抽象方法的第一个参数是String类型的,那么map中就只能引用String的方法String::toUpperCase)

/*
        方法引用(类名引用成员方法)
        格式
                类名::成员方法
        需求:
             集合里面一些字符串,要求变成大写后进行输出
       */

        //1.创建集合对象
        ArrayList<String> list = new ArrayList<>();
        //2.添加数据
        Collections.addAll(list, "aaa", "bbb", "ccc", "ddd");
        //3.变成大写后进行输出
        //map(String::toUpperCase)
        //拿着流里面的每一个数据,去调用String类中的toUpperCase方法,方法的返回值就是转换之后的结果。
        list.stream().map(String::toUpperCase).forEach(s -> System.out.println(s));


        //String --> String
       /* list.stream().map(new Function<String, String>() {
            @Override
            public String apply(String s) {
                return s.toUpperCase();
            }
        }).forEach(s -> System.out.println(s));*/

引用数组的构造方法

格式:数据类型[]::new
范例:int[]::new

        方法引用(数组的构造方法)
        格式
                数据类型[]::new
        目的:
                创建一个指定类型的数组
        需求:
             集合中存储一些整数,收集到数组当中

        细节:
            数组的类型,需要跟流中数据的类型保持一致。

        //1.创建集合并添加元素
        ArrayList<Integer> list = new ArrayList<>();
        Collections.addAll(list, 1, 2, 3, 4, 5);
        //2.收集到数组当中
        Integer[] arr2 = list.stream().toArray(Integer[]::new);
        //3.打印
        System.out.println(Arrays.toString(arr2));

        /*Integer[] arr = list.stream().toArray(new IntFunction<Integer[]>() {
            @Override
            public Integer[] apply(int value) {
                return new Integer[value];
            }
        });*/

练习

练习1
需求:
集合中存储一些字符串的数据,比如:张三,23。
收集到Student类型的数组当中

        //1.创建集合并添加元素
        ArrayList<String> list = new ArrayList<>();
        Collections.addAll(list, "张无忌,15", "周芷若,14", "赵敏,13", "张强,20", "张三丰,100", "张翠山,40", "张良,35", "王二麻子,37", "谢广坤,41");
        //2.先把字符串变成Student对象,然后再把Student对象收集起来
        Student[] arr = list.stream().map(Student::new).toArray(Student[]::new);
        //打印数组
        System.out.println(Arrays.toString(arr));

练习2

/*
        *   需求:
        *       创建集合添加学生对象
        *       学生对象属性:name,age
        *   要求:
        *       获取姓名并放到数组当中
        *       使用方法引用完成
        * */

        //1.创建集合
        ArrayList<Student> list = new ArrayList<>();
        //2.添加元素
        list.add(new Student("zhangsan",23));
        list.add(new Student("lisi",24));
        list.add(new Student("wangwu",25));
        //3.获取姓名并放到数组当中

        String[] arr = list.stream().map(Student::getName).toArray(String[]::new);


       /* String[] arr = list.stream().map(new Function<Student, String>() {
            @Override
            public String apply(Student student) {
                return student.getName();
            }
        }).toArray(String[]::new);*/

        System.out.println(Arrays.toString(arr));

技巧:
1.现在有没有一个方法符合我当前的需求
2.如果有这样的方法,这个方法是否满足引用的规则
      静态   类名::方法名
      成员方法:如果流中数据类型与符合需求的方法属于同一类,用类名::方法名
      构造方法  类名::new

异常

  1. 异常是什么
    程序中可能出现的问题

  2. 异常体系的最上层父类是谁?异常分为几类?
    父类:Exception。
    异常分为两类.编译时异常、运行时异常

  3. 编译时异常和运行时异常的区别?

    • 编译时异常:没有继RuntimeException的异常,直接继承于Exception
      编译阶段就会错误提示。(必须要手动修改代码,否则代码报错)
      除了RuntimeException以外其余都是编译时异常

    • 运行时异常:RuntimeException本身和子类。
      编译阶段没有错误提示,运行时出现的

在这里插入图片描述

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

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

  • 运行时异常:RuntimeException及其子类,编译阶段不会出现异常提醒。
    运行时出现的异常(如.数组索引越界异常)
    一般是由于参数传递错误带来的问题

  • 编译时异常:编译阶段就会出现异常提醒的(如:日期解析异常)

在这里插入图片描述

异常的作用

作用一:异常是用来查询bug的关键参考信息

在这里插入图片描述

作用二:异常可以作为方法内部的一种特殊返回值,以便通知调用者底层的执行情况

主函数:

//1.创建学生对象
        Student s1 = new Student();
        //年龄:(同学) 18~40岁
        s1.setAge(50);//就知道了50赋值失败

学生对象的setAge函数里放了判断年龄的异常:

    public void setAge(int age) {
        if(age < 18 || age > 40){
            //System.out.println("年龄超出范围");
            throw new RuntimeException();
        }else{
            this.age = age;
        }
    }

运行时控制台的显示:
在这里插入图片描述这样抛出异常就可以清楚地知道错误发生在哪

异常的处理方式

JVM默认的处理方式

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

在这里插入图片描述

自己处理

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

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

public static void main(String[] args) {
        System.out.println("臣本布衣躬耕于南阳");

        try{
            //可能出现异常的代码;
            System.out.println(2/0);//算术异常 ArithmeticException
            //此处出现了异常,程序就会在这里创建一个ArithmeticException对象
            //new ArithmeticException();
            //拿着这个对象到catch的小括号中对比,看括号中的变量是否可以接收这个对象
            //如果能被接收,就表示该异常就被捕获(抓住),执行catch里面对应的代码
            //当catch里面所有的代码执行完毕,继续执行try...catch体系下面的其他代码
        }catch (ArithmeticException a){
            //如果出现了ArrayIndexOutOfBoundsException异常,我该如何处理
            System.out.println("算术异常");
        }

        System.out.println("苟全性命于乱世,不求闻达于诸侯");
        System.out.println("先帝不以臣卑鄙,猥自枉屈");
    }

此时下面两个打印语句能被执行

在这里插入图片描述

自己处理(捕获异常)灵魂四问:
在这里插入图片描述

  • 灵魂一问:如果try中没有遇到问题,怎么执行?
    会把try里面所有的代码全部执行完毕,不会执行catch里面的代码
    注意: 只有当出现了异常才会执行catch里面的代码
public static void main(String[] args) {
        /*
            自己处理(捕获异常)灵魂四问:
                灵魂一问:如果try中没有遇到问题,怎么执行?
                            会把try里面所有的代码全部执行完毕,不会执行catch里面的代码
                            注意:
                                只有当出现了异常才会执行catch里面的代码
         */

        int[] arr = {1, 2, 3, 4, 5, 6};

        try{
            System.out.println(arr[0]);//1
        }catch(ArrayIndexOutOfBoundsException e){
            System.out.println("索引越界了");
        }

        System.out.println("看看我执行了吗?");//看看我执行了吗?
    }
  • 灵魂二问:如果try中可能会遇到多个问题,怎么执行?
    会写多个catch与之对应
    细节: 如果我们要捕获多个异常,这些异常中如果存在父子关系的话,那么父类一定要写在下面
    了解性:在JDK7之后,我们可以在catch中同时捕获多个异常,中间用|进行隔开
    表示如果出现了A异常或者B异常的话,采取同一种处理方案
    public static void main(String[] args) {
        /*
            自己处理(捕获异常)灵魂四问:
                灵魂二问:如果try中可能会遇到多个问题,怎么执行?
                        会写多个catch与之对应
                        细节:
                            如果我们要捕获多个异常,这些异常中如果存在父子关系的话,那么父类一定要写在下面

                        了解性:
                            在JDK7之后,我们可以在catch中同时捕获多个异常,中间用|进行隔开
                            表示如果出现了A异常或者B异常的话,采取同一种处理方案

         */

        //JDK7
        int[] arr = {1, 2, 3, 4, 5, 6};

        try{
            System.out.println(arr[10]);//ArrayIndexOutOfBoundsException
            System.out.println(2/0);//ArithmeticException
            String s = null;
            System.out.println(s.equals("abc"));
        }catch(ArrayIndexOutOfBoundsException | ArithmeticException e){
            System.out.println("索引越界了");
        }catch(NullPointerException e){
            System.out.println("空指针异常");
        }catch (Exception e){ //父类异常需要放在子类的后面,否则会报错
            System.out.println("Exception");
        }

        System.out.println("看看我执行了吗?");
    }

如果父类异常放在上面,父类指针可以指向子类对象,所有的异常就都被父类Exception接收了,永远执行不到下面的catch了
在这里插入图片描述

  • 灵魂三问: 如果try中遇到的问题没有被捕获,怎么执行?
    相当于try…catch的代码白写了,最终还是会交给虚拟机进行处理。
public static void main(String[] args) {
        /*
            自己处理(捕获异常)灵魂三问:
                如果try中遇到的问题没有被捕获,怎么执行?
                相当于try...catch的代码白写了,最终还是会交给虚拟机进行处理。
         */
        int[] arr = {1, 2, 3, 4, 5, 6};

        try{
            System.out.println(arr[10]);//new ArrayIndexOutOfBoundsException();
        }catch(NullPointerException e){
            System.out.println("空指针异常");
        }

        System.out.println("看看我执行了吗?");
    }

控制台结果:
在这里插入图片描述

  • 灵魂四问:如果try中遇到了问题,那么try下面的其他代码还会执行吗?
    下面的代码就不会执行了,直接跳转到对应的catch当中,执行catch里面的语句体
    但是如果没有对应catch与之匹配,那么还是会交给虚拟机进行处理
public static void main(String[] args) {
        /*
            自己处理(捕获异常)灵魂四问:
                如果try中遇到了问题,那么try下面的其他代码还会执行吗?
                下面的代码就不会执行了,直接跳转到对应的catch当中,执行catch里面的语句体
                但是如果没有对应catch与之匹配,那么还是会交给虚拟机进行处理
         */
        int[] arr = {1, 2, 3, 4, 5, 6};

        try{
            System.out.println(arr[10]);
            System.out.println("看看我执行了吗?... try");
        }catch(ArrayIndexOutOfBoundsException e){
            System.out.println("索引越界了");
        }

        System.out.println("看看我执行了吗?... 其他代码");
    }

控制台结果:
在这里插入图片描述

异常中的常见方法

上述catch中直接打印语句非常不专业,不应该写打印语句,一般用异常中常见方法
在这里插入图片描述

public static void main(String[] args) {
        /*
              public String getMessage()          返回此 throwable 的详细消息字符串
              public String toString()            返回此可抛出的简短描述

              public void printStackTrace()       在底层是利用System.err.println进行输出
                                                  把异常的错误信息以红色字体输出在控制台
                                                  细节:仅仅是打印信息,不会停止程序运行
        */


        int[] arr = {1, 2, 3, 4, 5, 6};

/*
        try {
            System.out.println(arr[10]);
        } catch (ArrayIndexOutOfBoundsException e) {
          /*  String message = e.getMessage();
            System.out.println(message);//Index 10 out of bounds for length 6*/

         /*   String str = e.toString();
            System.out.println(str);//java.lang.ArrayIndexOutOfBoundsException: Index 10 out of bounds for length 6*/

            e.printStackTrace(); //包含异常的位置信息,最常用

        }
        System.out.println("看看我执行了吗?");*/


        //正常的输出语句
        //System.out.println(123);

        //错误的输出语句(而是用来打印错误信息)
        //System.err.println(123);



    }

抛出异常

在这里插入图片描述
写在方法定义处的throws异常是runtimeException异常的子类时可以省略不写

public static void main(String[] args) {
/*
        throws:写在方法定义处,表示声明一个异常。告诉调用者,使用本方法可能会有哪些异常。
        throw :写在方法内,结束方法。手动抛出异常对象,交给调用者。方法中下面的代码不再执行了。


        需求:
            定义一个方法求数组的最大值
*/


        int[] arr = null;
        int max = 0;
        try {
            max = getMax(arr);
        } catch (NullPointerException e) {
            System.out.println("空指针异常");
        } catch (ArrayIndexOutOfBoundsException e) {
            System.out.println("索引越界异常");
        }

        System.out.println(max);


    }


    public static int getMax(int[] arr)/* throws NullPointerException,ArrayIndexOutOfBoundsException*/{
        if(arr == null){
            //手动创建一个异常对象,并把这个异常交给方法的调用者处理
            //此时方法就会结束,下面的代码不会再执行了
           throw new NullPointerException();
        }

        if(arr.length == 0){
            //手动创建一个异常对象,并把这个异常交给方法的调用者处理
            //此时方法就会结束,下面的代码不会再执行了
            throw new ArrayIndexOutOfBoundsException();
        }


        System.out.println("看看我执行了吗?");
        int max = arr[0];
        for (int i = 1; i < arr.length; i++) {
            if(arr[i] > max){
                max = arr[i];
            }
        }
        return max;
    }

练习

定义一个女朋友类,键盘输入女朋友的名字和年龄
要求:名字长度为2~10
年龄要在18~40之间
如果输入不符合要求需要重新输入,直到输入正确为止

主函数:

public static void main(String[] args) {
        girlFriend gf = new girlFriend();
        Scanner sc = new Scanner(System.in);
        while (true) {
            try {
                System.out.println("请输入女朋友的名字:");
                String name = sc.nextLine();
                gf.setName(name);
                System.out.println("请输入女朋友的年龄:");
                String age = sc.nextLine();
                int age1 = Integer.parseInt(age);
                gf.setAge(age1);
                break;
            } catch (NumberFormatException e) {
                System.out.println("年龄输入有误!");
            } catch (RuntimeException e) {
                System.out.println("名字长度或年龄长度有误!");
            }
        }
        System.out.println(gf);
    }

girlfriend类:

package Exception;

import java.util.Objects;

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

    public girlFriend() {
    }

    @Override
    public String toString() {
        return "girlFriend{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        girlFriend that = (girlFriend) o;
        return age == that.age && Objects.equals(name, that.name);
    }

    @Override
    public int hashCode() {
        return Objects.hash(name, age);
    }

    public String getName() {
        return name;
    }

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

    public int getAge() {
        return age;
    }

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

    public girlFriend(String name, int age) {

        this.name = name;
        this.age = age;
    }
}

自定义异常

有些时候的异常Java本身没有相对应的异常,就需要我们自己去定义异常。
步骤:

  1. 定义异常类, 类名要见名知意
  2. 写继承关系,运行时异常要继承RuntimeException;编译时异常要继承Exception
  3. 空参构造
  4. 有参构造

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/177499.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

19.2、Javaweb案例_Servlet代码抽取优化分页数据redis缓存优化分页数据展示

优化Servlet 目的 减少Servlet的数量&#xff0c;现在是一个功能一个Servlet&#xff0c;将其优化为一个模块一个Servlet&#xff0c;相当于在数据库中一张表对应一个Servlet&#xff0c;在Servlet中提供不同的方法&#xff0c;完成用户的请求。 Idea控制台中文乱码解决&…

线性代数复习——行列式

文章目录第一章 行列式1.1 克拉默法则1.2 n阶行列式1.3 特殊行列式1.4 行列式的性质和推论1.5 余子式和代数余子式1.6 范德蒙德行列式第一章 行列式 1.1 克拉默法则 举例&#xff1a; 对于三元线性方程组 {a11x1a12x2a13x3b1a21x1a22x2a23x3b2a31x1a32x2a33x3b3(1)\begin{cas…

Allegro如何自动导弧操作指导

Allegro如何自动导弧操作指导 在做PCB设计的时候,经常会需要给信号线导弧,如果有大量的走线,导弧线是个比较费时间的工作,Allegro可以自动给信号线导弧,如下图 具体操作如下 选择Route选择Unsupported Prototypes

金融帝国实验室(Capitalism Lab)官方正版游戏『优惠活动政策』

「金融帝国实验室」&#xff08;Capitalism Lab&#xff09;Enlight 官方正版游戏「2023春节特卖」 ■时间&#xff1a;2023.01.21&#xff5e;2023.02.28 ■游戏开发商&#xff1a;Enlight Software Ltd. 请您认准以下官方正版游戏购买链接&#xff1a;支持“支付宝&a…

使用ebpf 监控mysqld 内核

一、开发思路分析 我们使用ebpf 监控mysql的话有两个思路去做这件事情 1、kprobe -> hook 掉tcp_sendmsg 和 tcp_recvmsg 一类的内核函数去分析网络协议 2、uprobe -> hook 掉 mysqld 的api函数&#xff0c;然后在此基础上进行统计 我使用的是uprobe 去hook 掉mysql内…

24. 面向对象的思想

1. 面向对象 类似于c/c#/java等高级语言&#xff0c;python从设计之初就已经是一门面向对象的语言&#xff0c;正因为如此&#xff0c;在python中创建一个类和对象是很容易的。关于面向对象的思想及基础&#xff0c;此处不再赘述。 2. 类对象 (1) 类变量&#xff1a;类变量在…

计算机毕业设计选题推荐基于nodejs+Vue360学生宿舍系统

管理员&#xff1b;首页、个人中心、宿舍信息管理、学生管理、宿舍报修管理、访客信息管理、水电费管理、管理员管理、交流论坛、系统管理&#xff0c;学生&#xff1b;首页、个人中心、宿舍报修管理、水电费管理&#xff0c; 前台首页&#xff1b;首页、交流论坛、通知公告、个…

STM32之HAL源码阅读(GPIO章节)

前言 说明 本文只针对于软件层面的阅读&#xff0c;详细操作请查阅对应的手册,使用过标准库的朋友更好的能理解本文针对的是STM32F10x系列&#xff0c;其他的类似 参考资料 STM32F10x中文参考手册 工具 stm32cubemx6.5clion最新版 HAL源码之GPIO的阅读 步骤一&#xff…

Cesium:Indexed 3D Scene Layers (I3S)加载

点击此处,查看完整的OGC标准列表项。Indexed 3D Scene Layers(I3S)标准官网介绍地址为:I3S,相关的GitHub主页地址为:Esri/i3s-spec,其详细介绍文档地址可点击此处查阅。我们的核心点在于介绍如何通过Cesium.js开发框架加载I3S三维场景服务。 目录 Cesium.js:I3S支持情…

JUC面试(九)——Synchronized和Lock的区别

Synchronized和Lock的区别 前言 对象锁&#xff08;synchronized method{}&#xff09;和类锁&#xff08;static sychronized method{}的区别 对象锁也叫实例锁&#xff0c;对应synchronized关键字&#xff0c;当多个线程访问多个实例时&#xff0c;它们互不干扰&#xff0…

基于蜣螂优化的BP神经网络(分类应用) - 附代码

基于蜣螂优化的BP神经网络&#xff08;分类应用&#xff09; - 附代码 文章目录基于蜣螂优化的BP神经网络&#xff08;分类应用&#xff09; - 附代码1.鸢尾花iris数据介绍2.数据集整理3.蜣螂优化BP神经网络3.1 BP神经网络参数设置3.2 蜣螂算法应用4.测试结果&#xff1a;5.Mat…

Ubuntu16.04安装N卡驱动

最近碰到个实验&#xff0c;需要用pytorch0.4和python2.7的环境&#xff0c;因为环境比较老&#xff0c;所以新显卡可能不能装。紧急联系朋友搞了张1660ti想来跑实验&#xff0c;结果光是驱动就碰了一鼻子灰&#xff0c;这里简单做下总结&#xff0c;引以为戒。首先是系统版本&…

Python for循环及用法详解

Python 中的循环语句有 2 种&#xff0c;分别是 while 循环和 for 循环&#xff0c;前面章节已经对 while 做了详细的讲解&#xff0c;本节给大家介绍 for 循环&#xff0c;它常用于遍历字符串、列表、元组、字典、集合等序列类型&#xff0c;逐个获取序列中的各个元素。for 循…

ARP渗透与攻防(五)之Ettercap劫持用户流量

ARP-Ettercap劫持用户流量 系列文章 ARP渗透与攻防(一)之ARP原理 ARP渗透与攻防(二)之断网攻击 ARP渗透与攻防(三)之流量分析 ARP渗透与攻防(四)之WireShark截获用户数据 一.ettercap 工具介绍 项目官网&#xff1a;http://ettercap.github.io/ettercap/index.html EtterC…

(十五)ForkJoin框架

ForkJoinPoolForkJoinPool是一种“分治算法”的多线程并行计算框架&#xff0c;自Java7引入。它将一个大的任务分为若干个子任务&#xff0c;这些子任务分别计算&#xff0c;然后合并出最终结果。ForkJoinPool比普通的线程池可以更好地实现计算的负载均衡&#xff0c;提高资源利…

安装MikTeX-latex

安装MikTeX-latex一、报错信息二、重新安装三、编译MDPI Template一、报错信息 由于之前使用的是basic-miktex-2.9.7269-x64.exe这个版本&#xff0c;当安装完成后&#xff0c;在更新package时遇到了以下错误&#xff1a; MikTeX update error 于是&#xff0c;通过搜索&…

冯·诺依曼、哈佛、改进型哈佛体系结构解析

在如今的CPU中&#xff0c;由于Catch的存在&#xff0c;这些概念已经被模糊了。个人认为去区分他们并没有什么意义&#xff0c;仅作为知识点。 哈佛结构设计复杂&#xff0c;但效率高。冯诺依曼结构则比较简单&#xff0c;但也比较慢。CPU厂商为了提高处理速度&#xff0c;在C…

2023 年程序员的热门开发项目:掌握最新技术的教程和工具的完整列表

欢迎阅读我们关于“2023 年程序员的热门开发项目”的博文&#xff01;作为一名开发人员&#xff0c;了解最新的技术和工具对于在就业市场上保持竞争力至关重要。在这篇文章中&#xff0c;我们编制了一份 2023 年最热门开发项目的完整列表&#xff0c;以及掌握每个项目的教程和资…

ChatGPT付费版来啦,好用的AI生成产品还能免费使用吗?AIGC工具箱

​最新消息&#xff0c;chatGPT推出了付费版&#xff01;每月&#xff04;42美元&#xff0c;不限流使用&#xff0c;你会付费使用吗&#xff1f;&#x1f9f0;AIGC工具箱下面推荐几款AI 生成产品&#xff01;你觉得哪个更好用呢&#xff1f;AI 的出现&#xff0c;颠覆了内容生…

自己动手写一个操作系统——我们能做什么,我们需要做什么

文章目录计算机启动流程第一条指令BIOSMBRloaderkernel总结计算机启动流程 第一条指令 在开机的一瞬间&#xff0c;也就是上电的一瞬间&#xff0c;CPU 的 CS:IP 寄存器被硬件强制初始化为 0xF000:0xFFF0。 CS:IP 就是 PC 指针&#xff0c;也就是 CPU 下一条要执行的指令的地址…