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.时间:本地时间,时间戳(默认采用标准时区),时区