##java8内容
1.Lambda表达式 ★
2.函数式接口 ★
3.方法引用 ★
4.构造器引用|数组引用 ★
5.StreamAPI ★
6.接口中可以定义默认方法和静态方法 ★
7.Optional类的引入:为了减少空指针异常【了解】
8.新日期API【了解】
9.重复注解【了解】
10.Nashone引擎的使用:在jvm上运行js【后面课程】
##Java8引进
1995年推出jdk1.0
04年推出 jdk5 ,改革非常大
06年推出 jdk6 ,企业中使用的最长久版本
--------被Oralce公司收购-----------
11年 推出 jdk7
14年 推出 jdk8 LTS版本:受商业公司支持的长期版本
17年 9月份推出 jdk9, “六个月版本升级计划” 小版本
18年 3月份 推出jdk10 小版本
18年 9月份 推出jdk11 LTS版本:
##Lambda表达式
###理解
理解成一段可以传递的代码,作为函数式接口的实例出现
###应用场景
示例1:
接口 a = Lambda表达式;
示例2:【居多】
method(Lambda表达式);
public void method(接口 a){
}
###好处
1、语句更加简洁
2、更加紧凑,使java语言的表达能力得到了提升!
###语法 ★
(参数类型 参数名,参数类型 参数名)->{方法体的实现/Lambda体}
示例:
(String o1, String o2)->{return o1.compareTo(o2);}
特点:
①左侧参数列表中的参数类型可以省略!
②如果左侧参数列表中,只有一个参数,则小括号也可以省略
③右侧的Lambda体中如果仅有一句话,则大括号可以省略
④右侧的Lambda体中仅有的一句话为return语句,则return要求省略
##函数式接口
理解:
只有一个抽象方法的接口,称为函数式接口
Consumer消费型接口
Supplier供给型接口
Function
Predicate 断定型接口
##方法引用
###理解
* 方法引用实质上就是Lambda表达式的简化,同样可以作为函数式接口的实例出现
* 能用方法引用 的肯定能用Lambda表达式
* 能用Lambda表达式的不一定能用方法应用,除非满足以下要求:
###要求:
①Lambda体中仅仅只有一句话
②仅有的一句话为 方法调用!
③调用方法的参数列表和Lambda体实现的抽象方法的参数列表一致!
调用方法的返回类型和Lambda体实现的抽象方法的返回类型一致!
特例:类名::普通方法
调用方法的调用者正好是 抽象方法的第一个参数,并且其他参数一致
调用方法的返回类型和抽象方法的返回类型一致!
###语法:
类名或对象::方法名
###情况:
* 1、对象::普通方法
* 2、类名::静态方法
* 3、类名::普通方法
##关于方法引用、数组引用、构造器引用的总结
1.能使用方法引用|数组引用|构造器引用的地方,肯定能用Lambda表达式!
2.能用Lambda表达式的地方,不一定能用方法引用|数组引用|构造器引用,必须满足以下条件
3.能使用Lambda表达式的地方,肯定能用匿名内部类
能用匿名内部类的,不一定能用Lambda表达式,必须满足:
实现的接口为 函数式接口(里面只有一个抽象方法的接口)!
方法引用:
①Lambda体只有一句话
②仅有的一句话为调用方法
③方法的参数列表、返回类型正好与抽象方法的参数列表、返回类型一致!
特殊:如果是类名::普通方法,则要求方法的调用者为抽象的方法的第一个参数,其他一致
此类用于演示方法引用
* 举例:
* 匿名内部类:
* UnaryOperator up = new UnaryOperator(){
* public Double apply(Double t){
* return Math.sqrt(t);
* }
* };
* Lambda:
* UnaryOperator up =d->Math.sqrt(d);
* 方法引用:
UnaryOperator up = Math::sqrt;
*/
public class TestMethodRef {
//1、对象::普通方法
@Test
public void test1_1() {
//匿名内部类
// Runnable r = new Runnable() {
// @Override
// public void run() {
// System.out.println();
// }
// };
//方法引用
Runnable r2 = System.out::println;
r2.run();
}
//对象::普通方法
@Test
public void test1_2() {
//匿名内部类
// Consumer con = new Consumer() {
// @Override
// public void accept(String t) {
// System.out.println(t);
//
// }
// };
//方法引用
Consumer con2 = System.out::println;
con2.accept("赵丽颖和冯绍峰已经结婚了");
}
//2、类名::静态方法
@Test
public void test2() {
//匿名内部类
Comparator com = new Comparator() {
@Override
public int compare(Double o1, Double o2) {
return Double.compare(o1, o2);
}
};
//Lambda
Comparator com1 = (o1,o2)->Double.compare(o1, o2);
//方法引用
Comparator com2 =Double::compare;
}
//3、类名::普通方法
@Test
public void test3() {
//匿名内部类
// Function fun = new Function(){
// @Override
// public String apply(Employee t) {
// return t.getName();
// }
// };
//方法引用
Function fun2 = Employee::getName;
System.out.println(fun2.apply(new Employee("张无忌", 12, 1000, '男')));
}
//3、类名::普通方法
@Test
public void test3_2() {
//匿名内部类
// BiFunction bf = new BiFunction() {
// @Override
// public Boolean apply(String t, String u) {
// return t.equals(u);
// }
// };
//方法引用
BiFunction bf2 = String::equals;
System.out.println(bf2.apply("hello", "Hello"));
}
}
构造器引用
理解:构造器引用本质上就是Lambda表达式,只是语句更加简洁。作为函数式接口的实例,一般作为参数传递给方法!
能用构造器引用的地方,肯定能用Lambda表达式
但能用Lambda表达式的地方,不一定能用构造器引用
要求:
①Lambda体中仅仅只有一句话
②仅有的一句话为调用构造器 new ClassName(实参列表);
③抽象方法的参数列表和构造器的参数列表完全一致
抽象方法的返回类型为构造器所属的类型。
语法:
类名::new
此类用于演示构造器引用
public class TestConstructorRef {
//案例1:返回new String();
@Test
public void test1() {
//匿名内部类
// Supplier sup = new Supplier() {
// @Override
// public String get() {
// return new String();
// }
// };
//构造器引用
Supplier sup2 = String::new;
int length = sup2.get().length();
System.out.println(length);
}
//案例2:返回new StringBuffer(int capacity);
@Test
public void test2() {
Function fun = new Function(){
@Override
public StringBuffer apply(Integer t) {
return new StringBuffer(t);
}
};
Function fun2 = StringBuffer::new;
}
//案例3:返回new Employee("段誉", 22, 10000, '男')
@Test
public void test3() {
A a1 = (s,i,d,c)->new Employee(s,i,d,c);
A a = Employee::new;
}
}
interface A{
N test(T t,R r,U u,M m);
}
数组引用
理解:数组引用本质上就是Lambda表达式,只是语句更加简洁。作为函数式接口的实例,一般作为参数传递给方法!
能用数组引用的地方,肯定能用Lambda表达式
但能用Lambda表达式的地方,不一定能用数组引用
要求:
①Lambda体仅仅有一句话
②仅有的一句话为返回一个新数组 new 数组类型[长度];
③抽象方法的参数只有一个,就是数组的长度;抽象方法的返回为创建的新数组类型
语法:
数组类型[]::new
此类用于演示数组引用
public class TestArrayRef {
//案例1:返回一个String[] s = new String[5]
@Test
public void test1() {
//匿名内部类
// Function fun = new Function(){
// @Override
// public String[] apply(Integer t) {
// return new String[t];
// }
// };
//数组引用
Function fun2 = String[]::new;
String[] arr = fun2.apply(5);
for (String string : arr) {
System.out.println(string);
}
}
//案例2:返回一个Employee[]
@Test
public void test2() {
Function fun = Employee[]::new;
Employee[] apply = fun.apply(3);
for (Employee employee : apply) {
System.out.println(employee);
}
}
}
综述:匿名内部类》Lambda表达式》方法引用|数组引用|构造器引用
##StreamAPI
###好处
1、更高效
2、易于使用,提供了丰富强大的方法。
###使用步骤
1.开始操作(√)
说明:创建Stream对象,指向一个具体的数据源(集合、数组、一系列值、无线流)
2.中间操作(可选 0——n)
说明:对数据源数据的计算、处理
3.终止操作(√)
说明:想要一个结果(打印、计数、去最值等)
###使用特点
1、Stream讲究的是“计算”,可以处理数据,但不能更新源数据
2、Stream 属于“惰性操作”,必须等待终止操作执行后,前面的中间操作或开始操作才会处理
3、Stream只能消费一次,一旦消费,就不能再次使用,除非重新创建Stream对象
4、Stream的中间操作可以有0个或多个,每个操作都会返回一个新的Stream
5、Stream相当于一个 更强大的Iterator,可以处理更加复杂的数据,并且实现并行化,效率更高!
###开始操作的相关API
一、通过集合来获取Stream对象 ★
Stream stream = list.stream();
二、通过数组来获取Stream对象
Stream stream = Arrays.stream(数组);
三、通过一组指定的值来获取Stream对象
Stream stream = Stream.of(T…ts)
四、生成无限流
Stream stream= Stream.generate(Supplier sup);
/**
* 此类用于演示Stream的使用步骤一:创建Stream对象
* 引申:
* Stream的使用步骤
* 1、创建Stream对象,指向数据源
* 2、中间操作,处理数据,返回一个新的Stream
* 3、终止操作,执行
* 创建Stream对象的方式
* 1、通过集合对象创建Stream
* 2、通过数组对象创建Stream
* 3、通过一系列值创建Stream
* 4、生成无限流
*/
public class TestStreamStart {
// 1、通过集合对象创建Stream ★
@Test
public void test1() {
List list = EmployeeData.getData();//集合
//获取串行流对象stream
Stream stream = list.stream();
//获取并行流对象parallelStream
// Stream parallelStream = list.parallelStream();
//补充:终止操作
stream.forEach(System.out::println);
}
// 2、通过数组对象创建Stream
@Test
public void test2() {
Stream stream = Arrays.stream(new String[] {"白眉鹰王","青易斧王","紫衫龙王","金毛狮王"});
stream.forEach(System.out::println);
}
// 3、通过一系列值创建Stream
@Test
public void test3() {
Stream of = Stream.of("周芷若","小昭","殷离","赵敏");
of.forEach(System.out::println);
}
// 4、生成无限流
@Test
public void test4() {
Stream stream = Stream.generate(Math::random);
stream.forEach(System.out::println);
}
//案例:演示链式调用(流式编程)
@Test
public void exec1() {
//测试1
// EmployeeData.getData().stream().forEach(System.out::println);
//测试2
Person p = new Person();
// p.eat();
// p.sleep();
// p.play();
// p.study();
p.eat().sleep().play().study();
}
}
class Person{
public Person eat() {
System.out.println("吃");
return this;
}
public Person sleep() {
System.out.println("睡");
return this;
}
public Person play() {
System.out.println("玩");
return this;
}
public Person study() {
System.out.println("学");
return this;
}
}
###中间操作的相关API
* filter(Predicate):根据条件过滤
* limit(long max):截取size<=max的元素
* skip(long s):跳过前s个
* distinct():返回去重后的元素集合,根据hashCode和equals方法判断重复项
* map(Function):映射成新元素
* flatMap(Function):映射成Stream类型的新元素
* sorted():自然排序
* sorted(Comparator):定制排序
*
/**
* 此类用于演示Stream的使用步骤二:中间操作
* 特点:
* ①每个中间操作方法,都会返回一个持有结果的新的Stream对象
* ②中间操作是“惰性求值”,必须等待终止操作后,才会有处理结果
* ③Stream是一次性消费,每次消费后,则不能再次消费,除非重新创建新的Stream
* ④中间操作可以实现链式调用!
* 常见方法:
* filter(Predicate):过滤
* skip(n):去掉前几项
* limit(n):获取前n项
* distinct():去重
* map(Function):映射
* flatMap(Function):映射,返回Stream
* sorted():自然排序,要求元素本身实现Comparable接口
* sorted(Comparator):定制排序
*/
public class TestStreamMiddle {
List list;
@Before
public void before() {
list = EmployeeData.getData();
}
/*
* 筛选与切片
* filter(Predicate p)——接收 Lambda , 从流中排除某些元素。
limit(n)——截断流,使其元素不超过给定数量。
skip(n) —— 跳过元素,返回一个扔掉了前 n 个元素的流。若流中元素不足 n 个,则返回一个空流。与 limit(n) 互补
distinct()——筛选,通过流所生成元素的 hashCode() 和 equals() 去除重复元素
*/
@Test
public void test1() {
//1.创建Stream对象
Stream stream = list.stream();
//2.中间操作
//方式一:filter过滤
// Stream filter = stream.filter(t->t.getGender()=='男');
//方式二:limit限制,返回不超过指定条目数的数据
// Stream limit = stream.limit(5);
//方法三:skip(n),跳过指定的n条数据
// Stream skip = stream.skip(3);
//方法四:distinct()去重:两个元素通过equals判断,如果返回true,则为重复项,一般往往需要重写元素的equals和hashCode方法
Stream distinct = stream.distinct();
//3.终止操作
distinct.forEach(System.out::println);
}
/**
* 映射
* map:映射(将元素映射成任意类型)
* flatMap:映射(将元素映射成Stream类型)
*/
@Test
public void test2() {
//1.创建Stream对象
Stream stream = list.stream();
//2.中间操作
//方法5:map
// Stream map = stream.map(Employee::getName);
// Stream filter = map.filter(s->s.contains("段"));
Stream> map = stream.map(TestStreamMiddle::fromEmployeeToStream);
//方法6:flatMap
// Stream
###终止操作的相关API
* allMatch:测试是否全部匹配
* anyMatch:测试是否至少有一个匹配
* noneMatch:测试是否所有的都不匹配
* findFirst:返回第一个
* findAny:返回并行流中的任意一个
* count:统计个数
* max(comparator):返回最大值
* min(comparator):返回最小值
* forEach(Consumer):内部迭代(遍历)
* reduce(BinaryOperator):归约,往往和map搭配使用
/**
* 此类用于演示Stream的使用步骤三:终止操作
* 常见方法:
* reduce:归约 ★
* forEach:内部迭代 ★
* max:求最大值
* min:求最小值
* count:统计个数 ★
* allMatch:检测是否所有元素都匹配指定条件
* anyMatch:检测是否至少有一个元素匹配指定条件
* noneMatch:检测是否所有元素都不匹配指定条件
* findFirst:返回第一个元素
* findAny:返回任意一个元素
* collect:将Stream转换成集合对象
*/
public class TestStreamEnd {
List list;
@Before
public void before() {
list = EmployeeData.getData();
}
/**
* 匹配与查找
* allMatch(Predicate p)——检查是否匹配所有元素
anyMatch(Predicate p)——检查是否至少匹配一个元素
noneMatch(Predicate p)——检查是否没有匹配的元素
findFirst——返回第一个元素
findAny——返回当前流中的任意元素
*/
@Test
public void test1() {
//1.开始操作:创建Stream对象
Stream stream = list.stream();
//2.中间操作(支持0——n步)
//3.终止操作
boolean allMatch = stream.anyMatch(e->e.getSalary()>10000);
System.out.println(allMatch);
}
/*
* 统计和迭代
* count
* max
* min
* forEach
*/
@Test
public void test2() {
//1.开始操作:创建Stream对象
Stream stream = list.stream();
//2.中间操作(支持0——n步)
//3.终止操作
//方法1:count统计
// long count = stream.count();
// System.out.println(count);
//方法2:max求最大值
Optional max = stream.max((o1,o2)->o1.getAge()-o2.getAge());
System.out.println(max);
}
/*
* 归约
* reduce:反复结合流中的元素
*/
@Test
public void test3() {
//1.开始操作:创建Stream对象
Stream stream = list.stream();
//2.中间操作(支持0——n步)
//3.终止操作
Optional sum = stream.map(Employee::getSalary).reduce((t,u)->t+u);
System.out.println(sum);
}
/*
*收集
* collect:收集
*/
@Test
public void test4() {
//1.开始操作:创建Stream对象
Stream stream = list.stream();
//2.中间操作(支持0——n步)
//3.终止操作
List collect = stream.collect(Collectors.toList());
System.out.println(collect);
}
//练习2:员工姓名中包含“马”的员工个数
@Test
public void exec2() {
Optional reduce = list.stream().filter(t->t.getName().contains("马")).map(t->1).reduce((t,u)->t+u);
System.out.println(reduce);
}
//练习:返回最高的工资:
@Test
public void exec1() {
Optional max = list.stream().map(Employee::getSalary).max(Double::compare);
System.out.println(max);
}
}
JDK8对接口的改进
JDK8:
说明:接口中允许出现静态方法和默认方法
语法:
静态方法:
public static 返回类型 方法名(参数列表){
}
默认方法:
public default 返回类型 方法名(参数列表){
}
使用特点:
1、静态方法不能被实现类或子接口继承,只能通过所在的接口名调用!
2、默认方法具备继承性。可以被实现类或子接口继承过去
解决同名冲突的问题:
1、类优先原则
当父类或实现的接口中出现了同名并且已经实现好的方法时,如果子类中没有重写,则默认使用的是父类的方法
2、当实现的多个接口中出现了同名并且已经实现好的方法时,子类必须重写该方法,否则报错!
JDK9:
说明:接口中允许私有方法
语法:
private 返回类型 方法名(参数列表){
//代码
}
Optional的特性
理解:
Optional实质上是一个容器,里面包装了具体类型的对象。可以避免对象出现空指针异常的现象
方法:
of
ofNullable
orElse
重复注解
说明:jdk8支持重复注解
语法:
在被修饰的数据上方:
@注解类名
@注解类名
…
示例:
public class TestJava8Ann {
@MyAnn
@MyAnn
// @AnnArray({@MyAnn,@MyAnn})
String name;
}
@Repeatable(AnnArray.class)
@interface MyAnn{
//语法:类型 value() [default 值];
}
@interface AnnArray{
MyAnn[] value();
}
##日期API
第一代日期:
Date类
SimpleDateFormat类
Date date = new Date();//当前时间
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd hh::mm::ss");
String dateString = sdf.format(date);
/**
* 此类用于演示第一代日期类
* Date
* new Date();
* getTime()
* SimpleDateFormat
* format
* parse
*/
public class TestDate1 {
//创建Date对象,表示一个时间,精确到毫秒
@Test
public void test1() {
//代表当前系统时间
Date d1 = new Date();
System.out.println(d1);
//获取距离1970-1-1的毫秒数
System.out.println(d1.getTime());
}
//测试SimpleDateFormat,格式化或解析日期
@Test
public void test2() throws ParseException {
Date d=new Date();
//1.创建SimpleDateFormat对象
//①使用默认的格式 18-8-8 下午4:25
// SimpleDateFormat sdf = new SimpleDateFormat();
//②使用指定的格式【建议】
SimpleDateFormat sdf = new SimpleDateFormat("yyyy年MM月dd日 hh小时mm分钟ss秒 S毫秒");
//-------------格式化日期(将日期转换成字符串)------------
String format = sdf.format(d);
System.out.println(format);
//------------解析字符串为日期(将字符串转换成日期)--------
String s = "2018年12月12日 12小时12分钟12秒 12毫秒";
Date date = sdf.parse(s);
System.out.println(date);
}
}
第二代日期:
Calendar抽象类:更侧重于获取和设置日历字段
Calendar date = Calendar.getInstance();
System.out.println(date);
int year = date.get(Calendar.YEAR);
int month = date.get(Calendar.MONTH);
int day = date.get(Calendar.DAY_OF_MONTH);
System.out.println(year+"年"+(month+1)+"月"+day+"日");
/**
* 此类用于演示第二代日期类
* Calendar
*/
public class TestDate2 {
@Test
public void test1() {
//1.获取一个Calendar对象
Calendar c = Calendar.getInstance();
// System.out.println(c);
//2.获取日历字段
System.out.println("年:"+c.get(Calendar.YEAR));
System.out.println("月:"+(c.get(Calendar.MONTH)+1));
System.out.println("日:"+c.get(Calendar.DAY_OF_MONTH));
System.out.println("小时:"+c.get(Calendar.HOUR));
System.out.println("分钟:"+c.get(Calendar.MINUTE));
System.out.println("秒:"+c.get(Calendar.SECOND));
System.out.println("------------------------------------------");
//3.设置具体的时间
c.set(2000, 1, 1);
System.out.println("年:"+c.get(Calendar.YEAR));
System.out.println("月:"+(c.get(Calendar.MONTH)+1));
System.out.println("日:"+c.get(Calendar.DAY_OF_MONTH));
System.out.println("小时:"+c.get(Calendar.HOUR));
System.out.println("分钟:"+c.get(Calendar.MINUTE));
System.out.println("秒:"+c.get(Calendar.SECOND));
}
}
第三代日期:
//1.LocalDate/LocalTime/LocalDateTime————>Calendar
@Test
public void test1() {
//①创建日期对象
LocalDateTime ldt = LocalDateTime.now();
//System.out.println(ldt);
//②获取日历字段
int year = ldt.getYear();
int month = ldt.getMonthValue();
int day = ldt.getDayOfMonth();
int hour = ldt.getHour();
int minute = ldt.getMinute();
int second = ldt.getSecond();
System.out.println(year+"年"+month+"月"+day+"日 "+hour+"小时"+minute+"分钟"+second+"秒");
}
//2.Instant————————>Date
@Test
public void test2() {
//①创建Instant对象
Instant now = Instant.now();
//②实现Instant和Date之间的转换
//Instant——————>Date
Date date = Date.from(now);
System.out.println(date);
//Date——————>Instant
Instant instant = date.toInstant();
System.out.println(instant);
}
//3.DateTimeFormatter——————>SimpleDateFormat
/*
* 可以格式化LocalDateTime,也可以格式化Instant
*/
@Test
public void test3() {
LocalDateTime ldt = LocalDateTime.now();
//①创建DateTimeFormatter对象
DateTimeFormatter dtf = DateTimeFormatter.ofPattern("yyyy-MM-dd kk::mm::ss");
//②格式化日期(日期——————>字符串)
String format = dtf.format(ldt);
System.out.println(format);
//③解析日期(字符串——>日期)
LocalDateTime parse = LocalDateTime.parse("2018-08-08 16::32::21", dtf);
System.out.println(parse);
}