@一贤不穿小鞋
1.函数式编程的优缺点
- 省内存(节省创建类和创建对象所消耗的系统资源);代码简洁.
- 缺点:可读性差.
eg:lambda的案例使用1:
public static void main(String[] args) {
//创建集合对象
// TreeSet<Student> stuSet=new TreeSet(new Comparator<Student>() {
// /**
// * 重写排序方法
// * 排序规则:先根据年龄升序排序
// */
// @Override
// public int compare(Student o1, Student o2) {
// if (o1.sage.compareTo(o2.sage)!=0) {//年龄不同
// return o1.sage.compareTo(o2.sage);
// }else {
// return 0;
// }
// }
// });
//用函数式编程来创建集合
TreeSet<Student> stuSet=new TreeSet<Student>((e1,e2)->e1.sage.compareTo(e2.sage));
//向集合中添加元素
stuSet.add(new Student("aa", 34));
stuSet.add(new Student("hh", 24));
stuSet.add(new Student("cc", 54));
stuSet.add(new Student("bb", 44));
stuSet.add(new Student("uu", 34));
for (Student stu1 : stuSet) {
System.out.println(stu1);
}
}
2.(重点)Lambda表达式:
- 适用条件:所有匿名内部类对象使用的地方都可以用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:/**
* 过滤员工信息的接口
* @author sx
* @version 1.0 2020年11月6日
*/
public interface MyPridecate {
/**
* 过滤员工的方法
* @param e
* @return Boolean
*/
Boolean test(Employee e);
}
public class Test3 {
public static void main(String[] args) {
//创建集合
List<Employee> emplist1=new ArrayList();
//向集合中添加元素
emplist1.add(new Employee("1", "张三1", 33, 22000.0));
emplist1.add(new Employee("2", "张三2", 23, 12000.0));
emplist1.add(new Employee("3", "张三3", 43, 18000.0));
emplist1.add(new Employee("4", "张三4", 31, 8000.0));
emplist1.add(new Employee("5", "张三5", 22, 5000.0));
emplist1.add(new Employee("6", "张三6", 18, 2000.0));
System.out.println("原集合为:");
for (Employee emp1 : emplist1) {
System.out.println(emp1);
}
List<Employee> emplist2=filterEmployee(emplist1, (e)->e.esalary>10000);
System.out.println("工资大于10000的员工信息为:");
for (Employee emp1 : emplist2) {
System.out.println(emp1);
}
List<Employee> emplist3=filterEmployee(emplist1, (e)->e.eage>25);
System.out.println("年龄大于25的员工信息为:");
for (Employee emp1 : emplist3) {
System.out.println(emp1);
}
List<Employee> emplist4=filterEmployee(emplist1, (e)->e.eid.contains("4"));
System.out.println("工号包含4的员工信息为:");
for (Employee emp1 : emplist4) {
System.out.println(emp1);
}
List<Employee> emplist5=filterEmployee(emplist1, (e)->e.eage>25&&e.esalary>10000);
System.out.println("年龄大于25且工资大于10000员工信息为:");
for (Employee emp1 : emplist5) {
System.out.println(emp1);
}
}
/**
* 根据员工的属性过滤员工信息
* @param emplist
* @param mp 过滤接口
* @return
*/
public static List<Employee> filterEmployee(List<Employee> emplist,MyPridecate mp) {
//创建一个新集合,将符合条件的员工信息存在新集合中
List<Employee> emplist2=new ArrayList();
//遍历原集合,判断每个员工是否符合条件
for (int i = 0; i < emplist.size(); i++) {
if (mp.test(emplist.get(i))) {
emplist2.add(emplist.get(i));
}
}
return emplist2;
}
}
2.4:函数式接口:如果一个接口中只有一个抽象方法,这个接口叫函数式接口.
- 函数式接口可以使用Lambda表达式,lambda表达 式会被匹配到这个抽象方法上 .
- 如果一个接口上面加@FunctionalInterface 注解,编译器如果发现你标注了这个注解的接口有多于一个抽象方法的时候会报错的。
eg:public class FunctionTest {
public static void main(String[] args) {
//使用消费型接口
consumerTest(1000.0, (e)->System.out.println("花了"+e+"元买了玩具车"));
//使用供给型接口
Integer[] result=supplierTest(()->(int)(Math.random()*10));
for (Integer n1 : result) {
System.out.println(n1);
}
//使用函数型接口
// Integer result3=FunctionTest1("123", new Function<String, Integer>() {
//
// @Override
// public Integer apply(String t) {
// return Integer.valueOf(t);
// }
// });
Integer result2=FunctionTest1("123", (e)->{ return Integer.valueOf(e);});
System.out.println("***"+result2);
//使用断言型接口
predicateTest("Hello.java",(e)->e.endsWith(".java"));
}
/**
* 消费型 接口的使用
* @param money
* @param con
*/
public static void consumerTest(Double money,Consumer<Double> con) {
con.accept(money);
}
/**
* 供给型接口的使用
* @param sup
* @return
*/
public static Integer[] supplierTest(Supplier<Integer> sup) {
//声明一个数组存生成的对象
Integer[] nums=new Integer[10];
for (int i = 0; i < nums.length; i++) {
nums[i]=sup.get();
}
return nums;
}
/**
* 函数式接口
* @param s1
* @param f
* @return
*/
public static Integer FunctionTest1(String s1,Function<String, Integer> f) {
return f.apply(s1);
}
/**
* 断言型接口
* @param fileName
* @param p2
*/
public static void predicateTest(String fileName,Predicate<String> p2) {
if (p2.test(fileName)) {
System.out.println("这是一个java源文件");
}
}
}
3.(了解)方法引用:是lambda表达式的一种简写形式。
- 如果lambda表达式方法体中只是调用一个特定的已经存在的方法,则可以使用方法引用。
- 使用“::”操作符将方法名和对象或类的名字分隔开来。以下是四种使用情况:
3.1:对象::实例方法
3.2:类::静态方法
3.3:类::实例方法,第一个参数是这个实例方法的调用者,第二个参数是 这个实例方法的参数时,就可以使用这种语法。
3.4:类::new 调用无参构造
eg:public static void main(String[] args) {
//创建对象
Student stu1=new Student("张三");
//用对象调用实例方法
Supplier<String> s1=stu1::getSname;
System.out.println("姓名为:"+s1.get());
//用类名调用静态方法
Supplier<String> s2=Student::show2;
System.out.println("静态方法为:"+s2.get());
//用类名调用无参构造方法
Supplier<Student> s3=Student::new;
Student stu2=s3.get();
System.out.println(stu2);
//用类名调用实例方法
Function<String,Object> f2=String::valueOf;
System.out.println("字符串为:"+f2.apply("123"));
}
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<Integer> s1=Stream.of(11,22,33,55);
//第二种:获得流的方法
String[] ss= {"aa","cc","bb"};
Stream<String> s2=Stream.of(ss);
//第三种:获得流的方法
List<Integer> alist=new ArrayList();
alist.add(22);
alist.add(12);
alist.add(34);
//流中用单线程
Stream<Integer> s3=alist.stream();
//并行流,流中用多线程
Stream<Integer> s4=alist.parallelStream();
//第四种:获得无限随机流
Stream<Integer> s5=Stream.generate(()->(int)(Math.random()*21));
//第五种:获得无限自动增长的流
Stream<Integer> s6=Stream.iterate(1, (e)->e+2);
//遍历流中元素
s6.limit(5).forEach((e)->System.out.println(e));
}
4.3:Stream的常用中间操作:过滤,限制,跳过,去重,map映射,自然排序,定制排序
- 注意:Stream对象调用中间操作的方法后返回的还是Stream类型对象.
eg:public static void main(String[] args) {
//创建一个集合
List<Integer> alist=new ArrayList();
alist.add(22);
alist.add(12);
alist.add(34);
alist.add(44);
alist.add(54);
alist.add(94);
alist.add(74);
alist.add(22);
//过滤
Stream<Integer> s1= alist.stream().filter((e)->e.toString().endsWith("4"));
//限制
Stream<Integer> s2=alist.stream().limit(3);
//跳过
Stream<Integer> s3=alist.stream().skip(2);
//去重
Stream<Integer> s4=alist.stream().distinct();
//map映射
Stream<String> s5= alist.stream().map((e)->e.toString());
//自然排序
Stream<Integer> s6=alist.stream().sorted();
//定制排序
Stream<Integer> s7=alist.stream().sorted((o1,o2)->-o1.compareTo(o2));
//遍历流中数据
s7.forEach((e)->System.out.println(e));
}
4.4:Stream的终止操作:
- //(*)forEach–遍历流中数据
- //allMatch——检查是否匹配所有元素
- // anyMatch——检查是否至少匹配一个元素
- // noneMatch——检查是否没有匹配的元素
- // findFirst——返回第一个元素
- // findAny——返回当前流中的任意元素
- // (*)count——返回流中元素的总个数
- // max——返回流中大值
- // min——返回流中小值
- //(*)reduce处理集合中元素得到一个新数据,可以实现累加,累乘…
- //(*)将流转换为list集合
- //(*)将流转换为set集合
eg:public static void main(String[] args) {
//创建一个集合
List<Integer> alist=new ArrayList();
alist.add(22);
alist.add(12);
alist.add(34);
alist.add(44);
alist.add(54);
alist.add(94);
alist.add(74);
alist.add(22);
//allMatch——检查是否匹配所有元素
Boolean result1= alist.stream().allMatch((e)->e<10);
//System.out.println(result1);
// (*)count——返回流中元素的总个数
long result2=alist.stream().count();
//System.out.println(result2);
// max——返回流中大值
// min——返回流中小值
Optional<Integer> max=alist.stream().max((o1,o2)->o1.compareTo(o2));
//System.out.println(max);
//(*)reduce处理集合中元素得到个新数据,第一个参数是k的初值,e表示集合中每个元素,方法体k=k*e
Integer ss= alist.stream().reduce(1,(e,k)->e*k);
//上面的一句代码相当于下面这段代码
// int k=1;
// for (Integer e : alist) {
// k=k*e;
// }
//System.out.println(ss);
//(*)将流转换为set集合
Set<Integer> result3= alist.stream().collect(Collectors.toSet());
//(*)将流转换为list集合
List<Integer> result4=result3.stream().collect(Collectors.toList());
//遍历流中数据
result4.stream().forEach((e)->System.out.println(e));
}
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());
//在当前日期上减去2天
LocalDateTime d1=today.minusDays(2);
System.out.println(d1);
//在当前日期上加上3天
LocalDateTime d2=d1.plusDays(3);
System.out.println(d2);
}
5.2.3:Instant 时间戳 类似以前的Date和ZoneId时区
- //Date-----Instant-------LocalDateTime
- //LocalDateTime-----Instant-------Date---------
eg:public static void main(String[] args) {
//获得时间戳,得到的是标准时间
Instant n1=Instant.now();
System.out.println("当前时间戳:"+n1);
//获得所有时区
Set<String> allZone=ZoneId.getAvailableZoneIds();
System.out.println("所有时区:"+allZone);
ZoneId defaultZone=ZoneId.systemDefault();
System.out.println("默认时区:"+defaultZone);
//Date-----Instant-------LocalDateTime
//获得原来java中原始系统时间
Date dd1=new Date();
System.out.println("dd1:"+dd1);
//将原始系统时间转换为时间戳
Instant dd2=dd1.toInstant();
//将时间戳转换为LocalDateTime
LocalDateTime dd3=dd2.atZone(ZoneId.systemDefault()).toLocalDateTime();
System.out.println("dd3:"+dd3);
//LocalDateTime-----Instant-------Date---------
LocalDateTime dd4=LocalDateTime.now();
System.out.println("dd4:"+dd4);
//将localDatetime转换为时间戳
Instant dd5=dd4.atZone(ZoneId.systemDefault()).toInstant();
//将时间戳转换为java.util.date
Date dd6=Date.from(dd5);
System.out.println("dd6:"+dd6);
}
5.2.4:时间矫正器 TemporalAdjuster
5.2.5: DateTimeFormatter
eg:public static void main(String[] args) {
//创建一个格式化对象
DateTimeFormatter dtf=DateTimeFormatter.ofPattern("yyyy/MM/dd HH:mm:ss");
//将localDatetime转换指定格式的String类型
String s1=LocalDateTime.now().format(dtf);
System.out.println(s1);
//将String类型转换localDatetime类型,前提条件是String类型的字符串符合格式化对象中格式
LocalDateTime dd7=LocalDateTime.parse("2020/11/09 14:52:21", dtf);
System.out.println(dd7);
//时间矫正器 TemporalAdjuster
LocalDateTime dd8=LocalDateTime.now().with(TemporalAdjusters.lastDayOfMonth());
System.out.println("这个月的最后一天是:"+dd8);
}
6.(扩展):策略模式
6.1:优点:提高程序可扩展性.
6.2:实现原理
- 将相同行为抽成一个接口(行为接口),定义方法,将具体实现定义实现类(算法类),在调用算法的方法中,用行为接口作为参数,调用方法.在实参传具体接口实现类(算法类),从而不同实现类,实现不同效果.
6.3:案例
- 有个员工集合,根据不同条件筛选需要员工信息,相同点复选员工,不同点就是筛选条件不同,将筛选作为接口,声明几个不同筛选条件实现类.
eg:/**
* 过滤员工信息的接口
* @author sx
* @version 1.0 2020年11月6日
*/
public interface MyPridecate {
/**
* 过滤员工的方法
* @param e
* @return Boolean
*/
Boolean test(Employee e);
}
/**
* 1.获得工资大于10000所有员工信息
* 2.获得年龄大于25岁所有员工信息
* @author sx
* @version 1.0 2020年11月6日
*/
public class Test2 {
public static void main(String[] args) {
//创建集合
List<Employee> emplist1=new ArrayList();
//向集合中添加元素
emplist1.add(new Employee("1", "张三1", 33, 22000.0));
emplist1.add(new Employee("2", "张三2", 23, 12000.0));
emplist1.add(new Employee("3", "张三3", 43, 18000.0));
emplist1.add(new Employee("4", "张三4", 31, 8000.0));
emplist1.add(new Employee("5", "张三5", 22, 5000.0));
emplist1.add(new Employee("6", "张三6", 18, 2000.0));
System.out.println("原集合为:");
for (Employee emp1 : emplist1) {
System.out.println(emp1);
}
List<Employee> emplist2=filterEmployee(emplist1, new MyPridecate() {
@Override
public Boolean test(Employee e) {
if (e.esalary>10000) {
return true;
}
return false;
}
});
System.out.println("工资大于10000的员工信息为:");
for (Employee emp1 : emplist2) {
System.out.println(emp1);
}
List<Employee> emplist3=filterEmployee(emplist1, new MyPridecate() {
@Override
public Boolean test(Employee e) {
if (e.eage>25) {
return true;
}
return false;
}
});
System.out.println("年龄大于25的员工信息为:");
for (Employee emp1 : emplist3) {
System.out.println(emp1);
}
List<Employee> emplist4=filterEmployee(emplist1, new MyPridecate() {
@Override
public Boolean test(Employee e) {
if (e.eid.contains("4")) {
return true;
}
return false;
}
});
System.out.println("工号包含4的员工信息为:");
for (Employee emp1 : emplist4) {
System.out.println(emp1);
}
}
/**
* 根据员工的属性过滤员工信息
* @param emplist
* @param mp 过滤接口
* @return
*/
public static List<Employee> filterEmployee(List<Employee> emplist,MyPridecate mp) {
//创建一个新集合,将符合条件的员工信息存在新集合中
List<Employee> emplist2=new ArrayList();
//遍历原集合,判断每个员工是否符合条件
for (int i = 0; i < emplist.size(); i++) {
if (mp.test(emplist.get(i))) {
emplist2.add(emplist.get(i));
}
}
return emplist2;
}
}