写在前面:
记得有次面试。问道Java8新特性知道吗?Lambda用过吗?。灰溜溜的回答道。“没…”
现在公司普遍都是jdk1.8了,里面补充了很多新的语法新的类。Lambda和Stream尤为明显。之前看到腾哥,使用Stream流操作集合,那熟练度。正好用时下热门梗“小朋友,你是否有很多问号?”。当下jdk1.8也比较稳定,使用者占据了大多数。尽管后面java9,10都上了。但还是要遵循万物逐步演进的过程。先把当下已经稳定了的1.8学起来。后面的,可能还不是主流。会了1.8后面的也可以照猫画虎慢慢探索。话不多说,开始吧。
老样子。尚硅谷。B站。看视频。跟着敲。做笔记。做总结。
百度网盘资料:
B站地址 | https://www.bilibili.com/video/av62117143?p=21 |
百度网盘链接 | https://pan.baidu.com/s/1bpk5TmA2ZQ2XAYU4vydQng |
提取码 | 0axc |
主要目录
1. Lambda 表达式
2. 函数式接口
3. 方法引用与构造器引用
4. Stream API
5. 接口中的默认方法与静态方法
6. 新时间日期 API 7. 其他新特性
一、Lambda 表达式
Comparator和Comparable有什么区别?
①前者是java.util包。后者java.lang包
②Comparator不用修改源代码,但专门要实现一个比较器
Class UserComparator implements Comparator<User>{
@Overrde
Public int compare(User u1,User u2){
Return Integer.compare(u1.getAge(),u2.getAge());
}
}
业务代码一般通过Comparator内部类排序
Collections.sort(list,new Comparator<User>{
@Override
Public int compare(User u1,User u2){
Retun ….
}
});
③Comparable对代码有入侵性,破坏原子类。需要实现comparto方法
什么是Lambda?
Lambda 是一个匿名函数,我们可以把 Lambda 表达式理解为是一段可以传递的代码(将代码像数据一样进行传递)。可以写出更简洁、更灵活的代码。
//原来匿名内部类生成
@Test
public void test1(){
Comparator<Integer> comparator = new Comparator<Integer>() {
@Override
public int compare(Integer o1, Integer o2) {
return Integer.compare(o1,o2);
}
};
}
//lambad表达式
@Test
public void test2(){
Comparator<Integer> comparator = (x,y)->Integer.compare(x,y);
}
Lambda 操作符
Lambda 表达式在Java 语言中引入了一个新的语法元素和操作符。这个操作符为 “->”
它将 Lambda 分为两个部分:
左侧:指定了 Lambda 表达式需要的所有参数
右侧:指定了 Lambda 体,即 Lambda 表达式要执行的功能。
Lambda 表达式语法
//无参 无返回值
@Test
public void test1(){
int num = 0;//jdk1.7前 必须要加final jdk1.8后可以不加final
Runnable r1 = new Runnable() {
@Override
public void run() {
System.out.println("hello"+num);
}
};
r1.run();
System.out.println("===========================");
Runnable r2 = () -> System.out.println("world");
r2.run();
}
//有参 无返回值
//若只有一个参数 ()可省略
@Test
public void test2(){
Consumer consumer = (e) -> System.out.println(e);
consumer.accept(1);
}
//有多个参数 有返回值 且lambda体中有多条语句 加{}
//lambda体只有一条语句 可省略return和{}
@Test
public void test3(){
Comparator<Integer> comparator = (x,y)->{
System.out.println("lambda函数式接口");
return Integer.compare(x,y);
};
System.out.println(comparator.compare(10,5));
Comparator<Integer> comparator1 = (x,y)->Integer.compare(x,y);
System.out.println(comparator1.compare(10,20));
}
类型推断
Lambda表达式参数列表的数据类型可以省略不写,因为JVM编译器,通过上下文推断出数据类型。
二、函数式接口
什么是函数式接口?
接口中只有一个抽象方法的接口。
可以使用@FunctionalInterface修饰。
//自定义函数式接口
@FunctionalInterface
public interface MyFilter {
public (abstract) Integer filter(Integer integer);
}
//作为参数传递 Lambda 表达式
public Integer operation(Integer num,MyFilter myFilter){
return myFilter.filter(num);
}
//函数式接口
@Test
public void test(){
Integer flag = operation(101,(e) -> e*e);
System.out.println(flag);
// Integer num = operation(88, new MyFilter() {
// @Override
// public Integer filter(Integer integer) {
// return integer*integer;
// }
// });
// System.out.println(num);
}
Java 内置四大核心函数式接口
Consumer<T>消费型接口
void accept(T t)
@Test
public void test1(){
happy(1.1,(e)-> System.out.println("消费"+e));
}
public void happy(double money,Consumer<Double> consumer){
consumer.accept(money);
}
Supplier 供给型接口
T get()
//供给型接口 Supplier<T>
@Test
public void test2(){
List<Integer> list = getValue(10,() -> (int)(Math.random()*100));
for (Integer integer : list) {
System.out.println(integer);
}
}
//产生指定个数的随机数
public List<Integer> getValue(Integer num,Supplier<Integer> supplier){
List<Integer> list = new ArrayList<>();
for (int i = 0; i <= num ; i++) {
list.add(supplier.get());
}
return list;
}
Function<T,R>函数型接口
Apply(T t)
//函数型接口 Function<T,R>
@Test
public void test3(){
String s = strHandler(" qweasdzx c ", (t) -> t.trim().toUpperCase());
System.out.println(s);
}
public String strHandler(String str,Function<String,String> function){
return function.apply(str);
}
Predicate<T>断言型接口
Boolean test(T t)
@Test
public void test4(){
boolean predicate = predicate(100, (num) -> num >= 99 ? true : false);
System.out.println(predicate);
}
public boolean predicate(int num,Predicate<Integer> predicate){
return predicate.test(num);
}
三、方法引用与构造器引用
方法引用:当要传递给Lambda体的操作,已经有实现的方法了,可以使用方法引用!
使用操作符 “::” 将方法名和对象或类的名字分隔开来。
(实现抽象方法的参数列表,必须与方法引用方法的参数列表保持一致!)
如下三种主要使用情况:
对象::实例方法名
类::静态方法名
类::实例方法名
方法引用
//对象::实例方法名
@Test
public void test1(){
PrintStream ps = System.out;
Consumer<String> consumer = ps::println;
Consumer<String> consumer1 =(e) -> ps.println(e);
consumer1.accept("q");
}
//类::静态方法名
@Test
public void test3(){
Comparator<Integer> comparator = (x,y) -> Integer.compare(x,y);
System.out.println(comparator.compare(10,5));;
Comparator<Integer> comparator1 = Integer::compare;
System.out.println(comparator1.compare(10,20));
}
注意:当需要引用方法的第一个参数是调用对象,并且第二个参数是需要引用方法的第二个参数(或无参数)时:ClassName::methodName
构造器引用
//构造器引用 ClassName::new
@Test
public void test5(){
Supplier<Employee> supplier = () -> new Employee();
System.out.println(supplier.get());
Supplier<Employee> supplier1 = Employee::new;
System.out.println(supplier1.get());
}
数组引用
//数组引用 Type[]::new
@Test
public void test6(){
Function<Integer,String[]> function1 = (num) -> new String[num];
String[] strings = function1.apply(10);
System.out.println(strings.length);
Function<Integer,String[]> function2 = String[]::new;
String[] strings1 = function2.apply(20);
System.out.println(strings1.length);
}
四、Stream API
什么是Stream?
Stream 是 Java8 中处理集合的关键抽象概念,它可以指定你希望对集合进行的操作,可以执行非常复杂的查找、过滤和映射数据等操作。使用Stream API 对集合数据进行操作,就类似于使用 SQL执行的数据库查询。也可以使用 Stream API 来并行执行操作。
注意:
①Stream 自己不会存储元素。
②Stream 不会改变源对象。相反,他们会返回一个持有结果的新Stream。
③Stream 操作是延迟执行的。这意味着他们会等到需要结果的时候才执行。
Stream 的操作三个步骤
创建 Stream
创建Stream流的几种方式?
①Collection提供的stream()、paralleStream()并行
②Arrays.stream(数组)
③Stream.of("a", "b","c")
④无限流
//迭代
Stream<Integer> stream4 = Stream.iterate(0,(e)->e*2);
//生成
Stream<Integer> generate = Stream.generate(() -> (int)(Math.random()*100));
中间操作
多个中间操作可以连接起来形成一个流水线,除非流水线上触发终止操作,否则中间操作不会执行任何的处理!
(筛选与切片)
方 法 | 描 述 |
filter(Predicate p) | 接收 Lambda , 从流中排除某些元素。 |
distinct() | 筛选,通过流所生成元素的 hashCode() 和 equals() 去 除重复元素 |
limit(long maxSize) | 截断流,使其元素不超过给定数量。 |
skip(long n) | 跳过元素,返回一个扔掉了前 n 个元素的流。 |
(映射)
方 法 | 描 述 |
map(Function f) | 接收一个函数\lambda作为参数,该函数会被应用到每个元素。 |
flatMap(Function f) | 一对多,可以操作对象里的对象。 |
add和addAll有什么区别?
List<String> list = Arrays.asList("aaa","bbb","ccc","ddd","eee");
List list1 = new ArrayList();
List list2 = new ArrayList();
list1.add(list);
for (Object o : list1) {
System.out.print(o);
}
System.out.println("");
list2.addAll(list);
for (Object o : list2) {
System.out.print(o);
}
[aaa, bbb, ccc, ddd, eee]
aaabbbcccdddeee
addAll添加的是每个元素
(排序)
方 法 | 描 述 |
sorted() | 产生一个新流,其中按自然顺序排序 |
sorted(Comparator comp) | 产生一个新流,其中按比较器顺序排序 |
@Test
public void test3(){
employees.stream()
.sorted((x,y)->Integer.compare(x.getAge(),y.getAge()))
.forEach(System.out::println);
}
终止操作
方 法 | 描 述 |
allMatch(Predicate p) | 检查是否匹配所有元素 |
anyMatch(Predicate p) | 检查是否至少匹配一个元素 |
noneMatch(Predicate p) | 检查是否没有匹配所有元素 |
findFirst () | 返回第一个元素 |
findAny() | 返回当前流中的任意元素 |
count() | 返回流中元素总数 |
max(Comparator c) | 返回流中最大值 |
min(Comparator c) | 返回流中最小值 |
forEach(Consumer c) | 内部迭代(使用 Collection 接口需要用户去做迭代,称为外部迭代。相反,Stream API 使用内部迭代——它帮你把迭代做了) |
归约
可以将流中元素反复结合起来,得到一个值。返回 Optional
@Test
public void test2(){
Optional<Double> reduce = employees.stream()
.map(Employee::getSalary)
.reduce(Double::sum);
System.out.println(reduce.get());
}
备注:map 和 reduce 的连接通常称为 map-reduce 模式,因 Google 用它 来进行网络搜索而出名。
收集
方 法 | 描 述 |
collect(Collector c) | 将流转换为其他形式。接收一个 Collector接口的实现,用于给Stream中元素做汇总的方法 |
@Test
public void test3(){
//转换成HashSet Collectors.toCollection(HashSet::new)
HashSet<String> collect = employees.stream()
.map(Employee::getName)
.collect(Collectors.toCollection(HashSet::new));
collect.forEach((e)-> System.out.println(e));
//求平均值 Collectors.averagingDouble((e) -> e.getSalary())
Double collect1 = employees.stream()
.collect(Collectors.averagingDouble((e) -> e.getSalary()));
System.out.println(collect1);
//求和 Collectors.summingDouble(Employee::getSalary)
Double collect2 = employees.stream()
.collect(Collectors.summingDouble(Employee::getSalary));
System.out.println(collect2);
//最大值
Optional<Employee> collect3 = employees.stream()
.collect(Collectors.maxBy((x,y)->Double.compare(x.getSalary(),y.getSalary())));
System.out.println(collect3.get());
//最小值
Optional<Employee> collect4 = employees.stream()
.collect(Collectors.minBy((x, y) -> Double.compare(x.getSalary(), y.getSalary())));
System.out.println(collect4.get());
Optional<Double> collect5 = employees.stream()
.map(Employee::getSalary)
.collect(Collectors.minBy((x, y) -> Double.compare(x, y)));
System.out.println(collect5.get());
}
并行流与串行流
并行流就是把一个内容分成多个数据块,并用不同的线程分别处理每个数据块的流。
Stream API 可以声明性地通过 parallel() 与sequential()在并行流与顺序流之间进行切换。
五、接口中的默认方法与静态方法
①Java8接口中允许有方法实现。也包括允许有静态方法实现。
public interface Interface1 {
default String getName(){
return "Interface1";
}
public static void show(){
System.out.println("java8接口中允许带有静态方法");
}
}
public interface Interface2 {
default String getName(){
return "Interface2";
}
}
②接口默认方法的”类优先”原则。当继承某个类和实现了某个接口,它们的方法名和参数列表都一样时,优先加载继承类的方法。
public class Class1 {
public String getName(){
return "Class1";
}
}
public interface Interface1 {
default String getName(){
return "Interface1";
}
public static void show(){
System.out.println("java8接口中允许带有静态方法");
}
}
public class Impl extends Class1 implements Interface1 {
}
public class Main {
public static void main(String[] args) {
Impl impl = new Impl();
System.out.println(impl.getName());
}
}
③当实现了多个接口,且接口中同名同参时,必须重写该方法。
六、新时间API
Date
LocalDate、LocalTime、LocalDateTime
LocalDate、LocalTime、LocalDateTime 类的实例是不可变(线程安全)的对象,分别表示使用 ISO-8601日历系统的日期、时间、日期和时间。
@Test
public void test1(){
LocalDateTime ldf = LocalDateTime.now();
//加1000年
LocalDateTime plusYears = ldf.plusYears(1000);
//加20天
LocalDateTime minusDays = ldf.minusDays(20);
//指定日期
LocalDateTime ldf2 = LocalDateTime.of(1111,11,11,11,11,11);
System.out.println(ldf2.getYear());
System.out.println(ldf2.getMonthValue());
System.out.println(ldf2.getDayOfMonth());
System.out.println(ldf2.getHour());
System.out.println(ldf2.getMinute());
}
Instant 时间戳
用于“时间戳”的运算。它是以Unix元年(传统的设定为UTC时区1970年1月1日午夜时分)开始所经历的描述进行运算。
@Test
public void test2(){
Instant instant = Instant.now();
System.out.println(instant);
//时区偏移量 +8小时
OffsetDateTime offsetDateTime = instant.atOffset(ZoneOffset.ofHours(8));
System.out.println(offsetDateTime);
//转换为时间戳
long l = instant.toEpochMilli();
System.out.println(l);
//加10秒
Instant instant1 = Instant.ofEpochSecond(10);
System.out.println(instant1);
}
Duration
用于计算两个“时间”间隔
@Test
public void test3() throws InterruptedException {
Instant instant = Instant.now();
Thread.sleep(1000);
Instant instant1 = Instant.now();
System.out.println(Duration.between(instant,instant1).toMillis());
}
Period
用于计算两个“日期”间隔
@Test
public void test4(){
LocalDate ld1 = LocalDate.of(1998,10,17);
LocalDate ld2 = LocalDate.now();
System.out.println(Period.between(ld1,ld2).getYears());
System.out.println(Period.between(ld1,ld2).getMonths());
System.out.println(Period.between(ld1,ld2).getDays());
}
时间校正器
LocalDate localDate = LocalDate.now()
.with(TemporalAdjusters.next(DayOfWeek.MONDAY));
System.out.println(localDate);
TemporalAdjusters : 该类通过静态方法提供了
大量的常用 TemporalAdjuster 的实现。
解析与格式化
java.time.format.DateTimeFormatter
@Test
public void test6(){
//预定义的标准格式
DateTimeFormatter dtf = DateTimeFormatter.ISO_DATE;
LocalDate localDate = LocalDate.now();
String format = localDate.format(dtf);
System.out.println(format);
//自定义的格式
DateTimeFormatter dtf2 = DateTimeFormatter.ofPattern("yyyy-MM-dd");
String format1 = localDate.format(dtf2);
LocalDate parse = LocalDate.parse("1111-12-10", dtf2);
}
时区的处理
ZonedDate、ZonedTime、ZonedDateTime
@Test
public void test7(){
//获得所有时区
Set<String> availableZoneIds = ZoneId.getAvailableZoneIds();
//availableZoneIds.forEach(System.out::println);
//.now(ZoneId.of("{区域}/{城市}"))
LocalDateTime localDateTime = LocalDateTime.now(ZoneId.of("Asia/Tokyo"));
DateTimeFormatter dtf = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
String str = localDateTime.format(dtf);
System.out.println(str);
//.atZone()指定某个“{区域}/{城市}”
ZonedDateTime zonedDateTime = localDateTime.atZone(ZoneId.of("Asia/Tokyo"));
System.out.println(zonedDateTime);
}
七、其他新特性
Optional 类
Optional类(java.util.Optional) 是一个容器类,代表一个值存在或不存在,原来用null 表示一个值不存在,现在Optional可以更好的表达这个概念。并且可以避免空指针异常。
Optional.of(T t) : | 创建一个 Optional 实例 |
Optional.ofNullable(T t): | 允许传入Null |
isPresent() : | 相当于!=null |
orElse(T t) : | 若为空,则输出T。不为空,照常输出。 |
map(Function f): | 如果有值对其处理,并返回处理后的Optional,否则返回 Optional.empty() |
flatMap(Function mapper) | 与 map 类似,要求返回值必须是Optional |
@Test
public void test5(){
Optional<Employee> optional = Optional.ofNullable(new Employee(228, "张三", 8500.0, Employee.Status.BUSY.getName()));
//Optional<String> s = optional.map(Employee::getName);
//System.out.println(s);
Optional<Employee> employee = optional.flatMap((e) -> Optional.of(new Employee(28, "张三", 8500.0, Employee.Status.BUSY.getName())));
System.out.println(employee.get());
}
重复注解与类型注解
Java 8对注解处理提供了两点改进:可重复的注解及可用于类型的注解。
//可重复的
@Repeatable(MyAnnotaions.class)
@Target({TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE, TYPE_PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotaion {
String value1() default "atguigu";
}
@Target({TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE})
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotaions {
MyAnnotaion[] value();
}
public class TestAnnotaion {
@MyAnnotaion(value1="hello")
@MyAnnotaion(value1="world")
@MyAnnotaion()
public void show(@MyAnnotaion(value1 = "wowowo") String string){
System.out.println("string" + string);
}
@Test
public void test1() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException, InstantiationException {
Class<TestAnnotaion> c = TestAnnotaion.class;
Method method = c.getMethod("show",String.class);
TestAnnotaion testAnnotaion = c.newInstance();
method.invoke(testAnnotaion,"aaa");
MyAnnotaion[] annotationsByType = method.getAnnotationsByType(MyAnnotaion.class);
for (MyAnnotaion myAnnotaion : annotationsByType) {
System.out.println(myAnnotaion.value1());
}
}
}