34. java8新特性

1.函数式编程的优点:省内存(少创建好多类和对象);代码简洁.
缺点:可读性较差.

	eg:public static void main(String[] args) {
    
    
    /*1.传统java代码*/
    //声明集合
    TreeSet<String> tset=new TreeSet<>(new Comparator<String>() {
    
    
        @Override
        public int compare(String o1, String o2) {
    
    
            return o1.compareTo(o2);
        }
    });
    tset.add("fff");
    tset.add("bb");
    tset.add("aa");
    tset.add("hh");

    //遍历
    for (String s1:tset){
    
    
        System.out.println(s1);
    }

    System.out.println("---------------------------------------");
    /*2.用函数式编程实现上面的代码*/
    TreeSet<String> tset2=new TreeSet<>((o1,o2)->-o1.compareTo(o2));//用了lambda表达式
    tset2.add("fff");
    tset2.add("bb");
    tset2.add("aa");
    tset2.add("hh");
    //遍历
    tset2.forEach(System.out::println);//用函数式编程中函数,用了方法引用
}

2.Lambda表达式:
2.1:lambda表达式基本语法:
函数式接口:一个接口中只有一个抽象方法,叫函数式接口.
eg: <函数式接口> <变量名> = (参数1,参数2…) -> { //方法体 };
2.2:lambda表达式注意事项:
Lambda引入了新的操作符:->(箭头操作符)->将表达式分成两部分
2.2.1:左侧:(参数1,参数2…)表示参数列表; 右侧:{}内部是方法体
2.2.2:形参列表的数据类型会自动推断,所以小括号只需要写形参参数名;
2.2.3:如果形参列表为空,只需保留();
2.2:4:如果形参只有1个,()可以省略,只需要参数的名称即可;
2.2:5:如果执行语句只有1句,且无返回值,{}可以省略,
若有返回值,则若想省去{},则必须同时省略return,且执行语句也 保证只有1句;
2.2.6:lambda不会生成一个单独的内部类文件(省内存);
2.2.7:lambda表达式若访问了局部变量,则局部变量必须是final的,若是局部变量没有加final关键字,系统会自动添加,此 后在修改该局部变量,会报错。
2.3:lambda的使用:

	eg:lambda的案例使用1:
			需求1:有一个员工集合,获取年龄大于25的员工信息
			需求2:获取工资大于10000的员工信息
			eg:/**
 * 过滤接口
 * @version 1.0
 * @auth sx
 * @date 2020/4/9
 */
public interface MyPredicate<T> {
    
    
    /**
     * 过滤方法
     * @param t
     * @return
     */
    boolean test(T t);
}

public class LambdaTest3 {
    
    
    public static void main(String[] args) {
    
    
        List<Employee> empList=new ArrayList();
        empList.add(new Employee("ab唐三",18,18000.0));
        empList.add(new Employee("fefef萧炎",28,28000.0));
        empList.add(new Employee("c小舞",17,8000.0));
        empList.add(new Employee("kdf玉儿",38,28000.0));
        empList.add(new Employee("b白菜",48,12000.0));

        //获取年龄大于25的员工信息
        List<Employee> empList4=filterEmp(empList,(e)->e.getEage()>25);
        empList4.forEach(System.out::println);

        System.out.println("--------------------------");
        //获取工资大于10000的员工信息
        List<Employee> empList5=filterEmp(empList,(e)->e.getSalary()>10000);
        empList5.forEach(System.out::println);

        System.out.println("--------------------------");
        //获取姓名长度为3的员工信息
        List<Employee> empList6=filterEmp(empList,(e)->e.getEname().length()==3);
        empList6.forEach(System.out::println);
    }

    /**
     * 过滤员工信息的方法
     * @return List<Employee>
     */
    public static List<Employee> filterEmp(List<Employee> empList,MyPredicate<Employee> mp) {
    
    
        //声明一个集合满足要求的员工信息
        List<Employee> empList1=new ArrayList();
        //遍历原集合
        for (Employee e1:empList){
    
    
            //如果当前遍历员工满足要求,添加到新集合中存着
            if (mp.test(e1)){
    
    
                empList1.add(e1);
            }
        }
        return empList1;
    }
}
	      lambda结合Stream使用的案例使用2
			eg:public static void main(String[] args) {
    
    
    List<Employee> empList=new ArrayList();
    empList.add(new Employee("ab唐三",18,18000.0));
    empList.add(new Employee("fefef萧炎",28,28000.0));
    empList.add(new Employee("c小舞",17,8000.0));
    empList.add(new Employee("kdf玉儿",38,28000.0));
    empList.add(new Employee("b白菜",48,12000.0));

    //获取年龄大于25的员工信息
    empList.stream().filter((e)->e.getEage()>25).forEach(System.out::println);

    System.out.println("--------------------------");
    //获取工资大于10000的员工信息
    empList.stream().filter((e)->e.getSalary()>10000).forEach((e)-> System.out.println(e));

    System.out.println("--------------------------");
    //获取姓名长度为3的员工信息
    empList.stream().filter((e)->e.getEname().length()==3).forEach((e)-> System.out.println(e));
}

	2.4:函数式接口:如果一个接口中只有一个抽象方法,这个接口叫函数式接口.
		函数式接口可以使用Lambda表达式,lambda表达 式会被匹配到这个抽象方法上 .
		如果一个接口上面加@FunctionalInterface 注解,编译器如果发现你标注了这个			注解的接口有多于一个抽象方法的时候会报错的。
		 
eg:public class FunctionInterfaceTest {
    
    
    public static void main(String[] args) {
    
    
        //调用消费型接口使用的方法,接口实例对象传lambda表达式
        ConsumerMeth(1000.0,(e)-> System.out.println("花了"+e+"元吃饭"));
        //调用供给型接口使用的方法,接口实例对象传lambda表达式
        List<Double> numlist=supplierMeth(5,()->Math.random());
        numlist.forEach(System.out::println);

        //调用函数型接口使用的方法
        Integer num=functionMeth("123",(e)->Integer.valueOf(e));
        System.out.println("num:"+num);

        //调用断言型接口的使用方法
        List<Employee> empList=new ArrayList();
        empList.add(new Employee("a唐三",18,18000.0));
        empList.add(new Employee("f萧炎",28,28000.0));
        empList.add(new Employee("c小舞",17,8000.0));
        empList.add(new Employee("k玉儿",38,28000.0));
        empList.add(new Employee("b白菜",48,12000.0));

        List<Employee> empList3=PredicateMeth(empList,(e)->e.getEage()>25);
        empList3.forEach((e)-> System.out.println(e));
        System.out.println("---------------------------");
        empList3.forEach(System.out::println);
    }

    /**
     * 消费型接口使用的方法
     * @param money
     * @param con
     */
    public static void ConsumerMeth(Double money, Consumer<Double> con) {
    
    
        con.accept(money);
    }

    /**
     * 供给型接口使用的方法
     * @param count
     * @param sup
     * @return
     */
    public static List<Double> supplierMeth(int count, Supplier<Double> sup) {
    
    
        List<Double> nums=new ArrayList<>();
        for (int i=1;i<=count;i++){
    
    
            Double n=sup.get();
            nums.add(n);
        }
        return nums;
    }

    /**
     * 函数型接口使用的方法
     * @param s1
     * @param fun
     * @return
     */
    public static Integer functionMeth(String s1, Function<String,Integer> fun) {
    
    
        Integer num=fun.apply(s1);
        return num;
    }

    /**
     * 断言型接口的使用方法
     * @param empList
     * @param pre
     * @return
     */
    public static List<Employee> PredicateMeth(List<Employee> empList, Predicate<Employee> pre) {
    
    
        //声明一个集合存满足条件的元素
        List<Employee> empList2=new ArrayList<>();
        for (Employee e:empList){
    
    
            if (pre.test(e)){
    
    
                empList2.add(e);
            }
        }
        return empList2;
    }
}
2.5:lambda表达式的使用场景:当方法的形参是函数式接口,实参用lambda表达式.

3.方法引用:是lambda表达式的一种简写形式。 如果lambda表达式方法体中只是调用一个特定的已经存在的方法,则 可以使用方法引用。

	使用“::”操作符将方法名和对象或类的名字分隔开来。以下是四种使用情况:
			3.1:对象::实例方法 
			3.2:类::静态方法 
			3.3:类::实例方法 
			3.4:类::new        调用无参构造
	eg:public static void main(String[] args) {
    
    
    //创建一个员工对象
    Employee e1=new Employee("赵天慧",18,20000.0);

    //对象::实例方法
    Supplier<String> sup1=e1::getEname;
    System.out.println(sup1.get());

    //类::静态方法
    Supplier<Double> num1=Math::random;
    System.out.println(num1.get());

    //类::实例方法
    Function<Employee,String> fun1=Employee::getEname;
    System.out.println(fun1.apply(e1));

    //:类::new ,调用无参构造创建实例对象
    Supplier<Employee> sup3=Employee::new;
    System.out.println(sup3.get());
}

4.Stream API:一个Stream表面上与一个集合很类似,集合中保存的是数据,而流中对数据的 操作。

	4.1:Stream特点: 
		4.1.1:Stream 自己不会存储元素。  
		4.1.2:Stream 不会改变源对象。相反,他们会返回一个持有结果的新Stream。  
		4.1.3:Stream 操作是延迟执行的。这意味着他们会等到需要结果的时候才执行。 
	4.2:获得Stream的方法(6)
		eg:public static void main(String[] args) {
    
    
    //直接创建流
    Stream<String> s1=Stream.of("吕宗洪","黄小平","刚哥");

    //声明数组,将数组转换为流
    Integer[] nums={
    
    77,88,33,44};
    Stream<Integer> s2= Stream.of(nums);

    //将集合转换为流
    List<Employee> empList=new ArrayList();
    empList.add(new Employee("ab唐三",18,18000.0));
    empList.add(new Employee("fefef萧炎",28,28000.0));
    empList.add(new Employee("c小舞",17,8000.0));
    empList.add(new Employee("kdf玉儿",38,28000.0));
    empList.add(new Employee("b白菜",48,12000.0));
    //第一种转换流,单线程的
    Stream s3=empList.stream();
    //第二种转换并行注,用多线程的
    Stream s4=empList.parallelStream();

    //用跌代方式生成无限流
    Stream s5=Stream.iterate(1,(e)->e+2);
    //s5.limit(10).forEach(System.out::println);

    //用供给型接口直接生成无限流
    Stream s6=Stream.generate(()->Math.random());
    s6.limit(10).forEach(System.out::println);


    //遍历流中数据
    //s4.forEach((e)-> System.out.println(e));
}
4.3:Stream的常用中间操作:过滤,限制,跳过,去重,map映射,自然排序,定制排序
	注意:Stream对象调用中间操作的方法后返回的还是Stream类型对象.
		eg:public static void main(String[] args) {
    
    
    //声明集合存员工信息
    List<Employee> empList=new ArrayList();
    empList.add(new Employee("ab唐三",18,18000.0));
    empList.add(new Employee("fefef萧炎",28,28000.0));
    empList.add(new Employee("c小舞",17,8000.0));
    empList.add(new Employee("kdf玉儿",38,28000.0));
    empList.add(new Employee("b白菜",48,12000.0));
    empList.add(new Employee("ab唐三",28,18000.0));
   // 过滤
    Stream<Employee> empList2=empList.stream().filter((e)->e.getSalary()>10000);

    // 限制
    Stream<Employee> empList3=empList.stream().limit(3);

    // 跳过
    Stream<Employee> empList4=empList.stream().skip(2);

    // 去重,集合中元素如果不重写equals()和hashcode(),根据内存地址去重;如果重写了equals()和hashcode(),就根据元素的值来去重
    Stream<Employee> empList5=empList.stream().distinct();

    // map映射
    Stream<String> empList6=empList.stream().map((e)->e.getEname());

    // 自然排序,能用自然排序的集合的泛型类型一定要实现自然排序器接口,重写排序方法
    Stream<Integer> nums=Stream.of(88,77,11,33,22);
    Stream<Integer> empList7=nums.sorted();

    // 定制排序
    Stream<Employee> empList8=empList.stream().sorted((o1,o2)->{
        if(o1.getEname().compareTo(o2.getEname())!=0){
    
    //姓名不同,按姓名升序
            return o1.getEname().compareTo(o2.getEname());
        }else{
    
    姓名相同,按年龄降序
            return -o1.getEage().compareTo(o2.getEage());
        }
    });

    empList8.forEach(System.out::println);
}
4.4:Stream的终止操作:
		//allMatch——检查是否匹配所有元素
 // anyMatch——检查是否至少匹配一个元素
 // noneMatch——检查是否没有匹配的元素
 // findFirst——返回第一个元素
 // findAny——返回当前流中的任意元素
 // (*)count——返回流中元素的总个数
 // max——返回流中大值
 // min——返回流中小值
 //(*)reduce处理集合中元素得到个新数据
 //(*)将流转换为list集合
 //(*)将流转换为set集合
eg:public static void main(String[] args) {
    
    
    List<Integer> nums=new ArrayList<>();
    nums.add(77);
    nums.add(88);
    nums.add(44);
    nums.add(11);
    nums.add(33);
    nums.add(99);
    nums.add(77);

    //allMatch——检查是否匹配所有元素
    boolean result1=nums.stream().allMatch((e)->e>66);//false

    // anyMatch——检查是否至少匹配一个元素
    boolean result2=nums.stream().anyMatch((e)->e>66);//true

    // noneMatch——检查是否没有匹配的元素
    boolean result3=nums.stream().noneMatch((e)->e>66);//false

    // findFirst——返回第一个元素
    Integer result4=nums.stream().findFirst().get();//77

    // findAny——返回当前流中的任意元素
    Integer result5=nums.parallelStream().findAny().get();

    // count——返回流中元素的总个数
    long result6=nums.stream().count();

    // max——返回流中按排序规则排好序的最后一个值
    Integer result7=nums.stream().max((o1,o2)->-o1.compareTo(o2)).get();

    // min——返回流中按排序规则排好序的第一个值
    Integer result8=nums.stream().min((o1,o2)->o1.compareTo(o2)).get();

    //reduce处理集合中元素得到个新数据,x=第一个参数值,y遍历的集合中每个元素
    Integer result9=nums.stream().reduce(0,(x,y)->x+y);//累加效果 x=0,x=x+每个遍历的元素

    //将流转换为list集合
    List<Integer> result10=nums.stream().collect(Collectors.toList());

    //将流转换为set集合
    Set<Integer> result11=nums.stream().collect(Collectors.toSet());

    System.out.println(result11);
}

5.java8中新日期

	5.1:原来java中日期问题:非线程安全;设计差(java.util.Date,java.sql.Date);时区处理麻烦 
		java8中引入新日期时间API
			LocalDate:是一个不可变的日期时间对象,表示日期,通常被视为年月日
			LocalTime:是一个不可变的日期时间对象,代表一个时间,通常被看作是小时 - 秒
			LocalDateTime:是一个不可变的日期时间对象,代表日期时间,
						通常被视为年 - 月 - 日 - 时 - 分 - 秒。
			Instant:在时间线上的瞬间点,可以对日期作加,减操作。 
			ZoneId:用于识别用于在Instant和LocalDateTime之间转换的规则
			DateTimeFormatter:格式化器用于打印和解析日期时间对象。
	5.2:新日期时间的使用:
		5.2.1:原来Java.util.Date和Java.sql.Date线程安全性问题(了解)
		5.2.2:本地化日期时间 
				eg:public static void main(String[] args) {
    
    
    //获得当前系统时间
    LocalDateTime today=LocalDateTime.now();
    System.out.println("当前系统时间:"+today);
    System.out.println("年:"+today.getYear());
    System.out.println("月:"+today.getMonth());
    System.out.println("日:"+today.getDayOfMonth());
    System.out.println("时:"+today.getHour());
    System.out.println("分:"+today.getMinute());
    System.out.println("秒:"+today.getSecond());

    //其他时间
    LocalDateTime ldt=LocalDateTime.of(2020,5,1,1,1,1);
    System.out.println("自已设定的时间:"+ldt);

    //加时间
    LocalDateTime ldt2=ldt.plusDays(5);
    System.out.println("上课时间:"+ldt2);

    //减时间
    LocalDateTime ldt3=ldt.minusDays(20);
    System.out.println("五一之前的20天:"+ldt3);
}

		5.2.3:Instant 时间戳 类似以前的Date和ZoneId时区
			//Date-----Instant-------LocalDateTime 
			//LocalDateTime-----Instant-------Date---------
			eg:public static void main(String[] args) {
    
    
        //获得当前时间戳
//        Instant start=Instant.now();
//        System.out.println("start:"+start);
//        int num=0;
//        for (int i=1;i<20000;i++){
    
    
//            num=i;
//        }
//        Instant end=Instant.now();
//        System.out.println("end:"+end);
//        //计算时间之差Duration.between(d1,d2);
//        System.out.println("for循环执行时间为:"+ Duration.between(start,end));
//
//        //获得所有时区
//        Set<String> zoneAll=ZoneId.getAvailableZoneIds();
//        System.out.println("所有时区:"+zoneAll);
//        //获得当前默认时区
//        ZoneId currZone=ZoneId.systemDefault();
//        System.out.println("当前系统默认时区:"+currZone);


        /*Date-----Instant-------LocalDateTime*/
        //获得当前系统时间
        Date d1=new Date();
        System.out.println("d1:"+d1);
        //将date转换为instant
        Instant is1=d1.toInstant();
        //将instrant时间戳先转换为中国时区的,再转换为localDateTime
        LocalDateTime ldt1=is1.atZone(ZoneId.systemDefault()).toLocalDateTime();
        System.out.println("ldt1:"+ldt1);

        //LocalDateTime-----Instant-------Date---------
        //获得当前系统本地时间
        LocalDateTime ldt2=LocalDateTime.now();
        //将本时间转换为时间戳
        Instant is2=ldt2.atZone(ZoneId.systemDefault()).toInstant();
        //将时间戳转换为date
        Date d2=Date.from(is2);
        System.out.println("d2:"+d2);

    }


		5.2.4:时间矫正器 TemporalAdjuster 
			eg://时间矫正器
//当前时间
LocalDateTime ldt5=LocalDateTime.now();
//下个星期5
LocalDateTime ldt6= ldt5.with(TemporalAdjusters.next(DayOfWeek.FRIDAY));
System.out.println("下个星期五为:"+ldt6);
//下个星期1
LocalDateTime ldt7=ldt5.with(TemporalAdjusters.next(DayOfWeek.MONDAY));
System.out.println("下个周一为:"+ldt7);

		5.2.5: DateTimeFormatter
			eg://日期字符串的格式转换
DateTimeFormatter dtf=DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
//将日期转换为指定格式字符串
LocalDateTime ldt3=LocalDateTime.now();
String s1=ldt3.format(dtf);
System.out.println("s1:"+s1);

//将指定字符串转换日期
String s2="2020-04-13 10:29:11";
LocalDateTime ldt4=LocalDateTime.parse(s2,dtf);
System.out.println(ldt4);

6.(扩展):策略模式
6.1:优点:提高程序可扩展性.
6.2:实现原理:将相同行为抽成一个接口(行为接口),定义方法,将具体实现定义实现类(算法类),在调用算法的方法中,用行为接口作为参数,调用方法.在实参传具体接口实现类(算法类),从而不同实现类,实现不同效果.
6.3:案例:有个员工集合,根据不同条件筛选需要员工信息,相同点复选员工,不同点就是筛选条件不同,将筛选作为接口,声明几个不同筛选条件实现类.

总结:
1.lambda表达式及四大系统函数式(重点,要记住)接口的使用
2.方法引用,是对lambda表达式简写.
3.Stream:创建流6种,中间操作7种,终止操作记重点4种
4.时间:本地时间,时间戳(默认采用标准时区),时区

猜你喜欢

转载自blog.csdn.net/qq_44949002/article/details/120259485