Java 8笔记
1、Lambda表达式
1.1、什么叫Lambda表达式:
Lambda表达式就是一段带有参数的可执行语句块
1.2、Lambda表达式在java语言中引入了一个新的语法元素和操作符;
这个操作符为“->”,该操作符被称为Lambda或箭头操作符;它将Lambda
Fenwick两个部分:
左侧:指定了Lambda表达式需要的所有参数
右侧:指定了Lambda体,即Lambda所需要执行的功能
1.3、Lambda表达式的语法:
1.3.1、语法格式一:无参、无返回值,Lambda只需要一条语句
Runnable r1=() ->System.out.println(“hello word”);
1.3.2、语法格式二:带一个参数
Consumer<String> fun=(args) ->System.out.println(args);
1.3.3、语法格式三:Lambda只需要一个参数时,参数的小括号可以省略
Consumer<String> fun=args ->System.out.println(args);
1.3.4、语法格式四:Lambda只需要一个参数时,并且有返回值
BinaryOperator<Long> bo=(x,y) ->{
System.out.println(“实现函数式接口方法”);
return x+y;
};
1.3.5、语法格式五:当Lambda体只有一条语句是,大括号和return可以省略
BinaryOperator<Long> bo=(x,y) ->x+y
1.3.6、语法格式六:
BinaryOperator<Long> bo=(Long x,Long y) ->{
System.out.println(“实现函数式接口方法”);
return x+y;
};
//Lambda体参数类型可以省略,因为可有编译器推断的出来,称为类型推断
简单的举个例子:
*下面代码是老版本的Java中是如何排列字符串的:
List<String> names=Arrays.asList(“peter”,”anna”,”mike”,”xenia”);
Collections.sort(names,new Comparator<String>);
@Override
Public int compare(String a,String b){
Return b.compareTo(a);
}
//只需要给静态方法Collecyions.sort传入一个List对象以及一个比较起来按指 定顺序排列。通常做法都是创建一个匿名的比较器对象然后将其传递给sort 方法
*在java8中就没必要使用这种传统的匿名对象的方式,java8提供了更简洁的 语法,lambda表达式:
Collections sort(names,(a,b) ->b.compare(a))
2、函数式接口
2.1、什么是函数式接口:
2.1.1、只包含一个抽象方法的接口,称为“函数式接口”;
2.1.2、你可以通过Lambda表达式来创建该接口的对象,(若Lambda表达式抛出一 个受检查时异常,那么该异常需要在目标接口的抽象方法上进行声明)
2.1.3、可以在任意函数式上使用@FunctionalInterface注解,该注解可以检查它是否 是一个函数式接口,
2.2、java8中常用的全新接口
2.2.1、Predicate接口(断言型接口)
Predicate接口只有一个参数,返回boolean类型,该接口包含多种默认方法 来将Predicate组合成其他复杂的逻辑(比如:与,或,非),包含方法boolean test(T t):
代码如下:
Predicate<String> predicate=(s) ->s.length()>0;
Predicate.test(“foo”);//true
Predicate.negate().test(“foo”);//false
2.2.2、Function<T,R>接口(函数型接口):T代表参数,R代表返回值
Function接口有一个参数并且返回一个结果,并附带了一些可以和其他函 数组合的默认方法(compose,andThen),包含方法 R apply (T t);
代码如下:
Function<String,Integer> tolnteger=integer::valueOf;
Function<String,String> backToString=tointeger.andThen(String::valueOf);
backToString.apply(“123”);//123
2.2.3、Supplier接口(供给型接口):返回一个任意泛型的值,和Function不同的是该接 口没有任何参数,包含方法T get();
代码如下:
Supplier<Person> personSupplier=person::new;
personSupplier.get();//new Person
2.2.4、Consumer<T>接口(消费型接口):对类型为T的对象应用操作,包含方法void accept(T t);
代码如下:
Consumer<Person> greeter=(p) ->System.out.println(“hello”+p.firstName);
Greeter.accept(new Person(“Luke”,”Skywalker”));
3、方法引用和构造引用
3.1、方法引用
*当要传递给Lambda体的操作,已经有实现的方法了,可以使用方法引用!
(实现抽象方法的参数列表,必须与方法引用的参数列表保持一致!)
*方法引用:使用操作符”::” ,将方法名和对象或类的名字分开。
如下三种使用情况:
*对象::实例方法
*类::静态方法
*类::实例方法
例1:
(x) ->System.out.println(x);
等同于
System.out::println;
例2:
Compare((x,y) ->x.equals(y),”abc”,”abc”)
等同于
Compare(String::equals,”abc”,”abc”)
3.2、构造函数引用
用函数式接口相结合,自动与函数式接口种方法兼容,可以把构造器引用赋值给定 义的方法,构造器参数列表要与接口中抽象方法的参数列表一致。
例子:
Function<Integer,MyClass> fun=(n) ->new MyClass(n);
等同于
Function<Integer,MyClass> fun=MyClass::new;
3.3、数组引用
格式:Type[]::new
例子:Function<Integer,MyClass> fun=(n) ->new Integer[n];
等同于
Function<Integer,MyClass> fun=Integer::new;
4、Stream API
4.1、流(Stream)到底是什么
是数据渠道用于操作数据源(集合,数组等)所生成的元素序列
(集合讲究的是数据,流讲究的是计算)
注意:
*Stream自己不会存储元素
*Stream不会改变源对象;相反,他们会反回持有结果的新流(Stream)
*Stream操作是延迟执行的,需要结果的时候才会执行
4.2、Stream操作的三个步骤
4.2.1、创建Stream
一个数据源(如:集合、数组),获取一个流
4.2.2、中间操作
一个中间操作链,对数据源的数据进行处理
4.2.3、终止操作(终端操作)
一个终止操作,执行中间操作链,并产生结果
------>一系列的中间处理操作(如map(),filter())------>终止操作
4.3、Stream怎么用,
*Java8扩展了集合类,可以通过Collection.stream()或者Collection.parallelStream() 创建一个Stream
下面举例子说明:
List<String > stringCollection=new ArrayList<>();
stringCollection.add(“ddd2”);
stringCollection.add(“aaa2”);
stringCollection.add(“bbb1”);
stringCollection.add(“aaa1”);
stringCollection.add(“bbb3”);
stringCollection.add(“ccc”);
stringCollection.add(“bbb2”);
stringCollection.add(“ddd1”);
4.3.1:filter方法过滤:
StringCollection.stream()
.filete((s) ->s.startsWith(“a”))
.forEach(System.out::println);//输出结果”aaa2”,”aaa1”
4.3.2、sort排序
排序是个中间操作,返回的实拍序号后的stream,如果不指定一个字定义的 Comparator则会使用默认排序;
代码如下:
StringCollection.stream()
.sorted()
Filter((s) ->s.startsWith(“a”))
.forEach(System.out::println);//输出结果”aaa1”,”aaa1”
//排序只创建一个排序好后的stream,而不会影响原来的数据源,排序号之后源数 据StringCollection是不会修改的;
4.3.3、map映射
中间操作map会将元素根据指定的Function接口来依次将元素转成另外的对象,
下面的示例展示了将字符串转换成大写字符串,你也可以通过map来将对象转换 成其他类型,map返回的Stream类型是根据你map传递进去的函数的返回值决定的
例子:
StringCollection.stream()
.map(String::toUpperCase)
.sorted((a,b) ->b,compareTo(a))
.forEach(System.out::println);
//输出结果:”DDD2”,”DDD1”,”CCC”,”BBB3”,”BBB2”,”AAA2”,”AAA1”
4.3.4、Match匹配
Stream提供了多种匹配操作,允许检测制定的Predicate是否匹配整个Stream ,
所有的匹配操作都是最终操作,并且返回一个boolean类型的值
例子:
Boolean anyStartsWithA=
stringCollection.stream()
.anyMatch((s) ->s.startsWith(“a”))
System.out.println(anyStartsWithA);//true
4.3.5、count计数
技术是一个最终操作,返回Stream中元素的个数,返回值类型是long
例子:
Long startsWithB=
stringCollection.stream()
.filter((s)->s.startsWith(“b”))
.count();
System。Out.println(startsWithB);//3
4.3.6、规约
这是一个人最终操作,允许通过指定的函数来将stream中的多个元素规约为一个 元素,规约后的结果是通过Optional接口表示的
代码:
Optional<String> reduced=
stringCollection.stream()
.sorted()
.reduce((s1,s2) ->s1+”#”+s2);
Reduced.ifPresent(System.out::println);
//输出结果:“aaa1#aaa2#bbb2#bbb3#ccc#ddd1#ddd2”
4.3.7、并行Streams
Stream有串行和并行两种,串行Stream上的操作是在一个线程中依次完成,
而并行Stream则是在多个线程上同时执行
通过并行Stream可以提升性能,可以减少执行时间,他没有空闲的线程核
一个线程执行完了,它会去从别的别的线程核末尾偷一个执行
5、接口中的默认方法与静态方法
5.1、接口的默认方法
Java8允许我们给接口添加一个非抽象的方法实现,只需要使用default关键字即可,
这个特征有叫做扩展方法
代码如下:
Interface Formula{
double calculate(int a );
default double sqrt(int a ){
Return Math.sqrt(a);
}
}
在上面Formula接口在拥有calculate方法之外同时还定义了sqrt方法,实现了 Formula接口的子类只需要实现一个calculate方法,默认方法sqrt将在子类上可以 直接使用。
*接口默认方法”类优先”原则:
若一个接口中定义了一个默认的方法,而另外一个父类或接口中又定义了一个同名 的方法时。
---选择父类的方法,如果一个父类提供了具体的实现,那么接口中具有相同名称和 参数的默认方法会忽略
---接口冲突,如果一个父接口提供一个默认的方法,而另外一个接口也提供了一个 具有相同名称和参数列表的方法(不管方法是不是默认方法),那么必须覆盖该方 法来解决冲突
简单例子:
Interface MyFun(){
Defalut String getName()
Return “hello”;
}
}
Interface Named(){
Defalut String getName()
Return “hello word”;
}
}
Class MyClass implements MyFun, Named(){
Public String getName(){
Return Named.super.getName()
}
}
5.2静态方法
接口中允许添加静态方法
例子:
Interface Nmaed(){
Public Integer Myfun();
Defalut String getName(){
Return “hello”;
]
Static void show(){
System .out.println(“静态方法引用”);
}
}
6、新时间日期API
6.1、在java8中,添加了一个新包:java.time,它提供了结构良好的API来处理时间和日 期;
新的API:java.time,由5个包组成:
.java.time --包含值对象的基础包
.java.time.chrono --提供对不同的日历系统的访问
.java.time.format --格式化和解析时间日期
.java.time.temporal --包括底层框架和扩展特性
.java.time.zone --包含时区支持的类
*以前版本存在的问题
1、可变性:所有的日期类都是可变的,线程不安全
2、偏移性:Data中的年份是从1990开始的,而月份都是从0开始的
3、java的日期/时间类的定义并不一致,在Java.Util和Java.sql的包中都有日期类, 此为用于格式化和解析的类在java.text包中定义
4、java.util.Date 同时包含日期和时间,而java.sql.Date仅包含日期,将其纳入java.sql
包并不合理,另外这连个类都有相同的名字,本身设计不合理
5、对于时间、时间戳、格式化以及解析,并没有一些明确定义的类。对于格式化 和解析的需求,我们有java.text.DateFormat类被用于此类需求
6、日期类并不提供国际化,没有时区支持,因此java引入了java.util.Calendar和 java.util.TimeZone类,但他们同样存在上述所有的问题
*LocalDate类方法例子如下:
LocalDate date=LocalDate .of(2014,Month.JUNE,10);
Int year=date.getYear();//2014
Month month=date.getMonth();//6月
Int dom=date.getDayOfMonth();//10
DayOfWeek dow=date.getDayOfWeek();//星期二
Int len=date.lengthOfMonth();//30(6月份的天数)
bolean leap=date.isLeapYear();//false(不是闰年)
*LocalTime类的用法跟LacalDate相似:
LocalTime time=LocalTime.of(20,30);
Int hour=date.getHour();//20
Int minute=date.getMinute();//30
Time=time.withSecound(6);//20:30:06
Time=time.plusMinutes(3);//20:33:06
*LocalDateTime类是LocalDate和LocalTime的简单组合,它表示一个跟时区无关的日期 和时间;LocalDateTime可以直接创建,或者组合时间和日期:
例子:
LocalDateTime dt1=LocalDateTime.of(2014,Month.JUNE,10,20,30);
LocalDateTime dt2=LocalDateTime .of(date,time);
LocalDateTime dt2=date.atTime(20,30);
LocalDateTime dt4=date.atTime(time);
7、类型注解与重复注解
7.1什么是类型注解
7.1.1、Java8为ElementType枚举增加了TYPE_PARAMETER、TYPE_USE两个枚举值,从而可以使 用@Target(ElementType_TYPE_USE)修饰注解定义,这种注解称为类型注解,可以用在任何使 用到类型的地方;
* ElementType.TYPE_PARAMETE表示注解能写在类型变量的的声明语句中(如
class MyClass{...}).
*ElementType.TYPE_USE表示注解注解能写在使用类型的任何语句中(例如声明语
句、泛型和强制转换语句中的类型)
7.1.2、在Java8之前,注解只能在声明的地方使用,比如类、方法、属性;
java8里面,注解可以应用在任何地方;
简单的举几个例子:
*创建类实例
New @interned MyObject();
*类型映射
myString=(@NonNull String) str;
@Encrypted String data;
* 泛型
List<@NonNull String> strings;//防止非空,java8里面还没有内置这个注解, 编译时会报错要配合框架使用(checker framework)
MyGRAph=(@Immutable Graph) tmpGraph ;
7.2什么是可重复注解
*允许在同一申明类型(类、属性、方法)的多次使用同一个注解
例子:
*Java8之前也有重复注解的解决方案,但可读性不是很好,
Java8里面的做法:
@Repeatable(MyAnnotations.class)
Public @interface MyAnnotation{
String role();
}
Public @interface MyAnnotations{
MyAnnotation[] role();
}
Public class Repeat{
@MyAnnotation(role=”Admin”)
@MyAnnotation(role=”Admin”)
public void TestAnnotations(){
}
}
*Java8不同地方的是,创建重复注解MyAnnotation时,加上@Repeatable,指向存储
注解MyAnnotations(给注解创建一个容器),在使用时候,直接可以重复使用 MyAnnotation
注解
8、Optional类
8.1、*OPtional(java.util.Optional)是一个容器类,可以为null的容器对象,如果值存在则 isPresent()方法会返回true,调用get()方法会返回该对象
*OPtional类的引入很好的解决了空指针异常
8.2、of方法
可以用of方法通过工厂方法创建Optionnal类,创建对象时传入的参数不能为null,
如果传入参数为null,则抛出NullPointerException
简单例子:
//调用工厂方法创建Optional实例
Optional<String> name=Optional.of(“aaaa”);
//传入参数为null,抛出NullPointerException
Optional<String> someNull=Optional.pf(null);
8.3、ofNullable方法
为指定的值创建一个Optionnal,如果指定的值为null,则返回一个空 的 Optional;
ofNull与of方法相似,唯一的区别是可以接受参数为null的情况
示例如下:
//下面创建一个不包含任何值的Optional实例
//例如值为‘null’
Optional empty=Optional.ofNull(null);
8.4、isPresent方法
如果值存在返回true,否则返回false
例子:
//isPresent方法用来检查Optional实例中是否包含值
If(name.isPresent()){
//在Optional实例类调用get()返回存在的值
System.out.println(name.get());
}
8.5、get方法
如果Optional有值则将其返回,否则抛出NosuchElementException.
8.6、orElse方法
如果有值则将其返回,否则返回指定的其他值
8.7、orElesGet方法
orElesGet与orElse方法类似,区别在于得到的默认值。
orElse方法见传入的字符串作为默认值,orElesGet方法可以接受Supplier接口的实 现来生成默认值
8.8、orElseThrow方法
如果有值则将其返回,否则抛出Supplier接口创建的异常
8.9、map方法
如果有值,则对其执行调用mapping函数得到返回值,如果返回值不为null,则创 建包含mapping返回值的Optional作为map方法的返回值,否则返回空Optional.
代码如下:
//map方法执行传入的lambda表达式参数对Optional实力的值进行修改
//lambada表达式的返回值创建新的Optional实例作为map方法的返回值
Optional<String> upperName=name.map((value) ->value.toUpperCase());
System.out.println(upperName.orElse(“No value found”));
8.10、flatMap方法
如果有值,为其执行mapping函数返回Optional类型返回值,否则返回空Optional;
flatMap与map(Funtion)方法类似,区别在于flatMap中的mapper返回值必须是 Optional,调用结束时,flatMap不会对结果用Optional封装
8.11、filter方法
通过传入限定条件对Optional实例的值进行过滤
简单的举个例子:
//filter方法检查给定的Option值是否满足某些条件
//如果满足则返回同一个Optional实例,否则返回空Optional
Optional<String> longName=name.filter((value) ->value.length()>6);
System.out.println(longName.orElse(“The name less than 6characters”));